git:修改维护分支并将这些补丁应用到另一个分支的正确合并或重新定位工作流程是什么?

时间:2013-04-12 14:27:17

标签: git git-merge git-rebase

目标:我需要为上游项目的先前版本制作自定义补丁,我希望能够将这些补丁应用到更高版本。

问题:从维护分支重新绑定到更高版本会与维护分支中尚未修改的文件发生冲突。

怀疑:我正在为我正在努力完成的事情错误地应用合并或重新定位。

示例:这是我想要完成的事情的一个逻辑示例,但要了解标记版本v1.0和v2.0之间的提交历史记录可以是数百次提交。

我将带有多个标记版本(v1.0和v2.0)的上游存储库与主提交历史记录A通过D分叉.B是标记v1.0的最后一次提交。 C是标记v2.0的最后一次提交。 D正在不断发展。

$ git clone git@server:repo

A<--B(v1.0)<--C(v2.0)<--D Master

我将早期版本标记(v1.0)分支出来进行一些自定义修改,如下所示:

$ git checkout v1.0 -b v1.1

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F v2.1

我现在要做的是分支一个更高版本的标签(v2.0)并将我在v1.1分支(G'和H')中做出的补丁提交应用到v2.0分支。我需要保留我在v2.0日志中在v1.0中进行的各个提交。

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F<--G'<--H' v2.1

问题:应用这些更改并避免冲突的正确工作流程是什么?我尝试了很多merge和rebase的组合,(包括--onto)但都失败了。 git rebase想要默认为3向合并,并与不相关的文件冲突。

$ git rebase v2.1 v1.1
Falling back to patching base and 3-way merge
...
Failed to merge in the changes.

$ git checkout v2.1
$ git rebase v1.1
Falling back to patching base and 3-way merge
...
Failed to merge in the changes.

2 个答案:

答案 0 :(得分:5)

我建议您编辑您的问题,以包含您尝试重新绑定的确切命令--onto。这应该是你完成你正在尝试的方式,但是你可能以这种方式运行命令来触发比实际需要更多的变基。

如果您的rebase命令重写v1.0v.2.0之间的所有内容,那么如果该历史记录包含通过非快进合并解决的冲突,则可能会导致很多不必要的痛苦。

为了清楚起见,我已将关于合并冲突和重新定位的解释移到了这个答案的底部。但是,该部分只是推测,看到您尝试的rebase --onto示例会很有帮助。现在还没有,我会提供我认为应该做的事情。话虽如此,让我们开始使用您的解决方案。


解决方案

<小时/> Rebase --onto

我喜欢阅读-onto的论据以便更好地理解。向后阅读,--onto <1> <2> <3>读取为: - <3>上的<2>上的任何提交,并将其应用于<1>。提交不是&#34;移动&#34;,&#34;克隆&#34;,所以你的旧提交仍然在那里 - rebase --onto只是创建它们的副本并在{{之后应用它们1}}。

重要的是要知道在执行<1>之后你可能会陷入无头状态。如上所述应用新提交,但它们不会立即更改分支的状态。这会在流程中增加一个额外的步骤,但同时也为您提供了一个额外的安全性,即知道rebase不会破坏您的分支 - 在将更改应用到您的分支之前,您将有机会查看更改的历史记录。

从这个图表开始。

rebase --onto

仅限 E<--G<--H v1.1 / A<--B<--C<--D Master \ F v2.1 G关注H不含,包括F,根据您的描述,情况似乎如此,那么你应该尝试以下命令。

E

如果你的情况如此,我就尽可能少地写下了现实。

这将采用git rebase --onto F G^ v1.1 上存在 的任何提交,立即继续执行v1.1,并在G之后应用它们。由于实际重写的唯一提交是FG,因此没有理由为什么你应该得到与这两个提交更改无关的任何冲突。

<小时/> 无头状态

如上所述,你最终可能处于无头状态。这意味着您不再在您的分支机构中。此时你的图表实际上看起来像这样...

H

如您所见,分支v2.1仍在 E<--G<--H v1.1 / A<--B<--C<--D Master \ F v2.1 \ G'<--H' (currently checkout out headless state) ,但您已创建F的新历史记录。这是你想要的,但它不在你的A<--B<--C<--F<--G'<--H'分支中。现在,请查看您的历史记录并验证其所需内容。如果你愿意,甚至可以测试它。验证后,您只需要签出v2.1并运行以下命令...

v2.1

这假设您在v2.1上没有不在git checkout v2.1 git merge H' 中的新提交。为了确保,您可能希望在合并上使用H'标志,以便它拒绝合并而不是创建合并提交。

就像我上面说的那样,这是一个需要注意的额外步骤,但是由于这个原因,你可以重置确保--ff-only不会弄乱你的实际分支。如果你发现rebase没有按预期工作 - 你可以直接结账git rebase --onto,看看没有造成任何伤害。

<小时/> 结果

在快进合并强制执行后,您的历史记录将如下所示......

v2.1

<小时/> Cherry-picking vs Rebase --onto

不会详细介绍樱桃采摘,但我想明确以下内容..

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F<--G'<--H' v2.1

完全相同......

git checkout v2.1
git cherry-pick G^..H

Cherry pick的步骤更少,可以在不查看&#34; base&#34;的情况下完成rebase,在这两种情况下都是git rebase --onto v2.1 G^ H git checkout v2.1 git reset --hard <hash> <-- were hash is the commit the rebase kicks you into. 。另外正如解释bove rebase --onto不会直接影响你的分支,使得如果出现问题,更容易恢复。两者都克隆了#34;他们带到基地的承诺,原件不受影响。


问题


以上是关于如何实现您要求做的一般性解释。以下是我怀疑你为什么遇到你所描述的问题。

<小时/> 非快进冲突解决方案&amp;衍合

我的猜测是,在v1.0和v2.0之间,你有一些用于解决冲突的非快进合并。在非快进合并期间解决冲突时,该冲突的解决方案存储在合并提交中,而不是存储在违规提交中。合并提交在合并点的历史记录中稍后发生,而不是在冲突的提交本身上发生。

当您进行rebase时,git会单独逐步执行每个提交并重新启动它 - 因此您将重温因非快进合并而导致的所有冲突,但该冲突的解决方案在历史记录的后期才会出现。合并发生了。使用非快进合并解决的冲突不利于您将来重新分支某个分支的能力,除非您愿意逐个重新解决所有这些冲突。

<小时/> 您可能犯的错误

如果我对您的问题的猜测是正确的,那么您可能已经完成了以下操作......

v2.1

此变体或此变体会导致git rebase --onto v1.1 F v1.1 中所有未在F上的提交并将其追加到v1.1的末尾。如上所述,这将导致v1.1B之间的每次提交被逐个重新提交。如果存在使用非快进合并解决的冲突 - 那么您将重新尝试每个冲突,因为rebase步骤通过这些提交。

<小时/> 合并而不是重新定位

您的问题标题表明您可能愿意简单地合并这些历史记录。如果您不关心线性历史记录,您可能只想将v1.1合并到F中。这不应该导致任何奇怪的冲突,但它会使你的历史变得非常混乱。

答案 1 :(得分:2)

虽然它可能没有对rebase的细粒度控制,或者合并的简易性,但是将一系列提交传递给git cherry-pick似乎更适合在旧分支上进行单独更改并将其播放到现任分支。

所以,如果G和H是v1.1中的最后两次提交,你应该能够通过以下方式将它们挑选到v2.0

git cherry-pick v1.1~1

(或手动提供提交哈希值)

如果你已经尝试过这个,并且有缺点,请告诉我。我仍在努力完善这种工作流程:)