`index.add_frombuffer` not adding index unless flags changed
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 2.1k
- Forks
- 450
- Avg merge
- 11m
- Merged PRs (30d)
- 1
Description
I was writing some tests for our index resolving code, and noticed that when we're calling index.add_frombuffer if we don't clear the flags of our our IndexEntry, the tree that we write out ends up with that file missing.
Repro:
main.rs
use std::{
fs,
path::{Path, PathBuf},
};
use git2::IndexConflict;
use tempfile::tempdir;
/// Commit whatever is in the current working directory
fn commit<'a>(
repository: &'a git2::Repository,
parent: Option<&git2::Commit<'a>>,
) -> git2::Commit<'a> {
let signature = git2::Signature::now("Caleb", "caleb@gitbutler.com").unwrap();
let mut index = repository.index().unwrap();
// Make sure we're not having weird cached state
index.read(true).unwrap();
index
.add_all(["*"], git2::IndexAddOption::DEFAULT, None)
.unwrap();
let commit = repository
.commit(
None,
&signature,
&signature,
"Committee",
&repository.find_tree(index.write_tree().unwrap()).unwrap(),
parent.map(|c| vec![c]).unwrap_or_default().as_slice(),
)
.unwrap();
repository.find_commit(commit).unwrap()
}
fn bytes_to_path(path: &[u8]) -> PathBuf {
let path = std::str::from_utf8(path).unwrap();
Path::new(path).to_owned()
}
fn in_memory_repository(repository: &git2::Repository) -> git2::Repository {
let repository = git2::Repository::open(repository.path()).unwrap();
repository
.odb()
.unwrap()
.add_new_mempack_backend(999)
.unwrap();
repository
}
fn main() {
let tempdir = tempdir().unwrap();
let repository = git2::Repository::init(tempdir.path()).unwrap();
// Make some commits
fs::write(tempdir.path().join("foo.txt"), "a").unwrap();
let a = commit(&repository, None);
fs::write(tempdir.path().join("foo.txt"), "b").unwrap();
let b = commit(&repository, None);
fs::write(tempdir.path().join("foo.txt"), "c").unwrap();
let c = commit(&repository, None);
let in_memory_repository = in_memory_repository(&repository);
let mut index: git2::Index = repository
.merge_trees(
&a.tree().unwrap(), // Base
&b.tree().unwrap(), // Ours
&c.tree().unwrap(), // Theirs
None,
)
.unwrap();
in_memory_repository.set_index(&mut index).unwrap();
assert!(index.has_conflicts());
let mut conflicts = index.conflicts().unwrap().flatten().collect::<Vec<_>>();
assert_eq!(conflicts.len(), 1);
let conflict = conflicts.first_mut().unwrap();
let IndexConflict {
ancestor: Some(ancestor),
our: Some(our),
their: Some(their),
} = conflict
else {
panic!("Ahh");
};
index.remove_path(&bytes_to_path(&ancestor.path)).unwrap();
index.remove_path(&bytes_to_path(&their.path)).unwrap();
let blob = repository.find_blob(our.id).unwrap();
// our.flags = 0;
index.add_frombuffer(&our, blob.content()).unwrap();
let tree = index.write_tree_to(&repository).unwrap();
repository.find_tree(tree).unwrap();
assert_eq!(
tree,
git2::Oid::from_str("6e4760ce692776132d52ac0787b7dc1ca2ac15f4").unwrap()
) // Ends up being 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (empty tree)
}
Cargo.toml
[package]
name = "strange_behaviour"
version = "0.1.0"
edition = "2021"
[dependencies]
git2 = { version = "0.19.0", features = [
"vendored-openssl",
"vendored-libgit2",
] }
tempfile = "3.10"
If we set our.flags to 0 ourselves, before we call add_frombuffer, then we end up with the tree that we expected.
It's a bit strange that we're ending up with this empty tree rather than having some form of error
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the reproduction in main.rs with the dependencies in Cargo.toml, then trace the Index::add_frombuffer entry point and how IndexEntry flags are handled. Done means the resolved entry is included in the written tree without manually clearing our.flags, with a regression test covering the reported case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, rust
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100