Comment exporter tous les fichiers qui ont changé depuis ma dernière livraison ? Voici une question qui a du tarauder un certain nombre de développeur au moins une fois. C’est vrai, après tout, on investit du temps sur l’utilisation d’un système de suivi de version (ici svn), si c’est pour en perdre encore lorsqu’il faut livrer, où est l’intérêt ? Pourquoi livrer la bibliothèque monstrueuse qu’on a du ajouter en révision 132 alors qu’on est révision 683 et qu’on fait une livraison en r650. Et je passe sur la question du client qui se demande pourquoi on lui renvoie une archive de 60 Mo alors qu’il n’a demandé que quelques corrections d’orthographe et de typographie ( si ! si ! ça pèse super lourd une virgule !).
En effet, svn export exporte toute le projet à une révision donnée. Et svn diff qui affiche bien les différences entre deux révisions ne produit que des sorties au format patch (il n’indique que les lignes qui ont été modifiées).
Après un certain nombre de recherches, j’ai été bien désespéré de voir que non seulement je n’étais pas le seul à buter sur ce problème, mais qu’en plus aucune solution ne semblait faire consensus. J’ai trouvé quelques solutions maisons et même des scripts entiers, mais aucuns ne correspondait à ce que je voulais faire, c’est-à-dire un simple export différentiel qui ressemblerait à un svn export (donc en bash).
J’ai donc décidé de me lancer et de créer cette perle rare. Comme j’en étais content et que je me suis dit que ça pourrait resservir, je vous la sert donc ci-dessous (en gpl). Vous noterez que je suis tellement persuadé de la dimension universelle de ce problème que j’ai décidé de le coder (et de le commenter en anglais).
Voici le code bash desvnxport.sh.
- #!/bin/sh
- # svnxport.sh
- #Export only modified files in SVN
- #
- # Copyright (C) 2009 by Julien Falconnet
- # http://www.falconnet.fr
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # any later version.
- #
- #
- #BEWARE : This script does not operate correctly with files whose filename contains spaces
- # tests for parameters
- if [ ! $1 ];then echo "svnxport : No source specified. Needs : source revision target_directory";exit;fi
- if [ ! $2 ];then echo "svnxport : No revision specified. Needs : source revision target_directory";exit;fi
- if [ ! $3 ];then echo "svnxport : No target_directory specified. Needs : source revision target_directory";exit;fi
- # check if the target_directory allready exists
- if [ -d $3 ];then echo "svnxport : target_directory '$3' allready exists. Remove it or change target_directory parameter.";exit;fi
- echo "Processing : source($1), revision($2), target_directory($3)"
- # we use svn diff to select changed files and only keep those updated or added.
- # Then the 'for' separate status from filename (here is the problem with file with blanks)
- for myfile in `svn diff -r $2:HEAD --summarize $1 | grep -e '^M ' -e '^A '`
- do
- if [ "$myfile" = "M" -o "$myfile" = "AM" -o "$myfile" = "A" -o "$myfile" = "." -o -d $myfile ]
- then
- # we ignore the status, and the directory to update
- continue
- else
- #we focus on true changed files
- #first we create needed directories for the current file
- #note that we use a relative directory system
- outfile=`echo $myfile |sed "s|$1||g"`
- dir="$3/$outfile"
- mkdir -p $(dirname $dir)
- #then we export the file
- svn export $myfile $3/$outfile >> /dev/null
- echo "export $3/$outfile "
- fi
- done
- # List other files. Changed but not exported. Mainly the deleted ones.
- # Usefull to know which files should be removed and follow weird comportment
- echo "Watch for : "
- svn diff -r $2:HEAD --summarize $1 | grep -v -e 'M ' -e 'A ' |sed "s|$1||g"
Pour ceux que ça intéressent, deux points ont été compliqués. D’abord, le fait qu’en bash le for découpe sur les espaces (et non pas sur les retours à la ligne) : Un coup sur deux j’avais le statut, ce qui était pénible puisque je ne voulais traiter que les A et les M et pas les D. J’ai faillit passé par un marqueur et finalement j’ai trouvé plus élégant de passer par un grep (qui s’intercale avant le découpage du for).
L’autre soucis a été la création de l’arborescence. En effet j’ai été déçu de voir que ni touch, ni mkdir, ni svn export n’étaient capables de créer des sous répertoires en même temps que le répertoire père. Du coup j’ai été obligé de rajouter cette boucle toute laide de mkdir. [edit ; grâce au post de Rodney Amato j’ai put trouver le paramètre -p de mkdir qui remplace avantageusement la boucle en question]
Par contre, du coup, les espaces dans les noms de fichiers sont découpés par le for et les fichiers correspondants ne sont pas exportés par le script. Il faudrait remplacer les espaces dans les fichier à la volée avant le for et les re-remplacer après mais c’était tellement inélégant que je m’y suis refusé. D’autant que cela ne me sert pas puisque je ne mets jamais d’espace dans mes noms de fichiers (c’est trop laid). Par contre si quelqu’un à une solution élégante pour contourner ce problème je suis preneur.
A bientôt...