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, 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 aient 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 commande svn merge.

Dans les exemples qui suivent, nous supposerons que le client et le serveur Subversion sont tous deux en version 1.5 (ou plus récente). Si l'un ou l'autre sont en version plus ancienne, 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.

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 d'un ensemble de modifications implicite : 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.

Comment 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 le /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. 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 « surprise » 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 http://svn.exemple.com/depot/calc/trunk
--- Fusion 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). Après l'exécution de la commande de l'exemple précédent, la copie de travail de votre branche contient de nouvelles modifications locales, ces changements étant des duplications de toutes les modifications qui ont eu lieu sur le tronc depuis que vous avez créé votre branche :

$ svn status
 M     .
M      bouton.c
M      entier.c

Une fois rendu là, le plus sage est d'examiner attentivement les modifications avec svn diff et ensuite de compiler et de tester votre branche. Notez que le répertoire de travail actuel (« . ») a aussi été modifié ; svn diff indique que sa propriété svn:mergeinfo a été créée ou modifiée. Ceci est une méta-information importante liée à la fusion, à laquelle vous ne devriez pas toucher, puisqu'elle sera nécessaire aux futures commandes svn merge (nous en apprendrons plus sur cette métadonnée plus loin dans ce chapitre).

Après avoir effectué la fusion, vous aurez peut-être aussi besoin de résoudre des conflits (comme vous le faites pour svn update) ou éventuellement d'effectuer de petites modifications afin que les choses fonctionnent correctement (souvenez-vous, ce n'est pas parce qu'il n'y a pas de conflits syntaxiques qu'il n'y a pas de conflits sémantiques !). Si vous rencontrez de graves difficultés, vous pouvez toujours annuler les modifications locales en lançant svn revert . -R (qui annule toutes les modifications locales) et entamer avec vos collègues une longue discussion sur le thème « Qu'est-ce qui se passe ? ». Par contre, si les choses se passent bien, vous pouvez propager ces modifications dans le dépôt :

$ svn commit -m "Fusionné les dernières modifications de trunk avec ma-branche-calc"
Envoi          .
Envoi          bouton.c
Envoi          entier.c
Transmission des données ..
Révision 357 propagée.

À présent, votre branche privée est désormais « synchro » avec le tronc, vous pouvez donc vous détendre, sachant qu'en continuant votre travail en isolation, vous ne dériverez pas trop loin de ce que les autres font.

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 synchro. Lancez juste la même commande svn merge à nouveau !

$ svn merge http://svn.exemple.com/depot/calc/trunk
--- Fusion de r357 à r380 dans '.':
U    bouton.c
U    Makefile
A    LISEZMOI

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 les modifications locales à votre branche.

Réintegration d'une branche

Cependant, 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 :

$ svn merge http://svn.exemple.com/repos/calc/tronc
--- Fusion de r381 à r385 dans '.':
U    bouton.c
U    LISEZMOI

$ # compiler, tester, ...

$ svn commit -m "Fusion finale des modifications du tronc dans ma-branche-calc."
Envoi          .
Envoi          bouton.c
Envoi          LISEZMOI
Transmission des données ..
Révision 390 propagée.

À présent, utilisez svn merge pour répercuter les modifications de votre branche sur le tronc. Vous avez alors besoin d'une copie de travail de /trunk qui soit à jour. 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 »). Quelle que soit la manière dont vous obtenez une copie de travail, souvenez-vous qu'une bonne méthode est d'effectuer la fusion dans une copie de travail qui n'a pas été modifiée localement et qui a été mise à jour récemment (en d'autres termes, qui n'est pas un mélange de révisions locales). Si votre copie de travail n'est pas « propre » comme expliqué à l'instant, vous risquez de rencontrer des problèmes facilement évitables liés à des conflits et svn merge renverra probablement une erreur en retour.

Une fois que vous avez une copie de travail propre du tronc, vous êtes prêt pour y fusionner votre branche :

$ pwd
/home/user/calc-tronc

$ svn update  # (s'assurer que la copie de travail est à jour)
À la révision 390.

$ svn merge --reintegrate http://svn.exemple.com/depot/calc/branches/ma-branche-calc
--- Fusionne toutes les modifications non fusionnées des URLs sources dans '.':
U    bouton.c
U    entier.c
U    Makefile
U   .

$ # compiler, tester, vérifier, ...

$ svn commit -m "Fusionner ma-branche-calc dans le tronc !"
Envoi          .
Envoi          bouton.c
Envoi          entier.c
Envoi          Makefile
Transmission des données ..
Révision 391 propagée.

Félicitations, votre branche a maintenant réintégré la ligne de développement principale. Notez bien l'utilisation de l'option --reintegrate à cette occasion. L'option est essentielle pour répercuter les modifications d'une branche sur sa ligne de développement d'origine, ne l'oubliez pas ! Elle est nécessaire car ce type de « réintégration » est un type de tâche différent de ce que vous avez fait jusqu'à présent. Précédemment, nous demandions à svn merge de faire la liste des modifications ayant eu lieu dans une ligne de développement (le tronc) et de les dupliquer vers 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 voir qu'il fusionne en premier les modifications 345:356 du tronc vers la branche ; ensuite il continue en fusionnant le groupe contigu immédiatement suivant, 356:380. Quand il effectue la synchronisation finale, il fusionne le groupe 380:385.

Cependant, lors de la réintégration d'une branche dans le tronc, 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 de groupe de révisions contigu à recopier. En spécifiant l'option --reintegrate, vous demandez explicitement à Subversion de ne recopier que les modifications spécifiques à votre branche (et en fait il le fait en comparant l'arborescence la plus récente du tronc avec l'arborescence la plus récente de la branche : la différence qui en résulte constitue exactement les modifications de votre branche !).

Votre branche privée ayant réintégré le tronc, vous voudrez peut-être la supprimer du dépôt :

$ svn delete http://svn.exemple.com/depot/calc/branches/ma-branche-calc \
      -m "Supprime ma-branche-calc."
Révision 392 propagée.

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 /branches, son existence demeure une partie immuable de l'historique du dépôt. Une simple commande svn log appliquée à l'URL /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 »).

Dans Subversion 1.5, une fois que la fusion d'une branche vers le tronc a été faite avec l'option --reintegrate, la branche n'est plus utilisable. Elle ne peut absorber correctement de nouvelles modifications du tronc, ni être réintégrée à nouveau proprement dans le tronc. Pour cette raison, si vous voulez continuer à travailler sur la branche de votre fonctionnalité, nous vous recommandons de la détruire et de la recréer depuis le tronc :

$ svn delete http://svn.exemple.com/depot/calc/branches/ma-branche-calc \
      -m "Supprime ma-branche-calc."
Révision 392 propagée.

$ svn copy http://svn.exemple.com/depot/calc/trunk \
           http://svn.exemple.com/depot/calc/branches/nouvelle-branche
      -m "Crée une nouvelle branche a partir du tronc."
Révision 393 propagée.

$ cd ma-branche-calc

$ svn switch http://svn.exemple.com/depot/calc/branches/nouvelle-branche
À la révision 393.

La dernière commande de l'exemple précédent, svn switch, est une façon de mettre à jour une copie de travail existante afin qu'elle pointe vers un autre dossier du dépôt. Nous en parlerons plus en détail dans la section intitulée « Parcours des branches ».

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 les propriétés. 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 :

$ cd ma-branche-calc
$ svn propget svn:mergeinfo .
/trunk:341-390

Il est déconseillé de changer soi-même la valeur de cette propriété, à moins de savoir vraiment ce que vous faites. Cette propriété 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 est /trunk et le dossier qui a reçu les modifications spécifiées est /branches/ma-branche-calc.

Il existe également une sous-commande, svn mergeinfo, qui peut être utile pour voir non seulement 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.

$ cd ma-branche-calc

# Quelles modifications ont déjà été fusionnées du tronc vers la branche ?
$ svn mergeinfo http://svn.exemple.com/depot/calc/trunk
r341
r342
r343
…
r388
r389
r390

# Quelles modifications sont encore susceptibles d'être fusionnées du tronc vers la branche ?
$ svn mergeinfo http://svn.exemple.com/depot/calc/trunk --show-revs eligible
r391
r392
r393
r394
r395

La commande svn mergeinfo prend en paramètres une URL « source » (d'où les modifications viennent) et une URL « cible » optionnelle (vers laquelle les modifications sont fusionnées). Si aucune URL cible n'est fournie, elle suppose que le dossier actuel est la cible. Dans l'exemple précédent, puisque nous interrogeons la copie de travail de notre branche, la commande suppose que ce qui nous intéresse est de recevoir les modifications de l'URL spécifiée du tronc vers /branches/ma-branche-calc.

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 http://svn.exemple.com/depot/calc/trunk --dry-run
U    entier.c

$ 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 un tel environnement). 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.

[Astuce] Astuce

Bien qu'il soit parfaitement légitime de lancer svn merge et svn revert à plusieurs reprises pour préparer la fusion, vous risquez de rencontrer quelques obstacles (facilement surmontables). Par exemple, si l'opération de fusion ajoute un nouveau fichier (ou plus exactement programme son ajout), svn revert ne supprime pas le fichier ; il va juste déprogrammer l'ajout. Vous vous retrouvez avec un fichier non suivi en versions. Si ensuite vous tentez à nouveau de lancer la fusion, il risque d'y avoir des conflits dus au fichier non suivi en versions resté « en travers du chemin ». La Solution ? Après avoir effectué un retour en arrière à l'aide de svn revert, pensez à nettoyer la copie de travail et supprimer les fichiers et dossiers non suivis en versions. Il faut que le résultat de svn status soit le plus propre possible, et dans l'idéal vide.

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 303, qui affectait entier.c, est complètement incorrecte. Elle n'aurait jamais du être propagée. Vous pouvez utiliser svn merge pour revenir en arrière sur cette modification dans votre copie de travail, puis propager la modification locale au dépôt. Il suffit juste de spécifier une différence inversée (en indiquant soit --revision 303:302, soit --change -303, les deux se valent).

$ svn merge -c -303 http://svn.exemple.com/depot/calc/trunk
--- Fusion inverse de r303 dans 'entier.c':
U    entier.c

$ svn status
 M     .
M      entier.c

$ svn diff
…
# vérifier que la modification est supprimée
…

$ svn commit -m "Retour en arrière sur la modification propagée en r303."
Envoi          entier.c
Transmission des données .
Révision 350 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 nº303 à 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 303. Si quelqu'un extrait une version du projet calc entre les révisions 303 et 349, 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 [22].

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 « piquets de révisions » : machin.c@224, mentionnée dans la section intitulée « Piquets de révisions 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).

$ cd dossier-parent
$ svn log -v
…
------------------------------------------------------------------------
r808 | paul | 2003-12-26 14:29:40 -0600 (ven. 26 déc 2003) | 3 lignes
Chemins modifiés :
   D /calc/trunk/reel.c
   M /calc/trunk/entier.c

Ajouté les fonctions des transformées de fourier dans entier.c.
Supprimé reel.c car code désormais dans double.c.
…

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 808. 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 807.

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 808 « à 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 808 à l'envers programmerait non seulement l'ajout de reel.c, mais le message de propagation indique qu'il reviendrait aussi sur certaines modifications de entier.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 entier.c, mais cette technique fonctionne mal à plus grande échelle. Que dire, si 90 fichiers avaient été modifiés en révision 808 ?

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 http://svn.exemple.com/depot/calc/trunk/reel.c@807 ./reel.c

$ svn status
A  +   reel.c

$ svn commit -m "Ressuscité reel.c à partir de la révision 807, /calc/trunk/reel.c."
Ajout         reel.c
Transmission des données .
Révision 1390 propagée.

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 807. 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 http://svn.exemple.com/depot/calc/trunk/reel.c@807 > ./reel.c

$ svn add reel.c
A         reel.c

$ svn commit -m "Recréé reel.c à partir de la révision 807."
Ajout         reel.c
Transmission des données .
Révision 1390 propagée.

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 http://svn.exemple.com/depot/calc/trunk/reel.c@807 \
           http://svn.exemple.com/depot/calc/trunk/ \
      -m "Ressuscite reel.c dans la révision 807."
Révision 1390 propagée.

$ svn update
A    reel.c
À la révision 1390.


[22] 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 ».