gitrustlibgit2git2-rs

Git2 Rust: Unexplained INDEX_DELETED | WT_NEW Status and Appearance of Untracked 'commit2.txt' File After Reloading Repository


I've got an issue using Git2 lib for Rust. I'm trying to init a repository in a test with a specific state that could be recreated using theses commands:

git init
touch commit1.txt
git add commit1.txt
git commit -m "First Commit"
git checkout -b "ft/feature"
touch commit2.txt
git add commit2.txt
git commit -m "Second Commit"

To do that, I first init the repository using a temporary directory, then I pass the path to the temporary directory in the function that I'd like to test. In the function, after reopening the repository, I do have an issue where "commit2.txt" reappears in my statuses:

Status after initialization:

    Path:                                   C:/Users/mail/AppData/Local/Temp/.tmpuT5PaF/.git/

    Active Branch:

            Name:                                   refs/heads/ft/my-new-feature

            Last Commit ID:                         b6a3b9b8ab2347ad1a70a15a284c54275e31e062
            Last Commit Message:                    Feature Commit
            Repository Status:                      Clean
    File statuses:
    Branches:
            Local:
                    refs/heads/ft/my-new-feature
                    refs/heads/main
            Remote:
                    refs/remotes/origin/acceptance
                    refs/remotes/origin/develop
                    refs/remotes/origin/HEAD
                    refs/remotes/origin/main

Status after reload:

    Path:                                   C:/Users/mail/AppData/Local/Temp/.tmpuT5PaF/.git/
    Active Branch:
            Name:                                   refs/heads/ft/my-new-feature
            Last Commit ID:                         b6a3b9b8ab2347ad1a70a15a284c54275e31e062
            Last Commit Message:                    Feature Commit
            Repository Status:                      Clean
    File statuses:
            commit2.txt: Status(INDEX_DELETED | WT_NEW)
    Branches:
            Local:
                    refs/heads/ft/my-new-feature
                    refs/heads/main
            Remote:
                    refs/remotes/origin/acceptance
                    refs/remotes/origin/develop
                    refs/remotes/origin/HEAD
                    refs/remotes/origin/main

Here is the initialization code:

fn init_remote_repository(path: &Path) -> Repository {
  let repo = Repository::init(path).unwrap();
  let new_file_name = "commit1.txt";
  let mut f = File::create(path.join(new_file_name)).unwrap();
  f.write_all(b"First commit").unwrap();   
  {
        commit(&repo, "Initial Commit", vec![new_file_name]);
  }
  repo

}

fn init_local_repository(path: &Path, remote_repo: &Repository) -> Repository {
    let repo = Repository::clone(remote_repo.path().to_str().unwrap(), path).unwrap();
    let feature_branch_name = "ft/my-new-feature";
    let feature_branch_reference_name = format!("refs/heads/{}", feature_branch_name);
    let commit_id = repo.head().unwrap().target().unwrap();
    {
        let last_active_commit = repo.find_commit(commit_id).unwrap();
        repo.branch(feature_branch_name, &last_active_commit, false)
            .unwrap();
        repositories::checkout(&repo, &feature_branch_reference_name).unwrap();
        let new_file_name = "commit2.txt";
        let mut f = File::create(path.join(new_file_name)).unwrap();
        f.write_all(b"Second commit").unwrap();
        commit(&repo, "Feature Commit", vec![new_file_name]);
    }
    repo
}

pub fn get_last_active_commit(repo: &Repository) -> Commit {
    let reference = repo.head().unwrap();
    let commit_id = reference.target().unwrap();
    repo.find_commit(commit_id).unwrap()
}

pub fn commit<'a>(repo: &'a Repository, msg: &str, paths_to_add: Vec<&str>) -> Commit<'a> {
    let signature = Signature::now("testuser", "test@fakemail.com").unwrap();
    let mut index = repo.index().unwrap();
    for path in paths_to_add {
        index.add_path(Path::new(path)).unwrap();
    }
    let tree_id = index.write_tree().unwrap();
    let commit = match repo.head() {
        Ok(reference) => {
            let commit_id = reference.target().unwrap();
            let commit = repo.find_commit(commit_id).unwrap();
            Some(commit)
        }
        Err(_) => None,
    };
    let parents_commit = match commit.as_ref() {
        None => {
            vec![]
        }
        Some(c) => {
            vec![c]
        }
    };

    let tree = repo.find_tree(tree_id).unwrap();

    let last_commit_id = repo
        .commit(
            Some("HEAD"),
            &signature,
            &signature,
            msg,
            &tree,
            &parents_commit,
        )
        .unwrap();
    repositories::print_status("Local-Commit-AfterCommit", &repo);
    let commit = repo.find_commit(last_commit_id).unwrap();
    commit
}

Here is the code with the call to the the function to be tested:

repositories::merge(local_dir.path()).unwrap();

where local_dir is the directory used to initialize the repo. And here is the internal of

pub fn ulis3_merge(path: &Path) -> Result<(), Errors> {
    let repo = match Repository::open(path) {
        Ok(repo) => { repo }
        Err(e) => {
            return Err(Errors::CouldNotOpenRepository {
                path: path.to_str().unwrap_or("").to_string(),
                source: e,
            });
        }
    };
    let branch_name = current_branch_name(&repo)?;
    merge_and_push(&repo, &branch_name, "develop")?;
}

I'm not sure what I'm doing wrong :(


Solution

  • The problem was in my commit function, I needed to add index.write().unwrap(); after adding the files to the index:

    pub fn commit<'a>(repo: &'a Repository, msg: &str, paths_to_add: Vec<&str>) -> Commit<'a> {
        let signature = Signature::now("testuser", "test@fakemail.com").unwrap();
        let mut index = repo.index().unwrap();
        for path in paths_to_add {
            index.add_path(Path::new(path)).unwrap();
        }
        index.write().unwrap();
        let tree_id = index.write_tree().unwrap();
        let commit = match repo.head() {
            Ok(reference) => {
                let commit_id = reference.target().unwrap();
                let commit = repo.find_commit(commit_id).unwrap();
                Some(commit)
            }
            Err(_) => None,
        };
        let parents_commit = match commit.as_ref() {
            None => {
                vec![]
            }
            Some(c) => {
                vec![c]
            }
        };
    
        let tree = repo.find_tree(tree_id).unwrap();
    
        let last_commit_id = repo
            .commit(
                Some("HEAD"),
                &signature,
                &signature,
                msg,
                &tree,
                &parents_commit,
            )
            .unwrap();
        repositories::print_status("Local-Commit-AfterCommit", &repo);
        let commit = repo.find_commit(last_commit_id).unwrap();
        commit
    }