快照
这是项目的三个版本,版本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 2
,commit 3
与 commit 4
,commit 5
,commit 6
的合并吗?说到分支,我们总会联想到线,就会认为是线的合并。其实不是的,真实合并的是 3
和 6
。因为每一次提交都包含了项目完整的快照,即合并只是 tree 与 tree 的合并。
我们可以先想一个简单的算法。用来比较commit 3
和commit 6
。但是我们还需要一个比较的标准,如果只是commit 3
和commit 6
比较,那么commit 3
与commit 6
相比,添加了一个文件,也可以说成是commit 6
与commit 3
比删除了一个文件,这无法确切表示当前的冲突状态。因此我们选取他们的两个分支的分歧点(merge base)作为参考点,进行比较。
比较时,相对于 merge base(提交1)进行比较。
首先把commit 1
、commit 3
、commit 6
中所有的文件做一个列表,然后依次遍历这个列表中的文件。现在我们拿列表中的一个文件进行举例,把在提交commit 1
、commit 3
、commit 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
会产生一个新的提交: