撤消 git 中的合并并使用 rebase 重建状态

Undo a merge in git and reconstruct the state using rebase

假设我有这张图:

A - B -  C1.1 -  D - E - F -G (master)
       \_ C2.1 _/(feature)

我想撤消合并,将 C1.2 之类的东西添加到 master 分支,然后重新合并并应用 D、E、F、G。我知道通常的方法是使用 -m1 标志恢复合并,然后在我们想要重新合并时恢复恢复提交 (假设我们不想挑选 E、F、G、因为合并后可能会有更多的提交)。问题是:

为什么这样:

$ git resset --hard C1.1
$ git commit C1.2 # New commits here
$ git merge feature
$ git rebase G E --onto master

是不是解决不了问题?需要明确的是,"solving the situation" 具有类似于此图的内容(尽管提交的 SHA 值不同但内容相同):

A - B -  C1.1 - C1.2  - D - E - F -G (master)
       \_ _ _ C2.1 _ _ _/(feature)

如果我做对了,我会:

  • 回到 C1.1,挑选 C2.1,然后挑选 E、F 和 G:

git checkout C1.1 git cherry-pick C2.1 git cherry-pick D..G # D won't be applied so it's all fine

阅读了您发出的 rebase 命令中选项的含义后,我认为问题在于您说 git rebase G E --到 master 上,我认为它应该是 git rebase D G --onto master 以便实际上不应用 D,它从那里一直到 G(换句话说,应用 E、F 和 G)。至少,这是我从阅读 https://git-scm.com/docs/git-rebase

中得到的

首先,让我们确保我们清楚提交标识。 (你的 post 表明你知道这一点,但你的图表具有误导性。)如果你变基,你正在创建新的提交,并且最多你变基的分支将指向新的提交。 (如果你不为 rebase 使用分支引用,那么只有 HEAD 最终会引用新的提交,这基本上是临时的,除非你然后标记或分支给他们一个引用。)

让我们逐步完成您的程序。我们有

A - B - C1.1 - D - E - F -G <--(master)
      \      /
        C2.1 <--(feature)

所以我假设你在master,你说

git resset --hard C1.1

这给了我们

A - B - C1.1 <--(master)
     \       \
      \        D - E - F -G
       \     /
         C2.1 <--(feature)

请注意,DG 仍然存在(实际上您以后的命令取决于它)。然后你做一些新的提交

git commit #c1.2

给予

A - B - C1.1 - C1.2 <--(master)
     \       \
      \        D - E - F -G
       \     /
         C2.1 <--(feature)

现在您重新合并特征。

git merge feature

这是您创建新提交的第一个位置,但您的图表表明您可能认为自己在某种程度上 "reusing" 是一个旧提交。你真的有

A - B - C1.1 - C1.2 ------------ D' <--(master)
     \       \                  /
      \        D - E - F -G    /
       \     /                /
         C2.1 --------------- 
            ^--(feature)

接下来你的变基语法是错误的。上游在提交引用之前(通常是一个分支),我 认为 --onto 可能必须在它们中的任何一个之前。此外,上游将是最后一次提交 而不是 rebased,所以你真的想要像

这样的东西
git rebase --onto master D G

这给出了

                                   E' - F' - G' <--()
                                  /                 ^HEAD
A - B - C1.1 - C1.2 ------------ D' <--(master)
     \       \                  /
      \        D - E - F -G    /
       \     /                /
         C2.1 --------------- 
            ^--(feature)

请注意,这不会更新 master 引用,因此接下来您需要执行类似

的操作
git branch -f master

从这里开始,我将省略原来的 DEFG,但它们仍然存在。特别是它们仍然在遥控器中(假设你有一个遥控器)并且仍然可以从遥控器的 master 版本访问。这意味着要推送,您必须 "force push",这将使任何其他开发人员的回购处于问题状态。有关详细信息,请参阅 git 变基文档中的 "recovering from upstream rebase";如果这仍然是个好主意,推送命令将是 git push -f。那时,我们不再关心旧的提交,可以假设它们最终会被 gc 丢弃。

所以我们的图表将是

A - B - C1.1 - C1.2 - D' - E' - F' - G' <--(master)
     \               /
      C2.1 ----------
         ^--(feature)

D'...G' 是新提交;任何标签都不会被遗留,任何其他分支仍会指向旧分支,并且 SHA1 值(提交 ID)将发生变化。由于 git 存储数据的方式,当您重写历史记录时,这是不可避免的。