合并两个分支和Git历史

kel*_*dar 3 git merge git-merge

请有人告诉我Git在合并两个分支时的历史方面.

如果我有两个分支都在积极开发并且都包含提交,这类似于以下时间轴:

Branch #1: ----(branch)----C1----------C2-------(merge)------C5
                  \                             /
                   \                           /
                    \                         /
Branch #2:           -----------C3----------C4
Run Code Online (Sandbox Code Playgroud)

一旦两个分支合并,分支1的历史如何看待C5(提交#5)?我的印象是Git将合并所有历史记录给我以下内容:

Branch #1: ----------------C1----C3----C2----C4--------------C5
Run Code Online (Sandbox Code Playgroud)

这是正确的理解吗?

如果是这样,在紧急情况下,如何撤消合并,因为分支#2的所有历史肯定会与分支#1的历史交织在一起.

Enr*_*lio 6

TL;博士

在两个分支之间具有线性历史只能通过合并之前将一个分支重新设置在另一个分支之上来完成.如果您只合并两个已分歧的分支,Git将通过创建合并提交来加入两行历史记录.

合并提交合并

在Git中,提交通常包含对一个父级的引用.一个合并提交是一种特殊的提交引用两个或两个以上的父母.

在您的示例中,合并提交C5有两个父项:

  1. 一个父级C2,即另一个分支合并到的分支上的提交,即branch1
  2. 第二父C4,那就是承诺对被分支合并,即branch2.

当您git log使用branch1Git时,将按照以下两行历史记录显示:

C1--C2--C5 <- branch1
        /
  C3--C4 <- branch2
Run Code Online (Sandbox Code Playgroud)

如果你这样做git logbranch2,历史的线路将被交换:

     C3--C4 <- branch2
         /
C1--C2--C5 <- branch1
Run Code Online (Sandbox Code Playgroud)

合并没有合并提交

两个分支之间的提交似乎在同一行历史记录中的情况 - 没有涉及合并提交 - 是rebase的结果.

重订branch2之上branch1是这是一个操作概念不同,从合并的两个分支.

合并完成时:

git checkout branch1
git merge branch2
Run Code Online (Sandbox Code Playgroud)

重新定位完成:

git checkout branch2
git rebase branch1
Run Code Online (Sandbox Code Playgroud)

这基本上意味着:

找出所有到达从提交branch2,但branch1-在这种情况下,C3C4-和运用他们的最新顶级犯branch1.

所以最终的历史将如下所示:

C1--C2--C3--C4 <- branch2
     ^
     branch1
Run Code Online (Sandbox Code Playgroud)

请注意,branch1仍然指向C2- 与rebase之前相同的提交.由于两个分支之间的历史现在位于同一行,因此将它们合并为:

git checkout branch1
git merge branch2
Run Code Online (Sandbox Code Playgroud)

不会创建合并提交.相反,Git将简单地向前移动,branch1以便它指向相同的提交branch2.此操作称为快进:

换句话说,当你尝试将一个提交与可以通过遵循第一个提交的历史记录到达的提交合并时,Git通过向前移动指针来简化事情,因为没有不同的工作要合并在一起 - 这称为"快进".

撤消合并

撤消合并的方式将取决于合并是否通过合并提交完成.

如果存在合并提交,则只需移动branch1以指向合并提交的第一个父级即可撤消合并:

git checkout branch1       # branch1 points at the merge commit C5
git reset --hard branch1^  # branch1 now points at C2
Run Code Online (Sandbox Code Playgroud)

如果合并是在rebase之后完成的,事情就有点棘手了.您基本上需要还原branch1以指向它在合并之前执行的提交.如果合并是您所做的最新操作,您可以使用特殊参考ORIG_HEAD:

git reset --hard ORIG_HEAD
Run Code Online (Sandbox Code Playgroud)

否则你将不得不求助于reflog:

git checkout branch1
git reflog                 # Find the commit that branch1 pointed to before the merge
git reset --hard HEAD@{n}  # Move branch1 to point to that entry in the reflog
Run Code Online (Sandbox Code Playgroud)