94 votes

Comment récupérer les objets Git endommagés par une panne de disque dur ?

J'ai eu une panne de disque dur qui a endommagé certains fichiers d'un référentiel Git. En exécutant git fsck --full J'obtiens le résultat suivant :

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

J'ai des sauvegardes du référentiel, mais la seule sauvegarde qui inclut le fichier pack l'a déjà endommagé. Je pense donc que je dois trouver un moyen de récupérer les objets individuels à partir de différentes sauvegardes et d'une manière ou d'une autre demander à Git de produire un nouveau pack avec uniquement les objets corrects.

Pouvez-vous s'il vous plaît me donner des conseils sur la façon de corriger mon dépôt ?

2 votes

Ça vient de m'arriver. Je ne veux pas m'embrouiller avec les objets git... j'ai donc cloné à nouveau le projet à partir du dépôt distant dans un nouveau dossier, puis j'ai simplement copié tous les fichiers de mes dépôts problématiques (à l'exception du fichier .git bien sûr) dans le repo fraîchement cloné... et a ensuite fait git status dans le nouveau repo... git détecte correctement toutes les modifications affectées à mes fichiers et je peux recommencer mon travail.

86voto

Daniel Fanjul Points 2375

Dans certaines sauvegardes antérieures, vos mauvais objets peuvent avoir été emballés dans des fichiers différents ou être encore des objets libres. Vos objets peuvent donc être récupérés.

Il semble qu'il y ait quelques mauvais objets dans votre base de données. Vous pouvez donc le faire manuellement.

En raison de git hash-object , git mktree y git commit-tree n'écrivez pas les objets parce qu'ils se trouvent dans le pack, puis commencez à le faire :

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

(Vos paquets sont déplacés hors du référentiel, et déballés à nouveau dans celui-ci ; seuls les bons objets sont maintenant dans la base de données).

Vous pouvez le faire :

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

et vérifier le type de l'objet.

Si le type est blob : récupérer le contenu du fichier à partir de sauvegardes précédentes (avec git show o git cat-file o git unpack-file ; alors vous pouvez git hash-object -w pour réécrire l'objet dans votre référentiel actuel.

Si le type est un arbre : vous pouvez utiliser git ls-tree pour récupérer l'arbre à partir des sauvegardes précédentes ; puis git mktree pour le réécrire dans votre référentiel actuel.

Si le type est commit : même chose avec git show , git cat-file y git commit-tree .

Bien sûr, je ferais une sauvegarde de votre copie de travail originale avant de commencer ce processus.

Jetez également un coup d'œil à Comment récupérer un objet Blob corrompu ? .

2 votes

Merci, ça m'a sauvé ! Je vais poster mes étapes exactes dans une réponse séparée.

0 votes

Juste une correction : la commande pour se termine par "done" et non "end".

0 votes

J'essaie de le faire mais .git/objects/pack/ est vide

37voto

Christian Points 3966

Banengusk me mettait sur la bonne voie. Pour plus de référence, je veux poster les étapes que j'ai prises pour réparer la corruption de mon référentiel. J'ai eu la chance de trouver tous les objets nécessaires soit dans des packs plus anciens, soit dans des sauvegardes du référentiel.

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

Ajoutons à cela : Si la sauvegarde contient les fichiers manquants dans un pack, la manière correcte d'obtenir un blob hors du pack est 'git cat-file blob <SHA1> > file.dat', et pour le récupérer dans le repo endommagé, faites 'git hash-object -w file.dat', comme dans la réponse de Daniel.

0 votes

Comment trouver le dernier paquet non corrompu ? Merci.

19voto

kenorb Points 2464

Essayez d'abord les commandes suivantes (ré-exécutez-les si nécessaire) :

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

Et puis si vous avez encore des problèmes, essayez de le faire :

  • supprimer tous les objets corrompus, par exemple

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
  • supprimer tous les objets vides, par exemple

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
  • vérifier un message "lien brisé" en :

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8

    Cela vous indiquera de quel fichier provient le blob corrompu !

  • pour récupérer le fichier, vous pourriez avoir beaucoup de chance, et il pourrait s'agir de la version que vous avez déjà extraite dans votre arbre de travail :

    git hash-object -w my-magic-file

    encore une fois, et s'il sort le SHA1 manquant (4b945..) vous avez maintenant tout fait !

  • En supposant que c'est une ancienne version qui était cassée, la façon la plus simple de le faire est de faire :

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

    et cela vous montrera l'ensemble du journal pour ce fichier (veuillez réaliser que l'arbre que vous avez n'est peut-être pas l'arbre de premier niveau, vous devez donc déterminer vous-même dans quel sous-répertoire il se trouvait), vous pouvez alors recréer l'objet manquant avec hash-object à nouveau.

  • pour obtenir une liste de tous les refs avec des commits, arbres ou blobs manquants :

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

    Il se peut qu'il ne soit pas possible de supprimer certaines de ces références en utilisant les commandes normales branch -d ou tag -d, car elles mourront si git remarque la corruption. Utilisez donc la commande de plomberie git update-ref -d $ref à la place. Notez que dans le cas de branches locales, cette commande peut laisser une configuration de branche périmée dans .git/config. Elle peut être supprimée manuellement (cherchez la section [branch "$ref"]).

  • Après que tous les refs soient nettoyés, il peut encore y avoir des commits cassés dans le reflog. Vous pouvez effacer tous les reflogs en utilisant git reflog expire --expire=now --all. Si vous ne voulez pas perdre tous vos reflogs, vous pouvez rechercher les reflogs cassés dans les refs individuels :

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

    (Notez l'option -g ajoutée à git rev-list.) Ensuite, utilisez git reflog expire --expire=now $ref sur chacune d'entre elles. Lorsque toutes les réfs et reflogs cassées ont disparu, lancez git fsck --full afin de vérifier que le dépôt est propre. Les objets en suspens sont Ok.


Vous trouverez ci-dessous l'utilisation avancée de commandes qui peuvent potentiellement causer la perte de vos données dans votre dépôt git si elles ne sont pas utilisées à bon escient, alors faites une sauvegarde avant de causer accidentellement d'autres dommages à votre git. Essayez à vos risques et périls si vous savez ce que vous faites.


Tirer la branche courante sur le dessus de la branche amont après le fetching :

$ git pull --rebase

Vous pouvez également essayer d'extraire la nouvelle branche et de supprimer l'ancienne :

$ git checkout -b new_master origin/master

Pour trouver l'objet corrompu dans git afin de le supprimer, essayez la commande suivante :

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

Pour OSX, utilisez sed -E au lieu de sed -r .


L'autre idée est de décompresser tous les objets des fichiers pack pour régénérer tous les objets dans .git/objects, donc essayez d'exécuter les commandes suivantes dans votre dépôt :

$ 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

Si ce qui précède ne vous aide pas, vous pouvez essayer de rsync ou de copier les objets git d'un autre dépôt, par exemple.

$ 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

Pour corriger la branche cassée lors de la tentative de checkout comme suit :

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

Essayez de le supprimer et de vérifier à nouveau en amont :

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

Au cas où git vous mettrait dans un état de détachement, vérifiez le fichier master et y fusionner la branche détachée.


Une autre idée est de rebaser le master existant de manière récursive :

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

Voir aussi :

2voto

Jonathan Maim Points 728

Voici les étapes que j'ai suivies pour récupérer un objet blob corrompu.

1) Identifier le blob corrompu

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

Le blob corrompu est 241091723c324aed77b2d35f97a05e856b319efd

2) Déplacer le blob corrompu vers un endroit sûr (juste au cas où)

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

3) Obtenir le parent du blob corrompu

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

Le hachage des parents est 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Obtenir le nom du fichier correspondant au blob corrompu

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

Trouvez ce fichier particulier dans une sauvegarde ou dans le dépôt git en amont (dans mon cas, il s'agit de dump.tar.gz ). Puis copiez-le quelque part dans votre dépôt local.

5) Ajouter le fichier précédemment corrompu dans la base de données des objets git.

git hash-object -w dump.tar.gz

6) Faites la fête !

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)

1voto

Tim Lin Points 681

Le checkout de Git peut en fait récupérer des fichiers individuels dans une révision. Il suffit de lui donner le hash du commit et le nom du fichier. Plus d'informations ici.

Je pense que le moyen le plus simple de résoudre ce problème est de revenir à la dernière sauvegarde non commitée, puis de sélectionner les fichiers non corrompus dans les commits les plus récents. Bonne chance !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X