Git仓库损坏(头部检查不正确;松散对象已损坏)

92

昨晚我在写提交信息时遇到了停电。当我重新启动机器后,无法完成提交。我运行了git reset,添加了更改的文件,然后再次尝试,结果得到了这个:

% git commit
error: inflate: data stream error (incorrect header check)
error: unable to unpack a94406345ac44982b00cf57b4b9660a35436637f header
fatal: a94406345ac44982b00cf57b4b9660a35436637f is not a valid object

git fsck显示如下:

% git fsck --full
Checking object directories: 100% (256/256), done.
error: inflate: data stream error (incorrect header check)
error: unable to unpack 4346883490a0990e68db0187241abc1642765a73 header
error: inflate: data stream error (incorrect header check)
fatal: loose object 4346883490a0990e68db0187241abc1642765a73 (stored in .git/objects/43/46883490a0990e68db0187241abc1642765a73) is corrupt

我注意到这些信息在抱怨不同的对象。

我在Stack Overflow和网络上搜索并尝试了一些不同的方法,但都没有成功。

  • 我没有最近的备份副本。
  • 将存储库克隆到另一个目录中没有帮助;新存储库出现了完全相同的问题。
  • git stash 给出与 git commit 相同的消息。所有其他 git 命令似乎都正常工作。

我该如何确定问题所在并进行修复?

git log 输出(仅前几行):

% git log --oneline --decorate --all |head -n 8
253b086 (HEAD, new_tokenize) Normalized tokenizer interface slightly
0f2425a (master) Added procs to eval layer
a4d4c22 Added procedures as a type
d1e15ad (tag: v0.10) Added `if' form with tail call semantics
f94a992 (tag: v0.9) Completed environments
031116e Fixed bug where # on a line by itself caused segfault
3d8b09f Added environments, define and set!
01cc624 Put symbol table implementation into types.c

这是一个小型个人项目;我通常只在(master)上工作,但当时正在进行一个实验(new_tokenize)。253b086是停电前的最后一次成功提交。


也许可以尝试使用 git loggit log --oneline --decorate --all 命令来查看历史记录是什么样子的。 - jkyako
完成。不确定您需要什么,如果您需要完整的输出或其他内容,请告诉我。我没有做任何花哨的东西。 - trent
基本上只是试图弄清楚损坏的对象报告中最终失去/无法访问什么。 假设您已经将任何未提交的本地更改保存在其他位置,您能否在日志中列出的每个ID上执行“git checkout”操作?这让我想知道一种蛮力解决方案是否是对每个ID编写脚本,检出ID,将工作树复制到其他地方(可能是新的git存储库),以尝试重建非损坏的存储库。 - jkyako
3
刚刚阅读了这篇文章,并且想到将损坏的对象暂时移动到另一个位置,然后重试git fsck --full以查找当前引用有问题对象的内容,可能会很有趣。 - jkyako
1
谢谢@jkyako,这让我找对了方向。答案即将到来。 - trent
4个回答

89

看起来git在.git/objects文件夹中为新提交的文件创建了文件,但未成功写入。我通过逐个删除它们并重新运行git fsck --full来解决这个问题,以查找下一个文件。我从最初由git fsck报告的那个文件开始。

% rm -f .git/objects/43/46883490a0990e68db0187241abc1642765a73
% git fsck --full
Checking object directories: 100% (256/256), done.
error: inflate: data stream error (incorrect header check)
error: unable to unpack 86e7247af5865e857a3b61eed99986e2d9538df1 header
error: inflate: data stream error (incorrect header check)
fatal: loose object 86e7247af5865e857a3b61eed99986e2d9538df1 (stored in .git/objects/86/e7247af5865e857a3b61eed99986e2d9538df1) is corrupt
% rm -f .git/objects/86/e7247af5865e857a3b61eed99986e2d9538df1
% git fsck --full
Checking object directories: 100% (256/256), done.
error: inflate: data stream error (incorrect header check)
error: unable to unpack a94406345ac44982b00cf57b4b9660a35436637f header
error: inflate: data stream error (incorrect header check)
fatal: loose object a94406345ac44982b00cf57b4b9660a35436637f (stored in .git/objects/a9/4406345ac44982b00cf57b4b9660a35436637f) is corrupt

然后我在运行git fsck之前删除了五个对象,这应该对应我尝试创建的提交中的5个文件,但是检查显示文件历史记录并未损坏。

顺便说一下,我想到了另一种方法,似乎也可以解决问题。 git clone会复制错误的对象,但是git push不会。在备份后,我创建了一个新的空仓库(使用--bare选项,因为否则无法推送到主分支),然后取消暂存我的更改并将两个分支推送到新的仓库。然后只需再次检出,从备份中恢复最新的更改即可。

如果有人愿意解释这里的故障机制,我仍然很感兴趣。


4
在执行完这些步骤后,还需要执行 del .git\indexgit reset - ozba
@ozba 请详细说明。如果有必要的话,我会将其添加到我的答案中,但在我的情况下并不是必需的。 - trent
在我的情况下,这是必要的,因为索引也已经损坏了。 - ozba
@ozba 在这种情况下,如何判断索引是否已损坏?它会给出额外或稍微不同的错误吗? - trent
是的,如果你执行了所有你所展示的步骤,例如执行 git status 命令,它仍然会显示无法读取对象 XXX。 - ozba
删除文件后,使用 git fetch 重新制作文件。 - mmdreza baqalpour

11

此答案所述,我运行了:

git reflog expire --expire-unreachable=now --all
git gc --prune=now

这个命令可以删除所有的悬空Blob和悬空的提交,以及损坏的数据库对象。

相比逐个查找,它要快得多!


25
git gc --prune=now 仅仅给了我一个关于错误头部的相同错误。 - IcedDante
这对我有用。在过去的5次提交中,发生了两次损坏。第一次,我重命名了原始文件夹,再次克隆了项目,然后手动复制了我的(幸运的是很少的)更改。这一次我有更多的更改,所以我很高兴这个命令简单地修复了它。 - KYL3R

11

对于任何遇到这个问题的人,简单的解决方法是使用git clone命令。如果有远程仓库,则将其克隆到本地文件夹中(在删除受损本地仓库之后),如果没有远程仓库,则尝试将受损仓库推送到Github,然后从那里克隆它。我认为受损的对象不会被推送,这将修复该问题。


10
可能会“修复”问题,但您将失去损坏的文件。 - Lawrence Dol
我尝试了被接受的答案,但对我来说并没有奏效。然而https://wincent.com/wiki/Dealing_with_Git_repo_corruption起作用了,并且类似于您的想法-但有一个很大的警告,它会丢失对本地分支的追踪。我很幸运在文件损坏之前推送到了远程分支,因此在克隆后只需获取更改即可。 - Nathan Beck

0

我以一种奇怪的方式陷入了同样的问题,
在Windows上,
删除了.git文件夹,
在另一个文件夹中克隆了相同的repo,
.git文件夹从新文件夹复制到旧的repo中,
然后执行以下操作:
git add .
git commit -m ...
git push ....


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