我有一个小问题。我需要取消远程存储库上的合并,而无需在之后完成取消提交。参见下面的模型和期望值。
当前,我有这个模型:
我想要这个:
我已经检查了文档并在网上搜索,但没有找到任何智能解决方案。你对我有个主意吗?我会在将来考虑我的合并!
非常感谢您!
答案 0 :(得分:2)
一种方法是在O
之前的最后一次提交上进行硬重置,该提交不属于分支5 - 6 - 7
,即在这种情况下为N
。然后樱桃选择所有必需的提交,即P
和Q
。下面的示例:
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已在此分支中,并且在它们之上还存在这些提交的反向提交。
这意味着您将永远无法合并提交7
,5
,6
。
尽管有一些机制可以通过更改基准或进行小规模更改(仅出于更改ID的目的)来更改提交ID,但这会导致相同更改发生合并冲突。就个人而言,我不推荐这种方法。
答案 1 :(得分:2)
您可以重写分支历史记录,也可以还原合并。每个都有优点和缺点。
首先,让我们从略微修改的当前状态图开始,它反映了正在发生的事情。
A -- B -- C <--(branch>
\
M -- N -- O -- P -- Q <--(master)
您没有显示任何参考,所以我假设master
指向Q
。如果您在branch
上没有分支,则可能应该创建一个分支(除非您要永久丢弃A
,B
和C
中的更改)。另外,这只是一个小符号,但我已将所有提交都切换为字母(因为有时可能更清楚)。
历史记录重写
有关历史记录重写的所有常规警告均适用于此方法。通常,这意味着,如果master
被推到其他人已经“看到”提交O
作为master
历史的一部分,那么您将必须与他们协调才能成功做一个历史重写。重写将使master
的副本处于无法恢复的状态,如果他们以错误的方式进行操作(例如,如果您没有传达发生的情况,则可能会如此),那么您的工作就可以被撤消。有关更多信息,请参见git rebase
文档中的“从上游基准恢复”。这是适用的条件,无论您是否实际使用rebase
命令执行重写。
如果您确实想进行重写,则重新设置基数是最简单的方法。您将需要提交N
和O
的ID,或者需要解析为提交N
和O
的表达式。在此示例中,我们可以将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
,它将跳过提交A
,B
和C
。
一种解决此问题的方法是使用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)
由于您没有提及有关当前分支的任何内容,因此建议您首先在现有提交上创建一些分支(b7
和bkp
),以使更改后它们保持可见:
+----- b7
v
5 - 6 - 7 +-------- bkp
| v
M - N - O - P - Q
^
+-------- bQ
由于您可能有一个指向Q
的分支,因此可以在以下命令中使用它代替bQ
。
然后在提交P
之上git rebase
提交Q
和N
,以获得所需的结构。
命令是:
# 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
旧的O
,P
和Q
提交仍然存在,并且只要分支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
不同。
如果提交Q
,O
和P
已经(通过Q
分支被推送到远程存储库,则再次推送bQ
将失败。可以强制(git push --force origin bQ
)进行推送,但是此操作会使您已经获取bQ
分支的当前位置(它包含bQ
,O
和{ {1}}提交。
如果您真的需要执行此特技,请确保您将此更改通知所有人。
在这种情况下,更好的方法是git revert -m
提交P
。这将在Q
之上创建一个新的提交,该提交将引入更改,以取消由提交O
引入的更改。这很丑,但这是最安全的解决方案。