hgbook
diff fr/ch13-mq-collab.xml @ 1113:613690ad6a9c
Merge with dongsheng
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Sat Jul 10 16:21:26 2010 +0100 (2010-07-10) |
parents | 6b680d569bb4 f0110009e946 |
children |
line diff
1.1 --- a/fr/ch13-mq-collab.xml Sun Aug 16 04:58:01 2009 +0200 1.2 +++ b/fr/ch13-mq-collab.xml Sat Jul 10 16:21:26 2010 +0100 1.3 @@ -1,499 +1,525 @@ 1.4 <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> 1.5 1.6 -<chapter> 1.7 -<title>Advanced uses of Mercurial Queues</title> 1.8 -<para>\label{chap:mq-collab}</para> 1.9 - 1.10 -<para>While it's easy to pick up straightforward uses of Mercurial Queues, 1.11 -use of a little discipline and some of MQ's less frequently used 1.12 -capabilities makes it possible to work in complicated development 1.13 -environments.</para> 1.14 - 1.15 -<para>In this chapter, I will use as an example a technique I have used to 1.16 -manage the development of an Infiniband device driver for the Linux 1.17 -kernel. The driver in question is large (at least as drivers go), 1.18 -with 25,000 lines of code spread across 35 source files. It is 1.19 -maintained by a small team of developers.</para> 1.20 - 1.21 -<para>While much of the material in this chapter is specific to Linux, the 1.22 -same principles apply to any code base for which you're not the 1.23 -primary owner, and upon which you need to do a lot of development.</para> 1.24 - 1.25 -<sect1> 1.26 -<title>The problem of many targets</title> 1.27 - 1.28 -<para>The Linux kernel changes rapidly, and has never been internally 1.29 -stable; developers frequently make drastic changes between releases. 1.30 -This means that a version of the driver that works well with a 1.31 -particular released version of the kernel will not even <emphasis>compile</emphasis> 1.32 -correctly against, typically, any other version.</para> 1.33 - 1.34 -<para>To maintain a driver, we have to keep a number of distinct versions of 1.35 -Linux in mind.</para> 1.36 -<itemizedlist> 1.37 -<listitem><para>One target is the main Linux kernel development tree. 1.38 - Maintenance of the code is in this case partly shared by other 1.39 - developers in the kernel community, who make <quote>drive-by</quote> 1.40 - modifications to the driver as they develop and refine kernel 1.41 - subsystems.</para> 1.42 -</listitem> 1.43 -<listitem><para>We also maintain a number of <quote>backports</quote> to older versions of 1.44 - the Linux kernel, to support the needs of customers who are running 1.45 - older Linux distributions that do not incorporate our drivers. (To 1.46 - <emphasis>backport</emphasis> a piece of code is to modify it to work in an older 1.47 - version of its target environment than the version it was developed 1.48 - for.)</para> 1.49 -</listitem> 1.50 -<listitem><para>Finally, we make software releases on a schedule that is 1.51 - necessarily not aligned with those used by Linux distributors and 1.52 - kernel developers, so that we can deliver new features to customers 1.53 - without forcing them to upgrade their entire kernels or 1.54 - distributions. 1.55 -</para> 1.56 -</listitem></itemizedlist> 1.57 - 1.58 -<sect2> 1.59 -<title>Tempting approaches that don't work well</title> 1.60 - 1.61 -<para>There are two <quote>standard</quote> ways to maintain a piece of software that 1.62 -has to target many different environments. 1.63 -</para> 1.64 - 1.65 -<para>The first is to maintain a number of branches, each intended for a 1.66 -single target. The trouble with this approach is that you must 1.67 -maintain iron discipline in the flow of changes between repositories. 1.68 -A new feature or bug fix must start life in a <quote>pristine</quote> repository, 1.69 -then percolate out to every backport repository. Backport changes are 1.70 -more limited in the branches they should propagate to; a backport 1.71 -change that is applied to a branch where it doesn't belong will 1.72 -probably stop the driver from compiling. 1.73 -</para> 1.74 - 1.75 -<para>The second is to maintain a single source tree filled with conditional 1.76 -statements that turn chunks of code on or off depending on the 1.77 -intended target. Because these <quote>ifdefs</quote> are not allowed in the 1.78 -Linux kernel tree, a manual or automatic process must be followed to 1.79 -strip them out and yield a clean tree. A code base maintained in this 1.80 -fashion rapidly becomes a rat's nest of conditional blocks that are 1.81 -difficult to understand and maintain. 1.82 -</para> 1.83 - 1.84 -<para>Neither of these approaches is well suited to a situation where you 1.85 -don't <quote>own</quote> the canonical copy of a source tree. In the case of a 1.86 -Linux driver that is distributed with the standard kernel, Linus's 1.87 -tree contains the copy of the code that will be treated by the world 1.88 -as canonical. The upstream version of <quote>my</quote> driver can be modified 1.89 -by people I don't know, without me even finding out about it until 1.90 -after the changes show up in Linus's tree. 1.91 -</para> 1.92 - 1.93 -<para>These approaches have the added weakness of making it difficult to 1.94 -generate well-formed patches to submit upstream. 1.95 -</para> 1.96 - 1.97 -<para>In principle, Mercurial Queues seems like a good candidate to manage a 1.98 -development scenario such as the above. While this is indeed the 1.99 -case, MQ contains a few added features that make the job more 1.100 -pleasant. 1.101 -</para> 1.102 - 1.103 -<para>\section{Conditionally applying patches with 1.104 - guards} 1.105 -</para> 1.106 - 1.107 -<para>Perhaps the best way to maintain sanity with so many targets is to be 1.108 -able to choose specific patches to apply for a given situation. MQ 1.109 -provides a feature called <quote>guards</quote> (which originates with quilt's 1.110 -<literal>guards</literal> command) that does just this. To start off, let's 1.111 -create a simple repository for experimenting in. 1.112 -<!-- &interaction.mq.guards.init; --> 1.113 -This gives us a tiny repository that contains two patches that don't 1.114 -have any dependencies on each other, because they touch different files. 1.115 -</para> 1.116 - 1.117 -<para>The idea behind conditional application is that you can <quote>tag</quote> a 1.118 -patch with a <emphasis>guard</emphasis>, which is simply a text string of your 1.119 -choosing, then tell MQ to select specific guards to use when applying 1.120 -patches. MQ will then either apply, or skip over, a guarded patch, 1.121 -depending on the guards that you have selected. 1.122 -</para> 1.123 - 1.124 -<para>A patch can have an arbitrary number of guards; 1.125 -each one is <emphasis>positive</emphasis> (<quote>apply this patch if this guard is 1.126 -selected</quote>) or <emphasis>negative</emphasis> (<quote>skip this patch if this guard is 1.127 -selected</quote>). A patch with no guards is always applied. 1.128 -</para> 1.129 - 1.130 -</sect2> 1.131 -</sect1> 1.132 -<sect1> 1.133 -<title>Controlling the guards on a patch</title> 1.134 - 1.135 -<para>The <command role="hg-ext-mq">qguard</command> command lets you determine which guards should 1.136 -apply to a patch, or display the guards that are already in effect. 1.137 -Without any arguments, it displays the guards on the current topmost 1.138 -patch. 1.139 -<!-- &interaction.mq.guards.qguard; --> 1.140 -To set a positive guard on a patch, prefix the name of the guard with 1.141 -a <quote><literal>+</literal></quote>. 1.142 -<!-- &interaction.mq.guards.qguard.pos; --> 1.143 -To set a negative guard on a patch, prefix the name of the guard with 1.144 -a <quote><literal>-</literal></quote>. 1.145 -<!-- &interaction.mq.guards.qguard.neg; --> 1.146 -</para> 1.147 - 1.148 -<note> 1.149 -<para> The <command role="hg-ext-mq">qguard</command> command <emphasis>sets</emphasis> the guards on a patch; it 1.150 - doesn't <emphasis>modify</emphasis> them. What this means is that if you run 1.151 - <command role="hg-cmd">hg qguard +a +b</command> on a patch, then <command role="hg-cmd">hg qguard +c</command> on 1.152 - the same patch, the <emphasis>only</emphasis> guard that will be set on it 1.153 - afterwards is <literal>+c</literal>. 1.154 -</para> 1.155 -</note> 1.156 - 1.157 -<para>Mercurial stores guards in the <filename role="special">series</filename> file; the form in 1.158 -which they are stored is easy both to understand and to edit by hand. 1.159 -(In other words, you don't have to use the <command role="hg-ext-mq">qguard</command> command if 1.160 -you don't want to; it's okay to simply edit the <filename role="special">series</filename> 1.161 -file.) 1.162 -<!-- &interaction.mq.guards.series; --> 1.163 -</para> 1.164 - 1.165 -</sect1> 1.166 -<sect1> 1.167 -<title>Selecting the guards to use</title> 1.168 - 1.169 -<para>The <command role="hg-ext-mq">qselect</command> command determines which guards are active at a 1.170 -given time. The effect of this is to determine which patches MQ will 1.171 -apply the next time you run <command role="hg-ext-mq">qpush</command>. It has no other effect; in 1.172 -particular, it doesn't do anything to patches that are already 1.173 -applied. 1.174 -</para> 1.175 - 1.176 -<para>With no arguments, the <command role="hg-ext-mq">qselect</command> command lists the guards 1.177 -currently in effect, one per line of output. Each argument is treated 1.178 -as the name of a guard to apply. 1.179 -<!-- &interaction.mq.guards.qselect.foo; --> 1.180 -In case you're interested, the currently selected guards are stored in 1.181 -the <filename role="special">guards</filename> file. 1.182 -<!-- &interaction.mq.guards.qselect.cat; --> 1.183 -We can see the effect the selected guards have when we run 1.184 -<command role="hg-ext-mq">qpush</command>. 1.185 -<!-- &interaction.mq.guards.qselect.qpush; --> 1.186 -</para> 1.187 - 1.188 -<para>A guard cannot start with a <quote><literal>+</literal></quote> or <quote><literal>-</literal></quote> 1.189 -character. The name of a guard must not contain white space, but most 1.190 -other characters are acceptable. If you try to use a guard with an 1.191 -invalid name, MQ will complain: 1.192 -<!-- &interaction.mq.guards.qselect.error; --> 1.193 -Changing the selected guards changes the patches that are applied. 1.194 -<!-- &interaction.mq.guards.qselect.quux; --> 1.195 -You can see in the example below that negative guards take precedence 1.196 -over positive guards. 1.197 -<!-- &interaction.mq.guards.qselect.foobar; --> 1.198 -</para> 1.199 - 1.200 -</sect1> 1.201 -<sect1> 1.202 -<title>MQ's rules for applying patches</title> 1.203 - 1.204 -<para>The rules that MQ uses when deciding whether to apply a patch 1.205 -are as follows. 1.206 -</para> 1.207 -<itemizedlist> 1.208 -<listitem><para>A patch that has no guards is always applied. 1.209 -</para> 1.210 -</listitem> 1.211 -<listitem><para>If the patch has any negative guard that matches any currently 1.212 - selected guard, the patch is skipped. 1.213 -</para> 1.214 -</listitem> 1.215 -<listitem><para>If the patch has any positive guard that matches any currently 1.216 - selected guard, the patch is applied. 1.217 -</para> 1.218 -</listitem> 1.219 -<listitem><para>If the patch has positive or negative guards, but none matches 1.220 - any currently selected guard, the patch is skipped. 1.221 -</para> 1.222 -</listitem></itemizedlist> 1.223 - 1.224 -</sect1> 1.225 -<sect1> 1.226 -<title>Trimming the work environment</title> 1.227 - 1.228 -<para>In working on the device driver I mentioned earlier, I don't apply the 1.229 -patches to a normal Linux kernel tree. Instead, I use a repository 1.230 -that contains only a snapshot of the source files and headers that are 1.231 -relevant to Infiniband development. This repository is 1% the size 1.232 -of a kernel repository, so it's easier to work with. 1.233 -</para> 1.234 - 1.235 -<para>I then choose a <quote>base</quote> version on top of which the patches are 1.236 -applied. This is a snapshot of the Linux kernel tree as of a revision 1.237 -of my choosing. When I take the snapshot, I record the changeset ID 1.238 -from the kernel repository in the commit message. Since the snapshot 1.239 -preserves the <quote>shape</quote> and content of the relevant parts of the 1.240 -kernel tree, I can apply my patches on top of either my tiny 1.241 -repository or a normal kernel tree. 1.242 -</para> 1.243 - 1.244 -<para>Normally, the base tree atop which the patches apply should be a 1.245 -snapshot of a very recent upstream tree. This best facilitates the 1.246 -development of patches that can easily be submitted upstream with few 1.247 -or no modifications. 1.248 -</para> 1.249 - 1.250 -</sect1> 1.251 -<sect1> 1.252 -<title>Dividing up the <filename role="special">series</filename> file</title> 1.253 - 1.254 -<para>I categorise the patches in the <filename role="special">series</filename> file into a number 1.255 -of logical groups. Each section of like patches begins with a block 1.256 -of comments that describes the purpose of the patches that follow. 1.257 -</para> 1.258 - 1.259 -<para>The sequence of patch groups that I maintain follows. The ordering of 1.260 -these groups is important; I'll describe why after I introduce the 1.261 -groups. 1.262 -</para> 1.263 -<itemizedlist> 1.264 -<listitem><para>The <quote>accepted</quote> group. Patches that the development team has 1.265 - submitted to the maintainer of the Infiniband subsystem, and which 1.266 - he has accepted, but which are not present in the snapshot that the 1.267 - tiny repository is based on. These are <quote>read only</quote> patches, 1.268 - present only to transform the tree into a similar state as it is in 1.269 - the upstream maintainer's repository. 1.270 -</para> 1.271 -</listitem> 1.272 -<listitem><para>The <quote>rework</quote> group. Patches that I have submitted, but that 1.273 - the upstream maintainer has requested modifications to before he 1.274 - will accept them. 1.275 -</para> 1.276 -</listitem> 1.277 -<listitem><para>The <quote>pending</quote> group. Patches that I have not yet submitted to 1.278 - the upstream maintainer, but which we have finished working on. 1.279 - These will be <quote>read only</quote> for a while. If the upstream maintainer 1.280 - accepts them upon submission, I'll move them to the end of the 1.281 - <quote>accepted</quote> group. If he requests that I modify any, I'll move 1.282 - them to the beginning of the <quote>rework</quote> group. 1.283 -</para> 1.284 -</listitem> 1.285 -<listitem><para>The <quote>in progress</quote> group. Patches that are actively being 1.286 - developed, and should not be submitted anywhere yet. 1.287 -</para> 1.288 -</listitem> 1.289 -<listitem><para>The <quote>backport</quote> group. Patches that adapt the source tree to 1.290 - older versions of the kernel tree. 1.291 -</para> 1.292 -</listitem> 1.293 -<listitem><para>The <quote>do not ship</quote> group. Patches that for some reason should 1.294 - never be submitted upstream. For example, one such patch might 1.295 - change embedded driver identification strings to make it easier to 1.296 - distinguish, in the field, between an out-of-tree version of the 1.297 - driver and a version shipped by a distribution vendor. 1.298 -</para> 1.299 -</listitem></itemizedlist> 1.300 - 1.301 -<para>Now to return to the reasons for ordering groups of patches in this 1.302 -way. We would like the lowest patches in the stack to be as stable as 1.303 -possible, so that we will not need to rework higher patches due to 1.304 -changes in context. Putting patches that will never be changed first 1.305 -in the <filename role="special">series</filename> file serves this purpose. 1.306 -</para> 1.307 - 1.308 -<para>We would also like the patches that we know we'll need to modify to be 1.309 -applied on top of a source tree that resembles the upstream tree as 1.310 -closely as possible. This is why we keep accepted patches around for 1.311 -a while. 1.312 -</para> 1.313 - 1.314 -<para>The <quote>backport</quote> and <quote>do not ship</quote> patches float at the end of the 1.315 -<filename role="special">series</filename> file. The backport patches must be applied on top 1.316 -of all other patches, and the <quote>do not ship</quote> patches might as well 1.317 -stay out of harm's way. 1.318 -</para> 1.319 - 1.320 -</sect1> 1.321 -<sect1> 1.322 -<title>Maintaining the patch series</title> 1.323 - 1.324 -<para>In my work, I use a number of guards to control which patches are to 1.325 -be applied. 1.326 -</para> 1.327 - 1.328 -<itemizedlist> 1.329 -<listitem><para><quote>Accepted</quote> patches are guarded with <literal>accepted</literal>. I 1.330 - enable this guard most of the time. When I'm applying the patches 1.331 - on top of a tree where the patches are already present, I can turn 1.332 - this patch off, and the patches that follow it will apply cleanly. 1.333 -</para> 1.334 -</listitem> 1.335 -<listitem><para>Patches that are <quote>finished</quote>, but not yet submitted, have no 1.336 - guards. If I'm applying the patch stack to a copy of the upstream 1.337 - tree, I don't need to enable any guards in order to get a reasonably 1.338 - safe source tree. 1.339 -</para> 1.340 -</listitem> 1.341 -<listitem><para>Those patches that need reworking before being resubmitted are 1.342 - guarded with <literal>rework</literal>. 1.343 -</para> 1.344 -</listitem> 1.345 -<listitem><para>For those patches that are still under development, I use 1.346 - <literal>devel</literal>. 1.347 -</para> 1.348 -</listitem> 1.349 -<listitem><para>A backport patch may have several guards, one for each version 1.350 - of the kernel to which it applies. For example, a patch that 1.351 - backports a piece of code to 2.6.9 will have a <literal>2.6.9</literal> guard. 1.352 -</para> 1.353 -</listitem></itemizedlist> 1.354 -<para>This variety of guards gives me considerable flexibility in 1.355 -determining what kind of source tree I want to end up with. For most 1.356 -situations, the selection of appropriate guards is automated during 1.357 -the build process, but I can manually tune the guards to use for less 1.358 -common circumstances. 1.359 -</para> 1.360 - 1.361 -<sect2> 1.362 -<title>The art of writing backport patches</title> 1.363 - 1.364 -<para>Using MQ, writing a backport patch is a simple process. All such a 1.365 -patch has to do is modify a piece of code that uses a kernel feature 1.366 -not present in the older version of the kernel, so that the driver 1.367 -continues to work correctly under that older version. 1.368 -</para> 1.369 - 1.370 -<para>A useful goal when writing a good backport patch is to make your code 1.371 -look as if it was written for the older version of the kernel you're 1.372 -targeting. The less obtrusive the patch, the easier it will be to 1.373 -understand and maintain. If you're writing a collection of backport 1.374 -patches to avoid the <quote>rat's nest</quote> effect of lots of 1.375 -<literal>#ifdef</literal>s (hunks of source code that are only used 1.376 -conditionally) in your code, don't introduce version-dependent 1.377 -<literal>#ifdef</literal>s into the patches. Instead, write several patches, 1.378 -each of which makes unconditional changes, and control their 1.379 -application using guards. 1.380 -</para> 1.381 - 1.382 -<para>There are two reasons to divide backport patches into a distinct 1.383 -group, away from the <quote>regular</quote> patches whose effects they modify. 1.384 -The first is that intermingling the two makes it more difficult to use 1.385 -a tool like the <literal role="hg-ext">patchbomb</literal> extension to automate the process of 1.386 -submitting the patches to an upstream maintainer. The second is that 1.387 -a backport patch could perturb the context in which a subsequent 1.388 -regular patch is applied, making it impossible to apply the regular 1.389 -patch cleanly <emphasis>without</emphasis> the earlier backport patch already being 1.390 -applied. 1.391 -</para> 1.392 - 1.393 -</sect2> 1.394 -</sect1> 1.395 -<sect1> 1.396 -<title>Useful tips for developing with MQ</title> 1.397 - 1.398 -<sect2> 1.399 -<title>Organising patches in directories</title> 1.400 - 1.401 -<para>If you're working on a substantial project with MQ, it's not difficult 1.402 -to accumulate a large number of patches. For example, I have one 1.403 -patch repository that contains over 250 patches. 1.404 -</para> 1.405 - 1.406 -<para>If you can group these patches into separate logical categories, you 1.407 -can if you like store them in different directories; MQ has no 1.408 -problems with patch names that contain path separators. 1.409 -</para> 1.410 - 1.411 -</sect2> 1.412 -<sect2> 1.413 -<title>Viewing the history of a patch</title> 1.414 -<para>\label{mq-collab:tips:interdiff} 1.415 -</para> 1.416 - 1.417 -<para>If you're developing a set of patches over a long time, it's a good 1.418 -idea to maintain them in a repository, as discussed in 1.419 -section <xref linkend="sec:mq:repo"/>. If you do so, you'll quickly discover that 1.420 -using the <command role="hg-cmd">hg diff</command> command to look at the history of changes to a 1.421 -patch is unworkable. This is in part because you're looking at the 1.422 -second derivative of the real code (a diff of a diff), but also 1.423 -because MQ adds noise to the process by modifying time stamps and 1.424 -directory names when it updates a patch. 1.425 -</para> 1.426 - 1.427 -<para>However, you can use the <literal role="hg-ext">extdiff</literal> extension, which is bundled 1.428 -with Mercurial, to turn a diff of two versions of a patch into 1.429 -something readable. To do this, you will need a third-party package 1.430 -called <literal role="package">patchutils</literal> <citation>web:patchutils</citation>. This provides a 1.431 -command named <command>interdiff</command>, which shows the differences between 1.432 -two diffs as a diff. Used on two versions of the same diff, it 1.433 -generates a diff that represents the diff from the first to the second 1.434 -version. 1.435 -</para> 1.436 - 1.437 -<para>You can enable the <literal role="hg-ext">extdiff</literal> extension in the usual way, by 1.438 -adding a line to the <literal role="rc-extensions">extensions</literal> section of your <filename role="special"> /.hgrc</filename>. 1.439 -</para> 1.440 -<programlisting> 1.441 -<para> [extensions] 1.442 - extdiff = 1.443 -</para> 1.444 -</programlisting> 1.445 -<para>The <command>interdiff</command> command expects to be passed the names of two 1.446 -files, but the <literal role="hg-ext">extdiff</literal> extension passes the program it runs a 1.447 -pair of directories, each of which can contain an arbitrary number of 1.448 -files. We thus need a small program that will run <command>interdiff</command> 1.449 -on each pair of files in these two directories. This program is 1.450 -available as <filename role="special">hg-interdiff</filename> in the <filename class="directory">examples</filename> 1.451 -directory of the source code repository that accompanies this book. 1.452 -<!-- &example.hg-interdiff; --> 1.453 -</para> 1.454 - 1.455 -<para>With the <filename role="special">hg-interdiff</filename> program in your shell's search path, 1.456 -you can run it as follows, from inside an MQ patch directory: 1.457 -</para> 1.458 -<programlisting> 1.459 -<para> hg extdiff -p hg-interdiff -r A:B my-change.patch 1.460 -</para> 1.461 -</programlisting> 1.462 -<para>Since you'll probably want to use this long-winded command a lot, you 1.463 -can get <literal role="hg-ext">hgext</literal> to make it available as a normal Mercurial 1.464 -command, again by editing your <filename role="special"> /.hgrc</filename>. 1.465 -</para> 1.466 -<programlisting> 1.467 -<para> [extdiff] 1.468 - cmd.interdiff = hg-interdiff 1.469 -</para> 1.470 -</programlisting> 1.471 -<para>This directs <literal role="hg-ext">hgext</literal> to make an <literal>interdiff</literal> command 1.472 -available, so you can now shorten the previous invocation of 1.473 -<command role="hg-ext-extdiff">extdiff</command> to something a little more wieldy. 1.474 -</para> 1.475 -<programlisting> 1.476 -<para> hg interdiff -r A:B my-change.patch 1.477 -</para> 1.478 -</programlisting> 1.479 - 1.480 -<note> 1.481 -<para> The <command>interdiff</command> command works well only if the underlying 1.482 - files against which versions of a patch are generated remain the 1.483 - same. If you create a patch, modify the underlying files, and then 1.484 - regenerate the patch, <command>interdiff</command> may not produce useful 1.485 - output. 1.486 -</para> 1.487 -</note> 1.488 - 1.489 -<para>The <literal role="hg-ext">extdiff</literal> extension is useful for more than merely improving 1.490 -the presentation of MQ patches. To read more about it, go to 1.491 -section <xref linkend="sec:hgext:extdiff"/>. 1.492 -</para> 1.493 - 1.494 -</sect2> 1.495 -</sect1> 1.496 +<chapter id="chap:mq-collab"> 1.497 + <?dbhtml filename="advanced-uses-of-mercurial-queues.html"?> 1.498 + <title>Advanced uses of Mercurial Queues</title> 1.499 + 1.500 + <para id="x_15d">While it's easy to pick up straightforward uses of Mercurial 1.501 + Queues, use of a little discipline and some of MQ's less 1.502 + frequently used capabilities makes it possible to work in 1.503 + complicated development environments.</para> 1.504 + 1.505 + <para id="x_15e">In this chapter, I will use as an example a technique I have 1.506 + used to manage the development of an Infiniband device driver for 1.507 + the Linux kernel. The driver in question is large (at least as 1.508 + drivers go), with 25,000 lines of code spread across 35 source 1.509 + files. It is maintained by a small team of developers.</para> 1.510 + 1.511 + <para id="x_15f">While much of the material in this chapter is specific to 1.512 + Linux, the same principles apply to any code base for which you're 1.513 + not the primary owner, and upon which you need to do a lot of 1.514 + development.</para> 1.515 + 1.516 + <sect1> 1.517 + <title>The problem of many targets</title> 1.518 + 1.519 + <para id="x_160">The Linux kernel changes rapidly, and has never been 1.520 + internally stable; developers frequently make drastic changes 1.521 + between releases. This means that a version of the driver that 1.522 + works well with a particular released version of the kernel will 1.523 + not even <emphasis>compile</emphasis> correctly against, 1.524 + typically, any other version.</para> 1.525 + 1.526 + <para id="x_161">To maintain a driver, we have to keep a number of distinct 1.527 + versions of Linux in mind.</para> 1.528 + <itemizedlist> 1.529 + <listitem><para id="x_162">One target is the main Linux kernel development 1.530 + tree. Maintenance of the code is in this case partly shared 1.531 + by other developers in the kernel community, who make 1.532 + <quote>drive-by</quote> modifications to the driver as they 1.533 + develop and refine kernel subsystems.</para> 1.534 + </listitem> 1.535 + <listitem><para id="x_163">We also maintain a number of 1.536 + <quote>backports</quote> to older versions of the Linux 1.537 + kernel, to support the needs of customers who are running 1.538 + older Linux distributions that do not incorporate our 1.539 + drivers. (To <emphasis>backport</emphasis> a piece of code 1.540 + is to modify it to work in an older version of its target 1.541 + environment than the version it was developed for.)</para> 1.542 + </listitem> 1.543 + <listitem><para id="x_164">Finally, we make software releases on a schedule 1.544 + that is necessarily not aligned with those used by Linux 1.545 + distributors and kernel developers, so that we can deliver 1.546 + new features to customers without forcing them to upgrade 1.547 + their entire kernels or distributions.</para> 1.548 + </listitem></itemizedlist> 1.549 + 1.550 + <sect2> 1.551 + <title>Tempting approaches that don't work well</title> 1.552 + 1.553 + <para id="x_165">There are two <quote>standard</quote> ways to maintain a 1.554 + piece of software that has to target many different 1.555 + environments.</para> 1.556 + 1.557 + <para id="x_166">The first is to maintain a number of branches, each 1.558 + intended for a single target. The trouble with this approach 1.559 + is that you must maintain iron discipline in the flow of 1.560 + changes between repositories. A new feature or bug fix must 1.561 + start life in a <quote>pristine</quote> repository, then 1.562 + percolate out to every backport repository. Backport changes 1.563 + are more limited in the branches they should propagate to; a 1.564 + backport change that is applied to a branch where it doesn't 1.565 + belong will probably stop the driver from compiling.</para> 1.566 + 1.567 + <para id="x_167">The second is to maintain a single source tree filled with 1.568 + conditional statements that turn chunks of code on or off 1.569 + depending on the intended target. Because these 1.570 + <quote>ifdefs</quote> are not allowed in the Linux kernel 1.571 + tree, a manual or automatic process must be followed to strip 1.572 + them out and yield a clean tree. A code base maintained in 1.573 + this fashion rapidly becomes a rat's nest of conditional 1.574 + blocks that are difficult to understand and maintain.</para> 1.575 + 1.576 + <para id="x_168">Neither of these approaches is well suited to a situation 1.577 + where you don't <quote>own</quote> the canonical copy of a 1.578 + source tree. In the case of a Linux driver that is 1.579 + distributed with the standard kernel, Linus's tree contains 1.580 + the copy of the code that will be treated by the world as 1.581 + canonical. The upstream version of <quote>my</quote> driver 1.582 + can be modified by people I don't know, without me even 1.583 + finding out about it until after the changes show up in 1.584 + Linus's tree.</para> 1.585 + 1.586 + <para id="x_169">These approaches have the added weakness of making it 1.587 + difficult to generate well-formed patches to submit 1.588 + upstream.</para> 1.589 + 1.590 + <para id="x_16a">In principle, Mercurial Queues seems like a good candidate 1.591 + to manage a development scenario such as the above. While 1.592 + this is indeed the case, MQ contains a few added features that 1.593 + make the job more pleasant.</para> 1.594 + 1.595 + </sect2> 1.596 + </sect1> 1.597 + <sect1> 1.598 + <title>Conditionally applying patches with guards</title> 1.599 + 1.600 + <para id="x_16b">Perhaps the best way to maintain sanity with so many targets 1.601 + is to be able to choose specific patches to apply for a given 1.602 + situation. MQ provides a feature called <quote>guards</quote> 1.603 + (which originates with quilt's <literal>guards</literal> 1.604 + command) that does just this. To start off, let's create a 1.605 + simple repository for experimenting in.</para> 1.606 + 1.607 + &interaction.mq.guards.init; 1.608 + 1.609 + <para id="x_16c">This gives us a tiny repository that contains two patches 1.610 + that don't have any dependencies on each other, because they 1.611 + touch different files.</para> 1.612 + 1.613 + <para id="x_16d">The idea behind conditional application is that you can 1.614 + <quote>tag</quote> a patch with a <emphasis>guard</emphasis>, 1.615 + which is simply a text string of your choosing, then tell MQ to 1.616 + select specific guards to use when applying patches. MQ will 1.617 + then either apply, or skip over, a guarded patch, depending on 1.618 + the guards that you have selected.</para> 1.619 + 1.620 + <para id="x_16e">A patch can have an arbitrary number of guards; each one is 1.621 + <emphasis>positive</emphasis> (<quote>apply this patch if this 1.622 + guard is selected</quote>) or <emphasis>negative</emphasis> 1.623 + (<quote>skip this patch if this guard is selected</quote>). A 1.624 + patch with no guards is always applied.</para> 1.625 + 1.626 + </sect1> 1.627 + <sect1> 1.628 + <title>Controlling the guards on a patch</title> 1.629 + 1.630 + <para id="x_16f">The <command role="hg-ext-mq">qguard</command> command lets 1.631 + you determine which guards should apply to a patch, or display 1.632 + the guards that are already in effect. Without any arguments, it 1.633 + displays the guards on the current topmost patch.</para> 1.634 + 1.635 + &interaction.mq.guards.qguard; 1.636 + 1.637 + <para id="x_170">To set a positive guard on a patch, prefix the name of the 1.638 + guard with a <quote><literal>+</literal></quote>.</para> 1.639 + 1.640 + &interaction.mq.guards.qguard.pos; 1.641 + 1.642 + <para id="x_171">To set a negative guard 1.643 + on a patch, prefix the name of the guard with a 1.644 + <quote><literal>-</literal></quote>.</para> 1.645 + 1.646 + &interaction.mq.guards.qguard.neg; 1.647 + 1.648 + <para id="x_74a">Notice that we prefixed the arguments to the <command>hg 1.649 + qguard</command> command with a <literal>--</literal> here, so 1.650 + that Mercurial would not interpret the text 1.651 + <literal>-quux</literal> as an option.</para> 1.652 + 1.653 + <note> 1.654 + <title>Setting vs. modifying</title> 1.655 + 1.656 + <para id="x_172"> The <command role="hg-ext-mq">qguard</command> command 1.657 + <emphasis>sets</emphasis> the guards on a patch; it doesn't 1.658 + <emphasis>modify</emphasis> them. What this means is that if 1.659 + you run <command role="hg-cmd">hg qguard +a +b</command> on a 1.660 + patch, then <command role="hg-cmd">hg qguard +c</command> on 1.661 + the same patch, the <emphasis>only</emphasis> guard that will 1.662 + be set on it afterwards is <literal>+c</literal>.</para> 1.663 + </note> 1.664 + 1.665 + <para id="x_173">Mercurial stores guards in the <filename 1.666 + role="special">series</filename> file; the form in which they 1.667 + are stored is easy both to understand and to edit by hand. (In 1.668 + other words, you don't have to use the <command 1.669 + role="hg-ext-mq">qguard</command> command if you don't want 1.670 + to; it's okay to simply edit the <filename 1.671 + role="special">series</filename> file.)</para> 1.672 + 1.673 + &interaction.mq.guards.series; 1.674 + 1.675 + </sect1> 1.676 + <sect1> 1.677 + <title>Selecting the guards to use</title> 1.678 + 1.679 + <para id="x_174">The <command role="hg-ext-mq">qselect</command> command 1.680 + determines which guards are active at a given time. The effect 1.681 + of this is to determine which patches MQ will apply the next 1.682 + time you run <command role="hg-ext-mq">qpush</command>. It has 1.683 + no other effect; in particular, it doesn't do anything to 1.684 + patches that are already applied.</para> 1.685 + 1.686 + <para id="x_175">With no arguments, the <command 1.687 + role="hg-ext-mq">qselect</command> command lists the guards 1.688 + currently in effect, one per line of output. Each argument is 1.689 + treated as the name of a guard to apply.</para> 1.690 + 1.691 + &interaction.mq.guards.qselect.foo; 1.692 + 1.693 + <para id="x_176">In case you're interested, the currently selected guards are 1.694 + stored in the <filename role="special">guards</filename> file.</para> 1.695 + 1.696 + &interaction.mq.guards.qselect.cat; 1.697 + 1.698 + <para id="x_177">We can see the effect the selected guards have when we run 1.699 + <command role="hg-ext-mq">qpush</command>.</para> 1.700 + 1.701 + &interaction.mq.guards.qselect.qpush; 1.702 + 1.703 + <para id="x_178">A guard cannot start with a 1.704 + <quote><literal>+</literal></quote> or 1.705 + <quote><literal>-</literal></quote> character. The name of a 1.706 + guard must not contain white space, but most other characters 1.707 + are acceptable. If you try to use a guard with an invalid name, 1.708 + MQ will complain:</para> 1.709 + 1.710 + &interaction.mq.guards.qselect.error; 1.711 + 1.712 + <para id="x_179">Changing the selected guards changes the patches that are 1.713 + applied.</para> 1.714 + 1.715 + &interaction.mq.guards.qselect.quux; 1.716 + 1.717 + <para id="x_17a">You can see in the example below that negative guards take 1.718 + precedence over positive guards.</para> 1.719 + 1.720 + &interaction.mq.guards.qselect.foobar; 1.721 + 1.722 + </sect1> 1.723 + <sect1> 1.724 + <title>MQ's rules for applying patches</title> 1.725 + 1.726 + <para id="x_17b">The rules that MQ uses when deciding whether to apply a 1.727 + patch are as follows.</para> 1.728 + <itemizedlist> 1.729 + <listitem><para id="x_17c">A patch that has no guards is always 1.730 + applied.</para> 1.731 + </listitem> 1.732 + <listitem><para id="x_17d">If the patch has any negative guard that matches 1.733 + any currently selected guard, the patch is skipped.</para> 1.734 + </listitem> 1.735 + <listitem><para id="x_17e">If the patch has any positive guard that matches 1.736 + any currently selected guard, the patch is applied.</para> 1.737 + </listitem> 1.738 + <listitem><para id="x_17f">If the patch has positive or negative guards, 1.739 + but none matches any currently selected guard, the patch is 1.740 + skipped.</para> 1.741 + </listitem></itemizedlist> 1.742 + 1.743 + </sect1> 1.744 + <sect1> 1.745 + <title>Trimming the work environment</title> 1.746 + 1.747 + <para id="x_180">In working on the device driver I mentioned earlier, I don't 1.748 + apply the patches to a normal Linux kernel tree. Instead, I use 1.749 + a repository that contains only a snapshot of the source files 1.750 + and headers that are relevant to Infiniband development. This 1.751 + repository is 1% the size of a kernel repository, so it's easier 1.752 + to work with.</para> 1.753 + 1.754 + <para id="x_181">I then choose a <quote>base</quote> version on top of which 1.755 + the patches are applied. This is a snapshot of the Linux kernel 1.756 + tree as of a revision of my choosing. When I take the snapshot, 1.757 + I record the changeset ID from the kernel repository in the 1.758 + commit message. Since the snapshot preserves the 1.759 + <quote>shape</quote> and content of the relevant parts of the 1.760 + kernel tree, I can apply my patches on top of either my tiny 1.761 + repository or a normal kernel tree.</para> 1.762 + 1.763 + <para id="x_182">Normally, the base tree atop which the patches apply should 1.764 + be a snapshot of a very recent upstream tree. This best 1.765 + facilitates the development of patches that can easily be 1.766 + submitted upstream with few or no modifications.</para> 1.767 + 1.768 + </sect1> 1.769 + <sect1> 1.770 + <title>Dividing up the <filename role="special">series</filename> 1.771 + file</title> 1.772 + 1.773 + <para id="x_183">I categorise the patches in the <filename 1.774 + role="special">series</filename> file into a number of logical 1.775 + groups. Each section of like patches begins with a block of 1.776 + comments that describes the purpose of the patches that 1.777 + follow.</para> 1.778 + 1.779 + <para id="x_184">The sequence of patch groups that I maintain follows. The 1.780 + ordering of these groups is important; I'll describe why after I 1.781 + introduce the groups.</para> 1.782 + <itemizedlist> 1.783 + <listitem><para id="x_185">The <quote>accepted</quote> group. Patches that 1.784 + the development team has submitted to the maintainer of the 1.785 + Infiniband subsystem, and which he has accepted, but which 1.786 + are not present in the snapshot that the tiny repository is 1.787 + based on. These are <quote>read only</quote> patches, 1.788 + present only to transform the tree into a similar state as 1.789 + it is in the upstream maintainer's repository.</para> 1.790 + </listitem> 1.791 + <listitem><para id="x_186">The <quote>rework</quote> group. Patches that I 1.792 + have submitted, but that the upstream maintainer has 1.793 + requested modifications to before he will accept 1.794 + them.</para> 1.795 + </listitem> 1.796 + <listitem><para id="x_187">The <quote>pending</quote> group. Patches that 1.797 + I have not yet submitted to the upstream maintainer, but 1.798 + which we have finished working on. These will be <quote>read 1.799 + only</quote> for a while. If the upstream maintainer 1.800 + accepts them upon submission, I'll move them to the end of 1.801 + the <quote>accepted</quote> group. If he requests that I 1.802 + modify any, I'll move them to the beginning of the 1.803 + <quote>rework</quote> group.</para> 1.804 + </listitem> 1.805 + <listitem><para id="x_188">The <quote>in progress</quote> group. Patches 1.806 + that are actively being developed, and should not be 1.807 + submitted anywhere yet.</para> 1.808 + </listitem> 1.809 + <listitem><para id="x_189">The <quote>backport</quote> group. Patches that 1.810 + adapt the source tree to older versions of the kernel 1.811 + tree.</para> 1.812 + </listitem> 1.813 + <listitem><para id="x_18a">The <quote>do not ship</quote> group. Patches 1.814 + that for some reason should never be submitted upstream. 1.815 + For example, one such patch might change embedded driver 1.816 + identification strings to make it easier to distinguish, in 1.817 + the field, between an out-of-tree version of the driver and 1.818 + a version shipped by a distribution vendor.</para> 1.819 + </listitem></itemizedlist> 1.820 + 1.821 + <para id="x_18b">Now to return to the reasons for ordering groups of patches 1.822 + in this way. We would like the lowest patches in the stack to 1.823 + be as stable as possible, so that we will not need to rework 1.824 + higher patches due to changes in context. Putting patches that 1.825 + will never be changed first in the <filename 1.826 + role="special">series</filename> file serves this 1.827 + purpose.</para> 1.828 + 1.829 + <para id="x_18c">We would also like the patches that we know we'll need to 1.830 + modify to be applied on top of a source tree that resembles the 1.831 + upstream tree as closely as possible. This is why we keep 1.832 + accepted patches around for a while.</para> 1.833 + 1.834 + <para id="x_18d">The <quote>backport</quote> and <quote>do not ship</quote> 1.835 + patches float at the end of the <filename 1.836 + role="special">series</filename> file. The backport patches 1.837 + must be applied on top of all other patches, and the <quote>do 1.838 + not ship</quote> patches might as well stay out of harm's 1.839 + way.</para> 1.840 + 1.841 + </sect1> 1.842 + <sect1> 1.843 + <title>Maintaining the patch series</title> 1.844 + 1.845 + <para id="x_18e">In my work, I use a number of guards to control which 1.846 + patches are to be applied.</para> 1.847 + 1.848 + <itemizedlist> 1.849 + <listitem><para id="x_18f"><quote>Accepted</quote> patches are guarded with 1.850 + <literal>accepted</literal>. I enable this guard most of 1.851 + the time. When I'm applying the patches on top of a tree 1.852 + where the patches are already present, I can turn this patch 1.853 + off, and the patches that follow it will apply 1.854 + cleanly.</para> 1.855 + </listitem> 1.856 + <listitem><para id="x_190">Patches that are <quote>finished</quote>, but 1.857 + not yet submitted, have no guards. If I'm applying the 1.858 + patch stack to a copy of the upstream tree, I don't need to 1.859 + enable any guards in order to get a reasonably safe source 1.860 + tree.</para> 1.861 + </listitem> 1.862 + <listitem><para id="x_191">Those patches that need reworking before being 1.863 + resubmitted are guarded with 1.864 + <literal>rework</literal>.</para> 1.865 + </listitem> 1.866 + <listitem><para id="x_192">For those patches that are still under 1.867 + development, I use <literal>devel</literal>.</para> 1.868 + </listitem> 1.869 + <listitem><para id="x_193">A backport patch may have several guards, one 1.870 + for each version of the kernel to which it applies. For 1.871 + example, a patch that backports a piece of code to 2.6.9 1.872 + will have a <literal>2.6.9</literal> guard.</para> 1.873 + </listitem></itemizedlist> 1.874 + <para id="x_194">This variety of guards gives me considerable flexibility in 1.875 + determining what kind of source tree I want to end up with. For 1.876 + most situations, the selection of appropriate guards is 1.877 + automated during the build process, but I can manually tune the 1.878 + guards to use for less common circumstances.</para> 1.879 + 1.880 + <sect2> 1.881 + <title>The art of writing backport patches</title> 1.882 + 1.883 + <para id="x_195">Using MQ, writing a backport patch is a simple process. 1.884 + All such a patch has to do is modify a piece of code that uses 1.885 + a kernel feature not present in the older version of the 1.886 + kernel, so that the driver continues to work correctly under 1.887 + that older version.</para> 1.888 + 1.889 + <para id="x_196">A useful goal when writing a good backport patch is to 1.890 + make your code look as if it was written for the older version 1.891 + of the kernel you're targeting. The less obtrusive the patch, 1.892 + the easier it will be to understand and maintain. If you're 1.893 + writing a collection of backport patches to avoid the 1.894 + <quote>rat's nest</quote> effect of lots of 1.895 + <literal>#ifdef</literal>s (hunks of source code that are only 1.896 + used conditionally) in your code, don't introduce 1.897 + version-dependent <literal>#ifdef</literal>s into the patches. 1.898 + Instead, write several patches, each of which makes 1.899 + unconditional changes, and control their application using 1.900 + guards.</para> 1.901 + 1.902 + <para id="x_197">There are two reasons to divide backport patches into a 1.903 + distinct group, away from the <quote>regular</quote> patches 1.904 + whose effects they modify. The first is that intermingling the 1.905 + two makes it more difficult to use a tool like the <literal 1.906 + role="hg-ext">patchbomb</literal> extension to automate the 1.907 + process of submitting the patches to an upstream maintainer. 1.908 + The second is that a backport patch could perturb the context 1.909 + in which a subsequent regular patch is applied, making it 1.910 + impossible to apply the regular patch cleanly 1.911 + <emphasis>without</emphasis> the earlier backport patch 1.912 + already being applied.</para> 1.913 + 1.914 + </sect2> 1.915 + </sect1> 1.916 + <sect1> 1.917 + <title>Useful tips for developing with MQ</title> 1.918 + 1.919 + <sect2> 1.920 + <title>Organising patches in directories</title> 1.921 + 1.922 + <para id="x_198">If you're working on a substantial project with MQ, it's 1.923 + not difficult to accumulate a large number of patches. For 1.924 + example, I have one patch repository that contains over 250 1.925 + patches.</para> 1.926 + 1.927 + <para id="x_199">If you can group these patches into separate logical 1.928 + categories, you can if you like store them in different 1.929 + directories; MQ has no problems with patch names that contain 1.930 + path separators.</para> 1.931 + 1.932 + </sect2> 1.933 + <sect2 id="mq-collab:tips:interdiff"> 1.934 + <title>Viewing the history of a patch</title> 1.935 + 1.936 + <para id="x_19a">If you're developing a set of patches over a long time, 1.937 + it's a good idea to maintain them in a repository, as 1.938 + discussed in <xref linkend="sec:mq:repo"/>. If you do 1.939 + so, you'll quickly 1.940 + discover that using the <command role="hg-cmd">hg 1.941 + diff</command> command to look at the history of changes to 1.942 + a patch is unworkable. This is in part because you're looking 1.943 + at the second derivative of the real code (a diff of a diff), 1.944 + but also because MQ adds noise to the process by modifying 1.945 + time stamps and directory names when it updates a 1.946 + patch.</para> 1.947 + 1.948 + <para id="x_19b">However, you can use the <literal 1.949 + role="hg-ext">extdiff</literal> extension, which is bundled 1.950 + with Mercurial, to turn a diff of two versions of a patch into 1.951 + something readable. To do this, you will need a third-party 1.952 + package called <literal role="package">patchutils</literal> 1.953 + <citation>web:patchutils</citation>. This provides a command 1.954 + named <command>interdiff</command>, which shows the 1.955 + differences between two diffs as a diff. Used on two versions 1.956 + of the same diff, it generates a diff that represents the diff 1.957 + from the first to the second version.</para> 1.958 + 1.959 + <para id="x_19c">You can enable the <literal 1.960 + role="hg-ext">extdiff</literal> extension in the usual way, 1.961 + by adding a line to the <literal 1.962 + role="rc-extensions">extensions</literal> section of your 1.963 + <filename role="special">~/.hgrc</filename>.</para> 1.964 + <programlisting>[extensions] 1.965 +extdiff =</programlisting> 1.966 + <para id="x_19d">The <command>interdiff</command> command expects to be 1.967 + passed the names of two files, but the <literal 1.968 + role="hg-ext">extdiff</literal> extension passes the program 1.969 + it runs a pair of directories, each of which can contain an 1.970 + arbitrary number of files. We thus need a small program that 1.971 + will run <command>interdiff</command> on each pair of files in 1.972 + these two directories. This program is available as <filename 1.973 + role="special">hg-interdiff</filename> in the <filename 1.974 + class="directory">examples</filename> directory of the 1.975 + source code repository that accompanies this book. <!-- 1.976 + &example.hg-interdiff; --></para> 1.977 + 1.978 + <para id="x_19e">With the <filename role="special">hg-interdiff</filename> 1.979 + program in your shell's search path, you can run it as 1.980 + follows, from inside an MQ patch directory:</para> 1.981 + <programlisting>hg extdiff -p hg-interdiff -r A:B my-change.patch</programlisting> 1.982 + <para id="x_19f">Since you'll probably want to use this long-winded command 1.983 + a lot, you can get <literal role="hg-ext">hgext</literal> to 1.984 + make it available as a normal Mercurial command, again by 1.985 + editing your <filename 1.986 + role="special">~/.hgrc</filename>.</para> 1.987 + <programlisting>[extdiff] 1.988 +cmd.interdiff = hg-interdiff</programlisting> 1.989 + <para id="x_1a0">This directs <literal role="hg-ext">hgext</literal> to 1.990 + make an <literal>interdiff</literal> command available, so you 1.991 + can now shorten the previous invocation of <command 1.992 + role="hg-ext-extdiff">extdiff</command> to something a 1.993 + little more wieldy.</para> 1.994 + <programlisting>hg interdiff -r A:B my-change.patch</programlisting> 1.995 + 1.996 + <note> 1.997 + <para id="x_1a1"> The <command>interdiff</command> command works well 1.998 + only if the underlying files against which versions of a 1.999 + patch are generated remain the same. If you create a patch, 1.1000 + modify the underlying files, and then regenerate the patch, 1.1001 + <command>interdiff</command> may not produce useful 1.1002 + output.</para> 1.1003 + </note> 1.1004 + 1.1005 + <para id="x_1a2">The <literal role="hg-ext">extdiff</literal> extension is 1.1006 + useful for more than merely improving the presentation of MQ 1.1007 + patches. To read more about it, go to <xref 1.1008 + linkend="sec:hgext:extdiff"/>.</para> 1.1009 + 1.1010 + </sect2> 1.1011 + </sect1> 1.1012 </chapter> 1.1013 1.1014 <!-- 1.1015 local variables: 1.1016 sgml-parent-document: ("00book.xml" "book" "chapter") 1.1017 end: 1.1018 ---> 1.1019 \ No newline at end of file 1.1020 +-->