git log --follow奇遇记

本来已经口头分享给几个小伙伴了,后来想想,为了让更多小伙伴们少踩坑,还是写出来。

早前读《Git 版本控制管理》,书中提到git flow--follow参数用于追溯文件重命名前的历史记录,当时觉得挺好理解。谁知这次遇上了一个神奇的现象,且听我娓娓道来。

一个蛮久没有碰的项目,上线的时候,发现一个重大问题。结合我有限的记忆,定位到有个文件,少了一部分代码。很自然的希望通过查找历史记录找回之前的代码。我平时主要用Webstorm开发,它的git查询历史功能非常好用,但是这次翻遍该文件的所有提交记录,居然都找不到之前的代码,非常诡异!瞄了一眼分支列表,还有一个早前没删掉的分支,遂切过去,这时发现少了的代码,粗线了!

这就更神奇了!

后来仔细分析近期的所有提交记录,发现这个文件曾经被删除过

但是!

这个文件的『归来』,是通过把别的文件重命名而来!

而Webstorm的git查询历史功能是强制带--follow参数的(无法切换),而SourceTree是可以切换的,通过对比,发现带不带--follow参数,结果差异甚大。基本定位到这诡异的现象是由于这个文件曾经被删除,并且被重命名恢复回来。

我们通过一个小栗子,重演整个过程:

创建一个目录,比如叫test, 并在终端进入,依次执行:

  1. git init
  2. echo ‘1st line —in a’ >> a.txt
  3. git add a.txt && git commit -m ‘add line for a.txt’
  4. echo ‘2nd line —in a’ >> a.txt
  5. git add a.txt && git commit -m ‘add 1 line for a.txt’
  6. echo ‘line 1 —in b’ >> b.txt
  7. git add b.txt && git commit -m ‘add line for b.txt’
  8. echo ‘line 2 —in b’ >> b.txt
  9. git add b.txt && git commit -m ‘add line for b.txt’
  10. git rm a.txt && git commit -m ‘remove a.txt’
  11. 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遇上文件删除+文件重命名,它的表现并不一定令我们满意。

结论

  1. 善用git log--follow参数
  2. GUI工具固然直观,而且不同的GUI工具有不同的特点,不可盲目一味的只使用GUI,适当的时候需要与命令行结合