在执行了"git reset --hard HEAD^?"命令后,如何恢复已添加/暂存的文件?

90
我添加了一个新文件F1,并对另一个文件F2进行了更改,但然后执行了git reset --hard HEAD^,导致所有文件的更改都丢失了。是否有什么方法可以恢复它们?我查看了相关问题: 如何撤消 git reset --hard HEAD~1? 但是该问题假定已经完成了 Git 提交。

现在有一个 Git 脚本可以直接完成这个操作:https://github.com/pendashteh/git-recover-index - Alexar
6个回答

174

你可以(需要一些工作)恢复最后一次“git add <file>”之前的文件状态。你可以使用

$ git fsck --cache --no-reflogs --lost-found --dangling HEAD

然后检查'.git/lost-found/other'目录中的文件。

请阅读git fsck手册。


11
"git show -p --format=raw $blob > $blob.txt" 翻译成中文是:"将$blob的Git存储对象以原始格式显示,并将结果保存到$blob.txt文件中。" +1,救了我的命。 - Qix - MONICA WAS MISTREATED
15
在 Git 1.9.1 上,使用 --unreachable 命令会导致 .git/lost-found 目录不被创建。 - canton7
52
如所述,"unreachable" 命令可以防止创建 ".git/lost-found" 目录。我改用以下命令成功运行: $ git fsck --cache --no-reflogs --lost-found --dangling HEAD - Jacob T. Nielsen
3
貌似这个方法可行,但我丢失了所有文件的后缀名? - MichaelChirico
1
抱歉,一旦我恢复了 blob 文件,接下来该怎么做? - Alfredo Castaneda Garcia
显示剩余2条评论

45
  • 获取 git 知道文件名的不可访问文件列表:

    git fsck --unreachable --no-reflogs --no-cache HEAD | fgrep " tree " \
    | cut -d " " -f3 | xargs -r -n1 git ls-tree \
    | fgrep " blob " | cut -d " " -f 3- | sort -k2 -u
    
  • 如果你看到了一些有趣的东西,可以使用git cat-file blob SHA-1-of-interesting-file将文件输出到标准输出。 (例如:git cat-file blob b8f0bdf56 > recovered-logo.png

  • 不幸的是,如果丢失的文件不是任何提交的一部分,则 git 没有时间戳,因此无法按时间顺序打印各个版本的文件。

    如果丢失的文件从未被暂存(git stagegit add)或藏匿(git stash),那么您基本上就没有机会了,因为就 git 知道的而言,该文件从未存在过。(在这种情况下,您仍然可以尝试执行git fsck --no-reflogs --lost-found并查看目录.git/lost-found/other以查看是否有值得保留的内容,以防万一 git 确实通过某些幸运的事故拥有您丢失的文件的副本。在这种情况下,您没有文件名可用于帮助您,只有文件内容。)

    如果您只是丢失了一些提交(而不仅仅是文件),则可能需要运行类似以下的命令:

    gitk --all $( git fsck | awk '/dangling commit/ {print $3}'; git log -g --pretty='format:%H' )
    

    这将使用所有分支、所有Reflog和所有悬挂提交来运行gitk。如果您的存储库有非常多的提交(比如Linux内核),您可能需要添加-n 10000或其他限制。如果您没有gitk,您可以使用仅命令行的较小版本运行,像这样:

    git log --all --decorate --stat --graph --date-order $( git fsck | awk '/dangling commit/ {print $3}'; git log -g --pretty='format:%H' )
    

    或者使用输出更简洁的版本。

    git log --all --decorate --oneline --graph --date-order $( git fsck | awk '/dangling commit/ {print $3}'; git log -g --pretty='format:%H' )
    

    如果你看到某个提交,想要将其保存为名为 recovered1 的分支,只需执行 git checkout -b recovered1 <sha1-of-the-commit>


    2
    哇!刚刚发现了一个未被跟踪的文件,它是我不小心丢弃的(甚至没有提交),使用 git fsck --no-reflogs --lost-found 命令,然后在 .git/lost-found/other 目录下搜索它(使用 Notepade++Find in Files 搜索)。谢谢! - Ofir
    我必须补充说明,我强烈建议进行自动化版本备份,而不是尝试从Git历史记录中恢复文件。 - Mikko Rantalainen
    1
    哇,谢谢!@Ofir,同样的方法对我也有效!!执行 "$ git fsck --no-reflogs --lost-found" 命令,然后在 ".git/lost-found/other" 目录下搜索文件。 - greenhouse

    7

    哎呀,谢谢你! :) - DanDan
    不客气 :) 很高兴能帮到你。 - Ravi Krishna P
    这个答案让我在6年后受益匪浅,谢谢! - Bryan Lee

    6

    有一个 git插件 可以直接做到这一点:

    https://github.com/pendashteh/git-recover-index

    $ cd /path/to/disatered/repo
    $ git clone git@github.com:pendashteh/git-recover-index.git $HOME/.git-recover-index
    $ $HOME/.git-recover-index/git-recover-index.sh
    

    4
    实际上,如果您已将对象添加到索引中(使用git add),则为该对象的状态创建了一个blob,但没有树(因此也没有提交)对象引用它。这就是得到“悬挂”的松散对象文件的方式,如果运行git fsck,它将向您显示未引用的blob(如果运行,则git gc将删除这些类型的对象)。
    由于这个原因,如果启用了reflog,则可以尝试恢复已添加的文件F1的索引状态。如果您根本没有添加F2,则如Greg所说,git不知道任何关于它的信息,那么您在这方面就没有希望了。

    0
    如果你使用VSCode,你可以使用时间轴功能(通过右键点击更改的文件并选择“打开时间轴”)

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