hgbook

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