This text is a work in progress—highly subject to change—and may not accurately describe any released version of the Apache™ Subversion® software. Bookmarking or otherwise referring others to this page is probably not such a smart idea. Please visit http://www.svnbook.com/ for stable versions of this book.
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.
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.
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 | |
---|---|
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
, 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
(URL
^
)[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 ».
À 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.
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 | |
---|---|
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. |
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 | |
---|---|
Le terme « réintégration » provient de l'option
|
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.
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 | |
---|---|
Bien qu'il soit possible de modifier soi-même
|
Astuce | |
---|---|
La quantité de |
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 | |
---|---|
Après avoir effectué une opération de fusion, mais avant
d'en avoir propagé les résultats, vous pouvez utiliser
|
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.
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].
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.
[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 ».