Julien De Bona
Free Software, Cooking, and Everything


Contrôle de révision avec Subversion

Publié le 2005-01-01.

Principes du contrôle de révisions

Le contrôle de révision consiste à conserver l'historique de fichiers et à pouvoir ainsi revenir à n'importe quelle version antérieure du document, à examiner les différences entre deux versions. A celà s'ajoutent d'autres fonctionnalités, comme la gestion des conflits lorsque plusieurs personnes travaillent sur le document, la possibilité de définir des tags (accéder simplement à une version donnée grâce à un nom simple), la création de branches (faire évoluer le fichier dans deux directions différentes) et le fusionnement de plusieurs branches. Il est également important de stocker quelques métadonnées afin d'identifier les différentes étapes dans l'évolution du document.

L'outil le plus connu de contrôle de révisions est CVS, mais il possède certaines limitations bien connues, et Subversion tend à le remplacer (c'est d'ailleurs l'optique dans laquelle il a été créé). Les systèmes de contrôle de révisions sont très utilisés par les programmeurs et sont les plus efficaces sur des fichiers en texte simple, bien qu'ils puissent gérer des fichiers binaires, avec plus ou moins d'efficacité. Des projets tels que KDE, Apache et Debian utilisent Subversion.

Fonctionnement de Subversion

Tout d'abord, Subversion ne travaille pas sur un fichier, ni même des fichiers, mais sur une arborescence de fichiers, gérant ainsi par exemple les renommages et déplacements de fichiers. Toute cette arborescence est stockée dans un dépôt et n'est jamais éditée directement par les programmeurs. Ceux-ci en extraient d'abord une copie de travail sur laquelle ils effectuent leurs modifications. Ensuite, ils copient grâce à subversion leurs modifications dans le dépôt. Régulièrement, ils peuvent aussi mettre à jour leur copie de travail pour prendre en compte les modifications apportées par d'autres développeurs. Il n'y a pas de système de verrous, ce qui permet à quiconque d'éditer des fichiers en même temps qu'un autre. C'est au moment de la centralisation du travail que Subversion vérifie si les changements sont en conflit, et demande au programmeur de les régler le cas échéant.

Installation

Sous Debian, un simple apt-get install subversion se chargera de l'installation.

Initialisation d'un premier dépôt

On crée un nouveau dépôt avec la commande svnadmin create /chemin/vers/depot . On pourra alors accéder à ce dépôt via l'URL file:///chemin/vers/depot .

Subversion possède un raccourci afin d'initialiser un dépôt avec la première version. Il s'agit de la commande svn import /chemin/vers/repertoire/a/importer file:///chemin/vers/depot . Un éditeur de texte s'ouvre (par défaut vi) pour indiquer une petite description de l'action réalisée. Si on ne veut pas passer par l'éditeur, on peut passer l'option -m et cette description en argument à la commande.

Typiquement, ce répertoire à importer comporte trois sous-répertoires traditionnellement nommés trunk , branches et tags , et les fichiers du projet sont placés dans le répertoire trunk . Il s'agit d'une topologie reproduisant une méthode classique de développement. Bien que non recommandé, on peut tout-à-fait travailler autrement. Si l'on veut inclure plusieurs projets, on peut créer au-dessus de cette arborescence un répertoire avec le nom du projet, ce qui donnerait: projet/trunk , projet/branches et projet/tags .

Commencer le travail

La première chose à faire est de récupérer une copie de travail. Cette opération est le checkout . La commande est svn checkout file:///chemin/vers/depot/trunk (noter le /trunk à la fin puisque tous les fichiers ont été placés dans ce répertoire). Cela va créer dans le répertoire courant un répertoire nommé trunk. Pour créer la copie dans un autre répertoire, il suffit d'en indiquer le nom à la fin de la ligne de commande: svn checkout file:///chemin/vers/depot/trunk mon_projet . Pour faire plus court, checkout peut être abrégé en co . On peut alors aller dans le répertoire ainsi créer et modifier les fichiers.

Commandes courantes

Si l'on peut simplement éditer les fichiers existants, il faut indiquer à Subversion les opérations plus complexes, telles que l'effacement, le déplacement et l'ajout d'un fichier. Pour effacer un fichier, il ne faut donc pas simplement utiliser rm fichier, mais svn remove fichier (ou s vn rm fichier ). Pour ajouter un fichier, il faut d'abord le créer, puis utiliser svn add fichier . Pour le déplacement, la commande est svn move fichier destination (ou svn mv/rename/ren fichier destination ). Aucune de ces commandes n'a d'effet sur le dépôt. elles indiquent simplement que cette opération devra être exécutée lors du commit.

Valider ses changements

Lorsque l'on est satisfait de ses modifications, on peut les envoyer vers le dépôt. La commande est svn commit fichier(s) (le répertoire courant est utilisé si aucun fichier n'est utilisé). Comme pour l'import, un éditeur s'ouvre pour commenter les modifications. Et comme pour l'import, l'option -m permet d'ajouter ce texte sur la ligne de commande.

Informations sur la copie de travail

Il se révèle très vite utile de se remémorer où l'on en est dans son travail. Les commandes suivantes peuvent être utilisées lorsqu'on se trouve dans une copie de travail.

svn info cible(s) : cette commande permet de savoir d'où et quand viennent les fichiers cibles (l'URL et la révision); le répertoire courant est utilisé si aucune cible n'est mentionnée. Un nouveau dépôt commence à la révision 0, puis le premier import crée la révision 1. Le numéro de révision est incrémenté à chaque fois que l'on modifie le dépôt, et est global pour tout le dépôt, à l'inverse de CVS, où chaque fichier a son propre numéro de révision.

svn status cible(s) : le fonctionnement est le même que pour info, mais la commande indique l'état du fichier par rapport à sa version actuelle dans le dépôt. A indique que le fichier a été ajouté, M qu'il a été modifié, D qu'il a été effacé, ? que le fichier n'est pas traîté par Subversion (un fichier créé mais non ajouté avec la commande svn add .

svn diff cible(s) : un "diff" est affiché pour chaque cible. Il résume toutes les modifications apportées aux fichiers. Les lignes précédées d'un – sont les lignes qui ont été retirées, celles précédées d'un + sont celles qui ont été ajoutées. Pratique quand on vient d'introduire un bug pour éviter de rechercher dans le fichier en entier, ou pour examiner les changements avant un commit.

Quand on s'est trompé.

Si les modifications dans la copie de travail sont très importante et n'aboutissent à rien, il est plus simple de repartir de zéro. La commande svn revert cible(s) permet cela, en restaurant les fichiers tels qu'ils étaient lors du checkout.

Si par contre on a déjà committé les changements, c'est un peu plus subtil. Subversion étant conçu pour ne perdre aucune modification, il faudra procéder en committant de nouveaux changements qui annuleront ceux du commit précédent. On pourrait être tenté d'effectuer un update avec l'option -r 42 (si 42 est la révision avant les modifications non désirées), mais lors du commit, Subversion se plaindrait que la copie de travail n'est pas à jour avec la dernière version. Le truc est d'utiliser la commande svn merge , qui sera expliquée au chapitre "Branches et tags". Cela donnerait svn merge -r 43:42 file:///chemin/vers/depot/trunk . Il s'agit donc d'appliquer à la copie de travail les modification qui permettent de passer de la révision 43 (la dernière commitée) à la 42 (la dernière bonne). Ensuite, on peut à nouveau committer ces modifications.

Branches et tags

Souvent, le besoin de faire évoluer le travail dans des directions différentes se fait sentir. Les deux cas les plus courants sont les branches de développement et les branches de versions.

Branches de développement

Lorsqu'un développeur doit réaliser un travail qui va impliquer plusieurs commits qui vont laisser le travail dans un état inconsistant, il crée généralement une branche à partir du tronc, dans laquelle il pourra committer son travail. Lorsque tout sera bon, cette branche sera fusionnée dans le tronc et effacée.

Branches de versions

Lorsqu'une nouvelle version du projet est planifiée (par exemple: 1.0), on lui dédie généralement une branche. Ainsi, le travail normal, qui sera incorporé dans la version suivante, continue dans le tronc en même temps que d'autres développeurs travaillent sur la branche pour fixer les derniers bugs. Et après que la version 1.0 soit publiée, on pourra toujours ajouter dans la branche les correctifs de bugs, et à partir de cette branche publier une version 1.0.1.

Création d'une branche

Une branche est une simple copie ( svn copy ). Heureusement, Subversion ne mémorise que des modifications et non des fichiers. Ainsi, lorsque l'on envoie ses modifications vers le dépôt, on n'envoie que les modifications apportées à la copie locale, ce qui est utile si on accède au dépôt à travers un réseau. De même, pour la copie, seul l'opération "copie" est mémorisée, et le dépôt ne doublera pas de taille. Par simplicité, svn copy peut être utilisé avec deux URLs en arguments: svn copy file:///chemin/vers/depot/trunk file:///chemin/vers/depot/branches/1.0 .

Une fois que l'on a créé la branche, il faut s'en créer une copie de travail. La solution svn checkout peut être utilisée, mais il est plus simple d'utiliser la commande switch depuis la racine d'une copie de travail: svn switch file:///chemin/vers/depot/branches/1.0 . Subversion ne transfèrera que les différences entre le tronc et la branche, soit rien du tout si on vient de créer la branche. Un svn info montrera alors que l'on travaille sur la branche

Fusionner les branches

Lorsqu'une branche de développement a atteint son but, il est temps de la fusionner dans le tronc. La fusion s'utilise aussi sur les branches de versions, pour corriger les bugs dans le tronc et les branches en même temps. Pour fusionner, il faut d'abord se placer dans la copie de travail dans laquelle on veut importer les modifications (ex: le tronc. Ensuite, c'est l'histoire d'un svn merge -r 42:47 file:///chemin/vers/depot/branches/1.0 , où 42 et 47 sont les révisions entre lesquelles on désire récupérer les changements. Il s'agit de changements, et non de copies. Si par exemple on a modifié dans le tronc une ligne d'un fichier et dans la branche une autre ligne, la modification effectuée dans le tronc ne sera pas perdue. Si par contre les modifications portaient sur la même ligne, on a ce que l'on appelle un conflit, qui doit être résolu manuellement. Cela sera traité au chapitre "travail à plusieurs", car ce cas n'apparaît que très rarement quand on travaille seul.

Il est à noter que s'il n'y a pas de conflit, ce n'est pas pour autant que les versions fusionnées seront le résultat attendu. La fusion se contente en effet de repérer les lignes modifiées et des les modifier dans la copie de travail. Lorsque l'on estime que le travail de fusion est terminé, on valide les changements avec svn commit (depuis la racine pour rester simple), en prenant soin de mentionner dans le commentaire les numéros de versions et la branches fusionnées. Cela permettra de savoir de quand date la dernière fusion et de réaliser correctement la suivante.

Les tags

Le tag sert à repérer une version donnée du travail grâce à un nom. Tout comme les branches, Subversion ramène les tags à une opération de copie. La convention est donc de copier vers le répertoire /tags du dépôt. Un tag et une branche sont donc en interne la même chose, sauf que l'on décide de ne jamais faire de changements dans un tag.

Travail à plusieurs

Lorsque l'on travaille à plusieurs, les choses peuvent se compliquer. Ainsi, chacun committe ses modifications pendant que les autres travaillent, et lorsque ceux-ci commitent leur travail, ils le font à partir d'une version déjà obsolète. Pour remédier à cela, il faut régulièrement utiliser la commande svn update cible(s) . Couramment, on se place à la racine de la copie de travail et on ne mentionne aucune cible pour mettre à jour toute la copie. Subversion refusera tout commit portant sur des fichiers qui n'ont pas été mis à jour avec update dans la copie de travail. Lors du commit, les noms des fichiers mis à jour, précédés d'une lettre. Si cette lettre est U, le fichier n'a pas été modifié dans la copie de travail, mais bien dans le dépôt: il n'y a donc pas de conflit sur ce fichier. Si cette lettre est G, le fichier a été modifié dans la copie de travail et dans le dépôt, mais ces changements ne se superposent pas et Subversion a fusionné les modifications. Si par contre cette lettre est C, les changements dans le dépôt et la copie de travail se superposent: il y a conflit, et il faut le résoudre manuellement avant de pouvoir committer. Attention cependant: l'absence de conflit ne signifie pas que le travail dans son ensemble est un état cohérent. Si par exemple une fonction a été renommée dans le fichier où elle a été déclarée, mais pas dans les fichiers où elle est appelée, Subversion ne pourra pas fournir d'avertissement.

Résolution d'un conflit.

Lorsqu'un C apparaît en face d'un fichier lors de l'update, trois fichiers supplémentaires sont créés, ayant le même nom que le fichier en conflit, mais avec une extension en plus. Ainsi, si un fichier foo est en conflit, foo.mine sera une copie du fichier tel qu'il était dans la copie de travail avant l'update; foo.rN contient le fichier tel qu'il était au checkout ou au dernier update avant le conflit (révision N); foo.rP contient le fichier tel qu'on l'a reçu du dépôt (à la version P). Lors du travail classique, on a N < P. Quant à foo , il contient le fichier original où le maximum de changements ont été fusionnés. Les changements en conflit son placés entre des marqueurs spéciaux, par exemple:

<<<<<<< .mine
code modifié dans la copie de travail
=======
code modifié dans le dépôt
>>>>>>> .rP

Tant que les fichiers supplémentaires ( .mine , .rN , . rP ) existent, Subversion refusera de committer ce fichier. Pour résoudre le conflit, on peut soit:

  • modifier foo et en retirer tous les marqueurs spéciaux et ne laisser que le code qu'on veut committer

  • copier un des fichiers supplémentaires en lieu et place du fichier en conflit, pour ignorer toutes les modifications réalisées dans l'une des deux copies

  • utiliser svn revert foo pour effacer les modifications locales apportées à foo.

Evidemment, l'action à prendre dépend du résultat souhaité, et peut nécessiter une communication entre les développeurs, ce que Subversion ne rend de toute manière pas dispensable.

Lorsque le fichier foo correspond à ce qui est désiré, on peut indiquer à Subversion que le conflit est résolu, soit en utilisant la commande svn resolved foo , soit en effaçant les fichiers .mine , .rN et .rP . On peut alors procéder au commit.

Utilisation à distance

Evidemment, lorsqu'on travaille à plusieurs, il est recommandé de pouvoir accéder à distance au dépôt, ou même lorsqu'on est seul, pour placer le dépôt sur une machine accessible depuis Internet ou bien dont les fichiers sont sauvegardés automatiquement. Par ailleurs, Subversion a été optimisé pour se connecter le moins possible au dépôt et minimiser le trafic lors de ces connexions. Par exemple, dans une copie de travail, tous les fichiers obtenus lors du checkout ou de l'update sont conservés afin de pouvoir y revenir sans consulter le dépôt.

Subversion fournit un serveur svnserve (comparable au pserver de CVS), pouvant donner accès au dépôt à distance, en fonctionnant comme un démon, ou en étant lançé par inetd. Mais la façon la plus simple de le lancer est grâce à SSH. Aucune configuration n'est nécessaire pour cette dernière méthode; on accède au dépôt via une URL du genre svn+ssh://user@machine:/chemin/vers/depot . Si l'on utilise une authentification par mot de passe, il faut alors parfois le taper plusieurs fois car pour certaines situations Subversion ne peut pas effectuer le transfert en une seule connexion.

Subversion peut aussi être interfacé avec Apache 2 (URL de type http://).

Pour en savoir plus

Subversion est distribué avec un manuel très complet intitulé "Version Control with Subversion". Sous Debian, on peut le trouver au format HTML dans /usr/share/doc/subversion/book . Il est aussi édité aux éditions O'Reilly.


tags: svn

Quelques tags ...