stay hungry stay foolish

git merge原理

快照

这是项目的三个版本,版本1中有两个文件A和B,然后修改了A,变成了A1,形成了版本2,接着又修改了B变为B1,形成了版本3。

如果我们把项目的每个版本都保存到本地仓库,需要保存至少6个文件,而实际上,只有4个不同的文件,A、A1、B、B1。为了节省存储的空间,我们要想一个方法将同样的文件只需要保存一份。这就引入了Sha-1算法。

可以使用git命令计算文件的 sha-1 值:

echo 'test content' | git hash-object --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4

SHA-1将文件中的内容通过通过计算生成一个 40 位长度的hash值。 hash值相同,文件内容相同。因此,文件的sha-1值是可以作为文件的唯一 id。同时,它还有一个额外的功能,校验文件完整性。

有了 sha-1 的帮助,我们可以对项目版本的存储方式做一下调整。

Conflicts

git 中的分支十分轻量,因此我们在使用git的时候会频繁的用到分支。不可不免的需要将新创建的分支合并。

在 git 中合并分支有两种选择:merge 和 rebase。但是,无论哪一种,都有可能产生冲突。因此我们先来看一下冲突的产生。

图上的情况,并不是移动分支指针就能解决问题的,它需要一种合并策略。首先,我们需要明确的是谁和谁的合并,是 commit 2commit 3commit 4commit 5commit 6的合并吗?说到分支,我们总会联想到线,就会认为是线的合并。其实不是的,真实合并的是 36。因为每一次提交都包含了项目完整的快照,即合并只是 tree 与 tree 的合并。

我们可以先想一个简单的算法。用来比较commit 3commit 6。但是我们还需要一个比较的标准,如果只是commit 3commit 6比较,那么commit 3commit 6相比,添加了一个文件,也可以说成是commit 6commit 3比删除了一个文件,这无法确切表示当前的冲突状态。因此我们选取他们的两个分支的分歧点(merge base)作为参考点,进行比较。

比较时,相对于 merge base(提交1)进行比较。

首先把commit 1commit 3commit 6中所有的文件做一个列表,然后依次遍历这个列表中的文件。现在我们拿列表中的一个文件进行举例,把在提交commit 1commit 3commit 6中的该文件分别称为版本1版本3版本6

  • 版本1版本3版本6的 sha-1 值完全相同,这种情况表明没有冲突
  • 版本3版本6至少一个与版本1状态相同(指的是sha-1值相同或都不存在),这种情况可以自动合并。比如commit 1中存在一个文件,在commit 3中没有对该文件进行修改,而commit 6中删除了这个文件,则以commit 6为准就可以了
  • 版本3版本6都与版本1的状态不同,情况复杂一些,自动合并策略很难生效,需要手动解决。

实践

git merge 会产生一个新的提交: