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?
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 .
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!
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:
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)
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
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!
J'ai résolu ce problème pour ajouter des modifications telles que git add -A et git commit à nouveau.