hgbook
diff 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 |
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: