本章节介绍了 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(())
}
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)
}