撤销一个尚未推送的 Git 合并操作

4847

我不小心在本地主分支上运行了git merge some_other_branch。我还没有将更改推送到origin主分支。如何撤消合并?


合并后,git status显示:

# On branch master
# Your branch is ahead of 'origin/master' by 5 commits.

如何撤销所有这些提交?


3
如果您需要保留历史记录,换句话说,如果有任何人曾经从您那里拉过或者您已经将其推到其他地方,请使用Yuri Ushakov在下面回答中提供的解决方案! - Sedrik
6
请取消当前获胜的答案,因为许多人指出它是不安全的,尽管仍在收集投票。对我来说,“MBO”看起来最好,尽管得分要少得多。 - inger
5
如果您需要保留历史记录,请使用下面 Yuri 的解决方案(在 @Sedrik 的评论中添加了一个链接)。 - cregox
相关:回退到之前的Git提交 - user456814
4
这是一个来自Github的很棒的资源: 使用Git撤销(几乎)任何操作 - jasonleonhard
Git - 三棵树的故事(头部、索引和工作树以非常有帮助的方式解释):重置解密 - leanne
35个回答

5462

使用 git reflog 命令,查找合并前的最后一个提交记录(git refloggit log 更好用)。然后可以使用以下命令进行重置:

git reset --hard commit_sha

还有另外一种方法:

git reset --hard HEAD~1

它会将您带回到上一个提交。

请注意,任何已更改但未提交/保存的文件都将被重置为其未修改的状态。要保留更改,请隐藏它们或查看下面的--merge选项。


如@Velmont在他的答案中建议的那样,在这种直接情况下使用:

git reset --hard ORIG_HEAD

使用--merge开关而不是--hard可能会产生更好的结果,因为它可以保留您的更改。 ORIG_HEAD将指向合并之前的提交,因此您无需自行寻找。


另一个提示是使用--merge开关而不是--hard,因为它不会不必要地重置文件:

git reset --merge ORIG_HEAD

--merge

该选项会重置索引,并更新工作树中 <commit> 和 HEAD 之间不同的文件,但保留索引和工作树之间不同的文件(即有未添加更改的文件)。


147
我不认为这会总是奏效 - “合并之前的一个提交”将是从另一个分支合并的最近一次提交 - 它不会是当前分支上最新的提交。对吗?(这可能只是git log默认选择显示的结果 - 可能有不同输出的git loggit reflog可以用于此) - John Bachir
6
我认为这可能取决于你是否使用压缩合并。 - Marcin Gil
32
@JohnBachir是正确的。在git log命令的输出中,您需要查看两个父提交记录。其中一个是您所在分支的最新提交,另一个是您合并的分支的最新提交。您需要使用git reset --hard命令将当前分支重置到您合并的分支的父提交记录上。 - Justin
7
只要“合并”不是一个快进操作,就会产生一个新提交记录,它位于日志的顶部,并且该提交记录有两个父节点(如果进行了八叉合并,则可能有两个以上的父节点)。如果您删除此合并提交,则来自合并的所有旧提交记录也将消失。不过,为了安全起见,重置后Git会告诉您新的HEAD在哪里:“HEAD is now at 88a04de <commit message>”。我总是查看它以确保我到达了期望的位置。我的项目使用标准分支命名方案以使事情更易记。 - Mark E. Haase
54
我发现有用的是查看“git reflog”,并查找我在主分支上最后一次提交。然后执行 git reset --hard <commit_sha> - Max Williams
显示剩余27条评论

1695

假设你的本地主分支没有领先于origin/master,那么你应该可以执行以下操作:

git reset --hard origin/<branch-name>

所以假设你在 master 分支上执行了这个操作,那么你本地的 master 分支应该和 origin/master 一模一样。


82
@Carter,实际上这并不是最好的答案。如果在合并之前,origin/master 可能比您本地的 master 前进了一些提交,那么这种情况下它可能不能给出期望的结果。 - Dhruva Sagar
16
是的,但只要git没有提示你落后了并且你不进行fetch操作,一切都应该没问题。 - Kelvin
3
谢谢!只有当你有远程仓库时,这才是完美的。 - tomc
2
不,这不是完美的答案,需要看“假设”条款。MBO的答案实际上涵盖了这种情况,以及合并不是唯一的本地提交的情况。 - inger
2
再次强调,也许这个警告应该写在回答本身里面:永远避免重写 git 历史记录! - cregox
显示剩余7条评论

1250

10
@ perfectionist 同意 :) 有点希望有一种方法可以将这个答案迁移到另一个问题上 -(也许有吗?) - mikermcneil
有关还原的更多信息,请参见以下链接:link - assaqqaf
3
为了确认还原已经生效,您可以执行 git diff hash1 hash2 的命令,其中hash1是已提交的还原操作的哈希值,而hash2是您想要恢复到其状态的旧的提交记录的哈希值。如果没有输出,则表示操作成功。我通过多次执行此操作(从最近的合并开始向后还原)成功地还原了多个提交。使用 git diff 命令可以显示我是否已经成功回到了目标状态。 - Robert Sinton
8
请注意,这并没有真正解决原帖作者的问题。原帖作者已经使用了 git revert -m 1 <commit> 命令。问题在于这样做并不能消除他意外合并的错误提交(但尚未推送)。其他关于硬重置的回答更适合原帖作者的问题。 - user456814
这是一个直接来自Github的极好的资源:如何使用Git撤消(几乎)任何事情 - jasonleonhard
这是正确的答案! - Jin Lim

1153

最简单的命令竟然失踪了,这很奇怪。大多数答案都可以实现,但撤销你刚刚完成的合并,这是最简单且安全的方法

git reset --merge ORIG_HEAD

引用 ORIG_HEAD 指向合并之前的原始提交。

(--merge 选项与合并无关。它就像 git reset --hard ORIG_HEAD,但更安全,因为它不会触及未提交的更改。)


20
如果您在此之后修改了您的工作目录,git reset --merge ORIG_HEAD 命令将保留这些更改。 - yingted
4
这是唯一正确的答案(我并不是说这是最好的答案——请注意区别)。假设在主分支上,我在t1、t3和t5时进行了3个提交。假设在分支1上,我在t2、t4和t6时进行了3个提交(假定t1、t2、t3、t4、t5和t6按时间顺序排列)。任何类似于git reset --hard HEAD~5的命令都只会重置HEAD(可能会删除主分支和分支1中的提交)。只有使用--merge选项才会删除合并。 - Manu Manjunath
4
@Manu,“--merge”选项实际上不会删除合并,您可以使用“--hard”,它也能很好地工作。这里的关键是引用ORIG_HEAD,它在您执行合并到达那个点之前就已设置好了。 :) Translated: @Manu, the "--merge" option does not actually remove the merge, you can use "--hard" which will also work well. The key here is the reference ORIG_HEAD, which is set before you do a merge to where you are standing at that point. :) - odinho - Velmont
@yingted 你说的"If you've dirtied your working tree since, git reset --merge ORIG_HEAD preserves those changes."是什么意思?你是指在合并后更改文件吗?无论如何,我已经完成了合并,然后解决了一些冲突。但是后来我想重置合并,并按照这个答案中的说明进行了操作。一切都很好,它没有保留我在合并后所做的更改。我的本地存储库与合并之前的位置相同。 - Samitha Chathuranga
1
“git reset --hard ORIG_HEAD” 命令对我非常有效 - 可能是因为在尝试撤消本地“git merge”之后,我没有对存储库进行任何其他更改。该命令只是将存储库的状态重置回合并之前的状态。感谢这个好建议! - bluebinary

484

如果你使用较新版本的Git,并且你还没有提交合并,同时你遇到了合并冲突,那么可以简单地执行以下操作:

git merge --abort

来自 man git merge:

[这个命令] 只能在合并产生冲突后运行。 git merge --abort 命令将中止合并过程并尝试重建合并前的状态。


19
他已经完成合并(commit),但尚未推送(push)(请参见标题)。你的命令只在他仍在合并过程中有效。请注意,他已经完成了合并动作。为了使命令生效,他需要撤销该次合并操作并重新开始新的合并。 - JBoy

184

你应该重置到上一个提交。这应该可以工作:

git reset --hard HEAD^

或者甚至使用HEAD^^来还原该撤销提交。如果不确定应该回退多少步骤,您可以始终给出完整的SHA参考。

如果存在问题且您的主分支没有任何本地更改,则可以重置为origin/master


5
在我的看法中,最好的答案包括原帖作者自己提供的答案(假设只需一个步骤来还原,这似乎是该问题的情况),以及randomguy3提供的快捷方式答案(当"你的主分支没有本地更改"时可用)。 - inger
7
你们评论的 @Inger 和 @Konstantin,为什么?你们是在我的回答创建后才来到这里的,而且我的回答更正确。只是简单地往上一步经常是错误的,你们必须要实际计算需要往上多少步。Git 已经为你设置了 ORIG_HEAD,为什么不使用它呢? - odinho - Velmont
它会重置本地更改吗?#请更新。 - CoDe
这对我来说完美地解决了问题,像这样重置头部比这里的一半答案更有意义。 - Varda Elentári
HEAD^ 等于 HEAD 之前的提交吗?^^ 是两个提交之前的意思吗?猜测这在快进合并时不起作用? - Marcus Leon

103

最近,我一直在使用git reflog来帮助解决这个问题。这主要只适用于刚刚发生合并的情况,并且该合并是在您的计算机上进行的。

git reflog可能会返回类似以下内容的结果:

fbb0c0f HEAD@{0}: commit (merge): Merge branch 'master' into my-branch
43b6032 HEAD@{1}: checkout: moving from master to my-branch
e3753a7 HEAD@{2}: rebase finished: returning to refs/heads/master
e3753a7 HEAD@{3}: pull --rebase: checkout e3753a71d92b032034dcb299d2df2edc09b5830e
b41ea52 HEAD@{4}: reset: moving to HEAD^
8400a0f HEAD@{5}: rebase: aborting

第一行表示发生了合并。第二行是在我合并之前的时间。我只需使用git reset --hard 43b6032来强制让该分支跟踪合并之前的状态,并继续进行。


非常好的答案,谢谢!需要撤消合并,但其他答案只会让问题变得更加复杂,使用 reflog 获取 SHA 并将其传递到 git reset 中是可行的。 - user692942

82

如果你正在合并中,你可以随时中止它。

git merge --abort

10
谢谢兄弟,我本来要做那个可怕的事情,幸运的是我往下滚动了一下,发现正确答案。我只想删除合并头。 - Nyuu

59

使用现代 Git,您可以:

git merge --abort

旧的语法:

git reset --merge

老派:

git reset --hard

但实际上值得注意的是,只有在MERGE_HEAD存在的情况下,git merge --abort才等同于git reset --merge。这可以在Git合并命令的帮助文档中阅读到。

git merge --abort is equivalent to git reset --merge when MERGE_HEAD is present.

当合并失败时,如果没有MERGE_HEAD,可以使用git reset --merge撤销失败的合并,但不一定能使用git merge --abort,因此它们不仅是相同事物的旧语法和新语法。

个人认为git reset --merge在日常工作中更加强大和有用,所以我总是使用它。


1
对我来说非常有效。其他帖子都说这很复杂,但它确实做到了预期的效果。我想这只是因为存在冲突而起作用,这并没有完全回答原始问题。 - Jeremy
这个回答没有关注到提问者的情况,也省略了重要的背景信息。 - Ben Wheeler
1
谢谢!在 git apply --3way 失败后,git reset --merge 帮了我一个大忙,其他答案都没用。这似乎是因为没有 MERGE_HEAD。 - nckturner

58
如果分支已经合并但未推送,那么以下给出的git reset命令可以撤销合并操作:

如果分支已经合并但未推送,那么以下给出的git reset命令可以撤销合并操作:

git reset --merge ORIG_HEAD

例子:

git reset --merge origin/master

git reset --merge origin/master - Amit Tumkur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接