Dans les chapitres précédents, nous avons développé un exemple sur le projet hello-world.
Alice et Bob, les premiers programmeurs du projet, créaient une archive et quelques révisions.
Candice, un utilisateur du projet, créait sa propre archive, démarrait une branche du projet hello-world, et commençait à maintenir ses propres modifications locales.
Dans ce chapitre, nous allons commencer à étudier une situation plus typique et réaliste des projets libres. Ici, nous considérerons Alice et Bob comme les responsables du projet public, et Candice comme une contributrice importante du projet. Nous allons identifier les besoins en gestion de révisions engendrés par cette organisation, et regarder quelques commandes arch qui pourrons nous aider à les satisfaire.
Jusqu'ici, si vous avez suivis les exemples, Candice a une branche élémentaire. Elle a créé une branche à partir de la branche principale (mainline), effectué quelques modifications, et a gardé sa branche à jour par rapport à la branche principale d'Alice et Bob.
Nous supposons, maintenant, qu'Alice et Bob veulent fusionner les modifications d'Alice dans la branche principale.
Bien, ce travail de fusion a déjà été effectué. La dernière révision de Candice est exactement ce qu'Alice et Bob souhaitent. Ils peuvent incorporer cette fusion dans leur branche principale très simplement, en enregistrant la dernière révision de Candice dans leur propre branche principale :
% tla get -A candice@candice.net--2003-candice \
hello-world--candice--0.1 \
hw-C
[...]
% cd hw-C
% tla set-tree-version -A lord@emf.net--2003-example \
hello-world--mainline--0.1
% tla make-log
++log.hello-world--mainline--0.1--lord@emf.net--2003-example
[... edit log file (consider `tla log-for-merge') ... ]
% cat ++log.hello-world--mainline--0.1--lord@emf.net--2003-example
Summary: merge from Candice's Branch
Keywords:
Patches applied:
* candice@candice.net--2003-candice/hello-world--candice--0.1--patch-2
merge from mainline sources
* candice@candice.net--2003-candice/hello-world--candice--0.1--patch-1
Punctuated the output correctly
* candice@candice.net--2003-candice/hello-world--candice--0.1--base-0
tag of
lord@emf.net--2003-example/hello-world--mainline--0.1--patch-1
% tla commit
[....]
Note
Notez attentivement l'« astuce » que nous avons utilisée. La dernière révision d'Alice était exactement ce qu'Alice et Bob souhaitaient -- ils ont associés un get avec un set-tree-version pour faire en sorte que l'arborescence de Candice puisse facilement être archivée dans leur branche principale.
Considérons ce qui ce passe lorsqu'un développement se poursuit sur les deux branches. Pour cela, nous allons introduire une nouvelle chose : un système de diagramme composé des branches et des fusions entre elles.
Après nos exemples, nous avons cette situation :
mainline--0.1 candice--0.1 ------------- ------------ base-0 -----------> base-0 (a tag) patch-1 ---------' patch-1 patch-2 ----------> patch-2 patch-3 ----------' --------' patch-4 <-----------'
qui nous indique que la branche candice est un tag du patch-1 provenant de la branche principale; qu'au niveau du patch-2 de la branche candice, il y a eu une fusion de tous les patchs de la mainline jusqu'au patch-3; et finalement que le patch-4 fusionne tout jusqu'au patch-2 de la branche candice.
Lorsque nous avons un diagramme dans lequel aucune des lignes de fusion ne se croisent, on considère que c'est un « développement multi-branches, simple ».
La signification d'un développement multi-branches, simple, est qu'il s'agit d'un modèle où deux équipes peuvent travailler d'une manière asynchrone sur un même projet. Dans chaque équipe -- dans chaque branche -- les programmeurs utilisent un style de coopération basé sur update/commit (voir « Le style de coopération update/commit »). Ainsi, les changements dans chaque branches n'ont aucun effet sur les autres jusqu'à la fusion des deux branches.
Supposons qu'il y ait beaucoup plus de travaux dans chacune des branches mainline et candice, nous amenant à ça :
mainline--0.1 candice--0.1
------------- ------------
base-0 -----------> base-0 (a tag)
patch-1 ---------' patch-1
patch-2 ----------> patch-2
patch-3 ----------' --------' patch-3
patch-4 <-----------' patch-4
patch-5
patch-6
% tla revisions --summary -A candice@candice.net--2003-candice \
hello-world--candice--0.1
base-0
tag of
lord@emf.net--2003-example/hello-world--mainline--0.1--patch-1
patch-1
Punctuated the output correctly
patch-2
merge from mainline sources
patch-3
added a period to output string
patch-4
capitalized the output string
% tla revisions --summary -A lord@emf.net--2003-example \
hello-world--mainline--0.1
base-0
initial import
patch-1
Fix bugs in the "hello world" string
patch-2
commented return from main
patch-3
added copywrong statements
patch-4
merge from Candice's Branch
patch-5
fixed the copyrwrong for hw.c
patch-6
fixed the copyrwrong for main.c
Considérons le scénario suivant dans lequel notre but est de fusionner les nouveaux travaux de la branche mainline dans la branche candice. En d'autres termes, nous voulons arriver à ça :
mainline--0.1 candice--0.1 ------------- ------------ base-0 -----------> base-0 (a tag) patch-1 ---------' patch-1 patch-2 ----------> patch-2 patch-3 ----------' --------' patch-3 patch-4 <-----------' patch-4 patch-5 --------> patch-5 patch-6 ------------'
Comment pouvons-nous effectuer cette fusion ? Démarrons à la dernière révision de candice avant la fusion avec mainline (patch-4).
% tla get -A candice@candice.net--2003-candice \
hello-world--candice--0.1--patch-4 \
hw-C-4
[....]
% cd hw-C-4
Voici deux techniques qui ne marchent pas :
replay va essayer d'appliquer tous les changements qui manquent (missing) de mainline dans l'arborescence candice. La liste des changesets est indiquée :
% tla missing --summary \
-A candice@candice.net--2003-example \
hello-world--mainline--0.1
patch-4
merge from Candice's Branch
patch-5
fixed the copyrwrong for hw.c
patch-6
fixed the copyrwrong for main.c
Le problème est que le patch-4 apparaît dans la liste. C'est une fusion qui inclut tous les changements de la branche candice jusqu'au niveau de son patch-2. Mais ces changements sont déjà présents dans la révision patch-4 de la branche candice -- ainsi replay va les appliquer plusieurs fois (ce qui va créer un conflit de patch).
Attention!
La commande replay ne vous empêchera pas d'effectuer plusieurs replay même si l'arborescence n'est plus dans un état cohérent. tla dans son état actuel ne fusionne pas les fichiers de rejet. Cela laisse ouvert la possibilité de perdre des rejets de patchs si un second replay est opéré avant que les rejets ne soit traités. (Un jour tla sera capable de fusionner plusieurs rejets en un seul).
Note
Pour les utilisateurs avancés
La commande replay a une option qui nous permet d'éviter le patch-4 de la branche principale. Cela résoudrait le problème, mais il y a quelques inconvénients. Premièrement, ça signifie que le patch-4 continuera d'être signalé comme manquant par la commande missing de la branche candice. Deuxièmement, rien ne nous garantit que le changeset patch-4 contient seulement la fusion de la branche candice. Si Alice et Bob ont effectués d'autres changements dans patch-4, et que nous évitons ce changeset, ces changements seront perdus.
Supposons que nous essayons d'utiliser update à partir de la branche mainline. Souvenez-vous que update va créer un changeset entre le plus jeune ascendant de mainline jusqu'à l'arborescence actuelle, et ensuite appliquer ce changeset à la dernière révision de mainline.
Nous avons une notation pour cela. Un changeset entre X et Y est indiqué :
delta(X, Y)
Dans ce cas, update va démarrer en créant un changeset entre la révision patch-3 de mainline et notre arborescence :
delta(mainline--0.1--patch-3, hw-C-4)
L'arborescence résultante à l'application du changeset de X à Y vers l'arborescence Z est indiquée :
delta(X, Y) [ Z ]
En d'autres termes, le résultat de la commande update dans notre exemple peut être décrite comme cela :
delta(mainline--0.1--patch-3, hw-C-4) [mainline--0.1--patch-6]
Cependant, il y a un problème. La révision patch-3 de mainline n'a pas été fusionnée avec la branche de candice. Ainsi, le changeset
delta(mainline--0.1--patch-3, hw-C-4)
va inclure, en plus des autres changements, les modifications entre patch-1 et patch-2 de la branche candice.
Malheureusement, l'arborescence sur laquelle le changeset sera appliquée, mainline--0.1--patch-6, a déjà été fusionnée avec base-0...patch-2 de la branche candice.
Comme avec replay, update va entraîner des conflits de fusion en effectuant des changements redondants.
En utilisant notre notation delta et nos diagrammes de fusions, regardons comment résoudre ce problème de fusion proprement.
Souvenez-vous que nous avons actuellement :
mainline--0.1 candice--0.1 ------------- ------------ base-0 -----------> base-0 (a tag) patch-1 ---------' patch-1 patch-2 ----------> patch-2 patch-3 ----------' --------' patch-3 patch-4 <-----------' patch-4 patch-5 patch-6
et notre but est de créer une nouvelle fusion, pour le patch-5 de la branche de Candice :
--------> patch-5
patch-6 ------------'
Nous pourrions décider de démarrer avec la branche mainline et fusionner les changements de candice manquants, ou démarrer avec l'arborescence candice et fusionner les changements manquants de mainline. Prenons le dernier (fusion dans l'arborescence de candice).
Dans ce cas, la révision patch_6 de mainline-0.1 est « à jour » par rapport à la révision patch-2 de candice-0.1. Nous voulons appliquer tous les changements de cette révision jusqu'à la dernière révision de candice :
avec:
ascendant := candice--0.1--patch-2
fusion_dans := mainline--0.1--patch-6
résultat := candice--0.1--patch-4
réponse := delta(ascendant, fusion_dans)[résultat]
Les flèches dans le diagramme de fusion sont décisives pour obtenir la bonne réponse. Par exemple, supposons que la flèche du patch-2 de Candice vers la révision patch-4 de mainline ne soit pas là. Dans ce cas la réponse aurait été :
avec:
ascendant := mainline--0.1--patch-3
fusion_dans := mainline--0.1--patch-6
résultat := candice--0.1--patch-4
réponse := delta(ascendant, fusion_dans)[résultat]
Tracer les flèches pour une fusion donnée est un travail pénible. Il est automatique avec la commande star-merge :
Cela dépasserait le cadre de ce tutoriel d'expliquer la solution complète du problème de fusion d'un développement multi-branches en général. Les deux solutions montrées plus haut illustrent deux cas, mais des solutions légèrement différentes sont parfois nécessaires.
Ce que vous devriez savoir c'est que lorsque vous avez un développement multi-branches, simple (voir « Développement multi-branches, simple »), la commande star-merge sait fusionner les branches sans déclencher de faux conflits.
% tla get -A candice@candice.net--2003-candice \
hello-world--candice--0.1--patch-4 \
merge-temp
% tla star-merge lord@emf.net--2003/hello-world--mainline--0.1
Version du 18/09/2004 21h14 : wilk@flibuste.net--libre docs-tla--fr--1.0 patch-102 ... base-0