恢复已添加到索引但随后通过git reset删除的文件

时间:2012-05-28 10:17:47

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

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

  1. 我使用git add .
  2. 添加了所有文件
  3. 我随后承诺了
  4. 当我检查状态时,仍然有一些文件没有包含在添加的提交中,这很奇怪
  5. 我再次添加了未跟踪的文件,这次工作
  6. 但我希望一切都在一次提交中,所以我抬头看看如何取消我刚刚提交的内容
  7. 我使用了git reset --hard HEAD^ - 显然很糟糕,所有文件都已删除
  8. 所以我用git reflog找到了我离开的地方
  9. 然后我使用git reflog ______返回上一次提交。
  10. 然后我使用git reset HEAD取消了提交(我本来应该做的),但是我提交后添加的文件(见上文)仍然没有。
  11. 如何取回这些文件?

2 个答案:

答案 0 :(得分:21)

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

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

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

  

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

$ git add .

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

  然后我提交了

$ git commit -m'added all files'

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

  

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

$ git status

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

  

我再次添加了未跟踪的文件,这次工作

$ git add .

我假设您再次使用相同的add命令,如步骤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 reset HEAD

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


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

之后的状态

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


这里有几点说明:

如果要将文件添加到上次提交(或编辑其提交消息),则只需使用git read-tree即可。它基本上是git commit --amend的包装。

此外,使用git reset --soft HEAD^ && git commit -c HEAD@{1}几乎绝不是一个好主意。通常,您只想在创建新存储库时第一次使用它。 更好的替代方案是git add .git add -u,它会对跟踪文件进行所有更改。要跟踪新文件,请更明确地指定它们。

答案 1 :(得分:15)

我有一个类似的问题,但我的回购中有很多悬垂的斑点和树木,所以我最终用grep过滤了所有悬空斑点的输出并打印出匹配的斑点。假设${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