几天前,我有一个完全线性的分支。然后我创建了一个功能分支,我们称之为master。我在那个分支上工作,然后将其提交给代码评审,以便合并到feat/feature-a中。
在对master进行审查时,我想研究另一个依赖于feat/feature-a引入的代码的特性。所以我从feat/feature-a分支创建了一个feat/feature-b分支。
当我在工作时,feat/feature-a被合并到master中。所以现在master有了feat/feature-b引入的代码。我现在想将feat/feature-a合并到master中,但是我遇到了很多这样的合并冲突:

<<<<<<< HEAD
=======
    // Some code that was introduced by feat/feature-b
>>>>>>> c948094... My commit message from feat/feature-b

我的猜测是,因为我将feat/feature-a更改带入了我的feat/feature-b分支,所以我现在试图“复制”那些以合并冲突结尾的更改。
我可以手动解决这些问题,但是它们在几十个文件上存在多次,所以如果有一个文件,我想知道一个更好的解决方案。

最佳答案

摘要:使用git rebase --onto <target> <limit>
作为Useless suggested in a comment,如果您有一个真正的合并,这不应该发生。这是我所说的“真正的合并”的意思,还有一个图,如果你画出有问题的提交的图,分支看起来是什么样子的。我们从这样开始:

...--E---H         <-- master
      \
       F--G        <-- feat/feature-a
           \
            I--J   <-- feat/feature-b

这里有两个提交(尽管确切的数字无关紧要),它们只在feat/feature-b上,这里称为IJ;有两个提交在两个功能分支上,叫做FG;还有一个提交只在master上,叫做H。(提交E和更早版本都在所有三个分支上。)
假设我们在master上进行真正的合并以引入FG。看起来是这样,以图形的形式:
...--E---H--K      <-- master
      \    /
       F--G        <-- feat/feature-a
           \
            I--J   <-- feat/feature-b

注意,real mergeK作为其父提交历史指针,同时提交H(onmaster)和G(onfeat/feature-a)。因此,git后来知道合并J意味着“从G开始”。(更准确地说,commitG将是稍后合并的合并基础。)
合并就行了。但这不是以前发生过的事情:相反,无论是谁进行了合并,都使用了所谓的“挤压合并”功能。虽然挤压合并会带来与实际合并相同的更改,但它根本不会产生合并。相反,它只生成一个提交,复制合并后的多个提交的工作。在我们的例子中,它复制了FG中的工作,因此看起来如下:
...--E---H--K      <-- master
      \
       F--G        <-- feat/feature-a
           \
            I--J   <-- feat/feature-b

请注意,从KG没有返回指针。
因此,当您转到merge(real或squash-not-real-a-“merge”)feat/feature-b时,git认为应该从E开始。(从技术上讲,E是合并基础,而不是像前面的实际合并案例中那样,G)正如您所看到的,这最终会导致合并冲突。(通常它仍然“只是工作”无论如何,但有时在这种情况下,它没有。)
也许这对未来是好的,但现在的问题是如何解决它。
在这里您要做的是将排他性的-feat/feature-b提交复制到K之后的新提交。也就是说,我们希望图片看起来像这样:
              I'-J'  <-- feat/feature-b
             /
...--E---H--K        <-- master
      \
       F--G          <-- feat/feature-a
           \
            I--J     [no longer needed]

最简单的方法是重新设置这些提交的位置,因为重新设置意味着复制。问题是一个简单的git checkout feat/feature-b; git rebase master将复制太多提交。
解决方案是告诉git rebase哪个提交要复制。您可以通过将参数从master更改为feat/feature-a(或者commitG的原始散列id基本上是标识第一个提交不复制的任何内容)。但这告诉git rebase将它们复制到它们已经存在的位置;所以这不好。因此,解决这个新问题的方法是添加--onto,这样您就可以将“副本的去向”部分与“要复制的内容”部分分开:
git checkout feat/feature-b
git rebase --onto master feat/feature-a

(这假设您仍然有指向commitfeat/feature-a的名称G;如果没有,则必须找到其他方法来命名commitG—您可能希望绘制自己的图和/或仔细查看git log输出,以找到commit散列)。
1“第一”在git风格的反向时尚,即。我们从最新的提交开始,然后将连接向后追溯到较旧的提交。git做任何事情都是向后的,所以在这里向后思考是有帮助的。:-)

关于git - merge 基于另一个功能分支的功能分支,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42210712/

10-13 05:22