Git中合并多个提交的各种方法
为了保持分支提交历史的清晰、独立,我们经常需要将几个提交合并成一个;但 Git 没有专门为合并提交提供相应的命令,为解决这个问题,我专门研究了几种方法来实现 提交的合并;如下:
目录
- 1. 概念
- 2. 方式1:修复最后一次提交
- 3. 方式2:用reset重置
- 4. 方式3:用交互式rebase合并
- 5. 方式4:创建新分支并用rebase合并
- 6. 方式5:用rebase的autosquash合并
- 相关文章
内容
1. 概念
为了方便下文的描述,这里先定义几个概念;
若干个连续的提交 称为 提交段,即,一段提交的意思;也称 提交范围 ,在没有个歧义的情况下,也可简称 范围;
提交段中最开始(最早)的那个提交 称为 起始点;
提交段中最晚的那个提交 称为 结束点;
2. 方式1:修复最后一次提交
通过给 commit 命令添加 --amend
选项,可以修改最后一次的提交;它是将当前变更与最后一次提交的变更合并,并创建一个新的提交对象,然后用这个新的提交对象替换掉最后一次提交;
git commit --amend [-m <提交说明>]
示例:
假设最后一次提交是 C2
,如下图所示:
执行 git commit --amend
命令后:
3. 方式2:用reset重置
3.1. 思路
先用 reset 命令 重置到 需要被合并的提交的起始点的父提交;被还原的提交默认会被放置在暂存区中,然后再手动提交暂存区的内容,从而实现将被还原的所有提交合并成一个;
3.2. 步骤
- 用 reset 命令 重置到 需要被合并的提交的起始点的父提交;
-
语法:
git reset [--mixed | --soft] <起始点的父提交>
; 重围最后的n个提交,也可用git reset [--mixed | --soft] HEAD~n
-
示例:
git reset --soft aa29fd
、git reset HEAD~3
-
语法:
- 用重新提交被还原的提交:
git commit -m <提交说明>
4. 方式3:用交互式rebase合并
4.1. 思路
交互式的 rebase 命令,可以在变基的过程中更改提交,应用这个特性,我们可以将一系列的提交变基到它们原来所在的基上,而不是新的基上,从而可以实现对一系统列提交进行更改的操作;
4.2. 步骤
-
用交互式 rebase 命令对 需要被合并的提交 进行变基操作;
-
语法:
git rebase -i <起始点的父提交>
; 合并最后的n个提交,也可用git rebase -i HEAD~n
-
示例:
git rebase -i aa29fd
、git rebase -i HEAD~3
然后会弹出如下交互式界面:
交互式rebase默认界面说明,在互动界面中:
- 以
#
开头的是注释,前面不以#
开头的是命令,默认都是pick
命令,表示选中其后面的 提交。 - 这些命令会被从上到下执行;
- 可以对这些命令重新排序;
- 如果删除一行,则对应的提交将会丢失;然而如果删除了全部内容,变基操作将会终止;
- 空提交会被注释掉;
所有的命令如下:
-
pick <提交>
|p <提交>
:选中提交; -
reword <提交>
|r <提交>
:选中提交,并且修改提交说明; -
edit <提交>
|e <提交>
:选中提交,但在执行当前提交时会暂停,此时允许你修改这个提交; -
squash <提交>
|s <提交>
:选中提交,但会将当前提交合并到上一个提交中去; -
fixup <提交>
|f <提交>
:与squash相同,但不会保存当前提交的提交说明; -
exec <命令>
|x <提交>
:使用 shell 运行命令 -
break
|b
:在此处停止(使用git rebase --continue
继续变基) -
drop <提交>
|d <提交>
:删除提交 -
label <label>
|l <label>
:为当前HEAD打上标记 -
reset <label>
|t <label>
:重围HEAD到该标记 -
merge [-C <提交> | -c <提交>] <label> [# oneline]
:创建一个合并提交,并使用原始的合并提交说明(如果没有指定原始提交,使用注释部分的 oneline 作为提交说明)。使用-c <提交>
可以编辑提交说明;
-
语法:
-
将需要合并的提交前端的命令改成
更改交互式rebase界面中的命令squash
(或s
) 和fixup
(或f
),然后即同交互式界面;在交互式界面中,只有 squash 和 fixup 命令可以用来合并提交。如下图所示:
-
修改完成后,退出交互式界面(vim输入
:wq
保存并退出),然后按照交互式界面中设定好的命令顺序进行 rebase 操作;
5. 方式4:创建新分支并用rebase合并
如果被合并的提交段并不在分支的末端,而是在分支的中间,如下图:
被合并的提交在中间
我们也可以用交互式rebase的方法进行合并,只是需要在 交互式界面中 对不需要被合并的提交应用 pick 命令,但如果 被合并的提交是距当前HEAD很久远的提交,交互式界面中就会出现太多不相关的提交,便利查找想要合并的提交变更困难,我有一个方法可以简化这种操作,如下:
5.1. 思路
先基于被合并的提交段的终点提交创建一个临时的用于合并提交的分支A,则需要被合并的提交就会在在分支A的未端,然后在分支A上使用其它合并提交段的方法(如:方式1、方式2、方式3 等等)对提交进行合并,合并完成后,再将原分支中 终点提交 之后的所有提交变基到 分支A 上;
5.2. 步骤
合并中间提交段动画高清版的动画请见合并中间提交段动画-高清.gif 提取码:MI8K
- 基于需要被合并的提交段的终点提交 创建 新的临时分支T,并切换到该新分支上
git checkout -b 分支T <终点提交>
; - 用其它合并提交段的方法(如:方式1、方式2、方式3 等等)在 分支T 上对需要被合并的提交进行合并操作;
- 切换到 原来的分支 ,并通过命令
git rebase --onto 分支T <终点提交>
将原分支上 被合并的提交段 的终点提交 之后的所有提交 变基到 分支T上;
6. 方式5:用rebase的autosquash合并
这种方法与方式3:用交互式rebase合并的原理是一样的,只是不用手动更改交互式界面中的命令为 squash
或 fixup
了;它会自动根据提交说明中命令标识来在 交互式rebase 界面中自动自动更改相应的命令;
6.1. 使用方法
-
在进行提交时,如果这次提交需要与之前的某个提交合并,则使用
git commit --fixup=<合并的目标提交>
或者git commit --squash=<合并的目标提交>
; 这会生成一个带有标识的的提交;- 合并的目标提交: 指的是 需要与哪了提交进行合并;
- squash: 标识当前提交需要合并到指定的提交中去;
- fixup: 标识当前提交需要合并到指定的提交中去,但合并后不会保留当前提交的提交说明;
-
在需要合并的时候,执行命令
git rebase -i --autosquash <起始点的父提交>
,这时会弹出一个与 方式3:用交互式rebase合并 中的第1步的一样的交互式界面,并且上一步中被标识的提交会被自动修改成fixup
或squash
; -
修改完成后,退出交互式界面(vim输入
:wq
保存并退出),然后按照交互式界面中设定好的命令顺序进行 rebase 操作;