hgbook

annotate en/ch09-undo.xml @ 647:d0160b0b1a9e

Merge with http://hg.serpentine.com/mercurial/book
author Dongsheng Song <dongsheng.song@gmail.com>
date Wed Mar 18 20:32:37 2009 +0800 (2009-03-18)
parents cfdb601a3c8b
children
rev   line source
bos@559 1 <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : -->
bos@559 2
dongsheng@625 3 <chapter id="chap.undo">
bos@572 4 <?dbhtml filename="finding-and-fixing-mistakes.html"?>
bos@572 5 <title>Finding and fixing mistakes</title>
bos@559 6
bos@559 7 <para>To err might be human, but to really handle the consequences
bos@559 8 well takes a top-notch revision control system. In this chapter,
bos@559 9 we'll discuss some of the techniques you can use when you find
bos@559 10 that a problem has crept into your project. Mercurial has some
bos@559 11 highly capable features that will help you to isolate the sources
bos@559 12 of problems, and to handle them appropriately.</para>
bos@559 13
bos@559 14 <sect1>
bos@559 15 <title>Erasing local history</title>
bos@559 16
bos@559 17 <sect2>
bos@559 18 <title>The accidental commit</title>
bos@559 19
bos@559 20 <para>I have the occasional but persistent problem of typing
bos@559 21 rather more quickly than I can think, which sometimes results
bos@559 22 in me committing a changeset that is either incomplete or
bos@559 23 plain wrong. In my case, the usual kind of incomplete
bos@559 24 changeset is one in which I've created a new source file, but
bos@559 25 forgotten to <command role="hg-cmd">hg add</command> it. A
bos@559 26 <quote>plain wrong</quote> changeset is not as common, but no
bos@559 27 less annoying.</para>
bos@559 28
bos@559 29 </sect2>
dongsheng@625 30 <sect2 id="sec.undo.rollback">
bos@559 31 <title>Rolling back a transaction</title>
bos@559 32
dongsheng@625 33 <para>In section <xref linkend="sec.concepts.txn"/>, I mentioned
bos@559 34 that Mercurial treats each modification of a repository as a
bos@559 35 <emphasis>transaction</emphasis>. Every time you commit a
bos@559 36 changeset or pull changes from another repository, Mercurial
bos@559 37 remembers what you did. You can undo, or <emphasis>roll
bos@559 38 back</emphasis>, exactly one of these actions using the
bos@559 39 <command role="hg-cmd">hg rollback</command> command. (See
dongsheng@625 40 section <xref linkend="sec.undo.rollback-after-push"/> for an
bos@559 41 important caveat about the use of this command.)</para>
bos@559 42
bos@559 43 <para>Here's a mistake that I often find myself making:
bos@559 44 committing a change in which I've created a new file, but
bos@567 45 forgotten to <command role="hg-cmd">hg add</command>
bos@567 46 it.</para>
bos@567 47
bos@567 48 &interaction.rollback.commit;
bos@567 49
bos@567 50 <para>Looking at the output of <command role="hg-cmd">hg
bos@567 51 status</command> after the commit immediately confirms the
bos@567 52 error.</para>
bos@567 53
bos@567 54 &interaction.rollback.status;
bos@567 55
bos@567 56 <para>The commit captured the changes to the file
bos@567 57 <filename>a</filename>, but not the new file
bos@567 58 <filename>b</filename>. If I were to push this changeset to a
bos@567 59 repository that I shared with a colleague, the chances are
bos@567 60 high that something in <filename>a</filename> would refer to
bos@567 61 <filename>b</filename>, which would not be present in their
bos@559 62 repository when they pulled my changes. I would thus become
bos@559 63 the object of some indignation.</para>
bos@559 64
bos@559 65 <para>However, luck is with me&emdash;I've caught my error
bos@559 66 before I pushed the changeset. I use the <command
bos@559 67 role="hg-cmd">hg rollback</command> command, and Mercurial
bos@567 68 makes that last changeset vanish.</para>
bos@567 69
bos@567 70 &interaction.rollback.rollback;
bos@567 71
bos@567 72 <para>Notice that the changeset is no longer present in the
bos@567 73 repository's history, and the working directory once again
bos@567 74 thinks that the file <filename>a</filename> is modified. The
bos@567 75 commit and rollback have left the working directory exactly as
bos@567 76 it was prior to the commit; the changeset has been completely
bos@567 77 erased. I can now safely <command role="hg-cmd">hg
bos@567 78 add</command> the file <filename>b</filename>, and rerun my
bos@567 79 commit.</para>
bos@567 80
bos@567 81 &interaction.rollback.add;
bos@559 82
bos@559 83 </sect2>
bos@559 84 <sect2>
bos@559 85 <title>The erroneous pull</title>
bos@559 86
bos@559 87 <para>It's common practice with Mercurial to maintain separate
bos@559 88 development branches of a project in different repositories.
bos@559 89 Your development team might have one shared repository for
bos@559 90 your project's <quote>0.9</quote> release, and another,
bos@559 91 containing different changes, for the <quote>1.0</quote>
bos@559 92 release.</para>
bos@559 93
bos@559 94 <para>Given this, you can imagine that the consequences could be
bos@559 95 messy if you had a local <quote>0.9</quote> repository, and
bos@559 96 accidentally pulled changes from the shared <quote>1.0</quote>
bos@559 97 repository into it. At worst, you could be paying
bos@559 98 insufficient attention, and push those changes into the shared
bos@559 99 <quote>0.9</quote> tree, confusing your entire team (but don't
bos@559 100 worry, we'll return to this horror scenario later). However,
bos@559 101 it's more likely that you'll notice immediately, because
bos@559 102 Mercurial will display the URL it's pulling from, or you will
bos@559 103 see it pull a suspiciously large number of changes into the
bos@559 104 repository.</para>
bos@559 105
bos@559 106 <para>The <command role="hg-cmd">hg rollback</command> command
bos@559 107 will work nicely to expunge all of the changesets that you
bos@559 108 just pulled. Mercurial groups all changes from one <command
bos@559 109 role="hg-cmd">hg pull</command> into a single transaction,
bos@559 110 so one <command role="hg-cmd">hg rollback</command> is all you
bos@559 111 need to undo this mistake.</para>
bos@559 112
bos@559 113 </sect2>
dongsheng@625 114 <sect2 id="sec.undo.rollback-after-push">
bos@559 115 <title>Rolling back is useless once you've pushed</title>
bos@559 116
bos@559 117 <para>The value of the <command role="hg-cmd">hg
bos@559 118 rollback</command> command drops to zero once you've pushed
bos@559 119 your changes to another repository. Rolling back a change
bos@559 120 makes it disappear entirely, but <emphasis>only</emphasis> in
bos@559 121 the repository in which you perform the <command
bos@559 122 role="hg-cmd">hg rollback</command>. Because a rollback
bos@559 123 eliminates history, there's no way for the disappearance of a
bos@559 124 change to propagate between repositories.</para>
bos@559 125
bos@559 126 <para>If you've pushed a change to another
bos@559 127 repository&emdash;particularly if it's a shared
bos@559 128 repository&emdash;it has essentially <quote>escaped into the
bos@559 129 wild,</quote> and you'll have to recover from your mistake
bos@559 130 in a different way. What will happen if you push a changeset
bos@559 131 somewhere, then roll it back, then pull from the repository
bos@559 132 you pushed to, is that the changeset will reappear in your
bos@559 133 repository.</para>
bos@559 134
bos@559 135 <para>(If you absolutely know for sure that the change you want
bos@559 136 to roll back is the most recent change in the repository that
bos@559 137 you pushed to, <emphasis>and</emphasis> you know that nobody
bos@559 138 else could have pulled it from that repository, you can roll
bos@559 139 back the changeset there, too, but you really should really
bos@559 140 not rely on this working reliably. If you do this, sooner or
bos@559 141 later a change really will make it into a repository that you
bos@559 142 don't directly control (or have forgotten about), and come
bos@559 143 back to bite you.)</para>
bos@559 144
bos@559 145 </sect2>
bos@559 146 <sect2>
bos@559 147 <title>You can only roll back once</title>
bos@559 148
bos@559 149 <para>Mercurial stores exactly one transaction in its
bos@559 150 transaction log; that transaction is the most recent one that
bos@559 151 occurred in the repository. This means that you can only roll
bos@559 152 back one transaction. If you expect to be able to roll back
bos@559 153 one transaction, then its predecessor, this is not the
bos@567 154 behaviour you will get.</para>
bos@567 155
bos@567 156 &interaction.rollback.twice;
bos@567 157
bos@567 158 <para>Once you've rolled back one transaction in a repository,
bos@567 159 you can't roll back again in that repository until you perform
bos@559 160 another commit or pull.</para>
bos@559 161
bos@559 162 </sect2>
bos@559 163 </sect1>
bos@559 164 <sect1>
bos@559 165 <title>Reverting the mistaken change</title>
bos@559 166
bos@559 167 <para>If you make a modification to a file, and decide that you
bos@559 168 really didn't want to change the file at all, and you haven't
bos@559 169 yet committed your changes, the <command role="hg-cmd">hg
bos@559 170 revert</command> command is the one you'll need. It looks at
bos@559 171 the changeset that's the parent of the working directory, and
bos@559 172 restores the contents of the file to their state as of that
bos@559 173 changeset. (That's a long-winded way of saying that, in the
bos@559 174 normal case, it undoes your modifications.)</para>
bos@559 175
bos@559 176 <para>Let's illustrate how the <command role="hg-cmd">hg
bos@559 177 revert</command> command works with yet another small example.
bos@559 178 We'll begin by modifying a file that Mercurial is already
bos@567 179 tracking.</para>
bos@567 180
bos@567 181 &interaction.daily.revert.modify;
bos@567 182
bos@567 183 <para>If we don't
bos@559 184 want that change, we can simply <command role="hg-cmd">hg
bos@567 185 revert</command> the file.</para>
bos@567 186
bos@567 187 &interaction.daily.revert.unmodify;
bos@567 188
bos@567 189 <para>The <command role="hg-cmd">hg revert</command> command
bos@567 190 provides us with an extra degree of safety by saving our
bos@567 191 modified file with a <filename>.orig</filename>
bos@567 192 extension.</para>
bos@567 193
bos@567 194 &interaction.daily.revert.status;
bos@559 195
bos@559 196 <para>Here is a summary of the cases that the <command
bos@559 197 role="hg-cmd">hg revert</command> command can deal with. We
bos@559 198 will describe each of these in more detail in the section that
bos@559 199 follows.</para>
bos@559 200 <itemizedlist>
bos@559 201 <listitem><para>If you modify a file, it will restore the file
bos@559 202 to its unmodified state.</para>
bos@559 203 </listitem>
bos@559 204 <listitem><para>If you <command role="hg-cmd">hg add</command> a
bos@559 205 file, it will undo the <quote>added</quote> state of the
bos@559 206 file, but leave the file itself untouched.</para>
bos@559 207 </listitem>
bos@559 208 <listitem><para>If you delete a file without telling Mercurial,
bos@559 209 it will restore the file to its unmodified contents.</para>
bos@559 210 </listitem>
bos@559 211 <listitem><para>If you use the <command role="hg-cmd">hg
bos@559 212 remove</command> command to remove a file, it will undo
bos@559 213 the <quote>removed</quote> state of the file, and restore
bos@559 214 the file to its unmodified contents.</para>
bos@559 215 </listitem></itemizedlist>
bos@559 216
dongsheng@625 217 <sect2 id="sec.undo.mgmt">
bos@559 218 <title>File management errors</title>
bos@559 219
bos@559 220 <para>The <command role="hg-cmd">hg revert</command> command is
bos@559 221 useful for more than just modified files. It lets you reverse
bos@559 222 the results of all of Mercurial's file management
bos@559 223 commands&emdash;<command role="hg-cmd">hg add</command>,
bos@559 224 <command role="hg-cmd">hg remove</command>, and so on.</para>
bos@559 225
bos@559 226 <para>If you <command role="hg-cmd">hg add</command> a file,
bos@559 227 then decide that in fact you don't want Mercurial to track it,
bos@559 228 use <command role="hg-cmd">hg revert</command> to undo the
bos@559 229 add. Don't worry; Mercurial will not modify the file in any
bos@567 230 way. It will just <quote>unmark</quote> the file.</para>
bos@567 231
bos@567 232 &interaction.daily.revert.add;
bos@559 233
bos@559 234 <para>Similarly, if you ask Mercurial to <command
bos@559 235 role="hg-cmd">hg remove</command> a file, you can use
bos@559 236 <command role="hg-cmd">hg revert</command> to restore it to
bos@559 237 the contents it had as of the parent of the working directory.
bos@567 238 &interaction.daily.revert.remove; This works just as
bos@559 239 well for a file that you deleted by hand, without telling
bos@559 240 Mercurial (recall that in Mercurial terminology, this kind of
bos@567 241 file is called <quote>missing</quote>).</para>
bos@567 242
bos@567 243 &interaction.daily.revert.missing;
bos@559 244
bos@559 245 <para>If you revert a <command role="hg-cmd">hg copy</command>,
bos@559 246 the copied-to file remains in your working directory
bos@559 247 afterwards, untracked. Since a copy doesn't affect the
bos@559 248 copied-from file in any way, Mercurial doesn't do anything
bos@567 249 with the copied-from file.</para>
bos@567 250
bos@567 251 &interaction.daily.revert.copy;
bos@559 252
bos@559 253 <sect3>
bos@559 254 <title>A slightly special case: reverting a rename</title>
bos@559 255
bos@559 256 <para>If you <command role="hg-cmd">hg rename</command> a
bos@559 257 file, there is one small detail that you should remember.
bos@559 258 When you <command role="hg-cmd">hg revert</command> a
bos@559 259 rename, it's not enough to provide the name of the
bos@567 260 renamed-to file, as you can see here.</para>
bos@567 261
bos@567 262 &interaction.daily.revert.rename;
bos@567 263
bos@567 264 <para>As you can see from the output of <command
bos@567 265 role="hg-cmd">hg status</command>, the renamed-to file is
bos@567 266 no longer identified as added, but the
bos@567 267 renamed-<emphasis>from</emphasis> file is still removed!
bos@559 268 This is counter-intuitive (at least to me), but at least
bos@567 269 it's easy to deal with.</para>
bos@567 270
bos@567 271 &interaction.daily.revert.rename-orig;
bos@567 272
bos@567 273 <para>So remember, to revert a <command role="hg-cmd">hg
bos@567 274 rename</command>, you must provide
bos@567 275 <emphasis>both</emphasis> the source and destination
bos@567 276 names.</para>
bos@559 277
bos@559 278 <para>% TODO: the output doesn't look like it will be
bos@559 279 removed!</para>
bos@559 280
bos@559 281 <para>(By the way, if you rename a file, then modify the
bos@559 282 renamed-to file, then revert both components of the rename,
bos@559 283 when Mercurial restores the file that was removed as part of
bos@559 284 the rename, it will be unmodified. If you need the
bos@559 285 modifications in the renamed-to file to show up in the
bos@559 286 renamed-from file, don't forget to copy them over.)</para>
bos@559 287
bos@559 288 <para>These fiddly aspects of reverting a rename arguably
bos@559 289 constitute a small bug in Mercurial.</para>
bos@559 290
bos@559 291 </sect3>
bos@559 292 </sect2>
bos@559 293 </sect1>
bos@559 294 <sect1>
bos@559 295 <title>Dealing with committed changes</title>
bos@559 296
bos@559 297 <para>Consider a case where you have committed a change $a$, and
bos@559 298 another change $b$ on top of it; you then realise that change
bos@559 299 $a$ was incorrect. Mercurial lets you <quote>back out</quote>
bos@559 300 an entire changeset automatically, and building blocks that let
bos@559 301 you reverse part of a changeset by hand.</para>
bos@559 302
bos@559 303 <para>Before you read this section, here's something to keep in
bos@559 304 mind: the <command role="hg-cmd">hg backout</command> command
bos@559 305 undoes changes by <emphasis>adding</emphasis> history, not by
bos@559 306 modifying or erasing it. It's the right tool to use if you're
bos@559 307 fixing bugs, but not if you're trying to undo some change that
bos@559 308 has catastrophic consequences. To deal with those, see section
dongsheng@625 309 <xref linkend="sec.undo.aaaiiieee"/>.</para>
bos@559 310
bos@559 311 <sect2>
bos@559 312 <title>Backing out a changeset</title>
bos@559 313
bos@559 314 <para>The <command role="hg-cmd">hg backout</command> command
bos@559 315 lets you <quote>undo</quote> the effects of an entire
bos@559 316 changeset in an automated fashion. Because Mercurial's
bos@559 317 history is immutable, this command <emphasis>does
bos@559 318 not</emphasis> get rid of the changeset you want to undo.
bos@559 319 Instead, it creates a new changeset that
bos@559 320 <emphasis>reverses</emphasis> the effect of the to-be-undone
bos@559 321 changeset.</para>
bos@559 322
bos@559 323 <para>The operation of the <command role="hg-cmd">hg
bos@559 324 backout</command> command is a little intricate, so let's
bos@559 325 illustrate it with some examples. First, we'll create a
bos@567 326 repository with some simple changes.</para>
bos@567 327
bos@567 328 &interaction.backout.init;
bos@559 329
bos@559 330 <para>The <command role="hg-cmd">hg backout</command> command
bos@559 331 takes a single changeset ID as its argument; this is the
bos@559 332 changeset to back out. Normally, <command role="hg-cmd">hg
bos@559 333 backout</command> will drop you into a text editor to write
bos@559 334 a commit message, so you can record why you're backing the
bos@559 335 change out. In this example, we provide a commit message on
bos@559 336 the command line using the <option
bos@559 337 role="hg-opt-backout">-m</option> option.</para>
bos@559 338
bos@559 339 </sect2>
bos@559 340 <sect2>
bos@559 341 <title>Backing out the tip changeset</title>
bos@559 342
bos@559 343 <para>We're going to start by backing out the last changeset we
bos@567 344 committed.</para>
bos@567 345
bos@567 346 &interaction.backout.simple;
bos@567 347
bos@567 348 <para>You can see that the second line from
bos@567 349 <filename>myfile</filename> is no longer present. Taking a
bos@567 350 look at the output of <command role="hg-cmd">hg log</command>
bos@567 351 gives us an idea of what the <command role="hg-cmd">hg
bos@567 352 backout</command> command has done.
bos@567 353 &interaction.backout.simple.log; Notice that the new changeset
bos@567 354 that <command role="hg-cmd">hg backout</command> has created
bos@567 355 is a child of the changeset we backed out. It's easier to see
bos@567 356 this in figure <xref
dongsheng@640 357 endterm="fig.undo.backout.caption" linkend="fig.undo.backout"/>,
dongsheng@640 358 which presents a graphical
bos@559 359 view of the change history. As you can see, the history is
bos@559 360 nice and linear.</para>
bos@559 361
dongsheng@625 362 <informalfigure id="fig.undo.backout">
dongsheng@640 363 <mediaobject>
dongsheng@640 364 <imageobject><imagedata fileref="images/undo-simple.png"/>
dongsheng@640 365 </imageobject>
dongsheng@640 366 <textobject><phrase>XXX add text</phrase></textobject>
dongsheng@640 367 <caption><para id="fig.undo.backout.caption">Backing out
dongsheng@640 368 a change using the
dongsheng@640 369 <command role="hg-cmd">hg backout</command>
dongsheng@640 370 command</para></caption>
dongsheng@640 371 </mediaobject>
bos@559 372 </informalfigure>
bos@559 373
bos@559 374 </sect2>
bos@559 375 <sect2>
bos@559 376 <title>Backing out a non-tip change</title>
bos@559 377
bos@559 378 <para>If you want to back out a change other than the last one
bos@559 379 you committed, pass the <option
bos@559 380 role="hg-opt-backout">--merge</option> option to the
bos@567 381 <command role="hg-cmd">hg backout</command> command.</para>
bos@567 382
bos@567 383 &interaction.backout.non-tip.clone;
bos@567 384
bos@567 385 <para>This makes backing out any changeset a
bos@567 386 <quote>one-shot</quote> operation that's usually simple and
bos@567 387 fast.</para>
bos@567 388
bos@567 389 &interaction.backout.non-tip.backout;
bos@559 390
bos@559 391 <para>If you take a look at the contents of
bos@559 392 <filename>myfile</filename> after the backout finishes, you'll
bos@559 393 see that the first and third changes are present, but not the
bos@567 394 second.</para>
bos@567 395
bos@567 396 &interaction.backout.non-tip.cat;
bos@559 397
bos@559 398 <para>As the graphical history in figure <xref
dongsheng@640 399 endterm="fig.undo.backout-non-tip.caption"
dongsheng@625 400 linkend="fig.undo.backout-non-tip"/> illustrates, Mercurial
bos@559 401 actually commits <emphasis>two</emphasis> changes in this kind
bos@559 402 of situation (the box-shaped nodes are the ones that Mercurial
bos@559 403 commits automatically). Before Mercurial begins the backout
bos@559 404 process, it first remembers what the current parent of the
bos@559 405 working directory is. It then backs out the target changeset,
bos@559 406 and commits that as a changeset. Finally, it merges back to
bos@559 407 the previous parent of the working directory, and commits the
bos@559 408 result of the merge.</para>
bos@559 409
bos@559 410 <para>% TODO: to me it looks like mercurial doesn't commit the
bos@559 411 second merge automatically!</para>
bos@559 412
dongsheng@625 413 <informalfigure id="fig.undo.backout-non-tip">
dongsheng@640 414 <mediaobject>
dongsheng@640 415 <imageobject><imagedata fileref="images/undo-non-tip.png"/>
dongsheng@640 416 </imageobject>
dongsheng@640 417 <textobject><phrase>XXX add text</phrase></textobject>
dongsheng@640 418 <caption><para id="fig.undo.backout-non-tip.caption">Automated
dongsheng@640 419 backout of a non-tip change using the
dongsheng@640 420 <command role="hg-cmd">hg backout</command> command</para></caption>
dongsheng@640 421 </mediaobject>
bos@559 422 </informalfigure>
bos@559 423
bos@559 424 <para>The result is that you end up <quote>back where you
bos@559 425 were</quote>, only with some extra history that undoes the
bos@559 426 effect of the changeset you wanted to back out.</para>
bos@559 427
bos@559 428 <sect3>
bos@559 429 <title>Always use the <option
bos@559 430 role="hg-opt-backout">--merge</option> option</title>
bos@559 431
bos@559 432 <para>In fact, since the <option
bos@559 433 role="hg-opt-backout">--merge</option> option will do the
bos@559 434 <quote>right thing</quote> whether or not the changeset
bos@559 435 you're backing out is the tip (i.e. it won't try to merge if
bos@559 436 it's backing out the tip, since there's no need), you should
bos@559 437 <emphasis>always</emphasis> use this option when you run the
bos@559 438 <command role="hg-cmd">hg backout</command> command.</para>
bos@559 439
bos@559 440 </sect3>
bos@559 441 </sect2>
bos@559 442 <sect2>
bos@559 443 <title>Gaining more control of the backout process</title>
bos@559 444
bos@559 445 <para>While I've recommended that you always use the <option
bos@559 446 role="hg-opt-backout">--merge</option> option when backing
bos@559 447 out a change, the <command role="hg-cmd">hg backout</command>
bos@559 448 command lets you decide how to merge a backout changeset.
bos@559 449 Taking control of the backout process by hand is something you
bos@559 450 will rarely need to do, but it can be useful to understand
bos@559 451 what the <command role="hg-cmd">hg backout</command> command
bos@559 452 is doing for you automatically. To illustrate this, let's
bos@559 453 clone our first repository, but omit the backout change that
bos@559 454 it contains.</para>
bos@559 455
bos@567 456 &interaction.backout.manual.clone;
bos@567 457
bos@567 458 <para>As with our
bos@559 459 earlier example, We'll commit a third changeset, then back out
bos@567 460 its parent, and see what happens.</para>
bos@567 461
bos@567 462 &interaction.backout.manual.backout;
bos@567 463
bos@567 464 <para>Our new changeset is again a descendant of the changeset
bos@567 465 we backout out; it's thus a new head, <emphasis>not</emphasis>
bos@567 466 a descendant of the changeset that was the tip. The <command
bos@567 467 role="hg-cmd">hg backout</command> command was quite
bos@567 468 explicit in telling us this.</para>
bos@567 469
bos@567 470 &interaction.backout.manual.log;
bos@559 471
bos@559 472 <para>Again, it's easier to see what has happened by looking at
bos@559 473 a graph of the revision history, in figure <xref
dongsheng@640 474 endterm="fig.undo.backout-manual.caption"
dongsheng@625 475 linkend="fig.undo.backout-manual"/>. This makes it clear
bos@559 476 that when we use <command role="hg-cmd">hg backout</command>
bos@559 477 to back out a change other than the tip, Mercurial adds a new
bos@559 478 head to the repository (the change it committed is
bos@559 479 box-shaped).</para>
bos@559 480
dongsheng@625 481 <informalfigure id="fig.undo.backout-manual">
dongsheng@640 482 <mediaobject>
dongsheng@640 483 <imageobject><imagedata fileref="images/undo-manual.png"/>
dongsheng@640 484 </imageobject>
dongsheng@640 485 <textobject><phrase>XXX add text</phrase></textobject>
dongsheng@640 486 <caption><para id="fig.undo.backout-manual.caption">Backing out a
dongsheng@640 487 change using the <command role="hg-cmd">hg backout</command>
dongsheng@640 488 command</para></caption>
dongsheng@640 489 </mediaobject>
bos@559 490 </informalfigure>
bos@559 491
bos@559 492 <para>After the <command role="hg-cmd">hg backout</command>
bos@559 493 command has completed, it leaves the new
bos@559 494 <quote>backout</quote> changeset as the parent of the working
bos@567 495 directory.</para>
bos@567 496
bos@567 497 &interaction.backout.manual.parents;
bos@567 498
bos@567 499 <para>Now we have two isolated sets of changes.</para>
bos@567 500
bos@567 501 &interaction.backout.manual.heads;
bos@559 502
bos@559 503 <para>Let's think about what we expect to see as the contents of
bos@559 504 <filename>myfile</filename> now. The first change should be
bos@559 505 present, because we've never backed it out. The second change
bos@559 506 should be missing, as that's the change we backed out. Since
bos@559 507 the history graph shows the third change as a separate head,
bos@559 508 we <emphasis>don't</emphasis> expect to see the third change
bos@567 509 present in <filename>myfile</filename>.</para>
bos@567 510
bos@567 511 &interaction.backout.manual.cat;
bos@567 512
bos@567 513 <para>To get the third change back into the file, we just do a
bos@567 514 normal merge of our two heads.</para>
bos@567 515
bos@567 516 &interaction.backout.manual.merge;
bos@567 517
bos@567 518 <para>Afterwards, the graphical history of our repository looks
bos@567 519 like figure
dongsheng@640 520 <xref endterm="fig.undo.backout-manual-merge.caption"
dongsheng@640 521 linkend="fig.undo.backout-manual-merge"/>.</para>
dongsheng@625 522
dongsheng@625 523 <informalfigure id="fig.undo.backout-manual-merge">
dongsheng@640 524 <mediaobject>
dongsheng@640 525 <imageobject><imagedata fileref="images/undo-manual-merge.png"/>
dongsheng@640 526 </imageobject>
dongsheng@640 527 <textobject><phrase>XXX add text</phrase></textobject>
dongsheng@640 528 <caption><para id="fig.undo.backout-manual-merge.caption">Manually
dongsheng@640 529 merging a backout change</para></caption>
dongsheng@640 530 </mediaobject>
bos@559 531 </informalfigure>
bos@559 532
bos@559 533 </sect2>
bos@559 534 <sect2>
bos@559 535 <title>Why <command role="hg-cmd">hg backout</command> works as
bos@559 536 it does</title>
bos@559 537
bos@559 538 <para>Here's a brief description of how the <command
bos@559 539 role="hg-cmd">hg backout</command> command works.</para>
bos@559 540 <orderedlist>
bos@559 541 <listitem><para>It ensures that the working directory is
bos@559 542 <quote>clean</quote>, i.e. that the output of <command
bos@559 543 role="hg-cmd">hg status</command> would be empty.</para>
bos@559 544 </listitem>
bos@559 545 <listitem><para>It remembers the current parent of the working
bos@559 546 directory. Let's call this changeset
bos@559 547 <literal>orig</literal></para>
bos@559 548 </listitem>
bos@559 549 <listitem><para>It does the equivalent of a <command
bos@559 550 role="hg-cmd">hg update</command> to sync the working
bos@559 551 directory to the changeset you want to back out. Let's
bos@559 552 call this changeset <literal>backout</literal></para>
bos@559 553 </listitem>
bos@559 554 <listitem><para>It finds the parent of that changeset. Let's
bos@559 555 call that changeset <literal>parent</literal>.</para>
bos@559 556 </listitem>
bos@559 557 <listitem><para>For each file that the
bos@559 558 <literal>backout</literal> changeset affected, it does the
bos@559 559 equivalent of a <command role="hg-cmd">hg revert -r
bos@559 560 parent</command> on that file, to restore it to the
bos@559 561 contents it had before that changeset was
bos@559 562 committed.</para>
bos@559 563 </listitem>
bos@559 564 <listitem><para>It commits the result as a new changeset.
bos@559 565 This changeset has <literal>backout</literal> as its
bos@559 566 parent.</para>
bos@559 567 </listitem>
bos@559 568 <listitem><para>If you specify <option
bos@559 569 role="hg-opt-backout">--merge</option> on the command
bos@559 570 line, it merges with <literal>orig</literal>, and commits
bos@559 571 the result of the merge.</para>
bos@559 572 </listitem></orderedlist>
bos@559 573
bos@559 574 <para>An alternative way to implement the <command
bos@559 575 role="hg-cmd">hg backout</command> command would be to
bos@559 576 <command role="hg-cmd">hg export</command> the
bos@559 577 to-be-backed-out changeset as a diff, then use the <option
bos@559 578 role="cmd-opt-patch">--reverse</option> option to the
bos@559 579 <command>patch</command> command to reverse the effect of the
bos@559 580 change without fiddling with the working directory. This
bos@559 581 sounds much simpler, but it would not work nearly as
bos@559 582 well.</para>
bos@559 583
bos@559 584 <para>The reason that <command role="hg-cmd">hg
bos@559 585 backout</command> does an update, a commit, a merge, and
bos@559 586 another commit is to give the merge machinery the best chance
bos@559 587 to do a good job when dealing with all the changes
bos@559 588 <emphasis>between</emphasis> the change you're backing out and
bos@559 589 the current tip.</para>
bos@559 590
bos@559 591 <para>If you're backing out a changeset that's 100 revisions
bos@559 592 back in your project's history, the chances that the
bos@559 593 <command>patch</command> command will be able to apply a
bos@559 594 reverse diff cleanly are not good, because intervening changes
bos@559 595 are likely to have <quote>broken the context</quote> that
bos@559 596 <command>patch</command> uses to determine whether it can
bos@559 597 apply a patch (if this sounds like gibberish, see <xref
dongsheng@625 598 linkend="sec.mq.patch"/> for a
bos@559 599 discussion of the <command>patch</command> command). Also,
bos@559 600 Mercurial's merge machinery will handle files and directories
bos@559 601 being renamed, permission changes, and modifications to binary
bos@559 602 files, none of which <command>patch</command> can deal
bos@559 603 with.</para>
bos@559 604
bos@559 605 </sect2>
bos@559 606 </sect1>
dongsheng@625 607 <sect1 id="sec.undo.aaaiiieee">
bos@559 608 <title>Changes that should never have been</title>
bos@559 609
bos@559 610 <para>Most of the time, the <command role="hg-cmd">hg
bos@559 611 backout</command> command is exactly what you need if you want
bos@559 612 to undo the effects of a change. It leaves a permanent record
bos@559 613 of exactly what you did, both when committing the original
bos@559 614 changeset and when you cleaned up after it.</para>
bos@559 615
bos@559 616 <para>On rare occasions, though, you may find that you've
bos@559 617 committed a change that really should not be present in the
bos@559 618 repository at all. For example, it would be very unusual, and
bos@559 619 usually considered a mistake, to commit a software project's
bos@559 620 object files as well as its source files. Object files have
bos@559 621 almost no intrinsic value, and they're <emphasis>big</emphasis>,
bos@559 622 so they increase the size of the repository and the amount of
bos@559 623 time it takes to clone or pull changes.</para>
bos@559 624
bos@559 625 <para>Before I discuss the options that you have if you commit a
bos@559 626 <quote>brown paper bag</quote> change (the kind that's so bad
bos@559 627 that you want to pull a brown paper bag over your head), let me
bos@559 628 first discuss some approaches that probably won't work.</para>
bos@559 629
bos@559 630 <para>Since Mercurial treats history as accumulative&emdash;every
bos@559 631 change builds on top of all changes that preceded it&emdash;you
bos@559 632 generally can't just make disastrous changes disappear. The one
bos@559 633 exception is when you've just committed a change, and it hasn't
bos@559 634 been pushed or pulled into another repository. That's when you
bos@559 635 can safely use the <command role="hg-cmd">hg rollback</command>
bos@559 636 command, as I detailed in section <xref
dongsheng@625 637 linkend="sec.undo.rollback"/>.</para>
bos@559 638
bos@559 639 <para>After you've pushed a bad change to another repository, you
bos@559 640 <emphasis>could</emphasis> still use <command role="hg-cmd">hg
bos@559 641 rollback</command> to make your local copy of the change
bos@559 642 disappear, but it won't have the consequences you want. The
bos@559 643 change will still be present in the remote repository, so it
bos@559 644 will reappear in your local repository the next time you
bos@559 645 pull.</para>
bos@559 646
bos@559 647 <para>If a situation like this arises, and you know which
bos@559 648 repositories your bad change has propagated into, you can
bos@559 649 <emphasis>try</emphasis> to get rid of the changeefrom
bos@559 650 <emphasis>every</emphasis> one of those repositories. This is,
bos@559 651 of course, not a satisfactory solution: if you miss even a
bos@559 652 single repository while you're expunging, the change is still
bos@559 653 <quote>in the wild</quote>, and could propagate further.</para>
bos@559 654
bos@559 655 <para>If you've committed one or more changes
bos@559 656 <emphasis>after</emphasis> the change that you'd like to see
bos@559 657 disappear, your options are further reduced. Mercurial doesn't
bos@559 658 provide a way to <quote>punch a hole</quote> in history, leaving
bos@559 659 changesets intact.</para>
bos@559 660
bos@559 661 <para>XXX This needs filling out. The
bos@559 662 <literal>hg-replay</literal> script in the
bos@559 663 <literal>examples</literal> directory works, but doesn't handle
bos@559 664 merge changesets. Kind of an important omission.</para>
bos@559 665
bos@559 666 <sect2>
bos@559 667 <title>Protect yourself from <quote>escaped</quote>
bos@559 668 changes</title>
bos@559 669
bos@559 670 <para>If you've committed some changes to your local repository
bos@559 671 and they've been pushed or pulled somewhere else, this isn't
bos@559 672 necessarily a disaster. You can protect yourself ahead of
bos@559 673 time against some classes of bad changeset. This is
bos@559 674 particularly easy if your team usually pulls changes from a
bos@559 675 central repository.</para>
bos@559 676
bos@559 677 <para>By configuring some hooks on that repository to validate
dongsheng@625 678 incoming changesets (see chapter <xref linkend="chap.hook"/>),
bos@559 679 you can
bos@559 680 automatically prevent some kinds of bad changeset from being
bos@559 681 pushed to the central repository at all. With such a
bos@559 682 configuration in place, some kinds of bad changeset will
bos@559 683 naturally tend to <quote>die out</quote> because they can't
bos@559 684 propagate into the central repository. Better yet, this
bos@559 685 happens without any need for explicit intervention.</para>
bos@559 686
bos@559 687 <para>For instance, an incoming change hook that verifies that a
bos@559 688 changeset will actually compile can prevent people from
bos@559 689 inadvertantly <quote>breaking the build</quote>.</para>
bos@559 690
bos@559 691 </sect2>
bos@559 692 </sect1>
dongsheng@625 693 <sect1 id="sec.undo.bisect">
bos@559 694 <title>Finding the source of a bug</title>
bos@559 695
bos@559 696 <para>While it's all very well to be able to back out a changeset
bos@559 697 that introduced a bug, this requires that you know which
bos@559 698 changeset to back out. Mercurial provides an invaluable
bos@559 699 command, called <command role="hg-cmd">hg bisect</command>, that
bos@559 700 helps you to automate this process and accomplish it very
bos@559 701 efficiently.</para>
bos@559 702
bos@559 703 <para>The idea behind the <command role="hg-cmd">hg
bos@559 704 bisect</command> command is that a changeset has introduced
bos@559 705 some change of behaviour that you can identify with a simple
bos@559 706 binary test. You don't know which piece of code introduced the
bos@559 707 change, but you know how to test for the presence of the bug.
bos@559 708 The <command role="hg-cmd">hg bisect</command> command uses your
bos@559 709 test to direct its search for the changeset that introduced the
bos@559 710 code that caused the bug.</para>
bos@559 711
bos@559 712 <para>Here are a few scenarios to help you understand how you
bos@559 713 might apply this command.</para>
bos@559 714 <itemizedlist>
bos@559 715 <listitem><para>The most recent version of your software has a
bos@559 716 bug that you remember wasn't present a few weeks ago, but
bos@559 717 you don't know when it was introduced. Here, your binary
bos@559 718 test checks for the presence of that bug.</para>
bos@559 719 </listitem>
bos@559 720 <listitem><para>You fixed a bug in a rush, and now it's time to
bos@559 721 close the entry in your team's bug database. The bug
bos@559 722 database requires a changeset ID when you close an entry,
bos@559 723 but you don't remember which changeset you fixed the bug in.
bos@559 724 Once again, your binary test checks for the presence of the
bos@559 725 bug.</para>
bos@559 726 </listitem>
bos@559 727 <listitem><para>Your software works correctly, but runs 15%
bos@559 728 slower than the last time you measured it. You want to know
bos@559 729 which changeset introduced the performance regression. In
bos@559 730 this case, your binary test measures the performance of your
bos@559 731 software, to see whether it's <quote>fast</quote> or
bos@559 732 <quote>slow</quote>.</para>
bos@559 733 </listitem>
bos@559 734 <listitem><para>The sizes of the components of your project that
bos@559 735 you ship exploded recently, and you suspect that something
bos@559 736 changed in the way you build your project.</para>
bos@559 737 </listitem></itemizedlist>
bos@559 738
bos@559 739 <para>From these examples, it should be clear that the <command
bos@559 740 role="hg-cmd">hg bisect</command> command is not useful only
bos@559 741 for finding the sources of bugs. You can use it to find any
bos@559 742 <quote>emergent property</quote> of a repository (anything that
bos@559 743 you can't find from a simple text search of the files in the
bos@559 744 tree) for which you can write a binary test.</para>
bos@559 745
bos@559 746 <para>We'll introduce a little bit of terminology here, just to
bos@559 747 make it clear which parts of the search process are your
bos@559 748 responsibility, and which are Mercurial's. A
bos@559 749 <emphasis>test</emphasis> is something that
bos@559 750 <emphasis>you</emphasis> run when <command role="hg-cmd">hg
bos@559 751 bisect</command> chooses a changeset. A
bos@559 752 <emphasis>probe</emphasis> is what <command role="hg-cmd">hg
bos@559 753 bisect</command> runs to tell whether a revision is good.
bos@559 754 Finally, we'll use the word <quote>bisect</quote>, as both a
bos@559 755 noun and a verb, to stand in for the phrase <quote>search using
bos@559 756 the <command role="hg-cmd">hg bisect</command>
bos@559 757 command</quote>.</para>
bos@559 758
bos@559 759 <para>One simple way to automate the searching process would be
bos@559 760 simply to probe every changeset. However, this scales poorly.
bos@559 761 If it took ten minutes to test a single changeset, and you had
bos@559 762 10,000 changesets in your repository, the exhaustive approach
bos@559 763 would take on average 35 <emphasis>days</emphasis> to find the
bos@559 764 changeset that introduced a bug. Even if you knew that the bug
bos@559 765 was introduced by one of the last 500 changesets, and limited
bos@559 766 your search to those, you'd still be looking at over 40 hours to
bos@559 767 find the changeset that introduced your bug.</para>
bos@559 768
bos@559 769 <para>What the <command role="hg-cmd">hg bisect</command> command
bos@559 770 does is use its knowledge of the <quote>shape</quote> of your
bos@559 771 project's revision history to perform a search in time
bos@559 772 proportional to the <emphasis>logarithm</emphasis> of the number
bos@559 773 of changesets to check (the kind of search it performs is called
bos@559 774 a dichotomic search). With this approach, searching through
bos@559 775 10,000 changesets will take less than three hours, even at ten
bos@559 776 minutes per test (the search will require about 14 tests).
bos@559 777 Limit your search to the last hundred changesets, and it will
bos@559 778 take only about an hour (roughly seven tests).</para>
bos@559 779
bos@559 780 <para>The <command role="hg-cmd">hg bisect</command> command is
bos@559 781 aware of the <quote>branchy</quote> nature of a Mercurial
bos@559 782 project's revision history, so it has no problems dealing with
bos@559 783 branches, merges, or multiple heads in a repository. It can
bos@559 784 prune entire branches of history with a single probe, which is
bos@559 785 how it operates so efficiently.</para>
bos@559 786
bos@559 787 <sect2>
bos@559 788 <title>Using the <command role="hg-cmd">hg bisect</command>
bos@559 789 command</title>
bos@559 790
bos@559 791 <para>Here's an example of <command role="hg-cmd">hg
bos@559 792 bisect</command> in action.</para>
bos@559 793
bos@559 794 <note>
bos@559 795 <para> In versions 0.9.5 and earlier of Mercurial, <command
bos@559 796 role="hg-cmd">hg bisect</command> was not a core command:
bos@559 797 it was distributed with Mercurial as an extension. This
bos@559 798 section describes the built-in command, not the old
bos@559 799 extension.</para>
bos@559 800 </note>
bos@559 801
bos@559 802 <para>Now let's create a repository, so that we can try out the
bos@559 803 <command role="hg-cmd">hg bisect</command> command in
bos@567 804 isolation.</para>
bos@567 805
bos@567 806 &interaction.bisect.init;
bos@567 807
bos@567 808 <para>We'll simulate a project that has a bug in it in a
bos@567 809 simple-minded way: create trivial changes in a loop, and
bos@567 810 nominate one specific change that will have the
bos@567 811 <quote>bug</quote>. This loop creates 35 changesets, each
bos@567 812 adding a single file to the repository. We'll represent our
bos@567 813 <quote>bug</quote> with a file that contains the text <quote>i
bos@567 814 have a gub</quote>.</para>
bos@567 815
bos@567 816 &interaction.bisect.commits;
bos@559 817
bos@559 818 <para>The next thing that we'd like to do is figure out how to
bos@559 819 use the <command role="hg-cmd">hg bisect</command> command.
bos@559 820 We can use Mercurial's normal built-in help mechanism for
bos@567 821 this.</para>
bos@567 822
bos@567 823 &interaction.bisect.help;
bos@559 824
bos@559 825 <para>The <command role="hg-cmd">hg bisect</command> command
bos@559 826 works in steps. Each step proceeds as follows.</para>
bos@559 827 <orderedlist>
bos@559 828 <listitem><para>You run your binary test.</para>
bos@559 829 <itemizedlist>
bos@559 830 <listitem><para>If the test succeeded, you tell <command
bos@559 831 role="hg-cmd">hg bisect</command> by running the
bos@559 832 <command role="hg-cmd">hg bisect good</command>
bos@559 833 command.</para>
bos@559 834 </listitem>
bos@559 835 <listitem><para>If it failed, run the <command
bos@559 836 role="hg-cmd">hg bisect bad</command>
bos@559 837 command.</para></listitem></itemizedlist>
bos@559 838 </listitem>
bos@559 839 <listitem><para>The command uses your information to decide
bos@559 840 which changeset to test next.</para>
bos@559 841 </listitem>
bos@559 842 <listitem><para>It updates the working directory to that
bos@559 843 changeset, and the process begins again.</para>
bos@559 844 </listitem></orderedlist>
bos@559 845 <para>The process ends when <command role="hg-cmd">hg
bos@559 846 bisect</command> identifies a unique changeset that marks
bos@559 847 the point where your test transitioned from
bos@559 848 <quote>succeeding</quote> to <quote>failing</quote>.</para>
bos@559 849
bos@559 850 <para>To start the search, we must run the <command
bos@567 851 role="hg-cmd">hg bisect --reset</command> command.</para>
bos@567 852
bos@567 853 &interaction.bisect.search.init;
bos@559 854
bos@559 855 <para>In our case, the binary test we use is simple: we check to
bos@559 856 see if any file in the repository contains the string <quote>i
bos@559 857 have a gub</quote>. If it does, this changeset contains the
bos@559 858 change that <quote>caused the bug</quote>. By convention, a
bos@559 859 changeset that has the property we're searching for is
bos@559 860 <quote>bad</quote>, while one that doesn't is
bos@559 861 <quote>good</quote>.</para>
bos@559 862
bos@559 863 <para>Most of the time, the revision to which the working
bos@559 864 directory is synced (usually the tip) already exhibits the
bos@559 865 problem introduced by the buggy change, so we'll mark it as
bos@567 866 <quote>bad</quote>.</para>
bos@567 867
bos@567 868 &interaction.bisect.search.bad-init;
bos@559 869
bos@559 870 <para>Our next task is to nominate a changeset that we know
bos@559 871 <emphasis>doesn't</emphasis> have the bug; the <command
bos@559 872 role="hg-cmd">hg bisect</command> command will
bos@559 873 <quote>bracket</quote> its search between the first pair of
bos@559 874 good and bad changesets. In our case, we know that revision
bos@559 875 10 didn't have the bug. (I'll have more words about choosing
bos@567 876 the first <quote>good</quote> changeset later.)</para>
bos@567 877
bos@567 878 &interaction.bisect.search.good-init;
bos@559 879
bos@559 880 <para>Notice that this command printed some output.</para>
bos@559 881 <itemizedlist>
bos@559 882 <listitem><para>It told us how many changesets it must
bos@559 883 consider before it can identify the one that introduced
bos@559 884 the bug, and how many tests that will require.</para>
bos@559 885 </listitem>
bos@559 886 <listitem><para>It updated the working directory to the next
bos@559 887 changeset to test, and told us which changeset it's
bos@559 888 testing.</para>
bos@559 889 </listitem></itemizedlist>
bos@559 890
bos@559 891 <para>We now run our test in the working directory. We use the
bos@559 892 <command>grep</command> command to see if our
bos@559 893 <quote>bad</quote> file is present in the working directory.
bos@559 894 If it is, this revision is bad; if not, this revision is good.
bos@567 895 &interaction.bisect.search.step1;</para>
bos@559 896
bos@559 897 <para>This test looks like a perfect candidate for automation,
bos@567 898 so let's turn it into a shell function.</para>
bos@567 899 &interaction.bisect.search.mytest;
bos@567 900
bos@567 901 <para>We can now run an entire test step with a single command,
bos@567 902 <literal>mytest</literal>.</para>
bos@567 903
bos@567 904 &interaction.bisect.search.step2;
bos@567 905
bos@567 906 <para>A few more invocations of our canned test step command,
bos@567 907 and we're done.</para>
bos@567 908
bos@567 909 &interaction.bisect.search.rest;
bos@559 910
bos@559 911 <para>Even though we had 40 changesets to search through, the
bos@559 912 <command role="hg-cmd">hg bisect</command> command let us find
bos@559 913 the changeset that introduced our <quote>bug</quote> with only
bos@559 914 five tests. Because the number of tests that the <command
bos@559 915 role="hg-cmd">hg bisect</command> command performs grows
bos@559 916 logarithmically with the number of changesets to search, the
bos@559 917 advantage that it has over the <quote>brute force</quote>
bos@559 918 search approach increases with every changeset you add.</para>
bos@559 919
bos@559 920 </sect2>
bos@559 921 <sect2>
bos@559 922 <title>Cleaning up after your search</title>
bos@559 923
bos@559 924 <para>When you're finished using the <command role="hg-cmd">hg
bos@559 925 bisect</command> command in a repository, you can use the
bos@559 926 <command role="hg-cmd">hg bisect reset</command> command to
bos@559 927 drop the information it was using to drive your search. The
bos@559 928 command doesn't use much space, so it doesn't matter if you
bos@559 929 forget to run this command. However, <command
bos@559 930 role="hg-cmd">hg bisect</command> won't let you start a new
bos@559 931 search in that repository until you do a <command
bos@567 932 role="hg-cmd">hg bisect reset</command>.</para>
bos@567 933
bos@567 934 &interaction.bisect.search.reset;
bos@559 935
bos@559 936 </sect2>
bos@559 937 </sect1>
bos@559 938 <sect1>
bos@559 939 <title>Tips for finding bugs effectively</title>
bos@559 940
bos@559 941 <sect2>
bos@559 942 <title>Give consistent input</title>
bos@559 943
bos@559 944 <para>The <command role="hg-cmd">hg bisect</command> command
bos@559 945 requires that you correctly report the result of every test
bos@559 946 you perform. If you tell it that a test failed when it really
bos@559 947 succeeded, it <emphasis>might</emphasis> be able to detect the
bos@559 948 inconsistency. If it can identify an inconsistency in your
bos@559 949 reports, it will tell you that a particular changeset is both
bos@559 950 good and bad. However, it can't do this perfectly; it's about
bos@559 951 as likely to report the wrong changeset as the source of the
bos@559 952 bug.</para>
bos@559 953
bos@559 954 </sect2>
bos@559 955 <sect2>
bos@559 956 <title>Automate as much as possible</title>
bos@559 957
bos@559 958 <para>When I started using the <command role="hg-cmd">hg
bos@559 959 bisect</command> command, I tried a few times to run my
bos@559 960 tests by hand, on the command line. This is an approach that
bos@559 961 I, at least, am not suited to. After a few tries, I found
bos@559 962 that I was making enough mistakes that I was having to restart
bos@559 963 my searches several times before finally getting correct
bos@559 964 results.</para>
bos@559 965
bos@559 966 <para>My initial problems with driving the <command
bos@559 967 role="hg-cmd">hg bisect</command> command by hand occurred
bos@559 968 even with simple searches on small repositories; if the
bos@559 969 problem you're looking for is more subtle, or the number of
bos@559 970 tests that <command role="hg-cmd">hg bisect</command> must
bos@559 971 perform increases, the likelihood of operator error ruining
bos@559 972 the search is much higher. Once I started automating my
bos@559 973 tests, I had much better results.</para>
bos@559 974
bos@559 975 <para>The key to automated testing is twofold:</para>
bos@559 976 <itemizedlist>
bos@559 977 <listitem><para>always test for the same symptom, and</para>
bos@559 978 </listitem>
bos@559 979 <listitem><para>always feed consistent input to the <command
bos@559 980 role="hg-cmd">hg bisect</command> command.</para>
bos@559 981 </listitem></itemizedlist>
bos@559 982 <para>In my tutorial example above, the <command>grep</command>
bos@559 983 command tests for the symptom, and the <literal>if</literal>
bos@559 984 statement takes the result of this check and ensures that we
bos@559 985 always feed the same input to the <command role="hg-cmd">hg
bos@559 986 bisect</command> command. The <literal>mytest</literal>
bos@559 987 function marries these together in a reproducible way, so that
bos@559 988 every test is uniform and consistent.</para>
bos@559 989
bos@559 990 </sect2>
bos@559 991 <sect2>
bos@559 992 <title>Check your results</title>
bos@559 993
bos@559 994 <para>Because the output of a <command role="hg-cmd">hg
bos@559 995 bisect</command> search is only as good as the input you
bos@559 996 give it, don't take the changeset it reports as the absolute
bos@559 997 truth. A simple way to cross-check its report is to manually
bos@559 998 run your test at each of the following changesets:</para>
bos@559 999 <itemizedlist>
bos@559 1000 <listitem><para>The changeset that it reports as the first bad
bos@559 1001 revision. Your test should still report this as
bos@559 1002 bad.</para>
bos@559 1003 </listitem>
bos@559 1004 <listitem><para>The parent of that changeset (either parent,
bos@559 1005 if it's a merge). Your test should report this changeset
bos@559 1006 as good.</para>
bos@559 1007 </listitem>
bos@559 1008 <listitem><para>A child of that changeset. Your test should
bos@559 1009 report this changeset as bad.</para>
bos@559 1010 </listitem></itemizedlist>
bos@559 1011
bos@559 1012 </sect2>
bos@559 1013 <sect2>
bos@559 1014 <title>Beware interference between bugs</title>
bos@559 1015
bos@559 1016 <para>It's possible that your search for one bug could be
bos@559 1017 disrupted by the presence of another. For example, let's say
bos@559 1018 your software crashes at revision 100, and worked correctly at
bos@559 1019 revision 50. Unknown to you, someone else introduced a
bos@559 1020 different crashing bug at revision 60, and fixed it at
bos@559 1021 revision 80. This could distort your results in one of
bos@559 1022 several ways.</para>
bos@559 1023
bos@559 1024 <para>It is possible that this other bug completely
bos@559 1025 <quote>masks</quote> yours, which is to say that it occurs
bos@559 1026 before your bug has a chance to manifest itself. If you can't
bos@559 1027 avoid that other bug (for example, it prevents your project
bos@559 1028 from building), and so can't tell whether your bug is present
bos@559 1029 in a particular changeset, the <command role="hg-cmd">hg
bos@559 1030 bisect</command> command cannot help you directly. Instead,
bos@559 1031 you can mark a changeset as untested by running <command
bos@559 1032 role="hg-cmd">hg bisect --skip</command>.</para>
bos@559 1033
bos@559 1034 <para>A different problem could arise if your test for a bug's
bos@559 1035 presence is not specific enough. If you check for <quote>my
bos@559 1036 program crashes</quote>, then both your crashing bug and an
bos@559 1037 unrelated crashing bug that masks it will look like the same
bos@559 1038 thing, and mislead <command role="hg-cmd">hg
bos@559 1039 bisect</command>.</para>
bos@559 1040
bos@559 1041 <para>Another useful situation in which to use <command
bos@559 1042 role="hg-cmd">hg bisect --skip</command> is if you can't
bos@559 1043 test a revision because your project was in a broken and hence
bos@559 1044 untestable state at that revision, perhaps because someone
bos@559 1045 checked in a change that prevented the project from
bos@559 1046 building.</para>
bos@559 1047
bos@559 1048 </sect2>
bos@559 1049 <sect2>
bos@559 1050 <title>Bracket your search lazily</title>
bos@559 1051
bos@559 1052 <para>Choosing the first <quote>good</quote> and
bos@559 1053 <quote>bad</quote> changesets that will mark the end points of
bos@559 1054 your search is often easy, but it bears a little discussion
bos@559 1055 nevertheless. From the perspective of <command
bos@559 1056 role="hg-cmd">hg bisect</command>, the <quote>newest</quote>
bos@559 1057 changeset is conventionally <quote>bad</quote>, and the older
bos@559 1058 changeset is <quote>good</quote>.</para>
bos@559 1059
bos@559 1060 <para>If you're having trouble remembering when a suitable
bos@559 1061 <quote>good</quote> change was, so that you can tell <command
bos@559 1062 role="hg-cmd">hg bisect</command>, you could do worse than
bos@559 1063 testing changesets at random. Just remember to eliminate
bos@559 1064 contenders that can't possibly exhibit the bug (perhaps
bos@559 1065 because the feature with the bug isn't present yet) and those
bos@559 1066 where another problem masks the bug (as I discussed
bos@559 1067 above).</para>
bos@559 1068
bos@559 1069 <para>Even if you end up <quote>early</quote> by thousands of
bos@559 1070 changesets or months of history, you will only add a handful
bos@559 1071 of tests to the total number that <command role="hg-cmd">hg
bos@559 1072 bisect</command> must perform, thanks to its logarithmic
bos@559 1073 behaviour.</para>
bos@559 1074
bos@559 1075 </sect2>
bos@559 1076 </sect1>
bos@559 1077 </chapter>
bos@559 1078
bos@559 1079 <!--
bos@559 1080 local variables:
bos@559 1081 sgml-parent-document: ("00book.xml" "book" "chapter")
bos@559 1082 end:
bos@559 1083 -->