撤消尚未推送的Git合并


3937

在我的master分支中,我在git merge some-other-branch本地进行操作,但从未将更改推送到原始master。我不是要合并,所以我想撤消它。git status合并后进行一次操作时,我收到此消息:

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

根据我发现的一些说明,我尝试运行

git revert HEAD -m 1

但是现在我收到以下消息git status

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

我不希望我的分支领先于任何数量的提交。我如何回到这一点?


3
如果您需要保存历史记录,换句话说,就是有人曾经从您那里拉过钱,或者您已经将它推到某个地方,请使用下面的Yuri Ushakov解答中的解决方案!
塞德里克

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



3
这是来自Github的大量资源:如何使用Git撤消(几乎)任何操作
jasonleonhard

Answers:


4450

通过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

- 合并

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


129
我认为这不会(永远吗?)-“合并前一个”将是从另一个分支合并的最新提交-不会是当前分支上的最新提交。对?(这可能只是git log默认情况下选择显示的结果–也许有不同的输出git loggit reflog可以用于此)
John Bachir

6
我认为这可能取决于您是否要合并。
Marcin Gil

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

7
@JohnBachir:只要“合并”不是真正的快进,它就会导致一个新的提交位于日志的顶部,并且该提交有两个父级(如果您是章鱼,则两个以上)合并)。如果删除此一个合并提交,那么从合并中传入的所有较旧的提交也将消失。为了安全起见,重置后git会告诉您新的头在哪里:“ HEAD现在位于88a04de <commit message>”。我一直在研究以确保最终达到预期的效果。我的项目使用标准的分支命名方案,以使事情记忆深刻。
Mark E. Haase

44
我发现有用的是查看“ git reflog”并查找我在master中所做的最后一次提交。然后做git reset --hard <commit_sha>
马克斯·威廉斯

1453

假设您的本地主人不在原产地/主人之前,那么您应该可以

git reset --hard origin/master

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


71
@Carter实际上不是最佳答案。在某些合并之前,起源/母版可能比合并之前更早于本地母版,在这种情况下,这可能无法获得预期的结果
Dhruva Sagar

15
@ dhruva-sagar是的,但是只要git不会说您落后于您,并且您不进行抓取,就可以了。
开尔文

3
谢谢!当(且仅当)您拥有远程存储库时,这才是完美的。
tomc

2
不,它不是此问题的理想选择,请参阅“假定”子句。MBO的答案实际上涵盖了这种情况,以及合并不是唯一本地提交的情况。
inger

2
再一次,也许这个警告应该进入答案本身:始终避免重写git历史!
cregox

1174

第4章在Git的书,并在原来的职位由Linus Torvalds

撤消已经推送的合并:

git revert -m 1 commit_hash

如果您再次提交分支,请确保还原还原,如Linus所说。


10
@perfectionist同意:)有点希望有一种方法可以将这个答案转移到另一个问题上-(也许有吗?)
mikermcneil 2013年

有关还原的更多信息:链接
assaqqaf 2014年

1
为了确信此还原已成功,您可以执行git diff hash1 hash2 hash2,其中hash1是已提交的还原,而hash2是您试图返回其状态的旧提交。没有输出==成功!通过多次执行此操作,我可以还原多个提交,从还原最近的合并并向后工作开始。git diff向我表明我最终处于想要的状态。
罗伯特·辛顿

6
请注意,这并不能真正解决楼主的问题。原始海报被使用git revert -m 1 <commit>。问题在于这样做并不能消除他(并且尚未推动)的意外合并。涉及硬重置的其他答案更适合于原始海报的问题。

这是来自Github的大量资源:如何使用Git撤消(几乎)任何操作
jasonleonhard

986

最简单的命令丢失了很奇怪。大多数答案都有效,但是撤消您刚才所做的合并,这是简单安全的方法

git reset --merge ORIG_HEAD

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

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


17
如果自git reset --merge ORIG_HEAD那以后弄脏了工作树,请保留这些更改。
yingted 2013年

1
这是唯一正确的答案(我并不是说这是最佳答案-请注意区别)。假设,在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 Manjunath

@Manu该--merge选项实际上并不会删除合并,您可以使用--hard它也可以正常工作。这里是参考ORIG_HEAD,它是在您合并到此时的位置之前设置的。:)
odinho-Velmont '16

@yingted你的意思是“如果自那以后弄脏了工作树,git reset --merge ORIG_HEAD将保留这些更改。” 您的意思是合并后更改文件吗?无论如何,我做了合并,然后做了一些解决冲突的事情。但是后来我想重置合并,并按照此答案的指示进行操作。一切都很好,并没有保留合并后所做的更改。我的本地仓库与合并之前的职位相似。
Samitha Chathuranga

git reset --hard ORIG_HEAD命令对我来说非常有效- git merge在尝试撤消本地操作后,我没有对存储库进行任何其他更改,这可能有所帮助。该命令只是将存储库的状态重置为合并之前的状态。多谢小费!
bluebinary

391

使用较新的Git版本,如果您尚未提交合并,但是发生合并冲突,则可以执行以下操作:

git merge --abort

来自man git merge

[this]仅在合并导致冲突后才能运行。git merge --abort将中止合并过程,并尝试重建合并前状态。


8
他的合并已提交但尚未推送(请参见标题),他已经合并,仅当他仍处于合并中间时,您的命令才有效
JBoy 19'Mar

135

您应该重置为上一次提交。这应该工作:

git reset --hard HEAD^

甚至HEAD^^要还原该还原提交。如果您不确定应该返回多少步骤,则始终可以提供完整的SHA参考。

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


5
最好的答案恕我直言,它结合了OP自己的一个(假设仅还原了第一步,这在Q中就是这种情况),以及randomguy3的快捷方式(当“您的主分支没有任何本地更改时起作用”) “)
歌手歌手

4
您的评论者@Inger和@Konstantin,为什么?创建我的答案后,您来过这里,这更正确。仅将HEAD抬高一个步骤通常是错误的,并且您实际上必须算出需要走多远。Git已经ORIG_HEAD为您设置好了,为什么不使用它呢?
奥迪尼奥-费尔蒙特2014年

还会重置本地更改吗?#请更新。
CoDe

这对我来说非常有效,像这样复位头部比在这里给出一半的答案有意义得多。
VardaElentári16年

HEAD ^是否等于HEAD之前的提交?和^^是两次提交吗?猜猜这不适用于快速前进合并吗?
马库斯·莱昂

87

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

51

使用现代Git,您可以:

git merge --abort

较旧的语法:

git reset --merge

老套:

git reset --hard

但实际上,值得注意的是,它git merge --abort仅等同于存在的git reset --merge给定条件MERGE_HEAD。可以在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在日常工作中功能更强大,更有用,所以这就是我一直使用的功能。


对我来说很棒。其他每篇文章都说这是如此复杂,但这确实符合预期。我想它之所以起作用是因为存在冲突,而这并不能完全回答最初的问题。
杰里米(Jeremy)

这个答案不关注OP的情况,而忽略了重要的上下文。
本·惠勒

37

好的,这里其他人给我的答案很接近,但是没有用。这就是我所做的。

这样做...

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

19
否则您可能会用过HEAD^^^^
Hasen

17
也许甚至重置为origin/master;)
hasen

23

您可以git reflog用来查找先前的结帐。有时候,这是您想要返回的良好状态。

具体来说,

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

1
谢谢!您节省了我半天的时间。但是我无法使用任何命令退出reflog模式。
Katarzyna 2015年

1
@Katarzyna使用“ q”键退出reflog
Amjed Baig

21

如果您正在合并中,可以随时中止合并 git merge --abort


2
谢谢兄弟,我正要做那个可怕的东西正确的答案。幸运的是我向下滚动。我只想删除合并头
Nyuu

15

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

git reset --hard remotes/origin/HEAD

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


究竟!它将您的更改重置为分支的HEAD!不一一做
Carlos Zinato

没有为我工作。实际上最终将本地分支机构寄回了一个月或两个月。值得庆幸的是,这些都是本地的,所以我总是可以销毁分支并再次获取它。只是想指出,以防其他人尝试这样做。
Matt Pengelly

@MattPengelly此方法在很大程度上没有记录,通常在合并之前分支与远程分支同步时才有效。您的分支与远程分支同步以来已经有几个月了吗?
拉尔夫·里托奇

@MattPengelly还取决于HEAD指向哪个分支。我在其中一个项目上使用gitflow,即使我在开发分支上,也将remotes / origin / HEAD指向origin / master,因此,如果我需要撤消合并,则可能需要重置为remotes / origin / develop
拉尔夫·里托奇

14

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

$ git checkout -f

它将撤消合并(以及您所做的所有操作)。


对此进行了尝试,实际上它增加了我的本地分支在前面的提交次数。
巴克莱2015年

14

遇到此问题时,也希望还原为匹配原点(即,在原点之前没有提交)。进一步研究,发现有一个reset确切的命令:

git reset --hard @{u}

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


14

您必须更改您的HEAD,当然不是您的,而是git HEAD...。

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

First of all what is HEAD?

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

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


detached HEAD

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

在此处输入图片说明

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

在此处输入图片说明

在此处输入图片说明

有关如何从分离的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@{...}

这将使您回到所需的提交

在此处输入图片说明


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>

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

# 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

在此处输入图片说明


这是珍宝🐱‍👤🐱‍👤🐱‍👤
Jawand Singh,

12

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

先做 git reset --merge ORIG_HEAD

对于那些希望在更改后进行重置的人,请执行此操作(因为这是所有git reset merge问题看到的第一篇文章)

git push origin HEAD --force

这将以某种方式重置,使您在拉动后不会再次获得合并的更改。


10

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

我刚刚读了此页面,因为我不小心将测试分支而不是发布分支与master合并以进行部署(网站,master是实时的)。测试分支还有另外两个分支与之合并,总计约六次提交。

因此,要还原整个提交,我只需要一个即可git reset --hard HEAD^,它还原了整个合并。由于合并不快进,因此合并是一个块,而后退一步是“分支未合并”。


10

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

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

祝你好运,继续前进!


10

可以通过多种方式完成。

1)中止合并

如果您处于错误的合并之间(错误地用了错误的分支),并且想要避免合并,请如下所示最新返回分支:

git merge --abort

2)将HEAD重置为远程分支

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

git reset --hard origin/develop

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

考虑到您正在本地仓库中的develop分支上,该分支与远程/ 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

1
小心!混帐合并--abort“只能合并后运行造成了冲突git的合并--abort将中止合并进程,并试图重建预合并状态。”
佩德罗·加西亚麦地那

8

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

例如,我无意中将一个开发分支合并到master中,并希望撤消该操作。使用以下步骤:

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

瞧!母版与原点处于同一阶段,并且您误合并的状态将被清除。


1
注意:这不仅会撤消合并,还会撤消自最新推送到原始位置以来进行的任何本地提交。
马丁·海默斯

4

如果您需要命令行解决方案,我建议您只听MBO的回答。

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

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

4

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

理由:恢复合并非常困难。解决方案太多,取决于许多因素,例如您是否提交或推送了合并,或者自合并以来是否有新的提交。同样,您仍然需要对git有相对深入的了解,以使这些解决方案适合您的情况。如果您盲目地遵循一些说明,则可能会得到“空合并”,其中什么都不会合并,并且进一步的合并尝试将使Git告诉您“已经更新”。

解:

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

  1. 找到您要接收合并的修订:

    git log --oneline feature-1
    a1b2c3d4 Merge branch 'dev' into 'feature-1' <-- the merge you want to undo
    e5f6g7h8 Fix NPE in the Zero Point Module <-- the one before the merge, you probably want this one
    
  2. 签出(回到过去):

    git checkout e5f6g7h8
    
  3. 从那里创建一个新分支并签出:

    git checkout -b feature-1
    

现在,您可以重新启动合并:

  1. 合并: git merge dev

  2. 解决您的合并冲突。

  3. 承诺: git commit

  4. 对结果满意后,请删除旧分支: git branch --delete feature-1


2

只需创建新分支,然后选择所需的提交即可。

上面许多答案中所述的保护程序和更简单的重置方法


1
我同意这个建议,尤其是如果您对所列的git命令不完全满意的话。随着“辛劳”的增加,这可能会变慢,但是如果它没有太多发挥,并且您担心失去工作,那是值得的。
格雷格

1

我认为您可以在git rebase -i [hash] [branch_name] 哪里找到[hash]要回溯的标记哈希,然后再回溯再加上一个(或者无论您想回溯多少次提交),然后在编辑器中删除不需要的行。保存文件。出口。祈祷。应该倒退。您可能需要执行git reset --hard,但此时应该很好。如果您不想将特定的提交保留在历史记录中,则还可以使用它从堆栈中拉出特定的提交,但这会使您的存储库处于您可能不希望的状态。


1

如果您提交了合并:

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

1
  1. 首先,请确保您已完成所有工作。

  2. 然后将您的存储库重置为先前的工作状态:

    $ git reset f836e4c1fa51524658b9f026eb5efa24afaf3a36
    

    或使用--hard这将删除所有本地未提交的更改!):

    $ git reset f836e4c1fa51524658b9f026eb5efa24afaf3a36 --hard
    

    使用错误合并的提交之前的哈希值。

  3. 通过以下方法在先前的正确版本上检查您要重新提交的提交:

    $ git log 4c3e23f529b581c3cbe95350e84e66e3cb05704f
    
    commit 4c3e23f529b581c3cbe95350e84e66e3cb05704f
    
    ...
    
    commit 16b373a96b0a353f7454b141f7aa6f548c979d0a
    
    ...
    
  4. 通过以下方式将正确的提交应用于正确版本的存储库顶部:

    • 通过使用cherry-pick(某些现有提交所引入的更改)

          git cherry-pick ec59ab844cf504e462f011c8cc7e5667ebb2e9c7
      
    • 或通过以下方式挑选提交范围:

      • 在合并之前,请先检查正确的更改:

        git diff 5216b24822ea1c48069f648449997879bb49c070..4c3e23f529b581c3cbe95350e84e66e3cb05704f
        
      • 在合并之前,请先检查正确的更改:

        git cherry-pick 5216b24822ea1c48069f648449997879bb49c070..4c3e23f529b581c3cbe95350e84e66e3cb05704f
        

        这是您已提交的正确提交的范围(不包括错误提交的合并)。



0

如果您发现您需要在合并后立即回复,你的合并尝试后没有做别的,你可以发出此命令: git reset --hard HEAD@{1}

本质上,您的合并sha将指向HEAD@{0}合并之后是否未执行其他任何操作,因此HEAD@{1}将指向合并之前的前一点。


0

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

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


0

在这种情况下,您将要使用重置分支git reset --hard <branch_name>。如果要在重置之前保存更改,请确保创建一个新分支和git checkout <branch_name>

您也可以使用将状态重置为特定的提交git reset --hard <commit_id>

如果更改已被推送,则可以使用git revert <branch_name>。一定要检查出在其他情况下如何使用git revert和git checkout

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.