jerojasro@381: \chapter{Una gira de Mercurial: fusionar trabajo} jerojasro@336: \label{chap:tour-merge} jerojasro@336: jerojasro@383: Hasta ahora hemos cubierto cómo clonar un repositorio, hacer cambios, jerojasro@383: y jalar o empujar dichos cambios de un repositorio a otro. Nuestro jerojasro@383: siguiente paso es \emph{fusionar} cambios de repositorios separados. jerojasro@383: jerojasro@383: % TODO cambié streams por líneas. check please jerojasro@383: \section{Fusionar líneas de trabajo} jerojasro@383: jerojasro@383: Fusionar es una parte fundamental de trabajar con una herramienta jerojasro@383: de control distribuido de versiones. jerojasro@336: \begin{itemize} jerojasro@383: \item Alicia y Roberto tienen cada uno una copia personal del jerojasro@383: repositorio de un proyecto en el que están trabajando. Alicia jerojasro@383: arregla un fallo en su repositorio; Roberto añade una nueva jerojasro@383: característica en el suyo. Ambos desean que el repositorio jerojasro@383: compartido contenga el arreglo del fallo y la nueva jerojasro@383: característica. jerojasro@383: \item Frecuentemente trabajo en varias tareas diferentes en un mismo jerojasro@383: proyecto al mismo tiempo, cada una aislada convenientemente de las jerojasro@383: otras en su propio repositorio. Trabajar de esta manera significa jerojasro@383: que a menudo debo fusionar una parte de mi propio trabajo con jerojasro@383: otra. jerojasro@336: \end{itemize} jerojasro@336: jerojasro@383: Como fusionar es una operación tan necesaria y común, Mercurial la jerojasro@383: facilita. Revisemos el proceso. Empezaremos clonando (otro) jerojasro@383: % TODO poner interrogante de apertura jerojasro@383: repositorio (ve lo seguido que aparecen?) y haciendo un cambio en él. jerojasro@336: \interaction{tour.merge.clone} jerojasro@383: Ahora deberíamos tener dos copias de \filename{hello.c} con contenidos jerojasro@383: diferentes. El historial de los dos repositorios diverge ahora, como jerojasro@383: se ilustra en la figura~\ref{fig:tour-merge:sep-repos}. jerojasro@336: \interaction{tour.merge.cat} jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \centering jerojasro@336: \grafix{tour-merge-sep-repos} jerojasro@383: \caption{Historial reciente divergente de los repositorios jerojasro@383: \dirname{my-hello} y \dirname{my-new-hello}} jerojasro@336: \label{fig:tour-merge:sep-repos} jerojasro@336: \end{figure} jerojasro@336: jerojasro@383: Ya sabemos que jalar los cambios desde nuestro repositorio jerojasro@383: \dirname{my-hello} no tendrá efecto en el directorio de trabajo. jerojasro@336: \interaction{tour.merge.pull} jerojasro@384: Sin embargo, el comando \hgcmd{pull} dice algo acerca de jerojasro@384: ``frentes''\ndt{El autor se refiere a \emph{heads} aquí.}. jerojasro@384: jerojasro@384: \subsection{Conjuntos de cambios de frentes} jerojasro@384: jerojasro@384: Un frente es un cambio que no tiene descendientes, o hijos, como jerojasro@384: también se les conoce. La revisión de punta es, por tanto, un frente, jerojasro@384: porque la revisión más reciente en un repositorio no tiene ningún jerojasro@384: % TODO cambio en la redacción de la frase, pero espero que conserve el jerojasro@384: % sentido. Querido human@, apruebe o corrija :D jerojasro@384: hijo. Sin embargo, un repositorio puede contener más de un frente. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \centering jerojasro@336: \grafix{tour-merge-pull} jerojasro@384: \caption{Contenidos del repositorio después de jalar jerojasro@384: \dirname{my-hello} a \dirname{my-new-hello}} jerojasro@336: \label{fig:tour-merge:pull} jerojasro@336: \end{figure} jerojasro@336: jerojasro@384: En la figura~\ref{fig:tour-merge:pull} usted puede ver el efecto que jerojasro@384: tiene jalar los cambios de \dirname{my-hello} a \dirname{my-new-hello}. jerojasro@384: El historial que ya existía en \dirname{my-new-hello} se mantiene jerojasro@384: intacto, pero fue añadida una nueva revisión. Refiriéndonos a la jerojasro@384: figura~\ref{fig:tour-merge:sep-repos}, podemos ver que el \emph{ID del jerojasro@384: conjunto de cambios} se mantiene igual en el nuevo repositorio, pero jerojasro@384: el \emph{número de revisión} ha cambiado. (Incidentalmente, éste es un jerojasro@384: buen ejemplo de porqué no es seguro usar números de revisión cuando se jerojasro@384: habla de conjuntos de cambios). Podemos ver los frentes en un jerojasro@384: repositorio usando el comando \hgcmd{heads}\ndt{Frentes.}. jerojasro@336: \interaction{tour.merge.heads} jerojasro@336: jerojasro@385: \subsection{Hacer la fusión} jerojasro@385: jerojasro@385: % TODO poner interrogante de apertura jerojasro@385: Qué pasa si tratamos de usar el comando usual, \hgcmd{update}, para jerojasro@385: actualizar el nuevo frente? jerojasro@336: \interaction{tour.merge.update} jerojasro@385: Mercurial nos indica que el comando \hgcmd{update} no hará la fusión; jerojasro@385: no actualizará el directorio de trabajo cuando considera que lo que jerojasro@385: deseamos hacer es una fusión, a menos que lo obliguemos a hacerlo. jerojasro@385: En vez de \hgcmd{update}, usamos el comando \hgcmd{merge} para hacer jerojasro@385: la fusión entre los dos frentes. jerojasro@336: \interaction{tour.merge.merge} jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \centering jerojasro@336: \grafix{tour-merge-merge} jerojasro@385: \caption{Directorio de trabajo y repositorio durante la fusión, y jerojasro@385: consignación consecuente} jerojasro@336: \label{fig:tour-merge:merge} jerojasro@336: \end{figure} jerojasro@336: jerojasro@385: Esto actualiza el directorio de trabajo, de tal forma que contenga los jerojasro@385: cambios de \emph{ambos} frentes, lo que se ve reflejado tanto en la jerojasro@385: salida de \hgcmd{parents} como en los contenidos de \filename{hello.c}. jerojasro@336: \interaction{tour.merge.parents} jerojasro@336: jerojasro@385: \subsection{Consignar los resultados de la fusión} jerojasro@336: jerojasro@390: Siempre que hacemos una fusión, \hgcmd{parents} mostrará dos padres jerojasro@390: hasta que consignemos (\hgcmd{commit}) los resultados de la fusión. jerojasro@336: \interaction{tour.merge.commit} jerojasro@390: Ahora tenemos una nueva revisión de punta; note que tiene \emph{los jerojasro@390: dos} frentes anteriores como sus padres. Estos son las mismas jerojasro@390: revisiones que mostró previamente el comando \hgcmd{parents}. jerojasro@336: \interaction{tour.merge.tip} jerojasro@390: En la figura~\ref{fig:tour-merge:merge} usted puede apreciar una jerojasro@390: representación de lo que pasa en el directorio de trabajo durante la jerojasro@390: fusión cuando se hace la consignación. Durante la fusión, el jerojasro@390: directorio de trabajo tiene dos conjuntos de cambios como sus padres, jerojasro@390: y éstos se vuelven los padres del nuevo conjunto de cambios. jerojasro@390: jerojasro@390: \section{Fusionar cambios con conflictos} jerojasro@390: jerojasro@390: La mayoría de las fusiones son algo simple, pero a veces usted se jerojasro@390: encontrará fusionando cambios donde más de uno de ellos afecta las jerojasro@390: mismas secciones de los mismos ficheros. A menos que ambas jerojasro@390: modificaciones sean idénticas, el resultado es un \emph{conflicto}, en jerojasro@390: donde usted debe decidir cómo reconciliar ambos cambios y producir un jerojasro@390: resultado coherente. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \centering jerojasro@336: \grafix{tour-merge-conflict} jerojasro@390: \caption{Cambios con conflictos a un documento} jerojasro@336: \label{fig:tour-merge:conflict} jerojasro@336: \end{figure} jerojasro@336: jerojasro@390: La figura~\ref{fig:tour-merge:conflict} ilustra un ejemplo con dos jerojasro@390: cambios generando conflictos en un documento. Empezamos con una sola jerojasro@390: versión de el fichero; luego hicimos algunos cambios; mientras tanto, jerojasro@390: alguien más hizo cambios diferentes en el mismo texto. Lo que debemos jerojasro@390: hacer para resolver el conflicto causado por ambos cambios es decidir jerojasro@390: cómo debe quedar finalmente el fichero. jerojasro@390: jerojasro@390: Mercurial no tiene ninguna utilidad integrada para manejar conflictos. jerojasro@390: En vez de eso, ejecuta un programa externo llamado \command{hgmerge}. jerojasro@390: Es un guión de línea de comandos que es instalado junto con Mercurial; jerojasro@390: usted puede modificarlo para que se comporte como usted lo desee. Por jerojasro@390: defecto, lo que hace es tratar de encontrar una de varias herramientas jerojasro@390: para fusionar que es probable que estén instaladas en su sistema. jerojasro@390: Primero se intenta con unas herramientas para fusionar cambios jerojasro@390: automáticamente; si esto no tiene éxito (porque la fusión demanda jerojasro@390: una guía humana) o dichas herramientas no están presentes, el guión jerojasro@390: intenta con herramientas gráficas para fusionar. jerojasro@390: jerojasro@390: También es posible hacer que Mercurial ejecute otro programa o guión jerojasro@390: en vez de \command{hgmerge}, definiendo la variable de entorno jerojasro@390: \envar{HGMERGE} con el nombre del programa de su preferencia. jerojasro@390: jerojasro@390: \subsection{Usar una herramienta gráfica para fusión} jerojasro@390: jerojasro@390: Mi herramienta favorita para hacer fusiones es \command{kdiff3}, y la jerojasro@390: usaré para describir las características comunes de las herramientas jerojasro@390: gráficas para hacer fusiones. Puede ver una captura de pantalla de jerojasro@390: \command{kdiff3} ejecutándose, en la jerojasro@390: figura~\ref{fig:tour-merge:kdiff3}. El tipo de fusión que la jerojasro@390: herramienta hace se conoce como \emph{fusión de tres vías}, porque hay jerojasro@390: tres versiones diferentes del archivo en que estamos interesados. jerojasro@390: Debido a esto la herramienta divide la parte superior de la ventana en jerojasro@390: tres paneles. jerojasro@336: \begin{itemize} jerojasro@390: \item A la izquierda está la revisión \emph{base} del fichero, p.ej.~la jerojasro@390: versión más reciente de la que descienden las dos versiones que jerojasro@390: estamos tratando de fusionar. jerojasro@390: \item En la mitad está ``nuestra'' versión del fichero, con las jerojasro@390: modificaciones que hemos hecho. jerojasro@390: \item A la derecha está la versión del fichero de ``ellos'', la que jerojasro@390: forma parte del conjunto de cambios que estamos tratando de jerojasro@390: fusionar. jerojasro@336: \end{itemize} jerojasro@390: En el panel inferior se encuentra el \emph{resultado} actual de la jerojasro@390: fusión. Nuestra tarea es reemplazar todo el texto rojo, que muestra jerojasro@390: los conflictos sin resolver, con una fusión adecuada de ``nuestra'' jerojasro@390: versión del fichero y la de ``ellos''. jerojasro@390: jerojasro@390: Los cuatro paneles están \emph{enlazados}; si avanzamos vertical o jerojasro@390: horizontalmente en cualquiera de ellos, los otros son actualizados jerojasro@390: para mostrar las secciones correspondientes del fichero que tengan jerojasro@390: asociado. jerojasro@390: jerojasro@390: \begin{figure}[ht] jerojasro@390: \centering jerojasro@390: \grafix[width=\textwidth]{kdiff3} jerojasro@390: \caption{Usando \command{kdiff3} para fusionar versiones de un jerojasro@390: fichero} jerojasro@336: \label{fig:tour-merge:kdiff3} jerojasro@336: \end{figure} jerojasro@336: jerojasro@390: En cada conflicto del fichero podemos escoger resolverlo usando jerojasro@390: cualquier combinación del texto de la revisión base, la nuestra, o la jerojasro@390: de ellos. También podemos editar manualmente el fichero en que queda jerojasro@390: la fusión, si es necesario hacer cambios adicionales. jerojasro@390: jerojasro@390: Hay \emph{muchas} herramientas para fusionar ficheros disponibles. Se jerojasro@390: diferencian en las plataformas para las que están disponibles, y en jerojasro@390: sus fortalezas y debilidades particulares. La mayoría están afinadas jerojasro@390: para fusionar texto plano, mientras que otras están pensadas para jerojasro@390: formatos de archivos especializados (generalmente XML). jerojasro@336: jerojasro@336: \subsection{A worked example} jerojasro@336: jerojasro@336: In this example, we will reproduce the file modification history of jerojasro@336: figure~\ref{fig:tour-merge:conflict} above. Let's begin by creating a jerojasro@336: repository with a base version of our document. jerojasro@336: \interaction{tour-merge-conflict.wife} jerojasro@336: We'll clone the repository and make a change to the file. jerojasro@336: \interaction{tour-merge-conflict.cousin} jerojasro@336: And another clone, to simulate someone else making a change to the jerojasro@336: file. (This hints at the idea that it's not all that unusual to merge jerojasro@336: with yourself when you isolate tasks in separate repositories, and jerojasro@336: indeed to find and resolve conflicts while doing so.) jerojasro@336: \interaction{tour-merge-conflict.son} jerojasro@336: Having created two different versions of the file, we'll set up an jerojasro@336: environment suitable for running our merge. jerojasro@336: \interaction{tour-merge-conflict.pull} jerojasro@336: jerojasro@336: In this example, I won't use Mercurial's normal \command{hgmerge} jerojasro@336: program to do the merge, because it would drop my nice automated jerojasro@336: example-running tool into a graphical user interface. Instead, I'll jerojasro@336: set \envar{HGMERGE} to tell Mercurial to use the non-interactive jerojasro@336: \command{merge} command. This is bundled with many Unix-like systems. jerojasro@336: If you're following this example on your computer, don't bother jerojasro@336: setting \envar{HGMERGE}. jerojasro@336: \interaction{tour-merge-conflict.merge} jerojasro@336: Because \command{merge} can't resolve the conflicting changes, it jerojasro@336: leaves \emph{merge markers} inside the file that has conflicts, jerojasro@336: indicating which lines have conflicts, and whether they came from our jerojasro@336: version of the file or theirs. jerojasro@336: jerojasro@336: Mercurial can tell from the way \command{merge} exits that it wasn't jerojasro@336: able to merge successfully, so it tells us what commands we'll need to jerojasro@336: run if we want to redo the merging operation. This could be useful jerojasro@336: if, for example, we were running a graphical merge tool and quit jerojasro@336: because we were confused or realised we had made a mistake. jerojasro@336: jerojasro@336: If automatic or manual merges fail, there's nothing to prevent us from jerojasro@336: ``fixing up'' the affected files ourselves, and committing the results jerojasro@336: of our merge: jerojasro@336: \interaction{tour-merge-conflict.commit} jerojasro@336: jerojasro@336: \section{Simplifying the pull-merge-commit sequence} jerojasro@336: \label{sec:tour-merge:fetch} jerojasro@336: jerojasro@336: The process of merging changes as outlined above is straightforward, jerojasro@336: but requires running three commands in sequence. jerojasro@336: \begin{codesample2} jerojasro@336: hg pull jerojasro@336: hg merge jerojasro@336: hg commit -m 'Merged remote changes' jerojasro@336: \end{codesample2} jerojasro@336: In the case of the final commit, you also need to enter a commit jerojasro@336: message, which is almost always going to be a piece of uninteresting jerojasro@336: ``boilerplate'' text. jerojasro@336: jerojasro@336: It would be nice to reduce the number of steps needed, if this were jerojasro@336: possible. Indeed, Mercurial is distributed with an extension called jerojasro@336: \hgext{fetch} that does just this. jerojasro@336: jerojasro@336: Mercurial provides a flexible extension mechanism that lets people jerojasro@336: extend its functionality, while keeping the core of Mercurial small jerojasro@336: and easy to deal with. Some extensions add new commands that you can jerojasro@336: use from the command line, while others work ``behind the scenes,'' jerojasro@336: for example adding capabilities to the server. jerojasro@336: jerojasro@336: The \hgext{fetch} extension adds a new command called, not jerojasro@336: surprisingly, \hgcmd{fetch}. This extension acts as a combination of jerojasro@336: \hgcmd{pull}, \hgcmd{update} and \hgcmd{merge}. It begins by pulling jerojasro@336: changes from another repository into the current repository. If it jerojasro@336: finds that the changes added a new head to the repository, it begins a jerojasro@336: merge, then commits the result of the merge with an jerojasro@336: automatically-generated commit message. If no new heads were added, jerojasro@336: it updates the working directory to the new tip changeset. jerojasro@336: jerojasro@336: Enabling the \hgext{fetch} extension is easy. Edit your jerojasro@336: \sfilename{.hgrc}, and either go to the \rcsection{extensions} section jerojasro@336: or create an \rcsection{extensions} section. Then add a line that jerojasro@336: simply reads ``\Verb+fetch +''. jerojasro@336: \begin{codesample2} jerojasro@336: [extensions] jerojasro@336: fetch = jerojasro@336: \end{codesample2} jerojasro@336: (Normally, on the right-hand side of the ``\texttt{=}'' would appear jerojasro@336: the location of the extension, but since the \hgext{fetch} extension jerojasro@336: is in the standard distribution, Mercurial knows where to search for jerojasro@336: it.) jerojasro@336: jerojasro@336: %%% Local Variables: jerojasro@336: %%% mode: latex jerojasro@336: %%% TeX-master: "00book" jerojasro@336: %%% End: