Comment est-ce que `git pull` a mangé mes devoirs?

53

Je me sens comme un enfant dans le bureau du directeur expliquant que le chien a mangé mes devoirs la veille de l’échéance, mais je suis en train de regarder un bug de perte de données insensé et je ne peux pas comprendre comment cela s’est passé. Je voudrais savoir comment git pourrait manger mon référentiel entier! J'ai déjà passé plusieurs fois le git à l'essoreuse et elle n'a jamais clignoté. Je l'ai utilisé pour scinder un repo Subversion de 20 gigaoctets en 27 dépôts de git et filtrer le foo par filtrage pour démêler le désordre et il n'a jamais perdu un octet sur moi. Le reflog est toujours là pour se replier. Cette fois, le tapis est parti!

De mon point de vue, tout ce que j'ai fait est exécuté git pullet cela a détruit tout mon référentiel local. Je ne veux pas dire que "foiré la version extraite" ou "la branche sur laquelle j'étais" ou quelque chose comme ça. Je veux dire que tout est parti .

Voici une capture d'écran de mon terminal lors de l'incident:

capture d'écran de l'incident

Laisse-moi te guider à travers ça. Mon invite de commande inclut des données sur le référentiel git actuel (à l'aide de l'implémentation vcs_info de prezto) afin que vous puissiez voir quand le référentiel git a disparu. La première commande est assez normale:

  » caleb » jaguar » ~/p/w/incil.info » ◼  zend ★ »
❯❯❯ git co master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

Là, vous voyez que j'étais sur la branche 'zend' et que j'ai vérifié master. Jusqu'ici tout va bien. Vous verrez dans l'invite précédant ma prochaine commande qu'il a basculé avec succès:

  » caleb » jaguar » ~/p/w/incil.info » ◼  master ★ »
❯❯❯ git pull
remote: Counting objects: 37, done.
remote: Compressing objects: 100% (37/37), done.
remote: Total 37 (delta 25), reused 0 (delta 0)
Unpacking objects: 100% (37/37), done.
From gitlab.alerque.com:ipk/incil.info
 + 7412a21...eca4d26 master     -> origin/master  (forced update)
   f03fa5d..c8ea00b  devel      -> origin/devel
 + 2af282c...009b8ec verse-spinner -> origin/verse-spinner  (forced update)
First, rewinding head to replay your work on top of it...
>>> elapsed time 11s

Et juste comme ça, c'est parti. Le marqueur de temps écoulé est émis avant l'invite suivante si plus de 10 secondes se sont écoulées. Git n'a donné aucun résultat au-delà de la notification qu'il était en train de revenir en arrière. Aucune indication que ce soit fini.

L'invite suivante ne contient aucune donnée sur la branche sur laquelle nous nous trouvons ni sur l'état de git.

Ne remarquant pas que cela avait échoué, j'ai inconsciemment essayé de lancer une autre commande git pour me faire dire que je n'étais pas dans un dépôt git. Notez que la PWD n'a pas changé:

  » caleb » jaguar » ~/p/w/incil.info »
❯❯❯ git fetch --all
fatal: Not a git repository (or any parent up to mount point /home)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

Après cela, un coup d’œil a montré que j’étais dans un répertoire complètement vide. Rien. Aucun répertoire '.git', rien. Vide.

Mon git local est à la version 2.0.2. Voici quelques informations de ma config git susceptibles de nous aider à comprendre ce qui s'est passé:

[branch]
        autosetuprebase = always
        rebase = preserve
[pull]
        rebase = true
[rebase]
        autosquash = true
        autostash = true
[alias]
        co = checkout

Par exemple, j'ai git pulldéfini de toujours effectuer une refonte au lieu d'une fusion, de sorte qu'une partie de la sortie ci-dessus est normale.

Je peux récupérer les données. Je ne pense pas qu'il y avait d'autres objets git que des cachettes sans importance qui n'avaient pas été placées dans un dépôt, mais j'aimerais savoir ce qui s'est passé .

J'ai vérifié pour:

  • Messages dans dmesg ou dans le journal systemd. Rien, même à distance pertinente.
  • Il n'y a aucune indication de défaillance du lecteur ou du système de fichiers (LVM + LUKS + EXT4 semblent tous normaux). Il n'y a rien dans perdu + trouvé.
  • Je n'ai rien couru d'autre. Il n'y a rien dans l'historique que je ne montre pas ci-dessus, et aucun autre terminal n'a été utilisé pendant cette période. Il n'y a pas de rmcommande flottante qui aurait pu être exécutée dans le mauvais CWD, etc.
  • Piquer sur un autre dépôt git dans un autre répertoire ne montre aucune anomalie apparente lors de l'exécution de git pulls.

Que devrais-je chercher ici?

Caleb
la source
4
@Patrick Comme je l'ai déjà expliqué dans la question, non .gitn'existe pas. Rien ne se passe - ce qui était auparavant le répertoire racine de git n'a rien du tout.
Caleb
2
@Alexander L'opération d'extraction est normale (autre que d'être une base plutôt qu'une fusion). L'avis relatif à une mise à jour forcée indique que le dépôt avec lequel j'ai extrait a eu une force qui l'a réinitialisé depuis une position différente de celle du dernier dépôt local. Ceci est normal car je synchronise du matériel activement développé et fréquemment rebasé entre mes propres ordinateurs, et non pas une branche publique que d'autres développeurs verront.
Caleb
3
@Caleb votre invite de shell inclut l'indication de branche git, ce qui signifie que la formation de PS1 inclut des commandes git non exposées dans votre journal. Ils peuvent principalement changer l'image et peuvent être la source du problème. Vous devez mettre à jour la question décrivant précisément comment votre invite de shell est formée, quelles commandes sont exécutées pour obtenir une branche en cours, et réexaminez comment elles pourraient gâcher votre rapport.
Netch
2
@Caleb Vous devriez vraiment poser votre question sur la liste de diffusion git development; Vous pouvez l'écrire sous forme de rapport de bogue ou simplement demander de manière informelle - c'est la même chose quand même. Certains développeurs connaissent très bien git - ils peuvent probablement dire intuitivement ce qui aurait pu se passer. (Sinon, ils suivront la discussion tranquillement.) Et ils sauront si cela s'est passé auparavant. (En le signalant, il existe un moyen "officiel" de rapporter des bugs pour git)
Volker Siegel
7
@Wildcard En fait, j'avais l'intention de préparer une réponse à cette question, car j'avais en fait compris ce qui s'était passé. Le système venait de sortir du sommeil et le réseau était hors service pendant des jours avant de s'endormir. Quelque part dans ce processus, j'avais laissé un processus pacman en cours d'exécution qui tentait de mettre à niveau quelque chose sur le système. Pour résumer, glibc a été mis à jour et le binaire git a été écrasé. À cause de la façon dont il s'est fourré lui-même, une instance a fini par être différente de l'autre et ils ont déjeuné l'un l'autre. Le répertoire était vraiment vide (pas seulement appar
Caleb

Réponses:

6

Oui, gitj'ai mangé mes devoirs. Tout.

J'ai créé une ddimage de ce disque après l'incident et l'ai bousillé plus tard. En reconstruisant la série d’événements à partir de journaux système, j’en déduisais que ce qui s’était passé ressemblait à ceci:

  1. Une commande de mise à jour du système ( pacman -Syu) avait été émise quelques jours avant cet incident.
  2. Une panne de réseau prolongée signifiait qu'il était nécessaire de réessayer de télécharger des packages. Frustré par le manque d’Internet, j’avais mis le système en veille et je me suis couché.
  3. Quelques jours plus tard, le système s'est réveillé et la recherche et le téléchargement de paquets ont recommencé.
  4. Le téléchargement du paquet s'est terminé juste avant que je ne m'occupe de ce dépôt.
  5. L’ installation de la glibc système a été mise à jour après git checkoutet avant le git pull.
  6. Le gitbinaire a été remplacé après le git pulldébut et avant la fin.
  7. Et le septième jour, gitreposé de tous ses travaux. Et supprimé le monde afin que tous les autres devaient se reposer aussi.

Je ne sais pas exactement quelle condition de concurrence a provoqué cette situation, mais échanger des fichiers binaires au milieu d'une opération n'est certainement pas agréable, ni une condition vérifiable / répétable. Habituellement, une copie d'un fichier binaire en cours d'exécution est stockée dans la mémoire, mais elle gitest bizarre et présente un aspect de la façon dont elle reproduit des versions d'elle-même. Je suis sûre qu'elle a conduit à ce désordre. Évidemment, cela aurait dû mourir plutôt que de tout détruire, mais c'est ce qui s'est passé.

Caleb
la source
1
git aurait pu échouer parce que git est créé en utilisant des commandes differend, au lieu d’un seul binaire. Un simple git pull s'exécute git-fetch, git-rebaseou git-mergeetgit gc
Ferrybig
2

Peut-être en ne définissant pas le chemin du fichier à supprimer.

Votre cas m'a rappelé un beau jour lorsque ma remove(path)méthode maison a essayé de supprimer le dossier racine car le paramètre donné était une chaîne vide que le système d'exploitation a corrigée (!) En tant que dossier racine.

Cela peut être un bug similaire à Git. Tel que:

  1. La commande Rebase voulait supprimer un fichier tel que remove(project_folder + file_path)(pseudo-code)
  2. Était file_pathvide à l’époque.
  3. Commande évaluée comme quelque chose comme remove(project_folder)
malayas
la source
1

Avec de la chance, vous pouvez résoudre ce problème avec la commande suivante:

git reset --hard ORIG_HEAD  

Lorsque des changements potentiellement dangereux commencent, git bloque votre état actuel dans ORIG_HEAD. Avec cela, vous pouvez annuler une fusion ou une rebase.

Manuel Git: Annulation d'une fusion

Routhinator
la source
4
Je ne pense pas que vous lisiez toute la question. Ce type de correctif est complètement hors de question car il n'y a pas de méta-données git . Effectuer une réinitialisation comme celle-ci nécessite un répertoire .git existant et certains objets qu'il contient. Je n'ai rien. Ce n'est pas simplement un répertoire de travail gâché, ce n'est plus un référentiel d'aucune sorte.
Caleb
Ahh, mes excuses. C'est très inhabituel. Si git repo a disparu, je suppose qu’il n’y aura aucun moyen de le récupérer à moins d’observer les fichiers sous Linux et d’avoir les sauvegardes fs de ces fichiers. Je vais supprimer ma réponse car elle n’est pas pertinente.
Routhinator
Oui, je sais que c'est un problème inhabituel (et j'ai des sauvegardes). Ma question ici est de savoir comment cela a mal tourné ... où chercher le bogue dans git ou mon pilote de système de fichiers ou quoi que ce soit d'autre qui aurait pu empêcher de manger un répertoire au milieu d'une opération comme celle-ci.
Caleb
Je suis aussi très curieux. Hacherait pour que quelque chose comme ça arrive à mon repos.
Routhinator
-1

On dirait que quelqu'un a couru git push --forcesur ce dépôt et vous avez annulé ces changements. Essayez de cloner le repo frais, cela devrait vous ramener à l'état de travail propre.

conorsch
la source
1
La poussée forcée a rebasé la dernière poignée de commits. Ce n'est pas ce que j'ai retiré (le répertoire de travail n'est plus un répertoire de travail!) Et même s'il s'agissait d'un nouveau clonage, cela n'aurait aucun sens.
Caleb
4
Je ne pense pas que vous puissiez supprimer le .gitrépertoire de quelqu'un avec une poussée forcée
Grzegorz