changeset 1272:749c931aa47b

merge with stable
author Pierre-Yves David <pierre-yves.david@fb.com>
date Mon, 20 Apr 2015 14:21:38 +0200
parents 53e07f12a3e6 (current diff) 792998917ece (diff)
children 35d8789ea7a8
files hgext/evolve.py hgext/simple4server.py
diffstat 8 files changed, 979 insertions(+), 381 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Sat Apr 11 00:10:10 2015 -0400
+++ b/.hgtags	Mon Apr 20 14:21:38 2015 +0200
@@ -33,3 +33,4 @@
 bfe9be352453640233371cc7deb409a09decacf9 5.1.0
 9b8628e0a142e0e094e27430c388e3c12ceba513 5.1.1
 e886bc501796d53e0a19e608c6e5a6071191819e 5.1.2
+c52c15100fb2d85c3525d6d085b3663ec4f90fe6 5.1.3
--- a/README	Sat Apr 11 00:10:10 2015 -0400
+++ b/README	Mon Apr 20 14:21:38 2015 +0200
@@ -51,9 +51,17 @@
 Changelog
 =========
 
-5.1.3 --
+5.1.4 --
+
+- significant documentation update
+
+5.1.3 -- 2015-04-20
 
 - discovery: fix misbehaving discovery accros python version
+- pull: properly install the bundle2 par generator
+  (avoid sending all markers for each pull)
+- commit: avoid potential deadlock (acquires wlock before lock)
+- graft: avoid potential deadlock (acquires wlock before lock)
 
 5.1.2 -- 2015-04-01
 
--- a/docs/index.rst	Sat Apr 11 00:10:10 2015 -0400
+++ b/docs/index.rst	Mon Apr 20 14:21:38 2015 +0200
@@ -81,17 +81,51 @@
 ``evolve`` is experimental!
 ---------------------------
 
-TODO
+The long-term plan for ``evolve`` is to add it to core Mercurial.
+However, it is not yet stable enough for that. In particular:
+
+  * The UI is unstable: ``evolve``'s command names and command options
+    are not completely nailed down yet. They are subject to occasional
+    backwards-incompatible changes. If you write scripts that use
+    evolve commands, a future release could break your scripts.
 
-  * unstable UI
-  * some corner cases not covered yet
+  * There are still some corner cases that aren't handled yet. If you
+    think you have found such a case, please check if it's already
+    described in the Mercurial bug tracker (http://bz.selenic.com).
+    Bugs in ``evolve`` are files under component "evolution": use
+    `this query`_ to view open bugs in ``evolve``.
+
+.. _`this query`: http://bz.selenic.com/buglist.cgi?component=evolution&bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=NEED_EXAMPLE
 
 Installation and setup
 ----------------------
 
-TODO
+To use ``evolve``, you must:
+
+  #. Clone the ``evolve`` repository::
+
+       cd ~/src
+       hg clone https://bitbucket.org/marmoute/mutable-history
+
+  #. Configure the extension, either locally ::
+
+       hg config --local
+
+     or for all your repositories ::
+
+       hg config --edit
+
+     Then add ::
+
+       evolve=~/src/mutable-history/hgext/evolve.py
+
+     in the ``[extensions]`` section (adding the section if necessary). Use
+     the directory that you actually cloned to, of course.
+
 
 Next steps:
+-----------
+
   * For a practical guide to using ``evolve`` in a single repository,
     see the `user guide`_.
   * For more advanced tricks, see `sharing mutable history`_.
--- a/docs/sharing.rst	Sat Apr 11 00:10:10 2015 -0400
+++ b/docs/sharing.rst	Mon Apr 20 14:21:38 2015 +0200
@@ -4,13 +4,17 @@
 Evolve: Shared Mutable History
 ------------------------------
 
+.. contents::
+
 Once you have mastered the art of mutable history in a single
-repository, you might want to move up to the next level: *shared*
-mutable history. ``evolve`` lets you push and pull draft changesets
-between repositories along with their obsolescence markers. This opens
-up a number of interesting possibilities.
+repository (see the `user guide`_), you can move up to the next level:
+*shared* mutable history. ``evolve`` lets you push and pull draft
+changesets between repositories along with their obsolescence markers.
+This opens up a number of interesting possibilities.
 
-The most common scenario is a single developer working across two
+.. _`user guide`: user-guide.html
+
+The simplest scenario is a single developer working across two
 computers. Say you're working on code that must be tested on a remote
 test server, probably in a rack somewhere, only accessible by SSH, and
 running an “enterprise-grade” (out-of-date) OS. But you probably
@@ -33,19 +37,22 @@
 accidental ``rsync`` in the wrong direction could destroy hours of
 work.
 
-Using Mercurial with ``evolve`` to share mutable history solves all of
-these problems. As with single-repository ``evolve``, you can commit
+Using Mercurial with ``evolve`` to share mutable history solves these
+problems. As with single-repository ``evolve``, you can commit
 whenever the code is demonstrably better, even if all the tests aren't
 passing yet—just ``hg amend`` when they are. And you can transfer
 those half-baked changesets between repositories to try things out on
 your test server before anything is carved in stone.
 
-A less common scenario is multiple developers sharing mutable history.
-(This is in fact how Mercurial itself is developed.) We'll cover this
-scenario later. But first, single-user sharing.
+A less common scenario is multiple developers sharing mutable history,
+typically for code review. We'll cover this scenario later. But first,
+single-user sharing.
+
+Sharing with a single developer
+-------------------------------
 
 Publishing and non-publishing repositories
-------------------------------------------
+==========================================
 
 The key to shared mutable history is to keep your changesets in
 *draft* phase as you pass them around. Recall that by default, ``hg
@@ -55,20 +62,25 @@
 (Short version: set ``phases.publish`` to ``false``. Long version
 follows.)
 
-Setting things up
------------------
+Setting up
+==========
 
-We'll work an example with three local repositories, although in the
-real world they'd most likely be on three different computers. First,
-the public repository is where tested, polished changesets live, and
-it is where you push/pull changesets to/from the rest of your team. ::
+We'll work through an example with three local repositories, although
+in the real world they'd most likely be on three different computers.
+First, the ``public`` repository is where tested, polished changesets
+live, and it is where you synchronize with the rest of your team. ::
 
   $ hg init public
 
-We'll need two clones where work gets done::
+We'll need two clones where work gets done, ``test-repo`` and
+``dev-repo``::
 
-  $ hg clone -q public test-repo
-  $ hg clone -q test-repo dev-repo
+  $ hg clone public test-repo
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone test-repo dev-repo
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 ``dev-repo`` is your local machine, with GUI merge tools and IDEs and
 everything configured just the way you like it. ``test-repo`` is the
@@ -76,25 +88,30 @@
 we'll develop in ``dev-repo``, push to ``test-repo``, test and polish
 there, and push to ``public``.
 
-The key to making this whole thing work is to make ``test-repo``
-non-publishing::
+The key to shared mutable history is to make the target repository, in
+this case ``test-repo``, non-publishing. And, of course, we have to
+enable ``evolve`` in both ``test-repo`` and ``dev-repo``.
 
-  $ cat >> test-repo/.hg/hgrc <<EOF
+First, edit the configuration for ``test-repo``::
+
+  $ hg -R test-repo config --edit --local
+
+and add ::
+
   [phases]
   publish = false
-  EOF
 
-We also have to configure ``evolve`` in both ``test-repo`` and
-``dev-repo``, so that we can amend and evolve in both of them. ::
-
-  $ cat >> test-repo/.hg/hgrc <<EOF
   [extensions]
   evolve = /path/to/mutable-history/hgext/evolve.py
-  EOF
-  $ cat >> dev-repo/.hg/hgrc <<EOF
+
+Then edit the configuration for ``dev-repo``::
+
+  $ hg -R dev-repo config --edit --local
+
+and add ::
+
   [extensions]
   evolve = /path/to/mutable-history/hgext/evolve.py
-  EOF
 
 Keep in mind that in real life, these repositories would probably be
 on separate computers, so you'd have to login to each one to configure
@@ -106,15 +123,20 @@
   $ echo 'my new project' > file1
   $ hg add file1
   $ hg commit -m 'create new project'
-  $ hg push -q
+  $ hg push
+  [...]
+  added 1 changesets with 1 changes to 1 files
 
 and pull that into the development repository::
 
   $ cd ../dev-repo
   $ hg pull -u
+  [...]
+  added 1 changesets with 1 changes to 1 files
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-Amending a shared changeset
----------------------------
+Example 1: Amend a shared changeset
+===================================
 
 Everything you learned in the `user guide`_ applies to work done in
 ``dev-repo``. You can commit, amend, uncommit, evolve, and so forth
@@ -168,18 +190,21 @@
 we saw in the user guide)—but ``dev-repo`` knows nothing of these
 recent developments.
 
-  [figure SG02: rev 0:0dc9 public, rev 1:f649, 2:2a03 obsolete, rev 3:60ff draft -- but dev-repo same as in SG01]
+  [figure SG02: test-repo has rev 0:0dc9 public, rev 1:f649, 2:2a03 obsolete, rev 3:60ff draft; dev-repo same as in SG01]
 
 Let's resynchronize::
 
   $ cd ../dev-repo
   $ hg pull -u
+  [...]
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 As seen in figure 3, this transfers the new changeset *and* the
 obsolescence marker for revision 1. However, it does *not* transfer
-the temporary amend commit, because it is obsolete. Push and pull
+the temporary amend commit, because it is hidden. Push and pull
 transfer obsolesence markers between repositories, but they do not
-normally transfer obsolete changesets.
+transfer hidden changesets.
 
   [figure SG03: dev-repo grows new rev 2:60ff, marks 1:f649 obsolete]
 
@@ -187,8 +212,8 @@
 numbers in ``test-repo`` and ``dev-repo`` are no longer consistent. We
 *must* use changeset IDs.
 
-Amend again, locally
---------------------
+Example 2: Amend again, locally
+===============================
 
 This process can repeat. Perhaps you figure out a more elegant fix to
 the bug, and want to mutate history so nobody ever knows you had a
@@ -209,9 +234,10 @@
 Let's hop over to ``test-repo`` to test the more elegant fix::
 
   $ cd ../test-repo
-  $ hg update -q
+  $ hg update
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-This time, all the tests pass, so no further amendment is required.
+This time, all the tests pass, so no further amending is required.
 This bug fix is finished, so we push it to the public repository::
 
   $ hg push
@@ -219,33 +245,263 @@
   added 1 changesets with 1 changes to 1 files
 
 Note that only one changeset—the final version, after two
-amendments—was actually pushed. Again, Mercurial normally doesn't
-transfer obsolete changesets on push and pull. (Specifically, it
-doesn't transfer *hidden* changesets: roughly speaking, obsolete
-changesets with no non-obsolete descendants. If you're curious, see
-the `concept guide`_ for the precise definition of hidden.)
+amendments—was actually pushed. Again, Mercurial doesn't transfer
+hidden changesets on push and pull.
 
 .. _`concept guide`: concepts.html
 
 So the picture in ``public`` is much simpler than in either
-``dev-repo`` or ``test-repo``. None of our missteps or amendments are
-visible publicly, just the final, beautifully polished changeset:
+``dev-repo`` or ``test-repo``. Neither our missteps nor our amendments
+are publicly visible, just the final, beautifully polished changeset:
 
   [figure SG05: public repo with rev 0:0dc9, 1:de61, both public]
 
 There is one important step left to do. Because we pushed from
 ``test-repo`` to ``public``, the pushed changeset is in *public* phase
-in those two repositories. But ``dev-repo`` knows nothing of this:
-that changeset is still *draft* there. If we're not careful, we might
+in those two repositories. But ``dev-repo`` has been out-of-the-loop;
+changeset de61 is still *draft* there. If we're not careful, we might
 mutate history in ``dev-repo``, obsoleting a changeset that is already
-public. Let's avoid that situation for now by pulling from
-``test-repo`` down to ``dev-repo``::
+public. Let's avoid that situation for now by pushing up to
+``dev-repo``::
+
+  $ hg push ../dev-repo
+  pushing to ../dev-repo
+  searching for changes
+  no changes found
+
+Even though no *changesets* were pushed, Mercurial still pushed
+obsolescence markers and phase changes to ``dev-repo``.
+
+A final note: since this fix is now *public*, it is immutable. It's no
+longer possible to amend it::
+
+  $ hg amend -m 'fix bug 37'
+  abort: cannot amend public changesets
+
+This is, after all, the whole point of Mercurial's phases: to prevent
+rewriting history that has already been published.
+
+Sharing with multiple developers: code review
+---------------------------------------------
+
+Now that you know how to share your own mutable history across
+multiple computers, you might be wondering if it makes sense to share
+mutable history with others. It does, but you have to be careful, stay
+alert, and *communicate* with your peers.
+
+Code review is a good use case for sharing mutable history across
+multiple developers: Alice commits a draft changeset, submits it for
+review, and amends her changeset until her reviewer is satisfied.
+Meanwhile, Bob is also committing draft changesets for review,
+amending until his reviewer is satisfied. Once a particular changeset
+passes review, the respective author (Alice or Bob) pushes it to the
+public (publishing) repository.
+
+Incidentally, the reviewers here can be anyone: maybe Bob and Alice
+review each other's work; maybe the same third party reviews both; or
+maybe they pick different experts to review their work on different
+parts of a large codebase. Similarly, it doesn't matter if reviews are
+conducted in person, by email, or by carrier pigeon. Code review is
+outside of the scope of Mercurial, so all we're looking at here
+is the mechanics of committing, amending, pushing, and pulling.
+
+Setting up
+==========
+
+To demonstrate, let's start with the ``public`` repository as we left
+it in the last example, with two immutable changesets (figure 5
+above). We'll clone a ``review`` repository from it, and then Alice
+and Bob will both clone from ``review``. ::
+
+  $ hg clone public review
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone review alice
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone review bob
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+We need to configure Alice's and Bob's working repositories to enable
+``evolve``. First, edit Alice's configuration with ::
+
+  $ hg -R alice config --edit --local
+
+and add ::
+
+  [extensions]
+  evolve = /path/to/mutable-history/hgext/evolve.py
+
+Then edit Bob's repository configuration::
+
+  $ hg -R bob config --edit --local
+
+and add the same text.
+
+Example 3: Alice commits and amends a draft fix
+===============================================
+
+We'll follow Alice working on a bug fix. We're going to use bookmarks to
+make it easier to understand multiple branch heads in the ``review``
+repository, so Alice starts off by creating a bookmark and committing
+her first attempt at a fix::
+
+  $ hg bookmark bug15
+  $ echo 'fix' > file2
+  $ hg commit -A -u alice -m 'fix bug 15 (v1)'
+  adding file2
+
+Note the unorthodox "(v1)" in the commit message. We're just using
+that to make this tutorial easier to follow; it's not something we'd
+recommend in real life.
+
+Of course Alice wouldn't commit unless her fix worked to her
+satisfaction, so it must be time to solicit a code review. She does
+this by pushing to the ``review`` repository::
+
+  $ hg push -B bug15
+  [...]
+  added 1 changesets with 1 changes to 1 files
+  exporting bookmark bug15
+
+(The use of ``-B`` is important to ensure that we only push the
+bookmarked head, and that the bookmark itself is pushed. See this
+`guide to bookmarks`_, especially the `Sharing Bookmarks`_ section, if
+you're not familiar with bookmarks.)
+
+.. _`guide to bookmarks`: http://mercurial.aragost.com/kick-start/en/bookmarks/
+.. _`Sharing Bookmarks`: http://mercurial.aragost.com/kick-start/en/bookmarks/#sharing-bookmarks
+
+Some time passes, and Alice receives her code review. As a result,
+Alice revises her fix and submits it for a second review::
 
-  $ cd ../dev-repo
-  $ hg pull -u
+  $ echo 'Fix.' > file2
+  $ hg amend -m 'fix bug 15 (v2)'
+  $ hg push
+  [...]
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  updating bookmark bug15
+
+Figure 6 shows the state of the ``review`` repository at this point.
+
+  [figure SG06: rev 2:fn1e is Alice's obsolete v1, rev 3:cbdf is her v2; both children of rev 1:de61]
+
+After a busy morning of bug fixing, Alice stops for lunch. Let's see
+what Bob has been up to.
+
+Example 4: Bob implements and publishes a new feature
+=====================================================
+
+Meanwhile, Bob has been working on a new feature. Like Alice, he'll
+use a bookmark to track his work, and he'll push that bookmark to the
+``review`` repository, so that reviewers know which changesets to
+review. ::
+
+  $ cd ../bob
+  $ echo 'stuff' > file1
+  $ hg bookmark featureX
+  $ hg commit -u bob -m 'implement feature X (v1)'          # rev 4:1636
+  $ hg push -B featureX
+  [...]
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  exporting bookmark featureX
+
+When Bob receives his code review, he improves his implementation a
+bit, amends, and submits the resulting changeset for review::
+
+  $ echo 'do stuff' > file1
+  $ hg amend -m 'implement feature X (v2)'                  # rev 5:0eb7
+  $ hg push
+  [...]
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  updating bookmark featureX
+
+Unfortunately, that still doesn't pass muster. Bob's reviewer insists
+on proper capitalization and punctuation. ::
+
+  $ echo 'Do stuff.' > file1
+  $ hg amend -m 'implement feature X (v3)'                  # rev 6:540b
+
+On the bright side, the second review said, "Go ahead and publish once
+you fix that." So Bob immediately publishes his third attempt::
+
+  $ hg push ../public
+  [...]
+  added 1 changesets with 1 changes to 1 files
+
+It's not enough just to update ``public``, though! Other people also
+use the ``review`` repository, and right now it doesn't have Bob's
+latest amendment ("v3", revision 6:540b), nor does it know that the
+precursor of that changeset ("v2", revision 5:0eb7) is obsolete. Thus,
+Bob pushes to ``review`` as well::
 
-Getting into trouble
---------------------
+  $ hg push ../review
+  [...]
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  updating bookmark featureX
+
+Figure 7 shows the result of Bob's work in both ``review`` and
+``public``.
+
+  [figure SG07: review includes Alice's draft work on bug 15, as well as Bob's v1, v2, and v3 changes for feature X: v1 and v2 obsolete, v3 public. public contains only the final, public implementation of feature X]
+
+Incidentally, it's important that Bob push to ``public`` *before*
+``review``. If he pushed to ``review`` first, then revision 6:540b
+would still be in *draft* phase in ``review``, but it would be
+*public* in both Bob's local repository and the ``public`` repository.
+That could lead to confusion at some point, which is easily avoided by
+pushing first to ``public``.
+
+Example 5: Alice integrates and publishes
+=========================================
+
+Finally, Alice gets back from lunch and sees that the carrier pigeon
+with her second review has arrived (or maybe it's in her email inbox).
+Alice's reviewer approved her amended changeset, so she pushes it to
+``public``::
+
+  $ hg push ../public
+  [...]
+  remote has heads on branch 'default' that are not known locally: 540ba8f317e6
+  abort: push creates new remote head cbdfbd5a5db2!
+  (pull and merge or see "hg help push" for details about pushing new heads)
+
+Oops! Bob has won the race to push first to ``public``. So Alice needs
+to integrate with Bob: let's pull his changeset(s) and see what the
+branch heads are. ::
+
+  $ hg pull ../public
+  [...]
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg log -G -q -r 'head()' --template '{rev}:{node|short}  ({author})\n'
+  o  5:540ba8f317e6  (bob)
+  |
+  | @  4:cbdfbd5a5db2  (alice)
+  |/
+
+We'll assume Alice and Bob are perfectly comfortable with rebasing
+changesets. (After all, they're already using mutable history in the
+form of ``amend``.) So Alice rebases her changeset on top of Bob's and
+publishes the result::
+
+  $ hg rebase -d 5
+  $ hg push ../public
+  [...]
+  added 1 changesets with 1 changes to 1 files
+  $ hg push ../review
+  [...]
+  added 1 changesets with 0 changes to 0 files
+  updating bookmark bug15
+
+The result, in both ``review`` and ``public`` repositories, is shown
+in figure 8.
+
+  [figure SG08: review shows v1 and v2 of Alice's fix, then v1, v2, v3 of Bob's feature, finally Alice's fix rebased onto Bob's. public just shows the final public version of each changeset]
+
+Getting into trouble with shared mutable history
+------------------------------------------------
 
 Mercurial with ``evolve`` is a powerful tool, and using powerful tools
 can have consequences. (You can cut yourself badly with a sharp knife,
@@ -256,30 +512,121 @@
 the most common type of troubled changeset. (Recall that a
 non-obsolete changeset with obsolete ancestors is unstable.)
 
-Two other types of trouble can crop up: *bumped* and *divergent*
+Two other types of trouble can happen: *divergent* and *bumped*
 changesets. Both are more likely with shared mutable history,
 especially mutable history shared by multiple developers.
 
-To demonstrate, let's start with the ``public`` repository as we left
-it in the last example, with two immutable changesets (figure 5
-above). Two developers, Alice and Bob, start working from this point::
+Setting up
+==========
+
+For these examples, we're going to use a slightly different workflow:
+as before, Alice and Bob share a ``public`` repository. But this time
+there is no ``review`` repository. Instead, Alice and Bob put on their
+cowboy hats, throw good practice to the wind, and pull directly from
+each other's working repositories.
+
+So we throw away everything except ``public`` and reclone::
 
-  $ hg clone -q public alice
-  $ hg clone -q public bob
+  $ rm -rf review alice bob
+  $ hg clone public alice
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone public bob
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-We need to configure Alice's and Bob's working repositories similar to
-``test-repo``, i.e. make them non-publishing and enable ``evolve``::
+Once again we have to configure their repositories: enable ``evolve``
+and (since Alice and Bob will be pulling directly from each other)
+make their repositories non-publishing. Edit Alice's configuration::
+
+  $ hg -R alice config --edit --local
 
-  $ cat >> alice/.hg/hgrc <<EOF
+and add ::
+
+  [extensions]
+  rebase =
+  evolve = /path/to/mutable-history/hgext/evolve.py
+
   [phases]
   publish = false
-  [extensions]
-  evolve = /path/to/mutable-history/hgext/evolve.py
-  EOF
-  $ cp alice/.hg/hgrc bob/.hg/hgrc
+
+Then edit Bob's repository configuration::
+
+  $ hg -R bob config --edit --local
+
+and add the same text.
+
+Example 6: Divergent changesets
+===============================
+
+When an obsolete changeset has two successors, those successors are
+*divergent*. One way to get into such a situation is by failing to
+communicate with your teammates. Let's see how that might happen.
+
+First, we'll have Bob commit a bug fix that could still be improved::
+
+  $ cd bob
+  $ echo 'pretty good fix' >> file1
+  $ hg commit -u bob -m 'fix bug 24 (v1)'                   # rev 4:2fe6
+
+Since Alice and Bob are now in cowboy mode, Alice pulls Bob's draft
+changeset and amends it herself. ::
+
+  $ cd ../alice
+  $ hg pull -u ../bob
+  [...]
+  added 1 changesets with 1 changes to 1 files
+  $ echo 'better fix (alice)' >> file1
+  $ hg amend -u alice -m 'fix bug 24 (v2 by alice)'
+
+But Bob has no idea that Alice just did this. (See how important good
+communication is?) So he implements a better fix of his own::
+
+  $ cd ../bob
+  $ echo 'better fix (bob)' >> file1
+  $ hg amend -u bob -m 'fix bug 24 (v2 by bob)'             # rev 6:a360
+
+At this point, the divergence exists, but only in theory: Bob's
+original changeset, 4:2fe6, is obsolete and has two successors. But
+those successors are in different repositories, so the trouble is not
+visible to anyone yet. It will be as soon as Bob pulls from Alice's
+repository (or vice-versa). ::
+
+  $ hg pull ../alice
+  [...]
+  added 1 changesets with 1 changes to 2 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  2 new divergent changesets
+
+Figure 9 shows the situation in Bob's repository.
+
+  [figure SG09: Bob's repo with 2 heads for the 2 divergent changesets, 6:a360 and 7:e3f9; wc is at 6:a360; both are successors of obsolete 4:2fe6, hence divergence]
+
+Now we need to get out of trouble. As usual, the answer is to evolve
+history. ::
+
+  $ HGMERGE=internal:other hg evolve
+  merge:[6] fix bug 24 (v2 by bob)
+  with: [7] fix bug 24 (v2 by alice)
+  base: [4] fix bug 24 (v1)
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+
+Figure 10 shows how Bob's repository looks now.
+
+  [figure SG10: only one visible head, 9:5ad6, successor to hidden 6:a360 and 7:e3f9]
+
+We carefully dodged a merge conflict by specifying a merge tool
+(``internal:other``) that will take Alice's changes over Bob's. (You
+might wonder why Bob wouldn't prefer his own changes by using
+``internal:local``. He's avoiding a `bug`_ in ``evolve`` that occurs
+when evolving divergent changesets using ``internal:local``.)
+
+.. _`bug`: https://bitbucket.org/marmoute/mutable-history/issue/48/
+
+** STOP HERE: WORK IN PROGRESS **
 
 Bumped changesets: only one gets on the plane
----------------------------------------------
+=============================================
 
 If two people show up at the airport with tickets for the same seat on
 the same plane, only one of them gets on the plane. The would-be
@@ -298,15 +645,19 @@
 
   $ cd alice
   $ echo 'fix' > file2
-  $ hg commit -q -A -m 'fix bug 15'
+  $ hg commit -A -m 'fix bug 15'
+  adding file2
 
 Now Bob has a bad idea: he decides to pull whatever Alice is working
 on and tweak her bug fix to his taste::
 
   $ cd ../bob
-  $ hg pull -q -u ../alice
+  $ hg pull -u ../alice
+  [...]
+  added 1 changesets with 1 changes to 1 files
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo 'Fix.' > file2
-  $ hg amend -q -A -m 'fix bug 15 (amended)'
+  $ hg amend -A -m 'fix bug 15 (amended)'
 
 (Note the lack of communication between Alice and Bob. Failing to
 communicate with your colleagues is a good way to get into trouble.
@@ -319,7 +670,9 @@
 publish. ::
 
   $ cd ../alice
-  $ hg push  -q
+  $ hg push
+  [...]
+  added 1 changesets with 1 changes to 1 files
 
 This introduces a contradiction: in Bob's repository, changeset 2:e011
 (his copy of Alice's fix) is obsolete, since Bob amended it. But in
@@ -356,82 +709,6 @@
 
   [figure SG08: 5:227d is new, formerly bumped changeset 4:fe88 now hidden]
 
-Divergent changesets
---------------------
-
-In addition to *unstable* and *bumped*, there is a third kind of
-troubled changeset: *divergent*. When an obsolete changeset has two
-successors, those successors are divergent.
-
-To illustrate, let's start Alice and Bob at the same
-point—specifically, the point where Alice's repository currently
-stands. Bob's repository is a bit of a mess, so we'll throw it away
-and start him off with a copy of Alice's repository::
-
-  $ cd ..
-  $ rm -rf bob
-  $ cp -rp alice bob
-
-Now we'll have Bob commit a bug fix that could still be improved::
-
-  $ cd bob
-  $ echo 'pretty good fix' >> file1
-  $ hg commit -u bob -m 'fix bug 24 (v1)'
-
-This time, Alice meddles with her colleague's work (still a bad
-idea)::
-
-  $ cd ../alice
-  $ hg pull -q -u ../bob
-  $ echo 'better (alice)' >> file1
-  $ hg amend -u alice -m 'fix bug 24 (v2 by alice)'
-
-Here's where things change from the "bumped" scenario above: this
-time, the original author (Bob) decides to amend his changeset too. ::
-
-  $ cd ../bob
-  $ echo 'better (bob)' >> file1
-  $ hg amend -u bob -m 'fix bug 24 (v2 by bob)'
-
-At this point, the divergence exists, but only in theory: Bob's
-original changeset, 3:fe81, is obsolete and has two successors. But
-those successors are in different repositories, so the trouble is not
-visible to anyone yet. It will be as soon as one of our players pulls
-from the other's repository. Let's make Bob the victim again::
-
-  $ hg pull -q -u ../alice
-  not updating: not a linear update
-  (merge or update --check to force update)
-  2 new divergent changesets
-
-The “not a linear update” is our first hint that something is wrong,
-but of course “2 new divergent changesets” is the real problem. Figure
-9 shows both problems.
-
-  [figure SG09: bob's repo with 2 heads for the 2 divergent changesets, 5:fc16 and 6:694f; wc is at 5:fc16, hence update refused; both are successors of obsolete 3:fe81, hence divergence]
-
-Now we need to get out of trouble. Unfortunately, a `bug`_ in
-``evolve`` means that the usual answer (run ``hg evolve --all``) does
-not work. Bob has to figure out the solution on his own: in this case,
-merge. To avoid distractions, we'll set ``HGMERGE`` to make Mercurial
-resolve any conflicts in favour of Bob. ::
-
-  $ HGMERGE=internal:local hg merge
-  $ hg commit -m merge
-
-.. _`bug`: https://bitbucket.org/marmoute/mutable-history/issue/48/
-
-This is approximately what ``hg evolve`` would do in this
-circumstance, if not for that bug. One annoying difference is that
-Mercurial thinks the two divergent changesets are still divergent,
-which you can see with a simple revset query::
-
-  $ hg log -q -r 'divergent()'
-  5:fc16901f4d7a
-  6:694fd0f6b503
-
-(That annoyance should go away when the bug is fixed.)
-
 Conclusion
 ----------
 
--- a/docs/user-guide.rst	Sat Apr 11 00:10:10 2015 -0400
+++ b/docs/user-guide.rst	Mon Apr 20 14:21:38 2015 +0200
@@ -205,7 +205,7 @@
 Changeset obsolescence under the hood
 -------------------------------------
 
-So far, everything has gone just fine. We haven't run into merge
+So far, everything has gone just fine: we haven't run into merge
 conflicts or other trouble. Before we start exploring advanced usage
 that can run into trouble, let's step back and see what happens when
 Mercurial marks changesets obsolete. That will make it much easier to
@@ -217,8 +217,8 @@
 *successors*. The obsolete changesets are the *precursors* of their
 successors. This applies equally to built-in commands (``commit
 --amend``), commands added by ``evolve`` (``amend``, ``prune``,
-``uncommit``, ``fold``), and even commands provided by other
-extensions (``rebase``, ``histedit``).
+``uncommit``, ``fold``), and commands provided by other extensions
+(``rebase``, ``histedit``).
 
 Another way of looking at it is that obsolescence is second-order
 version control, i.e. the history of your history. We'll cover this in
@@ -248,10 +248,38 @@
 history modification work is not yet done. We'll see examples of that
 later, when we cover advanced usage.
 
-Seeing hidden changesets
-========================
+
+Understanding revision numbers and hidden changesets
+====================================================
+
+As the name implies, hidden changesets are normally not visible. If
+you run ``hg log`` on the repository from Figure 2, Mercurial will
+show revisions 0 and 3, but not 1 and 2. That's something you don't
+see with plain vanilla Mercurial—normally, revision *N* is always
+followed by revision *N* + 1.
+
+This is just the visible manifestation of hidden changesets. If
+revision 0 is followed by revision 3, that means there are two hidden
+changesets, 1 and 2, in between.
+
+To see those hidden changesets, use the ``--hidden`` option::
 
-TODO
+  $ hg --hidden log --graph --template '{rev}:{node|short}  {desc|firstline}\n'
+  @  3:934359450037  implement feature Y
+  |
+  | x  2:6c5f78d5d467  temporary amend commit for fe0ecd3bd2a4
+  | |
+  | x  1:fe0ecd3bd2a4  implement feature Y
+  |/
+  o  0:08c4b6f4efc8  init
+
+Note that changeset IDs are still the permanent, immutable identifier
+for changesets. Revision numbers are, as ever, a handy shorthand that
+work in your local repository, but cannot be used across repositories.
+They also have the useful property of showing when there are hidden
+changesets lurking under the covers, which is why this document uses
+revision numbers.
+
 
 Under the hood: Prune an unwanted changeset
 ===========================================
@@ -306,28 +334,14 @@
 Obsolete is not hidden
 ======================
 
-TODO
-
-
-Understanding revision numbers
-==============================
+So far, every obsolete changeset we have seen is also hidden. However,
+these are *not* the same thing—that's why they have different names.
+It's entirely possible to have obsolete changesets that are not
+hidden. We'll see examples of that soon, when we create *unstable*
+changesets.
 
-If you're trying these examples on your own, especially using ``hg
-log`` without ``--hidden``, you have probably noticed some funny
-business going on with revision numbers: there are now gaps in the
-sequence. That's something you don't see with plain vanilla Mercurial;
-normally, revision N is always followed by revision N+1.
-
-This is just the visible manifestation of hidden changesets. If
-revision 95 is followed by revision 98, that means there are two
-hidden changesets, 96 and 97, in between.
-
-Note that changeset IDs are still the permanent, immutable identifier
-for changesets. Revision numbers are, as ever, a handy shorthand that
-work in your local repository, but cannot be used across repositories.
-They also have the useful property of showing when there are hidden
-changesets lurking under the covers, which is why this document uses
-revision numbers.
+Note that all hidden changesets are obsolete: hidden is a subset of
+obsolete.
 
 
 Life with ``evolve`` (advanced usage)
@@ -353,7 +367,7 @@
 ===================================
 
 Sometimes you don't notice your mistakes until after you have
-committed some new changesets on top of them. ::
+committed new changesets on top of them. ::
 
   $ hg commit -m 'fix bug 17'         # rev 11 (mistake here)
   $ hg commit -m 'cleanup'            # rev 12
@@ -363,12 +377,12 @@
 fixes your mistake. That works, of course, but it makes you look bad:
 you made a mistake, and the record of that mistake is recorded in
 history for all eternity. (If the mistake was in the commit message,
-too bad.)
+too bad: you cannot fix it.)
 
 More subtly, there now exist changesets that are *worse* than what
 came before—the code no longer builds, the tests don't pass, or
-similar. Anyone reviewing these patches will waste time noticing the
-error in the earlier patch, and then the correction later on.
+similar. Anyone reviewing these patches will waste time on the error
+in the earlier patch, and then the correction later on.
 
 You can avoid all this by amending the bad changeset and *evolving*
 subsequent history. Here's how it works, assuming you have just
@@ -558,4 +572,30 @@
 Example 11: Recover an obsolete changeset
 =========================================
 
-TODO
+Sometimes you might obsolete a changeset, and then change your mind. You'll
+probably start looking for an “unobsolete” command to restore a changeset
+to normal state. For complicated implementation reasons, that command
+doesn't exist. (If you have already pushed an obsolescence marker to
+another repo, then Mercurial would need a way to revoke that remote
+obsolesence marker. That's a hard problem.)
+
+Instead, ``evolve`` provides a ``touch`` command to resurrect an
+obsolete changeset. An unexpected quirk: you almost certainly need to
+use ``--hidden``, since obsolete changesets tend to be hidden, and you
+can't reference a hidden changeset otherwise. Typical usage thus looks
+like ::
+
+  $ hg --hidden touch REV
+
+This creates a new, normal changeset which is the same as ``REV``—except
+with a different changeset ID. The new changeset will have the same parent
+as ``REV``, and will be a successor of ``REV``.
+
+The current implementation of ``hg touch`` is not ideal, and is likely to
+change in the future. Consider the history in Figure 12, where revision 27
+is obsolete and the child of 26, also obsolete. If we ``hg touch 27``, that
+creates a new revision which is a non-obsolete child of 26—i.e., it is
+unstable. It's also *divergent*, another type of trouble that we'll learn
+about in the `next section`_.
+
+.. _`next section`: sharing.html
--- a/hgext/evolve.py	Sat Apr 11 00:10:10 2015 -0400
+++ b/hgext/evolve.py	Mon Apr 20 14:21:38 2015 +0200
@@ -19,8 +19,8 @@
     - improves some aspect of the early implementation in Mercurial core
 '''
 
-__version__ = '5.1.2'
-testedwith = '3.3.3'
+__version__ = '5.1.3'
+testedwith = '3.3.3 3.4-rc'
 buglink = 'http://bz.selenic.com/'
 
 import sys, os
@@ -2110,8 +2110,9 @@
 @eh.wrapcommand('commit')
 def commitwrapper(orig, ui, repo, *arg, **kwargs):
     if kwargs.get('amend', False):
-        lock = None
+        wlock = lock = None
     else:
+        wlock = repo.wlock()
         lock = repo.lock()
     try:
         obsoleted = kwargs.get('obsolete', [])
@@ -2135,6 +2136,8 @@
     finally:
         if lock is not None:
             lock.release()
+        if wlock is not None:
+            wlock.release()
 
 @eh.wrapcommand('strip', extension='strip', opts=[
     ('', 'bundle', None, _("delete the commit entirely and move it to a "
@@ -2333,8 +2336,10 @@
     kwargs['rev'] = []
     obsoleted = kwargs.setdefault('obsolete', [])
 
-    lock = repo.lock()
+    wlock = lock = None
     try:
+        wlock = repo.wlock()
+        lock = repo.lock()
         if kwargs.get('old_obsolete'):
             if kwargs.get('continue'):
                 obsoleted.extend(repo.opener.read('graftstate').splitlines())
@@ -2349,7 +2354,7 @@
 
         return commitwrapper(orig, ui, repo,*revs, **kwargs)
     finally:
-        lock.release()
+        lockmod.release(lock, wlock)
 
 @eh.extsetup
 def oldevolveextsetup(ui):
@@ -2742,20 +2747,28 @@
 
 if getattr(exchange, '_getbundleobsmarkerpart', None) is not None:
     @eh.wrapfunction(exchange, '_getbundleobsmarkerpart')
-    def _getbundleobsmarkerpart(orig, bundler, repo, source, heads=None, common=None,
-                                bundlecaps=None, **kwargs):
+    def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs):
         if 'evo_obscommon' not in kwargs:
-            return orig(bundler, repo, source, heads, common, bundlecaps, **kwargs)
-
+            return orig(bundler, repo, source, **kwargs)
+
+        heads = kwargs.get('heads')
         if kwargs.get('obsmarkers', False):
             if heads is None:
                 heads = repo.heads()
             obscommon = kwargs.get('evo_obscommon', ())
+            assert obscommon
             obsset = repo.set('::%ln - ::%ln', heads, obscommon)
             subset = [c.node() for c in obsset]
             markers = repo.obsstore.relevantmarkers(subset)
             exchange.buildobsmarkerspart(bundler, markers)
 
+    @eh.uisetup
+    def installgetbundlepartgen(ui):
+        origfunc = exchange.getbundle2partsmapping['obsmarkers']
+        def newfunc(*args, **kwargs):
+            return _getbundleobsmarkerpart(origfunc, *args, **kwargs)
+        exchange.getbundle2partsmapping['obsmarkers'] = newfunc
+
 
 @eh.wrapfunction(exchange, '_pullobsolete')
 def _pullobsolete(orig, pullop):
--- a/hgext/simple4server.py	Sat Apr 11 00:10:10 2015 -0400
+++ b/hgext/simple4server.py	Mon Apr 20 14:21:38 2015 +0200
@@ -8,7 +8,7 @@
 For client side usages it is recommended to use the evolve extension for
 improved user interface.'''
 
-testedwith = '3.0.1'
+testedwith = '3.3.3 3.4-rc'
 buglink = 'https://bitbucket.org/marmoute/mutable-history/issues'
 
 import mercurial.obsolete
@@ -192,10 +192,6 @@
         ctx = unfi[i]
         entry = 0
         sha = util.sha1()
-        issue4587 = ctx.hex() == "195dbd1cef0c2f9f8bcf4ea303238105f716bda3"
-        if issue4587:
-            import sys
-            print >> sys.stderr, 'Witchcraft tracking: ON'
         # add data from p1
         for p in ctx.parents():
             p = p.rev()
@@ -206,9 +202,6 @@
             if p != node.nullid:
                 entry += 1
                 sha.update(p)
-                if issue4587:
-                    print >> sys.stderr, '  Parent: %r' % p
-                    print >> sys.stderr, '   Value: %s' % sha.hexdigest()
         tmarkers = repo.obsstore.relevantmarkers([ctx.node()])
         if tmarkers:
             bmarkers = []
@@ -220,16 +213,10 @@
             for m in bmarkers:
                 entry += 1
                 sha.update(m)
-                if issue4587:
-                    print >> sys.stderr, '  Marker: %r' % m
-                    print >> sys.stderr, '   Value: %s' % sha.hexdigest()
         if entry:
             cache.append((ctx.node(), sha.digest()))
         else:
             cache.append((ctx.node(), node.nullid))
-        if issue4587:
-            print >> sys.stderr, '   Final: %s' % sha.hexdigest()
-            print >> sys.stderr, 'Witchcraft tracking: OFF'
     return cache
 
 # from evolve extension: 3249814dabd1
@@ -265,10 +252,13 @@
         caps += ' _evoext_getbundle_obscommon'
     return caps
 
-def _getbundleobsmarkerpart(orig, bundler, repo, source, heads=None, common=None,
-                            bundlecaps=None, **kwargs):
+def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs):
     if 'evo_obscommon' not in kwargs:
-        return orig(bundler, repo, source, heads, common, bundlecaps, **kwargs)
+        return orig(bundler, repo, source, **kwargs)
+
+    heads = kwargs.get('heads')
+    if 'evo_obscommon' not in kwargs:
+        return orig(bundler, repo, source, **kwargs)
 
     if kwargs.get('obsmarkers', False):
         if heads is None:
@@ -294,6 +284,10 @@
     wireproto.commands['evoext_pullobsmarkers_0'] = (srv_pullobsmarkers, '*')
     # wrap module content
     extensions.wrapfunction(exchange, '_pullbundle2extraprepare', _getbundleobsmarkerpart)
+    origfunc = exchange.getbundle2partsmapping['obsmarkers']
+    def newfunc(*args, **kwargs):
+        return _getbundleobsmarkerpart(origfunc, *args, **kwargs)
+    exchange.getbundle2partsmapping['obsmarkers'] = newfunc
     extensions.wrapfunction(wireproto, 'capabilities', capabilities)
     # wrap command content
     oldcap, args = wireproto.commands['capabilities']
--- a/tests/test-sharing.t	Sat Apr 11 00:10:10 2015 -0400
+++ b/tests/test-sharing.t	Mon Apr 20 14:21:38 2015 +0200
@@ -11,8 +11,12 @@
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
   $ hg init public
-  $ hg clone -q public test-repo
-  $ hg clone -q test-repo dev-repo
+  $ hg clone public test-repo
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone test-repo dev-repo
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat >> test-repo/.hg/hgrc <<EOF
   > [phases]
   > publish = false
@@ -24,12 +28,26 @@
   $ echo 'my new project' > file1
   $ hg add file1
   $ hg commit -m'create new project'
-  $ hg push -q
+  $ hg push
+  pushing to $TESTTMP/public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
 
 and pull that into the development repository::
 
   $ cd ../dev-repo
-  $ hg pull -q -u
+  $ hg pull -u
+  pulling from $TESTTMP/test-repo
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  pull obsolescence markers
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Let's commit a preliminary change and push it to ``test-repo`` for
 testing. ::
@@ -63,7 +81,16 @@
 Pull into dev-repo: obsolescence markers are transferred, but not
 the new obsolete changeset.
   $ cd ../dev-repo
-  $ hg pull -q -u
+  $ hg pull -u
+  pulling from $TESTTMP/test-repo
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pull obsolescence markers
+  2 obsolescence markers added
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Figure SG03
   $ hg shortlog --hidden -G
@@ -92,7 +119,8 @@
   
 Figure SG04 (test-repo)
   $ cd ../test-repo
-  $ hg update -q
+  $ hg update
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg shortlog --hidden -G
   @  4:de6151c48e1c  draft  fix bug 37
   |
@@ -115,199 +143,402 @@
   pushing 4 obsolescence markers (* bytes) (glob)
   4 obsolescence markers added
 
+Now that the fix is public, we cannot amend it any more.
+  $ hg amend -m 'fix bug 37'
+  abort: cannot amend public changesets
+  [255]
+
 Figure SG05
   $ hg -R ../public shortlog -G
   o  1:de6151c48e1c  public  fix bug 37
   |
   o  0:0dc9c9f6ab91  public  create new project
   
-Oops, still have draft changesets in dev-repo.
-  $ cd ../dev-repo
-  $ hg shortlog -r 'draft()'
+Oops, still have draft changesets in dev-repo: push the phase change there.
+  $ hg -R ../dev-repo shortlog -r 'draft()'
   4:de6151c48e1c  draft  fix bug 37
-  $ hg pull -q -u
-  $ hg shortlog -r 'draft()'
+  $ hg push ../dev-repo
+  pushing to ../dev-repo
+  searching for changes
+  no changes found
+  pushing 4 obsolescence markers (* bytes) (glob)
+  0 obsolescence markers added
+  [1]
+  $ hg -R ../dev-repo shortlog -r 'draft()'
 
-Sharing by Alice and Bob to demonstrate bumped and divergent changesets.
-First, setup repos for them.
+Sharing with multiple developers: code review
 
   $ cd ..
-  $ hg clone -q public alice
-  $ hg clone -q public bob
+  $ hg clone public review
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone review alice
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone review bob
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat >> review/.hg/hgrc <<EOF
+  > [phases]
+  > publish = false
+  > EOF
+
+Alice commits a draft bug fix, pushes to review repo.
+  $ cd alice
+  $ hg bookmark bug15
+  $ echo 'fix' > file2
+  $ hg commit -A -u alice -m 'fix bug 15 (v1)'
+  adding file2
+  $ hg push -B bug15
+  pushing to $TESTTMP/review
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  pushing 4 obsolescence markers (* bytes) (glob)
+  0 obsolescence markers added
+  exporting bookmark bug15
+  $ hg -R ../review bookmarks
+     bug15                     2:f91e97234c2b
+
+Alice receives code review, amends her fix, and goes out to lunch to
+await second review.
+  $ echo 'Fix.' > file2
+  $ hg amend -m 'fix bug 15 (v2)'
+  $ hg push
+  pushing to $TESTTMP/review
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pushing 6 obsolescence markers (* bytes) (glob)
+  2 obsolescence markers added
+  updating bookmark bug15
+  $ hg -R ../review bookmarks
+     bug15                     3:cbdfbd5a5db2
+
+Figure SG06: review repository after Alice pushes her amended changeset.
+  $ hg --hidden -R ../review shortlog -G -r 1::
+  o  3:cbdfbd5a5db2  draft  fix bug 15 (v2)
+  |
+  | x  2:f91e97234c2b  draft  fix bug 15 (v1)
+  |/
+  @  1:de6151c48e1c  public  fix bug 37
+  |
+
+Bob commits a draft changeset, pushes to review repo.
+  $ cd ../bob
+  $ echo 'stuff' > file1
+  $ hg bookmark featureX
+  $ hg commit -u bob -m 'implement feature X (v1)'
+  $ hg push -B featureX
+  pushing to $TESTTMP/review
+  searching for changes
+  remote has heads on branch 'default' that are not known locally: cbdfbd5a5db2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pushing 4 obsolescence markers (* bytes) (glob)
+  0 obsolescence markers added
+  exporting bookmark featureX
+  $ hg -R ../review bookmarks
+     bug15                     3:cbdfbd5a5db2
+     featureX                  4:193657d1e852
+
+Bob receives first review, amends and pushes.
+  $ echo 'do stuff' > file1
+  $ hg amend -m 'implement feature X (v2)'
+  $ hg push
+  pushing to $TESTTMP/review
+  searching for changes
+  remote has heads on branch 'default' that are not known locally: cbdfbd5a5db2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pushing 6 obsolescence markers (* bytes) (glob)
+  2 obsolescence markers added
+  updating bookmark featureX
+
+Bob receives second review, amends, and pushes to public:
+this time, he's sure he got it right!
+  $ echo 'Do stuff.' > file1
+  $ hg amend -m 'implement feature X (v3)'
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  pushing 8 obsolescence markers (* bytes) (glob)
+  4 obsolescence markers added
+  $ hg -R ../public bookmarks
+  no bookmarks set
+  $ hg push ../review
+  pushing to ../review
+  searching for changes
+  remote has heads on branch 'default' that are not known locally: cbdfbd5a5db2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pushing 8 obsolescence markers (* bytes) (glob)
+  2 obsolescence markers added
+  updating bookmark featureX
+  $ hg -R ../review bookmarks
+     bug15                     3:cbdfbd5a5db2
+     featureX                  6:540ba8f317e6
+
+Figure SG07: review and public repos after Bob implements feature X.
+  $ hg --hidden -R ../review shortlog -G -r 1::
+  o  6:540ba8f317e6  public  implement feature X (v3)
+  |
+  | x  5:0eb74a7b6698  draft  implement feature X (v2)
+  |/
+  | x  4:193657d1e852  draft  implement feature X (v1)
+  |/
+  | o  3:cbdfbd5a5db2  draft  fix bug 15 (v2)
+  |/
+  | x  2:f91e97234c2b  draft  fix bug 15 (v1)
+  |/
+  @  1:de6151c48e1c  public  fix bug 37
+  |
+  $ hg --hidden -R ../public shortlog -G -r 1::
+  o  2:540ba8f317e6  public  implement feature X (v3)
+  |
+  o  1:de6151c48e1c  public  fix bug 37
+  |
+
+How do things look in the review repo?
+  $ cd ../review
+  $ hg --hidden shortlog -G -r 1::
+  o  6:540ba8f317e6  public  implement feature X (v3)
+  |
+  | x  5:0eb74a7b6698  draft  implement feature X (v2)
+  |/
+  | x  4:193657d1e852  draft  implement feature X (v1)
+  |/
+  | o  3:cbdfbd5a5db2  draft  fix bug 15 (v2)
+  |/
+  | x  2:f91e97234c2b  draft  fix bug 15 (v1)
+  |/
+  @  1:de6151c48e1c  public  fix bug 37
+  |
+
+Meantime, Alice is back from lunch. While she was away, Bob approved
+her change, so now she can publish it.
+  $ cd ../alice
+  $ hg --hidden shortlog -G -r 1::
+  @  4:cbdfbd5a5db2  draft  fix bug 15 (v2)
+  |
+  | x  3:55dd95168a35  draft  temporary amend commit for f91e97234c2b
+  | |
+  | x  2:f91e97234c2b  draft  fix bug 15 (v1)
+  |/
+  o  1:de6151c48e1c  public  fix bug 37
+  |
+  $ hg outgoing -q ../public
+  4:cbdfbd5a5db2
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  remote has heads on branch 'default' that are not known locally: 540ba8f317e6
+  abort: push creates new remote head cbdfbd5a5db2 with bookmark 'bug15'!
+  (pull and merge or see "hg help push" for details about pushing new heads)
+  [255]
+  $ hg pull ../public
+  pulling from ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pull obsolescence markers
+  4 obsolescence markers added
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg log -G -q -r 'head()'
+  o  5:540ba8f317e6
+  |
+  | @  4:cbdfbd5a5db2
+  |/
+  $ hg --hidden shortlog -G -r 1::
+  o  5:540ba8f317e6  public  implement feature X (v3)
+  |
+  | @  4:cbdfbd5a5db2  draft  fix bug 15 (v2)
+  |/
+  | x  3:55dd95168a35  draft  temporary amend commit for f91e97234c2b
+  | |
+  | x  2:f91e97234c2b  draft  fix bug 15 (v1)
+  |/
+  o  1:de6151c48e1c  public  fix bug 37
+  |
+
+Alice rebases her draft changeset on top of Bob's public changeset and
+publishes the result.
+  $ hg rebase -d 5
+  rebasing 4:cbdfbd5a5db2 "fix bug 15 (v2)" (bug15)
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  pushing 11 obsolescence markers (* bytes) (glob)
+  3 obsolescence markers added
+  $ hg push ../review
+  pushing to ../review
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 1 files
+  pushing 11 obsolescence markers (* bytes) (glob)
+  1 obsolescence markers added
+  updating bookmark bug15
+
+Figure SG08: review and public changesets after Alice pushes.
+  $ hg --hidden -R ../review shortlog -G -r 1::
+  o  7:a06ec1bf97bd  public  fix bug 15 (v2)
+  |
+  o  6:540ba8f317e6  public  implement feature X (v3)
+  |
+  | x  5:0eb74a7b6698  draft  implement feature X (v2)
+  |/
+  | x  4:193657d1e852  draft  implement feature X (v1)
+  |/
+  | x  3:cbdfbd5a5db2  draft  fix bug 15 (v2)
+  |/
+  | x  2:f91e97234c2b  draft  fix bug 15 (v1)
+  |/
+  @  1:de6151c48e1c  public  fix bug 37
+  |
+  $ hg --hidden -R ../public shortlog -G -r 1::
+  o  3:a06ec1bf97bd  public  fix bug 15 (v2)
+  |
+  o  2:540ba8f317e6  public  implement feature X (v3)
+  |
+  o  1:de6151c48e1c  public  fix bug 37
+  |
+  $ cd ..
+
+Setup for "cowboy mode" shared mutable history (to illustrate divergent
+and bumped changesets).
+  $ rm -rf review alice bob
+  $ hg clone public alice
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone public bob
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat >> alice/.hg/hgrc <<EOF
   > [phases]
   > publish = false
   > EOF
   $ cp alice/.hg/hgrc bob/.hg/hgrc
 
-Alice commits a bug fix.
-  $ cd alice
-  $ echo 'fix' > file2
-  $ hg commit -q -A -u alice -m 'fix bug 15'
-
-Bob pulls and amends Alice's fix.
-  $ cd ../bob
-  $ hg pull -q -u ../alice
-  $ echo 'Fix.' > file2
-  $ hg amend -q -A -u bob -m 'fix bug 15 (amended)'
-
-Figure SG06: Bob's repository after amending Alice's fix.
-(Nothing new here; we could have seen this in the user guide.
-  $ hg --hidden shortlog -G
-  @  4:fe884dfac355  draft  fix bug 15 (amended)
-  |
-  | x  3:0376cac226f8  draft  temporary amend commit for e011baf925da
-  | |
-  | x  2:e011baf925da  draft  fix bug 15
-  |/
-  o  1:de6151c48e1c  public  fix bug 37
-  |
-  o  0:0dc9c9f6ab91  public  create new project
-  
-
-But in the meantime, Alice decides the fix is just fine and publishes it.
-  $ cd ../alice
-  $ hg push -q
-
-Which means that Bob now has an formerly obsolete changeset that is
-also public (2:6e83). As soon as he pulls its phase change, he's got
-trouble: the successors of that formerly obsolete changeset are
-bumped.
+Now we'll have Bob commit a bug fix that could still be improved::
 
-  $ cd ../bob
-  $ hg --hidden shortlog -r 'obsolete()'
-  2:e011baf925da  draft  fix bug 15
-  3:0376cac226f8  draft  temporary amend commit for e011baf925da
-  $ hg pull -q -u
-  1 new bumped changesets
-  $ hg --hidden shortlog -r 'obsolete()'
-  3:0376cac226f8  draft  temporary amend commit for e011baf925da
-  $ hg shortlog -r 'bumped()'
-  4:fe884dfac355  draft  fix bug 15 (amended)
-
-Figure SG07: Bob's repo with one bumped changeset (rev 4:c02d)
-  $ hg --hidden shortlog -G
-  @  4:fe884dfac355  draft  fix bug 15 (amended)
-  |
-  | x  3:0376cac226f8  draft  temporary amend commit for e011baf925da
-  | |
-  | o  2:e011baf925da  public  fix bug 15
-  |/
-  o  1:de6151c48e1c  public  fix bug 37
-  |
-  o  0:0dc9c9f6ab91  public  create new project
-  
-
-Bob gets out of trouble by evolving the repository.
-  $ hg evolve --all
-  recreate:[4] fix bug 15 (amended)
-  atop:[2] fix bug 15
-  computing new diff
-  committed as 227d860d9ad0
-  working directory is now at 227d860d9ad0
-
-Figure SG08
-  $ hg --hidden shortlog -G
-  @  5:227d860d9ad0  draft  bumped update to e011baf925da:
-  |
-  | x  4:fe884dfac355  draft  fix bug 15 (amended)
-  | |
-  +---x  3:0376cac226f8  draft  temporary amend commit for e011baf925da
-  | |
-  o |  2:e011baf925da  public  fix bug 15
-  |/
-  o  1:de6151c48e1c  public  fix bug 37
-  |
-  o  0:0dc9c9f6ab91  public  create new project
-  
-
-Throw away Bob's messy repo and start over.
-  $ cd ..
-  $ rm -rf bob
-  $ cp -rp alice bob
-
-Bob commits a pretty good fix that both he and Alice will amend,
-leading to divergence.
   $ cd bob
   $ echo 'pretty good fix' >> file1
   $ hg commit -u bob -m 'fix bug 24 (v1)'
+  $ hg shortlog -r .
+  4:2fe6c4bd32d0  draft  fix bug 24 (v1)
 
-Alice pulls Bob's fix and improves it.
+Since Alice and Bob are now in cowboy mode, Alice pulls Bob's draft
+changeset and amends it herself. ::
+
   $ cd ../alice
-  $ hg pull -q -u ../bob
-  $ echo 'better (alice)' >> file1
+  $ hg pull -u ../bob
+  pulling from ../bob
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  pull obsolescence markers
+  0 obsolescence markers added
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo 'better fix (alice)' >> file1
   $ hg amend -u alice -m 'fix bug 24 (v2 by alice)'
 
-Likewise, Bob amends his own fix. Now we have an obsolete changeset
-with two successors, although the successors are in different repos.
-  $ cd ../bob
-  $ echo 'better (bob)' >> file1
-  $ hg amend -u bob -m 'fix bug 24 (v2 by bob)'
+Bob implements a better fix of his own::
 
-Bob pulls from Alice's repo and discovers the trouble: divergent changesets!
-  $ hg pull -q -u ../alice
-  not updating: not a linear update
-  (merge or update --check to force update)
-  2 new divergent changesets
-  $ hg shortlog -r 'divergent()'
-  5:fc16901f4d7a  draft  fix bug 24 (v2 by bob)
-  6:694fd0f6b503  draft  fix bug 24 (v2 by alice)
+  $ cd ../bob
+  $ echo 'better fix (bob)' >> file1
+  $ hg amend -u bob -m 'fix bug 24 (v2 by bob)'
+  $ hg --hidden shortlog -G -r 3::
+  @  6:a360947f6faf  draft  fix bug 24 (v2 by bob)
+  |
+  | x  5:3466c7f5a149  draft  temporary amend commit for 2fe6c4bd32d0
+  | |
+  | x  4:2fe6c4bd32d0  draft  fix bug 24 (v1)
+  |/
+  o  3:a06ec1bf97bd  public  fix bug 15 (v2)
+  |
 
-Figure SG09
-  $ hg --hidden shortlog -G
-  o  6:694fd0f6b503  draft  fix bug 24 (v2 by alice)
+Bob discovers the divergence.
+  $ hg pull ../alice
+  pulling from ../alice
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pull obsolescence markers
+  2 obsolescence markers added
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  2 new divergent changesets
+
+Figure SG09: multiple heads! divergence! oh my!
+  $ hg --hidden shortlog -G -r 3::
+  o  7:e3f99ce9d9cd  draft  fix bug 24 (v2 by alice)
   |
-  | @  5:fc16901f4d7a  draft  fix bug 24 (v2 by bob)
+  | @  6:a360947f6faf  draft  fix bug 24 (v2 by bob)
   |/
-  | x  4:162612d3335b  draft  temporary amend commit for fe81d904ed08
+  | x  5:3466c7f5a149  draft  temporary amend commit for 2fe6c4bd32d0
   | |
-  | x  3:fe81d904ed08  draft  fix bug 24 (v1)
+  | x  4:2fe6c4bd32d0  draft  fix bug 24 (v1)
   |/
-  o  2:e011baf925da  public  fix bug 15
+  o  3:a06ec1bf97bd  public  fix bug 15 (v2)
   |
-  o  1:de6151c48e1c  public  fix bug 37
-  |
-  o  0:0dc9c9f6ab91  public  create new project
-  
-Merge the trouble away.
-  $ hg merge --tool internal:local
+  $ hg --hidden shortlog -r 'successors(2fe6)'
+  6:a360947f6faf  draft  fix bug 24 (v2 by bob)
+  7:e3f99ce9d9cd  draft  fix bug 24 (v2 by alice)
+
+Use evolve to fix the divergence.
+  $ HGMERGE=internal:other hg evolve
+  merge:[6] fix bug 24 (v2 by bob)
+  with: [7] fix bug 24 (v2 by alice)
+  base: [4] fix bug 24 (v1)
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-  $ hg commit -m merge
-  $ hg shortlog -G
-  @    7:b1d30ba26e44  draft  merge
-  |\
-  | o  6:694fd0f6b503  draft  fix bug 24 (v2 by alice)
-  | |
-  o |  5:fc16901f4d7a  draft  fix bug 24 (v2 by bob)
-  |/
-  o  2:e011baf925da  public  fix bug 15
-  |
-  o  1:de6151c48e1c  public  fix bug 37
-  |
-  o  0:0dc9c9f6ab91  public  create new project
-  
+  working directory is now at 5ad6037c046c
   $ hg log -q -r 'divergent()'
-  5:fc16901f4d7a
-  6:694fd0f6b503
 
-# XXX hg evolve does not solve this trouble! bug in evolve?
-#Evolve the trouble away.
-#  $ hg evolve --all --tool=internal:local
-#  merge:[5] fix bug 24 (v2 by bob)
-#  with: [6] fix bug 24 (v2 by alice)
-#  base: [3] fix bug 24 (v1)
-#  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-#  $ hg status
-#  $ hg shortlog -G
-#  o  6:694fd0f6b503  draft  fix bug 24 (v2 by alice)
-#  |
-#  | @  5:fc16901f4d7a  draft  fix bug 24 (v2 by bob)
-#  |/
-#  o  2:e011baf925da  public  fix bug 15
-#  |
-#  o  1:de6151c48e1c  public  fix bug 37
-#  |
-#  o  0:0dc9c9f6ab91  public  create new project
-#  
-#  $ hg --hidden shortlog -G
+Figure SG10: Bob's repository after fixing divergence.
+  $ hg --hidden shortlog -G -r 3::
+  @  9:5ad6037c046c  draft  fix bug 24 (v2 by bob)
+  |
+  | x  8:bcfc9a755ac3  draft  temporary amend commit for a360947f6faf
+  | |
+  +---x  7:e3f99ce9d9cd  draft  fix bug 24 (v2 by alice)
+  | |
+  | x  6:a360947f6faf  draft  fix bug 24 (v2 by bob)
+  |/
+  | x  5:3466c7f5a149  draft  temporary amend commit for 2fe6c4bd32d0
+  | |
+  | x  4:2fe6c4bd32d0  draft  fix bug 24 (v1)
+  |/
+  o  3:a06ec1bf97bd  public  fix bug 15 (v2)
+  |
+  $ hg --hidden shortlog -r 'precursors(9)'
+  6:a360947f6faf  draft  fix bug 24 (v2 by bob)
+  7:e3f99ce9d9cd  draft  fix bug 24 (v2 by alice)
+  $ cat file1
+  Do stuff.
+  pretty good fix
+  better fix (alice)