【问题标题】:Recover files that were added to the index but then removed by a git reset恢复添加到索引但随后被 git reset 删除的文件
【发布时间】:2026-01-27 13:00:01
【问题描述】:

我在索引中添加了一些文件,但后来我错误地用git reset --hard 删除了它们。我该如何恢复它们? 事情是这样的:

  1. 我使用git add .添加了所有文件
  2. 然后我答应了
  3. 当我检查状态时,仍然有文件未包含在 add 的提交中,这很奇怪
  4. 我再次添加了未跟踪的文件,这次成功了
  5. 但我希望所有内容都在 1 次提交中,因此我查找了如何取消暂存我刚刚提交的内容
  6. 我使用了git reset --hard HEAD^ — 显然是个坏主意,所有文件都被删除了
  7. 然后我使用git reflog 找到我离开的地方
  8. 然后我使用git reflog ______ 回到我的上一次提交。
  9. 然后我使用 git reset HEAD 取消暂存提交(我最初应该这样做),但我在提交后添加的文件(见上文)仍然消失了。

如何取回这些文件?

【问题讨论】:

  • 如果它们从未被提交,并且你已经重置了索引,可能你不能。
  • 有没有办法撤销重置索引?
  • 运行git fsck --full 并在筛选所有将要报告的无法访问的 blob 时保持耐心可能会很幸运。

标签: git git-commit git-reset git-add git-reflog


【解决方案1】:

首先,对您的 Git 存储库进行完整备份!

当您git add 一个文件时,git 会从该文件的内容中创建一个 blob 并将其添加到其对象数据库 (.git/objects/??/*)。

让我们一一看看你的命令:

我使用 git add 添加了所有文件。

$ git add .

这会将当前目录及其子目录中包含的所有文件添加到 Git 的对象数据库中。不会添加与.gitignore 文件中的模式匹配的未跟踪文件。树文件也将被写入。请看我回答的结尾。

然后我就答应了

$ git commit -m'added all files'

这会将一个新的提交对象写入对象数据库。此提交将引用一棵树。树引用 blob(文件)和其他树(子目录)。

当我检查状态时,仍然有文件没有包含在 add 的提交中,这很奇怪

$ git status

我可以想到发生这种情况的两种情况:修改了您的文件或在您背后添加了新文件。

我再次添加了未跟踪的文件,这次成功了

$ git add .

我假设您再次使用了相同的add 命令,就像在步骤 1 中一样。

但我希望所有内容都在 1 次提交中,因此我查找了如何取消暂存我刚刚提交的内容

我会在这个答案的最后告诉你一个更好的方法,它不需要用户发出潜在危险的reset

我使用了 git reset --hard HEAD^ — 显然是个坏主意,所有文件都被删除了

$ git reset --hard HEAD^

此命令会将您当前的工作树和索引设置为恰好在提交 HEAD^(倒数第二个提交)处。换句话说,它将丢弃任何本地未提交的更改并将分支指针移回一次提交。它不会触及未跟踪的文件。

然后我使用 git reflog 找到我离开的地方

$ git reflog

这显示了最近签出的最后提交(与git reflog HEAD 相同)。如果您指定分支名称,它将显示该分支最近指向的最后一次提交。

然后我使用 git reflog __ 回到我的上一次提交。

不确定这个。 git reflog (大部分)是只读命令,不能用于“返回”提交。您只能使用它来查找指向的分支(或HEAD)的提交。

然后我使用 git reset HEAD 取消暂存提交(我最初应该做的)但是我在提交之后添加的文件(见上文)仍然消失了。 $ git 重置头

这不会取消暂存此提交,但会取消暂存索引中所有已暂存(但未提交)的更改。最初(第一步),你想说git reset HEAD^(或git reset --mixed HEAD^)——这将使你的工作树保持不变,但将索引设置为与HEAD^命名的提交所指向的树匹配。


现在,要取回您的文件,您必须使用 git fsck --full --unreachable --no-reflog。它将扫描 Git 对象数据库中的所有对象并执行可达性分析。您想查找blob 对象。还应该有一个tree 对象,描述第二个git add . 之后的状态

git cat-file -p <object hash> 将打印文件内容,因此您可以验证您是否拥有正确的对象。对于 blob,您可以使用 IO 重定向将内容写入正确的文件名。对于树,您必须使用 git 命令 (git read-tree)。如果只是几个文件,最好直接写入文件。


这里有几点说明:

如果您想将文件添加到最后一次提交(或编辑其提交消息),您可以简单地使用git commit --amend。它基本上是git reset --soft HEAD^ && git commit -c HEAD@{1} 的包装。

此外,使用git add . 几乎从来都不是一个好主意。通常,您只想在创建新存储库时第一次使用它。 更好的选择是git add -ugit commit -a,它们会将所有更改暂存到跟踪文件。要跟踪新文件,最好明确指定它们。

【讨论】:

  • 非常感谢。我现在就试试这个。
  • 是的!每个 blob 都有缺失的代码。您应该拥有自己的 Git 博客。感谢您花时间帮助我,非常感谢。
【解决方案2】:

我遇到了类似的问题,但我的 repo 中有很多悬空 blob 和树,所以我最终使用 grep 过滤所有悬空 blob 的输出并打印匹配的 blob。假设 ${UNIQUE_CODE} 是您在索引中拥有的文件所独有的一些代码,那么这应该为您提供您正在寻找的 blob 的哈希值:

for b in $(git fsck --lost-found | grep blob | awk '{print $3}'); do git cat-file -p $b | grep -q ${UNIQUE_CODE} && echo $b; done

【讨论】:

  • 谢谢伙计,它拯救了我的一天!但是,为了恢复,我做了以下操作: for b in $(git fsck --lost-found | grep blob | awk '{print $3}');做 git cat-file -p $b > ../$b ;完成