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.
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 :
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.
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
.
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.
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.
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.
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.
La plupart des 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 peut-être à 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 fusionner la branche vers le tronc avec l'option
--reintegrate
:
$ cd copie-de-travail-du-tronc $ svn update À la révision 1910. $ svn merge --reintegrate http://svn.exemple.com/depot/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 modification à la fois.