Sabit disk arızasından zarar gören Git nesneleri nasıl kurtarılır?


92

Git deposundaki bazı dosyaların zarar görmesine neden olan bir sabit disk hatası yaşadım. Çalıştırırken git fsck --fullaşağıdaki çıktıyı alıyorum:

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

Deponun yedeklerine sahibim, ancak paket dosyasını içeren tek yedeklemede zaten zarar görmüş. Bu yüzden, tek tek nesneleri farklı yedeklemelerden almanın bir yolunu bulmam ve bir şekilde Git'e yalnızca doğru nesnelerle yeni bir paket üretmesi talimatını vermem gerektiğini düşünüyorum.

Lütfen bana depomu nasıl düzelteceğime dair ipuçları verebilir misiniz?


2
Bu benim başıma geldi. Git nesneleri ile uğraşmak istemiyorum ... bu yüzden projeyi uzak depodan yeni bir klasöre yeniden klonladım ve sonra sorunlu depolarımdaki tüm dosyaları .git(tabii ki klasör hariç ) yeni kopyalanmış depoya kopyaladım ... ve sonra git statusyeni depoda yaptım ... git, dosyalarımdaki tüm etkilenen değişiklikleri doğru bir şekilde algılar ve işime yeniden başlayabilirim.
Rosdi Kasim

Yanıtlar:


82

Önceki bazı yedeklemelerde, kötü nesneleriniz farklı dosyalarda paketlenmiş olabilir veya henüz gevşek nesneler olabilir. Böylece nesneleriniz kurtarılabilir.

Veritabanınızda birkaç kötü nesne var gibi görünüyor. Böylece manuel şekilde yapabilirsiniz.

Çünkü git hash-object, git mktreeve git commit-treeonlar paketinde bulunan çünkü nesneleri yazmayın, o zaman yapmaya başlayabilir:

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

(Paketleriniz depodan çıkarılır ve tekrar paketten çıkarılır; artık veritabanında yalnızca iyi nesneler bulunur)

Yapabilirsin:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

ve nesnenin türünü kontrol edin.

Tür blob ise: dosyanın içeriğini önceki yedeklemelerden alın ( git showveya git cat-fileveya ile git unpack-file; o zaman git hash-object -wnesneyi mevcut deponuzda yeniden yazabilirsiniz.

Tür ağaç ise: git ls-treeağacı önceki yedeklemelerden kurtarmak için kullanabilirsiniz ; ardından git mktreemevcut deponuza tekrar yazmak için.

İle aynı: tip işlemek ise git show, git cat-fileve git commit-tree.

Tabii ki, bu işleme başlamadan önce orijinal çalışma kopyanızın yedeğini alırdım.

Ayrıca Bozuk Blob Nesnesi Nasıl Kurtarılır konusuna bakın .


1
Teşekkür ederim, bu beni kurtardı! Tam adımlarımı ayrı bir cevap olarak göndereceğim.
Hıristiyan

Sadece bir düzeltme: "bitti" ile biten komut, "son" değil.
Felipe

bunu yapmaya çalışıyorum ama .git/objects/pack/boş
kirill_igum

benim için a; git unpack-objects -r <$ i
mithrandir'den

@mithrandir: Bir önceki satıra 'bitti' yazarsanız: evet, noktalı virgül gerekir. Yazdıklarımı aynen yazarsan, yazmazsın.
Daniel Fanjul

38

Banengusk beni doğru yola sokuyordu . Daha fazla referans için, depo bozukluğumu düzeltmek için attığım adımları yayınlamak istiyorum. Gerek eski paketlerde gerekse depo yedeklemelerinde gerekli tüm nesneleri bulacak kadar şanslıydım.

# 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
Buna ek olarak: Yedeklemede bir pakette eksik dosyalar varsa, pakette bir blob almanın uygun yolu 'git cat-file blob <SHA1>> file.dat' ve onu hasarlı dosyaya geri almaktır. repo, Daniel'in cevabında olduğu gibi 'git hash-object -w file.dat' yapın.
Emil Styrke

Bozulmamış son paketi nasıl bulursunuz? teşekkürler
Romain Ourgorry

18

İlk önce aşağıdaki komutları deneyin (gerekirse tekrar çalıştırın):

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

Ve sonra hala sorunlarınız varsa, şunları deneyin:

  • tüm bozuk nesneleri kaldırın, örn.

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • tüm boş nesneleri kaldırın, örn.

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • "bozuk bağlantı" mesajını şu şekilde kontrol edin:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Bu size bozuk blobun hangi dosyadan geldiğini söyleyecektir!

  • Dosyayı kurtarmak için gerçekten şanslı olabilirsiniz ve çalışma ağacınızda halihazırda kontrol ettiğiniz sürüm olabilir:

    git hash-object -w my-magic-file
    

    tekrar ve eğer eksik SHA1 (4b945 ..) çıktısını verirse, işiniz bitti!

  • Bozuk olanın daha eski bir sürüm olduğunu varsayarsak, bunu yapmanın en kolay yolu yapmaktır:

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

    ve bu size o dosyanın tüm günlüğünü gösterecektir (lütfen sahip olduğunuz ağacın en üst düzey ağaç olmayabileceğini fark edin, bu nedenle hangi alt dizinde kendi başınıza olduğunu bulmanız gerekir), sonra şimdi yeniden oluşturabilirsiniz hash-object ile eksik nesne tekrar.

  • eksik tamamlama, ağaç veya blob içeren tüm referansların bir listesini almak için:

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

    Git bozulmayı fark ederse ölecekleri için, bu referanslardan bazılarını normal dallanma -d veya etiket -d komutlarını kullanarak kaldırmak mümkün olmayabilir. Onun yerine git update-ref -d $ ref sıhhi tesisat komutunu kullanın. Yerel dallar olması durumunda, bu komutun .git / config içinde eski dal yapılandırmasını geride bırakabileceğini unutmayın. Manuel olarak silinebilir ([branch "$ ref"] bölümüne bakın).

  • Tüm referanslar temizlendikten sonra, reflog'da hala bozuk kayıtlar olabilir. Git reflog expire --expire = now --all kullanarak tüm yeniden günlükleri temizleyebilirsiniz. Tüm yeniden bloglarınızı kaybetmek istemiyorsanız, kırık yeniden bloglar için bireysel referansları arayabilirsiniz:

    $ (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'e eklenen -g seçeneğini not edin.) Sonra, git reflog expire --expire = now $ ref kullanın. Tüm bozuk referanslar ve yeniden günlükler gittiğinde, deponun temiz olup olmadığını kontrol etmek için git fsck --full komutunu çalıştırın. Sarkan nesneler tamam.


Aşağıda, akıllıca kullanılmadığı takdirde potansiyel olarak git deponuzdaki verilerinizin kaybolmasına neden olabilecek gelişmiş komut kullanımını bulabilirsiniz, bu nedenle yanlışlıkla git'inize daha fazla zarar vermeden önce bir yedekleme yapın. Ne yaptığınızı biliyorsanız, kendi riskinizi deneyin.


Geçerli dalı getirdikten sonra yukarı akış dalının üstüne çekmek için:

$ git pull --rebase

Ayrıca yeni şubeyi kontrol etmeyi ve eskisini silmeyi deneyebilirsiniz:

$ git checkout -b new_master origin/master

Git'te bozuk nesneyi kaldırmak için bulmak için aşağıdaki komutu deneyin:

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 için sed -Eyerine kullanın sed -r.


Diğer bir fikir, .git / objects içindeki tüm nesneleri yeniden oluşturmak için paket dosyalarındaki tüm nesneleri açmaktır, bu nedenle deponuzda aşağıdaki komutları çalıştırmayı deneyin:

$ 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

Yukarıdakiler yardımcı olmazsa, git nesnelerini başka bir depodan yeniden senkronize etmeyi veya kopyalamayı deneyebilirsiniz, örn.

$ 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

Aşağıdaki gibi ödeme yapmaya çalışırken bozuk dalı düzeltmek için:

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

Bunu kaldırmayı ve tekrar yukarı akıştan ödemeyi deneyin:

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

Git'in sizi müstakil duruma getirmesi durumunda, kontrol edin masterve ayrılmış şubeyi onunla birleştirin.


Başka bir fikir, mevcut ana makineyi yinelemeli olarak yeniden oluşturmaktır:

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

Ayrıca bakınız:


2

Bozuk bir blob nesnesinden kurtarmak için izlediğim adımlar.

1) Bozuk blobu tanımlayın

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

Bozuk blob, 241091723c324aed77b2d35f97a05e856b319efd

2) Bozuk blobu güvenli bir yere taşıyın (her ihtimale karşı)

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

3) Bozuk blobun üstünü alın

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

Üst hash, 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 şeklindedir .

4) Bozuk blob'a karşılık gelen dosya adını alın

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

Bu belirli dosyayı bir yedekte veya yukarı akış git deposunda bulun (benim durumumda dump.tar.gz ). Ardından yerel deponuzun içinde bir yere kopyalayın.

5) Git nesne veritabanına önceden bozulmuş dosyayı ekleyin

git hash-object -w dump.tar.gz

6) Kutlayın!

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)

Bu benim için işe yaramadı. 4. adım sonuçlandı git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152dve ben de 2. Adım olmadan her şeyi tekrar denedim ve bu sonuçla sonuçlandıgit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Ryan,

1

Git checkout aslında bir revizyondan tek tek dosyaları seçebilir. Sadece commit karmasını ve dosya adını verin. Daha ayrıntılı bilgi burada.

Sanırım bunu güvenli bir şekilde düzeltmenin en kolay yolu, en yeni taahhüt edilmemiş yedeklemeye geri dönmek ve ardından, daha yeni kayıtlardan bozulmamış dosyaları seçerek seçmektir. İyi şanslar!


1

Aşağıda, yedeklemeniz bozuksa veya birkaç kısmen bozuk yedeğiniz varsa yardımcı olabilecek iki işlev vardır (bu, bozuk nesneleri yedeklerseniz olabilir).

Kurtarmaya çalıştığınız depoda ikisini de çalıştırın.

Standart uyarı: Yalnızca gerçekten çaresizseniz ve (bozuk) deponuzu yedeklediyseniz kullanın. Bu hiçbir şeyi çözmeyebilir, ancak en azından yolsuzluk seviyesini vurgulamalıdır.

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

ve

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

0

Git add -A ve git commit gibi bazı değişiklikleri tekrar eklemek için bu sorunu çözdüm.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.