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@391: % TODO traduje "worked" como "real" jerojasro@391: \subsection{Un ejemplo real} jerojasro@391: jerojasro@391: En este ejemplo, reproduciremos el historial de modificaciones al jerojasro@391: fichero de la figura~\ref{fig:tour-merge:conflict} mostrada jerojasro@391: anteriormente. Empecemos creando un repositorio con la versión base jerojasro@391: de nuestro documento. jerojasro@336: \interaction{tour-merge-conflict.wife} jerojasro@391: Clonaremos el repositorio y haremos un cambio al fichero. jerojasro@336: \interaction{tour-merge-conflict.cousin} jerojasro@391: Y haremos otro clon, para simular a alguien más haciendo un cambio al jerojasro@391: mismo fichero. (Esto introduce la idea de que no es tan inusual hacer jerojasro@391: fusiones consigo mismo, cuando usted aísla tareas en repositorios jerojasro@391: separados, y de hecho encuentra conflictos al hacerlo.) jerojasro@336: \interaction{tour-merge-conflict.son} jerojasro@391: Ahora que tenemos dos versiones diferentes de nuestro fichero, jerojasro@391: crearemos un entorno adecuado para hacer la fusión. jerojasro@336: \interaction{tour-merge-conflict.pull} jerojasro@336: jerojasro@391: En este ejemplo, no usaré el comando normal de Mercurial para hacer la jerojasro@391: fusión (\command{hgmerge}), porque lanzaría mi linda herramienta jerojasro@391: automatizada para correr ejemplos dentro de una interfaz gráfica de jerojasro@391: usuario. En vez de eso, definiré la variable de entorno jerojasro@391: \envar{HGMERGE} para indicarle a Mercurial que use el comando jerojasro@391: \command{merge}. Este comando forma parte de la instalación base de jerojasro@391: muchos sistemas Unix y similares. Si usted está ejecutando este jerojasro@391: ejemplo en su computador, no se moleste en definir \envar{HGMERGE}. jerojasro@336: \interaction{tour-merge-conflict.merge} jerojasro@391: Debido a que \command{merge} no puede resolver los conflictos que jerojasro@391: aparecen, él deja \emph{marcadores de fusión} en el fichero con jerojasro@391: conflictos, indicando si provienen de nuestra versión o de la de jerojasro@391: ellos. jerojasro@391: jerojasro@391: Mercurial puede saber ---por el código de salida del comando jerojasro@391: \command{merge}--- que no fue posible hacer la fusión exitosamente, jerojasro@391: así que nos indica qué comandos debemos ejecutar si queremos rehacer jerojasro@391: la fusión. Esto puede ser útil si, por ejemplo, estamos ejecutando una jerojasro@391: herramienta gráfica de fusión y salimos de ella porque nos confundimos jerojasro@391: o cometimos un error. jerojasro@391: jerojasro@391: Si la fusión ---automática o manual--- falla, no hay nada que nos jerojasro@391: impida ``arreglar'' los ficheros afectados por nosotros mismos, y jerojasro@391: consignar los resultados de nuestra fusión: jerojasro@391: % TODO este mercurial no tiene el comando resolve. Revisar si sigue jerojasro@391: % siendo necesario jerojasro@336: \interaction{tour-merge-conflict.commit} jerojasro@336: jerojasro@392: \section{Simplificar el ciclo jalar-fusionar-consignar} jerojasro@336: \label{sec:tour-merge:fetch} jerojasro@336: jerojasro@392: El proceso de fusionar cambios delineado anteriomente es directo, pero jerojasro@392: requiere la ejecución de tres comandos en sucesión. jerojasro@336: \begin{codesample2} jerojasro@336: hg pull jerojasro@336: hg merge jerojasro@392: hg commit -m 'Fusionados cambios remotos' jerojasro@336: \end{codesample2} jerojasro@392: En la consignación final usted debe proveer un mensaje adecuado, que jerojasro@392: casi siempre es un fragmento de texto ``de relleno'' carente de valor jerojasro@392: particular. jerojasro@392: jerojasro@392: Sería agradable reducir la cantidad de pasos necesarios, si fuera jerojasro@392: posible. De hecho, Mercurial es distribuido junto con una extensión jerojasro@392: llamada \hgext{fetch}\ndt{Descargar, traer.} que hace precisamente jerojasro@392: esto. jerojasro@392: jerojasro@392: Mercurial cuenta con un mecanismo de extensión flexible que le permite jerojasro@392: % TODO lets people => permite a usuarios jerojasro@392: a sus usuarios extender su funcionalidad, manteniendo el núcleo de jerojasro@392: Mercurial pequeño y fácil de manejar. Algunas extensiones añaden jerojasro@392: nuevos comandos que usted puede usar desde la línea de comandos, jerojasro@392: mientras que otros funcionan ``tras bambalinas'', por ejemplo, jerojasro@392: añadiendo funcionalidad al servidor. jerojasro@392: jerojasro@392: La extensión \hgext{fetch} añade un comando llamado, no jerojasro@392: sorpresivamente, \hgcmd{fetch}. Esta extensión actúa como una jerojasro@392: combinación de \hgcmd{pull}, \hgcmd{update} y \hgcmd{merge}. Empieza jerojasro@392: jalando cambios de otro repositorio al repositorio actual. Si jerojasro@392: encuentra que los cambios añaden un nuevo frente en el repositorio jerojasro@392: actual, inicia una fusión, y luego consigna el resultado de la misma jerojasro@392: con un mensaje generado automáticamente. Si no se añadieron nuevos jerojasro@392: frentes, actualiza el directorio de trabajo con el nuevo conjunto de jerojasro@392: cambios de punta. jerojasro@392: jerojasro@392: Activar la extensión \hgext{fetch} es fácil. Edite su jerojasro@392: \sfilename{.hgrc}, y vaya a (o cree) la sección jerojasro@392: \rcsection{extensions}. Luego añada una línea que diga simplemente jerojasro@392: ``\Verb+fetch +''. jerojasro@336: \begin{codesample2} jerojasro@336: [extensions] jerojasro@336: fetch = jerojasro@336: \end{codesample2} jerojasro@392: (Normalmente, a la derecha del ``\texttt{=}'' debería aparecer la jerojasro@392: ubicación de la extensión, pero como el comando \hgext{fetch} es parte jerojasro@392: de la distribución estándar, Mercurial sabe dónde buscarla.) jerojasro@336: jerojasro@336: %%% Local Variables: jerojasro@336: %%% mode: latex jerojasro@336: %%% TeX-master: "00book" jerojasro@336: %%% End: