时间:2018-09-04 09:51:25

标签: git git-revert

我有一个小问题。我需要取消远程存储库上的合并,而无需在之后完成取消提交。参见下面的模型和期望值。

当前,我有这个模型:

enter image description here

我想要这个:

enter image description here

我已经检查了文档并在网上搜索,但没有找到任何智能解决方案。你对我有个主意吗?我会在将来考虑我的合并!

非常感谢您!

3 个答案:

答案 0 :(得分:2)

一种方法是在O之前的最后一次提交上进行硬重置,该提交不属于分支5 - 6 - 7,即在这种情况下为N。然后樱桃选择所有必需的提交,即PQ。下面的示例:

git reset --hard N         #This resets the branch to N & removes all commits of merged branch `5 - 6 - 7`
git cherry-pick P          #Manually add the 2 commits you want back.
git cherry-pick Q

另一种方法是使用以下命令还原合并提交:

git revert -m 1 O .     #this is alphabet O - merge commit id. Not Numeric zero.

这将在Q的顶部添加一个新的提交-我们将其命名为O',其中

  • O'O的反向提交

caveat::如果以后尝试在5 - 6 - 7分支中进行一些更改并再次合并-它不会合并提交5,{{1} },6,因为这些提交ID已在此分支中,并且在它们之上还存在这些提交的反向提交。

这意味着您将永远无法合并提交756

尽管有一些机制可以通过更改基准或进行小规模更改(仅出于更改ID的目的)来更改提交ID,但这会导致相同更改发生合并冲突。就个人而言,我不推荐这种方法。

答案 1 :(得分:2)

您可以重写分支历史记录,也可以还原合并。每个都有优点和缺点。

首先,让我们从略微修改的当前状态图开始,它反映了正在发生的事情。

A -- B -- C <--(branch>
           \
  M -- N -- O -- P -- Q <--(master)

您没有显示任何参考,所以我假设master指向Q。如果您在branch上没有分支,则可能应该创建一个分支(除非您要永久丢弃ABC中的更改)。另外,这只是一个小符号,但我已将所有提交都切换为字母(因为有时可能更清楚)。


历史记录重写

有关历史记录重写的所有常规警告均适用于此方法。通常,这意味着,如果master被推到其他人已经“看到”提交O作为master历史的一部分,那么您将必须与他们协调才能成功做一个历史重写。重写将使master的副本处于无法恢复的状态,如果他们以错误的方式进行操作(例如,如果您没有传达发生的情况,则可能会如此),那么您的工作就可以被撤消。有关更多信息,请参见git rebase文档中的“从上游基准恢复”。这是适用的条件,无论您是否实际使用rebase命令执行重写。

如果您确实想进行重写,则重新设置基数是最简单的方法。您将需要提交NO的ID,或者需要解析为提交NO的表达式。在此示例中,我们可以将master~3用于N,将master~2用于O

git rebase --onto master~3 master~2 master

这将获取master可以访问但O无法访问的更改,并在N上重播;然后将master移至重写的提交。

A -- B -- C <--(branch>
           \
  M -- N -- O -- P -- Q <--(master@{1})
        \
         P' -- Q` <--(master)

旧的提交仍然存在(如我在此处所示,reflog仍可以到达它们-暂时在本地)。由于大多数工具都不遵循引用日志,因此您可能会看到类似

的内容
A -- B -- C <--(branch>

M -- N -- P' -- Q` <--(master)

实际上,在reflog过期之后,这就是将要保留的内容(如果您在此期间不做任何事情来保留旧的提交)。此时,要推动master,您必须进行强制推动。最安全的方法是

git push --force-with-lease

人们通常只推荐-f选项,但这并不安全,因为它可能会破坏远程master上您不知道的提交。无论如何,在强行推动之后,拥有master副本的其他任何人都必须从“上游重新设置基准”条件中恢复。

进行重写的其他方式(例如,通过重置然后进行“挑选”)在功能上是等效的(除非有一些奇怪的边缘情况),但是它们更手动,因此更容易出错。值得重申的是,即使此类替代方法可能不使用rebase命令,“上游重新设置基准”情况仍然会以完全相同的方式适用。


无需重写

如果历史记录重写不可行(在广泛共享的存储库中经常发生这种情况),则替代方法是还原合并提交。这将创建一个新提交,以“撤消”合并引入的更改。要在合并提交上使用revert,必须提供-m选项(它告诉revert哪个父级将还原为;如果您尝试撤消合并的影响,通常通常为-m 1

同样,您需要O的ID或可解析为该表达式的表达式;我们将在示例中使用master~2

git checkout master
git revert -m 1 master~2

现在有

A -- B -- C <--(branch>
           \
  M -- N -- O -- P -- Q -- !O <--(master)

其中!O撤消O应用于N的更改。

如其他地方所述,git将branch视为“已解决”-它没有跟踪!O的更改旨在作为O的还原/回滚或类似的操作那。因此,如果您以后要说git merge branch,它将跳过提交ABC

一种解决此问题的方法是使用rebase -f。例如,还原后您可以说

git rebase -f master~3 branch

,并且在branch进行合并之前,所有可以从master到达但不能从O到达的提交都将被重写。当然,这是对branch的重写。由于您可能一直在使用revert方法来避免重写master,因此您可能也不想重写branch。 (如果您确实重写了branch,并且如果branch与其他存储库共享,那么您就必须push --force-with-lease,其他用户必须从上游存储库中恢复。)

在您想要将branch合并回master时,另一种选择是“还原还原”。假设自还原合并以来已经过去了一段时间,并且您已经

A -- B -- C -- D -- E <--(branch>
           \
  M -- N -- O -- P -- Q -- !O -- R -- S <--(master)

现在可以将branch合并为master

git checkout master
git revert master~2
git merge branch

答案 2 :(得分:1)

由于您没有提及有关当前分支的任何内容,因此建议您首先在现有提交上创建一些分支(b7bkp),以使更改后它们保持可见:

        +----- b7
        v
5 - 6 - 7       +-------- bkp
        |       v
M - N - O - P - Q
                ^
                +-------- bQ

由于您可能有一个指向Q的分支,因此可以在以下命令中使用它代替bQ

然后在提交P之上git rebase提交QN,以获得所需的结构。

命令是:

# Preparations
# Create the branch `b7` to keep the commit `7` reachable after the change
git branch b7 7
# Create the branch `bkp` for backup (the commit `Q` will be discarded)
git branch bkp Q

# The operation
# Checkout `bQ` to start the change
git checkout bQ
# Move the `P` and `Q` commits on top of `N`
# Change "2" and "3" in the command below with the actual number of commits
# 2 == two commits: P and Q, 3 == 2 + 1
# Or you can use commit hashes (git rebase --onto N O)
git rebase --onto HEAD~3 HEAD~2

现在存储库如下所示:

        +----- b7
        v
5 - 6 - 7       +-------- bkp
        |       v
M - N - O - P - Q
    |
    P'- Q'
        ^
        +-------- bQ

旧的OPQ提交仍然存在,并且只要分支bkp或任何其他指向{{ 1}}仍然存在。如果您对此更改感到满意,则可以删除Q和其他所有指向Q的分支。

命令是:

bkp

除非您已经具有另一个指向提交git branch -D bkp 的分支,否则您可能不想删除b7。在这种情况下,您甚至不需要创建7

此操作后,存储库如下所示:

b7

请注意,提交 +----- b7 v 5 - 6 - 7 M - N - P'- Q' ^ +-------- bQ P'与原始提交Q'P不同。

警告

如果提交QOP已经(通过Q分支被推送到远程存储库,则再次推送bQ将失败。可以强制(git push --force origin bQ)进行推送,但是此操作会使您已经获取bQ分支的当前位置(它包含bQO和{ {1}}提交。

如果您真的需要执行此特技,请确保您将此更改通知所有人。

在这种情况下,更好的方法是git revert -m提交P。这将在Q之上创建一个新的提交,该提交将引入更改,以取消由提交O引入的更改。这很丑,但这是最安全的解决方案。