如何撤消git reset --hard HEAD〜1?


1154

是否可以撤消以下命令引起的更改?如果是这样,怎么办?

git reset --hard HEAD~1

8
我已经写了一个完整的指南来恢复任何丢失的git提交。:它甚至有插图:-) [看吧] [fixLink] [fixLink] programblings.com/2008/06/07/...
webmat

12
--hard放弃未提交的更改。由于这些不是git跟踪的,因此无法通过git恢复它们。
Zaz


1
是一篇很棒的文章,可以帮助您恢复文件。
Jan Swart

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

Answers:


1770

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

您可以在示例中看到,由于硬重置而删除了file2,但是当我通过reflog重置时,file2已放回原位。


222
您可以使用“ git reset --hard HEAD @ {1}”,而无需使用SHA1。在大多数情况下,使用“ git reset --hard ORIG_HEAD”就足够了。
2009年

39
git log -g与相比,查看reflog可能是更好的方法git reflog
Dan Moulding

60
有一个非常重要的警告说明,那就是“-硬”部分。--hard吹走了您本地未提交的更改。而且您无法像这样将它们找回来(因为它们尚未在任何地方提交)。我相信您无能为力:(
Michael Anderson

3
^如此一来,您知道可以在重设之前硬存储本地更改,然后将其弹出并不会丢失任何内容!要爱git。
Lethjakman 2014年

5
我喜欢git refloggit log -g简单的,因为你在一行上的所有信息与SHA1,HEAD信息,并提交所有排队的消息。更容易阅读。
Snowcrash 2014年

369

您要做的是指定要还原到的提交的sha1。您可以通过检查reflog(git reflog)来获取sha1 ,然后执行

git reset --hard <sha1 of desired commit>

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


2
几分钟前警告自己:这将重置所有修改。它不会触摸未跟踪的文件。
aexl

174

答案隐藏在上面的详细响应中,您只需执行以下操作即可:

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

(请参见git reflog show的输出)


17
请注意,如果自重置后进行了其他任何回购更改,则这不是解决方案。运行任何东西之前,请务必查看reflog。
forresthopkinsa

2
不小心重置了我的存储库,以为我的工作永远丢失了。这个答案救了我一天。

哇!你真的救了我这一天=)
jfcogato

大。真的有帮助。
Prashant Biradar

116

如果Git尚未进行垃圾收集,则可以恢复它。

使用以下内容获取悬空提交的概述fsck

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

使用rebase恢复悬空的提交:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

1
那真是太棒了,挽救了生命。
Mohhamad Hasham

可以在以下位置找到详细说明:medium.com/@CarrieGuss/…。救生用品。
tuan.dinh

49

如果像我一样真的很幸运,可以返回文本编辑器并单击“撤消”。

我知道这并不是一个正确的答案,但是它为我节省了半天的工作,因此希望对其他人也能做到!


3
这实际上是一个非常好的技巧,为我节省了很多时间;)它的方式比在git中做任何事情都更简单...
severin 2012年

3
并感谢您在这个世界上的一切恩惠。谢谢。谢谢。谢谢。
2012年

9
这是在硬重置后恢复文件中未暂存的更改的唯一方法。也救了我;)
Czarek Tomczak

4
作为附加提示,某些日食IDE也保存了最近的文件历史记录。这样,您甚至可以在关闭编辑器后恢复较旧的更改。这为我创造了奇迹。
马丁

1
上帝保佑你克里斯
亚当·怀特

44

据我所知,--hard将丢弃未提交的更改。由于这些不是git跟踪的。但是您可以撤消该操作discarded commit

$ git reflog

将列出:

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

这里4bac331discarded commit

现在只需将头部移至该提交::

$ git reset --hard 4bac331

34

IRL案例示例:

$ 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

First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.

2
晃来晃去的Blob听起来像是AD&D怪物!
sbichenko 2014年

谢谢@Stian好解释!我想为其他人找到此答案,如果您有多个“悬空”提交,则不确定您是否要在最后一行进行重新设置:)
JimiSweden

git show保存了我的一些文件,非常感谢老兄!
威廉

32

在大多数情况下,是的。

取决于运行命令时存储库所处的状态,的影响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

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

我使用进行了更改git add,但从未承诺。现在我的更改都消失了!

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

有关此操作的详细信息,请参见暂存区域中的未提交文件的撤消git reset --hard

我对工作目录中从未执行过的文件进行了更改git add,也从未提交过这些文件。现在我的更改都消失了!

哦哦 我不愿意告诉您这一点,但您可能不走运。git不会存储您未添加或未提交的更改,并且根据文档进行存储git reset

- 硬

重置索引和工作树。由于对工作树中跟踪文件的任何更改,<commit>都将被丢弃。

可能可以使用某种磁盘恢复实用程序或专业的数据恢复服务来恢复您的更改,但是在这一点上,麻烦可能比其价值更大。


1
一内衬为我工作,谢谢你,但我只是想知道究竟是什么是“@ {1}”不..
斯坦Bashtavenko

1
@StanB文档在这里:git-scm.com/docs/git-rev-parse基本上它指的是当前分支上的第一个reflog条目。
Ajedi32

感谢您涵盖所有情况。我没有承诺或添加我的。
xdhmoore

21

如果尚未对存储库进行垃圾回收(例如,使用git repack -dgit gc,但是请注意垃圾回收也可以自动进行),则您的提交仍然存在–不再可以通过HEAD进行访问。

您可以通过查看的输出来尝试查找提交git fsck --lost-found

较新版本的Git具有称为“ reflog”的内容,它是对ref所做的所有更改(与对存储库内容所做的更改相反)的日志。因此,例如,每次切换HEAD(即每次执行a git checkout切换分支)都会被记录。而且,当然,您git reset还操纵了HEAD,因此它也已记录下来。您可以通过使用@符号而不是来访问ref的旧状态,就像访问存储库的旧状态~一样git reset HEAD@{1}

我花了一段时间才了解HEAD @ {1}和HEAD〜1之间的区别,所以这里有一些解释:

git init
git commit --allow-empty -mOne
git commit --allow-empty -mTwo
git checkout -b anotherbranch
git commit --allow-empty -mThree
git checkout master # This changes the HEAD, but not the repository contents
git show HEAD~1 # => One
git show HEAD@{1} # => Three
git reflog

因此,HEAD~1意思是“在HEAD当前指向的提交之前转到提交”,而HEAD@{1}意思是“ 在HEAD当前指向的指向之前转到指向的提交”。

这样可以轻松地找到丢失的提交并进行恢复。


2
我认为更清楚的另一种解释是:HEAD〜1表示转到“ HEAD的父级”,而HEAD @ {1}转到“返回HEAD的历史一步”
kizzx2

1
问题在于,“历史记录”一词在VCS中确实超载。另一种表达方式是〜在提交历史中倒退,而@在时间或时间历史中倒退。但是这三个版本都不是特别好。
约尔格W¯¯米塔格

@ kizzx2(和Jorg)实际上将这3个解释放在一起会很有帮助-THX
Richard Le Mesurier

14

回答之前让我们添加一些背景,解释一下这是什么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 HEAD --hard <commit_id>

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

# 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

在此处输入图片说明


看来您的git reset HEAD --hard <commit_id>示例取自stackoverflow.com/questions/4114095/…-如果是这种情况,请您能注明出处吗?
罗布

12

git reflog

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

git cherry-pick <the sha>


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

1
每个人在寻找如何撤消硬重置更改时实际上都在寻找此内容。这个答案应该得到更多支持
ErenL

@ErenL我想我只是想知道,人们喜欢做额外的工作。哈哈
ScottyBlades

1
这是它,你刚才救了我的一天🙂
Sylvernus Akubo

11

我知道这是一个老话题...但是随着许多人在寻找方法来撤消Git中的内容,我仍然认为继续在此处提供提示可能是一个好主意。

当您执行“ git add”或将任何内容从git gui的左上角移动到左下角时,文件内容存储在blob中,并且文件内容可以从该blob中恢复。

因此,即使文件尚未提交但必须已添加,也可以恢复该文件。

git init  
echo hello >> test.txt  
git add test.txt  

现在已经创建了blob,但是它已由索引引用,因此在我们重置之前,它不会与git fsck一起列出。所以我们重置...

git reset --hard  
git fsck  

您将得到一个悬挂的斑点ce013625030ba8dba906f756967f9e9ca394464a

git show ce01362  

会给你文件内容“你好”

为了找到未引用的提交,我在某个地方找到了提示。

gitk --all $(git log -g --pretty=format:%h)  

我将它作为git gui中的工具使用,非常方便。


+1。如stackoverflow.com/a/21350689/6309中所述git fsck --lost-found可以提供帮助。
VonC 2014年

7

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

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



3

编写了一个小脚本,以使其稍微容易找到要查找的提交:

git fsck --lost-found | grep commit | cut -d ' ' -f 3 | xargs -i git show \{\} | egrep '^commit |Date:'

是的,可以使用awk或类似的东西使它漂亮得多,但是它很简单,我只需要它。可能会救别人30秒。


0

我的问题几乎相似。输入之前,我有未提交的文件git reset --hard

谢天谢地。我设法跳过了所有这些资源。当我注意到我可以撤消(ctrl-z)后。😊我只想将此添加到上面的所有答案中。

注意。无法ctrl-z打开未打开的文件。


0

这挽救了我的生命: https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c

基本上,您需要运行:

for blob in $(git fsck --lost-found | awk ‘$2 == “blob” { print $3 }’); do git cat-file -p $blob > $blob.txt; done

然后,手动进行操作以将文件重新组织为正确的结构。

要点:git reset --hard如果您不能完全100%理解它的工作原理,则不要使用,最好不要使用。

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.