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