hgbook
annotate en/mq-collab.tex @ 104:32bf9a5f22c0
Refactor MQ chapter into three.
Start text on guards.
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: |