hgbook
diff en/ch09-undo.xml @ 565:8a9c66da6fcb
Fix thinko
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Mon Mar 09 21:40:12 2009 -0700 (2009-03-09) |
parents | |
children | 8fcd44708f41 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/en/ch09-undo.xml Mon Mar 09 21:40:12 2009 -0700 1.3 @@ -0,0 +1,997 @@ 1.4 +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> 1.5 + 1.6 +<chapter id="chap:undo"> 1.7 + <title>Finding and fixing your mistakes</title> 1.8 + 1.9 + <para>To err might be human, but to really handle the consequences 1.10 + well takes a top-notch revision control system. In this chapter, 1.11 + we'll discuss some of the techniques you can use when you find 1.12 + that a problem has crept into your project. Mercurial has some 1.13 + highly capable features that will help you to isolate the sources 1.14 + of problems, and to handle them appropriately.</para> 1.15 + 1.16 + <sect1> 1.17 + <title>Erasing local history</title> 1.18 + 1.19 + <sect2> 1.20 + <title>The accidental commit</title> 1.21 + 1.22 + <para>I have the occasional but persistent problem of typing 1.23 + rather more quickly than I can think, which sometimes results 1.24 + in me committing a changeset that is either incomplete or 1.25 + plain wrong. In my case, the usual kind of incomplete 1.26 + changeset is one in which I've created a new source file, but 1.27 + forgotten to <command role="hg-cmd">hg add</command> it. A 1.28 + <quote>plain wrong</quote> changeset is not as common, but no 1.29 + less annoying.</para> 1.30 + 1.31 + </sect2> 1.32 + <sect2 id="sec:undo:rollback"> 1.33 + <title>Rolling back a transaction</title> 1.34 + 1.35 + <para>In section <xref linkend="sec:concepts:txn"/>, I mentioned 1.36 + that Mercurial treats each modification of a repository as a 1.37 + <emphasis>transaction</emphasis>. Every time you commit a 1.38 + changeset or pull changes from another repository, Mercurial 1.39 + remembers what you did. You can undo, or <emphasis>roll 1.40 + back</emphasis>, exactly one of these actions using the 1.41 + <command role="hg-cmd">hg rollback</command> command. (See 1.42 + section <xref linkend="sec:undo:rollback-after-push"/> for an 1.43 + important caveat about the use of this command.)</para> 1.44 + 1.45 + <para>Here's a mistake that I often find myself making: 1.46 + committing a change in which I've created a new file, but 1.47 + forgotten to <command role="hg-cmd">hg add</command> it. <!-- 1.48 + &interaction.rollback.commit; --> Looking at the output of 1.49 + <command role="hg-cmd">hg status</command> after the commit 1.50 + immediately confirms the error. <!-- 1.51 + &interaction.rollback.status; --> The commit captured the 1.52 + changes to the file <filename>a</filename>, but not the new 1.53 + file <filename>b</filename>. If I were to push this changeset 1.54 + to a repository that I shared with a colleague, the chances 1.55 + are high that something in <filename>a</filename> would refer 1.56 + to <filename>b</filename>, which would not be present in their 1.57 + repository when they pulled my changes. I would thus become 1.58 + the object of some indignation.</para> 1.59 + 1.60 + <para>However, luck is with me&emdash;I've caught my error 1.61 + before I pushed the changeset. I use the <command 1.62 + role="hg-cmd">hg rollback</command> command, and Mercurial 1.63 + makes that last changeset vanish. <!-- 1.64 + &interaction.rollback.rollback; --> Notice that the changeset 1.65 + is no longer present in the repository's history, and the 1.66 + working directory once again thinks that the file 1.67 + <filename>a</filename> is modified. The commit and rollback 1.68 + have left the working directory exactly as it was prior to the 1.69 + commit; the changeset has been completely erased. I can now 1.70 + safely <command role="hg-cmd">hg add</command> the file 1.71 + <filename>b</filename>, and rerun my commit. <!-- 1.72 + &interaction.rollback.add; --></para> 1.73 + 1.74 + </sect2> 1.75 + <sect2> 1.76 + <title>The erroneous pull</title> 1.77 + 1.78 + <para>It's common practice with Mercurial to maintain separate 1.79 + development branches of a project in different repositories. 1.80 + Your development team might have one shared repository for 1.81 + your project's <quote>0.9</quote> release, and another, 1.82 + containing different changes, for the <quote>1.0</quote> 1.83 + release.</para> 1.84 + 1.85 + <para>Given this, you can imagine that the consequences could be 1.86 + messy if you had a local <quote>0.9</quote> repository, and 1.87 + accidentally pulled changes from the shared <quote>1.0</quote> 1.88 + repository into it. At worst, you could be paying 1.89 + insufficient attention, and push those changes into the shared 1.90 + <quote>0.9</quote> tree, confusing your entire team (but don't 1.91 + worry, we'll return to this horror scenario later). However, 1.92 + it's more likely that you'll notice immediately, because 1.93 + Mercurial will display the URL it's pulling from, or you will 1.94 + see it pull a suspiciously large number of changes into the 1.95 + repository.</para> 1.96 + 1.97 + <para>The <command role="hg-cmd">hg rollback</command> command 1.98 + will work nicely to expunge all of the changesets that you 1.99 + just pulled. Mercurial groups all changes from one <command 1.100 + role="hg-cmd">hg pull</command> into a single transaction, 1.101 + so one <command role="hg-cmd">hg rollback</command> is all you 1.102 + need to undo this mistake.</para> 1.103 + 1.104 + </sect2> 1.105 + <sect2 id="sec:undo:rollback-after-push"> 1.106 + <title>Rolling back is useless once you've pushed</title> 1.107 + 1.108 + <para>The value of the <command role="hg-cmd">hg 1.109 + rollback</command> command drops to zero once you've pushed 1.110 + your changes to another repository. Rolling back a change 1.111 + makes it disappear entirely, but <emphasis>only</emphasis> in 1.112 + the repository in which you perform the <command 1.113 + role="hg-cmd">hg rollback</command>. Because a rollback 1.114 + eliminates history, there's no way for the disappearance of a 1.115 + change to propagate between repositories.</para> 1.116 + 1.117 + <para>If you've pushed a change to another 1.118 + repository&emdash;particularly if it's a shared 1.119 + repository&emdash;it has essentially <quote>escaped into the 1.120 + wild,</quote> and you'll have to recover from your mistake 1.121 + in a different way. What will happen if you push a changeset 1.122 + somewhere, then roll it back, then pull from the repository 1.123 + you pushed to, is that the changeset will reappear in your 1.124 + repository.</para> 1.125 + 1.126 + <para>(If you absolutely know for sure that the change you want 1.127 + to roll back is the most recent change in the repository that 1.128 + you pushed to, <emphasis>and</emphasis> you know that nobody 1.129 + else could have pulled it from that repository, you can roll 1.130 + back the changeset there, too, but you really should really 1.131 + not rely on this working reliably. If you do this, sooner or 1.132 + later a change really will make it into a repository that you 1.133 + don't directly control (or have forgotten about), and come 1.134 + back to bite you.)</para> 1.135 + 1.136 + </sect2> 1.137 + <sect2> 1.138 + <title>You can only roll back once</title> 1.139 + 1.140 + <para>Mercurial stores exactly one transaction in its 1.141 + transaction log; that transaction is the most recent one that 1.142 + occurred in the repository. This means that you can only roll 1.143 + back one transaction. If you expect to be able to roll back 1.144 + one transaction, then its predecessor, this is not the 1.145 + behaviour you will get. <!-- &interaction.rollback.twice; --> 1.146 + Once you've rolled back one transaction in a repository, you 1.147 + can't roll back again in that repository until you perform 1.148 + another commit or pull.</para> 1.149 + 1.150 + </sect2> 1.151 + </sect1> 1.152 + <sect1> 1.153 + <title>Reverting the mistaken change</title> 1.154 + 1.155 + <para>If you make a modification to a file, and decide that you 1.156 + really didn't want to change the file at all, and you haven't 1.157 + yet committed your changes, the <command role="hg-cmd">hg 1.158 + revert</command> command is the one you'll need. It looks at 1.159 + the changeset that's the parent of the working directory, and 1.160 + restores the contents of the file to their state as of that 1.161 + changeset. (That's a long-winded way of saying that, in the 1.162 + normal case, it undoes your modifications.)</para> 1.163 + 1.164 + <para>Let's illustrate how the <command role="hg-cmd">hg 1.165 + revert</command> command works with yet another small example. 1.166 + We'll begin by modifying a file that Mercurial is already 1.167 + tracking. <!-- &interaction.daily.revert.modify; --> If we don't 1.168 + want that change, we can simply <command role="hg-cmd">hg 1.169 + revert</command> the file. <!-- 1.170 + &interaction.daily.revert.unmodify; --> The <command 1.171 + role="hg-cmd">hg revert</command> command provides us with an 1.172 + extra degree of safety by saving our modified file with a 1.173 + <filename>.orig</filename> extension. <!-- 1.174 + &interaction.daily.revert.status; --></para> 1.175 + 1.176 + <para>Here is a summary of the cases that the <command 1.177 + role="hg-cmd">hg revert</command> command can deal with. We 1.178 + will describe each of these in more detail in the section that 1.179 + follows.</para> 1.180 + <itemizedlist> 1.181 + <listitem><para>If you modify a file, it will restore the file 1.182 + to its unmodified state.</para> 1.183 + </listitem> 1.184 + <listitem><para>If you <command role="hg-cmd">hg add</command> a 1.185 + file, it will undo the <quote>added</quote> state of the 1.186 + file, but leave the file itself untouched.</para> 1.187 + </listitem> 1.188 + <listitem><para>If you delete a file without telling Mercurial, 1.189 + it will restore the file to its unmodified contents.</para> 1.190 + </listitem> 1.191 + <listitem><para>If you use the <command role="hg-cmd">hg 1.192 + remove</command> command to remove a file, it will undo 1.193 + the <quote>removed</quote> state of the file, and restore 1.194 + the file to its unmodified contents.</para> 1.195 + </listitem></itemizedlist> 1.196 + 1.197 + <sect2 id="sec:undo:mgmt"> 1.198 + <title>File management errors</title> 1.199 + 1.200 + <para>The <command role="hg-cmd">hg revert</command> command is 1.201 + useful for more than just modified files. It lets you reverse 1.202 + the results of all of Mercurial's file management 1.203 + commands&emdash;<command role="hg-cmd">hg add</command>, 1.204 + <command role="hg-cmd">hg remove</command>, and so on.</para> 1.205 + 1.206 + <para>If you <command role="hg-cmd">hg add</command> a file, 1.207 + then decide that in fact you don't want Mercurial to track it, 1.208 + use <command role="hg-cmd">hg revert</command> to undo the 1.209 + add. Don't worry; Mercurial will not modify the file in any 1.210 + way. It will just <quote>unmark</quote> the file. <!-- 1.211 + &interaction.daily.revert.add; --></para> 1.212 + 1.213 + <para>Similarly, if you ask Mercurial to <command 1.214 + role="hg-cmd">hg remove</command> a file, you can use 1.215 + <command role="hg-cmd">hg revert</command> to restore it to 1.216 + the contents it had as of the parent of the working directory. 1.217 + <!-- &interaction.daily.revert.remove; --> This works just as 1.218 + well for a file that you deleted by hand, without telling 1.219 + Mercurial (recall that in Mercurial terminology, this kind of 1.220 + file is called <quote>missing</quote>). <!-- 1.221 + &interaction.daily.revert.missing; --></para> 1.222 + 1.223 + <para>If you revert a <command role="hg-cmd">hg copy</command>, 1.224 + the copied-to file remains in your working directory 1.225 + afterwards, untracked. Since a copy doesn't affect the 1.226 + copied-from file in any way, Mercurial doesn't do anything 1.227 + with the copied-from file. <!-- 1.228 + &interaction.daily.revert.copy; --></para> 1.229 + 1.230 + <sect3> 1.231 + <title>A slightly special case: reverting a rename</title> 1.232 + 1.233 + <para>If you <command role="hg-cmd">hg rename</command> a 1.234 + file, there is one small detail that you should remember. 1.235 + When you <command role="hg-cmd">hg revert</command> a 1.236 + rename, it's not enough to provide the name of the 1.237 + renamed-to file, as you can see here. <!-- 1.238 + &interaction.daily.revert.rename; --> As you can see from 1.239 + the output of <command role="hg-cmd">hg status</command>, 1.240 + the renamed-to file is no longer identified as added, but 1.241 + the renamed-<emphasis>from</emphasis> file is still removed! 1.242 + This is counter-intuitive (at least to me), but at least 1.243 + it's easy to deal with. <!-- 1.244 + &interaction.daily.revert.rename-orig; --> So remember, to 1.245 + revert a <command role="hg-cmd">hg rename</command>, you 1.246 + must provide <emphasis>both</emphasis> the source and 1.247 + destination names.</para> 1.248 + 1.249 + <para>% TODO: the output doesn't look like it will be 1.250 + removed!</para> 1.251 + 1.252 + <para>(By the way, if you rename a file, then modify the 1.253 + renamed-to file, then revert both components of the rename, 1.254 + when Mercurial restores the file that was removed as part of 1.255 + the rename, it will be unmodified. If you need the 1.256 + modifications in the renamed-to file to show up in the 1.257 + renamed-from file, don't forget to copy them over.)</para> 1.258 + 1.259 + <para>These fiddly aspects of reverting a rename arguably 1.260 + constitute a small bug in Mercurial.</para> 1.261 + 1.262 + </sect3> 1.263 + </sect2> 1.264 + </sect1> 1.265 + <sect1> 1.266 + <title>Dealing with committed changes</title> 1.267 + 1.268 + <para>Consider a case where you have committed a change $a$, and 1.269 + another change $b$ on top of it; you then realise that change 1.270 + $a$ was incorrect. Mercurial lets you <quote>back out</quote> 1.271 + an entire changeset automatically, and building blocks that let 1.272 + you reverse part of a changeset by hand.</para> 1.273 + 1.274 + <para>Before you read this section, here's something to keep in 1.275 + mind: the <command role="hg-cmd">hg backout</command> command 1.276 + undoes changes by <emphasis>adding</emphasis> history, not by 1.277 + modifying or erasing it. It's the right tool to use if you're 1.278 + fixing bugs, but not if you're trying to undo some change that 1.279 + has catastrophic consequences. To deal with those, see section 1.280 + <xref linkend="sec:undo:aaaiiieee"/>.</para> 1.281 + 1.282 + <sect2> 1.283 + <title>Backing out a changeset</title> 1.284 + 1.285 + <para>The <command role="hg-cmd">hg backout</command> command 1.286 + lets you <quote>undo</quote> the effects of an entire 1.287 + changeset in an automated fashion. Because Mercurial's 1.288 + history is immutable, this command <emphasis>does 1.289 + not</emphasis> get rid of the changeset you want to undo. 1.290 + Instead, it creates a new changeset that 1.291 + <emphasis>reverses</emphasis> the effect of the to-be-undone 1.292 + changeset.</para> 1.293 + 1.294 + <para>The operation of the <command role="hg-cmd">hg 1.295 + backout</command> command is a little intricate, so let's 1.296 + illustrate it with some examples. First, we'll create a 1.297 + repository with some simple changes. <!-- 1.298 + &interaction.backout.init; --></para> 1.299 + 1.300 + <para>The <command role="hg-cmd">hg backout</command> command 1.301 + takes a single changeset ID as its argument; this is the 1.302 + changeset to back out. Normally, <command role="hg-cmd">hg 1.303 + backout</command> will drop you into a text editor to write 1.304 + a commit message, so you can record why you're backing the 1.305 + change out. In this example, we provide a commit message on 1.306 + the command line using the <option 1.307 + role="hg-opt-backout">-m</option> option.</para> 1.308 + 1.309 + </sect2> 1.310 + <sect2> 1.311 + <title>Backing out the tip changeset</title> 1.312 + 1.313 + <para>We're going to start by backing out the last changeset we 1.314 + committed. <!-- &interaction.backout.simple; --> You can see 1.315 + that the second line from <filename>myfile</filename> is no 1.316 + longer present. Taking a look at the output of <command 1.317 + role="hg-cmd">hg log</command> gives us an idea of what the 1.318 + <command role="hg-cmd">hg backout</command> command has done. 1.319 + <!-- &interaction.backout.simple.log; --> Notice that the new 1.320 + changeset that <command role="hg-cmd">hg backout</command> has 1.321 + created is a child of the changeset we backed out. It's 1.322 + easier to see this in figure <xref 1.323 + linkend="fig:undo:backout"/>, which presents a graphical 1.324 + view of the change history. As you can see, the history is 1.325 + nice and linear.</para> 1.326 + 1.327 + <informalfigure id="fig:undo:backout"> 1.328 + <mediaobject><imageobject><imagedata 1.329 + fileref="undo-simple"/></imageobject><textobject><phrase>XXX 1.330 + add text</phrase></textobject><caption><para>Backing out 1.331 + a change using the <command role="hg-cmd">hg 1.332 + backout</command> 1.333 + command</para></caption></mediaobject> 1.334 + 1.335 + </informalfigure> 1.336 + 1.337 + </sect2> 1.338 + <sect2> 1.339 + <title>Backing out a non-tip change</title> 1.340 + 1.341 + <para>If you want to back out a change other than the last one 1.342 + you committed, pass the <option 1.343 + role="hg-opt-backout">--merge</option> option to the 1.344 + <command role="hg-cmd">hg backout</command> command. <!-- 1.345 + &interaction.backout.non-tip.clone; --> This makes backing out 1.346 + any changeset a <quote>one-shot</quote> operation that's 1.347 + usually simple and fast. <!-- 1.348 + &interaction.backout.non-tip.backout; --></para> 1.349 + 1.350 + <para>If you take a look at the contents of 1.351 + <filename>myfile</filename> after the backout finishes, you'll 1.352 + see that the first and third changes are present, but not the 1.353 + second. <!-- &interaction.backout.non-tip.cat; --></para> 1.354 + 1.355 + <para>As the graphical history in figure <xref 1.356 + linkend="fig:undo:backout-non-tip"/> illustrates, Mercurial 1.357 + actually commits <emphasis>two</emphasis> changes in this kind 1.358 + of situation (the box-shaped nodes are the ones that Mercurial 1.359 + commits automatically). Before Mercurial begins the backout 1.360 + process, it first remembers what the current parent of the 1.361 + working directory is. It then backs out the target changeset, 1.362 + and commits that as a changeset. Finally, it merges back to 1.363 + the previous parent of the working directory, and commits the 1.364 + result of the merge.</para> 1.365 + 1.366 + <para>% TODO: to me it looks like mercurial doesn't commit the 1.367 + second merge automatically!</para> 1.368 + 1.369 + <informalfigure id="fig:undo:backout-non-tip"> 1.370 + <mediaobject><imageobject><imagedata 1.371 + fileref="undo-non-tip"/></imageobject><textobject><phrase>XXX 1.372 + add text</phrase></textobject><caption><para>Automated 1.373 + backout of a non-tip change using the <command 1.374 + role="hg-cmd">hg backout</command> 1.375 + command</para></caption></mediaobject> 1.376 + </informalfigure> 1.377 + 1.378 + <para>The result is that you end up <quote>back where you 1.379 + were</quote>, only with some extra history that undoes the 1.380 + effect of the changeset you wanted to back out.</para> 1.381 + 1.382 + <sect3> 1.383 + <title>Always use the <option 1.384 + role="hg-opt-backout">--merge</option> option</title> 1.385 + 1.386 + <para>In fact, since the <option 1.387 + role="hg-opt-backout">--merge</option> option will do the 1.388 + <quote>right thing</quote> whether or not the changeset 1.389 + you're backing out is the tip (i.e. it won't try to merge if 1.390 + it's backing out the tip, since there's no need), you should 1.391 + <emphasis>always</emphasis> use this option when you run the 1.392 + <command role="hg-cmd">hg backout</command> command.</para> 1.393 + 1.394 + </sect3> 1.395 + </sect2> 1.396 + <sect2> 1.397 + <title>Gaining more control of the backout process</title> 1.398 + 1.399 + <para>While I've recommended that you always use the <option 1.400 + role="hg-opt-backout">--merge</option> option when backing 1.401 + out a change, the <command role="hg-cmd">hg backout</command> 1.402 + command lets you decide how to merge a backout changeset. 1.403 + Taking control of the backout process by hand is something you 1.404 + will rarely need to do, but it can be useful to understand 1.405 + what the <command role="hg-cmd">hg backout</command> command 1.406 + is doing for you automatically. To illustrate this, let's 1.407 + clone our first repository, but omit the backout change that 1.408 + it contains.</para> 1.409 + 1.410 + <para><!-- &interaction.backout.manual.clone; --> As with our 1.411 + earlier example, We'll commit a third changeset, then back out 1.412 + its parent, and see what happens. <!-- 1.413 + &interaction.backout.manual.backout; --> Our new changeset is 1.414 + again a descendant of the changeset we backout out; it's thus 1.415 + a new head, <emphasis>not</emphasis> a descendant of the 1.416 + changeset that was the tip. The <command role="hg-cmd">hg 1.417 + backout</command> command was quite explicit in telling us 1.418 + this. <!-- &interaction.backout.manual.log; --></para> 1.419 + 1.420 + <para>Again, it's easier to see what has happened by looking at 1.421 + a graph of the revision history, in figure <xref 1.422 + linkend="fig:undo:backout-manual"/>. This makes it clear 1.423 + that when we use <command role="hg-cmd">hg backout</command> 1.424 + to back out a change other than the tip, Mercurial adds a new 1.425 + head to the repository (the change it committed is 1.426 + box-shaped).</para> 1.427 + 1.428 + <informalfigure id="fig:undo:backout-manual"> 1.429 + <mediaobject><imageobject><imagedata 1.430 + fileref="undo-manual"/></imageobject><textobject><phrase>XXX 1.431 + add text</phrase></textobject><caption><para>Backing out 1.432 + a change using the <command role="hg-cmd">hg 1.433 + backout</command> 1.434 + command</para></caption></mediaobject> 1.435 + 1.436 + </informalfigure> 1.437 + 1.438 + <para>After the <command role="hg-cmd">hg backout</command> 1.439 + command has completed, it leaves the new 1.440 + <quote>backout</quote> changeset as the parent of the working 1.441 + directory. <!-- &interaction.backout.manual.parents; --> Now 1.442 + we have two isolated sets of changes. <!-- 1.443 + &interaction.backout.manual.heads; --></para> 1.444 + 1.445 + <para>Let's think about what we expect to see as the contents of 1.446 + <filename>myfile</filename> now. The first change should be 1.447 + present, because we've never backed it out. The second change 1.448 + should be missing, as that's the change we backed out. Since 1.449 + the history graph shows the third change as a separate head, 1.450 + we <emphasis>don't</emphasis> expect to see the third change 1.451 + present in <filename>myfile</filename>. <!-- 1.452 + &interaction.backout.manual.cat; --> To get the third change 1.453 + back into the file, we just do a normal merge of our two 1.454 + heads. <!-- &interaction.backout.manual.merge; --> Afterwards, 1.455 + the graphical history of our repository looks like figure 1.456 + <xref linkend="fig:undo:backout-manual-merge"/>.</para> 1.457 + 1.458 + <informalfigure id="fig:undo:backout-manual-merge"> 1.459 + <mediaobject><imageobject><imagedata 1.460 + fileref="undo-manual-merge"/></imageobject><textobject><phrase>XXX 1.461 + add text</phrase></textobject><caption><para>Manually 1.462 + merging a backout change</para></caption></mediaobject> 1.463 + 1.464 + </informalfigure> 1.465 + 1.466 + </sect2> 1.467 + <sect2> 1.468 + <title>Why <command role="hg-cmd">hg backout</command> works as 1.469 + it does</title> 1.470 + 1.471 + <para>Here's a brief description of how the <command 1.472 + role="hg-cmd">hg backout</command> command works.</para> 1.473 + <orderedlist> 1.474 + <listitem><para>It ensures that the working directory is 1.475 + <quote>clean</quote>, i.e. that the output of <command 1.476 + role="hg-cmd">hg status</command> would be empty.</para> 1.477 + </listitem> 1.478 + <listitem><para>It remembers the current parent of the working 1.479 + directory. Let's call this changeset 1.480 + <literal>orig</literal></para> 1.481 + </listitem> 1.482 + <listitem><para>It does the equivalent of a <command 1.483 + role="hg-cmd">hg update</command> to sync the working 1.484 + directory to the changeset you want to back out. Let's 1.485 + call this changeset <literal>backout</literal></para> 1.486 + </listitem> 1.487 + <listitem><para>It finds the parent of that changeset. Let's 1.488 + call that changeset <literal>parent</literal>.</para> 1.489 + </listitem> 1.490 + <listitem><para>For each file that the 1.491 + <literal>backout</literal> changeset affected, it does the 1.492 + equivalent of a <command role="hg-cmd">hg revert -r 1.493 + parent</command> on that file, to restore it to the 1.494 + contents it had before that changeset was 1.495 + committed.</para> 1.496 + </listitem> 1.497 + <listitem><para>It commits the result as a new changeset. 1.498 + This changeset has <literal>backout</literal> as its 1.499 + parent.</para> 1.500 + </listitem> 1.501 + <listitem><para>If you specify <option 1.502 + role="hg-opt-backout">--merge</option> on the command 1.503 + line, it merges with <literal>orig</literal>, and commits 1.504 + the result of the merge.</para> 1.505 + </listitem></orderedlist> 1.506 + 1.507 + <para>An alternative way to implement the <command 1.508 + role="hg-cmd">hg backout</command> command would be to 1.509 + <command role="hg-cmd">hg export</command> the 1.510 + to-be-backed-out changeset as a diff, then use the <option 1.511 + role="cmd-opt-patch">--reverse</option> option to the 1.512 + <command>patch</command> command to reverse the effect of the 1.513 + change without fiddling with the working directory. This 1.514 + sounds much simpler, but it would not work nearly as 1.515 + well.</para> 1.516 + 1.517 + <para>The reason that <command role="hg-cmd">hg 1.518 + backout</command> does an update, a commit, a merge, and 1.519 + another commit is to give the merge machinery the best chance 1.520 + to do a good job when dealing with all the changes 1.521 + <emphasis>between</emphasis> the change you're backing out and 1.522 + the current tip.</para> 1.523 + 1.524 + <para>If you're backing out a changeset that's 100 revisions 1.525 + back in your project's history, the chances that the 1.526 + <command>patch</command> command will be able to apply a 1.527 + reverse diff cleanly are not good, because intervening changes 1.528 + are likely to have <quote>broken the context</quote> that 1.529 + <command>patch</command> uses to determine whether it can 1.530 + apply a patch (if this sounds like gibberish, see <xref 1.531 + linkend="sec:mq:patch"/> for a 1.532 + discussion of the <command>patch</command> command). Also, 1.533 + Mercurial's merge machinery will handle files and directories 1.534 + being renamed, permission changes, and modifications to binary 1.535 + files, none of which <command>patch</command> can deal 1.536 + with.</para> 1.537 + 1.538 + </sect2> 1.539 + </sect1> 1.540 + <sect1 id="sec:undo:aaaiiieee"> 1.541 + <title>Changes that should never have been</title> 1.542 + 1.543 + <para>Most of the time, the <command role="hg-cmd">hg 1.544 + backout</command> command is exactly what you need if you want 1.545 + to undo the effects of a change. It leaves a permanent record 1.546 + of exactly what you did, both when committing the original 1.547 + changeset and when you cleaned up after it.</para> 1.548 + 1.549 + <para>On rare occasions, though, you may find that you've 1.550 + committed a change that really should not be present in the 1.551 + repository at all. For example, it would be very unusual, and 1.552 + usually considered a mistake, to commit a software project's 1.553 + object files as well as its source files. Object files have 1.554 + almost no intrinsic value, and they're <emphasis>big</emphasis>, 1.555 + so they increase the size of the repository and the amount of 1.556 + time it takes to clone or pull changes.</para> 1.557 + 1.558 + <para>Before I discuss the options that you have if you commit a 1.559 + <quote>brown paper bag</quote> change (the kind that's so bad 1.560 + that you want to pull a brown paper bag over your head), let me 1.561 + first discuss some approaches that probably won't work.</para> 1.562 + 1.563 + <para>Since Mercurial treats history as accumulative&emdash;every 1.564 + change builds on top of all changes that preceded it&emdash;you 1.565 + generally can't just make disastrous changes disappear. The one 1.566 + exception is when you've just committed a change, and it hasn't 1.567 + been pushed or pulled into another repository. That's when you 1.568 + can safely use the <command role="hg-cmd">hg rollback</command> 1.569 + command, as I detailed in section <xref 1.570 + linkend="sec:undo:rollback"/>.</para> 1.571 + 1.572 + <para>After you've pushed a bad change to another repository, you 1.573 + <emphasis>could</emphasis> still use <command role="hg-cmd">hg 1.574 + rollback</command> to make your local copy of the change 1.575 + disappear, but it won't have the consequences you want. The 1.576 + change will still be present in the remote repository, so it 1.577 + will reappear in your local repository the next time you 1.578 + pull.</para> 1.579 + 1.580 + <para>If a situation like this arises, and you know which 1.581 + repositories your bad change has propagated into, you can 1.582 + <emphasis>try</emphasis> to get rid of the changeefrom 1.583 + <emphasis>every</emphasis> one of those repositories. This is, 1.584 + of course, not a satisfactory solution: if you miss even a 1.585 + single repository while you're expunging, the change is still 1.586 + <quote>in the wild</quote>, and could propagate further.</para> 1.587 + 1.588 + <para>If you've committed one or more changes 1.589 + <emphasis>after</emphasis> the change that you'd like to see 1.590 + disappear, your options are further reduced. Mercurial doesn't 1.591 + provide a way to <quote>punch a hole</quote> in history, leaving 1.592 + changesets intact.</para> 1.593 + 1.594 + <para>XXX This needs filling out. The 1.595 + <literal>hg-replay</literal> script in the 1.596 + <literal>examples</literal> directory works, but doesn't handle 1.597 + merge changesets. Kind of an important omission.</para> 1.598 + 1.599 + <sect2> 1.600 + <title>Protect yourself from <quote>escaped</quote> 1.601 + changes</title> 1.602 + 1.603 + <para>If you've committed some changes to your local repository 1.604 + and they've been pushed or pulled somewhere else, this isn't 1.605 + necessarily a disaster. You can protect yourself ahead of 1.606 + time against some classes of bad changeset. This is 1.607 + particularly easy if your team usually pulls changes from a 1.608 + central repository.</para> 1.609 + 1.610 + <para>By configuring some hooks on that repository to validate 1.611 + incoming changesets (see chapter <xref linkend="chap:hook"/>), 1.612 + you can 1.613 + automatically prevent some kinds of bad changeset from being 1.614 + pushed to the central repository at all. With such a 1.615 + configuration in place, some kinds of bad changeset will 1.616 + naturally tend to <quote>die out</quote> because they can't 1.617 + propagate into the central repository. Better yet, this 1.618 + happens without any need for explicit intervention.</para> 1.619 + 1.620 + <para>For instance, an incoming change hook that verifies that a 1.621 + changeset will actually compile can prevent people from 1.622 + inadvertantly <quote>breaking the build</quote>.</para> 1.623 + 1.624 + </sect2> 1.625 + </sect1> 1.626 + <sect1 id="sec:undo:bisect"> 1.627 + <title>Finding the source of a bug</title> 1.628 + 1.629 + <para>While it's all very well to be able to back out a changeset 1.630 + that introduced a bug, this requires that you know which 1.631 + changeset to back out. Mercurial provides an invaluable 1.632 + command, called <command role="hg-cmd">hg bisect</command>, that 1.633 + helps you to automate this process and accomplish it very 1.634 + efficiently.</para> 1.635 + 1.636 + <para>The idea behind the <command role="hg-cmd">hg 1.637 + bisect</command> command is that a changeset has introduced 1.638 + some change of behaviour that you can identify with a simple 1.639 + binary test. You don't know which piece of code introduced the 1.640 + change, but you know how to test for the presence of the bug. 1.641 + The <command role="hg-cmd">hg bisect</command> command uses your 1.642 + test to direct its search for the changeset that introduced the 1.643 + code that caused the bug.</para> 1.644 + 1.645 + <para>Here are a few scenarios to help you understand how you 1.646 + might apply this command.</para> 1.647 + <itemizedlist> 1.648 + <listitem><para>The most recent version of your software has a 1.649 + bug that you remember wasn't present a few weeks ago, but 1.650 + you don't know when it was introduced. Here, your binary 1.651 + test checks for the presence of that bug.</para> 1.652 + </listitem> 1.653 + <listitem><para>You fixed a bug in a rush, and now it's time to 1.654 + close the entry in your team's bug database. The bug 1.655 + database requires a changeset ID when you close an entry, 1.656 + but you don't remember which changeset you fixed the bug in. 1.657 + Once again, your binary test checks for the presence of the 1.658 + bug.</para> 1.659 + </listitem> 1.660 + <listitem><para>Your software works correctly, but runs 15% 1.661 + slower than the last time you measured it. You want to know 1.662 + which changeset introduced the performance regression. In 1.663 + this case, your binary test measures the performance of your 1.664 + software, to see whether it's <quote>fast</quote> or 1.665 + <quote>slow</quote>.</para> 1.666 + </listitem> 1.667 + <listitem><para>The sizes of the components of your project that 1.668 + you ship exploded recently, and you suspect that something 1.669 + changed in the way you build your project.</para> 1.670 + </listitem></itemizedlist> 1.671 + 1.672 + <para>From these examples, it should be clear that the <command 1.673 + role="hg-cmd">hg bisect</command> command is not useful only 1.674 + for finding the sources of bugs. You can use it to find any 1.675 + <quote>emergent property</quote> of a repository (anything that 1.676 + you can't find from a simple text search of the files in the 1.677 + tree) for which you can write a binary test.</para> 1.678 + 1.679 + <para>We'll introduce a little bit of terminology here, just to 1.680 + make it clear which parts of the search process are your 1.681 + responsibility, and which are Mercurial's. A 1.682 + <emphasis>test</emphasis> is something that 1.683 + <emphasis>you</emphasis> run when <command role="hg-cmd">hg 1.684 + bisect</command> chooses a changeset. A 1.685 + <emphasis>probe</emphasis> is what <command role="hg-cmd">hg 1.686 + bisect</command> runs to tell whether a revision is good. 1.687 + Finally, we'll use the word <quote>bisect</quote>, as both a 1.688 + noun and a verb, to stand in for the phrase <quote>search using 1.689 + the <command role="hg-cmd">hg bisect</command> 1.690 + command</quote>.</para> 1.691 + 1.692 + <para>One simple way to automate the searching process would be 1.693 + simply to probe every changeset. However, this scales poorly. 1.694 + If it took ten minutes to test a single changeset, and you had 1.695 + 10,000 changesets in your repository, the exhaustive approach 1.696 + would take on average 35 <emphasis>days</emphasis> to find the 1.697 + changeset that introduced a bug. Even if you knew that the bug 1.698 + was introduced by one of the last 500 changesets, and limited 1.699 + your search to those, you'd still be looking at over 40 hours to 1.700 + find the changeset that introduced your bug.</para> 1.701 + 1.702 + <para>What the <command role="hg-cmd">hg bisect</command> command 1.703 + does is use its knowledge of the <quote>shape</quote> of your 1.704 + project's revision history to perform a search in time 1.705 + proportional to the <emphasis>logarithm</emphasis> of the number 1.706 + of changesets to check (the kind of search it performs is called 1.707 + a dichotomic search). With this approach, searching through 1.708 + 10,000 changesets will take less than three hours, even at ten 1.709 + minutes per test (the search will require about 14 tests). 1.710 + Limit your search to the last hundred changesets, and it will 1.711 + take only about an hour (roughly seven tests).</para> 1.712 + 1.713 + <para>The <command role="hg-cmd">hg bisect</command> command is 1.714 + aware of the <quote>branchy</quote> nature of a Mercurial 1.715 + project's revision history, so it has no problems dealing with 1.716 + branches, merges, or multiple heads in a repository. It can 1.717 + prune entire branches of history with a single probe, which is 1.718 + how it operates so efficiently.</para> 1.719 + 1.720 + <sect2> 1.721 + <title>Using the <command role="hg-cmd">hg bisect</command> 1.722 + command</title> 1.723 + 1.724 + <para>Here's an example of <command role="hg-cmd">hg 1.725 + bisect</command> in action.</para> 1.726 + 1.727 + <note> 1.728 + <para> In versions 0.9.5 and earlier of Mercurial, <command 1.729 + role="hg-cmd">hg bisect</command> was not a core command: 1.730 + it was distributed with Mercurial as an extension. This 1.731 + section describes the built-in command, not the old 1.732 + extension.</para> 1.733 + </note> 1.734 + 1.735 + <para>Now let's create a repository, so that we can try out the 1.736 + <command role="hg-cmd">hg bisect</command> command in 1.737 + isolation. <!-- &interaction.bisect.init; --> We'll simulate a 1.738 + project that has a bug in it in a simple-minded way: create 1.739 + trivial changes in a loop, and nominate one specific change 1.740 + that will have the <quote>bug</quote>. This loop creates 35 1.741 + changesets, each adding a single file to the repository. 1.742 + We'll represent our <quote>bug</quote> with a file that 1.743 + contains the text <quote>i have a gub</quote>. <!-- 1.744 + &interaction.bisect.commits; --></para> 1.745 + 1.746 + <para>The next thing that we'd like to do is figure out how to 1.747 + use the <command role="hg-cmd">hg bisect</command> command. 1.748 + We can use Mercurial's normal built-in help mechanism for 1.749 + this. <!-- &interaction.bisect.help; --></para> 1.750 + 1.751 + <para>The <command role="hg-cmd">hg bisect</command> command 1.752 + works in steps. Each step proceeds as follows.</para> 1.753 + <orderedlist> 1.754 + <listitem><para>You run your binary test.</para> 1.755 + <itemizedlist> 1.756 + <listitem><para>If the test succeeded, you tell <command 1.757 + role="hg-cmd">hg bisect</command> by running the 1.758 + <command role="hg-cmd">hg bisect good</command> 1.759 + command.</para> 1.760 + </listitem> 1.761 + <listitem><para>If it failed, run the <command 1.762 + role="hg-cmd">hg bisect bad</command> 1.763 + command.</para></listitem></itemizedlist> 1.764 + </listitem> 1.765 + <listitem><para>The command uses your information to decide 1.766 + which changeset to test next.</para> 1.767 + </listitem> 1.768 + <listitem><para>It updates the working directory to that 1.769 + changeset, and the process begins again.</para> 1.770 + </listitem></orderedlist> 1.771 + <para>The process ends when <command role="hg-cmd">hg 1.772 + bisect</command> identifies a unique changeset that marks 1.773 + the point where your test transitioned from 1.774 + <quote>succeeding</quote> to <quote>failing</quote>.</para> 1.775 + 1.776 + <para>To start the search, we must run the <command 1.777 + role="hg-cmd">hg bisect --reset</command> command. <!-- 1.778 + &interaction.bisect.search.init; --></para> 1.779 + 1.780 + <para>In our case, the binary test we use is simple: we check to 1.781 + see if any file in the repository contains the string <quote>i 1.782 + have a gub</quote>. If it does, this changeset contains the 1.783 + change that <quote>caused the bug</quote>. By convention, a 1.784 + changeset that has the property we're searching for is 1.785 + <quote>bad</quote>, while one that doesn't is 1.786 + <quote>good</quote>.</para> 1.787 + 1.788 + <para>Most of the time, the revision to which the working 1.789 + directory is synced (usually the tip) already exhibits the 1.790 + problem introduced by the buggy change, so we'll mark it as 1.791 + <quote>bad</quote>. <!-- &interaction.bisect.search.bad-init; 1.792 + --></para> 1.793 + 1.794 + <para>Our next task is to nominate a changeset that we know 1.795 + <emphasis>doesn't</emphasis> have the bug; the <command 1.796 + role="hg-cmd">hg bisect</command> command will 1.797 + <quote>bracket</quote> its search between the first pair of 1.798 + good and bad changesets. In our case, we know that revision 1.799 + 10 didn't have the bug. (I'll have more words about choosing 1.800 + the first <quote>good</quote> changeset later.) <!-- 1.801 + &interaction.bisect.search.good-init; --></para> 1.802 + 1.803 + <para>Notice that this command printed some output.</para> 1.804 + <itemizedlist> 1.805 + <listitem><para>It told us how many changesets it must 1.806 + consider before it can identify the one that introduced 1.807 + the bug, and how many tests that will require.</para> 1.808 + </listitem> 1.809 + <listitem><para>It updated the working directory to the next 1.810 + changeset to test, and told us which changeset it's 1.811 + testing.</para> 1.812 + </listitem></itemizedlist> 1.813 + 1.814 + <para>We now run our test in the working directory. We use the 1.815 + <command>grep</command> command to see if our 1.816 + <quote>bad</quote> file is present in the working directory. 1.817 + If it is, this revision is bad; if not, this revision is good. 1.818 + <!-- &interaction.bisect.search.step1; --></para> 1.819 + 1.820 + <para>This test looks like a perfect candidate for automation, 1.821 + so let's turn it into a shell function. <!-- 1.822 + &interaction.bisect.search.mytest; --> We can now run an 1.823 + entire test step with a single command, 1.824 + <literal>mytest</literal>. <!-- 1.825 + &interaction.bisect.search.step2; --> A few more invocations 1.826 + of our canned test step command, and we're done. <!-- 1.827 + &interaction.bisect.search.rest; --></para> 1.828 + 1.829 + <para>Even though we had 40 changesets to search through, the 1.830 + <command role="hg-cmd">hg bisect</command> command let us find 1.831 + the changeset that introduced our <quote>bug</quote> with only 1.832 + five tests. Because the number of tests that the <command 1.833 + role="hg-cmd">hg bisect</command> command performs grows 1.834 + logarithmically with the number of changesets to search, the 1.835 + advantage that it has over the <quote>brute force</quote> 1.836 + search approach increases with every changeset you add.</para> 1.837 + 1.838 + </sect2> 1.839 + <sect2> 1.840 + <title>Cleaning up after your search</title> 1.841 + 1.842 + <para>When you're finished using the <command role="hg-cmd">hg 1.843 + bisect</command> command in a repository, you can use the 1.844 + <command role="hg-cmd">hg bisect reset</command> command to 1.845 + drop the information it was using to drive your search. The 1.846 + command doesn't use much space, so it doesn't matter if you 1.847 + forget to run this command. However, <command 1.848 + role="hg-cmd">hg bisect</command> won't let you start a new 1.849 + search in that repository until you do a <command 1.850 + role="hg-cmd">hg bisect reset</command>. <!-- 1.851 + &interaction.bisect.search.reset; --></para> 1.852 + 1.853 + </sect2> 1.854 + </sect1> 1.855 + <sect1> 1.856 + <title>Tips for finding bugs effectively</title> 1.857 + 1.858 + <sect2> 1.859 + <title>Give consistent input</title> 1.860 + 1.861 + <para>The <command role="hg-cmd">hg bisect</command> command 1.862 + requires that you correctly report the result of every test 1.863 + you perform. If you tell it that a test failed when it really 1.864 + succeeded, it <emphasis>might</emphasis> be able to detect the 1.865 + inconsistency. If it can identify an inconsistency in your 1.866 + reports, it will tell you that a particular changeset is both 1.867 + good and bad. However, it can't do this perfectly; it's about 1.868 + as likely to report the wrong changeset as the source of the 1.869 + bug.</para> 1.870 + 1.871 + </sect2> 1.872 + <sect2> 1.873 + <title>Automate as much as possible</title> 1.874 + 1.875 + <para>When I started using the <command role="hg-cmd">hg 1.876 + bisect</command> command, I tried a few times to run my 1.877 + tests by hand, on the command line. This is an approach that 1.878 + I, at least, am not suited to. After a few tries, I found 1.879 + that I was making enough mistakes that I was having to restart 1.880 + my searches several times before finally getting correct 1.881 + results.</para> 1.882 + 1.883 + <para>My initial problems with driving the <command 1.884 + role="hg-cmd">hg bisect</command> command by hand occurred 1.885 + even with simple searches on small repositories; if the 1.886 + problem you're looking for is more subtle, or the number of 1.887 + tests that <command role="hg-cmd">hg bisect</command> must 1.888 + perform increases, the likelihood of operator error ruining 1.889 + the search is much higher. Once I started automating my 1.890 + tests, I had much better results.</para> 1.891 + 1.892 + <para>The key to automated testing is twofold:</para> 1.893 + <itemizedlist> 1.894 + <listitem><para>always test for the same symptom, and</para> 1.895 + </listitem> 1.896 + <listitem><para>always feed consistent input to the <command 1.897 + role="hg-cmd">hg bisect</command> command.</para> 1.898 + </listitem></itemizedlist> 1.899 + <para>In my tutorial example above, the <command>grep</command> 1.900 + command tests for the symptom, and the <literal>if</literal> 1.901 + statement takes the result of this check and ensures that we 1.902 + always feed the same input to the <command role="hg-cmd">hg 1.903 + bisect</command> command. The <literal>mytest</literal> 1.904 + function marries these together in a reproducible way, so that 1.905 + every test is uniform and consistent.</para> 1.906 + 1.907 + </sect2> 1.908 + <sect2> 1.909 + <title>Check your results</title> 1.910 + 1.911 + <para>Because the output of a <command role="hg-cmd">hg 1.912 + bisect</command> search is only as good as the input you 1.913 + give it, don't take the changeset it reports as the absolute 1.914 + truth. A simple way to cross-check its report is to manually 1.915 + run your test at each of the following changesets:</para> 1.916 + <itemizedlist> 1.917 + <listitem><para>The changeset that it reports as the first bad 1.918 + revision. Your test should still report this as 1.919 + bad.</para> 1.920 + </listitem> 1.921 + <listitem><para>The parent of that changeset (either parent, 1.922 + if it's a merge). Your test should report this changeset 1.923 + as good.</para> 1.924 + </listitem> 1.925 + <listitem><para>A child of that changeset. Your test should 1.926 + report this changeset as bad.</para> 1.927 + </listitem></itemizedlist> 1.928 + 1.929 + </sect2> 1.930 + <sect2> 1.931 + <title>Beware interference between bugs</title> 1.932 + 1.933 + <para>It's possible that your search for one bug could be 1.934 + disrupted by the presence of another. For example, let's say 1.935 + your software crashes at revision 100, and worked correctly at 1.936 + revision 50. Unknown to you, someone else introduced a 1.937 + different crashing bug at revision 60, and fixed it at 1.938 + revision 80. This could distort your results in one of 1.939 + several ways.</para> 1.940 + 1.941 + <para>It is possible that this other bug completely 1.942 + <quote>masks</quote> yours, which is to say that it occurs 1.943 + before your bug has a chance to manifest itself. If you can't 1.944 + avoid that other bug (for example, it prevents your project 1.945 + from building), and so can't tell whether your bug is present 1.946 + in a particular changeset, the <command role="hg-cmd">hg 1.947 + bisect</command> command cannot help you directly. Instead, 1.948 + you can mark a changeset as untested by running <command 1.949 + role="hg-cmd">hg bisect --skip</command>.</para> 1.950 + 1.951 + <para>A different problem could arise if your test for a bug's 1.952 + presence is not specific enough. If you check for <quote>my 1.953 + program crashes</quote>, then both your crashing bug and an 1.954 + unrelated crashing bug that masks it will look like the same 1.955 + thing, and mislead <command role="hg-cmd">hg 1.956 + bisect</command>.</para> 1.957 + 1.958 + <para>Another useful situation in which to use <command 1.959 + role="hg-cmd">hg bisect --skip</command> is if you can't 1.960 + test a revision because your project was in a broken and hence 1.961 + untestable state at that revision, perhaps because someone 1.962 + checked in a change that prevented the project from 1.963 + building.</para> 1.964 + 1.965 + </sect2> 1.966 + <sect2> 1.967 + <title>Bracket your search lazily</title> 1.968 + 1.969 + <para>Choosing the first <quote>good</quote> and 1.970 + <quote>bad</quote> changesets that will mark the end points of 1.971 + your search is often easy, but it bears a little discussion 1.972 + nevertheless. From the perspective of <command 1.973 + role="hg-cmd">hg bisect</command>, the <quote>newest</quote> 1.974 + changeset is conventionally <quote>bad</quote>, and the older 1.975 + changeset is <quote>good</quote>.</para> 1.976 + 1.977 + <para>If you're having trouble remembering when a suitable 1.978 + <quote>good</quote> change was, so that you can tell <command 1.979 + role="hg-cmd">hg bisect</command>, you could do worse than 1.980 + testing changesets at random. Just remember to eliminate 1.981 + contenders that can't possibly exhibit the bug (perhaps 1.982 + because the feature with the bug isn't present yet) and those 1.983 + where another problem masks the bug (as I discussed 1.984 + above).</para> 1.985 + 1.986 + <para>Even if you end up <quote>early</quote> by thousands of 1.987 + changesets or months of history, you will only add a handful 1.988 + of tests to the total number that <command role="hg-cmd">hg 1.989 + bisect</command> must perform, thanks to its logarithmic 1.990 + behaviour.</para> 1.991 + 1.992 + </sect2> 1.993 + </sect1> 1.994 +</chapter> 1.995 + 1.996 +<!-- 1.997 +local variables: 1.998 +sgml-parent-document: ("00book.xml" "book" "chapter") 1.999 +end: 1.1000 +-->