执行命令 git reset --hard HEAD 后,所有已暂存但未提交的文件都被删除了。

9

通常,您可以执行以下操作:

$ echo "Stanley, you must beware of the Drive bee" > file-a
$ echo "What's a Drive bee?" > file-b
$ git init .
$ git add file-b
$ git commit file-b -m "We don't know, but whatever error you make with it could be fatal."
$ git reset --hard HEAD
$ ls
file-a file-b

我认为自己做了一件非常糟糕的事情:

$ echo "What are you doing, you darn ?" > file-a
$ echo "Can't you see I'm trying to drive?" > file-
$ git init .
$ git add -A
$ git commit file- -m "Oh, my God! [It's] the Drive !"
$ git reset --hard HEAD
$ ls
file-

结果:所有暂存但未提交的文件都已删除0_o。
git reset --hard HEAD\^
fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree.

我刚才删除的文件能否恢复?换句话说,是否可以将git仓库还原到git add -A命令执行之前(或当时)的状态?

3个回答

20

是的,你非常幸运。你添加到 Git 索引中的内容实际上以某种方式存在。事实上,当文件被添加到索引时,Git 已经为每个文件创建了 blob 对象。索引本身仅存储树对象。

因此,你的暂存文件确实已创建了 blob 对象。你失去的只是树信息,即路径和文件名,但可以恢复其内容。

尝试运行 git fsck,你应该会得到一个悬空 blob 的列表:

Checking object directories: 100% (256/256), done.
dangling blob ac28af8d84fc71eb247ccf665c6d0f4bf1822520
dangling blob 2d152ff9f09cb08ebc495f453da63eddaa9e249f
dangling blob cd9567427762cd8066b4e802e5c170a31a026100

您可以通过执行 git cat-file -p ac28af8d 来恢复内容。例如,您可以将其导入到文件中:

git cat-file -p ac28af8d > recovered-file

对于所有的东西都这样做,然后你就把它们拿回来了。


2
只是一个大多数无关紧要的注释,你可以使用 git show 作为 git cat-file -p 的替代方法。非常好的答案。我自己没有意识到这一点。 - Michael Mior
@MichaelMior 确实,虽然我通常使用 cat-file -p,因为它可以为任何对象类型提供相同的输出 ;) - poke
谢谢!我也不知道这个。有点令人沮丧的现实是,重新创建所有文件会更容易,因为这是一个相对较新的Android项目,许多文件被删除,意味着相对较大的树已经丢失。但是,使用您的解决方案,我应该能够从块中获取我编写的代码,并将其添加回去。 (= - dcow
是的,不幸的是索引包含所有暂存的树对象,因此当索引丢失时,树也会丢失。 reset --hard 是摆脱索引的众多方法之一... ;) - poke
此外,如果您有许多悬空对象并想缩小范围,请使用文件系统:ls -lrt .git/objects 显示最新的前缀,然后在对象目录中再次执行,例如 ls -lrt .git/objects/f8。最后,执行 git cat-file -p f8<rest of name> - mik3y
如果您有很多悬空对象,另一个选择是使用 git fsck --lost-found 命令。这将把所有的悬空 blob 写入到 .git/lost-found/other 目录中。然后,您可以使用 less .git/lost-found/other/* 命令浏览文件,并使用 :n(下一个)和 :p(上一个)进行切换,或者通过 grep 命令查找您要查找的文件。 - user149408

2
以下脚本将git fsck输出中的所有悬空块恢复到文件中:
i=0; for x in `git fsck | grep "dangling blob [0-9a-f]" | cut -d ' ' -f 3`; do git cat-file -p $x > /tmp/test${i}; i=$((i+1)) ; done

如您所见,每个原始文件的输出都被恢复到通用文件名/tmp/test${i}...我还没有找到恢复文件名的方法...我尝试过的命令是git ls-tree:

首先,我已经获取了可能的对象:

./.git/objects/37
./.git/objects/37/5187f5882593f7e52c8efef602d98f1f049c5d
./.git/objects/37/98be05fcbd7c79e08703aead800529b362332b

然后,我尝试使用ls-tree技巧,但它无法识别这个作为有效的对象。

/<myproj>/.git/objects/37 ]$ git ls-tree 5187f5882593f7e52c8efef602d98f1f049c5d 
fatal: Not a valid object name 5187f5882593f7e52c8efef602d98f1f049c5d

0
我非常喜欢这个答案提供的解决方案,用于处理 Stack Overflow 相关问题:
  1. 使用 git log --diff-filter=D --summary 命令获取所有已删除文件和被删除文件的提交记录。
  2. 使用 git checkout $commit~1 filename 命令还原已删除的文件。

这个方案简洁易懂。


请不要在其他Stack Overflow问题中仅发布链接答案。相反,请投票/标记为重复,或者如果该问题不是重复的,则根据此特定问题调整答案 - Rizier123
@red-goblin 这个提交已经失效了。你不能从不存在的索引中进行检出操作。(即这不是你链接的那个问题的重复,因此它的答案在这里并不适用,但还是谢谢你) - dcow

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