git - 致命错误:您当前的分支似乎已经损坏(可能是由于中断的拉取操作引起的)。

3

问题:

  1. 不小心在第一台电脑上执行了 git --amend 并将其推送到了 USB 设备
  2. 从 USB 设备中拉取到第二台电脑上
  3. 第二台电脑的代码库现在已经被损坏了
  4. 在第一台电脑上执行 git pull 时会产生合并冲突; 不确定这是否也是一个损坏状态(如果 --amend 是有问题的)

症状:

大多数命令:

fatal: your current branch appears to be broken

.git/refs/heads/master:

$ cat .git/refs/heads/master

.git/refs/heads/master文件内容:

'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

$ git status
new file: ...
new file: ... [for every file in the repository; expected since on a corrupted branch]

我不确定是否应该归咎于 git --amend,因为它似乎有点无害。也许其他原因导致了问题。

如何修复?

修复这个问题是否简单到只需要从.git/logs/refs/heads/master中最新的好提交中获取哈希值并手动插入到.git/refs/heads/master中?如果可以,请问应该如何操作?

如果是这样,那么我应该销毁有问题的提交(以免以后会出现问题,例如使用git-repack或其他方法破坏文件)吗?

当我尝试运行less .git/logs/refs/heads/master并从形式的最后一行取出第一个哈希值时......

...
[hash for HEAD~2] [hash for HEAD~1] [authorname] ...
[hash for HEAD~1] [hash for HEAD] [authorname] ...
^^^^^^^^^^^^^^^^^   (corrupted)
EOF

将其粘贴到.git/refs/heads/master文件中后,我现在卡住了...

$ git fsck
error: inflate: data stream error (unknown compression method)
error: unable to unpack header of .git/objects/8f/1da374ffac3711f8cdde57379f90cb03bbb9ea
error: 8f1da374ffac3711f8cdde57379f90cb03bbb9ea: object corrupt or missing: .git/objects/8f/1da374ffac3711f8cdde57379f90cb03bbb9ea
error: inflate: data stream error (unknown compression method)
error: unable to unpack header of .git/objects/ac/2fcd052804fb7adac465220da5bcb04d008fc7
error: ac2fcd052804fb7adac465220da5bcb04d008fc7: object corrupt or missing: .git/objects/ac/2fcd052804fb7adac465220da5bcb04d008fc7
Checking object directories: 100% (256/256), done.
Checking objects: 100% (1147/1147), done.
error: inflate: data stream error (unknown compression method)
error: unable to unpack 8f1da374ffac3711f8cdde57379f90cb03bbb9ea header
error: inflate: data stream error (unknown compression method)
error: unable to unpack 8f1da374ffac3711f8cdde57379f90cb03bbb9ea header
fatal: loose object 8f1da374ffac3711f8cdde57379f90cb03bbb9ea (stored in .git/objects/8f/1da374ffac3711f8cdde57379f90cb03bbb9ea) is corrupt

我可以尝试破坏松散的对象,但我不确定它是否反过来是更腐败对象(例如树)的指针,这些对象也必须被摧毁。如果我知道命令(或可以在备份上尝试rm),我当然可以尝试摧毁此对象;我应该尝试吗?
此外,我如何从这个混乱中修复USB密钥和其他仓库?谢谢。
(新手的潜在有用参考资料,不太可能帮助能够回答这个问题的人:https://aboullaite.me/deep-dive-into-git-git-refs/) (类似的问题fatal: your current branch appears to be broken —没有指定错误的原因;许多事情可能会导致此错误)
编辑:
我对上面列出的每个对象都进行了rm .git/objects/...,现在我有...
$ git fsck
Checking object directories: 100% (256/256), done.
Checking objects: 100% (1147/1147), done.
error: refs/remotes/origin/HEAD: invalid sha1 pointer 0000000000000000000000000000000000000000
error: refs/remotes/origin/master: invalid sha1 pointer 0000000000000000000000000000000000000000
error: HEAD: invalid reflog entry 8f1da374ffac3711f8cdde57379f90cb03bbb9ea
error: refs/heads/master: invalid reflog entry 8f1da374ffac3711f8cdde57379f90cb03bbb9ea
error: bad ref for .git/logs/refs/remotes/origin/HEAD
error: bad ref for .git/logs/refs/remotes/origin/master
error: ac2fcd052804fb7adac465220da5bcb04d008fc7: invalid sha1 pointer in cache-tree
broken link from    tree b0d598ef5427d59ed31eb1b315c761fc89af40b7
              to    tree ac2fcd052804fb7adac465220da5bcb04d008fc7
dangling blob f4e39c36cc8df3f9f324c0ccca4ed6a7a3ffe6ac
dangling tree 068716abcf815b4eaf8f0fe74c3020bf6251bba0
dangling blob fb4cfe7c94e8b4d800fdb4935806577b2b99fd94
dangling blob 35cf2ca2ed03811c14f1598c50daacfab9032b8f
missing tree ac2fcd052804fb7adac465220da5bcb04d008fc7
dangling blob d056e38af637cf0de76dac5689a8c5e735d75793
dangling blob 3b3903cc7b4eb035e9c4508024acc3f81c015741
dangling blob b09c3cc95935a327ecf7fad8374f14c4e320f67e
3个回答

3
问题的根源很可能是您在操作系统写入USB键时,将USB键提前或期间取出。这导致了许多文件受损。
由于计算机因任何原因(如拔掉USB键、操作系统崩溃、断电、计算机着火等)而不当关闭会导致文件损坏,通常会损坏最近写入的文件。刚刚安静地待着的文件通常是完好无损的。虽然这里有很多警告,但这个一般的原则是适用的。
  • Files containing branch information are likely to be damaged if you were updating branches.

    Here, the damaged files included .git/refs/heads/master. This file should contain 41 bytes, which consist of a 40 byte text representation of the hash ID of the commit that should be considered the tip of branch master, followed by an ASCII newline character. There is no defininite way to guess which commit is the correct tip commit, which is why Git stores the answer to the question "what is the hash ID of the tip commit of master* in some files.

    (In some cases a valid answer might be available in .git/packed-refs, but in general, if .git/refs/heads/master exists, it is supposed to hold the correct answer. The correct answer changes over time, by you creating new commits, by you running git reset, by you running git branch -f, and so on.)

  • The damaged files are likely to include .git/index, which holds Git's index or staging area. Both terms mean the same thing in Git, and there is a third now-rarely-used term, the cache, for this same file. Except while you are using the index to work on a conflicted merge, the stuff in the index file is mostly pretty easy to re-compute with some effort: Git uses it to make things go faster, i.e., as a cache, hence the third rarely-used term.

    If (as is mostly the case) the cache contains nothing that cannot be recomputed, you can simply remove .git/index and then run git reset to recompute it. That would make all the "new file ..." messages go away, with the only thing lost being whether you had staged some particular updates.

    The word mostly appears here often because the index contains the hash ID of all of the blob objects ("files") that you intend to put in your next commit. If you have done something unusual, such as create a new unique version of a file and put that into the index and then remove it from everywhere else, the hash ID of that unique version of the file may not be easy to find anywhere. You could get this when using git add -p or git reset -p to stage a third variant of a file that differs from both the committed version and the work-tree version. In this case, removing and re-creating the index (with rm .git/index; git reset) will make the easiest way to reconstruct that file be to re-run the git add -p or git reset -p operation.

    In your case, it seems possible that the index was erased entirely, leading to Git claiming that every file was new. This is the same thing you get if you run rm .git/index, so you might as well run git reset. However, there's definitely something else going on here because of this message:

    error: ac2fcd052804fb7adac465220da5bcb04d008fc7: invalid sha1 pointer in cache-tree
    

    The cache-tree here refers to stuff in the index, using its old name cache. But since this is "cache", removing and re-creating the index could get past the problem, depending on other things.

  • The damaged files are likely to include some of your work-tree files. Git cannot help you with these: it is the rest of your computer that manages these files. Git writes copies of files to your work-tree, when you git checkout some existing commit, so that they are in a form that you can see and work with, but after that, it's all yours / your-computer's to manage.

  • The damaged files are likely to include some of Git's internal objects. In your case, this apparently did happen:

    error: unable to unpack header of .git/objects/8f/1da374ffac3711f8cdde57379f90cb03bbb9ea
    error: 8f1da374ffac3711f8cdde57379f90cb03bbb9ea: object corrupt or missing: .git/objects/8f/1da374ffac3711f8cdde57379f90cb03bbb9ea
    

    This means object 8f1da374ffac3711f8cdde57379f90cb03bbb9ea is damaged. It's impossible to tell what type this object was, much less what data were in it and whether they were valuable, without further information.

  • The damaged files may include various reflogs, stored in .git/logs/. In this case you got:

    invalid reflog entry 8f1da374ffac3711f8cdde57379f90cb03bbb9ea
    

    and:

    error: refs/heads/master: invalid reflog entry 8f1da374ffac3711f8cdde57379f90cb03bbb9ea
    

    using this same number. That's the damaged object hash ID we saw just a moment ago. Since branch names are required to point to commit objects, we can now guess that 8f1da374ffac3711f8cdde57379f90cb03bbb9ea was a commit object, before it got damaged. It was probably a recently-created commit, such as the one made by the git commit --amend.

从所有这些可以得出结论(但仍然是猜测),git commit --amend 的提交本身已经毁坏,并且 master 中损坏的哈希值应该是文本 8f1da374ffac3711f8cdde57379f90cb03bbb9ea。索引可能被损坏或完全删除,但通常只需删除它并重新构建其缓存方面即可,因此您可以在需要时再次删除它,然后从当前提交重建。一些 reflog 可能已经损坏,但是 reflog 都是辅助数据:其中没有任何关键信息与 Git 自身的操作无关,因此可以将受损的 reflog 截断。最大的问题是受损的 .git/objects/ 文件。
如果您愿意放弃该提交,则只需删除受损对象并将有效的哈希值放入 .git/refs/heads/master 中可能会自动修复。当您这样做时,fsck 仍然会发出警告:
error: ac2fcd052804fb7adac465220da5bcb04d008fc7: invalid sha1 pointer in cache-tree
broken link from    tree b0d598ef5427d59ed31eb1b315c761fc89af40b7
              to    tree ac2fcd052804fb7adac465220da5bcb04d008fc7
现在,第一行仍然是从.git/index中读取的内容。删除和重置将通过将其哈希ID写入.git/refs/heads/master来从您选择的提交中重建索引/缓存。如果树对象ac2fcd052804fb7adac465220da5bcb04d008fc7在存储库中没有其他用途,那么可能会使您拥有完整的存储库。
但是,如果不是这种情况,只有两种方法可以使这个特定的Git存储库自洽:
1. 删除直接或间接引用该树对象的所有提交以及它们的所有后代(如果有)。 2. 获取或重构丢失的对象。如果它在某个其他Git存储库(这个的克隆)中,那么获取它将更容易。在具有它的存储库中运行git cat-file -p ac2fcd052804fb7adac465220da5bcb04d008fc7。结果是树的文本表示形式。使用git hash-object -t tree -w 在USB键存储库中创建对象,以便现在存在。
请注意,此获取或重构方法适用于任何已损坏或丢失的对象:散列ID在每个克隆中都是全局唯一的,因此如果您可以在另一个克隆中找到该对象,则可以从该其他克隆中复制该对象。
结论:
最清晰的方法是使用某个其他克隆。查找相同存储库的未损坏的克隆。创建该克隆的克隆,成为您的新“修复克隆”结果。将来自已损坏存储库的新标签、树、blob和提交添加到该新克隆中,这些对象本身是未受损的。如遇到损坏对象,请尽可能恢复有用数据并继续。
结果始终是良好且有效的克隆,其中包含尽可能多的已恢复数据,并且您将确切地知道您恢复和丢失了什么。您甚至可以拥有一些文件缺失的提交(因为其内部blob对象已被无法恢复地损坏):您可以在某个地方保存注释,比如使用git notes或仅仅在纸上,并稍后返回并重构您可以的内容。

这种方法往往比较缓慢且痛苦。你使用的方法——尝试在原地修复受损克隆体——更快且更容易,但可能会留下一些隐藏的问题(例如,在你有意破坏受损克隆体之后、不记得的提交会丢失)。


非常感谢你详尽的回复!我从中学到了很多,相信这对其他人也会很有帮助。那么,是不是需要运行某种清理程序来清除孤立或损坏的对象?或者只要通过git fsck命令,就可以保证未来不会出现问题(例如,通过fsck命令表示将来重新打包时不会因为孤立或损坏的对象而失败)? - ninjagecko
如果 git fsck 认为一切正常,那么它们应该是真的正常。历史上有一些问题 git fsck 没有发现。(大多数都是相当小的问题,例如意外允许包含前导零的 mode 字符串的树对象,这种情况从不太可能到系统崩溃/移除驱动器类型的损坏。)因此,不能完全保证,但非常可靠。 - torek

1
Git 2.40(2023年第一季度)应该在类似的情况下更加健壮(中断拉取)。
首先,不要忘记激活core.fsync=reference,就像我在这里提到的(Git 2.36+)一样。
Git 2.40修复了fsync序列,以刷新输出到磁盘时遗漏的$GIT_DIR/packed-refs文件。
查看 提交ce54672(2022年12月20日),由Patrick Steinhardt (pks-t)提交。
(由Junio C Hamano -- gitster --合并于提交3ed91c5,2023年1月2日)

refs:通过正确同步打包引用到磁盘来修复损坏

签名作者:Patrick Steinhardt

在GitLab最近收到一份报告,其中一个存储库在节点硬件崩溃后仍然被留下了一个损坏的“packed-refs”文件,即使设置了“core.fsync = reference”。
理论上,如果我们正确地执行原子重命名操作,应该不会发生这种情况,以便:
1. 将数据写入临时文件。
2. 同步临时文件到磁盘。
3. 将临时文件重命名为目标文件。
因此,如果我们在编写“packed-refs”文件的过程中崩溃,我们只能看到文件的旧状态或新状态。
当我们编写“packed-refs”文件时,虽然我们已经执行了重命名操作,但确实存在一个陷阱:我们使用“FILE *”流来编写临时文件,但在将其同步到磁盘之前没有刷新它。
因此,任何仍在缓冲区中的数据都不会被同步,机器的崩溃可能会导致损坏。
通过在fsync之前刷新文件流来修复此错误。

0

[暂时回答自己的问题,但我想接受一个实际可行的答案]

  • 电脑1:
    • 在第一个可能有冲突的存储库上运行git fsck以确保不损坏它
    • 将存储库文件夹复制到电脑1的备份中
    • 在电脑1上提交任何更改(并/或者如果只有你可以重置)
    • 将更改推送到USB密钥(选项:之前在USB密钥上运行git fsck;不确定如何在裸存储库上运行因此将其克隆到临时存储库...)
  • 电脑2:
    • 将电脑2上的损坏存储库文件夹复制到备份中
    • 从USB密钥上使用git clone命令来恢复存储库

这显然不是一个真正的答案,但现在足够好用。正确的解决方法可能会对社群有所好处。


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