hgbook

view es/mq-collab.tex @ 494:f89480678965

translated section 13.7
author Javier Rojas <jerojasro@devnull.li>
date Wed Jan 07 23:19:40 2009 -0500 (2009-01-07)
parents 795f2964e104
children dfa2890d9b30
line source
1 \chapter{Usos avanzados de las Colas de Mercurial}
2 \label{chap:mq-collab}
4 Auunque es fácil aprender los usos más directos de las Colas de
5 Mercurial, tener algo de disciplina junto con algunas de las
6 capacidadees menos usadas de MQ hace posible trabajar en entornos de
7 desarrollo complejos.
9 En este capítulo, usaré como ejemplo una técnica que he usado para
10 administrar el desarrollo de un controlador de dispositivo Infiniband
11 para el kernel de Linux. El controlador en cuestión es grande
12 (al menos en lo que se refiere a controladores), con 25,000 líneas de
13 código esparcidas en 35 ficheros fuente. Es mantenido por un equipo
14 pequeño de desarrolladores.
16 Aunque mucho del material en este capítulo es específico de Linux, los
17 mismos principios aplican a cualquier base de código de la que usted
18 no sea el propietario principal, y sobre la que usted necesita hacer
19 un montón de desarrollo.
21 \section{El problema de múltiples objetivos}
23 El kernel de Linux cambia con rapidez, y nunca ha sido estable
24 internamente; los desarrolladores hacen cambios drásticos entre
25 %TODO no encontré una traducción adecuada para "release". Por eso el
26 %cambio
27 versiones frecuentemente. Esto significa que una versión del
28 controlador que funciona bien con una versión particular del kernel ni
29 siquiera \emph{compilará} correctamente contra, típicamente, cualquier
30 otra versión.
32 Para mantener un controlador, debemos tener en cuenta una buena
33 cantidad de versiones de Linux en mente.
34 \begin{itemize}
35 \item Un objetivo es el árbol de desarrollo principal del kernel de
36 Linux. En este caso el mantenimiento del código es compartido
37 parcialmente por otros desarrolladores en la comunidad del kernel,
38 %TODO drive-by.
39 quienes hacen modificaciones ``de-afán'' al controlador a medida que
40 desarrollan y refinan subsistemas en el kernel.
41 %TODO backport
42 \item También mantenemos algunos ``backports'' para versiones antiguas
43 del kernel de Linux, para dar soporte a las necesidades de los
44 clientes que están corriendo versiones antiguas de Linux que no
45 incorporan nuestros controladores. (Hacer el \emph{backport} de un
46 pedazo de código es modificarlo para que trabaje en una versión
47 de su entorno objetivo anterior a aquella para la cual fue escrito.)
48 \item Finalmente, nosotros liberamos nuestro software de acuerdo a un
49 cronograma que no necesariamente está alineado con el que usan los
50 distribuidores de Linux y los desarrolladores del kernel, así que
51 podemos entregar nuevas características a los clientes sin forzarlos
52 a actualizar kernels completos o distribuciones.
53 \end{itemize}
55 \subsection{Aproximaciones tentadoras que no funcionan adecuadamente}
57 Hay dos maneras estándar de mantener una porción de software que debe
58 funcionar en muchos entornos diferentes.
60 La primera es mantener varias ramas, cada una pensada para un único
61 entorno. El problema de esta aproximación es que usted debe tener una
62 disciplina férrea con el flujo de cambios entre repositorios. Una
63 nueva característica o un arreglo de fallo deben empezar su vida en un
64 repositorio ``prístino'', y luego propagarse a cada repositorio de
65 backport. Los cambios para backports están más limitados respecto a
66 las ramas a las que deberían propagarse; un cambio para backport que
67 es aplicado a una rama en la que no corresponde probablemente hará que
68 el controlador no compile.
70 La segunda es mantener un único árbol de código fuente lleno de
71 declaraciones que activen o desactiven secciones de código dependiendo
72 del entorno objetivo. Ya que estos ``ifdefs'' no están permitidos en
73 el árbol del kernel de Linux, debe seguirse algún proceso manual o
74 automático para eliminarlos y producir un árbol limpio. Una base de
75 código mantenida de esta manera se convierte rápidamente en un nido de
76 ratas de bloques condicionales que son difíciles de entender y
77 mantener.
79 %TODO canónica?
80 Ninguno de estos enfoques es adecuado para situaciones en las que
81 usted no es ``dueño'' de la copia canónica de un árbol de fuentes. En
82 el caso de un controlador de Linux que es distribuido con el kernel
83 estándar, el árbol de Linux contiene la copia del código que será
84 considerada por el mundo como la canónica. La versión oficial de
85 ``mi'' controlador puede ser modificada por gente que no conozco, sin
86 que yo siquiera me entere de ello hasta después de que los cambios
87 aparecen en el árbol de Linus.
89 Estos enfoques tienen la debilidad adicional de dificultar la
90 %TODO upstream. no no es río arriba
91 generación de parches bien formados para enviarlos a la versión
92 oficial.
94 En principio, las Colas de Mercurial parecen ser un buen candidato
95 para administrar un escenario de desarrollo como el de arriba. Aunque
96 este es de hecho el caso, MQ tiene unas cuantas características
97 adicionales que hacen el trabajo más agradable.
99 \section{Aplicar parches condicionalmente mediante guardias}
101 Tal vez la mejor manera de conservar la cordura con tantos entornos
102 objetivo es poder escoger parches específicos para aplicar para cada
103 situación. MQ provee una característica llamada ``guardias''
104 (que se origina del comando \texttt{guards} de Quilt) que hace
105 precisamente ésto. Para empezar, creemos un repositorio sencillo para
106 experimentar.
107 \interaction{mq.guards.init}
108 Esto nos brinda un pequeño repositorio que contiene dos parches que no
109 tienen ninguna dependencia respecto al otro, porque tocan ficheros
110 diferentes.
112 La idea detrás de la aplicación condicional es que usted puede
113 ``etiquetar'' un parche con un \emph{guardia}, que simplemente es una
114 cadena de texto de su elección, y luego decirle a MQ que seleccione
115 guardias específicos para usar cuando aplique parches. MQ entonces
116 aplicará, u omitirá, un parche vigilado, dependiendo de los guardias
117 que usted haya seleccionado.
119 Un parche puede tener una cantidad arbitraria de guardias; cada uno es
120 \emph{positivo} (``aplique el parche si este guardia es
121 seleccionado'') o \emph{negativo} (``omita este parche si este guardia
122 es seleccionado''). Un parche sin guardias siempre es aplicado.
124 \section{Controlar los guardias de un parche}
126 %TODO tal vez no decir determinar, sino definir?
127 El comando \hgxcmd{mq}{qguard} le permite determinar qué guardias
128 deben aplicarse a un parche, o mostrar los guardias que están en
129 efecto. Sin ningún argumento, el comando muestra los guardias del
130 parche actual de la parte más alta de la pila.
131 \interaction{mq.guards.qguard}
132 Para poner un guardia positivo en un parche, prefije el nombre del
133 guardia con un ``\texttt{+}''.
134 \interaction{mq.guards.qguard.pos}
135 Para poner un guardia negativo en un parche, prefije el nombre del
136 guardia con un ``\texttt{-}''.
137 \interaction{mq.guards.qguard.neg}
139 \begin{note}
140 El comando \hgxcmd{mq}{qguard} \emph{pone} los guardias en un
141 parche; no los \emph{modifica}. Esto significa que si usted ejecuta
142 \hgcmdargs{qguard}{+a +b} sobre un parche, y luego
143 \hgcmdargs{qguard}{+c} en el mismo parche, el único guardia sobre el
144 parche después del comando será \texttt{+c}.
145 \end{note}
147 Mercurial almacena los guardias en el fichero \sfilename{series}; la
148 forma en que son almacenados es fácil tanto de entender como de editar
149 a mano. (En otras palabras, usted no tiene que usar el comando
150 \hgxcmd{mq}{qguard} si no lo desea; está bien simplemente editar el
151 fichero \sfilename{series})
152 \interaction{mq.guards.series}
154 \section{Selecccionar los guardias a usar}
156 %TODO tal vez no decir determinar, sino definir?
157 El comando \hgxcmd{mq}{qselect} determina qué guardias están activos
158 en cualquier momento. El efecto de esto es determinar qué parches
159 aplicará MQ la próxima vez que usted ejecute \hgxcmd{mq}{qpush}. No
160 tiene ningún otro efecto; en particular, no hace nada a los parches
161 que ya han sido aplicados.
163 Sin argumentos, el comando \hgxcmd{mq}{qselect} lista los guardias en
164 efecto actualmente, uno por cada línea de salida. Cada argumento es
165 tratado como el nombre de un guardia a aplicar.
166 \interaction{mq.guards.qselect.foo}
167 Si está interesado, los guardias seleccionados actualmente están
168 almacenados en el fichero \sfilename{guards}.
169 \interaction{mq.guards.qselect.cat}
170 Podemos ver el efecto que tienen los guardias seleccionados cuando
171 ejecutamos \hgxcmd{mq}{qpush}.
172 \interaction{mq.guards.qselect.qpush}
174 Un guardia no puede empezar con un caracter ``\texttt{+}'' o
175 ``\texttt{-}''. El nombre del guardia no debe contener espacios en
176 blanco, pero muchos otros caracteres son aceptables. Si usted trata de
177 usar un guardia con un nombre inválido, MQ se quejará:
178 \interaction{mq.guards.qselect.error}
179 Cambiar los guardias seleccionados cambia los parches que son
180 aplicados.
181 \interaction{mq.guards.qselect.quux}
182 Usted puede ver en el ejemplo de abajo que los guardias negativos
183 tienen precedencia sobre los guardias positivos.
184 \interaction{mq.guards.qselect.foobar}
186 \section{Reglas de MQ para aplicar parches}
188 Las reglas que MQ usa para decidir si debe aplicar un parche son las
189 siguientes.
190 \begin{itemize}
191 \item Un parche sin guardias es aplicado siempre.
192 \item Si el parche tiene algún guardia negativo que corresponda con
193 cualquiera de los guardias seleccionados, se salta el parche.
194 \item Si el parche tiene algún guardia positivo que corresponda con
195 cualquiera de los guardias seleccionados, se aplica el parche.
196 \item Si el parche tiene guardias positivos o negativos, pero ninguno
197 corresponde con cualquiera de los guardias seleccionados, se salta
198 el parche.
199 \end{itemize}
201 \section{Podar el entorno de trabajo}
203 En el trabajo del controlador de dispositivo que mencioné
204 anteriormente, yo no aplico los parches a un árbol normal del kernel
205 de Linux. En cambio, uso un repositorio que sólo contiene una
206 instantánea de los ficheros fuente y de cabecera que son relevantes
207 para el desarrollo de Infiniband. Este repositorio tiene un~1\% del
208 tamaño del repositorio del kernel, por lo que es más fácil trabajar
209 con él.
211 Luego escojo una versión ``base'' sobre la cual son aplicados los
212 parches. Es una instantánea del árbol del kernel de Linux en una
213 revisión de mi elección. Cuando tomo la instantánea, almaceno el ID de
214 conjunto de cambios en el mensaje de consignación. Ya que la
215 instantánea preserva la ``forma'' y el contenido de las partes
216 relevantes del árbol del kernel, puedo aplicar mis parches sobre mi
217 pequeño repositorio o sobre un árbol normal del kernel.
219 Normalmente, el árbol base sobre el que se aplican los parches debería
220 ser una instantánea de un árbol de desarrollo muy reciente. Esto
221 facilita mucho el desarrollo de parches que puedan ser enviados al
222 árbol oficial con pocas o ninguna modificación.
224 \section{Dividir el fichero \sfilename{series}}
226 Yo categorizo los parches en el fichero \sfilename{series} en una
227 serie de grupos lógicos. Cada sección de parches similares empieza con
228 un bloque de comentarios que describen el propósito de los parches que
229 le siguen.
231 La secuencia de grupos de parches que mantengo se muestra a
232 continuación. El orden de los grupos es importante; explicaré porqué
233 luego de que presente los grupos.
234 \begin{itemize}
235 \item El grupo ``aceptado''. Son parches que el equipo de desarrollo
236 ha enviado al mantenedor del subsistema Infiniband, y que él ha
237 aceptado, pero que no están presentes en la instantánea en la cual
238 está basada el repositorio pequeño. Estos son parches de
239 ``sólo lectura'', presentes únicamente para transformar el árbol en
240 un estado similar al del repositorio del mantenedor oficial.
241 \item El grupo ``revisar''. Parches que yo he enviado, pero sobre los
242 que que el mantenedor oficial ha solicitado modificaciones antes de
243 aceptarlos.
244 \item El grupo ``pendiente''. Parches que no he enviado al mantenedor
245 oficial, pero que ya están terminados. Estos parches serán de
246 ``sólo lectura'' por un buen tiempo. Si el mantenedor oficial los
247 acepta cuando los envíe, los moveré al final del grupo ``aceptado''.
248 Si él solicita que modificaciones en alguno de ellos, los moveré al
249 principio del grupo ``revisar''.
250 \item El grupo ``en proceso''. Parches que están siendo activamente
251 desarrollados, y no deberían ser enviados a ninguna parte aún.
252 \item El grupo ``backport''. Parches que adaptan el árbol de fuentes a
253 versiones antiguas del árbol del kernel.
254 \item El grupo ``no enviar''. Parches que por alguna razón nunca deben
255 ser enviados al mantenedor oficial del kernel. Por ejemplo, alguno
256 de esos parches podría cambiar las cadenas de identificación
257 embebidas del controlador para hacer más fácil la distinción, en
258 pruebas de campo, entre una versión del controlador de
259 salida-del-árbol y una versión entregada por un vendedor de alguna
260 distribución.
261 \end{itemize}
263 Ahora volvemos a las razones para ordenar los grupos de parches en
264 esta manera. Quisiéramos que los parches del fondo de la pila sean tan
265 estables como sea posible, para no tener que revisar parches más
266 arriba debido a cambios de contexto. Poner los parches que nunca
267 cambiarán en el primer lugar del fichero \sfilename{series} sirve a
268 este propósito.
270 También desearíamos que los parches que sabemos que debemos modificar
271 sean aplicados sobre un árbol de fuentes que se parezca al oficial
272 tanto como sea posible. Es por esto que mantenemos los parches
273 aceptados disponibles por una buena cantidad de tiempo.
275 Los parches ``backport'' y ``no enviar'' flotan al final del fichero
276 \sfilename{series}. Los parches de backport deben ser aplicados encima
277 de todos los otros parches, y los parches ``no enviar'' pueden
278 perfectamente quedarse fuera del camino.
280 \section{Maintaining the patch series}
282 In my work, I use a number of guards to control which patches are to
283 be applied.
285 \begin{itemize}
286 \item ``Accepted'' patches are guarded with \texttt{accepted}. I
287 enable this guard most of the time. When I'm applying the patches
288 on top of a tree where the patches are already present, I can turn
289 this patch off, and the patches that follow it will apply cleanly.
290 \item Patches that are ``finished'', but not yet submitted, have no
291 guards. If I'm applying the patch stack to a copy of the upstream
292 tree, I don't need to enable any guards in order to get a reasonably
293 safe source tree.
294 \item Those patches that need reworking before being resubmitted are
295 guarded with \texttt{rework}.
296 \item For those patches that are still under development, I use
297 \texttt{devel}.
298 \item A backport patch may have several guards, one for each version
299 of the kernel to which it applies. For example, a patch that
300 backports a piece of code to~2.6.9 will have a~\texttt{2.6.9} guard.
301 \end{itemize}
302 This variety of guards gives me considerable flexibility in
303 determining what kind of source tree I want to end up with. For most
304 situations, the selection of appropriate guards is automated during
305 the build process, but I can manually tune the guards to use for less
306 common circumstances.
308 \subsection{The art of writing backport patches}
310 Using MQ, writing a backport patch is a simple process. All such a
311 patch has to do is modify a piece of code that uses a kernel feature
312 not present in the older version of the kernel, so that the driver
313 continues to work correctly under that older version.
315 A useful goal when writing a good backport patch is to make your code
316 look as if it was written for the older version of the kernel you're
317 targeting. The less obtrusive the patch, the easier it will be to
318 understand and maintain. If you're writing a collection of backport
319 patches to avoid the ``rat's nest'' effect of lots of
320 \texttt{\#ifdef}s (hunks of source code that are only used
321 conditionally) in your code, don't introduce version-dependent
322 \texttt{\#ifdef}s into the patches. Instead, write several patches,
323 each of which makes unconditional changes, and control their
324 application using guards.
326 There are two reasons to divide backport patches into a distinct
327 group, away from the ``regular'' patches whose effects they modify.
328 The first is that intermingling the two makes it more difficult to use
329 a tool like the \hgext{patchbomb} extension to automate the process of
330 submitting the patches to an upstream maintainer. The second is that
331 a backport patch could perturb the context in which a subsequent
332 regular patch is applied, making it impossible to apply the regular
333 patch cleanly \emph{without} the earlier backport patch already being
334 applied.
336 \section{Useful tips for developing with MQ}
338 \subsection{Organising patches in directories}
340 If you're working on a substantial project with MQ, it's not difficult
341 to accumulate a large number of patches. For example, I have one
342 patch repository that contains over 250 patches.
344 If you can group these patches into separate logical categories, you
345 can if you like store them in different directories; MQ has no
346 problems with patch names that contain path separators.
348 \subsection{Viewing the history of a patch}
349 \label{mq-collab:tips:interdiff}
351 If you're developing a set of patches over a long time, it's a good
352 idea to maintain them in a repository, as discussed in
353 section~\ref{sec:mq:repo}. If you do so, you'll quickly discover that
354 using the \hgcmd{diff} command to look at the history of changes to a
355 patch is unworkable. This is in part because you're looking at the
356 second derivative of the real code (a diff of a diff), but also
357 because MQ adds noise to the process by modifying time stamps and
358 directory names when it updates a patch.
360 However, you can use the \hgext{extdiff} extension, which is bundled
361 with Mercurial, to turn a diff of two versions of a patch into
362 something readable. To do this, you will need a third-party package
363 called \package{patchutils}~\cite{web:patchutils}. This provides a
364 command named \command{interdiff}, which shows the differences between
365 two diffs as a diff. Used on two versions of the same diff, it
366 generates a diff that represents the diff from the first to the second
367 version.
369 You can enable the \hgext{extdiff} extension in the usual way, by
370 adding a line to the \rcsection{extensions} section of your \hgrc.
371 \begin{codesample2}
372 [extensions]
373 extdiff =
374 \end{codesample2}
375 The \command{interdiff} command expects to be passed the names of two
376 files, but the \hgext{extdiff} extension passes the program it runs a
377 pair of directories, each of which can contain an arbitrary number of
378 files. We thus need a small program that will run \command{interdiff}
379 on each pair of files in these two directories. This program is
380 available as \sfilename{hg-interdiff} in the \dirname{examples}
381 directory of the source code repository that accompanies this book.
382 \excode{hg-interdiff}
384 With the \sfilename{hg-interdiff} program in your shell's search path,
385 you can run it as follows, from inside an MQ patch directory:
386 \begin{codesample2}
387 hg extdiff -p hg-interdiff -r A:B my-change.patch
388 \end{codesample2}
389 Since you'll probably want to use this long-winded command a lot, you
390 can get \hgext{hgext} to make it available as a normal Mercurial
391 command, again by editing your \hgrc.
392 \begin{codesample2}
393 [extdiff]
394 cmd.interdiff = hg-interdiff
395 \end{codesample2}
396 This directs \hgext{hgext} to make an \texttt{interdiff} command
397 available, so you can now shorten the previous invocation of
398 \hgxcmd{extdiff}{extdiff} to something a little more wieldy.
399 \begin{codesample2}
400 hg interdiff -r A:B my-change.patch
401 \end{codesample2}
403 \begin{note}
404 The \command{interdiff} command works well only if the underlying
405 files against which versions of a patch are generated remain the
406 same. If you create a patch, modify the underlying files, and then
407 regenerate the patch, \command{interdiff} may not produce useful
408 output.
409 \end{note}
411 The \hgext{extdiff} extension is useful for more than merely improving
412 the presentation of MQ~patches. To read more about it, go to
413 section~\ref{sec:hgext:extdiff}.
415 %%% Local Variables:
416 %%% mode: latex
417 %%% TeX-master: "00book"
418 %%% End: