belaran@953: \chapter{Un rapide tour de Mercurial: fusionner les travaux} bos@95: \label{chap:tour-merge} bos@94: Wilk@961: Nous avons maintenant étudié comment cloner un dépôt, effectuer belaran@953: des changements dedans, et récupérer ou transférer depuis un belaran@953: autre dépôt. La prochaine étape est donc de \emph{fusionner} les belaran@953: modifications de différents dépôts. belaran@953: belaran@953: \section{Fusionner différents travaux} %%%TODO: better translation belaran@953: %%% for 'Merging streams of work' ? belaran@953: La fusion\footnote{NdT: Je garde fusion mais le jargon professionnel belaran@954: emploiera généralement le terme \textit{merge}.} est un aspect Wilk@961: fondamental lorsqu'on travaille avec un gestionnaire de source belaran@954: distribué. bos@94: \begin{itemize} belaran@953: \item Alice et Bob ont chacun une copie personnelle du dépôt d'un belaran@953: projet sur lequel ils collaborent. Alice corrige un \textit{bug} belaran@953: dans son dépôt, et Bob ajoute une nouvelle fonctionnalité dans le belaran@953: sien. Ils veulent un dépôt partagé avec à la fois le correctif du belaran@953: \textit{bug} et la nouvelle fonctionnalité. belaran@953: \item Je travaille régulièrement sur plusieurs tâches différentes sur Wilk@961: un seul projet en même temps, chacune isolée dans son propre dépôt. belaran@953: Travailler ainsi signifie que je dois régulièrement fusionner une belaran@953: partie de mon code avec celui des autres. bos@94: \end{itemize} bos@94: Wilk@961: Parce que la fusion est une opération si commune à réaliser, belaran@954: Mercurial la rend facile. Étudions ensemble le déroulement des opérations. Wilk@961: Nous commencerons encore par faire un clone d'un autre dépôt (vous voyez Wilk@961: que l'on fait ça tout le temps ?) puis nous ferons quelques modifications belaran@953: dessus. bos@94: \interaction{tour.merge.clone} belaran@953: Nous devrions avoir maintenant deux copies de \filename{hello.c} avec belaran@953: des contenus différents. Les historiques de ces deux dépôts ont aussi belaran@953: divergés, comme illustré dans la figure~\ref{fig:tour-merge:sep-repos}. belaran@953: bos@94: \interaction{tour.merge.cat} bos@94: bos@99: \begin{figure}[ht] bos@99: \centering bos@99: \grafix{tour-merge-sep-repos} belaran@953: \caption{Historiques récent divergents des dépôts \dirname{my-hello} belaran@953: et \dirname{my-new-hello}} bos@99: \label{fig:tour-merge:sep-repos} bos@99: \end{figure} bos@99: belaran@953: Nous savons déjà que récupérer les modifications depuis notre dépôt belaran@953: \dirname{my-hello} n'aura aucun effet sur l'espace de travail. belaran@953: bos@94: \interaction{tour.merge.pull} belaran@953: belaran@954: Néanmoins, la commande \hgcmd{pull} nous indique quelque chose au belaran@953: sujet des ``heads''. belaran@953: belaran@953: \subsection{\textit{Head changesets}} %%%TODO: Hard (too?) to translate belaran@953: belaran@953: Une \textit{head}\footnote{NdT: Je garde \textit{head} que j'accorde Wilk@961: au féminin comme la coutume orale l'a imposée.} est un \textit{changeset} belaran@953: sans descendants, ou enfants, comme on les désigne parfois. La révision belaran@953: \textit{tip} est une \textit{head}, car la dernière révision dans un dépôt belaran@953: n'a aucun enfant, mais il est important de noter qu'un dépôt peut contenir belaran@953: plus d'une \textit{head}. bos@99: bos@99: \begin{figure}[ht] bos@99: \centering bos@99: \grafix{tour-merge-pull} Wilk@961: \caption{Contenu d'un dépôt après avoir transféré le contenu du dépôt belaran@953: \dirname{my-hello} dans le dépôt \dirname{my-new-hello}} bos@99: \label{fig:tour-merge:pull} bos@99: \end{figure} bos@99: belaran@953: Dans la figure~\ref{fig:tour-merge:pull}, vous pouvez constater l'effet belaran@953: d'un \textit{pull} depuis le dépôt \dirname{my-hello} dans le dépôt belaran@953: \dirname{my-new-hello}. L'historique qui était déjà présent dans le dépôt belaran@953: \dirname{my-new-hello} reste intact, mais une nouvelle révision a été belaran@953: ajoutée. En vous reportant à la figure~\ref{fig:tour-merge:sep-repos}, belaran@953: vous pouvez voir que le \textit{\emph{changeset ID}} reste le même dans belaran@953: le nouveau dépôt, mais que le \emph{numéro de révision} reste le même. Wilk@961: (Ceci est un parfait exemple du fait il n'est fiable d'utiliser les belaran@953: numéro de révision lorsque l'on discute d'un \textit{changeset}.) Vous Wilk@961: pouvez voir les \texit{heads} présentes dans le dépôt en utilisant la belaran@953: commande \hgcmd{heads}. bos@94: \interaction{tour.merge.heads} bos@102: belaran@953: \subsection{Effectuer la fusion} belaran@953: belaran@953: Que se passe-t-il quand vous essayez d'utiliser la commande \hgcmd{update} belaran@953: pour mettre à jour votre espace de travail au nouveau \textit{tip}. bos@94: \interaction{tour.merge.update} belaran@953: Mercurial nous prévient que la commande \hgcmd{update} n'effectuera pas belaran@953: la fusion, il ne veut pas mettre à jour l'espace de travail quand il belaran@953: estime que nous pourrions avoir besoin d'une fusion, à moins de lui belaran@953: forcer la main. À la place, il faut utiliser la commande \hgcmd{merge} belaran@953: pour fusionner les deux \textit{heads}. bos@94: \interaction{tour.merge.merge} bos@100: bos@100: \begin{figure}[ht] bos@100: \centering bos@100: \grafix{tour-merge-merge} belaran@953: \caption{Espace de travail et dépôt lors d'une fusion, et dans le belaran@953: \textit{commit} qui suit.} bos@100: \label{fig:tour-merge:merge} bos@100: \end{figure} bos@100: Wilk@961: Ceci met à jour l'espace de travail de manière à ce qu'il contienne belaran@954: les modifications des \emph{deux} \textit{heads}, ce qui apparaît dans belaran@953: les sorties de la commande \hgcmd{parents} et le contenu de belaran@953: \filename{hello.c}. bos@94: \interaction{tour.merge.parents} bos@102: belaran@954: \subsection{Effectuer le \textit{commit} du résultat de la fusion} belaran@954: Wilk@961: Dès l'instant où vous avez effectué une fusion, \hgcmd{parents} vous Wilk@961: affichera deux parents, avant que vous n'exécutiez la commande belaran@954: \hgcmd{commit} sur le résultat de la fusion. bos@94: \interaction{tour.merge.commit} Wilk@961: Nous avons maintenant un nouveau \textit{tip}, remarquez qu'il contient belaran@953: \emph{à la fois} nos anciennes \textit{heads} et leurs parents. Ce sont Wilk@961: les mêmes révisions que nous avions affichées avec la commande belaran@953: \hgcmd{parents}. belaran@953: bos@94: \interaction{tour.merge.tip} belaran@953: Dans la figure~\ref{fig:tour-merge:merge}, vous pouvez voir une représentation belaran@953: de ce qui se passe dans l'espace de travail pendant la fusion, et comment ceci belaran@953: affecte le dépôt lors du \textit{commit}. Pendant la fusion, l'espace de travail, belaran@953: qui a deux \texit{changesets} comme parents, voit ces derniers devenir le parent belaran@953: d'un nouveau \textit{changeset}. belaran@953: belaran@953: \section{Fusionner les modifications en conflit} belaran@953: Wilk@961: La plupart des fusions sont assez simples à réaliser, mais parfois Wilk@961: vous vous retrouverez à fusionner des fichiers où la modification touche belaran@953: la même portion de code, au sein d'un même fichier. À moins que ces belaran@953: modification ne soient identiques, ceci aboutira à un \emph{conflit}, belaran@954: et vous devrez décider comment réconcilier les différentes modifications belaran@953: dans un tout cohérent. bos@103: bos@103: \begin{figure}[ht] bos@103: \centering bos@103: \grafix{tour-merge-conflict} belaran@953: \caption{Modifications conflictuelles dans un document} bos@103: \label{fig:tour-merge:conflict} bos@103: \end{figure} bos@103: belaran@953: La figure~\ref{fig:tour-merge:conflict} illustre un cas de modifications belaran@953: conflictuelles dans un document. Nous avons commencé avec une version simple Wilk@961: de ce fichier, puis nous avons ajouté des modifications, pendant que Wilk@961: quelqu'un d'autre modifiait le même texte. Notre tâche dans la résolution belaran@953: du conflit est de décider à quoi le fichier devrait ressembler. belaran@953: belaran@953: Mercurial n'a pas de mécanisme interne pour gérer les conflits. belaran@954: À la place, il exécute un programme externe appelé \command{hgmerge}. belaran@953: Il s'agit d'un script shell qui est embarqué par Mercurial, vous belaran@953: pouvez le modifier si vous le voulez. Ce qu'il fait par défaut est belaran@953: d'essayer de trouver un des différents outils de fusion qui seront Wilk@961: probablement installés sur le système. Il commence par les outils Wilk@961: totalement automatiques, et s'ils échouent (parce que la résolution Wilk@961: du conflit nécessite une intervention humaine) ou s'ils sont absents, Wilk@961: le script tentera d'exécuter certains outils graphiques de fusion. belaran@953: belaran@953: Il est aussi possible de demander à Mercurial d'exécuter un autre belaran@953: programme ou un autre script au lieu de la commande \command{hgmerge}, belaran@954: en définissant la variable d'environnement \envar{HGMERGE} avec le nom belaran@953: du programme de votre choix. belaran@953: belaran@953: \subsection{Utiliser un outil graphique de fusion} belaran@953: belaran@953: Mon outil de fusion préféré est \command{kdiff3}, que j'utilise ici Wilk@961: pour illustrer les fonctionnalités classiques des outils graphiques belaran@953: de fusion. Vous pouvez voir une capture d'écran de l'utilisation de belaran@953: \command{kdiff3} dans la figure~\ref{fig:tour-merge:kdiff3}. Cet outil belaran@953: effectue une \emph{fusion \textit{three-way}}, car il y a trois différentes Wilk@961: versions du fichier qui nous intéressent. Le fichier découpe la partie belaran@953: supérieure de la fenêtre en trois panneaux: belaran@953: bos@103: \begin{itemize} Wilk@961: \item A gauche on a la version de \emph{base} du fichier, soit ~la plus Wilk@961: récente version des deux versions que l'on souhaite fusionner. belaran@953: \item Au centre, il y a ``notre'' version du fichier, avec le contenu belaran@953: que nous avons modifié. Wilk@961: \item Sur la droite, on trouve ``leur'' version du fichier, celui qui Wilk@961: contient le \textit{changeset} que nous souhaitons intégrer. bos@103: \end{itemize} belaran@953: belaran@953: Dans le panneau en dessous, on trouve le \emph{résultat} actuel de notre Wilk@961: fusion. Notre tâche consiste donc à remplacement tous les textes en rouges, Wilk@961: qui indiquent des conflits non résolus, avec une fusion manuelle et pertinente belaran@953: de ``notre'' version et de la ``leur''. belaran@953: Wilk@961: Tous les quatre panneaux sont \emph{accrochés ensemble}, si nous déroulons belaran@953: les ascenseurs verticalement ou horizontalement dans chacun d'entre eux, les Wilk@961: autres sont mis à jour avec la section correspondante dans leurs fichiers Wilk@961: respectifs. bos@103: bos@103: \begin{figure}[ht] bos@103: \centering bos@103: \grafix{kdiff3} Wilk@961: \caption{Utilisation de \command{kdiff3} pour fusionner différentes versions belaran@953: d'un fichier.} bos@103: \label{fig:tour-merge:kdiff3} bos@103: \end{figure} bos@103: belaran@953: Pour chaque portion de fichier posant problème, nous pouvons choisir Wilk@961: de résoudre le conflit en utilisant une combinaison belaran@953: de texte depuis la version de base, la notre, ou la leur. Nous pouvons Wilk@961: aussi éditer manuellement les fichiers à tout moment, si c'est belaran@953: nécessaire. belaran@953: belaran@953: Il y a \emph{beaucoup} d'outils de fusion disponibles, bien trop pour belaran@954: en parler de tous ici. Leurs disponibilités varient selon les plate formes belaran@954: ainsi que leurs avantages et inconvénients. La plupart sont optimisé pour belaran@953: la fusion de fichier contenant un texte plat, certains sont spécialisé belaran@954: dans un format de fichier précis (généralement XML). belaran@954: Wilk@961: \subsection{Un exemple concret} belaran@954: belaran@954: Dans cet exemple, nous allons reproduire la modification de l'historique belaran@954: du fichier de la figure~\ref{fig:tour-merge:conflict} ci dessus. Commençons belaran@954: par créer un dépôt avec une version de base de notre document. belaran@954: bos@103: \interaction{tour-merge-conflict.wife} belaran@954: Créons un clone de ce dépôt et faisons une modification dans le fichier. bos@103: \interaction{tour-merge-conflict.cousin} Wilk@961: Et un autre clone, pour simuler que quelqu'un d'autre effectue une belaran@954: modification sur le fichier. (Ceci pour suggérer qu'il n'est pas rare belaran@954: de devoir effectuer des \textit{merge} avec vos propres travaux quand Wilk@961: vous isolez les tâches dans des dépôts distincts. En effet, vous belaran@954: aurez alors à trouver et résoudre certains conflits). bos@103: \interaction{tour-merge-conflict.son} belaran@954: Maintenant que ces deux versions différentes du même fichier sont Wilk@961: créées, nous allons configurer l'environnement de manière appropriée pour Wilk@961: exécuter notre \textit{merge}. bos@103: \interaction{tour-merge-conflict.pull} bos@103: belaran@954: Dans cette exemple, je n'utiliserais pas la commande Mercurial belaran@954: habituelle \command{hgmerge} pour effectuer le \textit{merge}, belaran@954: car il me faudrait abandonner ce joli petit exemple automatisé belaran@954: pour utiliser un outil graphique. À la place, je vais définir belaran@954: la variable d'environnement \envar{HGMERGE} pour indiquer à belaran@954: Mercurial d'utiliser la commande non-interactive \command{merge}. Wilk@961: Cette dernière est embarquée par de nombreux systèmes ``à la Unix''. Wilk@961: Si vous exécutez cet exemple depuis votre ordinateur, ne vous belaran@954: occupez pas de définir \envar{HGMERGE}. bos@103: \interaction{tour-merge-conflict.merge} belaran@954: Parce que \command{merge} ne peut pas résoudre les modifications belaran@954: conflictuelles, il laisse des \emph{marqueurs de différences} belaran@954: \footnote{NdT: Oui, je traduis \textit{merge markers} par un sens belaran@954: inverse en Français, mais je pense vraiment que c'est plus clair belaran@954: comme ça...} à l'intérieur du fichier qui a des conflits, indiquant belaran@954: clairement quelles lignes sont en conflits, et si elles viennent de belaran@954: notre fichier ou du fichier externe. belaran@954: belaran@954: Mercurial peut distinguer, à la manière dont la commande \command{merge} belaran@954: se termine, qu'elle n'a pas été capable d'effectuer le \textit{merge}, Wilk@961: alors il nous indique que nous devons effectuer de nouveau cette belaran@954: opération. Ceci peut être très utile si, par exemple, nous exécutons un belaran@954: outil graphique de fusion et que nous le quittons sans se rendre compte belaran@954: qu'il reste des conflits ou simplement par erreur. belaran@954: belaran@954: Si le \textit{merge} automatique ou manuel échoue, il n'y a rien pour belaran@954: nous empêcher de ``corriger le tir'' en modifiant nous même les fichiers, belaran@954: et enfin effectuer le \textit{commit} du fichier: bos@103: \interaction{tour-merge-conflict.commit} bos@103: Wilk@961: \section{Simplification de la séquence pull-merge-commit} bos@224: \label{sec:tour-merge:fetch} bos@102: Wilk@961: La procédure pour effectuer la fusion indiquée ci-dessus est simple, Wilk@961: mais requiert le lancement de trois commandes à la suite. bos@102: \begin{codesample2} bos@102: hg pull bos@102: hg merge bos@102: hg commit -m 'Merged remote changes' bos@102: \end{codesample2} Wilk@961: Wilk@961: Lors du \textit{commit} final, vous devez également saisir un message, Wilk@961: qui aura vraisemblablement assez peu d'intérêt. Wilk@961: Wilk@961: Il serait assez sympathique de pouvoir réduire le nombre d'opérations Wilk@961: nécessaire, si possible. De fait Mercurial est fourni avec une Wilk@961: extension appelé \hgext{fetch} qui fait justement cela. Wilk@961: Wilk@961: Mercurial fourni un mécanisme d'extension flexible qui permet à chacun Wilk@961: d'étendre ces fonctionnalités, tout en conservant le cœur de Mercurial Wilk@961: léger et facile à utiliser. Certains extensions ajoutent de nouvelles Wilk@961: commandes que vous pouvez utiliser en ligne de commande, alors que Wilk@961: d'autres travaillent ``en coulisse,'' par exemple en ajoutant des Wilk@961: possibilités au serveur. Wilk@961: Wilk@961: L'extension \hgext{fetch} ajoute une nouvelle commande nommée, sans Wilk@961: surprise, \hgcmd{fetch}. Cette extension résulte en une combinaison Wilk@961: de \hgcmd{pull}, \hgcmd{update} and \hgcmd{merge}. Elle commence par Wilk@961: récupérer les modifications d'un autre dépôt dans le dépôt courant. Wilk@961: Si elle trouve que les modifications ajoutent une nouvelle \textit{head}, Wilk@961: elle effectue un \textit{merge}, et ensuite \texit{commit} le résultat Wilk@961: du \textit{merge} avec un message généré automatiquement. Si aucune Wilk@961: \textit{head} n'ont été ajouté, elle met à jour le répertoire de travail Wilk@961: au niveau du nouveau \textit{changeset} \textit{tip}. bos@102: bos@102: Enabling the \hgext{fetch} extension is easy. Edit your bos@102: \sfilename{.hgrc}, and either go to the \rcsection{extensions} section bos@102: or create an \rcsection{extensions} section. Then add a line that bos@102: simply reads ``\Verb+fetch +''. Wilk@961: Wilk@961: Activer l'extension \hgext{fetch} est facile. Modifiez votre \sfilename{.hgrc}, Wilk@961: et soit allez à la section \rcsection{extensions} soit créer une Wilk@961: section \rcsection{extensions}. Ensuite ajoutez une ligne qui consiste Wilk@961: simplement en ``\Verb+fetch =''. Wilk@961: bos@102: \begin{codesample2} bos@102: [extensions] bos@102: fetch = bos@102: \end{codesample2} Wilk@961: Wilk@961: (Normalement, sur la partie droite de ``\texttt{=}'' devrait apparaître Wilk@961: le chemin de l'extension, mais étant donné que l'extension \hgext{fetch} Wilk@961: fait partie de la distribution standard, Mercurial sait où la trouver.) bos@102: bos@84: %%% Local Variables: bos@84: %%% mode: latex bos@84: %%% TeX-master: "00book" bos@84: %%% End: