合并两个分支和Git历史

请合并两个分支时,有人告诉我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的历史记录相交织。


TL;博士

在两个分支之间有一个线性历史记录只能通过在合并之前重新分配一个分支在另一个分支之上来完成。 如果你只合并了两个分支的分支,Git将通过创建合并提交来加入两行历史记录。

合并提交

在Git中,一个提交通常持有对一个父代的引用。 合并提交是一种特殊的提交方式,引用两个或更多父母。

在你的例子中,合并提交C5有两个父母:

  • 第一个父节点是C2 ,即另一个分支合并到的分支上的提交,即branch1
  • 第二个父节点是C4 ,即合并的分支上的提交,即branch2
  • 当你在git log branch1 Git将遵循两行历史记录向你显示:

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

    如果您在branch2上执行git log ,历史行将被交换:

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

    没有合并提交的合并

    两个分支之间的提交似乎在同一行历史记录中的场景 - 不涉及合并提交 - 是重新分配的结果。

    branch2顶部branch1分支branch2是一个与合并两个分支概念不同的操作。

    合并完成时:

    git checkout branch1
    git merge branch2
    

    重新激活是通过以下方式完成的

    git checkout branch2
    git rebase branch1
    

    这基本上意味着:

    找出所有从到达的提交branch2而不是从branch1 -在这种情况下, C3C4 -并将其应用上的最新顶部提交branch1

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

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

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

    git checkout branch1
    git merge branch2
    

    将不会创建合并提交。 相反,Git只会将branch1向前移动,以便它指向与branch2相同的提交。 这个操作被称为快进:

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

    撤销合并

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

    如果存在合并提交,则可以通过简单移动branch1指向合并提交的第一个父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                 # 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的更改将在主分支上重播。 这解释了会发生什么: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中,因为这样做只会偶尔会导致很多冲突。


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

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

    最后,为了撤消合并,你有两种可能性:

  • 如果你的合并没有被另一个开发者拉动,你总是可以重置你的分支#1。 重置实际上只是将分支的标记移动到另一个提交。 在你的情况下,在完成分支#1的结账之后,它将通过git reset C2完成。 如果你有本地修改你想摆脱,你可以做git reset --hard C2 。 你必须小心,因为重置是git中罕见的命令之一,并且有能力失去你的工作

  • 如果你合并已经被分享,你仍然有可能做回复。 回复是ew commit,修改会删除对合并的修改。 这不是一个完美的解决方案,因为历史将被保留。

  • 有关这些操作的更多信息,请访问:https://www.atlassian.com/git/tutorials/undoing-changes

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

    链接地址: http://www.djcxy.com/p/26081.html

    上一篇: Merging two branches and Git history

    下一篇: Are there any Git command to combine all our ugly commits together to one?