Aller au menu - Aller au contenu

Icône Gérer les conflits

L'auteur de ce cours cherche un repreneur pour continuer son travail
Parfois, certains auteurs de cours n'ont plus le temps de rédiger et de mettre à jour leur travail. Ils recherchent alors un nouvel auteur pour "reprendre" leur tutoriel (voir la liste des tutoriels en attente de repreneur).
Contactez l'auteur si vous aimeriez continuer la rédaction !

Avatar
Mise à jour : 02/05/2009
1 845 visites depuis 7 jours, dont 106 sur ce chapitre classé 74/786
Dans ce chapitre, nous allons enfin apprendre à résoudre la chose la plus horrible... les conflits !

Comme nous l'avons déjà vu (mais un petit rappel ne fait jamais de mal), un conflit apparaît lorsque deux personnes (ou plus) décident de modifier un fichier au même moment. La plus rapide va commiter son changement sans aucun problème, mais la deuxième va devoir gérer le fait que, sur le serveur, se trouve une version plus récente que celle sur laquelle elle a travaillé.

Pour vous entraîner à gérer les conflits, il faut d'abord les créer. Pour en créer un, aucun problème, il suffit de faire un update du fichier voulu à une version antérieure, de modifier le fichier et de le commiter. Le serveur ne sera pas content puisque la version sur laquelle vous avez travaillé n'est pas la plus récente au moment du commit.

Pour éviter, lorsque vous travaillez sur les conflits, qu'il se passe des choses incompréhensibles parce que vous êtes cinq à tester les conflits sur le même fichier, créez un dossier, créez un fichier dans ce même dossier et remplissez-le ; faites un Code : Console
commit
, puis un deuxième avec un autre ajout de contenu, updatez à la première version du fichier (seulement la moitié du contenu) et entraînez-vous comme ça.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Contourner le conflit

Maintenant que tout le monde se rappelle comment créer un conflit, je vous propose de mettre en place votre environnement et d'en créer un.

Alors, quelles sont les options qui viennent à nous pour contourner le problème (le résoudre nous-mêmes ou le laisser à quelqu'un d'autre :p ) !? Trois commandes, en fait :
  • diff ;
  • revert ;
  • resolved.


diff



Cette commande va vous permettre de ne pas vous perdre. Vous codez plus vite que votre ombre ? Vous ne savez plus ce que vous avez modifié depuis le dernier update ? Pas de problème, svn diff est là pour vous expliquer ce que vous avez fait.

Code : Console
svn diff [Target[@Rev]...] [-N] [-r Rev1:Rev2] [--force] [--summarize] [--no-diff-deleted] \
[--username login] [--password mdp] [--no-auth-cache]

  • @Rev correspond à la révision de référence que vous voulez prendre. Il montrera les différences entre les versions du fichier en montrant ce qui a été ajouté ou supprimé par rapport à cette référence.
  • -N pour non récursif (uniquement ce dossier).
  • --force pour forcer la commande à s'exécuter.
  • --sumarize pour qu'il n'affiche qu'un résumé.
  • --no-diff-deleted pour qu'il n'affiche pas les différences pour les fichiers ayant été supprimés.


Bon, je vous ai montré la commande entière pour que vous sachiez l'utiliser, mais ce dont vous avez besoin pour voir les différences que vous avez apportées par rapport à votre dernier update, c'est juste :
Code : Console
svn diff

Et c'est tout.

À mon grand regret, je n'ai pas trouvé d'équivalent de la commande "" pour TortoiseSVN. Mais rassurez-vous, TortoiseSVN possède un outil bien plus puissant pour comparer deux versions d'un fichier, ou bien deux fichiers différents pour les fusionner dans un troisième. Ce sera vu en prochaine sous-partie.


revert



Cette commande va enlever la plupart des modifications effectuées sur les éléments (fichiers ou dossiers) depuis le dernier update.

Euh... ça veut dire quoi, "la plupart" ? Il ne va pas tout remettre ? C'est au petit bonheur la chance ?


Pas du tout ! Mais si vous regardez le manuel (si, si, le manuel), vous verrez que cette option ne permet pas de restaurer des dossiers supprimés. Sinon, c'est du tout cuit.
On voit d'ici le problème de cette commande. Toutes vos modifications vont partir à la poubelle et vous allez gagner le droit de les refaire sur le nouveau fichier après un update.

Eh oui, la vie est dure, mais c'est comme ça. Dans un projet, la plupart du temps, cette commande est utilisée parce que quelqu'un a édité un fichier binaire, ".doc" par exemple, alors que ce n'était pas son tour dans le planning. Or le problème, c'est que SVN ne sait pas fusionner des fichiers binaires ! Dommage... il va donc falloir tout refaire.

La commande s'utilise comme suit (très peu d'options) :
Code : Console
svn revert [CHEMIN] [--targets Fichier_darguments] [-R]


resolved



Cette commande a "deux fonctions". Elle ne permet que d'indiquer qu'un conflit est résolu (comme on aurait pu s'en douter), seulement, subtilité, il y a deux façons de s'en servir.

Méthode 1


J'ai un conflit, je me casse la tête et le résous, j'indique qu'il est résolu, je commite.

Méthode 2


J'ai un problème, je ne m'en occupe pas parce que je n'ai pas envie de refaire ce que j'ai fait et que c'était mon tour dans le planning, j'indique qu'il est résolu (même si ce n'est pas le cas), je commite.

Pour la méthode 1, nous allons voir comment fusionner le travail, en cas de conflit, dans la prochaine sous-partie. Pour la méthode 2, vous l'aurez compris, elle permet de ne pas avoir à tout refaire soi-même ; en revanche, elle donne tout à refaire à l'autre membre de l'équipe qui a commité avant vous.

Voici son utilisation (à nouveau très peu d'options) :
Code : Console
svn resolved [CHEMIN] [--targets Fichier_darguments] [-R]


Et TortoiseSVN



Eh bien, à nouveau, pas besoin de se casser la tête.
Clic droit >> TortoiseSVN >> Revert
Clic droit >> TortoiseSVN >> Resolved

TortoiseSVN est très bien fait, il n'affiche pas les actions qu'il est impossible d'effectuer. Ainsi, s'il n'y a rien à résoudre (ni à revert) sur le fichier ou dossier cliqué, il ne vous proposera pas la commande !

Fusionner le conflit

Ici, nous allons apprendre à résoudre le conflit de la manière la plus subtile : la fusion des versions en conflit.
Pourquoi est-ce que l'on ne fait pas toujours comme ça ?

Eh bien tout simplement parce que ce n'est pas toujours possible. Comme dit précédemment, il n'est pas possible d'user de cette méthode pour tout conflit. Elle ne fonctionnera pas avec les fichiers binaires par exemple. En revanche, elle fonctionne très bien avec les fichiers texte.

Je vais dans cette partie détailler la marche que j'ai suivie. Ça vous permettra d'utiliser la même pour mieux comprendre, et de vous servir un peu aussi votre esprit d'analyse. ^^

Commençons par la création du conflit :
Code : Console
>>> svn update -r 62 Fichier\ 1.txt 
U    Fichier 1.txt
Updated to revision 62.
>>> vim Fichier\ 1.txt
>>> svn ci -m "commit d'un conflit"
Sending        Fichier 1.txt
svn: Commit failed (details follow):
svn: Your file or directory 'Fichier 1.txt' is probably out-of-date
svn: The version resource does not correspond to the resource within the transaction.  Either the requested version resource is out of date (needs to be updated), or the requested version resource is newer than the transaction root (restart the commit).
>>> ls 
Dossier 1                monajout.cpp                testFile
Fichier 1 - Copie.txt        test                        youpi2.txt
Fichier 1.txt                test123.txt
argInfo                        test2
>>> svn status -u Fichier\ 1.txt 
M      *       62   Fichier 1.txt
Status against revision:     76

Et voilà, ce gentil petit message d'erreur nous explique que, contre toute attente, la version sur laquelle nous nous sommes basés pour faire nos changements n'est pas à jour par rapport au serveur (ce n'est pas la plus récente). Il va donc falloir corriger le tir.

Résolution manuelle



Regardons, au hasard, ce qui se passe si l'on tente un update sur ce fichier (non à jour mais modifié localement).

Code : Console
>>> svn update Fichier\ 1.txt 
C    Fichier 1.txt
Updated to revision 76.

Notez le C en première colonne qui nous indique que le fichier est Conflicted (en conflit). Mais regardons un peu plus loin.
Code : Console
>>> ls
Dossier 1                Fichier 1.txt.r76        test2
Fichier 1 - Copie.txt        argInfo                        testFile
Fichier 1.txt                monajout.cpp                youpi2.txt
Fichier 1.txt.mine        test
Fichier 1.txt.r62        test123.txt


On constate que l'update nous a rajouté tout un tas de nouveaux "Fichier 1.txt". Parmi ces nouveaux, on trouve :
  • "Fichier 1.txt", le vrai, celui sous contrôle de version, celui en conflit (tous les autres ne sont pas considérés comme étant sous contrôle de version, vous pourrez vous en persuader avec les icônes, ceux qui en ont et avec un svn ls pour les autres) ;
  • "Fichier 1.txt.mine", le mien, celui que j'ai modifié ;
  • "Fichier 1.txt.r62", le fichier 1.txt de la révision 62 (celle sur laquelle j'ai travaillé) ;
  • "Fichier 1.txt.r76", celui de la révision 76, la dernière révision en date lors du commit.


Un petit coup d'oeil sur ce fameux Fichier 1.txt qui est sous contrôle de version : qu'en a fait le update ?

Code : Console
>>> svn status -uv Fichier\ 1.txt
C              76       76 SdZ-Guest    Fichier 1.txt
Status against revision:     76
>>> Cat Fichier\ 1.txt
[... Début du Fichier ...]
Et je fous la merde

Merci

<<<<<<< .mine
OM en force ! 

Création d'un conflit
=======
OM en force !

Barça en berne lol
>>>>>>> .r76


On voit que les deux modifications sont présentes dans ce fichier. À la fois les miennes, mais aussi celles faites par le commit de la révision 76. Il est assez simple de comprendre comment les différences sont séparées :
  • tout ce qui est entre "<<<<<<< .mine" et "=======" sont mes modifications ;
  • tout ce qui se trouve entre "=======" et ">>>>>>> .r76" sont les modifications qui ont été commitées lors de la révision 76.

À partir de là, on garde ce que l'on veut, on remet dans l'ordre, on déclare le conflit comme resolved (résolu) pour autoriser le commit et on commite.

Code : Console
>>> vim Fichier\ 1.txt
>>> svn resolved Fichier\ 1.txt
Resolved conflicted state of 'Fichier 1.txt'
>>> svn ci Fichier\ 1.txt -m "resolution du conflit"
Sending        Fichier 1.txt
Transmitting file data .
Committed revision 77.

Oui, mais ça m'a l'air très manuel cette méthode.... Il n'y a pas plus subtil ?

Eh bien, oui et non. Pour la plupart des modifications effectuées, SVN ne peut se permettre de choisir pour vous, si deux personnes ont édité la même ligne (le même endroit dirons-nous plutôt), quelles modifications arrivent en premier ?

Résolution automatique



En revanche il sait très bien faire le merge (la fusion) tout seul si vous n'avez pas édité les mêmes parties d'un document.

Petit exemple.
Je prends le fichier monaj.cpp que je trouve sur le SVN au moment de la rédaction.
Citation : monaj.cpp
je rajoute un fichier

et je le modifie en plus
... tant pis

un ligne après
hop

J'update sur deux working copies (copies de travail) et j'édite sur les deux le fichier. Pour le premier, je rajoute "hop" au début du fichier (en rajoutant une ligne), pour le deuxième, je rajoute "hop" à la fin du fichier (sans rajouter de ligne).
Je commite avec l'un puis avec l'autre.
Le deuxième commit me donne une erreur, donc j'update.

Code : Console
>>> svn up monaj.cpp 
G    monaj.cpp
Updated to revision 90.
>>> ls
Dossier 1                monaj.cpp                testFile
Fichier 1 - Copie.txt        test123.txt                youpi2.txt
Fichier 1.txt                test2
argInfo                        test3
>>> cat monaj.cpp 
hop
je rajoute un fichier 

et je le modifie en plus
... tant pis

un ligne après
hop hop

On voit que le fichier n'est pas C pour indiquer un conflit, mais G pour indiquer une fusion automatique. Et on constate qu'en effet, aucun fichier .mine, .r89, .r90 n'a été créé (comme lors d'un conflit), et que le fichier monaj.cpp a bien intégré les deux modifications faites.

Et TortoiseSVN



Utilisateurs de Windows et de TortoiseSVN, regardez bien. Qu'a-t-on fait lors de cette sous-partie ? Uniquement des update et des commit. Mais vous savez déjà faire tout ça. Donc, vous savez tout faire.
Quand je vous disais que l'apprentissage d'un client n'était pas compliqué, ce n'était pas pour rien ! Il faut juste la théorie et hop ! c'est parti.

Information tout de même : pour faire un update vers une révision précédente avec TortoiseSVN, il faut faire :
Clic droit >> TortoiseSVN >> update to revision...


Pourtant, TortoiseSVN propose quand même un GROS plus : TortoiseMerge !

Eh oui, la résolution manuelle d'un conflit est quelque peu lourde : repérer les différences entre deux fichiers et tout et tout. TortoiseSVN a mis au point un outil pour le moins performant qu'est TortoiseMerge. Vous pouvez récupérer sa documentation par ici.
Une annexe aura très certainement lieu pour apprendre à s'en servir correctement. En attendant, il faudra vous contenter de la doc.

Éviter le conflit

Maintenant que l'on a vu comment le résoudre, on va voir comment l'éviter.

Ce que l'on a déjà vu



Eh oui, tout au long du tutoriel, j'ai essayé de vous sensibiliser le plus possible aux "best practices" (les bonnes pratiques) du SVN à mettre en oeuvre pour éviter d'avoir des conflits. Petit récapitulatif :
  • essayez de travailler le plus possible avec des fichiers texte et le moins possible avec des fichiers binaires ;
  • faites des update le plus souvent et régulièrement possible, et obligatoirement avant de commencer à travailler sur un fichier ;
  • faites des commit réguliers, dès que vous avez fini de travailler sur un fichier (pas deux heures après), et si le travail prend du temps, un petit commit intermédiaire ne fait pas de mal et sauvegarde votre travail ;
  • ayez un planning pour savoir à tout instant qui peut travailler quand sur les fichiers binaires ;
  • consultez régulièrement les logs et autres informations pour ne pas faire du travail pour rien.


Il est fortement conseillé d'appliquer ces bonnes pratiques !


lock et unlock



Il existe également deux commandes qui sont là rien que pour vous, exprès pour éviter les conflits. Il s'agit de lock (verrouiller) et unlock (déverrouiller).
Ces deux commandes ont un comportement très simple. lock va bloquer l'édition du fichier aux autres membres jusqu'à ce que vous fassiez un unlock.

Si l'on va voir l'icône associé sous TortoiseSVN, on trouve celle-ci : Icône fichier locké
Nous avons donc un très joli petit cadenas qui nous montre que le fichier possède un lock.

Lors de votre commit sur l'élément verrouillé, il va vous être proposé de relâcher le verrou. C'est d'ailleurs le comportement par défaut. Donc si vous ne voulez pas libérer le verrou, il faut le préciser.


Regardons ce que cela donne pour l'utilisation sous TortoiseSVN et en ligne de commande. Pour TortoiseSVN, toujours aussi simple : clic droit >> TortoiseSVN >> Get lock... et clic droit >> TortoiseSVN >> Release lock.
Pour la ligne de commande :
Code : Console
svn lock TARGET... [--target fichier_darguments] [-m "message"] [--force] [--force-log] [--username login] [--password mdp] [--no-auth-cache]
et
Code : Console
svn unlock TARGET...  [--target fichier_darguments] [--force] [--username login] [--password mdp] [--no-auth-cache]


Alors, dans ces multiples options, vous en retrouvez quelques-unes que vous connaissez déjà, on notera d'ailleurs la possibilité de mettre un message de log sur le lock pour le justifier, mais également quelques nouvelles options :
  • --no-auth-cache permet de ne pas stocker les informations d'accès au serveur (login & mdp) dans le cache. À utiliser si vous êtes sur l'ordinateur d'un autre par exemple ;
  • --force-log va, comme son nom l'indique encore une fois, forcer la validité de votre message de log ;
  • --force, lui, force l'exécution de la commande, même si quelqu'un d'autre avait, par exemple, déjà mis un verrou sur l'élément concerné.

--force ne devrait pas être utilisée. Cette option sert si vraiment quelqu'un dans le groupe a fait une connerie, a verrouillé quelque chose qu'il n'aurait pas dû ou bien l'a fait à un moment où il n'aurait pas dû.


Maintenant, vous l'aurez compris, la chose simple à faire pour éviter les conflits est de, lorsqu'on va travailler sur un fichier, le locker, verrouiller, pour être sûr qu'il n'y ait pas de conflit.
NON ! C'est une énorme erreur. Le lock se doit d'être utilisé avec parcimonie. Tout le but du merge automatique est de permettre à plusieurs personnes de travailler en même temps sur le même fichier. Si vous faites un lock à chaque fois, il y a une perte de productivité.

Le lock montre son utilité lors d'un travail sur un fichier binaire, car, je le rappelle une vingtième fois, on ne sait pas fusionner les fichiers binaires. Mais en dehors de ce cas-là, il n'est et ne devrait presque jamais être utilisé.
Vous venez de voir plusieurs méthodes pour résoudre des conflits. Le tout est maintenant de les maîtriser parce que vous aller forcément faire face à des conflits dans vos projets.
Chapitre précédent Sommaire Chapitre suivant

Partager

Il n'y a pas encore de commentaire pour ce tuto.

Ce tutoriel a été corrigé par les zCorrecteurs.