记录一次无聊的(经历了Nodejs -> Shell -> C)的探索问题过程
提出问题
在运行项目的服务器的git是1.8.3.1版本的时候,pm2 deploy 项目,服务器fetch不到最新的一次commit。
对于这个问题,在pm2的github也有issues讨论。然后开issues的人表示 pm2-deploy is garbage 并且觉得 I find it funny that it is easier for the authors to blame the problem on git or anything else rather than change one single line of code to make it work... excelent pm2 deploy couldn't be any more wonderfull. em… 呵呵,表示不服,其实这个问题真的有一半原因得归咎于git …
解决方案(针对服务器上git版本 < 1.9.0)
1. 升级服务器git版本 >= 1.9.0
2. 回退pm2版本至1.x,如果该项目用不到pm2 后期版本的一些功能的话...
由于文章中的测试验证过程略显无聊(一步一步的挖掘问题所在),且以上已给出解决方案,有遇到类似问题的小伙伴可参考方案解决问题,有兴趣交流下debugger过程的同学可继续往下看。
git都出了2.14.1了,为什么还在用1.8.3.1的老版本呢?
我们用的是阿里云的服务器,使用yum安装的git包,然而yum源里的git包是1.8.3.1的,对于像我这种的新手来说,要不是遇到问题,我的态度是 "还有这操作? (。◕ˇ∀ˇ◕)"
进入探索流程
问题由pm2出发,首先我们找到pm2是怎么处理deploy命令的:
打开 pm2 地址:https://github.com/Unitech/pm2 ,进入 lib/API.js ,我们在文件中找到:
require('./API/Deploy.js')(API);
于是打开该Deploy.js:
var Deploy = require('pm2-deploy'); /*.其他代码.*/ module.exports = function (CLI) {
CLI.prototype.deploy = function (file, commands, cb) {
Deploy.deployForEnv(json_conf.deploy, env, args, function (err, data) { /*.其他代码.*/ if (err) {
Common.printError('Deploy failed');
return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('--> Success');
return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT);
});
}
}
由此,我们又将查找对象指向了pm2-deploy模块的Deploy对象
打开 pm2-deploy 地址:https://github.com/Unitech/pm2-deploy ,找到deploy.js 并且打开:
// 在里面找到 deployForEnv 方法 并发现在进行一堆参数处理后走到了spawn方法,纵观spawn方法,执行了个shell脚本 function spawn(hostJSON, args, cb) {
var shellSyntaxCommand = "echo '" + hostJSON + "' | \"" + __dirname.replace(/\\/g, '/') + "/deploy\" " + args.join(' ');
var proc = childProcess.spawn('sh', ['-c', shellSyntaxCommand], {
stdio: 'inherit'
}); proc.on('error', function (e) {
return cb(e.stack || e);
}); proc.on('close', function (code) {
if (code == 0) return cb(null, args);
else return cb(code);
});
}
好了,现在到了shell了...
打开deploy的sh文件 => 找到deploy方法(所幸作者打了闪闪发光的注释,很简单的我们就找到哪里是重点):
deploy() {
# pre-deploy hook # fetch source
log fetching updates
run "cd $path/source && git fetch --depth=5 --all --tags"
test $? -eq 0 || abort fetch failed # latest tags & reset HEAD & link current & deploy log 等操作
}
在这里看到,最新版本的pm2-deploy执行了 git fetch --depth=5 --all --tags 来fetch最新一次的提交,那么这个fetch是不是存在问题呢?因为git-1.8.3.1对应使用pm2-1.x版本可行,那么我们看看pm2-deploy早期的deploy里的fetch是怎么写的,打开早期版本: https://github.com/Unitech/pm2-deploy/blob/0.2.0/deploy
deploy() {
# pre-deploy hook # fetch source
log fetching updates
run "cd $path/source && git fetch --all"
test $? -eq 0 || abort fetch failed # latest tags & reset HEAD & link current & deploy log 等操作
}
好了,看出区别了, git fetch --all 和 git fetch --depth=5 --all --tags
于是上git官网查这2个参数,然后失望而归,官网文档最老版本仅能找到1.9.0的,那么怎么办?
我们上这个网站:https://www.kernel.org 在其/pub/software/scm/git/文件夹下可以看到各个版本的git源码压缩包,下载git-1.8.3.1.tar.gz => 解压 => vscode中打开文件夹 ,git每个版本包自带文档,打开看看,Documentation/fetch-options.txt 1.9.0 对比 1.8.3.1:
/*
1.8.3.1
-t::
--tags::
This is a short-hand for giving "refs/tags/*:refs/tags/*"
refspec from the command line, to ask all tags to be fetched
and stored locally. Because this acts as an explicit
refspec, the default refspecs (configured with the
remote.$name.fetch variable) are overridden and not used. */ /*
1.9.0
-t
--tags
Fetch all tags from the remote (i.e., fetch remote tags refs/tags/*
into local tags with the same name), in addition to whatever else
would otherwise be fetched. Using this option alone does not subject
tags to pruning, even if --prune is used (though tags may be pruned
anyway if they are also the destination of an explicit refspec; see
--prune).
*/
大家来找茬 + 推测 --tags 参数的描述可能是导致问题的关键 :refspec(Reference Specification/参考规范,这里个人觉得理解为本地和远程的对应关系更适合),1.8.3.1 的说这个操作是基于明确的映射关系滴,所以默认的映射关系将被覆盖并且不被使用。
那么到底是不是refspecs存在问题呢? 还是明明是 remotes tags 存在问题导致不能fetch到?先测试看看结果,不同版本、不同参数的 git fetch 测试:
初始化
创建文件 test.txt 内容为 test > 3
push 到 test 分支
服务器 pull 代码,确保test.txt文件存在且内容值为 test > 3
如图:
1.8.3.1版本测试:
修改test.txt 为 test > 4
服务器上执行 git fetch --all --tags --depth=5
效果如图:
输出 test > 3
接下来去掉--tags 参数试试
服务器上执行 git fetch --all --depth=5
效果如图:
输出 test > 4
结果正确(但由于没有--tags参数,其实并没有拉取到所有tags)
人为指定映射关系,验证是不是因为 --tags 影响refspec而导致问题
到这里,对于问题而言我们确定了是 --tags 导致fetch不到最新代码,但不能确定是refspec的问题,那么接着下一步的验证:
修改test.txt 为 test > 5
push 代码
服务器上执行 git fetch origin test:refs/remotes/origin/test --depth=5 --all --tags
效果如图:
英文版报错:fetch --all does not take a repository argument
那么暂时去掉 --all ,接下去验证猜想
服务器上执行 git fetch origin test:refs/remotes/origin/test --depth=5 --tags
效果如图:
输出 test > 5
结果正确
2.7.4版本测试
修改test.txt 为 test > 6
服务器上执行 git fetch --depth=5 --all --tags
效果如图:
输出 test > 6
结果正确
结论:1.8.3.1 版本 git fetch --depth=5 --all --tags 的时候由于加了--tags 导致refspec出现问题
1.8.3.1版本里的 fetch 做了什么导致refspec不正确呢?
打开 git项目 里的 builtin/fetch.c => 找到 get_ref_map
static struct ref *get_ref_map(struct transport *transport,
struct refspec *refs, int ref_count, int tags,
int *autotags)
{
int i;
struct ref *rm;
struct ref *ref_map = NULL;
struct ref **tail = &ref_map; const struct ref *remote_refs = transport_get_remote_refs(transport);
int *num_i = &ref_count; /* ++ */
int *num_tags = &tags; /* ++ */
printf("ref_count -> %d\n",*num_i); /* ++ 打印ref_count*/
printf("tags -> %d\n",*num_tags); /* ++ 打印num_tags*/
if (ref_count || tags == TAGS_SET) {
for (i = ; i < ref_count; i++) {
get_fetch_map(remote_refs, &refs[i], &tail, );
if (refs[i].dst && refs[i].dst[])
*autotags = ;
printf("autotags -> %d\n",*autotags); /* ++ 打印autotags*/
}
/* Merge everything on the command line, but not --tags */
for (rm = ref_map; rm; rm = rm->next)
rm->merge = ;
if (tags == TAGS_SET)
get_fetch_map(remote_refs, tag_refspec, &tail, );
} else {
…
}
if (tags == TAGS_DEFAULT && *autotags)
find_non_local_tags(transport, &ref_map, &tail);
ref_remove_duplicates(ref_map); return ref_map;
}
上面是加了打印测试的代码,并未修改其逻辑,然后编译 => 配置 => 运行试试
测试ref_count和tags的打印结果
修改test.txt 为 test > 7
执行命令 git fetch --all --tags --depth=5
效果如图:
得出结果 ref_count = 0,并且都 Already up-to-date 了,拉没拉到最新提交,心里也有点B数了...
然后执行指定映射关系的命令 git fetch origin test:refs/remotes/origin/test --tags --depth=5
效果如图:
由于指定了映射关系,git知道该fetch哪些代码,于是获取到了最新的提交。
然后再看1.9.0的 fetch 代码
static struct ref *get_ref_map(struct transport *transport,
struct refspec *refspecs, int refspec_count,
int tags, int *autotags)
{
int i;
struct ref *rm;
struct ref *ref_map = NULL;
struct ref **tail = &ref_map; /* opportunistically-updated references: */
struct ref *orefs = NULL, **oref_tail = &orefs; const struct ref *remote_refs = transport_get_remote_refs(transport); if (refspec_count) {
for (i = ; i < refspec_count; i++) {
get_fetch_map(remote_refs, &refspecs[i], &tail, );
if (refspecs[i].dst && refspecs[i].dst[])
*autotags = ;
}
/* Merge everything on the command line (but not --tags) */
for (rm = ref_map; rm; rm = rm->next)
rm->fetch_head_status = FETCH_HEAD_MERGE; /*
* For any refs that we happen to be fetching via
* command-line arguments, the destination ref might
* have been missing or have been different than the
* remote-tracking ref that would be derived from the
* configured refspec. In these cases, we want to
* take the opportunity to update their configured
* remote-tracking reference. However, we do not want
* to mention these entries in FETCH_HEAD at all, as
* they would simply be duplicates of existing
* entries, so we set them FETCH_HEAD_IGNORE below.
*
* We compute these entries now, based only on the
* refspecs specified on the command line. But we add
* them to the list following the refspecs resulting
* from the tags option so that one of the latter,
* which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
* by ref_remove_duplicates() in favor of one of these
* opportunistic entries with FETCH_HEAD_IGNORE.
*/
for (i = ; i < transport->remote->fetch_refspec_nr; i++)
get_fetch_map(ref_map, &transport->remote->fetch[i],
&oref_tail, ); if (tags == TAGS_SET)
get_fetch_map(remote_refs, tag_refspec, &tail, );
} else {
…
} if (tags == TAGS_SET)
/* also fetch all tags */
get_fetch_map(remote_refs, tag_refspec, &tail, );
else if (tags == TAGS_DEFAULT && *autotags)
find_non_local_tags(transport, &ref_map, &tail); /* Now append any refs to be updated opportunistically: */
*tail = orefs;
for (rm = orefs; rm; rm = rm->next) {
rm->fetch_head_status = FETCH_HEAD_IGNORE;
tail = &rm->next;
} return ref_remove_duplicates(ref_map);
}
tags == TAGS_SET 和 refspec_count 单独判断,在 refspec_count = 0 的时候使用默认的refspec,这样get到的ref_map便是正确的,git之后的版本里把在 refspec_count 判断里的tags == TAGS_SET 判断和get_fetch_map移除了。但其实1.8.3.1官方文档说的覆盖和不使用默认的refspec,在上面代码里我还是没能看出是在哪里操作的(实在汗颜),猜测是在 Merge everything on the command line 这步,同时也求大神解释... 之前没接触过 C …
其实在这过程中,也产生了个问题,就是 refspec 关系的操作是怎么处理的,这个也值得探究探究额,决定再刷刷书熟悉下git核心那块,然后再根据源码探一探
git fetch 的参考文档
官网文档 : https://git-scm.com/docs/git-fetch
stackoverflow 大佬的回答:https://stackoverflow.com/questions/1204190/does-git-fetch-tags-include-git-fetch
以及各个版本源码:https://github.com/git/git/
说完git fetch的锅,然后回到之前说的 “有一半原因得归咎于git” ,另一半锅还是得pm2-deploy背,pm2-deploy在fetch的时候理应做个兼容,哪怕这个兼容并不是个很好的实践(因为pm2新版本有对git仓库的管理做了更严谨的把控)
比如在检测机器上git版本< 1.9.0,则走原先的 git fetch --all
如下代码:
version=`git --version | awk '{print $3}' | tr "." " "`
f=`echo $version | awk '{print $1}'`
s=`echo $version | awk '{print $2}'` if [[ $f -le 1 ]] && [[ $s -le 8 ]]
then
echo "version < 1.9.0"
else
echo "version >= 1.9.0"
fi
<(▰˘◡˘▰)> 完! 就这么一段无聊的debugger过程… 各位客官看看即可 有深入了解的大神也给小弟多分享下,非常感谢~ 不然只能以后自己功力深了再来解释了
记录一次无聊的(经历了Nodejs -> Shell -> C)的探索问题过程的更多相关文章
- 记录一次node中台转发表单上传文件到后台过程
首发掘金 记录一次node中台转发表单上传文件到后台过程 本篇跟掘金为同一个作者leung 公司几个项目都是三层架构模式即前台,中台(中间层),后台.前台微信端公众号使用vue框架,后台管理前端使 ...
- 记录一次OOM排查经历(一)
一.经历概要 程序里有个跑数据的job,这个job的主要功能是往数据库写假数据. 既需要跑历史数据(传给job的日期是过去的时间),也需要能够上线后,实时跑(十秒钟触发一次,传入触发时的当前时间). ...
- 记录一次ceph recovery经历
一次ceph recovery经历 背景 这是一个測试环境. 该环境中是cephfs 一共12个节点, 2个client.2个mds.8个osd mds: 2颗CPU,每一个4核.一共是8核. 128 ...
- 记录一次gdb debug经历
目录 问题描述 查看core文件 使用gdb查看core文件 总结 问题描述 今天在写代码时,运行时奔溃了.segment fault,而且是在程序退出main()函数后,才报的. 唯一的信息是:Se ...
- 记录一次OOM排查经历
我是用了netty搭建了一个UDP接收日志,堆启动配置 Xmx256 Xms256 ,项目刚启动的时候,系统进程占用内存很正常,在250M左右. 长时间运行之后发现,进程占用内存不断增长,远远超过了 ...
- 完整记录一则Oracle 11.2.0.4单实例打PSU补丁的过程
本文记录了打PSU的全过程,意在体会数据库打PSU补丁的整个过程. 1.OPatch替换为最新版本2.数据库软件应用19121551补丁程序3.数据库应用补丁4.验证PSU补丁是否应用成功 1.OPa ...
- 记录清除wnTKYg挖矿工木马(守护进程ddg.xxxx)的过程
起因,阿里云多次提醒我的一台服务器有恶意发包行为,且给出了一些解决办法.之前也没太在意,就按照解决办法处理了一下.然后过一段时间,还是提示有此行为. 猜肯定是中了木马了,开始以为是被肉鸡了拿来做DDo ...
- CentOS7下安装Mysql失败经历--CentOS7使用yum安装和卸载Mysql过程
起因 自己租用的BandwagonVPS上安装了个CentOS7,然后开始安装各种软件,结果yum安装MySQL发现MySQL在yum源中的Mysql不对劲,于是自己百度搜索安装方法. 终于我搜到了这 ...
- 记录一下在WinXP上搭建Apache的httpd+PHP+MySQL+Wordpress的过程
实验室有台旧电脑,想用它一台服务器. 不知为何,U盘启动盘死活不能启动,所以放弃了安装Linux的念头,直接在原来的XP上弄一个服务器,毕竟用的人也不多,也就局域网的这几个人, 本来主要是搭建一个FT ...
随机推荐
- JavaScript 学习笔记 - Web Workers
前言 本文仅是 Web Workers 的入门科普文章,不涉及太琐碎的知识点. 我们知道,在 Web Workers 出来之前,JavaScript 是单线程的.即使是 setTimeout 之类的看 ...
- Andrew Ng机器学习课程笔记--week10(优化梯度下降)
本周主要介绍了梯度下降算法运用到大数据时的优化方法. 一.内容概要 Gradient Descent with Large Datasets Stochastic Gradient Descent M ...
- SpringMVC基础-controller方法中的参数注解
@PathVariable 映射 URL 绑定的占位符 带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义 通过 ...
- JavaSE二次学习之标识符和编程命名相关的内容
前段时间阿里开源了<阿里巴巴 JAVA 开发手册>,里面详细叙述了有关编程命名.sql规约.工程规约等内容,作为一个初学者,只讨论一下-编程规约-的部分. 这几天又重新回去看了看JavaS ...
- 又一流氓推广Microsoft Edge,我勒个去
最新的Windows10 的升级也是醉了,不得不吐槽一个非常流氓的浏览器推广:Microsoft Edge(这小婊砸). 为了将之前的历史包袱IE干掉,这次微软也是蛮拼的,直接把IE从电脑里干掉了,你 ...
- We’re Just Beginning
"We are reading the first verse of the first chapter of a book whose pages are infinite…" ...
- Weave 如何与外网通信?- 每天5分钟玩转 Docker 容器技术(66)
上一节我们学习了 Weave 网络内部如何通信,今天讨论 Weave 如何与外界通信. weave 是一个私有的 VxLAN 网络,默认与外部网络隔离.外部网络如何才能访问到 weave 中的容器呢? ...
- Redis集群的相关概念
1.1 redis-cluster架构图 架构细节: (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2)节点的fail是通过集群中超过半数的节 ...
- 大道至简第一章观后感——java伪代码
一节: public class Yugongyishan_ { //定义一个名为Yugongyishan_的类 Public static void main(string args[]) // ...
- 汇编指令-位置无关码(BL)与绝对位置码(LDR)(2)
位置无关码,即该段代码无论放在内存的哪个地址,都能正确运行.究其原因,是因为代码里没有使用绝对地址,都是相对地址. 位置相关码,即它的地址与代码处于的位置相关,是绝对地址 BL :带链接分支跳转指令 ...