简而言之
如果两个分支之间有线性历史,只能在合并之前将一个分支rebase到另一个分支的顶部。如果您只是合并了两个分支,那么Git将通过创建merge commit来连接这两行历史记录。
使用合并提交进行合并
在Git中,提交通常保存对一个父提交的引用。merge commit是一种特殊类型的提交,它引用了两个或更多父提交。
在您的示例中,合并提交C5
有两个父提交:
- 第一个父提交是
C2
,即另一个分支合并到的分支上的提交,即branch1
- 第二个父提交是
C4
,即合并的分支上的提交,即branch2
。
当你在
branch1
上执行
git log
时,Git会跟随两条历史记录线路并向你展示这个结果:
C1
/
C3
如果你在
branch2
上执行
git log
命令,则历史记录的行将会被交换。
C3
/
C1
不使用合并提交的合并
在两个分支之间的提交看起来在同一历史线上,没有涉及到合并提交,这是变基的结果。
将branch2
变基到branch1
之上是一种操作,与合并这两个分支的操作 概念上 不同。
虽然合并是通过以下方式完成的:
git checkout branch1
git merge branch2
重新定位使用以下命令完成:
git checkout branch2
git rebase branch1
这基本意味着:
查找所有从branch2
可达但从branch1
不可达的提交记录 - 在此示例中为C3
和C4
- 并将它们应用于branch1
最新提交的顶部。
因此,最终的历史记录将如下所示:
C1--C2--C3--C4 <- branch2
^
branch1
注意,
branch1
仍然指向
C2
- 与重新贴基前相同的提交。由于两个分支之间的历史现在处于
同一条线上,因此可以使用以下命令将它们合并:
git checkout branch1
git merge branch2
不会创建合并提交。相反,Git将简单地将branch1
向前移动,使其指向与branch2
相同的提交。这个操作被称为快进:
换句话说,当您尝试将一个提交与可以通过跟随第一个提交的历史记录到达的提交合并时,Git通过向前移动指针来简化事情,因为没有需要合并的分歧工作 - 这称为“快进”。
撤消合并
撤消合并的方式取决于合并是否是通过合并提交完成的。
如果存在合并提交,则可以通过将branch1
移动到合并提交的第一个父提交来撤消合并:
git checkout branch1 # branch1 points at the merge commit C5
git reset --hard branch1^ # branch1 now points at C2
如果在变基之后进行了合并,情况会有些棘手。你基本上需要将
branch1
恢复到合并之前的提交点。如果合并是你最近执行的操作,你可以使用
特殊引用ORIG_HEAD
:
git reset --hard ORIG_HEAD
否则,您将不得不求助于
reflog:
git checkout branch1
git reflog
git reset --hard HEAD@{n}