libgit2使用教程(十二)git tag

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

解决了 WordPress 中文标签或分类目录返回404的问题

WordPress 版本: 4.4.1 中文版

问题描述:
在访问中文的标签[tag] 或分类目录[category] 的时候,系统返回404。
举个例子,可以试一下访问 https://ftxtool.org/index.php/tag/测试/ ,显示的页面是“未找到” ,然而是一个正常返回的页面,并不是404;而如果访问一个根本没有的标签或分来目录,比如 https://ftxtool.org/index.php/tag/根本没有/ ,显示的页面是 “有点尴尬诶!该页无法显示。”,而且 http 响应不是200而是404。
所以正确的姿势应该是,访问一个存在的标签,就应该返回200而不是404。

解决方法:
代码文件:wp-includes/class-wp.php
代码:

$req_uri = str_replace($pathinfo, '', $req_uri);

修改为:

$req_uri = str_replace($pathinfo, '', urldecode( $req_uri ));

吐槽:
我不知道网上分享解决方案的那些人是在什么样的环境下解决了问题的,我很愿意相信他们是真的亲自操作遇到问题并解决了。然而我的环境下的这个问题,用那些搜出来的长得都差不多的信息对于从根本上解决问题并没有什么帮助。

调试过程:
经过艰苦卓绝的调试,各种打 log ,最后发现 $_SERVER[‘PATH_INFO’] 和 $_SERVER[‘REQUEST_URI’] 这两个变量的值在编码上是有区别的。PATH_INFO 取出的值是其中的中文是经过 urldecode 之后的,就是正常的中文;而 REQUEST_URI 的中文并没有经过 urldecode ,是那种百分号和16进制数的那种形式。因为这样的区别,所以导致 str_replace 并没有达到其所期望的效果。
因为 replace 后的字符串是错的,导致后边一系列错误导致没有正确匹配到数据库中的数据。

最后吐槽一下 WordPress 官方。在解决完这个问题后,我想把修改的代码提交给官方,然后发现他们的 github 是不接受 pull request 的,只能去官方的网站提交 trac [https://core.trac.wordpress.org/] 。写好之后他们的回复到是挺快的,大叔告诉我这个问题已经有人提过 issus 了,在 #10249#17450,有兴趣的同学可以点进去看。尼玛都有5年的历史了,有木有……新的代码里还是没有加进来,有木有……这是准备了5年的愚人节惊喜么?

RSS
Follow by Email
YouTube
YouTube
Pinterest
fb-share-icon
LinkedIn
Share
VK
Weibo
WeChat
WhatsApp
Reddit
FbMessenger
Copy link
URL has been copied successfully!