view es/tour-merge.tex @ 484:0abd3d78172e

finished translation of a figure translated a few paragraphs of tour-merge
author Javier Rojas <jerojasro@devnull.li>
date Sun, 02 Nov 2008 13:54:42 -0500
parents 7f1572c365d2
children c3a867bba34a
line wrap: on
line source

\chapter{Una gira de Mercurial: fusionar trabajo}
\label{chap:tour-merge}

Hasta ahora hemos cubierto cómo clonar un repositorio, hacer cambios,
y jalar o empujar dichos cambios de un repositorio a otro. Nuestro
siguiente paso es \emph{fusionar} cambios de repositorios separados.

% TODO cambié streams por líneas. check please
\section{Fusionar líneas de trabajo}

Fusionar es una parte fundamental de trabajar con una herramienta 
de control distribuido de versiones.
\begin{itemize}
\item Alicia y Roberto tienen cada uno una copia personal del
    repositorio de un proyecto en el que están trabajando. Alicia
    arregla un fallo en su repositorio; Roberto añade una nueva
    característica en el suyo. Ambos desean que el repositorio
    compartido contenga el arreglo del fallo y la nueva
    característica.
\item Frecuentemente trabajo en varias tareas diferentes en un mismo
    proyecto al mismo tiempo, cada una aislada convenientemente de las
    otras en su propio repositorio. Trabajar de esta manera significa
    que a menudo debo fusionar una parte de mi propio trabajo con
    otra.
\end{itemize}

Como fusionar es una operación tan necesaria y común, Mercurial la
facilita. Revisemos el proceso. Empezaremos clonando (otro)
% TODO poner interrogante de apertura
repositorio (ve lo seguido que aparecen?) y haciendo un cambio en él.
\interaction{tour.merge.clone}
Ahora deberíamos tener dos copias de \filename{hello.c} con contenidos
diferentes.  El historial de los dos repositorios diverge ahora, como
se ilustra en la figura~\ref{fig:tour-merge:sep-repos}.
\interaction{tour.merge.cat}

\begin{figure}[ht]
  \centering
  \grafix{tour-merge-sep-repos}
  \caption{Historial reciente divergente de los repositorios
      \dirname{my-hello} y \dirname{my-new-hello}}
  \label{fig:tour-merge:sep-repos}
\end{figure}

Ya sabemos que jalar los cambios desde nuestro repositorio
\dirname{my-hello} no tendrá efecto en el directorio de trabajo.
\interaction{tour.merge.pull}
Sin embargo, el comando \hgcmd{pull} dice algo acerca de
``frentes''\ndt{El autor se refiere a \emph{heads} aquí.}.  

\subsection{Conjuntos de cambios de frentes}

Un frente es un cambio que no tiene descendientes, o hijos, como
también se les conoce. La revisión de punta es, por tanto, un frente,
porque la revisión más reciente en un repositorio no tiene ningún
% TODO cambio en la redacción de la frase, pero espero que conserve el
% sentido. Querido human@, apruebe o corrija :D
hijo. Sin embargo, un repositorio puede contener más de un frente.

\begin{figure}[ht]
  \centering
  \grafix{tour-merge-pull}
  \caption{Contenidos del repositorio después de jalar
      \dirname{my-hello} a \dirname{my-new-hello}}
  \label{fig:tour-merge:pull}
\end{figure}

En la figura~\ref{fig:tour-merge:pull} usted puede ver el efecto que
tiene jalar los cambios de \dirname{my-hello} a \dirname{my-new-hello}.
El historial que ya existía en \dirname{my-new-hello} se mantiene
intacto, pero fue añadida una nueva revisión. Refiriéndonos a la
figura~\ref{fig:tour-merge:sep-repos}, podemos ver que el \emph{ID del
conjunto de cambios} se mantiene igual en el nuevo repositorio, pero
el \emph{número de revisión} ha cambiado.  (Incidentalmente, éste es un
buen ejemplo de porqué no es seguro usar números de revisión cuando se
habla de conjuntos de cambios).  Podemos ver los frentes en un
repositorio usando el comando \hgcmd{heads}\ndt{Frentes.}.
\interaction{tour.merge.heads}

\subsection{Hacer la fusión}

% TODO poner interrogante de apertura
Qué pasa si tratamos de usar el comando usual, \hgcmd{update}, para
actualizar el nuevo frente?
\interaction{tour.merge.update}
Mercurial nos indica que el comando \hgcmd{update} no hará la fusión;
no actualizará el directorio de trabajo cuando considera que lo que
deseamos hacer es una fusión, a menos que lo obliguemos a hacerlo.
En vez de \hgcmd{update}, usamos el comando \hgcmd{merge} para hacer
la fusión entre los dos frentes.
\interaction{tour.merge.merge}

\begin{figure}[ht]
  \centering
  \grafix{tour-merge-merge}
  \caption{Directorio de trabajo y repositorio durante la fusión, y
  consignación consecuente}
  \label{fig:tour-merge:merge}
\end{figure}

Esto actualiza el directorio de trabajo, de tal forma que contenga los
cambios de \emph{ambos} frentes, lo que se ve reflejado tanto en la
salida de \hgcmd{parents} como en los contenidos de \filename{hello.c}.
\interaction{tour.merge.parents}

\subsection{Consignar los resultados de la fusión}

Whenever we've done a merge, \hgcmd{parents} will display two parents
until we \hgcmd{commit} the results of the merge.
\interaction{tour.merge.commit}
We now have a new tip revision; notice that it has \emph{both} of
our former heads as its parents.  These are the same revisions that
were previously displayed by \hgcmd{parents}.
\interaction{tour.merge.tip}
In figure~\ref{fig:tour-merge:merge}, you can see a representation of
what happens to the working directory during the merge, and how this
affects the repository when the commit happens.  During the merge, the
working directory has two parent changesets, and these become the
parents of the new changeset.

\section{Merging conflicting changes}

Most merges are simple affairs, but sometimes you'll find yourself
merging changes where each modifies the same portions of the same
files.  Unless both modifications are identical, this results in a
\emph{conflict}, where you have to decide how to reconcile the
different changes into something coherent.

\begin{figure}[ht]
  \centering
  \grafix{tour-merge-conflict}
  \caption{Conflicting changes to a document}
  \label{fig:tour-merge:conflict}
\end{figure}

Figure~\ref{fig:tour-merge:conflict} illustrates an instance of two
conflicting changes to a document.  We started with a single version
of the file; then we made some changes; while someone else made
different changes to the same text.  Our task in resolving the
conflicting changes is to decide what the file should look like.

Mercurial doesn't have a built-in facility for handling conflicts.
Instead, it runs an external program called \command{hgmerge}.  This
is a shell script that is bundled with Mercurial; you can change it to
behave however you please.  What it does by default is try to find one
of several different merging tools that are likely to be installed on
your system.  It first tries a few fully automatic merging tools; if
these don't succeed (because the resolution process requires human
guidance) or aren't present, the script tries a few different
graphical merging tools.

It's also possible to get Mercurial to run another program or script
instead of \command{hgmerge}, by setting the \envar{HGMERGE}
environment variable to the name of your preferred program.

\subsection{Using a graphical merge tool}

My preferred graphical merge tool is \command{kdiff3}, which I'll use
to describe the features that are common to graphical file merging
tools.  You can see a screenshot of \command{kdiff3} in action in
figure~\ref{fig:tour-merge:kdiff3}.  The kind of merge it is
performing is called a \emph{three-way merge}, because there are three
different versions of the file of interest to us.  The tool thus
splits the upper portion of the window into three panes:
\begin{itemize}
\item At the left is the \emph{base} version of the file, i.e.~the
  most recent version from which the two versions we're trying to
  merge are descended.
\item In the middle is ``our'' version of the file, with the contents
  that we modified.
\item On the right is ``their'' version of the file, the one that
  from the changeset that we're trying to merge with.
\end{itemize}
In the pane below these is the current \emph{result} of the merge.
Our task is to replace all of the red text, which indicates unresolved
conflicts, with some sensible merger of the ``ours'' and ``theirs''
versions of the file.

All four of these panes are \emph{locked together}; if we scroll
vertically or horizontally in any of them, the others are updated to
display the corresponding sections of their respective files.

\begin{figure}[ht]
  \centering
  \grafix{kdiff3}
  \caption{Using \command{kdiff3} to merge versions of a file}
  \label{fig:tour-merge:kdiff3}
\end{figure}

For each conflicting portion of the file, we can choose to resolve
the conflict using some combination of text from the base version,
ours, or theirs.  We can also manually edit the merged file at any
time, in case we need to make further modifications.

There are \emph{many} file merging tools available, too many to cover
here.  They vary in which platforms they are available for, and in
their particular strengths and weaknesses.  Most are tuned for merging
files containing plain text, while a few are aimed at specialised file
formats (generally XML).

\subsection{A worked example}

In this example, we will reproduce the file modification history of
figure~\ref{fig:tour-merge:conflict} above.  Let's begin by creating a
repository with a base version of our document.
\interaction{tour-merge-conflict.wife}
We'll clone the repository and make a change to the file.
\interaction{tour-merge-conflict.cousin}
And another clone, to simulate someone else making a change to the
file.  (This hints at the idea that it's not all that unusual to merge
with yourself when you isolate tasks in separate repositories, and
indeed to find and resolve conflicts while doing so.)
\interaction{tour-merge-conflict.son}
Having created two different versions of the file, we'll set up an
environment suitable for running our merge.
\interaction{tour-merge-conflict.pull}

In this example, I won't use Mercurial's normal \command{hgmerge}
program to do the merge, because it would drop my nice automated
example-running tool into a graphical user interface.  Instead, I'll
set \envar{HGMERGE} to tell Mercurial to use the non-interactive
\command{merge} command.  This is bundled with many Unix-like systems.
If you're following this example on your computer, don't bother
setting \envar{HGMERGE}.
\interaction{tour-merge-conflict.merge}
Because \command{merge} can't resolve the conflicting changes, it
leaves \emph{merge markers} inside the file that has conflicts,
indicating which lines have conflicts, and whether they came from our
version of the file or theirs.

Mercurial can tell from the way \command{merge} exits that it wasn't
able to merge successfully, so it tells us what commands we'll need to
run if we want to redo the merging operation.  This could be useful
if, for example, we were running a graphical merge tool and quit
because we were confused or realised we had made a mistake.

If automatic or manual merges fail, there's nothing to prevent us from
``fixing up'' the affected files ourselves, and committing the results
of our merge:
\interaction{tour-merge-conflict.commit}

\section{Simplifying the pull-merge-commit sequence}
\label{sec:tour-merge:fetch}

The process of merging changes as outlined above is straightforward,
but requires running three commands in sequence.
\begin{codesample2}
  hg pull
  hg merge
  hg commit -m 'Merged remote changes'
\end{codesample2}
In the case of the final commit, you also need to enter a commit
message, which is almost always going to be a piece of uninteresting
``boilerplate'' text.

It would be nice to reduce the number of steps needed, if this were
possible.  Indeed, Mercurial is distributed with an extension called
\hgext{fetch} that does just this.

Mercurial provides a flexible extension mechanism that lets people
extend its functionality, while keeping the core of Mercurial small
and easy to deal with.  Some extensions add new commands that you can
use from the command line, while others work ``behind the scenes,''
for example adding capabilities to the server.

The \hgext{fetch} extension adds a new command called, not
surprisingly, \hgcmd{fetch}.  This extension acts as a combination of
\hgcmd{pull}, \hgcmd{update} and \hgcmd{merge}.  It begins by pulling
changes from another repository into the current repository.  If it
finds that the changes added a new head to the repository, it begins a
merge, then commits the result of the merge with an
automatically-generated commit message.  If no new heads were added,
it updates the working directory to the new tip changeset.

Enabling the \hgext{fetch} extension is easy.  Edit your
\sfilename{.hgrc}, and either go to the \rcsection{extensions} section
or create an \rcsection{extensions} section.  Then add a line that
simply reads ``\Verb+fetch +''.
\begin{codesample2}
  [extensions]
  fetch =
\end{codesample2}
(Normally, on the right-hand side of the ``\texttt{=}'' would appear
the location of the extension, but since the \hgext{fetch} extension
is in the standard distribution, Mercurial knows where to search for
it.)

%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "00book"
%%% End: