Subversion, un outil de gestion de version Florent Guilleux Présentation pratique de Subversion à l’intention des débutants en gestion de version et des utilisateurs de CVS. Je n’aborde que les aspects clients. Ce n’est pas une présentation exhaustive des possibilités de Subversion, mais une présentation des concepts les plus importants et des bonnes pratiques les plus utiles. L’idée est découvrir rapidement les fonctionnalités offertes par Subversion et leur utilisation concrète pour de la gestion de version. Je commence par une introduction non technique.
Le développement de logiciel est une tâche complexe Mode de développement ouvert : contributeurs extérieurs relations à distance (mail, IRC, etc.) Gestion des diffusions (releases, correctifs de sécurité, etc.) Dépendances multiples (bibliothèques) … Depuis toujours (cf. le nombre de bogues qui ne semble pas décroître et l’insatisfaction des clients). Le développement de logiciel a été simplifié par l’apparition de nouveaux langages plus haut niveau, l’utilisation de méthode formelle de développement, etc. mais a aussi été en même temps complexifié par l’évolution de l’environnement informatique.
Le versionning apporte de nombreux gains Retours en arrière et corrections toujours possibles Historique de toutes les opérations Indispensable pour le travail en équipe Travaux en parallèle sur plusieurs branches Pour du code mais aussi un site web, de la doc… Dans le reste de la présentation, je présente Subversion dans le contexte du développement logiciel. Je parle donc de développeurs
… qui justifient l’effort de prise en main trunk patch commit version modules diff merge tag branches BASE update repository conflict HEAD check out
Un référentiel central et une copie de travail trunk/ 1 2 3 svn checkout svn commit svn commit Copie de travail d’Alice Je travaille en local, dans une copie de travail. Pour obtenir une copie de travail, j’utilise la commande checkout pour récupérer le contenu du dépôt central. Je peux ensuite reporter les modifications que j’ai faites dans ma copie de travail dans le dépôt central avec la commande commit. Les commits sont atomiques : soit ils sont appliqués, soit ils ne le sont pas, on est jamais dans un état intermédiaire non désiré. Au niveau du dépôt est créée une nouvelle révision, à laquelle est affecté un numéro de révision. Ce numérotage permet d’identifier les versions successives stockées au cours du temps dans le dépôt. Il est possible d’effectuer un commit pour un fichier ou un ensemble de fichiers. Un numéro de version désigne l’ensemble du dépôt, et non pas un fichier individuel. (différence avec CVS). La révision n est l’état du dépôt après n modifications qui y ont été affectées. Un fichier peut donc être identique entre deux révisions. Dans les clients graphiques (TortoiseSVN, Subclipse…) n’apparaissent pas explicitement les noms de commandes mentionnés dans cette présentation. Ils sont « cachés » sous la forme de menus. alice$work > svn co http://subversion.example.com/myProject/trunk myProject alice$work > svn commit myProject alice$work > svn commit myProject/file1.pl
Que faut-il enregistrer dans un référentiel ? tout ce qui peut est susceptible de changer au cours du temps le code + ce qui sert au déploiement de l’appli (scripts d’installation par exemple) la documentation du produit sauf ce qui peut être généré automatiquement (JavaDoc par exemple) éviter ce qui peut être généré automatiquement (JavaDoc par exemple) sauf si cela affranchit les utilisateurs d’installer un outil pour genérer ces élements
Quand faut-il faire des commit ? Souvent Après avoir testé et validé ses modifications En groupant dans un commit les modifications qui correspondent à une même fonctionnalité Il ne faut pas hésiter à effectuer des commit régulier (c’est une opération peu coûteuse, seulement un diff des modifications est propagé vers le serveur). Cela permet de simplifier la gestion des conflits quand on travaille à plusieurs, et les développeurs peuvent mieux suivre l’activité des uns des autres. Il est souhaitable qu’un commit corresponde à une modification précise et limitée, et qui a un sens fonctionnel du point de vue de l’application.
Récupérer n’importe quelle révision : svn checkout Par un numéro de révision alice$work > svn co –r 2 http://subversion.example.com/myProject myProject A myProject/trunk/file1.pl A myProject/trunk/file2.pl Checkout revision 2. Par une date alice$work > svn co –r {2006-01-15} http://subversion.example.com/myProject A myProject/trunk/file1.pl A myProject/trunk/file2.pl Checkout revision 1. Mettre à jour une copie locale : svn update Pour récupérer une copie de travail : svn checkout URL. Par défaut on récupère la dernière révision du référentiel. Il est possible de récupérer une révision donnée, ou à une date donnée. Attention quand on spécifie une date, le serveur Subversion va rendre la dernière révision précédant la date. Toutes les commandes acceptant l’option –r permettent l’utilisation d’une date. Cette commande checkout rapatrie des méta données qui permettront de manipuler la copie locale avec un client Subversion. La commande export permet de récupérer une révision dans un état « épuré », sans les méta information Subversion. alice$work > svn update myProject U myProject/file1.pl U myProject/file2.pl Updated to revision 3.
Connaître l’origine de sa copie locale : svn info alice$work > svn info myProject Path: myProject URL: http://subversion.example.com/myProject/trunk Repository UUID: d6959e13-b0o4-0673-7u654-a2v3e0b6c323 Revision: 2 Node Kind: directory Schedule: normal Last Changed Author: alice Last Changed Rev: 2 Last Changed Date: 2006-02-14 12:07:15 […]
Les autres opérations sur le référentiel svn add, copy, delete, move Référentiel trunk/ 3 4 svn checkout svn add svn commit Copie de travail d’Alice Un référentiel Subversion enregistre les versions successives d’une arborescence de fichiers (i.e. des fichiers et des répertoires). Il est possible de d’ajouter, copier, supprimer, déplacer et renommer des éléments (fichiers, répertoires) dans une copie de travail avec les commandes add, copier, delete, mkdir, move (ou rename). Un fichier modifié n’a pas besoin d’être signalé à Subversion qui le détecte automatiquement. On applique ces modifications dans le référentiel avec un commit car toutes ces opérations sont versionnées. Tout ce qui modifie le référentiel produit une nouvelle révision. On peut appliquer ces opérations directement sur le référentiel en spécifiant des URL en argument, dans ce cas pas besoin de commit ensuite. Différence avec CVS : svn add et delete fonctionne sur les répertoires et les fichiers, et conservent l’historique des modifications, tout comme move alice$work > svn add file3.pl alice$work > svn commit file3.pl alice$work > svn delete file2.pl
Connaître l’état de sa copie locale : svn status Référentiel alice$work > svn status myProject M myProject/file1.pl D + myProject/file2.pl A + myProject/file4.pl ? myProject/file5.pl ! myProject/file3.pl trunk/ 3 Copie de travail d’Alice svn checkout 1 2 3 1 4 5 svn delete 4 svn add svn status permet de voir quels changements ont été appliqués à sa copie de travail depuis sa récupération. C’est utile avant d’appliquer un commit. Le signe + dans la quatrième colonne indique que l’ajout et la suppression de ces fichiers est prévu dans le prochain commit. ? désigne un élément qui n’était pas dans la copie de travail lors de son checkout ! désigne un élément qui manque alors qu’il était présent dans la copie de travail Différence avec CVS : la sortie de status est lisible, donc à préférer à un update -n.
Les messages de journal (logs) A chaque commit est associé un message de journal alice$work > svn commit –m ‘chgt de $regexp’ myProject/file1.pl Le message doit indiquer pourquoi cette modification a été appliquée À chaque modification du référentiel (commit) doit être associé un message de journal (via l’appel d’un éditeur ou en option dans l’appel en ligne de commande) Un message de journal doit permettre de comprendre pourquoi cette modification a été faite. Il est inutile de dire ce qui changé car Subversion nous permettra de le retrouver. S’il s’agit d’une correction de bug, on peut référencer le numéro du bug report dans le message de journal. -m ‘Désormais on whitelist le format des paramètres CGI’
Connaître l’historique des modifications : svn log alice$work > svn log myProject/file1.pl ------------------------------------------------------ r3 | Alice | 2006-03-09 16:43:22 (Thu, 9 Mar 2006) Ajout de la gestion des sessions r2 | Alice | 2006-02-01 09:34:12 (Wed, 01 Feb 2006) Désormais on whitelist le format des paramètres CGI r1 | Alice | 2006-01-10 09:34:12 (Tue, 10 Jan 2006) Import initial alice$work > svn log –r 2 –v myProject/file1.pl ------------------------------------------------------ r2 | Alice | 2006-02-01 09:34:12 (Wed, 01 Feb 2006) Changed paths: M myProject/file1.pl M myProject/file2.pl Désormais on whitelist le format des paramètres CGI svn log renvoie tous les messages de journaux. Il est possible de spécifier une révision donnée, un intervalle entre deux révisions. L’option verbose indique quels sont les fichiers modifiés (ou ajoutés, supprimés, etc.) pour chaque révision
Connaître les modifications : svn diff Adresses Jean jean@example.com David david@example.com Email Jean jean@example.com Contacts.txt Contacts.txt (copie de travail) 1 svn diff Contacts.txt Index: Contacts.txt ================ --- Contacts.txt (revision 1) +++ Contacts.txt (working copy) @@ -1,2 +1,3 @@ - Email + Adresses Jean jean@example.com + David david@example.com svn diff donne les différences entre deux révisions du référentiel. On peut spécifier aussi des dates ou utiliser des mots clés. alice$myProject > svn diff –r X:Y file1.pl
Annuler des modifications dans la copie de travail Référentiel trunk/ 1 2 svn checkout svn commit Copie de travail d’Alice svn revert svn revert permet d’annuler les modifications apportées à sa copie locale : svn revert file.txt Pour l’appliquer à l’ensemble de sa copie locale : svn revert --recursive . alice$work > svn revert myProject/File1.pl Reverted ‘File1.pl’
Des conflits peuvent survenir Copie de travail de Bob svn commit svn checkout Référentiel 2 trunk/ 1 svn checkout svn commit Copie de travail d’Alice Deux personnes peuvent avoir récupéré des copies locales du référentiel, y apporter des modifications et les reporter dans le référentiel. Ces modifications concerner les mêmes fichiers et peuvent donc être incompatibles. Or Subversion ne permet pas* à un utilisateur de verrouiller des éléments du référentiel. Comment s’organiser pour gérer ces conflits potentiels, et que nous propose Subversion pour nous aider dans cette tâche ? Différence avec CVS : Subversion ne permet pas de commiter un fichier noté en conflit. Il faut d’abord explicitement lui signaler la résolution du conflit avec la commande svn resolved. * en fait si, Subversion 1.2 le permet
Résolution des conflits Copie de travail de Bob svn update svn commit svn checkout Référentiel trunk/ 1 2 svn checkout svn commit Copie de travail d’Alice Quand on veut commiter un fichier qui a changé dans le référentiel depuis son checkout, l’opération échoue : « commit failed » en indiquant que notre fichier local n’était plus à jour avec la version du référentiel. Il faut mettre à jour son fichier avec la version du référentiel avec la commande svn update*. Cette dernière fait un merge entre notre version locale et celle du référentielle. Il y a alors deux cas de figures : Subversion a réussi à fusionner les deux versions car les modifications ne concernaient pas les mêmes lignes (état «Merged ») Subversion n’a pas réussi à fusionner les deux versions car les modifications concernaient les mêmes lignes (état « conflict ») * Attention pour les utilisateurs de CVS : ne plus utiliser update pour visualiser les changements apportés en local, mais svn status. Svn update met à jour le référentiel et n’affiche pas les modifications locales. bob$myProject > svn commit File1.pl Sending File1.pl svn: commit failed (details follow): svn: Out of date: ‘/myProject/File1.pl’ in transaction ‘4’ bob$myProject > svn update File1.pl G File1.pl Updated to revision 2
Si les modifications concernent des lignes différentes État « merGed » : G Bob Zéro Un Deux Trois Quatre Zéro Un Deux Trois QUATRE svn commit svn update Zéro Un Deux Trois QUATRE Un Deux Trois Quatre Un Deux Trois QUATRE [enchaînement des commandes à réaliser] Alice
Si les modifications concernent les mêmes lignes État « Conflict » : C Un <<<< .mine Two ==== Dos >>>> .r2 Deux Trois QUATRE Un Two Trois Quatre svn update Un Deux Trois Quatre Un Dos Trois QUATRE File1.pl File1.pl.mine File1.pl.r2 File1.pl.r1 Subversion n’est pas capable de déterminer quelle fusion réaliser quand les modifications concernent les mêmes lignes. Les développeurs concernés doivent communiquer entre eux pour résoudre à la main dans le fichier les éléments qui sont en conflit. Si l’on considère que le conflit peut être résolu seul, on peut utiliser les fichiers .mine, .r2, .r1 pour écraser le fichier courant.
Si les modifications concernent les mêmes lignes Un <<<< .mine Two ==== Dos >>>> .r2 Trois QUATRE Un Two Trois QUATRE Un Two Trois QUATRE résolution manuelle svn resolved svn commit Un Two Trois QUATRE État « Conflict » : C Subversion n’est pas capable de déterminer quelle fusion réaliser quand les modifications concernent les mêmes lignes. Les développeurs concernés doivent communiquer entre eux pour résoudre à la main dans le fichier les éléments qui sont en conflit. La commande resolved indique à Subversion que le conflit est résolu.
Cycle de travail typique 1. Mettre à jour sa copie de travail svn update 2. Apporter des modifications svn add / copy / delete / move 5. Enregistrer ses modifications svn commit 4. Fusionner les modifications svn merge / resolved 3. Visualiser les modifications svn status / diff / revert Le premier update permet de mettre à jour sa copie de travail pour y inclure les dernières modifications des autres développeurs. Des éléments peuvent avoir été modifiés (U), ajoutés (A), supprimés (D), remplacés (R), fusionnés (G), ou être en conflits (C). Ensuite on peut travailler dans sa copie locale pour ajouter une nouvelle fonctionnalité, corriger une bogue, etc. Il est préférable de choisir avant de commencer ce que l’on va faire exactement. On peut éditer des fichiers, en ajouter, supprimer, changer l’arborescence, etc. Avant de commiter ces modifications, il est bon de revoir ce que l’on a fait. La commande status permet de voir tout ce qui a été modifié, et diff les modifications exactes. Revert permet de corriger des erreurs. Ces commandes ne consultent pas le référentiel, car la copie de travail a un cache de la version BASE. On a ainsi la possibilité de travailler sur les étapes 2 et 3 en mode déconnecté (c’est une différence avec CVS). Cela permet aussi au commit de n’être qu’une transmission des différences et non de l’ensemble de la copie locale. svn status –u permet de voir le status de sa copie locale par rapport au serveur et de détecter des éléments qui ne seraient déjà plus à jour par rapport au référentiel. Les commandes update et resolved permettent de résoudre les conflits potentiels (voir les diapos précédentes). Enfin svn commit permet d’enregistrer dans le référentiel ses modifications.