Merge pull request #1384 from gitbutlerapp/Fix-build-session-tree-function

don't reference alternate odb when flushing session
This commit is contained in:
Nikita Galaiko 2023-10-16 12:49:12 +02:00 committed by GitHub
commit 7c0b9a2150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 114 additions and 71 deletions

View File

@ -430,29 +430,32 @@ impl Repository {
let _lock = self.lock();
let git_repository = git::Repository::open(self.git_repository.path())?;
// update last timestamp
sessions::Writer::new(self).write(session)?;
let mut tree_builder = self.git_repository.treebuilder(None);
let mut tree_builder = git_repository.treebuilder(None);
tree_builder.upsert(
"session",
build_session_tree(self).context("failed to build session tree")?,
build_session_tree(&git_repository).context("failed to build session tree")?,
git::FileMode::Tree,
);
tree_builder.upsert(
"wd",
build_wd_tree(self, project_repository)
build_wd_tree(&git_repository, project_repository)
.context("failed to build working directory tree")?,
git::FileMode::Tree,
);
tree_builder.upsert(
"logs",
build_log_tree(self, project_repository).context("failed to build logs tree")?,
build_log_tree(&git_repository, project_repository)
.context("failed to build logs tree")?,
git::FileMode::Tree,
);
tree_builder.upsert(
"branches",
build_branches_tree(self).context("failed to build branches tree")?,
build_branches_tree(&git_repository).context("failed to build branches tree")?,
git::FileMode::Tree,
);
@ -501,10 +504,6 @@ impl Repository {
self.root().join("session")
}
pub(crate) fn session_wd_path(&self) -> std::path::PathBuf {
self.session_path().join("wd")
}
pub fn default_target(&self) -> Result<Option<target::Target>> {
let current_session = self
.get_or_create_current_session()
@ -617,27 +616,29 @@ impl Repository {
}
fn build_wd_tree(
gb_repository: &Repository,
git_repository: &git::Repository,
project_repository: &project_repository::Repository,
) -> Result<git::Oid> {
match gb_repository
.git_repository
.find_reference("refs/heads/current")
{
match git_repository.find_reference("refs/heads/current") {
Result::Ok(reference) => {
// build the working directory tree from the current commit
// and the session files
let tree = reference.peel_to_tree()?;
let wd_tree_entry = tree.get_name("wd").unwrap();
let wd_tree = gb_repository.git_repository.find_tree(wd_tree_entry.id())?;
let wd_tree = git_repository.find_tree(wd_tree_entry.id())?;
let mut index = git::Index::try_from(wd_tree)?;
let session_wd_reader = reader::DirReader::open(gb_repository.session_wd_path());
let session_wd_path = git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd");
let session_wd_reader = reader::DirReader::open(session_wd_path.clone());
let session_wd_files = session_wd_reader
.list_files(&path::PathBuf::from("."))
.context("failed to read session wd files")?;
for file_path in session_wd_files {
let abs_path = gb_repository.session_wd_path().join(&file_path);
let abs_path = session_wd_path.join(&file_path);
let metadata = abs_path.metadata().with_context(|| {
format!("failed to get metadata for {}", abs_path.display())
})?;
@ -651,7 +652,7 @@ fn build_wd_tree(
Result::Ok(reader::Content::UTF8(content)) => content,
Result::Ok(reader::Content::Large) => {
tracing::error!(
project_id = %gb_repository.project.id,
project_id = %project_repository.project().id,
path = %abs_path.display(),
"large file in session working directory"
);
@ -659,7 +660,7 @@ fn build_wd_tree(
}
Result::Ok(reader::Content::Binary) => {
tracing::error!(
project_id = %gb_repository.project.id,
project_id = %project_repository.project().id,
path = %abs_path.display(),
"binary file in session working directory"
);
@ -667,7 +668,7 @@ fn build_wd_tree(
}
Err(error) => {
tracing::error!(
project_id = %gb_repository.project.id,
project_id = %project_repository.project().id,
path = %abs_path.display(),
?error,
"failed to read file"
@ -689,7 +690,7 @@ fn build_wd_tree(
flags: 10, // normal flags for normal file (for the curious: https://git-scm.com/docs/index-format)
flags_extended: 0, // no extended flags
path: file_path.display().to_string().into(),
id: gb_repository.git_repository.blob(file_content.as_bytes())?,
id: git_repository.blob(file_content.as_bytes())?,
})
.with_context(|| {
format!("failed to add index entry for {}", file_path.display())
@ -697,11 +698,11 @@ fn build_wd_tree(
}
let wd_tree_oid = index
.write_tree_to(&gb_repository.git_repository)
.write_tree_to(git_repository)
.context("failed to write wd tree")?;
Ok(wd_tree_oid)
}
Err(git::Error::NotFound(_)) => build_wd_tree_from_repo(gb_repository, project_repository)
Err(git::Error::NotFound(_)) => build_wd_tree_from_repo(git_repository, project_repository)
.context("failed to build wd index"),
Err(e) => Err(e.into()),
}
@ -710,9 +711,15 @@ fn build_wd_tree(
// build wd index from the working directory files new session wd files
// this is important because we want to make sure session files are in sync with session deltas
fn build_wd_tree_from_repo(
gb_repository: &Repository,
git_repository: &git::Repository,
project_repository: &project_repository::Repository,
) -> Result<git::Oid> {
let session_wd_path = git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd");
let mut index = git::Index::new()?;
// create a new in-memory git2 index and open the working one so we can cheat if none of the metadata of an entry has changed
@ -725,12 +732,10 @@ fn build_wd_tree_from_repo(
// first, add session/wd files. session/wd are written at the same time as deltas, so it's important to add them first
// to make sure they are in sync with the deltas
for file_path in fs::list_files(gb_repository.session_wd_path()).with_context(|| {
format!(
"failed to session working directory files list files in {}",
gb_repository.session_wd_path().display()
)
})? {
for file_path in fs::list_files(&session_wd_path).context(format!(
"failed to session working directory files list files in {}",
session_wd_path.display()
))? {
let file_path = std::path::Path::new(&file_path);
if project_repository
.git_repository
@ -743,9 +748,9 @@ fn build_wd_tree_from_repo(
add_wd_path(
&mut index,
repo_index,
&gb_repository.session_wd_path(),
&session_wd_path,
file_path,
gb_repository,
git_repository,
)
.with_context(|| {
format!(
@ -782,7 +787,7 @@ fn build_wd_tree_from_repo(
repo_index,
project_repository.root(),
file_path,
gb_repository,
git_repository,
)
.with_context(|| {
format!(
@ -793,7 +798,7 @@ fn build_wd_tree_from_repo(
}
let tree_oid = index
.write_tree_to(&gb_repository.git_repository)
.write_tree_to(git_repository)
.context("failed to write tree to repo")?;
Ok(tree_oid)
}
@ -807,7 +812,7 @@ fn add_wd_path(
repo_index: &mut git::Index,
dir: &std::path::Path,
rel_file_path: &std::path::Path,
gb_repository: &Repository,
git_repository: &git::Repository,
) -> Result<()> {
let file_path = dir.join(rel_file_path);
@ -835,12 +840,6 @@ fn add_wd_path(
// insert a pointer as the blob content instead
// TODO: size limit should be configurable
let blob = if metadata.len() > 100_000_000 {
tracing::warn!(
project_id = %gb_repository.project.id,
path = %file_path.display(),
"file too big"
);
// get a sha256 hash of the file first
let sha = sha256_digest(&file_path)?;
@ -855,18 +854,15 @@ fn add_wd_path(
// write the file to the .git/lfs/objects directory
// create the directory recursively if it doesn't exist
let lfs_objects_dir = gb_repository.git_repository.path().join("lfs/objects");
let lfs_objects_dir = git_repository.path().join("lfs/objects");
std::fs::create_dir_all(lfs_objects_dir.clone())?;
let lfs_path = lfs_objects_dir.join(sha);
std::fs::copy(file_path, lfs_path)?;
gb_repository
.git_repository
.blob(lfs_pointer.as_bytes())
.unwrap()
git_repository.blob(lfs_pointer.as_bytes()).unwrap()
} else {
// read the file into a blob, get the object id
gb_repository.git_repository.blob_path(&file_path)?
git_repository.blob_path(&file_path)?
};
// create a new IndexEntry from the file metadata
@ -911,14 +907,14 @@ fn sha256_digest(path: &std::path::Path) -> Result<String> {
Ok(format!("{:X}", digest))
}
fn build_branches_tree(gb_repository: &Repository) -> Result<git::Oid> {
fn build_branches_tree(git_repository: &git::Repository) -> Result<git::Oid> {
let mut index = git::Index::new()?;
let branches_dir = gb_repository.root().join("branches");
let branches_dir = git_repository.path().join("gitbutler").join("branches");
for file_path in fs::list_files(&branches_dir).context("failed to find branches directory")? {
let file_path = std::path::Path::new(&file_path);
add_file_to_index(
gb_repository,
git_repository,
&mut index,
file_path,
&branches_dir.join(file_path),
@ -927,14 +923,14 @@ fn build_branches_tree(gb_repository: &Repository) -> Result<git::Oid> {
}
let tree_oid = index
.write_tree_to(&gb_repository.git_repository)
.write_tree_to(git_repository)
.context("failed to write index to tree")?;
Ok(tree_oid)
}
fn build_log_tree(
gb_repository: &Repository,
git_repository: &git::Repository,
project_repository: &project_repository::Repository,
) -> Result<git::Oid> {
let mut index = git::Index::new()?;
@ -944,14 +940,14 @@ fn build_log_tree(
add_log_path(
std::path::Path::new(&file_path),
&mut index,
gb_repository,
git_repository,
project_repository,
)
.with_context(|| format!("failed to add log file to index: {}", file_path.display()))?;
}
let tree_oid = index
.write_tree_to(&gb_repository.git_repository)
.write_tree_to(git_repository)
.context("failed to write index to tree")?;
Ok(tree_oid)
@ -960,7 +956,7 @@ fn build_log_tree(
fn add_log_path(
rel_file_path: &std::path::Path,
index: &mut git::Index,
gb_repository: &Repository,
gb_repository: &git::Repository,
project_repository: &project_repository::Repository,
) -> Result<()> {
let file_path = project_repository
@ -984,34 +980,33 @@ fn add_log_path(
flags: 10, // normal flags for normal file (for the curious: https://git-scm.com/docs/index-format)
flags_extended: 0, // no extended flags
path: rel_file_path.to_str().unwrap().to_string().into(),
id: gb_repository.git_repository.blob_path(&file_path)?,
id: gb_repository.blob_path(&file_path)?,
})?;
Ok(())
}
fn build_session_tree(gb_repository: &Repository) -> Result<git::Oid> {
fn build_session_tree(git_repository: &git::Repository) -> Result<git::Oid> {
let mut index = git::Index::new()?;
let session_path = git_repository.path().join("gitbutler").join("session");
// add all files in the working directory to the in-memory index, skipping for matching entries in the repo index
for file_path in
fs::list_files(&gb_repository.session_path()).context("failed to list session files")?
{
for file_path in fs::list_files(&session_path).context("failed to list session files")? {
let file_path = std::path::Path::new(&file_path);
if file_path.starts_with("wd/") {
continue;
}
add_file_to_index(
gb_repository,
git_repository,
&mut index,
file_path,
&gb_repository.session_path().join(file_path),
&session_path.join(file_path),
)
.with_context(|| format!("failed to add session file: {}", file_path.display()))?;
}
let tree_oid = index
.write_tree_to(&gb_repository.git_repository)
.write_tree_to(git_repository)
.context("failed to write index to tree")?;
Ok(tree_oid)
@ -1019,12 +1014,12 @@ fn build_session_tree(gb_repository: &Repository) -> Result<git::Oid> {
// this is a helper function for build_gb_tree that takes paths under .git/gb/session and adds them to the in-memory index
fn add_file_to_index(
gb_repository: &Repository,
git_repository: &git::Repository,
index: &mut git::Index,
rel_file_path: &std::path::Path,
abs_file_path: &std::path::Path,
) -> Result<()> {
let blob = gb_repository.git_repository.blob_path(abs_file_path)?;
let blob = git_repository.blob_path(abs_file_path)?;
let metadata = abs_file_path.metadata()?;
let mtime = FileTime::from_last_modification_time(&metadata);
let ctime = FileTime::from_creation_time(&metadata).unwrap_or(mtime);

View File

@ -285,7 +285,15 @@ mod test {
deltas::Operation::Insert((4, "2".to_string())),
);
assert_eq!(
std::fs::read_to_string(gb_repository.session_wd_path().join("test.txt"))?,
std::fs::read_to_string(
gb_repository
.git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd")
.join("test.txt")
)?,
"test2"
);
@ -358,7 +366,15 @@ mod test {
assert_eq!(deltas.len(), 1);
assert_eq!(deltas[0].operations.len(), 0);
assert_eq!(
std::fs::read_to_string(gb_repository.session_wd_path().join("test.bin"))?,
std::fs::read_to_string(
gb_repository
.git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd")
.join("test.bin")
)?,
""
);
@ -390,7 +406,15 @@ mod test {
deltas::Operation::Insert((0, "test".to_string())),
);
assert_eq!(
std::fs::read_to_string(gb_repository.session_wd_path().join("test.txt"))?,
std::fs::read_to_string(
gb_repository
.git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd")
.join("test.txt")
)?,
"test"
);
@ -421,7 +445,15 @@ mod test {
deltas::Operation::Insert((0, "test".to_string())),
);
assert_eq!(
std::fs::read_to_string(gb_repository.session_wd_path().join("test.txt"))?,
std::fs::read_to_string(
gb_repository
.git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd")
.join("test.txt")
)?,
"test"
);
@ -441,7 +473,15 @@ mod test {
deltas::Operation::Insert((4, "2".to_string())),
);
assert_eq!(
std::fs::read_to_string(gb_repository.session_wd_path().join("test.txt"))?,
std::fs::read_to_string(
gb_repository
.git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd")
.join("test.txt")
)?,
"test2"
);
@ -472,7 +512,15 @@ mod test {
deltas::Operation::Insert((0, "test".to_string())),
);
assert_eq!(
std::fs::read_to_string(gb_repository.session_wd_path().join("test.txt"))?,
std::fs::read_to_string(
gb_repository
.git_repository
.path()
.join("gitbutler")
.join("session")
.join("wd")
.join("test.txt")
)?,
"test"
);