Cómo obtener el comportamiento de `git checkout …` en Rust git2

4 minutos de lectura

Avatar de usuario de Ross MacArthur
ross macarthur

estoy usando el óxido git2 crate para clonar repositorios Git como este

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.
}

Quiero poder pagar una rama o una confirmación o una etiqueta.

He mirado la siguiente documentación pero todavía no estoy seguro de qué método usar

Puedo hacer lo siguiente pero solo cambia los archivos

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

Y si hago un reset cambia el HEAD pero no la rama actual

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

avatar de usuario de szignal
szignal

Los siguientes ejemplos son Rust v1.34 y git2 v0.8.

Para pagar una sucursal:

use git2::*;

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

    let branch_name = "my_branch";

    let head = repo.head().unwrap();
    let oid = head.target().unwrap();
    let commit = repo.find_commit(oid).unwrap();

    let branch = repo.branch(
        branch_name,
        &commit,
        false,
    );

    let obj = repo.revparse_single(&("refs/heads/".to_owned() + 
        branch_name)).unwrap();

    repo.checkout_tree(
        &obj,
        None
    );

    repo.set_head(&("refs/heads/".to_owned() + branch_name));
}

Para verificar un compromiso:

use git2::*;

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

    let my_oid_str = "9411953f92d100f767e6de6325b17afae5231779";

    let oid = Oid::from_str(my_oid_str).unwrap();
    let commit = repo.find_commit(oid).unwrap();

    let branch = repo.branch(
        my_oid_str,
        &commit,
        false,
    );

    let obj = repo.revparse_single(&("refs/heads/".to_owned() + my_oid_str)).unwrap(); 

    repo.checkout_tree(
        &obj,
        None,
    );

    repo.set_head(&("refs/heads/".to_owned() + my_oid_str));

}

Para pagar una etiqueta, intente algo como esto:

use git2::*;

fn main() {
    // No relation to the example project below.
    // It looks interesting and it has tags!
    let repo = Repository::clone(
        "https://github.com/narke/colorForth.git",
        "colorforth"
    ).expect("failed to clone repository");

    let tag_name = "v2012";

    let references = repo.references().unwrap();

    for reference in references {
        let _reference = reference.unwrap();

        if _reference.is_tag() == true {
            let tag = _reference.peel_to_tag().unwrap();
            if tag.name().unwrap() == tag_name {
                let target_oid = tag.target().unwrap().id();
                let commit = repo.find_commit(target_oid).unwrap();

                let branch = repo.branch(
                    tag_name,
                    &commit,
                    false,
                );

                let obj = repo.revparse_single(&("refs/heads/".to_owned() + tag_name)).unwrap();

                repo.checkout_tree(
                    &obj,
                    None,
                );

                repo.set_head(&("refs/heads/".to_owned() + tag_name)); 
            }
        }

    }
}

Apuesto a que generalmente hay una mejor manera, pero la pregunta no ha sido respondida durante meses y así es como lo descubrí hace unos momentos.

  • Mi publicación actualizada resuelve completamente la pregunta del OP. Se agradecen los votos a favor. Ni siquiera tengo suficientes puntos para comentar en la publicación de otros, como la del OP, así que puedo dejarles ahora que respondí con éxito la pregunta. Trabajaré para que la respuesta sea más idiomática si se muestra algún interés por esta pregunta.

    – szignal

    10 de julio de 2019 a las 22:39

  • En el ejemplo para verificar un compromiso, el método adecuado para usar es set_head_detached()set_head() espera un nombre de referencia como refs/heads/master o HEAD (pero un hash de confirmación no es una referencia). Así que la forma correcta es repo.set_head_detached(oid);.

    – sudormrfbin

    23 de abril de 2021 a las 10:27


avatar de usuario de sudormrfbin
sudormrfbin

Con una versión más reciente de 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");
}

Tenga en cuenta que checkout_tree sólo establece el contenido del árbol de trabajo, y set_head solo establece el HEAD. Ejecutar solo uno de ellos dejará el directorio en un estado sucio.

  • Gracias, esto es mucho más limpio!

    –Ross MacArthur

    14 de julio de 2021 a las 14:19

pienso repo.set_head("mybranch") es lo que buscas Más información está disponible aquí.

  • 1. repo.set_head("refs/heads/mybranch")2. esto no cambiará los archivos en el directorio de trabajo

    – Shimón S.

    12 de enero a las 13:58


¿Ha sido útil esta solución?