git - merge 基于另一个功能分支的功能分支

标签 git version-control merge merge-conflict-resolution

几天前,我有一个具有完全线性历史的 master 分支。然后我创建了一个功能分支,我们称之为 feat/feature-a。我在那个分支上工作,然后提交它进行代码审查以 merge 到 master 中。

在审查 feat/feature-a 时,我想开发另一个依赖于 feat/feature-a 引入的代码的功能。所以我从 feat/feature-a 分支创建了一个 feat/feature-b 分支。

当我在处理 feat/feature-b 时,feat/feature-a 被 merge 到了 master 中。所以现在master有了feat/feature-a引入的代码。我现在想将 feat/feature-b merge 到 master 中,但是我遇到了很多看起来像这样的 merge 冲突:

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

我的猜测是,因为我将 feat/feature-a 更改到我的 feat/feature-b 分支中,所以我现在正试图“复制”这些更改以 merge 冲突结束。

我可以手动解决这些问题,但它们在数十个文件中多次存在,所以如果有更好的解决方案,我想知道。

最佳答案

总结:使用git rebase --onto <target> <limit>

作为Useless suggested in a comment ,如果你有一个真正的 merge ,这不应该发生。这就是我所说的“真正的 merge ”,以及如果您绘制相关提交的图表,分支的外观图表。我们从这样的事情开始:

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

这里有两个提交(虽然确切的数字并不重要)feat/feature-b上, 称为 IJ这里; 两个 功能分支上都有两个提交,称为 FG ;并且在 master有一个提交, 称为 H . (提交 E 及更早版本在所有三个 分支上。)

假设我们在 master 上进行真正的 merge 带进来FG .看起来像这样,以图表形式:

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

注意真正的 merge K有,作为它的父提交历史指针,都提交 H (在 master 上)和 G (在 feat/feature-a 上)。因此,Git 稍后知道 merge J。表示“以 G 开头”。 (更准确地说,提交 G 将是以后 merge 的 merge 基础。)

该 merge 将正常工作。但这不是以前发生的事情:相反,进行 merge 的人使用了所谓的“挤压 merge ”功能。虽然 squash-merge 带来了与实际 merge 相同的变化,但它根本不会产生 merge 。相反,它会产生一个单一的提交,该提交复制了已 merge 的许多提交的工作。在我们的例子中,它复制了 F 中的工作。和 G ,所以它看起来像这样:

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

请注意 K 中缺少反向指针至 G .

因此,当您进行 merge 时(真正的 squash-not-really-a-“merge”)feat/feature-b , Git认为应该以E开头. (从技术上讲,E 是 merge 基础,而不是早期实际 merge 案例中的 G。)如您所见,这最终会给您带来 merge 冲突。 (通常它仍然“正常工作”,但有时——就像在这种情况下——它不会。)

也许这对 future 来说很好,但现在的问题是如何解决它。

你想在这里做的是复制独家- feat/feature-b提交 提交,在 K 之后.也就是说,我们希望图片看起来像这样:

              I'-J'  <-- feat/feature-b
             /
...--E---H--K        <-- master
      \
       F--G          <-- feat/feature-a
           \
            I--J     [no longer needed]

最简单的方法是 rebase 这些提交,因为 rebase 意味着复制。问题是一个简单的 git checkout feat/feature-b; git rebase master将复制太多提交。

解决方法是告诉git rebase promise 复制。您可以通过更改 master 中的参数来执行此操作至 feat/feature-a (或提交的原始哈希 ID G — 基本上,任何标识第一个1 提交 以复制的内容)。但这告诉git rebase将它们复制到它们已经存在的位置;所以那不好。所以问题的解决方案是添加--onto ,它让您可以将“副本去向”部分与“复制内容”部分分开:

git checkout feat/feature-b
git rebase --onto master feat/feature-a

(假设您仍然有名称 feat/feature-a 指向提交 G ;如果没有,您将不得不找到其他方式来命名提交 G —您可能希望绘制自己的图表和/或仔细查看 git log 输出,以找到提交哈希)。


1在 Git 风格的倒退时尚中,就是“第一”。我们从最近的提交开始,并按照连接向后返回到较旧的提交。 Git 做所有事情都是倒着走的,所以在这里倒着想是有帮助的。 :-)

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

相关文章:

Git 在 pull 时忽略文件

git - 使用指定仓库中的 Git 分支动态填充 Jenkins Choice 参数

python - 如何在 python 中合并和正确更新字典(其中值是一个列表)?

git - Bitbucket 全局钩子(Hook)或默认配置

python sh 库,带连字符/破折号的命令

git - 您可以在单个存储库中为每个目录添加额外的 .gitignore 吗?

version-control - 并行开发 : Should developers work within the same branch?

svn - 删除所有目录中的 .svn 文件

python - 自合并后 Pandas Dataframe 中出现重复

javascript - 如何使用 jquery/Javascript 将段落合并到父链接中?