【问题标题】:Merging two branches and Git history合并两个分支和 Git 历史
【发布时间】:2016-01-11 01:17:15
【问题描述】:

请有人告诉我 Git 在合并两个分支时在历史方面做了什么。

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

Branch #1: ----(branch)----C1----------C2-------(merge)------C5
                  \                             /
                   \                           /
                    \                         /
Branch #2:           -----------C3----------C4

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

Branch #1: ----------------C1----C3----C2----C4--------------C5

这是正确的理解吗?

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

【问题讨论】:

  • Git 通常不会在合并中展平历史记录,因此鉴于您所呈现的情况,您通常不会看到线性历史记录...分支 1 的历史记录看起来就像您的整个树在你的第一个密码箱里。
  • Undo a Git merge? 的可能重复项。

标签: git merge git-merge


【解决方案1】:

tl;博士

两个分支之间的线性历史只能通过重新定位一个分支在另一个分支之上在合并之前来完成。如果你只是合并两个已经分歧的分支,Git 将通过创建一个 merge commit 来加入这两条历史记录。

与合并提交合并

在 Git 中,提交通常包含对一个父级的引用。 merge commit 是一种特殊类型的提交,它引用两个或多个父级。

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

  1. 第一个父节点C2,即分支上的提交 另一个分支合并到,即branch1
  2. 第二个父节点C4,即合并分支上的提交,即branch2

当您在 branch1 上执行 git log 时,Git 将遵循两条历史记录向您显示:

C1--C2--C5 <- branch1
        /
  C3--C4 <- branch2

如果你对branch2 执行git log,历史行将被交换:

     C3--C4 <- branch2
         /
C1--C2--C5 <- branch1

在没有合并提交的情况下合并

两个分支之间的提交似乎在同一条历史记录中的场景——不涉及合并提交——是 rebase 的结果。

branch1 之上重新定位branch2conceptually different 合并两个分支的操作​​。

合并完成时:

git checkout branch1
git merge branch2

变基是通过以下方式完成的:

git checkout branch2
git rebase branch1

这基本上意味着:

找到所有可以从branch2 访问但不能branch1 (在本例中为C3C4)的提交——并将它们应用到@ 中的最新提交之上987654346@.

所以最终的历史会是这样的:

C1--C2--C3--C4 <- branch2
     ^
     branch1

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

git checkout branch1
git merge branch2

won't create a merge commit。相反,Git 将简单地将branch1 向前移动,使其指向与branch2 相同的提交。此操作称为fast-forward

换一种说法,当您尝试将一个提交与 可以通过遵循第一次提交的历史来达到的提交, Git 通过向前移动指针来简化事情,因为有 没有分歧的工作可以合并在一起——这被称为“快进”。

撤消合并

撤消合并的方式将根据合并是否通过合并提交而有所不同。

如果有合并提交,您可以通过简单地将branch1 指向合并提交的第一个父级来撤消合并:

git checkout branch1       # branch1 points at the merge commit C5
git reset --hard branch1^  # branch1 now points at C2

如果合并是在 rebase 之后完成的,事情就有点棘手了。您基本上需要恢复 branch1 以指向它在合并之前所做的提交。如果合并是您最近执行的操作,您可以使用special reference ORIG_HEAD

git reset --hard ORIG_HEAD

否则你将不得不求助于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

【讨论】:

    【解决方案2】:

    合并实际上不会创建您所描述的内容,合并将创建一个包含两个父提交的提交。在该提交中,分支#2 的更改将在您的主分支上重放。这解释了会发生什么:https://git-scm.com/docs/git-merge

    您描述的是当您将分支#2 重新定位到分支#1 时(某种)发生的情况:https://git-scm.com/docs/git-rebase。但是,您将获得带有变基的 C1、C2、C3、C4、C5。变基时,提交不按时间排序。当您将分支#2 重新设置为分支#1 时,#2 中的每个提交都将应用于此时的#1。

    撤消变基或合并就像在提交的情况下将头部重置到合并提交之前以及在开始变基之前重置到 #1 分支中的最后一次提交一样简单。

    最后,我当然不知道你的确切设置,但如果两个分支都有很多积极的开发,我会经常将 #2 合并到 #1,因为这样做只是偶尔会导致很多冲突。

    【讨论】:

      【解决方案3】:

      首先你需要明白git中的一个分支只是一个标签。在您的示例中,您的分支 #1 将设置为 C1,然后是 C2,然后是 C5(合并后),而您的分支 #2 将设置为 C3,然后是 C4。

      然后,如果我理解正确,当 git 说“提交属于分支 X”时,这意味着该提交是分支标记的当前提交或该提交的直接或间接祖先。所以是的,合并后,所有对 C1 到 C 的提交都属于分支#1

      最后,要撤消合并,您有两种可能:

      • 如果您的合并没有被其他开发者拉取,您可以随时重置您的分支 #1。重置实际上只是将分支的标签移动到另一个提交。在您的情况下,它将在分支 #1 上完成结帐后使用git reset C2 完成。如果你有想要摆脱的本地修改,你可以做git reset --hard C2。你必须小心,因为 reset 是 git 中罕见的命令之一,它会丢失你的工作

      • 如果您的合并已被共享,您仍有可能进行还原。恢复是一次新的提交,修改会删除合并的修改。这不是一个完美的解决方案,因为会保留历史记录。

      在此处了解有关这些操作的更多信息:https://www.atlassian.com/git/tutorials/undoing-changes

      我还建议阅读这本电子书:https://git-scm.com/book/en/v2,至少前三章,可以很好地理解 git 中使用的概念。

      【讨论】:

        【解决方案4】:

        要撤消合并,您可以使用:git checkout banrch#1; git reset --hard c2-hash

        【讨论】:

          【解决方案5】:

          这是正确的理解吗?

          合并后,分支 #1 的历史将与您在问题中发布的一样,尽管基础 merge mechanism 并不那么简单。

          将分支 2 合并到分支 1 将“混乱”(不确定这是否是正确的词)分支 1 的历史。另一种方法是进行 squash 合并,它将分支 2 中的所有提交(不在分支 1 中)合并为分支 1 中的单个提交。这样一来,分支 1 的历史记录保持干净,如果您需要从分支 2 撤消更改,您只需要恢复单个提交(压缩的合并提交)。

          注意:对使用 squash 合并的看法因人而异,因团队而异。因此,在您的生产存储库中实际使用它之前,最好先阅读它。在某些情况下,更简洁的分支历史可能更可取,而在其他情况下,更复杂的历史(由非壁球合并生成)可能更受欢迎。一些相关文章和链接:

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-05-28
            • 1970-01-01
            • 2011-11-08
            • 2015-04-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-24
            相关资源
            最近更新 更多