Git: 在合并过程中切换分支

9
我刚刚花了几个小时解决合并big-feature-branch-Bbig-feature-branch-A后出现的合并冲突。我终于完成了,所有的解决方案都已经准备好提交了。然而,在我工作的过程中,需要按照以下步骤进行:
  1. big-feature-branch-A创建一个分支(我将这个分支称为AB-merge-branch
  2. big-feature-branch-B合并到AB-merge-branch
  3. 创建一个PR将AB-merge-branch合并到big-feature-branch-A中,以便解决方案可以通过代码审查。
当我快要解决完合并冲突时,我意识到我正在big-feature-branch-A中解决它们,而不是在一个合并分支中。
我的问题是,在提交更改之前如何安全地更改分支?
我相信答案很简单,通常我会暂存我的更改,切换分支,然后恢复我的更改。但是,在合并过程中,我从未这样做过,并且我非常担心在这种情况下“尝试”它,因为我不想冒险再次解决所有这些冲突,而且我对我的git-fu不是非常自信。我也读到了一些恐怖的故事,比如这样的(但也许我的情况不同,因为我已经解决了所有冲突,并且所有更改都已经暂存?),并且在这种情况下不想进行实验。谢谢!

2
作为参考,“git stash”在合并过程中无法工作。(这是有道理的,因为上下文不仅仅是一组文件增量)。但仍然似乎这是git的一个缺点,即您无法“暂停”合并以切换分支。 - RJFalconer
3个回答

6

简单来说,你不能(切换分支...好吧,有点可以,虽然你可以这样做,但不建议这样做—它会直接干涉Git的内部操作):

$ git checkout -b newbr
foo.txt: needs merge
error: you need to resolve your current index first

您的索引处于特殊的“合并”状态,因此您不能stash。幸运的是,没有必要这样做。(如果您解决了所有问题,然后运行git checkout -b newbr并提交,您将获得一个非合并提交。您可以使用它,但让我们不要以这种方式处理。)

您应该继续完成合并:这给您想要的合并结果。然后,在您想要的分支中重新启动合并,并将刚才提交的结果作为您想要的结果获取,如果有必要的话。然后,完全丢弃原始的合并提交(如果需要的话)。(如果您意外地用成功的git checkout -b破坏了合并,那么这也是您需要做的。)

在某些情况下,包括您的情况,原始合并的父连接是您想要的,只需要重新标记合并即可。

我会先展示步骤,然后解释为什么它能够工作:

... finish merging ...
$ git commit                         # commit the merge
$ git checkout -b AB-merge-branch    # create the merge branch
$ git checkout big-feature-branch-A  # get back to other branch
$ git reset --hard HEAD^             # take the merge off of it

为什么这是一个答案(有多种方法,所以我不会说这是“唯一”的答案)

让我们把您开始合并时的设置绘制出来:

       o--...--o--o   <-- big-feature-branch-A (HEAD)
      /
...--*
      \
       o--...--o--o   <-- big-feature-branch-B

也就是说,你在“big-feature-branch-A”分支上,一切都很好,就像“git status”所表达的一样。每个“o”代表一个提交(而我用星号标记了合并基础,即合并点)。
然后,你本意是运行“git checkout -b AB-merge-branch”命令。如果你确实这样做了,图像将如下所示:
    o--...--o--o   <-- big-feature-branch-A, AB-merge-branch (HEAD)
   /
--*
   \
    o--...--o--o   <-- big-feature-branch-B

您运行了git merge,但是遇到了冲突。您已经解决了(大部分)的冲突(在继续之前必须全部解决)。当您最终提交时,将会得到一个新的合并提交,并且这将移动当前分支(由HEAD记住的分支):

    o--...--o--o   <-- big-feature-branch-A
   /            \
--*              M   <-- AB-merge-branch (HEAD)
   \            /
    o--...--o--o   <-- big-feature-branch-B

这里没有显示(因为很难显示出来)新合并的M第一个父提交是最右边(最新的)上一行的提交,第二个父提交是下方的提交。(这个第一个和第二个在稍后可能有所关系,当某人想要跟随“主”分支或“已合并的侧面功能”时:根据定义,“主”分支始终是第一个父提交。)

您忘记创建新的分支名称,因此现在会发生以下情况:

    o--...--o--o
   /            \
--*              M   <-- big-feature-branch-A (HEAD)
   \            /
    o--...--o--o   <-- big-feature-branch-B

第一个父提交仍然是最上面和最右边的提交,第二个父提交仍然是底部的这样的提交。新的合并提交 M 是完全相同的。只是移动到指向新合并 M 的“标签”是 big-feature-branch-A(即 HEAD),而不是不存在的 AB-merge-branch(显然不是 HEAD)。
所以现在你只需创建想要的标签即可。任何使新分支名称 AB-merge-branch 指向 M 的内容都足够。您可以使用 git checkout -b AB-merge-branch 或 git branch AB-merge-branch 进行操作。
如果您使用 git checkout -b,则现在必须返回 big-feature-branch-A 以使用 git reset 进行修复(还有其他命令可用,但我在这里坚持使用 reset)。如果您使用 git branch 创建新分支,则不会影响当前分支:您仍在 big-feature-branch-A 上。
无论如何,您希望将此 big-feature-branch-A 分支名称向后移动一步,移回到 M 的第一个父提交,就好像它从未首先向前移动到 M 一样。因此,您回到(或保留在)该分支上。一旦您在此分支上,就可以使用 git reset --hard HEAD^ 实现此向后移动一步。HEAD^ 表示“找到 HEAD 的第一个父提交”,如果 HEAD 名称为提交 M(确实如此),则意味着最右上角的提交,这是您希望分支指向的位置。git reset 命令执行此分支重新定位,并且还重新设置索引和工作树(以便一切都干净地在新提交中)。

一样,只是我不需要将big-feature-branch-A检出回来。 - VonC
非常出色和启发性的回答!+1 还有图表 :D - endemic
在重新阅读这个问题时,我发现 git 对我来说并不是这样工作的。如果我已经解决了所有冲突,那么 git checkout -b newbr 会成功完成,随意丢弃正在进行中的合并元数据。另一方面,git switch -c 则会拒绝。这是你答案中的错误(你假设存在未解决的冲突,但可能不存在),还是 git 的行为发生了变化? - Tao

4

首先,完成你的合并和提交。这样,你就不会丢失任何东西。


这是一个本地操作,不会被其他人看到。

从那个提交中,你可以创建你的分支AB-merge-branch。

 git checkout -b AB-merge-branch

您可以将之前的big-feature-branch-A重置为其第一个父提交。

 git branch --force big-feature-branch-A big-feature-branch-A^1

0

稍微纠正/补充一下@torek的答案,我发现如果我切换分支到一个新分支上提交我的冲突解决合并,使用git checkout -b AB-merge-branch分支更改成功,但由于分支更改的副作用,我也失去了合并状态

当我提交时,我不会得到一个基于合并的提交消息建议,而且结果只有一个父提交。

为了从这个“意外创建了一个新的独立提交”状态转移到我想要的状态,当然不需要重新解决已经解决的冲突,并假设我在AB-merge-branch上创建了这个“非合并提交”,我发现我需要做以下操作:

git switch -c AB-merge-branch-corrected AB-merge-branch~1
git merge -s ours --no-commit big-feature-branch-B
git restore --worktree --staged --source AB-merge-branch -- .
git commit # I get the expected merge message prompt
git switch AB-merge-branch
git reset AB-merge-branch-corrected
git branch -d AB-merge-branch-corrected

一般来说,最简单的建议是在开始合并之前创建新分支,就像您工作中的流程规定的那样。
如果您还没有切换分支,那么@torek上面的建议是正确/最简单的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接