撤消 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)
请注意,D
到 G
仍然存在(实际上您以后的命令取决于它)。然后你做一些新的提交
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
从这里开始,我将省略原来的 D
、E
、F
和 G
,但它们仍然存在。特别是它们仍然在遥控器中(假设你有一个遥控器)并且仍然可以从遥控器的 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 存储数据的方式,当您重写历史记录时,这是不可避免的。
假设我有这张图:
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)
请注意,D
到 G
仍然存在(实际上您以后的命令取决于它)。然后你做一些新的提交
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
从这里开始,我将省略原来的 D
、E
、F
和 G
,但它们仍然存在。特别是它们仍然在遥控器中(假设你有一个遥控器)并且仍然可以从遥控器的 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 存储数据的方式,当您重写历史记录时,这是不可避免的。