# HG changeset patch # User Bryan O'Sullivan # Date 1240382967 25200 # Node ID 7226e5e750a6d49085bc1888735709e7e13f9d4b # Parent 06458701453cb358d2f45d54b6edcdd16b6c2b1d Clean up chapter 8, and add content diff -r 06458701453c -r 7226e5e750a6 en/ch08-undo.xml --- a/en/ch08-undo.xml Tue Apr 21 21:07:20 2009 -0700 +++ b/en/ch08-undo.xml Tue Apr 21 23:49:27 2009 -0700 @@ -127,17 +127,17 @@ repository&emdash;particularly if it's a shared repository&emdash;it has essentially escaped into the wild, and you'll have to recover from your mistake - in a different way. What will happen if you push a changeset - somewhere, then roll it back, then pull from the repository - you pushed to, is that the changeset will reappear in your - repository. + in a different way. If you push a changeset somewhere, then + roll it back, then pull from the repository you pushed to, the + changeset you thought you'd gotten rid of will simply reappear + in your repository. - (If you absolutely know for sure that the change you want - to roll back is the most recent change in the repository that - you pushed to, and you know that nobody - else could have pulled it from that repository, you can roll - back the changeset there, too, but you really should really - not rely on this working reliably. If you do this, sooner or + (If you absolutely know for sure that the change + you want to roll back is the most recent change in the + repository that you pushed to, and you + know that nobody else could have pulled it from that + repository, you can roll back the changeset there, too, but + you really should not expect this to work reliably. Sooner or later a change really will make it into a repository that you don't directly control (or have forgotten about), and come back to bite you.) @@ -193,6 +193,21 @@ &interaction.daily.revert.status; + + Be careful with <filename>.orig</filename> files + + It's extremely unlikely that you are either using + Mercurial to manage files with .orig + extensions or that you even care about the contents of such + files. Just in case, though, it's useful to remember that + hg revert will + unconditionally overwrite an existing file with a + .orig extension. For instance, if you + already have a file named foo.orig when + you revert foo, the contents of + foo.orig will be clobbered. + + Here is a summary of the cases that the hg revert command can deal with. We will describe each of these in more detail in the section that @@ -249,63 +264,27 @@ with the copied-from file. &interaction.daily.revert.copy; - - - A slightly special case: reverting a rename - - If you hg rename a - file, there is one small detail that you should remember. - When you hg revert a - rename, it's not enough to provide the name of the - renamed-to file, as you can see here. - - &interaction.daily.revert.rename; - - As you can see from the output of hg status, the renamed-to file is - no longer identified as added, but the - renamed-from file is still removed! - This is counter-intuitive (at least to me), but at least - it's easy to deal with. - - &interaction.daily.revert.rename-orig; - - So remember, to revert a hg - rename, you must provide - both the source and destination - names. - - % TODO: the output doesn't look like it will be - removed! - - (By the way, if you rename a file, then modify the - renamed-to file, then revert both components of the rename, - when Mercurial restores the file that was removed as part of - the rename, it will be unmodified. If you need the - modifications in the renamed-to file to show up in the - renamed-from file, don't forget to copy them over.) - - These fiddly aspects of reverting a rename arguably - constitute a small bug in Mercurial. - - + Dealing with committed changes - Consider a case where you have committed a change $a$, and - another change $b$ on top of it; you then realise that change - $a$ was incorrect. Mercurial lets you back out - an entire changeset automatically, and building blocks that let - you reverse part of a changeset by hand. + Consider a case where you have committed a change + a, and another change + b on top of it; you then realise that + change a was incorrect. Mercurial lets you + back out an entire changeset automatically, and + building blocks that let you reverse part of a changeset by + hand. Before you read this section, here's something to keep in mind: the hg backout - command undoes changes by adding history, - not by modifying or erasing it. It's the right tool to use if - you're fixing bugs, but not if you're trying to undo some change - that has catastrophic consequences. To deal with those, see + command undoes the effect of a change by + adding to your repository's history, not by + modifying or erasing it. It's the right tool to use if you're + fixing bugs, but not if you're trying to undo some change that + has catastrophic consequences. To deal with those, see . @@ -392,17 +371,17 @@ As the graphical history in illustrates, Mercurial - actually commits two changes in this kind - of situation (the box-shaped nodes are the ones that Mercurial - commits automatically). Before Mercurial begins the backout - process, it first remembers what the current parent of the - working directory is. It then backs out the target changeset, - and commits that as a changeset. Finally, it merges back to - the previous parent of the working directory, and commits the - result of the merge. - - % TODO: to me it looks like mercurial doesn't commit the - second merge automatically! + still commits one change in this kind of situation (the + box-shaped node is the ones that Mercurial commits + automatically), but the revision graph now looks different. + Before Mercurial begins the backout process, it first + remembers what the current parent of the working directory is. + It then backs out the target changeset, and commits that as a + changeset. Finally, it merges back to the previous parent of + the working directory, but notice that it does not + commit the result of the merge. The repository + now contains two heads, and the working directory is in a + merge state.
Automated backout of a non-tip change using the @@ -417,6 +396,14 @@ were</quote>, only with some extra history that undoes the effect of the changeset you wanted to back out.</para> + <para>You might wonder why Mercurial does not commit the result + of the merge that it performed. The reason lies in Mercurial + behaving conservatively: a merge naturally has more scope for + error than simply undoing the effect of the tip changeset, + so your work will be safest if you first inspect (and test!) + the result of the merge, <emphasis>then</emphasis> commit + it.</para> + <sect3> <title>Always use the <option role="hg-opt-backout">--merge</option> option @@ -530,12 +517,12 @@ It remembers the current parent of the working directory. Let's call this changeset - orig + orig. It does the equivalent of a hg update to sync the working directory to the changeset you want to back out. Let's - call this changeset backout + call this changeset backout. It finds the parent of that changeset. Let's call that changeset parent. @@ -632,7 +619,7 @@ If a situation like this arises, and you know which repositories your bad change has propagated into, you can - try to get rid of the changeefrom + try to get rid of the change from every one of those repositories. This is, of course, not a satisfactory solution: if you miss even a single repository while you're expunging, the change is still @@ -644,10 +631,105 @@ provide a way to punch a hole in history, leaving changesets intact. - XXX This needs filling out. The - hg-replay script in the - examples directory works, but doesn't handle - merge changesets. Kind of an important omission. + + Backing out a merge + + Since merges are often complicated, it is not unheard of + for a merge to be mangled badly, but committed erroneously. + Mercurial provides an important safeguard against bad merges + by refusing to commit unresolved files, but human ingenuity + guarantees that it is still possible to mess a merge up and + commit it. + + Given a bad merge that has been committed, usually the + best way to approach it is to simply try to repair the damage + by hand. A complete disaster that cannot be easily fixed up + by hand ought to be very rare, but the hg backout command may help in + making the cleanup easier. It offers a option, which lets + you specify which parent to revert to when backing out a + merge. + +
+ A bad merge + + + XXX add text + +
+ + Suppose we have a revision graph like that in . What we'd like is to + redo the merge of revisions 2 and + 3. + + One way to do so would be as follows. + + + + Call hg backout --rev=4 + --parent=2. This tells hg backout to back out revision + 4, which is the bad merge, and to when deciding which + revision to prefer, to choose parent 2, one of the parents + of the merge. The effect can be seen in . +
+ Backing out the merge, favoring one parent + + + XXX add text + +
+
+ + + Call hg backout --rev=4 + --parent=3. This tells hg backout to back out revision + 4 again, but this time to choose parent 3, the other + parent of the merge. The result is visible in , in which the repository + now contains three heads. +
+ Backing out the merge, favoring the other + parent + + + XXX add text + +
+
+ + + Redo the bad merge by merging the two backout heads, + which reduces the number of heads in the repository to + two, as can be seen in . +
+ Merging the backouts + + + XXX add text + +
+
+ + + Merge with the commit that was made after the bad + merge, as shown in . +
+ Merging the backouts + + + XXX add text + +
+
+
+
Protect yourself from <quote>escaped</quote> @@ -670,12 +752,62 @@ propagate into the central repository. Better yet, this happens without any need for explicit intervention.</para> - <para id="x_125">For instance, an incoming change hook that verifies that a - changeset will actually compile can prevent people from - inadvertantly <quote>breaking the build</quote>.</para> + <para id="x_125">For instance, an incoming change hook that + verifies that a changeset will actually compile can prevent + people from inadvertently <quote>breaking the + build</quote>.</para> + </sect2> + + <sect2> + <title>What to do about sensitive changes that escape + + Even a carefully run project can suffer an unfortunate + event such as the committing and uncontrolled propagation of a + file that contains important passwords. + + If something like this happens to you, and the information + that gets accidentally propagated is truly sensitive, your + first step should be to mitigate the effect of the leak + without trying to control the leak itself. If you are not 100% + certain that you know exactly who could have seen the changes, + you should immediately change passwords, cancel credit cards, + or find some other way to make sure that the information that + has leaked is no longer useful. In other words, assume that + the change has propagated far and wide, and that there's + nothing more you can do. + You might hope that there would be mechanisms you could + use to either figure out who has seen a change or to erase the + change permanently everywhere, but there are good reasons why + these are not possible. + + Mercurial does not provide an audit trail of who has + pulled changes from a repository, because it is usually either + impossible to record such information or trivial to spoof it. + In a multi-user or networked environment, you should thus be + extremely skeptical of yourself if you think that you have + identified every place that a sensitive changeset has + propagated to. Don't forget that people can and will send + bundles by email, have their backup software save data + offsite, carry repositories on USB sticks, and find other + completely innocent ways to confound your attempts to track + down every copy of a problematic change. + + Mercurial also does not provide a way to make a file or + changeset completely disappear from history, because there is + no way to enforce its disappearance; someone could easily + modify their copy of Mercurial to ignore such directives. In + addition, even if Mercurial provided such a capability, + someone who simply hadn't pulled a make this file + disappear changeset wouldn't be affected by it, nor + would web crawlers visiting at the wrong time, disk backups, + or other mechanisms. Indeed, no distributed revision control + system can make data reliably vanish. Providing the illusion + of such control could easily give a false sense of security, + and be worse than not providing it at all. + Finding the source of a bug @@ -815,11 +947,11 @@ If the test succeeded, you tell hg bisect by running the - hg bisect good + hg bisect --good command. If it failed, run the hg bisect bad + role="hg-cmd">hg bisect --bad command.
The command uses your information to decide @@ -909,13 +1041,13 @@ When you're finished using the hg bisect command in a repository, you can use the - hg bisect reset command to + hg bisect --reset command to drop the information it was using to drive your search. The command doesn't use much space, so it doesn't matter if you forget to run this command. However, hg bisect won't let you start a new search in that repository until you do a hg bisect reset. + role="hg-cmd">hg bisect --reset. &interaction.bisect.search.reset; diff -r 06458701453c -r 7226e5e750a6 en/examples/backout --- a/en/examples/backout Tue Apr 21 21:07:20 2009 -0700 +++ b/en/examples/backout Tue Apr 21 23:49:27 2009 -0700 @@ -68,6 +68,10 @@ hg heads +#$ name: + +echo 'first change' > myfile + #$ name: manual.cat cat myfile diff -r 06458701453c -r 7226e5e750a6 en/examples/bisect --- a/en/examples/bisect Tue Apr 21 21:07:20 2009 -0700 +++ b/en/examples/bisect Tue Apr 21 23:49:27 2009 -0700 @@ -37,15 +37,15 @@ #$ name: search.init -hg bisect init +hg bisect --reset #$ name: search.bad-init -hg bisect bad +hg bisect --bad #$ name: search.good-init -hg bisect good 10 +hg bisect --good 10 #$ name: search.step1 @@ -70,7 +70,7 @@ fi echo this revision is $result - hg bisect $result + hg bisect --$result } #$ name: search.step2 @@ -85,7 +85,7 @@ #$ name: search.reset -hg bisect reset +hg bisect --reset #$ name: diff -r 06458701453c -r 7226e5e750a6 en/figs/bad-merge-1.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/bad-merge-1.dot Tue Apr 21 23:49:27 2009 -0700 @@ -0,0 +1,13 @@ +digraph bad_merge_1 { + ancestor [label="1: ancestor"]; + left [label="2: my change"]; + right [label="3: your change"]; + bad [label="4: bad merge"]; + new [label="5: new change"]; + + ancestor -> left; + ancestor -> right; + left -> bad; + right -> bad; + bad -> new; +} diff -r 06458701453c -r 7226e5e750a6 en/figs/bad-merge-2.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/bad-merge-2.dot Tue Apr 21 23:49:27 2009 -0700 @@ -0,0 +1,18 @@ +digraph bad_merge_2 { + ancestor [label="1: ancestor",color=grey,fontcolor=grey]; + left [label="2: my change",color=grey,fontcolor=grey]; + right [label="3: your change",color=grey,fontcolor=grey]; + bad [label="4: bad merge",color=grey,fontcolor=grey]; + new [label="5: new change",color=grey,fontcolor=grey]; + + bak_left [label="6: backout 1 of\nbad merge",shape=box]; + + ancestor -> left [color=grey]; + ancestor -> right [color=grey]; + left -> bad [color=grey]; + right -> bad [color=grey]; + bad -> new [color=grey]; + + bad -> bak_left; + left -> bak_left [style=dotted,label="--parent=2"]; +} diff -r 06458701453c -r 7226e5e750a6 en/figs/bad-merge-3.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/bad-merge-3.dot Tue Apr 21 23:49:27 2009 -0700 @@ -0,0 +1,22 @@ +digraph bad_merge_3 { + ancestor [label="1: ancestor",color="#bbbbbb",fontcolor="#bbbbbb"]; + left [label="2: my change",color="#bbbbbb",fontcolor="#bbbbbb"]; + right [label="3: your change",color="#bbbbbb",fontcolor="#bbbbbb"]; + bad [label="4: bad merge",color="#bbbbbb",fontcolor="#bbbbbb"]; + new [label="5: new change",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bak_left [label="6: backout 1 of\nbad merge",color=grey,shape=box]; + bak_right [label="8: backout 2 of\nbad merge",shape=box]; + + ancestor -> left [color="#bbbbbb"]; + ancestor -> right [color="#bbbbbb"]; + left -> bad [color="#bbbbbb"]; + right -> bad [color="#bbbbbb"]; + bad -> new [color="#bbbbbb"]; + + bad -> bak_left [color=grey]; + left -> bak_left [style=dotted,label="--parent=2",color=grey,fontcolor=grey]; + + bad -> bak_right; + right -> bak_right [style=dotted,label="--parent=3"]; +} diff -r 06458701453c -r 7226e5e750a6 en/figs/bad-merge-4.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/bad-merge-4.dot Tue Apr 21 23:49:27 2009 -0700 @@ -0,0 +1,26 @@ +digraph bad_merge_4 { + ancestor [label="1: ancestor",color="#bbbbbb",fontcolor="#bbbbbb"]; + left [label="2: my change",color="#bbbbbb",fontcolor="#bbbbbb"]; + right [label="3: your change",color="#bbbbbb",fontcolor="#bbbbbb"]; + bad [label="4: bad merge",color="#bbbbbb",fontcolor="#bbbbbb"]; + new [label="5: new change",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bak_left [label="6: backout 1 of\nbad merge",color=grey,fontcolor=grey,shape=box]; + bak_right [label="7: backout 2 of\nbad merge",color=grey,fontcolor=grey,shape=box]; + good [label="8: merge\nof backouts",shape=box]; + + ancestor -> left [color="#bbbbbb"]; + ancestor -> right [color="#bbbbbb"]; + left -> bad [color="#bbbbbb"]; + right -> bad [color="#bbbbbb"]; + bad -> new [color="#bbbbbb"]; + + bad -> bak_left [color=grey]; + left -> bak_left [style=dotted,label="--parent=2",color=grey,fontcolor=grey]; + + bad -> bak_right [color=grey]; + right -> bak_right [style=dotted,label="--parent=3",color=grey,fontcolor=grey]; + + bak_left -> good; + bak_right -> good; +} diff -r 06458701453c -r 7226e5e750a6 en/figs/bad-merge-5.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/bad-merge-5.dot Tue Apr 21 23:49:27 2009 -0700 @@ -0,0 +1,30 @@ +digraph bad_merge_5 { + ancestor [label="1: ancestor",color="#bbbbbb",fontcolor="#bbbbbb"]; + left [label="2: my change",color="#bbbbbb",fontcolor="#bbbbbb"]; + right [label="3: your change",color="#bbbbbb",fontcolor="#bbbbbb"]; + bad [label="4: bad merge",color="#bbbbbb",fontcolor="#bbbbbb"]; + new [label="5: new change",color=grey,fontcolor=grey]; + + bak_left [label="6: backout 1 of\nbad merge",color="#bbbbbb",fontcolor="#bbbbbb",shape=box]; + bak_right [label="7: backout 2 of\nbad merge",color="#bbbbbb",fontcolor="#bbbbbb",shape=box]; + good [label="8: merge\nof backouts",color=grey,fontcolor=grey,shape=box]; + last [label="9: merge with\nnew change",shape=box]; + + ancestor -> left [color="#bbbbbb"]; + ancestor -> right [color="#bbbbbb"]; + left -> bad [color="#bbbbbb"]; + right -> bad [color="#bbbbbb"]; + bad -> new [color="#bbbbbb"]; + + bad -> bak_left [color="#bbbbbb"]; + left -> bak_left [style=dotted,label="--parent=2",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bad -> bak_right [color="#bbbbbb"]; + right -> bak_right [style=dotted,label="--parent=3",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bak_left -> good [color=grey]; + bak_right -> good [color=grey]; + + good -> last; + new -> last; +}