GIT(一)——提交树的角度来看分支合并
大部分使用GIT的童鞋们,只限于pull push add commit merge branch这几个常用命令的使用,但是对于GIT的原理并不了解。
这在一些情况下将直接导致一些非常懵逼的事情发生而自己手足无措,而且事关代码仓库,出现问题是更是恐慌不敢操作。
希望通过这个系列带大家掌握好GIT的根本原理,做到对每一个操作心中有数便可自信操作,顺利提交。
提交树:先来个简单概括(便于理解但并不完全准确,后面文章会逐步深入)。
每一个commit命令都将产生一个节点,以一个40个随机字符为标识。
整个GIT仓库的所有内容都由这些节点组成,这就是提交树。
PS:每一个commit节点都代表了当前整个GIT仓库的所有数据
所以不存在某个commit存了哪些数据这种理解。
如你所想,却有冗余数据,但有压缩其实问题不大。
所以没事也别瞎commit,很占空间。
新建仓库
git init
添加一个文件并执行
git add .
git commit -m '第一次提交'
以树状形式查看节点树。
git log --oneline --graph --decorate --all
由此我们得到了一个只有一个commit节点的提交树。这里通过上图开始理解一个重要概念,在GIT里,分支是什么?
分支:一个指向某一个commit节点的指针。
具体形式是一个仅仅包含所指向commit节点40位ID号的文件。
上图中几个关键点。
1.master:一个指针,代表当前master分支处在提交树的哪个节点上。
2.HEAD:也是一个指针,他指向的是分支指针,代表当前所在的分支。
如图中HEAD->master表示目前我们处在master分支上。(右方的蓝色字也会显示HEAD所指向的分支指针,图中是master)
注意,真正有“实际内容”的文件都在commit节点上,master和HEAD这样的指针并不包含任何实际内容,仅仅是指向commit节点的指针而已。
所以,新建分支操作的本质就是新建一个指针,指向一个commit节点,仅此而已。
也就是此刻我们新建一个分支,那么将创建一个和master一样的指针指向当前所在所在分支(HEAD指向的分支,也就是master分支)指针,指向的commit节点。
执行
git branch
查看分支将看到只有master一个分支
现在我们执行
git checkout -b feature/01
该命令将新建一条分支并切换到新建分支上。
再次观察提交树和分支。
观察变化
1.HEAD指向了feature/01,代表我们所在分支变成了feature/01分支。
当然右方蓝色字也由master变成了feature/01。
2.提交树指向20ee949节点的指针,之前只有master,新增了一个feature/01指针。
3.查看分支时候多了一条分支feature/01并且绿色显示。也是因为HEAD指向了feature/01的缘故。
这就是新建分支的操作原理。
现在我们模拟两种情况下的分支合并,继续观察提交树的变化。
第一个种:feature/01分支有新的提交,master上并没有新的提交的情况。
我们在feature/01分支上进行一次提交并观察提交树。
新建分支新增提交.png
观察
1.这里新增了一个节点ed82f8d,被feature/01指针(分支就是指针)指向。
2.master节点依然指向之前的节点
此刻我们在feature/01分支上,然后开始进行merge。
此刻执行
git merge master
将无法合并,因为显然这种情况下,feature/01提交树所在的commit节点完全包含了master的所有内容。
所以此刻我们切换到master上来合并fearture/01,看看会发生什么
fast_forward方式master合并feature.png观察变化
1.提交树并未发生变化,依然只有两个节点
2.master指针指向了新增的ed82f8d节点
本质就是master指针修改指向节点,指向和feature/01指针相同的节点,这种情况下的merge我们称为fast-forward。
第二个种:feature/01分支有新的提交,master上也有新的提交的情况。
我们分别在feature/01分支和master上进行一次提交并观察提交树。
观察
1.提交树在两个分支上分别出现了一个新节点。
加上之前的2个,总共4个。
2.提交树出现了分叉。
显然此刻在任何一条分支上都可以merge另一条分支,我们来观察执行merge会发生什么。
如图我们处在featreu/01分支上,然后我们执行
git merge master
可以看到我们merge时候产生了冲突,并且再次查看提交树并没有发生变化。
很多朋友在此刻就会懵逼不知道发生了什么。
在这种两个都有提交的情况下,是无法直接通过挪动指针来完全merge的。
需要新建一个commit节点来完成merge,当没有冲突时候,是自动生成的。
如果有冲突,解冲突后再次提交,也会产生一个新的节点。
那么我们解冲突后再次执行的应该是
先执行添加文件到暂存区
git add .
进行提交
git commit -m '解决冲突'
注意
不管有没有冲突,在这种情况下,也就是双方都有新的提交时候的merge中。
是一定会产生新节点的,有冲突的情况下,修改完后一点要先add。
否则再次merge或者直接commit就会出现上方第一个红框中的报错。
观察
1.新增了一个commit节点,HEAD表示我们正在feature/01分支。
2.feature/01分支目前指向merge所新产生的节点。
3.master依然指向之前的节点!
这同时也表示,合并分支,仅仅是移动当前分支的指针而已。
并不对被'拉过来'合并的分支产生任何影响!
所以此刻切换回master文件内容依然是合并前的内容。
需要将master获得fearure/01的提交,需要切换到master上再次对feature/01执行merge。
此刻由于已经生成了新的节点了。
所以master对feature/01进行merge的时候,仅仅移动指针即可。
某种意义上第二次的合并也就变成了fast_forward。
这种合并的方式称为'三路合并'。
此刻对于GIT分支合并过程中发生的所有事情相信大家都了解了。
推理可知push pull之类的命令 无非是同步两个仓库间的提交树和分支指针罢了。
总结
1.提交树是GIT的整个仓库的核心,commit节点是提交树的基本单位。
2.两种merge,faster_forward和三路合并。
faster_forward并不产生新节点,仅仅移动指针。
三路合并必然产生新节点。
如果没有冲突,新节点自动生成。
如果产生冲突那么需要手动处理冲突后再次add commit才能产生新节点。