问题

  1. 基于 master 分支新开了功能分支,在半个月后需要同步 master 分支的工作(600+ 提交),于是合并 master 到功能分支。
  2. 合并完成后打开 Unity 发现工作区中多出一个 Assets/Plugins/iOS.meta 文件,此文件是 master 分支中最近新增的,功能分支上没有,但是合并 master 到功能分支后此文件被删除了!
  3. 回到合并之前的状态,重新合并发现并没有错误地删除此文件。

使用 git log -p 可以显示出文件被改名为功能分支中的一个 .meta 文件,但是 Git 为什么在首次合并时会错误地删除文件呢,而之后不会呢? 这里很奇怪,没有找到原因。

发现

在查找相关资料后发现,Git 从设计上就是不追踪文件改名操作的!Git 只是根据快照的内容推测文件路径的变化,显示在差异中。

Why does Git not “track” renames? - Git FAQ - Git SCM Wiki

Is it possible to move/rename files in Git and maintain their history? - Stack Overflow

在 Git 中很难追踪文件路径的变化。

分析

Git 是通过提交引入修改,准确的说是每一个提交是一个快照。

首先定义下普通提交与合并提并的概念:

  • 普通提交是指只有一个父结点的提交,只包含一次相关的改动
  • 合并提交是指有多个父结点的提交,包含非首个父结点中所有提交的改动

Git 建议小且原子性的提交,这样易于追踪变化。

合并提交有以下缺点:

  1. 提交多的分支向提交少的分支合并时,包含的改动很多,这样容易产生问题。
  2. 在查看历史时合并提交往往易被忽略,因为人们都是假定合并提交只是将一个分支与另一个分支融合,并不会想到合并提交与普通提交一样,可以在其中做任何改动。
  3. 查看文件改动时,只会在历史中显示有合并提交,但并不会显示出合并提交对其作的改动。

建议

  • 建议合并分支时仔细,出现任何问题都要查找发生原因。
  • 可以考虑使用变基代替合并,这样会减少合并大量提交时大量改动容易出错的问题。
  • 减少无用合并提交的数量,参见 Git 干净的历史 - 狂飙
  • 及早合并功能分支到 master 上。