hgbook

diff en/mq-collab.tex @ 104:32bf9a5f22c0

Refactor MQ chapter into three.
Start text on guards.
author Bryan O'Sullivan <bos@serpentine.com>
date Fri Oct 20 16:56:20 2006 -0700 (2006-10-20)
parents
children ecacb6b4c9fd
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/en/mq-collab.tex	Fri Oct 20 16:56:20 2006 -0700
     1.3 @@ -0,0 +1,164 @@
     1.4 +\chapter{Advanced uses of Mercurial Queues}
     1.5 +
     1.6 +While it's easy to pick up straightforward uses of Mercurial Queues,
     1.7 +use of a little discipline and some of MQ's less frequently used
     1.8 +capabilities makes it possible to work in complicated development
     1.9 +environments.
    1.10 +
    1.11 +In this chapter, I will discuss a technique I have developed to manage
    1.12 +the development of an Infiniband device driver for the Linux kernel.
    1.13 +The driver in question is large (at least as drivers go), with 25,000
    1.14 +lines of code spread across 35 source files.  It is maintained by a
    1.15 +small team of developers.
    1.16 +
    1.17 +While much of the material in this chapter is specific to Linux, the
    1.18 +same principles apply to any code base for which you're not the
    1.19 +primary owner, and upon which you need to do a lot of development.
    1.20 +
    1.21 +\section{The problem of many targets}
    1.22 +
    1.23 +The Linux kernel changes rapidly, and has never been internally
    1.24 +stable; developers frequently make drastic changes between releases.
    1.25 +This means that a version of the driver that works well with a
    1.26 +particular released version of the kernel will not even \emph{compile}
    1.27 +correctly against, typically, any other version.
    1.28 +
    1.29 +To maintain a driver, we have to keep a number of distinct versions of
    1.30 +Linux in mind.
    1.31 +\begin{itemize}
    1.32 +\item One target is the main Linux kernel development tree.
    1.33 +  Maintenance of the code is in this case partly shared by other
    1.34 +  developers in the kernel community, who make ``drive-by''
    1.35 +  modifications to the driver as they develop and refine kernel
    1.36 +  subsystems.
    1.37 +\item We also maintain a number of ``backports'' to older versions of
    1.38 +  the Linux kernel, to support the needs of customers who are running
    1.39 +  older Linux distributions that do not incorporate our drivers.
    1.40 +\item Finally, we make software releases on a schedule that is
    1.41 +  necessarily not aligned with those used by Linux distributors and
    1.42 +  kernel developers, so that we can deliver new features to customers
    1.43 +  without forcing them to upgrade their entire kernels or
    1.44 +  distributions.
    1.45 +\end{itemize}
    1.46 +
    1.47 +\subsection{Tempting approaches that don't work well}
    1.48 +
    1.49 +There are two ``standard'' ways to maintain a piece of software that
    1.50 +has to target many different environments.
    1.51 +
    1.52 +The first is to maintain a number of branches, each intended for a
    1.53 +single target.  The trouble with this approach is that you must
    1.54 +maintain iron discipline in the flow of changes between repositories.
    1.55 +A new feature or bug fix must start life in a ``pristine'' repository,
    1.56 +then percolate out to every backport repository.  Backport changes are
    1.57 +more limited in the branches they should propagate to; a backport
    1.58 +change that is applied to a branch where it doesn't belong will
    1.59 +probably stop the driver from compiling.
    1.60 +
    1.61 +The second is to maintain a single source tree filled with conditional
    1.62 +statements that turn chunks of code on or off depending on the
    1.63 +intended target.  Because these ``ifdefs'' are not allowed in the
    1.64 +Linux kernel tree, a manual or automatic process must be followed to
    1.65 +strip them out and yield a clean tree.  A code base maintained in this
    1.66 +fashion rapidly becomes a rat's nest of conditional blocks that are
    1.67 +difficult to understand and maintain.
    1.68 +
    1.69 +Neither of these approaches is well suited to a situation where you
    1.70 +don't ``own'' the canonical copy of a source tree.  In the case of a
    1.71 +Linux driver that is distributed with the standard kernel, Linus's
    1.72 +tree contains the copy of the code that will be treated by the world
    1.73 +as canonical.  The upstream version of ``my'' driver can be modified
    1.74 +by people I don't know, without me even finding out about it until
    1.75 +after the changes show up in Linus's tree.  
    1.76 +
    1.77 +These approaches have the added weakness of making it difficult to
    1.78 +generate well-formed patches to submit upstream.
    1.79 +
    1.80 +In principle, Mercurial Queues seems like a good candidate to manage a
    1.81 +development scenario such as the above.  While this is indeed the
    1.82 +case, MQ contains a few added features that make the job more
    1.83 +pleasant.
    1.84 +
    1.85 +\section{Conditionally applying patches with guards}
    1.86 +
    1.87 +Perhaps the best way to maintain sanity with so many targets is to be
    1.88 +able to choose specific patches to apply for a given situation.  MQ
    1.89 +provides a feature called ``guards'' (which originates with quilt's
    1.90 +\texttt{guards} command) that does just this.  To start off, let's
    1.91 +create a simple repository for experimenting in.
    1.92 +\interaction{mq.guards.init}
    1.93 +This gives us a tiny repository that contains two patches that don't
    1.94 +have any dependencies on each other, because they touch different files.
    1.95 +
    1.96 +The idea behind conditional application is that you can ``tag'' a
    1.97 +patch with a \emph{guard}, which is simply a text string of your
    1.98 +choosing, then tell MQ to select specific guards to use when applying
    1.99 +patches.  MQ will then either apply, or skip over, a guarded patch,
   1.100 +depending on the guards that you have selected.
   1.101 +
   1.102 +A patch can have an arbitrary number of guards;
   1.103 +each one is \emph{positive} (``apply this patch if this guard is
   1.104 +selected'') or \emph{negative} (``skip this patch if this guard is
   1.105 +selected'').  A patch with no guards is always applied.
   1.106 +
   1.107 +\section{Controlling the guards on a patch}
   1.108 +
   1.109 +The \hgcmd{qguard} command lets you determine which guards should
   1.110 +apply to a patch, or display the guards that are already in effect.
   1.111 +Without any arguments, it displays the guards on the current topmost
   1.112 +patch.
   1.113 +\interaction{mq.guards.qguard}
   1.114 +To set a positive guard on a patch, prefix the name of the guard with
   1.115 +a ``\texttt{+}''.
   1.116 +\interaction{mq.guards.qguard.pos}
   1.117 +To set a negative guard on a patch, prefix the name of the guard with
   1.118 +a ``\texttt{-}''.
   1.119 +\interaction{mq.guards.qguard.neg}
   1.120 +
   1.121 +\begin{note}
   1.122 +  The \hgcmd{qguard} command \emph{sets} the guards on a patch; it
   1.123 +  doesn't \emph{modify} them.  What this means is that if you run
   1.124 +  \hgcmdargs{qguard}{+a +b} on a patch, then \hgcmdargs{qguard}{+c} on
   1.125 +  the same patch, the \emph{only} guard that will be set on it
   1.126 +  afterwards is \texttt{+c}.
   1.127 +\end{note}
   1.128 +
   1.129 +Mercurial stores guards in the \sfilename{series} file; the form in
   1.130 +which they are stored is easy both to understand and to edit by hand.
   1.131 +(In other words, you don't have to use the \hgcmd{qguard} command if
   1.132 +you don't want to; it's okay to simply edit the \sfilename{series}
   1.133 +file.)
   1.134 +\interaction{mq.guards.series}
   1.135 +
   1.136 +\section{Selecting the guards to use}
   1.137 +
   1.138 +The \hgcmd{qselect} command determines which guards are active at a
   1.139 +given time.  The effect of this is to determine which patches MQ will
   1.140 +apply the next time you run \hgcmd{qpush}.  It has no other effect; in
   1.141 +particular, it doesn't do anything to patches that are already
   1.142 +applied.
   1.143 +
   1.144 +With no arguments, the \hgcmd{qselect} command lists the guards
   1.145 +currently in effect, one per line of output.  Each argument is treated
   1.146 +as the name of a guard to apply.
   1.147 +\interaction{mq.guards.qselect.foo}
   1.148 +In case you're interested, the currently selected guards are stored in
   1.149 +the \sfilename{guards} file.
   1.150 +\interaction{mq.guards.qselect.cat}
   1.151 +We can see the effect the selected guards have when we run
   1.152 +\hgcmd{qpush}.
   1.153 +\interaction{mq.guards.qselect.qpush}
   1.154 +
   1.155 +A guard cannot start with a ``\texttt{+}'' or ``\texttt{-}''
   1.156 +character.
   1.157 +\interaction{mq.guards.qselect.error}
   1.158 +Changing the selected guards changes the patches that are applied.
   1.159 +\interaction{mq.guards.qselect.quux}
   1.160 +You can see here that negative guards take precedence over positive
   1.161 +guards.
   1.162 +\interaction{mq.guards.qselect.foobar}
   1.163 +
   1.164 +%%% Local Variables: 
   1.165 +%%% mode: latex
   1.166 +%%% TeX-master: "00book"
   1.167 +%%% End: