撤销一个尚未推送的 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个回答

43

好的,其他人给我的答案接近正确,但是没用。这是我做的。

做这个...

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

20
你本可以使用 HEAD^^^^ - hasen
18
甚至可能要重置到 origin/master ;) - hasen

40
你需要改变的是git HEAD,而不是你自己的头部。在回答之前,让我们先了解一下什么是HEAD。
首先,什么是HEAD?HEAD是当前分支上最新提交的引用。在任何给定时间,只能有一个HEAD(不包括git worktree)。
HEAD的内容存储在.git/HEAD中,并包含当前提交的40字节SHA-1值。

分离头指针

如果您不在最新的提交上 - 意味着 HEAD 指向历史中的之前的提交,这被称为分离头指针

enter image description here

在命令行中,它看起来是这样的 - 使用SHA-1代替分支名称,因为HEAD没有指向当前分支的末端。

enter image description here

enter image description here

如何从分离的HEAD中恢复:

有几种选项:

git checkout

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@{...}

这将使您回到所需的提交

enter image description here


git reset --hard <commit_id>

将您的 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 <sha-1>

撤销给定的提交或提交范围。
重置命令将“撤消”在给定提交中所做的任何更改。
一个包含撤消补丁的新提交将被提交,同时原始提交也将保留在历史记录中。

# 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

enter image description here


2
这是宝藏。 - Jawand Singh
1
@CodeWizard 你的终端颜色方案是什么?是 ZX Spectrum 的颜色吗,还是你手动设置的? - alexey_the_cat

29

有多种方法可以完成。

1) 中止合并

如果您在错误地与错误分支进行合并的过程中,并且想要避免合并回到以下最新分支:

git merge --abort

2)将HEAD重置为远程分支

如果你正在从远程开发分支工作,可以按照以下方式将HEAD重置为远程分支上的最后一次提交:

git reset --hard origin/develop

3)删除当前分支,并从远程仓库再次检出

假设您正在本地存储库上开发分支,该分支与远程/develop分支同步,您可以按以下方式执行:

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) 中止合并" 已经足够了。点赞。 - CodeToLife
2
小心!git merge --abort“只能在合并导致冲突后运行。git merge --abort将中止合并过程并尝试重建合并前的状态。” - Pedro García Medina
git reset --hard origin/develop 这就是我要找的,谢谢! - Mattisdada

24

您可以使用git reflog命令查找之前的检出状态。有时这是您想要返回的良好状态。

具体而言,

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

1
谢谢!你帮我省了半天的工作时间。但是我无法通过任何命令退出 reflog 模式。 - Katarzyna
1
@Katarzyna 使用“q”键从reflog中退出。 - Amjed Baig

23
如果没有冲突并且合并完成,则:
git reset --hard HEAD~1

如果在合并过程中发生冲突,选择中止将会使你退出最近的合并更改。
git merge --abort

如果您想要回滚到特定的提交 ID。

git reset --hard <commit-id>

21

我能够用一条命令解决这个问题,而无需查找提交ID。

git reset --hard remotes/origin/HEAD

接受的答案对我没用,但这个命令实现了我想要的结果。


没错!它会将你的更改重置为分支的HEAD!不需要逐个操作。 - Carlos Zinato
对我来说没用。实际上让本地分支倒退了一个月或两个月。幸运的是这都是本地的,所以我可以随时销毁该分支并重新获取它。只是想指出这一点,以防其他人尝试这样做。 - Matt Pengelly
@MattPengelly 这个方法很少有文档记录,通常只有在你的分支与远程分支同步之后再进行合并才能生效。你的分支跟远程分支同步已经有几个月了吗? - Ralph Ritoch
@MattPengelly 这还取决于HEAD指向哪个分支。我在其中一个项目上使用gitflow,即使我在develop分支上,remotes/origin/HEAD也被指向到origin/master,因此如果我需要撤消合并,我可能需要重置为remotes/ origin/develop. - Ralph Ritoch

16

如果您尚未提交,您只能使用

$ git checkout -f

这将撤销合并操作(以及您所做的一切)。


尝试过这个方法后,我的本地分支超前的提交次数实际上增加了。 - barclay

16

在查找如何回滚到源代码(即不比源代码多提交任何修改)时,我发现了一个适用于此情况的reset命令:

git reset --hard @{u}

注意:@{u}origin/master的缩写。(当然,您需要拥有远程版本库才能使此命令生效。)


15

回答问题“撤销一个未推送的Git合并”:

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

考虑以下情况,其中有2个分支 master feature-1


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

执行Git合并操作

$ git merge feature-1

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

撤销Git合并


$ git reset --hard HEAD~1

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


12

您可以使用两个命令来撤消合并或从特定提交重新开始:

  1. git reset --hard commitHash(您应该使用要重新开始的提交,例如44a587491e32eafa1638aca7738)
  2. git push origin HEAD --force(将新的本地主分支发送到origin/master)

祝你好运并继续前进!


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