igor@440: \chapter{Administración de cambios con Colas de Mercurial} jerojasro@336: \label{chap:mq} jerojasro@336: igor@440: \section{El problema de la administración de parches} jerojasro@336: \label{sec:mq:patch-mgmt} jerojasro@336: igor@443: Un escenario frecuente: usted necesita instalar un paquete de software igor@443: desde las fuentes, pero encuentra un fallo que debe arreglar antes de igor@443: poder comenzar a usarlo. Hace sus cambios, y se olvida del paquete igor@443: por un tiempo, unos meses después necesita actualizar a una nueva igor@443: versión del paquete. Si la nueva versión del paquete todavía tiene el igor@443: fallo, debe extraer su arreglo del árbol de fuentes anteriores y igor@443: aplicarlo a la nueva versión. Una tarea tediosa en la cual es fácil igor@443: equivocarse. igor@443: igor@443: Este es un caso simple del problema del ``manejo de parches''. Usted igor@443: tiene un árbol de fuentes del ``mantenedor principal'' que no puede igor@443: cambiar: necesita hacer algunos cambios locales sobre el árbol igor@443: principal; y desearía poder mantener tales cambios separados, de forma igor@443: tal que pueda aplicarlos a versiones más nuevas del árbol principal. igor@443: igor@443: El problema de administración de parches surge en muchas situaciones. igor@443: Probablemente la más visible es cuando un usuario de un proyecto de igor@443: software de fuentes abiertas contribuye con un arreglo de un fallo o igor@443: una nueva característica a los mantenedores del proyecto en la forma igor@443: de un parche. igor@443: igor@443: Aquellos que distribuyen sistemas operativos que incluyen programas igor@443: abiertos usualmente requieren hacer cambios en los paquetes que igor@443: distribuyen de tal forma que se armen apropiadamente en sus ambientes. igor@443: igor@443: Cuando hay pocos cambios por mantener, es muy sencillo administrar un igor@443: solo parche con los programas estándar \command{diff} y jerojasro@480: \command{patch} (ver la sección~\ref{sec:mq:patch} para ver cómo igor@443: emplear tales herramientas). Cuando la cantidad de cambios comienza a igor@443: crecer, tiene sentido mantener parches como ``porciones de trabajo'' igor@443: individual, de forma que cada cambio contiene solamente un arreglo de jerojasro@480: un fallo (el parche puede modificar varios ficheros, pero está igor@443: ``haciendo una sola cosa''), y puede tener cierta cantidad de tales igor@443: parches para diferentes fallos y cambios locales. En esta situación, igor@443: si envía un parche que arregla un fallo a los mantenedores principales igor@443: de un paquete y ellos incluyen su arreglo en una publicación igor@443: posterior, puede deshacerse de tal parche cuando se actualice a la igor@443: nueva versión. igor@443: igor@443: Mantener un solo parche frente a un árbol principal es algo tedioso y igor@443: es fácil equivocarse, pero no es difícil. Aunque, la complejidad del igor@443: problema crece rápidamente a medida que la cantidad de parches que igor@443: tiene que mantener crece. Con más que una pequeña cantidad de igor@443: cambios, entender cuáles ha aplicado se convierte de algo desordenado igor@443: a algo avasallante. igor@443: igor@443: Afortunadamente Mercurial provee una extensión poderos: Colas de jerojasro@480: Mercurial (o simplemente ``MQ''), que simplifica en gran medida el igor@443: problema de administración de parches. igor@443: igor@443: \section{La prehistoria de las Colas de Mercurial} jerojasro@336: \label{sec:mq:history} jerojasro@336: igor@443: A finales de los 90s, muchos desarrolladores del núcleo de Linux igor@443: comenzaron a mantener ``series de parches'' que modificaron el igor@443: comportamiento del núcleo de Linux. Algunos se enfocaban en igor@443: estabilidad, otros en aumentar las características, y otros un poco igor@443: más especulativos. igor@443: igor@443: Los tamaños de las series de parches crecieron rápidamente. En el igor@443: 2002, Andrew Morton publicó algunos guiones de línea de órdenes que igor@443: estuvo usando para automatizar la tarea de administrar su cola de igor@443: parches. Andrew usó exitósamente tales guiones para administrar jerojasro@480: centenas (a veces millares) de parches en el núcleo de Linux. igor@443: igor@443: \subsection{Trabajar parches con quilt} jerojasro@336: \label{sec:mq:quilt} jerojasro@336: igor@443: A comienzos del 2003, Andreas Gruenbacher y Martin Quinson tomaron la igor@443: aproximación de los guiones de Andrew y publicaron una herramienta igor@443: llamada igor@443: ``patchwork quilt''~\cite{web:quilt}, o simplemente ``quilt'' igor@443: (ver~\cite{gruenbacher:2005} el paper que lo describe). Dado que igor@443: quilt automatizaba sustancialmente la administración de parches, fue igor@443: adoptado en gran medida por desarrolladores de programas abiertos. igor@443: igor@443: Quilt maneja una \emph{pila de parches} sobre un árbol de directorios. igor@443: Para comenzar, usted le indica a quilt que administre un árbol de igor@449: directorios, le indica qué ficheros manejar; Este almacena los nombres igor@449: y los contenidos de estos ficheros. Para arreglar un fallo, usted jerojasro@480: crea un nuevo parche (con una sola orden), edita los ficheros que está igor@443: arreglando y ``refresca'' el parche. igor@443: igor@443: El paso de refresco hace que quilt revise el árbol de directorios; igor@443: actualiza el parche con todos los cambios que usted haya hecho. Puede igor@443: crear otro parche sobre el primero, que hará seguimiento de los igor@443: cambios requeridos para modificar el árbol desde ``el árbol con un igor@443: parch aplicado'' a un ``árbol con dos parches aplicados''. igor@443: igor@443: Usted puede \emph{elegir} qué cambios desea aplicar al árbol. Si igor@443: ``pop''\ndt{saca} un parche, los cambios hechos por tal parchve igor@443: desapareceŕan del árbol de directorios. Quilt recuerda qué parches ha igor@443: sacado, para que pueda ``introducirlos''\ndt{push} posteriormente, así el igor@443: árbol de directorios se restaurará con las modificaciones que vienen igor@443: del parche. Lo más importante es que puede ejecutar la orden igor@443: ``refresh'' en cualquier momento, y el último parche será igor@443: actualizado. Esto significa que puede, en cualquier momento, cambiar igor@443: qué parches serán aplicados y qué modificaciones hacen ellos. igor@443: igor@443: Quilt no tiene nada que ver con herramientas de control de versiones, igor@443: y puede trabajar bien sobre un conjunto de fuentes que viene de un igor@449: fichero comprimido y empaquetado o una copia de trabajo de Subversion. igor@443: igor@443: \subsection{Pasar de trabajo con parches con Quilt hacia Colas de Mercurial} jerojasro@336: \label{sec:mq:quilt-mq} jerojasro@336: igor@443: A mediados de 2005, Chris Mason tomó las características de quilt y igor@443: escribió una extensión que llamó Colas de Mercurial\ndt{Mercurial igor@443: Queues}, que proporcionó un comportamiento a Mercurial al estilo igor@443: quilt. igor@443: igor@443: La diferencia clave entre quilt y MQ es que quilt no sabe nada acerca igor@443: del sistema de control de revisiones, mientras que MQ está igor@443: \emph{integrado} con Mercurial. Cada parche que usted introduce se igor@443: representa como un conjunto de cambios en Mercurial. Si sustrae un igor@443: parche, el conjunto de cambios desaparece.\ndt{introduce originalmente es igor@443: push y pop es sustraer en este contexto, usaremos el original en inglés igor@443: cuando encontremos que facilita la comprensión} igor@443: igor@443: Dado que quilt no se preocupa por las herramientas de control de igor@443: revisiones, continúa siendo una porción de software tremendamente útil igor@443: para aquellas situaciones en las cuales no puede usar Mercurial y MQ. igor@443: igor@443: \section{La gran ventaja de MQ} igor@443: igor@443: No puedo sobreestimar el valor que MQ ofrece en la unificación de igor@443: parches y el control de revisiones. igor@443: igor@443: La principal razón por la cual los parches han persistido en el mundo igor@443: del software libre y de fuentes abiertas--a pesar de la creciente igor@443: disponibilidad de herramientas poderosas de control de revisiones-- es igor@443: la \emph{agilidad} que ofrecen. igor@443: igor@443: Las herramientas tradicionales de control de revisiones llevan un igor@443: registro permanente e irreversible de todo lo que usted hace. A pesar igor@443: de que esto tiene gran valor, también es bastante sutil. Si requiere igor@443: realizar un experimento ((((wild-eyed)))), debe ser cuidadoso en cómo igor@443: lo hace, o puede dejar trazas innecesarias--o peor aún, igor@443: desconcertantes o desestabilizantes--- de los pasos y errores en el igor@443: registro de revisiones de forma permanente. igor@443: igor@443: En contraste, con la cohesión de MQ con el control de revisiones igor@443: distribuidos y los parches, resulta más sencillo aislar su trabajo. jerojasro@517: Sus parches viven encima del historial de revisiones normales, y igor@443: puede hacer que ellos desaparezcan o reaparezcan cuando lo desee. Si igor@443: no le gusta un parche, puede desecharlo. Si un parche no satisface igor@443: todo lo que usted desea, puede arreglarlo---tantas veces como lo igor@443: requiera, hasta que lo haya refinado lo suficiente hacia sus igor@443: expectativas. igor@443: igor@443: Por ejemplo, la integración de parches con el control de revisiones igor@443: hace que el entender los parches y revisar sus efectos---y sus igor@443: interacciones con el código en el cuál están enlazados--- sea igor@443: \emph{mucho} más sencillo. Dado que todo parche que se aplique tiene igor@443: un conjunto de cambios asociado, puede usar igor@443: \hgcmdargs{log}{\emph{filename}} para ver qué conjuntos de cambios y igor@443: parches afectaron un fichero. Puede usar la orden \hgext{bisect} para igor@443: hacer una búsqueda binaria sobre todos los conjuntos de cambios y igor@443: parches aplicados para ver dónde se introdujo un fallo o dónde fue igor@443: arreglado. Puede usar la orden \hgcmd{annotate} para ver qué igor@443: conjuntos de cambios o parches modificaron una línea particular de un igor@449: fichero fuente. Y mucho más. igor@443: igor@443: \section{Entender los parches} jerojasro@336: \label{sec:mq:patch} jerojasro@336: igor@443: Dado que MQ no esconde su naturaleza parche-céntrica, es muy útil para igor@443: entender de qué se tratan los parches, y un poco acerca de las igor@443: herramientas que trabajan con ellos. igor@443: igor@443: La orden de Unix tradicional \command{diff} compara dos ficheros, e igor@443: imprime una lista de diferencias de sus líneas. La orden igor@443: \command{patch} entiende esas diferencias como \emph{modificaciones} igor@443: para construir un fichero. Vea en la figura~\ref{ex:mq:diff} un igor@443: ejemplo sencillo de tales órdenes en acción. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.dodiff.diff} igor@443: \caption{Uso sencillo de las órdenes \command{diff} y \command{patch}} jerojasro@336: \label{ex:mq:diff} jerojasro@336: \end{figure} jerojasro@336: igor@443: El tipo de fichero que \command{diff} genera (y que \command{patch} igor@443: toma como entrada) se llama un ``parche'' o un ``diff''; no hay igor@443: diferencia entre un parche y un diff. (Usaremos el término ``parche'', igor@443: dado que es el que más comunmente se usa.) igor@443: igor@443: Un parche puede comenzar con un texto arbitrario; la orden \command{patch} igor@443: ignora este texto, pero MQ lo usa como el mensaje de consignación igor@443: cuando se crean conjuntos de cambios. Para encontrar el inicio del igor@443: contenido de un parche, la orden \command{patch} busca la primera igor@443: línea que comience con la cadena ``\texttt{diff~-}''. igor@443: igor@443: MQ trabaja con diffs \emph{unificados} (\command{patch} acepta varios igor@443: formatos de diff adicionales, pero MQ no). Un diff unificado contiene igor@443: dos clases de encabezados. El \emph{encabezado de fichero} describe igor@443: el fichero que se está modificando; contiene el nombre del fichero a igor@443: modificar. Cuando \command{patch} ve un nuevo encabezado de fichero, igor@443: busca un fichero con ese nombre para modificarlo. igor@443: igor@443: Después del encabezaado vienen varios \emph{trozos}. Cada trozo igor@443: comienza con un encabezado; que identifica el rango de líneas del igor@443: fichero que el trozo debe modificar. Después del encabezado, un trozo jerojasro@480: comienza y termina con unas pocas líneas (usualmente tres) de texto del igor@443: fichero que no han sido modificadas; las cuales llamamos el igor@443: \emph{contexto} del trozo. Si solamente hay una pequeña cantidad de igor@443: contexto entre trozos sucesivos, \command{diff} no imprime un nuevo igor@443: encabezado para el trozo, continua integrando los trozos, con unas igor@443: líneas de contexto entre las modificaciones. igor@443: igor@443: Cada línea de contexto comienza con un caracter de espacio. En el igor@443: trozo, si una línea comienza con ``\texttt{-}'' significa ``elimine igor@443: esta línea'', si la línea comienza con un ``\texttt{+}'' significa igor@443: ``inserte esta línea''. Por ejemplo, una línea que se modifica se igor@443: representa con una línea eliminada y una línea insertada. igor@443: jerojasro@480: Retomaremos aspectos más sutiles acerca de parches posteriormente (en igor@443: la sección~\ref{sec:mq:adv-patch}), pero en el momento usted ya igor@443: debería tener suficiente información para usar MQ. igor@443: igor@443: \section{Comenzar a usar Colas de Mercurial} jerojasro@336: \label{sec:mq:start} jerojasro@336: igor@443: Dado que MQ está implementado como una extensión, debe habilitarla igor@443: explícitamente antes de comenzar a usarla. (No necesita descargar igor@443: nada; MQ viene con la distribución estándar de Mercurial.) Para igor@443: habilitar MQ, edite su fichero \tildefile{.hgrc}, y añada las líneas igor@443: de la figura~\ref{ex:mq:config}. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \begin{codesample4} jerojasro@336: [extensions] jerojasro@336: hgext.mq = jerojasro@336: \end{codesample4} jerojasro@336: \label{ex:mq:config} igor@443: \caption{Líneas a añadir en \tildefile{.hgrc} para habilitar la extensión MQ} igor@443: \end{figure} igor@443: igor@443: Cuando la extensión esté habilitada, aparecerán varios comandos. Para igor@443: verificar que la extensión está trabajando, puede usar \hgcmd{help} igor@443: para ver si la orden \hgxcmd{mq}{qinit} está disponible; vea un igor@443: ejemplo en la figura~\ref{ex:mq:enabled}. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.qinit-help.help} igor@443: \caption{Cómo verificar que MQ está habilitado} jerojasro@336: \label{ex:mq:enabled} jerojasro@336: \end{figure} jerojasro@336: igor@443: Puede usar MQ en \emph{cualquier} repositorio de Mercurial, y sus igor@443: comandos solamente operarán con tal repositorio. Para comenzar, basta jerojasro@480: con preparar el repositorio con la orden \hgxcmd{mq}{qinit} (ver la igor@443: figura~\ref{ex:mq:qinit}). Esta orden crea un directorio vacío igor@443: llamado \sdirname{.hg/patches}, donde MQ mantendrá sus metadatos. Como igor@443: otras ordenes de Mercurial, la orden \hgxcmd{mq}{qinit} no imprime igor@443: nada cuando es exitosa. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qinit} igor@443: \caption{Preparar un repositorio para usar MQ} jerojasro@336: \label{ex:mq:qinit} jerojasro@336: \end{figure} jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qnew} igor@443: \caption{Crear un nuevo parche} jerojasro@336: \label{ex:mq:qnew} jerojasro@336: \end{figure} jerojasro@336: igor@443: \subsection{Crear un nuevo parche} igor@443: igor@443: Para comenzar a trabajar en un nuevo parche use la orden igor@443: \hgxcmd{mq}{qnew}. Esta orden recibe un argumento, el nombre del igor@443: parche a crear. MQ lo usará como el nombre del fichero en el igor@443: directorio \sdirname{.hg/patches}, como puede apreciarlo en la igor@443: figura~\ref{ex:mq:qnew}. igor@443: igor@443: También hay otros dos nuevos ficheros en el directorio igor@443: \sdirname{.hg/patches}: \sfilename{series} y \sfilename{status}. El igor@443: fichero \sfilename{series} lista todos los parches de los cuales MQ igor@443: tiene noticia para este repositorio, con un parche por línea. igor@443: Mercurial usa el fichero \sfilename{status} para mantener registros igor@443: interns; da seguimiento a todos los parches que MQ ha \emph{aplicado} igor@443: en el repositorio. jerojasro@336: jerojasro@336: \begin{note} igor@443: En ciertas ocasiones usted querrá editar el fichero igor@443: \sfilename{series} a mano; por ejemplo, cambiar el orden en que se igor@443: aplican ciertos parches. A pesar de esto, es una mala idea editar igor@443: manualmente el fichero \sfilename{status}, dado que es fácil igor@443: desorientar a MQ acerca de lo que está pasando. jerojasro@336: \end{note} jerojasro@336: igor@443: Una vez que haya creado un nuevo parche, puede editar los ficheros en igor@443: el directorio de trabajo, como lo haría usualmente. Toda las órdenes igor@443: que de a Mercurial, tales como \hgcmd{diff} y \hgcmd{annotate}, igor@443: trabajarán de la misma forma como lo han hecho antes. igor@443: igor@443: \subsection{Refrescar un parche} igor@443: igor@443: Cuando usted llega a un punto en el cual desea guardar su trabajo, use jerojasro@480: la orden \hgxcmd{mq}{qrefresh} (figura~\ref{ex:mq:qnew}) para igor@443: actualizar el parche en el cual está trabajando. Esta orden almacena igor@443: los cambios que haya hecho al directorio actual de trabajo en su igor@443: parche, y almacena el conjunto de cambios correspondiente que contiene igor@443: los cambios. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qrefresh} igor@443: \caption{Refrescar un parche} jerojasro@336: \label{ex:mq:qrefresh} jerojasro@336: \end{figure} jerojasro@336: igor@443: Puede ejecutar la orden \hgxcmd{mq}{qrefresh} tan seguido como quiera, igor@443: y es una buena forma de ``colocar marcas'' a su trabajo. Refresque su igor@443: parche en momentos oportunos; intente un experimento; si el igor@443: experimento no funciona, Use \hgcmd{revert} sobre sus modificaciones igor@443: para volver al refresco anterior. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qrefresh2} igor@443: \caption{Refrescar un parche muchas veces para acumular cambios} jerojasro@336: \label{ex:mq:qrefresh2} jerojasro@336: \end{figure} jerojasro@336: igor@443: \subsection{Aplicar un parche tras otro y dar seguimiento} igor@443: igor@443: Cuando haya terminado de trabajar en un parche, o necesite trabajar en igor@443: otro, puede usar la orden \hgxcmd{mq}{qnew} para crear un nuevo igor@443: parche. Mercurial aplicará este parche sobre su parche anterior. igor@443: Para un ejemplo, ver la figura~\ref{ex:mq:qnew2}. Note que el parche igor@443: contiene los cambios en nuestro parche anterior como parte de su jerojasro@480: contexto (lo verá más claramente en la salida de \hgcmd{annotate}). jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qnew2} igor@443: \caption{Aplicar un parche después del primero} jerojasro@336: \label{ex:mq:qnew2} jerojasro@336: \end{figure} jerojasro@336: igor@443: Hasta ahora, con excepción de \hgxcmd{mq}{qnew} y igor@443: \hgxcmd{mq}{qrefresh}, hemos sido cuidadosos para aplicar únicamente igor@443: órdenes usuaales de Mercurial. De todas maneras, MQ ofrece muchos igor@443: comandos que son más sencillos de usar cuando esté pensando acerca de igor@443: parches, como se puede ver en la figura~\ref{ex:mq:qseries}: jerojasro@336: jerojasro@336: \begin{itemize} igor@443: \item La orden \hgxcmd{mq}{qseries} lista cada parche del cual MQ igor@443: tiene noticia en este repositorio, desde el más antiguo hasta el más jerojasro@480: nuevo (El último \emph{creado}). igor@443: \item La orden \hgxcmd{mq}{qapplied} lista cada parche que MQ haya igor@443: \emph{aplicado} en este repositorio, de nuevo, desde el más antiguo igor@443: hasta el más nuevo (El aplicado más recientemente). jerojasro@336: \end{itemize} jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qseries} igor@443: \caption{Entender la pila de parches con \hgxcmd{mq}{qseries} y jerojasro@336: \hgxcmd{mq}{qapplied}} jerojasro@336: \label{ex:mq:qseries} jerojasro@336: \end{figure} jerojasro@336: igor@443: \subsection{Manipular la pila de parches} igor@443: igor@443: La discusión previa indicó que debe haber una diferencia entre los igor@443: parches ``conocidos'' y ``aplicados'', y efectivamente la hay. MQ igor@443: puede manejar un parche sin que este haya sido aplicado al igor@443: repositorio. igor@443: igor@443: Un parche \emph{aplicado} tiene su correspondiente conjunto de cambios igor@443: en el repositorio, y los efectos del parche y el conjunto de cambios igor@443: son visibles en el directorio de trabajo. Puede deshacer la igor@443: aplicación de un parche con la orden \hgxcmd{mq}{qpop}. MQ igor@443: \emph{sabe acerca de}, o maneja un parche sustraído, pero el parche ya igor@443: no tendrá un conjunto de cambios correspondientes en el repositorio, y igor@443: el directorio de trabajo no contendrá los cambios hechos por el igor@443: parche. La figura~\ref{fig:mq:stack} ilustra la diferencia entre igor@443: parches aplicados y seguidos. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \centering jerojasro@336: \grafix{mq-stack} igor@443: \caption{Parches aplicados y no aplicados en la pila de parches de MQ} jerojasro@336: \label{fig:mq:stack} jerojasro@336: \end{figure} jerojasro@336: igor@443: Puede reaplicar un parche no aplicado o sustraído con la orden igor@443: \hgxcmd{mq}{qpush}. Esto crea un nuevo conjunto de cambios igor@443: correspondiente al parche, y los cambios del parche estarán presentes igor@443: de nuevo en el directorio de trabajo. Vea ejemplos de igor@443: \hgxcmd{mq}{qpop} y \hgxcmd{mq}{qpush} en acción en la igor@443: figura~\ref{ex:mq:qpop}. Vea que hemos sustraído uno o dos parches, igor@443: la salida de\hgxcmd{mq}{qseries} continúa igual, mientras que igor@443: \hgxcmd{mq}{qapplied} ha cambiado. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qpop} igor@443: \caption{Modificar la pila de parches aplicados} jerojasro@336: \label{ex:mq:qpop} jerojasro@336: \end{figure} jerojasro@336: igor@443: \subsection{Introducir y sustraer muchos parches} igor@443: igor@443: Mientras que \hgxcmd{mq}{qpush} y \hgxcmd{mq}{qpop} operan sobre un igor@443: único parche cada vez, puede introducir y sustraer varios parches de igor@443: una vez. La opción \hgxopt{mq}{qpush}{-a} de \hgxcmd{mq}{qpush} igor@443: introduce todos los cambios que no hayan sido aplicados, mientras que igor@443: la opción \hgxopt{mq}{qpop}{-a} de \hgxcmd{mq}{qpop} sustrae todos los igor@443: cambios aplicados. (Vea la sección~\ref{sec:mq:perf} más adelante igor@443: en la cual se explican otras formas de de introducir y sustraer varios igor@443: cambios.) jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.qpush-a} jerojasro@336: \caption{Pushing all unapplied patches} jerojasro@336: \label{ex:mq:qpush-a} jerojasro@336: \end{figure} jerojasro@336: igor@443: \subsection{Medidas de seguridad y cómo saltarlas} igor@443: igor@443: Muchas órdenes MQ revisan el directorio de trabajo antes de hacer igor@443: cualquier cosa, y fallan si encuentran alguna modificación. Lo hacen igor@443: para garantizar que usted no pierda cambio alguno de los que haya igor@443: hecho, pero que no hayan sido incorporados en algún parche. La igor@443: figura~\ref{ex:mq:add} ilusta esto; la orden \hgxcmd{mq}{qnew} no igor@443: creará un nuevo parche si hay cambios notorios, causados en este caso igor@443: por aplicado la orden \hgcmd{add} a \filename{file3}. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tutorial.add} igor@443: \caption{Crear un parche a la fuerza} jerojasro@336: \label{ex:mq:add} jerojasro@336: \end{figure} jerojasro@336: igor@443: Las órdenes que revisan el directorio actual cuentan con una opción igor@443: ``Se lo que estoy haciendo'', que siempre está nombrada como igor@443: \option{-f}. El significado exacto de \option{-f} depende de la igor@443: orden. Por ejemplo, \hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} igor@443: incorporarán cualquier cambio notorio en el nuevo parche que crea pero igor@443: \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} revertirá las modificaciones a igor@443: cualquier fichero que haya sido afectado por el parche que está siendo igor@443: sustraído. ¡Asegúrese de leer la documentación de la opción \option{-f} igor@443: de cada comando antes de usarla! igor@443: igor@443: \subsection{Trabajar con varios parches a la vez} igor@443: igor@443: La orden \hgxcmd{mq}{qrefresh} siempre refresca el \emph{último} igor@443: parche aplicado. Esto significa que usted puede suspender su trabajo igor@443: en un parche (refrescándolo), sustraerlo o introducirlo para lograr igor@443: que otro parche esté de último y trabajar en \emph{ese} parche por un igor@443: rato. igor@443: igor@443: A continuación un ejemplo que ilustra cómo puede usar esta habilidad. igor@443: Digamos que está desarrollando una nueva característica en dos igor@443: parches. El primero es un cambio en la parte fundamental de su igor@443: programa, y el segundo--sobre el primero---cambia la interfaz de igor@443: usuario para usar el código que ha añadido a la parte fundamental. Si igor@443: ve que hay un fallo en la parte fundamental mientras está trabajando igor@443: en el parche de UI\ndt{Interfaz de Usuario, User Interface en inglés}, es fácil arreglar la parte fundamental. igor@443: Simplemente use \hgxcmd{mq}{qrefresh} sobre el parche de la UI para igor@443: guardar los cambios de su trabajo en progreso, y use \hgxcmd{mq}{qpop} igor@443: para sacar sustraer el parche de la parte fundamental. Arregla el igor@443: fallo sobre la parte fundamental, aplique \hgxcmd{mq}{qrefresh} sobre igor@443: el parche fundamental, y aplique \hgxcmd{mq}{qpush} sobre el parche de igor@443: UI para continuar donde había quedado. jerojasro@336: igor@444: \section{Más acerca de parches} jerojasro@336: \label{sec:mq:adv-patch} jerojasro@336: igor@444: MQ usa la orden GNU \command{patch} para aplicar los parches, por lo igor@444: tanto es útil conocer ciertos detalles de cómo trabaja igor@444: \command{patch}, y también acerca de los parches. igor@444: igor@444: \subsection{La cantidad de franjas} igor@444: igor@449: Si ve el encabezado de un parche, notará que la ruta al fichero tiene igor@444: un componente adicional al principio, que no está presente en la igor@444: ruta. Esta es una traza de cómo generaba anteriormente los parches la jerojasro@480: gente (algunos aún lo hacen, pero es raro con las herramientas de igor@444: control de revisiones del actuales). igor@444: igor@449: Alicia desempaquetaría un comprimido, editaría sus ficheros, y querría igor@444: crear un parche. Por lo tanto ella renombraría su directorio de jerojasro@520: trabajo, desempacaría el comprimido de nuevo (para lo cual necesitó el igor@444: renombramiento), y usaría las opciones \cmdopt{diff}{-r} y igor@444: \cmdopt{diff}{-N} de \command{diff} para generar recursivamente un igor@444: parche entre el directorio original y el modificado. El resultado igor@444: sería que el nombre del directorio original estaría al principio de igor@444: toda ruta en cada encabezado de fichero, y el nombre del directorio igor@444: modificado estaría al frente de la porción derecha de la ruta del igor@449: fichero. jerojasro@336: igor@446: Como alguien que reciba un parche de Alicia en la red podría obtener igor@446: dos directorios, uno original y el otro modificado con exactamente los igor@446: mismos nombres, la orden \command{patch} tiene la opción igor@446: \cmdopt{patch}{-p} que indica la cantidad de componentes de la ruta igor@446: a eliminar cuando se vaya a aplicar el parche. Este número se igor@446: llama la \emph{cantidad de eliminaciones}. igor@446: igor@446: La opción con ``\texttt{-p1}'' significa ``elimine uno''. Si igor@446: \command{patch} ve un nombre de fichero \filename{foo/bar/baz} en el igor@446: encabezado del fichero, eliminará \filename{foo} y tratará de parchar igor@446: un fichero llamado \filename{bar/baz}. (Hablando estrictamente, la igor@446: cantidad de eliminaciones se refiere a la cantidad de \emph{separadores de igor@446: ruta} (y los componentes que vayan con ellos) a eliminar. Si el igor@446: contador es uno volverá \filename{foo/bar} en \filename{bar}, pero igor@446: \filename{/foo/bar} (note la barra extra) en \filename{foo/bar}.) igor@446: igor@446: La cantidad a eliminar``estándar'' para parches es uno; casi todos los igor@446: parches contienen un componente inicial de la ruta que necesita ser igor@446: eliminado. La orden \hgcmd{diff} de Mercurial genera nombres de ruta igor@446: de esta forma, y la orden \hgcmd{import} y MQ esperan parches que igor@446: tengan a uno como cuenta de eliminaciones. igor@446: igor@446: Si recibe un parche de alguien de quien desea adicionar adicionar a su igor@446: cola de parches, y el parche necesita una cuenta de eliminación que no igor@446: sea uno, no podrá aplicar \hgxcmd{mq}{qimport} en primera medida, igor@446: porque \hgxcmd{mq}{qimport} no tiene todavía una opción \texttt{-p} igor@446: option (ver~\bug{311}). Lo mejor que puede hacer es aplicar igor@446: \hgxcmd{mq}{qnew} por su cuenta, y después usar \cmdargs{patch}{-p\emph{N}} igor@446: para aplicar tal parche, seguido de \hgcmd{addremove} para tener en igor@446: cuenta cualquier fichero adicionado o eliminado por el parche, seguido igor@446: de \hgxcmd{mq}{qrefresh}. Esta complejidad puede ser innecesaria; igor@446: consulte~\bug{311} para más información. igor@446: igor@446: \subsection{Estrategias para aplicar parches} igor@446: igor@446: Cuando \command{patch} aplica un trozo, intenta varias estrategias igor@446: sucesivas que decrecen en precisión para intentar aplicarlo. Esta igor@446: técnica de pruebas y error aveces permite que un parche que fue igor@446: generado contra una versión anterior de un fichero, sea aplicada sobre igor@446: una versión más nueva del mismo. igor@446: igor@446: Primero \command{patch} intenta una correspondencia perfecta donde los igor@446: números de línea, el contexto y el texto a modificar deben coincidir igor@446: perfectamente. Si no lo logra, intenta encontrar una correspondencia igor@446: exacta del contexto, sin tener en cuenta el número de línea. Si es igor@446: exitoso, imprime una línea indicando que el trozo fue aplicado, pero a igor@446: un \emph{corrimiento} del número de línea original. igor@446: igor@446: Si falla la correspondencia por contexto, \command{patch} elimina la igor@446: primera y la última línea del contexto, e intenta una correspondencia igor@446: \emph{reducida} del contexto. Si el trozo con contexto reducido es igor@446: exitoso, imprime un mensaje indicando que aplicó el trozo con un igor@446: \emph{factor difuso} (el número después del factor difuso indica igor@446: cuántas líneas de contexto \command{patch} tuvo que eliminar antes de igor@446: aplicar el parche). igor@446: igor@446: Cuando ninguna de estas técnicas funciona, \command{patch} imprime un igor@446: mensaje indicando que el trozo en cuestión se desechó. Almacena los jerojasro@480: trozos desechados (también llamados ``descartados'') en un fichero con igor@446: el mismo nombre, y la extensión \sfilename{.rej} añadida. También igor@446: almacena una copia igual al fichero original con la extensión igor@446: \sfilename{.orig}; la copia del fichero sin extensión contendrá igor@446: cualquier cambio hecho por los trozos que \emph{sí} se aplicaron sin igor@446: problema. Si usted tiene un parche que modifica \filename{foo} con igor@446: seis trozos, y uno de ellos falla al aplicarse, tendrá : un fichero igor@446: original \filename{foo.orig}, un fichero \filename{foo.rej} que igor@446: contiene el trozo, y \filename{foo}, que contiene los cambios que se igor@446: aplicaron por los cinco trozos exitosos. igor@446: igor@446: \subsection{Algunos detalles de la representación de parches} igor@446: igor@446: Hay ciertas cosas útiles por saber acerca de cómo trabaja igor@446: \command{patch} con los ficheros: jerojasro@336: \begin{itemize} igor@446: \item Debería ser obvio que \command{patch} no puede manipular igor@446: ficheros binarios. igor@446: \item No se preocupa por el bit ejecutable; crea ficheros nuevos en igor@446: modo lectura, pero no ejecutable. igor@446: \item \command{patch} intenta eliminar un fichero como una diferencia igor@446: entre el fichero a eliminar y un fichero vacío. Y por lo tanto su igor@446: idea de ``Borré este fichero'' debería pensarse como ``toda línea de igor@446: este fichero fue eliminada'' en un parche. igor@446: \item Trata la adición de un fichero como un diff entre un fichero igor@446: vacío y el fichero a ser adicionado. Por lo tanto en un parche su igor@446: idea de ``Añadí este fichero'' se vería como ``toda línea de este igor@446: fichero fue añadida''. igor@446: \item Trata el renombramiento de un fichero como la eliminación del igor@446: nombre anterior y la adición del nuevo nombre. Esto significa que igor@446: los ficheros renombrados dejan un rastro grande en los parches. igor@446: (Tenga en cuenta que Mercurial no trata de inferir cuando los igor@449: ficheros han sido renombrados o copiados en un parche en este igor@446: momento.) igor@446: \item \command{patch} no puede representar ficheros vacíos, por lo igor@446: tanto no puede usar un parche para representar la noción ``Añadí igor@446: este fichero vacío al árbol''. jerojasro@336: \end{itemize} igor@446: \subsection{Cuidado con los difusos} jerojasro@336: igor@447: Cuando aplique un trozo con un corrimiento, o con un factor difuso, igor@447: aveces será taotalmente exitoso, tales técnicas inexactas dejan igor@449: claramente la posibilidad de corromper el fichero parchado. Los casos igor@447: más típicos involucran aplicar un parche dos veces o en un sitio igor@447: incorrecto del fichero. Si \command{patch} o \hgxcmd{mq}{qpush} llegan igor@447: a mencionar un corrimiento o un factor difuso, debería asegurarse que igor@447: los ficheros modificados estén correctos después del suceso. igor@447: igor@447: Casi siempre es buena idea refrescar un parche que fue aplicado con un igor@447: corrimiento o un factor difuso; refrescar el parche genera nueva igor@447: información de contexto que permitirá aplicarlo limpiamente. Digo igor@447: ``casi siempre,'' no ``siempre'', puesto que en ciertas ocasiones igor@447: refrescar un parche lo hará fallar frente a una revisión diferente del igor@447: fichero. En algunos casos, como por ejemplo, cuando usted está igor@447: manteniendo un parche que debe estar encima de múltiples revisiones de igor@447: un árbol de fuentes, es aceptable tener un parche aplicado algo igor@447: difuso, siempre que haya verificado los resultados del proceso de igor@447: parchar. igor@447: igor@447: \subsection{Manejo de descartes} igor@447: igor@447: Si \hgxcmd{mq}{qpush} falla al aplicar un parche, mostrará un texto de igor@447: error y saldrá. Si ha dejado ficheros \sfilename{.rej}, es mejor igor@447: arreglar los trozos descartados antes de introducir parches igor@447: adicionales o hacer cualquier otra cosa. igor@447: igor@447: Si su parche \emph{solía} aplicarse limpiamente, y ya no lo hace igor@447: porque ha cambiado código subyacente en el cual se basa su parche, las igor@447: Colas de Mercurial pueden ayudar; consulte la sección~\ref{sec:mq:merge}. igor@447: igor@447: Desafortunadamente, no hay grandes técnicas para tratar los trozos igor@447: descartados. Casi siempre deberá consultar el fichero igor@447: \sfilename{.rej} y editar el fichero objetivo, aplicando los trozos igor@447: descartados a mano. igor@447: igor@447: Si es aventurero, Neil Brown, un hacker del núcleo Linux, escribió una igor@447: herramienta llamada \command{wiggle}~\cite{web:wiggle}, que es más igor@447: vigorosa que \command{patch} en su intento de hacer que se aplique un igor@447: parche. igor@447: igor@447: Otro hacker del nucleo Linux, Chris Mason (el autor de las Colas de igor@447: Mercurial), escribió una herramienta similar llamada igor@447: \command{mpatch}~\cite{web:mpatch}, que sigue una aproximación igor@447: sencilla para automatizar la aplicación de trozos descartados por igor@447: \command{patch}. La orden \command{mpatch} puede ayudar con cuatro igor@447: razones comunes por las cuales un parche ha sido descartado: jerojasro@336: jerojasro@336: \begin{itemize} igor@447: \item El contexto en la mitad de un trozo ha cambiado. igor@447: \item Un trozo ha perdido cierto contexto al principio o al final. igor@447: \item Un trozo largo podría aplicarse mejor---por completo o una igor@447: parte---si estaba cortado en trozos más pequeños. igor@447: \item Un trozo remueve líneas con contenido ligeramente diferente que igor@447: aquellas que están presentes en el fichero. jerojasro@336: \end{itemize} jerojasro@336: igor@447: Si usted usa \command{wiggle} o \command{mpatch}, debería ser igor@447: doblemente cuidadoso al revisar sus resultados cuando haya terminado. igor@447: De hecho, \command{mpatch} refuerza este método de revisar por partida igor@447: doble su salida, dejándolo a usted en un programa de fusión cuando la igor@447: herramienta haya terminado su trabajo, de tal forma que usted pueda igor@447: verificar lo que ha hecho y pueda terminar de aplicar cualquier fusión igor@447: faltante. igor@447: igor@449: \section{maximizar el rendimiento de MQ} jerojasro@336: \label{sec:mq:perf} jerojasro@336: igor@447: MQ es muy eficiente al tratar con una gran cantidad de parches. Corrí igor@447: unos experimentos de desempeño a mediados del 2006 para una charla que igor@447: dí en la conferencia EuroPython 2006~\cite{web:europython}. Empleé la igor@447: serie de parches para el núcleo Linux 2.6.17-mm1, que contaba con 1.738 igor@447: parches. Los apliqué sobre un repositorio del núcleo de Linux con igor@447: todas las 27.472 revisiones entre 2.6.12-rc2 y 2.6.17. igor@447: igor@448: En mi portátil antiguo y lento, logré aplicar igor@447: \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} a los 1.738 parches en 3.5 igor@447: minutos, y \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} en 30 segundos. igor@447: (En un portátil más nuevo, el tiempo para introducir todos los igor@447: parches, se logró en menos de dos minutos.) Apliqué jerojasro@480: \hgxcmd{mq}{qrefresh} sobre uno de los parches más grandes (que hizo igor@447: 22.779 líneas de cambios en 287 ficheros) en 6,6 segundos. igor@447: igor@447: Claramente, MQ funciona adecuadamente en árboles grandes, y además hay igor@447: unos trucos que pueden emplearse para obtener el máximo desempeño. igor@447: igor@447: En primer lugar, trate de hacer ``en lote'' las operaciones. Cada vez igor@447: que ejecute \hgxcmd{mq}{qpush} o \hgxcmd{mq}{qpop}, tales órdenes igor@447: revisan el directorio de trabajo para asegurarse de que usted no ha igor@447: hecho cambios y ha olvidado ejecutar \hgxcmd{mq}{qrefresh}. En un igor@447: árbol pequeño, el tiempo de esta revisión puede ser mínimo, Pero en jerojasro@480: un árbol mediano (con decenas de miles de ficheros), puede tomar un igor@447: segundo o más. igor@447: igor@447: Las órdenes \hgxcmd{mq}{qpush} y \hgxcmd{mq}{qpop} le permiten igor@447: introducir o sustraer varios parches en una operación. Puede igor@447: identificar el ``parche destino'' que desee. Cuando aplique igor@447: \hgxcmd{mq}{qpush} con un destino, introducirá tantos parches como sea igor@447: necesario hasta que el especificado esté en el tope de la pila. igor@447: Cuando emplee \hgxcmd{mq}{qpop} con un destino, MQ sustraerá parches igor@447: hasta que el parche destino esté en el tope. igor@447: igor@447: Puede identificar un parche destino con el nombre del parche o con el igor@447: número. Si se refiere al número, los parches se contarán desde cero; igor@447: esto significa que el primer parche es cero, el segundo es uno y así igor@447: sucesivamente. jerojasro@336: igor@448: \section{Actualiar los parches cuando el código cambia} jerojasro@336: \label{sec:mq:merge} jerojasro@336: igor@448: Es común contar con una pila de parches sobre un repositorio que usted igor@448: no modifica directamente. Si está trabajando en cambios de código de igor@448: otros, o en una característica que tarda bastante en desarrollarse igor@448: comparada con la tasa de cambio del código sobre la cual se está igor@448: trabajando, necesitará sincronizarse con el código, y ajustar igor@448: cualquier trozo en sus parches que ya no estén al día. A esto se le igor@448: llama hacer \emph{rebase} a su serie de parches. igor@448: igor@448: La vía más sencilla de hacerlo es con \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} igor@448: sobre sus parches, después hacer \hgcmd{pull} de los cambios en el igor@448: repositorio, y finalmente hacer igor@448: \hgcmdargs{qpush}{\hgxopt{mq}{qpop}{-a}} con sus parches de nuevo. MQ igor@448: dejará de de introducir parches siempre que llegue a un parche que no se pueda igor@448: aplicar debido a un conflicto, permitiéndole a usted arreglarlo, igor@448: aplicar \hgxcmd{mq}{qrefresh} al parche afectado y continuar igor@448: introduciendo hasta que haya arreglado la pila completa. igor@448: igor@448: Esta aproximación es sencilla y funciona bien si no espera cambios en igor@448: el código original que afecte en gran medida los parches que usted igor@448: esté aplicando. Si su pila de parches toca código que es modificado igor@448: frecuentemente o de forma invasiva sobre el código subyacente, igor@448: arreglar trozos manualmente se vuelve desgastante. igor@448: igor@448: Es posible automatizar de forma parcial el proceso de rebase. Si sus igor@448: parches se aplican limpiamente sobre algunas revisiones del igor@448: repositorio subyacente, MQ puede usar esta información para ayudarle a igor@448: a resolver conflictos entre sus parches y una revisión distinta. igor@448: igor@448: El proceso resulta un poco complejo: jerojasro@336: \begin{enumerate} igor@448: \item Para comenzar, haga \hgcmdargs{qpush}{-a} sobre todos los igor@448: parches que usted sepa se aplican limpiamente. igor@448: \item Guarde una copia de seguridad de su directorio de parches con igor@448: \hgcmdargs{qsave}{\hgxopt{mq}{qsave}{-e} \hgxopt{mq}{qsave}{-c}}. igor@448: Esto imprime el nombre del directorio en el cual se han guardado los igor@448: parches. Guardará los parches en un directorio llamado igor@448: \sdirname{.hg/patches.\emph{N}}, donde \texttt{\emph{N}} es un igor@448: entero pequeño. También consigna un ``conjunto de cambios de igor@448: seguridad'' sobre sus parches aplicados; esto es para mantener el igor@448: histórico, y guarda los estados de los ficheros \sfilename{series} igor@448: y \sfilename{status}. igor@448: \item Use \hgcmd{pull} para traer los nuevos cambios en el repositorio igor@448: subyacente. (No ejecute \hgcmdargs{pull}{-u}; vea más adelante por qué.) igor@448: \item Actualice a la nueva revisión punta con igor@448: \hgcmdargs{update}{\hgopt{update}{-C}} para sobreescribir los igor@448: parches que haya introducido. igor@448: \item Fusione todos los parches con \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m} igor@448: \hgxopt{mq}{qpush}{-a}}. La opción \hgxopt{mq}{qpush}{-m} de \hgxcmd{mq}{qpush} igor@448: le indica a MQ que haga una fusión que involucra tres fuentes si el igor@448: parche falla al aplicarse. jerojasro@336: \end{enumerate} jerojasro@336: igor@448: Durante el \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m}}, cada parche en igor@448: el fichero \sfilename{series} se aplica normalmente. Si un parche se igor@448: aplica difusamente o se niea a aplicarse, MQ consulta la cola que igor@448: usted guardó con \hgxcmd{mq}{qsave}, y aplica una fusión de tres con igor@448: el correspondiente conjunto de cambios. Esta fusión usa la maquinaria igor@448: de Mercurial, por lo tanto puede mostrar una herramienta de fusión GUI igor@448: para ayudarle a resolver los problemas. igor@448: igor@448: Cuando termine de resolver los efectos de un parche, MQ refrescará su igor@448: parche basado en el resultado de la fusión. igor@448: igor@448: Al final de este proceso, su repositorio tendrá una cabeza extra de la igor@448: antigua cola de parches, y una copia de la cola de parches anterio igor@448: estará en \sdirname{.hg/patches.\emph{N}}. Puede eliminar la cabeza igor@448: extra con \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a} \hgxopt{mq}{qpop}{-n} patches.\emph{N}} igor@448: o \hgcmd{strip}. Puede eliminar \sdirname{.hg/patches.\emph{N}} una igor@448: vez que esté seguro de que no lo necesita más como copia de seguridad. igor@448: igor@448: \section{Identificar parches} igor@448: igor@448: Las órdenes de MQ le permiten trabajar refiriéndose al nombre del igor@448: parche o al número. Es obvio hacerlo por el nombre; por ejemplo se igor@448: pasa el nombre \filename{foo.patch} a \hgxcmd{mq}{qpush}, que igor@448: introducirá los parches hasta que \filename{foo.patch} se aplique. igor@448: igor@448: Para hacerlo más corto, puede referirse a un parche con un nombre y un igor@448: corrimiento de número; por ejemplo, \texttt{foo.patch-2} significa igor@448: ``dos parches antes de \texttt{foo.patch}'', mientras que igor@448: \texttt{bar.patch+4} significa ``cuatro parches después de \texttt{bar.patch}''. igor@448: igor@448: Referirse a un parche por su índice no es muy diferente. El primer igor@448: parche que se imprime en la salida de \hgxcmd{mq}{qseries} es el jerojasro@480: parche cero (si, es el primero en los sistemas que comienzan su conteo igor@448: en cero); el segundo parche es uno y así sucesivamente. igor@448: igor@448: MQ facilita el trabajo cuando está usando órdenes normales de igor@448: Mercurial. Cada comando que acepte Identificadores de conjuntos de igor@448: cambios también aceptará el nombre de un parche aplicado. MQ aumenta jerojasro@519: las etiquetas normalmente en el repositorio con un distintivo para cada jerojasro@519: parche aplicado. Adicionalmente, las etiquetas especiales \index{tags!special tag igor@448: names!\texttt{qbase}}\texttt{qbase} y \index{tags!special tag igor@448: names!\texttt{qtip}}\texttt{qtip} identifican los parches igor@448: ``primero'' y último, respectivamente. jerojasro@336: igor@449: Junto con las capacidades de Mercurial para etiquetar, estas adiciones igor@449: hacen que trabajar con parches sea muy sencillo. jerojasro@336: \begin{itemize} igor@449: \item ¿Desea enviar una bomba de parches a una lista de correo con los igor@449: últimos cambios que ha hecho? jerojasro@336: \begin{codesample4} jerojasro@336: hg email qbase:qtip jerojasro@336: \end{codesample4} igor@449: (¿No sabe qué es una ``bomba de parches''? Consulte la igor@449: sección~\ref{sec:hgext:patchbomb}.) igor@449: \item ¿Desea ver todos los parches desde que se aplicó igor@449: \texttt{foo.patch} sobre los ficheros de un subdirectorio en su igor@449: árbol? jerojasro@336: \begin{codesample4} jerojasro@336: hg log -r foo.patch:qtip \emph{subdir} jerojasro@336: \end{codesample4} jerojasro@336: \end{itemize} jerojasro@336: igor@449: Dado que MQ nombra los parches disponibles al resto de Mercurial con igor@449: su maquinaria de etiquetas interna, usted no necesita teclear el igor@449: nombre completo de un parche cuando desea identificarlo por su nombre. jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.id.output} igor@449: \caption{Uso de las características de etiquetamiento al trabajar igor@449: con MQ} jerojasro@336: \label{ex:mq:id} jerojasro@336: \end{figure} jerojasro@336: igor@449: Otra consecuencia deseable al representar los nombres de parches como igor@449: etiquetas es que cuando ejecute la orden \hgcmd{log}, desplegará el igor@449: nombre del parche como una etiqueta, usualmente con la salida normal. igor@449: Esto facilita distinguir visualmente los parches aplicados de las igor@449: revisiones ``normales''. La figura~\ref{ex:mq:id} muestra algunos igor@449: comandos usuales de Mercurial al trabajar con parches. igor@449: igor@449: \section{Otra información útil} igor@449: igor@449: Hay una cantidad de aspectos que hacen que el uso de MQ no representen igor@449: secciones en sí mismas, pero de los cuales es bueno estar igor@449: enterado. Los presentamos en aquí: jerojasro@336: jerojasro@336: \begin{itemize} igor@449: \item Usualmente cuando hace \hgxcmd{mq}{qpop} a un parche y vuelve a igor@449: hacerle \hgxcmd{mq}{qpush}, el conjunto de cambios que representa el igor@449: parche después de introducir/sustraer tendrá una \emph{identidad igor@449: distinta} que aquella que representaba el conjunto de cambios igor@449: anteriormente. Consulte la secctión~\ref{sec:mqref:cmd:qpush} para igor@449: obtener información del por qué de esto. igor@449: \item No es una buena idea aplicar \hgcmd{merge} de cambios de otra igor@449: rama con un conjunto de cambios de parches, por lo menos si desea igor@449: mantener la ``información de parches'' de ese conjunto de cambios y igor@449: los conjuntos de cambios que se encuentran por debajo en la pila de igor@449: parches. Si intenta hacerlo, parecerá que ha sido exitoso, pero MQ igor@449: se confundirá. jerojasro@336: \end{itemize} jerojasro@336: igor@449: \section{Administrar parches en un repositorio} jerojasro@336: \label{sec:mq:repo} jerojasro@336: igor@449: Dado que el directorio \sdirname{.hg/patches} de MQ reside fuera del igor@449: repositorio de trabajo de Mercurial, el repositorio ``subyacente'' de igor@449: Mercurial no sabe nada acerca de la administración o presencia de igor@449: parches. igor@449: igor@449: Esto presenta la interesante posibilidad de administrar los contenidos jerojasro@517: del directorio de parches como un repositorio de Mercurial por su igor@449: cuenta. Puede ser una forma útil de trabajar. Por ejemplo, puede igor@449: trabajar en un parche por un rato, hacerle \hgxcmd{mq}{qrefresh} y igor@449: después hacer \hgcmd{commit} al estado actual del parche. Esto le igor@449: permite ``devolverse'' a esa versión del parche posteriormente. igor@449: igor@449: Puede también compartir diferentes versiones de la misma pila de igor@449: parches entre varios repositorios subyacentes. Uso esto cuando estoy igor@449: desarrollando una característica del núcleo Linux. Tengo una copia igor@449: original de las fuentes del núcleo para varias arquitecturas, y cloné igor@449: un rpositorio en cada una que contiene los parches en los cuales igor@449: estoy trabajando. Cuando quiero probar un cambio en una arquitectura igor@449: diferente, introduzco mis parches actuales al repositorio de parches igor@449: asociado con el árbol del kernel, sustraigo e introduzco todos mis igor@449: parches, armo y pruebo el núcleo. igor@449: igor@449: Llevar los parches en un repositorio permite que varios igor@449: desarrolladores puedan trabajar en la misma serie de parches sin jerojasro@526: sobreponerse, todo sobre la fuente base subyacente que pueden o no igor@449: controlar. igor@449: igor@449: \subsection{Soporte de MQ para repositorios de parches} igor@449: igor@449: MQ le ayuda a trabajar con el directorio \sdirname{.hg/patches} como igor@449: un repositorio; cuando usted prepara un repositorio para trabajar con igor@449: parches usando \hgxcmd{mq}{qinit}, puede pasarle la opción igor@449: \hgxopt{mq}{qinit}{-c} para que se cree el directorio igor@449: \sdirname{.hg/patches} como un repositorio de Mercurial. jerojasro@336: jerojasro@336: \begin{note} igor@449: Si olvida usar la opción \hgxopt{mq}{qinit}{-c} option, puede ir al igor@449: directorio \sdirname{.hg/patches} en cualquier momento y ejecutar igor@449: \hgcmd{init}. No olvide añadir una entrada en el fichero igor@449: \sfilename{status} del fichero \sfilename{.hgignore}, a pesar de que igor@449: (\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} hace estodo de forma igor@449: automática para usted); usted \emph{seguro} no quiere administrar el jerojasro@522: fichero \sfilename{status}. jerojasro@336: \end{note} jerojasro@336: igor@449: MQ nota convenientemente que el directorio \dirname{.hg/patches} igor@449: es un repositorio, hará \hgcmd{add} automáticamente a cada parche que igor@449: usted cree e importe. igor@449: igor@449: MQ provee una orden corta, \hgxcmd{mq}{qcommit}, que ejecuta igor@449: \hgcmd{commit} en el directorio \sdirname{.hg/patches}. Lo que ahorra igor@449: tecleo recurrente. igor@449: igor@449: Finalmente, para administrar convenientemente el directorio de igor@449: parches, puede definir el alias \command{mq} en sistemas Unix. Por igor@449: ejemplo, en sistemas Linux con el intérprete \command{bash}, puede igor@449: incluir el siguiente recorte de código\ndt{snippet} en su fichero igor@449: \tildefile{.bashrc}. jerojasro@336: jerojasro@336: \begin{codesample2} jerojasro@336: alias mq=`hg -R \$(hg root)/.hg/patches' jerojasro@336: \end{codesample2} jerojasro@336: igor@449: Puede aplicar las órdenes de la forma \cmdargs{mq}{pull} al igor@449: repositorio principal. igor@449: igor@449: \subsection{Detalles a tener en cuenta} igor@449: igor@449: El soporte de MQ para trabajar con un repositorio de parches es igor@449: limitado en ciertos aspectos: igor@449: igor@449: MQ no puede detectar automáticamente los cambios que haga al igor@449: directorio de parches. Si aplica \hgcmd{pull}, edita manualmente, o igor@449: hace \hgcmd{update} a los parches o el fichero \sfilename{series}, igor@449: tendrá que aplicar \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} y después igor@449: \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} en el repositorio subyacente igor@449: para que los cambios se reflejen allí. Si olvida hacerlo, puede igor@449: confundir a MQ en cuanto a qué parches se han aplicado. igor@449: igor@449: \section{Otras herramientas para trabajar con parches} jerojasro@336: \label{sec:mq:tools} jerojasro@336: igor@449: Cuando haya trabajado por cierto tiempo con parches, deseará igor@449: herramientas que le ayuden a entender y manipular los parches con los igor@449: que esté tratando. igor@449: igor@449: La orden \command{diffstat}~\cite{web:diffstat} genera un histograma igor@449: de modificaciones hechas a cada fichero en un parche. Provee una igor@449: interesante forma de ``dar un vistazo'' al parche---qué ficheros igor@449: afecta, y cuántos cambios introduce a cada fichero y en total. (Me ha igor@449: parecido interesante usar la opción \cmdopt{diffstat}{-p} de igor@449: \command{diffstat}, puesto que de otra forma intentará hacer cosas igor@449: inteligentes con prefijos de ficheros que terminan confundiéndome.) jerojasro@336: jerojasro@336: \begin{figure}[ht] jerojasro@336: \interaction{mq.tools.tools} igor@449: \caption{Las órdenes \command{diffstat}, \command{filterdiff}, y \command{lsdiff}} jerojasro@336: \label{ex:mq:tools} jerojasro@336: \end{figure} jerojasro@336: igor@449: El paquete \package{patchutils}~\cite{web:patchutils} es igor@449: invaluable. Provee un conjunto de pequeñas utilidades que siguen la igor@449: ``filosofía Unix''; cada una hace una cosa muy bien hecha a un igor@449: parche. La orden \package{patchutils} que más uso es igor@449: \command{filterdiff}, que extrae subconjuntos de un fichero de igor@449: parche. Por ejemplo, dado un parche que modifica centenas de ficheros igor@449: en docenas de directorios, una única invocación de igor@449: \command{filterdiff} puede generear un parche más pequeño que igor@449: solamente toca aquellos ficheros con un patrón. Puede ver otro igor@449: ejemplo en la sección~\ref{mq-collab:tips:interdiff}. igor@449: igor@449: \section{Buenas prácticas de trabajo con parches} igor@449: igor@449: En caso de que esté trabajando en una serie de parches para enviar a igor@449: un proyecto de software libre o de fuentes abiertas, o en una serie igor@449: que desea tratar como un conjunto de cambios regular, cuando haya igor@449: terminado, puede usar técnicas sencillas para mantener su trabajo bien igor@449: organizado. igor@449: igor@449: De nombres descriptivos a sus parches. Un buen nombre para un parche igor@449: podría ser \filename{rework-device-alloc.patch}, porque da de forma igor@449: inmediata una pista del propósito del parche. Los nombres largos no igor@449: deben ser un problema; no los estará tecleando repetidamente, pero igor@449: \emph{estará} ejecutando regularmente órdenes como igor@449: \hgxcmd{mq}{qapplied} y \hgxcmd{mq}{qtop}. Los nombres adecuados son igor@449: especialmente importantes cuando tiene bastantes parches con los igor@449: cuales trabajar, o si está trabajando en diferentes tareas y sus igor@449: parches toman solamente una porción de su atención. igor@449: igor@449: Tenga en cuenta en qué parche está trabajando. Use la orden \hgxcmd{mq}{qtop} igor@449: para dar un vistazo al texto de sus parches frecuentemente---por igor@449: ejemplo, use \hgcmdargs{tip}{\hgopt{tip}{-p}})---para asegurarse en igor@449: dónde está ubicado. En distintas oportunidades me sucedió que apliqué igor@449: \hgxcmd{mq}{qrefresh} a un parche distinto al que deseaba hacerlo, y igor@449: usualmente es complejo migrar los cambios al parche correcto después igor@449: de haberlo hecho mal. igor@449: igor@449: Por este motivo, vale la pena invertir ese poco tiempo para aprender igor@449: cómo usar otras herramientas que describí en la igor@449: sección~\ref{sec:mq:tools}, particularmente \command{diffstat} y igor@449: \command{filterdiff}. La primera le dará una idea de qué cambios está igor@449: haciendo su parche, mientras que la segunda permite seleccionar trozos igor@449: de un parche para colocarlos en otro. igor@449: igor@449: \section{Recetas de MQ} igor@449: igor@449: \subsection{Administrar parches ``triviales''} igor@449: igor@449: Puesto que colocar ficheros en un repositorio de Mercurial es tan igor@449: sencillo, tiene bastante sentido administrar parches de esta forma igor@449: incluso si desea hacer algunos cambios al paquete de ficheros que igor@449: descargó. igor@449: igor@449: Para comenzar a descargar y desempaqueter un paquete de ficheros, y igor@449: volverlo en un repositorio de Mercurial: jerojasro@336: \interaction{mq.tarball.download} jerojasro@336: igor@449: Continue creando una pila de parches y haga sus cambios. jerojasro@336: \interaction{mq.tarball.qinit} jerojasro@336: igor@449: Digamos que pasan unas semanas o meses, y el autor del paquete libera igor@449: una nueva versión. Primero se traen sus cambios al repositorio. jerojasro@336: \interaction{mq.tarball.newsource} igor@449: La porción que comienza con \hgcmd{locate} mostrada más arriba, borra igor@449: todos los ficheros en el directorio de trabajo, así que la opción igor@449: \hgopt{commit}{--addremove} de \hgcmd{commit} puede indicar qué igor@449: ficheros se han eliminado en la nueva versión del árbol de fuentes. igor@449: igor@449: Finalmente, puede aplicar sus parches encima del nuevo árbol de fuentes jerojasro@336: \interaction{mq.tarball.repush} jerojasro@336: igor@449: \subsection{Combinar parches completos} jerojasro@336: \label{sec:mq:combine} jerojasro@336: igor@449: MQ provee la orden \hgxcmd{mq}{qfold} que le permite combinar parches igor@449: enteros. Se ``integran''\ndt{fold} los parches que usted nombre, en igor@449: el orden que especifique, en el último parche aplicado, y concatena igor@449: sus descripciones al final de su descripción. Deberá sustraer los igor@449: cambios para poder integrarlos. igor@449: igor@449: El orden en el que integre los parches importa. Si el parche igor@449: últimamente aplicado es \texttt{foo}, e integra \hgxcmd{mq}{qfold} \texttt{bar} y igor@449: \texttt{quux} en él, terminará con un parche que tiene el mismo efecto igor@449: que si hubiera aplicado primero \texttt{foo}, y después \texttt{bar}, igor@449: seguido de \texttt{quux}. igor@449: igor@449: \subsection{Fusionar una porción de un parche dentro de otro} igor@449: igor@449: Fusionar \emph{partes} de un parche dentro de otro es más complejo que igor@449: combinar completamente dos parches. igor@449: igor@449: Si desea mover cambios de ficheros completos, puede usar las opciones igor@449: \command{filterdiff}'s \cmdopt{filterdiff}{-i} y igor@449: \cmdopt{filterdiff}{-x} para elegir las modificaciones remover de un igor@449: parche, concatenar su salida al final del parche donde desea igor@449: fusionarlo. Usualmente no necesitará modificar el parche del cuál ha igor@449: fusionado los cambios. En cambio, MQ reportará que hay unos trozos jerojasro@480: que se han desechado cuando usted aplique \hgxcmd{mq}{qpush} (de los igor@449: trozos que haya movido al otro parche), y puede sencillamente aplicar igor@449: \hgxcmd{mq}{qrefresh} para eliminar los trozos replicados. igor@449: igor@449: Si tiene un parche que tiene varios trozos que modifican un fichero, y igor@449: desea mover solamente unos de ellos, el trabajo es un poco más igor@449: enredado, pero puede automatizarlo parcialmente. Use igor@449: \cmdargs{lsdiff}{-nvv} para imprimir algunos metadatos del parche. jerojasro@336: \interaction{mq.tools.lsdiff} jerojasro@336: igor@449: Esta orden imprime tres clases diferentes de números: jerojasro@336: \begin{itemize} igor@449: \item (en la primera columna) un \emph{número de fichero} para igor@449: identificar cada fichero modificado en el parche; igor@449: \item (En la siguiente línea, indentado) el número de línea dentro de igor@449: un fichero modificado donde comienza el trozo; y igor@449: \item (en la misma línea) un \emph{número de trozo} que identifica el igor@449: trozo. jerojasro@336: \end{itemize} jerojasro@336: igor@449: Tendrá que hacer una inspección visual, y leer el parche para igor@449: identificar los números de fichero y trozo que desea, pero puede pasar igor@449: posteriormente a las opciones \cmdopt{filterdiff}{--files} y igor@449: \cmdopt{filterdiff}{--hunks} de \command{filterdiff}, para seleccionar igor@449: exactamente el fichero y el trozo que desea extraer. igor@449: igor@449: Cuando tenga el trozo, puede concatenarlo al final de su parche igor@449: objetivo y continuar como en la sección~\ref{sec:mq:combine}. igor@449: igor@449: \section{Diferencias entre quilt y MQ} igor@449: igor@449: Si le es familiar quilt, MQ provee un conjunto similar de órdenes. Hay igor@449: algunas diferencias en cómo funcionan. igor@449: igor@449: Debe haber notado que la mayoría de comandos de quilt tienen su igor@449: contraparte en MQ, que simplemente comienzan con ``\texttt{q}''. Las igor@449: excepciones son las órdenes \texttt{add} y \texttt{remove} de quilt, igor@449: que realmente son las órdenes \hgcmd{add} y \hgcmd{remove} de igor@449: Mercurial. No hay un equivalente en MQ para la orden igor@449: \texttt{edit} de quilt. jerojasro@336: jerojasro@336: %%% Local Variables: jerojasro@336: %%% mode: latex jerojasro@336: %%% TeX-master: "00book" jerojasro@336: %%% End: