web-dev-qa-db-fra.com

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

J'ai rencontré une défaillance du 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 de pack l'a déjà endommagé. Je pense donc que je dois trouver un moyen de récupérer les objets uniques à partir de différentes sauvegardes et demander en quelque sorte à Git de produire un nouveau pack contenant uniquement les objets corrects.

Pouvez-vous s'il vous plaît me donner des conseils sur la façon de réparer mon référentiel?

88
Christian

Dans certaines sauvegardes précédentes, vos objets défectueux peuvent avoir été compressés dans différents fichiers ou peuvent encore être des objets desserrés. Donc, vos objets peuvent être récupérés.

Il semble qu'il y ait quelques mauvais objets dans votre base de données. Donc, vous pouvez le faire de manière manuelle.

En raison de git hash-object, git mktree et git commit-tree, n'écrivez pas les objets car ils se trouvent dans le pack.

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

(Vos packs sont sortis du référentiel et y sont décompressés; seuls les objets valables sont maintenant dans la base de données)

Tu peux faire:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

et vérifiez le type de l'objet.

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

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

Si le type est commit: idem avec git show, git cat-file et git commit-tree.

Bien sûr, je voudrais sauvegarder votre copie de travail originale avant de commencer ce processus.

Regardez aussi Comment récupérer un objet blob corrompu .

81
Daniel Fanjul

Banengusk me mettait sur la bonne voie. Pour plus de références, je souhaite publier les étapes que j’ai effectuées pour réparer la corruption de mon référentiel. J'ai eu la chance de trouver tous les objets nécessaires dans des packs plus anciens ou dans des sauvegardes de référentiels.

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

Essayez d’abord les commandes suivantes (relancez-le si nécessaire):

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

Et puis vous vous avez encore des problèmes, essayez peut:

  • supprimez 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
    
  • supprimez 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 rompu" par:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Ce testament vous indique de quel fichier provient le blob corrompu!

  • pour récupérer le fichier, vous pourriez être vraiment chanceux, et il peut 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 terminé!

  • en supposant que c’était une version plus ancienne qui était cassée, le moyen le plus simple de le faire est de le faire:

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

    et cela vous montrera le journal complet pour ce fichier (sachez que l’arbre que vous aviez n’est peut-être pas l’arbre de niveau supérieur, vous devez donc déterminer le sous-répertoire dans lequel il se trouvait), vous pouvez alors recréer le fichier. objet manquant avec hash-object à nouveau.

  • pour obtenir une liste de toutes les références 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 ne sera peut-être pas possible de supprimer certaines de ces références en utilisant les commandes branch -d ou tag -d habituelles, 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 la configuration de branches obsolètes dans .git/config. Il peut être supprimé manuellement (recherchez la section [branch "$ ref"]).

  • Une fois que toutes les références sont propres, il peut rester des commits brisé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 références individuelles pour les reflogs brisés:

    $ (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 chacun de ces . Quand tous les refs et les reflogs brisés ont disparu, lancez git fsck --full afin de vérifier que le référentiel est propre. Les objets qui pendent sont OK.


Ci-dessous, vous trouverez une utilisation avancée des commandes pouvant entraîner la perte de vos données dans votre référentiel git si elles ne sont pas utilisées à bon escient. Faites donc une sauvegarde avant de causer d'autres dommages accidentels à votre git. Essayez à vos risques et périls si vous savez ce que vous faites.


Pour extraire la branche en cours au-dessus de la branche en amont après l'extraction:

$ git pull --rebase

Vous pouvez également essayer de vérifier une nouvelle branche et supprimer l’ancienne:

$ git checkout -b new_master Origin/master

Pour rechercher l'objet corrompu dans git pour 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.


Une autre idée est de décompresser tous les objets des fichiers de pack pour régénérer tous les objets dans .git/objects. Essayez donc d'exécuter les commandes suivantes dans votre référentiel:

$ 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 n’aide pas, vous pouvez essayer de rsync ou de copier les objets git d’un autre référentiel, 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 réparer la branche cassée en essayant de régler comme suit:

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

Essayez de l'enlever et de commander de nouveau en amont:

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

Au cas où git vous mettrait dans l'état détaché, vérifiez la master et fusionnez-la dans la branche détachée.


Une autre idée consiste à rebaser le maître existant de manière récursive:

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

Voir également:

15
kenorb

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
  ...

Blob corrompu est 241091723c324aed77b2d35f97a05e856b319efd

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

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

3) Obtenir le parent de 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 parent est 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Obtenir le nom du fichier correspondant au blob corrompu

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

Recherchez ce fichier dans une sauvegarde ou dans le référentiel git en amont (dans mon cas, il s'agit de dump.tar.gz). Ensuite, copiez-le quelque part dans votre référentiel local. 

5) Ajouter un fichier précédemment corrompu dans la base de données d’objets git

git hash-object -w dump.tar.gz

6) Célébrez!

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)
2
Jonathan Maim

Voici deux fonctions qui peuvent vous aider si votre sauvegarde est corrompue ou si vous avez également quelques sauvegardes partiellement corrompues (cela peut se produire si vous sauvegardez les objets corrompus).

Exécutez les deux dans le dépôt que vous essayez de récupérer.

Avertissement standard: utilisez-le uniquement si vous êtes vraiment désespéré et que vous avez sauvegardé votre dépôt (corrompu). Cela ne résoudra peut-être rien, mais il faudrait au moins souligner le niveau de corruption.

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

et

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
1
go2null

Git Checkout peut réellement choisir des fichiers individuels d'une révision. Donnez-lui simplement le hash de commit et le nom du fichier. Plus d'informations détaillées ici.

Je suppose que le moyen le plus simple de résoudre ce problème en toute sécurité est de revenir à la dernière sauvegarde non validée, puis de sélectionner de manière sélective les fichiers non corrompus des nouveaux commits. Bonne chance!

1
Tim Lin

J'ai résolu ce problème pour ajouter des modifications telles que git add -A et git commit à nouveau.

0
Dmitriy S