hgbook

annotate 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
rev   line source
bos@104 1 \chapter{Advanced uses of Mercurial Queues}
bos@104 2
bos@104 3 While it's easy to pick up straightforward uses of Mercurial Queues,
bos@104 4 use of a little discipline and some of MQ's less frequently used
bos@104 5 capabilities makes it possible to work in complicated development
bos@104 6 environments.
bos@104 7
bos@104 8 In this chapter, I will discuss a technique I have developed to manage
bos@104 9 the development of an Infiniband device driver for the Linux kernel.
bos@104 10 The driver in question is large (at least as drivers go), with 25,000
bos@104 11 lines of code spread across 35 source files. It is maintained by a
bos@104 12 small team of developers.
bos@104 13
bos@104 14 While much of the material in this chapter is specific to Linux, the
bos@104 15 same principles apply to any code base for which you're not the
bos@104 16 primary owner, and upon which you need to do a lot of development.
bos@104 17
bos@104 18 \section{The problem of many targets}
bos@104 19
bos@104 20 The Linux kernel changes rapidly, and has never been internally
bos@104 21 stable; developers frequently make drastic changes between releases.
bos@104 22 This means that a version of the driver that works well with a
bos@104 23 particular released version of the kernel will not even \emph{compile}
bos@104 24 correctly against, typically, any other version.
bos@104 25
bos@104 26 To maintain a driver, we have to keep a number of distinct versions of
bos@104 27 Linux in mind.
bos@104 28 \begin{itemize}
bos@104 29 \item One target is the main Linux kernel development tree.
bos@104 30 Maintenance of the code is in this case partly shared by other
bos@104 31 developers in the kernel community, who make ``drive-by''
bos@104 32 modifications to the driver as they develop and refine kernel
bos@104 33 subsystems.
bos@104 34 \item We also maintain a number of ``backports'' to older versions of
bos@104 35 the Linux kernel, to support the needs of customers who are running
bos@104 36 older Linux distributions that do not incorporate our drivers.
bos@104 37 \item Finally, we make software releases on a schedule that is
bos@104 38 necessarily not aligned with those used by Linux distributors and
bos@104 39 kernel developers, so that we can deliver new features to customers
bos@104 40 without forcing them to upgrade their entire kernels or
bos@104 41 distributions.
bos@104 42 \end{itemize}
bos@104 43
bos@104 44 \subsection{Tempting approaches that don't work well}
bos@104 45
bos@104 46 There are two ``standard'' ways to maintain a piece of software that
bos@104 47 has to target many different environments.
bos@104 48
bos@104 49 The first is to maintain a number of branches, each intended for a
bos@104 50 single target. The trouble with this approach is that you must
bos@104 51 maintain iron discipline in the flow of changes between repositories.
bos@104 52 A new feature or bug fix must start life in a ``pristine'' repository,
bos@104 53 then percolate out to every backport repository. Backport changes are
bos@104 54 more limited in the branches they should propagate to; a backport
bos@104 55 change that is applied to a branch where it doesn't belong will
bos@104 56 probably stop the driver from compiling.
bos@104 57
bos@104 58 The second is to maintain a single source tree filled with conditional
bos@104 59 statements that turn chunks of code on or off depending on the
bos@104 60 intended target. Because these ``ifdefs'' are not allowed in the
bos@104 61 Linux kernel tree, a manual or automatic process must be followed to
bos@104 62 strip them out and yield a clean tree. A code base maintained in this
bos@104 63 fashion rapidly becomes a rat's nest of conditional blocks that are
bos@104 64 difficult to understand and maintain.
bos@104 65
bos@104 66 Neither of these approaches is well suited to a situation where you
bos@104 67 don't ``own'' the canonical copy of a source tree. In the case of a
bos@104 68 Linux driver that is distributed with the standard kernel, Linus's
bos@104 69 tree contains the copy of the code that will be treated by the world
bos@104 70 as canonical. The upstream version of ``my'' driver can be modified
bos@104 71 by people I don't know, without me even finding out about it until
bos@104 72 after the changes show up in Linus's tree.
bos@104 73
bos@104 74 These approaches have the added weakness of making it difficult to
bos@104 75 generate well-formed patches to submit upstream.
bos@104 76
bos@104 77 In principle, Mercurial Queues seems like a good candidate to manage a
bos@104 78 development scenario such as the above. While this is indeed the
bos@104 79 case, MQ contains a few added features that make the job more
bos@104 80 pleasant.
bos@104 81
bos@104 82 \section{Conditionally applying patches with guards}
bos@104 83
bos@104 84 Perhaps the best way to maintain sanity with so many targets is to be
bos@104 85 able to choose specific patches to apply for a given situation. MQ
bos@104 86 provides a feature called ``guards'' (which originates with quilt's
bos@104 87 \texttt{guards} command) that does just this. To start off, let's
bos@104 88 create a simple repository for experimenting in.
bos@104 89 \interaction{mq.guards.init}
bos@104 90 This gives us a tiny repository that contains two patches that don't
bos@104 91 have any dependencies on each other, because they touch different files.
bos@104 92
bos@104 93 The idea behind conditional application is that you can ``tag'' a
bos@104 94 patch with a \emph{guard}, which is simply a text string of your
bos@104 95 choosing, then tell MQ to select specific guards to use when applying
bos@104 96 patches. MQ will then either apply, or skip over, a guarded patch,
bos@104 97 depending on the guards that you have selected.
bos@104 98
bos@104 99 A patch can have an arbitrary number of guards;
bos@104 100 each one is \emph{positive} (``apply this patch if this guard is
bos@104 101 selected'') or \emph{negative} (``skip this patch if this guard is
bos@104 102 selected''). A patch with no guards is always applied.
bos@104 103
bos@104 104 \section{Controlling the guards on a patch}
bos@104 105
bos@104 106 The \hgcmd{qguard} command lets you determine which guards should
bos@104 107 apply to a patch, or display the guards that are already in effect.
bos@104 108 Without any arguments, it displays the guards on the current topmost
bos@104 109 patch.
bos@104 110 \interaction{mq.guards.qguard}
bos@104 111 To set a positive guard on a patch, prefix the name of the guard with
bos@104 112 a ``\texttt{+}''.
bos@104 113 \interaction{mq.guards.qguard.pos}
bos@104 114 To set a negative guard on a patch, prefix the name of the guard with
bos@104 115 a ``\texttt{-}''.
bos@104 116 \interaction{mq.guards.qguard.neg}
bos@104 117
bos@104 118 \begin{note}
bos@104 119 The \hgcmd{qguard} command \emph{sets} the guards on a patch; it
bos@104 120 doesn't \emph{modify} them. What this means is that if you run
bos@104 121 \hgcmdargs{qguard}{+a +b} on a patch, then \hgcmdargs{qguard}{+c} on
bos@104 122 the same patch, the \emph{only} guard that will be set on it
bos@104 123 afterwards is \texttt{+c}.
bos@104 124 \end{note}
bos@104 125
bos@104 126 Mercurial stores guards in the \sfilename{series} file; the form in
bos@104 127 which they are stored is easy both to understand and to edit by hand.
bos@104 128 (In other words, you don't have to use the \hgcmd{qguard} command if
bos@104 129 you don't want to; it's okay to simply edit the \sfilename{series}
bos@104 130 file.)
bos@104 131 \interaction{mq.guards.series}
bos@104 132
bos@104 133 \section{Selecting the guards to use}
bos@104 134
bos@104 135 The \hgcmd{qselect} command determines which guards are active at a
bos@104 136 given time. The effect of this is to determine which patches MQ will
bos@104 137 apply the next time you run \hgcmd{qpush}. It has no other effect; in
bos@104 138 particular, it doesn't do anything to patches that are already
bos@104 139 applied.
bos@104 140
bos@104 141 With no arguments, the \hgcmd{qselect} command lists the guards
bos@104 142 currently in effect, one per line of output. Each argument is treated
bos@104 143 as the name of a guard to apply.
bos@104 144 \interaction{mq.guards.qselect.foo}
bos@104 145 In case you're interested, the currently selected guards are stored in
bos@104 146 the \sfilename{guards} file.
bos@104 147 \interaction{mq.guards.qselect.cat}
bos@104 148 We can see the effect the selected guards have when we run
bos@104 149 \hgcmd{qpush}.
bos@104 150 \interaction{mq.guards.qselect.qpush}
bos@104 151
bos@104 152 A guard cannot start with a ``\texttt{+}'' or ``\texttt{-}''
bos@104 153 character.
bos@104 154 \interaction{mq.guards.qselect.error}
bos@104 155 Changing the selected guards changes the patches that are applied.
bos@104 156 \interaction{mq.guards.qselect.quux}
bos@104 157 You can see here that negative guards take precedence over positive
bos@104 158 guards.
bos@104 159 \interaction{mq.guards.qselect.foobar}
bos@104 160
bos@104 161 %%% Local Variables:
bos@104 162 %%% mode: latex
bos@104 163 %%% TeX-master: "00book"
bos@104 164 %%% End: