git merge 与 git rebase 的区别
- 2021.05.11
开发者在使用 git 的时候经常会在 git merge 和 git rebase 之间难以抉择,针对这个问题,我们来解析下到底什么场景下适用merge,什么场景下适用rebase。
共同点
首先,这两个命令都有着相同的目的——————把不同分支的提交合并到一起。但是他们的实现方式确实不一样的。
git merge
我们先来看一下 git merge命令。
merge 是我们比较常用的 git 命令,当我们在创建分支进行测试、修复 bug,还是别的什么原因,经常都会需要把分支的提交 merging 到其它的分支上。常用的操作命令主要如下:
| 命令 | 说明 | 
|---|---|
| git merge A 分支 | 把 A 分支合入到当前分支,且为 merge 创建 commit | 
| git merge A 分支 B 分支 | 把 A 分支合入到 B 分支,且为 merge 创建 commit | 
假如我们有个多人协作的项目,项目有个 master 分支 和一个开发的 feature 分支,在开发的分支上我们进行了多次 commit 的提交,与此同时远程的master分支上其他协作者也在进行推送。

TIP
其中蓝色的主线是master分支,其他的颜色是对应的feature分支。
我们可以看到,当我们创建一个新分支,往master上推送一个 merge请求 的时候,都会往master上打一个点。当我们产生冲突的时候也只要解决一次冲突即可。
用一个比较生动的图来表示:

因此,我们可以很容易的总结 merge 的部分特点:
- 只处理一次冲突.
- 引入了一次合并的历史记录,合并后的所有 commit 会按照提交时间从旧到新排列.
- 所有的过程信息更多,可能会提高之后查找问题的难度.
TIP
为什么说 git merge 提交的信息过多可能会影响查找问题的难度呢?
因为在一个大型项目中,单纯依靠 git merge 方法进行合并,会保存所有的提交过程的信息:引出分支,合并分支,在分支上再引出新的分支等等,类似这样的操作一多,提交历史信息就会显得杂乱,这时如果有问题需要查找就会比较困难了。
git rebase
接着我们来看一下 git rebase 命令,这个命令在大项目的合作流程上非常有用。

直观的感受上我们会发现,git 的流水线变得更加清晰了。每个feature分支的提交都相对独立,是一个完整的功能单元。
这里我们没有展示在基于master分支的 A 节点创建了feature分支后,遇到master分支更新后,去执行feature分支的变基操作这一流程,我们可以通过下图去展示此流程:

从上图我们可以清楚的看到, rebase具有以下的几个特点:
- 当master分支变更后,rebase操作会改变当前分支从master上拉出分支的位置。
- 执行rebase后没有多余的合并历史的记录,此操作也导致了合并后的commit顺序不一定按照commit的提交时间排列。
- 可能会多次解决同一个地方的冲突(有 squash来解决,比如多个commit修改同一个文件导致的冲突解决)。
- 分支的流水线更加清爽一些,master分支上每个commit点都是相对独立完整的功能单元,不会像merge的时候每次打一个点。
git rebase 示例
为了更加深入的理解rebase的流程,我们来拿一个实际的项目进行演示:
为了脱敏,项目地址这里不放出来了,只放置项目的更新流程:
1. 创建项目
在 github 上创建一个git-rebase-demo的项目,并进行初始化操作。
创建后的项目如下所示:

接着我们新建一个index.js并且添加一个 demo1 方法:

接着我们将项目进行提交,推送到远端,打开下载好的Source Tree可视化工具,我们可以看到第一次的提交历史。

2. 基于 master 创建一个 feature1 分支
接下来我们新建一个feature1分支,并添加一个feature1方法和README.md更新并进行提交。


提交后我们把feature1分支 merge到 master分支上,看一下流:

3. 基于 master 创建一个 feature2 分支
当我们提交feature1到 master 后,接下来我们基于master分支新建一个feature1分支,并添加一个feature2方法和README.md更新并进行提交。

提交后的 git 流如下:

4. 在 master 分支上进行更新,模拟其他分支合并的场景
这步比较关键,在 master 分支上增加一个方法,注意此代码的行数会和feature2上的代码冲突。

接下来,我们在feature2分支上同步远程的master,模拟在上线前同步远程的变基操作:
git checkout feature2
git pull origin master --rebase

我们可以看到,由于index.js文件远程和该分支都进行了修改,这里产生了冲突,需要手动进行解决。而且在控制台也给出了我们执行rebase的命令。
TIP
当我们只有一个 commit 冲突的时候,我们只要解决该 commit 的冲突即可。
当我们有多个 commit 冲突的时候,我们需要一次又一次的解决冲突,并使用git rebase --continue命令进行后续的变基操作。
当然如果想要放弃变基,我们只需要执行git rebase --abort即可。
这里为了模拟多次 commit 冲突的情况,我们先执行git rebase --abort取消变基。
5. 基于 master 创建一个 feature3 分支
为了模拟多次冲突解决,我们基于master分支上继续创建一个 feature3分支进行修改。

我们看一下修改后的流:

5. 切回我们的开发分支 feature2 同步 master 变基
接下来,假设我们需要上线feature2分支了,此时这时候 master 上已经领先了我们两个版本,我们继续执行之前未完成的变基操作。
git checkout feature2
git pull origin master --rebase


合并feature2分支到master分支上。

我们可以看到,采用rebase变基后的分支非常的清晰。
git merge 示例
这里我们详细的展示了rebase变基后的流,那么关于merge的方式,通过上述的描述,大家能清晰的给出merge方式后的流表现么?
总结
到这里,想必大家对流程上两者的区别已经比较清楚了,如何去使用,我们再简单做下总结:
TIP
- git merge
- 当需要保留详细的合并信息的时候建议使用 git merge,特别是需要将分支合并进入 master 分支时;
- git rebase
- 多人合作大型项目保持流清晰,方便回退。
- 当发现自己修改某个功能时,频繁进行了 git commit 提交时,发现其实过多的提交信息没有必要时,可以尝试 git rebase。