Git:在合并中切换分支

ele*_*han 5 git git-merge

我刚刚花了最后几个小时来解决由于合并big-feature-branch-B到而导致的合并冲突big-feature-branch-A。我终于完成了,我所有的决议都已准备就绪,可以提出。但是,我工作的过程是:

  1. 创建一个分支big-feature-branch-A(我将其称为AB-merge-branch
  2. 合并big-feature-branch-BAB-merge-branch
  3. 创建要合并AB-merge-branch到的PR big-feature-branch-A,以便解决方案可以通过代码审查。

当我大部分时间都在解决合并冲突时,我意识到我正在解决它们big-feature-branch-A而不是合并分支。

我的问题是,如何在提交更改之前安全地更改分支?

我确信答案很简单,通常我只会存储我的更改,切换分支,然后弹出我的更改。但是,我从来没有在合并过程中这样做过,在这种情况下,我只是对“尝试”感到很ski跷,因为我不想冒险不得不再次解决所有这些冲突,而且我也不是超级对我的git-fu充满信心。我也读恐怖小说像这样(但也许我的情况是不同的,因为我已经解决了所有冲突,所有的改变都上演?),并且不觉得在这种情况下进行试验。谢谢!

tor*_*rek 5

简短的回答是你不能(切换分支......好吧,有点,虽然你可以,有点,但这是不可取的 - 它涉及直接与 Git 的内部进行猴子):

$ git checkout -b newbr
foo.txt: needs merge
error: you need to resolve your current index first
Run Code Online (Sandbox Code Playgroud)

您的索引处于这种特殊的“合并”状态,因此您也不能存储。幸运的是,没有必要这样做。(如果你解决了所有问题,然后运行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
Run Code Online (Sandbox Code Playgroud)

为什么这是一个答案(有多种方式,所以我不会说答案)

让我们绘制您第一次开始合并时的设置:

       o--...--o--o   <-- big-feature-branch-A (HEAD)
      /
...--*
      \
       o--...--o--o   <-- big-feature-branch-B
Run Code Online (Sandbox Code Playgroud)

也就是说,正如git status所说的那样,您“在分支 big-feature-branch-A 上”并且一切都很好。每个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
Run Code Online (Sandbox Code Playgroud)

你跑了git merge,它因冲突而失败。您解决了(大部分)冲突(您必须在继续之前解决所有冲突)。当您最终提交时,您将获得一个新的合并提交,这将移动当前分支(由 记住的分支HEAD):

    o--...--o--o   <-- big-feature-branch-A
   /            \
--*              M   <-- AB-merge-branch (HEAD)
   \            /
    o--...--o--o   <-- big-feature-branch-B
Run Code Online (Sandbox Code Playgroud)

此处未显示(因为太难显示)是新合并的第一个父项M是最右边(最新)的上行提交,第二个是较低的。(当有人想要遵循“主”分支与“已合并的侧功能”时,这第一个和第二个东西稍后很重要:根据定义,主分支始终是第一个父分支。)

你忘了创建一个新的分支名称,所以现在发生的事情是这样的:

    o--...--o--o
   /            \
--*              M   <-- big-feature-branch-A (HEAD)
   \            /
    o--...--o--o   <-- big-feature-branch-B
Run Code Online (Sandbox Code Playgroud)

第一个父级仍然是最顶部和最右侧的提交,第二个父级仍然是此类提交的底部。新的合并提交M完全一样的。只是移动的标签指向新的 merge M,是big-feature-branch-A(那个是 HEAD 的),而不是不存在的AB-merge-branch(显然不是 HEAD)。

所以你现在要做的就是创建你想要的标签。任何使新分支名称AB-merge-branch指向 的东西都M足以满足该部分的要求。您可以使用git checkout -b AB-merge-branchgit 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命名为 commit M(确实如此),则表示最右边的行提交,这是您希望分支指向的位置。该git reset命令会重新指向分支,并重新设置您的索引和工作树(以便所有内容都在新提交上干净利落)。