10.Git操作-分支合并

快进式合并 && 非快进式合并

快进式合并

如下图:

  • 当前处于master分支,master是主体对象分支
  • master分支若要走到fast分支并与之合并,当前主体对象分支只需要直线前进即可到达,类似此种情况的合并,都叫做快进式合并

fast forward

非快进式合并–情景1

如下图:

  • 当前处于fast分支,fast分支是主体对象分支
  • fast分支如果要走到master分支并与之合并,需要直线后退再与将fast的变更加到master,类似此种情况的合并,都叫做非快进式合并

no-fast-forwar-1

非快进式合并–情景2

如下图:

  • 当前master分支与fast分支有了分叉点
  • 不管是fast分支要走到master分支并与之合并,亦或master分支要走到fast分支并与之合并,都需要先后退至一个公共节点之后才能走到另一分支,再与之合并,类似此种情况的合并,都叫做非快进式合并

no-fast-forwar-2

综上所述可以看出:

  • 如果两个分支有了分歧点,则其默认合并方式一定是非快进式
  • 如果两个分支没有分歧点,默认合并方式采用哪一种合并方式裁决于当前分支与其合并目标分支是否需要需要后退

如何选择快进式非快进式的选择**

两种方式最大的区别(如下两图)

  • 快进式合并不会产生一个新的提交节点
  • 非快进式合并会产生一个新的节点

当前分支情况如下

now branch

两种合并方式结果分别如下

fast forward merge

no fast forward merge

  1. 从上图可以看出:快进式合并其合并后的结果非常的简洁;该种方式注重的是最终变更的合并结果不注重这些变更到底来自于哪个分支【当然如果采用这种方式,信息会过于简陋,你可能无法追踪一个功能点到底是哪个功能分支加入进来的,若:fast3提交点有误,此时你并不知道fast3提交节点到底是test1分支还是fast分支】
  2. 非快进式合并其合并之后的结果会产生新的合并提交节点,而实际上这些合并合并节点对你并没有直接作用,但该方式注重的每一个变更的过程,并不注重其结果是否简洁【如果你对于变更的过程(该变更到底来源于哪个分支)是不可忽视的那就一定要用该方式,但如果分支过多又没有一定的策略进行及时整理则会导致合并节点过最终不可控制,最终如下图一般】 complex-commit

选择建议:

  1. 选择在工作过程中,绝大多数情况我们都应该只选两者中的一者作为主要的合并方式,而另一种则应该是在某些特定情境下不得不采取的合并方式
  2. 如果是选择快进式合并方式,建议应该合理的使用上git tag对提交进行标记,当然tag也会有过多的时候,应该适时的进行清理(git gc会将.git/refs/下信息打包生成.git/pack_refs文件减少文件查询)或删除无用tag
  3. 如果是选择非快进式合并方式,(⊙o⊙)…还真没啥好建议,但非快进式合并方式在某些场景下也应该是必选的【例如:两个长期开发的功能分支合并集成到测试环境分支的时候,如果你强制使用手段让其变成快进式合并,你解决冲突可能会解决到蛋疼((((ToT)†~~~】

合并类型

“变更集”合并

fast-forward方式

  • Git的默认合并方式,优先以快进式 - fast-forward方式进行合并,若不能进行快进式合并再以非快进式进行合并

git-merge-fast-forward

$ git merge branch_name

–no-ff方式

now branch

$ git merge --no-ff fast

--no-ffno fast forward的缩写(猜的),要表达的意思就是:强制以非快进式进行合并.合并结果如下图:

git-merge-no-ff

square方式

$ git merge --square branch_name
  • --square合并分支会将待合入目标分支相对于当前分支的所有变更提取出来,并将这些一次性变更放到当前分支的工作区或暂缓区

git merge --square

  • 执行--square方式合并命令后,你需要额外的执行一次commit提交命令,将这些变更作为当前分支变更提交本地仓库,且此次提交与待合入目标分支没有关联

git merge --square

--square方式的合并适合于:

  1. 你一定是不在乎待合入目标分支的提交记录的,你要的只是待合入目标分支最后的一个结果
  2. 你在待合入目标分支中写了很多不符合规范的提交记录(如截图),这些提交是可有可无的且可以合并为一个提交记录的

“变基”合并

git-rebase-branch-now

假设当前分支情况如上图

  1. 当前的合并方式与目标合并分支一定是非快进式
  2. 你需要将fast分支(待合入目标源)与master分支(被合入当前分支)已快进式合并,且将fast分支所有提交信息保留

很明显前面讲的合并方式都不再适合当前的需求,唯一靠边的合并方式应是--square方式合并.但是--square方式最终只会保留一个变更记录,而此处需要保留全部(现实工作需求中可能包含的节点会更多)

想到的方式有两种: 方法1. 增对每一个节点建立一个分支,然后逐步前进的使用--square方式进行合并

但是:当合并的节点比较多的时候,那么你可能建分支建到怀疑人生,并且合并顺序如果有错,那么也可能会引发很多冲突,因此需要我们人工的去控制合并顺序

假设是否有方法2:

  1. 能够像--square方式,以master为原本(基础),将fast分支每一步的差异提取出来
  2. 并将提取出来的差异自下而上(从fast分支与master分支公共节点开始到fast最近一次提交)的顺序自动添加到master分支上,并保留每一次的提交信息

变基合并–rebase就是方法2的解决方案:rebase可以将当前分支补丁的方式在指定分支重演合并

注意:rebase和方法2中的方案有一个关键点不同,就是到底已谁为基础要分清,而rebase是以指定分支为基础版本将当前分支在其上进行演合一次

# 切换至目标分支(待合入当前分支的目标分支)
$ git checkout fast
# 执行演合操作
$ git rebase <other_branch>

演示如下

因为备份分支,前后图片的最终提交信息可能不同

git-rebase-branch

值得注意的两个问题是

  1. 使用rebase方式,到底是那条分支的信息产生会动了?
    • 是当前操作的分支数据前后产生了变动,因为:rebases是将当前的分支作为指定分支的补丁,从而使得两个分支的操作看似是fast-forward操作一般
  2. rebase铁律:rebase只能针对本地未提交的信息进行使用!
    • 远程仓库是大家公用的,如果你使用rebase将一个分支(这个分支已经推上了远程–其他人可能已经同步并且在使用中了)作为另一分支的补丁进行了变动,然后又推到了远程服务器上,那会产生什么后果? 可能会导致其他人在这个分支上拉取进行推送的时候有很多冲突【极大的概率是势必会发生的】;即使没有冲突,其他人使用拉取合并这个分支之后,相当于又覆盖了你将该分支作为补丁的操作,当你拉取下来的时候,你又可能会产生冲突并且会将你之前的补丁操作又覆盖回去,并且这个过程可能还会产生很多空白无变化的提交节点

因此在下图这种情况下是绝对禁止使用rebase合并【【fast分支相较与master分支的差异已经被推到了远程(哪怕只有其中一个差异被推到了远程也是禁止使用该命令进行操作的),上面的gif图可以是因为fast分支与master分支的所有差异变更都还在本地】

cann't use git rebase

扩展:“摘樱桃”模式–自由选择提交点

假设分支情况依然如上图所示,但我们很确定,其中真正有用的提交点只有最后一个我可是正儿八经的提交,而其他的提交节点其实是无效的(此处说的无效是指完全没有前面的变更也可以).那么:如何只将最后一个提交点移花接木到master分支上呢?

$ git cherry-pick commitId

git cherry-pick

如上图:我们看到指定提交节点的所有提交信息被复制移植了一份到当前分支

转载请声明出处: MinsonLee的博客:https://minsonlee.github.io

扫描下方二维码,关注公众号,接收更多实时内容

新猿呓码

打赏一个呗

取消

感谢客官打赏,您的打赏使我动力十足!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦