撤消尚未推送的 Git 合并

git undo git-merge

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

合并后,git status 说:

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

我如何撤消所有这些提交?

如果您需要保留历史记录,换句话说,有人曾经从您那里撤出过更改,或者您已将其推送到某个地方,请使用下面 Yuri Ushakov 回答中的解决方案!

请取消选择当前的获胜答案,尽管仍在收集选票,但它是不安全的(正如许多人指出的那样)。对我来说,“MBO”-s 看起来最好,尽管它的分数要少得多。

If you need to preserve history, use Yuri's solution down below! (只需添加指向@Sedrik 评论的链接)

相关:Revert to a previous Git commit

这是直接来自 Github 的一个很好的资源:How to undo (almost) anything with Git

E
Eric Platon

使用 git reflog 检查哪个提交在合并之前(git reflog 将是比 git log 更好的选择)。然后您可以使用以下方法重置它:

git reset --hard commit_sha

还有另一种方式:

git reset --hard HEAD~1

它会让你回到 1 次提交。

请注意,任何已修改和未提交/未存储的文件都将重置为未修改状态。要让他们保留更改,或者查看下面的 --merge 选项。

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

git reset --hard ORIG_HEAD

可能会产生更好的结果,因为它应该保留您的更改。 ORIG_HEAD 将在合并发生之前直接指向提交,因此您不必自己寻找它。

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

git reset --merge ORIG_HEAD

--merge 重置索引并更新工作树中 和 HEAD 之间不同的文件,但保留索引和工作树之间不同的文件(即具有尚未添加的更改)。

我认为这不会(总是?)工作——“合并之前的一次”将是从另一个分支合并的最新提交——它不会是当前分支上的最新提交.正确的? (这可能只是 git log 选择默认显示的结果 - 可能有不同的 git log 输出或 git reflog 可用于此)

我认为这可能取决于你是否压缩合并。

@JohnBachir 是对的。在 git log 输出中,您想要查看两个父提交。一个是您分支中的最新提交,一个是您合并到的分支中的最新提交。您想要 git reset --hard 到您合并到的分支上的父提交。

@JohnBachir:只要“合并”不是真正的快进,它就会导致日志顶部的新提交,并且这个提交有两个父母(或者如果你做章鱼,则超过2个合并)。如果你删除了这个合并提交,那么来自合并的所有旧提交也将消失。不过,为了安全起见,在重置后 git 会告诉你新的 head 在哪里:“HEAD 现在位于 88a04de ”。我总是看着那个,以确保我最终到达了我期望的位置。我的项目使用标准的分支命名方案来保持记忆。

我发现有用的是查看“git reflog”并查找我在 master 中所做的最后一次提交。然后执行git reset --hard <commit_sha>

S
Sridhar Ratnakumar

假设您的本地主人没有领先于原产地/主人,您应该能够做到

git reset --hard origin/master

然后您的本地 master 分支应该看起来与 origin/master 相同。

@Carter 它实际上不是最好的答案。在某些提交合并之前,origin/master 可能在本地 master 之前,在这种情况下,这可能不会给出预期的结果

@dhruva-sagar 是的,但只要 git 没有说你落后,并且你不获取,你应该没问题。

谢谢!如果(且仅当)您有一个远程存储库,这是完美的。

不,这不是这个问题的完美选择,请参阅“假设”条款。 MBO 的回答实际上涵盖了这种情况,以及合并不是唯一本地提交的情况。

再一次,也许这个警告应该进入答案本身:Always avoid rewriting git history!

P
Peter Mortensen

请参阅 chapter 4 in the Git bookthe original post by Linus Torvalds

要撤消已推送的合并:

git revert -m 1 commit_hash

如果您再次提交分支,请务必恢复还原,就像 Linus 所说的那样。

@perfectist 同意 :) 有点希望有办法将此答案迁移到另一个问题——(也许有?)

有关还原的详细信息:link

为了确信这个恢复工作,你可以做 git diff hash1 hash2 其中 hash1 是提交的恢复,而 hash2 是你试图恢复到其状态的旧提交。没有输出==成功!通过多次执行此操作,我能够回滚多个提交,首先是还原最近的合并并向后工作。 git diff 向我展示了我最终达到了我想要的状态。

请注意,实际上解决了原始发帖人的问题。原始海报已经使用了git revert -m 1 <commit>。问题是这样做并没有消除他所做的意外合并(并且还没有推动)。涉及硬重置的其他答案更适合原始发布者的问题。

这是直接来自 Github 的一个很好的资源:How to undo (almost) anything with Git

P
Peter Mortensen

奇怪的是缺少最简单的命令。大多数答案都有效,但是撤消您刚刚所做的合并,这是一种简单而安全的方法:

git reset --merge ORIG_HEAD

ref ORIG_HEAD 将指向合并之前的原始提交。

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

如果您此后弄脏了工作树,git reset --merge ORIG_HEAD 会保留这些更改。

这是 only 正确答案(我并不是说这是最佳答案 - 请注意区别)。假设,在 master 上,我在 t1、t3 和 t5 做了 3 次提交。假设在 branch1 上,我在 t2、t4 和 t6 做了 3 条评论(假设 t1、t2、t3、t4、t5 和 t6 按时间顺序排列)。任何类似于 git reset --hard HEAD~5 的命令只会重置 HEAD(可能会删除 master 和 branch1 中的提交)。只有 --merge 选项会删除 merge

@Manu --merge 选项实际上并没有删除合并,您可以使用 --hard 它也可以正常工作。此处的线索是参考 ORIG_HEAD,它是在您合并到您所在的位置之前设置的。 :)

@yingted “如果您此后弄脏了工作树,则 git reset --merge ORIG_HEAD 保留这些更改是什么意思。”您的意思是合并后更改文件吗?无论如何,我做了合并,然后做了一些解决冲突。但是后来我想重置合并并按照此答案中的说明进行操作。一切都很好,它没有保留我在合并后所做的更改。我的本地仓库与我进行合并之前的位置相似。

git reset --hard ORIG_HEAD 命令对我来说非常有效——这可能是因为我在尝试撤消本地 git merge 后没有对存储库进行任何其他更改。该命令只是将存储库的状态重置为合并之前的状态。感谢您的精彩提示!

S
Stefan van den Akker

对于较新的 Git 版本,如果您尚未提交合并并且您有合并冲突,您可以简单地执行以下操作:

git merge --abort

man git merge

[This] 只能在合并导致冲突后运行。 git merge --abort 将中止合并过程并尝试重建预合并状态。

他的合并已提交但未推送(见标题),他已经合并,您的命令仅在他仍在合并中间时才有效

P
Peter Mortensen

您应该重置为之前的提交。这应该有效:

git reset --hard HEAD^

甚至 HEAD^^ 来恢复该恢复提交。如果您不确定应该退后多少步,您可以随时提供完整的 SHA 参考。

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

最好的答案恕我直言,包含了 OP 自己的一个(假设只有 1 个步骤可以恢复,这在 Q 中似乎是这种情况),以及 randomguy3 的快捷方式一个(当“你的主分支没有任何本地更改时有效” ")

你们评论者,@Inger 和@Konstantin,为什么?您是在我的答案创建后来到这里的,而且更正确。仅仅向上走一步通常是错误的,你必须实际计算你需要走多远。 Git 已经为你设置了 ORIG_HEAD,为什么不使用它呢?

它也会重置本地更改吗? #请更新。

这对我来说非常有效,像这样重置头部比这里的一半答案更有意义。

HEAD^ 等于在 HEAD 之前提交? ^^ 是两次提交之前?猜测这不适用于快进合并?

P
Parris

最近,我一直在使用 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 工作。

这绝对节省了我的一天,以前从未使用过 git reflog,谢谢一百万

t
turivishal

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

git merge --abort

谢谢兄弟,我正要做那些可怕的事情正确答案。幸运的是我向下滚动。我只想删除合并头

P
Peter Mortensen

使用现代 Git,您可以:

git merge --abort

旧语法:

git reset --merge

老套:

git reset --hard

但实际上,值得注意的是,鉴于存在 MERGE_HEADgit 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 在日常工作中更加强大和有用,所以这是我一直使用的。

对我来说效果很好。其他所有帖子都说这太复杂了,但这完全符合预期。我想它只是因为存在冲突而起作用,这并不能完全回答最初的问题。

这个答案不关注 OP 的情况,并遗漏了重要的上下文。

谢谢! git apply --3way 失败后,git reset --merge 提供了帮助,而其他答案却没有。这似乎是因为没有 MERGE_HEAD。

K
Kostas Minaidis

如果分支被合并而不被推送,那么下面给出的 git reset 命令将用于撤消合并:

git reset --merge ORIG_HEAD

例子:

git reset --merge origin/master

示例:git reset --merge origin/master

P
Peter Mortensen

好的,这里其他人给我的答案很接近,但它不起作用。这就是我所做的。

这样做...

git reset --hard HEAD^
git status

...给了我以下状态。

# On branch master
# Your branch and 'origin/master' have diverged,
# and have 3 and 3 different commit(s) each, respectively.

然后我不得不多次输入相同的 git reset 命令。每次我这样做时,消息都会改变一个,如下所示。

> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 3 and 3 different commit(s) each, respectively.
> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 2 and 3 different commit(s) each, respectively.
> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 1 and 3 different commit(s) each, respectively.
> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch is behind 'origin/master' by 3 commits, and can be fast-forwarded.

此时,我看到状态消息发生了变化,因此我尝试执行 git pull,这似乎有效:

> git pull
Updating 2df6af4..12bbd2f
Fast forward
 app/views/truncated |    9 ++++++---
 app/views/truncated |   13 +++++++++++++
 app/views/truncated |    2 +-
 3 files changed, 20 insertions(+), 4 deletions(-)
> git status
# On branch master

长话短说,我的命令归结为:

git reset --hard HEAD^
git reset --hard HEAD^
git reset --hard HEAD^
git reset --hard HEAD^
git pull

或者您可以使用 HEAD^^^^

甚至可能重置为 origin/master ;)

C
CodeWizard

你必须改变你的 HEAD,当然不是你的,而是 git HEAD....

所以在回答之前让我们添加一些背景知识,解释一下这个 HEAD 是什么。

首先什么是HEAD?

HEAD 只是对当前分支上当前提交(最新)的引用。
在任何给定时间只能有一个 HEAD。 (不包括git worktree

HEAD 的内容存储在 .git/HEAD 中,它包含当前提交的 40 字节 SHA-1。

分离头

如果您不是最新的提交 - 这意味着 HEAD 指向历史上的先前提交,它称为 detached HEAD

https://i.stack.imgur.com/OlavO.png

在命令行上,它看起来像这样 - SHA-1 而不是分支名称,因为 HEAD 没有指向当前分支的尖端

https://i.stack.imgur.com/qplvo.png

关于如何从分离的 HEAD 中恢复的一些选项:

git结帐

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

这将签出指向所需提交的新分支。此命令将检出给定的提交。此时,您可以创建一个分支并从这一点开始工作。

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

您也可以始终使用 reflog
git reflog 将显示更新 HEAD 的任何更改,并且检查所需的 reflog 条目会将 HEAD 设置回此提交。

每次修改 HEAD 时,reflog 中都会有一个新条目

git reflog
git checkout HEAD@{...}

这将使您回到所需的提交

https://i.stack.imgur.com/atW9w.png

git reset --hard

将您的 HEAD “移动”回所需的提交。

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.

注意:(从 Git 2.7 开始)您也可以使用 git rebase --no-autostash。

git revert

“撤消”给定的提交或提交范围。重置命令将“撤消”在给定提交中所做的任何更改。将提交带有撤消补丁的新提交,而原始提交也将保留在历史记录中。

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

此架构说明了哪个命令执行什么操作。
如您所见,reset && checkout 修改 HEAD

https://i.stack.imgur.com/NuThL.png

这是宝🐱‍👤🐱‍👤🐱‍👤

@CodeWizard 你的终端的配色方案是什么?提醒我 ZX Spectrum 颜色,还是您手动设置了?

A
Amit Kaneria

它可以通过多种方式完成。

1) 中止合并

如果您处于错误合并之间(错误地使用错误的分支完成),并且希望避免合并返回到最新的分支,如下所示:

git merge --abort

2) 将 HEAD 重置为远程分支

如果您在远程开发分支上工作,您可以将 HEAD 重置为远程分支上的最后一次提交,如下所示:

git reset --hard origin/develop

3)删除当前分支,并再次从远程存储库中签出

考虑到,您正在本地仓库中开发与远程/开发分支同步的分支,您可以执行以下操作:

git checkout master 
##to delete one branch, you need to be on another branch, otherwise you will fall with the branch :) 

git branch -D develop
git checkout -b develop origin/develop

“1)中止合并”已经足够了。赞成。

当心! git merge --abort "只能在合并导致冲突后运行。git merge --abort 将中止合并过程并尝试重建合并前状态"

git reset --hard origin/develop 这正是我要找的,谢谢!

s
stephjang

您可以使用 git reflog 查找以前的结帐。有时那是你想要回到的一个很好的状态。

具体来说,

$ git reflog
$ git reset --hard HEAD@{0}

谢谢!你节省了我半天的工作时间。但是我无法使用任何命令退出 reflog 模式。

@Katarzyna 使用“q”键退出 reflog

R
Ralph Ritoch

我能够通过一个不涉及查找提交 ID 的命令来解决这个问题。

git reset --hard remotes/origin/HEAD

接受的答案对我不起作用,但这个命令达到了我想要的结果。

确切地!它将您的更改重置为分支的 HEAD!不一一做

对我不起作用。实际上最终将当地分支机构送回一两个月。谢天谢地,这都是本地的,所以我总是可以销毁分支并再次获取它。只是想指出这一点,以防其他人尝试这样做。

@MattPengelly 这种方法在很大程度上没有记录,如果您的分支在合并之前与远程分支同步,则通常可以使用。自从您的分支与远程分支同步以来已经有几个月了?

@MattPengelly 它还取决于 HEAD 指向的分支。我在我的一个项目上使用 gitflow,即使我在开发分支上,遥控器/原点/头指向 origin/master 所以如果我需要撤消合并,我可能需要重置为遥控器/起源/发展

I
Idealmind

如果你还没有提交,你只能使用

$ git checkout -f

它将撤消合并(以及您所做的一切)。

试过这个,它实际上增加了我的本地分支领先的提交数量。

l
leanne

遇到这个问题也希望恢复到匹配原点(即,在原点之前没有提交)。进一步研究,发现有一个 reset 命令可以做到这一点:

git reset --hard @{u}

注意:@{u}origin/master 的简写。 (当然,您需要该远程存储库才能正常工作。)

H
Harsha

最简单的答案是 odinho - Velmont 给出的答案

首先执行git reset --merge ORIG_HEAD

对于那些希望在推送更改后重置的人,请执行此操作(因为这是任何 git reset 合并问题的第一篇文章)

git push origin HEAD --force

这将以一种在拉取后不会再次恢复合并更改的方式重置。

b
blessed

回答“撤消尚未推送的 Git 合并”问题

你可以使用 git reset --hard HEAD~1

考虑以下有 2 个分支 master 和 feature-1 的情况:

$ git log --graph --oneline --all

https://i.imgur.com/Yw9XS1y.png

做 Git 合并

$ git merge feature-1

$ git log --graph --oneline --all

https://i.imgur.com/buVF9GJ.png

撤消 Git 合并

$ git reset --hard HEAD~1

$ git log --graph --oneline --all

https://i.imgur.com/UDIUJ3c.png

M
Matheus Abreu

您只能使用两个命令来恢复合并或通过特定提交重新启动:

git reset --hard commitHash (你应该使用你想要重新启动的提交,例如 44a587491e32eafa1638aca7738) git push origin HEAD --force (将新的本地 master 分支发送到 origin/master)

祝你好运,继续前进!

D
Damien Byrne

只是为了查看一个额外的选项,我一直主要遵循此处描述的分支模型:http://nvie.com/posts/a-successful-git-branching-model/,因此通常与 --no-ff 合并(没有快进)。

我刚刚阅读了这个页面,因为我不小心将一个测试分支而不是我的发布分支与用于部署的 master 合并(网站,master 是实时的)。测试分支合并了另外两个分支,总共大约有六个提交。

因此,要恢复整个提交,我只需要一个 git reset --hard HEAD^ 就可以恢复整个合并。由于合并没有快进,因此合并是一个块,后退一步是“分支未合并”。

P
Peter Mortensen

如果您的合并和相应的提交尚未推送,您可以随时切换到另一个分支,删除原始分支并重新创建它。

例如,我不小心将一个开发分支合并到 master 中并想撤消它。使用以下步骤:

git checkout develop
git branch -D master
git branch -t master origin/master

瞧! Master与origin处于同一阶段,并且您的错误合并状态被擦除。

注意:这不仅会撤消合并,还会撤消自最近一次推送到原点后所做的任何本地提交。

P
Peter Mortensen

如果您想要一个命令行解决方案,我建议您只使用 MBO 的答案。

如果您是新手,您可能会喜欢图形方法:

启动 gitk (从命令行,或在文件浏览器中右键单击,如果有的话)您可以轻松地在那里发现合并提交 - 从顶部开始的第一个节点,有两个父节点按照链接到第一个/左父节点(那个在合并之前的当前分支上,对我来说通常是红色的)在选定的提交上,右键单击“将分支重置到此处”,在那里选择硬重置

p
pyb

策略:创建一个新的分支,一切都很好。

理由:恢复合并很难。有太多的解决方案,这取决于许多因素,例如您是否已提交或推送您的合并,或者自合并后是否有新的提交。此外,您仍然需要对 git 有比较深入的了解才能使这些解决方案适应您的情况。如果你盲目地遵循一些说明,你最终可能会得到一个“空合并”,其中不会合并任何内容,进一步的合并尝试会让 Git 告诉你“已经是最新的”。

解决方案:

假设您要将 dev 合并到 feature-1

找到要接收合并的修订: git log --oneline feature-1 a1b2c3d4 Merge branch 'dev' into 'feature-1' <-- 要撤消的合并 e5f6g7h8 Fix NPE in the Zero Point Module <- - 合并之前的那个,你可能想要这个检查它(回到过去): git checkout e5f6g7h8 从那里创建一个新分支并检查它: git checkout -b feature-1

现在您可以重新开始合并:

合并: git merge dev 修复合并冲突。提交: git commit 对结果满意后,删除旧分支: git branch --delete feature-1

g
gdbdable

只需创建新分支,然后挑选所需的提交给它。

它的保护程序和简单的然后重置在上面的许多答案中描述

我同意这个建议,特别是如果您对列出的 git 命令并不完全满意。这可能会因更多的“辛劳”而变慢,但如果它不会出现太多并且您担心失去工作,那么值得付出努力。

v
vidur punj

如果没有冲突和合并完成,则:

  git reset --hard HEAD~1

如果在进行合并时发生冲突,则 abort 将使您退出最近的合并更改:

git merge --abort
t
tychoish

我认为您可以执行 git rebase -i [hash] [branch_name] ,其中 [hash] 是您想要倒退多远的标识哈希加一(或您想要返回的许多提交),然后删除您在编辑器中提交的行不想要了。保存文件。出口。祈祷。它应该被重绕。您可能必须执行 git reset --hard,但此时应该很好。如果您不想将它们保留在历史记录中,您也可以使用它从堆栈中提取特定提交,但这可能会使您的存储库处于您可能不想要的状态。

只有一个细节,交互式 rebase 允许通过删除行来删除提交,但如果你删除所有内容,rebase 将被简单地中止。这里最简单的技巧是删除除一行之外的所有行,并将最后一行标记为“drop”,以便丢弃所有内容。

L
Luis

最简单的机会,比这里所说的要简单得多:

删除您的本地分支(本地,而不是远程)并再次拉取它。这样,您将撤消主分支上的更改,并且任何人都会受到您不想推送的更改的影响。重新开始。

P
Peter Mortensen

如果您提交了合并:

git reset HEAD~1
# Make sure what you are reverting is in fact the merge files
git add .
git reset --hard
k
kenorb

首先,确保你已经承诺了一切。然后将您的存储库重置为之前的工作状态: $ git reset f836e4c1fa51524658b9f026eb5efa24afaf3a36 或使用 --hard (这将删除所有本地的,未提交的更改!): $ git reset f836e4c1fa51524658b9f026eb5efa24afaf3a36 --hard 使用在错误合并提交之前存在的哈希. Check which commits you'd like to re-commit on the top of the previous correct version by: $ git log 4c3e23f529b581c3cbe95350e84e66e3cb05704f commit 4c3e23f529b581c3cbe95350e84e66e3cb05704f ... commit 16b373a96b0a353f7454b141f7aa6f548c979d0a ... Apply your right commits on the top of the right version of your repository by : By using cherry-pick (the changes introduced by some existing commits) git cherry-pick ec59ab844cf504e462f011c8cc7e5667ebb2e9c7 Or by cherry-picking the range of commits by: First checking the right changes before merging them: git diff 5216b24822ea1c48069f648449997879bb49c070..4c3e23f529b581c3cbe95350e84e66e3cb05704f First checking the right在合并它们之前进行更改: git cherry-pick 5216b24822ea1c48069f648449997879bb49c070..4c3e23f529b581c3cbe95350e84e66e3cb05704f 其中这是您提交的正确提交的范围(不包括错误提交的合并)。