ハードディスク障害によって損傷したGitオブジェクトを回復するにはどうすればよいですか?


92

ハードディスクに障害が発生したため、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に指示する必要があると思います。

リポジトリを修正する方法のヒントを教えてください。


2
これはちょうど私に起こりました。私はgitオブジェクトを台無しにしたくありません...そのため、プロジェクトをリモートリポジトリから新しいフォルダーに再クローンし、問題のあるリポジトリ(.gitもちろんフォルダーを除く)からすべてのファイルを新しくクローンしたリポジトリにコピーします...その後git status、新しいリポジトリで実行しました... gitはファイルへの影響を受けるすべての変更を正しく検出し、作業を再開できます。
Rosdi Kasim

回答:


82

以前の一部のバックアップでは、不良オブジェクトが別のファイルにパックされているか、まだルーズオブジェクトである可能性があります。したがって、オブジェクトが回復される可能性があります。

データベースにいくつかの不良オブジェクトがあるようです。だからあなたはそれを手動で行うことができます。

以下のためgit hash-objectgit 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 showまたはgit cat-fileorまたはgit unpack-file;を使用git hash-object -wして、現在のリポジトリのオブジェクトを書き換えることができます。

タイプがツリーの場合:git ls-tree以前のバックアップからツリーを回復するために使用できます。次にgit mktree、現在のリポジトリに再度書き込みます。

タイプがcommitの場合:git showgit cat-fileおよびと同じgit commit-treeです。

もちろん、このプロセスを開始する前に、元の作業コピーをバックアップします。

また、破損したBlobオブジェクトを回復する方法もご覧ください。


1
ありがとう、助かった!正確な手順は別の回答として掲載します。
クリスチャン

ただの訂正です。コマンドは「終了」ではなく「終了」で終わります。
フェリペ

私はこれを実行しようとしていますが.git/objects/pack/、空です
kirill_igum 2012

私にとって git unpack-objects -r <$ iの後に欠落していた
mithrandir

@mithrandir:前の行に「完了」を入れた場合:はい、セミコロンが必要です。私が書いたものを正確にタイプすれば、タイプしません。
Daniel Fanjul 2014年

38

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!

3
これに追加:バックアップに不足しているファイルがパック内にある場合、パックからblobを取得する適切な方法は「git cat-file blob <SHA1>> file.dat」であり、それを損傷したものに戻すリポジトリ、ダニエルの答えのように、 'git hash-object -w file.dat'を実行します。
Emil Styrke 2013年

破損していない最後のパックをどのようにして見つけますか?ありがとう
Romain Ourgorry 16

18

最初に次のコマンドを試します(必要に応じて再実行します)。

$ 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
    

    これで、そのファイルのログ全体が表示されます(所有していたツリーが最上位のツリーではない可能性があるので、自分でどのサブディレクトリにあるかを確認する必要があることに注意してください)。次に、ハッシュオブジェクトを持つオブジェクトが再び見つかりません。

  • コミット、ツリー、ブロブが欠落しているすべての参照のリストを取得するには:

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

    通常のブランチ-dまたはタグ-dコマンドを使用してこれらの参照の一部を削除できない場合があります。gitが破損に気付いた場合、これらの参照は停止するためです。したがって、代わりに配管コマンドgit update-ref -d $ refを使用してください。ローカルブランチの場合、このコマンドは古いブランチ構成を.git / configに残す可能性があることに注意してください。手動で削除できます([branch "$ ref"]セクションを探してください)。

  • すべての参照がクリーンになった後も、参照ログに壊れたコミットがある可能性があります。git reflog expire --expire = now --allを使用して、すべてのreflogをクリアできます。すべてのreflogを失いたくない場合は、個々のrefで壊れた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を使用します。壊れた参照と参照ログがすべてなくなったら、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

上記の方法で問題が解決しない場合は、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してデタッチされたブランチにマージします。


別のアイデアは、既存のマスターを再帰的にリベースすることです:

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

以下も参照してください。


2

以下は、破損したblobオブジェクトから回復するために実行した手順です。

1)破損したブロブを特定する

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リポジトリ(私の場合は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)

これは私にはうまくいきませんでした。ステップ4はになり、git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152dステップ2なしでもう一度やり直しました。結果はgit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Ryan

1

Gitチェックアウトは、リビジョンから個々のファイルを実際に取り出すことができます。コミットハッシュとファイル名を指定するだけです。詳細はこちら。

これを安全に修正する最も簡単な方法は、コミットされていない最新のバックアップに戻し、破損していないファイルを新しいコミットから選択的に選択することだと思います。幸運を!


1

バックアップが破損している場合、または部分的に破損しているバックアップがいくつかある場合に役立つ2つの機能を次に示します(破損したオブジェクトをバックアップする場合に発生する可能性があります)。

回復しようとしているリポジトリで両方を実行します。

標準の警告:本当に必死で、(破損した)リポジトリをバックアップした場合にのみ使用してください。これは何も解決しないかもしれませんが、少なくとも腐敗のレベルを強調する必要があります。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.