本来已经口头分享给几个小伙伴了,后来想想,为了让更多小伙伴们少踩坑,还是写出来。
早前读《Git 版本控制管理》,书中提到git flow
的--follow
参数用于追溯文件重命名前的历史记录,当时觉得挺好理解。谁知这次遇上了一个神奇的现象,且听我娓娓道来。
一个蛮久没有碰的项目,上线的时候,发现一个重大问题。结合我有限的记忆,定位到有个文件,少了一部分代码。很自然的希望通过查找历史记录找回之前的代码。我平时主要用Webstorm开发,它的git查询历史功能非常好用,但是这次翻遍该文件的所有提交记录,居然都找不到之前的代码,非常诡异!瞄了一眼分支列表,还有一个早前没删掉的分支,遂切过去,这时发现少了的代码,粗线了!
这就更神奇了!
后来仔细分析近期的所有提交记录,发现这个文件曾经被删除过
但是!
这个文件的『归来』,是通过把别的文件重命名而来!
而Webstorm的git查询历史功能是强制带--follow
参数的(无法切换),而SourceTree是可以切换的,通过对比,发现带不带--follow
参数,结果差异甚大。基本定位到这诡异的现象是由于这个文件曾经被删除,并且被重命名恢复回来。
我们通过一个小栗子,重演整个过程:
创建一个目录,比如叫test, 并在终端进入,依次执行:
- git init
- echo ‘1st line —in a’ >> a.txt
- git add a.txt && git commit -m ‘add line for a.txt’
- echo ‘2nd line —in a’ >> a.txt
- git add a.txt && git commit -m ‘add 1 line for a.txt’
- echo ‘line 1 —in b’ >> b.txt
- git add b.txt && git commit -m ‘add line for b.txt’
- echo ‘line 2 —in b’ >> b.txt
- git add b.txt && git commit -m ‘add line for b.txt’
- git rm a.txt && git commit -m ‘remove a.txt’
- git mv b.txt a.txt && git commit -m ‘rename b.txt to a.txt’
整个过程经历了这么几个关键状态:
a.txt => a.txt b.txt => b.txt => a.txt
接下来就是见证奇迹的时刻:
git log -p a.txt
结果如下:
git log -p --follow a.txt
结果如下:
仔细观察,可发现,两者差异很大:
- 不带follow参数的情况下,git把名为a.txt的文件全部提交记录找出来
- 带follow参数的情况下,git会把当前以及重命名前的文件提交记录找出来
二者的差异,就像是,一个『浮于表面』,只认历史上与当前文件名匹配的提交记录,一个认准文件的『真身』,不管当前文件曾经披着什么的『皮』,始终追溯它的真身。
这么描述下来,似乎加上follow参数是更明智的选择。
嗯,是的,Webstorm就这么干的,而且不能更改。
但是,当--follow
遇上文件删除+文件重命名,它的表现并不一定令我们满意。
结论
- 善用
git log
的--follow
参数 - GUI工具固然直观,而且不同的GUI工具有不同的特点,不可盲目一味的只使用GUI,适当的时候需要与命令行结合