Gestion de versions avec Subversion [En cours de rédaction]

Pour Subversion 1.8

(Compilé à partir de r6065)

Ben Collins-Sussman

Brian W. Fitzpatrick

C. Michael Pilato

Ce travail est placé sous la licence Creative Commons Attribution. Pour voir le contenu de cette licence, rendez-vous sur http://creativecommons.org/licenses/by/2.0/fr/. Vous pouvez aussi envoyez une lettre à Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.


Table des matières

Avant-propos
Préface
Qu'est-ce que Subversion ?
Subversion est-il l'outil approprié ?
L'histoire de Subversion
L'architecture de Subversion
Les composantes de Subversion
Ce qui a changé dans Subversion
Public visé
Comment lire ce livre
Organisation de ce livre
Ce livre est libre
Remerciements
I. Faire connaissance avec Subversion
1. Notions fondamentales
Notions générales de la gestion de versions
Le dépôt
Copie de travail
Modèles de gestion de versions
Problématique du partage de fichiers
Modèle verrouiller-modifier-libérer
Modèle copier-modifier-fusionner
Subversion en action
Dépôts Subversion
Révisions
URL des dépôts Subversion
Copies de travail
Fonctionnement de la copie de travail
Actions de base sur la copie de travail
Copies de travail mixtes, à révisions mélangées
Résumé
2. Utilisation de base
À l'aide !
Enregistrement de données dans le dépôt
Importation de fichiers et de dossiers
Organisation conseillée d'un dépôt
Limitations sur les noms
Création d'une copie de travail
Cycle de travail de base
Mise à jour de la copie de travail
Modifications dans la copie de travail
Revue des changements apportés
Vue d'ensemble des changements effectués
Détail des modifications effectuées localement
Annulation des changements de la copie de travail
Résolution des conflits
Traitement des lignes en conflit de façon interactive
Résolution des conflits en mode interactif
Remise à plus tard de la résolution d'un conflit
Résolution des conflits à la main
Abandon des modifications au profit de la révision la plus récente
Retour en arrière avec svn revert
Propagation des modifications
Recherche dans l'historique
Détail des modifications passées
Modifications locales
Comparaison entre la copie de travail et le dépôt
Comparaison entre des révisions du dépôt
Historique des modifications
Navigation dans le dépôt
Affichage du contenu d'un fichier
Affichage ligne par ligne des auteurs de modifications
Contenu des dossiers suivis en versions
Extraction d'anciennes versions au sein d'un dépôt
Parfois, il suffit de faire le ménage
Suppression d'une copie de travail
Reprise après une interruption
Gestion des conflits d'arborescences
Un exemple de conflit d'arborescences
Résumé
3. Sujets avancés
Identifiants de révisions
Mots-clés de révision
Dates de révision
Révisions pivots et révisions opérationnelles
Propriétés
Utilisation des propriétés
Manipuler les propriétés
Les propriétés et le cycle de travail Subversion
Propriétés héritées
Configuration automatique des propriétés
Propriétés réservées à l'usage de Subversion
Propriétés suivies en versions
Propriétés non suivies en versions
Portabilité des fichiers
Type de contenu des fichiers
Fichiers exécutables ou non
Caractères de fin de ligne
Occultation des éléments non suivis en versions
Substitution de mots-clés
Répertoires clairsemés
Verrouillage
Création d'un verrou
Identification d'un verrou
Cassage et vol d'un verrou
Communication par l'intermédiaire des verrous
Définition de références externes
Listes de modifications
Création et modification de listes de modifications
Listes de modifications : des filtres pour vos opérations
Limitations des listes de modifications
Modèle de communication réseau
Requêtes et réponses
Éléments d'authentification du client
Mise en cache des éléments d'authentification
Désactivation de la mise en cache des mots de passe
Suppression des éléments d'authentification déjà en cache
Authentification en ligne de commande
Un dernier mot sur l'authentification
Travail sans copie de travail
Opérations du client texte interactif à distance
Utilisation de svnmucc
Résumé
4. Gestion des branches
Définition d'une branche
Utilisation des branches
Création d'une branche
Travail sur votre branche
Gestion des branches par Subversion : notions clés
Fusions : pratiques de base
Ensembles de modifications
Garder une branche synchronisée
Fusions de sous-arborescences et mergeinfo
Réintegration d'une branche
Mergeinfo et aperçus
Retour en arrière sur des modifications
Résurrection des éléments effacés
Fusions : pratiques avancées
Sélection à la main
Syntaxe de la fusion : pour tout vous dire
Fusions sans mergeinfo
Plus de détails sur les conflits liés aux fusions
Blocage de modifications
Historiques et annotations tenant compte des fusions passées
Prise en compte ou non de l'ascendance
Fusions, copies et renommages
Blocage des clients qui ne prennent pas en compte les fusions
Recommandations finales sur le suivi des fusions
Parcours des branches
Étiquettes
Création d'une étiquette simple
Création d'une étiquette complexe
Maintenance des branches
Agencement du dépôt
Durée de vie des données
Modèles courants de gestion des branches
Branches de publication
Branches fonctionnelles
Branches fournisseurs
Procédure générale de gestion des branches fournisseurs
Branches fournisseurs depuis des dépôts externes
Branches fournisseurs à partir de sources mirroirs
Créer une branche ou ne pas créer une branche ?
Résumé
5. Administration d'un dépôt
Définition d'un dépôt Subversion
Stratégies de déploiement d'un dépôt
Stratégies d'organisation d'un dépôt
Stratégies d'hébergement d'un dépôt
Contrôle d'accès au dépôt
Création et configuration d'un dépôt
Création d'un dépôt
Mise en place des procédures automatiques
Configuration de l'environnement des procédures automatiques
Utilisations classiques des procédures automatiques
Trouver des procédures automatiques ou écrire les vôtres
Configuration de FSFS
Maintenance d'un dépôt
Boîte à outils de l'administrateur
svnadmin
svnlook
svndumpfilter
svnrdump
svnsync
fsfs-reshard.py
Correction des commentaires de propagation
Gestion de l'espace disque
Économie d'espace disque
Suppression des transactions mortes
Tasser le système de fichiers FSFS
Migration des données d'un dépôt
Migration des données d'un dépôt à l'aide de svnadmin
Migration des données d'un dépôt en utilisant svnrdump
Filtrage de l'historique d'un dépôt
Réplication d'un dépôt
Réplication avec svnsync
Réplication partielle avec svnsync
Une astuce rapide pour la création de mirroirs
Autour des réplications
Sauvegarde d'un dépôt
Gestion des identifiants uniques (UUID) des dépôts
Déplacement et suppression d'un dépôt
Résumé
6. Configuration du serveur
Présentation générale
Choix d'une configuration serveur
Serveur svnserve
svnserve sur SSH
Serveur HTTP Apache
Recommandations
svnserve, un serveur sur mesure
Démarrage du serveur
svnserve en serveur autonome
svnserve via inetd
svnserve via xinetd
svnserve encapsulé dans un tunnel
svnserve en tant que service Windows
svnserve en tant que tâche launchd
Authentification et contrôle d'accès intégrés
Création d'un fichier utilisateurs et d'un domaine d'authentification
Mise en place du contrôle d'accès
Utilisation de svnserve avec SASL
Authentification par SASL
Chiffrement SASL
Encapsulation de svnserve dans un tunnel SSH
Astuces de configuration de SSH
Mise en œuvre initiale
Contrôle de la commande à exécuter
Référence pour la configuration de svnserve
Configuration générale
Configuration de Cyrus SASL
httpd, le serveur HTTP Apache
Prérequis
Configuration Apache de base
Options d'authentification
Mise en place de l'authentification HTTP basique
Authentification par condensat (Digest)
Contrôle d'accès
Contrôle d'accès général
Contrôle d'accès par répertoire
Désactivation du contrôle sur les chemins
Fichier de contrôle d'accès suivi en versions dans le dépôt
Encapsulation du trafic réseau avec SSL
Configuration du certificat serveur SSL de Subversion
Gestion des certificats clients SSL Subversion
Amélioration des performances
KeepAlive
Mises à jour groupées
Fonctionnalités bonus
Navigation dans les dépôts
Journalisation Apache
Mandataire en écriture
Autres fonctionnalités d'Apache
Référence pour la configuration d'un serveur Subversion HTTP Apache
Directives de configuration de mod_dav_svn
Directives de configuration de mod_authz_svn
Contrôle d'accès basé sur les chemins
Introduction au contrôle d'accès basé sur les chemins
Contrôle d'accès par groupes
Alias
Fonctionnalités avancées de contrôle d'accès
Embûches avec le contrôle d'accès
Journalisation du haut-niveau
Optimisation du serveur
Mise en cache des données
Compression des données sur le réseau
Accès au dépôt par plusieurs méthodes
7. Personnalisation de Subversion
Zone de configuration des exécutables
Agencement de la zone de configuration
Configuration via la base de registre Windows
Options de configuration
Configuration générale
Configuration spécifique à un serveur
Localisation
Généralités sur la localisation
Utilisation des paramètres régionaux par Subversion
Utilisation d'éditeurs externes
Utilisation des outils externes de comparaison et de fusion
Programmes externes de comparaison
Programmes externes de comparaison de trois fichiers
Outils de fusion externes
Résumé
8. Intégration de Subversion
Organisation des bibliothèques en couches successives
Couche dépôt
Couche d'accès au dépôt
Couche client
Utilisation des API
APR, la bibliothèque Apache de portabilité des exécutables
Fonctions et bâtons
Prérequis pour les URL et les chemins
Utilisation d'autres langages que C et C++
Exemples de code
Résumé
II. Guide de référence des commandes Subversion
I. Guide de référence de svn : le client texte interactif
svn add
svn blame (praise, annotate, ann)
svn cat
svn changelist (cl)
svn checkout (co)
svn cleanup
svn commit (ci)
svn copy (cp)
svn delete (del, remove, rm)
svn diff (di)
svn export
svn help (h, ?)
svn import
svn info
svn list (ls)
svn lock
svn log
svn merge
svn mergeinfo
svn mkdir
svn move (mv)
svn patch
svn propdel (pdel, pd)
svn propedit (pedit, pe)
svn propget (pget, pg)
svn proplist (plist, pl)
svn propset (pset, ps)
svn relocate
svn resolve
svn resolved
svn revert
svn status (stat, st)
svn switch (sw)
svn unlock
svn update (up)
svn upgrade
II. Guide de référence de svnadmin : administration des dépôts Subversion
svnadmin crashtest
svnadmin create
svnadmin deltify
svnadmin dump
svnadmin freeze
svnadmin help (h, ?)
svnadmin hotcopy
svnadmin list-dblogs
svnadmin list-unused-dblogs
svnadmin load
svnadmin lock
svnadmin lslocks
svnadmin lstxns
svnadmin pack
svnadmin recover
svnadmin rmlocks
svnadmin rmtxns
svnadmin setlog
svnadmin setrevprop
svnadmin setuuid
svnadmin unlock
svnadmin upgrade
svnadmin verify
III. Guide de référence de svnlook : outil d'exploration du contenu d'un dépôt Subversion
svnlook author
svnlook cat
svnlook changed
svnlook date
svnlook diff
svnlook dirs-changed
svnlook filesize
svnlook help (h, ?)
svnlook history
svnlook info
svnlook lock
svnlook log
svnlook propget (pget, pg)
svnlook proplist (plist, pl)
svnlook tree
svnlook uuid
svnlook youngest
IV. Guide de référence de svnserve : serveur Subversion sur mesure
svnserve
V. Guide de référence de svnversion : informations relatives à la copie de travail Subversion
svnversion
VI. Guide de référence de svnsync : réplication de dépôt Subversion
svnsync copy-revprops
svnsync help
svnsync info
svnsync initialize (init)
svnsync synchronize (sync)
VII. Guide de référence de svnrdump : migration à distance des données d'un dépôt Subversion
svnrdump dump
svnrdump help
svnrdump load
VIII. guide de référence de svndumpfilter : outil de filtrage de l'historique de Subversion
svndumpfilter exclude
svndumpfilter include
svndumpfilter help
IX. Guide de référence de svnmucc : client texte Subversion pour URL multiples
svnmucc
X. Guide de référence des procédures automatiques de Subversion
start-commit
pre-commit
post-commit
pre-revprop-change
post-revprop-change
pre-lock
post-lock
pre-unlock
post-unlock
III. Appendices
A. Guide de démarrage rapide avec Subversion
Installation de Subversion
Tutoriel rapide
B. Guide Subversion à l'usage des utilisateurs de CVS
Les numéros de révisions sont différents
Suivi de versions des répertoires
Davantage d'opérations en mode déconnecté
Distinction entre les commandes status et update
Status
Update
Branches et étiquettes
Propriétés des métadonnées
Résolution des conflits
Fichiers binaires et conversions
Gestion de versions des modules
Authentification
Conversion d'un dépôt CVS vers Subversion
C. WebDAV et la gestion de versions automatique
À propos de WebDAV
Gestion de versions automatique
Interopérabilité des clients
Applications WebDAV autonomes
Microsoft Office, Dreamweaver, Photoshop
cadaver, DAV Explorer
Greffons WebDAV pour explorateur de fichiers
Dossiers Web de Microsoft
Nautilus, Konqueror
Implémentations de WebDAV en système de fichiers
WebDrive, NetDrive
Mac OS X
davfs2 (Linux)
D. Le système de fichiers Berkeley DB
Configuration de l'environnement Berkeley DB
Limites de Berkeley DB
Limites architecturales
Déploiement sur un partage réseau
Tolérance aux pannes et restauration
Maintenance d'un dépôt Berkeley DB
Rétablissement de bases de données Berkeley DB
Purge des fichiers de journalisation inutilisés
Utilitaires Berkeley DB
E. Copyright
Index

Liste des illustrations

1. L'architecture de Subversion
1.1. Un authentique système client/serveur
1.2. La situation à éviter
1.3. Modèle verrouiller-modifier-libérer
1.4. Modèle copier-modifier-fusionner
1.5. Modèle copier-modifier-fusionner (suite)
1.6. L'évolution de l'arborescence au cours du temps
1.7. Système de fichiers du dépôt
4.1. Branches de développement
4.2. Structure initiale du dépôt
4.3. Dépôt avec nouvelle copie
4.4. Historique des branches d'un fichier
8.1. Fichiers et répertoires en deux dimensions
8.2. Prise en compte du temps — la troisième dimension de la gestion de versions !

Liste des tableaux

1.1. Les différents motifs d'URL pour l'accès aux dépôts
2.1. Requêtes classiques dans l'historique
4.1. Commandes de gestion des branches et des fusions
6.1. Comparaison des fonctionnalités des serveurs Subversion
C.1. Principaux clients WebDAV

Liste des exemples

4.1. Procédure automatique de vérification des capacités de suivi des fusions avant une propagation
5.1. hooks-env (configuration personnalisée de l'environnement des procédures automatiques)
5.2. procédure automatique start-commit qui s'assure que le client sait suivre les fusions.
5.3. txn-info.sh (lister les transactions inachevées)
5.4. Procédure automatique pre-revprop-change du dépôt miroir
5.5. Procédure automatique start-commit du dépôt miroir
6.1. Exemple de fichier de définition de tâche launchd pour svnserve
6.2. Exemple-type de configuration : accès anonyme
6.3. Exemple-type de configuration : accès authentifié
6.4. Exemple-type de configuration : accès mixte authentifié/anonyme
6.5. Désactiver complètement les contrôles sur les chemins
6.6. Utilisation d'un fichier de contrôle d'accès unique suivi en versions à l'intérieur d'un dépôt
6.7. Utilisation d'un fichier de contrôle d'accès spécifique pour chaque dépôt
7.1. Exemple de fichier de modification de la base de registre (.reg)
7.2. diffwrap.py
7.3. diffwrap.bat
7.4. diff3wrap.py
7.5. diff3wrap.bat
7.6. mergewrap.py
7.7. mergewrap.bat
8.1. Utilisation de la couche dépôt
8.2. Utilisation de la couche dépôt en Python
8.3. Une version de status en Python

Avant-propos

Karl Fogel

Chicago, le 14 mars 2004.

Une mauvaise FAQ est composée non pas des questions que posent les utilisateurs, mais de celles que l'auteur de la FAQ voudrait qu'on lui pose. Peut-être avez-vous rencontré ce type de FAQ :

Q : Comment peut-on utiliser Glorbosoft XYZ pour maximiser la productivité de nos équipes ?

R : Beaucoup de nos clients veulent savoir comment maximiser la productivité avec notre nouvelle suite bureautique brevetée. La réponse est simple : cliquez sur le menu Fichier, puis trouvez Améliorer la productivité plus bas, ensuite…

Le problème avec de telles FAQ, c'est qu'elles ne sont pas du tout, au sens propre, des FAQ. Personne n'a appelé le support technique et demandé « Comment pouvons-nous améliorer la productivité ? » Au lieu de ça, les gens posent des questions très précises, telles que « Comment pouvons-nous configurer le système de calendrier pour envoyer les rappels deux jours en avance au lieu de 24 heures ? » etc. Hélas, il est tellement plus facile d'imaginer des questions que de trouver celles qui sont vraiment fréquemment posées. Rédiger une vraie FAQ requiert un effort continu et une bonne organisation : tout au long de la vie du logiciel, les questions posées ainsi que leurs réponses doivent être suivies de près, puis rassemblées et organisées de façon claire et cohérente dans un tout qui doit refléter l'expérience des utilisateurs. Cela nécessite d'être patient et observateur, tel un naturaliste. Ici, pas de grandes théories ni de discours visionnaires, ce qu'il faut avant tout, c'est ouvrir les yeux et prendre des notes.

Ce que j'aime à propos de ce livre, c'est qu'il a été créé en suivant ce procédé, ce qui se ressent à chacune de ses pages. C'est le résultat direct de la rencontre des auteurs et des utilisateurs. Tout a commencé lorsque Ben Collins-Sussman remarqua que les gens posaient constamment les mêmes questions de base sur la liste de diffusion de Subversion : Quelles sont les procédures pour travailler avec Subversion ? Est-ce que les branches et les étiquettes fonctionnent comme dans les autres systèmes de gestion de versions ? Comment est-ce que je peux trouver qui a fait telle ou telle modification ?

Frustré de voir revenir les mêmes questions jour après jour, Ben travailla d'arrache-pied pendant un mois durant l'été 2002 pour écrire The Subversion Handbook, un manuel de soixante pages couvrant toutes les bases de Subversion. Le manuel ainsi écrit n'avait pas la prétention d'être complet, mais il fut distribué avec Subversion pour aider les utilisateurs à faire leurs premiers pas dans l'apprentissage de Subversion. Quand O'Reilly and Associates décidèrent de publier un livre complet sur Subversion, la voie la plus facile était la plus évidente : simplement améliorer The Subversion Handbook.

Une opportunité inhabituelle se présenta donc aux trois co-auteurs de ce nouveau livre. Officiellement, leur tâche était d'écrire un livre « académique », en partant d'une table des matières et d'une première ébauche. Mais ils avaient aussi accès à un flux constant, une quantité incontrôlable en fait, de réactions en provenance des utilisateurs. Subversion était déjà entre les mains de quelques milliers d'utilisateurs précoces, et ces derniers envoyaient des tonnes de commentaires, pas seulement sur Subversion, mais aussi sur sa documentation d'alors.

Pendant que Ben, Mike et Brian écrivaient ce livre, ils surveillèrent sans relâche la liste de diffusion et les salons de discussion de Subversion, notant consciencieusement les problèmes que rencontraient les utilisateurs dans la réalité. Assurer le suivi de ces retours d'expériences faisait de toutes façons partie intégrante de leur travail à CollabNet, et cela leur donna un énorme avantage quand ils commencèrent à rédiger la documentation de Subversion. Le livre qu'ils ont écrit repose sur un socle d'expérience pratique, pas sur une liste abstraite de bonnes intentions ; il possède à la fois les qualités du mode d'emploi et de la FAQ. Cette dualité ne saute pas immédiatement aux yeux. Lu dans l'ordre, de la première à la dernière page, ce livre décrit de manière simple un logiciel. Il y a la vue d'ensemble, l'incontournable visite guidée, le chapitre sur la configuration et l'administration, quelques sujets avancés, et bien évidemment une liste complète des commandes ainsi qu'un guide de débogage. Mais c'est quand on revient chercher dans ce livre une réponse à un problème spécifique qu'on réalise son authenticité, faite de détails révélateurs ne pouvant provenir que de cas concrets et inattendus, d'exemples tirés de situations réelles, et par-dessus tout de l'attention portée aux besoins et aux remarques des utilisateurs.

Bien sûr, personne ne peut affirmer que ce livre répondra à toutes vos questions sur Subversion. De temps en temps, la précision avec laquelle il anticipe vos questions vous semblera presque télépathique ; mais d'autres fois, vous tomberez sur une lacune dans le savoir de la communauté, et vous rentrerez bredouille. Quand cela arrive, le mieux que vous puissiez faire est d'envoyer un courrier électronique à (en anglais si possible) en y décrivant votre problème. Les auteurs sont toujours là, à l'affût, et il ne s'agit pas seulement des trois personnes citées sur la couverture du livre, mais aussi de beaucoup d'autres contributeurs ayant apporté corrections et améliorations. Pour la communauté, résoudre votre problème est une composante agréable d'un projet bien plus vaste, celui de peaufiner petit à petit ce livre, et finalement Subversion lui-même, pour encore mieux coller à l'utilisation que les gens en ont. Les auteurs sont très enthousiastes à l'idée de communiquer avec vous, pas seulement parce qu'ils peuvent vous aider, mais aussi parce que vous pouvez les aider. Avec Subversion, comme avec tous les projets de logiciels libres en activité, vous n'êtes pas seul.

Ce livre est votre premier compagnon.

Préface

 

« Il est important de ne pas laisser la perfection devenir l'ennemi du bien, même lorsque vous pouvez être d'accord sur ce qu'est la perfection. Encore plus lorsque vous ne le pouvez pas. Aussi déplaisant qu'il soit d'être piégé par les erreurs du passé, vous ne pouvez pas faire de progrès en ayant peur de votre propre ombre pendant la conception. »

 
 --Greg Hudson, développeur de Subversion

Dans le monde du logiciel libre, le logiciel « Concurrent Versions System » (CVS) fut l'outil de choix pour la gestion de versions pendant de nombreuses années. Et à juste titre. CVS était lui-même libre et son mode de fonctionnement non-restrictif couplé à son support des opérations réseau permettait à des dizaines de programmeurs dispersés aux quatre coins du monde de partager leur travail. Cela collait très bien à la nature collaborative du logiciel libre. CVS et son modèle de développement semi-chaotique sont depuis devenus des pierres angulaires de la culture du logiciel libre.

Mais CVS n'était pas parfait et simplement corriger ses défauts promettait d'être un énorme effort. C'est ici que Subversion entre en jeu. Les créateurs de Subversion l'ont créé pour être un successeur de CVS et l'ont fait de façon à gagner le cœur des utilisateurs de CVS de deux façons : en concevant un logiciel libre doté d'une interface similaire à CVS et en tentant d'éviter la plupart des défauts majeurs de CVS. Bien que le résultat ne soit pas forcément la prochaine évolution majeure dans les systèmes de gestion de versions, Subversion est très puissant, parfaitement utilisable et très flexible.

Ce livre est écrit pour documenter les versions 1.8 du système de gestion de versions Apache™ Subversion®[1]. Nous avons tenté d'être aussi complet que possible dans cet ouvrage. Cependant, Subversion a une communauté prospère et dynamique ; il y a donc déjà un certain nombre de fonctionnalités et d'améliorations prévues pour des versions futures de Subversion qui peuvent modifier quelques commandes ou rendre caduques certaines notes spécifiques de ce livre.

Qu'est-ce que Subversion ?

Subversion est un logiciel libre de la catégorie systèmes de gestion de versions (VCS en anglais, pour Version Control System). Cela signifie que Subversion gère les fichiers et les répertoires, ainsi que les changements dont ils font l'objet, à travers le temps. Vous pouvez ainsi retrouver d'anciennes versions de vos données ou parcourir l'historique des changements de vos données. Cela fait dire à certains que les systèmes de gestion de versions sont en quelque sorte des « machines à remonter le temps ».

Subversion peut fonctionner en réseau, ce qui autorise un partage des données sur des ordinateurs différents. D'une certaine manière, la possibilité pour un groupe de personnes de modifier et gérer le même ensemble de données en restant derrière son propre poste de travail renforce la collaboration. Le travail peut avancer sans nécessiter un circuit unique de validation. Et comme ce qui est réalisé est suivi en versions, vous n'avez pas à craindre que la disparition du circuit de validation ne se fasse au détriment de la qualité— si de mauvaises données sont entrées, vous n'avez qu'à annuler la modification.

Certains systèmes de gestion de versions sont aussi des systèmes de gestion de configuration logicielle (GCL). Ces systèmes sont spécialement conçus pour gérer des arborescences de code source et possèdent de nombreuses fonctionnalités propres au développement logiciel, comme la reconnaissance des langages de programmation ou des outils de construction/compilation de logiciel. Subversion, cependant, ne fait pas partie de cette catégorie. C'est un système généraliste qui peut être utilisé pour gérer n'importe quel ensemble de fichiers. Pour vous ce peut être du code source ; pour d'autres cela va de la liste de courses jusqu'aux vidéos des vacances et bien au-delà.

Subversion est-il l'outil approprié ?

Si, en tant qu'utilisateur ou administrateur système, vous réfléchissez à la mise en place de Subversion, la première question à vous poser est :« est-ce bien l'outil adéquat pour ce que je veux faire ? » Subversion est un marteau fantastique, mais il faut faire attention à ne pas assimiler tout problème à un clou.

En premier lieu, vous devez décider si la gestion de versions en général répond à votre besoin. Si vous voulez archiver de vieilles versions de vos fichiers et dossiers, éventuellement de les ressusciter, ou d'examiner les journaux détaillant leurs évolutions, les systèmes de gestion de versions le font très bien. Si vous avez besoin de travailler sur des documents en collaboration avec d'autres personnes (habituellement via un réseau) et de conserver la trace de qui a apporté quelles modifications, les systèmes de gestion de versions font également l'affaire. En fait, c'est pour ces raisons que les systèmes de gestion de versions tels que Subversion sont souvent utilisés dans des environnements de développement logiciel ; travailler au sein d'une équipe de développement est par nature une activité sociale où les modifications au code source sont constamment discutées, réalisées, évaluées et parfois retirées. Les outils de gestion de versions rendent très facile ce type de collaboration.

Bien sûr il existe aussi un coût lié à l'utilisation de la gestion de versions. À moins de pouvoir externaliser l'administration du système de gestion de versions, vous devrez évidemment en assumer l'administration système. En travaillant au jour le jour avec les données, vous ne pourrez pas copier, déplacer, renommer ou supprimer des fichiers de la façon dont vous le faisiez auparavant. À la place, vous devrez accomplir ces tâches via Subversion.

En supposant que cette quantité de travail supplémentaire ne vous pose pas de problème, vous devriez quand même vérifier que vous n'allez pas utiliser Subversion pour résoudre un problème que d'autres outils pourraient résoudre de manière bien plus efficace. Par exemple, parce que Subversion fournit une copie des données à tous les utilisateurs concernés, une erreur courante est de le traiter comme un système de distribution générique. Les gens utilisent parfois Subversion pour partager d'immenses collections de photos, de musique numérique ou de packs logiciels. Le problème est que ce type de donnée ne change en général jamais. La collection grandit au fil du temps, mais les fichiers individuels à l'intérieur de la collection ne changent pas. Dans ce cas, utiliser Subversion est « disproportionné ». [2] Il existe des outils plus simples, capables de copier des données efficacement sans s'embarrasser de toute la gestion du suivi des modifications, tels que rsync ou unison.

Une fois que vous avez décidé d'utiliser un système de gestion de versions, vous aurez pléthore de choix. Au moment où la première version de Subversion a été conçue et est sortie, la version prédominante de la gestion de versions était la gestion centralisée de versions— un serveur maître distant hébergeait les données suivies en versions et les utilisateurs travaillaient localement avec des copies dont l'historique était restreint. Subversion s'est rapidement imposé lors de sa sortie comme le leader incontesté de ce paradigme, étant largement adopté et remplaçant de nombreux systèmes de conception plus anciennes. Il conserve toujours cette position de leader aujourd'hui.

Beaucoup de choses ont changé depuis ce temps. Dans les années qui ont suivi la sortie de Subversion, un nouveau concept de gestion de versions, appelé gestion de versions distribuée (DVCS en anglais, pour distributed version control system), a également suscité beaucoup d'intérêt et a largement été adopté. Des outils tels que Git (https://git-scm.com/) et Mercurial (https://www.mercurial-scm.org/) se sont imposés dans ce créneau des DVCS. Les systèmes de gestion de versions distribués tirent parti du haut débit disponible pour les accès au réseau et du faible coût de stockage pour offrir une approche différente du modèle centralisé de gestion de versions. D'abord, et ce qui est le plus évident, c'est qu'il n'y a plus de serveur central hébergeant seul les données suivies en versions. Chaque utilisateur conserve dans, et travaille sur, des dépôts locaux l'historique (complet dans un sens) des données suivies en versions. Le travail collaboratif a toujours lieu, mais s'accomplit par l'échange direct entre utilisateurs d'ensembles de modifications faites à chacun des éléments suivis en versions, sans passer par un serveur central maître. En fait, toute déclaration de données « maîtres » d'un projet suivi en versions est une pure convention, adoptée par les différents collaborateurs à ce projet.

il existe des avantages et des inconvénients à chacune des approches. Les deux principaux bénéfices que l'on peut tirer des outils décentralisés sont les incroyables performances des opérations quotidiennes (parce que le dépôt des données est stocké localement) et une meilleure gestion des fusions entre branches (parce que les algorithmes de fusion sont ceux qui constituent le cœur même de ce type de logiciel). La contrepartie est que ces systèmes possèdent de manière inhérente un modèle de gestion plus compliqué, qui peut constituer un frein certain au travail collaboratif. Par ailleurs, les DVCS font bien leur travail en partie grâce au contrôle délégué à l'utilisateur, alors que les systèmes centralisés prennent ce contrôle à leur compte — la possibilité de gérer les accès en fonction du chemin dans l'arborescence, la flexibilité pour mettre à jour ou recouvrer individuellement des éléments suivis en versions, etc. Heureusement, beaucoup de grandes organisations ont convenu que le débat n'avait pas à être dogmatique et que Subversion ainsi que les DVCS tels que Git peuvent être utilisés harmonieusement ensemble dans l'organisation, chacun étant utilisé dans l'environnement qui lui convient le mieux.

Hélas, ce livre traite de Subversion, donc nous ne tenterons pas de mener une comparaison exhaustive de Subversion avec les autres outils. Le lecteur à qui il revient de choisir un système de gestion de versions est encouragé à s'enquérir de toutes les options qui s'offrent à lui et à choisir l'outil qui convient le mieux, à lui et à ses collaborateurs. Et si c'est Subversion qui a retenu ses faveurs, ce livre fournit, dans les chapitres qui suivent, des myriades d'informations détaillées pour conduire avec succès la mise en œuvre de Subversion !

L'histoire de Subversion

Au début des années 2000, CollabNet, Inc. (maintenant connue sous le nom de Digital.ai, https://digital.ai) commença à rechercher des développeurs pour écrire un remplaçant à CVS. CollabNet fournissait [3] une suite logicielle collaborative appelée « CollabNet Enterprise Edition (CEE) » dont l'un des composants est la gestion de versions. Même si CEE utilisait CVS comme système de gestion de versions initial, les limitations de celui-ci étaient évidentes depuis le début, et CollabNet savait qu'il lui faudrait au final trouver quelque chose de mieux. Malheureusement, CVS était devenu le standard de fait dans le monde du logiciel libre, essentiellement parce qu'il n'y avait rien de mieux, en tout cas sous licence libre. Donc CollabNet décida d'écrire un nouveau système de gestion de versions ex nihilo, en conservant les idées de base de CVS, mais sans ses bogues ni ses limitations fonctionnelles.

En février 2000, CollabNet contacta Karl Fogel, l'auteur de Open Source Development with CVS (Coriolis, 1999), et lui demanda s'il aimerait travailler sur ce nouveau projet. Il se trouve qu'au même moment Karl ébauchait la conception d'un nouveau système de gestion de versions avec son ami Jim Blandy. En 1995, ils avaient créé ensemble Cyclic Software, une société fournissant des contrats de support pour CVS, et bien qu'ils aient plus tard revendu la société, ils utilisaient toujours CVS quotidiennement dans leur travail. Leurs frustrations à propos de CVS avaient conduit Jim à élaborer mentalement de meilleures façons de gérer les données suivies en versions. Il avait déjà non seulement trouvé le nom de « Subversion », mais aussi les principes de base du stockage de données de Subversion. Quand CollabNet les appela, Karl accepta immédiatement de travailler sur le projet et Jim obtint de son employeur, Red Hat Software, qu'il le délègue au projet pour une durée indéterminée. CollabNet embaucha Karl et Ben Collins-Sussman, et le travail de conception détaillée commença en mai. Grâce à des coups de pouce efficaces de Brian Behlendorf et Jason Robbins de CollabNet, et de Greg Stein (qui travaillait alors en tant que développeur indépendant, et participait aux spécifications du projet WebDAV/DeltaV), Subversion attira rapidement une communauté de développeurs actifs. Il s'avéra que beaucoup d'entre eux avaient ressenti les mêmes frustrations avec CVS et ils saisirent l'opportunité de pouvoir enfin y faire quelque chose.

L'équipe d'origine se mit d'accord sur quelques objectifs simples. Ils ne voulaient pas inventer de nouvelles méthodes de gestion de versions, ils voulaient juste corriger ce qui n'allait pas dans CVS. Ils décidèrent que Subversion reprendrait les fonctionnalités de CVS et préserverait son modèle de développement, mais ne reproduirait pas ses faiblesses les plus évidentes. Malgré le fait que Subversion devait pouvoir avoir ses propres spécificités, il devait être suffisamment semblable à CVS pour que n'importe lequel de ses utilisateurs puisse facilement passer à Subversion.

Le 31 août 2001, après 14 mois de codage, Subversion devint « auto-hébergeant ». Ce qui veut dire que les développeurs de Subversion cessèrent d'utiliser CVS pour gérer le propre code source de Subversion et commencèrent à utiliser Subversion à la place.

Bien que CollabNet ait initié le projet et qu'elle subventionne encore une grosse partie du travail (en payant les salaires complets de quelques développeurs de Subversion), Subversion fonctionne comme la plupart des projets de logiciel libre, dirigé par un ensemble de règles vagues et transparentes qui encouragent la méritocratie. En 2009, Collabnet a travaillé avec les développeurs de Subversion pour intégrer le projet dans la Apache Software Foundation (ASF), un des regroupement les plus mondialement connus de projets de logiciels libres. Les racines techniques de Subversion, les priorités de sa communauté et les pratiques de développement collaient parfaitement aux de l'ASF, dont beaucoup de membres étaient déjà des contributeurs actifs à Subversion. Début 2010, Subversion a été complètement adopté par la famille des projets phares de l'ASF, a déplacé son point d'ancrage sur le Web vers https://subversion.apache.org et a été rebaptisé « Apache Subversion ».

L'architecture de Subversion

La Figure 1, « L'architecture de Subversion » donne une « vue d'ensemble » du schéma de conception de Subversion.

Figure 1. L'architecture de Subversion

L'architecture de Subversion


D'un côté, nous avons un dépôt Subversion qui contient toutes vos données suivies en versions. De l'autre côté, il y a votre programme client Subversion, qui gère des versions locales d'une partie de ces données suivies en versions. Entre ces deux extrêmes, il y a des chemins variés utilisant différentes couches d'accès au dépôt. Certains de ces chemins passent par des réseaux informatiques et des serveurs réseau avant d'atteindre le dépôt. D'autres court-circuitent complètement le réseau et accèdent directement au dépôt.

Les composantes de Subversion

Une fois installé, Subversion est constitué de nombreux composants. Ce qui suit est un survol rapide de ce que vous obtenez. Ne vous inquiétez pas si certaines de ces brèves descriptions vous laissent dubitatif ; ce livre contient de nombreuses pages destinées à dissiper toute confusion.

svn

Le programme client texte interactif.

svnversion

Un programme permettant d'examiner l'état d'une copie de travail (en termes de révisions des éléments présents).

svnlook

Un outil qui permet d'examiner directement un dépôt Subversion.

svnadmin

Un outil destiné à la création, la modification ou la réparation d'un dépôt Subversion.

mod_dav_svn

Un greffon pour le serveur HTTP Apache, utilisé pour rendre votre dépôt disponible à d'autres personnes à travers un réseau.

svnserve

Un serveur autonome créé sur mesure pour Subversion, pouvant fonctionner comme un processus démon ou pouvant être invoqué par SSH ; une autre façon de rendre votre dépôt accessible à d'autres personnes à travers un réseau.

svndumpfilter

Un programme qui permet de filtrer les flux d'exports de l'historique de vos dépôts.

svnsync

Un programme capable de synchroniser de manière incrémentale un dépôt avec un autre dépôt à travers un réseau.

svnrdump

Un programme destiné à réaliser des exports et des chargements de l'historique d'un dépôt à travers un réseau.

svnmucc

Un programme capable d'effectuer des opérations basées sur les URL sur plusieurs dépôts en une seule opération et sans avoir besoin de passer par des copies de travail.

Ce qui a changé dans Subversion

La première édition de ce livre a été publiée par O'Reilly Media en 2004, peu après que Subversion ait atteint la version 1.0. Depuis, le projet Subversion a régulièrement publié de nouvelles versions majeures du logiciel. Voici un résumé rapide des changements majeurs qui ont eu lieu depuis Subversion 1.0. Cette liste n'est pas exhaustive ; pour tous les détails, merci de vous rendre sur le site web de Subversion à l'adresse https://subversion.apache.org.

Subversion 1.1 (septembre 2004)

En version 1.1 fut introduit FSFS qui permet de stocker le dépôt sous forme de fichiers textes. Bien que les bases Berkeley DB soient toujours très utilisées et supportées par la communauté, FSFS est devenu le choix par défaut pour la création de nouveaux dépôts, grâce à sa prise en main facile et à ses besoins minimes en termes de maintenance. Dans cette version ont également été ajoutées les possibilités de suivre en versions des liens symboliques et de prendre en compte automatiquement des URL, ainsi qu'une interface utilisateur régionalisée.

Subversion 1.2 (mai 2005)

La version 1.2 introduisit la possibilité de créer des verrous sur les fichiers côté serveur, sérialisant ainsi l'accès des propagations à certaines ressources. Bien que Subversion soit toujours fondamentalement un système de gestion de versions à accès simultanés, certains types de fichiers binaires (par exemple des images de synthèse) ne peuvent pas être fusionnés. Le mécanisme de verrouillage répond aux besoins de suivi en versions et de protection de ces données. Avec le verrouillage est également apparue une implémentation complète de l'auto-versionnement WebDAV, permettant aux dépôts Subversion d'être accessibles sous la forme de dossiers partagés sur le réseau. Enfin, Subversion 1.2 commença à utiliser un nouvel algorithme plus rapide de différenciation de données binaires pour compresser et récupérer de vieilles versions de fichiers.

Subversion 1.3 (décembre 2005)

Avec la version 1.3, le serveur svnserve sait contrôler les droits en fonction des chemins, ce qui correspondait à une fonctionnalité existant uniquement à cette époque dans le serveur Apache. Cependant, le serveur Apache bénéficia lui-même de nouvelles fonctionnalités de journalisation et les API de connexion entre Subversion et d'autres langages firent également de grands pas en avant.

Subversion 1.4 (septembre 2006)

En version 1.4 fut introduit un tout nouvel outil, svnsync, permettant la réplication, dans une seule direction, d'un dépôt via le réseau. Des parties importantes des métadonnées des copies de travail changèrent de format afin de ne plus utiliser XML (avec pour conséquence des gains en rapidité côté client), tandis que le gestionnaire de base de données des dépôts Berkeley DB acquit la capacité de rétablir les bases automatiquement suite à un crash du serveur.

Subversion 1.5 (juin 2008)

Sortir la version 1.5 prit beaucoup plus de temps que les autres versions, mais la fonctionnalité vedette était titanesque : le suivi semi-automatisé des branches et des fusions. Ce fut une véritable bénédiction pour les utilisateurs et propulsa Subversion bien au-delà des possibilités de CVS, le plaçant à la hauteur de ses concurrents commerciaux tels que Perforce et Clearcase. En version 1.5 tout un tas d'autres fonctionnalités axées sur l'utilisateur furent introduites, telles que la résolution interactive des conflits entre fichiers, les extractions partielles, la gestion des listes de modifications côté client, une nouvelle syntaxe très puissante pour les définitions externes et le support par le serveur svnserve de l'authentification par SASL.

Subversion 1.6 (mars 2009)

La version 1.6 renforça la gestion des branches et des fusions en introduisant la notion de conflits d'arborescences. Elle améliorait également plusieurs fonctionnalités déjà existantes : davantage d'options pour la résolution interactive des conflits, support d'exclusions pour les extractions partielles, définitions externes à partir de fichiers, journalisation opérationnelle pour la commande svnserve similaire à celle fournie par mod_dav_svn. Par ailleurs, le client texte interactif introduisit de nouveaux raccourcis pour référencer les URL de dépôts Subversion.

Subversion 1.7 (octobre 2011)

La version 1.7 constituait en premier lieu un moyen de livrer deux grosses évolutions à des composants un peu vieillissants de Subversion. Celle ayant le plus d'impact s'appelait « WC-NG » ; une réécriture complète de la bibliothèque libsvn_wc de gestion des copies de travail. La deuxième évolution était l'introduction d'un protocole HTTP plus léger pour l'interaction entre le client et le serveur Subversion. Subversion 1.7 offrait une poignée de nouvelles fonctionnalités, beaucoup de résolutions de bugs et aussi quelques notables améliorations de performances.

Subversion 1.8 (juin 2013)

Dans la version 1.8, le client Subversion améliore le suivi des renommages de fichiers et de répertoires ; la commande svn merge a suffisamment muri pour rendre l'utilisation de l'option --reintegrate inutile. Certaines nouvelles valeurs de propriétés suivies en versions peuvent être héritées des dossiers parents. Cette fonctionnalité permet maintenant de fixer des valeurs par défaut pour la définition des propriétés automatiques et les motifs de noms de fichiers à ignorer, ce qui apporte de la cohérence pour tous les utilisateurs d'un dépôt alors que, auparavant, cela devait se gérer de manière collaborative. Il intègre aussi un nouvel outil de fusion en ligne de commande pour la résolution interactive des conflits. Et comme toujours, Subversion 1.8 inclut beaucoup de fonctionnalités supplémentaires, la résolution de bugs et des améliorations dans le comportement et les performances.

Public visé

Ce livre est écrit pour les personnes désirant utiliser Subversion pour gérer leurs données. Subversion fonctionne sur un grand nombre de systèmes d'exploitation et son interface première est en ligne de commande. Ce programme (svn) et certains programmes auxiliaires sont le sujet de ce livre.

Par souci de cohérence, les exemples du livre supposent que le lecteur utilise un système de type Unix et qu'il est relativement à l'aise avec ce système ainsi qu'avec les interfaces en ligne de commande. Cela dit, le programme svn fonctionne également sur les systèmes qui ne sont pas basés sur Unix, tel que Microsoft Windows. Avec quelques petites exceptions, telles que l'utilisation d'anti-slashs (\) au lieu de slashs (/) dans les chemins, les entrées et sorties de ce programme sous Windows sont identiques à leurs équivalentes Unix.

La plupart des lecteurs sont probablement des programmeurs ou des administrateurs systèmes qui ont besoin de suivre les changements faits à du code source. C'est l'utilisation la plus courante de Subversion et c'est ce que l'on supposera tout au long des exemples du livre. Cependant, Subversion peut être utilisé pour gérer les changements pour toutes sortes de données : images, musique, bases de données, documentation, etc. Pour Subversion, toutes les données sont juste des données.

Bien que ce livre soit écrit en supposant que le lecteur n'a jamais utilisé un système de gestion de versions, nous avons aussi essayé de rendre facile le passage de CVS (et autres systèmes) à Subversion. De temps en temps, quelques encadrés mentionnent d'autres systèmes de gestion de versions et l'Annexe B, Guide Subversion à l'usage des utilisateurs de CVS résume la plupart des différences entre CVS et Subversion.

Notez également que les exemples de code source présentés au cours du livre ne sont que des exemples. Même s'ils compilent avec les commandes de compilation adéquates, ils n'ont pour but que d'illustrer une situation particulière et ne sont pas nécessairement de bons exemples de style ou techniques de programmation.

Comment lire ce livre

Les manuels techniques doivent toujours faire face au dilemme suivant : choisir une approche d'apprentissage descendante ou ascendante pour le lecteur ? Un adepte de l'approche descendante préférera lire ou survoler la documentation, pour obtenir une vision globale du fonctionnement du système ; à partir de ce moment seulement, il commence à utiliser le logiciel. Un adepte de l'approche ascendante est plus un autodidacte, il se jette directement dans le logiciel et en comprend au fur et à mesure les fonctionnalités, se référant au manuel en tant que de besoin. La plupart des livres sont écrits pour un certain type de lecteur et celui-ci est indubitablement orienté pour les adeptes de l'approche descendante (d'ailleurs, si vous lisez ce chapitre, c'est que vous êtes probablement dans cette catégorie !). Mais si vous êtes autodidacte, ne fuyez pas. Bien que le livre puisse être vu comme un large survol des fonctionnalités de Subversion, le contenu de chaque paragraphe est gorgé d'exemples et d'exercices pratiques. Pour les plus impatients, rendez-vous directement à l' Annexe A, Guide de démarrage rapide avec Subversion.

Quelle que soit votre manière d'apprendre, ce livre se veut utile pour des gens ayant des parcours et des compétences très variés, depuis le novice en gestion de versions jusqu'à l'administrateur système expérimenté. En fonction de votre expérience, certains chapitres vous sembleront plus ou moins importants. Nous proposons ci-dessous quelques « parcours » adaptés à différents types de lecteurs :

Administrateur système expérimenté

Nous supposons dans ce cas que vous avez déjà utilisé un système de gestion de versions et que vous voulez monter un serveur Subversion le plus rapidement possible. Le Chapitre 5, Administration d'un dépôt et le Chapitre 6, Configuration du serveur expliquent comment créer votre premier dépôt et le mettre à disposition sur le réseau. Ceci fait, le Chapitre 2, Utilisation de base et l'Annexe B, Guide Subversion à l'usage des utilisateurs de CVS sont le plus court chemin pour apprendre à utiliser le client Subversion.

Novice

Votre administrateur vient probablement de mettre en place Subversion et vous devez apprendre à utiliser le client. Si vous n'avez jamais utilisé de système de gestion de versions, alors le Chapitre 1, Notions fondamentales est une introduction indispensable aux concepts de la gestion de versions. Le Chapitre 2, Utilisation de base est un tour du propriétaire du client Subversion.

Utilisateur avancé

Que vous soyez utilisateur ou administrateur, votre projet va finir par prendre de l'importance. Il vous faudra apprendre comment effectuer des opérations plus pointues avec Subversion, comme utiliser des branches ou effectuer des fusions (Chapitre 4, Gestion des branches), utiliser les propriétés des objets Subversion (Chapitre 3, Sujets avancés), configurer les options d'exécution (Chapitre 7, Personnalisation de Subversion) et d'autres choses encore. Ces chapitres ne sont pas indispensables au début, mais pensez bien à les lire une fois que vous vous sentirez à l'aise avec les bases.

Développeur

Vous êtes certainement déjà habitué à Subversion et vous voulez à présent étendre ses fonctionnalités ou développer un nouveau logiciel utilisant ses nombreuses API. Le Chapitre 8, Intégration de Subversion est écrit pour vous.

Le livre se termine par le Partie II, « Guide de référence des commandes Subversion ». C'est le guide de référence pour toutes les commandes de Subversion, les annexes couvrant certaines notions particulièrement utiles. Ce sont certainement les chapitres vers lesquels vous retournerez une fois la première lecture terminée.

Organisation de ce livre

Les chapitres qui suivent, ainsi que leur contenu, sont listés ci-dessous :

Chapitre 1, Notions fondamentales

Explique les bases de la gestion de versions ainsi que les différents modèles associés, les notions de dépôts Subversion, de copies de travail et de révisions.

Chapitre 2, Utilisation de base

Une balade dans l'utilisation quotidienne de Subversion. Ce chapitre explique comment récupérer, modifier et propager des données à l'aide du client Subversion.

Chapitre 3, Sujets avancés

Ce chapitre couvre des fonctionnalités plus complexes, que les utilisateurs réguliers auront à utiliser un jour, comme les métadonnées suivies en versions, le verrouillage de fichiers et les piquets de révisions.

Chapitre 4, Gestion des branches

Ce chapitre traite des branches, des fusions et des étiquettes, y compris les bonnes pratiques pour la gestion et la fusion de branches, des cas d'école, comment revenir en arrière sur des modifications et comment passer facilement d'une branche à une autre.

Chapitre 5, Administration d'un dépôt

Ce chapitre décrit les bases d'un dépôt Subversion, comment le créer, le configurer et en assurer la maintenance. Il présente également les outils disponibles pour toutes ces actions.

Chapitre 6, Configuration du serveur

Ce chapitre explique comment configurer votre serveur Subversion et présente différentes manières d'accéder à votre dépôt : HTTP, le protocole svn et l'accès au disque en local. Il couvre aussi l'authentification, les autorisations et les accès anonymes.

Chapitre 7, Personnalisation de Subversion

Ce chapitre explore les fichiers de configuration du client Subversion, décrit la prise en compte des contenus internationaux et montre comment utiliser des programmes externes conjointement avec Subversion.

Chapitre 8, Intégration de Subversion

Ce chapitre décrit l'architecture interne de Subversion, le système de fichiers associé et les zones administratives des copies de travail, du point de vue du programmeur. Il montre comment utiliser les API publiques pour écrire un programme qui utilise Subversion.

Partie II, « Guide de référence des commandes Subversion »

Ce chapitre explique de manière très détaillée chacune des sous-commandes svn, svnadmin et svnlook avec tout un tas d'exemples pour contenter l'ensemble de la famille !

Annexe A, Guide de démarrage rapide avec Subversion

Pour les impatients, l'installation de Subversion et son utilisation en moins de deux minutes chrono. Vous êtes prévenu.

Annexe B, Guide Subversion à l'usage des utilisateurs de CVS

Cette annexe couvre les similitudes et les différences entre Subversion et CVS, avec des suggestions pour perdre les mauvaises habitudes que vous avez acquises durant des années d'utilisation de CVS. Cela comprend les descriptions des numéros de révision de Subversion, les répertoires suivis en versions, les opérations sans connexion réseau, la distinction entre status et update, les branches, les étiquettes, les métadonnées, la résolution de conflits et l'authentification.

Annexe C, WebDAV et la gestion de versions automatique

Cette annexe décrit en détail WebDAV et DeltaV ; elle explique comment configurer votre dépôt Subversion pour qu'il puisse être monté en lecture/écriture par des clients DAV.

Annexe E, Copyright

Cette annexe contient une copie de la Licence Creative Commons dont ce livre fait l'objet.

Ce livre est libre

Ce livre est parti de quelques morceaux de documentation écrits par les développeurs du projet Subversion, qui furent alors fusionnés en un seul travail et réécrits. En tant que tel, il a toujours été sous licence libre (cf. l'Annexe E, Copyright). En fait, le livre a été écrit sous le regard du public, faisant au départ partie intégrante du projet Subversion. Cela veut dire deux choses :

  • Vous trouverez toujours la version la plus récente de ce livre dans le propre dépôt Subversion du livre.

  • Vous pouvez modifier ce livre et le redistribuer comme vous le voulez, il est sous licence libre. Votre seule obligation est de conserver correcte l'attribution du copyright aux auteurs d'origine. Bien sûr, nous préférerions que vous envoyiez vos commentaires et vos correctifs à la communauté des développeurs Subversion, plutôt que de distribuer votre version privée de ce livre.

Le portail internet de développement de ce livre, et de la plupart de ses traductions, est accessible à l'adresse : https://svnbook.red-bean.com. Vous y trouverez des liens sur les dernières parutions et les versions étiquetées du livre dans différents formats, ainsi que des instructions pour accéder au dépôt Subversion du livre (où se trouve son code source XML DocBook). Vos réactions sont les bienvenues et même encouragées. Prière de soumettre tous vos commentaires, réclamations et correctifs concernant les sources du livre à .

Remerciements

Ce livre n'aurait pas été possible (ni très utile) si Subversion n'existait pas. C'est pourquoi les auteurs tiennent à remercier Biran Behlendorf et CollabNet pour avoir vu l'intérêt et avoir osé investir dans un nouveau projet de logiciel libre aussi risqué et ambitieux ; Jim Blandy pour le nom et le design original de Subversion, on t'aime Jim ; et Karl Fogel pour être, surtout, un si bon ami et, ensuite, un grand leader pour la communauté. [4]

Merci à O'Reilly et aux différents professionnels de l'édition qui nous ont aidés à rendre le contenu de ce livre plus attrayant à différentes étapes de sa construction : Chuck Toporek, Linda Mui, Tatiana Apandi, Mary Brady et Mary Treseler. Leur patience et leur soutien ont été extraordinaires.

Enfin, nous remercions le nombre incalculable de personnes qui ont contribué à ce livre par des relectures informelles, des suggestions et des corrections. La liste exhaustive de ces personnes serait impossible à imprimer et à maintenir ici, mais que leurs noms restent à jamais gravés dans l'historique de la gestion de versions de ce livre !




[1] Nous y ferons référence simplement par « Subversion » dans ce livre. Vous nous remercierez quand vous vous rendrez compte de l'espace que cela a économisé !

[2] Ou comme le dit un de mes amis, c'est « tuer les mouches à coup de canon ».

[3] CollabNet Enterprise Edition a depuis été remplacé par une nouvelle ligne de produits appelée Collabnet TeamForge

[4] Et puis merci, Karl, d'être trop occupé pour rédiger ce livre toi-même.

Partie I. Faire connaissance avec Subversion

Chapitre 1. Notions fondamentales

Ce chapitre est une introduction rapide à Subversion et de son approche de la gestion de versions. Nous allons commencer par une présentation des notions générales de la gestion de versions, puis étudier plus précisément les idées particulières qui se cachent derrière Subversion et enfin donner quelques exemples simples d'utilisation de Subversion.

Même si les exemples de ce chapitre mettent en scène des personnes partageant du code source, gardez à l'esprit que Subversion peut gérer n'importe quel type d'ensemble de fichiers, il n'est pas réservé aux programmeurs.

Notions générales de la gestion de versions

Un système de gestion de versions (ou de contrôle de versions) est un système qui garde une trace de toutes les versions (ou révisions) des fichiers et, dans certains cas, des répertoires au cours du temps. Bien sûr, simplement garder la trace de toutes les modifications apportées par un utilisateur (ou un groupe d'utilisateurs) à des fichiers et des répertoires n'est pas intéressant en tant que tel. Ce qui définit l'utilité d'un système de gestion de versions est la possibilité d'examiner les modifications apportées par chaque changement et de faciliter le retour vers n'importe quelle version.

Dans cette section, nous allons introduire des notions et des outils de haut-niveau pour la gestion de versions. Nous limiterons notre exposé aux systèmes modernes de gestions de versions— dans notre monde connecté actuel, il est de peu d'intérêt de s'attarder sur les systèmes qui ne peuvent pas fonctionner au travers des grands réseaux.

Le dépôt

Le dépôt constitue le cœur d'un système de gestion de versions ; c'est le lieu de stockage central des données gérées. Généralement, les informations y sont organisées sous la forme d'une arborescence de fichiers, c'est-à-dire une hiérarchie classique de fichiers et de répertoires. Un certain nombre de clients se connectent au dépôt, et parcourent ou modifient ces fichiers. En modifiant des données, un client rend ces informations disponibles aux autres clients ; en lisant des données, le client reçoit des informations des autres clients. La Figure 1.1, « Un authentique système client/serveur » illustre cela.

Figure 1.1. Un authentique système client/serveur

Un authentique système client/serveur


Quel est l'intérêt ? Jusque-là, cela ressemble à la définition d'un serveur de fichiers classique. En fait, le dépôt est bien une sorte de serveur de fichiers, mais d'un type particulier. Ce qui rend le dépôt spécial, c'est qu'il se souvient de toutes les versions de chacun des fichiers.

Quand un client parcourt le dépôt, il consulte généralement la dernière version de l'arborescence du système de fichiers. Mais, et c'est là l'intérêt d'un système de gestion de versions, le client est également capable de demander au dépôt les états antérieurs du système de fichiers. Par exemple, un client peut poser des questions concernant l'historique des données, comme « Que contenait ce répertoire mercredi dernier ? » ou « Quelle est la dernière personne qui a modifié ce fichier, et quels changements a-t-elle effectués ? ». C'est le genre de questions qui est au cœur de tout logiciel de gestion de versions.

Copie de travail

La plus-value d'un système de gestion de versions provient de sa capacité à garder une trace de toutes les versions des fichiers et répertoires, mais les autres logiciels n'intègrent pas cette notion de « versions de fichiers et répertoires ». La plupart des logiciels ne savent manipuler qu'une seule version d'un type de fichier. Alors, comment l'utilisateur du système de gestion de versions interagit-il concrètement avec un dépôt abstrait — et souvent distant — plein de multiples versions des différents fichiers ? Comment son traitement de textes, son logiciel de présentation, son éditeur de code source ou de site web, ou n'importe quel autre logiciel peut-il avoir accès à ces fichiers suivis en versions ? La réponse se trouve dans ce que la gestion de versions appelle la copie de travail.

Une copie de travail est, littéralement, une copie locale d'une version particulière des données gérées en versions par l'utilisateur, sur laquelle il est libre de travailler. Les copies de travail[5] sont vus par les autres logiciels comme n'importe quel autre répertoire rempli de fichiers, ainsi ces programmes n'ont pas besoin d'être « conscients » de la gestion de versions pour lire ou écrire des données dans ces fichiers. C'est le rôle du logiciel de gestion de versions de prendre en compte et communiquer les modifications du contenu de la copie de travail ou du dépôt.

Modèles de gestion de versions

Si la mission première d'un logiciel de gestion de versions est de tracer l'historique des différentes versions dans le temps des données numériques, une seconde mission concomitante dans n'importe quel gestionnaire de versions moderne consiste à permettre l'édition collaborative et le partage de ces données. Mais il existe différentes stratégies pour arriver à cette fin. Comprendre ces différentes stratégies est important à plusieurs titres. Tout d'abord, cela vous aidera à comparer et différencier les logiciels de gestion de versions existants, au cas où vous rencontriez d'autres logiciels similaires à Subversion. Ensuite, cela vous aidera également à utiliser plus efficacement Subversion, puisque Subversion lui-même autorise différentes façons de travailler.

Problématique du partage de fichiers

Tous les logiciels de gestion de versions doivent résoudre le même problème fondamental : comment le logiciel va-t-il permettre aux utilisateurs de partager l'information, tout en les empêchant de se marcher mutuellement sur les pieds par accident ? Il est vraiment trop facile pour les utilisateurs d'écraser malencontreusement les changements effectués par d'autres dans le dépôt.

Observons le scénario décrit à la Figure 1.2, « La situation à éviter ». Supposons que nous ayons deux collaborateurs, Harry et Sally. Ils décident tous les deux d'éditer au même moment le même fichier dans le dépôt. Si Harry sauvegarde ses modifications dans le dépôt en premier, il est possible que, quelques instants plus tard, Sally les écrase avec sa propre version du fichier. Bien que la version de Harry ne soit pas perdue pour toujours, car le système se souvient de tous les changements, aucune des modifications effectuées par Harry n'est présente dans la nouvelle version du fichier de Sally, car elle n'a jamais vu les changements réalisés par Harry. De fait, le travail de Harry est perdu ou, du moins, perdu dans la version finale du fichier, et ceci probablement par accident. Il s'agit précisément d'une situation que nous voulons à tout prix éviter !

Figure 1.2. La situation à éviter

La situation à éviter


Modèle verrouiller-modifier-libérer

De nombreux logiciels de gestion de versions utilisent le modèle verrouiller-modifier-libérer pour résoudre le problème de plusieurs auteurs annihilant le travail des autres. Dans ce modèle, le dépôt ne permet qu'à une seule personne de modifier un fichier à un instant donné. Cette politique exclusive est gérée grâce à des verrous (lock en anglais). Harry doit « verrouiller » un fichier avant de commencer à le modifier. Si Harry a verrouillé un fichier, alors Sally ne peut pas le verrouiller et ne peut donc faire aucun changement dessus. Tout ce qu'elle peut faire, c'est lire le fichier et attendre que Harry ait fini ses changements puis libéré le verrou. Après que Harry ait libéré le fichier, Sally pourra à son tour le verrouiller et l'éditer. La Figure 1.3, « Modèle verrouiller-modifier-libérer » illustre cette solution très simple.

Figure 1.3. Modèle verrouiller-modifier-libérer

Modèle verrouiller-modifier-libérer


Le problème avec le modèle verrouiller-modifier-libérer est qu'il est relativement restrictif et devient souvent un barrage pour les utilisateurs :

  • Le verrouillage peut créer des problèmes d'administration. Parfois, Harry va verrouiller un fichier et oublier qu'il l'a fait. Pendant ce temps, Sally, qui est encore en train d'attendre pour éditer le fichier, est bloquée. Puis Harry part en vacances. Sally doit alors aller trouver un administrateur pour libérer le verrou de Harry. La situation finit par générer beaucoup de délais inutiles et de temps perdu.

  • Le verrouillage peut créer une sérialisation inutile. Que se passe-t-il lorsque Harry veut éditer le début d'un fichier texte et que Sally veut simplement éditer la fin de ce même fichier ? Ces changements ne se chevauchent pas du tout. Ils pourraient aisément éditer le fichier simultanément et il n'y aurait pas beaucoup de dégâts, en supposant que les changements soient correctement fusionnés. Dans cette situation, il n'est pas nécessaire de les forcer à éditer le fichier chacun à leur tour.

  • Le verrouillage peut créer un faux sentiment de sécurité. Supposons que Harry verrouille et édite le fichier A, alors qu'au même moment Sally verrouille et édite le fichier B. Que se passe-t-il si A et B dépendent l'un de l'autre et que les changements faits à chacun sont incompatibles d'un point de vue sémantique ? A et B ne fonctionnent soudainement plus ensemble. Le système de verrouillage a été incapable d'empêcher ce problème, bien qu'il ait d'une certaine manière instillé un faux sentiment de sécurité. Il est facile pour Harry et Sally d'imaginer qu'en verrouillant les fichiers, chacun commence une tâche isolée, sans danger et donc que ce n'est pas la peine de discuter à l'avance de leurs modifications incompatibles. Verrouiller devient souvent un substitut à une réelle communication.

Modèle copier-modifier-fusionner

Subversion, CVS et beaucoup d'autres logiciels de gestion de versions utilisent le modèle copier-modifier-fusionner comme alternative au verrouillage. Dans ce modèle, chaque utilisateur contacte le dépôt du projet via son client et crée une copie de travail personnelle, une sorte de version locale des fichiers et répertoires du dépôt. Les utilisateurs peuvent alors travailler, simultanément et indépendamment les uns des autres, et modifier leurs copies privées. Pour finir, les copies privées sont fusionnées au sein d'une nouvelle version finale. Le logiciel de gestion de versions fournit de l'aide afin de réaliser cette fusion, mais au final la responsabilité de s'assurer que tout se passe bien incombe à un être humain.

Voici un exemple. Supposons que Harry et Sally aient créé chacun des copies de travail du même projet, copiées à partir du dépôt. Ils travaillent simultanément et effectuent sur leur copie des modifications du même fichier A. Sally sauvegarde ses changements dans le dépôt en premier. Lorsque Harry essaie par la suite de sauvegarder ses modifications, le dépôt l'informe que son fichier A est périmé. En d'autres termes, le fichier A du dépôt a changé, d'une façon ou d'une autre, depuis la dernière fois qu'il l'avait copié. Harry demande donc à son client de fusionner tous les changements en provenance du dépôt dans sa copie de travail du fichier A. Il y a des chances que les modifications de Sally n'empiètent pas sur les siennes ; une fois qu'il a intégré les changements provenant des deux côtés, il sauvegarde sa copie de travail dans le dépôt. La Figure 1.4, « Modèle copier-modifier-fusionner » et la Figure 1.5, « Modèle copier-modifier-fusionner (suite) » illustrent ce processus.

Figure 1.4. Modèle copier-modifier-fusionner

Modèle copier-modifier-fusionner


Figure 1.5. Modèle copier-modifier-fusionner (suite)

Modèle copier-modifier-fusionner (suite)


Mais que se passe-t-il quand les modifications de Sally empiètent sur celles de Harry ? Que fait-on dans ce cas-là ? Cette situation est appelée un conflit et ne constitue pas, en général, un gros problème. Lorsque Harry demande à son logiciel client de fusionner les changements les plus récents du dépôt dans sa copie de travail, sa copie du fichier est en quelque sorte marquée comme étant dans un état de conflit : il a la possibilité de voir les deux ensembles de changements entrant en conflit et de choisir manuellement entre les deux. Notez bien qu'un logiciel ne peut pas résoudre automatiquement les conflits ; seuls les humains sont capables de comprendre et de faire les choix intelligents nécessaires. Une fois que Harry a manuellement résolu les modifications se chevauchant, par exemple après une discussion avec Sally, il peut sauvegarder le fichier fusionné en toute sécurité dans le dépôt.

Le modèle copier-modifier-fusionner peut sembler un peu chaotique mais, en pratique, il fonctionne de façon très fluide. Les utilisateurs peuvent travailler en parallèle, sans jamais devoir s'attendre les uns les autres. Lorsqu'ils travaillent sur les mêmes fichiers, il s'avère que la plupart des changements réalisés en parallèle ne se chevauchent pas du tout ; les conflits sont rares. Et le temps nécessaire à la résolution des conflits est en général bien inférieur au temps gaspillé par un système de verrouillage.

Au final, tout revient à un facteur critique : la communication entre les utilisateurs. Lorsque les utilisateurs communiquent mal, les conflits syntaxiques et sémantiques augmentent. Aucun système ne peut forcer les utilisateurs à communiquer parfaitement et aucun système ne peut détecter les conflits sémantiques. Il n'y a donc aucun intérêt à se laisser endormir par un faux sentiment de sécurité selon lequel un système de verrouillage permettrait d'éviter les conflits ; en pratique, le verrouillage semble limiter la productivité plus qu'aucun autre facteur.

Subversion en action

Nous avons déjà indiqué que Subversion est un logiciel de gestion de versions moderne et en réseau. Comme décrit dans la section intitulée « Notions générales de la gestion de versions », un dépôt sert de cœur de stockage pour les données suivies en versions et c'est par l'intermédiaire de copies de travail que les utilisateurs et les logiciels qu'ils manipulent interagissent avec les données. Dans cette section, nous allons commencer par introduire les différentes manières dont Subversion implémente la gestion de versions.

Dépôts Subversion

Subversion implémente le concept de dépôt comme tout autre logiciel de gestion de versions moderne. Contrairement à une copie de travail, un dépôt Subversion est une entité abstraite qui ne peut être manipulée pratiquement que par les bibliothèques et les propres outils de Subversion. Comme la plupart des interactions d'un utilisateur de Subversion se font par l'intermédiaire du client Subversion et qu'elles ont lieu dans le contexte de la copie de travail, ce livre traite en une grande partie de la copie de travail et des actions que l'on peut y appliquer. Pour les détails du dépôt, vous pouvez cependant vous référer au Chapitre 5, Administration d'un dépôt.

[Avertissement]Avertissement

Dans Subversion, l'entité que possède chaque utilisateur du logiciel — le répertoire des fichiers suivis en versions ainsi que les métadonnées qui permettent au système de tracer les données et de communiquer avec le serveur — s'appelle la copie de travail. Bien que d'autres logiciels de gestion de versions utilisent le terme de « dépôt » pour l'entité côté client, c'est à la fois incorrect et une source fréquente de confusion d'utiliser ce terme dans ce sens là dans le contexte de Subversion.

Les copies de travail sont abordées plus loin, dans la section intitulée « Copies de travail ».

Révisions

Une opération svn commit propage (c'est-à-dire communique au serveur) les modifications d'un nombre quelconque de fichiers et de répertoires en une seule opération atomique. Par opération atomique, nous entendons que, soit toutes les modifications sont prises en compte par le dépôt, soit rien n'est pris en compte. Subversion essai d'être robuste dans ce concept d'atomicité, même en cas de plantage du programme, du système, de problèmes réseau ou d'actions de la part des autres utilisateurs.

Chaque fois que le dépôt accepte une propagation (c'est-à-dire une opération svn commit), il crée un nouvel état de l'arborescence du système de fichiers que l'on appelle révision. À Chaque révision est associé un entier naturel unique, immédiatement supérieur à l'entier associé à la révision précédente. La révision initiale d'un dépôt nouvellement créé à pour numéro 0 et n'est constituée que d'un répertoire racine vide.

La Figure 1.6, « L'évolution de l'arborescence au cours du temps » illustre une manière intéressante de se représenter le dépôt. Imaginez un tableau de numéros de révisions, commençant à zéro et s'étirant de la gauche vers la droite. À Chaque numéro de révision est suspendue une arborescence du système de fichiers, qui est une photo, un « instantané » (snapshot en anglais) du dépôt prise après une propagation.

Figure 1.6. L'évolution de l'arborescence au cours du temps

L'évolution de l'arborescence au cours du temps


URL des dépôts Subversion

les logiciels côté client de Subversion utilisent des URL pour identifier les fichiers et répertoires suivis en versions dans les dépôts Subversion. Pour leur grande partie, ces URL utilisent la syntaxe standard, permettant de spécifier le nom du serveur et le numéro de port directement dans l'URL.

  • http://svn.exemple.com/svn/projet
  • http://svn.exemple.com:9834/depot

Les URL de dépôts Subversion ne se limitent pas au domaine http://. Comme Subversion permet à ses clients de dialoguer de différentes manières avec les dépôts, les URL utilisées pour se connecter diffèrent subtilement en fonction du protocole employé. La Tableau 1.1, « Les différents motifs d'URL pour l'accès aux dépôts » décrit la correspondance entre les différents motifs d'URL et les méthodes d'accès aux dépôts. Pour plus de détails sur les options du serveur Subversion, reportez-vous au Chapitre 6, Configuration du serveur.

Tableau 1.1. Les différents motifs d'URL pour l'accès aux dépôts

SchémaMéthode d'accès
file:///Accès direct au dépôt (sur disque local)
http://Accès par protocole WebDAV sur un serveur Apache disposant d'un connecteur Subversion
https://Comme http://, avec une encapsulation SSL
svn://Accès via un protocole personnalisé à un serveur svnserve
svn+ssh://Comme svn://, avec une encapsulation dans un tunnel SSH


La gestion des URL par Subversion possède quelques particularités qu'il convient de noter. Par exemple, les URL contenant la méthode d'accès file:// (utilisée pour les dépôts locaux) doivent spécifier, par convention, soit le nom de serveur localhost, soit pas de nom de serveur :

  • file:///var/svn/depot
  • file://localhost/var/svn/depot

D'autre part, les utilisateurs du procédé file:// sur les plateformes Windows doivent se servir d'une syntaxe qui est un « standard » officieux pour accéder à leurs dépôts se trouvant sur la même machine mais sur un disque différent du disque de travail habituel du client. Les deux syntaxes de chemin d'URL suivantes fonctionnent, X étant le disque sur lequel le dépôt se trouve :

  • file:///X:/var/svn/depot
  • file:///X|/var/svn/depot

Remarquez qu'une URL utilise des barres obliques (/) alors que la forme native (non-URL) d'un chemin sous Windows utilise des barres obliques inversées (\). Notez aussi que, dans la seconde syntaxe, vous devez entourer l'URL de guillemets pour éviter que la barre verticale ne soit interprétée comme un symbole de redirection (un « pipe »).

[Note]Note

Les URL Subversion file:// ne peuvent pas être utilisées dans un navigateur web classique de la même façon qu'une URL file:// habituelle. Lorsque vous essayez de visualiser une URL file:// dans un navigateur web classique, il lit et affiche le contenu du fichier situé à cet emplacement en interrogeant directement le système de fichiers. Cependant, les ressources de Subversion existent dans un système de fichier virtuel (cf. la section intitulée « Couche dépôt ») et votre navigateur ne comprend pas comment interagir avec ce système de fichiers.

Il faut noter que le client Subversion encode automatiquement les URL en cas de besoin, exactement comme le fait un navigateur web. Par exemple, l'URL http://host/path with space/project/españa — qui contient à la fois des espaces et des caractères non ASCII— est automatiquement interprétée par Subversion comme si vous aviez fourni http://host/path%20with%20space/project/espa%C3%B1a. Si l'URL contient des espaces, prenez bien soin de les placer entre guillemets dans la ligne de commande afin que le shell traite le tout comme un unique argument du programme.

Il existe aussi une exception à la gestion des URL par Subversion qui s'applique à la gestion des chemins locaux dans beaucoup de contextes. Si le dernier élément de l'URL ou du chemin contient le signe arobase (@), vous devez utiliser une syntaxe particulière, décrite dans la section intitulée « Révisions pivots et révisions opérationnelles », afin que Subversion prenne proprement en compte l'adresse de cette ressource.

Dans Subversion 1.6, une nouvelle notation, dite syntaxe avec chapeau (^) a été introduite comme raccourci pour « l'URL du répertoire racine du dépôt ». Par exemple, vous pouvez utiliser ^tags/gros-sandwich pour désigner l'URL du répertoire /tags/gros-sandwich à la racine du dépôt. Une telle URL est appelée URL relative au dépôt. Remarquez que cette syntaxe d'URL ne fonctionne que si votre répertoire de travail est une copie de travail, le client texte interactif récupérant l'URL de la racine du dépôt dans les métadonnées de la copie de travail. Notez aussi que lorsque vous voulez faire référence précisément au répertoire racine du dépôt, vous devez utiliser ^/ (avec la barre oblique terminale) et non simplement ^. Les utilisateurs sous Windows ne doivent pas oublier que le caret est un caractère d'échappement pour ce système. En conséquence, vous devez utiliser un double caret ^^ si votre client Subversion tourne sur une machine Windows.

Copies de travail

Une copie de travail Subversion est une arborescence classique de répertoires de votre système local, contenant un ensemble de fichiers. Vous pouvez éditer ces fichiers comme vous le voulez et, s'il s'agit de code source, vous pouvez compiler votre programme à partir de ceux-ci de la façon habituelle. Votre copie de travail est votre espace de travail personnel privé : Subversion n'y incorpore jamais les changements d'autres personnes ni ne rend jamais disponibles vos propres changements à d'autres personnes tant que vous ne lui demanderez pas explicitement de le faire. Vous pouvez même avoir plusieurs copies de travail d'un même projet.

Après que vous ayez apporté quelques modifications aux fichiers de votre copie de travail et vérifié qu'elles fonctionnent correctement, Subversion vous fournit des commandes pour « publier » vos changements vers les autres personnes qui travaillent avec vous sur votre projet (en les transmettant au dépôt). Si d'autres personnes publient leurs propres modifications, Subversion vous fournit des commandes pour fusionner ces changements dans votre copie de travail (en les obtenant du dépôt). Remarquez que le dépôt central joue le rôle d'intermédiaire pour les changements de tout le monde dans Subversion ; les modifications ne passent pas directement d'une copie de travail à une autre copie de travail dans le processus classique.

Une copie de travail contient également quelques fichiers supplémentaires, créés et gérés par Subversion, pour l'aider à effectuer ces opérations. En particulier, chaque copie de travail contient un sous-répertoire nommé .svn, aussi connu sous l'appellation de répertoire administratif de votre copie de travail. Les fichiers de ce répertoire administratif permettent à Subversion d'identifier quels fichiers contiennent des modifications non-publiées et quels fichiers sont périmés vis-à-vis du travail des autres personnes.

[Note]Note

Avant la version 1.7, Subversion plaçait un répertoire .svn dans chaque sous-répertoire géré en versions de la copie de travail. Subversion 1.7 utilise une approche complètement nouvelle pour stocker, suivre et travailler avec les métadonnées, et le changement le plus visible de cette approche est qu'il n'existe dans la copie de travail qu'un seul sous-répertoire .svn situé à la racine de la copie de travail.

Fonctionnement de la copie de travail

Pour chaque fichier d'un répertoire de travail, Subversion enregistre deux informations essentielles dans la zone administrative .svn/ :

  • la révision sur laquelle votre fichier de travail est basé (qui est appelée la révision de travail du fichier) et

  • la date et l'heure de la dernière mise à jour de la copie locale depuis le dépôt

À partir de ces informations, en dialoguant avec le dépôt, Subversion est capable de déterminer dans lequel des quatre états suivants se trouve un fichier de travail 

Inchangé et à jour

Le fichier est inchangé dans le répertoire de travail et aucune modification de ce fichier n'a été propagée vers le dépôt depuis sa révision de travail. Un appel à svn commit sur le fichier ne fait rien, un appel à svn update sur le fichier ne fait rien non plus.

Modifié localement et à jour

Le fichier a été modifié dans le répertoire de travail et aucune modification du fichier n'a été propagée dans le dépôt depuis la dernière mise à jour. Il existe des modifications locales qui n'ont pas été propagées vers le dépôt, donc un appel à svn commit sur le fichier permet de publier vos modifications et un appel à svn update ne fait rien.

Inchangé et périmé

Le fichier n'a pas été modifié dans le répertoire de travail mais a changé dans le dépôt. Le fichier devra être mis à jour à un moment ou à un autre, pour l'amener au niveau de la dernière révision publique. Un appel à svn commit sur le fichier ne fait rien et un appel à svn update incorpore les dernières modifications dans votre copie de travail.

Modifié localement et périmé

Le fichier a été modifié à la fois dans le répertoire de travail et dans le dépôt. Un appel à svn commit sur le fichier va échouer, renvoyant comme erreur « Périmé » (out-of-date en anglais). Le fichier doit d'abord être mis à jour ; un appel à svn update va tenter de fusionner les modifications publiques avec les modifications locales. Si Subversion ne parvient pas à réaliser automatiquement cette fusion de manière crédible, il va laisser à l'utilisateur le soin de résoudre le conflit.

Actions de base sur la copie de travail

Un dépôt Subversion contient bien souvent les fichiers (ou code source) de plusieurs projets ; habituellement, chaque projet est un sous-répertoire de l'arborescence du système de fichiers du dépôt. Dans cette situation, la copie de travail d'un utilisateur correspond à une sous-arborescence particulière du dépôt.

Par exemple, supposons que votre dépôt contienne deux projets logiciels, paint et calc. Chaque projet réside dans son propre sous-répertoire racine, comme indiqué dans la Figure 1.7, « Système de fichiers du dépôt ».

Figure 1.7. Système de fichiers du dépôt

Système de fichiers du dépôt


Pour obtenir une copie de travail, vous devez extraire une sous-arborescence du répertoire (le terme « extraire », check out en anglais, peut vous faire penser que cela a quelque chose à voir avec verrouiller ou réserver des ressources, mais ce n'est pas le cas ; cela crée simplement pour vous une copie privée du projet). Par exemple, si vous extrayez /calc, vous obtenez une copie de travail qui ressemble à ceci :

$ svn checkout http://svn.exemple.com/depot/calc
A    calc/Makefile
A    calc/entier.c
A    calc/bouton.c
Révision 56 extraite.
$ ls -A calc
Makefile  bouton.c entier.c .svn/
$

Les lettres A qui s'affichent dans la marge de gauche indiquent que Subversion est en train d'ajouter des éléments dans votre copie de travail. Vous avez désormais votre copie personnelle du répertoire /calc du dépôt, avec une entrée supplémentaire, .svn, qui contient des informations complémentaires nécessaires à Subversion, comme évoqué précédemment.

Supposons que vous fassiez des modifications à bouton.c. Comme le répertoire .svn se souvient de la date de modification et du contenu du fichier original, Subversion peut en déduire que vous avez modifié le fichier. Néanmoins, Subversion ne rend pas vos modifications publiques tant que vous ne lui dites pas de le faire. L'action de publication de vos modifications est plus communément appelée propagation (« commit » ou check in en anglais et, parfois, archivage ou livraison en français) des modifications au sein du dépôt.

Pour rendre publiques vos modifications, vous pouvez utiliser la commande Subversion svn commit :

$ svn commit bouton.c -m "Coquille corrigée dans bouton.c."
Ajout        bouton.c
Transmission des données .
Révision 57 propagée.
$

À présent, vos modifications de bouton.c ont été propagées au sein du dépôt, avec un commentaire décrivant ces changements (« vous avez corrigé une coquille »). Si un autre utilisateur extrait une copie de travail de /calc/, il verra vos modifications dans la dernière version du fichier.

Supposons que vous ayez une collaboratrice, Sally, qui a extrait une copie de travail de /calc en même temps que vous. Lorsque vous propagez votre modification de bouton.c, la copie de travail de Sally reste inchangée ; Subversion ne modifie les copies de travail qu'à la demande des utilisateurs.

Pour mettre son projet à jour, Sally peut demander à Subversion de mettre à jour (update en anglais) sa copie de travail, en utilisant la commande svn update. Cela va intégrer vos modifications dans sa copie de travail, ainsi que celles qui ont été envoyées par d'autres personnes depuis qu'elle l'avait extraite.

$ pwd
/home/sally/calc

$ ls -A
Makefile bouton.c entier.c .svn/
$ svn update
U    bouton.c
Actualisé à la révision 57.
$

En sortie, la commande svn update indique que Subversion a mis à jour le contenu de bouton.c. Remarquez que Sally n'a pas eu besoin de spécifier quels fichiers devaient être mis à jour ; Subversion utilise les informations contenues dans le répertoire .svn, ainsi que d'autres informations en provenance du dépôt, pour décider quels fichiers doivent être mis à jour.

Copies de travail mixtes, à révisions mélangées

Un principe général de Subversion est d'être aussi flexible que possible. Un type particulier de flexibilité est la capacité d'avoir une copie de travail contenant des fichiers et des répertoires avec un mélange de différents numéros de révision. Les copies de travail ne correspondent pas toujours à une révision unique du dépôt  elles peuvent contenir des fichiers qui sont à des révisions différentes. Par exemple, supposons que vous extrayez une copie de travail d'un dépôt dont la révision la plus récente porte le numéro 4 :



calc/

   Makefile:4



   entier.c:4

   bouton.c:4

Tel quel, le répertoire de travail correspond exactement à la révision 4 du dépôt. Maintenant, supposons que vous modifiez le fichier bouton.c et que vous propagiez cette modification. Si aucune autre propagation n'a eu lieu entre temps, votre propagation crée la révision 5 du dépôt et votre copie de travail ressemble à :



calc/

   Makefile:4



   entier.c:4

   bouton.c:5

Supposons que, à ce moment ci, Sally propage une modification à entier.c, créant ainsi la révision 6. Si vous faites svn update pour mettre à jour votre copie de travail, elle ressemble à :



calc/

   Makefile:6



   entier.c:6

   bouton.c:6

Les modifications apportées par Sally à entier.c apparaissent dans votre copie de travail et vos modifications sont toujours présentes dans bouton.c. Dans cet exemple, le texte de Makefile est identique dans les révisions 4, 5 et 6 mais Subversion marque votre copie de travail de Makefile comme étant à la révision 6 pour indiquer qu'elle est à jour. Ainsi, quand vous effectuez une mise à jour au niveau de la racine de votre copie de travail, celle-ci correspond en général à une révision donnée du dépôt.

Mise à jour et propagation sont deux opérations distinctes

Une des règles fondamentales de Subversion est que l'action de « pousser » ne déclenche pas une action de « tirer », ni l'inverse. Le simple fait que vous soyez prêt à soumettre vos nouvelles modifications au dépôt ne veut pas dire que vous êtes prêts à recevoir les modifications d'autres personnes. Et si vous avez de nouvelles modifications encore en cours, alors svn update fusionne élégamment les changements du dépôt avec les vôtres, plutôt que de vous forcer à les publier.

Le principal effet secondaire de cette règle est que la copie de travail a de la comptabilité supplémentaire à effectuer pour suivre les mélanges de révision et également être tolérante vis-à-vis de l'ensemble. Cela est rendu encore plus difficile par le fait que les répertoires eux-mêmes sont suivis en versions.

Par exemple, supposons que vous ayez une copie de travail qui soit intégralement à la révision 10. Vous éditez le fichier truc.html et réalisez ensuite un svn commit qui crée la révision 15 dans le dépôt. Après que la propagation ait réussi, nombreux sont ceux parmi les nouveaux utilisateurs qui s'attendraient à ce que toute la copie de travail soit à la révision 15, mais ce n'est pas le cas ! Un certain nombre de modifications ont pu avoir lieu dans le dépôt entre les révisions 10 et 15. Le client ne sait rien de ces changements qui ont été apportés au dépôt, puisque vous n'avez pas encore exécuté la commande svn update et la commande svn commit ne récupère pas les nouvelles modifications. D'un autre côté, si la commande svn commit téléchargeait automatiquement les modifications les plus récentes, alors il serait possible d'avoir toute la copie de travail à la révision 15 mais, dans ce cas, nous enfreindrions la règle fondamentale selon laquelle « pousser » et « tirer » doivent demeurer des actions distinctes. Ainsi, la seule chose que le client Subversion peut faire en toute sécurité est de marquer le fichier truc.html, et lui seulement, comme étant à la révision 15. Le reste de la copie de travail reste à la révision 10. Seule l'exécution de la commande svn update permet de récupérer les dernières modifications et de marquer la copie de travail comme étant à la révision 15.

Des révisions mélangées sont normales

Le fait est qu'à chaque fois que vous exécutez la commande svn commit, votre copie de travail se retrouve composée d'un mélange de révisions. Les éléments que vous venez juste de propager sont marqués comme ayant un numéro de révision plus élevé que tous les autres. Après plusieurs propagations (sans mise à jour entre-temps), votre copie de travail va contenir tout un mélange de révisions. Même si vous êtes la seule personne à utiliser le dépôt, vous constaterez quand même ce phénomène. Pour étudier votre propre mélange de révisions de travail, utilisez la commande svn status avec l'option --verbose (voir la section intitulée « Vue d'ensemble des changements effectués » pour plus d'informations).

Souvent, les nouveaux utilisateurs n'ont pas du tout conscience que leur copie de travail contient des révisions mélangées. Cela peut être déroutant car beaucoup de commandes client sont sensibles à la révision de travail de l'élément qu'elles examinent. Par exemple, la commande svn log est utilisée pour afficher l'historique des modifications d'un fichier ou d'un répertoire (cf. la section intitulée « Historique des modifications »). Lorsque l'utilisateur appelle cette commande sur un objet de la copie de travail, il s'attend à obtenir l'historique complet de celui-ci. Mais si la révision de travail de l'objet est assez ancienne (souvent parce que svn update n'a pas été lancé depuis un certain temps), alors c'est l'historique de l'ancienne version de l'objet qui est affiché.

Les mélanges de révisions sont utiles

Si votre projet est suffisamment complexe, vous allez découvrir qu'il est parfois pratique d'effectuer un retour en arrière forcé (c'est-à-dire de faire une mise à jour vers une version plus ancienne que celle que vous avez déjà) sur certaines parties de votre copie de travail vers des révisions plus anciennes ; vous apprendrez comme le faire dans le Chapitre 2, Utilisation de base. Vous avez peut-être envie de tester une version précédente d'un sous-module contenu dans un sous-répertoire ou bien de comprendre comment un bogue est apparu pour la première fois dans un fichier donné. C'est le côté « machine à voyager dans le temps » d'un logiciel de gestion de versions, la fonctionnalité qui vous permet de déplacer n'importe quelle partie de votre copie de travail en avant ou en arrière dans le temps.

Les mélanges de révisions ont des limites

Quelle que soit la façon dont vous utilisez les mélanges de révision dans votre copie de travail, il existe des limites à cette flexibilité.

Premièrement, vous ne pouvez pas propager la suppression d'un fichier ou d'un répertoire qui n'est pas complètement à jour. Si une version plus récente de l'élément existe dans le dépôt, votre tentative de suppression est rejetée, afin de vous empêcher de détruire accidentellement des modifications dont vous n'aviez pas encore connaissance.

Deuxièmement, vous ne pouvez propager la modification des métadonnées d'un répertoire que si celui-ci est complètement à jour. Vous apprendrez comment associer des « propriétés » à des éléments dans le Chapitre 3, Sujets avancés. La révision de travail d'un répertoire définit un ensemble précis d'entrées et de propriétés et propager la modification d'une propriété d'un répertoire périmé risquerait de détruire des propriétés dont vous n'aviez pas encore connaissance.

Enfin, à partir de Subversion 1.7, vous ne pouvez pas utiliser par défaut une copie de travail à révisions mélangées comme cible d'une opération de fusion ; cette limitation a été introduite pour prévenir certains problèmes qui apparaissent dans ce cas.

Résumé

Nous avons couvert un certain nombre de concepts fondamentaux de Subversion dans ce chapitre :

  • Nous avons introduit les notions de dépôt central, de copie de travail du client et d'ensemble des révisions de l'arborescence du dépôt.

  • Nous avons vu quelques exemples simples de la façon dont deux collaborateurs peuvent utiliser Subversion pour publier et recevoir des modifications en provenance l'un de l'autre, en utilisant le modèle « copier-modifier-fusionner ».

  • Nous avons évoqué la façon dont Subversion suit et gère les informations dans une copie de travail.

À présent, vous avez probablement une bonne idée générale de la façon dont Subversion fonctionne. Armé de cette connaissance, vous devez désormais être prêt à passer au chapitre suivant qui traite en détail des commandes et des fonctionnalités de Subversion.




[5] Le terme « copie de travail » peut s'appliquer à n'importe quel fichier dans sa version extraite. Cependant, la majeure partie des gens utilise ce terme pour désigner la sous-arborescence complète contenant les fichiers et répertoires gérés par le système de gestion de versions.

Chapitre 2. Utilisation de base

La théorie est utile, mais son application est tout simplement passionnante. Nous allons maintenant voir plus en détail l'utilisation de Subversion. Quand vous aurez terminé ce chapitre, vous serez capable d'effectuer toutes les tâches nécessaires à une utilisation quotidienne de Subversion. Nous allons commencer par enregistrer nos fichiers dans Subversion, puis extraire notre code. Ensuite, nous expliquons comment modifier des fichiers et examiner ces changements. Nous voyons aussi comment faire pour intégrer les changements venant d'autres personnes dans notre copie de travail, les examiner et résoudre les conflits qui pourraient apparaître.

Notez que ce chapitre ne doit pas être vu comme une liste complète de toutes les commandes de Subversion, mais plutôt comme une introduction conviviale aux opérations Subversion les plus courantes que vous êtes susceptible de rencontrer. Ici, nous supposons que vous avez lu et compris le Chapitre 1, Notions fondamentales et que vous êtes familier avec le modèle général de Subversion. Pour une liste complète de toutes les commandes, reportez-vous à la Partie II, « Guide de référence des commandes Subversion ».

Dans ce chapitre, nous considérons également que le lecteur cherche comment interagir de manière simple avec un dépôt Subversion existant. S'il n'y a pas de dépôt, il n'y a pas de copie de travail ; s'il n'y a pas de copie de travail, ce chapitre ne présente pas grand intérêt. Il existe de nombreux sites Internet qui propose d'héberger un dépôt Subversion gratuitement ou pour une somme modique. Sinon, si vous préférez mettre en place et administrer vos propres dépôts, référez-vous au Chapitre 5, Administration d'un dépôt. Mais n'espérez pas que les exemples de ce chapitre fonctionnent si vous n'avez pas accès à un dépôt Subversion.

Enfin, toutes les opérations Subversions qui contactent le dépôt à travers le réseau peuvent potentiellement nécessiter une authentification de l'utilisateur. Par souci de simplicité, nos exemples tout au long de ce chapitre n'abordent pas l'authentification. Soyez conscient que si vous souhaitez mettre en pratique les exemples fournis ici avec une instance Subversion du monde réel, vous serez certainement obligé de fournir un identifiant et un mot de passe au serveur. Lisez la section intitulée « Éléments d'authentification du client » pour une description détaillée de la façon dont Subversion gère l'authentification des utilisateurs et les éléments associés.

À l'aide !

Il va de soi que ce livre existe dans le but d'informer et d'assister les utilisateurs de Subversion, qu'ils soient débutants ou chevronnés. Cependant, pour plus de confort, le client texte interactif de Subversion inclut sa propre documentation, réduisant le besoin d'aller chercher un livre dans les rayonnages (qu'ils soient en bois, virtuels ou quoi que soit d'autre). La commande svn help est la porte d'entrée vers cette documentation.

$ svn help 
Client texte interactif de Subversion, version 1.8.13.
Entrer 'svn help <sous-commande>' pour l'aide sur une sous-commande.
Entrer 'svn --version' pour avoir la version et les modules d'accès (RA)
    ou 'svn --version --quiet' pour la version seule.

La plupart des sous-commandes prennent en argument des dossiers et/ou
des fichiers, et s'appliquent récursivement sur les dossiers.
Si aucun argument n'est précisé à une telle sous-commande, elle s'applique
par défaut récursivement sur le dossier courant, qui est inclus.

Sous-commandes disponibles :
…

Comme indiqué dans la sortie d'écran précédente, vous pouvez demande de l'aide sur une sous-commande particulière en lançant svn help SOUS_COMMANDE. Subversion affiche le message complet d'utilisation de cette sous-commande, ce qui inclut la syntaxe, les options et le comportement :

$ svn help help 
help (?, h): Décrit l'usage de ce programme ou de ses sous-commandes.
usage : help [SOUS_COMMANDE...]

Options globales :
  --username ARG           : précise le nom d'utilisateur ARG
  --password ARG           : précise le mot de passe ARG
…

Beaucoup de distributions de Subversion sur des plateformes de type Unix incluent des pages de manuel qui peuvent être affichées en utilisant la commande man, mais ces pages ne recèlent généralement que des pointeurs vers d'autres sources d'aide réelle, telles que le site Internet du projet et le site qui héberge ce livre. Par ailleurs, plusieurs entreprises proposent de l'assistance et de l'aide pour Subversion, habituellement par l'intermédiaire de forums de discussion en ligne et de prestations de conseil. Et bien sûr, Internet regorge de discussions relatives à Subversion, prêtes à être mises au jour à l'aide de votre moteur de recherche favori. L'aide de Subversion ne se trouve jamais bien loin.

Enregistrement de données dans le dépôt

Deux moyens sont à votre disposition pour enregistrer de nouveaux fichiers dans votre dépôt Subversion : svn import et svn add. Nous abordons ici la commande svn import et, plus loin dans le chapitre, la commande svn add, lorsque nous passerons en revue une journée typique avec Subversion.

Importation de fichiers et de dossiers

La commande svn import est un moyen rapide de copier une arborescence non-suivie en versions dans le dépôt, créant des dossiers intermédiaires si nécessaire. svn import ne nécessite pas de copie de travail et vos fichiers sont immédiatement propagés dans le dépôt. Ce moyen est utilisé essentiellement quand vous avez une arborescence existante dont vous voulez suivre les changements dans votre dépôt Subversion. Par exemple :

$ svn import chemin/vers/mon/arborescence \
             http://svn.exemple.com/svn/depot/un/projet \
             -m "Import initial"
Ajout         mon-arborescence/truc.c
Ajout         mon-arborescence/machin.c
Ajout         mon-arborescence/sous-repertoire
Ajout         mon-arborescence/sous-repertoire/bidule.h

Révision 1 propagée.
$

L'exemple précédent copie le contenu d'un dossier local mon-arborescence dans le dossier un/projet du dépôt. Notez que vous n'avez pas eu besoin de créer d'abord ce nouveau dossier, svn import le faisant pour vous. Immédiatement après la propagation, vous pouvez voir vos données dans le dépôt :

$ svn list http://svn.exemple.com/svn/depot/un/projet
machin.c
sous-repertoire/
truc.c
$

Notez qu'après la fin de l'import, l'arborescence d'origine n'est pas transformée en copie de travail. Pour commencer à travailler, vous devez extraire grâce à svn checkout une copie de travail toute fraîche de l'arborescence.

Organisation conseillée d'un dépôt

On ne peut pas être plus flexible que Subversion pour l'organisation de vos données. Comme il ne fait que suivre en versions les fichiers et les dossiers et comme il n'attache aucune sémantique à ces objets, vous avez tout loisir de disposer les données dans votre dépôt comme bon vous semble. Malheureusement, la contrepartie de cette flexibilité est qu'il est facile de se retrouver perdu quand on essaye de parcourir différents dépôts Subversion, les schémas de stockage des données pouvant être très différents voire imprévisibles.

Pour pallier cette confusion possible, nous vous recommandons de suivre la convention suivante (établie il y a bien longtemps, avec l'éclosion du projet Subversion lui-même) qui consiste à nommer judicieusement quelques dossiers du dépôt Subversion en fonction des données qu'ils renferment. La plupart des projets possèdent « une ligne de développement principale » identifiable, autrement appelée tronc (trunk en anglais), des branches, qui sont des copies avec des variantes de la ligne de développement principale et des étiquettes (tags en anglais) qui sont des instantanés de la ligne de développement que l'on a nommés. En conséquence, nous recommandons premièrement que chaque projet dispose d'une racine de projet bien identifiée au sein du dépôt, un dossier sous lequel toutes les données du projet — et uniquement les données du projet — seront hébergées. Deuxièmement, nous recommandons que chaque racine de projet contienne un sous-dossier trunk pour la ligne de développement principale, un sous-dossier branches dans lequel les branches spécifiques (ou groupes de branches) seront créées et un sous-dossier tags dans lequel seront placés les instantanés (ou les groupes d'instantanés). Bien sûr, si un dépôt n'héberge qu'un seul projet, la racine du dépôt peut servir de racine du projet.

Voici quelques exemples :

$ svn list file:///var/svn/depot-projet-unique
trunk/
branches/
tags/
$ svn list file:///var/svn/depot-multiples-projets
projet-A/
projet-B/
$ svn list file:///var/svn/depot-multiples-projets/projet-A
trunk/
branches/
tags/
$

Vous en apprendrez plus sur les étiquettes et les branches dans le Chapitre 4, Gestion des branches. Pour plus de détails et pour voir comment gérer plusieurs projets, reportez-vous à la section intitulée « Agencement du dépôt », et à la section intitulée « Stratégies d'organisation d'un dépôt » pour en savoir plus sur les dossiers racines d'un projet.

Limitations sur les noms

Subversion s'efforce de ne pas limiter le type de données que vous placez en suivi de versions. Le contenu des fichiers et les valeurs des propriétés sont stockés et transmis en tant que données binaires. la section intitulée « Type de contenu des fichiers » vous explique comment indiquer à Subversion que des opérations « textuelles » n'ont pas de sens pour un fichier particulier. Il existe toutefois quelques cas pour lesquels Subversion définit des limitations sur les informations qu'il stocke.

Subversion gère en interne certaines parties des données au format Unicode UTF-8, par exemple les noms de propriétés, les noms de chemins et les messages de trace. Cela ne veut toutefois pas dire que toutes vos interactions avec Subversion doivent faire intervenir l'UTF-8. En règle générale, les clients Subversion vont gérer de façon transparente les conversions entre l'UTF-8 et le système de codage utilisé par votre ordinateur, si cela a un sens de faire une telle conversion (ce qui est le cas pour les codages les plus courants utilisés de nos jours).

Dans les échanges WebDAV ainsi que dans des anciennes versions de certains fichiers de gestion interne de Subversion, les chemins sont utilisés en tant que valeurs d'attribut XML et les noms de propriétés en tant que noms de tags XML. Cela veut dire que les chemins ne peuvent contenir que des caractères XML (1.0) valides et que les noms de propriétés sont encore plus limités, ne pouvant contenir que des caractères ASCII. Subversion interdit également les caractères TAB (tabulation), CR et LF (caractères de fin de ligne) dans les noms de chemins pour empêcher les chemins d'être coupés en deux lors des comparaisons ou dans les sorties de commandes comme svn log ou svn status.

Bien que cela fasse beaucoup de choses à se rappeler, ces limitations sont rarement un problème en pratique. Tant que vos paramètres régionaux sont compatibles avec UTF-8 et que vous n'utilisez pas de caractères de contrôle dans les chemins, vous ne devriez pas avoir de problème pour communiquer avec Subversion. Le client texte interactif apporte un peu d'aide supplémentaire : afin de créer des versions « valides » pour un usage interne, il banalise automatiquement, si nécessaire, les caractères illégaux contenus dans les chemins d'URL que vous tapez.

[Avertissement]Avertissement

Évidemment, au moment de choisir des noms de chemins valides, Subversion n'est pas l'unique facteur limitant. Les équipes qui travaillent avec plusieurs systèmes d'exploitation doivent aussi prendre en compte les limitations imposées par ces systèmes d'exploitation. Par exemple, Windows n'autorise pas l'utilisation de la virgule dans les noms de fichiers alors qu'un utilisateur de Linux peut très bien placer un tel fichier en suivi de versions, générant des données qui ne pourront plus être extraites sous Windows. De même, ajouter plusieurs fichiers dont les noms ne diffèrent que par la casse entraîne des problèmes pour les utilisateurs de systèmes de fichiers non sensibles à la casse. En conséquence, nous vous recommandons de bien prendre aussi en compte les limitations posées par les différents systèmes d'exploitation et systèmes de fichiers.

Création d'une copie de travail

En général, vous commencerez à utiliser un dépôt Subversion en faisant une extraction (checkout en anglais) de votre projet. Extraire un dossier d'un dépôt crée sur votre ordinateur une copie de travail de ce dossier. Cette copie contient la dernière version (c'est-à-dire la plus récemment créée ou modifiée) du dossier et de sa sous-arborescence trouvée dans le dépôt Subversion :

$ svn checkout http://svn.exemple.com/svn/depot/trunk
A    trunk/LISEZMOI
A    trunk/INSTALL
A    trunk/src/main.c
A    trunk/src/header.h
…
Révision 8810 extraite.
$

Alors que l'exemple précédent extrait le dossier de base trunk, vous pouvez tout aussi facilement extraire un sous-dossier situé à n'importe quelle profondeur dans le dépôt en spécifiant le sous-dossier dans l'URL d'extraction :

$ svn checkout http://svn.exemple.com/svn/depot/trunk/src
A    src/main.c
A    src/header.h
A    src/lib/helpers.c
…
Révision 8810 extraite.
$

Comme Subversion utilise le modèle copier-modifier-fusionner à la place du modèle verrouiller-modifier-libérer (voir la section intitulée « Modèles de gestion de versions »), vous pouvez commencer immédiatement à modifier les fichiers et les dossiers de votre copie de travail. Votre copie de travail n'est qu'un ensemble de fichiers et de dossiers comme les autres dans votre système. Vous pouvez y éditer des fichiers, la modifier, la déplacer, vous pouvez même supprimer toute votre copie de travail et l'oublier définitivement.

[Avertissement]Avertissement

Bien que votre copie de travail « n'est qu'un ensemble de fichiers et de dossiers comme les autres dans votre système », vous pouvez éditer vos fichiers comme vous le voulez, mais vous devez signaler à Subversion toutes vos autres opérations. Par exemple, si vous voulez copier ou déplacer un élément dans votre copie de travail, vous devez utiliser svn copy ou svn move à la place des commandes de copie ou de déplacement fournies par votre système d'exploitation. Nous aborderons plus en détail ces commandes plus loin dans ce chapitre.

À moins que vous ne soyez prêt à propager l'ajout d'un nouveau fichier ou d'un nouveau dossier ou la modification d'un fichier ou dossier existant, il n'est pas nécessaire d'informer davantage le serveur Subversion que vous avez fait quelque chose.

Vous avez certainement remarqué que, dans les deux exemples précédents, Subversion crée la copie de travail dans un dossier nommé d'après le dernier composant de l'URL extraite. Ce comportement n'est qu'une facilité lorsque la seule information fournie à la commande svn checkout est l'URL d'extraction. Le client texte interactif de Subversion vous permet d'indiquer le nom d'un répertoire local afin d'y placer la copie de travail nouvellement créée. Par exemple :

$ svn checkout http://svn.exemple.com/svn/depot/trunk ma-copie-de-travail
A    ma-copie-de-travail/LISEZMOI
A    ma-copie-de-travail/INSTALL
A    ma-copie-de-travail/src/main.c
A    ma-copie-de-travail/src/header.h
…
Révision 8810 extraite.
$

Le dossier ma-copie-de-travail est créé par la commande svn checkout s'il n'existait pas auparavant.

Cycle de travail de base

Subversion dispose de nombreuses fonctionnalités, d'options, d'avertissements et de garde-fous. Mais dans une utilisation au jour le jour, vous n'utilisez qu'un petit nombre d'entre eux. Dans cette section, nous passons en revue l'utilisation quotidienne de Subversion.

Le cycle de travail typique ressemble à ceci :

  1. Mettre à jour votre copie de travail, par l'utilisation de la commande svn update.

  2. Faire des changements. Les changements les plus courants seront des modifications du contenu des fichiers existants. Parfois, vous aurez besoin d'ajouter, supprimer, copier ou déplacer des fichiers et des dossiers ; les commandes svn add, svn delete, svn copy et svn move prennent en charge ces modifications structurelles de la copie de travail.

  3. Examiner les changements effectués. Les commandes svn status et svn diff sont là pour passer en revue les modifications que vous avez apportées à votre copie de travail.

  4. Annuler des changements. Personne n'est parfait, aussi lorsque vous passez en revue vos modifications, vous pouvez constater des erreurs. Parfois, le plus simple est de repartir de zéro. La commande svn revert rétablit un fichier ou un dossier dans son état initial.

  5. Résoudre les conflits (fusionner les modifications). Dans le temps qu'il vous faut pour réaliser et passer en revue vos modifications, d'autres utilisateurs effectuent et publient peut-être des modifications également. Vous voulez sûrement intégrer leurs changements dans votre copie de travail pour éviter qu'ils ne deviennent « périmés » quand vous voudrez les publier. Une fois encore, la commande svn update prend cette opération en charge. Si des conflits apparaissent sur votre copie locale, vous devrez les résoudre à l'aide de la commande svn resolve.

  6. Propager les changements. La commande svn commit transmet vos modifications au dépôt et, si elles sont acceptées, créeront une nouvelle version de toutes les choses que vous avez modifiées. Maintenant, les autres aussi peuvent voir votre travail !

Mise à jour de la copie de travail

Si vous travaillez sur un projet donné qui est modifié par des copies de travail multiples, vous voudrez mettre à jour votre copie locale pour recevoir toutes les modifications qui ont pu être faites par les autres copies de travail depuis votre dernière mise à jour. Ce peut être des modifications que les autres membres de l'équipe de projet ont faites, ou simplement des modifications que vous avez faites vous-même sur un autre ordinateur. Afin de protéger vos données, Subversion ne vous autorise pas à publier des changements sur des fichiers ou des dossiers qui ne sont pas à jour. Il est donc toujours mieux d'avoir les dernières versions de tous les fichiers et dossiers des projets sur lesquels vous travaillez avant d'apporter vous-même des modifications.

Utilisez svn update pour synchroniser votre copie de travail avec la dernière version présente dans le dépôt :

$ svn update
Mise à jour de '.' :
U  truc.c
U  machin.c
Actualisé à la révision 2.
$

Dans cet exemple, il se trouve que quelqu'un a propagé des modifications à truc.c ainsi qu'à machin.c depuis votre dernière mise à jour et Subversion vient de répercuter ces modifications dans votre copie de travail.

Lorsque le serveur envoie des modifications vers votre copie de travail via svn update, un code, sous forme de lettre, est affiché à côté de chaque élément pour vous permettre de savoir quelles actions Subversion a effectuées pour mettre votre copie de travail à jour. Pour en savoir plus sur le sens de ces lettres, exécutez svn help update ou reportez-vous à svn update (up) dans Guide de référence de svn : le client texte interactif.

Modifications dans la copie de travail

Vous pouvez à présent vous mettre au travail et apporter des modifications à votre copie de travail. Vous pouvez effectuer deux types de modifications : les modifications de fichiers et les modifications de dossiers. Vous n'avez pas à prévenir Subversion que vous voulez modifier un fichier ; faites vos modifications avec un éditeur de texte, un traitement de texte, un logiciel de dessin ou n'importe quel autre outil que vous utilisez d'habitude. Subversion détecte automatiquement quels fichiers ont été modifiés et, en plus, il traite les fichiers binaires tout aussi facilement et aussi efficacement que les fichiers textes. Les modifications dans l'arborescence sont différentes ; cela comprend l'ajout et la suppression de fichiers, le renommage de fichiers ou de dossier ainsi que la copie ou le déplacement de fichiers ou dossiers vers un nouvel emplacement. Pour les modifications d'arborescence, les opérations de Subversion consistent à « marquer » la suppression, l'ajout, la copie ou le déplacement des fichiers et dossiers. Ces modifications prennent effet immédiatement sur votre copie de travail mais aucun ajout ou suppression n'interviendra sur le dépôt avant que vous ne les propagiez.

Voici un aperçu des cinq sous-commandes Subversion les plus utilisées pour faire des modifications sur l'arborescence :

svn add TRUC

Marque le fichier, le dossier ou le lien symbolique TRUC pour ajout. Lors de la prochaine propagation, TRUC devient un fils de son dossier parent. Notez que si TRUC est un dossier, tout ce qui se trouve à l'intérieur de TRUC est marqué pour ajout. Si vous ne désirez ajouter que TRUC lui-même, passez l'option --depth empty.

svn delete TRUC

Marque le fichier, le dossier ou le lien symbolique TRUC pour suppression. TRUC est immédiatement supprimé de votre copie de travail. Bien sûr, rien n'est jamais totalement supprimé du dépôt — seulement de la révision HEAD du dépôt. Vous avez accès à l'élément supprimé dans les révisions précédentes. [6]

svn copy TRUC MACHIN

Crée un nouvel élément MACHIN par duplication de TRUC et marque automatiquement MACHIN pour ajout. Lorsque MACHIN est ajouté au dépôt, lors de la prochaine propagation, son historique est enregistré (comme ayant été créé à partir de TRUC). svn copy ne crée pas de dossiers intermédiaires, à moins que vous ne lui passiez l'option --parents.

svn move TRUC MACHIN

Cette commande équivaut exactement à svn copy TRUC MACHIN; svn delete MACHIN. C'est-à-dire que MACHIN est marqué pour ajout en tant que copie de TRUC et que TRUC est marqué pour suppression. svn move ne crée pas de dossiers intermédiaires, à moins que vous ne lui passiez l'option --parents.

svn mkdir TRUC

Cette commande équivaut exactement à mkdir TRUC; svn add TRUC. C'est-à-dire qu'un nouveau dossier nommé TRUC est créé et marqué pour ajout.

Revue des changements apportés

Une fois vos modifications apportées, vous devez les intégrer au dépôt. Avant de le faire, il est souvent utile de jeter un coup d'œil sur ces modifications pour savoir exactement ce que vous avez changé. En examinant les modifications avant de les intégrer au dépôt, vous pouvez rédiger un commentaire de propagation, c'est-à-dire une description à l'attention des utilisateurs des changements propagés, ce commentaire étant conservé avec les données dans le dépôt. Éventuellement, vous verrez que vous avez modifié un fichier par inadvertance et cela vous donne une chance de revenir sur ces modifications avant de les propager au dépôt. En outre, c'est une bonne occasion de passer en revue et d'examiner les modifications avant de les publier. Vous pouvez obtenir une vue d'ensemble des modifications que vous avez faites en utilisant la commande svn status et voir le détail de ces changements en utilisant svn diff.

Vue d'ensemble des changements effectués

Pour avoir une vue d'ensemble des changements que vous avez effectués, utilisez la commande svn status. C'est certainement la commande Subversion que vous utiliserez le plus.

[Astuce]Astuce

Parce que l'affichage produit par la commande cvs status était tellement verbeux et parce que la commande cvs update ne faisait pas qu'une mise à jour mais affichait également l'état des changements effectués localement, la plupart des utilisateurs de CVS ont appris à utiliser cvs update pour propager leurs modifications. Dans Subversion, les opérations de mise-à-jour et d'examen des changements sont complètement séparées. Reportez-vous à la section intitulée « Distinction entre les commandes status et update » pour plus de détails.

Si vous lancez svn status sans argument à la racine de votre copie de travail, Subversion détecte toutes les modifications effectuées sur les fichiers et sur l'arborescence.

$ svn status
?       gribouillage.c
A       bazar/pognon
A       bazar/pognon/nouveau.h
D       bazar/vieux.c
M       machin.c

Dans le format d'affichage par défaut, svn status affiche sept colonnes de caractères, suivis par plusieurs espaces, suivis par un nom de fichier ou de dossier. La première colonne indique le statut du fichier ou du dossier et/ou son contenu. Les codes les plus utilisés sont :

? élément

Le fichier, dossier ou lien symbolique élément n'est pas suivi en versions.

A élément

Le fichier, dossier ou lien symbolique élément est marqué pour ajout au dépôt.

C élément

Le fichier élément est dans un état de conflit. C'est-à-dire que des modifications ont eu lieu dans le dépôt depuis votre dernière mise à jour et ces modifications interfèrent avec les modifications que vous avez effectuées sur votre copie de travail (et la mise à jour n'a pas résolu ce conflit). Vous devez résoudre ce conflit avant de propager vos changements vers le dépôt.

D élément

Le fichier, dossier ou lien symbolique élément est marqué pour suppression (Deletion en anglais).

M élément

Le contenu du fichier élément a été modifié.

Si vous spécifiez un chemin à svn status, vous obtenez uniquement les informations relatives à ce chemin :

$ svn status bazar/poisson.c
D      bazar/poisson.c

svn status possède aussi une option --verbose (-v) pour la rendre plus verbeuse : elle affiche alors le statut de tous les éléments de votre copie de travail, même ceux qui n'ont pas subi de modification :

 
$ svn status -v
M               44        23    sally     LISEZMOI
                44        30    sally     INSTALL
M               44        20    harry     machin.c
                44        18    ira       bazar
                44        35    harry     bazar/truite.c
D               44        19    ira       bazar/poisson.c
                44        21    sally     bazar/divers
A                0         ?     ?        bazar/divers/bitoniau.h
                44        36    harry     bazar/divers/tartempion.c

C'est la « version longue » de l'affichage de svn status. Les lettres de la première colonne ont la même signification que précédemment, mais la deuxième colonne indique le numéro de révision de travail de l'élément. Les troisième et quatrième colonnes indiquent le numéro de la révision dans laquelle a eu lieu le changement le plus récent et qui l'a effectué.

Aucune des commandes citées ci-dessus n'induit de connexion vers le dépôt : elles comparent les métadonnées de la zone administrative et les horodatages avec la copie de travail. Parfois, il peut être utile de voir quels éléments de la copie de travail ont été modifiés dans le dépôt depuis la dernière mise à jour de la copie de travail. À cette fin, svn status propose l'option --show-updates (-u) qui effectue une connexion au dépôt et ajoute les informations sur les éléments périmés :

$ svn status -u -v
M      *        44        23    sally     LISEZMOI
M               44        20    harry     machin.c
       *        44        35    harry     bazar/truite.c
D               44        19    ira       bazar/poisson.c
A                0         ?     ?        bazar/divers/bitoniau.h
État par rapport à la révision   46

Notez les deux astérisques : si vous lanciez la commande svn update, vous recevriez les changements relatifs à LISEZMOI et truite.c. Cela vous procure des informations particulièrement intéressantes, puisque l'un des éléments a fait l'objet de modifications de votre part (le fichier LISEZMOI) ; vous devez faire une mise à jour et récupérer les changements effectués sur LISEZMOI avant de propager les vôtres, sinon le dépôt rejettera votre propagation en la considérant comme périmée (le sujet est approfondi plus tard).

svn status peut afficher beaucoup plus d'informations sur les fichiers et dossiers de votre copie de travail que ce que nous venons de voir ici. Pour obtenir une description exhaustive de svn status et de ses modes d'affichage, lancez svn help status ou reportez-vous à svn status (stat, st) dans Guide de référence de svn : le client texte interactif.

Détail des modifications effectuées localement

La commande svn diff offre une autre façon d'examiner vos changements. En lançant la commande svn diff dans le dossier racine de votre copie de travail et sans argument, Subversion affiche les changements que vous avez apportés dans les fichiers lisibles par les humains de votre copie de travail. Il affiche ces changements au format diff unifié, un format qui décrit les changements par des « morceaux » (hunks ou snippets en anglais) du contenu des fichiers, où chaque ligne de texte est préfixée par un code sur un caractère : une espace signifie que la ligne n'a pas été modifiée ; un signe moins (-) signifie que la ligne a été supprimée du fichier ; et un signe plus (+) signifie que la ligne a été ajoutée au fichier. Dans le contexte de svn diff, les préfixes respectivement + et - montrent le contenu des lignes respectivement avant et après vos modifications.

Voici un exemple :

$ svn diff
Index: truc.c
===================================================================
--- machin.c	(révision 3)
+++ machin.c	(copie de travail)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>
 int main(void) {
-  printf("Soixante-quatre tranches de fromage...\n");
+  printf("Soixante-cinq tranches de fromage...\n");
 return 0;
 }
Index: LISEZMOI
===================================================================
--- LISEZMOI	(révision 3)
+++ LISEZMOI	(copie de travail)
@@ -193,3 +193,4 @@
+Pense-bête : passer au pressing.
Index: bazar/poisson.c
===================================================================
--- bazar/poisson.c	(révision 1)
+++ bazar/poisson.c	(copie de travail)
-Bienvenue dans le fichier 'poisson'.
-Plus d'informations seront disponibles prochainement.
Index: bazar/divers/machin.h
==================================================================
--- bazar/divers/bitoniau.h	(révision 8)
+++ bazar/divers/bitoniau.h	(copie de travail)
+Voici un nouveau fichier pour
+écrire sur les petites choses.

La commande svn diff produit ces lignes en comparant vos fichiers de travail aux copies « originales » en cache dans la zone administrative. Les fichiers marqués pour ajout sont affichés comme toute section de texte ajoutée, et les fichiers marqués pour suppression sont affichés comme toute section de texte supprimée. L'affichage de svn diff est compatible avec le programme patch — et encore davantage avec l'introduction de la commande svn patch dans Subversion 1.7. Les commandes patch lisent et appliquent des correctifs (patches en anglais), c'est-à-dire des fichiers qui décrivent les modifications appliquées à un ou plusieurs fichiers. Ainsi, vous pouvez partager les modifications que vous avez faites sur votre copie de travail avec quelqu'un d'autre sans propager ces modifications, mais seulement en créant un fichier correctif obtenu par la redirection de l'affichage de svn diff.

$ svn diff > fichier-correctif
$

Subversion utilise son propre moteur de calcul de différences, qui produit par défaut des résultats au format diff unifié. Si vous désirez obtenir les différences dans un autre format, spécifiez un programme de comparaison externe en utilisant l'option --diff-cmd et en fournissant les paramètres que vous voulez à l'aide de l'option --extensions (-x). Par exemple, pour obtenir les différences entre votre version locale du fichier et l'original de truc.c au format « contexte » et en ignorant la casse des caractères, vous pouvez lancer la commande suivante :

$ svn diff --diff-cmd /usr/bin/diff -x "-i" truc.c
…
$

Annulation des changements de la copie de travail

Supposons qu'en examinant la sortie de svn diff, vous vous rendiez compte que tous les changements effectués sur un fichier donné sont erronés. Peut-être auriez-vous dû laisser le fichier tel quel, ou bien peut-être qu'il serait plus facile de reprendre les changements depuis le début. Vous pourriez éditer à nouveau le fichier et défaire tous les changements. Vous pourriez essayer de trouver une copie du fichier tel qu'il était avant les changements et copier son contenu à la place de votre fichier modifié. Vous pourriez essayer d'appliquer les changements à l'envers en utilisant svn patch --reverse-diff ou depuis le système d'exploitation avec patch -R. Et il existe probablement encore d'autres méthodes.

Heureusement, avec Subversion annuler son travail et repartir de zéro ne nécessite pas autant d'acrobaties. Vous avez juste à utiliser la commande svn revert:

$ svn status LISEZMOI
M       LISEZMOI
$ svn revert LISEZMOI
'LISEZMOI' réinitialisé
$

Dans cet exemple, Subversion ramène le fichier dans son état d'avant les modifications en le remplaçant par la copie de l'original stockée dans la zone d'administration. Mais notez aussi que svn revert peut annuler n'importe quelle opération. Par exemple, vous pouvez décider que, après tout, vous ne voulez pas ajouter tel fichier :

$ svn status truc
?      truc

$ svn add truc
A         truc

$ svn revert truc
'truc' réinitialisé

$ svn status truc
?      truc
$

Ou, si vous avez enlevé un fichier du suivi de versions par erreur :

$ svn status LISEZMOI

$ svn delete LISEZMOI
D       LISEZMOI
$ svn revert LISEZMOI
'LISEZMOI' réinitialisé
$ svn status LISEZMOI

$

La commande svn revert est le parachute des gens imparfaits. Elle peut vous faire gagner un temps et une énergie considérables que vous auriez dépensé autrement à faire des corrections manuelles, voire à récupérer une copie de travail vierge juste pour retrouver un environnement de travail propre.

Résolution des conflits

Nous avons déjà vu que svn status -u est capable de prévoir les conflits, mais il reste à savoir gérer ces conflits. Des conflits peuvent survenir à chaque fois que vous voulez fusionner ou intégrer (dans le sens le plus général) des modifications en provenance du dépôt dans votre copie de travail. Vous savez désormais que la commande svn update génère ce type de situation ; la fonction essentielle de cette commande est de mettre à jour votre copie de travail en y intégrant toutes les modifications effectuées depuis votre dernière mise à jour. Comment Subversion vous rend-il compte de ces conflits et comment pouvez-vous les gérer ?

Supposons que vous lanciez svn update et que le résultat suivant apparaisse :

$ svn update
Mise à jour de '.' :
U  INSTALL
G  LISEZMOI
Conflit découvert dans 'machin.c'.
Sélectionner : (p) report, (df) diff complet, (e) édite, (m) fusion,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) voir toutes les options:

Les codes U (qui veut dire « mis à jour », updated en anglais) et G (qui veut dire fusionné, merGed en anglais) ne doivent pas vous inquiéter, les fichiers correspondants ayant absorbé sans problème les modifications venant du dépôt. Les fichiers notés U ne contenaient aucun changement local mais ont été mis à jour à partir de changements présents dans le dépôt. Le fichier marqué G avait subi des changements localement, mais les changements en provenance du dépôt ont pu être appliqués sans affecter les changements locaux.

Ce sont les quelques lignes suivantes qui sont intéressantes. D'abord, Subversion vous avertit que sa tentative pour fusionner les modifications en provenance du serveur dans le fichier local machin.c, il a détecté que certains changements interfèrent avec les vôtres quelqu'un a peut-être modifié la même ligne que vous. Quelle qu'en soit la raison, Subversion marque immédiatement le fichier comme étant dans un état de conflit. Ensuite, il vous demande ce que vous voulez faire pour résoudre ce conflit, vous proposant de choisir une action de manière interactive. Les options les plus utilisées sont affichées, mais vous pouvez voir toutes les options possibles en tapant s :

…
Sélectionner : (p) report, (df) diff complet, (e) édite, (m) fusion,
               (mc) my side of conflict, (tc) their side of conflict,
               (s) voir toutes les options:

  (e)  - change merged file in an editor  [edit]
  (df) - show all changes made to merged file
  (r)  - accept merged version of file

  (dc) - show all conflicts (ignoring merged version)
  (mc) - accept my version for all conflicts (same)  [mine-conflict]
  (tc) - accept their version for all conflicts (same)  [theirs-conflict]

  (mf) - accept my version of entire file (even non-conflicts)  [mine-full]
  (tf) - accept their version of entire file (same)  [theirs-full]

  (m)  - use internal merge tool to resolve conflict
  (l)  - launch external tool to resolve conflict  [launch]
  (p)  - mark the conflict to be resolved later  [postpone]
  (q)  - postpone all remaining conflicts
  (s)  - show this list (also 'h', '?')
Words in square brackets are the corresponding --accept option arguments.

Select: (p) postpone, (df) show diff, (e) edit file, (m) fusion,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options:

Regardons brièvement ce que recèle chaque option avant de les détailler :

(e) edit [edit]

Ouvrir le fichier en conflit avec votre éditeur de texte favori, qui est spécifié dans la variable d'environnement EDITOR.

(df) diff-full

afficher les différences entre la révision de base et le fichier en conflit au format diff unifié.

(r) resolved

Après édition du fichier, indiquer à Subversion que vous avez résolu les conflits à l'intérieur du fichier et qu'il doit accepter son contenu actuel ; en bref, vous avez « résolu » le conflit.

(dc) display-conflict

Afficher toutes les parties du fichier en conflit, en ignorant les modifications qui ont réussi à être fusionnées.

(mc) mine-conflict [mine-conflict]

ignorer les changements envoyés par le serveur qui entrent en conflit avec vos modifications locales pour le fichier concerné. Cependant, accepter et fusionner toutes les parties qui n'engendrent pas de conflit.

(tc) theirs-conflict [theirs-conflict]

Ignorer les modifications locales qui engendrent des conflits avec la version envoyée par le serveur pour le fichier concerné. Cependant, conserver toutes les parties qui n'engendrent pas de conflit pour ce fichier.

(mf) mine-full [mine-full]

Ignorer les changements envoyés par le serveur et utiliser uniquement votre version locale pour le fichier concerné.

(tf) theirs-full [theirs-full]

Ignorer vos changements sur le fichier concerné et utiliser la version envoyée par le serveur.

(m) fusion

Lancer un outil interne de fusion pour traiter le conflit. Cette option est disponible à partir de Subversion 1.8.

(l) launch

Lancer un programme externe pour résoudre le conflit. Ceci nécessite un peu de préparation en amont.

(p) postpone [postpone]

Laisser le fichier en état de conflit, conflit que vous devrez résoudre après la fin de la mise à jour.

(s) show all

Afficher la liste de toutes les commandes que vous pouvez utiliser dans la résolution interactive des conflits.

Nous allons maintenant passer en revue chaque commande, en les classant par fonctionnalité.

Traitement des lignes en conflit de façon interactive

Avant de décider comment résoudre un conflit de manière interactive, il est probable que vous vouliez examiner le détail des lignes en conflit. Deux des commandes accessibles via l'invite interactive de gestion des conflits répondent à ce besoin. La première est la commande df (pour diff-full en anglais), qui affiche toutes les modifications locales du fichier ainsi que les zones en conflit :

…
Sélectionner : (p) report, (df) diff complet, (e) édite, (m) fusion,
               (mc) my side of conflict, (tc) their side of conflict,
               (s) voir toutes les options:df
--- .svn/text-base/sandwich.txt.svn-base      mar. 11 déc. 2007, 21:33:57
+++ .svn/tmp/tempfile.32.tmp     mar. 11 déc. 2007, 21:34:33
@@ -1 +1,5 @@
-Achète-moi un sandwich.
+<<<<<<< .mien
+Va chercher un hamburger.
+=======
+Apporte-moi un taco !
+>>>>>>> .r32
…

La première ligne du diff correspond à ce que contenait la copie de travail dans l'ancienne version (la révision BASE), la ligne suivante correspond à vos modifications et la dernière ligne contient les modifications reçues du serveur (la révision HEAD la plupart du temps).

La deuxième commande est similaire à la première mais dc(display conflict en anglais) affiche uniquement les zones en conflits, pas toutes les modifications apportées au fichier. En plus, cette commande utilise un format d'affichage légèrement différent qui permet de comparer facilement le contenu du fichier tel qu'il serait dans les trois états : original et non édité ; avec les modifications locales et les changements provenant du serveur qui génèrent des conflits ignorés ; uniquement les changements provenant du serveur et vos modifications locales qui génèrent des conflits ignorés.

Après avoir pris connaissance des informations fournies par ces commandes, vous être prêt à l'action suivante.

Résolution des conflits en mode interactif

La manière la plus directe de résoudre interactivement un conflit utilise un outil interne de fusion. Cet outil vous demande quoi faire pour chaque modification qui pose problème et permet de fusionner et d'adapter les modifications de manière interactive. Cependant, il existe d'autres manières de faire interactivement— deux outils peuvent fusionner et adapter les modifications à l'aide d'éditeurs externes, les autres vous permettent simplement de choisir une version du fichier parmi celles proposées et de passer à la suite.

Vous avez déjà passé en revue les modifications qui posent problème, il est donc temps de résoudre ces conflits. La première commande qui peut vous aider est « merge » (m), disponible à partir de la version 1.8 de Subversion. Cette commande affiche les zones en conflit et vous permet de choisir parmi différentes options pour chaque zone en conflit :

Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: m
Merging 'Makefile'.
Conflicting section found during merge:
(1) their version (at line 24)                  |(2) your version (at line 24)
------------------------------------------------+------------------------------------------------
top_builddir = /truc                             |top_builddir = /machin
------------------------------------------------+------------------------------------------------
Select: (1) use their version, (2) use your version,
        (12) their version first, then yours,
        (21) your version first, then theirs,
        (e1) edit their version and use the result,
        (e2) edit your version and use the result,
        (eb) edit both versions and use the result,
        (p) postpone this conflicting section leaving conflict markers,
        (a) abort file merge and return to main menu:

Comme vous le constatez, en utilisant l'outil de fusion interne, vous pouvez naviguer entre les différentes zones en conflit dans le fichier et choisir différentes stratégies de résolution, voire remettre à plus tard la résolution du conflit pour certaines zones.

Cependant, si vous souhaitez utiliser un éditeur externe pour choisir une combinaison de vos modifications locales, vous pouvez utiliser la commande « édite » (e) pour modifier manuellement dans un éditeur de texte (configuré en suivant les instructions données dans la section intitulée « Utilisation d'éditeurs externes ») le fichier avec des marqueurs indiquant les conflits. Après avoir édité le fichier et si vous êtes satisfait de vos changements, vous pouvez indiquer à Subversion que le fichier n'est plus en conflit en utilisant la commande « résolu » (r).

En dehors de quelques puristes d'Unix, l'édition manuelle de ce fichier avec votre éditeur préféré peut sembler quelque peu « bas de gamme » (voir la section intitulée « Résolution des conflits à la main » pour une description détaillée), c'est pourquoi Subversion propose d'utiliser des outils graphiques plus évolués et spécialisés dans la fusion de documents (voir la section intitulée « Outils de fusion externes ») avec la commande « launch » (lancer en français), raccourci l).

Il existe deux autres options qui offrent des compromis. Les commandes respectivement « mien-conflits » (mc) et « leur-conflits » (tc) demandent à Subversion de conserver respectivement vos modifications locales et les modifications en provenance du serveur pour tous les conflits rencontrés. Mais, au contraire de « mien complet » et « autre complet », ces commandes conservent à la fois vos modifications et les modifications en provenance du serveur dans les zones du fichier où il n'est pas détecté de conflit.

Enfin, si vous décidez que vous ne voulez pas fusionner les modifications mais simplement conserver une version du fichier ou une autre, vous pouvez choisir votre version (étiquetée « mine ») en sélectionnant la commande « mine-full » (mf) ou choisir la version du dépôt en sélectionnant la commande « theirs-full » (tf).

Remise à plus tard de la résolution d'un conflit

Le titre peut laisser penser à un paragraphe sur l'amélioration des relations conjugales, mais il s'agit bien toujours de Subversion, voyez plutôt. Si, lorsque vous effectuez une mise à jour, Subversion soulève un conflit que vous n'êtes pas prêt à résoudre, vous pouvez, fichier par fichier, taper p, pour remettre à plus tard la résolution du conflit. Si, lors de votre mise à jour, vous ne voulez résoudre aucun conflit, vous pouvez passer l'option --non-interactive à svn update et les fichiers en conflit sont automatiquement marqués C.

À partir de Subversion 1.8, un outil interne de fusion vous permet de différer la résolution de conflits pour certains conflits et de résoudre les autres. Ainsi, vous pouvez différer la résolution des conflits zone par zone et non plus uniquement fichier par fichier.

Le C indique un conflit, c'est-à-dire que les changements sur le serveur interfèrent avec les vôtres et vous devez donc choisir manuellement entre les différentes modifications après la fin de la procédure de mise à jour. Quand vous repoussez à plus tard la résolution d'un conflit, Subversion accomplit trois actions qui vous aideront à repérer et à résoudre ce conflit :

  • Subversion affiche un C pendant la mise à jour et enregistre que le fichier est dans un état de conflit.

  • Si Subversion considère que le fichier peut être fusionné, il place dans le fichier des marqueurs de conflit (des chaînes de caractères spéciales qui dénotent les « contours » des conflits) pour mettre en exergue les zones de conflit (Subversion utilise la propriété svn:mime-type pour déterminer si un fichier peut subir une fusion contextuelle ligne par ligne ; voir la section intitulée « Type de contenu des fichiers » pour en apprendre davantage).

  • Pour chaque fichier en conflit, Subversion place trois fichiers supplémentaires non-suivis en versions dans votre copie de travail :

    nom_du_fichier.mine

    C'est votre fichier tel qu'il était dans votre copie de travail avant la mise à jour, c'est-à-dire sans les marqueurs de conflits. Ce fichier ne comporte que vos derniers changements (si Subversion considère que le fichier ne peut pas être fusionné, le fichier .mine n'est pas créé, car il serait identique à la version de travail).

    nom_du_fichier.rANCIENNE_REV

    C'est le fichier tel qu'il était à la révision BASE, avant la mise à jour de votre copie de travail. C'est donc le fichier que vous avez extrait avant de faire vos dernières modifications, ANCIENNE_REV désignant le numéro de révision de cette version de base.

    nom_du_fichier.rNOUVELLE_REV

    C'est le fichier que le client Subversion vient de recevoir du serveur via l'opération de mise à jour, où NOUVELLE_REV désigne le numéro de révision de la mise à jour demandée (HEAD, à moins d'avoir spécifié une autre révision).

Par exemple, Sally effectue un changement sur le fichier sandwich.txt mais elle ne propage pas immédiatement ses modifications. Pendant ce temps, Harry propage des changements sur ce même fichier. Sally met à jour sa copie de travail avant d'effectuer la propagation et un conflit apparaît, dont elle remet la résolution à plus tard :

$ svn update
Mise à jour de '.' :
Conflit découvert dans 'sandwich.txt'.
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: p
C  sandwich.txt
Actualisé à la révision 2.
Résumé des conflits :
  Text conflicts: 1
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2

À partir de là, Subversion n'autorise pas Sally à propager le fichier sandwich.txt avant que les trois fichiers temporaires ne soient effacés :

$ svn commit -m "Quelques petits ajouts"
svn: Échec de la propagation (commit), détails :
svn: Arrêt de la propagation : '/home/sally/travail-svn/sandwich.txt'
                               demeure en conflit

Si vous avez remis à plus tard la résolution d'un conflit, vous devez le résoudre pour que Subversion vous autorise à propager vos changements. Vous pouvez le faire avec la commande svn resolve. Cette commande possède l'option --accept afin de spécifier votre manière de résoudre le conflit. Avant Subversion 1.8, la commande svn resolve exigeait de spécifier l'option. Dorénavant, Subversion vous autorise à ne pas la spécifier. Dans ce cas, Subversion passe en mode de résolution interactif tel que présenté dans la section précédente (voir la section intitulée « Résolution des conflits en mode interactif »). Dans cette section, nous allons présenter l'utilisation de l'option --accept pour la résolution de conflits.

L'option --accept de la commande svn resolve demande à Subversion d'utiliser une des approches prédéfinies pour résoudre un conflit. Si vous choisissez la version du fichier que vous avez extraite avant de faire vos changements, utilisez l'argument --accept=base. Si vous préférez garder la version qui contient uniquement vos changements, utilisez l'argument --accept=mine-full. Vous pouvez aussi choisir la version la plus récente venant du serveur (et donc abandonner tous vos changements) en utilisant l'argument --accept=theirs-full. Il existe d'autres méthodes de résolution « pré-emballées ». Lisez --accept ACTION dans Guide de référence de svn : le client texte interactif pour en obtenir les détails.

Vous n'êtes pas limité à ces choix tout ou rien. Si vous comptez effectuer un mélange de vos modifications et des modifications rapatriées du serveur, fusionnez le fichier en conflit « à la main » (examinez et éditez les marqueurs de conflit dans le fichier) puis indiquez à Subversion de résoudre le conflit en gardant la version de travail dans son état actuel par la commande svn resolve avec l'argument --accept=working.

La commande svn resolve supprime les trois fichiers temporaires et retient la version du fichier que vous avez spécifié avec l'option --accept. À la fin d'exécution de la commande (en considérant bien évidemment que vous n'avez décidé de reporter la résolution du conflit), Subversion considère que le fichier n'est plus dans un état de conflit :

$ svn resolve --accept working sandwich.txt
Conflit sur 'sandwich.txt' résolu

Résolution des conflits à la main

Résoudre les conflits à la main peut paraître quelque peu intimidant la première fois. Mais avec un peu de pratique, un enfant de cinq ans y arriverait.

Prenons un exemple. Par manque de communication entre Sally (votre collaboratrice) et vous-même, vous éditez en même temps le fichier sandwich.txt. Sally propage ses changements et, quand vous mettez à jour votre copie de travail, un conflit apparaît, que vous devez résoudre en éditant sandwich.txt. Jetons un œil à ce fichier :

$ cat sandwich.txt
Tranche de pain supérieure
Mayonnaise
Laitue
Tomate
Comté
<<<<<<< .mine
Saucisson
Mortadelle
Jambon
=======
Choucroute
Poulet rôti
>>>>>>> .r2
Moutarde
Tranche de pain inférieure

Les suites de caractères inférieur-à (<), égal(=) ou supérieur-à (>) sont des marqueurs de conflit, ils ne font pas partie des données elles-mêmes. Vous devrez en général vous assurer qu'elles ont disparu du fichier avant de propager vos modifications. Le texte entre les deux premiers marqueurs est constitué des modifications que vous avez apportées dans la zone de conflit :

<<<<<<< .mine
Saucisson
Mortadelle
Jambon
=======

Le texte entre le deuxième et le troisième marqueur est celui du fichier propagé par Sally :

=======
Choucroute
Poulet rôti
>>>>>>> .r2

Normalement, vous n'allez pas juste supprimer les marqueurs et les changements effectués par Sally (elle sera affreusement déçue quand on lui apportera un sandwich différent de ce qu'elle a commandé). Vous décrochez donc le téléphone, ou vous traversez le bureau, pour expliquer à Sally qu'on ne met pas de choucroute dans un sandwich. [7] Après vous être mis d'accord sur les changements à enregistrer, éditez votre fichier et enlevez les marqueurs de conflit.

Tranche de pain supérieure
Mayonnaise
Laitue
Tomate
Comté
Saucisson
Mortadelle
Jambon
Moutarde
Tranche de pain inférieure

Maintenant utilisez svn resolve et vous êtes paré pour propager vos changements :

$ svn resolve --accept working sandwich.txt
Conflit sur 'sandwich.txt' résolu
$ svn commit -m "Va pour mon sandwich et au diable celui de Sally !"

Soyez prudent et ne lancez svn resolve qu'une fois certain que vous avez résolu le conflit dans votre fichier : une fois les fichiers temporaires effacés, Subversion vous laisse propager le fichier même s'il contient toujours des marqueurs de conflit.

Si jamais vous êtes perdu lors de l'édition du fichier en conflit, vous pouvez toujours consulter les trois fichiers que Subversion a créé pour vous dans votre copie de travail, y compris le fichier tel qu'il était avant que vous ne lanciez la mise à jour. Vous pouvez même utiliser un outil externe interactif spécialisé dans les fusions pour examiner ces trois fichiers.

Abandon des modifications au profit de la révision la plus récente

Si vous faites face à un conflit et que vous décidez d'abandonner vos changements, vous pouvez lancer svn resolve --accept theirs-full CHEMIN-DU-CONFLIT, Subversion abandonne alors vos modifications et supprime les fichiers temporaires :

$ svn update
Conflit découvert dans 'machin.c'.
Sélectionner : (p) report, (df) diff complet, (e) édite,
        (h) aide pour plus d'options :
C    sandwich.txt
Actualisé à la révision 2.
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ svn resolve --accept theirs-full sandwich.txt
Conflit sur 'sandwich.txt' résolu
$

Retour en arrière avec svn revert

Si vous faites face à un conflit et qu'après examen de la situation, vous décidez d'abandonner vos changements et de repartir de zéro (peu importe en fait que ce soit après un conflit ou à n'importe quel autre moment), contentez-vous de revenir en arrière sur vos changements :

$ svn revert sandwich.txt
'sandwich.txt' réinitialisé
$ ls sandwich.*
sandwich.txt

Notez que quand vous revenez en arrière sur un fichier en conflit, vous n'avez pas besoin de lancer svn resolve.

Propagation des modifications

Enfin ! Vos modifications sont terminées, vous les avez fusionnées avec celles du serveur et vous êtes prêt à les propager vers le dépôt.

La commande svn commit envoie vos changements au dépôt. Quand vous propagez un changement, vous devez l'accompagner d'un commentaire de propagation qui décrit ce changement. Votre commentaire est associé à la nouvelle révision que vous créez. Si votre commentaire est bref, vous pouvez le passer en ligne de commande en utilisant l'option --message (ou -m) :

$ svn commit -m "Nombre de tranches de fromage corrigé."
Envoi        sandwich.txt
Transmission des données .
Révision 3 propagée.

Cependant, si vous avez rédigé votre commentaire au fur et à mesure, vous souhaitez sûrement indiquer à Subversion de le récupérer dans un fichier en lui donnant le nom du fichier avec l'option --file (-F) :

$ svn commit -F commentaire_de_propagation
Envoi        sandwich.txt
Transmission des données .
Révision 4 propagée.

Si vous ne spécifiez ni l'option --message ni l'option --file, Subversion lance automatiquement votre éditeur de texte favori (voir les détails de editor-cmd dans la section intitulée « Configuration générale ») pour que vous rédigiez le commentaire de propagation.

[Astuce]Astuce

Si, au moment où vous rédigez votre commentaire de propagation, vous décidez d'annuler la propagation, vous n'avez qu'à quitter l'éditeur de texte sans sauvegarder les changements. Si vous avez déjà sauvegardé le commentaire, effacez le texte, sauvegardez à nouveau puis choisissez d'annuler :

$ svn commit
Attente de Emacs...Fait

Entrée du journal non modifié ou non précisé
a)nnule, c)ontinue, e)dite
a
$

Le dépôt ne sait pas si vos changements ont un sens ou pas ; il vérifie seulement que personne n'a modifié, pendant que vous aviez le dos tourné, un des fichiers que vous-même avez modifié. Si c'est le cas, la propagation toute entière échoue, affichant un message vous informant qu'un ou plusieurs de vos fichiers ne sont plus à jour :

$ svn commit -m "Ajout d'une autre règle"
Envoi        règles.txt
Transmission des données .
svn: E155011: Echec de la propagation (commit), détails :
svn: E155011: Fichier '/règles.txt' obsolète
…

Notez que le phrasé exact de ce message d'erreur dépend du protocole réseau et du serveur que vous utilisez, mais l'idée reste la même.

À ce moment là, vous devez lancer svn update, traiter les fusions ou conflits qui apparaissent et retenter une propagation.

Nous en avons terminé avec le cycle d'utilisation de base de Subversion. Subversion offre beaucoup d'autres fonctionnalités pour gérer votre dépôt et votre copie de travail, mais l'utilisation quotidienne de Subversion ne requiert pratiquement que les commandes que nous venons de voir dans ce chapitre. Intéressons-nous quand même à quelques commandes supplémentaires utilisées relativement souvent.

Recherche dans l'historique

Votre dépôt Subversion est comme une machine à remonter le temps. Il garde une trace de tous les changements propagés et permet de parcourir cet historique en examinant aussi bien les versions précédentes des fichiers et des dossiers que les métadonnées associées. D'une simple commande Subversion, vous pouvez extraire (ou restaurer) une copie de travail du dépôt tel qu'il était à n'importe quelle date ou numéro de révision passée. Cependant, vous voulez parfois juste sonder le passé sans y retourner.

Plusieurs commandes renvoient des informations sur l'historique des données présentes dans le dépôt :

svn diff

affiche les détails, ligne par ligne, d'un changement donné.

svn log

fournit beaucoup d'informations : les commentaires de propagation avec la date et l'auteur de la révision ainsi que les chemins qui ont été modifiés à chaque révision.

svn cat

récupère le fichier tel qu'il existait à un numéro de révision donné et l'affiche à l'écran.

svn annotate

récupère un fichier humainement lisible tel qu'il existait à un numéro de révision donné, affiche son contenu sous la forme d'un tableau avec, pour chaque ligne, les informations relatives au dernier changement de celle-ci.

svn list

liste les fichiers contenus dans un dossier à une révision donnée.

Détail des modifications passées

Nous avons déjà vu la commande svn diff, qui affiche les différences entre fichiers au format diff unifié ; nous l'avons utilisée pour afficher les modifications locales effectuées sur notre copie de travail avant de les propager vers le dépôt.

En fait, il y a trois façons différentes d'utiliser svn diff :

  • Examiner des modifications locales.

  • Comparer votre copie de travail au dépôt.

  • Comparer des révisions du dépôt.

Modifications locales

Comme nous l'avons vu précédemment, svn diff, s'il est invoqué sans option, compare les fichiers de votre copie de travail à leurs versions « originales » gardées en cache dans la zone .svn :

$ svn diff
Index: règles.txt
===================================================================
--- règles.txt	(révision 3)
+++ règles.txt	(copie de travail)
@@ -1,4 +1,5 @@
 Être attentif envers les autres
 Liberté = Responsabilité
 Tout dans la modération
-Mâcher la bouche ouverte
+Mâcher la bouche fermée
+Écouter quand les autres parlent
$

Comparaison entre la copie de travail et le dépôt

Si un seul numéro de révision est fourni à l'option --revision (-r), votre copie de travail est comparée à la révision spécifiée du dépôt :

$ svn diff -r 3 règles.txt
Index: règles.txt
===================================================================
--- règles.txt	(révision 3)
+++ règles.txt	(copie de travail)
@@ -1,4 +1,5 @@
 Être attentif envers les autres
 Liberté = Responsabilité
 Tout dans la modération
-Mâcher la bouche ouverte
+Mâcher la bouche fermée
+Écouter quand les autres parlent
$

Comparaison entre des révisions du dépôt

Si deux numéros de révision sont fournis à l'option --revision (-r), séparés par le caractère deux-points (:), les deux révisions sont directement comparées :

 
$ svn diff -r 2:3 règles.txt
Index: règles.txt
===================================================================
--- règles.txt	(révision 2)
+++ règles.txt	(révision 3)
@@ -1,4 +1,4 @@
 Être attentif envers les autres
-Liberté = Glace au chocolat
+Liberté = Responsabilité
 Tout dans la modération
 Mâcher la bouche ouverte
$

Une autre façon de comparer une révision à la précédente, plus conviviale, est d'utiliser l'option --change (-c) :

 
$ svn diff -c 3 règles.txt
Index: règles.txt
===================================================================
--- règles.txt	(révision 2)
+++ règles.txt	(révision 3)
@@ -1,4 +1,4 @@
 Être attentif envers les autres
-Liberté = Glace au chocolat
+Liberté = Responsabilité
 Tout dans la modération
 Mâcher la bouche ouverte
$

Enfin, vous pouvez comparer des révisions du dépôt même si vous n'avez pas de copie de travail en local sur votre ordinateur, simplement en incluant l'URL appropriée sur la ligne de commande :

$ svn diff -c 5 http://svn.exemple.com/depot/exemple/trunk/texte/règles.txt
…
$

Historique des modifications

Pour connaître l'historique d'un fichier ou d'un dossier, utilisez la commande svn log. Elle affiche la liste des gens qui ont modifié le fichier ou le dossier en question, le numéro de chaque révision où il a changé, l'heure et la date de cette révision et, s'il y en avait un, le commentaire associé à la propagation :

 
$ svn log
------------------------------------------------------------------------
r3 | sally | 2008-05-15 23:09:28 -0500 (jeu. 15 Mai 2008) | 1 ligne

Ajout des lignes include et correction du nombre de tranches de fromage.
------------------------------------------------------------------------
r2 | harry | 2008-05-14 18:43:15 -0500 (mer. 14 Mai 2008) | 3 lignes

Ajout des méthodes main().
------------------------------------------------------------------------
r1 | sally | 2008-05-10 19:50:31 -0500 (sam. 10 Mai 2008) | 1 ligne

Import initial
------------------------------------------------------------------------

Notez que, par défaut, l'historique est affiché en ordre chronologique inverse. Si vous voulez afficher un intervalle de révisions donné dans un ordre particulier ou juste une seule révision, ajoutez l'option --revision (-r) :

Tableau 2.1. Requêtes classiques dans l'historique

CommandeDescription
svn log -r 5:19Affiche l'historique a partir de la révision 5 jusqu'à la révision 19 dans l'ordre chronologique.
svn log -r 19:5Affiche l'historique à partir de la révision 5 jusqu'à la révision 19 dans l'ordre chronologique inverse.
svn log -r 8Affiche l'historique pour la révision 8 uniquement.


Vous pouvez aussi afficher l'historique d'un fichier ou d'un dossier particulier. Par exemple :

$ svn log truc.c
…
$ svn log http://truc.com/svn/trunk/code/truc.c
…

Ceci n'affiche le contenu de l'historique que pour les révisions dans lesquelles le fichier de travail (ou l'URL) a changé.

Si vous voulez obtenir plus d'informations sur un fichier ou un dossier, svn log accepte également l'option --verbose (-v). Comme Subversion autorise les déplacements et les copies de dossiers et de fichiers, il est important de pouvoir tracer ces modifications de chemin dans le système de fichiers. Ainsi, en mode verbeux, svn log affiche la liste des déplacements au cours de la révision concernée :

$ svn log -r 8 -v
------------------------------------------------------------------------ 
r8 | sally | 2008-05-21 13:19:25 -0500 (mer. 21 Mai 2008) | 1 ligne
Chemins modifiés :
   M /trunk/code/truc.c
   M /trunk/code/machin.h
   A /trunk/code/doc/LISEZMOI

Machination du bidule.

svn log accepte aussi l'option --quiet (-q), qui permet de ne pas afficher le contenu du commentaire de propagation. En combinaison avec --verbose, svn log n'affiche que les noms des fichiers qui ont changé.

Depuis Subversion 1.7, les utilisateurs du client texte interactif bénéficient du mode d'affichage spécial de svn log qui intègre un format similaire à celui produit par svn diff, que nous avons abordé précédemment. Si vous lancez svn log avec l'option --diff, Subversion ajoute, à chaque partie de l'historique d'une révision, une édition des différences au style diff. C'est très pratique pour visualiser à la fois les changements sémantiques, de haut niveau et les modifications ligne par ligne d'une révision.

À partir de Subversion 1.8, svn log reconnait les options --search et --search-and. Ces options vous permettent de filtrer la sortie de svn log en fonction du motif que vous fournissez. Lorsque vous utilisez ces options, le commentaire de propagation n'est affiché que si l'auteur, la date, le commentaire ou la liste des chemins modifiés correspond au motif fourni.

Navigation dans le dépôt

Grâce aux commandes svn cat et svn list, vous pouvez afficher des révisions variées des fichiers et dossiers sans changer la révision de votre copie de travail. En fait, vous n'avez même pas besoin d'avoir une copie de travail pour les utiliser.

Affichage du contenu d'un fichier

Si vous voulez examiner une version antérieure d'un fichier et pas nécessairement les différences entre deux fichiers, vous pouvez utiliser svn cat :

 
$ svn cat -r 2 règles.txt
Être attentif envers les autres
Liberté = Glace au chocolat
Tout dans la modération
Mâcher la bouche ouverte
$

Vous pouvez également rediriger la sortie de svn cat directement dans un fichier :

 
$ svn cat -r 2 règles.txt > règles.txt.v2
$

Affichage ligne par ligne des auteurs de modifications

La commande svn annotate ressemble beaucoup à la commande svn cat que nous venons d'aborder à la section précédente. Cette commande affiche aussi le contenu du fichier suivi en versions mais utilise un format en tableau. Chaque ligne, en plus du contenu du fichier, indique également le nom de l'utilisateur, le numéro de la révision et (optionnellement) l'horodatage de la révision dans laquelle la ligne a été modifiée pour la dernière fois.

Quand vous lui spécifiez comme argument un fichier de la copie de travail, svn annotate affiche par défaut les attributs du fichier correspondant à la version de la copie de travail.

$ svn annotate règles.txt
     1      harry Être attentif envers les autres
     3      sally Liberté = Responsabilité
     1      harry Tout dans la modération
     -          - Mâcher la bouche fermée
     -          - Écouter quand les autres parlent

Remarquez que, pour certaines lignes, il n'y a pas d'auteur indiqué. C'est parce que ces lignes ont été modifiées dans la copie de travail du fichier. De cette façon, svn annotate devient un outil supplémentaire pour visualiser quelles lignes du fichier vous avez modifiées. Vous pouvez utiliser le mot-clé BASE (voir la section intitulée « Mots-clés de révision ») pour visualiser la version non modifiée du fichier telle qu'elle est stockée dans votre copie de travail.

$ svn annotate règles.txt@BASE
     1      harry Être attentif envers les autres
     3      sally Liberté = Responsabilité
     1      harry Tout dans la modération
     1      harry Mâcher la bouche ouverte

L'option --verbose (-v) demande à svn annotate d'inclure également pour chaque ligne l'horodatage de la révision. Cela augmentant de manière significative la largeur de chaque ligne, nous nous passerons d'exemple pour cette option.

De même que pour svn cat, vous pouvez demander à svn annotate d'afficher les versions précédentes du fichier. C'est une astuce particulièrement utile lorsque, après avoir trouvé qui a modifié telle ligne dans le fichier, vous voulez savoir qui avait écrit la version précédente de cette ligne.

 
$ svn blame règles.txt -r 2
     1      harry Être attentif envers les autres
     1      harry Liberté = Glace au chocolat
     1      harry Tout dans la modération
     1      harry Mâcher la bouche ouverte

Au contraire de svn cat, le fonctionnement de svn annotate est fortement corrélé à la notion de « ligne » d'un fichier texte lisible par un humain. Ainsi, si vous essayez de lancer la commande sur un fichier que Subversion considère non lisible par un humain (en raison de sa propriété svn:mime-type, voir la section intitulée « Type de contenu des fichiers » pour les détails), vous obtiendrez un message d'erreur.

$ svn annotate images/logo.png 
Skipping binary file (use --force to treat as text): 'images/logo.png'
$

Comme l'indique le message d'erreur, vous pouvez spécifier l'option --force pour outrepasser la vérification et effectuer les annotations comme pour tout fichier contenant des lignes et lisible par un humain. Naturellement, si vous forcez Subversion à effectuer des annotations sur un fichier non textuel, vous obtiendrez ce à quoi vous devez vous attendre : un cafouillis incompréhensible.

$ svn annotate images/logo.png --force
     6      harry \211PNG
     6      harry ^Z
     6      harry
     7      harry \274\361\MI\300\365\353^X\300…
[Astuce]Astuce

En fonction de votre humeur du moment ou des raisons pour lesquelles vous lancez cette commande, vous vous retrouverez à taper svn blame … ou svn praise … au lieu de la forme canonique de la commande svn annotate. Pas de souci, les développeurs de Subversion l'ont anticipé et ces alias fonctionnent parfaitement.

Pour finir, comme pour beaucoup de commandes de Subversion qui interrogent les fichiers suivis en versions, svn annotate peut faire référence aux fichiers par leur URL dans le dépôt, ce qui permet un accès aux informations même sans disposer d'une copie de travail.

Contenu des dossiers suivis en versions

La commande svn list liste les fichiers présents dans un dossier du dépôt sans pour autant les télécharger :

$ svn list http://svn.exemple.com/depot/projet
LISEZMOI
branches/
tags/
trunk/

Si vous désirez une liste plus détaillée, passez l'option --verbose (-v) et vous obtenez alors quelque chose comme ceci :

$ svn list -v http://svn.exemple.com/depot/projet
  23351 sally                 05 fev.,  13:26 ./
  20620 sally            1084 13 janv. 2006   LISEZMOI
  23339 harry                 02 sept., 01:40 branches/
  23198 harry                 23 janv., 17:17 tags/
  23351 sally                 27 fevr., 13:26 trunk/

Les colonnes vous indiquent la révision à laquelle le fichier ou le dossier a été modifié pour la dernière fois, qui est l'auteur de ce changement, la taille du fichier si c'en est un, la date de dernière modification et le nom de l'élément.

[Avertissement]Avertissement

La commande svn list sans argument prend pour cible l'URL du dépôt correspondant au dossier local en cours, pas le dossier en cours de la copie de travail. Après tout, si vous voulez voir le contenu de votre dossier local, vous pouvez utiliser ls, tout simplement (ou l'équivalent sur votre système non-Unix).

Extraction d'anciennes versions au sein d'un dépôt

En plus de toutes les commandes citées précédemment, vous pouvez utiliser svn update et svn checkout avec l'option --revision pour ramener une copie de travail complète « dans le passé » : [8]

 
# met le dossier courant tel qu'il était à la révision 1729.
$ svn update -r 1729 
Mise à jour de '.' :
…
$
[Astuce]Astuce

Beaucoup de nouveaux utilisateurs de Subversion essaient d'utiliser la commande svn update comme dans cet exemple pour « annuler » des modifications propagées, mais cela ne fonctionne pas parce que vous ne pouvez pas propager des changements que vous avez obtenus en remontant le temps d'une copie de travail si le contenu des fichiers a changé dans des révisions plus récentes. Reportez-vous à la section intitulée « Résurrection des éléments effacés » pour une description de comment « annuler » une propagation.

Si vous préférez créer une nouvelle copie de travail complète à partir d'un instantané plus ancien, vous pouvez modifier l'appel classique à svn checkout. De même que pour svn update, vous pouvez passer l'option --revision (-r). Mais pour des raisons que nous explicitons dans la section intitulée « Révisions pivots et révisions opérationnelles », vous voudrez certainement spécifier la révision cible comme une partie de l'URL étendue suivant la syntaxe de Subversion.

 
# Extraie la branche principale à la révision 1729.
$ svn checkout http://svn.exemple.com/svn/depot/trunk@1729 trunk-1729
… 
# Extraie la branche principale actuelle telle qu'elle était
# à la révision 1729.
$ svn checkout http://svn.exemple.com/svn/depot/trunk -r 1729 trunk-1729
…
$

Enfin, si vous êtes en train de réaliser une version officielle et que vous voulez empaqueter vos fichiers et dossiers suivis en versions, vous pouvez utiliser la commande svn export pour créer une copie locale de tout ou partie de votre dépôt sans que la zone administrative .svn ne soit incluse. La syntaxe de base de cette sous-commande est identique à celle de svn checkout :

 
# Exporte la branche principale à partir de la dernière révision.
$ svn export http://svn.exemple.com/svn/depot/trunk trunk-export
… 
# Exporte la branche principale à partir de la révision 1729.
$ svn export http://svn.exemple.com/svn/depot/trunk@1729 trunk-1729
… 
# Exporte la branche principale actuelle, telle qu'elle était à la
# révision 1729.
$ svn export http://svn.example.com/svn/repo/trunk -r 1729 trunk-1729
…
$

Parfois, il suffit de faire le ménage

Maintenant que nous avons traité les tâches quotidiennes pour lesquelles vous utiliserez Subversion, nous allons passer en revue quelques tâches administratives liées à votre copie de travail.

Suppression d'une copie de travail

Subversion ne conserve sur le serveur aucune trace de l'état ni de l'existence des copies de travail, il n'y a donc aucun impact côté serveur si des copies de travail traînent un peu partout. De la même façon, pas besoin de prévenir le serveur quand vous effacez une copie de travail.

Si vous envisagez de réutiliser une copie de travail, ça ne pose aucun problème de la laisser sur le disque jusqu'à ce que vous soyez prêts à l'utiliser à nouveau et, le moment venu, il suffit de lancer svn update pour la mettre à jour et ainsi la rendre utilisable.

Cependant, si vous êtes certain de ne plus utiliser une copie de travail, vous pouvez la supprimer entièrement, en utilisant les commandes de votre système d'exploitation. Vous seriez cependant bien inspiré d'y jeter un œil avant, en lançant la commande svn status afin d'examiner tous les fichiers marqués d'un ? pour vous assurer qu'ils ne sont d'aucune importance.

Reprise après une interruption

Quand Subversion modifie votre copie de travail (ou toute information dans la zone administrative), il essaie de le faire de la manière la plus sûre possible. Avant de modifier votre copie de travail, Subversion inscrit ses intentions dans un fichier de traces. Ensuite, il exécute les commandes du fichier de traces pour appliquer les modifications demandées, en plaçant un verrou sur la partie concernée de la copie de travail pendant cette opération (pour empêcher d'autres clients Subversion d'accéder à cette copie de travail au beau milieu des changements). Pour finir, Subversion enlève son verrou et supprime le fichier de traces. D'un point de vue architectural, c'est le même fonctionnement qu'un système de fichiers journalisé. Si une opération Subversion est interrompue (par exemple si le processus est tué ou si la machine plante), le fichier de traces reste sur le disque. En exécutant de nouveau le fichier de traces, Subversion peut terminer l'opération en cours et votre copie de travail retrouve un état cohérent.

C'est exactement ce que fait la commande svn cleanup : elle trouve et exécute les fichiers de traces restant dans votre copie de travail, en enlevant les verrous au passage. Si un beau jour Subversion vous indique qu'une partie de votre copie de travail est verrouillée (locked en anglais), c'est la commande qu'il faut lancer. Par ailleurs, svn status vous informe des verrous posés dans la copie de travail, en affichant un L devant les éléments verrouillés :

$ svn status
  L    un-repertoire
M      un-repertoire/truc.c

$ svn cleanup
$ svn status
M      un-repertoire/truc.c

Ne confondez pas ces verrous agissant sur la copie de travail avec les verrous ordinaires que les utilisateurs de Subversion créent quand ils utilisent le modèle de gestion de versions parallèles verrouiller-modifier-libérer ; voir l'encadré Les différents types de « verrous » pour des éclaircissements.

Gestion des conflits d'arborescences

Jusqu'à présent, nous nous sommes occupés des conflits intéressant le contenu des fichiers. Quand vos collaborateurs et vous faites des changements qui interfèrent dans le même fichier, Subversion vous force à fusionner ces changements avant de pouvoir les propager. [9]

Mais que se passe-t-il si vos collaborateurs déplacent ou suppriment un fichier sur lequel vous êtes en train de travailler ? À la suite d'un malentendu, quelqu'un a pensé que le fichier devait être supprimé alors qu'un autre veut toujours propager des modifications sur le fichier. Ou à l'occasion d'un réusinage de code, des collaborateurs ont renommé des fichiers et déplacé des dossiers. Si vous travailliez toujours sur ces fichiers, les modifications devront sûrement s'appliquer aux fichiers à leurs nouveaux emplacements. Ces conflits, qui se manifestent au niveau de la structure des dossiers plutôt que dans le contenu des fichiers eux-mêmes, portent le nom de conflits d'arborescences.

Comme pour les conflits textuels, les conflits d'arborescences empêchent la propagation d'avoir lieu en l'état, donnant l'opportunité à l'utilisateur d'examiner les conflits qui se présentent dans la copie de travail et de les résoudre avant de réaliser la propagation.

Un exemple de conflit d'arborescences

Prenons l'exemple d'un projet logiciel sur lequel vous travaillez et qui ressemble à ceci :

$ svn list -Rv svn://svn.exemple.com/trunk/
     13 harry                 06 sept., 10:34 ./
     13 harry              27 06 sept., 10:34 COPYING
     13 harry              41 06 sept., 10:32 Makefile
     13 harry              53 06 sept., 10:34 LISEZMOI
     13 harry                 06 sept., 10:32 code/
     13 harry              54 06 sept., 10:32 code/machin.c
     13 harry             130 06 sept., 10:32 code/truc.c
$

Plus tard, lors de la révision 14, votre collaborateur Harry renomme le fichier machin.c en bidule.c. Malheureusement, vous n'y prêtez pas attention toute de suite. Vous êtes occupé à réaliser un ensemble de modifications différent, dont certaines concernent machin.c :

$ svn diff
Index: code/truc.c
===================================================================
--- code/truc.c	(revision 13)
+++ code/truc.c	(copie de travail)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("Je n'aime pas être baladé !\n%s", machin());
-    return 0;
+    return 1;
 }
Index: code/machin.c
===================================================================
--- code/machin.c	(revision 13)
+++ code/machin.c	(copie de travail)
@@ -1,4 +1,4 @@
 const char *machin(void)
 {
-    return "Moi non plus !\n";
+    return "Bah, moi j'aime bien être ballotté !\n";
 }
$

Vous commencez à réaliser que quelqu'un a modifié machin.c quand votre tentative de propagation échoue :

$ svn commit -m "Quelques petits changements."
Envoi          code/machin.c
Transmission des données .
svn: E155011: Échec de la propagation (commit), détails :
svn: E155011: Fichier '/home/svn/projet/code/machin.c' obsolète
svn: E160013: Fichier non trouvé : transaction '14-e', chemin '/code/machin.c'
$

À ce moment, vous devez lancer svn update.Non seulement votre copie de travail est mise à jour et vous pouvez voir les modifications effectuées par Harry, mais aussi l'arborescence est marquée comme étant en conflit afin que vous puissiez réellement mesurer ce qui a été modifié et résoudre le conflit proprement.

$ svn update 
Mise à jour de '.' :
   C  code/machin.c
A     code/bidule.c
U     Makefile
Actualisé à la révision 14.
Résumé des conflits :
  Arborescences en conflit : 1

Dans la sortie qu'elle produit, svn update indique un conflit d'arborescence en utilisant un C majuscule dans la quatrième colonne. svn status fournit plus de détails sur ce conflit :

$ svn status
M       code/truc.c
A  +  C code/machin.c
      >   local edit, incoming delete upon update
Résumé des conflits :
  Arborescences en conflit : 1
$

Notez que machin.c est automatiquement marqué pour ajout dans votre copie de travail, ce qui simplifie les choses si vous décidez de conserver le fichier.

Du fait qu'un déplacement dans Subversion est implémenté comme une copie suivie d'un effacement et que ces deux opérations ne peuvent pas être corrélées facilement lors d'une mise à jour, tout ce que Subversion peut vous dire c'est que la mise à jour va effacer le fichier sur lequel vous avez fait des modifications. L'effacement peut faire partie d'un déplacement ou n'être simplement qu'un effacement en tant que tel. Déterminer exactement quelle est la sémantique associée à cette modification dans le dépôt est important : vous devez savoir dans quelle mesure vos modifications s'intègrent dans l'évolution générale du projet. Lisez donc les commentaires de propagation, discutez avec vos collaborateurs, étudiez les lignes modifiées à l'aide de la visualisation « diff ». Bref, faites ce que vous avez à faire pour déterminer quelle est la conduite à tenir dans ce cas précis.

Ici, le commentaire de propagation de Harry vous donne toutes les informations utiles.

$ svn log -r14 ^/trunk
------------------------------------------------------------------------ 
r14 | harry | 2011-09-06 10:38:17 -0400 (dim. 06. sept. 2011) | 1 ligne
Chemins modifiés:
   M /Makefile
   D /code/machin.c
   A /code/bidule.c (de /code/machin.c:13)

J'ai renommé machin.c en bidule.c et modifié le Makefile en conséquence.
------------------------------------------------------------------------
$

svn info affiche les URL des éléments en conflit. L'URL de gauche indique la source locale du conflit alors que l'URL droite indique la source externe du conflit. Ces URL vous montrent où vous devez commencer à chercher les modifications qui génèrent le conflit dans l'historique du dépôt.

$ svn info code/machin.c
Chemin: code/machin.c
Nom: machin.c
URL: http://svn.exemple.com/svn/depot/trunk/code/machin.c
… 
Tree conflict: local edit, incoming delete upon update
  Source gauche: (fichier) ^/trunk/code/machin.c@4
  Source  droit: (aucun) ^/trunk/code/machin.c@5

$

On dit que machin.c est victime du conflit d'arborescence. Il ne peut pas être propagé tant que le conflit n'est pas résolu :

$ svn commit -m "Petites résolutions de problèmes"
svn: E155015: Échec de la propagation (commit), détails :
svn: E155015: Arrêt de la propagation : '/home/svn/projet/code/machin.c' demeure en conflit
$

Pour résoudre ce conflit, vous devez soit approuver soit rejeter le déplacement effectué par Harry.

Si vous approuvez le déplacement, votre machin.c est superflu. Vous pouvez alors l'effacer et marquer le conflit comme résolu. Mais attendez : vous avez effectué des modifications dans ce fichier ! Avant d'effacer machin.c, vous devez décider si les modifications que vous avez effectuées doivent être appliquées ailleurs, par exemple dans le nouveau fichier bidule.c, qui contient en effet tout l'ancien code de machin.c. Considérons que vos modifications doivent « suivre le mouvement ». Subversion n'est pas assez perfectionné pour faire ce travail à votre place[10], vous devez donc faire ces modifications manuellement.

Dans notre exemple, vous pourriez manuellement refaire vos modifications sur machin.c assez facilement — ce n'était qu'un seul changement de ligne après tout. Cependant, ce n'est pas toujours le cas, c'est pourquoi nous allons voir une approche qui peut s'appliquer sur de plus gros changements. Nous commencerons par utiliser svn diff pour générer un fichier patch. Ensuite, nous allons modifier les entêtes de ce fichier patch pour pointer vers le nouveau nom de notre fichier déplacé. Finalement, nous appliquons à nouveau le fichier patch modifié sur la copie de travail.

 
$ svn diff code/machin.c > FICHIER_CORRECTIF
$ cat FICHIER_CORRECTIF
Index: code/machin.c
===================================================================
--- code/machin.c	(révision 14)
+++ code/machin.c	(copie de travail)
@@ -1,4 +1,4 @@
 const char *machin(void)
 {
-    return "Moi non plus!\n";
+    return "Bah, moi j'aime bien être ballotté !\n";
 }
$ ### Editer FICHIER_CORRECTIF pour pointer vers code/bidule.c au lieu de code/machin.c

$ cat FICHIER_CORRECTIF
Index: code/bidule.c
===================================================================
--- code/bidule.c	(révision 14)
+++ code/bidule.c	(copie de travail)
@@ -1,4 +1,4 @@
 const char *machin(void)
 {
-    return "Moi non plus!\n";
+    return "Bah, moi j'aime bien être ballotté !\n";
 }
$ svn patch FICHIER_CORRECTIF
U         code/bidule.c
$

Maintenant que les changements que vous aviez faits originellement sur machin.c ont été correctement reproduits dans bidule.c, vous pouvez effacer machin.c et résoudre le conflit en indiquant que vous choisissez la version de la copie de travail comme valide.

 
$ svn delete --force code/machin.c
D         code/machin.c
$ svn resolve --accept=working code/machin.c
Conflit sur 'code/machin.c' résolu
$ svn status
M       code/bidule.c
M       code/truc.c
$ svn diff
Index: code/truc.c
===================================================================
--- code/truc.c  (revision 14)
+++ code/truc.c  (working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("Je n'aime pas être baladé !\n%s", machin());
-    return 0;
+    return 1;
 }
Index: code/bidule.c
===================================================================
--- code/bidule.c  (revision 14)
+++ code/bidule.c  (working copy)
@@ -1,4 +1,4 @@
 const char *machin(void)
 {
-    return "Moi non plus!\n";
+    return "Bah, moi j'aime bien être ballotté !\n";
 }
$

Mais que se passe-t-il si vous n'êtes pas d'accord avec le déplacement ? Eh bien, dans ce cas, vous pouvez effacer bidule.c après vous être assuré que celui-ci ne contient pas de modification postérieure à son renommage que vous souhaiteriez conserver (n'oubliez pas non plus de revenir sur les modifications effectuées par Harry sur Makefile). Comme machin.c est déjà prévu d'être ajouté lors de la prochaine propagation, il n'y a rien de plus à faire et le conflit peut être marqué comme résolu :

$ svn delete --force code/bidule.c
D         code/bidule.c
$ svn resolve --accept=working code/machin.c
Conflit sur 'code/machin.c' résolu
$ svn status
M       code/truc.c
A  +    code/machin.c
D       code/bidule.c
M       Makefile
$ svn diff
Index: code/truc.c
===================================================================
--- code/truc.c	(revision 14)
+++ code/truc.c	(copie de travail)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("Je n'aime pas être baladé !\n%s", machin());
-    return 0;
+    return 1;
 }
Index: code/machin.c
===================================================================
--- code/machin.c	(revision 14)
+++ code/machin.c	(copie de travail)
@@ -1,4 +1,4 @@
 const char *machin(void)
 {
-    return "Moi non plus!\n";
+    return "Bah, moi j'aime bien être ballotté !\n";
 }
Index: code/bidule.c
===================================================================
--- code/bidule.c	(revision 14)
+++ code/bidule.c	(copie de travail)
@@ -1,4 +0,0 @@
-const char *machin(void)
-{
-    return "Moi non plus!\n";
-}
Index: Makefile
===================================================================
--- Makefile	(revision 14)
+++ Makefile	(copie de travail)
@@ -1,2 +1,2 @@
 truc:
-	$(CC) -o $@ code/truc.c code/bidule.c
+	$(CC) -o $@ code/truc.c code/machin.c

Vous avez résolu votre premier conflit d'arborescence. Vous pouvez propager vos modifications et vous plaindre auprès de Harry à la pause café à propos de la surcharge de travail qu'il vous a infligée.

Résumé

Nous en avons maintenant terminé avec la plupart des commandes du client Subversion. Les exceptions notables concernent les branches et la fusion (voir le Chapitre 4, Gestion des branches) ainsi que les propriétés (voir le la section intitulée « Propriétés »). Cependant, prenez le temps de parcourir le Guide de référence de svn : le client texte interactif pour vous faire une idée de toutes les commandes de Subversion et de la manière dont vous pouvez les utiliser pour rendre votre travail plus convivial.




[6] Si vous avez l'intention de recouvrer cet élément afin qu'il soit de nouveau présent dans HEAD, lisez la section intitulée « Résurrection des éléments effacés ».

[7] Et si vous commandez ça, on vous chassera de la ville à coup de baguette rassie.

[8] Vous voyez, on vous avait bien dit que Subversion était une machine à remonter le temps.

[9] Certes, vous pouvez simplement marquer les fichiers en conflits comme résolus et les propager, si vous le voulez vraiment. Mais c'est rarement le cas en pratique.

[10] Dans certains cas, il arrive que Subversion 1.5 ou 1.6 fasse le transfert pour vous, mais cette fonctionnalité un peu « ça passe ou ça casse » a été enlevée dans Subversion 1.7.

Chapitre 3. Sujets avancés

Si vous lisez ce livre chapitre par chapitre, du début à la fin, vous avez acquis maintenant suffisamment de connaissance du fonctionnement de Subversion pour effectuer les opérations les plus courantes de gestion de versions. Vous savez comment extraire une copie de travail du dépôt Subversion. Vous n'avez aucune difficulté à propager vos modifications et à recevoir des mises à jour en utilisant les commandes svn commit et svn update. Vous avez probablement acquis le réflexe, presque inconscient, de lancer la commande svn status. Bref, vous êtes apte à utiliser Subversion dans un environnement normal pour tout type de projet.

Mais les fonctionnalités de Subversion ne s'arrêtent pas aux « opérations courantes de gestion de versions ». Il possède d'autres atouts, en plus de permettre simplement le partage de fichiers et de dossiers depuis un dépôt central.

Ce chapitre dévoile certaines fonctionnalités de Subversion qui, bien qu'importantes, ne sont pas d'une utilisation quotidienne pour un utilisateur normal. Nous supposons que vous êtes familier avec les possibilités de base de gestion de versions sur les fichiers et dossiers. Sinon, reportez-vous au Chapitre 1, Notions fondamentales et au Chapitre 2, Utilisation de base. Une fois que vous maîtriserez ces bases et que vous aurez assimilé ce chapitre, vous serez un super-utilisateur de Subversion !

Identifiants de révisions

Comme vous avez pu le constater dans la section intitulée « Révisions », les numéros de révision dans Subversion sont d'une grande simplicité, formant une suite d'entiers incrémentés au fur et à mesure des changements propagés dans le dépôt. Néanmoins, il ne faudra pas longtemps avant que vous ne puissiez plus vous rappeler exactement quel changement correspond à quelle révision. Heureusement, le fonctionnement normal de Subversion ne requiert pas souvent que vous fournissiez explicitement un numéro de révision pour une opération. Pour les opérations qui nécessitent vraiment un numéro de révision, c'est généralement un numéro de révision que vous avez vu soit dans un mail de propagation, soit dans la sortie d'une autre opération Subversion, soit dans un autre contexte où ce numéro possédait une signification particulière.

[Note]Note

Faire référence aux numéros de révision avec le préfixe « r » (par exemple r314) est d'un usage courant dans la communauté Subversion et est accepté voire encouragé par de nombreux outils en relation avec Subversion. Dans la plupart des cas où vous spécifiez un numéro de révision dans la ligne de commande, vous pouvez tout aussi bien utliser la syntaxe rNNN.

À l'occasion, vous aurez besoin d'indiquer un moment précis dans le temps pour lequel vous n'avez pas encore le numéro de révision sous la main ou en mémoire. C'est pourquoi, en sus des numéros de révision, la commande svn autorise d'autres formes d'appellations pour les révisions : les mots-clés de révision et les dates de révision.

[Note]Note

Les différentes formes d'appellations pour les révisions peuvent être mélangées et comparées pour définir des intervalles de révisions. Par exemple, vous pouvez spécifier -r REV1:REV2REV1 est un mot-clé de révision et REV2 est un numéro de révision, ou bien où REV1 est une date et REV2 est un numéro de révision. Comme chaque appellation de révision est évaluée indépendamment, vous pouvez placer n'importe quel type d'appellation de chaque côté du symbole deux-points.

Mots-clés de révision

Le client Subversion accepte une grande variété de mots-clés de révision. En tant qu'argument de l'option --revision (-r) ces mots-clés peuvent être utilisés en lieu et place des numéros et sont remplacés par les numéros correspondants par Subversion :

HEAD

La dernière (c'est-à-dire la plus récente) révision présente dans le dépôt.

BASE

Le numéro de révision d'un élément de la copie de travail. Si l'élément a été modifié localement, la « version BASE » fait référence à l'élément tel qu'il était sans ces modifications locales.

COMMITTED

La révision la plus récente avant (ou égale à) BASE, dans laquelle un élément a changé.

PREV

La révision précédant immédiatement la dernière révision dans laquelle un élément a changé. Techniquement, cela revient à COMMITTED−1.

Comme vous pouvez le deviner d'après leur description, les mots-clés de révision PREV, BASE et COMMITTED ne sont utilisés que pour faire référence à un chemin dans la copie de travail ; ils ne s'appliquent pas à des URL du dépôt. En revanche, HEAD peut être utilisé avec les deux types de chemin (local ou URL du dépôt).

Vous trouvez ci-dessous des exemples de l'utilisation de ces mots-clés :

$ svn diff -r PREV:COMMITTED machin.c
# affiche le dernier changement propagé concernant machin.c

$ svn log -r HEAD
# affiche le commentaire associé à la dernière propagation dans le dépôt.

$ svn diff -r HEAD
# compare votre copie de travail (avec tous ses changements locaux)
# à la dernière version de l'arborescence correspondante du dépôt.

$ svn diff -r BASE:HEAD machin.c
# compare la version non modifiée localement de machin.c avec la dernière
# version de machin.c dans le dépôt.

$ svn log -r BASE:HEAD
# affiche, pour le dossier suivi en versions courant, les commentaires
# de propagation depuis la dernière mise à jour (svn update).

$ svn update -r PREV machin.c
# revient une version en arrière pour le fichier machin.c. Ceci diminue
# de un la révision de la version de travail du fichier machin.c.

$ svn diff -r BASE:14 machin.c
# compare la version non modifiée localement de machin.c avec
# la version de ce fichier à la révision 14.


Dates de révision

Les numéros de révision n'ont aucune signification en dehors du système de gestion de versions. Cependant, parfois, vous avez besoin d'associer une date réelle à un moment précis de l'historique des versions. À cette fin, l'option --revision (-r) accepte comme argument une date placée entre accolades ({ et }). Subversion accepte les dates et les heures aux formats définis dans le standard ISO-8601 ainsi que quelques autres formats. Voici quelques exemples :

$ svn update -r {2006-02-17}
$ svn update -r {15:30}
$ svn update -r {15:30:00.200000}
$ svn update -r {"2006-02-17 15:30"}
$ svn update -r {"2006-02-17 15:30 +0230"}
$ svn update -r {2006-02-17T15:30}
$ svn update -r {2006-02-17T15:30Z}
$ svn update -r {2006-02-17T15:30-04:00}
$ svn update -r {20060217T1530}
$ svn update -r {20060217T1530Z}
$ svn update -r {20060217T1530-0500}
…
[Note]Note

Gardez à l'esprit que la plupart des interpréteurs de commandes (shells) requièrent de mettre les dates qui contiennent des espaces entre guillemets ou « d'échapper » les espaces. Certains interpréteurs peuvent aussi poser problème avec les accolades si elles ne sont pas échappées. Consulter la documentation de votre interpréteur pour connaître les spécificités de votre environnement.

Quand vous spécifiez une date, Subversion convertit cette date vers le numéro de révision le plus récent du dépôt à la date spécifiée. Puis, il continue son travail avec ce numéro de révision :

$ svn log -r {2006-11-28}
------------------------------------------------------------------------ 
r12 | ira | 2006-11-27 12:31:51 -0600 (lun. 27 nov. 2006) | 6 lignes
…

Vous pouvez également utiliser des intervalles de dates. Subversion trouve alors les révisions incluses entre ces deux dates :

$ svn log -r {2006-11-20}:{2006-11-29}
…
[Avertissement]Avertissement

L'aptitude de Subversion à convertir correctement les horodatages de révisions en numéros de révision repose sur le fait que les horodatages de révisions sont ordonnées de manière croissante (plus le numéro est élevé, plus l'horodatage est récent). Mais l'horodatage d'une révision étant stocké comme une propriété modifiable et non suivie en versions de la révision (reportez-vous à la section intitulée « Propriétés »), les horodatages peuvent être modifiés et ne plus suivre la chronologie réelle. Aujourd'hui, cela n'affecte pas la plupart des opérations de Subversion (c'est le numéro de révision qui est l'identifiant primaire d'une révision). Mais si l'ordonnacement des horodatages n'est pas maintenu, il y a de grandes chances que l'utilisation des dates pour spécifier des intervalles de révisions dans votre dépôt ne fournisse pas les résultats attendus. Combiner les historiques de plusieurs dépôts pour n'en former qu'un (tel que décrit dans la section intitulée « Migration des données d'un dépôt ») est la cause principale de ce type de situation.

Révisions pivots et révisions opérationnelles

Continuellement, nous copions, déplaçons, renommons et remplaçons des fichiers et des dossiers sur nos ordinateurs. Et votre système de gestion de versions ne doit pas être un obstacle à ces opérations sur les fichiers et dossiers suivis en versions. La gestion des fichiers par Subversion se fait pratiquement oublier, étant presque aussi flexible pour les fichiers suivis en versions que pour les autres. Mais cette flexibilité signifie qu'au cours de la vie de votre dépôt un objet suivi en versions a un certain nombre de chemins et qu'un chemin donné peut représenter plusieurs objets suivis en versions tout à fait différents. Cela ajoute un niveau de complexité supplémentaire dans les actions sur les chemins et les objets.

Subversion est plutôt adroit pour détecter les « changements d'adresses » dans l'historique du suivi de versions d'un objet. Par exemple, si vous demandez l'historique d'un fichier qui a été renommé la semaine dernière, Subversion fournit ce journal :la révision dans laquelle s'est produit le changement de nom et les journaux pertinents avant et après ce renommage. Ainsi, la plupart du temps, vous n'avez pas à vous préoccuper de ces opérations. Mais il arrive que Subversion ait besoin de votre aide pour lever des ambiguïtés.

L'exemple correspondant le plus simple est quand un fichier ou un dossier est supprimé du suivi de versions, puis qu'un nouveau dossier ou fichier est créé avec le même nom et ajouté au suivi de versions. L'objet qui a été effacé et celui qui a été ajouté plus tard ne sont pas les mêmes. Ils se trouve qu'ils ont juste le même chemin (/trunk/objet par exemple). Que signifie alors de demander à Subversion l'historique de /trunk/objet ? La question concerne-t-elle l'objet actuellement à cet emplacement ou l'objet précédent qui a été supprimé ? Ou encore les opérations sur tous les objets qui ont résidé à cet emplacement ? Subversion a besoin de savoir ce que vous demandez réellement.

Et, en raison des déplacements, l'historique des objets suivis en versions peut être beaucoup plus tordu que cela. Par exemple, vous pouvez avoir un dossier appelé concept, contenant une ébauche de projet logiciel sur lequel vous vous êtes essayé. Il se peut que ce projet mûrisse et que l'idée soit pertinente au point que, chose inimaginable, vous décidiez de donner un nom au projet [11]. Imaginons que vous nommiez ce logiciel Frabnaggilywort. Il semble alors logique de renommer le dossier concept en frabnaggilywort pour refléter le nom du projet. L'eau coule sous les ponts et Frabnaggilywort sort en version 1.0, est téléchargé et utilisé quotidiennement par des tonnes de gens qui veulent se faciliter la vie.

Quelle belle histoire ! Mais elle ne s'arrête pas là. Comme vous avez une âme d'entrepreneur, vous avez déjà une autre idée derrière la tête : vous créez donc un nouveau dossier concept et la boucle est bouclée. En fait, ce cycle recommence plusieurs fois au fil du temps, à chaque fois à partir de ce vieux dossier concept qui, quelquefois, est renommé, quand l'idée plaît et, d'autres fois, est effacé quand l'idée ne convient pas. En plus, pour être réellement tordu, vous donnez parfois à concept un autre nom temporaire, puis renommez ce même dossier concept pour une raison quelconque.

Avec de tels scénarios, demander à Subversion d'apprendre à travailler avec ces renommages multiples est un peu comme dire à un automobiliste de la banlieue de prendre la direction de Paris et de prendre à gauche sur « la rue du Château  » : il croisera la rue du Château à Asnières, La Garenne-Colombes, Nanterre, Neuilly, Rueil-Malmaison, … et, non, ce n'est pas la même rue à chaque fois. De la même manière, Subversion a besoin d'un peu plus de précisions pour travailler correctement.

Heureusement, Subversion vous permet de lui indiquer de quelle rue du Château vous parlez exactement. Le mécanisme utilisé s'appelle les révisions pivots et vous les fournissez à Subversion uniquement pour identifier de manière unique une ligne de l'historique. Comme il y a au plus un objet suivi en versions à un endroit et à un moment donnés (ou plus précisément à une révision donnée), la combinaison d'un chemin et d'une révision pivot est tout ce dont vous avez besoin pour désigner une ligne spécifique de l'historique. Les révisions pivots sont indiquées au client texte interactif Subversion en utilisant la notation at (on l'appelle ainsi parce que la syntaxe de la commande utilise le signe « arobase » :@) suivi de la révision pivot demandée, en fin de chemin.

Mais alors qu'en est-il de l'option --revision (-r) dont nous avons tant parlé dans ce livre ? Cette révision (ou ensemble de révisions) est appelée la révision opérationnelle (ou intervalle de révisions opérationnelles). Une fois qu'une ligne particulière de l'historique a été identifiée en utilisant un chemin et une révision pivot, Subversion effectue la requête en utilisant la révision opérationnelle (ou l'intervallle de révisions opérationnelles). Pour reprendre notre analogie avec les rues françaises, si on vous dit d'aller au 15 de la rue du Château à Rueil-Malmaison [12], vous pouvez penser que « la rue du Château » est le chemin dans le système de fichiers et « Rueil-Malmaison » la révision pivot. Ces deux informations identifient de manière unique une route donnée et vous évitent de parcourir une autre rue du Château à la recherche de votre destination finale. Maintenant, vous pouvez rechercher le « 15 » comme numéro de révision opérationnelle puisque nous savons exactement où aller.

Supposons que nous ayons créé notre dépôt il y a longtemps et que dans la révision 1 nous ayons ajouté notre premier dossier concept ainsi qu'un fichier IDÉE, situé dans ce dossier, contenant la description du concept. Nous avons ensuite ajouté et modifié de véritables lignes de code. À la révision 20, nous avons renommé ce dossier en frabnaggilywort. Lors de la révision 27, nous développons un nouveau concept et un nouveau dossier concept est créé pour l'héberger, avec un nouveau fichier IDÉE pour le décrire. Cinq ans et vingt mille révisions passent, comme dans tout bon roman d'amour.

À présent, plusieurs années plus tard, nous nous demandons à quoi ressemblait le fichier IDÉE en révision 1. Mais Subversion a besoin de savoir si nous demandons à quoi ressemble le fichier actuel tel qu'il était lors de la révision 1 ou si nous demandons le contenu du fichier positionné dans l'arborescence à concept/IDÉE au moment de la révision 1. Ces questions ont certainement des réponses différentes et grâce aux révisions pivots, nous pouvons poser ces deux questions. Pour obtenir le contenu du fichier IDÉE actuel tel qu'il était dans cette vieille révision, tapez :

$ svn cat -r 1 concept/IDÉE
svn: E195012: Impossible de trouver la localisation dans le dépôt de 'concept/IDÉE' pour la révision 1

Bien sûr, dans cet exemple, le fichier IDÉE actuel n'existait pas lors de la révision 1, c'est pourquoi Subversion renvoie une erreur. La commande ci-dessus est un raccourci pour la notation plus longue qui explicite la révision pivot. La notation complète est donc :

$ svn cat -r 1 concept/IDÉE@BASE
svn: E195012: Impossible de trouver la localisation dans le dépôt de 'concept/IDÉE' pour la révision 1

On obtient bien le résultat attendu.

Le lecteur perspicace est certainement en train de se demander si la syntaxe des révisions pivots ne pose pas de problèmes pour les chemins ou les URL qui comportent déjà le signe arobase. Après tout, comment svn peut-il savoir si nouveau@11 est le nom d'un dossier dans mon arborescence ou juste la syntaxe pour « révision 11 de nouveau » ? Dieu merci, alors que svn opte par défaut pour cette dernière hypothèse, il existe une solution de contournement triviale : il suffit juste d'ajouter un signe arobase à la fin du chemin, comme ceci : nouveau@11@. svn ne s'intéresse qu'au dernier arobase de l'argument et il n'est pas considéré comme illégal d'omettre le spécificateur de révision pivot après cet arobase. Cette solution de contournement s'applique même aux chemins qui se terminent par arobase (utilisez nom-du-fichier@@ pour désigner le fichier nom-du-fichier@.

Posons maintenant l'autre question : dans la révision 1, quel était le contenu du fichier qui occupait l'adresse concept/IDÉE à ce moment là ? Nous allons utiliser explicitement une révision pivot pour nous aider :

$ svn cat concept/IDÉE@1
L'idée de ce projet est de fournir un logiciel qui peut frabber un
naggily wort. Frabber les naggilys worts est particulièrement difficile
et ne pas le faire correctement aurait des conséquences inimaginables.
Nous devons donc utiliser des mécanismes de vérification des
entrées et des données du dernier cri.

Remarquez que cette fois nous n'avons pas fourni de révision opérationnelle. C'est parce que, quand aucune révision opérationnelle n'est spécifiée, Subversion considère que le numéro de révision opérationnelle est égal au numéro de révision pivot.

Comme vous pouvez le constater, la résultat de la commande semble être correct. Le texte parle même de "frabber les naggilys worts", ce qui laisse supposer que c'est certainement le fichier décrivant le logiciel maintenant connu sous le nom de Frabnaggilywort. En fait, on peut le vérifier en combinant une révision pivot explicite et une révision opérationnelle explicite. Nous savons que dans HEAD, le projet Frabnaggilywort se situe dans le dossier frabnaggilywort. Nous spécifions donc que nous voulons voir à quoi ressemblait le fichier frabnaggilywort/IDÉE identifié dans HEAD au moment de la révision 1.

$ svn cat -r 1 frabnaggilywort/IDÉE@HEAD
L'idée de ce projet est de fournir un logiciel qui peut frabber un
naggily wort. Frabber les naggilys worts est particulièrement difficile
et ne pas le faire correctement aurait des conséquences inimaginables.
Nous devons donc utiliser des mécanismes de vérification des
entrées et des données du dernier cri.

Vous pouvez aussi spécifier des révisions pivots et des révisions opérationnelles moins triviales. Par exemple, disons que frabnaggilywort a été effacé de HEAD, mais nous savons qu'il existait en révision 20 et nous voulons voir les différences entre la révision 4 et la révision 10 pour son fichier IDÉE. Nous pouvons utiliser la révision pivot 20 en conjonction avec l'URL qu'avait le fichier frabnaggilywort/IDÉE dans la révision 20 et utiliser 4 et 10 pour spécifier l'intervalle de révisions opérationnelles.

 
$ svn diff -r 4:10 http://svn.red-bean.com/projets/frabnaggilywort/IDÉE@20
Index: frabnaggilywort/IDÉE
===================================================================
--- frabnaggilywort/IDÉE	(révision 4)
+++ frabnaggilywort/IDÉE	(révision 10)
@@ -1,5 +1,5 @@
-L'idée de ce projet est de fournir un logiciel qui peut frabber un
-naggily wort. Frabber les naggilys worts est particulièrement difficile
-et ne pas le faire correctement aurait des conséquences inimaginables.
-Nous devons donc utiliser des mécanismes de vérification des
-entrées et des données du dernier cri.
+L'idée de ce projet est de fournir un logiciel client-serveur qui peut
+frabber un naggily wort de manière distante. Frabber les naggilys worts
+est particulièrement difficile et ne pas le faire correctement aurait
+des conséquences inimaginables. Nous devons donc utiliser des mécanismes
+de vérification des entrées et des données du dernier cri.

Heureusement, la plupart d'entre vous n'auront pas à faire face à des situations aussi complexes. Mais si jamais c'est le cas, rappelez-vous que les révisions pivots sont les informations complémentaires dont a besoin Subversion pour lever toute ambiguïté.

Propriétés

Nous avons vu en détail comment Subversion stocke et récupère les différentes versions des fichiers et dossiers dans le dépôt. Des chapitres entiers ont décrit cette fonctionnalité fondamentale de l'outil. Et si la gestion de versions se limitait à ça, Subversion couvrirait déjà complètement les besoins attendus pour un système de gestion de versions.

Mais ce n'est pas tout.

En plus de gérer les versions de vos dossiers et de vos fichiers, Subversion fournit une interface pour ajouter, modifier et supprimer des méta-données suivies en versions pour chacun de vos dossiers et de vos fichiers. On appelle ces méta-données des propriétés. Elles peuvent être pensées comme des tableaux à deux colonnes, qui associent des noms de propriétés à des valeurs arbitraires, pour chaque élément de votre copie de travail. En termes simples, vous pouvez assigner n'importe quel nom et n'importe quelle valeur à vos propriétés, à la seule condition que le nom ne contiennent que des caractères ASCII. Et l'atout principal de ces propriétés réside dans le fait que ces propriétés sont également suivies en versions, tout comme le contenu textuel de vos fichiers. Vous pouvez modifier, propager et revenir en arrière sur les propriétés aussi facilement que sur le contenu des fichiers. L'envoi et la réception des changements concernant les propriétés intervient lors de vos propagations et mises à jour : vous n'avez pas à changer vos habitudes pour les utiliser.

[Note]Note

Subversion a réservé pour son propre usage les propriétés dont le nom commence par svn:. Bien qu'il n'y en ait seulement que quelques unes d'utilisées actuellement, vous ne devez pas créer vos propres propriétés avec un nom commençant par ce préfixe. Sinon, vous courez le risque qu'une future version de Subversion définisse une propriété ayant le même nom mais pour un usage tout autre.

Les propriétés sont aussi présentes ailleurs dans Subversion. De la même manière que pour les fichiers et dossiers, chaque révision en tant que telle peut avoir des propriétés arbitraires associées. Les mêmes contraintes s'appliquent : nom lisible par un humain et valeur arbitraire, éventuellement binaire. La différence principale est que les propriétés des révisions ne sont pas suivies en versions. Autrement dit, si vous changez la valeur ou si vous supprimez une propriété d'une révision, il n'y a pas moyen, en utilisant Subversion, de revenir à la valeur précédente.

Subversion ne fournit pas de recommandation particulière quant à l'utilisation des propriétés. Il demande seulement de ne pas utiliser de nom de propriété qui commence par le préfixe svn:. C'est l'espace de noms qu'il garde pour son propre usage. Et Subversion utilise bien lui-même les propriétés, suivies en versions ou pas. Certaines propriétés suivies en versions ont une signification particulière ou des effets particuliers quand elles font référence à un fichier ou à un dossier, ou stockent des informations relatives à la révision à laquelle elles sont rattachées. Certaines propriétés de révision sont automatiquement rattachées à une révision par la procédure de propagation et stockent des informations relatives à cette révision. La plupart de ces propriétés sont mentionnées ailleurs dans ce chapitre ou dans d'autres chapitres comme faisant partie de sujets plus généraux. Pour une liste exhaustive des propriétés pré-définies de Subversion, référez-vous à la section intitulée « Propriétés réservées à l'usage de Subversion ».

[Note]Note

Bien que Subversion crée automatiquement des propriétés à chaque révision (svn:date, svn:author, svn:log, etc.), il ne présage pas de leur existence par la suite et vous (ou les outils que vous utilisez) ne devriez pas non plus présager de leur existence dans vos interactions avec le dépôt. Les propriétés de révisions peuvent être effacées par programmation ou via le client (si les procédures automatiques l'autorisent) sans remettre en cause le bon fonctionnement de Subversion. En conséquence, lors de l'écriture de scripts qui opèrent sur les données du dépôt, veillez à ne pas considérer l'existence d'une propriété de révision comme acquis.

Dans cette section, nous examinons l'utilité des propriétés, à la fois pour l'utilisateur et pour Subversion lui-même. Vous apprendrez les sous-commandes svn relatives aux propriétés et comment la modification des propriétés change votre manière habituelle d'utiliser Subversion.

Utilisation des propriétés

À l'instar de Subversion, qui utilise les propriétés pour stocker des méta-données sur les fichiers, les dossiers et les révisions qu'il gère, vous pouvez faire une utilisation similaire des propriétés. Vous pouvez trouver utile d'avoir un endroit, près de vos données suivies en versions, pour stocker des méta-données relatives à vos données.

Imaginons que vous vouliez créer un site Web qui héberge beaucoup de photos et qui les affiche avec une légende et une date. D'accord, mais votre collection de photos change constamment, donc vous voulez automatiser le plus possible la gestion du site. Ces photos peuvent être relativement volumineuses et vous voulez pouvoir fournir des miniatures à vos visiteurs, comme c'est généralement le cas sur ce genre de site.

Certes, vous pouvez le faire en utilisant des fichiers traditionnels. C'est-à-dire que vous avez votre image123.jpg et une image123-thumbnail.jpg côte à côte dans un dossier. Ou, si vous voulez garder les mêmes noms de fichier, vous placez vos miniatures dans un dossier différent, comme thumbnails/image123.jpg. Vous pouvez également stocker vos légendes et dates de la même façon, séparées encore une fois du fichier image original. Mais le problème est que votre collection de fichiers s'agrandit de plusieurs fichiers à chaque nouvelle photo ajoutée au site.

Maintenant, considérons le même site Web conçu en utilisant les propriétés des fichiers fournies par Subversion. Imaginez un simple fichier image, image123.jpg, et un ensemble de propriétés relatives à ce fichier nommées légende, date et même miniature. À présent, le dossier de votre copie de travail se gère beaucoup plus facilement ; en fait, vu du navigateur, il semble ne contenir que des images. Mais vos scripts d'automatisation vont plus loin : ils savent qu'ils peuvent utiliser les commandes svn (ou mieux, ils peuvent utiliser les connecteurs spécifiques au langage utilisé, voir la section intitulée « Utilisation des API ») pour extraire les informations dont votre site a besoin sans avoir à lire un fichier d'index ou à jouer avec des chemins de fichiers.

[Note]Note

Bien que Subversion n'impose que peu de restrictions sur les noms et les valeurs des propriétés, il n'a pas été conçu pour gérer de façon optimale des valeurs de propriétés de grande taille ou un grand nombre de propriétés sur un fichier ou un dossier donné. Subversion garde souvent en mémoire en même temps tous les noms et valeurs de propriétés associés à un élément, ce qui peut engendrer des problèmes de performance lors de l'utilisation de très gros ensembles de propriétés.

On utilise également fréquemment des propriétés de révisions personnalisées. Une utilisation classique est d'avoir une propriété qui contient un identifiant en provenance d'un autre outil de gestion et de l'associer à une révision. Par exemple, l'outil de gestion est utilisé pour suivre les bogues et la révision corrige le bogue associé à l'identifiant. Il s'agit parfois aussi d'utiliser des noms plus conviviaux pour les révisions : il peut être difficile de se remémorer que la révision 1935 correspond à une révision qui a subi la totalité des tests, alors qu'une propriété resultat-des-tests avec la valeur tout ok est autrement plus utile.

$ svn commit -m "Corrige la dernière régression connue." \
             --with-revprop "resultat-des-tests=tout ok"
Envoi         lib/crit_bits.c
Transmission des données .
Révision 912 propagée.
$

Manipuler les propriétés

La commande svn offre différentes possibilités pour ajouter ou modifier des propriétés sur les fichiers et les dossiers. Pour les propriétés avec des valeurs courtes, lisibles par un humain, la solution la plus simple est sûrement de spécifier le nom de la propriété et sa valeur en ligne de commande avec la sous-commande svn propset :

$ svn propset copyright '(c) 2006 Red-Bean Software' calc/bouton.c
Propriété 'copyright' définie sur 'calc/bouton.c'
$

Mais nous avons vanté la souplesse de Subversion pour spécifier les valeurs des propriétés. Ainsi, si vous envisagez d'avoir des valeurs de plusieurs lignes de texte, ou même une valeur binaire, la passer en ligne de commande ne vous convient pas. La sous-commande svn propset accepte donc l'option --file (-F) pour spécifier le nom d'un fichier qui contient la nouvelle valeur de la propriété.

$ svn propset licence -F /chemin/vers/LICENCE calc/bouton.c
Propriété 'licence' définie sur 'calc/bouton.c'
$

Il y a quelques restrictions sur les noms de propriétés. Un nom de propriété doit commencer par une lettre, le caractère deux points (:), ou le caractère souligné (_) ; ensuite, vous pouvez aussi utiliser des chiffres, des tirets (-) et des points (.) [13].

En plus de la commande propset, svn dispose de la commande propedit. Cette commande utilise l'éditeur de texte pré-configuré (reportez-vous à la section intitulée « Configuration générale ») pour ajouter ou modifier des propriétés. Quand vous exécutez la commande, svn lance votre éditeur de texte avec un fichier temporaire qui contient la valeur actuelle de la propriété (ou un contenu vierge si vous ajoutez une nouvelle propriété). Vous pouvez alors modifier la valeur dans l'éditeur de texte pour y placer votre nouvelle valeur, sauvegarder le fichier temporaire et quitter l'éditeur. Si Subversion détecte que la valeur a effectivement changé, il la prend en compte. Si vous quittez l'éditeur sans faire de changement, la propriété n'est pas modifiée :

$ svn propedit copyright calc/bouton.c  ### sortez de l'éditeur sans faire de modification
Pas de modification de la propriété 'copyright' sur 'calc/bouton.c'
$

Vous pouvez noter que, à l'instar des autres commandes svn, celles relatives aux propriétés fonctionnent aussi sur des chemins multiples. Vous pouvez ainsi modifier les propriétés d'un ensemble de fichiers en une seule commande. Par exemple, nous aurions pu taper :

$ svn propset copyright '(c) 2006 Red-Bean Software' calc/* 
Propriété 'copyright' définie sur 'calc/Makefile'
Propriété 'copyright' définie sur 'calc/bouton.c'
Propriété 'copyright' définie sur 'calc/entier.c'
…
$

Toutes ces manipulations de propriétés ne seraient pas vraiment utiles si vous ne pouviez pas récupérer facilement la valeur d'une propriété. Subversion propose donc deux sous-commandes pour afficher les noms et les valeurs des propriétés associées aux fichiers et dossiers. La commande svn proplist fournit la liste des noms de propriétés qui existent dans un chemin. Une fois que vous connaissez les noms des propriétés d'un élément, vous pouvez obtenir les valeurs correspondantes avec la commande svn propget. Cette commande affiche sur la sortie standard la valeur de la propriété dont le nom et le chemin (ou l'ensemble des chemins) ont été passés en paramètres.

$ svn proplist calc/bouton.c
Propriétés sur 'calc/bouton.c':
  copyright
  licence
$ svn propget copyright calc/bouton.c
(c) 2006 Red-Bean Software
$

Il y a même une variante de la commande proplist qui liste à la fois le nom et la valeur de toutes les propriétés. Ajoutez simplement l'option --verbose (-v) à la commande :

$ svn proplist -v calc/bouton.c
Propriétés sur 'calc/bouton.c':
  copyright
    (c) 2006 Red-Bean Software
  license
    ================================================================
    Copyright (c) 2006 Red-Bean Software.  All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions, and the recipe for Fitz's famous
    red-beans-and-rice.
    …

La dernière sous-commande relative aux propriétés est propdel. Puisque Subversion vous autorise à stocker des propriétés avec une valeur vide, vous ne pouvez pas supprimer une propriété en utilisant svn propedit ou svn propset. Par exemple, la commande suivante ne produit pas le résultat escompté :

$ svn propset licence calc/bouton.c
Propriété 'licence' définie sur 'calc/bouton.c' 
$ svn proplist -v calc/bouton.c
Propriétés sur 'calc/bouton.c':
  copyright
    (c) 2006 Red-Bean Software
  license

$

Vous devez utiliser la sous-commande propdel pour supprimer complètement une propriété. La syntaxe est similaire aux autres commandes sur les propriétés :

$ svn propdel license calc/bouton.c
Propriété 'licence' supprimée de 'calc/bouton.c'.
$ svn proplist -v calc/bouton.c
Propriétés sur 'calc/bouton.c':
  copyright
    (c) 2006 Red-Bean Software
$

Vous souvenez-vous des propriétés de révision non suivies en versions ? Vous pouvez les modifier elles-aussi en utilisant les mêmes sous-commandes svn que nous venons de décrire. Il suffit juste d'ajouter l'option --revprop au client texte interactif et de spécifier la révision à laquelle s'applique la modification. Puisque les numéros de révisions s'appliquent à l'ensemble de l'arborescence, vous n'avez pas besoin d'indiquer un chemin pour ces commandes, du moment que vous êtes dans une copie de travail du dépôt contenant la révision dont vous voulez modifier la propriété. Autrement, vous pouvez simplement fournir n'importe quelle URL du dépôt en question (y compris l'URL racine). Par exemple, imaginons que vous vouliez remplacer le commentaire associé à la propagation d'une révision précédente [14]. Si le dossier actuel fait partie de votre copie de travail du dépôt, vous pouvez simplement lancer la commande svn propset sans spécifier de chemin :

$ svn propset svn:log "* bouton.c: corrige un avertissement du compilateur." -r11 --revprop
Nouvelle valeur définie pour la propriété 'svn:log' à la révision du dépôt '11'
$

Et même si vous n'avez pas extrait de copie de travail du dépôt, vous pouvez toujours modifier la propriété en indiquant l'URL racine du dépôt :

$ svn propset svn:log "* bouton.c: corrige un avertissement du compilateur." -r11 --revprop \
              http://svn.exemple.com/depot/projet
Nouvelle valeur définie pour la propriété 'svn:log' à la révision du dépôt '11'
$

Notez que le droit de modifier cette propriété non suivie en versions doit être explicitement ajouté par l'administrateur du dépôt (voir la section intitulée « Correction des commentaires de propagation »). En effet, la propriété n'étant pas suivie en versions, vous risquez une perte d'informations si vous la modifiez à tort et à travers. L'administrateur du dépôt peut mettre en place des protections contre ce type d'incident et, par défaut, la modification de propriétés non suivies en versions est désactivée.

[Astuce]Astuce

Dans la mesure du possible, il est recommandé d'utiliser svn propedit au lieu de svn propset. Bien que le résultat soit identique, la première permet de visualiser la valeur actuelle de la propriété que l'on veut modifier, ce qui aide à vérifier que l'on fait bien ce que l'on pense faire. C'est particulièrement vrai dans le cas des propriétés non suivies en versions. Il est aussi beaucoup plus facile de modifier un texte de plusieurs lignes dans un éditeur de texte qu'en ligne de commande.

Les propriétés et le cycle de travail Subversion

Maintenant que vous êtes familier avec toutes les sous-commandes svn relatives aux propriétés, voyons comment la modification des propriétés change le cycle habituel d'utilisation de Subversion. Comme mentionné précédemment, les propriétés des fichiers et dossiers sont suivies en versions, à l'instar du contenu des fichiers. En conséquence, Subversion offre les mêmes possibilités pour fusionner (proprement ou quand apparaissent des conflits) vos modifications avec celles des autres collaborateurs.

De même que pour le contenu des fichiers, les modifications de propriétés sont locales. Elles ne deviennent permanentes que quand vous les propagez dans le dépôt via svn commit. Vos modifications sur les propriétés peuvent aussi être annulées facilement : la commande svn revert restaure vos fichiers et dossiers dans leur état d'avant les modifications, y compris pour les propriétés. Vous pouvez également obtenir des informations intéressantes sur l'état des propriétés de vos fichiers et dossiers en utilisant les commandes svn status et svn diff.

$ svn status calc/bouton.c
 M     calc/bouton.c
$ svn diff calc/bouton.c
Modification de propriétés sur calc/bouton.c
___________________________________________________________________
Added: copyright
## -0,0 +1 ##
+(c) 2006 Red-Bean Software
$

Remarquez que la sous-commande status place le M dans la deuxième colonne plutôt que dans la première. C'est parce que nous avons modifié les propriétés de calc/bouton.c, mais pas son contenu. Si nous avions changé les deux, nous aurions vu le M dans la première colonne également (reportez-vous à la section intitulée « Vue d'ensemble des changements effectués »).

Vous avez peut-être remarqué que Subversion affiche les différences au niveau des propriétés d'une manière non standard. Certes, vous pouvez toujours rediriger la sortie de svn diff pour créer un fichier correctif utilisable : le programme patch ignore ce qui concerne les propriétés (comme il ignore tout ce qu'il ne comprend pas). Malheureusement, cela signifie aussi que pour appliquer intégralement un correctif généré par svn diff, les modifications concernant les propriétés doivent être faites à la main.

Subversion 1.7 améliore la situation sur deux points. D'abord, son affichage non standard des différences de propriétés est au moins traitable par machine — c'est une amélioration par rapport à l'affichage des propriétés des versions antérieures à la 1.7. Ensuite, Subversion 1.7 introduit la sous-commande svn patch, conçue spécifiquement pour prendre en charge les informations supplémentaires que génère la sortie de svn diff afin d'appliquer les changements à la copie de travail. Ainsi, pour ce qui nous concerne directement dans ce chapitre, les différences de propriétés indiquées dans les fichiers diff produits par svn diff de Subversion 1.7 ou ultérieur peuvent être appliqués automatiquement à une copie de travail par la commande svn patch. Pour plus d'informations concernant svn patch, référez-vous à svn patch dans Guide de référence de svn : le client texte interactif.

[Note]Note

Il existe une exception à l'indication des modifications de propriétés que rapporte la commande svn diff : les modifications à la propriété spéciale de Subversion svn:mergeinfo (cette propriété est utilisée pour garder trace des fusions qui ont été effectuées dans le dépôt) sont affichées d'une manière plus lisible pour les humains. C'est une facilité accordée à ceux qui doivent lire ces descriptions. Mais cela sert également à ce que les programmes qui appliquent les patchs (y compris svn patch) sautent ces descriptions en les traitant comme du bruit non significatif. Cela pourrait ressembler à un bogue, mais pas du tout car cette propriété a pour finalité d'être gérée uniquement par la sous-commmande svn merge. Pour plus d'information sur le suivi des fusions, regardez Chapitre 4, Gestion des branches.

Propriétés héritées

Subversion 1.8 introduit le concept de propriétés héritées. Il n'y a rien de particulier concernant une propriété qui fait qu'on peut en hériter. En fait, on peut hériter de toutes les propriétés suivies en versions ! La principale différence entre les propriétés suivies en version avant Subversion 1.8 et après est que ces dernières proposent un mécanisme pour trouver les propriétés définies sur des chemins cibles parents même si ces parents ne se trouvent plus dans la copie de travail.

L'héritage de propriété se concrètise par l'intermédiaire de quelques commandes. D'abord, les sous-commandes svn proplist et svn propget peuvent retrouver toutes les propriétés de parents de chemins d'une URL ou d'une copie de travail en utilisant l'option --show-inherited-props. Vous pouvez vous représenter cette option comme étant l'opposé de l'option --recursive : au lieu de parcourir récursivement « vers le bas » les sous-dossiers de la cible, les sous-commandes avec l'option --show-inherited-props parcourrent « vers le haut » les dossiers parents de la cible. Les sous-commandes svnlook propget et svnlook proplist peuvent aussi utiliser l'option --show-inherited-props de la même manière.

Regardons un exemple pour mieux en comprendre le fonctinnement. La commande propget récursive ci-dessous appliquée à la racine de la copie de travail trouve la propriété svn:auto-props définie à la fois sur la cible et sur l'un de ses sous-dossiers site :

$ svn pg svn:auto-props --verbose -R .
Propriétés sur '.':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Propriétés sur 'site':
  svn:auto-props
    *.html = svn:eol-style=native

Si nous spécifions comme cible de la sous-commande le sous-dossier site et que nous utilisons l'option --show-inherited-props, nous trouvons que la propriété svn:auto-props est définie sur la cible et son parent. Les propriétés des parents sont affichées en tant que « héritées » :

$ svn pg svn:auto-props --verbose --show-inherited-props site
Propriétés héritées sur 'site'
de '.':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Propriétés sur 'site':
  svn:auto-props
    *.html = svn:eol-style=native

Dans les exemples précédents, la racine de la copie de travail correspond à la racine du dépôt, mais les propriétés peuvent aussi être héritées de l'extérieur de la copie de travail quand les racines ne coïncident pas. Réalisons une extraction du dossier site de l'exemple précédent et faisons-en la racine de notre copie de travail :

$ svn co http://svn.exemple.com/depot site-ct
A    site-ct/publication
A    site-ct/publication/ch2.html
A    site-ct/publication/actualités.html
A    site-ct/publication/ch3.html
A    site-ct/publication/faq.html
A    site-ct/publication/index.html
A    site-ct/publication/ch1.html
 U   site-ct
Révision 19 extraite.
$ cd site-ct

Maintenant, quand nous cherchons les propriétés héritées sur un chemin d'une copie de travail, nous pouvons constater qu'une propriété est héritée d'un parent de la copie de travail et une est héritée d'un parent du dépôt, c'est-à-dire à un emplacement « au-dessus » de la racine de la copie de travail :

$ svn pg svn:auto-props --verbose --show-inherited-props publication
Propriétés héritées sur 'publication',
de 'http://svn.exemple.com/depot':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native
Propriétés héritées sur 'publication',
de '.':
  svn:auto-props
    *.html = svn:eol-style=native
[Avertissement]Avertissement

Vous ne pouvez hériter de propriétés des chemins dans des dépôts pour lesquels vous avez un droit en lecture — regardez la section intitulée « Authentification et contrôle d'accès intégrés » et la section intitulée « Contrôle d'accès ». Si vous n'avez pas de droit de lecture à un chemin parent alors tout se passe comme si le parent n'avait pas de propriété définie.

Comme indiqué précédemment, les commandes svnlook proplist et svnlook propget acceptent l'option --show-inherited-props, mais au lieu de travailler sur des copies de travail ou des URL, elles travaillent sur des chemins de dépôts :

$ svnlook pg repos svn:auto-props /site/publication --show-inherited-props -v
Propriétés héritées sur '/site/publish'
de '/':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Propriétés héritées sur '/site/publish'
de '/site':
  svn:auto-props
    *.html = svn:eol-style=native

Les propriétés héritées en amont de la racine d'une copie de travail sont mises en cache dans la zone administrative de la copie de travail au momemnt de l'extraction et des mises à jour de la copie de travail. Cela signifie que vous n'avez pas besoin d'accéder au dépôt pour visualiser les propriétés héritées. Cela permet aux sous-commandes Subversion qui n'accèdent tradionnellement pas au dépôt (par exemple svn add) de restées « déconnectées » tout en ayant accès aux propriétés héritées de chemins qui ne sont pas dans la copie de travail. Cependant, cela signifie aussi que les propriétés héritées en amont de la racine de la copie de travail peuvent avoir été modifiées depuis la dernière mise à jour, ce qui rend votre cache local obsolète. Donc, si vous avez absolument besoin des dernières valeurs de propriétés héritées, il est toujours préférable de d'abord mettre à jour votre copie de travail ou de requêter directement le dépôt.

Arrivé à ce point, vous devez vous dire : « pas mal, mais à quoi cela sert-il vraiment ? ». En tant que tel, l'héritage de propriété n'est pas très utile. Avant la version 1.8, toutes les propriétés svn:* que Subversion se réserve (et possiblement toutes les propriétés spécifiques définies par les utilisateurs) s'appliquaient seulement sur le chemin sur lequel elles étaient définies ou, au plus, sur les enfants directs des chemins[15]. Les propriétés héritées sont un moyen utilisé par Subversion pour accomplir d'autres choses intéressantes, comme définir des propriétés automatiques avec svn:auto-props ou des bannissements sur tout le dépôt avec la propriété svn:global-ignores. Reportez-vous à la section intitulée « Configuration automatique des propriétés » et la section intitulée « Occultation des éléments non suivis en versions » pour plus d'informations sur ces propriétés spéciales et les manières de les utiliser.

[Astuce]Astuce

Aujourd'hui les propriétés héritables sont surtout utiles pour le fonctionnement de svn:auto-props et svn:global-ignores, mais cela ne signifie pas pour autant la fin de l'histoire. Les futures versions de Subversion intégreront d'autres fonctionnalités basées sur l'héritage de propriétés (un mécanisme pour définir des modèles de commentaires de propagation est le premier exemple qui nous vient à l'esprit). D'ici là, vous pouvez mettre en œuvre cette fonctionnalité comme bon vous semble. N'importe quelle métadonnées suivie en versions que vous voulez appliquer sur l'ensemble du dépôt (ou sur une grande partie de celui-ci) peut être stockée facilement dans une propriété à la racine du dépôt (ou sur le sous-arbre approprié). Nous sommes convaincu que des utilisateurs ou des administrateurs trouveront des utilisations à l'héritage de propriétés que nous n'avons jamais envisagées.

Configuration automatique des propriétés

Les propriétés constituent une fonctionnalité très puissante de Subversion et sont un élément central de nombreuses fonctionnalités de Subversion présentées ailleurs dans ce chapitre ainsi que dans les autres chapitres : comparaisons et fusions textuelles, substitution de mots-clés, transformation des retours à la ligne, etc. Mais pour profiter pleinement des propriétés, il faut les placer sur les dossiers et fichiers adéquats. Malheureusement, cette étape peut passer à la trappe dans le train-train quotidien, d'autant plus qu'oublier de configurer une propriété n'engendre généralement pas une erreur qui saute aux yeux (du moins comparativement à oublier d'ajouter un fichier dans la gestion de versions). Pour vous aider à placer vos propriétés au bon endroit, Subversion propose deux fonctionnalités simples mais néanmoins utiles.

Au moment d'introduire un fichier en suivi de versions à l'aide de la commande svn add ou svn import, Subversion essaie de vous aider en configurant automatiquement certaines propriétés communes des fichiers. D'abord, sur les systèmes d'exploitation dont le système de fichiers utilise un bit « exécutable », Subversion ajoute automatiquement la propriété svn:executable aux nouveaux fichiers, ajoutés ou importés, qui ont ce bit activé (voir la section intitulée « Fichiers exécutables ou non » plus loin dans ce chapitre pour plus de détails sur cette propriété).

Ensuite, Subversion essaie de déterminer le type MIME du fichier. Si vous avez configuré le paramètre mime-types-files, Subversion essaie de trouver un type MIME correspondant à l'extension du nom de fichier. Si un tel type MIME existe, il définit automatiquement la propriété svn:mime-type avec la valeur du type trouvé. S'il ne trouve pas de type MIME correspondant ou s'il n'existe pas de fichier définissant les correspondances, Subversion applique des heuristiques pour déterminer le type MIME. En fonction de la façon dont il a été compilé, Subversion 1.7 peut utiliser des bibliothèques qui scrutent le contenu du fichier[16] pour déterminer son type. En dernier recours, Subversion utilise sa propre heuristique très basique pour déterminer si le fichier contient des éléments non textuels. Si c'est le cas, Subversion attribue automatiquement la valeur propriété application/octet-stream (type MIME générique indiquant « une suite d'octets ») à la propriété svn:mime-type. Bien sûr, si Subversion se trompe ou si vous voulez indiquer un type plus précis (par exemple image/png ou application/x-shockwave-flash), vous pouvez toujours supprimer ou modifier cette propriété (pour d'avantage d'informations sur la gestion des types MIME par Subversion, reportez vous à la section intitulée « Type de contenu des fichiers » plus loin dans ce chapitre).

[Note]Note

L'encodage UTF-16 est communément utilisé pour des fichiers dont le contenu sémantique est textuel par nature, mais cet encodage utilise beaucoup les octets en dehors de l'intervalle typique ASCII. Ainsi, Subversion aura tendance à classer de tels fichiers dans la catégorie binaire, au grand regret des utilisateurs qui souhaitent pouvoir effectuer des comparaisons et des fusions, de la substitution de mots-clés ou d'autres manipulations sur ces fichiers.

Subversion fournit également, via sa zone de configuration (voir la section intitulée « Zone de configuration des exécutables »), une fonction de renseignement automatique des propriétés, plus flexible, qui vous permet de créer des associations entre, d'une part, des motifs de noms de fichiers et, d'autre part, des noms de propriétés / valeurs de propriétés. Là encore, ces associations modifient le comportement des commandes add et import, pouvant non seulement passer outre la décision prise par défaut d'attribution d'une propriété de type MIME mais pouvant aussi définir d'autres propriétés, qu'elles soient utilisées par Subversion ou personnalisées. Par exemple, vous pouvez créer une association qui, à chaque ajout d'un fichier JPEG (c'est-à-dire dont le nom est du type *.jpg), fixe la propriété svn:mime-type de ce fichier à la valeur image/jpeg. Ou alors affecte à tout fichierde type *.cpp la propriété svn:eol-style avec la valeur native et la propriété svn:keywords avec la valeur Id. Reportez-vous à la section intitulée « Configuration générale » pour plus d'informations sur la configuration de cette fonction.

Bien que la configuration automatique de propriétés par la zone de configuration soit certainement très facile d'utilisation, les administrateurs de Subversion préféreront peut-être définir automatiquement, sur un serveur donné, un ensemble de propriétés pour tous les clients qui se connectent pour des extractions. Les clients Subversion 1.8 et plus récents possèdent cette fonctionnalité via la propriété héritable svn:auto-props.

La propriété svn:auto-props fonctionne comme la zone de configuration pour automatiquement définir des propriétés sur les fichiers lorsqu'ils sont ajoutés ou importés. La valeur de la propriété svn:auto-props doit être la même que celle de auto-props dans la zone de configuration (c'est-à-dire un nombre quelconque de paires clé-valeur au format MOTIF_FICHIER = NOM_PROPRIETE=VALEUR[;NOM_PROPRIETE=VALEUR ...]). Comme pour l'option de configuration auto-props, la propriété svn:auto-props peut être ignorée en spécifiant l'option --no-auto-props, mais contrairement à l'option de la zone de configuration, la propriété svn:auto-props n'est pas désactivée par l'option de la zone de configuration enable-auto-props définie à no.

Par exemple, considérons que vous avez extrait une copie de travail du tronc (trunk) et que avez besoin d'ajouter un nouveau fichier (nous supposons que les propriétés automatiques sont désactivées dans votre zone de configuration) :

$ svn st
?       calc/données.c

$ svn add calc/données.c
A         calc/données.c

$ svn proplist -v calc/données.c
Propriétés sur 'calc/données.c':
  svn:eol-style
    native

Vous notez qu'après avoir placé le fichier données.c sous suivi de versions, la propriété svn:eol-style a automatiquement été définie sur lui. Puisque nous avons supposé que l'option de la zone de configuration auto-props est désactivée, nous savons que la propriété svn:auto-props doit être définie sur un chemin parent de données.c. En utilisant la commande svn propget avec l'option --show-inherited-props, nous pouvons vérifier que c'est effectivement le cas :

$ svn propget svn:auto-props --show-inherited-props -v calc
Propriétés héritées sur 'calc'
de 'http://svn.exemple.com/depot':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Au contraire de la propriété svn:global-ignores et de la directive homologue de la zone de configuration global-ignores, qui se combinent, la propriété svn:auto-props prévaut sur l'option de la zone de configuration auto-props si elle définit une propriété automatique pour le même motif que la zone de configuration. Les propriétés automatiques héritées[17] d'un chemin peuvent aussi prévaloir sur un motif identique hérité d'un chemin différent. L'ordre de priorité est défini comme suit :

  • Une propriété automatique, pour un motif donné, définie par svn:auto-props prévaut sur la même propriété automatique pour un motif identique définie par auto-props dansla zone de configuration.

  • Si une propriété automatique, pour un motif donné, est héritée depuis plus d'un chemin parent par la propriété svn:auto-props, le chemin parent le plus près prévaut sur les chemins parents plus éloignés.

  • Une propriété automatique, pour un motif donné, définie par la propriété svn:auto-props explicitement appliquée à un chemin prévaut sur la même (ou les mêmes) propriétés automatiques pour un motif identique héritées de parents.

Prenons un exemple. Supposons que nous avons ce contenu dans le fichier de configuration:

[miscellany]
enable-auto-props = yes
[auto-props]
*.py  = svn:eol-style=CR
*.c   = svn:eol-style=CR
*.h   = svn:eol-style=CR
*.cpp = svn:eol-style=CR

Vous voulez ajouter trois fichiers dans le dossier calc de votre copie de travail:

$ svn st
?       calc/données-binding.cpp
?       calc/données.c
?       calc/éditeur.py

Vérifions que svn:auto-props s'applique sur calc :

$ svn propget svn:auto-props -v --show-inherited-props calc
Propriétés héritées sur 'calc'
de 'http://svn.exemple.com/depot':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Propriétés héritées sur 'calc'
de '.':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:keywords=Auteur Date Id Rev URL

Quand nous ajoutons ces trois fichiers, qu'attendons nous pour auto-props ? Nous ajoutons le trio au suivi de versions et nous vérifions :

$ svn add calc --force
A         calc/données-binding.cpp
A         calc/données.c
A         calc/éditeur.py

Le nom de fichier données-binding.cpp ne correspond qu'à un seul motif, *.cpp = svn:eol-style=CR dans la zone de configuration, donc la propriété svn:eol-style est évidemment définie à CR :

$ svn proplist -v calc/données-binding.cpp
Propriétés sur 'calc/données-binding.cpp':
  svn:eol-style
    CR

Le nom de fichier éditeur.py correspond à un seul motif dans la zone de configuration et à deux pour la propriété svn:auto-props mais, en raison de l'ordre de priorité défini auparavant, la propriété définie explicitement sur calc, *.py = svn:eol-style=native, est prioritaire. Donc la propriété svn:eol-style est définie à native: :

$ svn proplist -v calc/éditeur.py
Propriétés sur 'calc/éditeur.py':
  svn:eol-style
    native

Le nom de fichier données.c correspond à des motifs dans la zone de configuration et dans les propriétés héritées svn:auto-props. La propriété automatique svn:keywords n'est définie qu'une seule fois, sur calc, donc données.c possède automatiquement cette propriété. svn:auto-props sur calc ne définit pas de valeur pour svn:eol-style, donc le parent le plus proche http://svn.exemple.com/depot, définit la valeur :

$ svn proplist -v calc/données.c
Propriétés sur 'calc/données.c':
  svn:eol-style
    native
  svn:keywords
    Auteur Date Id Rev URL
[Avertissement]Avertissement

L'application des priorités est uniquement valable pour des motifs identiques. Si un nom de fichier à ajouter ou importer correspond à plus d'un motif, alors il n'y a aucune garantie concernant le motif des propriétés automatiques qui est appliqué. Par exemple, disons que vous voulez ajouter le fichier truc.cpp dans le dossier machin. Supposons encore que la propriété svn:auto-props est définie sur machin avec la valeur :

*.c*  = svn:eol-style=native
*.cpp = svn:eol-style=native;svn:keywords=Auteur Date Id Rev URL

Comme truc.cpp correspond aux deux motifs,il n'est pas possible de savoir si la propriété svn:keywords sera définie sur truc.cpp quand on l'ajoutera.

Un dernier point sur svn:auto-props. Cette propriété (tout comme svn:global-ignores, voir la section intitulée « Occultation des éléments non suivis en versions ») ne constitue qu'une indication aux clients qui comprennent la signification de cette propriété. Les clients plus anciens ignorent ces propriétés, l'option --no-auto-props les laisse de côté, un utilisateur peut très bien modifier ou supprimer les propriétés automatiquement après qu'elles aient été définies ; il existe pléthore de moyens de passer outre les indications fournies par le contenu de svn:auto-props. Sachant cela, les administrateurs doivent toujours utiliser des procédures automatiques (hook scripts en anglais) pour valider la politique des propriétés qu'ils souhaitent mettre en place ; ils peuvent ainsi rejeter les propagations qui ne respectent pas les règles. Reportez-vous à la section intitulée « Mise en place des procédures automatiques » pour plus de détails sur les procédures automatiques du dépôt.

Propriétés réservées à l'usage de Subversion

Dans cette section, nous allons brièvement aborder toutes les propriétés que Subversion réserve à son propre usage. Nous regardons les deux types de propriétés, celles associées à un fichier ou un dossier particulier suivi en versions et celles associées aux révisions

Propriétés suivies en versions

Les propriétés suivies en versions que Subversion réserve à son propre usage sont les suivantes :

svn:auto-props

Si elle est définie sur un dossier, la valeur est un ensemble de définitions de propriétés qui s'appliquent à tous les fichiers sous le dossier. Voir la section intitulée « Configuration automatique des propriétés ».

svn:executable

Si elle est définie sur un fichier, le client rendra le fichier exécutable sur les copies de travail d'un système de type Unix. Voir la section intitulée « Fichiers exécutables ou non ».

svn:mime-type

Si elle est définie sur un fichier, la valeur indique le type MIME du fichier. Cela permet au client de décider si des fusions à partir des lignes sont pertinentes lors des mises à jour et cela peut aussi affecter le comportement des fichiers lorsqu'on les parcourt avec un navigateur web. Voir la section intitulée « Type de contenu des fichiers ».

svn:ignore

Si elle est définie sur un dossier, la valeur est une liste de motifs de noms de fichiers non suivis en versions que les sous-commandes svn status et autres ignoreront. Voir la section intitulée « Occultation des éléments non suivis en versions ».

svn:global-ignores

Si elle définie sur un dossier, la valeur est une liste de motifs de noms de fichiers non suivis en versions que les sous-commandes svn status et autres ignoreront. Au contraire de svn:ignore, les motifs s'appliquent à toute la sous-arborescence du dossier et pas seulement aux noms de fichiers qui sont fils directs du dossier. Voir la section intitulée « Occultation des éléments non suivis en versions ».

svn:keywords

Si elle est définie sur un fichier, la valeur indique au client comment remplacer des mots-clés particuliers à l'intérieur du fichier. Voir la section intitulée « Substitution de mots-clés ».

svn:eol-style

Si elle est définie sur fichier, la valeur indique au client comment les fins de ligne doivent être comprises dans la copie de travail et dans les exports d'arborescences. Voir la section intitulée « Caractères de fin de ligne » et svn export.

svn:externals

Si elle est définie sur un dossier, la valeur est une liste sur plusieurs lignes de chemins et d'URL que le client doit extraire. Voir la section intitulée « Définition de références externes ».

svn:special

Si elle est définie sur un ficiher, elle indique que ce fichier n'est pas ordinaire, mais est plutôt un lien symbolique ou autre objet spécial.[18]

svn:needs-lock

Si elle est définie sur un fichier, elle indique au client de placer le fichier en lecture seule dans la copie de travail, pour rappeler que ce fichier doit être verrouillé avant toute édition. Voir la section intitulée « Communication par l'intermédiaire des verrous ».

svn:mergeinfo

Utilisée par Subversion pour suivre les métadonnées liées aux fusions. Voir la section intitulée « Mergeinfo et aperçus » pour les détails  vous ne devriez jamais éditer cette propriété à moins de réellement savoir ce que vous faites.

Propriétés non suivies en versions

Les propriétés suivantes, toujours réservées par Subversion pour son propre usage, ne sont pas suivies en versions et s'appliquent aux révisions. La plupart d'entre elles apparaissent sur chaque révision dans le dépôt, stockant des informations importantes sur l'origine et la nature des modifications faites par cette révision.

svn:author

Si elle est définie, elle contient le nom de l'utilisateur authentifié qui a créé la révision. Si elle n'est pas définie, la révision a été propagée de manière anonyme.

svn:autoversioned

Si elle est définie, la révision a été créée par un mécanisme d'autoversionnage automatique. Voir la section intitulée « Gestion de versions automatique ».

svn:date

Contient l'horodatage UTC correspondant à la création de la révision, au format ISO 8601. La valeur provient de l'horloge du serveur, pas du client.

svn:log

Contient le commentaire de propagation relatif à la révision.

Certains outils connexes de la collection Subversion (svnrdump et svnsync) utilisent également les propriétés non suivies en version pour stocker des informations les concernant. Ces propriétés sont présentes uniquement sur la révision 0 des dépôts sur lesquels ces outils opèrent. Pour plus d'information sur svnrdump et svnsync, reportez-vous à Chapitre 5, Administration d'un dépôt. Les propriétés créées et gérées par ces outils sont les suivantes :

svn:rdump-lock

Utilisée pour mettre en œuvre un accès exclusif temporaire au dépôt par la commande svnrdump load. Cette propriété est généralement présente seulement quand une opération de ce type est en cours (ou quand une commande svnrdump a échoué à se déconnecter proprement du dépôt). Cette propriété n'a de sens que si elle est définie sur la révision 0.

svn:sync-currently-copying

Contient le numéro de révision du dépôt source qui est en train d'être recopié sur le dépôt cible par la commande svnsync. Cette propriété n'a de sens que si elle est définie sur la révision 0.

svn:sync-from-uuid

Contient l'UUID du dépôt dont le dépôt cible est un mirroir, créé par l'outil svnsync. Cette propriété n'a de sens que si elle est définie sur la révision 0.

svn:sync-from-url

Contient l'URL du dépôt dont le dépôt cible est un mirroir, créé par l'outil svnsync. Cette propriété n'a de sens que si elle est définie sur la révision 0.

svn:sync-last-merged-rev

Contient le numéro de révision du dépôt source qui a été dupliqué avec succès le plus récemment sur le dépôt cible. Cette propriété n'a de sens que si elle est définie sur la révision 0.

svn:sync-lock

Utilisée pour mettre en œuvre un accès exclusif temporaire au dépôt par la commande de duplication svnsync. Cette propriété est généralement présente seulement quand une opération de ce type est en cours (ou quand une commande svnsync a échoué à se déconnecter proprement du dépôt). Cette propriété n'a de sens que si elle est définie sur la révision 0.

Portabilité des fichiers

Heureusement pour les utilisateurs de Subversion qui travaillent sur différents ordinateurs et systèmes d'exploitation, le comportement du client texte interactif est pratiquement identique sur tous les systèmes. Si vous vous débrouillez avec svn sur un système, vous devriez vous en sortir sur n'importe quel système.

Cependant, ce n'est pas toujours le cas pour d'autres types de logiciels ou pour les fichiers que vous gérez dans Subversion. Par exemple, sur un système Windows, la définition d'un « fichier texte » est similaire à la définition de Linux, mais avec une différence notable pour ce qui concerne les retours à la ligne. Il y a aussi d'autres différences. Les plateformes Unix (et Subversion) supportent la notion de lien symbolique ; Windows non. Les plateformes Unix utilisent les permissions du fichier pour déterminer si un fichier est exécutable ; Windows utilise l'extension du fichier.

Subversion n'a pas la possibilité d'unifier toutes ces définitions et ces implémentations. Tout ce qu'il peut faire, c'est aider au maximum l'utilisateur qui travaille sur plusieurs systèmes et plusieurs ordinateurs. Cette section décrit comment Subversion s'y prend.

Type de contenu des fichiers

Subversion fait partie des nombreuses applications qui reconnaissent et utilisent les types MIME (Multipurpose Internet Mail Extensions). Ainsi, la valeur de la propriété svn:mime-type permet, en plus de stocker le type de contenu d'un fichier, de changer le comportement de Subversion lui-même.

Par exemple, un avantage fourni par cette reconnaissance de type par Subversion est la possibilité de fusion contextuelle, ligne par ligne, des changements reçus lors d'une mise à jour. En revanche, pour les fichiers contenant autre chose que du texte, il n'y a souvent pas de concept de « ligne ». En conséquence, pour les fichiers suivis en versions dont la propriété svn:mime-type contient une valeur de type MIME non textuel (généralement un intitulé qui ne commence pas par text/, bien qu'il y ait des exceptions), Subversion ne tente pas de fusion contextuelle pendant la mise à jour. À la place, chaque fois que vous avez modifié localement un fichier binaire qui a été mis à jour sur le dépôt, Subversion ne touche pas à votre fichier mais crée deux nouveaux fichiers. Un fichier avec l'extension .oldrev qui contient la version du fichier à la révision BASE. Un autre fichier avec l'extension .newrev qui contient la version à jour du fichier. Ce comportement est dicté par la volonté d'éviter que l'utilisateur ne tente d'effectuer une fusion qui échouerait parce que les fichiers ne peuvent tout simplement pas être fusionnés.

De plus, puisque le fait d'afficher les différences et les modifications ligne par ligne est, c'est évident, dépendant de la signification que l'on accorde à une « ligne » du fichier considéré, les fichiers dont le type MIME n'est pas compatible avec du texte déclenchent par défaut des erreurs lorsqu'ils sont la cible de sous-commandes telles que svn diff et svn annotate. Cela peut s'avérer particulièrement frustrant pour les utilisateurs de fichiers XML dont la propriété svn:mime-type est définie avec une valeur comme application/xml qui n'est pas interprétée par Subversion comme lisible par un humain car relativement ambigüe. Heureusement, ces sous-commandes proposent l'option --force pour obliger Subversion à essayer d'opérer sur ces fichiers malgré leur apparente illisibilité pour un humain.

[Avertissement]Avertissement

La propriété svn:mime-type, lorsqu'elle est définie avec une valeur qui n'indique pas de contenu textuel, peut entrainer des comportements inattendus avec les autres propriétés. Par exemple, comme la notion de fin de ligne (et donc de conversion de caractère de fin de ligne) n'a pas de sens pour un fichier binaire, Subversion vous empêche de définir la propriété svn:eol-style sur ces fichiers. Cela saute aux yeux lorsque vous travaillez sur un seul fichier et que svn propset génère une erreur. C'est beaucoup moins évident si vous effectuez une opération récursive, où Subversion omet silencieusement les fichiers qu'il considère inappropriés pour une propriété donnée.

Subversion fournit plusieurs mécanismes pour définir automatiquement la propriété svn:mime-type sur un fichier suivi en versions. Consultez la section intitulée « Configuration automatique des propriétés » pour en obtenir les détails.

Par ailleurs, si la propriété svn:mime-type est définie, alors le greffon Apache pour Subversion utilise cette valeur pour renseigner le champ Content-type: de l'en-tête HTTP en réponse à une requête GET. Cela fournit au navigateur Web une indication très importante pour pouvoir afficher correctement le fichier, quand vous l'utilisez pour parcourir le contenu du dépôt Subversion.

Fichiers exécutables ou non

Sur beaucoup de systèmes d'exploitation, la capacité de rendre un fichier exécutable dépend d'un bit dit « exécutable ». Habituellement, ce bit est désactivé par défaut et doit être explicitement activé par l'utilisateur pour chaque fichier concerné. Ce serait une perte de temps énorme d'avoir à se rappeler exactement quel fichier, parmi ceux que l'on vient d'extraire du dépôt, doit avoir le bit exécutable positionné et ensuite de devoir le faire manuellement. C'est pourquoi Subversion fournit la propriété svn:executable pour spécifier que le bit exécutable doit être activé pour le fichier concerné. Subversion s'occupe lui-même de cette tâche quand il rapatrie de tels fichiers dans la copie de travail locale.

Cette propriété n'a aucun effet sur les systèmes de fichiers qui ne possèdent pas le concept du bit exécutable, tels que FAT32 et NTFS [20]. Par ailleurs, bien qu'elle n'ait pas de valeurs définies, Subversion lui attribue la valeur * lorsqu'il active cette propriété. Enfin, cette propriété n'est valide que sur des fichiers, pas sur des dossiers.

Caractères de fin de ligne

Pour tout fichier suivi en versions, Subversion considère que le contenu est lisible par un humain sauf si la propriété svn:mime-type indique le contraire. En règle générale, Subversion utilise cette information pour déterminer s'il est possible d'effectuer une comparaison contextuelle pour ce fichier. Sinon, pour Subversion, les octets sont des octets.

Cela veut dire que par défaut, Subversion ne s'intéresse pas au type de caractère utilisé pour marquer les fins de lignes (EOL en anglais, pour End Of Line). Malheureusement, des conventions différentes sont utilisées suivant les systèmes d'exploitation pour indiquer une fin de ligne de texte dans un fichier. Par exemple, les logiciels sous Windows utilisent généralement une paire de caractères de contrôle ASCII : un retour chariot (CR, carriage return) suivi par un saut de ligne (LF, line feed). Les logiciels Unix, cependant, utilisent uniquement le caractère LF pour indiquer les fins de lignes.

Tous les programmes ne savent pas gérer les fichiers utilisant un marqueur de fin de ligne « exogène » au système d'exploitation sur lequel ils tournent. Ainsi, il n'est pas rare de voir les programmes Unix traiter le marqueur CR des fichiers Windows comme un caractère normal (en affichant à l'écran un ^M) et les programmes Windows combiner en une seule ligne immense un fichier Unix parce qu'ils n'y ont pas trouvé la combinaison retour chariot-passage à la ligne (CR-LF).

Cette incapacité de traiter correctement les marqueurs de fin de ligne d'autres plates-formes peut être assez frustrante pour ceux qui partagent des fichiers entre différents systèmes d'exploitation. Prenons l'exemple d'un fichier de code source qui est édité par des développeurs à la fois sous Windows et sous Unix. Si tous les développeurs utilisent des outils qui se plient à la convention utilisée par le fichier, pas de problème.

Mais, en pratique, de nombreux outils largement utilisés soit ne parviennent pas à lire correctement un fichier utilisant une convention différente pour les fins de ligne, soit ils convertissent les fins de lignes au format local lors de la sauvegarde. Dans le premier cas, le développeur doit utiliser des outils externes (tels que dos2unix et son compagnon unix2dos) pour préparer le fichier avant l'édition. Dans le deuxième cas, pas besoin de préparation. Mais dans les deux cas, le fichier résultant diffère de l'original littéralement pour toutes les lignes ! Avant de propager ses changements, l'utilisateur a deux choix. Soit il utilise un utilitaire de conversion pour revenir à la même convention qu'avant l'édition. Soit il propage le fichier avec la nouvelle convention de fin de ligne.

Au final, les deux hypothèses conduisent à une perte de temps et des modifications inutiles sur les fichiers propagés. La perte de temps est déjà pénible. Mais si en plus la propagation change chaque ligne du fichier, trouver quelle ligne a effectivement changé devient non trivial. À quel endroit ce bogue a-t-il réellement été corrigé ? Dans quelle ligne y avait-il cette erreur de syntaxe ?

La solution à ce problème est la propriété svn:eol-style (eol pour End Of Line). Quand cette propriété possède une valeur valide, Subversion l'utilise pour déterminer quel traitement il doit appliquer pour que le fichier ne change pas de convention à chaque propagation provenant d'un système d'exploitation différent. Les valeurs valides sont :

native

Ceci force le fichier à adopter la convention utilisée par le système d'exploitation sur lequel s'exécute Subversion. En d'autres termes, si un utilisateur d'une machine Windows récupère une copie de travail d'un fichier dont la propriété svn:eol-style vaut native, ce fichier contiendra le marqueur CRLF pour indiquer les fins de ligne. Un utilisateur Unix qui récupère une copie de travail qui contient le même fichier verra simplement LF pour indiquer les fins de ligne sur sa copie.

Notez que Subversion stocke en fait le fichier dans le dépôt en utilisant le marqueur standard LF indépendamment du système d'exploitation. Cela reste toutefois tout à fait transparent pour l'utilisateur.

CRLF

Le fichier contiendra le marqueur CRLF pour indiquer les fins de ligne, quel que soit le système d'exploitation..

LF

Le fichier contiendra le marqueur LF pour indiquer les fins de ligne, quel que soit le système d'exploitation.

CR

Le fichier contiendra le marqueur CR pour indiquer les fins de ligne, quel que soit le système d'exploitation. Ce marqueur de fin de ligne n'est pas très courant.

Occultation des éléments non suivis en versions

Dans n'importe quelle copie de travail, il y a de grandes chances que les fichiers et dossiers suivis en versions côtoient d'autres fichiers et dossiers non suivis en versions ou qui n'ont pas lieu de l'être. Les éditeurs de texte remplissent les dossiers avec des fichiers de sauvegarde. Les compilateurs créent des fichiers intermédiaires (ou même des fichiers finaux) que vous ne voudrez pas suivre en versions. Et les utilisateurs eux-mêmes déposent des fichiers et des dossiers où bon leur semble, souvent dans des copies de travail locales.

Il est ridicule de penser que les copies de travail Subversion échappent à ce type de méli-mélo. En fait, Subversion prend en compte (c'est une fonctionnalité) dès le début que les copies de travail sont des dossiers comme les autres, comme ceux qui ne sont pas suivis en versions. Mais ces fichiers et dossiers qui-n-ont-pas-vocation-à-être-suivis-en-versions peuvent perturber les utilisateurs de Subversion. Par exemple, comme les commandes svn add et svn import sont récursives par défaut et ne savent pas quels fichiers de l'arborescence vous voulez suivre ou non en versions, il est relativement facile d'ajouter au suivi de versions des éléments que vous ne vouliez pas suivre. Et comme la commande svn status liste, par défaut, tous les éléments intéressants de la copie de travail, y compris les fichiers et dossiers non suivis en versions, son affichage devient rapidement confus avec de tels imbroglios.

C'est pourquoi Subversion fournit plusieurs méthodes pour pouvoir lui indiquer quels fichiers vous souhaitez ignorer. La première implique l'utilisation de la zone de configuration (voir la section intitulée « Zone de configuration des exécutables ») et, par conséquent, s'applique à toutes les opérations Subversion qui utilisent cette zone de configuration, généralement toutes celles de l'ordinateur ou d'un utilisateur particulier de l'ordinateur. Deux autres méthodes utilisent les propriétés Subversion des dossiers et sont plus liées à l'arborescence suivie en versions elle-même. Par conséquent, elles affectent tous ceux qui possèdent une copie de travail de cette arborescence. Tous ces mécanismes utilisent des motifs de noms de fichiers (des chaines de caractères simples ou des jokers) pour trouver des correspondances avec les noms de fichiers qu'il faut ignorer.

La zone de configuration de Subversion propose une directive, global-ignores, dont la valeur est un ensemble de motifs de noms de fichiers séparés par des espaces. Le client Subversion compare ces motifs aux noms des fichiers que l'on tente d'ajouter au suivi de versions, ainsi qu'aux noms des fichiers non suivis en versions détectés par la commande svn status. Si un nom de fichier correspond au motif, Subversion ignore totalement ce fichier. C'est particulièrement utile pour les fichiers que vous ne voulez jamais suivre en versions, comme les fichiers de sauvegarde créés par les éditeurs de texte (par exemple, les fichiers *~ et .*~ créés par Emacs).

Pour un dossier suivi en versions, la propriété svn:ignore est supposée contenir une liste de motifs de noms de fichiers (un motif par ligne) que Subversion utilise pour déterminer quels objets ignorer dans le dossier concerné. Ces motifs ne remplacent pas les motifs inscrits dans la directive global-ignores de la zone de configuration, mais s'ajoutent à cette liste. Veuillez également noter que, contrairement à la directive global-ignores, les motifs de la propriété svn:ignore s'appliquent uniquement au dossier pour lequel la propriété est définie et pas à ses sous-dossiers. La propriété svn:ignore est utile pour indiquer à Subversion d'ignorer les fichiers susceptibles d'être présents dans la copie de travail de ce dossier chez chaque utilisateur comme les fichiers produits par les compilateurs ou, pour citer un exemple plus approprié à ce livre, les fichiers HTML, PDF ou PostScript générés par la conversion des fichiers sources DocBook XML vers un format de fichier plus lisible.

Subversion 1.8 fournit une version plus puissante de la propriété svn:ignore : la propriété svn:global-ignores. Comme la propriété svn:ignore, svn:global-ignores ne peut être définie que pour un dossier et elle contient les motifs de noms de fichiers que Subversions doit ignorer[21]. Ces motifs à ignorer sont aussi ajoutés aux motifs définis dans la zone de configuration par la directive global-ignores et à ceux définis par la propriété svn:ignore. Contrairemen à svn:ignore, la propriété svn:global-ignores s'hérite[22] et elle s'applique à tous les chemins placés sous le dossier sur lequel la propriété est définie, pas seulement les fils directs de ce dossier.

[Note]Note

Le support des motifs de fichiers à ignorer dans Subversion s'applique uniquement à la procédure d'ajout de fichiers et dossiers non suivis en versions vers la gestion de versions. Une fois que l'objet est suivi en versions par Subversion, les mécanismes permettant d'ignorer certains fichiers selon des motifs prédéfinis ne s'appliquent plus. Autrement dit, ne pensez pas que Subversion ne propagera pas les changements que vous avez faits à un fichier suivi en versions simplement parce que son nom correspond à un motif à ignorer : Subversion prend toujours en compte l'ensemble des objets qu'il gère.

La liste globale des motifs à ignorer définie dans la directive global-ignores de la zone de configuration reste une affaire de goût[23], car elle doit davantage s'intégrer à la collection d'outils de l'utilisateur que répondre aux besoins d'une copie de travail particulière. C'est pourquoi le reste de cette section s'attache à décrire l'utilisation des propriétés svn:ignore et svn:global-ignores.

Prenons par exemple le résultat suivant de la commande svn status :

 M     calc/bouton.c
?      calc/calculatrice
?      calc/donnees.c
?       calc/debug_log
?       calc/debug_log.1
?       calc/debug_log.2.gz
?       calc/debug_log.3.gz

Dans cet exemple, des modifications ont été faites sur les propriétés de bouton.c et il y a aussi des fichiers non suivis en versions : le programme calculatrice (résultat de votre dernière compilation du code source), un fichier source donnees.c et un ensemble de fichiers de traces pour le débogage. Vous êtes conscient du fait que compiler votre code engendre à chaque fois la création du programme calculatrice [24]. Vous savez également que vous avez toujours des fichiers de traces qui traînent. On peut faire ce constat pour toutes les copies de travail locales de ce projet, pas seulement la vôtre. Et vous savez que cela ne vous intéresse pas et que cela n'intéresse très probablement aucun autre développeur, de voir ces fichiers apparaître à chaque commande svn status. Vous allez donc utiliser svn propedit svn:ignore calc pour ajouter des motifs à ignorer pour le dossier calc.

$ svn propget svn:ignore calc 
calculatrice
debug_log*
$

Après avoir ajouté cette propriété, vous avez une propriété modifiée localement dans votre dossier calc. Mais notez les autres différences sur le résultat de la commande svn status :

$ svn status
 M      calc
 M     calc/bouton.c
?      calc/donnees.c

Maintenant, tout le superflu a disparu ! Bien sûr, votre programme compilé et les fichiers de trace sont toujours présents dans votre copie locale. Subversion ne vous présente pas ces fichiers présents mais non suivis en versions, c'est tout. Et maintenant que ces parasites sont supprimés de l'affichage, il ne vous reste plus que les éléments intéressants, tels que le fichier source donnees.c que vous avez probablement oublié d'ajouter au suivi de versions.

Bien évidemment, ce compte-rendu plus succinct de l'état de votre copie de travail locale n'est pas le seul possible. Si vous voulez voir les fichiers ignorés dans le compte-rendu, vous pouvez ajouter l'option --no-ignore à la commande Subversion :

$ svn status --no-ignore
 M      calc
 M     calc/bouton.c
I      calc/calculatrice
?      calc/donnees.c
I       calc/debug_log
I       calc/debug_log.1
I       calc/debug_log.2.gz
I       calc/debug_log.3.gz

Tous les fichiers non suivis en versions auparavant occultés apparaissent de nouveau, mais avec un état I pour Ignoré. Mais attendez, qu'en est-il du fichier wip.1.diff ? La propriété svn:ignore définie sur calc ne comporte aucun motif qui corresponde à ce nom de fichier, alors pourquoi est-il occulté[25] ? La réponse réside dans la troisième méthode qu'utilise Subversion pour ignorer les chemins non suivis en versions, la propriété héritée svn:global-ignores. Utilisez la sous-commande svn propget avec l'option --show-inherited-props ,vous verrez alors que la propriété svn:global-ignores est définie à la racine de votre copie de travail et, pour sûr, qu'elle définit un motif qui correspond au nom de fichier cherché :

$ svn pg svn:global-ignores calc -v --show-inherited-props
Propriétés héritées sur 'calc'
de '.':
  svn:global-ignores
    *.diff
    *.patch

Comme mentionné auparavant, la liste des motifs de fichiers à ignorer est aussi utilisée par svn add et svn import. Ces deux opérations demandent à Subversion de gérer un ensemble de fichiers et de dossiers. Plutôt que de forcer l'utilisateur à choisir dans l'arborescence quels fichiers il souhaite suivre en versions, Subversion utilise les motifs de fichiers à ignorer, à la fois la liste globale et ceux définis par dossier, pour déterminer quels fichiers suivre (ou ne pas suivre) en versions dans sa procédure récursive d'ajout ou d'import. Là encore, vous pouvez utiliser l'option --no-ignore pour indiquer à Subversion d'ignorer ces listes et de d'agir effectivement sur tous les fichiers et dossiers présents.

[Astuce]Astuce

Même si svn:ignore ou svn:global-ignores sont définies, vous risquez de rencontrer des problèmes si vous utilisez des caractères spéciaux du shell dans une commande. Les caractères spéciaux sont remplacés par une liste explicite de cibles avant que Subversion n'agisse sur eux et donc lancer svn SOUS-COMMANDE * revient à lancer svn SOUS-COMMANDE fichier1 fichier2 fichier3 …. Dans le cas de la commande svn add, ceci a un effet similaire à l'option --no-ignore. Par conséquent, au lieu d'utiliser un caractère spécial, utilisez plutôt svn add --force . pour marquer d'un seul coup les éléments non suivis en versions pour ajout. La cible explicite permet de s'assurer que le dossier en cours ne sera pas négligé car déjà suivi en versions et l'option --force force Subversion à parcourir ce dossier, ajoutant les fichiers non suivis en versions, tout en respectant les propriétés svn:ignore et sv:global-ignores ainsi que la directive global-ignores de la zone de configuration. Pensez aussi à rajouter l'option --depth files à la commande svn add si vous ne voulez pas que la recherche de fichiers à ajouter au suivi de versions ne parcoure le dossier de façon récursive.

Substitution de mots-clés

Subversion a la capacité de substituer des mots-clés dans les fichiers suivis en versions par des informations dynamiques et utiles. Les mots-clés fournissent généralement des indications sur les dernières modifications faites au fichier. Comme ces informations changent à chaque fois que le fichier change et, plus spécifiquement, juste après que le fichier change, c'est compliqué pour tout processus, excepté pour le système de gestion de versions, de garder les données à jour. Sans outil automatique, adieu la pertinence de ces informations !

Par exemple, prenons un document dont vous voulez afficher la date de dernière modification. Vous pouvez charger chaque contributeur du document de renseigner le champ correspondant juste avant de propager ses changements. Mais un jour ou l'autre, quelqu'un oubliera de le faire. Demandez plutôt à Subversion de substituer le mot-clé LastChangedDate. Vous contrôlez où est inséré le mot-clé dans votre document en plaçant un signet à l'endroit voulu dans le fichier. Ce signet est juste une chaîne de caractères formatée comme ceci : $NomDuMotClé$.

Ajouter un signet dans votre fichier ne fait rien de particulier. Subversion n"essayera jamais de le substituer tant que vous ne lui demandez pas explicitement de le faire. Après tout, vous êtes peut-être en train de rédiger un document[26] sur l'utilisation des mots-clés et vous ne voulez pas que Subversion substitue à vos beaux exemples de signets non substitués leur valeur réelle !.

Pour indiquer à Subversion que vous voulez substituer les mots-clés d'un fichier particulier, nous nous tournons une fois de plus vers les sous-commandes relatives aux propriétés. La propriété svn:keywords, quand elle est définie sur un fichier suivi en versions, contrôle quels mots-clés doivent être substitués dans ce fichier. Elle doit contenir une liste de mots-clés ou d'alias séparés par des espaces.

Par exemple, admettons que vous ayez un fichier suivi en versions nommé météo.txt qui ressemble à ceci :


Voici les dernières prévisions de nos spécialistes :
$LastChangedDate$
$Rev$
Les cumulus sont de plus en plus nombreux au fur et à mesure que l'été approche.

Sans la propriété svn:keywords définie sur ce fichier, Subversion ne fait rien de spécial. À présent, si nous activons les substitutions pour le mot-clé LastChangedDate :

$ svn propset svn:keywords "Date Author" météo.txt
Propriété 'svn:keywords' définie sur 'météo.txt'
$

Vous venez d'effectuer une modification locale des propriétés du fichier météo.txt. Vous ne verrez aucun changement dans le contenu du fichier (à moins d'avoir fait des modifications avant d'activer la propriété). Notez que le fichier contenait un signet pour le mot-clé Rev et que nous n'avons pas inclus ce mot-clé dans la valeur de la propriété. Subversion ignore simplement les requêtes de substitutions de mots-clés qui ne sont pas présents dans le fichier et ne substitue pas de mot-clé qui ne soit pas présent dans la valeur de la propriété svn:keywords.

Immédiatement après avoir propagé cette modification de propriété, Subversion met à jour votre copie de travail avec le nouveau texte substitué. Au lieu de voir votre signet $LastChangedDate$, vous voyez le résultat de la substitution. Ce résultat contient aussi le nom du mot-clé et est toujours entouré par des caractères dollar ($). Comme prévu, le mot-clé Rev n'a pas été substitué parce que nous n'avons pas demandé qu'il le soit.

Notez également que la substitution s'est bien passée alors que nous avons indiqué Date Author comme valeur de propriété svn:keywords et que le signet utilisait l'alias $LastChangedDate$ :


Voici les dernières prévisions de nos spécialistes :
$LastChangedDate: 2006-07-22 21:42:37 -0700 (sam. 22 juil. 2006) $
$Rev$
Les cumulus sont de plus en plus nombreux au fur et à mesure que l'été approche.

Si quelqu'un d'autre propage une modification de météo.txt, votre copie de ce fichier continue à afficher la même valeur substituée de mot-clé, jusqu'à ce que vous mettiez à jour votre copie de travail. Aux mots-clés de météo.txt sont alors à nouveau substituées les informations qui se rapportent à la plus récente propagation du fichier.

Tous les mots-clés sont sensibles à la casse des caractères quand ils apparaissent en tant que signets : vous devez placer les majuscules aux bons endroits pour que le mot-clé soit effectivement remplacé. Vous devez aussi considérer que la valeur de la propriété svn:keywords est sensible à la casse (case-sensitive en anglais) : certains mots-clés sont reconnus indépendamment de la casse, mais ce comportement est obsolète.

Subversion définit la liste des mots-clés disponibles pour les substitutions. Cette liste contient les cinq mots-clés suivants (certains d'entre eux ont des alias que vous pouvez aussi utiliser) :

Date

Ce mot-clé indique la date du dernier changement connu dans le dépôt. Il est de la forme $Date: 2006-07-22 21:42:37 -0700 (sam. 22 juil. 2006) $. Il peut également être spécifié en tant que LastChangedDate. Contrairement au mot-clé Id, qui utilise l'heure UTC, le mot-clé Date affiche la date et l'heure locales.

Revision

Ce mot-clé indique la dernière révision connue pour laquelle le fichier a changé dans le dépôt. Il fournit une réponse du type $Revision: 144 $. Il peut aussi être spécifié en tant que LastChangedRevision ou Rev..

Author

Ce mot-clé indique le nom du dernier utilisateur qui a modifié le fichier dans le dépôt et retourne une valeur du type $Author: harry $. Il peut aussi être spécifié en tant que LastChangedBy.

HeadURL

Ce mot-clé décrit l'URL complète de la dernière version du fichier dans le dépôt et ressemble à $HeadURL: http://svn.exemple.com/depot/trunk/calc.c $. Il peut être abrégé en URL.

Id

Ce mot-clé est une combinaison abrégée des autres mots-clés. Sa substitution donne quelque chose comme $Id: calc.c 148 2006-07-28 21:30:43Z sally $, que l'on interprète comme suit : « Le fichier calc.c a été modifié en dernier par l'utilisateur sally lors de la révision 148 le 28 juillet 2006 au soir. » La date et l'heure affichées sont en heure UTC, contrairement au mot-clé Date qui utilise l'heure locale.

Header

Ce mot-clé est similaire au mot-clé Id, mais contient l'URL complète de la dernière révision de l'élément, à l'identique de HeadURL. Sa substitution donne un résultat du type $Header: http://svn.exemple.com/depot/trunk/calc.c 148 2006-07-28 21:30:43Z sally $.

Une bonne partie des définitions qui précèdent utilisent la locution « dernière … connue » ou quelque chose d'équivalent. Rappelez-vous que la substitution des mots-clés est une opération effectuée côté client et que votre client ne connaît pas les changements qui ont eu lieu dans le dépôt depuis votre dernière mise à jour. Si vous ne mettez jamais à jour de votre copie de travail locale, vos mots-clés restent figés à la même valeur même si des changements ont lieu régulièrement dans le dépôt.

En complément de ces mots-clés et alias prédéfinis, Subversion 1.8 vous permet de définir et d'utiliser les mots-clés personnalisés. Pour définir un mot-clé personnalisé, ajoutez une jeton à la propriété svn:keywords avec la syntaxe suivante : MyMot-Clé=FORMAT. MyMot-Clé est le nom du mot-clé (celui que vous utiliserez dans le signet) et FORMAT est une chaine formatée dans laquelle l'information sera insérée lorsque le mot-clé sera substitué dans votre fichier.

La syntaxe de la chaine formatée utilisée pour les mots-clés personnalisés accepte les codes de substitution suivants :

%a

L'auteur de la révision %r.

%b

Le nom de fichier de l'URL du fichier.

%d

La date de la révision %r au format court.

%D

La date de la révision %r au format long.

%P

Le chemin du fichier, relativement à la racine du dépôt.

%r

La dernière révision connue pour laquelle ce fichier a été modifié dans le dépôt (c'est la même révision que pour le mot-clé Revision).

%R

L'URL de la racine du dépôt.

%u

L'URL du fichier.

%_

Un caractère « espace » (la définition d'un mot-clé ne peut pas contenir d'espace).

%%

Le caractère pourcent ('%').

%H

Équivaut à %P%_%r%_%d%_%a.

%I

Équivaut à %b%_%r%_%d%_%a.

Comme vous pouvez le constater, beaucoup de codes de format reprennent les informations disponibles dans les mots-clés prédéfinis. Mais bien ûr, les mots-clés personnalisés vous offrent plus de souplesse dans l'organisation de l'information. Par exemple, vous pouvez avoir envie d'avoir un mot-clé dans vos fichiers qui donne le chemin relatif dans le dépôt du fichier et la dernière révision qui le modifie, formattée à votre convenance pour une meilleure lisibilité. Pour ce faire, vous devez d'abord définir votre mot-clé personnalisé :

$ svn pset svn:keywords "CheminRev=%P,%_r%r" calc/bouton.c
Propriété 'svn:keywords' définie sur 'bouton.c'
$

Ensuite, vous devez éditer votre fichier pour ajouter le signet pour votre mot-clé, ce qui nous donne dans ce cas $CheminRev$. Après avoir propagé ces modifications, si vous regardez le contenu du fichier, vous constaterez que le mot-clé a été substitué conformément au résultat attendu : où le fichier contenait $CheminRev$, il contient maintenant $cheminRev: trunk/calc/bouton.c, r23 $.

[Note]Note

Subversion tronque automatiquement toute expansion de mot-clé qui dépasse 255 caractères de long. Les définitions de mots-clés dont le nom dépasse 255 caractères sont aussi ignorées.

Vous pouvez aussi demander à Subversion de conserver une longueur constante (en nombre d'octets consommés) pour la substitution d'un mot-clé. En utilisant la séquence double deux-points (::), suivi par le nombre de caractères désiré, vous définissez la largeur voulue. Quand Subversion substituera le mot-clé pour le mot-clé et sa valeur, il ne remplacera que les caractères espaces, laissant le reste du champ de mot-clé inchangé. Si la valeur de substitution est plus courte que la largeur définie, il y aura des caractères de remplissage à la fin du champ substitué ; si elle est trop longue, elle sera tronquée avec un caractère spécial « dièse » (#) juste avant le caractère dollar qui sert de délimiteur de fin.

Par exemple, supposons que vous ayez un document dans lequel vous avez un paragraphe avec des données tabulées qui représentent les mots-clés Subversion du document. Si vous utilisez la syntaxe originale Subversion pour la substitution des mots-clés, votre fichier ressemblera à :


$Rev$:     Révision de la dernière propagation
$Author$:  Auteur de la dernière propagation
$Date$:    Date de la dernière propagation

Tel quel, cela semble joli et bien aligné. Mais quand vous allez propager ce fichier (avec la substitution des mots-clés activée, bien évidemment), vous verrez :


$Rev: 12 $:     révision de la dernière propagation
$Author: harry $:  auteur de la dernière propagation
$Date: 2006-03-15 02:33:03 -0500 (mer. 15 Mar 2006) $:    date de la dernière propagation

Le résultat n'est pas très heureux. Vous êtes alors tenté d'ajuster le fichier après la substitution pour le remettre en forme. Mais cela ne fonctionne que tant que les substitutions de mots-clés gardent la même longueur. Si le numéro de la dernière révision propagée passe de 99 à 100 par exemple, ou si une autre personne avec un nom d'utilisateur plus long propage le fichier, tout le travail est à refaire. Cependant, si vous travaillez avec Subversion 1.2 ou plus récent, vous pouvez utiliser la nouvelle syntaxe des mots-clés à longueur fixe et spécifier la largeur adéquate de certains champs ; le fichier ressemble alors à ceci :


$Rev::               $:  révision de la dernière propagation
$Author::            $:  auteur de la dernière propagation
$Date::              $:  date de la dernière propagation

Propagez les modifications du fichier. Cette fois, Subversion prend en compte la nouvelle syntaxe des mots-clés à longueur fixe et conserve la largeur des champs telle que définie en complétant en tant que de besoin entre le double deux-points et le dollar de fin. Après substitution, la largeur des champs n'a pas changé : les valeurs courtes comme Rev et Author sont complétées avec des espaces et le champ Date, trop long, est tronqué par un caractère dièse :


$Rev:: 13            $:  révision de la dernière propagation
$Author:: harry      $:  auteur de la dernière propagation
$Date:: 2006-03-15 0#$:  date de la dernière propagation

L'utilisation de champs de mots-clés à longueur fixe est particulièrement utile lorsque vous faites des substitutions dans des fichiers dont le format est complexe et fait lui-même usage de champs à longueurs fixes ou lorsque la place disponible pour le stockage du champ de donnée est vraiment trop difficile à changer en dehors de l'application native elle-même. Bien sûr, dans le cas de fichiers binaires, vous devez toujours faire très attention à ce que les substitutions de mots-clés (à longueur fixe ou non) ne cassent pas l'intégrité du fichier. Bien que cela semble facile, cela peut être étonnament difficile pour la plupart des formats de fichiers binaires utilisés de nos jours et vous ne devez pas sous-estimer l'ampleur de cette tâche !

[Avertissement]Avertissement

Soyez conscient que, comme la taille d'un mot-clé est mesurée en octets, les valeurs utilisant des données codées sur plusieurs octets peuvent être corrompues. Par exemple, un nom d'utilisateur qui contient des caractères au format UTF-8 codés sur plusieurs octets risque d'être tronqué en plein milieu d'un de ces caractères multi-octets. Cette troncature est valide au niveau du traitement des octets mais résulte en une chaîne UTF-8 incorrecte en raison du caractère final tronqué. Il est ainsi possible que certaines applications, au moment de charger le fichier, remarquent que le texte UTF-8 est invalide, considèrent tout le fichier comme corrompu et refusent de travailler dessus. En conséquence, lorsque vous utilisez les mots-clés à longueur fixe, veillez à choisir une taille adaptée à des valeurs pouvant contenir des caractères éventuellement codés sur plusieurs octets.

Répertoires clairsemés

Par défaut, la plupart des opérations Subversion sur des dossiers agissent de manière récursive. Par exemple, svn checkout crée une copie de travail avec tous les fichiers et dossiers de la zone spécifiée du dépôt, en descendant récursivement dans l'arborescence du dépôt pour en copier la structure complète sur votre disque local. Subversion 1.5 introduit une nouvelle fonctionnalité appelée répertoires clairsemés (ou extractions superficielles) qui permet d'obtenir facilement une copie de travail (ou une simple portion d'une copie de travail) moins profonde que via la récursion complète, avec la possibilité de n'extraire que plus tard les dossiers et les fichiers ignorés auparavant.

Par exemple, imaginons un dépôt dont l'arborescence des fichiers et dossiers est constituée des noms des membres d'une famille et de leurs animaux de compagnie (c'est assurément un exemple bizarre, mais soit). Un svn checkout standard nous donne une copie de travail de l'ensemble de l'arborescence :

$ svn checkout file:///var/svn/depot maman
A    maman/fils
A    maman/fils/petit-fils
A    maman/fille
A    maman/fille/petite-fille1
A    maman/fille/petite-fille1/lapinou1.txt
A    maman/fille/petite-fille1/lapinou2.txt
A    maman/fille/petite-fille2
A    maman/fille/poissonou.txt
A    maman/minou1.txt
A    maman/toutou1.txt
Révision 1 extraite.
$

Maintenant, extrayons la même arborescence, mais cette fois en demandant à Subversion de nous donner uniquement le dossier racine sans les enfants :

$ svn checkout file:///var/svn/depot maman-vide --depth empty
Révision 1 extraite.
$

Remarquez que nous avons ajouté l'option --depth à la commande svn checkout originale. Cette option existe pour de nombreuses sous-commandes Subversion et est similaire aux options --non-recursive (-N) et --recursive (-R). En fait, elle combine, améliore, remplace et, à terme, rend obsolète ces deux options plus anciennes. Déjà, elle permet à l'utilisateur de spécifier le niveau de profondeur de la récursion de façon plus précise, en ajoutant des niveaux auparavant non supportés (ou supportés de manière peu satisfaisante). Voici les valeurs de niveau de profondeur de récursion que vous pouvez ajouter à vos requêtes Subversion :

--depth empty

Inclut uniquement la cible immédiate de l'opération, sans aucun fichier ou dossier fils.

--depth files

Inclut la cible immédiate de l'opération et tous les fichiers fils immédiats.

--depth immediates

Inclut la cible immédiate de l'opération et tous ses sous-dossiers et fils immédiats. Les dossiers fils seront eux-mêmes vides.

--depth infinity

Inclut la cible immédiate, les fichiers et dossiers fils, les fils des fils et ainsi de suite de façon à réaliser une récursion complète.

Bien sûr, la simple combinaison de deux options existantes en une seule ne constitue pas une nouvelle fonctionnalité méritant une section complète de ce livre. Heureusement, il y a plus à en dire. Ce concept de profondeur de récursion ne s'applique pas uniquement aux opérations réalisées avec le client Subversion mais il s'étend aussi à la description de la copie de travail elle-même, en tant que niveau associé de manière permanente par la copie de travail à chaque élément. La force de ce concept est cette permanence. La copie de travail se rappelle le niveau de profondeur que vous avez choisi pour chaque élément qui la compose, jusqu'à ce que vous en changiez. Par défaut, les commandes Subversion agissent sur les éléments présents dans la copie de travail, indépendamment de leur niveau de récursion propre.

[Astuce]Astuce

Vous pouvez vérifier le niveau de profondeur d'une copie de travail en utilisant la commande svn info. Si le niveau de récursion n'est pas infini, svn info affiche une ligne indiquant le niveau de profondeur :

$ svn info maman-immediats | grep '^Profondeur :'
Profondeur : immédiates
$

Ces premiers exemples comportaient des extractions avec un niveau infini de profondeur (la valeur par défaut de svn checkout) ou avec un niveau nul. Voyons maintenant des exemples avec d'autres valeurs de niveau de profondeur :

$ svn checkout file:///var/svn/depot maman-fichiers --depth files
A    maman-fichiers/minou1.txt
A    maman-fichiers/toutou1.txt
Révision 1 extraite.
$ svn checkout file:///var/svn/depot maman-immediats --depth immediates
A    maman-immediats/fils
A    maman-immediats/fille
A    maman-immediats/minou1.txt
A    maman-immediats/toutou1.txt
Révision 1 extraite.
$

Comme indiqué, chacun de ces deux niveaux se situe quelque part entre la cible toute simple et la récursion complète.

Nous avons utilisé la commande svn checkout pour nos exemples, mais l'option --depth est également accessible depuis beaucoup d'autres commandes Subversion. Pour ces autres commandes, spécifier un niveau de profondeur de la récursion est une manière de limiter le rayon d'action d'une opération à un niveau, à l'instar des vieilles options --non-recursive (-N) et --recursive (-R). Cela veut dire que lorsque vous travaillez sur une copie de travail d'un certain niveau et que vous faites une opération sur un niveau plus faible, l'opération est limitée à ce niveau faible. En fait, on peut généraliser ce raisonnement : pour une copie de travail d'un niveau de profondeur de récursion arbitraire (éventuellement hétérogène) et pour une commande Subversion comportant un niveau de profondeur, la commande conserve le niveau de récursion associé aux éléments de la copie de travail tout en limitant le rayon d'action de l'opération au niveau demandé (ou celui par défaut).

En plus de l'option --depth, les sous-commandes svn update et svn switch acceptent une deuxième option relative au niveau de profondeur de la récursion : --set-depth. C'est cette option qui vous permet de changer le niveau de profondeur de la récursion associé à un élément d'une copie de travail. Regardez ce qui se passe après avoir extrait notre niveau zéro puis graduellement augmenté le niveau de profondeur de la récursion en utilisant la commande svn update --set-depth NOUVELLE-PROFONDEUR CIBLE:

$ svn update --set-depth files maman-vide
A    maman-vide/minou1.txt
A    maman-vide/toutou1.txt
Actualisé à la révision 1. 
$ svn update --set-depth immediates maman-vide
A    maman-vide/fils
A    maman-vide/fille
Actualisé à la révision 1.
$ svn update --set-depth infinity maman-vide
A    maman-vide/fils/petit-fils
A    maman-vide/fille/petite-fille1
A    maman-vide/fille/petite-fille1/lapinou1.txt
A    maman-vide/fille/petite-fille1/lapinou2.txt
A    maman-vide/fille/petite-fille2
A    maman-vide/fille/poissonou1.txt
Actualisé à la révision 1.
$

Au fur et à mesure que nous avons augmenté le niveau de profondeur de la récursion, le dépôt a complété progressivement notre arborescence.

Dans notre exemple, nous n'avons agi que sur la racine de notre copie de travail, en changeant la valeur du niveau associé de profondeur de la récursion. Mais nous pouvons aussi changer de façon indépendante le niveau associé de profondeur de la récursion à chaque sous-dossier de la copie de travail. Une utilisation minutieuse de cette option nous permet de récupérer uniquement certaines portions de la copie de travail, en laissant de côté toutes les autres portions (d'où le nom « clairsemé » de la fonctionnalité). Voici un exemple montrant comment construire une portion d'une branche de notre arbre généalogique, activer la récursion totale sur une autre branche et élaguer le reste (qui ne sera donc pas sur notre disque dur).

$ rm -rf maman-vide
$ svn checkout file:///var/svn/depot maman-vide --depth empty
Révision 1 extraite.
$ svn update --set-depth empty maman-vide/fils
A    maman-vide/fils
Actualisé à la révision 1.
$ svn update --set-depth empty maman-vide/fille
A    maman-vide/fille
Actualisé à la révision 1.
$ svn update --set-depth infinity maman-vide/fille/petite-fille1
A    maman-vide/fille/petite-fille1
A    maman-vide/fille/petite-fille1/lapinou1.txt
A    maman-vide/fille/petite-fille1/lapinou2.txt
Actualisé à la révision 1.
$

Heureusement, même avec différents niveaux de récursion définis au sein d'une même copie de travail, les actions sur la copie de travail ne s'en trouvent pas plus compliquées. Vous pouvez toujours effectuer des modifications et les propager, revenir en arrière ou afficher les modifications locales de votre copie de travail sans spécifier d'option particulière (y compris --depth et --set-depth) aux dites commandes. Même svn update fonctionne normalement quand on ne lui fournit pas de niveau de récursion spécifique : elle met à jour les cibles de la copie de travail qui sont présentes en tenant compte des niveaux de récursion qui leur sont associés.

Vous devez vous demander : « Bien. Mais quand aurais-je besoin d'utiliser ça ? » Un cas classique est lié à une architecture du dépôt particulière : lorsque de nombreux projets et modules logiciels liés cohabitent au même niveau dans un dépôt (trunk/projet1, trunk/projet2, trunk/projet3, etc.). Dans de tels scénarios, il est probable que seuls quelques projets vous intéressent personnellement, sans doute pas plus d'un projet principal et de quelques autres modules dont il dépend. Vous pouvez extraire une copie de travail pour chacune de ces arborescences, mais ces copies de travail sont séparées et, par conséquent, il peut être fastidieux d'effectuer des opérations sur plusieurs ou sur l'ensemble des copies de travail en même temps. L'autre solution est d'utiliser la fonctionnalité de répertoires clairsemés, en construisant une seule copie de travail qui ne contient que les modules qui vous intéressent. Vous partez d'une extraction du dossier parent commun aux différents projets avec un niveau zéro de profondeur de récursion (empty-depth), puis vous mettez à jour avec un niveau infini de récursion les éléments que vous voulez récupérer, comme nous l'avons fait dans l'exemple précédent. Voyez ça comme un système d'inclusion optionnelle des éléments qui peuplent la copie de travail.

L'implémentation des extractions superficielles de Subversion 1.5 était relativement bonne mais elle ne permettait pas de diminuer le niveau de profondeur de récursion d'un élément de la copie de travail. Subversion 1.6 pallie ce problème. Par exemple, si vous lancez svn update --set-depth empty sur une copie de travail de niveau de récursion infini, vous ne conserverez que le dossier sommital[27]. Subversion 1.6 introduit également une autre valeur de profondeur de récursion pour l'option --set-depth : exclude. En indiquant --set-depth exclude à svn update, la cible de la mise à jour sera entièrement supprimée de la copie de travail, le dossier cible lui-même étant lui aussi supprimé. C'est utile lorsqu'il y a davantage d'éléments de la copie de travail que vous voulez garder par rapport à ce que vous voulez supprimer.

Considérons un dossier avec des centaines de sous-dossiers, dont un que vous ne voulez pas voir dans votre copie de travail. En utilisant une approche « additive » des dossiers clairsemés, vous pourriez l'extraire avec un niveau de profondeur nul, puis explicitement augmenter (en utilisant svn update --set-depth infinity) le niveau de profondeur pour tous les autres sous-dossiers.

$ svn checkout http://svn.exemple.com/depot/plein-de-repertoires --depth empty
…
$ svn update --set-depth infinity plein-de-repertoires/rep-voulu-1
…
$ svn update --set-depth infinity plein-de-repertoires/rep-voulu-2
…
$ svn update --set-depth infinity plein-de-repertoires/rep-voulu-3
…
### etc., etc.

C'est peut-être un peu fastidieux, d'autant que vous n'avez pas les « souches » de ces dossiers dans votre copie de travail pour les manipuler. Une telle copie de travail aurait de plus une caractéristique que vous ne soupçonnez pas ou ne voulez pas : si quelqu'un d'autre crée un sous-dossier dans le dossier racine, vous ne le recevrez pas lors de vos mises à jour.

À partir de Subversion 1.6, vous pouvez adopter une approche différente. En premier lieu, faites une extraction du dossier en entier. Ensuite, lancez svn update --set-depth exclude sur le dossier que vous ne voulez pas voir.

$ svn checkout http://svn.exemple.com/depot/plein-de-repertoires
…
$ svn update --set-depth exclude plein-de-repertoires/j-en-veux-pas
D         plein-de-repertoires/j-en-veux-pas
$

Cette approche laisse votre copie de travail avec le même contenu que la première approche, mais si un sous-dossier est créé au niveau du dossier racine, il apparaîtra lors de la mise à jour de votre copie de travail. L'inconvénient de cette approche est que vous devez extraire tout le sous-dossier que vous ne voulez pas garder afin de pouvoir dire à Subversion que vous n'en voulez pas. Il n'est pas impossible que ce sous-dossier soit trop gros pour votre disque dur (ce qui pourrait expliquer, après tout, que vous n'en voulez pas dans votre copie de travail).

[Note]Note

Alors qu'exclure un élément existant de la copie de travail ne fait pas partie de la commande svn update, vous avez sans doute remarqué que la sortie de svn update --set-depth exclude diffère de celle d'une mise à jour normale. Cette sortie trahit le fait que, sous le capot, l'exclusion est une opération réalisée entièrement du côté du client, à l'opposé d'une mise à jour classique.

Dans une telle situation, vous pouvez adopter une approche intermédiaire. D'abord, faites une extraction du dossier racine avec l'option --depth immediates. Puis, faites une extraction du dossier que vous ne souhaitez pas conserver avec svn update --set-depth exclude. Enfin, récupérez tous les éléments qui restent à un niveau de profondeur infini, ce qui relativement facile puisque tous sont visibles par votre interpréteur de commandes.

$ svn checkout http://svn.exemple.com/depot/plein-de-repertoires --depth immediates
…
$ svn update --set-depth exclude plein-de-repertoires/j-en-veux-pas
D         plein-de-repertoires/j-en-veux-pas
$ svn update --set-depth infinity plein-de-repertoires/*
…
$

Cette fois encore, votre copie de travail contiendra la même chose que dans les deux cas précédents. Mais maintenant, chaque fois qu'un nouveau fichier ou sous-dossier sera propagé au niveau du dossier racine, vous le recevrez (à un niveau de profondeur nul) au moment de votre mise à jour. Vous pouvez décider à ce stade ce que vous voulez faire avec ce nouvel élément : le récupérer avec un niveau de profondeur infini ou l'exclure.

Verrouillage

Le modèle copier-modifier-fusionner de gestion de versions de Subversion repose sur ses algorithmes de fusion, notamment sur la manière dont ils gèrent les conflits quand de multiples collaborateurs modifient le même fichier simultanément. Subversion lui-même ne propose qu'un seul algorithme de ce type, un algorithme qui détecte les modifications par trois méthodes et qui est suffisamment intelligent pour gérer les données à la ligne près. Subversion vous permet également d'utiliser en plus des outils externes lors du processus de fusion (comme indiqué dans la section intitulée « Programmes externes de comparaison de trois fichiers » et la section intitulée « Outils de fusion externes »), parfois encore meilleurs que ceux inclus dans Subversion, proposant par exemple une granularité plus fine allant jusqu'au mot, voire au caractère, au lieu de s'arrêter à la ligne. Mais, en règle générale, ces algorithmes ne fonctionnent que sur des fichiers texte. Le paysage est beaucoup plus sombre lorsque l'on recherche des outils de fusion pour des formats de fichiers non-texte. Et quand vous ne trouvez pas d'outil capable de fusionner de tels fichiers, les limites du modèle copier-modifier-fusionner se font vite sentir.

Prenons un exemple de la vie réelle où ce type de problème apparaît. Harry et Sally sont deux graphistes travaillant sur le même projet (du marketing pour le patron d'un garage). Au cœur d'une affiche de ce projet se trouve l'image d'une voiture dont la carrosserie a besoin d'être réparée, stockée dans un fichier image au format PNG. L'agencement de l'affiche est pratiquement terminé, et Harry et Sally sont contents de la photo qu'ils ont choisie pour leur voiture endommagée : une Ford Mustang bleue de 1967, avec un gnon sur l'aile avant gauche.

C'est alors, comme c'est souvent le cas dans le domaine du graphisme, que des contraintes extérieures imposent de changer la couleur de la voiture. Sally met donc à jour sa copie de travail à la révision HEAD, lance son outil d'édition de photos et commence à modifier la photo de manière à obtenir une voiture rouge cerise. Pendant ce temps, Harry, particulièrement inspiré ce jour-là, décide que l'image serait plus percutante si la voiture était davantage endommagée. Lui aussi met à jour sa copie de travail à la révision HEAD, puis dessine des fissures sur le pare-brise. Il termine son travail avant que Sally ne termine le sien, admire son chef-d'œuvre et propage les changements. Peu après, Sally en termine avec la nouvelle couleur de la voiture et essaie de propager ses modifications. Mais, comme prévu, Subversion ne parvient pas à valider la propagation et informe Sally que sa version de l'image est dorénavant obsolète.

Voilà où résident les difficultés : si Harry et Sally avaient effectué leurs changements sur un fichier texte, Sally aurait simplement mis à jour sa copie de travail, recevant au passage les modifications de Harry. Dans le pire des cas, ils auraient modifié la même portion du fichier et Sally aurait eu à résoudre les conflits manuellement. Mais, ici, nous avons affaire à des images binaires, pas des fichiers texte. Et s'il est relativement facile de décrire ce que devrait être l'image finale, il y a très peu de chances qu'un logiciel soit suffisamment intelligent pour détecter les parties communes de l'image sur laquelle les artistes ont travaillé, les changements effectués par Harry et les changements effectués par Sally et pour en tirer une image d'une Mustang rouge avec un pare-brise fissuré !

Clairement, les choses se seraient mieux passées si Harry et Sally avaient sérialisé leurs modifications : par exemple, si Harry avait attendu et dessiné ses fissures sur la voiture nouvellement rouge de Sally, ou si Sally avait changé la couleur d'une voiture avec un pare-brise déjà fissuré. Comme indiqué dans la section intitulée « Modèle copier-modifier-fusionner », la plupart de ces problèmes disparaissent complètement quand une communication parfaite existe entre Harry et Sally [28]. Mais comme un système de gestion de versions est en fait un mode de communication, il s'ensuit que si ce type de logiciel facilite la sérialisation de tâches d'édition non parallélisables, c'est plutôt une bonne chose. C'est ici que l'implémentation du concept verrouiller-modifier-libérer dans Subversion prend tout son sens. Il est temps de parler de la fonctionnalité de verrouillage de Subversion, qui est similaire aux mécanismes permettant de « réserver pour modifications » des fichiers dans d'autres systèmes de gestion de versions.

En fin de compte, la fonctionnalité de verrouillage existe afin de minimiser les pertes de temps et les efforts. En autorisant un utilisateur à s'arroger logiciellement le droit exclusif de modifier un fichier dans le dépôt, cet utilisateur peut être suffisamment confiant dans le fait que son travail ne sera pas vain — la propagation de ses changements réussira. Aussi, en signifiant aux autres utilisateurs qu'une sérialisation a lieu pour un objet suivi en versions, ces utilisateurs peuvent raisonnablement s'attendre à ce que cet objet soit modifié par quelqu'un d'autre. Eux aussi peuvent alors éviter de perdre leur temps et leur énergie sur des modifications qui ne peuvent pas être fusionnées en raison d'un problème d'obsolescence du fichier correspondant.

La fonctionnalité de verrouillage de Subversion comporte en fait plusieurs facettes, qui permettent entre autres de verrouiller un fichier suivi en versions[29] (demander le droit exclusif de modification sur le fichier), de le déverrouiller (abandonner le droit exclusif de modification), de voir la liste des fichiers qui sont verrouillés et par qui, d'annoter des fichiers pour lesquels le verrouillage est fortement recommandé avant édition, etc. Dans cette section, nous abordons toutes les facettes de cette fonctionnalité de verrouillage.

Création d'un verrou

Dans un dépôt Subversion, un verrou est une méta-donnée qui alloue à un utilisateur un accès exclusif en écriture sur un fichier. Cet utilisateur est appelé détenteur du verrou. Chaque verrou possède également un identifiant unique, en général une longue chaîne de caractères, appelé jeton de verrouillage. Le dépôt gère les verrous en assurant in fine leur création, leur application et leur suppression. Si une propagation tente de modifier ou effacer un fichier verrouillé (ou effacer un dossier parent dudit fichier), le dépôt demande deux informations : que le client effectuant la propagation s'authentifie en tant que détenteur du verrou et que le jeton de verrouillage soit fourni lors de la procédure de propagation afin de montrer que le client sait bien quel verrou il utilise.

Pour illustrer la création d'un verrou, reprenons notre exemple de graphistes travaillant sur les mêmes fichiers images binaires. Harry a décidé de changer cette image JPEG. Pour interdire aux autres collaborateurs d'effectuer des changements sur le fichier pendant qu'il le modifie (et pour les avertir qu'il va modifier ce fichier), il verrouille le fichier dans le dépôt en utilisant la commande svn lock :

$ svn lock banane.jpg -m "Édition du fichier pour la livraison de demain."
'banane.jpg' verrouillé par l'utilisateur 'harry'.
$

Il y a plusieurs points intéressants dans l'exemple ci-dessus : d'abord, notez que Harry utilise l'option --message (-m) de svn lock. Comme svn commit, la commande svn lock accepte des commentaires (soit via --message (-m), soit via --file (-F)) pour indiquer la raison du verrouillage du fichier. En revanche, contrairement à svn commit, svn lock n'exige pas automatiquement un message en lançant votre éditeur de texte préféré. Les commentaires de verrouillage sont optionnels, mais néanmoins recommandés pour faciliter la communication entre collaborateurs.

Ensuite, la tentative de verrouillage a réussi. Cela signifie que le fichier n'était pas préalablement verrouillé et que Harry disposait de la dernière version du fichier. Si la copie de travail de Harry avait été obsolète, le dépôt aurait refusé la demande, forçant Harry à effectuer une mise à jour (svn update) et à relancer ensuite la commande de verrouillage. La commande de verrouillage aurait également échoué si le fichier avait déjà été verrouillé par quelqu'un d'autre.

Comme vous pouvez le constater, la commande svn lock affiche la confirmation que le verrouillage a réussi. Dès lors, le verrouillage du fichier apparaît dans le résultat des commandes svn status et svn info :

$ svn status
     K banane.jpg

$ svn info banane.jpg
Chemin : banane.jpg
Nom : banane.jpg
URL : http://svn.exemple.com/depot/projet/banane.jpg
Racine du dépôt : http://svn.exemple.com/depot
UUID du dépôt : edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 2198
Type de nœud : file
Tâche programmée : normale
Auteur de la dernière modification : frank
Révision de la dernière modification : 1950
Date de la dernière modification : 2006-03-15 12:43:04 -0600 (mer. 15 mars 2006)
Texte mis à jour : 2006-06-08 19:23:07 -0500 (jeu. 08 juin 2006)
Propriétés mis à jour : 2006-06-08 19:23:07 -0500 (jeu. 08 juin 2006)
Somme de contrôle: 3b110d3b10638f5d1f4fe0f436a5a2a5
Nom de verrou : opaquelocktoken:0c0f600b-88f9-0310-9e48-355b44d4a58e
Propriétaire du verrou : harry
Verrou créé : 2006-06-14 17:20:31 -0500 (mer. 14 juin 2006)
Commentaire du verrou (1 ligne):
Édition du fichier pour la livraison de demain.

$

Le fait que la commande svn info, qui ne contacte pas le dépôt quand elle porte sur un chemin d'une copie de travail, affiche bien le jeton de verrouillage, révèle une caractéristique importante des jetons de verrouillage : ils sont intégrés dans la copie de travail. La présence du jeton de verrouillage est primordiale. Il authorise la copie de travail à utiliser le verrou ultérieurement. Par ailleurs, la commande svn status affiche un K (raccourci pour locKed« verrouillé » en anglais) avant le nom du fichier, indiquant que le jeton de verrouillage est présent.

Maintenant que Harry a verrouillé banane.jpg, Sally ne peut ni modifier ni effacer ce fichier :

$ svn delete banane.jpg
D         banane.jpg
$ svn commit -m "Suppression des fichiers inutiles."
Suppression    banane.jpg
svn: E175002: Échec de la propagation (commit), (détails):
svn: E175002: Suppression de '/depot/projet/!svn/wrk/64bad3a9-96f9-0310-818a-df4224ddc35d/banane.jpg':
423 Verrouillé (http://svn.exemple.com)
$

Mais Harry, après avoir fait ses retouches sur sa belle banane jaune, peut propager ses changements sur le fichier. C'est parce qu'il s'authentifie en tant que détenteur du verrou et aussi parce que sa copie de travail possède le bon jeton de verrouillage :

$ svn status
M    K banane.jpg
$ svn commit -m "Rendu la banane plus jaune."
Envoi        banane.jpg
Transmission des données .
Révision 2201 propagée.
$ svn status
$

Notez qu'après que la propagation est terminée, svn status permet de voir que le jeton de verrouillage n'est plus présent dans la copie de travail. C'est le comportement normal de svn commit : elle recherche dans la copie de travail (ou dans une liste de cibles, si vous fournissez une telle liste) les modifications effectuées localement et elle envoie les jetons de verrouillage qu'elle trouve durant sa recherche au serveur, en tant que partie intégrante du processus de propagation. Après que la propagation a réussi, tous les verrous du dépôt qui ont été mentionnés sont libérés, même ceux pour lesquels les fichiers n'ont pas été propagés. Ce comportement a pour but de dissuader les utilisateurs d'être négligents avec leurs verrous ou de garder des verrous trop longtemps. Si Harry verrouille au hasard trente fichiers dans un répertoire nommé Images parce qu'il n'est pas sûr de savoir quels fichiers il doit modifier et qu'il ne modifie finalement que quatre fichiers, alors quand il lance la commande svn commit Images, la procédure libère les trente verrous.

Ce mode de fonctionnement (libérer automatiquement les verrous) peut être modifié avec l'option --no-unlock de svn commit. C'est utile quand vous voulez propager des changements mais que vous prévoyez d'effectuer des changements supplémentaires et que donc vous avez toujours besoin des verrous. Vous pouvez également en faire le fonctionnement par défaut en réglant l'option no-unlock dans la zone de configuration (voir la section intitulée « Zone de configuration des exécutables »).

Bien sûr, verrouiller un fichier n'oblige pas l'utilisateur à propager une modification sur ce modifier. Le verrou peut être libéré n'importe quand avec la commande svn unlock :

$ svn unlock banane.c
'banane.c' déverrouillé.

Identification d'un verrou

Quand une propagation échoue parce que quelqu'un d'autre a posé un verrou, il est facile de savoir pourquoi. La commande la plus simple est svn status -u:

$ svn status -u
M               23   truc.c
M    O          32   raisin.jpg
        *       72   machin.h
État par rapport à la révision      105

$

Dans cet exemple, Sally peut voir que non seulement sa copie de travail de machin.h n'est plus à jour, mais aussi qu'un des deux fichiers qu'elle prévoie de propager est verrouillé dans le dépôt. La lettre O (Others — autres en anglais) indique qu'un verrou est posé sur ce fichier et qu'il a été créé par quelqu'un d'autre. Si elle essayait de lancer svn commit, le verrou sur raisin.jpg l'en empêcherait. Sally est laissée dans l'expectative de savoir qui a posé le verrou, quand et pourquoi. Là encore, svn info trouve la réponse :

$ svn info ^/raisin.jpg 
Chemin : raisin.jpg
Nom : raisin.jpg
URL: http://svn.exemple.com/depot/projet/raisin.jpg
UUID du dépôt : edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Révision: 105
Type de nœud : file
Auteur de la dernière modification : sally
Révision de la dernière modification : 32
Texte mis à jour : 2006-01-25 12:43:04 -0600 (Sun, 25 Jan 2006)
Nom de verrou : opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Propriétaire du verrou : harry
Verrou créé : 2006-02-16 13:29:18 -0500 (jeu. 16 févr. 2006)
Commentaire du verrou (1 ligne):
Besoin de faire une retouche rapide sur cette image.
$

De la même manière que svn info peut être utilisée pour examiner les objets de la copie de travail, elle peut être utilisée pour examiner les objets du dépôt. Si l'argument principal de svn info est un chemin de la copie de travail, alors toutes les informations stockées localement sont affichées ; toute mention d'un verrou signifie que la copie de travail détient un jeton de verrouillage (si le fichier est verrouillé par un autre utilisateur ou depuis une autre copie de travail, alors lancer svn info sur la copie de travail ne renvoie aucune information relative au verrou). Si l'argument principal de svn info est une URL, alors les informations affichées se rapportent à la dernière version de l'objet dans le dépôt et toute mention d'un verrou concerne le verrou en cours sur l'objet.

Ainsi, dans notre exemple, Sally peut voir que Harry a verrouillé le fichier le 16 février pour effectuer une « retouche rapide ». Comme nous sommes en juin, elle suspecte qu'il a probablement oublié le verrou. Elle pourrait téléphoner à Harry pour le lui signaler et lui demander de libérer le verrou. S'il n'est pas joignable, elle peut toujours essayer de forcer le verrou elle-même, ou demander à un administrateur de le faire.

Cassage et vol d'un verrou

Un verrou n'est pas quelque chose de sacré : dans la configuration par défaut de Subversion, les verrous peuvent être libérés non seulement par leur détenteur, mais aussi par n'importe qui d'autre. Quand quelqu'un d'autre que le détenteur d'un verrou le libère, nous appelons ça casser le verrou.

Avec un statut d'administrateur, il est facile de casser un verrou. Les programmes svnlook et svnadmin peuvent afficher et casser les verrous directement dans le dépôt (pour plus d'informations sur ces outils, reportez-vous à la section intitulée « Boîte à outils de l'administrateur »).

$ svnadmin lslocks /var/svn/depot
Chemin : /projet2/images/banane.jpg
Chaîne UUID : opaquelocktoken:c32b4d88-e8fb-2310-abb3-153ff1236923
Propriétaire : frank
Créé : 2006-06-15 13:29:18 -0500 (jeu. 15 juin 2006)
Expire :
Commentaire (1 ligne):
J'améliore encore la couleur jaune.

Chemin : /projet/raisin.jpg
Chaîne UUID : opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Propriétaire : harry
Créé : 2006-02-16 13:29:18 -0500 (jeu. 16 fév. 2006)
Expire :
Commentaire (1 ligne):
Besoin de faire une retouche rapide sur cette image.
  
$ svnadmin rmlocks /var/svn/depot /projet/raisin.jpg
'/projet/raisin.jpg' déverrouillé
$

L'option la plus intéressante est celle qui permet aux utilisateurs de casser les verrous détenus par d'autres personnes à travers le réseau. Pour ce faire, Sally doit simplement ajouter l'option --force à la commande svn unlock :

$ svn status -u
M               23   machin.c
M    O          32   raisin.jpg
        *       72   truc.h
État par rapport à la révision     105
$ svn unlock raisin.jpg
svn: E195013: 'raisin.jpg' n'est pas verrouillé dans cette copie de travail.
$ svn info raisin.jpg | grep ^URL 
URL: http://svn.exemple.com/depot/projet/raisin.jpg
$ svn unlock http://svn.exemple.com/depot/projet/raisin.jpg
svn: warning: W160039: Unlock failed  on 'raisin.jpg' (403 Forbidden)
$ svn unlock --force http://svn.exemple.com/depot/projet/raisin.jpg
'raisin.jpg' déverrouillé.
$

Ainsi, la tentative initiale de Sally pour libérer le verrou a échoué parce qu'elle a lancé svn unlock directement sur le fichier de sa copie de travail, où aucun jeton de verrouillage n'était présent. Pour casser le verrou directement dans le dépôt, elle doit passer une URL à svn unlock. Son premier essai pour casser le verrou avec l'URL échoue car elle ne peut pas s'authentifier comme détentrice du verrou (et elle n'a pas non plus le jeton de verrouillage). Mais quand elle passe l'option --force, les pré-requis d'authentification et d'autorisation sont ignorés et le verrou est cassé.

Casser le verrou peut ne pas être suffisant. Dans l'exemple, Sally ne veut pas seulement casser le verrou oublié par Harry, mais également re-verrouiller le fichier pour son propre usage. Elle peut le faire en lançant svn unlock avec l'option --force puis svn lock à la suite, mais il existe une petite chance que quelqu'un d'autre verrouille le fichier entre les deux commandes. La meilleure solution est donc de voler le verrou, ce qui implique de casser et re-verrouiller le fichier en une seule opération atomique. Pour ce faire, Sally passe l'option --force à la commande svn lock :

$ svn lock raisin.jpg 
svn: avertissement : W160035: Path '/project/raisin.jpg' is already locked by
user 'harry' in filesystem '/var/svn/repos/db'
$ svn lock --force raisin.jpg
 
'raisin.jpg' verrouillé par l'utilisateur 'sally'.
$

Dans tous les cas, que le verrou soit cassé ou volé, Harry est bon pour une bonne surprise. La copie de travail de Harry contient toujours le jeton de verrouillage original, mais le verrou n'existe plus. Le jeton de verrouillage est dit défunt. Le verrou associé au jeton de verrouillage a été soit cassé (il n'existe plus dans le dépôt) soit volé (remplacé par un autre verrou). Quoi qu'il en soit, Harry peut voir ce qu'il en est en demandant à svn status de contacter le dépôt :

$ svn status
     K  raisin.jpg
$ svn status -u
     B          32   raisin.jpg 
À la révision 105.
$ svn update
Updating '.':
  B  raisin.jpg 
À la révision 105.
$ svn status
$

Si le verrou dans le dépôt a été cassé, alors svn status --show-updates affiche un B (pour Broken« cassé » en anglais) à côté du fichier. Si un nouveau verrou existe en lieu et place de l'ancien, alors un T (pour sTolen« volé » en anglais) est affiché. Finalement, svn update détecte les jetons de verrouillage défunts et les supprime de la copie de travail.

Communication par l'intermédiaire des verrous

Nous avons vu comment svn lock et svn unlock peuvent être utilisés pour poser, libérer, casser ou voler des verrous. Cela résout le problème de la sérialisation des accès à un fichier. Mais qu'en est-il du problème plus vaste d'éviter les pertes de temps ?

Par exemple, supposons que Harry verrouille un fichier image et commence à l'éditer. Pendant ce temps, loin de là, Sally veut faire la même chose. Elle ne pense pas à faire un svn status -u et n'a donc pas la moindre idée que Harry a déjà verrouillé le fichier. Elle passe des heures à modifier le fichier et quand elle tente de propager ses changements, elle découvre soit que le fichier est verrouillé, soit que son propre fichier n'était pas à jour. Quoi qu'il en soit, ses modifications ne peuvent pas être fusionnées avec celles de Harry. L'un des deux doit passer ses modifications par pertes et profits, un temps conséquent a été gaspillé.

La solution proposée par Subversion à ce problème est de fournir un mécanisme pour rappeler aux utilisateurs qu'un fichier devrait être verrouillé avant de faire des modifications. Ce mécanisme est mis en œuvre par une propriété spéciale : svn:needs-lock. Si cette propriété est associée à un fichier (quelle que soit sa valeur, qui n'est pas prise en compte), alors Subversion essaie d'utiliser les permissions du système de fichiers pour le placer en lecture seule — à moins, bien sûr, que l'utilisateur ait explicitement verrouillé le fichier. Quand un jeton de verrouillage est présent (indiquant que svn lock a été lancée), le fichier est placé en lecture-écriture. Quand le verrou est libéré, le fichier passe de nouveau en lecture seule.

La théorie est donc que si le fichier image a cette propriété définie, alors Sally remarquera tout de suite quelque chose d'étrange à l'ouverture du fichier : beaucoup d'applications avertissent l'utilisateur immédiatement quand un fichier en lecture seule est ouvert pour édition et pratiquement toutes l'empêchent de sauvegarder ses modifications dans le fichier. Cela lui rappelle de verrouiller le fichier avant de l'éditer, découvrant ainsi le verrou pré-existant :

$ /usr/local/bin/gimp raisin.jpg 
gimp: erreur: le fichier est en lecture seule !
$ ls -l raisin.jpg
-r--r--r--   1 sally   sally   215589 juin  8 19:23 raisin.jpg
$ svn lock raisin.jpg
svn: avertissement : W160035: Échec de la demande de verrou : '/projet/raisin.jpg'
is already locked by user 'harry' in filesystem '/var/svn/depot/db'

$ svn info http://svn.exemple.com/depot/projet/raisin.jpg | grep errou
Nom de verrou : opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Propriétaire du verrou : harry
Verrou créé : 2006-06-08 07:29:18 -0500 (jeu. 08 juin 2006)
Commentaire de verrouillage (1 ligne):
J'effectue quelques retouches.  Je le verrouille pour deux heures.
$
[Astuce]Astuce

Les utilisateurs et les administrateurs sont tous encouragés à positionner la propriété svn:needs-lock sur les fichiers qui ne peuvent pas être contextuellement fusionnés. C'est la technique de base pour favoriser les bonnes habitudes de verrouillage et éviter les pertes de temps.

Notez que cette propriété est un outil de communication qui fonctionne indépendamment de la politique de verrouillage. Autrement dit, n'importe quel fichier peut être verrouillé, que cette propriété existe ou pas. Et réciproquement, l'existence de cette propriété ne rend pas obligatoire le verrouillage pour pouvoir propager des modifications.

Malheureusement, le système n'est pas parfait. Il est possible que, même si le fichier possède la propriété, l'avertissement de lecture seule ne marche pas. Quelquefois, les applications ne suivent pas les normes et « piratent » le fichier en lecture seule, autorisant sans rien dire l'utilisateur à modifier et sauvegarder le fichier. Subversion ne peut pas faire grand chose dans ce genre de cas : au final, rien ne remplace une bonne communication entre les membres d'une équipe [30].

Définition de références externes

Parfois il peut être utile de construire une copie de travail issue de différentes extractions. Par exemple, vous pouvez avoir envie d'avoir différents sous-répertoires provenant de différents endroits du dépôt ou même carrément de différents dépôts. Vous pouvez arriver à un tel enchevêtrement manuellement, en utilisant svn checkout, pour créer le genre de structure voulu pour votre copie de travail. Mais si cette configuration est importante pour tous les utilisateurs de votre dépôt, chacun doit effectuer les mêmes opérations d'extraction que vous.

Heureusement, Subversion supporte la définition de références externes. Une définition de référence externe est une association entre un répertoire local et une URL (et idéalement un numéro de révision particulier) pour un répertoire suivi en versions. Dans Subversion, vous déclarez les définitions de références externes dans des groupes en utilisant la propriété svn:externals. Vous pouvez créer et modifier cette propriété en utilisant svn propset ou svn propedit (voir la section intitulée « Manipuler les propriétés »). Elle peut être définie sur tous les répertoires suivis en versions et sa valeur décrit à la fois l'URL du dépôt externe et le répertoire côté client dans lequel est extrait cette URL.

L'un des attraits de la propriété svn:externals est qu'une fois qu'elle est définie pour un répertoire suivi en versions, chaque utilisateur qui extrait une copie de travail de ce répertoire bénéficie des définitions de références externes. En d'autres termes, une fois qu'un utilisateur a fait l'effort de définir la structure de la copie de travail imbriquée, tout le monde en bénéficie automatiquement : Subversion, lors de l'extraction de la copie de travail originale, extraie également les copies de travail externes.

[Avertissement]Avertissement

Les sous-dossiers cibles des définitions de références externes ne doivent pas déjà exister sur votre système ou sur le systèmes des autres utilisateurs : Subversion les crée lors de l'extraction des copies de travail externes.

Vous bénéficiez avec les définitions de références externes de tous les avantages liés aux propriétés Subversion. Les définitions sont suivies en versions. Si vous avez besoin de changer une définition de référence externe, vous pouvez le faire à l'aide des sous-commandes classiques sur les propriétés. Quand vous propagez des modifications relatives à la propriété svn:externals, Subversion synchronise les éléments extraits par rapport à la définition de références externes modifiée dès que vous lancez svn update. Tous ceux qui mettent à jour leur copie de travail reçoivent vos modifications concernant les définitions de références externes.

[Astuce]Astuce

Comme la valeur de la propriété svn:externals est constituée de plusieurs lignes, nous vous recommandons fortement d'utiliser svn propedit plutôt que svn propset.

Les versions de Subversion antérieures à 1.5 utilisent un format de définitions externes qui est un tableau sur plusieurs lignes composées de sous-dossiers (relativement au dossier suivi en versions sur lequel est définie la propriété), d'indicateurs de révision optionnels et l'URL, absolue et complètement qualifiée, du dépôt Subversion. Par exemple :

$ svn propget svn:externals calc
# Ressources suivies en versions ailleurs
tierce-partie/sons                 http://svn.exemple.com/depot/sons
tierce-partie/themes -r148         http://svn.exemple.com/projet-themes
tierce-partie/themes/outils -r21   http://svn.exemple.com/outils-themes
$

Dans l'exemple précédent, trois définitions externes sont définies. La première est « libre », elle fait référence à la révision HEAD de sa cible. Les deux autres ont des révisions spécifiques déclarées. L'exemple montre aussi que les lignes qui commencent par un croisillon (#) sont considérées comme des commentaires et ignorées dans l'analyse des définitions externes.

Quand quelqu'un extrait une copie de travail du dossier calc décrit dans l'exemple ci-dessus, Subversion extrait également les éléments trouvés dans les définitions de références externes.

$ svn checkout http://svn.exemple.com/depot/calc
A  calc
A  calc/Makefile
A  calc/entier.c
A  calc/bouton.c
Révision 148 extraite.

Récupération de la référence externe dans 'calc/tierce-partie/sons'
A  calc/tierce-partie/sons/ding.ogg
A  calc/tierce-partie/sons/dong.ogg
A  calc/tierce-partie/sons/clang.ogg
…
A  calc/tierce-partie/sons/bang.ogg
A  calc/tierce-partie/sons/twang.ogg
Révision 14 extraite.

Récupération de la référence externe dans 'calc/tierce-partie/themes'
…

À partir de la version 1.5 de Subversion, un nouveau format de la propriété svn:externals est supporté. Les références externes sont toujours multi-lignes mais l'ordre et le format des différentes informations ont changé. La nouvelle syntaxe ressemble plus à l'ordre des arguments que vous passez à la commande svn checkout : l'indicateur optionnel de révision est placé en premier, puis l'URL du dépôt Subversion externe et, enfin, le sous-dossier local relatif. Notez cependant que cette fois-ci nous n'avons pas indiqué « URL absolue et complètement qualifiée » pour le dépôt externe. En effet, le nouveau format accepte les URL relatives et les URL avec des révisions pivots. L'exemple précédent sur les références externes donne avec Subversion 1.5 :

$ svn propget svn:externals calc
# Ressources suivies en versions ailleurs
      http://svn.exemple.com/depot/sons     tierce-partie/sons
-r148 http://svn.exemple.com/projet-themes  tierce-partie/themes
-r21  http://svn.exemple.com/outils-themes  tierce-partie/themes/outils
$

En utilisant la syntaxe avec les révisions pivots (décrite en détail dans la section intitulée « Révisions pivots et révisions opérationnelles »), il peut aussi être écrit comme ceci :

$ svn propget svn:externals calc
# Ressources suivies en versions ailleurs
http://svn.exemple.com/depot/sons         tierce-partie/sons
http://svn.exemple.com/projet-themes@148  tierce-partie/themes
http://svn.exemple.com/outils-themes@21   tierce-partie/themes/outils
$
[Astuce]Astuce

Il est particulièrement conseillé d'utiliser des numéros de révision explicites dans toutes vos références externes. Ainsi, vous conservez la possibilité de décider quand rapatrier une nouvelle version de vos informations externes et quelle version exacte rapatrier. En plus de vous éviter la surprise de recevoir des changements effectués sur des dépôts tiers dont vous n'avez pas la maîtrise, l'utilisation de numéros de révisions explicites signifie aussi que, si vous revenez à une version de travail antérieure, vos références externes reviendront elles aussi dans l'état où elles étaient au moment de cette version antérieure. Cela signifie aussi que les copies de travail externes sont actualisées pour refléter leur état au moment de la révision antérieure. Pour des projets logiciels, cela peut faire la différence entre la réussite et l'échec de la compilation d'une version antérieure d'un code source complexe.

Pour la plupart des dépôts, les trois formats de références externes ont le même effet au final. Ils apportent tous les mêmes avantages. Malheureusement, ils possèdent aussi les mêmes inconvénients. Puisque les références indiquées utilisent des URL absolues, déplacer ou copier un répertoire auquel elles sont rattachées n'affecte pas ce qui est extrait en externe (alors qu'une référence relative est, bien évidemment, déplacée avec le répertoire). Cela peut vous induire en erreur, voire être assez frustrant, dans certaines situations. Par exemple, imaginons un dossier racine appelé mon-projet pour lequel nous avons défini des références externes dans un sous-dossier (mon-projet/un-rep) vers la dernière révision d'un autre sous-dossier (mon-projet/rep-externe).

$ svn checkout http://svn.exemple.com/projets .
A    mon-projet
A    mon-projet/un-rep
A    mon-projet/rep-externe
…
Récupération de la référence externe dans 'mon-projet/un-rep/sous-rep'
Référence externe actualisée à la révision 11.

Actualisé à la révision 11.
$ svn propget svn:externals mon-projet/un-rep
sous-rep http://svn.exemple.com/projets/mon-projet/rep-externe

$

Maintenant utilisez la commande svn move pour renommer le répertoire mon-projet. À ce moment là, vos définitions de références externes pointent toujours vers un chemin sous le répertoire mon-projet, même si ce répertoire n'existe plus.

 
$ svn move -q mon-projet nouveau-projet
$ svn commit -m "Renommé mon-projet en nouveau-projet."
Suppression    mon-projet
Ajout          nouveau-projet

Révision 12 propagée.
$ svn update
Mise à jour de '.' :
svn: warning: W200000: Erreur dans la récupération de la référence externe dans 'nouveau-projet/un-rep/sous-rep'
svn: warning: W170000: Le dépôt http://svn.exemple.com/projets/mon-projet/rep-externe n'existe pas
À la révision 12.
$

De plus, les URL absolues utilisées par les références externes peuvent causer des problèmes pour les dépôts accessibles via plusieurs types d'URL. Par exemple, si votre serveur Subversion est configuré pour autoriser tout le monde à consulter le dépôt via http:// ou https://, mais que les opérations de propagation doivent être effectuées uniquement via https://, vous vous retrouvez bien embêté. Si vos références externes pointent vers une URL de type http://, vous ne pouvez pas effectuer de propagation depuis les copies de travail créées via ces références externes. D'un autre côté, si vous utilisez la forme https:// pour les URL, ceux qui voudront effectuer des consultations via http://, parce que leur client ne sait pas traiter le https://, sont incapables de récupérer les éléments externes. Soyez conscient également que si vous avez besoin de déplacer toute votre copie de travail (avec svn switch et l'option --relocate), les références externes ne seront pas mises à jour en conséquence.

Subversion 1.5 franchit un grand pas dans la résolution de ces soucis. Comme indiqué précédemment, les URL utilisées dans le nouveau format des définitions des références externes peuvent être relatives. Par ailleurs, Subversion autorise une syntaxe magique pour spécifier plusieurs types d'URL relatives.

../

Relative à l'URL du répertoire sur lequel la propriété svn:externals est définie.

^/

Relative à la racine du dépôt pour lequel la propriété svn:externals est suivie en versions.

//

Relative au type d'URL du répertoire sur lequel la propriété svn:externals est définie.

/

Relative à l'URL du serveur sur lequel la propriété svn:externals est suivie en versions.

^/../NOM-DÉPÔT

Relative à un dépôt frère sous le même emplacement SVNParentPath-que le dépôt dans laquelle la référence svn:externals est définie.

Donc, considérons pour la quatrième fois la définition de nos références externes de l'exemple précédent et utilisons la nouvelle syntaxe de différentes manière. Nous obtenons :

$ svn propget svn:externals calc
# Ressources suivies en versions ailleurs
^/sons                             tierce-partie/sons
/themes@148                        tierce-partie/themes
//svn.exemple.com/outils-themes@21 tierce-partie/themes/outils
$

Subversion 1.6 apporta deux nouvelles améliorations aux définitions de références externes. D'abord, il ajouta un mécanisme d'échappement et mise entre guillemets à la syntaxe afin de traiter correctement des chemins de copies de travail externes contenant des espaces. Cela posait auparavant des problèmes car l'espace est un délimiteur de champs dans les définitions de références externes. Maintenant, vous n'avez qu'à entourer la spécification du chemin entre des guillemets doubles (") ou échapper les caractères qui posent problème dans le chemin avec la barre oblique inversée (\). Bien sûr, si l'URL de la référence externe comporte des espaces, vous devrez utiliser l'encodage standard des URI pour les représenter.

$ svn propget svn:externals paint 
http://svn.site-tiers.fr/depot/Mon%20projet "Mon projet"
http://svn.site-tiers.fr/depot/%22Avec%20des%20guillemets%22 \"Avec\ des\ guillemets\"
$

Subversion 1.6 introduit également le support pour la définition de références externes de fichiers. Les définitions de références externes de fichiers sont configurées de la même manière que les répertoires externes et apparaissent comme des fichiers suivis en versions dans la copie de travail.

Par exemple, supposons que vous ayez le fichier /trunk/couleurs_vélo/bleu.html dans votre dépôt et que vous souhaitiez que ce fichier, tel qu'il était dans la révision 40, apparaisse dans votre copie de travail de /trunk/www/ comme le fichier vert.html.

La définition de référence externe nécessaire pour obtenir ce résultat devrait vous être familière maintenant :

$ svn propget svn:externals www/
^/trunk/couleurs_vélo/bleu.html@40 vert.html
$ svn update
Mise à jour de '.' :

Récupération de la référence externe dans 'www' :
E    www/vert.html
Référence externe à la révision 40.

À la révision 103.
$ svn status
    X   www/vert.html
$

Comme vous pouvez le voir dans l'exemple ci-dessus, Subversion annote les fichiers externes avec la lettre E quand ils sont rappatriés vers la copie de travail et avec la lettre X lors de l'affichage de l'état de la copie de travail.

[Avertissement]Avertissement

Alors que les définitions de références externes pour un répertoire peuvent placer le répertoire à n'importe quelle profondeur et créer les répertoires intermédiaires manquants, les définitions de fichiers externes doivent être placées dans une copie de travail qui a déjà fait l'objet d'une extraction.

Lorsque vous examinez un fichier externe à l'aide de la commande svn info, vous pouvez voir l'URL et la révision qui sont à l'origine du fichier.

$ svn info www/vert.html
Chemin : www/vert.html
Nom : green.html
Chemin racine de la copie de travail : /home/harry/projects/my-project
URL : http://svn.example.com/projects/my-project/trunk/bikeshed/blue.html
Relative URL: ^/trunk/bikeshed/blue.html
Racine du dépôt : http://svn.example.com/projects/my-project
UUID du dépôt : b2a368dc-7564-11de-bb2b-113435390e17
Révision: 40
Type de nœud : fichier
Tâche programmée : normale
Auteur de la dernière modification : harry
Révision de la dernière modification : 40
Date de la dernière modification : 2009-07-20 20:38:20 +0100 (lun. 20 juil. 2009)
Texte mis à jour : 2009-07-20 23:22:36 +0100 (lun. 20 juil. 2009)
Somme de contrôle : 01a58b04617b92492d99662c3837b33b
$

Parce que les fichier externes sont traités dans la copie de travail comme des fichiers suivis en versions, ils peuvent être modifiés et même propagés s'ils font référence à un fichier à la révision HEAD. Les modifications propagées sont répercutées dans le fichier externe comme dans le fichier référencé. Cependant, dans notre exemple, nous avons pointé vers un fichier à une révision antérieure, c'est pourquoi une tentative de propagation du fichier externe échoue :

$ svn status 
M   X   www/vert.html
$ svn commit -m "change la couleur" www/vert.html
Envoi        www/vert.html
svn: E155011: Échec de la propagation (commit), détails :
svn: E155011: Fichier '/trunk/couleurs_vélo/bleu.html' obsolète
$

Gardez cela à l'esprit quand vous définissez des références vers des fichiers externes. Si vous avez besoin de pointer vers une révision particulière d'un fichier, vous ne pourrez pas modifier ce fichier externe. Si vous voulez pouvoir modifier le fichier externe, vous ne pouvez pas spécifier une révision autre que HEAD, ce qui est implicite si aucune révision n'est spécifiée.

Malheureusement, le support des références externes de Subversion reste largement perfectible. Les fichiers et les répertoires externes possèdent leur lot d'inconvénients. Quel que soit le type de référence externe, le sous-répertoire local faisant partie de la définition ne peut pas contenir d'indicateurs vers le répertoire parent « .. » (par exemple ../../skins/perso). Les fichiers externes ne peuvent pas faire référence à des fichiers d'autres dépôts. L'URL d'un fichier externe doit toujours être dans le même dépôt que sa cible. Aussi, les fichiers externes ne peuvent pas être déplacés ou effacés. C'est la propriété svn:externals qui doit être modifiée à la place. Cependant, les fichiers externes peuvent être copiés.

Ce qui est peut-être le plus ennuyeux, c'est que les copies de travail créées via le support de définition de références externes sont toujours déconnectées de la copie de travail primaire (sur laquelle le répertoire suivi en versions possède la propriété svn:externals). Et Subversion continue à fonctionner seulement sur des copies de travail conjointes. Ainsi, par exemple, si vous voulez propager des modifications que vous avez faites sur une ou plus de ces copies de travail externes, vous devez lancer svn commit explicitement sur ces copies de travail (la propagation sur la copie de travail initiale ne se répercute pas sur les copies externes).

Nous avons déjà mentionné quelques inconvénients de l'ancienne syntaxe svn:externals et comment la nouvelle syntaxe des versions plus récentes que Subversion 1.5 résolvent ce problème. Mais soyez vigilant lorsque vous utilisez cette nouvelle syntaxe de ne pas introduire de nouveaux problèmes par inadvertance. Par exemple, alors que les nouveaux clients sont capables de reconnaitre et prendre en charge l'ancienne syntaxe de défintion des références externes, les clients pré-1.5 ne peuvent pas analyser correctement la nouvelle syntaxe. Si vous changez vos définitions de références externes au nouveau format, vous forcez tous ceux qui utilisent ces références externes à effectivement mettre à niveau leur client Subversion vers une version capable de lire ce format. De même, soyez attentif à ne pas naïvement déplacer la portion -rNNN de la définition (l'ancien format utilise cette révision comme pivot, mais le nouveau format l'utilise comme révision opérationnelle avec une révision pivot à HEAD, sauf mention contraire  lisez la section intitulée « Révisions pivots et révisions opérationnelles » pour une explication complète de cette distinction).

[Avertissement]Avertissement

Les copies de travail externes sont toujours des copies de travail totalement auto-suffisantes. Vous pouvez effectuez les opérations que vous effectueriez sur toute autre copie de travail. Cela peut s'avérer très utile, vous permettant d'examiner une copie de travail externe indépendamment de toute copie de travail primaire dont la propriété svn:externals a causé l'instanciation. Faites attention, cependant, à ne pas modifier par mégarde votre copie de travail externe par des modifications subtiles qui peuvent engendrer des problèmes. Par exemple, bien que des définitions de références externes peuvent spécifier que la copie de travail externe pointe vers un numéro de révision particulier, si vous lancez la commande svn update directement sur la copie de travail externe, Subversion s'exécutera et votre copie de travail sera maintenant désynchronisée de la déclaration dans la copie de travail primaire. Utiliser svn switch pour faire pointer directement la copie de travail externe vers une nouvelle URL engendre le même type de problème si le contenu de la copie de travail primaire s'attend à un contenu particulier de la copie de travail externe.

Tout comme les commandes svn checkout, svn update, svn switch et svn export qui gèrent les sous-répertoires disjoints (ou déconnectés) depuis lesquels les références externes sont extraites, la commande svn status reconnait les définitions de références externes. Elle affiche un statut sous le code X pour les sous-répertoires externes disjoints, puis explore récursivement ces sous-répertoires pour afficher le statut des éléments externes eux-mêmes. Vous pouvez spécifier l'option --ignore-externals à n'importe laquelle de ces sous-commandes pour désactiver le traitement des définitions des références externes.

Listes de modifications

Il est très courant pour un développeur d'avoir à effectuer, en même temps, des modifications multiples n'ayant rien à voir entre elles sur une portion de code donnée. Ce n'est pas nécessairement la preuve d'une mauvaise gestion de son temps, ni d'une forme de masochisme numérique. Un ingénieur de développement repère souvent des bogues annexes lorsqu'il travaille sur un morceau de code particulier. Ou alors c'est une fois rendu à mi-chemin d'un changement important qu'il prend conscience que la solution qu'il a choisie serait mieux propagée sous la forme de plusieurs unités logiques plus petites, ces unités pouvant se recouper, affecter des fichiers différents d'un même module ou même toucher à des lignes différentes d'un même fichier.

Plusieurs méthodes sont à disposition des développeurs pour gérer ces ensembles de modifications. Certains développeurs utilisent des copies de travail séparées, du même dépôt, pour y conserver les progrès faits pour chaque changement. D'autres développeurs choisissent de créer au sein du dépôt des branches fonctionnelles à durée de vie très courte et d'utiliser une unique copie de travail qu'ils font pointer selon les besoins du moment vers une branche de ce type ou vers une autre. Enfin, d'autres développeurs utilisent les outils diff et patch pour sauvegarder et restaurer des changements non propagés sur les fichiers touchés par les modifications. Chacune de ces méthodes a des avantages et des inconvénients et, en général, le détail des changements à effectuer influence fortement le choix de la méthode utilisée pour les distinguer.

Subversion fournit la fonctionnalité des listes de modifications, qui vient s'ajouter aux méthodes mentionnées ci-dessus. Les listes de modifications sont en gros des étiquettes arbitraires (une au plus par fichier pour l'instant) attachées à des fichiers de la copie de travail dans le seul but de regrouper plusieurs fichiers ensemble. Les utilisateurs de bon nombre de logiciels fournis par Google sont déjà habitués à ce concept. Dans Gmail, vous associez des étiquettes arbitraires à des e-mails et plusieurs e-mails peuvent être considérés comme faisant partie du même groupe s'ils partagent une étiquette donnée. Dès lors, ne voir qu'un groupe d'e-mails portant la même étiquette n'est plus qu'un simple jeu d'affichage. De nombreux autres sites web 2.0 possèdent des mécanismes similaires. Prenez par exemple les « tags » (« étiquette » en anglais) utilisés sur des sites comme YouTube ou Flickr, les « catégories » utilisées pour regrouper les articles de blogs, etc. Aujourd'hui les gens ont compris que l'organisation des données est essentielle et la façon dont ces données sont organisées doit être flexible. Le vieux paradigme des répertoires et des fichiers est bien trop rigide pour certaines applications.

Cette fonctionnalité de Subversion vous permet de créer des listes de modifications en étiquetant les fichiers que vous voulez inclure, de supprimer ces étiquettes et de limiter le rayon d'action des sous-commandes aux seuls fichiers qui portent l'étiquette donnée. Dans ce paragraphe, nous allons voir en détails comment accomplir ces actions.

Création et modification de listes de modifications

Vous pouvez créer, modifier et supprimer des listes de modifications en utilisant la commande svn changelist. Plus précisément, vous pouvez utiliser cette commande pour activer ou désactiver l'association d'une liste de modifications avec un fichier donné de la copie de travail. La création d'une liste de modifications a lieu la première fois que vous étiquetez un fichier avec ce nom de liste ; elle n'est supprimée que quand vous effacez l'étiquette du dernier fichier qui la portait. Examinons un cas concret pour illustrer ces notions.

Harry est en train de corriger des bogues dans le module de logique mathématique de l'application « calculatrice ». Son travail l'amène à modifier deux fichiers :

$ svn status
M      entier.c
M      ops-math.c
$

En testant son correctif, Harry s'aperçoit que ses modifications lui indiquent qu'un bogue collatéral existe au sein de la logique de l'interface utilisateur, située dans le fichier bouton.c. Harry décide alors qu'il va aussi corriger ce bogue, dans une propagation séparée de ses propres correctifs mathématiques. Dans une copie de travail de petite taille, ne contenant qu'une poignée de fichiers, et pour juste quelques modifications logiques, Harry pourrait probablement gérer mentalement ces deux ensembles logiques de modifications sans le moindre problème. Mais aujourd'hui il a décidé qu'il allait utiliser les listes de modifications de Subversion, pour faire une faveur aux auteurs de ce livre.

Harry commence par créer une première liste de modifications et y associe les deux premiers fichiers qu'il a déjà modifié. Pour ce faire, il utilise la commande svn changelist afin d'associer le même nom arbitraire de liste de modifications aux deux fichiers :

$ svn changelist correctifs-maths entier.c ops-math.c
A [correctifs-maths] entier.c
A [correctifs-maths] ops-math.c
$ svn status

--- Liste de changements 'correctifs-maths' :
M       entier.c
M       ops-math.c
$

Comme vous pouvez le constater, le résultat de svn status reflète bien ce nouvel ensemble.

Harry se lance alors dans la correction du problème d'interface graphique collatéral. Puisqu'il sait quel fichier il va modifier, il associe également ce chemin à une liste de modifications. Mais malencontreusement Harry associe ce troisième fichier à la même liste de modifications que les deux premiers :

$ svn changelist correctifs-maths bouton.c
A [math-fixes] bouton.c
$ svn status

--- :Liste de changements 'correctifs-maths' :
        bouton.c
M       entier.c
M       ops-math.c
$

Par chance, Harry prend conscience de son erreur. Deux options se présentent alors à lui. Il peut supprimer l'association de bouton.c avec la liste de modifications, puis lui associer un nouveau nom de liste de modifications :

$ svn changelist --remove bouton.c
D [correctifs-maths] bouton.c
$ svn changelist correctifs-graphiques bouton.c
A [correctifs-graphiques] bouton.c
$

Ou alors il peut sauter l'étape de suppression et juste associer un nouveau nom de liste de modifications à bouton.c. Dans ce cas, Subversion signale à Harry que bouton.c va être supprimé de la première liste de modifications :

$ svn changelist correctifs-graphiques bouton.c
D [correctifs-maths] bouton.c
A [correctifs-graphiques] bouton.c
$ svn status

--- Liste de changements 'correctifs-graphiques' :
       bouton.c

--- Liste de changements 'correctifs-maths' :
M      entier.c
M      ops-maths.c
$

Harry dispose donc à présent de deux listes de modifications distinctes dans sa copie de travail et svn status présente ses résultats en les regroupant par liste de modifications. Notez que bien qu'Harry n'ait pas encore modifié bouton.c, celui-ci est quand même mentionné par svn status car une liste de modifications lui est associée. Les listes de modifications peuvent être associées, ou enlevées, aux fichiers à tout moment, indépendamment du fait que ces fichiers contiennent des modifications locales ou pas.

Harry règle maintenant le problème de l'interface graphique dans bouton.c.

$ svn status


--- Liste de changements 'correctifs-graphiques':
M      bouton.c

--- Liste de changements 'correctifs-maths':
M      entier.c
M      ops-math.c
$

Listes de modifications : des filtres pour vos opérations

Le regroupement visuel qu'Harry constate en sortie de svn status, comme indiqué précédemment, est intéressant d'un point de vue esthétique, mais pas vraiment utile. La commande status n'est qu'une des commandes qu'il est susceptible de lancer sur sa copie de travail. Heureusement, bon nombre des autres opérations de Subversion sont capables d'agir sur les listes de modifications grâce à l'option --changelist.

Quand l'option --changelist est présente, les commandes Subversion limitent leur champ d'action aux fichiers auxquels est associé le nom de liste de modifications donné. Si Harry veut voir quels changements il a effectué sur les fichiers de sa liste correctifs-maths, il pourrait lister explicitement les fichiers faisant partie de cette liste de modifications avec la commande svn diff.

$ svn diff entier.c ops-math.c
Index: entier.c
===================================================================
--- entier.c	(révision 1157)
+++ entier.c	(copie de travail)
…
Index: ops-math.c
===================================================================
--- ops-math.c	(révision 1157)
+++ ops-math.c	(copie de travail)
…
$

Cette méthode fonctionne bien pour un petit nombre de fichiers, mais qu'en est-il si Harry a modifié une vingtaine ou une trentaine de fichiers ? Fournir la liste de tous ces fichiers serait assez pénible. Mais puisqu'il utilise les listes de modifications, Harry peut désormais éviter de lister explicitement tous les fichiers et ne donner à la place que le nom de la liste de modifications :

$ svn diff --changelist correctifs-maths

Index: entier.c
===================================================================
--- entier.c	(révision 1157)
+++ entier.c	(copie de travail)
…
Index: ops-math.c
===================================================================
--- ops-math.c	(révision 1157)
+++ ops-math.c	(copie de travail)
…
$

Et au moment de lancer la propagation, Harry peut à nouveau se servir de l'option --changelist pour limiter le rayon d'action de la propagation aux fichiers de sa liste de modifications. Par exemple, il peut propager ses changements concernant l'interface graphique en lançant :

$ svn commit -m "Corrigé un bug de l'interface graphique découvert en travaillant sur la logique mathématique." \
      --changelist correctifs-graphiques
Envoi        bouton.c
Transmission des données .
Révision 1158 propagée.
$

En fait, la commande svn commit accepte une deuxième option liée aux listes de modifications : --keep-changelists. Normalement, l'association des listes de modifications avec les fichiers est supprimée dès que ceux-ci ont été propagés. Mais si l'option --keep-changelists est ajoutée sur la ligne de commande, les fichiers propagés (qui ne sont donc plus dans l'état modifié) restent associés aux listes de modifications en question. Dans tous les cas, propager des fichiers faisant partie d'une liste de modification laisse les autres listes de modifications intactes.

$ svn status


--- Liste de changements 'correctifs-maths':
M      entier.c
M      ops-math.c
$
[Note]Note

L'option --changelist agit comme un filtre sur les cibles des commandes Subversion et n'ajoute jamais de cible à une opération. Par exemple, lors d'une opération de propagation lancée via svn commit /chemin/vers/rep, la cible est le répertoire /chemin/vers/rep et ses fils (avec une profondeur infinie). Si ensuite vous ajoutez une option spécifiant une liste de modifications à cette commande, seuls les fichiers se trouvant sous le chemin /chemin/vers/rep et associés à cette liste de modifications sont pris en compte en tant que cibles de la propagation ; ne sont pas inclus les fichiers situés ailleurs (tels ceux sous /chemin/vers/autre-rep), quelle que soit la liste de modifications à laquelle ils appartiennent, même s'il font partie de la même copie de travail que la ou les cibles de l'opération.

Même la commande svn changelist accepte l'option --changelist. Ceci vous permet de renommer ou supprimer facilement une liste de modifications :

$ svn changelist bogues-maths --changelist correctifs-maths --depth infinity .
D [correctifs-maths] entier.c
A [bogues-maths] entier.c
D [correctifs-maths] ops-math.c
A [bogues-maths] ops-math.c
$ svn changelist --remove --changelist bogues-maths --depth infinity .
D [bogues-maths] entier.c
D [bogues-maths] ops-math.c
$

Enfin, vous pouvez spécifier plusieurs instances de l'option --changelist dans une même ligne de commande. Ceci limite le champ d'action de votre opération aux fichiers faisant partie de toutes les listes de modifications spécifiées.

Limitations des listes de modifications

La fonctionnalité de listes de modifications de Subversion est un outil très pratique pour créer des groupes de fichiers au sein de la copie de travail, mais elle a cependant quelques limitations. Les listes de modifications sont des objets contenus à l'intérieur d'une copie de travail, ce qui signifie que les associations entre fichiers et listes de modifications ne peuvent pas être propagées vers le dépôt, ni partagées avec d'autres utilisateurs. Les listes de modifications ne peuvent être associées qu'à des fichiers, Subversion n'offre pas cette possibilité pour les répertoires. Enfin, vous pouvez avoir au plus un nom de liste de modifications associé à un fichier donné. C'est ici que l'analogie avec les articles de blogs et les services d'étiquetage de photos en ligne part en fumée. S'il vous faut associer un fichier à plusieurs listes de modifications, vous êtes coincé.

Modèle de communication réseau

À un moment ou à un autre, vous aurez besoin de comprendre comment le client Subversion communique avec le serveur. La couche réseau de Subversion est abstraite, c'est-à-dire que les clients Subversion ont le même comportement quel que soit le type de serveur auquel ils ont affaire. Qu'ils communiquent via le protocole HTTP (http://) avec un serveur HTTP Apache ou via le protocole Subversion (svn://) avec svnserve, le modèle de communication réseau est le même. Dans cette section, nous expliquons les fondamentaux de ce modèle de communication réseau, y compris la façon dont Subversion gère les authentifications et les autorisations.

Requêtes et réponses

Le client Subversion passe la plupart de son temps à gérer des copies de travail. Cependant, quand il a besoin d'informations disponibles dans un dépôt distant, il envoie une requête sur le réseau et le serveur lui répond. Les détails du protocole réseau sont cachés à l'utilisateur : le client essaie d'accéder à une URL et, suivant le format de cette URL, utilise un protocole particulier pour contacter le serveur (voir la section intitulée « URL des dépôts Subversion »).

[Astuce]Astuce

Tapez svn --version pour voir quels types d'URL et de protocoles sont utilisables par votre client.

Quand le serveur reçoit une requête d'un client, il demande souvent au client de s'identifier. Il envoie un défi d'authentification vers le client et le client répond en fournissant les éléments d'authentification au serveur. Une fois cette authentification terminée, le serveur répond à la requête originale du client. Remarquez que ce fonctionnement est différent de celui de CVS où le client envoie systématiquement au préalable ses identifiants de connexion (procédure de « log in »), avant même de formuler la moindre requête. Dans Subversion, le serveur requiert explicitement l'authentification du client (par un défi d'authentification) au moment approprié, au lieu que ce soit le client qui s'authentifie a priori. Certaines opérations sont donc effectuées plus élégamment. Par exemple, si un serveur est configuré pour laisser tout le monde accéder au dépôt en lecture, alors le serveur n'envoie jamais de défi d'authentification quand un client tente un svn checkout.

Si une requête d'un client conduit à la création d'une nouvelle révision du dépôt (par exemple un svn commit), alors Subversion utilise le nom d'utilisateur fourni lors de la phase d'authentification comme auteur de la révision. C'est-à-dire que le nom d'utilisateur est stocké dans la propriété svn:author de la nouvelle révision (voir la section intitulée « Propriétés réservées à l'usage de Subversion »). Si le client n'a pas été authentifié (en d'autres termes, si le serveur n'a jamais envoyé de défi d'authentification), alors la propriété svn:author de la révision est vide.

Éléments d'authentification du client

Beaucoup de serveurs sont configurés afin de requérir une authentification. Parfois, les opérations de lecture sont autorisées aux utilisateurs anonymes alors que les opérations d'écriture nécessitent une authentification. Dans d'autres cas, à la fois les opérations de lecture et d'écriture requièrent une authentification. Les différentes options du serveur Subversion gèrent différents protocoles d'authentification mais, du point de vue de l'utilisateur, l'authentification se résume typiquement à un identifiant et un mot de passe. Les clients Subversion offrent différentes façon de gérer les éléments d'authentification de l'utilisateur, depuis la saisie interactive de l'identifiant et du mot de passe jusqu'à la mise en cache des éléments dans un conteneur chiffré ou non sur le disque.

Ici, le lecteur soucieux de sécurité va immédiatement bondir de son siège : « Mettre en cache des mots de passe sur le disque ? Quelle horreur ! Jamais ! ». Ne vous inquiétez pas outre mesure : ce n'est pas aussi terrible que ça en a l'air. Les paragraphes qui suivent vont détailler les différents types de cache pour les éléments d'authentification que Subversion utilise, les phases où il les utilise et comment désactiver cette fonctionnalité en totalité ou en partie.

Mise en cache des éléments d'authentification

Subversion propose une solution à ceux qui en ont assez de taper leur nom d'utilisateur et mot de passe à tout bout de champs. Par défaut, dès que le client texte interactif passe avec succès un défi d'authentification, les éléments d'authentification sont mis en cache sur le disque et associés à la combinaison du nom du serveur, son port et le domaine d'authentification. Ce cache sera automatiquement consulté lors des prochains défis, l'utilisateur n'ayant plus à retaper ses éléments d'authentification. Si aucun élément convenable n'est présent dans le cache, ou si les éléments du cache échouent à l'authentification, le client invitera l'utilisateur à fournir les informations nécessaires.

Les développeurs de Subversion reconnaissent que le stockage sur disque des éléments d'authentification peut consistuer une vulnérabilité. C'est pourquoi Subversion est capable de fonctionner avec les différents mécanismes offerts par le système d'exploitation et l'environnement de travail pour minimiser les risques de fuites d'information.

  • Sous Windows, le client Subversion stocke les mots de passe dans le répertoire %APPDATA%/Subversion/auth/. Sous Windows 2000 et ses successeurs, le client Subversion utilise les services cryptographiques standards de Windows pour chiffrer le mot de passe sur le disque. Comme la clé de chiffrement est gérée par Windows et qu'elle est associée à l'identifiant de connexion de l'utilisateur, lui seul peut déchiffrer le mot de passe en cache. Notez que si le mot de passe de l'utilisateur est réinitialisé par un administrateur, tous les mots de passe en cache deviennent indéchiffrables. Le client Subversion agit comme s'ils n'existaient pas, en redemandant le mot de passe quand c'est nécessaire.

  • De manière similaire, sur Mac OS X, le client Subversion stocke tous les mots de passe dans le jeton de connexion (géré par le service « Keychain »), qui est protégé par le mot de passe du compte utilisateur. La configuration des « préférences utilisateur » peut imposer une politique plus stricte, comme demander le mot de passe du compte utilisateur à chaque fois qu'un mot de passe Subversion est utilisé.

  • Pour les systèmes de type Unix, il n'existe pas de standard de service de type « Keychain ». Cependant, le client Subversion sait stocker de manière sûre les mots de passe en utilisant les services « Gnome Keyring », « KDE Wallet » et « GnuPG Agent ». Aussi, avant de stocker les mots de passe sans chiffrement dans la zone de cache ~/.subversion/auth/, le client Subversion demandera à l'utilisateur l'autorisation de le faire. Notez que la zone de cache auth/ reste protégée par les droits système et seul l'utilisateur (le propriétaire) des données peut les lire. Les droits sur les fichiers fournis par le système d'exploitation protègent les mots de passe vis-à-vis des autres utilisateurs non administrateurs sur le même système, pour autant qu'ils n'aient pas un accès physique direct au dispositif de stockage du répertoire de l'utilisateur ou aux sauvegardes.

Bien sûr, si vous êtes complètement paranoïaque, aucun de ces mécanismes n'est parfait. Ainsi, pour ceux qui sont prêts à sacrifier le confort d'utilisation au profit de la sécurité absolue, Subversion fournit de nombreuses façons de désactiver les systèmes de cache d'authentification.

Désactivation de la mise en cache des mots de passe

Quand vous effectuez une opération Subversion qui nécessite de vous authentifier, par défaut Subversion essaie de mettre en cache vos éléments d'authentification sur le disque sous une forme chiffrée. Sur certains systèmes, Subversion peut être incapable de chiffrer vos éléments d'authentification. Dans ce cas, Subversion vous demandera si vous voulez vraiment mettre en cache vos éléments d'authentification sur le disque sous forme claire :

$ svn checkout https://host.example.com:443/svn/private-repo
-----------------------------------------------------------------------
ATTENTION!  Your password for authentication realm:

   <https://host.example.com:443> Subversion Repository

can only be stored to disk unencrypted!  You are advised to configure
your system so that Subversion can store passwords encrypted, if
possible.  See the documentation for details.

You can avoid future appearances of this warning by setting the value
of the 'store-plaintext-passwords' option to either 'yes' or 'no' in
'/tmp/servers'.
-----------------------------------------------------------------------
Store password unencrypted (yes/no)?

Si vous voulez profiter de la facilité de ne pas avoir à entrer votre mot de passe à chaque nouvelle opération, vous pouvez répondre yes à cette invite. Si vous ne voulez pas stocker votre mot de passe en clair et que vous ne voulez pas que Subversion vous pose la question à chaque fois, vous pouvez désactiver la mise en cache en clair des mots de passe, soit de manière permanente, soit en fonction du serveur auquel vous vous connectez.

[Avertissement]Avertissement

Au moment de faire votre choix concernant la mise en cache des mots de passe, vérifier la politique de sécurité relative à votre poste de travail. Beaucoup d'entreprises ont des règles strictes sur le stockage des mots de passe de leurs employés.

Pour désactiver de manière permanente la mise en cache des mots de passe en clair, ajouter la ligne store-plaintext-passwords = no à la section [global] du fichier de configuration servers de votre machine locale. Pour la désactiver uniquement pour un serveur particulier, utiliser la même directive dans la section appropriée du fichier de configuration servers (pour plus de détails, reportez-vous à la section intitulée « Options de configuration » dans Chapitre 7, Personnalisation de Subversion.)

Pour désactiver la mise en cache des mots de passe pour les opérations en ligne de commande, utilisez l'option --no-auth-cache. Pour désactiver la mise en cache de manière permanente, ajoutez l'option store-passwords = no dans le fichier de configuration Subversion de votre machine.

Suppression des éléments d'authentification déjà en cache

Il arrive que les utilisateurs veuillent effacer certains mots de passe du cache disque. Pour ce faire, vous devez vous rendre dans la zone auth/ et effacer manuellement le fichier de cache approprié. Les éléments d'authentification sont mis en cache dans des fichiers individuels ; si vous affichez chaque fichier, vous voyez des clés et des valeurs. La clé svn:realmstring décrit le domaine du serveur auquel est associé le fichier :

$ ls ~/.subversion/auth/svn.simple/
5671adf2865e267db74f09ba6f872c28
3893ed123b39500bca8a0b382839198e
5c3c22968347b390f349ff340196ed39

$ cat ~/.subversion/auth/svn.simple/5671adf2865e267db74f09ba6f872c28

K 8
username
V 3
joe
K 8
password
V 4
blah
K 15
svn:realmstring
V 45
<https://svn.domaine.fr:443> dépôt de Paul
END

Une fois le bon fichier trouvé, effacez-le.

Authentification en ligne de commande

Toutes les opérations en ligne de commandes acceptent les options --username et --password, qui vous permettent de spécifier respectivement un nom d'utilisateur et un mot de passe afin que Subversion ne soit pas obligé de vous inviter à les entrer. C'est particulièrement pratique si vous devez invoquer Subversion dans un script et que vous ne pouvez pas vous fier au fait que Subversion trouvera des éléments d'authentification valides dans le cache pour vous. Ces options sont aussi utiles lorsque Subversion a déjà mis en cache les éléments d'authentification pour vous, mais que vous savez que vous ne voulez pas utiliser ces éléments. Vous pouvez omettre l'option --password si vous souhaitez que Subversion utilise seulement le nom d'utilisateur fourni mais continue à demander un mot de passe pour cet utilisateur.

Un dernier mot sur l'authentification

La façon dont svn gère l'authentification, avec un zoom sur les options --username et --password. Beaucoup de sous-commandes du client acceptent ces options, mais il est important de comprendre que l'utilisation de ces options n'envoie pas automatiquement les éléments d'authentification au serveur. Comme vu précédemment, le serveur demande explicitement l'authentification au client quand il estime que c'est nécessaire ; le client ne les envoie pas à sa convenance. Même si un nom d'utilisateur et/ou un mot de passe sont passés en option, ils ne sont envoyés au serveur que si celui-ci les demande. Ces options sont couramment utilisées pour s'authentifier sous un nom d'utilisateur différent de celui que Subversion aurait choisi par défaut (comme votre nom de compte système), ou quand on ne veut pas de commande interactive (par exemple, utilisation de la commande svn dans un script).

[Note]Note

Une erreur classique consiste à mal configurer un serveur de telle sorte qu'il n'envoie jamais de défi d'authentification. Quand les utilisateurs passent les options --username et --password, ils sont surpris de voir qu'elles ne sont jamais utilisées, c'est-à-dire que les nouvelles révisions semblent toujours avoir été propagées de façon anonyme !

En résumé, voici comment un client Subversion se comporte quand il reçoit un défi d'authentification :

  1. D'abord, le client vérifie si l'utilisateur a spécifié explicitement des éléments d'authentification dans la ligne de commande (options --username et/ou --password). Si c'est le cas, le client essaie de s'authentifier auprès du serveur avec ces éléménts.

  2. Si les éléments d'authentification ne sont pas passés en ligne de commande, ou si ceux qui ont été fournis ne sont pas valides, le client regarde dans la zone auth/ s'il trouve le nom, le port et le domaine du serveur pour voir si l'utilisateur a déjà les éléments d'authentification en cache. Si c'est le cas, il essaie d'utiliser ces éléments pour s'authentifier.

  3. Finalement, si les mécanismes précédents ont abouti à des échecs d'authentification sur le serveur, le client se résout à demander les éléments à l'utilisateur (à moins qu'il ne lui ait été indiqué de ne pas le faire via l'option --non-interactive ou son équivalent spécifique au client).

Si le client réussit à s'authentifier par l'une ou l'autre de ces méthodes, il essaie de mettre en cache les éléments d'authentification sur le disque (à moins que cette fonctionnalité ne soit désactivée, comme indiqué auparavant).

Travail sans copie de travail

Comme nous l'avons indiqué dans la section intitulée « Copies de travail », la copie de travail Subversion est une sorte de zone de transit où l'utilisateur peut effectuer des modifications privées à ses données suivies en versions et, une fois que ces modifications sont terminées et prêtes à être partagées, il peut les propager vers le dépôt. Cela ne doit donc pas vous surprendre si l'on vous dit que la majeure partie des interactions que vous aurez avec Subversion seront des demandes au client Subversion de faire quelque chose concernant un ou plusieurs éléments de la copie de travail locale. Même pour les opérations qui ne manipulent pas de données de la copie de travail elle-même (telles que svn log par exemple), il est souvent plus facile d'utiliser un fichier ou un dossier de la copie de travail comme cible de l'opération.

Clairement, l'approche canonique pour effectuer des modifications sur vos données suivies en versions se fait via une propagation d'une copie de travail Subversion. Heureusement, ce n'est pas l'unique façon de faire. Les utilisateurs de Subversion qui ont besoin de faire des modifications simples à leurs données suivies en versions peuvent le faire en s'affranchissant du coût d'une copie de travail. Dans cette section, nous allons décrire quelques opérations de ce type.

Opérations du client texte interactif à distance

Le client texte interactif Subversion sait faire plusieurs opérations en utilisant des URL du dépôt et sans copie de travail afin d'effectuer des modifications simples. Certaines sont décrites ailleurs dans ce livre, mais nous vous en donnons la liste exhaustive ici.

Certainement la plus évidente des opérations à distance de type propagation est la comande svn import. Nous décrivons cette commande dans la section intitulée « Importation de fichiers et de dossiers » lorsque nous expliquons comment insérer facilement une arborescence complète de données non suivies en versions dans un dépôt Subversion afin de démarrer le processus de suivi de versions sur ces données.

Les commandes svn mkdir et svn delete, lorsqu'elle ciblent des URL, sont aussi des opérations de type propagation. Elles permettent respectivement à l'utilisateur de créer un ou plusieurs nouveaux dossiers suivis en versions ou supprimer (récursivement) un ou plusieurs fichiers ou dossiers suivis en versions, sans l'utilisation d'une copie de travail. Chaque fois que vous entrez une de ces commandes, le client communique avec le serveur de la même manière que s'il décrivait la propagation de l'ajout d'un dossier ou de la suppression d'un élément de la copie de travail. S'il n'y a pas de problème ou de conflit détecté par cette opération, le serveur propage les ajouts ou les suppressions dans une nouvelle révision unique.

Vous pouvez utiliser svn copy ou svn move avec deux URL (une URL source de la copie/déplacement et une URL destination) pour propager des copies ou des déplacements de fichiers ou dossiers directement dans le dépôt. Ces opérations s'avèrent être les plus coûteuses quand elles sont effectuées à l'intérieur d'une copie de travail alors qu'elles s'exécutent en temps constant lorsqu'elles sont effectuées à distance en utilisant des URL du dépôt. En fait, l'opération distante svn copy est communément utilisée pour créer des branches dans Subversion, comme nous le montrons dans la section intitulée « Création d'une branche ».

Comme pour la commande svn commit habituelle, vous pouvez spécifier un commentaire de propagation avec toutes ces commandes pour décrire les modifications que vous effectuez. Utilisez l'option --file (-F) ou --message (-m), sinon autorisez le client à vous inviter à lui fournir un commentaire de propagation.

Enfin, il y a plusieurs opérations relatives aux propriétés non suivies en versions que vous pouvez effectuer directement sur le dépôt. En fait, les propriétés de révisions sont quelques peu uniques dans ce contexte, puisqu'elles ne sont pas stockées dans les copies de travail et, par conséquent. doivent être modifiées sans toucher à la copie de travail. Reportez-vous à la section intitulée « Propriétés » pour une description détaillée de la façon de gérer les propriétés dans Subversion.

Utilisation de svnmucc

Un inconvénient de la propagation à distance par le client texte interactif est que vous êtes limité à une seule opération (réellement un type d'opération) par propagation. Par exemple, il est parfaitement naturel et possible de, disons, utiliser svn delete suivi de svn mkdir dans une copie de travail pour remplacer un répertoire suivi en versions existant avec un tout neuf. Lorsque vous propagez ces opérations, une seule nouvelle révision est créée dans le dépôt et cette révision comporte le remplacement complet de votre répertoire. Vous ne pouvez pas faire la même chose en opération à distance en utilisant le client texte interactif, tout en préservant l'unité de la transaction (svn delete URL créera une nouvelle révision qui supprime le répertoire et svn mkdir URL créera une deuxième révision qui recrée le répertoire).

Heureusement, Subversion fournit un utilitaire séparé qui a pour vocation de permettre aux utilisateurs d'enchaîner un ensemble d'opérations à distance et de les propager comme une modification atomique. Cet outil est svnmucc, pour Subversion Multiple URL Command Client (que l'on pourrait traduire par client Subversion pour les commandes avec de multiples URL) :

$ svnmucc --help
…Subversion multiple URL command client
usage: svnmucc ACTION...

  Perform one or more Subversion repository URL-based ACTIONs, committing
  the result as a (single) new revision.

Actions:
  cp REV SRC-URL DST-URL : copy SRC-URL@REV to DST-URL
  mkdir URL              : create new directory URL
  mv SRC-URL DST-URL     : move SRC-URL to DST-URL
  rm URL                 : delete URL
  put SRC-FILE URL       : add or modify file URL with contents copied from
                           SRC-FILE (use "-" to read from standard input)
  propset NAME VALUE URL : set property NAME on URL to VALUE
  propsetf NAME FILE URL : set property NAME on URL to value read from FILE
  propdel NAME URL       : delete property NAME from URL
…

svnmucc est inlus dans le code source du projet Subversion depuis de nombreuses années (en tant que commande mucc historiquement), mais c'est seulement à partir de Subversion 1.8 qu'il est devenu un membre à part entière de la panoplie des utilitaires en ligne de commande de Subversion.

L'utilitaire svnmucc peut effectuer toutes les manipulations que svn fait. Mais, au contraire de svn, svnmucc ne comporte pas de sous-commande. La syntaxe adoptée consiste à lui fournir une liste d'actions et d'opérandes dans une seule ligne de commande (ou depuis un fichier, via l'option --extra-args (-X)). Certaines actions autorisées par svnmucc singent le fonctionnement du client texte interactif. Vous aurez noté dans la sortie de la commande précédente que les cp, mkdir, mv et rm ressemblent bigrement aux commandes que nous avons citées dans la section intitulée « Opérations du client texte interactif à distance ». Mais souvenez-vous que la différence fondamentale réside dans le fait que vous pouvez enchaîner plusieurs actions dans une seule ligne de commande et que cela ne générera qu'une seule révision dans le dépôt.

Prenons l'exemple précédent qui consiste à simplement remplacer un répertoire à distance. En utilisant svnmucc, vous le feriez ainsi :

$ svnmucc rm http://svn.exemple.fr/projets/pour-jouer \
          mkdir http://svn.exemple.fr/projets/pour-jouer \
          -m "Remplace mon vieux projet pour-jouer par un tout neuf."
r22 propagée par harry le 2013-01-15T21:45:26.442865Z
$

Comme vous pouvez le constater, svnmucc a réalisé en une seule révision ce qui aurait nécessité deux révision pour svn (sans copie de travail à disposition).

[Avertissement]Avertissement

Une autre différence entre svnmucc et svn réside dans le fait que le premier ne vous demandera pas d'entrer un commentaire de propagation si vous ne le fournissez pas via la ligne de commande. En lieu et place, il utilisera un message par défaut (ce qui est relativement inutile).

L'utilitaire svnmucc n'est pas simplement limité aux actions que svn ferait tout aussi bien. Il introduit des fonctionnalités supplémentaires que l'on ne trouve pas dans le client texte interactif. Par exemple, vous pouvez utiliser l'action put pour ajouter ou modifier un fichier dans le dépôt, en copiant le contenu d'un fichier présent sur votre machine locale ou depuis le flux de l'entrée standard. Cet outil permet aussi de manipuler les propriétés des fichiers et dossiers suivis en versions avec les actions propset, propsetf et propdel, explicitement ou en copiant les valeurs des propriétés à partir d'un fichier local. Ces actions ne sont pas possible depuis le client texte interactif à l'heure actuelle.

Il est temps de préciser la différence entre ce que peut faire svnmucc et ce qu'il est souhaitable qu'il fasse. Deux exemples viennent à l'esprit :

 

« On demandera beaucoup à qui l'on a beaucoup donné. »

 
 --Jesus
 

« Un grand pouvoir implique de grandes responsabilités. »

 
 --"Spiderman" Oncle Ben de Peter Parker

Comme vous travaillez sans copie de travail, il est impossible pour Subversion de détecter des conflits avant la propagation. Lors d'une utilisation classique de svn, les changements qui sont propagés vers le serveur sont comparés à une version de base déterminée du fichier ou du répertoire, afin de ne pas écraser par inadvertance des modifications concurrentes apportées par un autre membre de l'équipe. Le serveur connait la version du fichier que vous aviez avant vos modifications et il sait si d'autres gens ont changé ce fichier depuis la révision que vous détenez. Ce sont ces informations dont le serveur a besoin pour refuser votre propagation et qu'elle n'écrase pas les changements faits par quelqu'un d'autre ; il vous force alors à intégrer ces changements dans votre copie de travail et à reconsidérer vos propres modifications. Avec svnmucc, il n'y a pas de copie de travail, ce qui vous donne la possibilité de contourner ces sécurités et d'agir comme si l'état courant du dépot était celui qui vous sert de base de travail. Heureusement, il est évident pour vous qu'un tel pouvoir ne s'utilise pas à la légère.

Heureusement, svnmucc vous permet de poser un garde-fou lors de son utilisation. Afin d'offrir un mécanisme de sécurité comparable à l'utilisation d'une copie de travail, svnmucc propose l'option --revision (-r). Avec cette option, vous pouvez spécifier manuellement une révision de base pour les modifications que vous essayez de propager. Cette révision de base est idéalement la plus récente du dépôt dont vous ayez connaissance.

[Avertissement]Avertissement

Nous encourageons fermement les utilisateurs à utiliser, et de manière appropriée, l'option --revision (-r) de svnmucc.

L'utilisation appropriée de svnmucc put démontre sans équivoque comment l'option --revision (-r) doit être invoquée. Considérons que Harry veuille modifier le fichier suivi en versions LISEZMOI sans s'embêter à rappatrier une copie de travail complète (nous supposons qu'il n'y a pas d'autre raison de disposer d'une copie de travail pour cette action, telles que le lancement de scripts avant de propager une révision pour vérifier qu'elle satisfait certains critères). Il doit en premier lieu décider avec quelle révision du fichier il va travailler. Typiquement, les utilisateurs souhaitent travailler avec la version la plus à jour d'un fichier. Ainsi, Harry effectue la requête relative à la révision de la dernière modification du fichier et modifie le contenu du fichier dans un fichier temporaire local :

$ svn info http://svn.exemple.com/projets/bac-à-sable/LISEZMOI
Chemin : LISEZMOI
URL : http://svn.exemple.com/projets/bac-à-sable/LISEZMOI
Relative URL : ^/bac-à-sable/LISEZMOI
Racine du dépôt : http://svn.example.com/projects
UUID du dépôt : 13f79535-47bb-0310-9956-ffa450edef68
Révision : 22
Type de nœud : fichier
Auteur de la dernière modification : sally
Révision de la dernière modification : 14
Date de la dernière modification : 2012-09-02 10:34:09 -0400 (dim. 02 sep. 2012)

$ svn cat -r 14 http://svn.exemple.com/projets/bac-à-sable/LISEZMOI \
      > LISEZMOI.tempo
$

Harry possède maintenant une copie du fichier LISEZMOI tel qu'il était lors de sa dernière modification. Il fait ses modifications dans cette copie du fichier. Naturellement, lorsqu'il a terminé, il veut propager ces modifications vers le dépôt.

Maintenant, si Harry utilise naïvement svnmucc put …, pour remplacer le contenu de LISEZMOI dans le dépôt par le contenu de son fichier local, il fait un abus de pouvoir avec svnmucc. Que se passe-t-il si, quelques microsecondes avant sa propagation, Sally a aussi modifié le fichier LISEZMOI ? Tout comme svn, svnmucc n'essaiera pas de fusionner sur le serveur les modifications de chacun pour les préserver. Non, svnmucc remplacera simplement la dernière version du fichier par la nouvelle qu'on lui donne. Harry n'en aura pas conscience, Sally sera furieuse.

$ svnmucc put LISEZMOI.tempo \
          http://svn.exemple.com/projets/bac-à-sable/LISEZMOI \
          -m "Modifié le fichier LISEZMOI."
r24 propagée par harry le 2013-01-21T16:21:23.100133Z
$
Message from sally@shell.example.com on pts/2 at 16:26 ...
Il faut qu'on se parle. Maintenant.
EOF

À la place, Harry doit rappeler la révision qu'il a utilisée comme base de travail à la commande svnmucc via l'option --revision (-r). Ainsi, le serveur pourra rejeter la propagation si, par malheur, Harry essaie de modifier un élément obsolète :

$ svnmucc -r 14 put LISEZMOI.tempo \
          http://svn.exemple.com/projets/bac-à-sable/LISEZMOI \
          -m "Modifié le fichier LISEZMOI."
svnmucc: E170004: Item '/bac-à-sable/LISEZMOI' est obsolète.
$

Comme toutes les autres options de svnmucc, l'option --revision (-r) est globale pour la commande, c'est-à-dire qu'elle s'applique à toutes les actions spécifiées dans la commande. Cela vous permet d'avoir les mêmes sortes de garde-fous que si vous aviez extrait une copie de travail du dépôt tout entier (et donc comme si vous travailliez sur une copie de travail uniformisée à la même révision), vous aviez effectué vos modifications sur cette copie de travail, puis vous aviez propagé toutes les modifications en même temps.

Comme vous pouvez le constater, svnmucc trouve bien sa place dans la boite à outils Subversion. Pour une référence complète des possibilités de cet outil, reportez-vous à Guide de référence de svnmucc : client texte Subversion pour URL multiples.

Résumé

Après avoir lu ce chapitre, vous devez désormais avoir une bonne compréhension de certaines fonctionnalités de Subversion qui, bien qu'elles ne servent pas systématiquement à chaque utilisation du système de gestion de versions, peuvent rendre de grands services. Ne vous arrêtez pas là ! Lisez le chapitre suivant, où vous découvrirez les branches, les étiquettes et les fusions. Vous aurez alors la maîtrise quasi-complète du client Subversion. Bien que nos avocats ne nous autorisent pas à vous promettre quoi que ce soit, ces connaissances supplémentaires feront déjà de vous quelqu'un de bien plus branché. [31]




[11] « Sulli, il ne faut pas l'appeler ! Tu commences par l'appeler et tu finis par t'attacher », Bob Razowski (le cyclope de Monstres et Cie).

[12] Au 15 de la rue du Château à Rueil-Malmaison se trouve un musée d'histoire (consacré à Joséphine, épouse de Napoléon). Cela nous a semblé approprié…

[13] Pour ceux qui connaissent le XML, c'est à peu près le sous-ensemble ASCII pour la syntaxe du champ "Name" en XML.

[14] Corriger les fautes d'orthographe, les erreurs de grammaire et les informations simplement erronées au sein des commentaires de propagation est peut-être l'usage le plus courant de l'option --revprop.

[15] L'exception notable à cet état de fait est la propriété svn:mergeinfo qui est héritable, voir la section intitulée « Mergeinfo et aperçus »

[16] Actuellement, la bibliohèque utilisée est libmagic.

[17] Rappelez-vous que les utilisateurs ne peuvent hériter de propriétés pour lesquelles ils ont un droit en lecture. Donc si un administrateur définit la propriété svn:auto-props sur un chemin parent très ascendant (par exemple à la racine du dépôt), il doit s'assurer que tous les utilisateurs possèdent le droit de lecture sur ce chemin ou la configuration automatique de propriété voulue ne fonctionnera pas.

[18] Au moment de la rédaction de ce livre, les liens symboliques sont en fait les seuls objets « spéciaux » traités. Mais rien n'interdit qu'il y en ait d'autres dans des versions futures de Subversion.

[19] Ca vous semble dur ? Et bien, à la même période, WordPerfect utilisait aussi .DOC comme extension préférée de son format de fichier propriétaire !

[20] Les systèmes de fichiers Windows utilisent les extensions des fichiers (telles que .EXE, .BAT et .COM) pour indiquer que les fichiers sont exécutables.

[21] Les motifs de la propriété svn:global-ignores doivent être séparés par des blancs (comme pour la directive global-ignores de la zone de configuration), pas uniquement des fins de lignes (au contraire de la propriété svn:ignore).

[22] Naturellement, seuls les clients Subversion 1.8 ou plus récents reconnaîtront l'héritage et la signification de la propriété svn:global-ignores !

[23] Bien que ce soit une affaire de goût, si vous ne définissez pas explicitement de valeur pour la directive global-ignores dans la zone de configuration, soit avec votre ensemble préféré de motifs, soit avec une chaine vide, alors Subversion utilise une valeur par défaut. Regardez l'entrée global-ignores dans la section intitulée « Configuration générale ».

[24] N'est-ce pas précisément la finalité d'un système de compilation ?

[25] Supposons que vous n'avez pas non plus de motif qui corresponde dans la directive global-ignores de la zone de configuration.

[26] …ou peut-être même un paragraphe d'un livre…

[27] En toute sécurité, bien sûr. Comme dans les autres situations, Subversion laissera sur le disque tous les fichiers que vous avez modifiés ou qui ne sont pas suivis en versions.

[28] À ce propos, un peu de communication n'aurait pas non plus été un mauvais remède pour leurs homonymes hollywoodiens.

[29] Pour l'instant, Subversion ne permet pas de poser de verrou sur un dossier.

[30] À part, peut-être, la fusion mentale vulcaine.

[31] Aucun achat nécessaire. Offre soumise à conditions. Aucune garantie de branchitude, ni explicite ni implicite, n'est fournie. Les performances passées ne préjugent pas des performances futures.

Chapitre 4. Gestion des branches

 

« 君子务本 (C'est sur le Tronc qu'un gentleman travaille). »

 
 --Confucius

La création et la fusion de branches sont des concepts fondamentaux des systèmes de gestion de versions, simples à expliquer d'un point de vue conceptuel mais offrant suffisamment de complexité et de nuances pour mériter un chapitre dans ce livre. Nous allons introduire le concept général de ces opérations ainsi que l'approche, quelque peu unique, adoptée par Subversion. Ce chapitre suppose que vous êtes déjà familier avec les notions de bases de Subversion (expliquées dans le Chapitre 1, Notions fondamentales)

Définition d'une branche

Supposons que votre travail soit de maintenir un document pour une division de votre entreprise, un manuel par exemple. Un beau jour, une autre division vous demande le même manuel, mais avec quelques parties « modifiées » spécialement pour elle, puisqu'elle fait les choses légèrement différemment.

Que faites-vous dans cette situation ? Tout naturellement, vous créez une seconde copie du document et commencez à maintenir les deux copies séparément. Puis, quand chaque division vous demande de faire des petites modifications, vous les incorporez dans une copie ou dans l'autre.

Vous voulez souvent faire la même modification dans les deux copies. Par exemple, si vous découvrez une coquille dans la première copie, il est très probable que la même coquille existe dans la deuxième copie. Les deux documents sont presque identiques, après tout ; ils ne diffèrent qu'en quelques points mineurs et spécifiques.

Voilà le concept de branche, c'est-à-dire une ligne de développement qui existe indépendamment d'une autre ligne, mais partage cependant une histoire commune avec elle, si vous remontez suffisamment loin en arrière dans le temps. Une branche commence toujours sa vie en tant que copie de quelque chose, puis diffère à partir de là, selon une histoire qui lui est propre (voir la Figure 4.1, « Branches de développement »).

Figure 4.1. Branches de développement

Branches de développement


Subversion possède des commandes pour vous aider à maintenir des branches parallèles de vos fichiers et répertoires. Il vous permet de créer des branches en faisant des copies de vos données et se souvient que les copies sont liées les unes aux autres. Il vous aide aussi à dupliquer les modifications d'une branche vers une autre. Enfin, il permet que des portions de votre copie de travail correspondent à différentes branches, afin que vous puissiez « mélanger » différentes lignes de développement dans votre travail quotidien.

Utilisation des branches

Rendu à ce chapitre, vous devriez avoir compris que chaque propagation crée une arborescence de fichiers entièrement nouvelle (appelée « révision ») dans le dépôt. Si ce n'est pas le cas, retournez vous informer sur les révisions dans la section intitulée « Révisions ».

Pour ce chapitre, nous reprendrons le même exemple qu'au Chapitre 1, Notions fondamentales. Souvenez-vous que votre collaboratrice Sally et vous partagez un dépôt qui contient deux projets, paint et calc. Notez cependant que dans la Figure 4.2, « Structure initiale du dépôt », le dossier de chaque projet contient désormais des sous-dossiers nommés trunk et branches. Les raisons de cette arborescence apparaîtront bientôt clairement.

Figure 4.2. Structure initiale du dépôt

Structure initiale du dépôt


Comme avant, supposons que Sally et vous avez tous deux une copie de travail du projet « calc ». Plus spécifiquement, vous avez chacun une copie de travail de /calc/trunk. Tous les fichiers du projet sont dans ce sous-dossier plutôt que dans /calc lui-même, parce que votre équipe a décidé que la « ligne principale » de développement du projet allait se situer dans /calc/trunk.

Disons que l'on vous a attribué la tâche d'implémenter une fonctionnalité du logiciel qui prendra longtemps à écrire et touchera à tous les fichiers du projet. Le problème immédiat est que vous ne voulez pas déranger Sally, qui est en train de corriger des bogues mineurs ici et là. Elle a besoin que la dernière version du projet (dans /calc/trunk) demeure en permanence utilisable. Si vous commencez à propager des changements petit à petit, vous allez sûrement rendre les choses difficiles pour Sally (ainsi que pour d'autres membres de l'équipe).

Une stratégie possible est de vous isoler : vous pouvez arrêter de partager des informations avec Sally pendant une semaine ou deux. C'est-à-dire commencer à modifier et à réorganiser les fichiers dans votre copie de travail, mais sans effectuer de propagation ni de mise à jour avant que vous n'ayez complètement terminé la tâche. Cette stratégie comporte certains risques. Premièrement, ce n'est pas sans danger. La plupart des gens aiment propager leurs modifications fréquemment, au cas où leur copie de travail aurait un accident. Deuxièmement, ce n'est pas très flexible. Si vous travaillez sur différents ordinateurs (vous avez peut-être une copie de travail de /calc/trunk sur deux machines différentes), vous aurez besoin de transférer manuellement vos changements entre les deux, ou bien de travailler sur une seule machine. De la même façon, il est difficile de partager vos changements en cours avec quelqu'un d'autre. Une des « bonnes pratiques » du monde du développement logiciel est de permettre à vos pairs de passer votre travail en revue au fur et à mesure. Si personne n'a accès à vos propagations intermédiaires, vous vous coupez d'éventuelles critiques et risquez de partir dans une mauvaise direction pendant des semaines avant que quelqu'un ne s'en aperçoive. Enfin, quand vous en aurez fini avec tous vos changements, vous pourriez avoir du mal à fusionner votre travail avec le code du reste de l'équipe. Sally (et les autres) peuvent avoir apporté de nombreux autres changements au dépôt, changements qui seront difficiles à incorporer dans votre copie de travail, notamment si vous lancez svn update après des semaines d'isolation.

Une solution bien meilleure est de créer votre propre branche, ou ligne de développement, dans le dépôt. Ceci vous permettra de sauvegarder fréquemment votre travail un peu boiteux sans interférer avec vos collaborateurs ; vous pourrez toutefois partager une sélection d'informations avec eux. Vous découvrirez comment tout cela fonctionne exactement au fur et à mesure de ce chapitre.

Création d'une branche

Créer une branche est très simple : il s'agit juste de faire une copie du projet dans le dépôt avec la commande svn copy. Subversion est capable de copier non seulement de simples fichiers, mais aussi des dossiers entiers. Dans le cas présent, vous voulez faire une copie du dossier /calc/trunk. Où doit résider la nouvelle copie ? Là où vous le désirez, cette décision faisant partie de la gestion du projet. Enfin, votre branche se doit de posséder un nom, pour la distinguer des autres branches. Là encore, le nom que vous choisissez importe peu à Subversion (vous pouvez utiliser le nom qui vous convient personnellement ou à votre équipe).

Supposons que votre équipe (comme la plupart des équipes) ait pour convention de créer les branches dans le répertoire branches qui se trouve au même niveau que la branche principale de votre projet (le répertoire /calc/branches dans notre scénario). Comme vous manquez d'inspiration, vous vous décidez pour ma-branche-calc come nom pour votre branche. Cela veut dire que vous allez créer un nouveau dossier, /calc/branches/ma-branche-calc, qui commence ainsi sa vie en tant que copie de /calc/trunk.

Vous avez peut-être déjà utilisé svn copy pour copier un fichier vers un autre à l'intérieur d'une copie de travail. Mais il peut aussi être utilisé pour effectuer une copie distante (une copie qui propage immédiatement une nouvelle révision dans le dépôt et pour laquelle aucune copie de travail n'est nécessaire). Il suffit juste de copier une URL vers une autre :

$ svn copy ^/calc/trunk ^/calc/branches/ma-branche-calc \
      -m "Création d'une branche privée à partir de /calc/trunk."

Révision 341 propagée.
$

Cette commande entraîne une opération quasi-instantanée dans le dépôt, créant un nouveau dossier à la révision 341. Ce nouveau dossier est une copie de /calc/trunk, comme l'illustre la Figure 4.3, « Dépôt avec nouvelle copie » [32]. Bien qu'il soit aussi possible de créer une branche en utilisant svn copy pour dupliquer un dossier à l'intérieur de la copie de travail, cette technique n'est pas recommandée. Elle peut s'avérer assez lente, en fait ! Copier un dossier côté client est une opération linéaire en terme de durée, puisque chaque fichier et chaque dossier doit être dupliqué sur le disque local. Copier un dossier sur le serveur, par contre, est une opération dont la durée est constante et c'est ainsi que la plupart des gens créent des branches. En plus, cette façon de faire engendre le risque de copier des copies de travail à révisions mélangées. Ce n'est pas intrinsèquement dangereux, mais peut causer des complications inutiles plus tard lors des fusions.

Figure 4.3. Dépôt avec nouvelle copie

Dépôt avec nouvelle copie


Travail sur votre branche

Maintenant que vous avez créé votre branche du projet, vous pouvez extraire une nouvelle copie de travail et commencer à l'utiliser :

$ svn checkout http://svn.exemple.com/depot/calc/branches/ma-branche-calc
A  ma-branche-calc/doc
A  ma-branche-calc/src
A  ma-branche-calc/doc/INSTALL
A  ma-branche-calc/src/reel.c
A  ma-branche-calc/src/main.c
A  ma-branche-calc/src/bouton.c
A  ma-branche-calc/src/entier.c
A  ma-branche-calc/Makefile
A  ma-branche-calc/LISEZMOI
Révision 341 extraite.
$

Cette copie de travail n'a rien de spéciale ; elle correspond juste à un dossier différent du dépôt. Cependant, quand vous propagerez vos modifications, Sally ne les verra pas quand elle effectuera une mise à jour, car sa copie de travail correspond à calc/trunk (pensez bien à lire la section intitulée « Parcours des branches » plus loin dans ce chapitre : la commande svn switch est une méthode alternative pour créer une copie de travail d'une branche).

Imaginons qu'une semaine passe et que les propagations suivantes ont lieu :

  • Vous modifiez /calc/branches/ma-branche-calc/src/bouton.c, ce qui crée la révision 342.

  • Vous modifiez /calc/branches/ma-branche-calc/src/entier.c, ce qui crée la révision 343.

  • Sally modifie /calc/trunk/src/entier.c, ce qui crée la révision 344.

À présent, deux lignes de développement indépendantes (voir la Figure 4.4, « Historique des branches d'un fichier ») existent pour entier.c.

Figure 4.4. Historique des branches d'un fichier

Historique des branches d'un fichier


Les choses deviennent intéressantes quand on regarde l'historique des modifications apportées à votre copie de entier.c :

$ pwd
/home/utilisateur/ma-branche-calc

$ svn log -v src/entier.c
------------------------------------------------------------------------
r343 | utilisateur | 2002-11-07 15:27:56 -0600 (jeu. 07 nov. 2002) | 2 lignes
Chemins modifiés :
   M /calc/branches/ma-branche-calc/src/entier.c

* entier.c:  machiné le bidule.

------------------------------------------------------------------------
r341 | utilisateur | 2002-11-03 15:27:56 -0600 (jeu. 07 nov. 2002) | 2 lignes
Chemins modifiés :
   A /calc/branches/ma-branche-calc (from /calc/trunk:340)

Création d'une branche privée à partir de /calc/trunk.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (mar. 29 oct. 2002) | 2 lignes
Chemins modifiés :
   M /calc/trunk/src/entier.c

* entier.c:  modifié une docstring.
------------------------------------------------------------------------
…
------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (ven. 22 fev. 2002) | 2 lignes
Chemins modifiés :
  A /calc/trunk/src/entier.c

* entier.c:  modifié l'API trucplusse.
------------------------------------------------------------------------
r8 | sally | 2002-01-17 16:55:36 -0500 (mar. 17 jan. 2002) | 1 ligne
Changed paths:
   A /calc/trunk/Makefile
   A /calc/trunk/LISEZMOI
   A /calc/trunk/doc/INSTALL
   A /calc/trunk/src/bouton.c
   A /calc/trunk/src/entier.c
   A /calc/trunk/src/main.c
   A /calc/trunk/src/reel.c

Import initial du code dans trunk pour le projet calc.
------------------------------------------------------------------------

Notez bien que Subversion reprend tout l'historique du entier.c de votre branche à travers le temps, remontant même au delà du point où il a été copié. Il liste la création d'une branche en tant qu'élément de l'historique, parce qu'entier.c a été copié implicitement lorsque calc/trunk tout entier a été copié. Maintenant regardez ce qui se passe quand Sally lance la même commande sur sa copie du fichier :

$ pwd
/home/sally/calc

$ svn log -v src/entier.c
------------------------------------------------------------------------
r344 | sally | 2002-11-07 15:27:56 -0600 (jeu. 07 nov. 2002) | 2 lignes
 Chemins modifiés :
    M /calc/trunk/src/entier.c

 * entier.c:  réusinage des fonctions trucmuches.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (mar. 29 oct. 2002) | 2 lignes
 Chemins modifiés :
    M /calc/trunk/entier.c

 * entier.c:  modifié une docstring.

------------------------------------------------------------------------
…
------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (ven. 22 fev. 2002) | 2 lignes
Chemins modifiés :
  A /calc/trunk/src/entier.c

* entier.c:  modifié l'API trucplusse.
------------------------------------------------------------------------
r8 | sally | 2002-01-17 16:55:36 -0500 (mar. 17 jan. 2002) | 1 ligne
Changed paths:
   A /calc/trunk/Makefile
   A /calc/trunk/LISEZMOI
   A /calc/trunk/doc/INSTALL
   A /calc/trunk/src/bouton.c
   A /calc/trunk/src/entier.c
   A /calc/trunk/src/main.c
   A /calc/trunk/src/reel.c

Import initial du code dans trunk pour le projet calc.
------------------------------------------------------------------------

Sally voit la modification due à sa propre révision 344, mais pas le changement que vous avez effectué dans la révision 343. Pour Subversion, ces deux propagations ont touché des fichiers différents dans des dossiers distincts. Néanmoins, Subversion indique bien que les deux fichiers partagent une histoire commune. Avant que la copie de branche n'ait été faite en révision 341, les fichiers ne faisaient qu'un. C'est pourquoi Sally et vous voyez tous les deux les modifications apportées entre les révisions 8 et 303.

Gestion des branches par Subversion : notions clés

Il y a deux leçons importantes à retenir de ce paragraphe. Premièrement, Subversion n'a pas de notion interne de branche — il sait seulement faire des copies. Quand vous copiez un dossier, le dossier qui en résulte n'est une « branche » que parce que vous le considérez comme tel. Vous aurez beau envisager ce dossier différemment ou le traiter différemment, pour Subversion c'est juste un dossier ordinaire auquel sont associées des informations extérieures relatives à son historique.

Deuxièmement, en raison de ce mécanisme de copie, les branches de Subversion existent en tant que dossiers classiques du système de fichiers du dépôt. En cela, Subversion diffère des autres systèmes de gestion de versions, où les branches sont définies par l'ajout d'« étiquettes » (labels en anglais) extra-dimensionnelles à des groupes de fichiers. L'emplacement du dossier de votre branche importe peu à Subversion. La plupart des équipes ont pour convention de placer toutes les branches dans un dossier /branches, mais vous êtes libre d'inventer la convention qui vous plaît.

Fusions : pratiques de base

Désormais, Sally et vous travaillez sur des branches parallèles du projet : vous travaillez sur une branche privée et Sally travaille sur le tronc (trunk en anglais), la branche de développement principale.

Pour les projets qui ont un grand nombre de contributeurs, il est d'usage que la plupart des gens ait des copies de travail du tronc. Dès que quelqu'un doit faire des modifications de longue haleine, susceptibles de perturber le tronc, une procédure standard est qu'il crée une branche privée et qu'il y propage les modifications jusqu'à ce que tout le travail soit terminé.

Bref, la bonne nouvelle est que Sally et vous n'empiétez pas l'un sur l'autre. La mauvaise nouvelle est qu'il est très facile de dériver chacun de son côté. Rappelez-vous qu'un des problèmes lié à la stratégie d'« isolement » est que lorsque vous en aurez fini avec votre branche, il risque d'être quasi impossible de refusionner vos modifications dans le tronc sans avoir à faire face à un grand nombre de conflits.

À la place, Sally et vous pourriez continuer de partager vos changements au fur et à mesure de votre travail. C'est à vous de décider quelles modifications valent la peine d'être partagées ; Subversion vous offre la possibilité de « copier » sélectivement des modifications entre les branches. Et quand vous aurez tout fini dans votre branche, l'ensemble de vos modifications pourra être recopié en entier vers le tronc. Dans la terminologie Subversion, l'action générale de réplication des modifications d'une branche vers une autre s'appelle la fusion et elle s'effectue à l'aide de plusieurs exécutions de la sous-commande svn merge.

Dans les exemples qui suivent, nous supposons que le client et le serveur Subversion sont tous deux en version 1.8 (ou plus récente). Si l'un ou l'autre sont en version plus ancienne que la 1.5, les choses sont plus compliquées : le système ne gére pas les changements de façon automatique et vous devrez utiliser des méthodes manuelles pénibles pour obtenir des résultats similaires. Vous devrez en effet toujours utiliser la syntaxe détaillée de la fusion spécifiant l'éventail des révisions à répliquer (voir la section intitulée « Syntaxe de la fusion : pour tout vous dire » plus loin dans ce chapitre) et penser à garder trace de ce qui a déjà été fusionné et de ce qui ne l'a pas encore été. Pour cette raison, nous recommandons fortement de vous assurer que client et serveur sont au moins en version 1.5.

Ensembles de modifications

Avant que nous n'allions plus loin, nous devons vous avertir que les pages suivantes contiennent de nombreuses discussions portant sur les « modifications ». Beaucoup de gens ayant de l'expérience dans les systèmes de gestion de versions utilisent le terme « modifications » et le terme « ensemble de modifications » de façon interchangeable et nous allons donc clarifier ce que Subversion entend par ensemble de modifications (changeset en anglais).

Chacun semble avoir sa propre définition, variant légèrement, d'un ensemble de modifications, ou tout du moins a une attente différente quant à leur traitement par le système de gestion de versions. En ce qui nous concerne, disons qu'un ensemble de modifications n'est qu'un simple regroupement de modifications identifié par un nom unique. Les modifications peuvent inclure des changements textuels du contenu des fichiers, des modifications de l'arborescence ou des ajustements portant sur les méta-données. En langage plus courant, un ensemble de modifications n'est qu'un correctif avec un nom auquel vous pouvez vous référer.

Dans Subversion, un numéro de révision globale N désigne une arborescence dans le dépôt : c'est ce à quoi le dépôt ressemblait après la N-ième propagation. C'est aussi le nom implicite d'un ensemble de modifications : si vous comparez l'arborescence N avec l'arborescence N-1, vous pouvez en déduire exactement le correctif qui a été propagé. Pour cette raison, il est facile de se représenter une révision N non seulement comme une arborescence, mais aussi comme un ensemble de modifications. Si vous utilisez un système de gestion des incidents pour gérer vos bogues, vous pouvez utiliser les numéros de révision pour vous référer à des correctifs particuliers permettant de résoudre des bogues — par exemple, « cet incident a été corrigé par r9238 ». Quelqu'un peut alors lancer svn log -r 9238 pour obtenir le détail des modifications qui ont corrigé le bogue et lancer svn diff -c 9238 pour voir le correctif lui-même. De plus (comme nous le verrons bientôt), la commande svn merge de Subversion est capable d'utiliser les numéros de révision. Vous pouvez fusionner des listes de modifications spécifiques d'une branche à une autre en les nommant dans les paramètres de la fusion : donner comme argument -c 9238 à svn merge fusionne la liste de modifications r9238 avec votre copie de travail.

Garder une branche synchronisée

Continuons avec notre exemple précédent et imaginons qu'une semaine a passé depuis que vous avez commencé à travailler sur votre branche privée. Votre nouvelle fonctionnalité n'est pas encore terminée, mais en même temps vous savez que d'autres personnes de votre équipe ont continué à faire des modifications importantes sur l'arborescence /trunk du projet. Vous avez intérêt à recopier ces modifications dans votre propre branche, juste pour vous assurer qu'elles se combinent bien avec vos propres modifications. Cette opération s'effectue par fusion automatique de synchronisation (une opération de fusion destinée à garder votre branche synchronisée avec les modifications faites dans l'arborescence « ancestrale » de création de ladite branche). Une fusion automatique est simplement une fusion pour laquelle vous ne fournissez que le minimum d'informations requis (c'est-à-dire une seule source et une copie de travail pour destination) et que vous laissez Subversion déterminer quels modifications doivent être fusionnées — dans une fusion automatique, aucun ensemble de modifications n'est passé à la commande svn merge par l'option -r ou -c.

[Astuce]Astuce

En fait, c'est là une bonne pratique : synchroniser fréquemment votre branche avec la ligne de développement principale permet d'éviter les conflits « surprises » le jour où vous reversez vos modifications dans le tronc.

Subversion connaît l'historique de votre branche et sait à quel moment elle s'est séparée du tronc. Afin de récupérer les modifications du tronc les plus récentes et les plus importantes, assurez-vous en premier lieu que votre copie de travail est « propre », c'est-à-dire que svn status ne liste aucune modification locale. Puis lancez juste :

$ pwd
/home/user/ma-branche-calc

$ svn merge ^/calc/trunk
--- Fusion de r341 à r351 dans '.':
--- Stockage des informations de fusion (mergeinfo) de r345 à r356 dans '.' :
U    bouton.c
U    entier.c
$

La syntaxe de base, svn merge URL, indique à Subversion qu'il doit fusionner toutes les modifications récentes depuis l'URL vers le répertoire de travail actuel (qui est bien souvent la racine de votre copie de travail). Remarquez que nous utilisons la syntaxe circonflexe (^)[33] afin d'éviter d'avoir à taper l'URL complète jusqu'au trunk. Remarquez également la notification de Subversion « Recording mergeinfo for merge … ». Ceci vous indique que la fusion met à jour la propriété svn:mergeinfo. Nous aborderons cette propriété et les notifications plus loin dans ce chapitre, dans la section intitulée « Mergeinfo et aperçus ».

[Astuce]Astuce

Dans ce chapitre et en général (listes de diffusion de Subversion, articles sur le suivi de fusions, etc.), vous rencontrerez souvent le terme mergeinfo (informations de fusion en français). C'est simplement un raccourci pour désigner la propriété svn:mergeinfo

À la fin de cet exemple, votre copie de travail de la branche contient de nouvelles modifications locales qui correspondent à toutes les modifications qui ont eu lieu sur le tronc depuis la création de votre branche :

$ svn status
 M      .
M       Makefile
M       doc/INSTALL
M       src/bouton.c
M       src/reel.c

Maintenant, le plus sage consiste à examiner attentivement chaque modification avec svn diff, puis à compiler et tester votre branche. Notez que le répertoire de travail actuel (« . ») a aussi été modifié. La commande svn diff indique que sa propriété svn:mergeinfo a été créée.

$ svn diff --depth empty .
Index: .
===================================================================
--- .   (révision 351)
+++ .   (copie de travail)

Modification de propriétés sur .
___________________________________________________________________
Ajouté : svn:mergeinfo
   Fusionné /calc/trunk:r341-351

Cette nouvelle propriété contient d'importantes métadonnées relatives à la fusion que vous ne devez pas modifier, car elles sont nécessaires aux futures commandes svn merge (nous en apprendrons plus sur ces métadonnées plus loin dans ce chapitre).

Après cette fusion, vous êtes susceptible de devoir résoudre quelques conflits (de même que lorsque vous effectuez une mise à jour avec svn update) ou d'effectuer des corrections à la main pour que les choses fonctionnent correctement : rappelez-vous que l'absence de conflits syntaxiques ne veut pas dire l'absence de conflits sémantiques ! Si vous rencontrez de sérieux problèmes, vous pouvez toujours abandonner vos modifications locales en lançant la commande svn revert . -R et ouvrir une conversation qui promet d'être longue avec vos collaborateurs sur le thème « c'est quoi ce truc ? ». Mais si les choses se passent bien, vous pouvez propager les modifications dans le dépôt :


$ svn commit -m "Synchronisation des dernières modifications du tronc avec ma-branche-calc."
Envoi              .
Envoi              Makefile
Envoi              doc/INSTALL
Envoi              src/bouton.c
Envoi              src/reel.c
Transmission des données .
Révision 352 propagée.

À ce stade, votre branche privée est « en phase » avec le tronc et vous pouvez dormir tranquille car vous savez que vous pouvez continuer à travailler dans votre coin tout en ne dérivant pas trop par rapport au reste de l'équipe.

Supposons qu'une autre semaine s'est écoulée. Vous avez propagé des modifications supplémentaires dans votre branche et vos camarades ont également continué à améliorer le tronc. Une fois encore, vous aimeriez répercuter les dernières modifications du tronc vers votre branche et ainsi être en phase. Lancez juste la même commande svn merge à nouveau !

$ svn merge ^/calc/trunk 
svn: E195020: Cannot merge into mixed-revision working copy [352:357]; try updating first
$

Ça par exemple, nous ne attendions pas à ça ! Après avoir fait des modifications dans votre branche cette semaine, vous vous retrouvez avec une copie de travail à révisions mélangées (voir la section intitulée « Copies de travail mixtes, à révisions mélangées »). Avec Subversion 1.7 ou plus récent, la sous-commande svn merge interdit par défaut les fusions dans les copies de travail à révisions mélangées. Sans rentrer dans les détails, cela résulte de la façon dont la trace des fusions est conservée dans la propriété svn:mergeinfo (lisez la section intitulée « Mergeinfo et aperçus » pour les détails). Des fusions dans les copies de travail à révisions mélangées peuvent créer des conflits textuels ou d'arborescence[34]. Nous ne voulons pas conflit inutile, c'est pourquoi nous mettons à jour la copie de travail et nous réessayons la fusion.

$ svn up
Mise à jour de '.' :
Actualisé à la révision 361.

$ svn merge ^/calc/trunk
--- Fusion de r352 à r361 dans '.':
U    src/reel.c
U    src/main.c
-- Stockage des informations de fusion (mergeinfo) de r352 à r361 dans '.' :
 U   .

Subversion sait quelles sont les modifications du tronc que vous avez déjà répercutées vers votre branche, il ne répercute donc que les modifications que vous n'avez pas encore. Une fois de plus, vous devrez compiler, tester et propager avec svn commit les modifications locales à votre branche.

Fusions de sous-arborescences et mergeinfo

Dans la plupart des exemples de ce chapitre, la cible de la fusion est le répertoire racine d'une branche (voir la section intitulée « Définition d'une branche »). Bien que ce soit une bonne pratique, vous aurez peut-être l'occasion de devoir fusionner avec un enfant de la racine de votre branche. Ce type de fusion est appelé une fusion de sous-arborescence et les informations de fusions (« mergeinfo ») stockées pour décrire cela s'appellent les informations de fusion de sous-arborescence ou mergeinfo de sous-arborecences. Il n'y a rien de particulier à signaler pour les fusions de sous-arborescences et les mergeinfo de sous-arborescences. En fait, il n'y a vraiment qu'un seul point à retenir pour ces concepts : l'enregistrement complet des fusions pour une branche peut ne pas être contenu uniquement dans le mergeinfo de la racine de la branche. Vous pouvez avoir à prendre en compte les mergeinfos des sous-arborescences pour obtenir le décompte total. Heureusement, Subversion le fait pour vous et vous n'aurez que rarement l'occasion de vous en préoccuper personnellement. Un court exemple vaut mieux qu'un long discours :

# We devons fusionner r958 depuis le tronc vers branches/proj-X/doc/INSTALL,
# mais cette révision touche aussi main.c, que nous ne voulons pas fusionner :
$ svn log --verbose --quiet -r 958 ^/
------------------------------------------------------------------------
r958 | bruce | 2011-10-20 13:28:11 -0400 (jeu. 20 oct 2011)
Chemins modifiés :
   M /trunk/doc/INSTALL
   M /trunk/src/main.c
------------------------------------------------------------------------

# Pas de problème, nous allons effectuer une fusion d'arborescence
# directement sur le fichier INSTALL, mais d'abord notons les
# informations de mergeinfo relatives à la racine de la branche :
$ cd branches/proj-X

$ svn propget svn:mergeinfo --recursive
Propriétés sur '.'
  svn:mergeinfo
    /trunk:651-652

# Maintenant nous effectuons la fusion d'arborescence.
# Remarquez que la source et la destination de la fusion pointent sur INSTALL :
$ svn merge ^/trunk/doc/INSTALL doc/INSTALL -c 958
-- Fusion de r958 dans 'doc/INSTALL':
U    doc/INSTALL
-- Stockage des informations de fusion (mergeinfo) de r958 dans 'doc/INSTALL' :
 G   doc/INSTALL

# Une fois la fusion effectuée, l'information de fusion de l'arboresence
# est disponible dans INSTALL :
$ svn propget svn:mergeinfo --recursive
Propriétés sur '.' :
  svn:mergeinfo
    /trunk:651-652
Propriétés sur 'doc/INSTALL' :
  svn:mergeinfo
    /trunk/doc/INSTALL:651-652,958

# Que se passe-t-il si nous décidons maintenant d'avoir l'intégralité de
# r958 ? Facile, nous avons seulement à répéter l'opération de fusion
# de cette révision, mais cette fois à la racine de la branche.
# Subversion prend en compte les informations de fusion sur INSTALL et
# n'essaie pas de fusionner quoi ce soit sur ce fichier ; seuls les
# changements sur main.c sont fusionnés.
$ svn merge ^/subversion/trunk . -c 958
-- Fusion de r958 dans '.':
U    src/main.c
-- Stockage des informations de fusion (mergeinfo) de r958 dans '.' :
 U   .
-- Nettoyage des informations de fusion (mergeinfo) de 'doc/INSTALL' :
 U   doc/INSTALL

Vous devez vous demander pourquoi INSTALL dans l'exemple ci-dessus possède des informations de fusion pour r651-652 alors que nous n'avons fusionné que r958. C'est en raison de l'héritage des informations de fusion, que nous abordons dans l'encart particulier Héritages des informations de fusion. Notez aussi que les informations de fusion de l'arborescence ont été supprimées (ou « nettoyées ») de doc/INSTALL. Ce nettoyage des informations de fusion a lieu quand Subversion détecte des informations redondantes.

[Astuce]Astuce

Avant Subversion 1.7, les fusions mettaient à jour de manière inconditionnelle les informations de fusion sous la destination pour décrire la fusion. Pour les utilisateurs qui ont beaucoup d'informations de fusion sur leurs arborescences, cela voulait dire que même des fusions relativement « simles » (par exemple une fusion qui ne concerne qu'un seul fichier) impliquaient des modifications de mergeinfo dans toutes les sous-arborescences, y compris celles qui n'avaient pas de lien de parenté avec le(s) chemin(s) concerné(s). Cela engendrait de la confusion et de la frustration. Subversion 1.7 et suivants répondent à ce problème en ne mettant à jour que les informations de fusion des arborescences qui ont des liens de parenté avec les chemins modifiés par la fusion (c'est-à-dire les chemins modifiés, ajoutés ou supprimés par application d'un changement, voir la section intitulée « Syntaxe de la fusion : pour tout vous dire »). La cible de la fusion fait exception à ce comportement ; les informations de fusion de la cible de fusion sont toujours mises à jour décrire la fusion, même si l'application de la fusion ne produit aucun changement.

Réintegration d'une branche

Que se passe-t-il quand vous finissez enfin votre travail ? Votre nouvelle fonctionnalité est terminée et vous êtes prêt à fusionner les changements de votre branche avec le tronc (pour que votre équipe puisse bénéficier du fruit de votre travail). La procédure est simple. Premièrement, synchronisez à nouveau votre branche avec le tronc, comme vous le faites depuis le début [35]


$ svn up # (pour être sûr que la copie de travail est à jour)
Mise à jour de '.' :
À la révision 378.

$ svn merge ^/calc/trunk
--- Fusion de r362 à r378 dans '.':
U    src/main.c
--- Stockage des informations de fusion (mergeinfo) de r381 à r385 dans '.' :
 U   .

$ # compiler, tester, ...

$ svn commit -m "Fusion finale des modifications du tronc dans ma-branche-calc."
Envoi              .
Envoi              src/main.c
Transmission des données .
Révision 379 propagée.

À présent, utilisez la sous-commande svn merge pour répercuter automatiquement les modifications de votre branche sur le tronc. Ce type de fusion est appelée une fusion de « réintégration automatique ». Vous aurez besoin d'une copie de travail de /calc/trunk. Vous pouvez vous la procurer soit en effectuant un svn checkout, soit en reprenant une vieille copie de travail du tronc, soit en utilisant svn switch (voir la section intitulée « Parcours des branches »).

[Astuce]Astuce

Le terme « réintégration » provient de l'option --reintegrate de la sous-commande merge. Cette option est obsolète dans Subversion 1.8 (qui détecte automatiquement quand une fusion de réintégration est nécessaire), mais elle est demandée par les clients des versions 1.5 à 1.7 de Subversion lorsque vous effectuez des fusions de réintégration.

Votre copie de travail du tronc ne doit avoir aucune modification locale, aucun chemin qui ne pointe vers une autre branche et ne pas comporter de mélange de révisions (voir la section intitulée « Copies de travail mixtes, à révisions mélangées »). Bien que ce soient de bonnes pratiques pour les fusions de toute façon, c'est particulièrement obligatoire pour une fusion de réintégration automatique.

Une fois que vous avez une copie de travail propre du tronc, vous êtes prêt pour y fusionner votre branche :

$ pwd
/home/utilisateur/calc-trunk

$ svn update
Mise à jour '.' :
À la révision 390.


$ svn merge ^/calc/branches/ma-branche-calc
--- Fusion des différences des URLs du dépôt vers '.' :
U    src/reel.c
U    src/main.c
U    Makefile 
-- Stockage des informations de fusion (mergeinfo) des URLs du dépôt vers '.' :
 U   .

$ # compiler, tester, vérifier, ...

$ svn commit -m "ma-branche-calc réintégrée dans le tronc !"
Envoi          .
Envoi          Makefile
Envoi          src/main.c
Envoi          src/reel.c
Transmission des données ...
Révision 380 propagée.

Félicitations, votre branche a maintenant réintégré la ligne de développement principale. Notez que la fusion de réintégration automatique a effectué un travail différent de ce que vous avez fait jusqu'à maintenant. Auparavant, nous demandions à svn merge de récupérer le « prochain lot de modifications » d'une ligne de développement (le tronc en l'occurence) et de l'appliquer à une autre (votre branche). C'est assez simple à réaliser et à chaque fois Subversion sait reprendre là où il s'était arrêté. Dans nos exemples précédents, vous pouvez constater qu'il fusionne en premier les modifications 341:351 de /calc/trunk vers /calc/branches/ma-branche-calc ; ensuite il continue en fusionnant l'intervalle immédiatement suivant, 351:361. Quand il effectue la synchronisation finale, il fusionne l'intervalle 361:378.

Cependant, quand il fusionne /calc/branches/ma-branche-calc vers /calc/trunk, la logique sous-jacente est assez différente. Votre branche dédiée est à présent un amoncellement de modifications provenant à la fois du tronc et de votre branche privée et il n'y a donc pas d'intervalle de révisions contigues à recopier. En utilisant la fusion automatique, vous demandez à Subversion de ne recopier que les modifications spécifiques à votre branche (et en fait il le fait en comparant la version la plus récente de l'arborescence du tronc avec la version la plus récente de l'arborescence de la branche : la différence qui en résulte constitue exactement les modifications de votre branche !).

Gardez à l'esprit que les fusions de réintégration automatiques ne fonctionnent que dans le cas cité ci-dessus. En raison de cette configuration particulière et des autres prérequis annoncés précédemment (une copie de travail à jour[36] sans révisions mélangées, sans chemins qui pointent vers d'autres branches ou modifications locales), elles ne fonctionneront pas en combinaison avec la plupart des autres options de la sous-commande svn merge. Vous obtiendrez une erreur si vous utilisez n'importe laquelle des options non-globales autres que celles-ci : --accept, --dry-run, --diff3-cmd, --extensions ou --quiet.

Maintenant que votre branche privée a réintégré le tronc, vous voudrez peut-être la supprimer du dépôt :

$ svn delete ^/calc/branches/ma-branche-calc \
      -m "Supprime ma-branche-calc, réintégrée dans le tronc à r391."
…

Mais attendez ! L'historique de votre branche ne possède-t-il pas une certaine valeur ? Et si un beau jour quelqu'un voulait auditer l'évolution de votre fonctionnalité et examiner toutes les modifications de votre branche ? Pas la peine de s'inquiéter. Souvenez-vous que, même si votre branche n'est plus visible dans le dossier /calc/branches, son existence demeure une partie immuable de l'historique du dépôt. Une simple commande svn log appliquée à l'URL /calc/branches vous renverra l'historique complet de votre branche. Votre branche pourrait même ressusciter un jour ou l'autre, si vous le désirez (voir la section intitulée « Résurrection des éléments effacés »).

Si vous décidez de ne pas détruire votre branche après réintégration dans le tronc, vous pouvez continuer à effectuer des fusions de synchronisation depuis le tronc puis réintégrer la branche à nouveau [37]. Si vous adoptez ce comportement, seules les modifications effectuées sur votre branche après la réintégration seront fusionnées vers le tronc.

Mergeinfo et aperçus

Le mécanisme de base que Subversion utilise pour gérer les ensembles de modifications, c'est-à-dire quelles modifications ont été fusionnées dans quelles branches, est l'enregistrement de données dans des propriétés suivies en versions. Plus précisément, les informations de fusion sont conservées dans la propriété svn:mergeinfo qui est associée aux fichiers et aux dossiers (si les propriétés de Subversion ne vous sont pas familières, c'est le moment de lire la section intitulée « Propriétés »).

Vous pouvez examiner cette propriété comme n'importe quelle autre propriété suivie en versions :

$ cd ma-branche-calc

$ svn pg svn:mergeinfo -v
Propriétés sur '.'
  svn:mergeinfo
    /calc/trunk:341-378
[Avertissement]Avertissement

Bien qu'il soit possible de modifier soi-même svn:mergeinfo comme n'importe qu'elle autre propriété suivie en versions, nous déconseillons vivement de le faire à moins de réellement savoir ce que vous faites.

[Astuce]Astuce

La quantité de svn:mergeinfo sur un simple chemin peut être assez grande, de même que la sortie produite par svn propget --recursive ou svn proplist --recursive lorsque vous ciblez de grosses quantités d'arborescences, comme l'indique la section intitulée « Fusions de sous-arborescences et mergeinfo ». Dans ces cas, le formatage de la sortie produit par l'option --verbose avec ces deux commandes est souvent bien utile.

La propriété svn:mergeinfo est manipulée automatiquement par Subversion à chaque fois que vous lancez svn merge. Sa valeur indique quelles modifications (pour un chemin donné) ont été recopiées dans le dossier en question. Dans le cas présent, le chemin d'origine de la fusion des modifications est /calc/trunk et le dossier qui a reçu les modifications spécifiées est /calc/branches/ma-branche-calc. Les vieilles versions de Subversion tenaient à jour la propriété svn:mergeinfo silencieusement. Vous en détectiez quand même les modifications, après une fusion, lors de l'utilisation des sous-commandes svn diff ou svn status, mais la fusion en elle-même n'indiquait rien de la modification de la propriété svn:mergeinfo. Dans les versions 1.7 et ultérieures de Subversion, ce n'est plus le cas puisque plusieurs notifications vous avertissent de la mise à jour de la propriété svn:mergeinfo par une opération de fusion. Ces notifications commencent toutes par « --- Stockage des informations de fusion (mergeinfo) » et sont indiquées à la fin de l'opération de fusion. Contrairement aux autres notifications de la fusion, elles ne décrivent pas les modifications apportées à la copie de travail (voir la section intitulée « Syntaxe de la fusion : pour tout vous dire »), mais plutôt la conservation des modifications effectuées pour garder la trace de ce qui a été fusionné.

Il existe également une sous-commande, svn mergeinfo, qui peut être utile pour voir les relations de fusions entre deux branches ; particulièrement, quels ensembles de modifications un dossier a absorbés, mais aussi quels ensembles de modifications il est encore susceptible de recevoir. Ceci donne une sorte d'aperçu du prochain ensemble de modifications que svn merge recopiera vers votre branche. Par défaut, svn mergeinfo donne un aperçu graphique de la relation entre les branches. Pour en revenir à notre exemple précédent, nous utilisons la sous-commande pour analyser la relation entre /calc/trunk et /calc/branches/ma-branche-calc  :

$ cd ma-branche-calc

$svn mergeinfo ^/calc/trunk
    youngest common ancestor
    |         last full merge
    |         |        tip of branch
    |         |        |         repository path
    340                382
    |                  |
  -------| |------------         calc/trunk
     \          /
      \        /
       --| |------------         calc/branches/ma-branche-calc
              |        |
              379      382

Le diagramme indique que /calc/branches/ma-branche-calc a été copié à partir de /calc/trunk@340 et que la fusion automatique la plus récente était la fusion de réintégration que nous avons faite depuis la branche vers le tronc à r380. Notez que le diagramme n'indique pas les quatre fusions de synchronisation que nous avons effectuées aux révisions 352, 362, 372 et 379. Seule la fusion automatique la plus récente est indiquée, quelle que soit sa direction [38]. Cet affichage par défaut est utile pour obtenir un aperçu des fusions entre deux branches, mais pour voir les révisions spécifiques qui ont fait l'objet d'une fusion, nous utilisons l'option --show-revs=merged :

$ svn mergeinfo ^/calc/trunk --show-revs merged
r344
r345
r346
…
r366
r367
r368

De la même manière, pour voir quelles modifications sont éligibles pour une fusion depuis le tronc vers la branche, nous pouvons utiliser l'option --show-revs=eligible :

$ svn mergeinfo ^/calc/trunk --show-revs eligible
r380
r381
r382

La sous-commande svn mergeinfo requiert une URL « source » (d'où proviennent les modifications) et prend optionnellement une URL « cible » (où les modifications sont fusionnées). Si aucune URL cible n'est fournie, Subversion suppose que le répertoire courant est la cible. Dans l'exemple précédent, comme nous interrogeons notre copie de travail de la branche, la commande suppose que nous nous intéressons aux modifications que nous souhaitons apporter à /calc/branches/ma-branche-calc depuis l'URL du tronc telle que spécifiée.

Depuis Subversion 1.7, la sous-commande svn mergeinfo peut également traiter les informations de fusion de la sous-arborescence et les informations de fusion non héritables. Elle traite les informations de fusion de sous-arborescence avec les options --recursive ou --depth, et les informations de fusion non héritables sont traitées par défaut.

Considérons que nous avons une branche avec à la fois une sous-arborescence et des informations de fusion non héritables :

$ svn pg svn:mergeinfo -vR 
# informations de fusion non héritables
Propriétés sur '.'
  svn:mergeinfo
    /calc/trunk:354,385-388* 
# sous-arborescence d'informations de fusion
Propriétés sur 'doc/INSTALL'
  svn:mergeinfo
    /calc/trunk/Makefile:354,380

Dans les informations de fusion ci-dessus, nous voyons que r385-388 a été fusionnée seulement à la racine de la branche et dans aucun des enfants de la racine. Nous voyons aussi que r380 a été fusionnée seulement dans Makefile. Quand nous utilisons svn mergeinfo avec l'option --recursive pour voir ce qui a été fusionné depuis /calc/trunk vers cette branche, nous voyons trois révisions qui sont marquées avec * :

$ svn mergeinfo -R --show-revs=merged ^/calc/trunk .
r354
r380*
r385
r386
r387*
r388*

Cette marque * indique des révisions qui ont fait l'objet de fusions partielles vers la cible en question (la signification est la même que lorsque nous cherchons des révisions éligibles). Dans cet exemple, cela signifie que si nous essayons de fusionner r380, r387 ou r388 depuis ^/trunk alors des modifications seront apportées. De la même manière, puisque r354, r385 et r386 ne sont pas marquées avec *, nous savons que fusionner à nouveau ces révisions ne produira aucun changement.[39]

Une autre manière d'obtenir un aperçu plus précis d'une opération de fusion est d'utiliser l'option --dry-run :

$ svn merge ^/paint/trunk ma-branche-peinture --dry-run
--- Fusion de r290 à r383 dans 'ma-branche-peinture':
U    ma-branche-peinture/src/palettes.c
U    ma-branche-peinture/src/brosses.c
U    ma-branche-peinture/Makefile

$ svn status 
#  rien ne s'affiche, la copie de travail n'a pas changé.

L'option --dry-run n'effectue en fait pas de modification locale sur la copie de travail. Elle ne fait qu'indiquer les codes d'état qui seraient affichés par une vraie fusion. Ceci permet d'obtenir un « aperçu général » d'une fusion potentielle, pour les fois où svn diff renvoie trop de détails.

[Astuce]Astuce

Après avoir effectué une opération de fusion, mais avant d'en avoir propagé les résultats, vous pouvez utiliser svn diff --depth=empty /chemin/vers/la/cible/de/la/fusion pour visualiser uniquement les modifications apportées à la cible immédiate de votre fusion. Si la cible de la fusion est un dossier, seules les différences de propriétés sont alors affichées. C'est un moyen très pratique pour voir les modifications de la propriété svn:mergeinfo enregistrées par l'opération de fusion, qui vous rappellera ce que vous venez juste de fusionner.

Bien sûr, la meilleure façon d'avoir un aperçu d'une opération de fusion est tout simplement de la réaliser ! Souvenez-vous que lancer svn merge n'est pas une opération risquée en soi (à moins que vous ayez effectué des modifications locales dans votre copie de travail, mais nous avons déjà souligné que vous ne devriez pas faire de fusion dans de telles circonstances). Si les résultats de la fusion ne vous plaisent pas, lancez juste svn revert . -R pour ôter les modifications de votre copie de travail et réessayez la commande avec des options différentes. La fusion n'est définitive qu'une fois que vous en avez propagé les résultats par svn commit.

Retour en arrière sur des modifications

Un usage très répandu de svn merge est le retour en arrière sur une modification qui a déjà été propagée. Supposons que vous travaillez tranquillement sur une copie de travail de /calc/trunk et que vous découvrez tout à coup que la modification faite il y a longtemps lors de la révision 392, qui affectait plusieurs fichier sources, est complètement incorrecte. Elle n'aurait jamais du être propagée. Vous pouvez utiliser svn merge pour « revenir en arrière » sur ces modifications dans votre copie de travail, puis propager les modifications locales au dépôt. Il vous suffit juste de spécifier une différence inversée (en indiquant soit --revision 392:391, soit --change -392, les deux se valent).

$ svn merge ^/calc/trunk . -c-392 
-- Fusion inverse de r392 dans '.' :
U    src/reel.c
U    src/main.c
U    src/bouton.c
U    src/entier.c
-- Stockage des informations de fusion (mergeinfo) inverse de r392 vers '.' :
 U   .

$ svn st
M       src/bouton.c
M       src/entier.c
M       src/main.c
M       src/reel.c
$ svn diff
… 
# vérifions que les modifications ont été annulées
…

$ svn commit -m "Retour en arrière sur les modifications propagées en r392."
Envoi          src/bouton.c
Envoi          src/entier.c
Envoi          src/main.c
Envoi          src/reel.c
Envoi          entier.c
Transmission des données ....
Révision 399 propagée.

Comme nous l'avons signalé précédemment, une façon de se représenter une révision du dépôt est de la considérer comme un ensemble de modifications spécifique. En utilisant l'option -r, vous pouvez demander à svn merge d'appliquer un ensemble de modifications, ou tout un groupe d'ensembles de modifications, à votre copie de travail. Dans le cas présent, pour revenir en arrière, nous demandons à svn merge d'appliquer dans le sens inverse l'ensemble de modifications r392 à notre copie de travail.

Gardez à l'esprit que revenir en arrière sur une modification de cette façon est similaire à toute autre opération svn merge, vous devez donc ensuite utiliser svn status et svn diff pour vous assurer que votre travail est dans l'état que vous voulez, puis utiliser svn commit pour propager la version finale au dépôt. Après la propagation, cet ensemble de modifications particulier n'est plus présent dans la révision HEAD.

À nouveau vous vous dites : bon, ceci n'a pas vraiment annulé la propagation, n'est-ce pas ? La modification existe toujours en révision 392. Si quelqu'un extrait une version du projet calc entre les révisions 392 et 398, il verra toujours la mauvaise modification, non ?

Oui, c'est vrai. Quand nous parlons de « supprimer » une modification, il s'agit de la supprimer de la révision HEAD. La modification originale existe toujours dans l'historique du dépôt. Dans la plupart des situations, c'est suffisant. La plupart des gens ne s'intéressent d'ailleurs qu'à la révision HEAD du projet. Il y a des cas particuliers, cependant, où l'on voudra vraiment détruire toute preuve de la propagation (quelqu'un a peut-être accidentellement propagé un document confidentiel). Cela ne s'avère pas si facile, parce que Subversion a été conçu délibérément pour ne jamais perdre d'information. Les révisions sont des arborescences immuables qui sont empilées les unes par dessus les autres. Supprimer une révision de l'historique créerait un effet domino, engendrant le chaos dans les révisions ultérieures et invalidant potentiellement toutes les copies de travail [40].

Résurrection des éléments effacés

Ce qu'il y a de formidable dans les systèmes de gestion de versions, c'est que les informations ne sont jamais perdues. Même si vous effacez un fichier ou un dossier, s'il disparaît bien de la révision HEAD, l'objet existe toujours dans les révisions précédentes. Une des questions les plus courantes que posent les nouveaux utilisateurs est : « Comment est-ce que je récupère mon ancien fichier ou dossier ? »

La première étape est de définir exactement quel élément vous essayez de ressusciter. Voici une métaphore utile : vous pouvez imaginer votre objet dans le dépôt comme existant dans une sorte de système à deux dimensions. La première coordonnée est une révision correspondant à une arborescence particulière ; la deuxième coordonnée est un chemin à l'intérieur de cette arborescence. Ainsi, toute version d'un fichier ou d'un dossier peut être définie par une paire de coordonnées qui lui est propre (souvenez-vous de la syntaxe des « révisions pivots » : machin.c@224, mentionnée dans la section intitulée « Révisions pivots et révisions opérationnelles »).

Tout d'abord, vous allez peut-être avoir besoin de svn log pour identifier précisément les coordonnées du fichier ou dossier que vous voulez ressusciter. À cette fin, une bonne stratégie est de lancer svn log --verbose dans un dossier qui contenait votre élément effacé. L'option --verbose (-v) renvoie la liste de tous les éléments modifiés par chaque révision ; il vous suffit alors de trouver la révision dans laquelle vous avez effacé le fichier ou le dossier en question. Vous pouvez accomplir cette recherche soit visuellement soit en utilisant un autre outil pour examiner le résultat de la commande svn log (via grep ou peut-être via une recherche incrémentale dans un éditeur). Si vous savez que l'élément en question a été effacé recemment, vous pouvez utiliser l'option --limit pour conserver un affichage de l'historique suffisamment bref afin d'être exploité manuellement.

$ cd calc/trunk

$ svn log -v --limit 3
------------------------------------------------------------------------ 
r401 | sally | 2013-02-18 23:15:44 -0500 (mar. 19 fév. 2013) | 1 ligne
Chemins modifiés :
   M /calc/trunk/src/main.c

Suite à r400 : corrections de coquilles dans le texte d'aide.
------------------------------------------------------------------------
r400 | bill | 2013-02-19 20:55:08 -0500 (mar. 19 fév. 2013) | 4 lignes
Chemins modifiés :
   M /calc/trunk/src/main.c
   D /calc/trunk/src/reel.c


* calc/trunk/src/main.c: mise à jour du texte d'aide.

* calc/trunk/src/reel.c: fichier supprimé, aucune API de ce fichier
  n'est encore utilisée.
------------------------------------------------------------------------
r399 | sally | 2013-02-19 20:05:14 -0500 (mar. 19 fév. 2013) | 1 ligne
Chemins modifiés :
   M /calc/trunk/src/bouton.c
   M /calc/trunk/src/entier.c
   M /calc/trunk/src/main.c
   M /calc/trunk/src/reel.c

Retour en arrière sur les modifications propagées en r392.
------------------------------------------------------------------------

Dans l'exemple ci-dessus, nous supposons que vous recherchez un fichier effacé nommé reel.c. En examinant le journal du dossier parent, vous avez découvert que ce fichier a été effacé en révision 400. La dernière version du fichier à avoir existé était donc dans la révision précédant celle-ci. Conclusion : vous voulez ressusciter le chemin /calc/trunk/reel.c tel qu'il était en révision 399.

Voilà, c'était la partie difficile : la recherche. Maintenant que vous savez ce que vous voulez récupérer, deux options s'offrent à vous.

Une possibilité serait d'utiliser svn merge pour appliquer la révision 400 « à l'envers » (nous avons déjà parlé de comment revenir sur des modifications dans la section intitulée « Retour en arrière sur des modifications »). Ceci aurait pour effet de ré-ajouter reel.c en tant que modification locale. Le fichier serait alors programmé pour être ajouté et après la propagation le fichier existerait à nouveau dans HEAD.

Cependant, dans cet exemple particulier, ce n'est probablement pas la meilleure stratégie. Appliquer la révision 400 à l'envers programmerait non seulement l'ajout de reel.c, mais le commentaire de propagation indique qu'il reviendrait aussi sur certaines modifications de main.c, ce que vous ne voulez pas. Vous pourriez certainement fusionner à l'envers la révision 808 et ensuite revenir sur les modifications locales faites dans main.c, mais cette technique fonctionne mal à plus grande échelle. Que dire si 90 fichiers avaient été modifiés en révision 400 ?

Une seconde stratégie, plus ciblée, est de ne pas utiliser svn merge du tout, mais plutôt d'utiliser la commande svn copy. Copiez juste la révision et le chemin exacts (vos deux « coordonnées ») du dépôt vers votre copie de travail :

$ svn copy ^/calc/trunk/src/reel.c@399 ./reel.c
A         reel.c
$ svn st
A  +    reel.c
# Propager la résurrection
…

Le symbole plus dans le résultat de la commande svn status indique que l'élément n'est pas simplement programmé pour ajout, mais programmé pour ajout « avec son historique ». Subversion se souviendra d'où il a été copié. Dans le futur, lancer svn log sur ce fichier parcourra tout son historique en passant par la résurrection du fichier ainsi que tout ce qui précédait la révision 399. En d'autres termes, ce nouveau reel.c n'est pas vraiment nouveau ; c'est un descendant direct du fichier original qui avait été effacé. En général c'est une bonne chose, dont l'utilité est avérée. Si cependant vous vouliez récupérer le fichier sans conserver de lien historique avec l'ancien fichier, la technique suivante fonctionnerait tout aussi bien :

$ svn cat ^/calc/trunk/reel.c@399 > ./reel.c

$ svn add reel.c
A         reel.c

# Propager la résurrection
…

Bien que notre exemple ne porte que sur la résurrection d'un fichier, remarquez que ces mêmes techniques fonctionnent tout aussi bien pour ressusciter des dossiers effacés. Remarquez aussi que cette résurrection ne doit pas forcément avoir lieu dans votre copie de travail ; elle peut avoir lieu entièrement dans le dépôt :

$ svn copy ^/calc/trunk/src/reel.c@399 ^/calc/trunk/src/reel.c \
           -m "Ressuscite reel.c depuis la révision 399."
Révision 402 propagée.

$ svn up
Mise à jour de '.' :
A    reel.c
À la révision 402.

Fusions : pratiques avancées

Ici finit la magie automatisée. Tôt ou tard, une fois que vous maîtrisez bien la gestion des branches et les fusions, vous allez vous retrouver à demander à Subversion de fusionner des modifications spécifiques d'un endroit à un autre. Pour faire cela, vous allez devoir commencer à passer des paramètres plus compliqués à svn merge. Le paragraphe suivant décrit la syntaxe complète de la commande et aborde un certain nombre de scénarios d'utilisation courants qui exploitent la commande.

Sélection à la main

De la même façon que le terme « ensemble de modifications » est utilisé couramment dans les systèmes de gestion de versions, le terme « sélectionner à la main » l'est aussi. Il désigne l'action de choisir une liste de modifications particulière au sein d'une branche et de la recopier dans une autre. Sélectionner à la main peut aussi faire référence à l'action de dupliquer un ensemble de modifications (pas nécessairement contiguës !) d'une branche vers une autre. Ceci est en opposition avec des scénarios de fusion plus courants, où l'ensemble de révisions contiguës « suivant » est dupliqué automatiquement.

Pourquoi voudrait-on ne recopier qu'une modification unique ? Cela arrive plus souvent qu'on ne croit. Par exemple, imaginons que vous ayez créé une branche pour une nouvelle fonctionnalité /calc/branches/ma-branche-nouvelle-calc copiée à partir de /calc/trunk :

$ svn log ^/calc/branches/ma-branche-nouvelle-calc -v -r403
------------------------------------------------------------------------
r403 | user | 2013-02-20 03:26:12 -0500 (mer. 20 fév. 2013) | 1 ligne
Chemins modifiés :
   A /calc/branches/ma-branche-nouvelle-calc (de /calc/trunk:402)

Création d'une nouvelle branche calc pour la fonctionnalité 'X'.
------------------------------------------------------------------------

À la machine à café, vous apprenez par hasard que Sally a apporté une modification intéressante à main.c dans le tronc. Vous reportant à l'historique des propagations du tronc, vous vous apercevez qu'elle a corrigé un bogue crucial en révision 413, qui impacte directement la fonctionnalité sur laquelle vous êtes en train de travailler. Vous n'êtes peut-être pas encore prêt à fusionner toutes les modifications du tronc dans votre branche, mais vous avez certainement besoin de ce correctif pour continuer votre travail.

$ svn log ^/calc/trunk -r413 -v
------------------------------------------------------------------------
r413 | sally | 2013-02-21 01:57:51 -0500 (jeu. 21 fév. 2013) | 3 lignes
Chemins modifiés :
   M /calc/trunk/src/main.c

Corrige le problème #22 'Passer une valeur null dans l'argument truc
de machin() devrait être toléré, mais cela produit un plantage'.
------------------------------------------------------------------------

$ svn diff ^/calc/trunk -c413
Index: src/main.c
===================================================================
--- src/main.c  (revision 412)
+++ src/main.c  (revision 413)
@@ -34,6 +34,7 @@
…
Détails de la correction
…

De la même façon que vous avez utilisé svn diff dans l'exemple précédent pour examiner la révision 413, vous pouvez passer le même paramètre à svn merge :

$ cd ma-branche-nouvelle-calc

$ svn merge ^/calc/trunk -c413
--- Fusion de r413 dans '.':
U    src/main.c
-- Stockage des informations de fusion (mergeinfo) de r413 dans '.' :
 U   .

$ svn st
 M      .
M       src/main.c

Vous pouvez à présent lancer les procédures habituelles de tests, avant de propager cette modification à votre branche. Après la propagation, Subversion met à jour la propriété svn:mergeinfo de votre branche pour indiquer que r413 a été fusionnée dans la branche. Cela empêche qu'une future fusion automatique de synchronisation ne tente d'appliquer une nouvelle fois r413 (fusionner une même modification dans une même branche aboutit presque toujours à un conflit !). Notez aussi les informations de fusion /calc/branches/my-calc-branch:341-379. Cela a été enregistré lors de la précédente fusion de réintégration vers /calc/trunk depuis la branche /calc/branches/ma-branche-calc que nous avons effectué en r380. Quand nous avons créé la branche ma-branche-nouvelle-calc en r403, les informations de fusion sont venues avec le reste de la copie.

$ svn pg svn:mergeinfo -v
Propriétés sur '.' :
  svn:mergeinfo
    /calc/branches/ma-branche-calc:341-379
    /calc/trunk:413

Vous pouvez remarquer que mergeinfo ne marque pas r413 comme « éligible » pour une fusion puisqu'elle a été déjà fusionnée :

$ svn mergeinfo ^/calc/trunk --show-revs eligible
r404
r405
r406
r407
r409
r410
r411
r412
r414
r415
r416
…
r455
r456
r457

Ce qui précède signifie que, quand le temps sera venu de faire une fusion de synchronisation automatique, Subversion séparera la fusion en deux parties : d'abord il fusionnera toutes les révisions éligibles jusqu'à r412, puis il fusionnera toutes les révisions éligibles depuis r414 jusqu'à la révision HEAD. Comme nous avons déjà sélectionné à la main r413, cette modification est sautée :

$ svn merge ^/calc/trunk 
--- Fusion de r403 à r412 dans '.':
U    doc/INSTALL
U    src/main.c
U    src/bouton.c
U    src/entier.c
U    Makefile
U    LISEZMOI
--- Fusion de r414 à r458 dans '.':
G    doc/INSTALL
G    src/main.c
G    src/entier.c
G    Makefile
--- Stockage des informations de fusion (mergeinfo) de r403 à r458  dans '.' :
 U   .

Ce type d'utilisation de la copie (ou rétroportage) de correctifs d'une branche à une autre est peut-être la raison la plus répandue pour sélectionner à la main des modifications ; le cas se présente très souvent, par exemple lorsqu'une équipe gère une « branche de production » du logiciel (ce thème est développé dans la section intitulée « Branches de publication »).

[Avertissement]Avertissement

Avez-vous remarqué la façon dont, dans le dernier exemple, le lancement de la fusion a eu pour effet l'application de deux ensembles distincts de fusions ? La commande svn merge a appliqué deux correctifs indépendants à votre copie de travail, afin de sauter l'ensemble de modifications 413, que votre branche contenait déjà. Il n'y a rien de mal en soi là-dedans, sauf que ça risque de rendre plus délicate la résolution des conflits. Si le premier groupe de modifications engendre des conflits, vous devrez les résoudre de façon interactive pour que la procédure de fusion puisse continuer et appliquer le deuxième groupe de modifications. Si vous remettez à plus tard un conflit lié à la première vague de modifications, la commande de fusion renverra au final un message d'erreur et vous devrez résoudre le conflit avant de lancer la fusion une deuxième fois pour récupérer le reste des modifications.

Avertissement : bien que svn diff et svn merge soient conceptuellement très similaires, leur syntaxe est différente dans de nombreux cas. Pour plus de détails, reportez-vous au Guide de référence de svn : le client texte interactif ou consultez svn help. Par exemple, svn merge demande en entrée, en tant que cible, le chemin d'une copie de travail, c'est-à-dire un emplacement où il va appliquer le correctif généré. Si la cible n'est pas spécifiée, elle suppose que vous essayez d'exécuter l'une des opérations suivantes :

  • Vous voulez fusionner les modifications du dossier dans votre dossier de travail en cours.

  • Vous voulez fusionner les modifications d'un fichier donné dans un fichier du même nom existant dans votre dossier de travail en cours.

Si vous fusionnez un dossier et que vous n'avez pas encore spécifié de cible, svn merge suppose qu'il est dans la première situation et essaie d'appliquer les modifications dans votre dossier en cours. Si vous fusionnez un fichier et que ce fichier (ou un fichier du même nom) existe dans votre dossier de travail en cours, svn merge suppose qu'il est dans la seconde situation et essaie d'appliquer les modifications au fichier local du même nom.

Syntaxe de la fusion : pour tout vous dire

Nous venons de voir des exemples d'utilisation de la commande svn merge et nous allons bientôt en voir plusieurs autres. Si vous n'avez pas bien assimilé le le fonctionnement des fusions, rassurez-vous, vous n'êtes pas un cas isolé. De nombreux utilisateurs (en particulier ceux qui découvrent la gestion de versions) commencent par une phase de perplexité au sujet de la syntaxe de la commande, ainsi que quand et comment utiliser cette fonctionnalité. Mais, en fait, cette commande est bien plus simple que vous ne le pensez ! Il y a une technique très simple pour comprendre comment svn merge agit.

La raison principale de la confusion est le nom de la commande. Le terme merge (« fusionner » en anglais) indique en quelque sorte que les branches vont être combinées, ou qu'un mystérieux mélange des données va avoir lieu. Ce n'est pas le cas. Un nom plus approprié pour cette commande aurait pu être « comparer-et-appliquer », car c'est là tout ce qui se passe : deux arborescences sont comparées et les différences sont appliquées à une copie de travail.

Si vous utilisez svn merge pour effectuer de simples copies de modifications entre branches, elle fait généralement ce qu'il faut automatiquement. Par exemple, une commande telle que :

$ svn merge ^/calc/branches/une-branche

tente de dupliquer toutes les modifications faites dans une-branche vers votre répertoire de travail actuel, qui est sans doute une copie de travail partageant des liens historiques avec la branche. La commande est suffisamment intelligente pour ne copier que les modifications que votre copie de travail ne possède pas encore. Si vous répétez cette commande une fois par semaine, elle ne copie que les modifications « les plus récentes » qui ont eu lieu depuis la dernière fusion.

Si vous choisissez d'utiliser la commande svn merge dans sa version intégrale en lui fournissant les groupes de révisions spécifiques à copier, la commande prend trois paramètres :

  1. une arborescence initiale (souvent appelée côté gauche de la comparaison) ;

  2. une arborescence finale (souvent appelée côté droit de la comparaison) ;

  3. une copie de travail qui reçoit les différences en tant que modifications locales (souvent appelée cible de la fusion).

Une fois ces trois paramètres fournis, les deux arborescences sont comparées et les différences sont appliquées à la copie de travail cible en tant que modifications locales. Une fois que la commande s'est terminée, le résultat est le même que si vous aviez édité les fichiers à la main ou lancé diverses commandes svn add ou svn delete vous-même. Si le résultat vous plaît, vous pouvez le propager. S'il ne vous plaît pas, vous pouvez toujours lancer svn revert pour revenir en arrière sur toutes les modifications.

La syntaxe de svn merge est assez flexible quant à la façon de spécifier les trois paramètres. Voici quelques exemples :

$ svn merge http://svn.exemple.com/depot/branche1@150 \
             http://svn.exemple.com/depot/branche2@212 \
             ma-copie-de-travail

$ svn merge -r 100:200 http://svn.exemple.com/depot/trunk ma-copie-de-travail

$ svn merge -r 100:200 http://svn.exemple.com/depot/trunk

La première syntaxe liste les trois arguments de façon explicite, spécifiant chaque arborescence sous la forme URL@REV et incluant la copie de travail cible. La deuxième syntaxe peut être utilisée comme raccourci pour les cas où vous comparez des révisions différentes de la même URL. Ce type de fusion est appelée (pour des raisons évidentes) une fusion « à 2-URL ». La dernière syntaxe indique que le paramètre copie de travail est optionnel ; s'il est omis, elle utilise par défaut le répertoire en cours.

Si le premier exemple donne la syntaxe « complète » de svn merge, celle-ci doit être utilisée avec grande prudence ; elle peut en effet aboutir à des fusions qui n'enregistrent pas la moindre méta-donnée svn:mergeinfo. Le paragraphe suivant évoque ceci plus en détail.

Fusions sans mergeinfo

Subversion essaie de générer des métadonnées de fusion dès qu'il le peut, afin de rendre plus intelligentes les invocations suivantes de svn merge. Néanmoins, il reste des situations où les données svn:mergeinfo ne sont ni créées ni modifiées. Pensez à être prudent avec les scénarios suivants :

Fusionner des sources sans lien de parenté

Si vous demandez à svn merge de comparer deux URLs qui n'ont pas de lien entre elles, un correctif est quand même généré et appliqué à votre copie de travail, mais aucune métadonnée de fusion n'est créée. Il n'y a pas d'historique commun aux deux sources et les futures fusions « intelligentes » dépendent de cet historique commun.

Fusionner avec des dépôts extérieurs

Bien qu'il soit possible de lancer une commande telle que svn merge -r 100:200 http://svn.projetexterieur.com/depot/trunk, le correctif résultant ne comporte aucune métadonnée historique de fusion. À la date d'aujourd'hui, Subversion n'est pas capable de représenter des URL de dépôts différents au sein de la propriété svn:mergeinfo.

Utiliser --ignore-ancestry

Si ce paramètre est passé à svn merge, il force la logique de fusion à générer les différences sans réfléchir, de la même façon que svn diff les génère, en ignorant toute considération historique. Nous traitons ce point plus loin dans ce chapitre dans la section intitulée « Prise en compte ou non de l'ascendance ».

Appliquer des fusions inversées à l'historique naturel de la cible

Précédemment dans ce chapitre (dans la section intitulée « Retour en arrière sur des modifications »), nous avons vu comment utiliser svn merge pour appliquer un « correctif inversé », comme moyen de revenir en arrière sur des modifications. Si cette technique est utilisée pour revenir sur une modification faite à l'historique propre d'un objet (par exemple, propager r5 au tronc, puis revenir immédiatement en arrière sur r5 en utilisant svn merge . -c -5), ce type de fusion ne touche pas aux informations de fusion (mergeinfo) enregistrées [41].

Plus de détails sur les conflits liés aux fusions

Tout comme la commande svn update, svn merge applique les modifications à votre copie de travail. Elle est donc aussi susceptible de créer des conflits. Cependant, les conflits engendrés par svn merge sont parfois différents et ce paragraphe va expliquer ces différences.

Pour commencer, supposons que votre copie de travail n'a pas de modification locale en cours. Quand vous lancez svn update pour la mettre à jour à une révision particulière, les modifications envoyées par le serveur s'appliquent toujours « proprement » à votre copie de travail. Le serveur génère le delta en comparant deux arborescences : d'une part un instantané virtuel de votre copie de travail, d'autre part l'arborescence de la révision qui vous intéresse. Parce que la partie gauche de la comparaison est parfaitement égale à ce que vous avez déjà, il est garanti que le delta va convertir correctement votre copie de travail en l'arborescence de droite.

Mais svn merge ne dispose pas de telles garanties et peut être bien plus chaotique : l'utilisateur avancé peut demander au serveur de comparer n'importe quelle paire d'arborescences, même des arborescences n'ayant aucun rapport avec la copie de travail ! Cela laisse potentiellement beaucoup de place à l'erreur humaine. Les utilisateurs vont parfois comparer deux arborescences qui ne sont pas les bonnes, créant ainsi un delta qui ne s'appliquera pas proprement. La sous-commande svn merge fera de son mieux pour appliquer la plus grande partie possible du delta, mais ça risque d'être impossible pour certains morceaux. Un conflit d'arborescences inattendu est un bon indice pour détecter que vous avez fusionné un mauvais delta 

$ svn merge ^/calc/trunk -r104:115
--- Fusion de r105 à r115 dans '.' :
   C doc
   C src/bouton.c
   C src/entier.c
   C src/reel.c
   C src/main.c
-- Stockage des informations de fusion (mergeinfo) de r105 à r115 dans '.' :
 U   .  
Résumé des conflits :
  Arborescences en conflit : 3

$ svn st
 M      .
!     C doc
      >   local dir missing, incoming dir edit upon merge
!     C src/button.c
      >   local file missing, incoming file edit upon merge
!     C src/integer.c
      >   local file missing, incoming file edit upon merge
!     C src/main.c
      >   local file missing, incoming file edit upon merge
!     C src/real.c
      >   local file missing, incoming file edit upon merge
Résumé des conflits :
  Arborescences en conflit : 5

Dans l'exemple précédent, il est possible que doc et les quatre fichiers *.c existent dans les deux instantanés de la branche en question. Le delta résultant tente de modifier le contenu des fichiers dans votre copie de travail mais ces fichiers n'existent pas dans la copie de travail. Quoi qu'il en soit, un nombre élevé de conflits d'arborescences signifie généralement que l'utilisateur compare les mauvaises arborescences ou qu'il essaie de fusionner vers une mauvaise copie de travail ; c'est le signe classique d'une erreur de l'utilisateur. Quand ça arrive, il est facile de revenir en arrière de manière récursive sur toutes les modifications créées par la fusion (svn revert . --recursive), d'effacer tout fichier ou dossier non suivi en versions restant après le retour en arrière et de relancer svn merge avec des paramètres différents.

Soyez aussi conscient qu'une fusion dans une copie de travail sans modification locale peut produire également des conflits textuels.

$ svn st

$ svn merge ^/paint/trunk -r289:291
--- Fusion de r290 à r291 dans '.' :
C    Makefile
-- Stockage des informations de fusion (mergeinfo) de r290 à r291 dans '.' :
 U   .  
Résumé des conflits :
  Text conflicts: 1
Conflit découvert dans le fichier 'Makefile'.
Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
        (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p
$ svn st
 M      .
C       Makefile
?       Makefile.merge-left.r289
?       Makefile.merge-right.r291
?       Makefile.working
Résumé des conflits :
  Text conflicts: 2

Comment un conflit peut-il se produire ? Encore une fois, parce que l'utilisateur peut demander à svn merge de calculer et appliquer n'importe quel vieux delta sur la copie de travail, ce delta pouvant contenir des modifications textuelles qui ne s'appliquent pas proprement à un fichier de la copie de travail, même si ce fichier ne comporte aucune modification locale.

Une autre petite différence entre svn update et svn merge réside dans le nom des fichiers textuels créés lorsqu'un conflit survient. Dans la section intitulée « Résolution des conflits », nous avons vu qu'une mise à jour produit des fichiers nommés nom_du_fichier.mine, nom_du_fichier.rANCIENNE_REV et nom_du_fichier.rNOUVELLE_REV. Quand svn merge génère un conflit, elle crée trois fichiers dont les noms sont nom_du_fichier.working, nom_du_fichier.merge-left.rANCIENNE_REV et nom_du_fichier.merge-right.rNOUVELLE_REV. Dans ce cas, les termes « merge-left » (fusion-gauche en anglais) et « merge-right » (fusion-droite en anglais) indiquent de quel côté de la comparaison d'arborescences provient le fichier en question ; « rANCIENNE_REV » indique la révision du côté gauche et « rNOUVELLE_REV » indique la révision du côté droit. Dans tous les cas, ces noms différenciés vous aident à distinguer les conflits qui résultent d'une mise à jour de ceux qui résultent d'une fusion.

Blocage de modifications

Il peut parfois y avoir un ensemble de modifications particulier dont vous ne voulez pas qu'il soit fusionné automatiquement. Par exemple, peut–être que l'habitude dans votre équipe est d'effectuer tout nouveau travail de développement dans /trunk, mais d'être plus conservateur en ce qui concerne le rétroportage des modifications vers une branche stable que vous utilisez pour la publication. À l'extrême, vous pouvez sélectionner à la main des ensembles de modifications individuels du tronc à porter vers la branche : juste les changements qui sont suffisamment stables pour être acceptables. Peut-être que les choses ne sont pas aussi strictes après tout ; peut-être que la plupart du temps vous aimeriez juste laisser svn merge fusionner automatiquement la plupart des modifications du tronc vers la branche. Dans ce cas, il vous faudrait une façon de masquer quelques modifications particulières, c'est-à-dire d'empêcher qu'elles ne soient fusionnées automatiquement.

Pour bloquer une liste de modifications, vous devez faire croire à Subversion que la modification a déjà été fusionnée. Pour cela, il est possible de lancer la sous-commande de fusion avec l'option --record-only. Cette option demande à Subversion d'enregistrer les informations de fusion comme s'il avait réellement effectué la fusion, mais aucun changement n'est effectivement appliqué :

$ cd ma-branche-calc

$ svn merge ^/calc/trunk -r386:388 --record-only 
--- Stockage des informations de fusion (mergeinfo) de r387 à r388 dans '.' :
 U   .
 
# Seules les informations de fusion sont modifiées.
$ svn st
 M      .

$ svn pg svn:mergeinfo -vR
Propriétés sur '.' :
  svn:mergeinfo
    /calc/trunk:341-378,387-388


$ svn commit -m "Bloque r387-388 vis-à-vis d'une fusion vers ma-branche-calc."
Envoi              .

Révision 461 propagée.

Depuis Subversion 1.7, les fusions avec --record-only sont transitives. Cela veut dire que, en plus d'enregistrer les informations de fusion décrivant la ou les révisions bloquées, toute modification de la propriété svn:mergeinfo dans la source de la fusion est aussi appliquée. Par exemple, supposons que nous voulions bloquer la fonctionnalité "paint-python-wrapper" d'être fusionnée depuis ^/paint/trunk vers la branche ^/paint/branches/paint-1.0.x. Nous savons que le travail sur cette fonctionnalité a été effectué dans sa propre branche, qui a été réintégrée dans /paint/trunk au moment de la révision 465 :

$ svn log -v -r465 ^/paint/trunk
------------------------------------------------------------------------
r465 | joe | 2013-02-25 14:05:12 -0500 (lun. 25 fév. 2013) | 1 ligne
Chemins modifiés :
   M /paint/trunk
   A /paint/trunk/python (de /paint/branches/paint-python-wrapper/python:464)

Réintégration de Paint Python Wrapper.
------------------------------------------------------------------------

Comme la révision 465 était une fusion de réintégration, nous savons que les informations de fusion ont été enregistrées pour décrire cette fusion :

$ svn diff ^/paint/trunk --depth empty -c465
Index: .
===================================================================
--- .   (revision 464)
+++ .   (revision 465)

Modification de propriétés sur .
___________________________________________________________________ 
Added: svn:mergeinfo
   Fusionné /paint/branches/paint-python-wrapper:r463-464

Maintenant, simplement bloquer les fusions de la révision 465 de /paint/trunk n'est pas suffisant car quelqu'un pourrait fusionner r462:464 directement depuis /paint/branches/paint-python-wrapper. Heureusement, grace à la transitivité de --record-only, les fusions seront interdites. La fusion --record-only applique le delta calculé à partir de svn:mergeinfo à la révision 465, bloquant par conséquent les fusions de cette modification directement depuis /paint/trunk et indirectement depuis /paint/branches/paint-python-wrapper :

$ cd paint/branches/paint-1.0.x

$ svn merge ^/paint/trunk --record-only -c465
--- Fusion de r465 dans '.' :
 U   .  
--- Stockage des informations de fusion (mergeinfo) de r465 dans '.' :
 G   .

$ svn diff --depth empty
Index: .
===================================================================
--- .   (révision 462)
+++ .   (copie de travail)

Modification de propriétés sur  .
___________________________________________________________________
Added: svn:mergeinfo
   Fusionné /paint/branches/paint-python-wrapper:r463-464
   Fusionné /paint/trunk:r465

$ svn ci -m "Blocage du wrapper Python dans la version 1.0 de paint."
Envoi           .

Révision 466 propagée.

Maintenant, toute tentative de fusionner la fonctionnalité vers /paint/trunk est inopérante :

$ svn merge ^/paint/trunk -c465
--- Stockage des informations de fusion (mergeinfo) de r456 dans '.' :
 U   .

$ svn st # Rien n'a changé !

$ svn merge ^/paint/branches/paint-python-wrapper -r462:464
---  Stockage des informations de fusion (mergeinfo) de r463 bis r464 in '.' :
 U   .

$ svn st # Rien n'a changé !
$

Si, plus tard, vous vous rendez compte que vous avez besoin de fusionner la fonctionnalité bloquée vers /paint/trunk , vous avez deux possibilités. Vous pouvez rejouer à l'envers la fusion r466 (la révision dans laquelle vous avez bloqué la fonctionnalité), comme cela a été montré dans la section intitulée « Retour en arrière sur des modifications ». Une fois que vous avez propagé cette modification, vous pouvez recommencer la fusion de r465 de /paint/trunk. L'autre façon, vous pouvez simplement rejouer la fusion de r465 depuis /paint/trunk avec l'option --ignore-ancestry. Cela forcera la fusion à ignorer les informations contenues dans mergeinfo et appliquera simplement les changements demandés, comme indiqué dans la section intitulée « Prise en compte ou non de l'ascendance ».

$ svn merge ^/paint/trunk -c465 --ignore-ancestry
--- Fusion de r465 in '.' :
A    python
A    python/paint.py
 G   .

Bloquer des modifications avec l'option --record-only fonctionne, mais cela peut s'avérer dangereux.Le problème principal est que nous ne faisons pas clairement la différence entre « J'ai déjà cette modification » et « Je n'ai pas cette modification, mais je n'en veux pas pour le moment. ». En fait, nous mentons au système, en lui faisant croire que la modification a déjà été fusionnée. Ce qui transfère vers vous, l'utilisateur, la responsabilité de vous rappeler que la modification n'a en fait pas été fusionnée, qu'elle n'était tout simplement pas voulue. Il n'y a pas moyen de demander à Subversion la liste des « listes des modifications bloquées ». Si vous voulez en conserver la trace (afin de pouvoir les débloquer un jour), vous devrez les consigner dans un fichier texte quelque part, ou peut-être dans une propriété inventée de toutes pièces pour l'occasion.

Historiques et annotations tenant compte des fusions passées

Une des fonctionnalités principales de tout système de gestion de versions est de conserver la trace de qui a modifié quoi et quand ils l'ont fait. Les sous-commandes svn log et svn blame sont les outils adaptés pour cela : quand on les applique à des fichiers individuels, ils renvoient non seulement l'historique des ensembles de modifications qui ont touché le fichier, mais aussi exactement quel utilisateur a écrit quelle ligne de code et quand il l'a fait.

Cependant, quand des modifications commencent à être copiées entre des branches, les choses se compliquent. Par exemple, si vous interrogiez svn log sur l'historique de votre branche fonctionnelle, elle renverrait exactement toutes les révisions qui ont touché cette branche :

$ cd ma-branche-calc

$ svn log -q
------------------------------------------------------------------------
r461 | utilisateur | 2013-02-25 05:57:48 -0500 (lun. 25 fév. 2013)
------------------------------------------------------------------------
r379 | utilisateur | 2013-02-18 10:56:35 -0500 (lun. 18 fév. 2013)
------------------------------------------------------------------------
r378 | utilisateur | 2013-02-18 09:48:28 -0500 (lun. 18 fév. 2013)
------------------------------------------------------------------------
…
------------------------------------------------------------------------
r8 | sally | 2013-01-17 16:55:36 -0500 (jeu. 17 janv. 2013)
------------------------------------------------------------------------
r7 | bill | 2013-01-17 16:49:36 -0500 (jeu. 17 janv. 2013)
------------------------------------------------------------------------
r3 | bill | 2013-01-17 09:07:04 -0500 (jeu. 17 janv. 2013)
------------------------------------------------------------------------

Mais est-ce bien là une description adéquate de tous les changements qui ont eu lieu sur cette branche ? Ce qui manque ici, c'est le fait que les révisions 352, 362, 372 et 379 résultaient en fait de fusions en provenance du tronc. Si vous regardez plus en détail l'historique d'une de ces révisions, vous ne verrez nulle part les multiples ensembles de modifications du tronc qui ont été reportés sur la branche :

$ svn log ^/calc/branches/ma-branche-calc -r352 -v
------------------------------------------------------------------------ 
r352 | utilisateur | 2013-02-16 09:35:18 -0500 (sam. 16 fév. 2013) | 1 ligne
Chemins modifiés :
   M /calc/branches/ma-branche-calc
   M /calc/branches/ma-branche-calc/Makefile
   M /calc/branches/ma-branche-calc/doc/INSTALL
   M /calc/branches/ma-branche-calc/src/bouton.c
   M /calc/branches/ma-branche-calc/src/reel.c

Synchronisation des dernières modifications du tronc dans ma-branche-calc.

Il se trouve que nous savons que cette fusion vers la branche n'était qu'une fusion de modifications du tronc. Comment pouvons-nous également voir ces modifications du tronc ? La réponse est d'utiliser l'option --use-merge-history (-g). Cette option donne le détail des modifications « filles » qui faisaient partie de la fusion.

$ svn log ^/calc/branches/my-calc-branch -r352 -v -g
------------------------------------------------------------------------ 
r352 | utilisateur | 2013-02-16 09:35:18 -0500 (sam. 16 fév. 2013) | 1 ligne
Chemins modifiés :
   M /calc/branches/ma-branche-calc
   M /calc/branches/ma-branche-calc/Makefile
   M /calc/branches/ma-branche-calc/doc/INSTALL
   M /calc/branches/ma-branche-calc/src/bouton.c
   M /calc/branches/ma-branche-calc/src/reel.c

Synchronisation des dernières modifications du tronc dans ma-branche-calc.
------------------------------------------------------------------------ 
r351 | sally | 2013-02-16 08:04:22 -0500 (sam. 16 fév. 2013) | 2 lignes
Chemins modifiés :
   M /calc/trunk/src/real.c
Fusion via : r352

Travail sur le tronc du projet calc.
------------------------------------------------------------------------
…
------------------------------------------------------------------------ 
r345 | sally | 2013-02-15 16:51:17 -0500 (ven. 15 fév. 2013) | 2 lignes
Chemins modifiés :
   M /calc/trunk/Makefile
   M /calc/trunk/src/entier.c
Fusion via : r352

Travail sur le tronc du projet calc.
------------------------------------------------------------------------
r344 | sally | 2013-02-15 16:44:44 -0500 (ven. 15 fév. 2013) | 1 ligne
Chemins modifiés :
   M /calc/trunk/src/entier.c
Fusion via : r352

Réusinage des fonctions trucmuches.

En forçant l'opération svn log à utiliser l'historique des fusions, nous obtenons non seulement la révision que nous avions demandé (r352), mais aussi les deux révisions qui l'accompagnaient — deux modifications du tronc faites par Sally. C'est une image bien plus complète de l'historique !

La commande svn blame accepte également l'option --use-merge-history (-g). Si cette option est omise, quelqu'un qui regarderait un relevé annoté ligne par ligne pour bouton.c risquerait d'avoir l'impression erronée que vous êtes responsable des lignes qui ont corrigé une certaine erreur :

$ svn blame src/button.c
…
   352    utilisateur  retval = inverse_func(button, path);
   352    utilisateur  return retval;
   352    utilisateur  }
…

Et bien qu'il soit vrai que vous avez propagé ces trois lignes lors de la révision 352, deux d'entre elles ont en fait été écrites par Sally auparavant, en révision 348, et ont été injectées dans votre branche lors d'une fusion de synchronisation :

$ svn blame button.c -g
…
G    348    sally        retval = inverse_func(button, path);
G    348    sally        return retval;
     352    utilisateur  }
…

À présent, nous savons qui doit réellement être tenu responsable pour ces deux lignes de code !

Prise en compte ou non de l'ascendance

Si vous discutez avec un développeur Subversion, il est probable qu'il fasse référence au terme d'ascendance. Ce mot est utilisé pour décrire la relation entre deux objets dans un dépôt : s'ils sont liés l'un à l'autre, un des objets est alors qualifié d'ancêtre de l'autre.

Par exemple, supposons que vous propagiez la révision 100 qui contient une modification d'un fichier truc.c. Dès lors, truc.c@99 est un « ancêtre » de truc.c@100. En revanche, supposons que vous propagiez la suppression de truc.c en révision 101 et ensuite l'ajout d'un nouveau fichier du même nom en révision 102. Dans ce cas, truc.c@99 et truc.c@102 pourraient sembler apparentés (ils ont le même chemin), mais en fait ce sont des objets complètement différents au sein du dépôt. Ils ne partagent aucun historique ou « ascendance ».

Nous abordons ce point pour mettre en évidence une différence importante entre svn diff et svn merge. La première commande ignore toute ascendance, tandis que la seconde y est particulièrement sensible. Par exemple, si vous demandez à svn diff de comparer les révisions 99 et 102 de truc.c, vous obtenez des différences basées sur les lignes ; la commande svn diff compare deux chemins à l'aveugle. Mais si vous demandez à svn merge de comparer les deux mêmes objets, elle remarque qu'ils ne sont pas liés et essaie d'abord de supprimer l'ancien fichier, puis d'ajouter le nouveau fichier ; le résultat indique une suppression puis un ajout :

D    truc.c
A    truc.c

La plupart des fusions impliquent de comparer des arborescences qui ont une relation d'ascendance de l'une à l'autre ; c'est pourquoi svn merge a ce comportement par défaut. Cependant, à l'occasion, vous pourriez vouloir que la commande svn merge compare deux arborescences sans relation d'ascendance entre elles. Par exemple, vous avez peut-être importé deux arborescences de code source représentant des publications différentes de deux fournisseurs d'un projet logiciel (voir la section intitulée « Branches fournisseurs »). Si vous demandez à svn merge de comparer les deux arborescences, vous verrez la première arborescence complètement supprimée, puis l'ajout de la seconde arborescence toute entière ! Dans ce genre de situations, vous attendez de svn merge qu'il effectue une comparaison basée sur les chemins uniquement, en ignorant toute relation entre les fichiers et les dossiers. Ajoutez l'option --ignore-ancestry à votre commande svn merge et elle se comportera comme svn diff (et inversement, l'option --notice-ancestry fera se comporter svn diff comme la commande svn merge).

[Astuce]Astuce

L'option --ignore-ancestry désactive le suivi des fusions (voir Suivi de fusions). Das bedeutet, dass weder svn:mergeinfo berücksichtigt wird, wenn svn merge ermittelt, welche Revisionen zusammengeführt werden sollen, noch svn:mergeinfo aufgezeichnet wird, um die Zusammenführung zu beschreiben.

Fusions, copies et renommages

Il arrive souvent qu'on veuille réorganiser le code source, en particulier dans les projets logiciels en Java. Les fichiers et les répertoires sont déplacés et renommés, causant souvent de grandes perturbations pour tous ceux qui travaillent sur le projet. Ceci semble être le cas idéal où utiliser une branche, n'est-ce pas ? Créer une branche, réorganiser les choses, et ensuite fusionner la branche vers le tronc, non ?

Hélas, ce scénario ne fonctionne pas si bien pour le moment et est même considéré comme l'une des faiblesses de Subversion. Le problème est que la commande svn update de Subversion n'est pas aussi robuste qu'elle le devrait, en particulier en ce qui concerne les opérations de copies et de déplacements.

Quand vous utilisez svn copy pour dupliquer un fichier, le dépôt se souvient d'où venait le nouveau fichier, mais il ne transmet pas cette information au client qui lance svn update ou svn merge. Au lieu de dire au client « Copie ce fichier que tu as déjà vers ce nouvel emplacement », il envoie un fichier entièrement nouveau. Ceci peut engendrer des problèmes, notamment des conflits d'arborescences dans le cas de renommage de fichiers, pour ce qui concerne la nouvelle copie et la suppression de l'ancien emplacement. Un fait peu connu à propos de Subversion est qu'il lui manque de « vrais renommages » — la commande svn move n'est rien de plus que l'agrégation de svn copy et svn delete.

Par exemple, supposons que vous travaillez sur votre branche privée /calc/branches/ma-branche-calc. D'abord, vous effectuez une fusion automatique de synchronisation avec /calc/trunk et vous la propagez dans r470 :

$ cd calc/trunk

$ svn merge ^/calc/trunk 
--- Fusion des différences des URLs du dépôt vers '.' :
U    doc/INSTALL
A    FAQ
U    src/main.c
U    src/bouton.c
U    src/entier.c
U    Makefile
U    LISEZMOI
 U   .
-- Stockage des informations de fusion (mergeinfo) des URLs du dépôt dans '.' :
 U   .

$ svn ci -m "Synchronisation des changements de ^/calc/trunk jusqu'à r469."
Envoi         .
Envoi         Makefile
Envoi         LISEZMOI
Envoi         FAQ
Envoi         doc/INSTALL
Envoi         src/main.c
Envoi         src/bouton.c
Envoi         src/entier.c
Transmission des données ....
Révision 470 propagée.

Puis vous renommez entier.c en tout.c dans r471 et vous effectuez des modifications dans ce même fichier dans r473. Concrètement, vous avez créé un nouveau fichier dans votre branche (c'est une copie du fichier original avec quelques modifications) et vous avez effacé le fichier original. Pendant ce temps, sur /calc/trunk, Sally a propagé des améliorations de son cru au fichier entier.c lors de la r472 :

$ svn log -v -r472 ^/calc/trunk
------------------------------------------------------------------------
r472 | sally | 2013-02-26 07:05:18 -0500 (dim. 26 fév. 2013) | 1 ligne
Chemins modifiés :
   M /calc/trunk/src/entier.c

Travail d'amélioration de entier.c sur le tronc.
------------------------------------------------------------------------

C'est alors que vous décidez de fusionner votre branche vers le tronc. Comment Subversion combine-t-il le renommage et les modifications que vous avez faits avec les modifications de Sally ?

$ svn merge ^/calc/branches/ma-branche-calc
--- Fusion des différences des URLs du dépôt dans '.' :
   C src/entier.c
 U   src/reel.c
A    src/tout.c
--- Stockage des informations de fusion (mergeinfo) des URLs du dépôt dans '.' :
 U   .
Résumé des conflits:
  conflits d'arborescences : 1

$ svn st
 M      .
      C src/entier.c
      >   local file edit, incoming file delete upon merge
 M      src/reel.c
A  +    src/tout.c
Résumé des conflits:
  conflits d'arborescences : 1

La réponse est que Subversion ne combinera pas les modifications, mais générera un conflit d'arborescences[42]parce qu'il a besoin de votre aide pour déterminer quelle part de vos modifications et quelle part des modifications de Sally doivent finalement se retrouver dans tout.c,ou même si le renommage a tout simplement lieu d'être !

Vous devrez résoudre le conflit d'arborescences avant de pouvoir propager la fusion, ce qui requiert un minimum d'intervention manuelle de votre part, voir la section intitulée « Gestion des conflits d'arborescences ». La morale de cette histoire, c'est que tant que Subversion ne se sera pas amélioré, soyez vigilant lors des fusions avec copies et renommages d'une branche vers une autre et, lorsque vous le faites, préparez-vous à effectuer des résolutions manuelles.

Blocage des clients qui ne prennent pas en compte les fusions

Si vous venez juste de mettre à niveau votre serveur Subversion à la version 1.5 ou plus, il existe un risque significatif que les clients Subversion pré-1.5 sèment la pagaille dans votre suivi automatique des fusions. Pourquoi ? Quand un client Subversion pré-1.5 exécute svn merge, il ne modifie pas du tout la valeur de la propriété svn:mergeinfo. La propagation qui s'ensuit, bien qu'elle soit le résultat d'une fusion, n'envoie donc aucune indication au dépôt au sujet des modifications dupliquées — ces informations sont perdues. Par la suite, lorsque des clients « qui prennent en compte les fusions » tentent d'effectuer une fusion automatique, ils rencontreront probablement toutes sortes de conflits résultant des fusions répétées.

Si votre équipe et vous dépendez des fonctionnalités de suivi des fusions de Subversion, vous voudrez peut-être configurer votre dépôt pour qu'il empêche les anciens clients de propager des modifications. La méthode la plus simple est d'examiner le paramètre « capabilities » dans la procédure automatique de début de propagation (start-commit). Si le client indique être capable de gérer les mergeinfo, la procédure automatique peut l'autoriser à commencer la propagation. Si le client n'indique pas en être capable, la procédure automatique doit lui refuser la propagation. Exemple 4.1, « Procédure automatique de vérification des capacités de suivi des fusions avant une propagation » donne un exemple d'une telle procédure automatique.

Exemple 4.1. Procédure automatique de vérification des capacités de suivi des fusions avant une propagation

# -*- coding: utf-8 -*-
#!/usr/bin/env python
import sys

# La procédure automatique start-commit est appelée après la création
# de la transaction Subversion et peuplée avec propriétés de révision
#
#
# [1] CHEMIN-DEPOT  (le chemin du dépôt)
# [2] UTILISATEUR   (l'utilisateur authentifié qui tente la propagation)
# [3] CAPACITES     (liste des capacités qu'annonce le client,
#                    les éléments sont séparés par des ':'
#                    voir ci-dessous)
# [4] NOM-TRANSACTION  (nom de la transaction qui vient d'être créée)

capacites = sys.argv[3].split(':')
if "mergeinfo" not in capacites:
  sys.stderr.write("Les propagations depuis un client non capable de"
                   "suivre les fusions sont interdites. "
                   "Veuillez mettre à niveau votre client  à "
                   "Subversion 1.5 ou plus récent.\n")
  sys.exit(1)
sys.exit(0)


Pour plus d'informations sur les procédures automatiques, reportez-vous au la section intitulée « Mise en place des procédures automatiques ».

Recommandations finales sur le suivi des fusions

En fin de compte, la fonctionnalité de suivi des fusions de Subversion possède une mécanique interne extrêmement complexe et la propriété svn:mergeinfo est la seule lorgnette dont l'utilisateur dispose pour observer cette mécanique.

Quand et pourquoi les informations de fusions sont enregistrées par une fusion peut parfois être difficile à comprendre. De plus, la gestion des informations de fusions comporte tout un ensemble de taxonomies et de règles, telles que les informations « explicites » et celles « implicites », les révisions « effectives » et les « non-effectives », le « nettoyage » et « l'héritage » d'un dossier parent vers les dossiers enfants.

Nous avons choisi de ne pas couvrir en détail ces sujets dans ce livre pour plusieurs raisons. Premièrement, l'utilisateur moyen serait totalement submergé par le niveau de détail disponible. Deuxièmement, et c'est le plus important, nous estimons que l'utilisateur moyen ne doit pas avoir à comprendre ces concepts ; en tant que détails d'implémentation, ils restent à l'arrière-plan. Malgré tout, si vous appréciez ce genre de choses, vous en trouverez une formidable vue d'ensemble dans un article posté sur le site internet de Collabnet (aujourd'hui recopié sur le site Web de Subversion) : https://subversion.apache.org/blog/2008-05-06-merge-info.html.

Pour le moment, si vous voulez rester à l'écart de la complexité du suivi de fusions, nous vous recommandons de vous en tenir simplement aux bonnes pratiques suivantes :

  • Pour les branches fonctionnelles à courte durée de vie, suivez la procédure simple décrite dans la section intitulée « Fusions : pratiques de base ».

  • Evitez les fusions sous les sous-arborescences et de générer des informations de fusions sur les sous-dossiers. Ne pratiquez de fusions que sur la racine de la branche, pas sur des sous-répertoires. la section intitulée « Fusions de sous-arborescences et mergeinfo »).

  • Ne modifiez jamais la propriété svn:mergeinfo directement ; utilisez svn merge avec l'option --record-only pour appliquer une modification désirée à cette métadonnée (comme expliqué dans la section intitulée « Blocage de modifications »).

  • Votre cible de fusion devrait toujours être une copie de travail qui est la racine d'une arborescence complète représentant une seule position dans le dépôt à un moment bien précis dans le temps :

    • mettez à jour avant de fusionner ! N'utilisez pas l'option --allow-mixed-revisions pour fusionner vers des copies de travail à révisions mélangées.

    • ne fusionnez pas vers des cibles dont des dossiers pointent vers d'autres branches (tels que décrits dans la section intitulée « Parcours des branches »).

    • évitez les fusions vers des cibles avec des répertoires clairsemés. De la même manières, ne fusionnez pas pour des profondeurs autres que --depth=infinity

    • Assurez-vous de toujours avoir l'accès complet en lecture à toutes vos sources de fusion et l'accès en lecture/écriture à l'ensemble de la cible de la fusion.

Bien sûr, vous pouvez être amené parfois à devoir violer certaines bonnes pratiques. Dans ce cas, ne vous inquietez pas, soyez seulement conscient des conséquences que cela engendre.

Parcours des branches

La commande svn switch transforme une copie de travail existante de telle sorte qu'elle pointe vers une branche différente. Bien que la connaissance de cette commande ne soit pas absolument nécessaire pour travailler avec des branches, elle fournit un raccourci utile. Dans l'un de nos exemples précédents, après avoir créé votre branche privée, vous avez extrait une toute nouvelle copie de travail du nouveau répertoire du dépôt. À la place, vous pouvez simplement demander à Subversion de modifier votre copie de travail de /calc/trunk pour qu'elle pointe vers l'emplacement de la nouvelle branche :

$ cd calc

$ svn info | grep URL
URL: http://svn.exemple.com/depot/calc/trunk

$ svn switch ^/calc/branches/ma-branche-calc
U   entier.c
U   bouton.c
U   Makefile
Actualisé à la révision 341.

$ svn info | grep URL
URL: http://svn.exemple.com/depot/calc/branches/ma-branche-calc

« Faire pointer » (ou « déporter ») une copie de travail qui n'a pas de modifications locales vers une branche différente a pour résultat que la copie de travail a exactement le même aspect que si vous aviez effectué une extraction brute du répertoire. C'est en général plus efficace d'utiliser cette commande, car les différences entre les branches sont souvent minimes. Le serveur n'envoie que le minimum de modifications nécessaire pour faire pointer votre copie de travail vers le répertoire de la branche.

La commande svn switch accepte également l'option --revision (-r), pour que que vous ne soyez pas obligé de faire pointer votre copie de travail vers la révision HEAD de la branche.

Bien sûr, beaucoup de projets sont plus compliqués que notre exemple calc et contiennent de multiples sous-dossiers. Les utilisateurs de Subversion suivent souvent un algorithme précis quand ils utilisent des branches :

  1. Copier le « tronc » entier du projet vers une nouvelle branche ;

  2. Ne déporter qu'une partie de la copie de travail du tronc pour qu'elle pointe sur la branche.

En d'autres termes, si un utilisateur sait que le travail sur la branche ne doit avoir lieu que sur un sous-dossier donné, il utilise svn switch pour ne faire pointer que ce sous-dossier vers la branche (ou parfois des utilisateurs ne vont faire pointer qu'un unique fichier de travail vers la branche !). De cette façon, l'utilisateur peut continuer à recevoir les mises à jour normales du « tronc » vers la plus grande partie de sa copie de travail, mais les portions déportées ne seront pas touchées (à moins que quelqu'un ne propage une modification à sa branche). Cette fonctionnalité ajoute une dimension complètement nouvelle au concept de « copie de travail mixte » : les copies de travail peuvent non seulement contenir un mélange de révisions de travail, mais elles peuvent également contenir un mélange d'emplacements du dépôt.

[Astuce]Astuce

Typiquement, les sous-dossiers déportés partagent un ancêtre commun avec l'emplacement d'où ils ont été déportés. Cependant, svn switch peut déporter un sous-dossier pour refléter un emplacement du dépôt qui ne partage aucun ancêtre avec ce sous-dossier. Pour ce faire, vous devez utiliser l'option --ignore-ancestry.

Si votre copie de travail contient un certain nombre de sous-arborescences pointant vers des emplacements variés du dépôt, elle continue à fonctionner normalement. Quand vous la mettez à jour, vous recevez comme il se doit les correctifs pour chaque sous-arborescence. Quand vous effectuez une propagation, vos modifications locales s'appliquent toujours au dépôt en tant qu'une unique modification atomique.

Remarquez que, bien qu'il soit possible pour votre copie de travail de pointer vers une variété d'emplacements du dépôt, ces emplacements doivent tous faire partie du même dépôt. Les dépôts Subversion ne sont pas encore capables de communiquer entre eux ; cette fonctionnalité est prévue à l'avenir [43].

[Astuce]Astuce

Les administrateurs qui doivent modifier l'URL d'un dépôt accessible via HTTP sont encouragés à ajouter à leur fichier de configuration httpd.conf une redirection permanente de l'ancienne URL vers la nouvelle (à l'aide de la directive RedirectPermanent). Les clients Subversion affichent généralement la nouvelle URL du dépôt dans les messages d'erreur produits lorsque l'utilisateur essaye de travailler avec une copie de travail qui pointe vers l'ancienne URL. Depuis Subversion 1.7, les clients font un pas supplémentaire en faisant pointer automatiquement la copie de travail vers la nouvelle URL.

Parce que svn switch est essentiellement une variante de svn update, elle se comporte de la même manière ; toute modification locale présente dans votre copie de travail est préservée lorsque de nouvelles données arrivent en provenance du dépôt.

[Astuce]Astuce

Vous-êtes vous déjà trouvés dans une situation où vous effectuez des modifications complexes (dans votre copie de travail de /trunk) et réalisez soudainement :« Mais, ces modifications ne devraient-elles pas être dans leur propre branche ? » Une excellente technique pour accomplir ceci peut être résumée en deux étapes :

$ svn copy http://svn.exemple.com/depot/calc/trunk \
           http://svn.exemple.com/depot/calc/branches/nouvelle-branche \
      -m "Création de la branche 'nouvelle-branche'."
Révision 353 propagée.
$ svn switch http://svn.exemple.com/depot/calc/branches/nouvelle-branche
À la révision 353.

La commande svn switch, à l'instar de svn update, préserve vos modifications locales. Désormais, votre copie de travail pointe vers la branche nouvellement créée et la prochaine fois que vous lancerez svn commit vos modifications y seront envoyées.

Étiquettes

Un autre concept courant en gestion de versions est l'étiquette (parfois appelée tag). Une étiquette n'est qu'un « instantané » d'un projet à un moment donné. Dans Subversion, cette idée semble être présente partout. Chaque révision du dépôt est exactement cela : un instantané du système de fichiers pris après chaque propagation.

Cependant les gens veulent souvent donner des noms plus conviviaux aux étiquettes, tel que version-1.0. Et ils veulent prendre des instantanés de sous-sections plus restreintes du système de fichiers. Après tout, ce n'est pas si facile de se rappeler que la version 1.0 d'un logiciel donné correspond à un sous-dossier particulier de la révision 4822.

Création d'une étiquette simple

Une fois encore, svn copy vient à la rescousse. Si vous voulez créer un instantané de /calc/trunk identique à ce qu'il est dans la révision HEAD, faites-en une copie :

$ svn copy http://svn.exemple.com/depot/calc/trunk \
           http://svn.exemple.com/depot/calc/tags/version-1.0 \
      -m "Étiquetage de la version 1.0 du projet 'calc'."

Révision 902 propagée.

Cet exemple présuppose qu'un répertoire /calc/tags existe déjà (s'il n'existe pas, vous pouvez le créer en utilisant svn mkdir). Une fois la copie terminée, le nouveau dossier version-1.0 sera pour toujours un instantané du dossier /trunk tel qu'il était en révision HEAD au moment où vous avez effectué la copie. Bien sûr, vous voudriez peut-être être plus précis quant à quelle révision vous copiez, au cas où quelqu'un d'autre aurait propagé des modifications au projet pendant que vous regardiez ailleurs. Donc si vous savez que la révision 901 de /calc/trunk est exactement l'instantané que vous voulez, vous pouvez le spécifier en passant -r 901 à la commande svn copy.

Mais attendez un moment : cette procédure de création d'étiquette, n'est-ce pas la même procédure que nous avons utilisé pour créer une branche ? En fait, oui. Dans Subversion, il n'y pas de différence entre une étiquette et une branche. Toutes deux ne sont que des répertoires ordinaires qui sont créés par copie. Comme pour les branches, la seule raison qui fasse qu'un répertoire copié soit une « étiquette » est que les humains ont décidé de le traiter de cette façon : aussi longtemps que personne ne propage de modification à ce répertoire, il reste un instantané. Si les gens commencent à y propager des choses, il devient une branche.

Si vous administrez un dépôt, il y a deux approches possibles pour gérer les étiquettes. La première approche est une politique de « non-intervention » : en tant que convention définie pour le projet, vous décidez où vos étiquettes sont placées et vous vous assurez que tous les utilisateurs savent comment traiter les répertoires qu'ils copient (c'est-à-dire que vous vous assurez qu'ils savent qu'ils ne doivent rien y propager). La seconde approche est plus paranoïaque : vous pouvez utiliser un des contrôles d'accès fournis avec Subversion pour empêcher que quiconque ne puisse faire autre chose dans la zone des étiquettes que d'y créer de nouvelles copies (voir le Chapitre 6, Configuration du serveur). L'approche paranoïaque n'est cependant pas nécessaire, en général. Si un utilisateur propage accidentellement une modification à un répertoire d'étiquettes, vous pouvez simplement revenir en arrière sur cette modification comme expliqué dans le paragraphe précédent. C'est ça la gestion de versions, après tout !

Création d'une étiquette complexe

Il vous arrivera sûrement de vouloir que votre « instantané » soit plus compliqué qu'un simple répertoire à une unique révision donnée.

Par exemple, imaginons que votre projet est bien plus vaste que notre exemple calc : supposons qu'il contient un bon nombre de sous-dossiers et bien plus de fichiers encore. Au cours de votre travail, vous pouvez très bien décider que vous avez besoin de créer une copie de travail destinée à des fonctionnalités particulières et à des corrections de bogues. Pour cela vous pouvez antidater de manière sélective des fichiers ou dossiers à des révisions données (en utilisant généreusement svn update avec l'option -r), déporter des fichiers et des dossiers vers des branches particulières (au moyen de svn switch) ou même effectuer manuellement un tas de modifications locales. Quand vous en avez terminé, votre copie de travail est un vrai bazar, fait d'emplacements du dépôt à des révisions différentes. Mais après l'avoir testée, vous êtes alors certain que c'est l'exacte combinaison de données que vous vouliez étiqueter.

C'est alors le moment de prendre un cliché. Copier une URL vers une autre ne fonctionnera pas cette fois. Dans le cas présent, vous voulez prendre un cliché de l'arrangement exact de votre copie de travail et le placer dans le dépôt. Par chance, svn copy possède en fait quatre utilisations différentes (au sujet desquelles vous pouvez obtenir des informations au Partie II, « Guide de référence des commandes Subversion »), dont la possibilité de copier une arborescence de travail vers le dépôt :

$ ls
ma-copie-de-travail/

$ svn copy ma-copie-de-travail \
           http://svn.exemple.com/depot/calc/tags/mon-etiquette \
           -m "Étiquette l'état de ma copie de travail existante."

Révision 940 propagée.

Désormais il y a un nouveau répertoire dans le dépôt, /calc/tags/mon-etiquette, qui est un instantané exact de votre copie de travail : révisions mixtes, URL, modifications locales et tout et tout…

D'autres utilisateurs ont trouvé des usages intéressants pour cette fonctionnalité. Il y a parfois des situations où votre copie de travail contient un paquet de modifications locales que vous aimeriez montrer à un collaborateur. Au lieu de lancer svn diff et d'envoyer un fichier patch (qui ne listera pas les modifications de répertoires, de liens symboliques ou de propriétés), vous pouvez utiliser svn copy pour « envoyer » votre copie de travail vers une zone privée du dépôt. Votre collaborateur peut ensuite soit extraire une copie carbone de votre copie de travail, soit utiliser svn merge pour recevoir exactement vos modifications.

Bien que ce soit une méthode élégante pour mettre à disposition un instantané rapide de votre copie de travail, remarquez que ce n'est pas une bonne manière de créer une branche initialement. La création de branche devrait être un évènement en soi, tandis que cette méthode combine la création d'une branche avec des modifications supplémentaires apportées aux fichiers, le tout au sein d'une seule révision. Ceci rend très difficile (à terme) d'identifier un unique numéro de révision en tant que point de création de la branche.

Maintenance des branches

À ce stade, vous vous êtes certainement rendu compte que Subversion est extrêmement flexible. Parce qu'il implémente les branches et les étiquettes avec le même mécanisme sous-jacent (des copies de répertoires) et parce que les branches et les étiquettes apparaissent au sein de l'espace standard du système de fichiers, beaucoup de gens sont intimidés par Subversion. Il est presque trop flexible. Dans ce paragraphe, nous proposons des suggestions pour organiser et gérer vos données au fil du temps.

Agencement du dépôt

Il existe des méthodes standard recommandées pour structurer un dépôt. La plupart des gens créent un répertoire trunk pour la « ligne de développement principale » (le tronc), un répertoire branches qui contiendra les copies de branches et un répertoire tags qui contiendra les copies étiquetées. Si un dépôt ne comprend qu'un seul projet, les gens créent souvent les dossiers suivants à la racine :



/

   trunk/

   branches/

   tags/

Si un dépôt contient plusieurs projets, les administrateurs indexent généralement la structure du dépôt par projet (voir la section intitulée « Stratégies d'organisation d'un dépôt » pour en savoir plus sur les « dossiers racine d'un projet »), mais en voici directement un exemple :



/

   paint/

      trunk/

      branches/

      tags/

   calc/

      trunk/

      branches/

      tags/

Bien sûr, vous restez libre d'ignorer ces agencements courants. Vous pouvez créer toutes sortes de variantes, selon ce qui fonctionne le mieux pour vous ou pour votre équipe. Souvenez-vous que quel que soit votre choix, ce n'est pas un engagement définitif. Vous pouvez réorganiser votre dépôt à tout moment. Parce que les branches et les étiquettes sont des répertoires ordinaires, la commande svn move peut les déplacer ou les renommer selon vos désirs. Passer d'un agencement à un autre consiste juste à lancer une série d'opérations de déplacement côté serveur ; si vous n'aimez pas la façon dont les choses sont organisées dans le dépôt, modifiez juste leur agencement.

Souvenez-vous néanmoins que bien qu'il soit facile de déplacer des dossiers, vous devez aussi rester attentif à vos utilisateurs. Vos modifications sont susceptibles de déboussoler ceux qui ont des copies de travail existantes. Si un utilisateur a une copie de travail d'un répertoire donné du dépôt, votre opération svn move risque de supprimer ce chemin de la révision la plus récente. Lorsque par la suite l'utilisateur lancera svn update, il se verra annoncer que sa copie de travail pointe vers un chemin qui n'existe plus et sera contraint d'effectuer un svn switch vers le nouvel emplacement.

Durée de vie des données

Une autre fonctionnalité intéressante liée aux principes de fonctionnement de Subversion est que les branches et les étiquettes peuvent avoir des durées de vie limitées, tout comme n'importe quel autre élément suivi en versions. Par exemple, supposons que vous avez enfin terminé votre travail sur votre branche personnelle du projet calc. Après avoir fusionné toutes vos modifications vers /calc/trunk, le répertoire contenant votre branche privée n'a plus de raison d'exister :

$ svn delete http://svn.exemple.com/depot/calc/branches/ma-branche-calc \
             -m "Suppression d'une branche obsolète du projet calc."

Révision 474 propagée.
[Astuce]Astuce

Rappelez-vous que, comme indiqué dans la section précédente, si votre copie de travail pointe vers un chemin du dépôt qui a été supprimé, une erreur apparaitra à la prochaine mise à jour :

$ svn up
Actualise '.' :
svn: E160005: chemin '/calc/branches/my-calc-branch' n'existe pas

Vous n'avez qu'à basculer votre copie de travail vers un chemin qui existe encore :

$ svn sw ^/calc/trunk 
D    src/tout.c
 U   src/reel.c
A    src/entier.c
 U   .
Actualisé à la révision 474.

Et maintenant votre branche a disparu. Bien sûr, elle n'a pas vraiment disparu : le répertoire est juste absent de la révision HEAD, ne gênant plus personne. Si vous utilisez svn checkout, svn switch ou svn list pour examiner une révision plus ancienne, vous pourrez toujours voir votre vieille branche.

Si la navigation dans votre dossier supprimé ne vous suffit pas, vous pouvez toujours le récupérer. Ressusciter des données est très facile dans Subversion. S'il y a un dossier (ou un fichier) supprimé que vous aimeriez faire réapparaître dans HEAD, utilisez simplement svn copy pour le copier depuis l'ancienne révision :

$ svn copy ^/calc/branches/ma-branche-calc@473 \
           ^/calc/branches/ma-branche-calc \
           -m "Restaure ma-branche-calc."

Révision 475 propagée.

Dans notre exemple, votre branche personnelle a eu une durée de vie relativement limitée : vous l'aviez peut-être créée pour corriger un bogue ou implémenter une nouvelle fonctionnalité. Quand votre tâche est finie, il en va de même pour la branche. Cependant, en développement logiciel, il est aussi courant d'avoir deux branches « principales » côte à côte pour de très longues périodes. Par exemple, supposons que le moment est venu de publier une version stable du projet calc pour le public. Vous savez qu'il faudra quelques mois pour éliminer les bogues du logiciel. Vous ne voulez pas que les gens ajoutent de nouvelles fonctionnalités au projet, mais vous ne voulez pas non plus dire à tous les développeurs d'arrêter de programmer. Donc à la place, vous créez une branche « stable » du logiciel qui ne changera pas beaucoup :

$ svn copy ^/calc/trunk ^/calc/branches/stable-1.0 \ 
           -m "Création de la branche stable du projet calc."

Révision 476 propagée.

Dès lors les développeurs sont libres de continuer à ajouter des fonctionnalités de pointe (ou expérimentales) à /calc/trunk et vous pouvez poser comme convention pour le projet que seules les corrections de bogues seront propagées dans /calc/branches/stable-1.0. C'est-à-dire qu'au fur et à mesure que les gens continuent de travailler sur le tronc, quelqu'un reporte de façon sélective les corrections de bogues vers la branche stable. Même après que la branche stable aura été publiée, vous continuerez probablement à maintenir la branche pendant longtemps, c'est-à-dire pour aussi longtemps que vous continuerez à fournir aux clients un support sur cette version. Nous évoquons ceci plus en détails dans le prochain paragraphe.

Modèles courants de gestion des branches

Il existe de nombreux usages pour la création et la fusion des branches ; ce paragraphe décrit les plus courants.

Le plus souvent, la gestion de versions est utilisée pour le développement de logiciels, voici donc un coup d'œil rapide à deux des modèles les plus courants de création et de fusion de branches utilisés par les équipes de programmeurs. Si vous ne vous servez pas de Subversion pour développer des logiciels, n'hésitez pas à sauter ce paragraphe. Si vous êtes un développeur de logiciels qui utilise la gestion de versions pour la première fois, soyez très attentifs, car ces modèles sont souvent considérés comme des bonnes pratiques par les développeurs plus expérimentés. Ces procédures ne sont pas spécifiques à Subversion ; elles sont applicables à tout système de gestion de versions. Néanmoins, les voir explicitées en termes Subversion peut aider.

Branches de publication

En général un logiciel suit un cycle de vie classique, répétant les trois étapes suivantes en boucle : code, test, publication. Il y a deux problèmes avec ce processus. Premièrement, les développeurs doivent continuer à écrire de nouvelles fonctionnalités pendant que les équipes d'assurance qualité prennent le temps de tester des versions supposées stables du logiciel. Les nouveaux développements ne peuvent pas s'arrêter pendant que le logiciel est en cours de test. Deuxièmement, l'équipe doit presque toujours effectuer le support des versions anciennes et publiées du logiciel ; si un bogue est découvert dans le code le plus récent, il existe probablement aussi dans les versions qui ont été publiées et les clients voudront obtenir le correctif pour ce bogue sans avoir à attendre la publication d'une nouvelle version majeure.

C'est là où la gestion de versions peut s'avérer utile. La procédure standard ressemble à ceci :

  1. Les développeurs propagent tout nouveau travail vers le tronc. Les modifications quotidiennes sont propagées vers /trunk : nouvelles fonctionnalités, corrections de bogues, etc.

  2. Le tronc est copié vers une branche « de publication ». Lorsque l'équipe estime que le logiciel est prêt à être publié (disons en version 1.0), /trunk peut être copié vers /branches/1.0.

  3. Les équipes continuent à travailler en parallèle. Une équipe commence à tester rigoureusement la branche de publication, pendant qu'une autre équipe continue avec les nouvelles tâches (disons pour la version 2.0) sur /trunk. Si des bogues sont découverts dans l'un ou l'autre des emplacements, les correctifs sont reportés de l'un à l'autre selon les besoins. Il arrive cependant un moment où même ce processus s'arrête. La branche est « gelée » pour les tous derniers tests juste avant publication.

  4. La branche est étiquetée et publiée. Quand les tests sont terminés, /branches/1.0 est copiée vers /tags/1.0.0 en tant que cliché de référence. L'étiquette est exportée et livrée aux clients.

  5. La branche est gérée au fil du temps. Pendant que le travail continue sur /trunk en vue de la version 2.0, les correctifs de bogues continuent à être reportés de /trunk à /branches/1.0. Lorsque suffisamment de correctifs se sont accumulés, les responsables peuvent décider de publier une version 1.0.1 : /branches/1.0 est copiée vers /tags/1.0.1 et cette étiquette est exportée et publiée.

Ce processus entier se répète au fur et à mesure que le logiciel gagne en maturité : quand le travail pour la version 2.0 est terminé, une nouvelle branche de publication 2.0 est créée, testée, étiquetée et finalement publiée. Au bout de quelques années, le dépôt finit par avoir un certain nombre de branches de publication en mode « maintenance » et un certain nombre d'étiquettes représentant les versions finales publiées.

Branches fonctionnelles

Une branche fonctionnelle est la sorte de branche qui est l'exemple dominant dans ce chapitre (celle sur laquelle vous travailliez pendant que Sally continuait à travailler sur /trunk). C'est une branche temporaire créée pour travailler sur un changement complexe sans interférer avec la stabilité de /trunk. À la différence des branches de publication (dont le support doit parfois être prolongé très longtemps), les branches fonctionnelles naissent, sont utilisées pendant un temps, sont fusionnées vers le tronc et sont finalement supprimées. Elles ont une utilité limitée dans le temps.

Encore une fois, les stratégies varient énormément au sujet du moment approprié pour créer une branche fonctionnelle. Certains projets n'utilisent jamais de branche fonctionnelle : n'importe qui peut propager des modifications à /trunk. L'avantage de ce système est qu'il est simple : personne n'a besoin d'être formé aux branches ou aux fusions. L'inconvénient est que le code du tronc est souvent instable ou inutilisable. D'autres projets utilisent les branches à l'extrême : une modification n'est jamais propagée directement dans le tronc. Même les modifications les plus triviales sont faites au sein d'une branche à courte durée de vie, vérifiées attentivement, puis fusionnées vers le tronc. La branche est ensuite supprimée. Ce système garantit que le tronc restera exceptionnellement stable et utilisable à tout moment, mais aux dépens des coûts de gestion liés à cette procédure très lourde.

En général, les projets choisissent une approche à mi-chemin entre les deux. Ils insistent généralement pour qu'à tout moment /trunk puisse être compilé et passe avec succès les tests de régression. Une branche fonctionnelle n'est nécessaire que quand une modification nécessite un grand nombre de propagations susceptibles de déstabiliser le tronc. Une bonne méthode empirique est de se poser la question suivante : si le développeur travaillait pendant plusieurs jours en isolation et ensuite propageait cette grosse modification en une seule fois (afin que /trunk ne soit jamais déstabilisé), est-ce que ce serait une modification trop grosse à vérifier ? Si la réponse à cette question est « oui », alors la modification devrait être développée sur une branche fonctionnelle. Au fur et à mesure que le développeur propage ses modifications incrémentales dans la branche, elles peuvent facilement être vérifiées par ses pairs.

Finalement, il reste la question de savoir quelle est la meilleure méthode pour garder une branche synchronisée avec le tronc au fur et à mesure que le travail avance. Comme nous l'avons mentionné précédemment, il est très risqué de travailler sur une branche pendant des semaines ou des mois ; le tronc continuera sûrement à recevoir des modifications, au point que les deux lignes de développement risquent de s'éloigner tellement l'une de l'autre qu'essayer de fusionner la branche vers le tronc devienne un cauchemar.

Le mieux pour éviter une telle situation est de fusionner régulièrement les modifications du tronc vers la branche. Faites-en une habitude : une fois par semaine, fusionnez les modifications du tronc de la semaine précédente vers la branche.

Le moment arrivera où vous serez prêt à fusionner la branche fonctionnelle « synchronisée » vers le tronc. Commencez donc par effectuer une dernière fusion des modifications les plus récentes du tronc vers la branche. Une fois que c'est fait, les dernières versions de la branche et du tronc sont absolument identiques, mises à part vos propres modifications sur la branche. Vous êtes alors en mesure de lancer une fusion automatique de réintégration de la branche vers le tronc :

$ cd copie-de-travail-du-tronc

$ svn update
À la révision 1910.

$ svn merge ^/calc/branches/ma-branche
--- Fusion des différences des URLs du dépôt vers '.':
U    reel.c
U    entier.c
A    nouveau-dossier
A    nouveau-dossier/nouveau-fichier
 U   .
…

Une autre façon de concevoir ce modèle est d'imaginer que votre synchronisation hebdomadaire du tronc vers la branche est analogue au lancement de svn update dans une copie de travail, tandis que l'étape finale de fusion est analogue au lancement de svn commit depuis une copie de travail. Après tout, une copie de travail n'est rien d'autre qu'une branche privée très superficielle : c'est une branche qui n'est capable de ne contenir qu'une seule modification à la fois.

Branches fournisseurs

Comme c'est particulièrement le cas en développement logiciel, les données que vous gérez dans votre système de gestion de versions ont souvent un lien étroit avec les données de quelqu'un d'autre, ou en sont peut-être dépendantes. Généralement, les besoins de votre projet vous obligent à rester aussi à jour que possible avec les données fournies par cette entité externe, sans sacrifier la stabilité de votre propre projet. Ce scénario arrive très souvent, partout où les informations générées par un groupe de personnes ont un effet direct sur celles qui sont générées par un autre groupe de personnes.

Par exemple, il arrive que des développeurs de logiciel travaillent sur une application qui utilise une bibliothèque tierce. Subversion a justement une relation de ce type avec la bibliothèque Apache Portable Runtime (APR) (voir la section intitulée « APR, la bibliothèque Apache de portabilité des exécutables »). Le code source de Subversion dépend de la bibliothèque APR pour tous ses besoins de portabilité. Durant les étapes initiales de développement de Subversion, le projet suivait les changements de l'interface de programmation d'APR de près, restant toujours « à la pointe » des évolutions du code de la bibliothèque. Maintenant que APR et Subversion ont tous deux gagné en maturité, Subversion n'essaie de se synchroniser avec l'interface de programmation de l'APR qu'à des étapes de publication stables et bien testées.

Donc, si votre projet dépend des informations de quelqu'un d'autre, vous pourriez tenter de synchroniser ces informations avec les vôtres de plusieurs manières. La plus pénible serait de donner des instructions orales ou écrites à tous les contributeurs de votre projet, leur demandant de s'assurer qu'ils disposent des bonnes versions de ces informations tierces dont votre projet a besoin. Si les informations tierces sont gérées dans un dépôt Subversion, vous pourriez aussi utiliser les définitions externes de Subversion pour en fait « agrafer » des versions spécifiques de ces informations à un endroit quelconque dans le dossier de votre copie de travail (voir la section intitulée « Définition de références externes »).

Mais parfois vous voulez gérer des modifications personnalisées de ce code tierce à l'intérieur de votre propre système de gestion de versions. En reprenant l'exemple du développement logiciel, les programmeurs peuvent vouloir apporter des modifications à cette bibliothèque tierce pour leurs propres besoins. Ces modifications incluent peut-être de nouvelles fonctionnalités ou des corrections de bogues, gérées en interne seulement jusqu'à ce qu'elles soient incluses dans une version officielle de la bibliothèque tierce. Ou alors ces changements ne seront peut-être jamais remontés vers ceux qui gèrent cette bibliothèque, existant seulement en tant qu'optimisations « maison » permettant de mieux adapter la bibliothèque aux besoin des développeurs du logiciel.

À présent vous êtes face à une situation intéressante. Votre projet pourrait héberger ses modifications maison des données tierces de manière désordonnée, par exemple en utilisant des correctifs de type patch ou des versions alternatives complètes des fichiers et dossiers. Mais ces méthodes deviennent rapidement de vrais casse-tête à gérer, nécessitant des mécanismes pour reporter vos modifications maison au code tierce et nécessitant le report de ces modifications à chaque version successive du code tierce dont vous dépendez.

La solution de ce problème est d'utiliser des branches fournisseurs. Une branche fournisseur est une arborescence au sein de votre propre système de gestion de versions qui contient des informations fournies par une entité tierce, ou fournisseur. Chaque version des données du fournisseur que vous décidez d'incorporer dans votre projet est appelée une livraison fournisseur.

Les branches fournisseur présentent deux avantages. Premièrement, en incluant la livraison fournisseur actuellement supportée dans votre propre système de gestion de versions, vous avez la garantie que les membres de votre projet n'auront jamais besoin de se demander s'ils ont la bonne version des données du fournisseur. Ils reçoivent simplement la bonne version pendant les mises à jour usuelles de leur copie de travail. Deuxièmement, parce que ces données font partie de votre propre dépôt Subversion, vous pouvez y conserver vos modifications maison : vous n'avez plus besoin d'une méthode automatisée (ou pire, manuelle) pour reporter vos propres changements.

Malheureusement, il n'existe pas de « meilleure » façon pour gérer les branches fournisseurs dans Subversion. La flexibilité offerte par le système permet plusieurs approches différentes, chacune ayant ses avantages et ses inconvénients, et aucune ne peut être considérée comme « la méthode qui tue » pour ce problème. Nous allons décrire quelques unes des approches sans rentrer dans les détails dans les paragraphes qui suivent, en prenant comme exemple un projet logiciel qui dépend d'une bibliothèque tierce.

Procédure générale de gestion des branches fournisseurs

Gérer des modifications personnalisées d'une bibliothèque tierce met en jeu trois sources de données : la version de la bibliothèque tierce sur laquelle vos modifications ont porté la dernière fois, la version personnalisée (c'est-à-dire la branche fournisseur actuelle) de cette bibliothèque qui est utilisée par votre projet et toute nouvelle version de la bibliothèque externe dont vous espérez sûrement effectuer la mise à niveau. Gérer la branche fournisseur (qui doit résider dans le dépôt de votre code source, par définition) consiste alors essentiellement à effectuer des fusions (au sens général du terme). Mais chaque équipe peut choisir sa méthode pour ce qui concerne les autres sources de données : les versions originales du code source de la bibliothèque tierce. Donc, nous avons plusieurs façons pour effectuer les fusions requises.

Stricto sensu, il existe deux façons de faire ces fusions. Afin de simplifier et de rester concret dans ce paragraphe, nous supposons qu'il n'y a qu'une seule branche fournisseur qui est mise à niveau lors de chaque mise à jour des bibliothèques tierces (qui décrivent les différences entre les versions courantes et les nouvelles versions des sources de la bibliothèque).

[Note]Note

Une autre approche est de créer une nouvelle branche fournisseur pour chaque version de la bibliothèque, en appliquant les différences entre la bibliothèque originale courante et la version personnalisée vers la nouvelle branche. Cette approche n'est pas mauvaise, nous ne nous sentons juste pas obligés de documenter ici toutes les façons de faire.

Les paragraphes suivants décrivent comment créer et gérer une branche fournisseur suivant quelques scénarios différents. Dans les exemples qui suivent, nous supposons que la bibliothèque tierce s'appelle libcomplex et que nous allons implémenter une branche fournisseur basée sur libcomplex 1.0.0 qui est stockée dans notre dépôt à l'emplacement ^/vendor/libcomplex-perso. Nous allons voir comment nous pouvons mettre à niveau vers libcomplex 1.0.1 tout en préservant nos personnalisations de cette bibliothèque.

Branches fournisseurs depuis des dépôts externes

Dans un premier temps, regardons comment gérer une branche fournisseur lorsque la bibliothèque originale est accessible par Subversion. Pour les besoins de cet exemple, nous allons considérer que la bibliothèque libcomplex dont nous avons parlé est développée dans un dépôt Subversion librement accessible et que les développeurs utilisent une procédure de publication de bon aloi qui comporte la création d'une étiquette pour chaque version stable publiée.

Depuis Subversion 1.5, la sous-commande svn merge est capable d'effectuer ce que l'on appelle des fusions avec un dépôt externe, où les sources de la fusion sont stockées dans un dépôt différent du dépôt dont la copie de travail, cible de la fusion, a été extraite. Et dans Subversion 1.8, le comportement de svn copy a été modifié de manière à ce que l'arborescence résultante d'une copie depuis un dépôt externe vers une copie de travail existante soit incorporée dans cette copie de travail et placée pour ajout lors de la prochaine propagation. C'est cette fonctionnalité de copie à partir d'un dépôt externe que nous allons utiliser pour initier notre branche fournisseur.

Créons donc notre branche fournisseur. Nous commençons par créer un dossier d'accueil pour toutes les branches fournisseurs dans notre dépôt puis nous extrayons une copie de travail à cet emplacement.

$ svn mkdir http://svn.exemple.com/projets/vendor \
            -m "Création d'un conteneur pour les branches fournisseurs."
Révision 1160 propagée.
$ svn checkout http://svn.exemple.com/projets/vendor \
               /chemin/vers/fournisseur
Révision 1160 extraite.
$

Maintenant nous allons profiter de la capacité de Subversion à copier un dépôt externe pour obtenir une copie exacte de libcomplex 1.0.0 (y compris les propriétés Subversion stockées dans ces fichiers et dossiers) à partir du dépôt du fournisseur.

$ cd /chemin/vers/fournisseur
$ svn copy http://svn.monfournisseur.fr/depot/libcomplex/tags/1.0.0 \
           libcomplex-perso
--- Copying from foreign repository URL 'http://svn.monfournisseur.fr/depot/libcomplex/tags/1.0.0' :
A    libcomplex-perso
A    libcomplex-perso/README
A    libcomplex-perso/LICENSE
…
A    libcomplex-perso/src/code.c
A    libcomplex-perso/tests
A    libcomplex-perso/tests/TODO
$ svn commit -m "Initialisation de la branche fournisseur libcomplex avec libcomplex 1.0.0."
Ajout         libcomplex-custom
Ajout         libcomplex-custom/README
Ajout         libcomplex-custom/LICENSE
…
Ajout         libcomplex-custom/src
Ajout         libcomplex-custom/src/code.h
Ajout         libcomplex-custom/src/code.c
Transmission des données .......................................
Révision 1161 propagée.
$
[Note]Note

Si vous êtes amené à utiliser une vieille version de Subversion, la meilleure façon d'approcher cette nouvelle fonctionnalité de svn copy est d'importer une copie de travail (avec svn import) de la version étiquetée du fournisseur, en spécifiant bien les options --no-auto-props et --no-ignore pour que l'arborescence complète et les propriétés suivies en versions soient correctement répliquées dans votre propre dépôt.

Maintenant que nous avons la branche fournisseur basée sur libcomplex 1.0.0, nous pouvons commencer à personnaliser libcomplex pour satisfaire nos besoins, en propageant les modifications directement vers la branche fournisseur que nous avons créée. Et bien sûr, nous pouvons commencer à utiliser libcomplex dans notre propre application.

Quelques temps plus tard, les développeurs de libcomplex publient une nouvelle version de leur bibliothèque, la version 1.0.1. Après avoir passé en revue les modifications, nous décidons de mettre à niveau notre branche fournisseur vers cette nouvelle version. Et c'est ici que l'opération de fusion à partir d'un dépôt externe de Subversion est utile. Nous avons dans notre branche fournisseur la libcomplex 1.0.0 originale plus nos personnalisations. Nous avons besoin maintenant d'y insérer l'ensemble des modifications que le fournisseur a effectué entre 1.0.0 et 1.0.1, idéalement sans fracasser nos personnalisations. C'est précisément ce que la forme de svn merge à 2-URL sait faire.

$ cd /chemin/vers/fournisseur
$ svn merge http://svn.autrefournisseur.com/depot/libcomplex/tags/1.0.0 \
            http://svn.autrefournisseur.com/depot/libcomplex/tags/1.0.1 \
            libcomplex-perso
-- Fusion des différences des URLs du dépôt externe dans '.':
U    libcomplex-perso/src/code.h
C    libcomplex-perso/src/code.c
U    libcomplex-perso/README
Résumé des conflits :
  Text conflicts: 1
Conflit découvert dans le fichier 'libcomplex-custom/src/code.c'.
Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
        (mc) mine-conflict, (tc) theirs-conflict, (s) show all options:

Comme vous pouvez le constater, svn merge a fusionné les modifications pour obtenir libcomplex 1.0.1 à partir de libcomplex 1.0.0 dans votre copie de travail. Dans cet exemple, il a même découvert et marqué un fichier comme étant en conflit. Il semble que le fournisseur a modifié une zone d'un des fichiers que nous avons personnalisé. Subversion détecte ce conflit et nous donne la possibilité de le résoudre de manière sécurisée, c'est-à-dire que nos modifications à ce qui est maintenant libcomplex 1.0.1 puissent continuer à faire sens. Pour plus d'informations sur la résolution des conflits de ce type, reportez-vous à la section intitulée « Résolution des conflits ».

Une fois que nous avons résolu les conflits et effectué nos tests ou passé en revue ce que est nécessaire, nous pouvons propager les modifications vers notre branche fournisseur.

 
$ svn status libcomplex-perso
M       libcomplex-perso/src/code.h
M       libcomplex-perso/src/code.c
M       libcomplex-perso/README
$ svn commit -m "Mise à niveau de la branche fournisseur vers libcomplex 1.0.1." \
             libcomplex-perso
Sending        libcomplex-perso/README
Sending        libcomplex-perso/src/code.h
Sending        libcomplex-perso/src/code.c
Transmission des données ...
Révision 1282 propagée.
$

Voilà, à grosses mailles, comment gérer des branches fournisseurs quand les sources originales sont accessible par Subversion. Il faut noter quand même quelques manques. D'abord, les fusions de dépôts externes ne sont pas automatiquement tracées par Subversion lui-même comme le sont les fusions internes au dépôt. Cela veut dire que c'est à l'utilisateur de savoir quels changements ont été appliqués sur la branche fournisseur et de construire lui-même la prochaine fusion pour mettre à niveau la branche. De plus, comme c'est le cas pour toutes les fusions faites par Subversion, les renommages à l'intérieur des sources de la fusion peuvent entrainer certaines complications et frustrations. Malheureusement, à l'heure actuelle, nous n'avons pas de recommandation particulière réellement valable pour vous soulager dans ce cas.

Branches fournisseurs à partir de sources mirroirs

Dans le paragraphe précédent (la section intitulée « Branches fournisseurs depuis des dépôts externes ») nous avons vu comment implémenter et maintenir une branche fournisseur quand celui-ci fournit un accès via Subversion, ce qui est le scénario idéal pour les branches fournisseurs. Subversion se distingue particulièrement lorsqu'il s'agit de fusionner des contenus gérés par Subversion. Malheureusement, ce n'est pas toujours le cas. Souvent, un projet dépend d'une bibliothèque qui n'est accessible que via des mécanismes non-Subversion, tels que des archives compressées de code source. Dans ces circonstances, nous recommandons fortement de faire tout ce que vous pouvez pour insérer ces données non-Subversion dans Subversion de la manière la plus propre possible. Examinons donc une approche de branche fournisseur dans laquelle les différentes versions de la bibliothèque tierce sont répliquées dans votre propre dépôt.

Configurer la branche fournisseur pour la première fois est très simple, vraiment. Dans notre exemple, nous considérons que libcomplex 1.0.0 est fournie dans une archive compressée classique. Pour créer notre branche fournisseur, nous allons dans un premier temps copier le contenu de l'archive dans notre dépôt comme une sorte d'arborescence étiquetée fournisseur en lecture seule (par convention uniquement).

$ tar xvfz libcomplex-1.0.0.tar.gz
libcomplex-1.0.0/
libcomplex-1.0.0/README
libcomplex-1.0.0/LICENSE
…
libcomplex-1.0.0/src/code.c
libcomplex-1.0.0/tests
libcomplex-1.0.0/tests/TODO
$ svn import libcomplex-1.0.0 \
             http://svn.exemple.com/projets/vendor/libcomplex-1.0.0 \
             --no-ignore --no-auto-props \
             -m "import des sources de libcomplex 1.0.0."
Ajout         libcomplex-custom
Ajout         libcomplex-custom/README
Ajout         libcomplex-custom/LICENSE
…
Ajout         libcomplex-custom/src
Ajout         libcomplex-custom/src/code.h
Ajout         libcomplex-custom/src/code.c
Transmission des données .......................................
Révision 1160 propagée.
$

Notez que dans l'exemple, nous avons utilisé l'option --no-ignore pour l'import de manière à ce que Subversion récupère bien tous les fichiers de la livraison. Nous avons également utilisé l'option --no-auto-props afin que notre client n'affecte pas de lui-même des propriétés qui ne seraient pas présentes dans l'archive officielle[44].

Maintenant que la première archive du fournisseur est présente dans notre dépôt, nous pouvons nous en servir comme point de départ pour créer notre branche fournisseur en utilisant svn copy comme pour toute autre branche.

 
$ svn copy http://svn.exemple.com/projets/vendor/libcomplex-1.0.0 \
           http://svn.exemple.com/projets/vendor/libcomplex-perso \
           -m "Initialisation de la branche fournisseur libcomplex à partir de libcomplex 1.0.0."
Révision 1161 propagée.
$

Parfait. Ici, nous avons une branche fournisseur basée sur libcomplex 1.0.0. Nous sommes maintenant aptes à personnaliser libcomplex pour nos besoins propres, en propageant directement les modifications vers la branche fournisseur que nous venons de créer. Puis, nous pouvons utilisé notre libcomplex personnalisée dans nos applications.

Quelques temps plus tard, libcomplex 1.0.1 est publiée. Après avoir passé en revue les modifications, nous décidons de mettre à niveau notre branche fournisseur vers cette nouvelle version. Afin d'effectuer cette mise à niveau, nous devons essentiellement appliquer le même ensemble de modifications à notre branche fournisseur que le fournisseur a fait entre la version 1.0.0 et la version 1.0.1 de sa bibliothèque, ceci sans écraser nos propres personnalisations. La façon la plus sûre de le faire est d'insérer libcomplex 1.0.1 dans notre dépôt, comme un delta vis-à-vis du code de libcomplex 1.0.0 de notre dépôt. Ensuite, nous utilisons la forme 2-URL de la sous-commande svn merge pour répliquer ces mêmes modifications vers notre branche fournisseur.

Il existe plusieurs manières d'insérer correctement libcomplex 1.0.1 dans notre dépôt[45]. L'approche que nous décrivons ici est relativement rudimentaire, mais elle convient pour illustrer notre exemple.

Rappelez-vous que nous voulons que notre replique de la livraison libcomplex-1.0.1 partage des ancêtres avec notre livraison 1.0.0, afin de produire plus tard les meilleurs résultats quand nous devrons fusionner les modifications entre les archives vers la branche fournisseur. Nous allons commencer par créer une branche libcomplex-1.0.1 comme copie de notre branche « étiquetée fournisseur » libcomplex-1.0.0. Cette copie sera destinée à devenir une réplique de libcomplex 1.0.1.

$ svn copy http://svn.exemple.com/projets/vendor/libcomplex-1.0.0 \
           http://svn.exemple.com/projets/vendor/libcomplex-1.0.1 \
           -m "Construction de la zone pour accueillir libcomplex 1.0.1."
Révision 1282 propagée.
$

Nous avons besoin maintenant d'avoir une copie de travail de notre branche libcomplex-1.0.1 et de la faire ressembler à libcomplex 1.0.1. Pour ce faire, nous allons profiter du fait que svn checkout peut agir en superposition dans un répertoire existant et, si l'option --force est fournie, alors les différences entre l'arborescence extraite et l'arborescence cible sur laquelle est appliquée l'extraction sont marquées comme modifications locales de la nouvelle copie de travail.

$ tar xvfz libcomplex-1.0.1.tar.gz
libcomplex-1.0.1/
libcomplex-1.0.1/README
libcomplex-1.0.1/LICENSE
…
libcomplex-1.0.1/src/code.c
libcomplex-1.0.1/tests
libcomplex-1.0.1/tests/TODO
$ svn checkout http://svn.exemple.com/projets/vendor/libcomplex-1.0.1 \
               libcomplex-1.0.1 \
               --force
E    libcomplex-1.0.1/README
E    libcomplex-1.0.1/LICENSE
E    libcomplex-1.0.1/INSTALL
…
E    libcomplex-1.0.1/src/code.c
E    libcomplex-1.0.1/tests
E    libcomplex-1.0.1/tests/TODO
Révision 1282 extraite.
$ svn status libcomplex-1.0.1
M       libcomplex-1.0.1/src/code.h
M       libcomplex-1.0.1/src/code.c
M       libcomplex-1.0.1/README
$

Comme vous pouvez le constater, après avoir extrait ce qu'était réellement libcomplex 1.0.0 « par dessus » l'archive décompressée de libcomplex 1.0.1, nous obtenons une copie de travail avec des modifications locales (les modifications qui correspondent aux différences entre la précédente version du fournisseur que nous avions intégrée et la nouvelle).

Certes, c'est un exemple particulièrement simple. Les modifications pour effectuer la mise à niveau ne font intervenir que quelques modifications dans des fichiers existants. Dans la réalité, les nouvelles versions des bibliothèques tierces ajoutent ou déplacent sûrement des fichiers ou des dossiers, renomment des fichiers ou des dossiers, etc. Dans ces cas, il sera plus compliqué de calquer la nouvelle version étiquetée avec la livraison qu'elle est censée représenter. Nous laissons en exercice au lecteur la résolution des détails de cette transformation[46].

Quelle que soit la manière dont nous y sommes arrivés, nous disposons maintenant d'une copie de travail de la nouvelle version étiquetée qui reflète exactement la livraison originale du fournisseur. Nous pouvons maintenant propager ces modifications à notre dépôt.

$ svn commit -m "Mise à niveau de la branche fournisseur vers libcomplex 1.0.1." \
             libcomplex-1.0.1
Envoi      libcomplex-1.0.1/README
Envoi      libcomplex-1.0.1/src/code.h
Envoi      libcomplex-1.0.1/src/code.c
Transmission des données ...
Révision 1283 propagée.
$

Nous sommes finalement prêt à mettre à niveau notre branche fournisseur, notre but étant d'obtenir les changements faits par le fournisseur entre les versions 1.0.0 et 1.0.1 de sa bibliothèque dans notre branche fournisseur. C'est là que la forme à 2-URL de svn merge, appliquée à une copie de travail de notre branche fournisseur, entre en scène.

$ svn checkout http://svn.exemple.com/projets/vendor/libcomplex-perso \
               libcomplex-perso
E    libcomplex-perso/README
E    libcomplex-perso/LICENSE
E    libcomplex-perso/INSTALL
…
E    libcomplex-perso/src/code.c
E    libcomplex-perso/tests
E    libcomplex-perso/tests/TODO
Révision 1283 extraite.
$ cd libcomplex-perso
$ svn merge ^/vendor/libcomplex-1.0.0 \
            ^/vendor/libcomplex-1.0.1
--- Fusion des différences entre les URL du dépôt dans '.' :
U    src/code.h
C    src/code.c
U    README
Résumé des conflits :
  Text conflicts: 1
Conflit découvert dans le fichier 'src/code.c'.
Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
        (mc) mine-conflict, (tc) theirs-conflict, (s) show all options:

Comme vous pouvez le constater, svn merge a fusionné les modifications demandées dans notre copie de travail, marquant un conflit où le fournisseur a modifié la même zone d'un fichier que nous lors de notre personnalisation. Subversion détecte le conflit et nous donne l'opportunité de le résoudre (en utilisant les méthodes décrites dans la section intitulée « Résolution des conflits ») de façon à ce que nos personnalisations relatives à ce qui est maintenant libcomplex 1.0.1 fassent toujours sens. Une fois que nous avons résolu les conflits et effectué les tests et revues nécessaires, nous pouvons propager les modifications à notre branche fournisseur.

$ svn status
M       src/code.h
M       src/code.c
M       README
$ svn commit -m "Mise à niveau de la branche fournisseur vers libcomplex 1.0.1."
Envoi     README
Envoi     src/code.h
Envoi     src/code.c
Transmission des données ...
Révision 1294 propagée.
$

La mise à niveau de notre branche fournisseur est terminée. Et la prochaine fois que nous aurons besoin de mettre à niveau cette branche, nous suivrons la même procédure.

Créer une branche ou ne pas créer une branche ?

Créer une branche ou ne pas créer une branche ? Voilà une question intéressante. Ce chapitre vous a montré jusqu'à maintenant force détails quant aux créations de branches et aux fusions, des sujets qui ont historiquement été la plus grande source de confusion pour les utilisateurs. Comme si l'enchainement mécanique des actions qui sont mises en œuvre dans la création et la fusion de branches n'était pas assez compliqué, quelques utilisateurs restent à se demander si cela vaut le coup de créer une branche ou pas. Comme vous l'avez appris, Subversion gère les scénarios classiques de création et gestion de branches. Ainsi, la décision de créer ou pas une branche d'un projet ne relève pas de critères techniques. C'est davantage les impacts sociaux qui pèsent le plus dans la décision. Examinons quelques avantages et inconvénients d'utiliser des branches dans un projet logiciel.

Le bénéfice le plus évident de travailler sur une branche est l'isolation. Les modifications faites à la branche n'affectent pas les autres lignes de développement du projet ; les modifications des autres lignes n'affectent pas la branche. Dans un certain sens, une branche peut servir de terrain d'expérimentation de nouvelles fonctionnalités, de correction pour des bogues complexes, des réécritures majeures du code, etc. Peu importe la quantité de choses cassées dans la branche de Sally, Harry et le reste de l'équipe peuvent continuer leur travail sans entrave, en dehors de la branche.

Les branches fournissent aussi un mécanisme très élégant pour organiser les modifications qui sont reliées entre elles dans des ensembles immédiatement reconnaissables. Par exemple, les modifications qui résolvent complètement un bogue particulier peuvent se trouver dans une liste de révisions dont les numéros ne se suivent pas. Vous pouvez les énoncer comme « les révisions 1534, 1543, 1587 und 1588 ». Vous les indiquerez sûrement à la main (ou autrement) dans le système de suivi de bogues qui prend en compte ce bogue. Lorsque vous porterez cette correction de bogue vers d'autres versions du produit, vous devrez vous assurer que vous n'oubliez aucune révision. Mais si ces modifications ont toutes été faites dans la même branche, vous pourrez vous référer uniquement à cette branche, par son nom, dans les conversations, dans les commentaires du système de suivi de bogues et lorsque vous portez les modifications ailleurs.

L'inconvénient des branches, cependant, c'est que cette isolation forte qui les rend si utiles peut parfois aller à l'encontre du besoin de collaboration de l'équipe de projet. En fonction des habitudes de travail de vos collègues, les modifications faites à votre branches ne recevront peut-être pas toutes les revues, critiques et tests que subissent les changements de la ligne principale de développement. L'isolation de la branche peut encourager les utilisateurs à oublier certaines bonnes pratiques de la gestion de versions, entrainant un historique des versions difficile à exploiter a posteriori. Les développeurs de branches à longue durée de vie doivent parfois travailler beaucoup plus dur pour s'assurer que la direction vers laquelle évolue leur branche isolée est bien en harmonie avec la direction prise par leurs collègues dans la ligne de développement principale. Maintenant, ces inconvénients peuvent s'avérer sans objet si le développement de la branche consiste justement à explorer une nouvelle souche du logiciel dont le résultat n'a pas vocation à réintégrer la ligne de développement principale (l'application stricte des politiques de développement ne doit pas freiner l'innovation !). En tout état de cause, les projets retirent en général un bon bénéfice d'une approche méthodique dans la gestion de versions où le code et les modifications font l'objet d'un passage en revue et de compréhension par plus d'un membre de l'équipe.

En fin de compte, nous ne proclamons pas qu'il n'y a aucun inconvénient technique à créer des branches. Pardonnez-nous de « diverger » quelque peu dans ce paragraphe. Si vous réfléchissez bien, à chaque fois que vous extrayez une copie de travail Subversion, vous créez en quelque sorte une branche de votre projet. C'est une branche d'une sorte un peu spéciale, qui ne réside que sur la machine cliente, pas dans le dépôt. Vous synchronisez cette branche avec les modifications faites dans le dépôt par la commande svn update (qui agit d'une certaine manière comme une version simplifiée de la commande svn merge [47]. Vous réintégrer effectivement la branche à chaque fois que vous lancez svn commit. Ainsi, dans un certain sens, les utilisateurs de Subversion créent des branches et les fusionnent sans arrêt. Compte tenu des similitudes entre la mise à jour et la fusion, il n'est pas étonnant que les points durs de Subversion (la gestion des renommages des fichiers et de dossiers ainsi que les conflits d'arborescences) soient aussi problématiques pour les opérations svn update et svn merge. Malheureusement, svn merge souffre encore plus, précisément parce que, alors que svn update n'est qu'un cas particulier, simplifié, d'une opération de fusion, une véritable opération de fusion Subversion n'est pas un cas particulier ou simplifié. C'est pourquoi les opérations de fusion sont beaucoup plus lentes de les mises à jour, demandent d'avoir un traçage de l'historique (via la propriété svn:mergeinfo que nous avons abordée dans ce chapitre) et des calculs sur cet historique, introduisant généralement beaucoup d'occasions pour que quelque chose se passe de travers.

Créer une branche ou ne pas créer une branche ? Finalement, cela dépend des besoins de votre équipe pour trouver le juste équilibre entre le travail collaboratif et l'isolation.

Résumé

Nous avons traité de nombreux sujets dans ce chapitre. Nous avons présenté les concepts d'étiquettes et de branches et montré comment Subversion implémente ces concepts en copiant des répertoires avec la commande svn copy. Nous avons expliqué comment utiliser svn merge pour copier des modifications d'une branche à l'autre ou pour revenir en arrière sur des modifications non-satisfaisantes. Nous avons étudié l'utilisation de svn switch pour créer des copies de travail mixtes, pointant vers des emplacements variés d'un dépôt. Et nous avons évoqué la façon dont on peut gérer l'organisation et le cycle de vie des branches dans un dépôt.

Tâchez de garder en mémoire la devise de Subversion : les branches et les étiquettes ne coûtent quasiment rien. Donc n'ayez pas peur de les utiliser quand vous en avez besoin !

En guise de pense-bête face à toutes les opérations dont nous avons parlé, voici un tableau de référence très pratique, à consulter lorsque vous commencerez à utiliser des branches.

Tableau 4.1. Commandes de gestion des branches et des fusions

ActionCommande
Créer une branche ou une étiquettesvn copy URL1 URL2
Faire pointer une copie de travail vers une branche ou une étiquettesvn switch URL
Synchroniser une branche avec le troncsvn merge trunkURL; svn commit
Voir l'historique des fusions ou les ensembles de modifications susceptibles d'être fusionnéssvn mergeinfo SOURCE CIBLE
Réintégrer une branche dans le troncsvn merge branchURL; svn commit
Fusionner une modification précisesvn merge -c REV URL; svn commit
Fusionner un intervalle de modificationssvn merge -r REV1:REV2 URL; svn commit
Empêcher qu'une modification ne soit fusionnée automatiquementsvn merge -c REV --record-only URL; svn commit
Prévisualiser une fusionsvn merge URL --dry-run
Abandonner une fusionsvn revert -R .
Ressusciter un élément de l'historiquesvn copy URL@REV localPATH
Revenir en arrière sur une modification déjà propagéesvn merge -c -REV URL; svn commit
Examiner l'historique en tenant compte des informations de fusionsvn log -g; svn blame -g
Créer une version étiquetée à partir d'une copie de travailsvn copy . tagURL
Réorganiser une branche ou une version étiquetéesvn move URL1 URL2
Supprimer une branche ou une version étiquetéesvn delete URL





[32] Subversion n'accepte pas les copies entre des dépôts distincts. Quand vous utilisez des URLs avec svn copy et svn move, vous ne pouvez copier que des éléments faisant partie du même dépôt.

[33] Cette notation a été introduite par Subversion 1.6

[34] L'option --allow-mixed-revisions de la sous-commande svn merge vous permet de lever cette interdiction, mais vous ne devriez le faire que si vous comprenez les implications et que vous avez une bonne raison de le faire.

[35] Depuis Subversion 1.7 vous n'avez pas absolument besoin de resynchroniser complètement votre branche avec le tronc comme nous le faisons dans cet exemple. Si votre branche est effectivement synchronisée par une série de fusions d'arborescences alors la réintégration fonctionnera, mais demandez-vous, si la branche est effectivement synchronisée, pourquoi effectuez-vous des fusions d'arborescences ? Le faire est pratiquement toujours inutilement complexe.

[36] les fusions de réintégration automatiques sont autorisées si la cible est une extraction partielle (voir la section intitulée « Répertoires clairsemés »), mais alors chaque chemin concerné par le calcul de différence et qui est « absent » en raison de l'extraction partielle sera ignoré, ce qui n'est probablement pas ce que vous recherchez !

[37] Seul Subversion 1.8 autorise cette réutilisation d'une branche. Les précédentes versions demandaient quelques manipulations préalables afin de pouvoir réintegrer à nouveau une branche. Consultez les versions antérieures de ce chapitre pour plus d'informations : https://svnbook.red-bean.com/fr/1.5/svn.branchmerge.basicmerging.html#svn.branchemerge.basicmerging.reintegrate

[38] Par « direction », nous entendons les fusions soit du tronc vers la branche (synchronisation automatique), soit de la branche vers le tronc (réintégration automatique).

[39] C'est un bon exemple de révisions non-effectives pour la fusion.

[40] Le projet Subversion prévoit néanmoins d'implémenter, un jour, une commande qui accomplirait la tâche de supprimer des informations de façon permanente. En attendant, en guise de palliatif, voir la section intitulée « svndumpfilter ».

[41] À noter qu'après être revenu en arrière sur une révision de cette manière, nous ne serions plus capables de ré-appliquer cette révision avec svn merge . -c 5, puisque les informations de fusion marqueraient déjà r5 comme ayant été appliquée. Nous serions alors obligés d'utiliser l'option --ignore-ancestry pour forcer la commande de fusion à ignorer le contenu de mergeinfo !

[42] Si Sally n'avait pas propagé ses modifications dans r472, alors Subversion aurait remarqué que entier.c dans la copie de travail cible était identique à entier.c du côté gauche de la fusion et aurait permis le succès de votre renommage sans conflit d'arborescence :

$ svn merge ^/calc/branches/ma-branche-calc
--- Fusion des différences entre les URLs du dépôt dans '.' :
 U   src/reel.c
A    src/tout.c
D    src/entier.c
--- Stockage des informations de fusion (mergeinfo) des URLs du dépôt dans '.' :
 U   .

[43] Vous pouvez cependant utiliser svn relocate si l'URL de votre serveur change et si vous ne voulez pas abandonner votre copie de travail existante. Reportez-vous à svn relocate dans Guide de référence de svn : le client texte interactif pour des détails et des exemples.

[44] Techniquement, nous pourrions laisser la fonctionnalité auto-props faire son travail mais l'essentiel,c'est que chaque archive du fournisseur subisse exactement le même traitement pour les propriétés automatiques.

[45] Utiliser une autre svn import ne serait pas correct, puisque libcomplex 1.0.1 et 1.0.0 se retrouveraient sans aucun ancêtre commun.

[46] Un début de solution peut être :svn add --force /chemin/vers/copie-de-travail --no-ignore --no-auto-props est super pratique pour ajouter tout nouvel élément de la version fournisseur en suivi de versions dans cette situation.

[47] En fait, vous pourriez utiliser svn merge -rDERNIERE_REV_EXTRAITE:HEAD . dans votre copie de travail pour fusionner toutes les modifications en provenance du dépôt depuis votre dernière extraction si vous le vouliez !

Chapitre 5. Administration d'un dépôt

Le dépôt Subversion est le centre de stockage de toutes vos données suivies en versions. Ainsi, il est de facto l'objet de toute l'attention et de tous les soins de l'administrateur. Bien que ce soit un élément ne nécessitant pas énormément de maintenance, il est important de comprendre comment le configurer et le surveiller de manière à éviter d'éventuels problèmes et à résoudre proprement ceux qui se présentent.

Dans ce chapitre, nous expliquons comment créer et configurer un dépôt Subversion. Nous abordons également la maintenance du dépôt, en donnant des exemples d'utilisation des divers outils fournis avec Subversion. Nous étudions quelques questions et erreurs communes et nous donnons des conseils sur l'organisation des données dans le dépôt.

Si vous n'envisagez pas d'utiliser un dépôt Subversion autrement qu'en simple utilisateur des données (c'est-à-dire en utilisant un client Subversion), vous pouvez sauter ce chapitre. Cependant, si vous êtes (ou si vous êtes appelé à être) l'administrateur d'un dépôt[48], ce chapitre est fait pour vous.

Définition d'un dépôt Subversion

Avant d'aborder le vaste sujet de l'administration d'un dépôt, définissons plus précisément ce qu'est un dépôt. À quoi ressemble-t-il ? Que ressent-il ? Est-ce qu'il préfère son thé chaud ou glacé, sucré, avec une tranche de citron ? En tant qu'administrateur, vous vous devez de comprendre de quoi est composé un dépôt, à la fois au niveau du système d'exploitation (à quoi ressemble le dépôt et comment il réagit vis-à-vis des outils autres que Subversion) et au niveau logique de l'organisation des données (comment elles sont représentées à l'intérieur du dépôt).

Du point de vue d'un explorateur de fichiers classique (comme Windows Explorer) ou d'un outil de navigation du système de fichiers en ligne de commande, un dépôt Subversion n'est rien d'autre qu'un répertoire contenant plein de choses. Il y a des sous-dossiers avec des fichiers de configuration lisibles par un humain, des sous-dossiers avec des fichiers de données binaires déjà bien moins lisibles, etc. À l'instar d'autres parties de Subversion, la modularité est une préoccupation majeure et l'organisation hiérarchique prévaut sur le bazar. Ainsi, un rapide coup d'œil dans un dépôt ordinaire est suffisant pour obtenir la liste des composants essentiels d'un dépôt :

$ ls dépot
conf/  db/  format  hooks/  locks/  README.txt

Effectuons un survol rapide de ce que nous voyons dans ce répertoire (ne vous inquiétez pas si vous ne comprenez pas tous les termes employés, ils sont expliqués dans ce chapitre ou ailleurs dans ce livre) :

conf/

Un dossier destiné à accueillir les fichiers de configuration.

db/

Le magasin de données pour toutes vos données suivies en versions[49].

format

Ce fichier décrit le schéma d'organisation interne du dépôt. Il se trouve que le sous-dossier db contient parfois aussi un fichier format qui ne décrit que le contenu de ce sous-dossier et ne doit pas être confondu avec ce fichier.

hooks/

Ce dossier contient les modèles de procédures automatiques (et les procédures automatiques elles-mêmes, une fois installées).

locks/

Ce répertoire est utilisé pour les fichiers de verrous du dépôt Subversion, de façon à gérer les accès concurrents au dépôt.

README.txt

Un petit fichier qui ne fait qu'informer son lecteur qu'il se trouve dans un dépôt Subversion.

[Note]Note

Avant Subversion 1.5, la structure du dépôt sur le disque contenait toujours un sous-dossier dav. mod_dav_svn utilisait ce dossier pour stocker des informations relatives aux activités WEBDAV (l'équivalent haut-niveau du protocole WebDAV au concept de transaction dans les propagations Subversion). Subversion 1.5 a modifié cette situation en transférant la propriété du dossier activités et la possibilité de configurer son emplacement à mod_dav_svn lui-même. Dorénavant, les dépôts ne possèdent pas nécessairement un sous-dossier dav, à moins que vous n'utilisiez mod_dav_svn et que vous ne l'ayez pas configuré afin qu'il place la base de données relative à ses activités ailleurs. Reportez-vous à la section intitulée « Directives de configuration de mod_dav_svn » pour plus d'informations.

Bien sûr, quand on y accède via les bibliothèques Subversion, cet ensemble de fichiers et de dossiers se transforme en un système de fichiers suivis en versions virtuel, complet et comportant une gestion des événements personnalisable. Ce système de fichiers possède ses propres notions de répertoires et de fichiers, très similaires aux notions des systèmes de fichiers réels (tels que NTFS, FAT32, ext3, etc.). Mais c'est un système de fichiers spécial : il base ces répertoires et ces fichiers sur les révisions, conservant l'historique de tous les changements effectués de manière sûre et pérenne. C'est là que la totalité de vos données suivies en versions réside.

Stratégies de déploiement d'un dépôt

En grande partie grâce à la conception épurée du dépôt Subversion et des technologies sous-jacentes, il est particulièrement aisé de créer et configurer un dépôt. Il y a quelques choix préliminaires à faire mais l'essentiel du travail de création et de configuration d'un dépôt Subversion est simple et convivial, facilement reproductible si vous êtes amené à effectuer des installations multiples.

Voici quelques questions à se poser avant toute chose :

  • Quelles données vont être hébergées dans le dépôt (ou les dépôts) et quelle en sera l'organisation ?

  • Où sera placé le dépôt et comment les utilisateurs y accéderont-ils ?

  • De quels types de contrôle d'accès avez-vous besoin ?

Dans cette section, nous essayons de vous aider à répondre à ces questions.

Stratégies d'organisation d'un dépôt

Bien que Subversion vous permette de déplacer des fichiers et des répertoires suivis en versions sans perte d'information et qu'il fournisse même des outils pour déplacer des ensembles complets de données suivies en version d'un dépôt à un autre, ces opérations peuvent perturber le travail des autres collaborateurs qui accèdent souvent au dépôt et qui s'attendent à trouver chaque chose à sa place. Ainsi, avant de créer un nouveau dépôt, essayez de vous projeter un peu dans le futur ; préparez à l'avance le passage de vos données sous gestion de versions. Cette réflexion sur la manière d'organiser vos données dans le dépôt vous évitera de futurs et nombreux maux de tête.

Supposons qu'en tant qu'administrateur d'un dépôt, vous êtes responsable de l'administration du système de gestion de versions pour plusieurs projets. La première décision à prendre est de choisir entre un seul dépôt pour tous les projets et un dépôt par projet, ou bien un compromis entre ces deux solutions.

Un seul dépôt pour tous les projets offre des avantages, ne serait-ce que pour la maintenance unifiée. Un seul dépôt signifie qu'il n'y a qu'un seul jeu de procédures automatiques, une seule sauvegarde à gérer, un seul jeu d'opérations de déchargement et de chargement à effectuer si la nouvelle version de Subversion est incompatible avec l'ancienne version, etc. Vous pouvez également déplacer facilement des données entre les projets, sans perdre l'historique de ces informations.

Les inconvénients à utiliser un seul dépôt sont que les différents projets auront certainement des besoins différents en termes de gestion des événements, comme la notification par e-mail des propagations à des listes d'adresses différentes ou des définitions différentes de ce qui constitue une propagation légitime. Bien sûr, ce ne sont pas des problèmes insurmontables — cela implique juste que vos procédures automatiques doivent tenir compte de l'organisation du dépôt dans lequel elles sont invoquées plutôt que de considérer que l'ensemble du dépôt est associé à un seul groupe d'utilisateurs. Rappelez-vous également que Subversion utilise des numéros de révisions globaux au dépôt. Bien que ces numéros ne possèdent pas de pouvoirs magiques particuliers, certaines personnes n'aiment pas voir le numéro de révision augmenter alors qu'elles n'ont pas touché à leur propre projet[52].

On peut aussi adopter une approche intermédiaire. Par exemple, les projets peuvent être regroupés par thème. Vous pouvez avoir quelques dépôts, avec une poignée de projets dans chaque dépôt. Ainsi, les projets susceptibles de partager des données le font aisément et les développeurs sont tenus au courant des avancées des projets en relation avec les leurs par le biais des nouvelles révisions du dépôt.

Une fois l'organisation des dépôts définie, il faut se préoccuper de la hiérarchie des répertoires à l'intérieur des dépôts eux-mêmes. Comme Subversion utilise de simples copies de répertoires pour créer les branches et les étiquettes (voir le Chapitre 4, Gestion des branches), la communauté Subversion recommande de choisir un endroit dans le dépôt pour la racine de chaque projet (le répertoire dont la sous-arborescence contient toutes les données relatives à un projet) et d'y placer trois sous-répertoires : trunk (« tronc » en français), le dossier qui héberge les principaux développements du projet ; branches, le dossier dans lequel seront créées les différentes variations de la ligne de développement principale ; et tags (« étiquettes » en français), qui contient un ensemble d'instantanés de l'arborescence (les instantanés sont créés, voire détruits, mais jamais modifiés)[53].

Par exemple, votre dépôt peut ressembler à ceci :



/

   calc/

      trunk/

      tags/

      branches/

   calendrier/

      trunk/

      tags/

      branches/

   tableur

      trunk/

      tags/

      branches/

   …

Veuillez noter que l'emplacement du projet dans le dépôt n'est pas important. Si vous n'avez qu'un seul projet par dépôt, il est logique de placer la racine du projet à la racine du dépôt correspondant. Si vous avez plusieurs projets, vous voulez peut-être les classer par groupes dans des sous-répertoires communs du dépôt, en fonction des objectifs ou du code à partager par exemple, ou tout simplement en les groupant par ordre alphabétique. Voici un exemple :



/

   utilitaires

      calc/

         trunk/

         tags/

         branches/

      calendrier

         trunk/

         tags/

         branches/

      …

   bureautique/

      tableur/

         trunk/

         tags/

         branches/

      …

Organisez votre dépôt comme vous le sentez. Subversion n'a aucune exigence en la matière — pour lui, un répertoire est un répertoire. L'objectif est d'avoir une organisation qui réponde aux besoins des collaborateurs des différents projets.

Cependant, par souci de transparence, nous indiquons une autre organisation également très répandue. Dans cette organisation, les répertoires trunk, tags et branches sont situés à la racine du dépôt et les projets sont placés dans des sous-répertoires juste en dessous, comme ceci :



/

   trunk/

      calc/

      calendrier/

      tableur/

      …

   tags/

      calc/

      calendrier/

      tableur/

      …

   branches/

      calc/

      calendrier/

      tableur/

      …

Il n'y a rien d'incorrect dans une telle organisation, mais elle peut ne pas être très intuitive pour vos utilisateurs. En particulier dans des situations complexes avec plusieurs projets et un grand nombre d'utilisateurs, dont la plupart ne connaissent qu'un ou deux projets du dépôt. Mais cette approche « plusieurs projets par branche » a tendance à favoriser l'ouverture de chaque projet sur les autres et pousse à envisager l'ensemble des projets comme une seule entité. Cela reste un problème social. Nous aimons l'organisation suggérée au début pour des raisons purement pratiques : il est plus facile de faire des requêtes (ou des modifications, des migrations) sur l'historique complet d'un projet quand une sous-arborescence du dépôt contient l'ensemble des données (passé, présent, étiquettes et branches) de ce projet et elles seules.

Stratégies d'hébergement d'un dépôt

Avant de créer votre dépôt Subversion, vous devez vous demander où il va résider. Cette question est fortement liée à une myriade d'autres questions telles que  : qui sont les utilisateurs (sont-ils à l'intérieur de votre réseau interne, derrière le pare-feu de votre entreprise, ou bien s'agit-il de n'importe qui, n'importe où sur Internet ?), comment les utilisateurs accèdent au dépôt (via un serveur Subversion ou directement), quels autres services vous fournissez autour de Subversion (une interface pour navigateur Web, des notifications par email des propagations, etc.), quelle est votre politique de sauvegarde et ainsi de suite.

Le choix et la configuration du serveur sont abordés au Chapitre 6, Configuration du serveur, mais nous voulons signaler dès maintenant que certains choix pour l'une ou l'autre de ces questions ont des implications sur l'endroit où implémenter votre serveur. Par exemple, certains scénarios de déploiement nécessitent, pour plusieurs ordinateurs, l'accès au dépôt via un système de fichiers distant ou l'utilisation de plusieurs dépôts dont les contenus sont répartis géographiquement afin d'offrir des accès performants à tous les utilisateurs quels que soient leurs emplacements. Étudier chaque cas possible de déploiement de Subversion est à la fois impossible et hors du champ de ce livre. Nous vous encourageons simplement à considérer les différentes options présentées dans ces pages et ailleurs dans d'autres références, puis de planifier vos déploiements en conséquence.

Contrôle d'accès au dépôt

Le contrôle d'accès dans Subversion est presqu'entièrement géré par le processus serveur. Nous abordons les serveurs fournis par Subversion dans Chapitre 6, Configuration du serveur et nous expliquons le contrôle d'accès basé sur les chemins spécifiquement dans la section intitulée « Contrôle d'accès basé sur les chemins ». En complément du contrôle d'accès des utilisateurs, vous devez vous assurer de l'accès à votre dépôt par les programmes hébergés sur votre machine et qui ont besoin cet accès. Là encore, reportez-vous à Chapitre 6, Configuration du serveur pour trouver toutes les informtions utiles pour prendre vos décisions.

Création et configuration d'un dépôt

Dans ce chapitre (dans la section intitulée « Stratégies de déploiement d'un dépôt »), nous avons passé en revue quelques décisions importantes à prendre avant de créer et de configurer votre dépôt Subversion. Maintenant nous allons enfin mettre les mains dans le cambouis ! Dans cette section, nous voyons comment créer un dépôt Subversion et comment le configurer pour qu'il effectue des actions personnalisées lorsque certains événements ont lieu.

Création d'un dépôt

La création d'un dépôt Subversion est une tâche incroyablement simple. L'utilitaire svnadmin, fourni avec Subversion, dispose d'une sous-commande qui est justement destinée à cela (svnadmin create)  :

 
$ # Créer un dépôt
$ svnadmin create /var/svn/depot
$

Si l'on considère que le répertoire parent /var/svn existe déjà et que vous avez les droits suffisants pour modifier ce répertoire, la commande précédente crée un nouveau dépôt dans le répertoire /var/svn/depot avec le magasin de données par défaut (FSFS). Vous pouvez choisir explicitement le type de système de fichiers avec l'option --fs-type qui accepte comme argument soit fsfs, soit bdb.

$ # Créer un dépôt FSFS
$ svnadmin create --fs-type fsfs /var/svn/depot
$
$ # Créer un dépôt Berkeley DB
$ svnadmin create --fs-type bdb /var/svn/depot
$

Après l'exécution de cette simple commande, vous disposez d'un dépôt Subversion. En fonction de la manière dont vos utilisateurs accéderont à ce nouveau dépôt, vous aurez peut-être besoin de bidouiller les droits du système de fichiers. Mais comme l'administration système de base est plutôt hors de propos dans ce livre, nous laissons en exercice au lecteur le soin d'explorer plus avant ce sujet.

[Astuce]Astuce

Le chemin en argument de svnadmin est juste un chemin classique du système de fichiers, pas une URL comme celles que le client svn utilise pour spécifier un dépôt. Les commandes svnadmin et svnlook sont toutes les deux considérées comme des utilitaires coté serveur : elles sont utilisées sur la machine qui héberge le dépôt pour examiner ou modifier certains aspects du dépôt et ne sont pas capables d'effectuer des actions via le réseau. Une erreur classique des nouveaux utilisateurs de Subversion est d'essayer de passer une URL (même « locale » comme file://) à ces deux programmes.

Dans le sous-répertoire db/ de votre dépôt, vous trouvez l'implémentation du système de fichiers suivi en versions. Le nouveau système de fichiers suivi en versions de votre dépôt commence sa vie à la révision 0, qui est définie comme contenant le répertoire racine (/) et lui seul. Initialement, la révision 0 possède une seule propriété de révision, svn:date, dont la valeur est la date de création du dépôt.

Maintenant que vous disposez d'un dépôt, il est temps de le personnaliser.

[Avertissement]Avertissement

Alors que certaines parties d'un dépôt Subversion sont conçues pour être examinées et modifiées « à la main » (comme les fichiers de configuration et les procédures automatiques), vous ne devez pas (et vous ne devriez pas avoir besoin de) modifier les autres parties « à la main ». L'outil svnadmin est censé être suffisant pour toutes les modifications à apporter à votre dépôt, mais vous pouvez également vous servir d'outils tiers pour configurer les parties adéquates du dépôt. Ne tentez surtout pas de manipuler manuellement l'historique du suivi de versions en touchant aux fichiers du magasin de données du dépôt !

Mise en place des procédures automatiques

Une procédure automatique (hook en anglais) est un programme activé par certains événements du dépôt, comme la création d'une nouvelle révision ou la modification d'une propriété non suivie en versions. Certaines procédures automatiques (appelées « pré-hooks ») sont déclenchées avant l'opération sur le dépôt et permettent à la fois de rendre compte de ce qui va se passer et d'empêcher que cela se passe. D'autres procédures automatiques (appelées « post-hooks ») sont déclenchées après la fin d'un événement et servent à effectuer des tâches de surveillance (mais pas de modification) du dépôt. Chaque procédure automatique reçoit suffisamment d'informations pour déterminer la nature de l'événement, les modifications proposées (ou effectuées) du dépôt et le nom d'utilisateur de la personne qui a déclenché l'événement.

Le sous-répertoire hooks contient, par défaut, des modèles pour diverses procédures automatiques :

$ ls depot/hooks/
post-commit.tmpl          post-unlock.tmpl  pre-revprop-change.tmpl
post-lock.tmpl            pre-commit.tmpl   pre-unlock.tmpl
post-revprop-change.tmpl  pre-lock.tmpl     start-commit.tmpl
$

Il y a un modèle pour chaque type de procédure automatique que le dépôt Subversion sait prendre en charge ; en examinant le contenu de ces modèles de scripts, vous pouvez voir ce qui déclenche le script et quelles données sont passées en paramètres. Vous trouvez également dans beaucoup de ces scripts des exemples d'utilisation permettant de réaliser des tâches récurrentes utiles, en conjonction avec d'autres programmes fournis avec Subversion. Concrètement, pour activer une procédure automatique, il suffit de placer dans le répertoire depot/hooks un programme ou un script exécutable, qui sera invoqué via le nom de la procédure automatique (comme start-commit pour le début d'une propagation ou post-commit pour la fin d'une propagation).

Sur les plateformes Unix, cela veut dire fournir un programme ou un script (pouvant être un script shell, un programme Python, l'exécutable binaire d'un programme en C ou tout un tas d'autres choses) dont le nom est exactement le nom de la procédure automatique. Bien sûr, les modèles qui sont fournis ne le sont pas juste à titre d'information. Le moyen le plus facile pour mettre en place une procédure automatique sur les plateformes Unix consiste tout simplement à copier le fichier du modèle adéquat vers un nouveau fichier qui n'aura pas l'extension .tmpl, d'adapter son contenu à votre environnement et de vous assurer qu'il est exécutable. Sous Windows, comme l'extension du fichier détermine s'il est exécutable ou non, vous devez fournir un programme dont la base du nom est le nom de la procédure automatique et dont l'extension est l'une de celles reconnue comme exécutable par Windows, comme .exe pour les programmes ou .bat pour les fichiers batch.

Les procédures automatiques de Subversion sont lancées par l'utilisateur propriétaire du processus ayant accès au dépôt Subversion. La plupart du temps, on accède au dépôt via un serveur Subversion, donc cet utilisateur est le même que celui qui fait tourner le processus serveur sur le système. Les procédures automatiques elles-mêmes doivent être configurées pour être exécutables, au niveau du système d'exploitation, par ledit utilisateur. Cela implique également que tout programme ou fichier (y compris le dépôt Subversion) utilisé directement ou indirectement par la procédure automatique l'est par ledit utilisateur. En d'autres termes, faites bien attention aux problèmes de droits d'accès et d'exécution qui peuvent empêcher les procédures automatiques d'effectuer correctement les tâches pour lesquelles elles ont été conçues.

Il y a plusieurs procédures automatiques implémentées dans le dépôt Subversion et vous pouvez obtenir des détails sur chacune d'elles dans Guide de référence des procédures automatiques de Subversion. En tant qu'administrateur du dépôt, vous devez décider quelles procédures automatiques vous voulez mettre en œuvre (c'est-à-dire les nommer correctement et leur donner les droits appropriés) et de quelle manière. Lorsque vous prenez cette décision, gardez à l'esprit l'architecture de votre dépôt. Par exemple, si vous vous servez de la configuration du serveur pour déterminer les droits de propagation sur votre dépôt, vous n'avez pas besoin de mettre en place un contrôle d'accès de ce style via les procédures automatiques.

Configuration de l'environnement des procédures automatiques

Par défaut, le dépôt Subversion exécute les procédures automatiques avec un environnement vide — c'est-à-dire sans aucune variable d'environnement définie, même pas $PATH (ou %PATH% sous Windows). C'est ainsi que de nombreux administrateurs sont perplexes lorsque leurs programmes fonctionnent correctement à la main mais pas dans Subversion. Assurez-vous de définir explicitement toutes les variables d'environnement nécessaires dans votre procédure automatique.

Subversion 1.8 introduit une nouvelle façon de gérer l'environnement dans lequel s'exécutent les procédures automatiques, le fichier de configuration de l'environnement des procédures automatiques. Si le serveur Subversion trouve un fichier nommé hooks-env dans le sous-répertoire conf/ du dépôt, il analyse ce fichier à la manière d'un fichier .INI et insère les options et variables qu'il a trouvées dans l'environnement de la procédure automatique via des variables d'environnement.

La syntaxe du fichier hooks-env est très simple : chaque nom de section correspond à la procédure automatique à laquelle la section s'applique (tels que [pre-commit] ou [post-revprop-change]) et les éléments à l'intérieur des sections sont les variables d'environnement dont on souhaite définir les valeurs. En plus, il existe une section générale [default], qui permet de définir la valeur de variables d'environnement pour toutes les procédures automatiques (sauf si cette variable est explicitement redéfinie à l'intérieur d'une section particulière à une procédure automatique). Exemple 5.1, « hooks-env (configuration personnalisée de l'environnement des procédures automatiques) » fournit un exemple de fichier de configuration hooks-env.

Exemple 5.1. hooks-env (configuration personnalisée de l'environnement des procédures automatiques)

# Toutes les procédures seront configurées en français encodé en UTF-8
# et auront le répertoire contenant nos utilitaires dans leur chemin
# par défaut.

[default]
LANG = fr_FR.UTF-8
PATH = /usr/local/svn/tools:/usr/bin


# Les procédures post-commit et post-revprop-change ont aussi besoin
# de nos programmes personnalisés pour la synchronisation

[post-commit]
PATH = /usr/local/synctools-1.1/bin:%(PATH)s

[post-revprop-change]
PATH = /usr/local/synctools-1.1/bin:%(PATH)s


[Note]Note

Exemple 5.1, « hooks-env (configuration personnalisée de l'environnement des procédures automatiques) » montre aussi la fonctionnalité de substitution des chaînes de caractères de l'analyseur du fichier de configuration. Dans cet exemple, la valeur de l'option PATH (récupérée depuis la section [default] du fichier) remplacera à la volée le texte %(PATH)s dans les sections dédiées à chaque procédure automatique. Pour plus d'informations sur le fonctionnement de cette syntaxe, reportez-vous au fichier README.txt qui se trouve dans le répertoire de configuration de Subversion (et pour plus d'information sur ce répertoire, lisez la section intitulée « Zone de configuration des exécutables »).

Bien sûr, avoir exactement les mêmes informations dans chaque fichier de configuration de l'environnement des procédures automatiques, dans chaque répertoire conf/ de chaque dépôt serait vite très lourd, surtout quand vous devez les modifier tous. C'est pourquoi le serveur Subversion vous permet de spécifier un emplacement alternatif (possiblement partagé) pour ces informations de configuration.

Utilisations classiques des procédures automatiques

Les procédures automatiques du dépôt permettent de réaliser une large palette de fonctions, mais la plupart sont en fait utilisées pour quelques actions simples : la notification, la validation et la réplication.

Les procédures de notification sont celles qui indiquent à quelqu'un que quelque chose est arrivé. Celles que l'on retrouve le plus souvent dans l'utilisation de Subversion envoient des courriels de compte-rendu de propagation (resp. de modification de propriété de révision) aux membres du projet, elles sont pilotées par la procédure automatique post-commit (resp. post-revprop-change). Il existe beaucoup d'autres types de notification, depuis l'intégration dans un outil de suivi de bogues jusqu'au robot IRC qui annonce que quelque chose a changé dans le dépôt.

Pour ce qui concerne la validation, les procédures automatiques start-commit et pre-commit sont largement utilisées pour autoriser ou interdire des propagations en fonction de divers critères : l'auteur de la propagation, le format ou le contenu du commentaire de propagation voire même des détails de bas-niveau relatifs aux modifications faites aux fichiers et dossiers par la propagation. De la même manière, la procédure automatique pre-revprop-change agit comme un point de passage obligé pour les modifications des propriétés de révisions, ce qui est particulièrement utile compte tenu du fait que les propriétés de révisions ne sont pas suivies en versions et ne sont donc modifiées qu'en détruisant l'ancienne valeur.

Une catégorie de validation des modifications se généralise depuis la sortie de Subversion 1.5 : la validation du logiciel client lui-même. Quand la fonctionnalité de suivi des fusions (qui est décrite en détails dans Chapitre 4, Gestion des branches) est apparue dans Subversion 1.5, les administrateurs de Subversion ont eu besoin de s'assurer que, une fois que certains commençaient à utiliser cette fonctionnalité, alors tous leurs fusions devaient être suivies. Afin de réduire la chance que quelqu'un propage une fusion non suivie vers le dépôt, ils ont utilisé la procédure automatique start-commit pour examiner les capacités annoncées par le client Subversion. Si celui-ci n'indiquait pas savoir suivre les fusions, la propagation était interdite avec un message pour l'utilisateur lui indiquant de mettre à niveau le client Subversion ! Exemple 5.2, « procédure automatique start-commit qui s'assure que le client sait suivre les fusions. » donne un exemple de script start-commit qui implémente précisément cette fonction.

Exemple 5.2. procédure automatique start-commit qui s'assure que le client sait suivre les fusions.

#!/usr/bin/env python
import sys

# sys.argv[3] contient la liste, séparée par des ':', des capacités du client
if 'mergeinfo' not in sys.argv[3].split(':'):
  sys.stderr.write("""\
ERREUR : les propagations (commit) vers ce dépôt ne sont possible
qu'avec des clients qui savent suivre les fusions. Veuillez mettre à
niveau votre client à Subversion 1.5 ou ultérieur.
""")
  sys.exit(1)


Depuis Subversion 1.8, les clients qui propagent des modifications sur un serveur Subversion 1.8 fournissent toujours une chaîne indiquant leurs capacités mais fournissent aussi des informations complémentaires les concernant en utilisant des propriétés éphémères de transaction. Ces propriétés éphémères de transaction sont principalement des propriétés de révision qui sont définies sur la transaction de propagation par le client dès qu'il a l'occasion de le faire au moment de la propagation. Elle sont automatiquement supprimées par le serveur juste avant que la transaction ne devienne une révision. Vous pouvez inspecter ces propriétés, pendant le laps de temps séparant les procédures automatiques start-commit et pre-commit, à l'aide des mêmes outils que ceux que vous utilisez pour inspecter les autres propriétés non suivies en versions.

Vous trouverez ci-après les propriétés éphémères de transactions que Subversion reconnait actuellement :

svn:txn-client-compat-version

Contient la chaîne de caractères indiquant la version de la bibliothèque Subversion dont le client se réclame compatible. C'est utile pour décider si le client implémente le jeu de fonctionnalités suffisant pour gérer proprement les données du dépôt.

svn:txn-user-agent

Contient la chaîne de caractères décrivant le « programme utilisateur » (user agent en anglais) qui effectue la propagation. La bibliothèque Subversion définit la première partie de cette chaîne mais un programme tiers qui utilise l'API (client avec une interface graphique, etc.) peut y ajouter des informations personnalisées.

[Note]Note

alors que la plupart des clients vont transmettre les propriétés éphémères de transaction assez tôt dans le processus de transaction et pouvoir ainsi être examinées par la procédure automatique start-commit, certaines configurations de Subversion vont entrainer que ces propriétés ne seront définies dans la transaction que plus tard dans le processus de propagation. Les administrateurs doivent donc prendre le parti d'effectuer les validations basées sur les propriétés éphémères à la fois dans les procédures automatiques start-commit et pre-commit (dans le premier cas pour éviter qu'un client invalide ne transmette le contenu de la propagation, dans le deuxième cas « juste au cas où » les vérifications n'auraient pas pu être effectuées par la procédure automatique start-commit).

Comme nous l'avons déjà noté, les propriétés éphémères de transaction sont supprimées de la transaction juste avant qu'elle ne soit promue en nouvelle révision. Certains administrateurs peuvent vouloir conserver les informations de ces propriétés pour plus tard. Nous vous conseillons alors d'utiliser la procédure automatique pre-commit pour copier les valeurs des propriétés vers de nouvelles propriétés avec un nom différent. En fait, le code source distribué avec Subversion fournit un script persist-ephemeral-txnprops.py (situé dans le dossier tools/hook-scripts/), qui fait exactement cela.

La troisième catégorie que l'on rencontre souvent dans l'utilisation des procédures automatiques concerne la réplication. Que ce soit pour simplement faire une copie de sauvegarde ou pour un scénario impliquant des dépôts mirroirs, les procédures automatiques peuvent s'avérer critiques. Lisez la section intitulée « Sauvegarde d'un dépôt » et la section intitulée « Réplication d'un dépôt » pour davantage d'informations sur ces aspects de la maintenance du dépôt.

Trouver des procédures automatiques ou écrire les vôtres

Comme vous pouvez l'imaginer, il n'est pas concevable d'avoir un de procédures automatiques et de scripts divers qui ne soient librement accessibles que ce soit au sein de la communauté Subversion ou ailleurs. En fait, la distribution Subversion fournit plusieurs procédures automatiques utilisées un peu partout dans son répertoire tools/hook-scripts/. Cependant, si vous ne trouvez pas votre bonheur dans ce répertoire, vous pouvez écrire la vôtre. Lisez Chapitre 8, Intégration de Subversion pour obtenir des informations sur le développement d'applications utilisant l'API publique de Subversion.

[Avertissement]Avertissement

les procédures automatiques sont capables de faire tout et n'importe quoi, mais leurs auteurs doivent faire preuve de modération. Il pourrait être tentant, disons, d'utiliser les procédures automatiques pour corriger automatiquement des erreurs, des coquilles ou des violations de la politique dans les fichiers propagés. Malheureusement, une telle action peut engendrer des problèmes. Subversion conserve en cache, côté client, certaines parties des données du dépôt et, si vous modifiez une transaction de propagation de cette façon, ces caches seront périmés sans que cela ne puisse être détecté. De telles incohérences peuvent aboutir à des comportements surprenants et inattendus. Autant il est généralement correct d'ajouter des propriétés de transaction par une procédure automatique, autant tout le reste dans une transaction de propagation doit être considéré comme en lecture seule. Au lieu de modifier une transaction pour la rendre plus policée, contentez-vous de vérifier la transaction dans la procédure automatique pre-commit et rejetez-la si elle ne remplit pas les conditions nécessaires. Entre autre avantages, vos utilisateurs prendront ainsi des habitudes de travail empreintes de respect des procédures et de qualité.

Configuration de FSFS

Avec Subversion 1.6, le système de fichiers FSFS possède plusieurs paramètres que l'administrateur peut configurer pour piloter finement les performances ou l'utilisation du disque de leurs dépôts. Vous pouvez trouver ces options (et leur documentation) dans le fichier db/fsfs.conf du dépôt.

Maintenance d'un dépôt

Assurer la maintenance d'un dépôt Subversion peut être intimidant, certainement parce que les systèmes qui comprennent une base de données sont complexes. Le faire bien est une question de maîtrise des outils — connaître leur fonction, quand les utiliser et comment. Cette section vous présente les outils fournis par Subversion pour assurer l'administration du dépôt et décrit leur maniement pour réaliser des opérations telles que migrations de données, mises à jour, sauvegardes et nettoyages.

Boîte à outils de l'administrateur

Subversion fournit une poignée d'utilitaires pour créer, inspecter, modifier et réparer votre dépôt. Étudions de plus près chacun de ces outils.

svnadmin

Le programme svnadmin est le meilleur ami de l'administrateur de dépôts. En plus de fournir la possibilité de créer des dépôts Subversion, ce programme vous permet d'effectuer de nombreuses opérations de maintenance sur ces dépôts. La syntaxe de svnadmin est similaire à celle des autres programmes en ligne de commande de Subversion :

$ svnadmin help
usage général : svnadmin SOUS_COMMANDE DÉPÔT [ARGS & OPTIONS ...]
Entrer 'svnadmin help <sous-commande>' pour une aide spécifique.
Entrer 'svnadmin --version' pour avoir la version et les modules de stockages.

Sous-commandes disponibles :
   crashtest
   create
   deltify
…

Au début de ce chapitre (dans la section intitulée « Création d'un dépôt »), nous vous avons présenté la sous-commande svnadmin create. Beaucoup d'autres sous-commandes svnadmin sont couvertes plus loin dans ce chapitre. Vous pouvez également consulter Guide de référence de svnadmin : administration des dépôts Subversion pour une liste complète des sous-commandes et des fonctionnalités qu'elles apportent.

svnlook

svnlook est un outil de Subversion pour examiner les différentes révisions et transactions (qui sont des révisions en cours de création) dans un dépôt. Aucune modification n'est faite au dépôt par cet outil. svnlook est généralement utilisé par les procédures automatiques du dépôt pour signaler les changements qui vont être propagés (dans le cas de la procédure automatique pre-commit) ou qui viennent d'être propagés (dans le cas de la procédure automatique post-commit). Un administrateur peut être amené à utiliser cet outil à des fins de diagnostic.

La syntaxe de svnlook est particulièrement simple :

$ svnlook help
usage général : svnlook SOUS_COMMANDE CHEMIN_DÉPÔT [ARGS & OPTIONS...]
Note : Quand --revision ou --transaction ne sont pas précisées, les sous-
       commandes qui en ont besoin utilisent la révision la plus récente.
Entrer 'svnlook help <sous-commande>' pour une aide spécifique.
Entrer 'svnlook --version' pour avoir la version et les modules de stockage.
…

Beaucoup de sous-commandes svnlook peuvent être appliquées soit à une révision soit à une arborescence de transaction, affichant les informations à propos de l'arborescence elle-même ou les différences par rapport à la révision précédente du dépôt. Pour spécifier quelle révision ou quelle transaction examiner, utilisez respectivement les options --revision (-r) et --transaction (-t). En l'absence des options --revision (-r) ou --transaction (-t), svnlook examine la révision la plus récente (la révision HEAD) du dépôt. Ainsi, les deux commandes suivantes font exactement la même chose si la révision la plus récente du dépôt situé à l'emplacement /var/svn/depot porte le numéro 19 :

$ svnlook info /var/svn/depot
$ svnlook info /var/svn/depot -r 19

Signalons une exception à ces règles concernant les sous-commandes : la sous-commande svnlook youngest ne prend aucune option et affiche simplement le numéro de la révision la plus récente du dépôt :

$ svnlook youngest /var/svn/depot
19
$
[Note]Note

Gardez à l'esprit que les seules transactions que vous pouvez examiner sont celles qui n'ont pas été propagées. La plupart des dépôts ne comportent pas de transactions de ce type parce que les transactions sont habituellement soit propagées (auquel cas vous devriez y avoir accès sous la forme de révisions via l'option --revision (-r)), soit annulées et supprimées.

La sortie de svnlook est conçue pour être à la fois lisible par un humain et analysable par une machine. Prenons, par exemple, la sortie de la sous-commande svnlook info :

$ svnlook info /var/svn/depot
sally
2002-11-04 09:29:13 -0600 (lun. 04 nov. 2002)
27
J'ai ajouté le traditionnel
Arbre grec.
$

La sortie de svnlook info est constituée des éléments suivants, par ordre d'apparition :

  1. L'auteur, suivi d'un passage à la ligne.

  2. La date, suivie d'un passage à la ligne.

  3. Le nombre de caractères du commentaire de propagation, suivi d'un passage à la ligne.

  4. Le commentaire de propagation lui-même, suivi d'un passage à la ligne.

Cette sortie est lisible par un humain, ce qui veut dire que les éléments tels que la date sont représentés par du texte simple au lieu d'un obscur code (comme le nombre de nanosecondes depuis le passage aux nouveaux francs). Mais cette sortie est aussi analysable par une machine — parce que le commentaire de propagation peut comporter plusieurs lignes et n'est pas limité en taille, svnlook affiche la longueur du commentaire avant le commentaire lui-même. Cela permet aux scripts et autres utilitaires faisant appel à cette commande de prendre des décisions opportunes à propos du commentaire de propagation, comme savoir combien de mémoire allouer pour le commentaire ou au moins savoir combien d'octets sauter dans le cas où les données affichées par svnlook ne sont pas les dernières données du flux.

svnlook peut répondre à un tas d'autres requêtes : afficher des sous-ensembles des informations précédemment citées, lister récursivement les arborescences suivies en versions des répertoires, lister les chemins modifiés lors de telle révision ou transaction, afficher les différences de contenu et de propriétés pour les fichiers et répertoires,  etc. Reportez-vous à Guide de référence de svnlook : outil d'exploration du contenu d'un dépôt Subversion pour la liste complète des fonctionnalités offertes par svnlook.

svndumpfilter

Bien que ce ne soit pas l'outil qu'un administrateur utilise le plus, svndumpfilter fournit une fonctionnalité d'un genre très particulier qui est d'une grande utilité : la possibilité de modifier rapidement et facilement des flux de l'historique du dépôt Subversion en agissant en tant que filtre sur les chemins.

La syntaxe de svndumpfilter est la suivante :

$ svndumpfilter help
usage général : svndumpfilter SOUS_COMMANDE [ARGS & OPTIONS ...]
Entrer 'svndumpfilter help <sous-commande>' pour l'aide spécifique.
Entrer 'svndumpfilter --version' pour avoir le numéro de version du programme.

Sous-commandes disponibles :
   exclude
   include
   help (?, h)

Il n'y a que deux sous-commandes intéressantes : svndumpfilter exclude et svndumpfilter include. Elles vous permettent de choisir entre l'inclusion implicite et l'inclusion explicite des chemins dans le flux. Vous en saurez plus sur ces sous-commandes et sur l'utilité si particulière de svndumpfilter plus loin dans ce chapitre, dans la section intitulée « Filtrage de l'historique d'un dépôt ».

svnrdump

Le programme svnrdump est, pour faire simple, essentiellement une variante qui fonctionne avec un réseau des sous-commandes svnadmin dump et svnadmin load.

$ svnrdump help
usage général : svnrdump SOUS_COMMANDE DÉPÔT [-r BAS[:HAUT]]
Entrer 'svnrdump help <sous-commande>' pour une aide spécifique.
Entrer 'svnrdump --version' pour la version et les modules d'accès (RA).

Sous-commandes disponibles :
   dump
   load
   help (?, h)

$

Nous entrerons dans le détail de l'utilisation des commandes svnrdump et svnadmin plus loin dans ce chapitre (voir la section intitulée « Migration des données d'un dépôt »).

svnsync

Le programme svnsync fournit toutes les fonctionnalités requises pour faire fonctionner un miroir en lecture seule d'un dépôt Subversion. Ce programme a une et une seule fonction : transférer l'historique d'un dépôt vers un autre dépôt. Et, bien qu'il y ait différentes manières de faire, sa force réside dans sa capacité de travailler à distance : les dépôts « source » et « destination » peuvent être sur deux ordinateurs différents et svnsync sur un troisième.

Comme vous vous en doutez, svnsync possède une syntaxe très proche des autres programmes déjà mentionnés dans ce chapitre :

$ svnsync help
usage général : svnsync SOUS_COMMANDE DÉPÔT [ARGS & OPTIONS ...]
Entrer 'svnsync help <sous-commande>' pour une aide spécifique.
Entrer 'svnsync --version' pour la version et les modules d'accès (RA).

Sous-commandes disponibles :
   initialize (init)
   synchronize (sync)
   copy-revprops
   info
   help (?, h)
$

Nous revenons en détail sur la réplication de dépôts avec svnsync plus loin dans ce chapitre (voir la section intitulée « Réplication d'un dépôt »).

fsfs-reshard.py

Bien qu'il ne fasse pas officiellement partie des outils Subversion, le script fsfs-reshard.py (situé dans le répertoire tools/server-side du code source de Subversion) est un outil particulièrement utile à l'administrateur pour optimiser les performances de dépôts Subversion utilisant un magasin de données FSFS. Les dépôts FSFS contiennent des fichiers qui décrivent les changements apportés dans chaque révision. Parfois ces fichiers sont conservés dans un même répertoire, parfois ils sont répartis (sharded en anglais, d'où le nom du script) dans plusieurs répertoires.

Les versions anciennes de FSFS contenaient tous les fichiers de révision dans un répertoire unique qui grandissait (un fichier par nouvelle révision) au fur et à mesure. Cela entrainait des problèmes sur les systèmes qui ont des limites fixées sur le nombre maximum de fichiers dans un répertoire et cela pouvait engendrer des chutes de performances sur certains systèmes.

>À partir de la version 1.5, Subversion crée des dépôts FSFS en utilisant une disposition légèrement modifiée dans laquelle le contenu du répertoire des fichiers de révisions est éparpillé (c'est-à-dire réparti dans plusieurs sous-répertoires). Cela peut réduire de manière considérable le temps nécessaire au système pour trouver n'importe quel fichier et ainsi améliore la performance globale de Subversion lors des lectures dans le dépôt.

Le nombre de fichiers autorisés dans un sous-répertoire donné est configurable (les valeurs par défaut sont toutefois raisonnables pour la plupart des plateformes connues), mais modifier cette configuration après que le dépôt a été utilisé depuis quelque temps peut empêcher Subversion de retrouver les fichiers qu'il cherche. C'est l'objet de la commande fsfs-reshard.py.

fsfs-reshard.py remanie la structure du dépôt pour se conformer au nombre de sous-répertoires demandés et met à jour la configuration du dépôt pour conserver cette modification. Utilisé en combinaison avec svnadmin upgrade , c'est particulièrement utile pour convertir un dépôt Subversion pre-1.5 vers le dernier schéma réparti de Subversion (ce que Subversion ne fait pas automatiquement pour vous). Vous pouvez aussi ajuster finement cette valeur dans un dépôt déjà réparti.

Correction des commentaires de propagation

Il arrive qu'un utilisateur se trompe dans son commentaire de propagation (une faute d'orthographe ou une coquille, par exemple). Si le dépôt est configuré (en utilisant la procédure automatique pre-revprop-change, voir la section intitulée « Mise en place des procédures automatiques ») pour accepter les modifications de ce commentaire après la fin de la propagation, l'utilisateur peut corriger son commentaire à distance en utilisant svn propset (voir svn propset (pset, ps)). Cependant, en raison de la possibilité de perte d'information irrémédiable, les dépôts Subversion ne sont pas configurés, par défaut, pour autoriser les modifications de propriétés non suivies en versions — sauf de la part d'un administrateur.

Si un administrateur est amené à changer un commentaire de propagation, il peut le faire avec svnadmin setlog. Cette commande change le commentaire de propagation (la propriété svn:log) d'une révision donnée du dépôt, la nouvelle valeur étant lue dans un fichier.

$ echo "Voici le nouveau commentaire de propagation, en version corrigée" > nouveau-commentaire.txt
$ svnadmin setlog mon-depot nouveau-commentaire.txt -r 388

La commande svnadmin setlog, par défaut, possède les mêmes garde-fous pour empêcher de modifier des propriétés non suivies en versions qu'un client distant (les procédures automatiques pre-revprop-change et post-revprop-change sont toujours activées et doivent donc être configurées afin d'accepter ce type de changement. Mais un administrateur peut contourner ces protections en passant l'option --bypass-hooks à la commande svnadmin setlog.

[Avertissement]Avertissement

Souvenez-vous cependant que, en contournant les procédures automatiques, vous êtes susceptible de ne pas activer certaines actions telles que la notification par email du changement des propriétés, la sauvegarde par les systèmes qui surveillent les propriétés non suivies en versions, etc. En d'autres termes, faites particulièrement attention aux changements que vous apportez et à la manière dont vous le faites.

Gestion de l'espace disque

Bien que le coût de stockage ait diminué de manière drastique ces dernières années, l'utilisation de l'espace disque reste une des préoccupations de l'administrateur qui doit suivre en versions de grandes quantités de données. Chaque élément de l'historique de chaque donnée stockée dans un dépôt actif doit être sauvegardé ailleurs, peut-être même de nombreuses fois dans le cas de sauvegardes tournantes. Il est utile de savoir quelles données d'un dépôt Subversion doivent rester sur le site de production, lesquelles doivent être sauvegardées et lesquelles peuvent être supprimées sans risque.

Économie d'espace disque

Pour garder un dépôt petit, Subversion utilise la différenciation (ou « stockage différentiel ») à l'intérieur du dépôt lui-même. La différenciation implique l'encodage de la représentation d'un groupe de données sous la forme d'un ensemble de différences par rapport à un autre groupe de données. Si les deux groupes de données sont très similaires, la différenciation économise de l'espace pour le groupe différencié — au lieu de prendre le même espace que les données originales, le groupe occupe juste l'espace nécessaire pour dire : « je ressemble à l'autre groupe de données là-bas, sauf pour les deux ou trois changements qui suivent ». Au final, l'espace occupé par l'ensemble des données du dépôt (c'est-à-dire le contenu des fichiers suivis en versions) est beaucoup plus petit que la représentation textuelle originale de ces données.

Le stockage sous forme différenciée a fait partie de l'architecture conceptuelle de Subverson dès le début de sa conception ; et il a subi des améliorations au cours des ans. Les dépôts créés avec la version 1.4 ou ultérieure de Subversion bénéficient de la compression du contenu des fichiers « pleins-textes ». Les dépôts créés avec la version 1.6 ou ultérieure de Subversion économisent davantage d'espace disque grace au partage de représentation, une fonctionnalité qui permet à plusieurs fichiers ou révisions de fichiers dont le contenu est identique de faire référence à une seule instance partagée de ce contenu plutôt que chacun n'en conserve sa propre copie.

Suppression des transactions mortes

Bien que rares, il y a des circonstances dans lesquelles le déroulement d'une propagation Subversion peut mal se terminer, laissant derrière elle dans le dépôt des restes de cette tentative de propagation : une transaction inachevée et toutes les modifications de fichiers et de répertoires associées. Il peut y avoir plusieurs raisons à cet échec : l'utilisateur a peut-être brutalement interrompu l'opération côté client ou bien une coupure réseau s'est peut-être produite au milieu de l'opération. Quoi qu'il en soit, des transactions mortes peuvent apparaître. Elles ne sont pas dangereuses mais elles consomment inutilement de l'espace disque. Un administrateur consciencieux se doit néanmoins de les supprimer.

Vous pouvez utiliser la commande svnadmin lstxns pour obtenir la liste des noms des transactions non encore réglées :

$ svnadmin lstxns mon-depot
19
3a1
a45
$

Chaque élément de la sortie de cette commande peut être passé en argument de svnlook (avec l'option --transaction (-t)) pour déterminer qui est à l'origine de la transaction, quand elle a eu lieu et quels types de changements ont été effectués — ces informations sont très utiles pour savoir si on peut supprimer la transaction sans arrière pensée ! Si vous décidez effectivement de supprimer la transaction, son nom peut être passé à svnadmin rmtxns qui fera le nettoyage adéquat. En fait, svnadmin rmtxns peut directement prendre en entrée la sortie de svnadmin lstxns !

 
$ svnadmin rmtxns mon-depot `svnadmin lstxns mon-depot`
$

Si vous utilisez ces deux sous-commandes ainsi, vous devriez envisager de rendre votre dépôt temporairement indisponible pour les clients. De cette manière, personne ne peut initier une transaction légitime avant que le nettoyage n'ait commencé. L'exemple Exemple 5.3, « txn-info.sh (lister les transactions inachevées) » contient quelques lignes de script shell qui peuvent produire les informations relatives à chaque transaction inachevée de votre dépôt.

Exemple 5.3. txn-info.sh (lister les transactions inachevées)

#!/bin/sh

### Produit les informations relatives à toutes les transactions
### inachevées d'un dépôt Subversion

DEPOT="${1}"
if [ "x$DEPOT" = x ] ; then
  echo "utilisation: $0 CHEMIN_VERS_LE_DEPOT"
  exit
fi

for TXN in `svnadmin lstxns ${DEPOT}`; do
  echo "---[ Transaction ${TXN} ]-------------------------------------------"
  svnlook info "${DEPOT}" -t "${TXN}"
done


La sortie produite par ce script est, en bref, la concaténation des différents groupes d'informations fournis par svnlook info (voir la section intitulée « svnlook ») et ressemble à ceci :

$ txn-info.sh mon-depot
---[ Transaction 19 ]-------------------------------------------
sally
2001-09-04 11:57:19 -0500 (mar. 04 sep. 2001)
0
---[ Transaction 3a1 ]-------------------------------------------
harry
2001-09-10 16:50:30 -0500 (lun. 10 sep. 2001)
39
Tentative de propagation dans un réseau capricieux
---[ Transaction a45 ]-------------------------------------------
sally
2001-09-12 11:09:28 -0500 (mer. 12 sep. 2001)
0
$

Une transaction initiée depuis longtemps correspond en général à une propagation qui a été interrompue ou qui a échoué. L'horodatage de la transaction peut fournir des informations intéressantes — par exemple, quelle est la probabilité qu'une transaction commencée il y a neuf mois soit toujours active ?

En résumé, la décision de supprimer une transaction ne doit pas être prise à la légère. D'autres sources d'informations (comme les journaux d'Apache sur les erreurs et les accès, les journaux opérationnels de Subversion, l'historique des révisions Subversion, etc.) peuvent aider à la prise de décision. Et bien sûr, l'administrateur peut toujours entrer en contact (par email, par exemple) avec l'auteur d'une transaction qui semble abandonnée pour vérifier que c'est bien le cas.

Tasser le système de fichiers FSFS

Les dépôts FSFS contiennent des fichiers de révision qui décrivent, pour chaque révision, les modifications apportées et les propriétés de la révision concernée. Les dépôts créés avec des versions de Subversion antérieures à 1.5 gardent ces fichiers dans deux dossiers (un dossier pour chaque type de fichier). Au fur et à mesure que les révisions sont propagées dans le dépôt, Subversion dépose autant de fichiers dans ces dossiers. Avec le temps, le nombre de fichiers dans chaque dossier peut se révéler relativement important. Cela peut être la cause de chutes de performances sur certains systèmes de fichiers réseaux.

Le premier problème est que le système d'exploitation doit référencer ces fichiers sur une courte période de temps. Cela conduit à une mauvaise utilisation du cache disque et, en conséquence, à plus de temps pour les recherches sur les gros disques. C'est pour cette raison que Subversion est pénalisé lorsqu'il accède à vos données suivies en versions.

Le deuxième problème est un peu plus subtil. En raison de la manière dont la plupart des systèmes de fichiers allouent l'espace disque, chaque fichier utilise sur le disque plus de place qu'il n'en prend réellement. Cette quantité d'espace disque nécessaire pour stocker un seul fichier peut atteindre de 2 à 16 kilooctets par fichier, en fonction du type de système de fichiers. Cela se traduit directement par un gachis d'espace disque à chaque révision pour les dépôts FSFS. L'effet est d'autant plus sensible pour les dépôts qui ont de petites révisions, puisque le surplus d'espace nécessaire pour stocker le fichier de révision dépasse rapidement la taille des données effectivement stockées.

Afin de résoudre ce problème, Subversion 1.6 introduit la commande svnadmin pack. En « tassant » (pack en anglais) tous les fichiers d'un dépôt fragmenté dans un seul fichier et en supprimant les fichiers correspondants à chaque révision, svnadmin pack remplace la fragmentation par un seul fichier. Ainsi,le cache du système de fichiers est plus performant et le temps d'accès aux fichiers est réduit.

Subversion peut tasser des dépôts fragmentés qui ont été mis à niveau vers le système de fichiers 1.6 ou ultérieur (voir svnadmin upgrade dans Guide de référence de svnadmin : administration des dépôts Subversion). Pour le faire, lancez simplement svnadmin pack sur le dépôt:

 
$ svnadmin pack /var/svn/depot
Packing shard 0...done.
Packing shard 1...done.
Packing shard 2...done.
…
Packing shard 34...done.
Packing shard 35...done.
Packing shard 36...done.
$

Comme le processus de tassage obtient les verrous nécessaires avant de faire son travail, vous pouvez lancer la commande sur un dépôt en service, ou même comme action dans la procédure automatique post-commit. Tasser un dépôt déjà tassé est autorisé, mais n'aura aucun effet sur l'utilisation de l'espace disque par le dépôt.

La commande svnadmin pack n'a aucun effet sur un dépôt BDB.

Migration des données d'un dépôt

Un système de fichiers Subversion a ses données réparties dans les fichiers du dépôt d'une manière que seuls les développeurs Subversion eux-mêmes comprennent (et s'y intéressent). Il peut cependant y avoir des circonstances qui obligent à copier ou déplacer l'ensemble (ou une partie) des données d'un dépôt à un autre.

Subversion fournit cette fonctionnalité par le biais des flux de déchargement du dépôt. Un flux de déchargement de dépôt (« fichier dump » ou dump file en anglais, quand il est stocké dans un fichier sur le disque) est un format de fichier portable, contenant des données brutes, qui décrit les différentes révisions de votre dépôt — ce qui a été modifié, par qui, quand, etc. Ce fichier dump est le principal mécanisme utilisé pour réorganiser des historiques de versions — en partie ou en totalité, avec ou sans modification — entre des dépôts. Et Subversion fournit les outils nécessaires à la création et au chargement de ces fichiers dump : les sous-commandes svnadmin dump et svnadmin load respectivement.

[Avertissement]Avertissement