Search code examples
gitrustlibgit2

Rust libgit2: how to add --force


How to do a git add --force * with libgit2 in Rust?

I tried the following code from the documentation:

use git2::{Index, IndexAddOption, Repository};

let repo = Repository::open("/path/to/a/repo").expect("failed to open");
let mut index = repo.index().expect("cannot get the Index file");
index.add_all(["*"].iter(), IndexAddOption::DEFAULT, None);
index.write();

and it seems to work. Then I replaced IndexAddOption::DEFAULT to IndexAddOption::FORCE and I get the following error (example from a test on the Linux Kernel source code):

Error { code: -1, klass: 10, message: "invalid path: 'arch/i386/'" }', src/main.rs:58:14

What I understand is that when I use FORCE, it will add "files" that are "non-standard" (like binaries or even directories) for git. That's why it is trying to add a directory and raises the invalid path error.

I just would like to implement a git add --force * using libgit2 API in Rust. Any suggestions?

Thank you.


Solution

  • It is possible to define a callback function IndexMatchedPath like in this example.

    Using this function raises the same error with a precision that it is from git.status_file(). In fact, on Line 42 of the previous example, we have this line:

            let status = repo.status_file(path).unwrap();
    

    First, to remove the error, we can check if path is a directory or not. We can ignore if it is a directory like mentioned in the documentation of git2::IndexMatchedPath:

    Return 0 to confirm the operation on the item, > 0 to skip the item, and < 0 to abort the scan.

    However, we still have the error with if path.is_dir() { 1 }else {...}. Hence, just extracting the string with path.to_str().unwrap() and testing if it ends_with("/") seems to work. Our function looks like this:

            let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
                if path.to_str().unwrap().ends_with("/") { 1 }else{
                    let status = self.repository.status_file(path).unwrap();
                    ...
            };
    

    Now that we removed the error, we move to the implementation of --force. In the example, it checks the status of the files to add:

            let ret = if status.contains(git2::Status::WT_MODIFIED)
                || status.contains(git2::Status::WT_NEW)
    

    It adds modified or new files (more info here). So, in addition of modified and new files, we can include ignored files with git2::Status::IGNORED:

            let ret = if status.contains(git2::Status::WT_MODIFIED)
                || status.contains(git2::Status::WT_NEW)
                || status.contains(git2::Status::IGNORED)
    

    So now, we use IndexAddOption::FORCE and cb:

    index.add_all(["*"].iter(), IndexAddOption::FORCE, Some(cb as &mut git2::IndexMatchedPath));