Git stash: perdre du code, c’est possible

Parfois on pense naïvement que certains logiciels sont vraiment sûrs. C’est faux et on l’a vu avec OpenSSL (heartbleed, etc), une librairie critique. A présent c’est à git de me décevoir, célèbre logiciel de gestion de source.

Le bug est simple, vos fichiers sont perdus, un comble pour un logiciel de versioning de fichiers. La commande en question est git stash à utiliser when you want to record the current state of the working directory and the index, but want to go back to a clean working directory).

Après un git pull foireux, je me dis que je vais stasher mon code. ERREUR, car j’étais dans un cas particulier où git stash détruit magnifiquement les fichiers sans avertissement. Voici la procédure de reproduction du bug:

$ git --version
git version 2.2.1
$ mkdir test
$ cd test
$ git init
Dépôt Git vide initialisé
$ # création d'un fichier nommé "important"
$ echo "initial" > important
$ cat important 
initial
$ git add .
$ git commit -m "init"
[master (commit racine) 2847f2d] init
 1 file changed, 1 insertion(+)
 create mode 100644 important
$ # puis soudain, erreur de logique humaine, un fichier se transforme en dossier
$ rm important
$ mkdir important
$ # creation d'un fichier woot.txt dans important
$ echo "super important" > important/woot.txt
$ cat important/woot.txt 
super important
$ # le temps passe et un jour... git stash
$ git stash
Saved working directory and index state WIP on master: 2847f2d init
HEAD est maintenant à 2847f2d init
$ git st
Sur la branche master
rien à valider, la copie de travail est propre
$ # ensuite on restaure nos données
$ git stash pop
Suppression de important
Sur la branche master
Modifications qui ne seront pas validées :
  (utilisez "git add/rm ..." pour mettre à jour ce qui sera validé)
  (utilisez "git checkout -- ..." pour annuler les modifications dans la copie de travail)

        supprimé :        important

aucune modification n'a été ajoutée à la validation (utilisez "git add" ou "git commit -a")
refs/stash@{0} supprimé (9dcc39966175d918f30bb21bfbe11c9e6e8ab5ca)
$ # et BIM, tout a disparu
$ ls -a
.  ..  .git

Le dossier important n’existe plus, il n’était d’ailleurs pas versionné. A noter que git stash -u ne change rien sur la perte de données:

$ mkdir test02
$ cd test02
$ git init
$ echo "initial" > important
$ git add .
$ git commit -m "init"
$ rm important
$ mkdir important
$ echo "super important" > important/woot.txt
$ git st
$ git stash -u
$ git stash pop
$ ls -a
.  ..  .git  important
$ cat important
initial

Évidemment, c’est totalement con de transformer un fichier en dossier. Sauf dans le cas d’un lien symbolique vers un dossier qui se transforme en un vrai dossier, exemple:

$ mkdir test02
$ cd test02
$ git init
$ mkdir dir1
$ ln -s dir1 dir2
$ echo "initial" > dir1/important
$ git add .
$ git commit -m "init"
$ unlink dir2
$ mkdir dir2
$ echo "super important" > dir2/woot.txt
$ git st
$ git stash
$ git stash pop
$ ls -a
$ .  ..  dir1  .git

La logique humaine est incompréhensible sur le pourquoi du comment on se retrouve dans ce cas, au point de se dire que c’est à cause d’une mauvaise utilisation, mais je ne pense pas, un logiciel de gestion de version se doit de gérer tous les cas, mais surtout il se doit impérativement d’informer l’utilisateur en cas de perte de données possible.

Dans mon cas personnel, c’était un dossier « test », avec heureusement presque rien dedans. Sur un repository de longue date, on oublie facilement qu’un lien symbolique a été transformé en dossier, et on fait confiance au stash qui est censé tout sauvegarder (sauf les untracked, sauf si c’est demandé, dans ce cas comme on a vu, le untracked ne restaure pas les bons fichiers ensuite).

C’est un cas que je qualifierai de très exceptionnel, mais il est arrivé. Perdre des données c’est possible. J’ai quand même cherché pour savoir si j’étais tout seul, mais il semblerait que non, avec TortoiseGit, dont la conclusion est:

I’m able to reproduce this with git version 1.7.10.msysgit.1 as well as on Linux, so please report this upstream to the git mailing list. Thank you.

Même si le cas ne semble pas trop identique (cela concerne des fichiers ignorés), le problème est le même: perte de données. Moi qui utilisait git stash très souvent avec une grande confiance, à présent un backup du working directory sera effectué pour éviter toutes mésaventures sur du vrai code.

Cela me rappelle la fiabilité de linux avec cet article, où au lieu de couper/coller des fichiers, je devais, suite à un bug, copier/coller puis supprimer une fois que tout était bon pour éviter la perte de données… rebelote avec git, faire un backup avant de lancer une commande de « backup »… ahahaha, mais c’est vraiment triste pour linux en fait.