首页 > 其他分享 >git 的分支合并策略

git 的分支合并策略

时间:2023-03-17 13:12:32浏览次数:48  
标签:Git 合并 git commit 节点 分支

一、Git 的合并策略

了解完怎么合并两个文件之后,我们来看一个使用 git merge 来做分支合并。如上图,将 master 分支合并到 feature 分支上,会新增一个 commit 节点来记录这次合并。

Git 会有很多合并策略,其中常见的是 Fast-forward、Recursive 、Ours、Theirs、Octopus。下面分别介绍不同合并策略的原理以及应用场景。默认 Git 会帮你自动挑选合适的合并策略,如果你需要强制指定,使用git merge -s <策略名字>

了解 Git 合并策略的原理可以让你对 Git 的合并结果有一个准确的预期。

Fast-forward

Fast-forward 是最简单的一种合并策略,如上图中将 some feature 分支合并进 master 分支,Git 只需要将 master 分支的指向移动到最后一个 commit 节点上。

Fast-forward 是 Git 在合并两个没有分叉的分支时的默认行为,如果不想要这种表现,想明确记录下每次的合并,可以使用git merge --no-ff

Recursive

Recursive 是 Git 分支合并策略中最重要也是最常用的策略,是 Git 在合并两个有分叉的分支时的默认行为。其算法可以简单描述为:递归寻找路径最短的唯一共同祖先节点,然后以其为 base 节点进行递归三向合并。说起来有点绕,下面通过例子来解释。

如下图这种简单的情况,圆圈里面的英文字母为当前 commit 的文件内容,当我们要合并中间两个节点的时候,找到他们的共同祖先节点(左边第一个),接着进行三向合并得到结果为 B。(因为合并的 base 是“A”,下图靠下的分支没有修改内容仍为“A”,下图靠上的分支修改成了“B”,所以合并结果为“B”)。

但现实情况总是复杂得多,会出现历史记录链互相交叉等情况,如下图:

当 Git 在寻找路径最短的共同祖先节点的时候,可以找到两个节点的,如果 Git 选用下图这一个节点,那么 Git 将无法自动的合并。因为根据三向合并,这里是是有冲突的,需要手动解决。(base 为“A“,合并的两个分支内容为”C“和”B“)

而如果 Git 选用的是下图这个节点作为合并的 base 时,根据三向合并,Git 就可以直接自动合并得出结果“C”。(base 为“B“,合并的两个分支内容为”C“和”B“)

作为人类,在这个例子里面我们很自然的就可以看出来合并的结果应该是“C”(如下图,节点 4、5 都已经是“B”了,节点 6 修改成“C”,所以合并的预期为“C”)

那怎么保证 Git 能够找到正确的合并 base 节点,尽可能的减少冲突呢?答案就是,Git 在寻找路径最短的共同祖先节点时,如果满足条件的祖先节点不唯一,那么 Git 会继续递归往下寻找直至唯一。还是以刚刚这个例子图解。

如下图所示,我们想要合并节点 5 和节点 6,Git 找到路径最短的祖先节点 2 和 3。

因为共同祖先节点不唯一,所以 Git 递归以节点 2 和节点 3 为我们要合并的节点,寻找他们的路径最短的共同祖先,找到唯一的节点 1。

接着 Git 以节点 1 为 base,对节点 2 和节点 3 做三向合并,得到一个临时节点,根据三向合并的结果,这个节点的内容为“B”。

再以这个临时节点为 base,对节点 5 和节点 6 做三向合并,得到合并节点 7,根据三向合并的结果,节点 7 的内容为“C”

至此 Git 完成递归合并,自动合并节点 5 和节点 6,结果为“C”,没有冲突。

Recursive 策略已经被大量的场景证明它是一个尽量减少冲突的合并策略,我们可以看到有趣的一点是,对于两个合并分支的中间节点(如上图节点 4,5),只参与了 base 的计算,而最终真正被三向合并拿来做合并的节点,只包括末端以及 base 节点。

需要注意 Git 只是使用这些策略尽量的去帮你减少冲突,如果冲突不可避免,那 Git 就会提示冲突,需要手工解决。(也就是真正意义上的冲突)。

Ours & Theirs

Ours 和 Theirs 这两种合并策略也是比较简单的,简单来说就是保留双方的历史记录,但完全忽略掉这一方的文件变更。如下图在 master 分支里面执行git merge -s ours dev,会产生蓝色的这一个合并节点,其内容跟其上一个节点(master 分支方向上的)完全一样,即 master 分支合并前后项目文件没有任何变动。

而如果使用 theirs 则完全相反,完全抛弃掉当前分支的文件内容,直接采用对方分支的文件内容。

这两种策略的一个使用场景是比如现在要实现同一功能,你同时尝试了两个方案,分别在分支是 dev1 和 dev2 上,最后经过测试你选用了 dev2 这个方案。但你不想丢弃 dev1 的这样一个尝试,希望把它合入主干方便后期查看,这个时候你就可以在 dev2 分支中执行git merge -s ours dev1

Octopus

这种合并策略比较神奇,一般来说我们的合并节点都只有两个 parent(即合并两条分支),而这种合并策略可以做两个以上分支的合并,这也是 git merge 两个以上分支时的默认行为。比如在 dev1 分支上执行git merge dev2 dev3

他的一个使用场景是在测试环境或预发布环境,你需要将多个开发分支修改的内容合并在一起,如果不用这个策略,你每次只能合并一个分支,这样就会导致大量的合并节点产生。而使用 Octopus 这种合并策略就可以用一个合并节点将他们全部合并进来。

Git rebase

git rebase 也是一种经常被用来做合并的方法,其与 git merge 的最大区别是,他会更改变更历史对应的 commit 节点。

如下图,当在 feature 分支中执行 rebase master 时,Git 会以 master 分支对应的 commit 节点为起点,新增两个全新的 commit 代替 feature 分支中的 commit 节点。其原因是新的 commit 指向的 parent 变了,所以对应的 SHA1 值也会改变,所以没办法复用原 feature 分支中的 commit。(这句话的理解需要这篇文章的基础知识)

对于合并时候要使用 git merge 还是 git rebase 的争论,我个人的看法是没有银弹,根据团队和项目习惯选择就可以。git rebase 可以给我们带来清晰的历史记录,git merge 可以保留真实的提交时间等信息,并且不容易出问题,处理冲突也比较方便。唯一有一点需要注意的是,不要对已经处于远端的多人共用分支做 rebase 操作。

我个人的一个习惯是:对于本地的分支或者确定只有一个人使用的远端分支用 rebase,其余情况用 merge。

rebase 还有一个非常好用的东西叫 interactive 模式,使用方法是git rebase -i。可以实现压缩几个 commit,修改 commit 信息,抛弃某个 commit 等功能。比如说我要压缩下图 260a12a5、956e1d18,将他们与 9dae0027 合并为一个 commit,我只需将 260a12a5、956e1d18 前面的 pick 改成“s”,然后保存就可以了。

 

 

转载自 这才是真正的 Git——分支合并 - 知乎 (zhihu.com)·

 Git合并分支代码到主分支的方法总结_git合并到主分支_每天都要进步一点点的博客-CSDN博客

 

标签:Git,合并,git,commit,节点,分支
From: https://www.cnblogs.com/dzwj/p/17226255.html

相关文章

  • 在线视频文件合并
    经常在浏览一些在线视频,是由大量分段视频组成,可以通过合并,并转换成mp4等格式。方法一:使用软件合并,如格式工厂的视频合并: 注:由于格式工厂每次添加的文件......
  • 什么是 Go语言的分支语句?
    本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!作者|慕课网精英讲师Codey本文主要学习Go语言中的分支语句。一共要介绍的有两个,分别是 if…else 和......
  • docker下compose及gitlab安装配置
      部署逻辑与部署Jenkins类似,但是实现方式做了一些调整。先安装docker-compose,docker-compose是doker容器的管理工具,通过yaml来实现多个容器之间的调用关系。通过d......
  • git踩坑
      打开.git/config vi.git/config 将[remote“origin”]选项下的url的值从git@http开头改为http开头,即去掉最前面的git@ ......
  • 【Git】git设置代理
    在大陆访问github等网络非常不稳定,中途各种错误断开造成克隆项目失败,可以尝试设置代理解决(前提是得有代理,这里起的作用只是让git操作走代理路径,但是如果代理路径本身就不存......
  • git commit提交代码时跳过 eslint校验
    在使用命令gitcommit-m'提交信息'将本地代码提交到远程仓库时候,如果项目安装了per-commit,则会在Git键入提交信息前进行代码风格检查,如果代码不符合相应规则,则报错。......
  • github更新项目中的子模块
    若a项目已经引入了b项目作为子模块,可以使用以下步骤来同步更新b子模块:进入a项目的根目录,执行以下命令来切换到b子模块的目录:cdb执行以下命令,将b子模块更新到最新版本:git......
  • ruoyi-vue启动报错error:03000086:digital envelope routines::initialization error
    原因分析可能是因为node是最新版本导致ERR_OSSL_EVP_UNSUPPORTED错误SSL数字信封不支持解决措施(ps:网上找的)setNODE_OPTIONS=--openssl-legacy-provider亲测并......
  • 第七节:TortoiseGit、HbuilderX、VSCode、Visual Studio 2022 等客户端的使用
    一.        二.        三.         !作       者:Yaopengfei(姚鹏飞)博客地址:http://www.cnblog......
  • gitlab centos安装
    相关配置:1、yum安装相关依赖安装curl,policycoreutils-python,openssh-server服务#yuminstallcurlpolicycoreutils-pythonopenssh-server-y2、启动sshd服务,并激活开......