如何撤销 git reset --hard HEAD~1?

1610

以下命令引起的更改是否可以被撤销?如果可以,该如何操作?

git reset --hard HEAD~1

14
我已经写了一份完整的指南,可以帮助你使用git找回丢失的提交记录,甚至还配有插图 :-) [点击这里查看][fixLink]。[fixLink]: http://www.programblings.com/2008/06/07/the-illustrated-guide-to-recovering-lost-commits-with-git/ - webmat
38
--hard 会丢弃未提交的更改。由于这些更改未被 Git 跟踪,因此无法通过 Git 恢复它们。 - Zaz
1
这是一篇很棒的文章,可以帮助您恢复文件。 - Jan Swart
2
这是一份来自Github的绝佳资源:如何使用Git撤销(几乎)所有操作 - jasonleonhard
我也遇到过这种情况,吸取了教训:不要使用--hard。相反,只需使用git reset开始 - 如果出现错误,它会提醒你--hard的实际含义... - undefined
显示剩余7条评论
20个回答

2383

Pat Notz是正确的。只要提交记录在几天之内就可以找回。Git只有在约一个月后才进行垃圾回收,除非您明确告诉它删除更新的blob。

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

从示例中可以看出,在进行硬重置后,文件2被删除了,但是当我通过reflog进行重置时,它又被放回原处。


288
你可以使用"git reset --hard HEAD@{1}",无需使用SHA1。在大多数情况下,使用"git reset --hard ORIG_HEAD"就足够了。 - Jakub Narębski
50
使用git log -g比git reflog更加友好,可以更轻松地查看引用日志。 - Dan Moulding
92
这件事有一个非常重要的限制条件,那就是 "--hard" 部分。使用 "--hard" 会彻底清除您本地未提交的更改,这些更改无法通过此方式恢复(因为它们还没有被提交到任何地方)。我认为这方面并没有什么可做的 :( - Michael Anderson
8
知道吗,你可以在执行 --hard 重置前将本地更改藏起来,然后再弹出它们,这样就不会失去任何东西!Git 真是太棒了。 - Lethjakman
8
我更喜欢使用git reflog而不是git log -g,因为前者可以在一行中获得所有信息,包括SHA1,HEAD信息和提交消息,使阅读变得更加容易。 - Snowcrash
显示剩余10条评论

533

您需要做的是指定要恢复到的提交的sha1。 您可以通过检查reflog(git reflog)来获取sha1,然后执行以下操作:

git reset --hard <sha1 of desired commit>

但不要等太久... 几周后,Git最终会将该提交视为未引用并删除所有blob。


9
几分钟前的我提醒自己:这将重置所有更改。但不会影响未跟踪的文件。请注意。 - ᴍᴇʜᴏᴠ

268

答案已经在上面详细的回复中给出,你可以简单地执行以下操作:

$> git reset --hard HEAD@{1}

(查看git reflog show的输出)


29
请注意,如果您在重置后进行了其他仓库更改,则这不是解决方案。在运行任何操作之前一定要查看reflog。 - forresthopkinsa
1
当你意识到由于错误操作而重置了一个分支并开始惊慌时,这很棒。 - Sebastián Vansteenkiste
惊人的解决方案。只需使用“git reflog”查看您想要撤回的提交,然后执行上述解决方案即可。 - Thieu Nguyen
你刚刚节省了一整天的辛勤工作。谢谢你。 - undefined

173
据我所知,--hard会丢弃未提交的更改,因为这些更改没有被Git跟踪。但是您可以撤销已经被丢弃的提交

据我所知,--hard会丢弃未提交的更改,因为这些更改没有被Git跟踪。但是您可以撤销已经被丢弃的提交

$ git reflog

将列出:

b0d059c HEAD@{0}: reset: moving to HEAD~1
4bac331 HEAD@{1}: commit: added level introduction....
....

其中 4bac331 是被丢弃的提交。

现在只需将 HEAD 移动到该提交:

$ git reset --hard 4bac331

151

如果 Git 还没进行垃圾回收,就有可能恢复它。

使用 fsck 获取悬空提交的概览:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

使用 rebase 恢复悬空提交:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

2
详细的解释可以在这里找到:https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c。救命的东西。 - tuan.dinh

82
如果你非常幸运,就像我一样,你可以返回到文本编辑器并点击“撤消”。

我知道这不是一个正式的答案,但它为我节省了半天的工作时间,希望也能为其他人节省时间!


4
这其实是一个非常好的建议,帮我省了很多次的麻烦 ;) 而且比在Git中做任何事情要简单得多... - severin
15
这是在硬重置之后恢复文件中未暂存更改的唯一方法。对我也很有帮助 ;) - Czarek Tomczak
5
作为一个额外的提示,一些 IDE (例如 eclipse)还会保存最近打开的文件记录。这样,即使在编辑器关闭后,您也可能能够恢复更早的更改。这对我来说非常有帮助。 - martin
今天这个救了我。虽然没有太大的改变,但在进行了很多实验后,我还是感到很累,直到我把一件事情搞砸了。我想知道是否有“git”的方法来解决这个问题!Reflog只能回到提交的地方,虽然有点遗憾。而且我正在使用 Eclipse,如果关闭 IDE,它不会保存历史记录。但对于一个特定的文件,我打开了那个 IDE,那是我本地更改中要完成的主要文件。 :P - master_dodo
2
不错,好建议。在 IntelliJ 家族(例如 PhpStorm、WebStorm、RubyMine、PyCharm)中,您可以右键单击文件名,选择“本地历史记录 > 显示历史记录”,也可以通过这种方式恢复。假设 IDE 已经打开并跟踪本地更改。 - Elijah Lynn
显示剩余3条评论

66

在大多数情况下,是的。

取决于你运行该命令时仓库所处的状态,git reset --hard 的影响范围可能从微不足道的撤销到基本不可能。

下面我列出了一系列不同的可能情况,以及如何从中恢复。

我的所有更改都已提交,但现在提交记录被删除了!

通常情况下,当你带有参数运行 git reset,比如 git reset --hard HEAD~ 时会发生这种情况。不用担心,这很容易恢复!

如果你只是运行了 git reset,并且此后没有做任何其他操作,你可以使用以下一行命令回到之前的状态:

git reset --hard @{1}

这将重置您当前的分支状态,无论它在上次修改之前处于什么状态(在您的情况下,分支的最近修改是您试图撤销的硬重置)。

但是,如果您自重置以来对分支进行了其他修改,则上面的一行代码将不起作用。相反,您应该运行git reflog <branchname>以查看所有最近对您的分支所做的更改(包括重置)。该列表将类似于以下内容:

7c169bd master@{0}: reset: moving to HEAD~
3ae5027 master@{1}: commit: Changed file2
7c169bd master@{2}: commit: Some change
5eb37ca master@{3}: commit (initial): Initial commit

在列表中找到您想要“撤消”的操作。在上面的示例中,它将是第一行,即写着“reset: moving to HEAD~”的那一行。然后复制该操作之前的提交表示形式(下面)。在我们的情况下,这将是 master@{1}(或3ae5027,它们都代表相同的提交),然后运行git reset --hard <commit> 将当前分支重置回该提交。

我使用git add暂存了更改,但从未提交。现在我的更改不见了!

这有点棘手。Git确实有您添加的文件的副本,但由于这些副本从未被绑定到任何特定提交,因此您无法一次性恢复更改。相反,您必须手动在Git的数据库中找到各个文件并将其还原。您可以使用git fsck来执行此操作。

关于此详细信息,请参见取消已暂存但未提交的文件的git reset --hard.

我对工作目录中的文件进行了更改,但从未使用git add暂存,也从未提交。现在我的更改不见了!

哎呀。我很抱歉告诉您这个,但您可能没有办法恢复了。 Git不会存储您未添加或提交的更改,并根据git reset文档

--hard

重置索引和工作树。自从<commit>以来在工作树中对已跟踪文件所做的任何更改都将被丢弃。

您可能可以使用某种磁盘恢复实用程序或专业数据恢复服务来恢复更改,但此时可能比它真正值得的麻烦要大。


1
一行代码对我有用,谢谢,但我想知道那个“@{1}”具体是做什么的。 - Stan Bashtavenko
1
@StanB 这里有文档:https://git-scm.com/docs/git-rev-parse 基本上它是指当前分支上第一个reflog条目。 - Ajedi32
感谢您覆盖了所有情况。我还没有提交或添加我的。 - xdhmoore

44

Example of IRL case:

$ git fsck --lost-found

Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
dangling blob 025cab9725ccc00fbd7202da543f556c146cb119
dangling blob 84e9af799c2f5f08fb50874e5be7fb5cb7aa7c1b
dangling blob 85f4d1a289e094012819d9732f017c7805ee85b4
dangling blob 8f654d1cd425da7389d12c17dd2d88d318496d98
dangling blob 9183b84bbd292dcc238ca546dab896e073432933
dangling blob 1448ee51d0ea16f259371b32a557b60f908d15ee
dangling blob 95372cef6148d980ab1d7539ee6fbb44f5e87e22
dangling blob 9b3bf9fb1ee82c6d6d5ec9149e38fe53d4151fbd
dangling blob 2b21002ca449a9e30dbb87e535fbd4e65bac18f7
dangling blob 2fff2f8e4ea6408ac84a8560477aa00583002e66
dangling blob 333e76340b59a944456b4befd0e007c2e23ab37b
dangling blob b87163c8def315d40721e592f15c2192a33816bb
dangling blob c22aafb90358f6bf22577d1ae077ad89d9eea0a7
dangling blob c6ef78dd64c886e9c9895e2fc4556e69e4fbb133
dangling blob 4a71f9ff8262701171d42559a283c751fea6a201
dangling blob 6b762d368f44ddd441e5b8eae6a7b611335b49a2
dangling blob 724d23914b48443b19eada79c3eb1813c3c67fed
dangling blob 749ffc9a412e7584245af5106e78167b9480a27b
dangling commit f6ce1a403399772d4146d306d5763f3f5715cb5a    <- it's this one

$ git show f6ce1a403399772d4146d306d5763f3f5715cb5a

commit f6ce1a403399772d4146d306d5763f3f5715cb5a
Author: Stian Gudmundsen Høiland <stian@Stians-Mac-mini.local>
Date:   Wed Aug 15 08:41:30 2012 +0200

    *MY COMMIT MESSAGE IS DISPLAYED HERE*

diff --git a/Some.file b/Some.file
new file mode 100644
index 0000000..15baeba
--- /dev/null
+++ b/Some.file
*THE WHOLE COMMIT IS DISPLAYED HERE*

$ git rebase f6ce1a403399772d4146d306d5763f3f5715cb5a

将当前分支的提交沿着指定的提交(f6ce1a403399772d4146d306d5763f3f5715cb5a)进行变基操作。
First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.

2
“悬挂的 blob” 听起来像是 AD&D 游戏里的怪物! - Stas Bichenko
谢谢@Stian的解释!我想为其他看到这个答案的人补充一点,如果你有多个“悬挂”的提交,那么不确定你是否要在最后一行上进行变基 :) - JimiSweden

35

git reflog

  • 在列表中找到您的提交sha,然后将其复制并粘贴到此命令中:

git cherry-pick <the sha>


1
git-cherry-pick - 应用一些现有提交引入的更改。我认为在这种情况下它很简单且非常有帮助。 - Amitesh Bharti

25

如果您正在使用JetBrains IDE(任何基于IntelliJ的IDE),您可以通过其“本地历史记录”功能恢复未提交的更改。

在您的文件树中右键单击顶级目录,找到上下文菜单中的“本地历史记录”,并选择“显示历史记录”。这将打开一个视图,在其中可以找到您最近的编辑。一旦您找到要返回的修订版本,请右键单击它并单击“还原”。


这个功能对于那些在 Git 上比较马虎(像我一样)的人来说是救命稻草。 - Dimitris Paraschakis

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