快进式合并 && 非快进式合并
快进式合并
如下图:
- 当前处于
master
分支,master是主体对象分支master
分支若要走到fast
分支并与之合并,当前主体对象分支
只需要直线前进
即可到达,类似此种情况的合并,都叫做快进式
合并
非快进式合并–情景1
如下图:
- 当前处于
fast
分支,fast分支是主体对象分支fast
分支如果要走到master
分支并与之合并,需要直线后退
再与将fast的变更加到master,类似此种情况的合并,都叫做非快进式
合并
非快进式合并–情景2
如下图:
- 当前
master
分支与fast
分支有了分叉点- 不管是
fast
分支要走到master
分支并与之合并,亦或master
分支要走到fast
分支并与之合并,都需要先后退
至一个公共节点之后才能走到另一分支,再与之合并,类似此种情况的合并,都叫做非快进式
合并
综上所述可以看出:
- 如果两个分支有了
分歧点
,则其默认合并方式一定是非快进式
的- 如果两个分支没有
分歧点
,默认合并方式采用哪一种合并方式裁决于当前分支
与其合并目标分支
是否需要需要后退
如何选择快进式
与非快进式
的选择**
两种方式最大的区别(如下两图)
快进式
合并不会产生一个新的提交节点非快进式
合并会产生一个新的节点
当前分支情况如下
两种合并方式结果分别如下
- 从上图可以看出:
快进式
合并其合并后的结果非常的简洁
;该种方式注重的是最终变更的合并结果
并不注重这些变更到底来自于哪个分支
【当然如果采用这种方式,信息会过于简陋,你可能无法追踪一个功能点到底是哪个功能分支加入进来的,若:fast3提交点有误,此时你并不知道fast3提交节点到底是test1分支还是fast分支】非快进式合并
其合并之后的结果会产生新的合并提交节点
,而实际上这些合并合并节点对你并没有直接作用,但该方式注重的每一个变更的过程
,并不注重其结果是否简洁
【如果你对于变更的过程(该变更到底来源于哪个分支)是不可忽视的那就一定要用该方式,但如果分支过多又没有一定的策略进行及时整理则会导致合并节点过最终不可控制,最终如下图一般】
选择建议:
- 选择在工作过程中,绝大多数情况我们都应该只选两者中的一者作为主要的合并方式,而另一种则应该是在某些特定情境下不得不采取的合并方式
- 如果是选择
快进式
合并方式,建议应该合理的使用上git tag
对提交进行标记,当然tag
也会有过多的时候,应该适时的进行清理(git gc
会将.git/refs/
下信息打包生成.git/pack_refs
文件减少文件查询)或删除无用tag
- 如果是选择
非快进式
合并方式,(⊙o⊙)…还真没啥好建议,但非快进式
合并方式在某些场景下也应该是必选的【例如:两个长期开发的功能分支合并集成到测试环境分支的时候,如果你强制使用手段让其变成快进式合并,你解决冲突可能会解决到蛋疼((((ToT)†~~~】
合并类型
“变更集”合并
fast-forward方式
- Git的默认合并方式,优先以
快进式 - fast-forward
方式进行合并,若不能进行快进式合并再以非快进式
进行合并
$ git merge branch_name
–no-ff方式
$ git merge --no-ff fast
--no-ff
是no fast forward
的缩写(猜的),要表达的意思就是:强制以非快进式
进行合并.合并结果如下图:
square方式
$ git merge --square branch_name
--square
合并分支会将待合入目标分支
相对于当前分支
的所有变更提取出来,并将这些一次性变更放到当前分支的工作区或暂缓区
- 执行
--square
方式合并命令后,你需要额外的执行一次commit提交命令,将这些变更作为当前分支变更
提交本地仓库,且此次提交与待合入目标分支
没有关联
--square
方式的合并适合于:
- 你一定是不在乎
待合入目标分支
的提交记录的,你要的只是待合入目标分支
最后的一个
结果- 你在
待合入目标分支
中写了很多不符合规范的提交记录(如截图),这些提交是可有可无的且可以合并为一个提交记录的
“变基”合并
假设当前分支情况如上图
- 当前的合并方式与目标合并分支一定是
非快进式
- 你需要将
fast分支
(待合入目标源)与master分支
(被合入当前分支)已快进式
合并,且将fast分支所有提交信息保留
很明显前面讲的合并方式都不再适合当前的需求,唯一靠边的合并方式应是--square
方式合并.但是--square
方式最终只会保留一个变更记录,而此处需要保留全部(现实工作需求中可能包含的节点会更多)
想到的方式有两种:
方法1. 增对每一个节点建立一个分支,然后逐步前进的使用--square
方式进行合并
但是:当合并的节点比较多的时候,那么你可能建分支建到怀疑人生,并且合并顺序如果有错,那么也可能会引发很多冲突,因此需要我们人工的去控制合并顺序
假设是否有方法2:
- 能够像
--square
方式,以master
为原本(基础),将fast分支
每一步的差异提取出来 - 并将提取出来的差异自下而上(从fast分支与master分支公共节点开始到fast最近一次提交)的顺序自动添加到
master
分支上,并保留每一次的提交信息
变基合并–rebase就是方法2的解决方案:rebase
可以将当前分支
以补丁
的方式在指定分支
上重演合并
注意:
rebase
和方法2中的方案有一个关键点不同,就是到底已谁为基础
要分清,而rebase是以指定分支为基础版本将当前分支在其上进行演合一次
# 切换至目标分支(待合入当前分支的目标分支)
$ git checkout fast
# 执行演合操作
$ git rebase <other_branch>
演示如下
因为备份分支,前后图片的最终提交信息可能不同
值得注意的两个问题是
- 使用rebase方式,到底是那条分支的信息产生会动了?
- 是当前操作的分支数据前后产生了变动,因为:rebases是将当前的分支作为指定分支的补丁,从而使得两个分支的操作看似是
fast-forward
操作一般
- 是当前操作的分支数据前后产生了变动,因为:rebases是将当前的分支作为指定分支的补丁,从而使得两个分支的操作看似是
- rebase铁律:
rebase只能针对本地未提交的信息进行使用!
- 远程仓库是大家公用的,如果你使用rebase将一个分支(这个分支已经推上了远程–其他人可能已经同步并且在使用中了)作为另一分支的补丁进行了变动,然后又推到了远程服务器上,那会产生什么后果? 可能会导致其他人在这个分支上拉取进行推送的时候有很多冲突【极大的概率是势必会发生的】;即使没有冲突,其他人使用拉取合并这个分支之后,相当于又覆盖了你将该分支作为补丁的操作,当你拉取下来的时候,你又可能会产生冲突并且会将你之前的补丁操作又覆盖回去,并且这个过程可能还会产生很多空白无变化的提交节点
因此在下图这种情况下是绝对禁止使用rebase合并【【fast分支相较与master分支的差异已经被推到了远程
(哪怕只有其中一个差异被推到了远程也是禁止使用该命令进行操作的),上面的gif图可以是因为fast分支与master分支的所有差异变更
都还在本地】
扩展:“摘樱桃”模式–自由选择提交点
假设分支情况依然如上图所示,但我们很确定,其中真正有用的提交点只有最后一个我可是正儿八经的提交
,而其他的提交节点其实是无效的(此处说的无效是指完全没有前面的变更也可以).那么:如何只将最后一个提交点移花接木
到master分支上呢?
$ git cherry-pick commitId
如上图:我们看到指定提交节点
的所有提交信息被复制移植
了一份到当前分支