hgbook

view en/undo.tex @ 126:fa3c43dd3a1e

More undo-related verbiage.
author Bryan O'Sullivan <bos@serpentine.com>
date Tue Dec 26 16:56:00 2006 -0800 (2006-12-26)
parents 8f8a1ad9627a
children 73efa1a01a6c
line source
1 \chapter{Finding and fixing your mistakes}
2 \label{chap:undo}
4 To err might be human, but to really handle the consequences well
5 takes a top-notch revision control system. In this chapter, we'll
6 discuss some of the techniques you can use when you find that a
7 problem has crept into your project. Mercurial has some highly
8 capable features that will help you to isolate the sources of
9 problems, and to handle them appropriately.
11 \section{Erasing local history}
13 \subsection{The accidental commit}
15 I have the occasional but persistent problem of typing rather more
16 quickly than I can think, which sometimes results in me committing a
17 changeset that is either incomplete or plain wrong. In my case, the
18 usual kind of incomplete changeset is one in which I've created a new
19 source file, but forgotten to \hgcmd{add} it. A ``plain wrong''
20 changeset is not as common, but no less annoying.
22 \subsection{Rolling back a transaction}
23 \label{sec:undo:rollback}
25 In section~\ref{sec:concepts:txn}, I mentioned that Mercurial treats
26 each modification of a repository as a \emph{transaction}. Every time
27 you commit a changeset or pull changes from another repository,
28 Mercurial remembers what you did. You can undo, or \emph{roll back},
29 exactly one of these actions using the \hgcmd{rollback} command.
31 Here's a mistake that I often find myself making: committing a change
32 in which I've created a new file, but forgotten to \hgcmd{add} it.
33 \interaction{rollback.commit}
34 Looking at the output of \hgcmd{status} after the commit immediately
35 confirms the error.
36 \interaction{rollback.status}
37 The commit captured the changes to the file \filename{a}, but not the
38 new file \filename{b}. If I were to push this changeset to a
39 repository that I shared with a colleague, the chances are high that
40 something in \filename{a} would refer to \filename{b}, which would not
41 be present in their repository when they pulled my changes. I would
42 thus become the object of some indignation.
44 However, luck is with me---I've caught my error before I pushed the
45 changeset. I use the \hgcmd{rollback} command, and Mercurial makes
46 that last changeset vanish.
47 \interaction{rollback.rollback}
48 Notice that the changeset is no longer present in the repository's
49 history, and the working directory once again thinks that the file
50 \filename{a} is modified. The commit and rollback have left the
51 working directory exactly as it was prior to the commit; the changeset
52 has been completely erased. I can now safely \hgcmd{add} the file
53 \filename{b}, and rerun my commit.
54 \interaction{rollback.add}
56 \subsection{The erroneous pull}
58 It's common practice with Mercurial to maintain separate development
59 branches of a project in different repositories. Your development
60 team might have one shared repository for your project's ``0.9''
61 release, and another, containing different changes, for the ``1.0''
62 release.
64 Given this, you can imagine that the consequences could be messy if
65 you had a local ``0.9'' repository, and accidentally pulled changes
66 from the shared ``1.0'' repository into it. At worst, you could be
67 paying insufficient attention, and push those changes into the shared
68 ``0.9'' tree, confusing your entire team (but don't worry, we'll
69 return to this horror scenario later). However, it's more likely that
70 you'll notice immediately, because Mercurial will display the URL it's
71 pulling from, or you will see it pull a suspiciously large number of
72 changes into the repository.
74 The \hgcmd{rollback} command will work nicely to expunge all of the
75 changesets that you just pulled. Mercurial groups all changes from
76 one \hgcmd{pull} into a single transaction, so one \hgcmd{rollback} is
77 all you need to undo this mistake.
79 \subsection{Rolling back is useless once you've pushed}
81 The value of the \hgcmd{rollback} command drops to zero once you've
82 pushed your changes to another repository. Rolling back a change
83 makes it disappear entirely, but \emph{only} in the repository in
84 which you perform the \hgcmd{rollback}. Because a rollback eliminates
85 history, there's no way for the disappearance of a change to propagate
86 between repositories.
88 If you've pushed a change to another repository---particularly if it's
89 a shared repository---it has essentially ``escaped into the wild,''
90 and you'll have to recover from your mistake in a different way. What
91 will happen if you push a changeset somewhere, then roll it back, then
92 pull from the repository you pushed to, is that the changeset will
93 reappear in your repository.
95 (If you absolutely know for sure that the change you want to roll back
96 is the most recent change in the repository that you pushed to,
97 \emph{and} you know that nobody else could have pulled it from that
98 repository, you can roll back the changeset there, too, but you really
99 should really not rely on this working reliably. If you do this,
100 sooner or later a change really will make it into a repository that
101 you don't directly control (or have forgotten about), and come back to
102 bite you.)
104 \subsection{You can only roll back once}
106 Mercurial stores exactly one transaction in its transaction log; that
107 transaction is the most recent one that occurred in the repository.
108 This means that you can only roll back one transaction. If you expect
109 to be able to roll back one transaction, then its predecessor, this is
110 not the behaviour you will get.
111 \interaction{rollback.twice}
112 Once you've rolled back one transaction in a repository, you can't
113 roll back again in that repository until you perform another commit or
114 pull.
116 \section{Reverting the mistaken change}
118 If you make a modification to a file, and decide that you really
119 didn't want to change the file at all, and you haven't yet committed
120 your changes, the \hgcmd{revert} command is the one you'll need. It
121 looks at the changeset that's the parent of the working directory, and
122 restores the contents of the file to their state as of that changeset.
123 (That's a long-winded way of saying that, in the normal case, it
124 undoes your modifications.)
126 Let's illustrate how the \hgcmd{revert} command works with yet another
127 small example. We'll begin by modifying a file that Mercurial is
128 already tracking.
129 \interaction{daily.revert.modify}
130 If we don't want that change, we can simply \hgcmd{revert} the file.
131 \interaction{daily.revert.unmodify}
132 The \hgcmd{revert} command provides us with an extra degree of safety
133 by saving our modified file with a \filename{.orig} extension.
134 \interaction{daily.revert.status}
136 Here is a summary of the cases that the \hgcmd{revert} command can
137 deal with. We will describe each of these in more detail in the
138 section that follows.
139 \begin{itemize}
140 \item If you modify a file, it will restore the file to its unmodified
141 state.
142 \item If you \hgcmd{add} a file, it will undo the ``added'' state of
143 the file, but leave the file itself untouched.
144 \item If you delete a file without telling Mercurial, it will restore
145 the file to its unmodified contents.
146 \item If you use the \hgcmd{remove} command to remove a file, it will
147 undo the ``removed'' state of the file, and restore the file to its
148 unmodified contents.
149 \end{itemize}
151 \subsection{File management errors}
152 \label{sec:undo:mgmt}
154 The \hgcmd{revert} command is useful for more than just modified
155 files. It lets you reverse the results of all of Mercurial's file
156 management commands---\hgcmd{add}, \hgcmd{remove}, and so on.
158 If you \hgcmd{add} a file, then decide that in fact you don't want
159 Mercurial to track it, use \hgcmd{revert} to undo the add. Don't
160 worry; Mercurial will not modify the file in any way. It will just
161 ``unmark'' the file.
162 \interaction{daily.revert.add}
164 Similarly, if you ask Mercurial to \hgcmd{remove} a file, you can use
165 \hgcmd{revert} to restore it to the contents it had as of the parent
166 of the working directory.
167 \interaction{daily.revert.remove}
168 This works just as well for a file that you deleted by hand, without
169 telling Mercurial (recall that in Mercurial terminology, this kind of
170 file is called ``missing'').
171 \interaction{daily.revert.missing}
173 If you revert a \hgcmd{copy}, the copied-to file remains in your
174 working directory afterwards, untracked. Since a copy doesn't affect
175 the copied-from file in any way, Mercurial doesn't do anything with
176 the copied-from file.
177 \interaction{daily.revert.copy}
179 \subsubsection{A slightly special case: reverting a rename}
181 If you \hgcmd{rename} a file, there is one small detail that
182 you should remember. When you \hgcmd{revert} a rename, it's not
183 enough to provide the name of the renamed-to file, as you can see
184 here.
185 \interaction{daily.revert.rename}
186 As you can see from the output of \hgcmd{status}, the renamed-to file
187 is no longer identified as added, but the renamed-\emph{from} file is
188 still removed! This is counter-intuitive (at least to me), but at
189 least it's easy to deal with.
190 \interaction{daily.revert.rename-orig}
191 So remember, to revert a \hgcmd{rename}, you must provide \emph{both}
192 the source and destination names.
194 (By the way, if you rename a file, then modify the renamed-to file,
195 then revert both components of the rename, when Mercurial restores the
196 file that was removed as part of the rename, it will be unmodified.
197 If you need the modifications in the renamed-to file to show up in the
198 renamed-from file, don't forget to copy them over.)
200 These fiddly aspects of reverting a rename arguably constitute a small
201 bug in Mercurial.
203 \section{Dealing with committed changes}
205 Consider a case where you have committed a change $a$, and another
206 change $b$ on top of it; you then realise that change $a$ was
207 incorrect. Mercurial lets you ``back out'' an entire changeset
208 automatically, and building blocks that let you reverse part of a
209 changeset by hand.
211 Before you read this section, here's something to keep in mind: the
212 \hgcmd{backout} command undoes changes by \emph{adding} history, not
213 by modifying or erasing it. It's the right tool to use if you're
214 fixing bugs, but not if you're trying to undo some change that has
215 catastrophic consequences. To deal with those, see
216 section~\ref{sec:undo:aaaiiieee}.
218 \subsection{Backing out a changeset}
220 The \hgcmd{backout} command lets you ``undo'' the effects of an entire
221 changeset in an automated fashion. Because Mercurial's history is
222 immutable, this command \emph{does not} get rid of the changeset you
223 want to undo. Instead, it creates a new changeset that
224 \emph{reverses} the effect of the to-be-undone changeset.
226 The operation of the \hgcmd{backout} command is a little intricate, so
227 let's illustrate it with some examples. First, we'll create a
228 repository with some simple changes.
229 \interaction{backout.init}
231 The \hgcmd{backout} command takes a single changeset ID as its
232 argument; this is the changeset to back out. Normally,
233 \hgcmd{backout} will drop you into a text editor to write a commit
234 message, so you can record why you're backing the change out. In this
235 example, we provide a commit message on the command line using the
236 \hgopt{backout}{-m} option.
238 \subsection{Backing out the tip changeset}
240 We're going to start by backing out the last changeset we committed.
241 \interaction{backout.simple}
242 You can see that the second line from \filename{myfile} is no longer
243 present. Taking a look at the output of \hgcmd{log} gives us an idea
244 of what the \hgcmd{backout} command has done.
245 \interaction{backout.simple.log}
246 Notice that the new changeset that \hgcmd{backout} has created is a
247 child of the changeset we backed out. It's easier to see this in
248 figure~\ref{fig:undo:backout}, which presents a graphical view of the
249 change history. As you can see, the history is nice and linear.
251 \begin{figure}[htb]
252 \centering
253 \grafix{undo-simple}
254 \caption{Backing out a change using the \hgcmd{backout} command}
255 \label{fig:undo:backout}
256 \end{figure}
258 \subsection{Backing out a non-tip change}
260 If you want to back out a change other than the last one you
261 committed, pass the \hgopt{backout}{--merge} option to the
262 \hgcmd{backout} command.
263 \interaction{backout.non-tip.clone}
264 This makes backing out any changeset a ``one-shot'' operation that's
265 usually simple and fast.
266 \interaction{backout.non-tip.backout}
268 If you take a look at the contents of \filename{myfile} after the
269 backout finishes, you'll see that the first and third changes are
270 present, but not the second.
271 \interaction{backout.non-tip.cat}
273 As the graphical history in figure~\ref{fig:undo:backout-non-tip}
274 illustrates, Mercurial actually commits \emph{two} changes in this
275 kind of situation (the box-shaped nodes are the ones that Mercurial
276 commits automatically). Before Mercurial begins the backout process,
277 it first remembers what the current parent of the working directory
278 is. It then backs out the target changeset, and commits that as a
279 changeset. Finally, it merges back to the previous parent of the
280 working directory, and commits the result of the merge.
282 \begin{figure}[htb]
283 \centering
284 \grafix{undo-non-tip}
285 \caption{Automated backout of a non-tip change using the \hgcmd{backout} command}
286 \label{fig:undo:backout-non-tip}
287 \end{figure}
289 The result is that you end up ``back where you were'', only with some
290 extra history that undoes the effect of the changeset you wanted to
291 back out.
293 \subsubsection{Always use the \hgopt{backout}{--merge} option}
295 In fact, since the \hgopt{backout}{--merge} option will do the ``right
296 thing'' whether or not the changeset you're backing out is the tip
297 (i.e.~it won't try to merge if it's backing out the tip, since there's
298 no need), you should \emph{always} use this option when you run the
299 \hgcmd{backout} command.
301 \subsection{Gaining more control of the backout process}
303 While I've recommended that you always use the
304 \hgopt{backout}{--merge} option when backing out a change, the
305 \hgcmd{backout} command lets you decide how to merge a backout
306 changeset. Taking control of the backout process by hand is something
307 you will rarely need to do, but it can be useful to understand what
308 the \hgcmd{backout} command is doing for you automatically. To
309 illustrate this, let's clone our first repository, but omit the
310 backout change that it contains.
312 \interaction{backout.manual.clone}
313 As with our earlier example, We'll commit a third changeset, then back
314 out its parent, and see what happens.
315 \interaction{backout.manual.backout}
316 Our new changeset is again a descendant of the changeset we backout
317 out; it's thus a new head, \emph{not} a descendant of the changeset
318 that was the tip. The \hgcmd{backout} command was quite explicit in
319 telling us this.
320 \interaction{backout.manual.log}
322 Again, it's easier to see what has happened by looking at a graph of
323 the revision history, in figure~\ref{fig:undo:backout-manual}. This
324 makes it clear that when we use \hgcmd{backout} to back out a change
325 other than the tip, Mercurial adds a new head to the repository (the
326 change it committed is box-shaped).
328 \begin{figure}[htb]
329 \centering
330 \grafix{undo-manual}
331 \caption{Backing out a change using the \hgcmd{backout} command}
332 \label{fig:undo:backout-manual}
333 \end{figure}
335 After the \hgcmd{backout} command has completed, it leaves the new
336 ``backout'' changeset as the parent of the working directory.
337 \interaction{backout.manual.parents}
338 Now we have two isolated sets of changes.
339 \interaction{backout.manual.heads}
341 Let's think about what we expect to see as the contents of
342 \filename{myfile} now. The first change should be present, because
343 we've never backed it out. The second change should be missing, as
344 that's the change we backed out. Since the history graph shows the
345 third change as a separate head, we \emph{don't} expect to see the
346 third change present in \filename{myfile}.
347 \interaction{backout.manual.cat}
348 To get the third change back into the file, we just do a normal merge
349 of our two heads.
350 \interaction{backout.manual.merge}
351 Afterwards, the graphical history of our repository looks like
352 figure~\ref{fig:undo:backout-manual-merge}.
354 \begin{figure}[htb]
355 \centering
356 \grafix{undo-manual-merge}
357 \caption{Manually merging a backout change}
358 \label{fig:undo:backout-manual-merge}
359 \end{figure}
361 \subsection{Why \hgcmd{backout} works as it does}
363 Here's a brief description of how the \hgcmd{backout} command works.
364 \begin{enumerate}
365 \item It ensures that the working directory is ``clean'', i.e.~that
366 the output of \hgcmd{status} would be empty.
367 \item It remembers the current parent of the working directory. Let's
368 call this changeset \texttt{orig}
369 \item It does the equivalent of a \hgcmd{update} to sync the working
370 directory to the changeset you want to back out. Let's call this
371 changeset \texttt{backout}
372 \item It finds the parent of that changeset. Let's call that
373 changeset \texttt{parent}.
374 \item For each file that the \texttt{backout} changeset affected, it
375 does the equivalent of a \hgcmdargs{revert}{-r parent} on that file,
376 to restore it to the contents it had before that changeset was
377 committed.
378 \item It commits the result as a new changeset. This changeset has
379 \texttt{backout} as its parent.
380 \item If you specify \hgopt{backout}{--merge} on the command line, it
381 merges with \texttt{orig}, and commits the result of the merge.
382 \end{enumerate}
384 An alternative way to implement the \hgcmd{backout} command would be
385 to \hgcmd{export} the to-be-backed-out changeset as a diff, then use
386 the \cmdopt{patch}{--reverse} option to the \command{patch} command to
387 reverse the effect of the change without fiddling with the working
388 directory. This sounds much simpler, but it would not work nearly as
389 well.
391 The reason that \hgcmd{backout} does an update, a commit, a merge, and
392 another commit is to give the merge machinery the best chance to do a
393 good job when dealing with all the changes \emph{between} the change
394 you're backing out and the current tip.
396 If you're backing out a changeset that's~100 revisions back in your
397 project's history, the chances that the \command{patch} command will
398 be able to apply a reverse diff cleanly are not good, because
399 intervening changes are likely to have ``broken the context'' that
400 \command{patch} uses to determine whether it can apply a patch (if
401 this sounds like gibberish, see \ref{sec:mq:patch} for a
402 discussion of the \command{patch} command). Also, Mercurial's merge
403 machinery will handle files and directories being renamed, permission
404 changes, and modifications to binary files, none of which
405 \command{patch} can deal with.
407 \section{Changes that should never have been}
408 \label{sec:undo:aaaiiieee}
410 Most of the time, the \hgcmd{backout} command is exactly what you need
411 if you want to undo the effects of a change. It leaves a permanent
412 record of exactly what you did, both when committing the original
413 changeset and when you cleaned up after it.
415 On rare occasions, though, you may find that you've committed a change
416 that really should not be present in the repository at all. For
417 example, it would be very unusual, and usually considered a mistake,
418 to commit a software project's object files as well as its source
419 files. Object files have almost no intrinsic value, and they're
420 \emph{big}, so they increase the size of the repository and the amount
421 of time it takes to clone or pull changes.
423 Before I discuss the options that you have if you commit a ``brown
424 paper bag'' change (the kind that's so bad that you want to pull a
425 brown paper bag over your head), let me first discuss some approaches
426 that probably won't work.
428 Since Mercurial treats history as accumulative---every change builds
429 on top of all changes that preceded it---you generally can't just make
430 disastrous changes disappear. The one exception is when you've just
431 committed a change, and it hasn't been pushed or pulled into another
432 repository. That's when you can safely use the \hgcmd{rollback}
433 command, as I detailed in section~\ref{sec:undo:rollback}.
435 After you've pushed a bad change to another repository, you
436 \emph{could} still use \hgcmd{rollback} to make your local copy of the
437 change disappear, but it won't have the consequences you want. The
438 change will still be present in the remote repository, so it will
439 reappear in your local repository the next time you pull.
441 If a situation like this arises, and you know which repositories your
442 bad change has propagated into, you can \emph{try} to get rid of the
443 changeefrom \emph{every} one of those repositories. This is, of
444 course, not a satisfactory solution: if you miss even a single
445 repository while you're expunging, the change is still ``in the
446 wild'', and could propagate further.
448 If you've committed one or more changes \emph{after} the change that
449 you'd like to see disappear, your options are further reduced.
450 Mercurial doesn't provide a way to ``punch a hole'' in history,
451 leaving changesets intact.
453 %%% Local Variables:
454 %%% mode: latex
455 %%% TeX-master: "00book"
456 %%% End: