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.

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.



[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.