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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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