我在索引中添加了一些文件但是我错误地用git reset --hard
删除了它们。我该如何恢复它们?
这是发生的事情:
git add .
git reset --hard HEAD^
- 显然很糟糕,所有文件都已删除git reflog
找到了我离开的地方git reflog ______
返回上一次提交。git reset HEAD
取消了提交(我本来应该做的),但是我提交后添加的文件(见上文)仍然没有。如何取回这些文件?
答案 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