Domanda

Ho riscontrato un errore del disco rigido che ha provocato il danneggiamento di alcuni file di un repository Git. Quando eseguo git fsck --full ottengo il seguente output:

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

Ho dei backup del repository, ma l'unico backup che include il file del pacchetto lo ha già danneggiato. Quindi penso che devo trovare un modo per recuperare i singoli oggetti da diversi backup e in qualche modo istruire Git a produrre un nuovo pacchetto con solo oggetti corretti.

Potete per favore darmi suggerimenti su come riparare il mio repository?

È stato utile?

Soluzione

In alcuni backup precedenti, i tuoi oggetti danneggiati potrebbero essere stati impacchettati in file diversi o potrebbero essere ancora oggetti sciolti. Quindi i tuoi oggetti potrebbero essere recuperati.

Sembra che ci siano alcuni oggetti danneggiati nel tuo database. Quindi potresti farlo nel modo manuale.

A causa di git hash-object , git mktree e git commit-tree non scrivono gli oggetti perché si trovano nel pacchetto , quindi iniziare a fare questo:

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

(I tuoi pacchetti vengono spostati dal repository e decompressi di nuovo in esso; solo gli oggetti buoni sono ora nel database)

Puoi fare:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

e controlla il tipo di oggetto.

Se il tipo è BLOB: recupera il contenuto del file dai backup precedenti (con git show o git cat-file o git unpack-file ; quindi puoi git hash-object -w per riscrivere l'oggetto nel tuo repository corrente.

Se il tipo è albero: è possibile utilizzare git ls-tree per ripristinare l'albero dai backup precedenti; quindi git mktree per riscriverlo nel tuo repository corrente.

Se il tipo è commit: lo stesso con git show , git cat-file e git commit-tree .

Naturalmente, farei il backup della tua copia di lavoro originale prima di iniziare questo processo.

Inoltre, dai un'occhiata a Come recuperare l'oggetto BLOB corrotto .

Altri suggerimenti

Banengusk mi stava mettendo sulla buona strada. Per ulteriori riferimenti, desidero pubblicare i passaggi che ho intrapreso per correggere il danneggiamento del mio repository. Ho avuto la fortuna di trovare tutti gli oggetti necessari nei pacchetti più vecchi o nei backup del repository.

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

Prova inizialmente i seguenti comandi (riesegui di nuovo se necessario):

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

E poi hai ancora problemi, prova a:

  • rimuove tutti gli oggetti corrotti, ad es.

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • rimuove tutti gli oggetti vuoti, ad esempio

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • controlla un "link interrotto" messaggio di:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Questo ti dirà da quale file proviene il BLOB corrotto!

  • per recuperare il file, potresti essere davvero fortunato e potrebbe essere la versione che hai già estratto nel tuo albero di lavoro:

    git hash-object -w my-magic-file
    

    di nuovo, e se restituisce lo SHA1 mancante (4b945 ..) ora hai finito!

  • supponendo che fosse una versione precedente che era rotta, il modo più semplice per farlo è fare:

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

    e questo ti mostrerà l'intero registro per quel file (tieni presente che l'albero che hai potrebbe non essere l'albero di livello superiore, quindi devi capire in quale sottodirectory era da solo), quindi puoi ora ricrea nuovamente l'oggetto mancante con hash-object.

  • per ottenere un elenco di tutti i ref con commit, alberi o BLOB mancanti:

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

    Potrebbe non essere possibile rimuovere alcuni di questi ref usando i normali comandi branch -d o tag -d, poiché moriranno se git nota la corruzione. Quindi usa invece il comando idraulico git update-ref -d $ ref. Si noti che nel caso di filiali locali, questo comando potrebbe lasciare una configurazione di rami obsoleta in .git / config. Può essere eliminato manualmente (cerca la sezione [branch " $ ref "]).

  • Dopo che tutti i ref sono puliti, nel reflog potrebbero esserci ancora interruzioni di commit. Puoi cancellare tutti i reflog usando git reflog expire --expire = now --all. Se non si desidera perdere tutti i reflog, è possibile cercare i ref ref singoli per reflog interrotti:

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

    (Nota l'opzione -g aggiunta per git rev-list.) Quindi, usa git reflog expire --expire = now $ ref su ognuno di questi. Quando tutti i ref e reflog non funzionanti, esegui git fsck --full per verificare che il repository sia pulito. Gli oggetti pendenti sono ok.


Di seguito puoi trovare un uso avanzato di comandi che potenzialmente possono causare la perdita dei tuoi dati nel tuo repository git se non usati saggiamente, quindi fai un backup prima di fare accidentalmente ulteriori danni a git. Prova a tuo rischio e pericolo se sai cosa stai facendo.


Per estrarre il ramo corrente sopra il ramo a monte dopo il recupero:

$ git pull --rebase

Puoi anche provare a controllare il nuovo ramo ed eliminare quello vecchio:

$ git checkout -b new_master origin/master

Per trovare l'oggetto corrotto in git per la rimozione, provare il seguente comando:

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

Per OSX, utilizzare sed -E anziché sed -r .


Un'altra idea è di decomprimere tutti gli oggetti dai file pack per rigenerare tutti gli oggetti all'interno di .git / objects, quindi prova a eseguire i seguenti comandi nel tuo repository:

$ 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

Se sopra non aiuta, puoi provare a risincronizzare o copiare gli oggetti git da un altro repository, ad esempio

$ 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

Per correggere il ramo spezzato quando si tenta di effettuare il checkout come segue:

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

Prova a rimuoverlo ed esegui nuovamente il checkout dall'upstream:

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

Nel caso in cui git ti porti nello stato distaccato, controlla il master e unisci in esso il ramo staccato.


Un'altra idea è quella di riformulare ricorsivamente il master esistente:

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

Vedi anche:

Ecco i passaggi che ho seguito per recuperare da un oggetto BLOB corrotto.

1) Identifica BLOB corrotto

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

Il BLOB corrotto è 241091723c324aed77b2d35f97a05e856b319efd

2) Spostare il BLOB corrotto in un posto sicuro (per ogni evenienza)

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

3) Ottieni genitore di BLOB corrotto

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

L'hash del genitore è 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Ottieni il nome del file corrispondente al BLOB corrotto

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

Trova questo particolare file in un backup o nel repository git upstream (nel mio caso è dump.tar.gz ). Quindi copiarlo da qualche parte all'interno del repository locale.

5) Aggiungi file corrotto in precedenza nel database degli oggetti git

git hash-object -w dump.tar.gz

6) Festeggia!

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)

Il checkout di Git può effettivamente selezionare singoli file da una revisione. Basta dargli l'hash di commit e il nome del file. Informazioni più dettagliate qui.

Immagino che il modo più semplice per risolvere questo problema in modo sicuro sia quello di ripristinare il più recente backup senza commit e quindi selezionare selettivamente i file non corretti dai commit più recenti. Buona fortuna!

Qui ci sono due funzioni che possono aiutare se il tuo backup è danneggiato, o hai anche alcuni backup parzialmente corrotti (questo può accadere se esegui il backup degli oggetti danneggiati).

Esegui entrambi nel repository che stai tentando di ripristinare.

Avviso standard: utilizzare solo se si è davvero alla disperata ricerca e si è eseguito il backup del repository (danneggiato). Questo potrebbe non risolvere nulla, ma almeno dovrebbe evidenziare il livello di corruzione.

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

e

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

Ho risolto questo problema per aggiungere alcune modifiche come git add -A e git commit di nuovo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top