Git et les fichiers zip

Publié le 01/08/2015

Si un projet se base sur un fichier zip (pas seulement les fichier “.zip”, mais tout fichier qui zipe le contenu comme les fichiers libreoffice, openoffice, vym, etc…) alors vous allez vous confronter au souci de versionnement par Git. Car celui-ci les gère comme étant des fichiers binaires. Or vous voulez versionner le contenu. Et bien voilà ma méthode (certes pas des plus jolies mais ça marche)

Je me suis trouvé confronté à cette situation en voulant versionner un livre sur le langage Go que je tape depuis pas mal de temps. Jusqu’ici je versionnais le fichier “.vym” (car j’utilise vym pour ça, je parle bien de View Your Mind hein, mais j’en reparelarai bientôt de tout ça) qui est ni plus ni moins qu’un fichier zippé et renommé en “.vym”. Or, je me suis fais une bonne frayeur en écransant connement mon fichier après un “git pull” trop rapidement validé.

Résultat, git a écrasé le fichier sans faire de “diff” - ce qui est logique car il ne le fait pas pour les fichier binaires.

Bref, pour éviter ce genre de boulette je me suis simplement ateler à versionner non plus le fichier, mais son contenu. Ça vous parait peut-être barbarre mais ça marche.

Premier chose à faire, on teste !

Testons la construction

On commence par se placer dans un dossier et on va décompresser le contenu du fichier (vous pouvez le faire avec un fichier .odt, un .vym ou un .docx si vous êtes nostalgique de Word…)

mkdir ~/test/constructor && cd ~/test/constructor
unzip /chemin/vers/fichier.vym

On voit alors quelques fichiers et répertoires, on reconstruit:

zip -r test ./*
mv test.zip ../fichier-reconstruit.vym

Et on ouvre ce fichier (placé dans “~/test/”). Tout à l’air ok ! Bien on passe à la suite.

Automatison

Dans mon répertoire git, j’ai donc créé un Makefile, et voilà ce que j’ai entré:


FILE="monfichier.vym"

explode:
    mkdir src/ || :
    cd src && unzip ../$(FILE)
    
implode:
    cd src && zip -r ../temp ./*
    mv temp.zip $(FILE)

Ainsi, quand je tape “make explode” le contenu de mon projet est décompressé dans le répertoire “src” que je vais pouvoir versionner. Et quand je récupère le projet depuis “git” (”git pull”) alors je tape simplement “make implode” pour reconstruire mon fichier.

Coté git

Coté git, je ne sauvegarde pas le fichier de travail (ici mon fichier “.vym”) car il va être reconstruit par le makefile. Donc mon fichier .gitignore est de la forme:

*.vym

Du coup, je suis tranquile, et surtout que la suite des opérations va engendrer une création de fichiers de sauvgarde, par conséquent on les ignorera aussi puisqu’ils utiliseront l’extension “.vym” (encore une fois, adaptez mes exemples à votre type de fichier)

Maintenant… les problèmes commencent…

Les problèmes et les solutions

Il se peut que votre “fichier zip” ait besoin de répertoires vides, par exemple Vym demande un répertoire “flags” et un autre “images” non vide. Or, git ne les versionne pas. Git ne peut gérer que des fichiers en fonction de leur chemin, et par conséquent un répertoire “vide” n’est pas un fichier, il va le zapper.

La solution, est simplement de créer des fichiers vides dans ces répertoires. Ainsi dans le Makefile, on va créer une cible de préparation “_prep” (le underscore pour éviter de le faire apparaitre dans l’autocompletion de bash) qui va écrire des fichiers nommés “.gitkeep” seulement dans les répertoires de projet (en évitant donc .git). Je remercie cette réponse de StackOverFlow.


FILE="monfichier.vym"

explode: _explode _prep

_explode:
    mkdir src/ || :
    cd src && unzip ../$(FILE)
    
implode:
    cd src && zip -r ../temp ./*
    mv temp.zip $(FILE)

_prep:
    find . -type d -empty -not -path "./.git/*" -exec touch \{\}/.gitkeep \;

J’ai décalé la cible “explode” en “_explode” pour changer l’ordre d’appel. Si vous suivez bien le Makefile, “make explode” va appeler “_explode” qui va decompresser le zip, puis “_prep” qui prépare les répertoires en “touchant” des fichier “.gitkeep”.

Autre chose, j’ai décidé de sauvegarder mon fichier de travail avant de le recréer. On ajoute une dernière règle nommée “_backup” qui copie le fichier en le datant à la seconde.

J’ajoute aussi un peu de propreté en séparant le nom de fichier et son extension.


BASE:="monfichier"
EXT:=".vym"
FILE:=$(BASE)$(EXT)
DATE=$(shell date "+%Y%m%d-%H%M%S")

explode: _explode _prep

_explode:
    mkdir src/ || :
    cd src && unzip ../$(FILE)
    
implode: _backup
    cd src && zip -r ../temp ./*
    mv temp.zip $(FILE)

_prep:
    find . -type d -empty -not -path "./.git/*" -exec touch \{\}/.gitkeep \;

_backup:
    cp $(FILE) $(BASE)-$(DATE)$(EXT)

Comme ça, chaque fois que j’appelle “make implode” pour reconstruire mon fichier “vym”, j’ai une sauvegarde de faite.

Petite parenthèse, dans un makefile on peut assigner une variable en utilisant “:=” ou “=”. La différence est que “:=” var interpréter “de suite” la valeur à assigner; alors que “=” attendra qu’on appelle la variable pour que sa valeur soit interprétée. Du coup, ici, la variable “$(DATE)” sera interprété au moment de l’appel dans “_backup”. C’est pas super utile ici mais par habitude…

Et dans la vie réelle ?

Dans la vie réelle, j’ai testé et ça se passe bien ! Voici ce qu’il peut arriver (et qui d’ailleurs m’ait arrivé):

  • je prend mon pc portable et je commence à toucher mon fichier vym
  • je suis content, je me dis que je peux pousser tout ça sur mon serveur git
  • je lance “make explode” puis “git commit src && git push” et là… le drame !

Git me dit que je suis pas à jour ! ça veut dire que j’ai oublié de faire un “pull” avant de pousser. Effectivement, la veille j’ai travaillé sur mon pc de bureau et j’avais poussé. Mais tête en l’ait que je suis, j’ai oublié cette scéance de boulot.

Il va se passer quoi ?

En premier lieu, je pull ! et je reconstruit mon fichier avec “make implode”. Vym recharge mon fichier. Deux possibilités:

  • Ça a l’air pas mal - auquel cas on peut refaire un “make explode” et pousser les le merge.
  • Tout est pété - là on transpire…

Si tout est pété, comme on a été intelligent, on a une sauvegarde qui a été créé lors du “make implode” dont le nom est la date d’aujourd’hui + l’heure de sauvegarde. Je peux reprendre cette version, me débrouiller pour reconstruire le fichier et commiter.

Au pire, on peut toucher le fichier XML contenu dans “src” car, je vous le rappelle, on travaille désormais avec des fichiers non compressés ce qui a pour effet de vous donner les “diffs”, voir les lignes qui ont sautées, etc.

Donc en clair, ma méthode de décompression + makefile qui fait le travaille de sauvegarde à ma place, ça me sauve des heures de travail et ma tension me remercie.

Notez aussi que cette technique peut s’appliquer aux fichier “.tar”, “.tar.gz” ou encore “.7z”. Le tout étant simplement de s’adapter au format. Le principe, je vous le rapelle, étant de versionner le contenu du fichier compressé et non pas le fichier compressé lui-même.

D’autres solutions ? d’autres méthodes ? faites le moi savoir !

comments powered by Disqus