belaran@964: belaran@964: belaran@976: belaran@976: wilk@1006: Un tour rapide de Mercurial : fusionner les travaux belaran@976: belaran@976: Nous avons maintenant étudié comment cloner un dépôt, effectuer belaran@976: des changements dedans, et récupérer ou transférer depuis un belaran@976: autre dépôt. La prochaine étape est donc de fusionner les belaran@976: modifications de différents dépôts. belaran@976: belaran@976: belaran@976: Fusionner différents travaux wilk@1006: La fusion est un aspect fondamental lorsqu'on wilk@1006: travaille avec un gestionnaire de révisions distribué. belaran@976: belaran@976: belaran@995: belaran@995: Alice et Bob ont chacun une copie personnelle du dépôt d'un belaran@995: projet sur lequel ils collaborent. Alice corrige un bug belaran@995: dans son dépôt, et Bob ajoute une nouvelle fonctionnalité dans le belaran@995: sien. Ils veulent un dépôt partagé avec à la fois le correctif du belaran@995: bug et la nouvelle fonctionnalité. belaran@995: belaran@995: belaran@995: Je travaille régulièrement sur plusieurs tâches différentes sur belaran@995: un seul projet en même temps, chacun isolé dans son propre dépôt. belaran@995: Travailler ainsi signifie que je dois régulièrement fusionner une belaran@995: partie de mon code avec celui des autres. belaran@995: belaran@995: belaran@995: andre@1016: Parce que nous devons fusionner souvent, andre@1016: Mercurial rend cette opération facile. Étudions ensemble le déroulement des belaran@995: opérations. Nous commencerons encore par faire un clone d'un autre belaran@995: dépôt (vous voyez que l'on fait ça tout le temps ?) puis nous ferons belaran@995: quelques modifications dessus. belaran@995: belaran@995: &interaction.tour.merge.clone; belaran@995: belaran@995: Nous devrions avoir maintenant deux copies de belaran@995: hello.c avec des contenus différents. Les belaran@995: historiques de ces deux dépôts ont aussi divergés, comme illustré dans belaran@995: la figure . belaran@995: belaran@995: &interaction.tour.merge.cat1; belaran@995: wilk@1006: Et ici se trouve notre version légèrement différente du belaran@995: dépôt. belaran@995: belaran@995: &interaction.tour.merge.cat2; belaran@995: belaran@995:
wilk@1006: Historique divergeant des dépôts <filename belaran@995: class="directory">my-hello</filename> et <filename belaran@995: class="directory">my-new-hello</filename>. belaran@995: belaran@995: belaran@995: XXX ajoute un test belaran@995: belaran@995:
belaran@995: belaran@995: Nous savons déjà que récupérer les modifications depuis belaran@995: notre dépôt my-hello n'aura belaran@995: aucun effet sur l'espace de travail. belaran@995: belaran@995: &interaction.tour.merge.pull; belaran@995: belaran@995: Néanmoins, la commande hg belaran@995: pull nous indique quelque chose au sujet des belaran@995: heads. belaran@995: belaran@995: wilk@1006: Les révisions <quote>heads</quote> belaran@995: andre@1016: Rappelez vous que Mercurial enregistre quelle révision belaran@995: est le parent de chaque révision. Si une révision a un parent, nous wilk@1006: l'appelons un enfant child ou un descendant de ce parent. Une wilk@1006: head est une révision qui n'a donc pas d'enfant. La révision tip wilk@1006: est donc une head, car c'est la révision la plus récente du dépôt belaran@995: qui n'a pas d'enfant. Il y a des moments où un dépôt peut contenir wilk@1006: plusieurs heads. belaran@995: belaran@997:
wilk@1006: Contenu du dépôt après une récupération (pull) depuis le belaran@995: dépôt <filename belaran@995: class="directory">my-hello</filename> vers le dépôt <filename belaran@995: class="directory">my-new-hello</filename> belaran@995: belaran@995: belaran@995: belaran@995: belaran@995: XXX ajoute un texte belaran@995: belaran@995:
belaran@995: belaran@995: Dans la figure , wilk@1006: vous pouvez constater l'effet d'un pull depuis le dépôt belaran@995: my-hello dans le dépôt belaran@995: my-new-hello. L'historique qui belaran@995: était déjà présent dans le dépôt my-new-hello reste intact, mais une belaran@995: nouvelle révision a été ajoutée. En vous reportant à la figure , vous pouvez voir que l' wilk@1006: ID de révision changeset ID reste le même dans belaran@995: le nouveau dépôt, mais que le numéro de belaran@995: révision reste le même. (Ceci est un parfait exemple de belaran@995: pourquoi il n'est fiable d'utiliser les numéros de révision lorsque wilk@1006: l'on discute d'un changeset.) Vous pouvez voir les heads belaran@995: présentes dans le dépôt en utilisant la commande hg heads. belaran@995: belaran@995: &interaction.tour.merge.heads; belaran@995:
belaran@995: belaran@995: belaran@995: Effectuer la fusion belaran@995: belaran@997: Que se passe-t-il quand vous essayez d'utiliser la belaran@995: commande hg update pour mettre à andre@1016: jour votre espace de travail au nouveau tip ? belaran@995: belaran@995: &interaction.tour.merge.update; belaran@995: belaran@995: belaran@995: Mercurial nous prévient que la commande hg update n'effectuera pas belaran@995: la fusion, il ne veut pas mettre à jour l'espace de travail quand il belaran@995: estime que nous pourrions avoir besoin d'une fusion, à moins de lui belaran@995: forcer la main. À la place, il faut utiliser la commande hg merge pour fusionner les deux andre@1016: heads. andre@1016: andre@1016: wilk@1006: wilk@1006: Pour commencer une fusion (merge) entre deux heads, belaran@995: nous utilisons la commande hg merge. belaran@995: belaran@995: &interaction.tour.merge.merge; belaran@995: belaran@995: Nous résolvons les conflits dans le fichier belaran@995: hello.c. Ceci met à jour le répertoire de travail andre@1016: de sorte qu'il ne contienne les modifications en provenance des wilk@1006: deux heads, ce qui est indiqué par la belaran@995: la sortie de la commande hg belaran@995: parents et le contenu du fichier belaran@995: hello.c. belaran@995: belaran@995: &interaction.tour.merge.parents; belaran@995: belaran@995: belaran@995: belaran@995: Effectuer l'ajout (commit) du résultat de la fusion belaran@995: belaran@995: Dès l'instant où vous avez effectué une fusion belaran@995: (merge), hg parents vous belaran@995: affichera deux parents, avant que vous n'exécutiez la commande belaran@995: hg commit sur le résultat de la belaran@995: fusion. belaran@995: belaran@995: &interaction.tour.merge.commit; belaran@995: andre@1016: Nous avons maintenant un nouveau tip, remarquez qu'il wilk@1006: contient à la fois nos anciennes heads et leurs belaran@995: parents. Ce sont les mêmes révisions que nous avions affichées avec belaran@995: la commande hg parents. belaran@995: belaran@995: &interaction.tour.merge.tip; belaran@995: belaran@995: Dans la figure , belaran@995: vous pouvez voir une représentation de ce qui se passe dans l'espace belaran@995: de travail pendant la fusion, et comment ceci affecte le dépôt lors wilk@1006: du commit. Pendant la fusion, l'espace de travail, qui a deux belaran@995: révisions (changesets) comme parents, voit ces derniers devenir le parent belaran@995: d'une nouvelle révision (changeset). belaran@995: belaran@995:
wilk@1006: Répertoire de travail et dépôt pendant une fusion, wilk@1006: et le <quote>commit</quote> qui suit belaran@995: belaran@995: belaran@995: belaran@995: belaran@995: XXX ajoute texte belaran@995: belaran@995:
belaran@995: belaran@995:
belaran@995:
belaran@995: belaran@995: belaran@995: Fusionner les modifications en conflit belaran@995: wilk@1006: La plupart des fusions sont assez simples à réaliser, mais belaran@995: parfois vous vous retrouverez à fusionner des fichiers où la modification belaran@995: touche la même portion de code, au sein d'un même fichier. À moins belaran@995: que ces modification ne soient identiques, ceci aboutira à un belaran@995: conflit, et vous devrez décider comment réconcilier wilk@1006: les différentes modifications dans un ensemble cohérent. belaran@995: belaran@997:
andre@1016: Modifications en conflit dans un document belaran@995: belaran@995: belaran@995: XXX ajoute texte belaran@995: belaran@995:
belaran@995: belaran@995: La figure belaran@995: illustre un cas de modifications conflictuelles dans un document. Nous belaran@995: avons commencé avec une version simple de ce fichier, puis nous avons belaran@995: ajouté des modifications, pendant que quelqu'un d'autre modifiait le même belaran@995: texte. Notre tâche dans la résolution du conflit est de décider à quoi le belaran@995: fichier devrait ressembler. belaran@995: belaran@995: Mercurial n'a pas de mécanisme interne pour gérer belaran@995: les conflits. À la place, il exécute un programme externe appelé belaran@995: hgmerge. Il s'agit d'un script shell qui est wilk@1006: compris avec Mercurial, vous pouvez le modifier si vous voulez. belaran@995: Ce qu'il fait par défaut est d'essayer de trouver un des différents belaran@995: outils de fusion qui seront probablement installés sur le système. wilk@1006: Il commence par les outils totalement automatiques, et s'ils belaran@995: échouent (parce que la résolution du conflit nécessite une wilk@1006: intervention humaine) ou s'ils sont absents, le script tente belaran@995: d'exécuter certains outils graphiques de fusion. belaran@995: belaran@995: Il est aussi possible de demander à Mercurial d'exécuter belaran@995: un autre programme ou un autre script en définissant la variable belaran@995: d'environnement HGMERGE avec le nom belaran@995: du programme de votre choix. belaran@995: belaran@995: belaran@995: Utiliser un outil graphique de fusion belaran@995: belaran@995: Mon outil de fusion préféré est belaran@995: kdiff3, que j'utilise ici pour illustrer les belaran@995: fonctionnalités classiques des outils graphiques de fusion. Vous pouvez belaran@995: voir une capture d'écran de l'utilisation de kdiff3 belaran@995: dans la figure . Cet outil wilk@1006: effectue une fusion three-way, car il y a wilk@1006: trois différentes versions du fichier qui nous intéressent. Le fichier wilk@1006: découpe la partie supérieure de la fenêtre en trois panneaux : belaran@995: wilk@1006: À gauche on trouve la version de belaran@995: base du fichier, soit la plus récente version belaran@995: des deux versions qu'on souhaite fusionner. belaran@995: Au centre, il y a notre belaran@995: version du fichier, avec le contenu que nous avons modifié. belaran@995: Sur la droite, on trouve belaran@995: leur version du fichier, celui qui contient la wilk@1006: révision que nous souhaitons intégrer. belaran@995: belaran@995: Dans le panneau en dessous, on trouve le belaran@995: résultat actuel de notre fusion. Notre tâche andre@1016: consiste donc à remplacer tous les textes en rouge, belaran@995: qui indiquent des conflits non résolus, avec une fusion manuelle et belaran@995: pertinente de notre version et de la leur. belaran@995: belaran@995: andre@1016: Les quatre panneaux sont accrochés ensemble, belaran@995: si nous déroulons les ascenseurs verticalement ou horizontalement dans chacun belaran@995: d'entre eux, les autres sont mis à jour avec la section correspondante dans leurs belaran@995: fichiers respectifs. belaran@995: belaran@995:
belaran@995: Utiliser <command>kdiff3</command> pour fusionner les belaran@995: différentes version d'un fichier. belaran@995: belaran@995: belaran@995: belaran@995: belaran@995: XXX ajoute texte belaran@995: belaran@995: belaran@995:
belaran@995: belaran@995: Pour chaque portion de fichier posant problème, nous belaran@995: pouvons choisir de résoudre le conflit en utilisant une combinaison de andre@1016: touches depuis la version de base, la nôtre, ou la leur. Nous pouvons belaran@995: aussi éditer manuellement les fichiers à tout moment, si c'est nécessaire. belaran@995: belaran@995: Il y a beaucoup d'outils de wilk@1006: fusion disponibles, bien trop pour parler de tous ici. Leurs andre@1016: disponibilités varient selon les plateformes ainsi que leurs wilk@1006: avantages et inconvénients. La plupart sont optimisés pour wilk@1006: la fusion de fichier contenant un texte plat, certains sont spécialisés belaran@995: dans un format de fichier précis (généralement XML). belaran@995:
belaran@995: belaran@995: belaran@995: Un exemple concret belaran@995: belaran@995: Dans cet exemple, nous allons reproduire la belaran@995: modification de l'historique du fichier de la figure ci-dessus. Commençons par créer belaran@995: un dépôt avec une version de base de notre document. belaran@995: belaran@995: &interaction.tour-merge-conflict.wife; belaran@995: wilk@1006: Créons un clone de ce dépôt et effectuons une belaran@995: modification dans le fichier. belaran@995: belaran@995: &interaction.tour-merge-conflict.cousin; belaran@995: belaran@995: Et un autre clone, pour simuler que quelqu'un d'autre effectue une belaran@995: modification sur le fichier. (Ceci pour suggérer qu'il n'est pas rare belaran@995: de devoir effectuer des fusions (merges) avec vos propres travaux quand belaran@995: vous isolez les tâches dans des dépôts distincts. En effet, vous belaran@995: aurez alors à trouver et résoudre certains conflits). belaran@995: belaran@995: &interaction.tour-merge-conflict.son; belaran@995: belaran@995: Maintenant que ces deux versions différentes du même fichier sont belaran@995: créées, nous allons configurer l'environnement de manière appropriée pour belaran@995: exécuter notre fusion (merge). belaran@995: belaran@995: &interaction.tour-merge-conflict.pull; belaran@995: belaran@995: Dans cette exemple, je n'utiliserais pas la commande Mercurial wilk@1006: habituelle hgmerge pour effectuer la belaran@995: fusion (merge), car il me faudrait abandonner ce joli petit exemple automatisé belaran@995: pour utiliser un outil graphique. À la place, je vais définir la belaran@995: variable d'environnement HGMERGE pour indiquer à belaran@995: Mercurial d'utiliser la commande non-interactive merge. wilk@1006: Cette dernière est comprise dans de nombreux systèmes à la Unix. belaran@995: Si vous exécutez cet exemple depuis votre ordinateur, ne vous andre@1016: occupez pas de définir HGMERGE. andre@1016: andre@1016: belaran@995: belaran@995: &interaction.tour-merge-conflict.merge; belaran@995: belaran@995: belaran@995: Parce que merge ne peut pas résoudre belaran@995: les modifications conflictuelles, il laisse des marqueurs de belaran@995: différences à l'intérieur du fichier qui a des conflits, andre@1016: indiquant clairement quelles lignes sont en conflit, et si elles belaran@995: viennent de notre fichier ou du fichier externe. belaran@995: belaran@995: belaran@995: Mercurial peut distinguer, à la manière dont la belaran@995: commande merge se termine, qu'elle n'a pas été belaran@995: capable d'effectuer la fusion (merge), alors il nous indique que nous belaran@995: devons effectuer de nouveau cette opération. Ceci peut être très utile belaran@995: si, par exemple, nous exécutons un outil graphique de fusion et que belaran@995: nous le quittons sans nous rendre compte qu'il reste des conflits ou belaran@995: simplement par erreur. belaran@995: belaran@995: Si la fusion (merge) automatique ou manuelle échoue, belaran@995: il n'y a rien pour nous empêcher de corriger le tir en andre@1016: modifiant nous-même les fichiers, et enfin effectuer le commit du belaran@995: fichier: belaran@995: belaran@995: &interaction.tour-merge-conflict.commit; belaran@995: belaran@995: andre@1016: Où est la commande <command>hg resolve</command> ? belaran@995: belaran@995: La commande hg resolve a été andre@1016: introduite dans la version 1.1 de Mercurial, qui a été publiée en belaran@995: décembre 2008. Si vous utilisez une version plus anciennne de belaran@995: Mercurial (exécutez la command hg version pour en wilk@1006: avoir le cœur net), cette commande ne sera pas disponible. Si votre belaran@995: version de Mercurial est plus ancienne que la 1.1, vous devriez très wilk@1006: fortement considérer une mise à jour vers une version plus récente avant belaran@995: d'essayer de régler des fusions complexes. belaran@995: belaran@995: belaran@995:
belaran@995: belaran@995: wilk@1006: Simplification de la séquence <quote>pull-merge-commit</quote> belaran@995: belaran@995: La procédure pour effectuer la fusion indiquée belaran@995: ci-dessus est simple, mais requiert le lancement de trois commandes à la belaran@995: suite. belaran@995: belaran@995: hg pull -u belaran@995: hg merge belaran@995: hg commit -m 'Merged remote changes' belaran@995: wilk@1006: Lors du commit final, vous devez également saisir un belaran@995: message, qui aura vraisemblablement assez peu d'intérêt. belaran@995: belaran@995: Il serait assez sympathique de pouvoir réduire le belaran@995: nombre d'opérations nécessaire, si possible. De fait Mercurial est wilk@1006: fournit avec une extension appelée fetch belaran@995: qui fait justement cela. belaran@995: wilk@1006: Mercurial fournit un mécanisme d'extension flexible qui permet à chacun belaran@995: d'étendre ces fonctionnalités, tout en conservant le cœur de Mercurial wilk@1006: léger et facile à utiliser. Certaines extensions ajoutent de nouvelles belaran@995: commandes que vous pouvez utiliser en ligne de commande, alors que wilk@1006: d'autres travaillent en coulisse, par exemple en ajoutant des belaran@995: possibilités au serveur. belaran@995: belaran@995: L'extension fetch belaran@995: ajoute une nouvelle commande nommée, sans surprise, hg fetch. Cette extension consiste en une belaran@995: combinaison de hg pull, hg update et hg belaran@995: merge. Elle commence par récupérer les modifications d'un belaran@995: autre dépôt dans le dépôt courant. Si elle trouve que les wilk@1006: modifications ajoutent une nouvelle head, elle effectue un merge, wilk@1006: et ensuite commit le résultat du merge avec un message généré wilk@1006: automatiquement. Si aucune head n'a été ajouté, elle met à jour le wilk@1006: répertoire de travail au niveau de la nouvelle révision tip. belaran@995: belaran@995: Activer l'extension fetch est facile. Modifiez votre fichier .hgrc, et soit allez à la section extensions soit créez une section belaran@995: extensions. Ensuite ajoutez wilk@1006: une ligne qui consiste simplement en fetch =. belaran@995: belaran@995: [extensions] belaran@995: fetch = belaran@995: belaran@995: (Normalement, sur la partie droite de belaran@995: = devrait apparaître le chemin de belaran@995: l'extension, mais étant donné que l'extension fetch fait partie de la distribution standard, belaran@995: Mercurial sait où la trouver.) belaran@995: belaran@995: belaran@995: belaran@995: belaran@995: Renommer, copier, et fusionner (merge) belaran@995: belaran@995: En cours de la vie d'un projet, nous allons souvent belaran@995: vouloir changer la disposition de ses fichiers et de ses répertoires. belaran@995: Ceci peut être aussi simple que de changer le nom d'un seul fichier, andre@1016: et aussi compliqué que de restructurer une hiérarchie entière de fichiers belaran@995: au sein du projet. belaran@995: belaran@995: Mercurial permet de faire ce genre de modification de belaran@995: manière fluide, à condition de l'informer de ce que nous faisons. Si wilk@1006: vous voulez renommer un ficher, vous devriez utiliser la commande belaran@995: hg rename wilk@1006: Si vous êtes un utilisateur d'Unix, vous serez content wilk@1006: de savoir que la commande hg rename belaran@995: peut être abrégée en hg mv. belaran@995: pour changer son nom, ainsi Mercurial peut ensuite prendre andre@1016: la bonne décision, plus tard, en cas de fusion (merge). belaran@995: wilk@1006: Nous étudierons, en détail, l'utilisation de ces commandes wilk@1006: dans le chapitre . belaran@995: belaran@964:
belaran@964: belaran@964: