Come recuperare gli oggetti Git danneggiati da un guasto del disco rigido?


92

Ho avuto un errore del disco rigido che ha provocato il danneggiamento di alcuni file di un repository Git. Durante l'esecuzione git fsck --fullottengo 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 pack lo ha già danneggiato. Quindi penso di dover 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.

Puoi darmi suggerimenti su come riparare il mio repository?


2
Questo è appena successo a me. Non voglio fare confusione con gli oggetti git ... quindi ho ri-clonato il progetto dal repository remoto in una nuova cartella, quindi copia semplicemente tutti i file dai miei repository problematici (esclusa la .gitcartella ovviamente) nel repository appena clonato ... e poi fatto git statusnel nuovo repository ... git rileva correttamente tutte le modifiche interessate ai miei file e posso ricominciare il mio lavoro.
Rosdi Kasim

Risposte:


82

In alcuni backup precedenti, gli oggetti danneggiati potrebbero essere stati compressi 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 manualmente.

A causa di git hash-object, git mktreee git commit-treenon scrivere gli oggetti perché si trovano nella confezione, 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)

Tu 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 showo git cat-fileo git unpack-file; quindi puoi git hash-object -wriscrivere l'oggetto nel tuo repository corrente.

Se il tipo è albero: è possibile utilizzare git ls-treeper ripristinare l'albero dai backup precedenti; quindi git mktreeper riscriverlo nel tuo attuale repository.

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

Ovviamente, eseguirò il backup della tua copia di lavoro originale prima di iniziare questo processo.

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


1
Grazie, questo mi ha salvato! Pubblicherò i miei passi esatti come risposta separata.
Christian

Solo una correzione: il comando per finisce con "fatto" e non "fine".
Felipe

Sto provando a farlo ma .git/objects/pack/è vuoto
kirill_igum

per me un; mancava dopo git unpack-objects -r <$ i
mithrandir

@mithrandir: se metti "fatto" nella riga precedente: sì, hai bisogno di un punto e virgola. Se scrivi esattamente quello che ho scritto, non lo fai.
Daniel Fanjul

38

Banengusk mi stava mettendo sulla strada giusta. Per ulteriore riferimento, voglio pubblicare i passaggi che ho seguito per correggere il danneggiamento del mio repository. Ho avuto la fortuna di trovare tutti gli oggetti necessari nei vecchi pacchetti 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!

3
In aggiunta a questo: se il backup contiene i file mancanti in un pacchetto, il modo corretto per estrarre un blob dal pacchetto è 'git cat-file blob <SHA1>> file.dat' e riportarlo nella directory danneggiata repo, fai 'git hash-object -w file.dat', come nella risposta di Daniel.
Emil Styrke

Come trovi l'ultimo pacchetto non danneggiato? grazie
Romain Ourgorry,

18

Inizialmente prova 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 i problemi, prova puoi:

  • rimuovere 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
    
  • rimuovere tutti gli oggetti vuoti, es

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • controlla un messaggio di "collegamento interrotto" 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 emette l'SHA1 mancante (4b945 ..) ora hai finito!

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

    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 avevi potrebbe non essere l'albero di primo livello, quindi devi capire in quale sottodirectory si trovava da solo), quindi ora puoi ricreare il oggetto mancante con hash-object di nuovo.

  • 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 noterà il danneggiamento. Quindi usa il comando idraulico git update-ref -d $ ref invece. Nota che in caso di rami locali, questo comando potrebbe lasciare la configurazione del ramo obsoleto in .git / config. Può essere cancellato manualmente (cerca la sezione [branch "$ ref"]).

  • Dopo che tutti gli arbitri sono puliti, potrebbero esserci ancora commit non funzionanti nel reflog. Puoi cancellare tutti i reflog usando git reflog expire --expire = now --all. Se non vuoi perdere tutti i tuoi reflog, puoi cercare i singoli ref per reflog non funzionanti:

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

    (Notare l'aggiunta dell'opzione -g a git rev-list.) Quindi, utilizzare git reflog expire --expire = now $ ref su ciascuno di essi. Quando tutti i ref e reflog non funzionanti sono stati eliminati, eseguire git fsck --full per verificare che il repository sia pulito. Gli oggetti penzolanti vanno bene.


Di seguito puoi trovare un utilizzo avanzato dei comandi che potenzialmente possono causare la perdita dei tuoi dati nel tuo repository git se non usati saggiamente, quindi fai un backup prima di danneggiare accidentalmente ulteriormente il tuo git. Prova a tuo rischio se sai cosa stai facendo.


Per spostare 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 danneggiato in git per la rimozione, prova 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, usa sed -Einvece di sed -r.


Un'altra idea è decomprimere tutti gli oggetti dai file pack per rigenerare tutti gli oggetti all'interno di .git / objects, quindi prova a eseguire i seguenti comandi all'interno del 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 rsync o copiare gli oggetti git da un altro repository, ad es

$ 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 riparare il ramo rotto durante il tentativo di checkout come segue:

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

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

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

Nel caso in cui git ti porti in uno stato disconnesso, controlla mastere unisci in esso il ramo disconnesso.


Un'altra idea è rebase il master esistente in modo ricorsivo:

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

Guarda anche:


2

Ecco i passaggi che ho seguito per eseguire il ripristino da un oggetto BLOB danneggiato.

1) Identifica il BLOB danneggiato

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

Il blob danneggiato è 241091723c324aed77b2d35f97a05e856b319efd

2) Sposta il blob danneggiato in un luogo sicuro (per ogni evenienza)

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

3) Ottieni il genitore del blob danneggiato

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 genitore è 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Ottieni il nome del file corrispondente al blob danneggiato

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 copialo da qualche parte all'interno del tuo repository locale.

5) Aggiungi il file precedentemente danneggiato 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)

Questo non ha funzionato per me. Il passaggio 4 è arrivato git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152d, e ho anche provato di nuovo senza il passaggio 2, e il risultato è statogit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Ryan,

1

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

Immagino che il modo più semplice per risolvere questo problema in modo sicuro sia ripristinare il backup più recente non registrato e quindi selezionare selettivamente i file non danneggiati dai commit più recenti. In bocca al lupo!


1

Di seguito sono riportate due funzioni che possono essere utili se il backup è danneggiato o se si hanno anche alcuni backup parzialmente danneggiati (ciò può accadere se si esegue il backup degli oggetti danneggiati).

Esegui entrambi nel repository che stai cercando di recuperare.

Avviso standard: usalo solo se sei davvero disperato e hai eseguito il backup del tuo 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

0

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.