【消失的代码】Git 合并分支导致代码消失
共 3334字,需浏览 7分钟
·
2022-01-03 11:47
1. 问题背景
A 页面的代码莫名其妙消失了,而且不清楚是什么时候被删的。
发现这个问题之后,心里除了一句“草泥马”以外,也萌生了很多疑惑。比如说,团队在代码上线前,是有 CR 流程的,为什么这个代码消失的 commit 会逃过这么多高工的法眼?
我们希望能找回代码,并查出是哪次 commit 涉及到的,进而找出操作过程,以防后续再有人出现类似操作。
2. 处理方式
2.1 通过 git log 查找出修改过指定文件的 commit
目前文件已经被删除了,但是根据项目的代码结构,可以推测出原本是存在 A/index.js 这个文件的。
尝试检测一下在所有历史记录中,对该文件的处理,用到的命令如下:
git log --stat --full-history --simplify-merges -- A/index.js
上述命令将会展示涉及到该文件更改的 commit,从输出结果我们可以看到,在 fix:1 这个 commit 中,删了 200 行代码,而之后就再没有 commit 处理过该文件了,所以可以推测文件就是在这个 commit 中被删除了。
然后通过 git checkout 6df716248794c3c54873f73002b8bd0854ac0805,去到删除操作前最后修改过该文件的的 commit,即可拿到被删除前的代码了。
2.2. 解释一下命令及每个参数的作用
2.2.1. git log 查看对指定文件修改过的commit
git log -- A/index.js
只使用上述命令去查找文件历史,会存在一个问题:如果文件目前不存在,则什么记录都没有。
既然如此,我们先把代码恢复,再看看会展示什么:
上图可以看到,只有恢复之后的那次 commit 的记录。删除代码、以及删除代码前对该文件的所有 commit 都不会展示出来。这又是为什么呢?
这是因为 git log 的一个默认策略:
也就是默认模式下,git log 会简化文件历史,如果一些分支合起来看之后的结果是相同的,就不会展示这些分支。
因为之前对这个 index.js 文件从新建到删除,中间的所有 commit 合起来看是相互抵消的(因为文件最后被删除了,相当于没有新建过),所以单单输入 git log 指令,什么也看不到。即使代码被恢复后再输入 git log 指令,也只会展示恢复代码的那次 commit。
2.2.2. --stat 生成差异统计
git log 默认情况下不会生成文件差异:
加了 --stat 参数,即可生成文件差异的统计,执行以下命令:
git log --stat -- A/index.js
对比没加 –stat 参数的结果,可以看到多输出了文件的变更记录,具体到变更了多少文件、多少行代码。
2.2.3. --full-history
由 2.2.1 的介绍可知,git log 的默认模式是会简化文件历史的。为此,我们需要加上 --full-history 这个参数,去掉这个简化的功能。
执行以下命令:
git log --full-history -- A/index.js
对比 2.2.1,可以看到加了 --full-history 参数的输出结果没有进行简化,所有处理过该代码的 commit 都展示出来了。
2.2.4. --simplify-merges
--simplify-merges 可以增强 --full-history 的能力,因为 --full-history 会把一些无用的合并 commit 也输出出来(可以看 2.2.3 中的 commit 信息,有一些是 Merge branch xxx),增加 --simplify-merges 参数可以去除这些无用的 commit 信息。
执行以下命令:
git log --full-history --simplify-merges -- A/index.js
对比 2.2.3 中的输出结果,可以看到已经没有 Merge branch xxx 的 commit 了,这里展示的每个 commit 都是实实在在对指定文件进行了修改的。
再加上 --stat 参数输出文件的差异信息,最终可以得出我们前文使用到的查询指令:
git log --stat --full-history --simplify-merges -- <path>
3. 分析原因
3.1 为什么代码被删除了,CR 时却没有发现,仍能合到主干?
从上面的分析可以知道,代码是在 fix:1 这个 commit 中被删除的。而在工蜂(公司内类似 gitlab 的代码管理平台)中,根本就没有记录显示代码被删除。
我们使用 git show 命令来看下该 commit 的更改内容:
结果发现没有显示任何文件更改。
这就是 CR 时没有发现问题的原因了,因为删除代码的记录根本就没有出现在工蜂上,所以没人知道这些代码被删除了。
3.2 为什么工蜂和 git show 无法展示该 commit 的记录呢?
3.2.1 工蜂的结论
到底是不是因为这个原因呢?实践出真知,我们用一个例子去试一下:
在一个项目内,模拟两个分支在同时进行开发,在分支 A 新增了文件 new2.js,且修改 const.js。
新建 new2.js 如下:
修改 const.js 如下:
然后分支 B 再修改了 const.js:
分支 B 在 push 的时候,则需要处理一下冲突文件了。
此时我们关注到暂存区里的 new2.js:
如果在此时把 new2.js 从暂存区里剔除,冲突选择 Current Change,再提交代码,就能成功复现工蜂不展示代码被删的问题了。
如果去 VSCode 上看,还是可以看到代码被删除的:
3.2.2 分析一下
合并后,项目的主干路径变为了红色的三个点,相当于 A 分支的两个修改都被 B 分支的 merge 操作覆盖掉了(新文件剔除出暂存区、冲突选择分支B部分)。最终 fix:fix1 节点相对于分支 B 的最新节点没有变化,故工蜂中 fix:fix1 节点显示没有文件变化。在分支 A 里新增的 new2.js 文件,相对于合并后的主干代码来说,就像从来没有出现过一样,所以在合并分支的节点中就不会有它被删除的记录。
回到丢失代码的项目里,打开 VSCode 的 git 管理模块查看该 commit:
能够看到是修改了很多文件的,其中就有删除 A 页面代码的记录,和我们例子的表现一致。
所以可以证明工蜂说的没错,应该是当时操作者在合并代码时,不知因为什么原因,把 A 页面代码剔除出了暂存区,最终导致 A 页面的代码像消失了一样。
4. 预防措施
目前发现代码被删除是被动的,也就是需要去找这些代码时,才能发现代码不见了,这也是代码被删了 8 个月才被发现的原因之一。
所以我们希望能够化被动为主动,通过程序去帮助开发者提前发现这些问题,而不是在需要用到这些代码的时候,才发现代码已经没了,时间久了再排查、恢复都比较困难。
因此可以考虑实现一个 主干检查程序,将手动的处理方式改为使用代码逻辑去实现,然后每隔一段时间触发一次,检查有无类似的情况发生,能够做到出现类似情况发生后及时通知到开发者。
最后
欢迎加我微信,拉你进技术群,长期交流学习...
欢迎关注「前端Q」,认真学前端,做个专业的技术人...
![](https://filescdn.proginn.com/0a6894c4433b70649a86d4a57dc3fd45/9d1c7c30d4aad1385abd83f665ab8bfc.webp)