Skip to content

index.add_frombuffer not adding index unless flags changedΒ #1086

Open
@Caleb-T-Owens

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

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions