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