如何恢复受硬盘故障损坏的Git对象?

我有一个硬盘故障,导致一些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 

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

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

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

看来你的数据库中有一些不好的对象。 所以你可以用手工方式来做。

由于git hash-objectgit mktreegit commit-tree不会写对象,因为它们在包中find,那么就开始这样做:

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

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

你可以做:

 git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee 

并检查对象的types。

如果types是blob:从以前的备份中检索文件的内容(使用git show或者git cat-file或者git unpack-file ;那么你可以使用git hash-object -w来重写当前版本库中的对象。

如果types是树:可以使用git ls-tree tree从前面的备份中恢复树; 然后git mktree再次写入你的当前库。

如果types是commit:与git showgit cat-filegit commit-tree

当然,我会在开始这个过程之前备份你原来的工作副本。

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

巴南戈斯正在把我放在正确的轨道上。 为了进一步的参考,我想发布我修复我的存储库损坏的步骤。 我很幸运地find所有需要的对象,无论是在较旧的包还是在存储库备份中。

 # 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! 

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

 $ 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 

    再次,如果它输出缺less的SHA1(4b945 ..),你现在都完成了!

  • 假设这是一些破旧的旧版本,最简单的方法就是做:

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

    这将显示该文件的整个日志(请注意,您拥有的树可能不是顶级树,所以您需要找出自己的哪个子目录),那么现在可以重新创build再次丢失哈希对象的对象。

  • 获取缺less提交,树木或blob的所有ref的列表:

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

    使用常规分支-d或标签-d命令可能无法删除其中的一些ref,因为如果git发现损坏,它们将会死亡。 所以使用pipe道命令git update-ref -d $ ref来代替。 请注意,在本地分支的情况下,该命令可能会在.git / config后面留下陈旧的分支configuration。 它可以手动删除(查找[分支“$ ref”]部分)。

  • 毕竟refs是干净的,在reflog中可能还有违规提交。 你可以使用git reflog expire –expire = now –all清除所有reflog。 如果你不想失去你所有的推荐日志,你可以search个人参考文献:

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

    (注意添加-g选项git rev-list。)然后,使用git reflog expire –expire = now $ ref在每个这些。 当所有的错误refs和reflog都没有了,运行git fsck –full来检查版本库是否干净。 摇晃的物体是好的。


下面你可以find高级用法的命令,如果不明智地使用,可能会导致你的数据在你的git仓库中丢失,所以在你意外地对你的git造成进一步的损害之前进行备份。 如果你知道自己在做什么,试着自己承担风险。


在获取之后将当前分支拉到上游分支的顶部:

 $ git pull --rebase 

您也可以尝试结帐新分支并删除旧分支:

 $ git checkout -b new_master origin/master 

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

 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


其他的想法是从pack文件中解压缩所有对象以重新生成.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 

如果上面没有帮助,你可以尝试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并合并到分离的分支。


另一个想法是recursion地重新设置现有的主设备:

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

也可以看看:

  • 一些技巧重buildBlob对象,以修复损坏的存储库。
  • 如何修复损坏的存储库?
  • 如何从存储库中删除所有破碎的裁判?
  • 如何修复损坏的git存储库? (seeques)
  • 如何修复损坏的git存储库? (qnundrum)
  • 在Git中使用SourceTree时出现错误:“摘要”在代码128中失败:致命:无法读取树
  • 恢复损坏的Git裸仓库
  • 恢复损坏的git存储库
  • 如何解决git错误:对象是empy / corrupt
  • 如何诊断和修复git fatal:无法读取树
  • 如何处理这个git错误
  • 如何修复损坏的git存储库?
  • 如何在Git的另一个分支上“覆盖”而不是“合并”分支?
  • 如何从另一个分支完全replacegit中的master分支?
  • Git:“腐败的松散物体”
  • Git reset = fatal:无法读取树

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

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

以下是我从一个损坏的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)把腐败的斑点移到一个安全的地方(以防万一)

 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仓库find这个特定的文件(在我的情况下是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) 

如果您的备份损坏,或者您也有一些部分损坏的备份(这可能会发生,如果您备份损坏的对象),这可能有助于两个function。

在试图恢复的回购中运行。

标准警告:只有当你真的绝望,你已经备份(损坏)的回购。 这可能无法解决任何问题,但至less应该突出腐败的程度。

 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 

我已经解决了这个问题添加一些像git add -A和git commit的改变。