上一篇文章 深入理解git分支(一)介绍了git项目的一些概念,如
.git目录
、
对象
、
文件状态
、
工作区
,主要介绍的是单分支的情况。本文将继续使用上一篇文章中的项目梳理git分支的原理,分析项目分支变多的情况下,进行分支间的切换、合并等操作时到底发生了什么。
快照
目前为止,我在git项目中进行了两次commit,最终的结果是:
随着不断commit,commit的链会越来越长,但是每个commit仍然指向一个tree对象,如果把commit所指的tree对象以及此tree的其他节点看成一个整体的话,那么commit链就可以简化,这个整体就称为快照(snapshot)
。
最近的一次commit是"2c69a",通过.git/refs/heads/master文件可以看到,master分支现在指向的就是commit 2c69a,HEAD引用master,所以它们指向相同的commit,所以master分支的commit历史是这样的:
为了叙述方便,这里直接用tree的哈希值表示快照
增加分支
创建新的分支
现在我们创建并切换到新的分支dev:
git checkout -b dev
回到.git目录,查看文件变化,发现HEAD文件的引用变成了ref: refs/heads/dev,refs文件夹下也创建了dev分支对应的文件,refs/heads/dev文件同样指向的是commit 2c69a。
在新的分支上工作
目前我们在分支dev上,添加file3.txt文件并提交,这个操作会产生新的commit对象以及新的快照。同样,refs/heads/dev文件、HEAD文件和index文件都会被更新,新的对象也会被添加到objects文件夹中。
合并分支
fast-forward merge
切换回master分支并且将dev分支上的工作合并到master分支上:
git checkout master
git merge dev
结果是:
在这种情况下,两个分支master和dev在一条commit链上,所以git会直接移动master指针到dev处,不会产生新的git对象,这种merge称为fast-forward merge
。
three-way merge
如果合并的两个分支不在同一条commit链上呢?
回到master分支,添加file4.txt文件并提交;回到dev分支,编辑file3.txt文件并提交。现在,master分支和dev分支不在一条commit链上了。
在master上合并dev:
git merge dev
由于master和dev不在一条commit链上,git会将找到master和dev的共同父commit节点bcc81,对bcc81、131f2和0a09b进行三方合并,创建新的commit对象4748a和快照,并且移动master指针。由于合并涉及到三方:两个分支和它们的共同父节点,所以这种合并称为three-way merge
。
在进行有冲突的分支合并时原理是一样的,只不过需要进行额外的手动合并
小结
本文介绍了两种多分支合并以及各自情况下git对象和分支指针的变化情况,结合上一篇文章深入理解git分支(一),关于git分支的介绍差不多就是这么多。