Comment réparer un référentiel git corrompu?

104

J'ai essayé de cloner mon référentiel que je garde sur mon dossier Ubuntu sur une nouvelle machine et j'ai obtenu ceci:

christopher@christopher-laptop:~/source/personal$ git clone ~/Ubuntu\ One\ Side\ Work/projects.git/
Cloning into 'projects'...
done.
fatal: unable to read tree 29a422c19251aeaeb907175e9b3219a9bed6c616
christopher@christopher-laptop:~/source/personal$ 

J'ai donc essayé de regarder les nombreuses autres questions comme celle-ci qui ont été posées ici et la plupart d'entre elles disent de courir git fsck --fullet puis j'obtiens ceci quand j'essaye cela.

christopher@christopher-laptop:~/Ubuntu One Side Work/projects.git$ git fsck --full
Checking object directories: 100% (256/256), done.
Checking objects: 100% (447/447), done.
broken link from  commit 235ae1f48701d577d71ebd430344a159e5ba4881
              to  commit 984c11abfc9c2839b386f29c574d9e03383fa589
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    blob 25a742dff0a403b2b3884f2ffddf63eb45721fac
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    blob dd4e97e22e159a585b20e21028f964827d5afa4e
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    tree 29a422c19251aeaeb907175e9b3219a9bed6c616
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    tree 8084e8e04d510cc28321f30a9646477cc50c235c
broken link from    tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
              to    blob a0daa0c1567b55d8de2b4d7a3bc010f58c047eab
broken link from    tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
              to    blob e9052d35bfb6d30065b206fc43f4200a04d5281b
broken link from    tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
              to    blob 1a3a5e4dd2502ac121c22f743c4250e254a94eeb
broken link from    tree 4aa336dc1a5838e8918e03b85580069d83f4ad09
              to    tree 8cc55ec952dc192a233e062201d1e7e873ac3db0
broken link from    tree e5674a91a53e15575a1f3bf5786bc5cc719fb483
              to    blob 4a994e1e7bb7ce28dcec98bad48b9a891d7dec51
broken link from    tree e5674a91a53e15575a1f3bf5786bc5cc719fb483
              to    blob ac033bf9dc846101320c96a5ce8aceb8c96ec098
broken link from    tree 252ab84542264e1589576b6ee51e7a31e580a0e2
              to    tree 2069041cd5950e529e2991d37b7290ec021d90d4
broken link from    tree 2d4964aa4d4f5d8c7228518ce72ef6a63f820c6d
              to    blob d83690e1b9a6bdd8a08754b38231799acefcb2ab
broken link from    tree c7192e82fc581bd6448bda1a25e8729bdac5f4ff
              to    blob 30d54d47ae82add1917ca173d42e58b396df580b
broken link from    tree 7c66306901fc71389623286936cef172d4ffe408
              to    blob bc7e05d705401273b1df4e939de0f540597c0931
broken link from    tree 0940f5fd227d4c84d6e6749d872db50a4522ae3a
              to    tree 923767594ac22023e824948d65622fe5b407d1a1
broken link from    tree 8eadcd2a971e8357d24f0d80f993d2963452209f
              to    blob 2598bde3dc8cb80ee49510b8159344004b88645f
broken link from    tree ffa302dd0d969172ef23caeefe856ab2f57a4e4d
              to    blob d6925fa431be1ac585bf9a481e98f75107a6e6fb
broken link from    tree 7045b8870a49ce30a2027537a96d73d162bda773
              to    blob 25688652dea26f61f576ca1b52b9d1a18fbfd01d
broken link from    tree 37e4705d34bd440ce681ae32ae9a180a13256d72
              to    tree 246f564d4cee53339b8a4244f3173b61caa518eb
missing blob d6925fa431be1ac585bf9a481e98f75107a6e6fb
missing blob ac033bf9dc846101320c96a5ce8aceb8c96ec098
missing tree 29a422c19251aeaeb907175e9b3219a9bed6c616
missing tree 8084e8e04d510cc28321f30a9646477cc50c235c
missing blob 30d54d47ae82add1917ca173d42e58b396df580b
missing tree 8cc55ec952dc192a233e062201d1e7e873ac3db0
missing blob e9052d35bfb6d30065b206fc43f4200a04d5281b
dangling tree 4b26e95db542c72ac4a22ec25abe38fb2de79752
missing blob d83690e1b9a6bdd8a08754b38231799acefcb2ab
missing blob 25a742dff0a403b2b3884f2ffddf63eb45721fac
missing tree 923767594ac22023e824948d65622fe5b407d1a1
missing blob 25688652dea26f61f576ca1b52b9d1a18fbfd01d
missing blob 2598bde3dc8cb80ee49510b8159344004b88645f
dangling tree 3a683869f1bb0c1634de75700c316b3b36570dbd
dangling blob 4098d30843380d798a811f1aa9a02994f0dbbb27
missing tree 2069041cd5950e529e2991d37b7290ec021d90d4
missing blob 4a994e1e7bb7ce28dcec98bad48b9a891d7dec51
missing blob 1a3a5e4dd2502ac121c22f743c4250e254a94eeb
missing blob a0daa0c1567b55d8de2b4d7a3bc010f58c047eab
dangling tree 6c7b5162aa7a303fa3fe8dc393c5da564e309521
missing commit 984c11abfc9c2839b386f29c574d9e03383fa589
missing blob bc7e05d705401273b1df4e939de0f540597c0931
missing blob dd4e97e22e159a585b20e21028f964827d5afa4e
missing tree 246f564d4cee53339b8a4244f3173b61caa518eb
dangling commit a01f5c1e5315dc837203d6dee00d3493be9c5db9

Cela semble vraiment mauvais. Quand je fais un git log | headje reçois ça

christopher@christopher-laptop:~/Ubuntu One Side Work/projects.git$ git log | head
error: Could not read 984c11abfc9c2839b386f29c574d9e03383fa589
fatal: Failed to traverse parents of commit 235ae1f48701d577d71ebd430344a159e5ba4881
commit 2fb0d2d0643b445440f01b164f11ee9ee71fca48
Author: christopher <[email protected]>
Date:   Wed Aug 7 15:51:42 2013 -0400

    finishing chapter 7

D'autres questions ici ont dit à regarder ./git/refs/heads/master. C'est un repo nu et refs/heads/existe mais refs/heads/masterpas. HEAD dans le repo nu dit ref: refs/heads/mastercependant

packed-refs dit cela cependant

# pack-refs with: peeled 
2fb0d2d0643b445440f01b164f11ee9ee71fca48 refs/heads/master

D'autres questions ont suggéré de courir git refloget aucune sortie n'apparaît lorsque je l'exécute.

Donc, je ne sais vraiment pas quoi faire ici. Quelle stratégie adopter? Est-il possible de réinitialiser head sur ce dernier commit le 7 août

ÉDITER:

Faire un journal git et aller au bas de la sortie de l'écran montre ceci:

commit 996e03b949aea176238e3c7a8452700bbb987ac9
Author: christopher <christopher@christopher>
Date:   Wed Jul 3 23:00:44 2013 -0400

    many many changes
error: Could not read 984c11abfc9c2839b386f29c574d9e03383fa589
fatal: Failed to traverse parents of commit 235ae1f48701d577d71ebd430344a159e5ba4881

Cela semble empêcher le git prune de fonctionner

TinyGrasshopper
la source
3
Cela n'aide pas beaucoup mais: ne stockez pas les dépôts Git dans Dropbox ou dans tout autre service de synchronisation. Git n'est pas conçu pour gérer un autre programme qui verrouille et réécrit aléatoirement des fichiers pendant qu'il fait autre chose.
millimoose
En relation: stackoverflow.com/q/18550073/1301972
Todd A. Jacobs
2
Presque toutes les réponses supposent que l'on peut simplement re-cloner à partir d'une origine distante non corrompue. Voici le problème ... Et si vous êtes à l'origine et que vous êtes corrompu? Droite. Donc, voici: git-repairest un programme qui s'exécutera git fscket essaiera très fort de résoudre les problèmes qu'il rencontre. git-repair.branchable.com Cela semble tout à fait capable, et bien que vous puissiez finir par devoir copier (si vous le pouvez!) des objets à partir d'une sauvegarde (vous avez une sauvegarde, non?), cela devrait vous faire gagner beaucoup de temps en récupérer tout ce qu'il peut et vous laisser le vrai travail, pas beaucoup de tâches automatisables. Aucune affiliation, etc.
underscore_d

Réponses:

111

Comme alternative à la dernière option de CodeGnome, si seul le dépôt local est corrompu et que vous connaissez l'URL de la télécommande, vous pouvez l'utiliser pour réinitialiser votre .gitpour qu'il corresponde à la télécommande (en remplaçant ${url}par l'URL distante):

mv -v .git .git_old &&            # remove old git
git init &&                       # initialise new repo
git remote add origin "${url}" && # link to old repo
git fetch &&                      # get old history
git reset origin/master --mixed   # force update to old history

Cela laisse votre arbre de travail intact et n'affecte que la comptabilité de git.
J'ai aussi récemment créé un script bash à cette fin (Annexe A), qui englobe un peu de sécurité autour de cette opération.

Remarque:

Si votre dépôt a des sous-modules, ce processus les gâchera d'une manière ou d'une autre, et la seule solution que j'ai trouvée jusqu'à présent est de les supprimer puis de les utiliser git submodule update --init(ou de re-cloner le dépôt, mais cela semble trop drastique).

Annexe A - Script complet

#!/bin/bash

# Author: Zoey Llewellyn "Zobean" Hewll
#
# Usage: fix-git [REMOTE-URL]
#   Must be run from the root directory of the repository.
#   If a remote is not supplied, it will be read from .git/config
# 
# For when you have a corrupted local repo, but a trusted remote.
# This script replaces all your history with that of the remote.
# If there is a .git, it is backed up as .git_old, removing the last backup.
# This does not affect your working tree.
#
# This does not currently work with submodules!
# This will abort if a suspected submodule is found.
# You will have to delete them first
# and re-clone them after (with `git submodule update --init`)
#
# Error codes:
# 1: If a url is not supplied, and one cannot be read from .git/config
# 4: If the url cannot be reached
# 5: If a git submodule is detected


if [[ "$(find -name .git -not -path ./.git | wc -l)" -gt 0 ]] ;
then
    echo "It looks like this repo uses submodules" >&2
    echo "You will need to remove them before this script can safely execute" >&2
    echo "Then use \`git submodule update --init\` to re-clone them" >&2
    exit 5
fi

if [[ $# -ge 1 ]] ;
then
    url="$1"
else
    if ! url="$(git config --local --get remote.origin.url)" ;
    then
        echo "Unable to find remote 'origin': missing in '.git/config'" >&2
        exit 1
    fi
fi
url_base="$(echo "${url}" | sed -E 's;^([^/]*://)?([^/]*)(/.*)?$;\2;')"
echo "Attempting to access ${url_base} before continuing"
if ! wget -p "${url_base}" -O /dev/null -q --dns-timeout=5 --connect-timeout=5 ;
then
    echo "Unable to reach ${url_base}: Aborting before any damage is done" >&2
    exit 4
fi

echo
echo "This operation will replace the local repo with the remote at:"
echo "${url}"
echo
echo "This will completely rewrite history,"
echo "but will leave your working tree intact"
echo -n "Are you sure? (y/N): "

read confirm
if ! [ -t 0 ] ; # i'm open in a pipe
then
    # print the piped input
    echo "${confirm}"
fi
if echo "${confirm}"|grep -Eq "[Yy]+[EeSs]*" ; # it looks like a yes
then
    if [[ -e .git ]] ;
    then
        # remove old backup
        rm -vrf .git_old | tail -n 1 &&
        # backup .git iff it exists
        mv -v .git .git_old
    fi &&
    git init &&
    git remote add origin "${url}" &&
    git config --local --get remote.origin.url | sed 's/^/Added remote origin at /' &&
    git fetch &&
    git reset origin/master --mixed
else
    echo "Aborting without doing anything"
fi
Zoey Hewll
la source
3
Spectaculaire, j'ai fait une sauvegarde de mon projet et essayé votre solution. Git a été assez mal corrompu d'une manière ou d'une autre. J'ai essayé votre solution et cela a parfaitement fonctionné, merci beaucoup.
kequc
2
Je n'ai pas utilisé le script car je suis sous Windows, mais ces commandes ont juste enregistré mon dossier .git qui montrait un nouveau dépôt pour chaque sauvegarde effectuée sur un fichier suivi (donc je me suis retrouvé avec environ 50 référentiels principaux avant d'essayer ce correctif )
cyclomoteur
1
J'ai pu restaurer mon .gitdossier après avoir fait cela. J'ai trouvé que git reset origin/master --hardc'était plus utile que --mixed.
Felipe Alvarez
1
Génial. Dans le cas où il y a des sous-modules qui ne contiennent pas de modifications, supprimez-les d'abord, puis exécutez le sous-module init pour les récupérer.
herm
1
@ w33haa Malheureusement, cette solution n'est applicable que dans le cas où vous disposez d'un référentiel distant valide et accessible. Certaines des autres réponses concernent le cas d'une télécommande inaccessible ou corrompue.
Zoey Hewll
53

TL; DR

Git ne stocke pas vraiment l'historique comme vous le pensez. Il calcule l' historique au moment de l'exécution sur la base d'une chaîne d'ancêtres. S'il manque des blobs, des arbres ou des validations à votre ascendance, vous ne pourrez peut-être pas récupérer complètement votre historique.

Restaurer les objets manquants à partir des sauvegardes

La première chose que vous pouvez essayer est de restaurer les éléments manquants de la sauvegarde. Par exemple, voyez si vous avez une sauvegarde de la validation stockée sous .git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589. Si c'est le cas, vous pouvez le restaurer.

Vous pouvez également consulter les objets git-verify-pack et git-unpack-objects dans le cas où le commit a déjà été compressé et que vous souhaitez le renvoyer à un objet lâche à des fins de chirurgie du dépôt.

Résection chirurgicale

Si vous ne pouvez pas remplacer les éléments manquants d'une sauvegarde, vous pourrez peut-être supprimer l'historique manquant. Par exemple, vous pouvez examiner votre historique ou reflog pour trouver un ancêtre du commit 984c11abfc9c2839b386f29c574d9e03383fa589. Si vous en trouvez un intact, alors:

  1. Copiez votre répertoire de travail Git dans un répertoire temporaire quelque part.
  2. Effectuez une réinitialisation matérielle du commit non corrompu.
  3. Copiez vos fichiers actuels dans l'arborescence de travail Git, mais assurez-vous de ne pas recopier le dossier .git!
  4. Validez l'arbre de travail actuel et faites de votre mieux pour le traiter comme un commit écrasé de tout l'historique manquant.

Si cela fonctionne, vous perdrez bien sûr l'histoire intermédiaire. À ce stade, si vous disposez d'un journal d'historique de travail, il est judicieux d'élaguer votre historique et les reflogs de tous les commits et objets inaccessibles.

Restaurations complètes et réinitialisation

Si votre référentiel est toujours cassé, nous espérons que vous disposez d'une sauvegarde ou d'un clone non corrompu à partir de laquelle vous pouvez restaurer. Sinon, mais votre répertoire de travail actuel contient des fichiers valides, vous pouvez toujours réinitialiser Git. Par exemple:

rm -rf .git
git init
git add .
git commit -m 'Re-initialize repository without old history.'

C'est drastique, mais cela peut être votre seule option si l'historique de votre référentiel est vraiment irrécupérable. YMMV.

Todd A. Jacobs
la source
3
Je viens de rencontrer ce problème et je voulais le souligner car ce n'est pas dans votre réponse, mais mon problème était juste un simple problème d'autorisations. error: Could not read abcdeMon dépôt est géré par GitLab et il créait des fichiers que mon utilisateur ne pouvait pas lire. Un sudo chownplus tard et j'étais prêt à partir.
Aust
6

Si vous avez une télécommande configurée et que vous avez / ne vous souciez pas de perdre du code non poussé, vous pouvez faire:

git fetch && git reset --hard
user5169648
la source
2
Dans certains cas, git ne vous permettra pas de récupérer si certains objets sont corrompus, vous devez donc d'abord réinitialiser votre dépôt.
Zoey Hewll
Cela n'a pas fonctionné pour moi quand j'avais fatal: pack has 13 unresolved deltas.
Artem Russakovskii le
5

Voici un script (bash) pour automatiser la première solution de @CodeGnome pour restaurer à partir d'une sauvegarde (exécutée à partir du niveau supérieur du dépôt corrompu). La sauvegarde n'a pas besoin d'être complète, elle n'a besoin que des objets manquants.

git fsck 2>&1 | grep -e missing -e invalid | awk '{print $NF}' | sort -u |
    while read entry; do
        mkdir -p .git/objects/${entry:0:2}
        cp ${BACKUP}/objects/${entry:0:2}/${entry:2} .git/objects/${entry:0:2}/${entry:2}
    done
Campkeith
la source
4

Avant d'essayer l'un des correctifs décrits sur cette page, je vous conseillerais de faire une copie de votre dépôt et de travailler uniquement sur cette copie. Ensuite, à la fin, si vous pouvez le réparer, comparez-le avec l'original pour vous assurer que vous n'avez perdu aucun fichier lors du processus de réparation.

Une autre alternative qui a fonctionné pour moi était de réinitialiser la tête git et l'index à son état précédent en utilisant:

git reset --keep

Vous pouvez également faire la même chose manuellement en ouvrant l'interface graphique de Git et en sélectionnant chaque "Modifications par étapes" et en cliquant sur "Annuler la mise en scène". Lorsque tout n'est pas mis en scène, vous devriez maintenant pouvoir compresser votre base de données, vérifier votre base de données et valider.

J'ai également essayé les commandes suivantes mais elles n'ont pas fonctionné pour moi, mais elles pourraient vous convenir en fonction du problème exact que vous rencontrez:

git reset --mixed
git fsck --full
git gc --auto
git prune --expire now
git reflog --all

Enfin, pour éviter ce problème de synchronisation qui endommage votre index git (ce qui peut arriver avec DropBox, SpiderOak ou tout autre disque cloud), vous pouvez effectuer les opérations suivantes:

  1. Convertissez votre .gitdossier en un seul fichier git "bundle" en utilisant :,git bundle create my_repo.git --all alors cela devrait fonctionner comme avant, mais comme tout est dans un seul fichier, vous ne risquez plus que la synchronisation endommage votre dépôt git.
  2. Désactiver la synchronisation instantanée : SpiderOak vous permet de définir la planification de la vérification des modifications sur «automatique» (ce qui signifie que c'est le plus tôt possible, en surveillant les modifications de fichiers grâce aux notifications du système d'exploitation). Ceci est mauvais car il commencera à télécharger les modifications dès que vous effectuez une modification, puis à télécharger la modification, ce qui pourrait effacer les dernières modifications que vous venez de faire. Une solution pour résoudre ce problème consiste à définir le délai de surveillance des modifications sur 5 minutes ou plus. Cela résout également les problèmes avec les applications de prise de notes d'enregistrement instantané (telles que Notepad ++).
joyeux
la source
3

Si vous êtes désespéré, vous pouvez essayer ceci:

git clone ssh://[email protected]/path/to/project destination --depth=1

Il obtiendra vos données, mais vous perdrez l'historique. Je suis allé avec essais et erreurs sur mon repo et j'ai --depth=10travaillé, mais --depth=50j'ai échoué.

alberto56
la source
3

J'ai essayé de déplacer les fichiers objets avec 0 octet et de les récupérer à nouveau à partir de la télécommande, et cela a fonctionné:

find . -type f -size 0 -exec mv {} /tmp \;
git fetch

Il a récupéré les objets manquants de la télécommande et m'a permis de continuer à travailler sans réinitialiser l'ensemble du dépôt.

AlejandroVD
la source
2

J'étais confronté au même problème, j'ai donc remplacé le dossier ".git" par une version sauvegardée et cela ne fonctionnait toujours pas car le fichier .gitconfig était corrompu. Le BSOD sur mon ordinateur portable l'a corrompu. Je l'ai remplacé par le code suivant et sourcetree a restauré tous mes référentiels.

[user]
name = *your username*
email = *your email address*
[core]
autocrlf = true
excludesfile = C:\\Users\\*user name*\\Documents\\gitignore_global.txt

Je ne sais pas si cela aidera quelqu'un, mais ce n'est qu'une autre solution qui a fonctionné pour moi.

AspirantCanadien
la source
2

Supprimer l'index et réinitialiser

rm -f .git/index
git reset
Ashfaq
la source
2

J'ai rencontré des problèmes similaires en utilisant la version 2.7.1 de git sous Ubuntu 18.04.3 récemment. Voici comment j'ai fait:

sudo apt install git-repair
git-repair  # fix a broken git repository
or
git-repair --force  # force repair, even if data is lost
git fsck  # to verify it was fixed

La plupart du temps, le processus de récupération a réussi

Jonathan L
la source
git-repair est en effet un outil très utile. Cela m'a aidé à récupérer un référentiel. Le problème avec la plupart des autres méthodes est que vous perdez vos cachettes, ce qui n'était pas une option pour moi.
Jan Rychter le
1

Dans mon cas, je créais le référentiel à partir du code source déjà dans mon PC et cette erreur est apparue. J'ai supprimé le dossier .git et tout fait à nouveau et cela a fonctionné :)

Caroline
la source
1

Je voulais ajouter ceci en commentaire sous la réponse géniale de Zoey Hewil ci-dessus, mais je n'ai actuellement pas assez de représentants pour le faire, je dois donc l'ajouter ici et donner du crédit pour son travail: P

Si vous utilisez Poshgit et que vous vous sentez exceptionnellement paresseux, vous pouvez utiliser ce qui suit pour extraire automatiquement votre URL de votre configuration git et rendre le travail encore plus facile. Les mises en garde standard s'appliquent au sujet du test sur une copie / sauvegarde de votre dépôt local en premier au cas où il vous exploserait au visage.

$config = get-content .git\config
$url = $config -match " url = (?<content>.*)"
$url = $url.trim().Substring(6)
$url

move-item -v .git .git_old;
git init;
git remote add origin "$url";
git fetch;
git reset origin/master --mixed
Gesthemene
la source
0

Un moyen rapide si vous avez des modifications sur votre projet actuel et que vous ne voulez pas les perdre, déplacez votre projet actuel quelque part, clonez le projet de github vers ce dossier et effectuez des modifications et essayez à nouveau de valider. Ou supprimez simplement le dépôt et clonez-le à nouveau, cela a fonctionné pour moi.

certilrémie
la source
-2

Cette commande a fonctionné pour moi:

$ git reset --mixed 
Pas de regrets
la source