恢复被git reset从索引中删除的文件

13
我将一些文件添加到了Git索引中,但是由于错误而使用了 git reset --hard 删除了它们。 我该怎么恢复这些文件呢? 以下是发生的情况:
  1. 我使用 git add . 添加了所有文件。
  2. 然后我进行了提交。
  3. 当我检查状态时,仍有一些文件未包含在提交中,这很奇怪。
  4. 我再次添加了未跟踪的文件,这一次操作成功了。
  5. 但我希望所有更改都在一个提交中,所以我查找了如何取消刚刚提交的内容的操作步骤。
  6. 我使用了git reset --hard HEAD^ - 显然是个坏主意,所有的文件都被删除了。
  7. 因此,我使用了git reflog来查找我离开的位置。
  8. 然后我使用了git reflog ______回到我的最后一次提交。
  9. 然后我使用了git reset HEAD取消提交(这是我最初应该做的),但是我添加的文件(参见上文)仍然丢失了。
那我该如何找回这些文件呢?

如果它们从未被提交,并且您已将索引重置,则可能无法实现。 - Asherah
有没有一种方法可以撤销重置索引? - Jeff Eisley
1
你可能需要运行 git fsck --full 命令,并耐心地筛选出所有它将报告的不可达 blob,这样或许会有所收获。 - knittl
2个回答

23

首先,完全备份您的Git仓库!

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

让我们逐个查看您的命令:

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

$ git add .

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

然后我提交了

$ git commit -m'added all files'

这将向对象数据库写入一个新的提交对象。该提交将引用单个树。该树引用了blob文件和其他树形子目录。

当我检查状态时,仍有未包括在添加提交中的文件,这很奇怪。

$ git status

我能想到两种情况会导致这种情况发生:有东西修改了你的文件或者新文件在你背后被添加了。

我重新添加了这些未跟踪的文件,这次它奏效了。

$ git add .

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

但我希望所有更改都能在一个提交中,所以查找了如何取消刚刚提交的内容。

我会在本答案结尾告诉你一个更好的方法,不需要用户执行可能危险的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 reset HEAD

这不会取消暂存此提交,但它将取消暂存索引中的所有已暂存但未提交的更改。最初(第一步),您想说 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> 将打印文件内容,以便您可以验证是否正确。对于 blobs,您可以使用 IO 重定向将内容写入正确的文件名。对于树,您必须使用 git 命令(git read-tree)。如果只有几个文件,最好直接将它们写入文件中。


这里有一些注意事项:

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

此外,几乎从不使用 git add . 是个好习惯。通常,您只需要在创建新仓库时使用它一次。更好的选择是 git add -ugit commit -a,它们将暂存所有已跟踪文件的更改。要跟踪新文件,最好明确指定它们。


是的!每个代码块都有缺失的代码。你应该托管自己的Git博客。感谢您抽出时间来帮助我,非常感激。 - Jeff Eisley

16

我遇到了类似的问题,但我的仓库中有很多悬空的 blob 和树形结构,所以最终我用过滤命令grep来筛选所有悬挂的 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}'); do git cat-file -p $b > ../$b ; done - Rafael Diego Nicoletti

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