Git恢复:"目标文件为空"。如何重建树木?

时间:2013-02-10 13:00:06

标签: git git-submodules corruption recovery

注意:我没有此存储库的任何先腐败克隆。我相信我的情况与此处描述的其他情况不同,因为我错过了一棵树,而不是一块树。

发生了什么:

当我尝试通过LAN克隆存储库(通过SSH)时,Git返回错误,说存储库已损坏:

remote: error: object file ./objects/2e/223ce259e9e33998d434acc778bc64b393d5d4 is empty
remote: fatal: loose object 2e223ce259e9e33998d434acc778bc64b393d5d4 (stored in ./objects/2e/223ce259e9e33998d434acc778bc64b393d5d4) is corrupt
error: git upload-pack: git-pack-objects died with error.
fatal: git upload-pack: aborting due to possible repository corruption on the remote side.
remote: aborting due to possible repository corruption on the remote side.

我发现git fsck可用于诊断腐败的地方,但它并没有告诉我任何新内容:

git fsck --full
error: object file ./objects/2e/223ce259e9e33998d434acc778bc64b393d5d4 is empty
fatal: loose object 2e223ce259e9e33998d434acc778bc64b393d5d4 (stored in ./objects/2e/223ce259e9e33998d434acc778bc64b393d5d4) is corrupt

我尝试在本地克隆存储库(使用--no-hardlinks)来查看会发生什么,但我得到了完全相同的结果。

然后我偶然发现了this question,回答的人刚刚删除了空文件(步骤3),所以我这样做了(即我已经从子目录{{删除了文件223ce259e9e33998d434acc778bc64b393d5d4 1}})。

再次

objects/2e/,我看到了:

git fsck

因此,现在树Checking object directories: 100% (256/256), done. broken link from tree 838e437f371c652fa4393d25473ce21cbf697d7a to tree 2e223ce259e9e33998d434acc778bc64b393d5d4 dangling commit 54146bc0dc4eb3eede82a0405b749e05c11c5522 missing tree 2e223ce259e9e33998d434acc778bc64b393d5d4 dangling commit 864864feec207786b84158e526b2faec7799fd4e dangling blob d3cfd7cc7718d5b76df70cf9865db01c25181bfb 存在问题。这不是上面提到的人发生的事情,所以我去google搜索并找到了some information from Linus

所以,我做了838e437f37,在输出中有一行读数:

git ls-tree 838e437f371c652fa4393d25473ce21cbf697d7a

现在,“moje”是一个目录(与Linus解释的例子不同,这是一个文件)。我猜这就是为什么Linus建议的下一步,040000 tree 2e223ce259e9e33998d434acc778bc64b393d5d4 moje 返回git hash-object moje

但无论如何,我所需要的机会很小,所以我更进一步。我运行了fatal: Unable to hash moje,根据Linus的指南,应该有一个提交,它将2e223列为某些内容的SHA-2哈希,但没有。该列表以

结尾
git log --raw --all --full-history -- moje/

我尝试查看该错误之前列出的最后一次提交,但我没有找到此哈希。我见过this,但它没有帮助我,可能是因为 在有问题的版本和工作树的当前状态之间发生了一些变化。

有一件事可能很重要:在fatal: unable to read source tree (2e223ce259e9e33998d434acc778bc64b393d5d4) 内部有一个目录moje/,它本身就是一个Git存储库(一个子模块)。我在那里寻找有问题的SHA-2哈希,但还没有找到它。

我该怎么办?

2 个答案:

答案 0 :(得分:5)

命令(由Chronial建议)

git rev-list --all | xargs -l -I '{}' sh -c 'if git ls-tree -rt {} > /dev/null 2>&1 ; then true; else git log --oneline -1 {}; git ls-tree -r -t {} | tail -1; fi'

返回了第一个依赖于缺少2e223ce对象的提交 - 它的SHA-2哈希是499b8fb。它的父母没事(我可以看到它的内容,检查出来等等),而且我也能够在破坏之后检查下一次提交(89b0fc4)。

现在我需要看看这两个“好”提交之间发生了什么变化 - 这很简单:git diff 499b8fb~ 89b0fc4返回

diff --git a/somefile b/somefile
deleted file mode 100644
index f5d1e1e..0000000
--- a/somefile
+++ /dev/null
@@ -1,79 +0,0 @@
[ contents of the deleted "somefile"... ]
diff --git a/moje/cli b/moje/cli
index 640a825..c0b1a24 160000
--- a/moje/cli
+++ b/moje/cli
@@ -1 +1 @@
-Subproject commit 640a825cd671dfba83601d6271e7e027665eaca8
+Subproject commit c0b1a24aa246289831ec7db3a8596376db1f625a

现在我知道在错误提交的父级和良好提交之间删除了文件somefile,并且子模块的HEAD从640a825更改为c0b1a24。我去了子模块库,询问这两者之间发生了什么样的提交:

git log --oneline 640a825..c0b1a24

返回

c0b1a24 <commit message>
8be9433 <commit message>
02564e1 <commit message>

现在我知道499b8fb~89b0fc4之间发生了四件事:

  • somefile已删除
  • /moje/cli HEAD已从640a825更改为02564e1
  • /moje/cli HEAD已从02564e1更改为8be9433
  • /moje/cli HEAD已从8be9433更改为c0b1a24

我不知道499b8fb(错误提交)中发生了哪个部分,以及89b0fc4中发生了哪个部分。但幸运的是,没有那么多的可能性,所以我只尝试了其中的每一个。我使用每个组合进行提交,以便Git计算适当的对象并将它们存储在数据库中。事实证明,当/moje/cli HEAD位于8be9433时,git commit导致创建了丢失的2e223ce对象 - 万岁!

注意:如果你遇到类似的情况而且你正在寻找哪些提交是好的以及Git可以告诉你什么,请记住能够checkout提交和{{1这是两件不同的事情。例如,我最初认为如果show抛出错误,则意味着git show somesha提交已损坏,我无法将其用于任何事情。结果证明是错误的:当somesha返回错误时,我能够git show 89b0fc4git checkout 89b0fc4工作。

我想这是因为git diff 499b8fb~ 89b0fc4显示git show somesha引入了哪些更改,并且Git需要读取前一次提交的内容(在这种情况下是损坏的内容)。显然,Git不需要查看之前提交的内容。

(我设法做到这一点归功于Chronial's answer - 对他赞不绝口!我被建议将此作为我自己的答案发布。)

答案 1 :(得分:2)

使用此命令获取包含缺失树的提交列表:

git rev-list --all | xargs -l -I '{}' sh -c 'if git ls-tree -rt {} > /dev/null 2>&1 ; then true; else git log --oneline -1 {}; git ls-tree -r -t {} | tail -1; fi'

现在你需要重新创建丢失的树,将完全相同的内容放在那里,就像你在那里那样,然后将那棵树添加到repo中。最简单的方法是重新创建内容,然后将它们提交到repo(之后可以删除该提交)。