Search code examples
gitrustcheckoutlibgit2

How to get the behaviour of `git checkout ...` in Rust git2


I am using the Rust git2 crate to clone Git repositories like this

use git2::Repository;

fn main() {
    let repo = Repository::clone(
        "https://github.com/rossmacarthur/dotfiles",
        "dotfiles"
     ).expect("failed to clone repository");

     repo.checkout("mybranch");  // need something like this.
}

I want to be able to checkout a branch or a commit or a tag.

I have looked at the following documentation but am still not sure which method to use

I am able to do the following but it only changes the files

let object = repo
    .revparse_single("mybranch")
    .expect("failed to find identifier");
repo.checkout_tree(&object, None)
    .expect(&format!("failed to checkout '{:?}'", object));

And if I do a reset it changes the HEAD but not the current branch

repo.reset(&object, git2::ResetType::Soft, None)
    .expect(&format!("failed to checkout '{:?}'", object));

Solution

  • With a more recent version of git2 (v0.13.18):

    use git2::Repository;
    
    fn main() {
        let repo = Repository::clone("https://github.com/rossmacarthur/dotfiles", "/tmp/dots")
            .expect("Failed to clone repo");
    
        let refname = "master"; // or a tag (v0.1.1) or a commit (8e8128)
        let (object, reference) = repo.revparse_ext(refname).expect("Object not found");
        
        repo.checkout_tree(&object, None)
            .expect("Failed to checkout");
    
        match reference {
            // gref is an actual reference like branches or tags
            Some(gref) => repo.set_head(gref.name().unwrap()),
            // this is a commit, not a reference
            None => repo.set_head_detached(object.id()),
        }
        .expect("Failed to set HEAD");
    }
    
    

    Note that checkout_tree only sets the contents of the working tree, and set_head only sets the HEAD. Running only one of them will leave the directory in a dirty state.