94

我遇到了硬盘故障,导致 Git 存储库的某些文件损坏。运行时,git fsck --full我得到以下输出:

error: .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack SHA1 checksum mismatch
error: index CRC mismatch for object 6c8cae4994b5ec7891ccb1527d30634997a978ee from .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack at offset 97824129
error: inflate: data stream error (invalid code lengths set)
error: cannot unpack 6c8cae4994b5ec7891ccb1527d30634997a978ee from .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack at offset 97824129
error: inflate: data stream error (invalid stored block lengths)
error: failed to read object 0dcf6723cc69cc7f91d4a7432d0f1a1f05e77eaa at offset 276988017 from .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack
fatal: object 0dcf6723cc69cc7f91d4a7432d0f1a1f05e77eaa is corrupted

我有存储库的备份,但包含包文件的唯一备份已经损坏。所以我认为我必须找到一种方法来从不同的备份中检索单个对象,并以某种方式指示 Git 生成一个只包含正确对象的新包。

你能给我提示如何修复我的存储库吗?

4

8 回答 8

86

在以前的一些备份中,您的坏对象可能已经打包在不同的文件中,或者可能是松散的对象。所以你的对象可能会被恢复。

您的数据库中似乎有一些坏对象。所以你可以用手动的方式来做。

因为git hash-object,git mktree并且git commit-tree不要写对象,因为它们是在包中找到的,然后开始这样做:

mv .git/objects/pack/* <somewhere>
for i in <somewhere>/*.pack; do
  git unpack-objects -r < $i
done
rm <somewhere>/*

(您的包从存储库中移出,并在其中再次解包;现在只有好的对象在数据库中)

你可以做:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

并检查对象的类型。

如果类型是 blob:从以前的备份中检索文件的内容(使用git showorgit cat-filegit unpack-file; 那么您可以git hash-object -w重写当前存储库中的对象。

如果类型是树:您可以使用git ls-tree从以前的备份中恢复树;然后git mktree在您当前的存储库中再次编写它。

如果类型是提交:与git show,git cat-file和相同git commit-tree

当然,我会在开始此过程之前备份您的原始工作副本。

另外,请查看如何恢复损坏的 Blob 对象

于 2009-04-29T13:09:54.833 回答
37

Banengusk让我走上了正轨。为了进一步参考,我想发布我为修复存储库损坏而采取的步骤。我很幸运能够在旧包或存储库备份中找到所有需要的对象。

# Unpack last non-corrupted pack
$ mv .git/objects/pack .git/objects/pack.old
$ git unpack-objects -r < .git/objects/pack.old/pack-012066c998b2d171913aeb5bf0719fd4655fa7d0.pack
$ git log
fatal: bad object HEAD

$ cat .git/HEAD 
ref: refs/heads/master

$ ls .git/refs/heads/

$ cat .git/packed-refs 
# pack-refs with: peeled 
aa268a069add6d71e162c4e2455c1b690079c8c1 refs/heads/master

$ git fsck --full 
error: HEAD: invalid sha1 pointer aa268a069add6d71e162c4e2455c1b690079c8c1
error: refs/heads/master does not point to a valid object!
missing blob 75405ef0e6f66e48c1ff836786ff110efa33a919
missing blob 27c4611ffbc3c32712a395910a96052a3de67c9b
dangling tree 30473f109d87f4bcde612a2b9a204c3e322cb0dc

# Copy HEAD object from backup of repository
$ cp repobackup/.git/objects/aa/268a069add6d71e162c4e2455c1b690079c8c1 .git/objects/aa
# Now copy all missing objects from backup of repository and run "git fsck --full" afterwards
# Repeat until git fsck --full only reports dangling objects

# Now garbage collect repo
$ git gc
warning: reflog of 'HEAD' references pruned commits
warning: reflog of 'refs/heads/master' references pruned commits
Counting objects: 3992, done.
Delta compression using 2 threads.
fatal: object bf1c4953c0ea4a045bf0975a916b53d247e7ca94 inconsistent object length (6093 vs 415232)
error: failed to run repack

# Check reflogs...
$ git reflog

# ...then clean
$ git reflog expire --expire=0 --all

# Now garbage collect again
$ git gc       
Counting objects: 3992, done.
Delta compression using 2 threads.
Compressing objects: 100% (3970/3970), done.
Writing objects: 100% (3992/3992), done.
Total 3992 (delta 2060), reused 0 (delta 0)
Removing duplicate objects: 100% (256/256), done.
# Done!
于 2009-05-04T13:46:24.047 回答
19

首先尝试以下命令(如果需要,请再次运行):

$ git fsck --full
$ git gc
$ git gc --prune=today
$ git fetch --all
$ git pull --rebase

然后你仍然有问题,尝试可以:

  • 删除所有损坏的对象,例如

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • 删除所有空对象,例如

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • 通过以下方式检查“断开的链接”消息:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    这将告诉您损坏的 blob 来自哪个文件!

  • 要恢复文件,您可能真的很幸运,它可能是您已经在工作树中签出的版本:

    git hash-object -w my-magic-file
    

    再次,如果它输出丢失的 SHA1 (4b945..) 你现在就完成了!

  • 假设它是一些旧版本被破坏,最简单的方法是:

    git log --raw --all --full-history -- subdirectory/my-magic-file
    

    这将向您显示该文件的整个日志(请注意,您拥有的树可能不是顶级树,因此您需要自己弄清楚它在哪个子目录中),然后您现在可以重新创建再次缺少带有散列对象的对象。

  • 获取所有缺少提交、树或 blob 的 ref 的列表:

    $ git for-each-ref --format='%(refname)' | while read ref; do git rev-list --objects $ref >/dev/null || echo "in $ref"; done
    

    使用常规的 branch -d 或 tag -d 命令可能无法删除其中的一些 ref,因为如果 git 注意到损坏,它们将会死掉。因此,请改用管道命令 git update-ref -d $ref。请注意,对于本地分支,此命令可能会在 .git/config 中留下陈旧的分支配置。它可以手动删除(查找 [branch "$ref"] 部分)。

  • 在所有 refs 都干净之后,reflog 中可能仍然存在损坏的提交。您可以使用 git reflog expire --expire=now --all 清除所有 reflog。如果您不想丢失所有的 reflog,您可以在各个 refs 中搜索损坏的 reflog:

    $ (echo HEAD; git for-each-ref --format='%(refname)') | while read ref; do git rev-list -g --objects $ref >/dev/null || echo "in $ref"; done
    

    (注意在 git rev-list 中添加了 -g 选项。)然后,在每个选项上使用 git reflog expire --expire=now $ref。当所有损坏的 refs 和 reflogs 都消失后,运行 git fsck --full 以检查存储库是否干净。悬空物体没问题。


您可以在下面找到命令的高级用法,如果使用不当,可能会导致 git 存储库中的数据丢失,因此请在不小心对 git 造成进一步损害之前进行备份。如果您知道自己在做什么,请自行承担风险。


获取后将当前分支拉到上游分支之上:

$ git pull --rebase

您也可以尝试检查新分支并删除旧分支:

$ git checkout -b new_master origin/master

要在 git 中查找损坏的对象以进行删除,请尝试以下命令:

while [ true ]; do f=`git fsck --full 2>&1|awk '{print $3}'|sed -r 's/(^..)(.*)/objects\/\1\/\2/'`; if [ ! -f "$f" ]; then break; fi; echo delete $f; rm -f "$f"; done

对于 OSX,请sed -E使用sed -r.


另一个想法是从包文件中解压所有对象以重新生成 .git/objects 中的所有对象,因此请尝试在存储库中运行以下命令:

$ cp -fr .git/objects/pack .git/objects/pack.bak
$ for i in .git/objects/pack.bak/*.pack; do git unpack-objects -r < $i; done
$ rm -frv .git/objects/pack.bak

如果上面没有帮助,您可以尝试从另一个 repo rsync 或复制 git 对象,例如

$ rsync -varu git_server:/path/to/git/.git local_git_repo/
$ rsync -varu /local/path/to/other-working/git/.git local_git_repo/
$ cp -frv ../other_repo/.git/objects .git/objects

在尝试结帐时修复损坏的分支,如下所示:

$ git checkout -f master
fatal: unable to read tree 5ace24d474a9535ddd5e6a6c6a1ef480aecf2625

尝试将其删除并再次从上游结帐:

$ git branch -D master
$ git checkout -b master github/master

如果 git 让您进入分离状态,请签出master并将分离的分支合并到其中。


另一个想法是递归地 rebase 现有的 master:

$ git reset HEAD --hard
$ git rebase -s recursive -X theirs origin/master

也可以看看:

于 2014-03-27T17:07:47.503 回答
2

以下是我从损坏的 blob 对象中恢复所遵循的步骤。

1) 识别损坏的 blob

git fsck --full
  error: inflate: data stream error (incorrect data check)
  error: sha1 mismatch 241091723c324aed77b2d35f97a05e856b319efd
  error: 241091723c324aed77b2d35f97a05e856b319efd: object corrupt or missing
  ...

损坏的 blob 是241091723c324aed77b2d35f97a05e856b319efd

2) 将损坏的 blob 移动到安全的地方(以防万一)

mv .git/objects/24/1091723c324aed77b2d35f97a05e856b319efd ../24/

3) 获取损坏 blob 的父级

git fsck --full
  Checking object directories: 100% (256/256), done.
  Checking objects: 100% (70321/70321), done.
  broken link from    tree 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180
              to    blob 241091723c324aed77b2d35f97a05e856b319efd

父哈希是0716831e1a6c8d3e6b2b541d21c4748cc0ce7180

4) 获取与损坏的 blob 对应的文件名

git ls-tree 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180
  ...
  100644 blob 241091723c324aed77b2d35f97a05e856b319efd    dump.tar.gz
  ...

在备份或上游 git 存储库中找到这个特定文件(在我的例子中是dump.tar.gz)。然后将其复制到本地存储库中的某个位置。

5)在git对象数据库中添加以前损坏的文件

git hash-object -w dump.tar.gz

6)庆祝!

git gc
  Counting objects: 75197, done.
  Compressing objects: 100% (21805/21805), done.
  Writing objects: 100% (75197/75197), done.
  Total 75197 (delta 52999), reused 69857 (delta 49296)
于 2015-10-30T03:51:51.993 回答
1

Git checkout 实际上可以从修订中挑选出单个文件。只需给它提交哈希和文件名。更详细的信息在这里。

我想安全地解决这个问题的最简单方法是恢复到最新的未提交备份,然后有选择地从较新的提交中挑选出未损坏的文件。祝你好运!

于 2009-04-29T10:01:13.477 回答
1

如果您的备份已损坏,或者您有一些部分损坏的备份(如果您备份损坏的对象,可能会发生这种情况),这里有两个功能可能会有所帮助。

在您尝试恢复的存储库中运行两者。

标准警告:仅在您真的很绝望并且您已经备份了(损坏的)回购时才使用。这可能无法解决任何问题,但至少应该突出腐败程度。

fsck_rm_corrupted() {
    corrupted='a'
    while [ "$corrupted" ]; do
        corrupted=$(                                  \
        git fsck --full --no-dangling 2>&1 >/dev/null \
            | grep 'stored in'                          \
            | sed -r 's:.*(\.git/.*)\).*:\1:'           \
        )
        echo "$corrupted"
        rm -f "$corrupted"
    done
}

if [ -z "$1" ]  || [ ! -d "$1" ]; then
    echo "'$1' is not a directory. Please provide the directory of the git repo"
    exit 1
fi

pushd "$1" >/dev/null
fsck_rm_corrupted
popd >/dev/null

unpack_rm_corrupted() {
    corrupted='a'
    while [ "$corrupted" ]; do
        corrupted=$(                                  \
        git unpack-objects -r < "$1" 2>&1 >/dev/null \
            | grep 'stored in'                          \
            | sed -r 's:.*(\.git/.*)\).*:\1:'           \
        )
        echo "$corrupted"
        rm -f "$corrupted"
    done
}

if [ -z "$1" ]  || [ ! -d "$1" ]; then
    echo "'$1' is not a directory. Please provide the directory of the git repo"
    exit 1
fi

for p in $1/objects/pack/pack-*.pack; do
    echo "$p"
    unpack_rm_corrupted "$p"
done
于 2015-11-07T22:07:06.567 回答
0

我已经通过添加一些更改来解决这个问题,就像一次git add -A又一次git commit

于 2017-03-27T18:38:33.583 回答
0

Daniel Fanjul 的解决方案看起来很有希望。我能够找到该 blob 文件并将其提取(“git fsck --full --no-dangling”、“git cat-file -t {hash}”、“git show {hash} > file.tmp”)但是当我尝试使用“git hash-object -w file.tmp”更新包文件时,它显示正确的哈希但错误仍然存​​在。

所以我决定尝试不同的方法。我可以简单地删除本地存储库并从远程下载所有内容,但是本地存储库中的某些分支提前 8 次提交,我不想丢失这些更改。由于那个很小的 ​​6kb mp3 文件,我决定完全删除它。我尝试了很多方法,但最好的方法来自这里:https ://itextpdf.com/en/blog/technical-notes/how-completely-remove-file-git-repository

我通过运行此命令“ git rev-list --objects --all | grep {hash} ”获得了文件名。然后我做了一个备份(强烈建议这样做,因为我失败了 3 次)然后运行命令:

java -jar bfg.jar --delete-files {文件名} --no-blob-protection 。

您可以从这里获取 bfg.jar 文件https://rtyley.github.io/bfg-repo-cleaner/所以根据文档我应该接下来运行这个命令:

“git reflog expire --expire=now --all && git gc --prune=now --aggressive”

当我这样做时,我在最后一步出现错误。因此,我从备份中恢复了所有内容,这一次,在删除文件后,我签出到分支(导致该错误),然后签回主目录,并且只有在一个接一个地运行命令之后:

"git reflog expire --expire=now --all" "git gc --prune=now --aggressive"

然后我将我的文件添加回其位置并提交。但是,由于许多本地提交已更改,我无法将任何内容推送到服务器。所以我备份服务器上的所有内容(以防万一我搞砸了),检查受影响的分支并运行命令“ git push --force ”。

我从这个案例中了解到了什么?GIT 很棒,但非常敏感...我应该可以选择简单地忽略一个 f... 6kb 文件我知道我在做什么。我不知道为什么“git hash-object -w”也不起作用=(吸取的教训,推送所有提交,不要等待,不时备份存储库。我也知道如何从存储库中删除文件,如果我永远需要=)

我希望这可以节省某人的时间

于 2021-01-31T07:21:26.957 回答