如何更改另一个分支上发生的提交范围?

时间:2014-07-27 19:24:34

标签: git git-merge git-rebase

我的假设是解决方案很简单,但我找不到任何明确解决我问题的例子。我寻求以下指导:

我目前有一段历史:

A - - - C - - - - - F            Master
  \       \           \
    B - - - D - E - - - G - H    Topic

DEGH应该全部发生在Master上。此时唯一的区别应该是提交B。所以看起来应该是这样的:

A - - - C - D - E - F - G - H    Master
  \
    B                            Topic

甚至更好:

A - C - D - E - F - G - H        Master
                          \
                            B    Topic

......但我也不知道如何实现这一目标。

似乎我不能同时合并DEGH。我一直在B中以Master结束。

我相信我可以挑选它们,但这似乎是未来的问题。什么是最好的解决方案?

2 个答案:

答案 0 :(得分:1)

如果您已将这些分支机构推送到任何远程仓库,那么重写过去的历史记录并不是一个好主意。如果你推了,我会通过这样做避免改写:

  1. Master' F HEAD。
  2. 开始
  3. D上挑选EGHMaster
  4. 结帐Topic并从Master合并。
  5. 如果你这样做,你将拥有:

    A - - - C - - - - - F - - - - - D' - E' - G' - H'-       Master
      \       \           \                            \
        B - - - D - E - - - G - H - - - - - - - - - - - -    Topic
    

    历史不会看起来超级整洁,但你会在你想要的分支中拥有所有代码。


    如果您真的想重写历史记录,因为这只是您本地的,或者您不在乎与可能从您的回购中提取的其他人混淆,那么:

    1. 将大师回滚到C(在回滚之前记下F的哈希值)。
    2. D上挑选EFGHMaster
    3. 来自Topic2' Master头的H分行。
    4. B上的Cherry-pick Topic2
    5. 删除Topic,将Topic2重命名为Topic
    6. 你得到:

      A - C - D - E - F - G - H        Master
                                \
                                  B    Topic
      

答案 1 :(得分:1)

假设图纸准确无误,“你无法从这里到达那里”。 (尽管如此,你可能已经足够接近。)具体来说,绊脚石是提交F

您标记为F当前位于分支Master的提交已提交C作为其父级。

在新的所需图表中,标记为F的提交已将提交E作为其父级。

由于提交是其所有内容(其树,消息,作者/提交者和时间戳,以及 - 这里的问题 - 所有其父ID)的加密校验和,因此任何新的“F-like”将E作为其父级的提交必然是不同的提交。

“更好的”图表显示以B作为其父级的提交H,因此这与“现有”图表不同。因此,这与F具有相同的问题(具有相同的潜在解决方案)。

分支标签MasterTopic也必须更改。这两种变化都不会是“快进”(用git术语)。

您需要回答的问题是:除了您自己以外还有谁可以访问此存储库或此存储库的副本,并且他们(所有此类访问者)是否愿意为处理非快进操作所需的操作分支标签改变?

如果答案是“没有其他人有这样的访问权”,或者“他们都愿意做所需的事情”,你就没事了。如果没有,你需要另一种方法。让我们假设现在最好。

你仍然无法从这里到达那里,因为我们需要对你提交的内容进行更改F,并且永远不能更改提交。您也无法更改DE,虽然您不需要直接更改H,但您需要更改G - 它目前有两个父母 - 并改变“泡沫通过”:我们会在一瞬间看到为什么父母ID会发生变化。

您需要做的是提交复制到新的,略有不同的版本。我们会将提交D复制到一个版本,该版本会忽略B的任何更改,并且只有C作为其父级。我们将此副本称为D',以表明它与“D非常相似,但不完全相同”。

然后,我们会将E复制到与旧D - 到 - E具有相同更改的版本,但会将D'列为其父级。

接下来,我们会发现CF之间发生了什么,并对E'中的内容进行了相同的更改,并将其作为F'提交,并使用相同的消息等,与原始F中一样。

最后,我们希望FG之间发生的更改( EG之间的更改)应用于F'已提交G',并在G中使用相同的消息等提交G;然后我们将更改从H更改为G',将其应用到H',然后提交为A---C---D'--E'--F'--G'--H' <-- Master \ \ \ \-----F [old Master was here] \ \ \ B----D--E--G--H <-- Topic

结果看起来像这样(我们还不太喜欢你不太喜欢的选择):

Topic

现在要让BB结束,我们只需将其重新指向提交A---C---D'--E'--F'--G'--H' <-- Master \ \ \ \-----F \ \ \ B----D--E--G--H `.......................<-- Topic 即可。剩下的提交仍然在存储库中,但没有指向它们的标签(除了reflog之外):

B

我们现在拥有您想要的不太优选的选择。

要获得您的首选替代方案,我们现在只需将Master重新定位到B。这会将B'复制到与旧A - 到 - B具有相同更改的新提交C(忽略由于进入{{}}而不再需要的任何提交H {1}}到H'),但A---C---D'--E'--F'--G'--H' <-- Master \ B' <-- Topic 为其父级。重新绘制图形,省略所有不再标记的提交,给出:

git cherry-pick

至于实际复制提交的机制,那么,执行此操作的工作是git rebase -i。使用-p(没有-m),你可以让git做一系列的挑选。但是,合并提交有一些绊脚石。交互式rebase可以省略简单的合并,但是对于更复杂的合并,您需要向git cherry-pick提供git checkout -b temp Master^ # make new "temp" branch pointing to commit C git cherry-pick -m 2 Topic~3 # get C->D changes, make D' git cherry-pick Topic~2 # get D->E changes, make E' git cherry-pick Master # get C->F changes, make F' git cherry-pick -m 2 Topic~1 # get F->G changes, make G' git cherry-pick Topic # get G->H changes, make H' 参数,以告诉它选择合并的哪一侧。

所以,从现有的起点来看,我就是这样做的(有很多可能的方法):

git show

一路上或最后,在每次提交时使用-m以确保您获得了所需的内容(并且我在此处Master参数正确)。

如果一切顺利,请移动分支temp以匹配新的git checkout master; git reset --hard temp # reset Master to equal temp git branch -d temp # and delete temp, no longer needed

Topic

现在您可以重置并重新定位git checkout Topic; git reset --hard HEAD~4 # reset to point to B git rebase Master # copy B to B' and reset to B'

git checkout -b temp

(略微更高级的技巧:代替git checkout,只需Master并使用分离的HEAD模式构建所需的新{{1}},然后手动移动。这就是交互式rebase的方式shell脚本做到了。但它更令人困惑,所以最好是明确的,如上所述。)


如果您有其他人使用这些分支机构,他们必须通过自己的重置和/或重组从“非快进”更改中恢复。该方法在git rebase documentation中被描述为“从上游rebase中恢复”。