本章节介绍了 libgit2 开发时碰到的一些问题。主要问题体现在 libgit2 操作和 git cli 操作不一致的地方。

Checkout

和 Git cli 表现的简洁性不同。Checkout 实际上分为了两步:

  • 将 HEAD 指向指定 commit。

  • 将指定 commit 情况下的文件放在磁盘中。

在 libgit2 中这两步是分开的,使用 libgit2 实现此功能的步骤为:

/// https://stackoverflow.com/a/46758861
fn checkout_branch(&self, name: &str) -> AppResult<()> {
    let branch = self.revparse_single(name)?;
    let mut opts = CheckoutBuilder::new();
    let opts = opts.safe().force();
    self.checkout_tree(&branch, Some(opts))?;
    self.set_head(&format!("refs/heads/{name}"))?;

    Ok(())
}

检出到 commit 而不是 branch 的操作与此不同:

pub fn commit_checkout(repo_path: &str, commit: &str) -> AppResult<()> {
    let repo = utils::open_repo(repo_path)?;
    let oid = Oid::from_str(commit)?;
    repo.set_head_detached(oid)?;

    let mut opts = CheckoutBuilder::new();
    let opts = opts.safe().force(); (1)
    repo.checkout_head(Some(opts))?;

    Ok(())
}
  1. opts 必须提供。否则检出的分支不是干净的。

Tag

Git 中分为两种 tag:轻量型 tag 和 Annotated tags。

  • 轻量型 tag:默认 tag。不创建 tag 对象。tag 直接指向原始 commit。

  • Annotated tag:如果指定了 -a, -s, -u 或者使用 GPG 对 tag 签名了。则创建一个 tag 对象,tag 的 hash 值指向 tag 自身的 hash。

Annotated tag 在进行 checkout 后,检出后的 hash 和 tag hash 不同。

由于轻量型 tag 实际上并没有创建 tag 对象,因此无法使用 find_tag 进行索引。

下面演示了如何获取所有 tag 并获取 Annotated tag 指向的 commit 的 hash:

fn get_tags(&self) -> AppResult<Vec<TagInfo>> {
    let mut taginfos = vec![];
    self.tag_foreach(|oid, name| {
        let name = std::str::from_utf8(name)
            .unwrap()
            .strip_prefix("refs/tags/")
            .unwrap()
            .to_string();
        let commit = oid.to_string();
        let mut ref_hash = commit.clone();

        if let Ok(tag) = self.find_tag(oid) {
            ref_hash = tag.target().unwrap().id().to_string();
        }
        taginfos.push(TagInfo {
            name,
            commit,
            ref_hash,
        });
        true
    })?;

    Ok(taginfos)
}
Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx