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

Jan*_*hoł 3 git recovery corruption git-submodules

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

发生了什么:

当我尝试通过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.
Run Code Online (Sandbox Code Playgroud)

我找到了git fsck可用于诊断腐败的地方,但它并没有告诉我任何新的东西:

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

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

然后我偶然发现了这个问题,回答的人刚刚删除了空文件(步骤3),所以我做了这个(即我已经223ce259e9e33998d434acc778bc64b393d5d4从子目录中删除了文件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
Run Code Online (Sandbox Code Playgroud)

所以,现在树有一个问题838e437f37.这不是上面提到的人发生的事情,所以我去谷歌搜索并从Linus找到了一些信息.

所以,我做了,git ls-tree 838e437f371c652fa4393d25473ce21cbf697d7a并在输出中有一行读数:

040000 tree 2e223ce259e9e33998d434acc778bc64b393d5d4    moje
Run Code Online (Sandbox Code Playgroud)

现在,"moje"是一个目录(不像Linus解释的那个例子,它是一个文件).我猜这就是为什么Linus提出的下一步git hash-object moje回归的原因fatal: Unable to hash moje.

但无论如何,这是我所需要的一个小机会,所以我进一步寻找.git log --raw --all --full-history -- moje/根据Linus的指南,我跑了,应该有一个提交,它将2e223列为某些内容的SHA-2哈希,但没有.列表以.结尾

fatal: unable to read source tree (2e223ce259e9e33998d434acc778bc64b393d5d4)
Run Code Online (Sandbox Code Playgroud)

我试着查看错误之前列出的最后一次提交,但我没有找到这个哈希.我见过这个,但它并不能帮助我,可能是因为有有问题的版本和工作树的当前状态之间的一些变化.

有一件事可能很重要:里面moje/有一个目录cli/,它是一个Git存储库本身(一个子模块).我在那里寻找有问题的SHA-2哈希,但还没有找到它.

我该怎么办?

Jan*_*hoł 6

命令(由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'
Run Code Online (Sandbox Code Playgroud)

返回了第一个依赖于缺少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
Run Code Online (Sandbox Code Playgroud)

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

git log --oneline 640a825..c0b1a24
Run Code Online (Sandbox Code Playgroud)

哪个回来了

c0b1a24 <commit message>
8be9433 <commit message>
02564e1 <commit message>
Run Code Online (Sandbox Code Playgroud)

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

  • somefile 被删除了
  • /moje/cliHEAD从改变640a82502564e1
  • /moje/cliHEAD从改变02564e18be9433
  • /moje/cliHEAD从改变8be9433c0b1a24

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

注意:如果你遇到类似的情况并且你正在四处寻找哪些提交是好的以及Git可以告诉你什么,请记住能够checkout提交show它并且它是两个不同的东西.例如,我最初认为如果git show somesha抛出错误,则意味着somesha提交已损坏,我无法将其用于任何事情.结果证明是错误的:虽然git show 89b0fc4返回了一个错误,但我能够git checkout 89b0fc4并且也git diff 499b8fb~ 89b0fc4工作了.

我想那是因为git show somesha显示了引入的更改somesha,并且Git需要读取先前提交的内容(在这种情况下是损坏的内容).显然,Git不需要查看以前的提交来查看一个.

(由于Chronial的答案,我设法做到了这一点- 对他的称赞!我被建议将其作为我自己的答案发布.)