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

Bre*_*ner 6 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
Run Code Online (Sandbox Code Playgroud)

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

$ git checkout v1.0 -b v1.1

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F v2.1
Run Code Online (Sandbox Code Playgroud)

我现在要做的是分支一个更高版本的标签(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
Run Code Online (Sandbox Code Playgroud)

问题:应用这些更改并避免冲突的正确工作流程是什么?我尝试了很多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.
Run Code Online (Sandbox Code Playgroud)

edd*_*oya 5

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

如果衍合命令重写之间的一切v1.0v.2.0,那么这可能会导致很多不必要的疼痛,如果这么做历史包括通过非快速向前合并解决的冲突.

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


解决方案


Rebase --onto

我喜欢阅读-onto的论据,以便更好地理解.落后的阅读,--onto <1> <2> <3>读作-以任何提交上<3>,使不上<2>,并将其应用于<1>.提交不是"移动",而是"克隆",所以你的旧提交仍然在那里 - rebase --onto只是创建它们的副本并在之后应用它们<1>.

重要的是要知道在表演之后rebase --onto你可能最终处于无头状态.如上所述应用新提交,但它们不会立即更改分支的状态.这会在流程中增加一个额外的步骤,但也可以让您更加安全地知道rebase不会破坏您的分支 - 在将更改应用到您的分支之前,您将有机会查看更改的历史记录.

从这个图开始.

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F v2.1
Run Code Online (Sandbox Code Playgroud)

要获得G并且H遵循F,包括E,根据您的描述似乎是这种情况,那么您应该尝试以下命令.

git rebase --onto F G^ v1.1
Run Code Online (Sandbox Code Playgroud)

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

这将在提交任何存在v1.1就立即提交继续存在G,并在他们以后适用F.由于实际被重写的唯一提交是GH,因此没有理由为什么你应该得到与这两个提交改变无关的任何冲突.


无头国家

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

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F v2.1
          \
           G'<--H' (currently checkout out headless state)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,分支v2.1仍然存在F,但您已经创建了一个新的历史记录A<--B<--C<--F<--G'<--H'.这是你想要的,但它不在你的v2.1分支中.现在,请查看您的历史记录并验证其所需内容.如果你愿意,甚至可以测试它.验证后,您只需要签出v2.1并运行以下命令...

git checkout v2.1
git merge H'
Run Code Online (Sandbox Code Playgroud)

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

就像我上面说的那样,这是一个需要注意的额外步骤,但是由于这个原因,你可以重置确保git rebase --onto不会弄乱你的实际分支.如果你发现rebase没有按预期工作 - 你可以简单地检查v2.1并看到没有造成任何伤害.


结果

在快进合并强制执行后,您将拥有一个看起来像这样的历史......

     E<--G<--H v1.1
    /
A<--B<--C<--D Master
        \
         F<--G'<--H' v2.1
Run Code Online (Sandbox Code Playgroud)


Cherry-picking vs Rebase --onto

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

git checkout v2.1
git cherry-pick G^..H
Run Code Online (Sandbox Code Playgroud)

完全等同于......

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.
Run Code Online (Sandbox Code Playgroud)

Cherry pick的步骤较少,可以在不查看"base"的情况下完成rebase,在这两种情况下都是如此v2.1.另外正如解释bove rebase --onto不会直接影响你的分支,使得如果出现问题就更容易恢复.两者都"克隆"它们带到底座上的提交,使原件保持不变.


问题


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


非快进冲突解决和重新定位

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

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


你可能的错误

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

git rebase --onto v1.1 F v1.1
Run Code Online (Sandbox Code Playgroud)

这本或一些变化会导致采取在所有提交F使不上 v1.1,并将它们附加到年底v1.1.如上所述,这将导致逐个提交BF重新提交.如果存在使用非快进合并解决的冲突 - 那么您将重新考虑每个冲突,因为rebase步骤通过这些提交.


合并而不是重新定位

你的问题标题表明你可能只是简单地合并这些历史.如果您不关心线性历史,您可能只想将v1.1合并到F.这不应该导致任何奇怪的冲突,但它会使你的历史显着混乱.