tag 的作用是作为重要节点的标记,在需要的时候可以直接切过去。所以在 git 管理系统,比如 github ,直接以带信息描述的 tag 作为 release。并且通常也习惯以版本编号作为 tag 名。
首先来看看仓库里有哪些 tag ,libgit2 提供了两个接口,它们有不同的作用。
1. list
git_strarray tags = {0};
git_tag_list(&tags, rep);
for (int i = 0; i < tags.count; ++i)
{
std::cout << "tag: " << tags.strings[i] << "\n";
}
输出的是 tag 的名字,并且没有更多的详细信息。
2. foreach
int tag_foreach_cb(const char *name, git_oid *oid, void *payload)
{
git_repository *rep = (git_repository *) payload;
git_tag *tag = nullptr;
int error = 0;
std::cout << "tag name: " << name << "\n";
error = git_tag_lookup(&tag, rep, oid);
if (error < 0) {
const git_error *e = giterr_last();
std::cout << "Error: " << error << " / " << e->klass << " : " << e->message << std::endl;
return 0;
}
std::cout << "tag message: " << git_tag_message(tag) << "\n";
git_tag_free(tag);
return 0;
}
git_tag_foreach(rep, tag_foreach_cb, rep);
这个接口是通过回调的方式遍历所有的 tag。回调中的参数 name 返回的是 tag 的完整路径(refs/tags/tagname 这种形式,这样看实际上它也是一种 reference),这一点要特别注意;oid 是这个 tag 的 oid,如果要获取这个 tag 对象的指针,则需要使用 lookup 这个接口去获取——这个接口需要仓库指针作为参数,所以仓库指针需要通过 payload 传入。lookup 的时候有可能会出现找不到的情况,那是因为只有带描述的 tag 才能被找到,也就是说只有名字的轻量级 tag 是找不到的。
然后有必要解释一下什么是轻量级 tag 么?
轻量级 tag 是这个 tag 只有名字,没有其他信息。通过 git tag [tagname] 这个命令来创建;
除了轻量级 tag 之外还有一种就是带附注的 tag,这个 tag 要求创建的时候输入一段描述信息,通过 git tag -a [tagname] -m [message] 这个命令创建。
因此使用 libgit2 创建 tag 也有不同的接口来实现。
1. 轻量级 tag
git_reference *head = nullptr;
git_annotated_commit *commit = nullptr;
git_object *target = nullptr;
git_oid tag_oid;
git_repository_head( &head, rep);
git_annotated_commit_from_ref( &commit, rep, head);
git_object_lookup( &target, rep, git_annotated_commit_id(commit), GIT_OBJ_COMMIT);
error = git_tag_create_lightweight(&tag_oid, rep, "v1.2.3", target, false);
if (error == GIT_EEXISTS)
{
std::cout<< "tag 123 already exists \n";
}
因为 tag 是一种 reference 所以,它一定是以 commit 为节点的,所以 target 参数要找一个目标 commit。
2. 带附注的 tag
git_reference *head = nullptr;
git_annotated_commit *commit = nullptr;
git_object *target = nullptr;
git_oid tag_oid;
git_signature *me = nullptr;
git_repository_head( &head, rep);
git_annotated_commit_from_ref( &commit, rep, head);
git_object_lookup( &target, rep, git_annotated_commit_id(commit), GIT_OBJ_COMMIT);
git_signature_now( &me, "XiaochenFTX", "xiaochenftx@gmail.com");
error = git_tag_create(&tag_oid, rep, "v3.2.1", target, me, "message", false);
if(error==GIT_EEXISTS)
{
std::cout<< "tag 321 already exists \n";
}
相比 lightweight 版本多了两个参数,一个是提交者的信息,另一个是这个 tag 附带的信息。这个信息在 github 的 release 中会对外显示。
下面介绍一个模糊搜索的功能,对应的是 git tag -l “*.*” 这个命令:
git_strarray tags = {0};
git_tag_list_match(&tags, "v0.*", rep);
for (int i = 0; i < tags.count; ++i)
{
std::cout << "tag: " << tags.strings[i] << "\n";
}
接下来是删除 git tag -d [tagname]
error = git_tag_delete(rep, "v1");
如果 error 返回的是 -3 也就是 GIT_ENOTFOUND 这个宏,表示并没有找到输入的这个 tag。
接下来,tag 作为一个标记,就是为了作为一个目标点可以直接切过去。使用命令行工具的命令是 git checkout [tagname],它实现的是将当前 head 切成一个空分支,这个分支指向的就是目标 tag。
然而,在代码中这个操作并不是通过 checkout 系列的接口实现,我们的目标是把对象换到 head 所以,需要的是设置当前的 head。
git_repository_set_head(rep, "refs/tags/v3.2.1");
这里的重点是第二个参数,需要这个 tag 的 reference 形式的全名,在 foreach 的回调中可以获取到,在文件目录里也可以找得到。
最后,要将 tag 上传到远端仓库,需要在 push 的时候显示的指出。就像命令行:git push origin –tags。在代码中的重点就是 push 接口的 refspecs 参数,由这个参数指定 push 什么东西。
git_push_options push_opts = GIT_PUSH_OPTIONS_INIT;
push_opts.callbacks.credentials = cred_acquire_cb;
const char *refs[] = {"refs/tags/*:refs/tags/*"};
git_strarray strarr = {(char **) refs, 1};
git_remote_lookup(&remote, rep, "origin");
git_remote_push(remote, &strarr, &push_opts);
示例代码:sample12