git 基础之 merge 和 rebase 的比较

在团队软件开发过程中,代码合并是一个基本且频繁执行的任务。

Git 提供了多种合并代码的策略,其中最常用的是 merge 和 rebase。

尽管二者的终极目标是相同的------整合代码变更------它们的方法和推荐的使用场景却有所区别。本文将详细介绍和比较这两种策略。

一、基于 merge 的合并介绍

1.1 如何使用

情景 1:

当你的代码处在分支 branch-a,而本地有另外一个分支 branch-b 的时候,使用

复制代码
git merge branch-b

可以将 branch-b 分支的代码,merge 到当前所处的 branch-a 分支上。

情景 2:

当你的代码处在分支 branch-a,直接使用 git pull origin branch-b 拉取仓库中 branch-b 分支的最新代码时,默认情况下,git pull 命令执行的是 fetch 后跟 merge。也即:

bash 复制代码
git pull origin branch-b
# 等价于
git fetch origin branch-b  # 拉取远端 branch-b 的最新状态,此刻本地的 branch-b 依旧不是最新代码
git merge origin/branch-b  # 合并远端 branch-b 最新代码,此刻本地的 branch-b 依旧不是最新代码

最新版本的 git 会在项目首次 pull 时,让你设置默认的合并行为;这里说的默认选用 merge 是老版本的 git。

要全局设置 git pull 使用 merge,可以配置

复制代码
git config --global pull.rebase false

下图是一个更加复杂的示例,branch-a 签出 branch-bbranch-c,按照时间顺序(图中由下到上)发生了如下事情

  1. branch-a 签出 branch-bbranch-c
  2. branch-b 提交了一次代码(节点 b1
  3. branch-c 提交了一次代码(节点 c1
  4. branch-b 提交了一次代码(节点 b2
  5. branch-c 提交了一次代码(节点 c2
  6. branch-c 被合并进了 branch-a(图中这种情况不会发生任何冲突,因为 branch-c 签出、提交、合并过程中,branch-a 并未发生代码改变)
  7. branch-b 提交了一次代码(节点 b3
  8. branch-b 被合并进了 branch-a(此时有可能发生冲突,因为 branch-bbranch-c 可能修改同一个位置的代码)

1.2 合并方式

git merge 创建一个新的合并提交,它具有两个父提交,分别指向合并前两个分支的最新提交。

例如上面的例子中,branch-c 被合并进 branch-a

  • 节点 a2 就是新的合并提交
  • 节点 a1c2 就是 a2 的两个父提交

1.3 历史记录呈现形式

使用 merge 时,历史记录保持了分支形式,从而可以清晰地展示各个分支的合并点与路径。

例如上面的例子中,合并后即便删掉 branch-bbranch-c,提交历史也会保留,即: b1b2b3c1c2

1.4 冲突处理方式

如果合并时遇到冲突,Git 会停止合并过程,让用户一次性解决冲突,然后创建一个新的合并提交来完成合并过程。

二、基于 rebase 的合并介绍

2.1 如何使用

情景 1

当你的代码处在分支 branch-a,而本地有另外一个分支 branch-b 的时候,使用

复制代码
git rebase branch-b

可以将 branch-b 分支的代码,merge 到当前所处的 branch-a 分支上。

git rebase 命令用于将一个分支的更改重新应用到另一个分支上。在分支上执行 git rebase 时,Git 将更改从一个分支上提取出来,然后在基底分支的头部重新应用它们。

情景 2

要使 git pull 默认执行 rebase 而不是 merge,可以通过 git config --global pull.rebase true 命令进行设置。

关于 rebase 的示例,要比 merge 的复杂一些,让我们先考虑如下情况

  1. branch-a 签出 branch-bbranch-c
  2. branch-b 提交了一次代码(节点 b1
  3. branch-c 提交了一次代码(节点 c1
  4. branch-b 提交了一次代码(节点 b2
  5. branch-c 提交了一次代码(节点 c2

此时如果处于 branch-a 分支上,执行

bash 复制代码
git rebase branch-c

将得到如下的分支和提交结构

此时如果处于 branch-a 分支上,执行

bash 复制代码
git rebase branch-b

将得到如下的分支和提交结构

那么,为什么 branch-c 的操作早于 branch-b,但是整体操作完,b1b2 位于 c1c2 之前呢?

这是因为执行 git rebase branch-name 的时候,会找到当前分支和 branch-name 分支的共同祖先,然后把当前分支在最早公共祖先之后的提交,好像新的提交一样一个个提交到 branch-name 的最新代码之后。

在上面的例子中,branch-a 分支和 branch-b 的公共祖先是 a1,所以会以 branch-b 的最后一次提交(b2)作为基础,将 branch-aa1 之后的提交(c1c2)依次应用上去。

合并方式

rebase 通过提取分支上的更改,并将它们应用到目标分支的最新提交之后,从而使得项目历史线性化

在上面的例子中我们可以看到,使用 rebase 合并得到的提交记录不像使用 merge 合并得到的提交记录一样交错在一起,不容易拆分。

历史记录呈现形式

使用 rebase 会产生一个线性的历史记录,好像所有更改都是顺序发生的,使得项目历史看起来像是单线程进行的。

冲突处理方式

rebase 过程中的冲突必须按照每个提交顺序单独解决。解决每个冲突后,需使用 git rebase --continue 来继续 rebase 过程。

三、二者对比

适用场景比较

  • merge 适合于需要保持详细合并历史的公共分支,如主分支或发布分支。
  • rebase 更适合于个人开发分支,在合并到公共分支之前,保持历史记录的整洁和线性。
比较维度 merge rebase
合并方式 创建新的合并提交,有两个父提交 提取提交在目标分支最新提交后重新应用
历史记录呈现形式 分支式,能看到分支合并情况 线性,看上去像所有提交在一条直线上
冲突处理方式 一次性解决冲突后提交合并结果 逐个解决每个提交的冲突,使用 git rebase --continue 继续
适合的公共分支场景 多开发者频繁合并,要求完整历史记录 需要线性历史记录,对美观度有要求
适合的个人分支场景 无特殊要求,记录分支合并历史即可 保持历史整洁和线性,便于审查

在实际开发中,团队可能会结合使用 merge 和 rebase 策略来充分利用它们的优势,例如在个人分支上使用 rebase 保持历史整洁,最后在合并到公共分支时使用 merge。

相关推荐
一念&6 小时前
Git 与 GitHub 的对比与使用指南
git·github
我是李武涯9 小时前
svn与git Merge重要区别讲解
git·svn
ん贤10 小时前
Git分支
git
迷你二鹏12 小时前
前端之Git
前端·git
哈里谢顿13 小时前
常见 git push 问题及解决方案
git
MarkGosling13 小时前
【开源项目】轻量加速利器 HubProxy 自建 Docker、GitHub 下载加速服务
运维·git·docker·容器·开源·github·个人开发
Aomnitrix18 小时前
【分布式版本控制系统】Git的使用
分布式·git
向上的车轮1 天前
SVN与GIT的区别,分别使用与哪些管理场景?
git·svn
java叶新东老师1 天前
git 提交时排除一个或多个文件
大数据·git·elasticsearch
我会冲击波2 天前
功能分支落后于develop太多,需要把开发分支合并到功能分支吗?
git·intellij idea