Bagaimana memulihkan objek Git yang rusak karena kegagalan hard disk?

92

Saya mengalami kegagalan hard disk yang mengakibatkan beberapa file di repositori Git rusak. Saat menjalankan git fsck --fullsaya mendapatkan output berikut:

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

Saya memiliki cadangan repositori, tetapi satu-satunya cadangan yang menyertakan file paket telah rusak. Jadi saya pikir saya harus mencari cara untuk mengambil objek tunggal dari backup yang berbeda dan entah bagaimana menginstruksikan Git untuk membuat paket baru dengan hanya objek yang benar.

Bisakah Anda memberi saya petunjuk cara memperbaiki repositori saya?

Kristen
sumber
2
Ini baru saja terjadi pada saya. Saya tidak ingin mengacaukan objek git ... jadi kloning ulang proyek dari repositori jarak jauh ke folder baru, lalu salin semua file dari repositori bermasalah saya (tidak termasuk .gitfolder tentunya) ke dalam repo yang baru saja dikloning ... dan kemudian melakukannya git statusdi repo baru ... git dengan benar mendeteksi semua perubahan yang terpengaruh pada file saya dan saya dapat memulai pekerjaan saya lagi.
Rosdi Kasim

Jawaban:

82

Dalam beberapa cadangan sebelumnya, objek buruk Anda mungkin telah dikemas dalam file berbeda atau mungkin merupakan objek lepas. Jadi objek Anda dapat dipulihkan.

Sepertinya ada beberapa objek buruk di database Anda. Jadi Anda bisa melakukannya dengan cara manual.

Karena git hash-object, git mktreedan git commit-treejangan tulis objek karena ditemukan dalam paket, maka mulailah melakukan ini:

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

(Paket Anda dipindahkan dari repositori, dan dibongkar lagi di dalamnya; hanya objek bagus yang sekarang ada di database)

Anda dapat melakukan:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

dan periksa jenis objeknya.

Jika jenisnya adalah blob: ambil konten file dari cadangan sebelumnya (dengan git showatau git cat-fileatau git unpack-file; Anda dapat git hash-object -wmenulis ulang objek tersebut di repositori Anda saat ini.

Jika jenisnya adalah pohon: Anda dapat menggunakan git ls-treeuntuk memulihkan pohon dari cadangan sebelumnya; lalu git mktreeuntuk menuliskannya lagi di repositori Anda saat ini.

Jika tipenya komit: sama dengan git show, git cat-filedan git commit-tree.

Tentu saja, saya akan membuat cadangan copy pekerjaan asli Anda sebelum memulai proses ini.

Juga, lihat Cara Memulihkan Objek Blob yang Rusak .

Daniel Fanjul
sumber
1
Terima kasih, itu menyelamatkan saya! Saya akan memposting langkah tepat saya sebagai jawaban terpisah.
Christian
Hanya koreksi: perintah untuk diakhiri dengan "selesai" dan bukan "akhir".
Felipe
saya mencoba melakukan ini tetapi .git/objects/pack/kosong
kirill_igum
bagi saya a; hilang setelah git unpack-objects -r <$ i
mithrandir
@mithrandir: jika Anda meletakkan 'selesai' di baris sebelumnya: ya, Anda perlu titik koma. Jika Anda mengetik persis apa yang saya tulis, Anda tidak.
Daniel Fanjul
38

Banengusk telah menempatkan saya di jalur yang benar. Untuk referensi lebih lanjut, saya ingin memposting langkah-langkah yang saya ambil untuk memperbaiki kerusakan repositori saya. Saya cukup beruntung untuk menemukan semua objek yang dibutuhkan baik dalam paket lama atau dalam cadangan repositori.

# 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!
Kristen
sumber
3
Menambahkan ke ini: Jika cadangan memiliki file yang hilang dalam satu paket, cara yang tepat untuk mengeluarkan blob dari paket adalah 'git cat-file blob <SHA1>> file.dat', dan mengembalikannya ke file yang rusak. repo, lakukan 'git hash-object -w file.dat', seperti dalam jawaban Daniel.
Emil Styrke
Bagaimana Anda menemukan paket terakhir yang tidak rusak? terima kasih
Romain Ourgorry
18

Coba perintah berikut pada awalnya (jalankan kembali jika perlu):

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

Dan kemudian Anda masih memiliki masalah, coba dapat:

  • hapus semua objek yang rusak, mis

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • singkirkan semua benda kosong, mis

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • periksa pesan "tautan rusak" dengan:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Ini akan memberi tahu Anda dari file apa gumpalan korup itu berasal!

  • untuk memulihkan file, Anda mungkin sangat beruntung, dan ini mungkin versi yang sudah Anda periksa di pohon kerja Anda:

    git hash-object -w my-magic-file
    

    sekali lagi, dan jika itu mengeluarkan SHA1 (4b945 ..) yang hilang, Anda sekarang sudah selesai!

  • dengan asumsi bahwa itu adalah versi lama yang rusak, cara termudah untuk melakukannya adalah dengan:

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

    dan itu akan menunjukkan kepada Anda seluruh log untuk file itu (harap diketahui bahwa pohon yang Anda miliki mungkin bukan pohon tingkat atas, jadi Anda perlu mencari tahu sendiri di subdirektori mana), lalu sekarang Anda dapat membuat ulang objek yang hilang dengan hash-object lagi.

  • untuk mendapatkan daftar semua ref dengan commit, tree, atau blob yang hilang:

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

    Beberapa ref tersebut mungkin tidak dapat dihapus menggunakan perintah branch -d atau tag -d biasa, karena mereka akan mati jika git mengetahui adanya kerusakan. Jadi gunakan perintah pipa git update-ref -d $ ref sebagai gantinya. Perhatikan bahwa dalam kasus cabang lokal, perintah ini mungkin meninggalkan konfigurasi cabang lama di .git / config. Ini dapat dihapus secara manual (cari bagian [cabang "$ ref"]).

  • Setelah semua ref bersih, mungkin masih ada komitmen yang rusak di reflog. Anda dapat menghapus semua reflog menggunakan git reflog expire --expire = now --all. Jika Anda tidak ingin kehilangan semua reflog Anda, Anda dapat mencari referensi individu untuk reflog yang rusak:

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

    (Perhatikan opsi -g yang ditambahkan ke git rev-list.) Kemudian, gunakan git reflog expire --expire = sekarang $ ref untuk masing-masingnya. Ketika semua ref dan reflog yang rusak hilang, jalankan git fsck --full untuk memeriksa apakah repositori bersih. Benda yang menggantung tidak apa-apa.


Di bawah ini Anda dapat menemukan penggunaan perintah tingkat lanjut yang berpotensi dapat menyebabkan hilangnya data Anda di repositori git jika tidak digunakan dengan bijak, jadi buatlah cadangan sebelum Anda secara tidak sengaja melakukan kerusakan lebih lanjut pada git Anda. Cobalah risiko Anda sendiri jika Anda tahu apa yang Anda lakukan.


Untuk menarik cabang saat ini di atas cabang upstream setelah mengambil:

$ git pull --rebase

Anda juga dapat mencoba untuk membayar cabang baru dan menghapus yang lama:

$ git checkout -b new_master origin/master

Untuk menemukan objek yang rusak di git untuk dihapus, coba perintah berikut:

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

Untuk OSX, gunakan sed -Ebukan sed -r.


Ide lainnya adalah membongkar semua objek dari file paket untuk membuat ulang semua objek di dalam .git / objek, jadi cobalah untuk menjalankan perintah berikut di dalam repositori Anda:

$ 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

Jika di atas tidak membantu, Anda dapat mencoba untuk rsync atau menyalin objek git dari repo lain, mis

$ 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

Untuk memperbaiki cabang yang rusak saat mencoba melakukan pembayaran sebagai berikut:

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

Coba hapus dan lakukan pembayaran dari upstream lagi:

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

Jika git membawa Anda ke status terlepas, lakukan pembayaran masterdan gabungkan ke dalamnya cabang yang terlepas.


Ide lain adalah untuk mendasarkan kembali master yang ada secara rekursif:

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

Lihat juga:

kenorb
sumber
2

Berikut adalah langkah-langkah yang saya ikuti untuk memulihkan dari objek blob yang rusak.

1) Identifikasi gumpalan yang korup

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

Blob yang rusak adalah 241091723c324aed77b2d35f97a05e856b319efd

2) Pindahkan gumpalan korup ke tempat yang aman (untuk berjaga-jaga)

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

3) Dapatkan induk dari gumpalan korup

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

Hash induk adalah 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Dapatkan nama file yang sesuai dengan gumpalan korup

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

Temukan file khusus ini dalam cadangan atau di repositori git hulu (dalam kasus saya ini adalah dump.tar.gz ). Kemudian salin di suatu tempat di dalam repositori lokal Anda.

5) Tambahkan file yang sebelumnya rusak dalam database objek git

git hash-object -w dump.tar.gz

6) Rayakan!

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)
Jonathan Maim
sumber
Ini tidak berhasil untuk saya. Langkah 4 menghasilkan git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152d, dan saya juga mencobanya lagi tanpa Langkah 2, dan itu menghasilkangit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Ryan
1

Git checkout sebenarnya dapat memilih file individual dari revisi. Berikan saja hash komit dan nama file. Info lebih detail ada di sini.

Saya kira cara termudah untuk memperbaikinya dengan aman adalah dengan kembali ke cadangan terbaru yang tidak terikat dan kemudian secara selektif memilih file yang tidak rusak dari tindakan yang lebih baru. Semoga berhasil!

Tim Lin
sumber
1

Berikut adalah dua fungsi yang dapat membantu jika cadangan Anda rusak, atau Anda juga memiliki beberapa cadangan yang rusak sebagian (ini mungkin terjadi jika Anda membuat cadangan objek yang rusak).

Jalankan keduanya di repo yang Anda coba pulihkan.

Peringatan standar: hanya gunakan jika Anda benar-benar putus asa dan Anda telah mencadangkan repo (rusak) Anda. Ini mungkin tidak menyelesaikan apa pun, tetapi setidaknya harus menyoroti tingkat korupsi.

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

dan

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
go2null
sumber
0

Saya telah menyelesaikan masalah ini untuk menambahkan beberapa perubahan seperti git add -A dan git commit lagi.

Dmitriy S
sumber