changeset 1426:6db55f28c965

merge with stable
author Pierre-Yves David <pierre-yves.david@fb.com>
date Tue, 23 Jun 2015 15:32:47 -0700
parents ecd669c36c12 (diff) e8f875227901 (current diff)
children fcc467ca740e
files README hgext/evolve.py tests/test-evolve.t
diffstat 29 files changed, 2970 insertions(+), 572 deletions(-) [+]
line wrap: on
line diff
--- a/README	Tue Jun 23 15:32:15 2015 -0700
+++ b/README	Tue Jun 23 15:32:47 2015 -0700
@@ -51,6 +51,20 @@
 Changelog
 =========
 
+5.2.0 --
+
+- evolve: gain a --rev option to control what revisions to evolve (issue4391)
+- evolve: revision are processed in the order they stack on destination
+- evolve: properly skip unstable revision with non-evolved unstable parent
+- evolve: gain --unstable --divergent --bumped flag to select the trouble
+- evolve: issue more useful error message and hint when evolve has nothing to
+          do as invocated.
+- evolve: bare `hg evolve` commands now abort when multiple changesets could be
+          a target.
+- evolve: `hg evolve --all` only evolve changeset that will end up as
+          descendant of the current working copy. The old behavior of `--all`
+          in now in `--all --any`.
+
 5.1.5 -- 2015-06-23
 
 - minor documentation cleanup
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/directaccess.py	Tue Jun 23 15:32:47 2015 -0700
@@ -0,0 +1,175 @@
+""" This extension provides direct access
+It is the ability to refer and access hidden sha in commands provided that you 
+know their value.
+For example hg log -r xxx where xxx is a commit has should work whether xxx is
+hidden or not as we assume that the user knows what he is doing when referring
+to xxx.
+"""
+from mercurial import extensions
+from mercurial import cmdutil
+from mercurial import repoview
+from mercurial import branchmap
+from mercurial import revset
+from mercurial import error
+from mercurial import commands
+from mercurial import hg
+from mercurial.i18n import _
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+# By default, all the commands have directaccess with warnings
+# List of commands that have no directaccess and directaccess with no warning
+directaccesslevel = [
+    # Format:
+    # ('nowarning', 'evolve', 'prune'),
+    # means: no directaccess warning, for the command in evolve named prune
+    #
+    # ('error', None, 'serve'),
+    # means: no directaccess for the command in core named serve
+    #
+    # The list is ordered alphabetically by command names, starting with all
+    # the commands in core then all the commands in the extensions
+    #
+    # The general guideline is:
+    # - remove directaccess warnings for read only commands
+    # - no direct access for commands with consequences outside of the repo
+    # - leave directaccess warnings for all the other commands
+    #
+    ('nowarning', None, 'annotate'),
+    ('nowarning', None, 'archive'),
+    ('nowarning', None, 'bisect'),
+    ('nowarning', None, 'bookmarks'),
+    ('nowarning', None, 'bundle'),
+    ('nowarning', None, 'cat'),
+    ('nowarning', None, 'diff'),
+    ('nowarning', None, 'export'),
+    ('nowarning', None, 'identify'),
+    ('nowarning', None, 'incoming'),
+    ('nowarning', None, 'log'),
+    ('nowarning', None, 'manifest'),
+    ('error', None, 'outgoing'), # confusing if push errors and not outgoing
+    ('error', None, 'push'), # destructive
+    ('nowarning', None, 'revert'),
+    ('error', None, 'serve'),
+    ('nowarning', None, 'tags'),
+    ('nowarning', None, 'unbundle'),
+    ('nowarning', None, 'update'),
+]
+
+def reposetup(ui, repo):
+    repo._explicitaccess = set()
+
+def _computehidden(repo):
+    hidden = repoview.filterrevs(repo, 'visible')
+    cl = repo.changelog
+    dynamic = hidden & repo._explicitaccess
+    if dynamic:
+        blocked = cl.ancestors(dynamic, inclusive=True)
+        hidden = frozenset(r for r in hidden if r not in blocked)
+    return hidden
+
+def setupdirectaccess():
+    """ Add two new filtername that behave like visible to provide direct access
+    and direct access with warning. Wraps the commands to setup direct access """
+    repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden})
+    repoview.filtertable.update({'visible-directaccess-warn': _computehidden})
+    branchmap.subsettable['visible-directaccess-nowarn'] = 'visible'
+    branchmap.subsettable['visible-directaccess-warn'] = 'visible'
+
+    for warn, ext, cmd in directaccesslevel:
+        try:
+            cmdtable = extensions.find(ext).cmdtable if ext else commands.table
+            wrapper = wrapwitherror if warn == 'error' else wrapwithoutwarning
+            extensions.wrapcommand(cmdtable, cmd, wrapper)
+        except (error.UnknownCommand, KeyError):
+            pass
+
+def wrapwitherror(orig, ui, repo, *args, **kwargs):
+    if repo and repo.filtername == 'visible-directaccess-warn':
+        repo = repo.filtered('visible')
+    return orig(ui, repo, *args, **kwargs)
+
+def wrapwithoutwarning(orig, ui, repo, *args, **kwargs):
+    if repo and repo.filtername == 'visible-directaccess-warn':
+        repo = repo.filtered("visible-directaccess-nowarn")
+    return orig(ui, repo, *args, **kwargs)
+
+def uisetup(ui):
+    """ Change ordering of extensions to ensure that directaccess extsetup comes
+    after the one of the extensions in the loadsafter list """
+    loadsafter = ui.configlist('directaccess','loadsafter')
+    order = list(extensions._order)
+    directaccesidx = order.index('directaccess')
+
+    # The min idx for directaccess to load after all the extensions in loadafter
+    minidxdirectaccess = directaccesidx
+
+    for ext in loadsafter:
+        try:
+            minidxdirectaccess = max(minidxdirectaccess, order.index(ext))
+        except ValueError:
+            pass # extension not loaded
+
+    if minidxdirectaccess > directaccesidx:
+        order.insert(minidxdirectaccess + 1, 'directaccess')
+        order.remove('directaccess')
+        extensions._order = order
+
+def _repository(orig, *args, **kwargs):
+    """Make visible-directaccess-warn the default filter for new repos"""
+    repo = orig(*args, **kwargs)
+    return repo.filtered("visible-directaccess-warn")
+
+def extsetup(ui):
+    extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
+    extensions.wrapfunction(hg, 'repository', _repository)
+    setupdirectaccess()
+
+def gethashsymbols(tree):
+    # Returns the list of symbols of the tree that look like hashes
+    # for example for the revset 3::abe3ff it will return ('abe3ff')
+    if not tree:
+        return []
+
+    if len(tree) == 2 and tree[0] == "symbol":
+        try:
+            int(tree[1])
+            return []
+        except ValueError as e:
+            return [tree[1]]
+    elif len(tree) == 3:
+        return gethashsymbols(tree[1]) + gethashsymbols(tree[2])
+    else:
+        return []
+
+def _posttreebuilthook(orig, tree, repo):
+    # This is use to enabled direct hash access
+    # We extract the symbols that look like hashes and add them to the
+    # explicitaccess set
+    orig(tree, repo)
+    filternm = ""
+    if repo is not None:
+        filternm = repo.filtername
+    if filternm is not None and filternm.startswith('visible-directaccess'):
+        prelength = len(repo._explicitaccess)
+        accessbefore = set(repo._explicitaccess)
+        repo.symbols = gethashsymbols(tree)
+        cl = repo.unfiltered().changelog
+        for node in repo.symbols:
+            try:
+                node = cl._partialmatch(node)
+            except error.LookupError:
+                node = None
+            if node is not None:
+                rev = cl.rev(node)
+                if rev not in repo.changelog:
+                    repo._explicitaccess.add(rev)
+        if prelength != len(repo._explicitaccess):
+            if repo.filtername != 'visible-directaccess-nowarn':
+                unhiddencommits = repo._explicitaccess - accessbefore
+                repo.ui.warn( _("Warning: accessing hidden changesets %s " 
+                                "for write operation\n") % 
+                                (",".join([str(repo.unfiltered()[l]) 
+                                    for l in unhiddencommits])))
+            repo.invalidatevolatilesets()
--- a/hgext/evolve.py	Tue Jun 23 15:32:15 2015 -0700
+++ b/hgext/evolve.py	Tue Jun 23 15:32:47 2015 -0700
@@ -23,11 +23,48 @@
 testedwith = '3.3.3 3.4.1'
 buglink = 'http://bz.selenic.com/'
 
+
+evolutionhelptext = """
+Obsolescence markers make it possible to mark changesets that have been
+deleted or superset in a new version of the changeset.
+
+Unlike the previous way of handling such changes, by stripping the old
+changesets from the repository, obsolescence markers can be propagated
+between repositories. This allows for a safe and simple way of exchanging
+mutable history and altering it after the fact. Changeset phases are
+respected, such that only draft and secret changesets can be altered (see
+:hg:`hg phases` for details).
+
+Obsolescence is tracked using "obsolete markers", a piece of metadata
+tracking which changesets have been made obsolete, potential successors for
+a given changeset, the moment the changeset was marked as obsolete, and the
+user who performed the rewriting operation. The markers are stored
+separately from standard changeset data can be exchanged without any of the
+precursor changesets, preventing unnecessary exchange of obsolescence data.
+
+The complete set of obsolescence markers describes a history of changeset
+modifications that is orthogonal to the repository history of file
+modifications. This changeset history allows for detection and automatic
+resolution of edge cases arising from multiple users rewriting the same part
+of history concurrently.
+
+Current feature status
+======================
+
+This feature is still in development.  If you see this help, you have enable an
+extension that turned this feature on.
+
+Obsolescence markers will be exchanged between repositories that explicitly
+assert support for the obsolescence feature (this can currently only be done
+via an extension).""".strip()
+
+
 import sys, os
 import random
 from StringIO import StringIO
 import struct
 import re
+import collections
 import socket
 import errno
 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
@@ -45,6 +82,8 @@
 except (ImportError, AttributeError):
     gboptslist = gboptsmap = None
 
+# Flags for enabling optional parts of evolve
+commandopt = 'allnewcommands'
 
 from mercurial import bookmarks
 from mercurial import cmdutil
@@ -54,6 +93,7 @@
 from mercurial import error
 from mercurial import exchange
 from mercurial import extensions
+from mercurial import help
 from mercurial import httppeer
 from mercurial import hg
 from mercurial import lock as lockmod
@@ -144,8 +184,11 @@
         """
         for cont, funcname, func in self._duckpunchers:
             setattr(cont, funcname, func)
-        for command, wrapper in self._commandwrappers:
-            extensions.wrapcommand(commands.table, command, wrapper)
+        for command, wrapper, opts in self._commandwrappers:
+            entry = extensions.wrapcommand(commands.table, command, wrapper)
+            if opts:
+                for short, long, val, msg in opts:
+                    entry[1].append((short, long, val, msg))
         for cont, funcname, wrapper in self._functionwrappers:
             extensions.wrapfunction(cont, funcname, wrapper)
         for c in self._uicallables:
@@ -166,13 +209,20 @@
             revset.symbols[name] = symbol
         for name, kw in self._templatekws:
             templatekw.keywords[name] = kw
-        for ext, command, wrapper in self._extcommandwrappers:
+        for ext, command, wrapper, opts in self._extcommandwrappers:
             if ext not in knownexts:
-                e = extensions.find(ext)
-                if e is None:
-                    raise util.Abort('extension %s not found' % ext)
+                try:
+                    e = extensions.find(ext)
+                except KeyError:
+                    # Extension isn't enabled, so don't bother trying to wrap
+                    # it.
+                    continue
                 knownexts[ext] = e.cmdtable
-            extensions.wrapcommand(knownexts[ext], commands, wrapper)
+            entry = extensions.wrapcommand(knownexts[ext], command, wrapper)
+            if opts:
+                for short, long, val, msg in opts:
+                    entry[1].append((short, long, val, msg))
+
         for c in self._extcallables:
             c(ui)
 
@@ -260,7 +310,7 @@
             return keyword
         return dec
 
-    def wrapcommand(self, command, extension=None):
+    def wrapcommand(self, command, extension=None, opts=[]):
         """Decorated function is a command wrapper
 
         The name of the command must be given as the decorator argument.
@@ -279,12 +329,16 @@
                 ui.note('Barry!')
                 return orig(ui, repo, *args, **kwargs)
 
+        The `opts` argument allows specifying additional arguments for the
+        command.
+
         """
         def dec(wrapper):
             if extension is None:
-                self._commandwrappers.append((command, wrapper))
+                self._commandwrappers.append((command, wrapper, opts))
             else:
-                self._extcommandwrappers.append((extension, command, wrapper))
+                self._extcommandwrappers.append((extension, command, wrapper,
+                    opts))
             return wrapper
         return dec
 
@@ -330,6 +384,31 @@
 reposetup = eh.final_reposetup
 
 #####################################################################
+### Option configuration                                          ###
+#####################################################################
+
+@eh.reposetup # must be the first of its kin.
+def _configureoptions(ui, repo):
+    # If no capabilities are specified, enable everything.
+    # This is so existing evolve users don't need to change their config.
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if not evolveopts:
+        evolveopts = ['all']
+        ui.setconfig('experimental', 'evolution', evolveopts)
+
+@eh.uisetup
+def _configurecmdoptions(ui):
+    # Unregister evolve commands if the command capability is not specified.
+    #
+    # This must be in the same function as the option configuration above to
+    # guarantee it happens after the above configuration, but before the
+    # extsetup functions.
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if evolveopts and (commandopt not in evolveopts and
+                       'all' not in evolveopts):
+        cmdtable.clear()
+
+#####################################################################
 ### experimental behavior                                         ###
 #####################################################################
 
@@ -590,9 +669,16 @@
 @eh.wrapcommand("pull")
 def wrapmayobsoletewc(origfn, ui, repo, *args, **opts):
     """Warn that the working directory parent is an obsolete changeset"""
-    res = origfn(ui, repo, *args, **opts)
-    if repo['.'].obsolete():
-        ui.warn(_('working directory parent is obsolete!\n'))
+    def warnobsolete():
+        if repo['.'].obsolete():
+            ui.warn(_('working directory parent is obsolete!\n'))
+    wlock = None
+    try:
+        wlock = repo.wlock()
+        repo._afterlock(warnobsolete)
+        res = origfn(ui, repo, *args, **opts)
+    finally:
+        lockmod.release(wlock)
     return res
 
 # XXX this could wrap transaction code
@@ -962,7 +1048,11 @@
 
     This function is loosely based on the extensions.wrapcommand function.
     '''
-    aliases, entry = cmdutil.findcmd(newalias, cmdtable)
+    try:
+        aliases, entry = cmdutil.findcmd(newalias, cmdtable)
+    except error.UnknownCommand:
+        # Commands may be disabled
+        return
     for alias, e in cmdtable.iteritems():
         if e is entry:
             break
@@ -1025,6 +1115,23 @@
 
 @command('debugobsstorestat', [], '')
 def cmddebugobsstorestat(ui, repo):
+    def _updateclustermap(nodes, mark, clustersmap):
+        c = (set(nodes), set([mark]))
+        toproceed = set(nodes)
+        while toproceed:
+            n = toproceed.pop()
+            other = clustersmap.get(n)
+            if (other is not None
+                and other is not c):
+                other[0].update(c[0])
+                other[1].update(c[1])
+                for on in c[0]:
+                    if on in toproceed:
+                        continue
+                    clustersmap[on] = other
+                c = other
+            clustersmap[n] = c
+
     """print statistic about obsolescence markers in the repo"""
     store = repo.obsstore
     unfi = repo.unfiltered()
@@ -1054,42 +1161,12 @@
         if parents:
             parentsdata += 1
         # cluster handling
-        nodes = set()
+        nodes = set(mark[1])
         nodes.add(mark[0])
-        nodes.update(mark[1])
-        c = (set(nodes), set([mark]))
-
-        toproceed = set(nodes)
-        while toproceed:
-            n = toproceed.pop()
-            other = clustersmap.get(n)
-            if (other is not None
-                and other is not c):
-                other[0].update(c[0])
-                other[1].update(c[1])
-                for on in c[0]:
-                    if on in toproceed:
-                        continue
-                    clustersmap[on] = other
-                c = other
-            clustersmap[n] = c
+        _updateclustermap(nodes, mark, clustersmap) 
         # same with parent data
         nodes.update(parents)
-        c = (set(nodes), set([mark]))
-        toproceed = set(nodes)
-        while toproceed:
-            n = toproceed.pop()
-            other = pclustersmap.get(n)
-            if (other is not None
-                and other is not c):
-                other[0].update(c[0])
-                other[1].update(c[1])
-                for on in c[0]:
-                    if on in toproceed:
-                        continue
-                    pclustersmap[on] = other
-                c = other
-            pclustersmap[n] = c
+        _updateclustermap(nodes, mark, pclustersmap) 
 
     # freezing the result
     for c in clustersmap.values():
@@ -1143,216 +1220,148 @@
         mean = sum(len(x[1]) for x in allpclusters) // nbcluster
         ui.write('        mean length:        %9i\n' % mean)
 
-@command('^evolve|stabilize|solve',
-    [('n', 'dry-run', False,
-        'do not perform actions, just print what would be done'),
-     ('', 'confirm', False,
-        'ask for confirmation before performing the action'),
-    ('A', 'any', False, 'also consider troubled changesets unrelated to current working directory'),
-    ('a', 'all', False, 'evolve all troubled changesets in the repo '
-                        '(implies any)'),
-    ('c', 'continue', False, 'continue an interrupted evolution'),
-    ] + mergetoolopts,
-    _('[OPTIONS]...'))
-def evolve(ui, repo, **opts):
-    """solve trouble in your repository
-
-    - rebase unstable changesets to make them stable again,
-    - create proper diffs from bumped changesets,
-    - merge divergent changesets,
-    - update to a successor if the working directory parent is
-      obsolete
-
-    By default a single changeset is evolved for each invocation and only
-    troubled changesets that would evolve as a descendant of the current
-    working directory will be considered. See --all and --any options to change
-    this behavior.
-
-    - For unstable, this means taking the first which could be rebased as a
-      child of the working directory parent revision or one of its descendants
-      and rebasing it.
-
-    - For divergent, this means taking "." if applicable.
-
-    With --any, evolve picks any troubled changeset to repair.
-
-    The working directory is updated to the newly created revision.
-    """
-
-    contopt = opts['continue']
-    anyopt = opts['any']
-    allopt = opts['all']
-    dryrunopt = opts['dry_run']
-    confirmopt = opts['confirm']
-    ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
-
-    startnode = repo['.']
-
-    if contopt:
-        if anyopt:
-            raise util.Abort('cannot specify both "--any" and "--continue"')
-        if allopt:
-            raise util.Abort('cannot specify both "--all" and "--continue"')
-        graftcmd = commands.table['graft'][0]
-        return graftcmd(ui, repo, old_obsolete=True, **{'continue': True})
-
-    tro = _picknexttroubled(ui, repo, anyopt or allopt)
-    if tro is None:
-        if repo['.'].obsolete():
-            displayer = cmdutil.show_changeset(
-                ui, repo, {'template': shorttemplate})
-            successors = set()
-
-            for successorsset in obsolete.successorssets(repo, repo['.'].node()):
-                for nodeid in successorsset:
-                    successors.add(repo[nodeid])
-
-            if not successors:
-                ui.warn(_('parent is obsolete without successors; ' +
-                          'likely killed\n'))
-                return 2
-
-            elif len(successors) > 1:
-                ui.warn(_('parent is obsolete with multiple successors:\n'))
-
-                for ctx in sorted(successors, key=lambda ctx: ctx.rev()):
-                    displayer.show(ctx)
-
-                return 2
-
+def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category):
+    """Resolve the troubles affecting one revision"""
+    wlock = lock = tr = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+        tr = repo.transaction("evolve")
+        if 'unstable' == category:
+            result = _solveunstable(ui, repo, ctx, dryrun, confirm, progresscb)
+        elif 'bumped' == category:
+            result = _solvebumped(ui, repo, ctx, dryrun, confirm, progresscb)
+        elif 'divergent' == category:
+            result = _solvedivergent(ui, repo, ctx, dryrun, confirm,
+                                   progresscb)
+        else:
+            assert False, "unknown trouble category: %s" % (category)
+        tr.close()
+        return result
+    finally:
+        lockmod.release(tr, lock, wlock)
+
+def _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat):
+    """Used by the evolve function to display an error message when
+    no troubles can be resolved"""
+    troublecategories = ['bumped', 'divergent', 'unstable']
+    unselectedcategories = [c for c in troublecategories if c != targetcat]
+    msg = None
+    hint = None
+
+    troubled = {
+            "unstable": repo.revs("unstable()"),
+            "divergent": repo.revs("divergent()"),
+            "bumped": repo.revs("bumped()"),
+            "all": repo.revs("troubled()"),
+    }
+
+
+    hintmap = {
+            'bumped': _("do you want to use --bumped"),
+            'bumped+divergent': _("do you want to use --bumped or --divergent"),
+            'bumped+unstable': _("do you want to use --bumped or --unstable"),
+            'divergent': _("do you want to use --divergent"),
+            'divergent+unstable': _("do you want to use --divergent"
+                                    " or --unstable"),
+            'unstable': _("do you want to use --unstable"),
+            'any+bumped': _("do you want to use --any (or --rev) and --bumped"),
+            'any+bumped+divergent': _("do you want to use --any (or --rev) and"
+                                      " --bumped or --divergent"),
+            'any+bumped+unstable': _("do you want to use --any (or --rev) and"
+                                     "--bumped or --unstable"),
+            'any+divergent': _("do you want to use --any (or --rev) and"
+                               " --divergent"),
+            'any+divergent+unstable': _("do you want to use --any (or --rev)"
+                                        " and --divergent or --unstable"),
+            'any+unstable': _("do you want to use --any (or --rev)"
+                              "and --unstable"),
+    }
+
+    if revopt:
+        revs = scmutil.revrange(repo, revopt)
+        if not revs:
+            msg = _("set of specified revisions is empty")
+        else:
+            msg = _("no %s changesets in specified revisions") % targetcat
+            othertroubles = []
+            for cat in unselectedcategories:
+                if revs & troubled[cat]:
+                    othertroubles.append(cat)
+            if othertroubles:
+                hint = hintmap['+'.join(othertroubles)]
+
+    elif anyopt:
+        msg = _("no %s changesets to evolve") % targetcat
+        othertroubles = []
+        for cat in unselectedcategories:
+            if troubled[cat]:
+                othertroubles.append(cat)
+        if othertroubles:
+            hint = hintmap['+'.join(othertroubles)]
+
+    else:
+        # evolve without any option = relative to the current wdir
+        if targetcat == 'unstable':
+            msg = _("nothing to evolve on current working copy parent")
+        else:
+            msg = _("current working copy parent is not %s") % targetcat
+
+        p1 = repo['.'].rev()
+        othertroubles = []
+        for cat in unselectedcategories:
+            if p1 in troubled[cat]:
+                othertroubles.append(cat)
+        if othertroubles:
+            hint = hintmap['+'.join(othertroubles)]
+        else:
+            l = len(troubled[targetcat])
+            if l:
+                hint = (_("%d other %s in the repository, do you want --any or --rev")
+                        % (l, targetcat))
             else:
-                ctx = successors.pop()
-
-                ui.status(_('update:'))
-                if not ui.quiet:
-                    displayer.show(ctx)
-
-                if dryrunopt:
-                    return 0
+                othertroubles = []
+                for cat in unselectedcategories:
+                    if troubled[cat]:
+                        othertroubles.append(cat)
+                if othertroubles:
+                    hint = hintmap['any+'+('+'.join(othertroubles))]
                 else:
-                    res = hg.update(repo, ctx.rev())
-                    if ctx != startnode:
-                        ui.status(_('working directory is now at %s\n') % ctx)
-                    return res
-
-        troubled = repo.revs('troubled()')
-        if troubled:
-            ui.write_err(_('nothing to evolve here\n'))
-            ui.status(_('(%i troubled changesets, do you want --any ?)\n')
-                      % len(troubled))
-            return 2
-        else:
-            ui.write_err(_('no troubled changesets\n'))
-            return 1
-
-    def progresscb():
-        if allopt:
-            ui.progress('evolve', seen, unit='changesets', total=count)
-    seen = 1
-    count = allopt and _counttroubled(ui, repo) or 1
-
-    while tro is not None:
-        progresscb()
-        wlock = lock = tr = None
-        try:
-            wlock = repo.wlock()
-            lock = repo.lock()
-            tr = repo.transaction("evolve")
-            result = _evolveany(ui, repo, tro, dryrunopt, confirmopt,
-                                progresscb=progresscb)
-            tr.close()
-        finally:
-            lockmod.release(tr, lock, wlock)
-        progresscb()
-        seen += 1
-        if not allopt:
-            if repo['.'] != startnode:
-                ui.status(_('working directory is now at %s\n') % repo['.'])
-            return result
-        progresscb()
-        tro = _picknexttroubled(ui, repo, anyopt or allopt)
-
-    if allopt:
+                    msg = _("no troubled changesets")
+
+    assert msg is not None
+    ui.write_err(msg+"\n")
+    if hint:
+        ui.write_err("("+hint+")\n")
+        return 2
+    else:
+        return 1
+
+def _cleanup(ui, repo, startnode, showprogress):
+    if showprogress:
         ui.progress('evolve', None)
-
     if repo['.'] != startnode:
         ui.status(_('working directory is now at %s\n') % repo['.'])
 
-
-def _evolveany(ui, repo, tro, dryrunopt, confirmopt, progresscb):
-    repo = repo.unfiltered()
-    tro = repo[tro.rev()]
-    cmdutil.bailifchanged(repo)
-    troubles = tro.troubles()
-    if 'unstable' in troubles:
-        return _solveunstable(ui, repo, tro, dryrunopt, confirmopt, progresscb)
-    elif 'bumped' in troubles:
-        return _solvebumped(ui, repo, tro, dryrunopt, confirmopt, progresscb)
-    elif 'divergent' in troubles:
-        return _solvedivergent(ui, repo, tro, dryrunopt, confirmopt,
-                               progresscb)
-    else:
-        assert False  # WHAT? unknown troubles
-
-def _counttroubled(ui, repo):
-    """Count the amount of troubled changesets"""
-    troubled = set()
-    troubled.update(getrevs(repo, 'unstable'))
-    troubled.update(getrevs(repo, 'bumped'))
-    troubled.update(getrevs(repo, 'divergent'))
-    return len(troubled)
-
-def _picknexttroubled(ui, repo, pickany=False, progresscb=None):
-    """Pick a the next trouble changeset to solve"""
-    if progresscb: progresscb()
-    tro = _stabilizableunstable(repo, repo['.'])
-    if tro is None:
-        wdp = repo['.']
-        if 'divergent' in wdp.troubles():
-            tro = wdp
-    if tro is None and pickany:
-        troubled = list(repo.set('unstable()'))
-        if not troubled:
-            troubled = list(repo.set('bumped()'))
-        if not troubled:
-            troubled = list(repo.set('divergent()'))
-        if troubled:
-            tro = troubled[0]
-
-    return tro
-
-def _stabilizableunstable(repo, pctx):
-    """Return a changectx for an unstable changeset which can be
-    stabilized on top of pctx or one of its descendants. None if none
-    can be found.
+class MultipleSuccessorsError(RuntimeError):
+    """Exception raised by _singlesuccessor when multiple sucessors sets exists
+
+    The object contains the list of successorssets in its 'successorssets'
+    attribute to call to easily recover.
     """
-    def selfanddescendants(repo, pctx):
-        yield pctx
-        for prec in repo.set('allprecursors(%d)', pctx):
-            yield prec
-        for ctx in pctx.descendants():
-            yield ctx
-            for prec in repo.set('allprecursors(%d)', ctx):
-                yield prec
-
-    # Look for an unstable which can be stabilized as a child of
-    # node. The unstable must be a child of one of node predecessors.
-    directdesc = set([pctx.rev()])
-    for ctx in selfanddescendants(repo, pctx):
-        for child in ctx.children():
-            if ctx.rev() in directdesc and not child.obsolete():
-                directdesc.add(child.rev())
-            elif child.unstable():
-                return child
-    return None
-
-def _solveunstable(ui, repo, orig, dryrun=False, confirm=False,
-                   progresscb=None):
-    """Stabilize a unstable changeset"""
-    obs = orig.parents()[0]
-    if not obs.obsolete():
-        obs = orig.parents()[1] # second parent is obsolete ?
-    assert obs.obsolete()
+
+    def __init__(self, successorssets):
+        self.successorssets = successorssets
+
+def _singlesuccessor(repo, p):
+    """returns p (as rev) if not obsolete or its unique latest successors
+
+    fail if there are no such successor"""
+
+    if not p.obsolete():
+        return p.rev()
+    obs = repo[p]
+    ui = repo.ui
     newer = obsolete.successorssets(repo, obs.node())
     # search of a parent which is not killed
     while not newer or newer == [()]:
@@ -1362,11 +1371,309 @@
         obs = obs.parents()[0]
         newer = obsolete.successorssets(repo, obs.node())
     if len(newer) > 1:
-        raise util.Abort(_("conflict rewriting. can't choose destination\n"))
+        raise MultipleSuccessorsError(newer)
+
+    return repo[newer[0][0]].rev()
+
+def builddependencies(repo, revs):
+    """returns dependency graphs giving an order to solve instability of revs
+    (see _orderrevs for more information on usage)"""
+
+    # For each troubled revision we keep track of what instability if any should
+    # be resolved in order to resolve it. Example:
+    # dependencies = {3: [6], 6:[]}
+    # Means that: 6 has no dependency, 3 depends on 6 to be solved
+    dependencies = {}
+    # rdependencies is the inverted dict of dependencies
+    rdependencies = collections.defaultdict(set)
+
+    for r in revs:
+        dependencies[r] = set()
+        for p in repo[r].parents():
+            try:
+                succ = _singlesuccessor(repo, p)
+            except MultipleSuccessorsError, exc:
+                dependencies[r] = exc.successorssets
+                continue
+            if succ in revs:
+                dependencies[r].add(succ)
+                rdependencies[succ].add(r)
+    return dependencies, rdependencies
+
+def _selectrevs(repo, allopt, revopt, anyopt, targetcat):
+    """select troubles in repo matching according to given options"""
+    revs = set()
+    if allopt or revopt:
+        revs = repo.revs(targetcat+'()')
+        if revopt:
+            revs = scmutil.revrange(repo, revopt) & revs
+        elif not anyopt and targetcat == 'unstable':
+            revs = set(_aspiringdescendant(repo, repo.revs('(.::) - obsolete()::')))
+    elif anyopt:
+        revs = repo.revs('first(%s())' % (targetcat))
+    elif targetcat == 'unstable':
+        revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::')))
+        if 1 < len(revs):
+            msg = "multiple evolve candidates"
+            hint = (_("select one of %s with --rev")
+                    % ', '.join([str(repo[r]) for r in sorted(revs)]))
+            raise error.Abort(msg, hint=hint)
+    elif targetcat in repo['.'].troubles():
+        revs = set([repo['.'].rev()])
+    return revs
+
+
+def _orderrevs(repo, revs):
+    """Compute an ordering to solve instability for the given revs
+
+    - Takes revs a list of instable revisions
+
+    - Returns the same revisions ordered to solve their instability from the
+    bottom to the top of the stack that the stabilization process will produce
+    eventually.
+
+    This ensure the minimal number of stabilization as we can stabilize each
+    revision on its final, stabilized, destination.
+    """
+    # Step 1: Build the dependency graph
+    dependencies, rdependencies = builddependencies(repo, revs)
+    # Step 2: Build the ordering
+    # Remove the revisions with no dependency(A) and add them to the ordering.
+    # Removing these revisions leads to new revisions with no dependency (the
+    # one depending on A) that we can remove from the dependency graph and add
+    # to the ordering. We progress in a similar fashion until the ordering is
+    # built
+    solvablerevs = collections.deque([r for r in sorted(dependencies.keys())
+                                      if not dependencies[r]])
+    ordering = []
+    while solvablerevs:
+        rev = solvablerevs.popleft()
+        for dependent in rdependencies[rev]:
+            dependencies[dependent].remove(rev)
+            if not dependencies[dependent]:
+                solvablerevs.append(dependent)
+        del dependencies[rev]
+        ordering.append(rev)
+
+    ordering.extend(sorted(dependencies))
+    return ordering
+
+@command('^evolve|stabilize|solve',
+    [('n', 'dry-run', False,
+        'do not perform actions, just print what would be done'),
+     ('', 'confirm', False,
+        'ask for confirmation before performing the action'),
+    ('A', 'any', False, 'also consider troubled changesets unrelated to current working directory'),
+    ('r', 'rev', [], 'solves troubles of these revisions'),
+    ('', 'bumped', False, 'solves only bumped changesets'),
+    ('', 'divergent', False, 'solves only divergent changesets'),
+    ('', 'unstable', False, 'solves only unstable changesets (default)'),
+    ('a', 'all', False, 'evolve all troubled changesets related to the current '
+                         'working directory and its descendants'),
+    ('c', 'continue', False, 'continue an interrupted evolution'),
+    ] + mergetoolopts,
+    _('[OPTIONS]...'))
+def evolve(ui, repo, **opts):
+    """solve troubles in your repository
+
+    - rebase unstable changesets to make them stable again,
+    - create proper diffs from bumped changesets,
+    - fuse divergent changesets back together,
+    - update to a successor if the working directory parent is
+      obsolete
+
+    If no argument are passed and the current working copy parent is obsolete,
+    :hg:`evolve` will update the working copy to the successors of this working
+    copy parent. If the working copy parent is not obsolete (and still no
+    argument passed) each invocation of :hg:`evolve` will evolve a single
+    unstable changeset, It will only select a changeset to be evolved if it
+    will result in a new children for the current working copy parent or its
+    descendants. The working copy will be updated on the result
+    (this last behavior will most likely to change in the future).
+    You can evolve all the unstable changesets that will be evolved on the
+    parent of the working copy and all its descendants recursively by using
+    :hg:`evolve` --all.
+
+    You can decide to evolve other categories of trouble using the --divergent
+    and --bumped flags. If no other option are specified, this will try to
+    solve the specified troubles for the working copy parent.
+
+    You can also evolve changesets affected by troubles of the selected
+    category using the --rev options. You can pick the next one anywhere in the
+    repo using --any.
+
+    You can evolve all the changesets affected by troubles of the selected
+    category using --all --any.
+
+    The working directory is updated to the newly created revision.
+    """
+
+    # Options
+    contopt = opts['continue']
+    anyopt = opts['any']
+    allopt = opts['all']
+    startnode = repo['.']
+    dryrunopt = opts['dry_run']
+    confirmopt = opts['confirm']
+    revopt = opts['rev']
+    troublecategories = ['bumped', 'divergent', 'unstable']
+    specifiedcategories = [t for t in troublecategories if opts[t]]
+    targetcat = 'unstable'
+    if 1 < len(specifiedcategories):
+        msg = _('cannot specify more than one trouble category to solve (yet)')
+        raise util.Abort(msg)
+    elif len(specifiedcategories) == 1:
+        targetcat = specifiedcategories[0]
+    elif repo['.'].obsolete():
+        displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+        # no args and parent is obsolete, update to successors
+        try:
+            ctx = repo[_singlesuccessor(repo, repo['.'])]
+        except MultipleSuccessorsError, exc:
+            repo.ui.write_err('parent is obsolete with multiple successors:\n')
+            for ln in exc.successorssets:
+                for n in ln:
+                    displayer.show(repo[n])
+            return 2
+
+
+        ui.status(_('update:'))
+        if not ui.quiet:
+            displayer.show(ctx)
+
+        if dryrunopt:
+            return 0
+        res = hg.update(repo, ctx.rev())
+        if ctx != startnode:
+            ui.status(_('working directory is now at %s\n') % ctx)
+        return res
+
+    ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
+    troubled = set(repo.revs('troubled()'))
+
+    # Progress handling
+    seen = 1
+    count = allopt and len(troubled) or 1
+    showprogress = allopt
+
+    def progresscb():
+        if revopt or allopt:
+            ui.progress('evolve', seen, unit='changesets', total=count)
+
+    # Continuation handling
+    if contopt:
+        if anyopt:
+            raise util.Abort('cannot specify both "--any" and "--continue"')
+        if allopt:
+            raise util.Abort('cannot specify both "--all" and "--continue"')
+        graftcmd = commands.table['graft'][0]
+        return graftcmd(ui, repo, old_obsolete=True, **{'continue': True})
+    cmdutil.bailifchanged(repo)
+
+
+    if revopt and allopt:
+        raise util.Abort('cannot specify both "--rev" and "--all"')
+    if revopt and anyopt:
+        raise util.Abort('cannot specify both "--rev" and "--any"')
+
+    revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat)
+
+    if not revs:
+        return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
+
+    # For the progress bar to show
+    count = len(revs)
+    # Order the revisions
+    if targetcat == 'unstable':
+        revs = _orderrevs(repo, revs)
+    for rev in revs:
+        progresscb()
+        _solveone(ui, repo, repo[rev], dryrunopt, confirmopt,
+                progresscb, targetcat)
+        seen += 1
+    progresscb()
+    _cleanup(ui, repo, startnode, showprogress)
+
+def _possibledestination(repo, rev):
+    """return all changesets that may be a new parent for REV"""
+    tonode = repo.changelog.node
+    parents = repo.changelog.parentrevs
+    torev = repo.changelog.rev
+    dest = set()
+    tovisit = list(parents(rev))
+    while tovisit:
+        r = tovisit.pop()
+        succsets = obsolete.successorssets(repo, tonode(r))
+        if not succsets:
+            tovisit.extend(parents(r))
+        else:
+            # We should probably pick only one destination from split
+            # (case where '1 < len(ss)'), This could be the currently tipmost
+            # but logic is less clear when result of the split are now on
+            # multiple branches.
+            for ss in succsets:
+                for n in ss:
+                    dest.add(torev(n))
+    return dest
+
+def _aspiringchildren(repo, revs):
+    """Return a list of changectx which can be stabilized on top of pctx or
+    one of its descendants. Empty list if none can be found."""
+    target = set(revs)
+    result = []
+    for r in repo.revs('unstable() - %ld', revs):
+        dest = _possibledestination(repo, r)
+        if target & dest:
+            result.append(r)
+    return result
+
+def _aspiringdescendant(repo, revs):
+    """Return a list of changectx which can be stabilized on top of pctx or
+    one of its descendants recursively. Empty list if none can be found."""
+    target = set(revs)
+    result = set(target)
+    paths = collections.defaultdict(set)
+    for r in repo.revs('unstable() - %ld', revs):
+        for d in _possibledestination(repo, r):
+            paths[d].add(r)
+
+    result = set(target)
+    tovisit = list(revs)
+    while tovisit:
+        base = tovisit.pop()
+        for unstable in paths[base]:
+            if unstable not in result:
+                tovisit.append(unstable)
+                result.add(unstable)
+    return sorted(result - target)
+
+def _solveunstable(ui, repo, orig, dryrun=False, confirm=False,
+                   progresscb=None):
+    """Stabilize a unstable changeset"""
+    obs = orig.parents()[0]
+    if not obs.obsolete() and len(orig.parents()) == 2:
+        obs = orig.parents()[1] # second parent is obsolete ?
+
+    if not obs.obsolete():
+        ui.warn("cannot solve instability of %s, skipping\n" % orig)
+        return False
+    newer = obsolete.successorssets(repo, obs.node())
+    # search of a parent which is not killed
+    while not newer or newer == [()]:
+        ui.debug("stabilize target %s is plain dead,"
+                 " trying to stabilize on its parent\n" %
+                 obs)
+        obs = obs.parents()[0]
+        newer = obsolete.successorssets(repo, obs.node())
+    if len(newer) > 1:
+        msg = _("skipping %s: divergent rewriting. can't choose destination\n" % obs)
+        ui.write_err(msg)
+        return 2
     targets = newer[0]
     assert targets
     if len(targets) > 1:
-        raise util.Abort(_("does not handle split parents yet\n"))
+        msg = _("does not handle split parents yet\n")
+        ui.write_err(msg)
         return 2
     target = targets[0]
     displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
@@ -1399,15 +1706,19 @@
 def _solvebumped(ui, repo, bumped, dryrun=False, confirm=False,
                  progresscb=None):
     """Stabilize a bumped changeset"""
+    repo = repo.unfiltered()
+    bumped = repo[bumped.rev()]
     # For now we deny bumped merge
     if len(bumped.parents()) > 1:
-        raise util.Abort('late comer stabilization is confused by bumped'
-                         ' %s being a merge' % bumped)
+        msg = _('skipping %s : we do not handle merge yet\n' % bumped)
+        ui.write_err(msg)
+        return 2
     prec = repo.set('last(allprecursors(%d) and public())', bumped).next()
     # For now we deny target merge
     if len(prec.parents()) > 1:
-        raise util.Abort('late comer evolution is confused by precursors'
-                         ' %s being a merge' % prec)
+        msg = _('skipping: %s: public version is a merge, this not handled yet\n' % prec)
+        ui.write_err(msg)
+        return 2
 
     displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
     if not ui.quiet or confirm:
@@ -1451,6 +1762,9 @@
         files = set()
         copied = copies.pathcopies(prec, bumped)
         precmanifest = prec.manifest()
+        # 3.3.2 needs a list.
+        # future 3.4 don't detect the size change during iteration
+        # this is fishy
         for key, val in list(bumped.manifest().iteritems()):
             precvalue = precmanifest.get(key, None)
             if precvalue is not None:
@@ -1502,40 +1816,51 @@
 
 def _solvedivergent(ui, repo, divergent, dryrun=False, confirm=False,
                     progresscb=None):
+    repo = repo.unfiltered()
+    divergent = repo[divergent.rev()]
     base, others = divergentdata(divergent)
     if len(others) > 1:
         othersstr = "[%s]" % (','.join([str(i) for i in others]))
-        hint = ("changeset %d is divergent with a changeset that got splitted "
-                "| into multiple ones:\n[%s]\n"
-                "| This is not handled by automatic evolution yet\n"
-                "| You have to fallback to manual handling with commands "
-                "such as:\n"
-                "| - hg touch -D\n"
-                "| - hg prune\n"
-                "| \n"
-                "| You should contact your local evolution Guru for help.\n"
-                % (divergent, othersstr))
-        raise util.Abort("we do not handle divergence with split yet",
-                         hint=hint)
+        msg = _("skipping %d:divergent with a changeset that got splitted into multiple ones:\n"
+                 "|[%s]\n"
+                 "| This is not handled by automatic evolution yet\n"
+                 "| You have to fallback to manual handling with commands "
+                 "such as:\n"
+                 "| - hg touch -D\n"
+                 "| - hg prune\n"
+                 "| \n"
+                 "| You should contact your local evolution Guru for help.\n"
+                 % (divergent, othersstr))
+        ui.write_err(msg)
+        return 2
     other = others[0]
     if divergent.phase() <= phases.public:
-        raise util.Abort("we can't resolve this conflict from the public side",
-                    hint="%s is public, try from %s" % (divergent, other))
+        msg = _("skipping %s: we can't resolve divergence from the public side\n") % divergent
+        ui.write_err(msg)
+        hint = _("(%s is public, try from %s)\n" % (divergent, other))
+        ui.write_err(hint)
+        return 2
     if len(other.parents()) > 1:
-        raise util.Abort("divergent changeset can't be a merge (yet)",
-                    hint="You have to fallback to solving this by hand...\n"
-                         "| This probably means redoing the merge and using "
-                         "| `hg prune` to kill older version.")
+        msg = _("skipping %s: divergent changeset can't be a merge (yet)\n" % divergent)
+        ui.write_err(msg)
+        hint = _("You have to fallback to solving this by hand...\n"
+                 "| This probably means redoing the merge and using \n"
+                 "| `hg prune` to kill older version.\n")
+        ui.write_err(hint)
+        return 2
     if other.p1() not in divergent.parents():
-        raise util.Abort("parents are not common (not handled yet)",
-                    hint="| %(d)s, %(o)s are not based on the same changeset.\n"
-                         "| With the current state of its implementation, \n"
-                         "| evolve does not work in that case.\n"
-                         "| rebase one of them next to the other and run \n"
-                         "| this command again.\n"
-                         "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n"
-                         "| - or:     hg rebase --dest 'p1(%(o)s)' -r %(d)s"
-                              % {'d': divergent, 'o': other})
+        msg = _("skipping %s: have a different parent than %s (not handled yet)\n") % (divergent, other)
+        hint = _("| %(d)s, %(o)s are not based on the same changeset.\n"
+                 "| With the current state of its implementation, \n"
+                 "| evolve does not work in that case.\n"
+                 "| rebase one of them next to the other and run \n"
+                 "| this command again.\n"
+                 "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n"
+                 "| - or:     hg rebase --dest 'p1(%(o)s)' -r %(d)s\n"
+                 % {'d': divergent, 'o': other})
+        ui.write_err(msg)
+        ui.write_err(hint)
+        return 2
 
     displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
     if not ui.quiet or confirm:
@@ -1740,6 +2065,7 @@
     [('n', 'new', [], _("successor changeset (DEPRECATED)")),
      ('s', 'succ', [], _("successor changeset")),
      ('r', 'rev', [], _("revisions to prune")),
+     ('k', 'keep', None, _("does not modify working copy during prune")),
      ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")),
      ('B', 'bookmark', '', _("remove revs only reachable from given"
                              " bookmark"))] + metadataopts,
@@ -1779,10 +2105,11 @@
     if not revs:
         raise util.Abort(_('nothing to prune'))
 
-    wlock = lock = None
+    wlock = lock = tr = None
     try:
         wlock = repo.wlock()
         lock = repo.lock()
+        tr = repo.transaction('prune')
         # defines pruned changesets
         precs = []
         revs.sort()
@@ -1796,6 +2123,10 @@
         if not precs:
             raise util.Abort('nothing to prune')
 
+        if not obsolete.isenabled(repo, obsolete.allowunstableopt):
+            if repo.revs("(%ld::) - %ld", revs, revs):
+                raise util.Abort(_("cannot prune in the middle of a stack"))
+
         # defines successors changesets
         sucs = scmutil.revrange(repo, succs)
         sucs.sort()
@@ -1813,12 +2144,6 @@
         if biject:
             relations = [(p, (s,)) for p, s in zip(precs, sucs)]
 
-        # create markers
-        obsolete.createmarkers(repo, relations, metadata=metadata)
-
-        # informs that changeset have been pruned
-        ui.status(_('%i changesets pruned\n') % len(precs))
-
         wdp = repo['.']
 
         if len(sucs) == 1 and len(precs) == 1 and wdp in precs:
@@ -1828,27 +2153,55 @@
             # update to an unkilled parent
             newnode = wdp
 
-            while newnode.obsolete():
+            while newnode in precs or newnode.obsolete():
                 newnode = newnode.parents()[0]
 
+
         if newnode.node() != wdp.node():
-            bookactive = bmactive(repo)
-            # Active bookmark that we don't want to delete (with -B option)
-            # we deactivate and move it before the update and reactivate it
-            # after
-            movebookmark = bookactive and not bookmark
-            if movebookmark:
-                bmdeactivate(repo)
-                repo._bookmarks[bookactive] = newnode.node()
-                repo._bookmarks.write()
-            commands.update(ui, repo, newnode.rev())
-            ui.status(_('working directory now at %s\n') % newnode)
-            if movebookmark:
-                bmactivate(repo, bookactive)
+            if opts.get('keep', False):
+                # This is largely the same as the implementation in
+                # strip.stripcmd(). We might want to refactor this somewhere
+                # common at some point.
+
+                # only reset the dirstate for files that would actually change
+                # between the working context and uctx
+                descendantrevs = repo.revs("%d::." % newnode.rev())
+                changedfiles = []
+                for rev in descendantrevs:
+                    # blindly reset the files, regardless of what actually changed
+                    changedfiles.extend(repo[rev].files())
+
+                # reset files that only changed in the dirstate too
+                dirstate = repo.dirstate
+                dirchanges = [f for f in dirstate if dirstate[f] != 'n']
+                changedfiles.extend(dirchanges)
+                repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles)
+                repo.dirstate.write()
+            else:
+                bookactive = bmactive(repo)
+                # Active bookmark that we don't want to delete (with -B option)
+                # we deactivate and move it before the update and reactivate it
+                # after
+                movebookmark = bookactive and not bookmark
+                if movebookmark:
+                    bmdeactivate(repo)
+                    repo._bookmarks[bookactive] = newnode.node()
+                    repo._bookmarks.write()
+                commands.update(ui, repo, newnode.rev())
+                ui.status(_('working directory now at %s\n') % newnode)
+                if movebookmark:
+                    bmactivate(repo, bookactive)
 
         # update bookmarks
         if bookmark:
             _deletebookmark(ui, marks, bookmark)
+
+        # create markers
+        obsolete.createmarkers(repo, relations, metadata=metadata)
+        
+        # informs that changeset have been pruned
+        ui.status(_('%i changesets pruned\n') % len(precs))
+
         for ctx in repo.unfiltered().set('bookmark() and %ld', precs):
             # used to be:
             #
@@ -1863,8 +2216,10 @@
                     updatebookmarks = _bookmarksupdater(repo, ctx.node())
                     updatebookmarks(dest.node())
                     break
+
+        tr.close()
     finally:
-        lockmod.release(lock, wlock)
+        lockmod.release(tr, lock, wlock)
 
 @command('amend|refresh',
     [('A', 'addremove', None,
@@ -2053,11 +2408,16 @@
             if ctx.p1() == rev or ctx.p2() == rev:
                 raise util.Abort(_("cannot uncommit to parent changeset"))
 
+        onahead = old.rev() in repo.changelog.headrevs()
+        disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt)
+        if disallowunstable and not onahead:
+            raise util.Abort(_("cannot uncommit in the middle of a stack"))
+
         # Recommit the filtered changeset
         tr = repo.transaction('uncommit')
         newid = None
-        if (pats or opts.get('include') or opts.get('exclude')
-            or opts.get('all')):
+        includeorexclude = opts.get('include') or opts.get('exclude')
+        if (pats or includeorexclude or opts.get('all')):
             match = scmutil.match(old, pats, opts)
             newid = _commitfiltered(repo, old, match, target=rev)
         if newid is None:
@@ -2107,6 +2467,31 @@
     finally:
         lockmod.release(lock, wlock)
 
+@eh.wrapcommand('strip', extension='strip', opts=[
+    ('', 'bundle', None, _("delete the commit entirely and move it to a "
+        "backup bundle")),
+    ])
+def stripwrapper(orig, ui, repo, *revs, **kwargs):
+    if (not ui.configbool('experimental', 'prunestrip') or
+        kwargs.get('bundle', False)):
+        return orig(ui, repo, *revs, **kwargs)
+
+    if kwargs.get('force'):
+        ui.warn(_("warning: --force has no effect during strip with evolve "
+                  "enabled\n"))
+    if kwargs.get('no_backup', False):
+        ui.warn(_("warning: --no-backup has no effect during strips with "
+                  "evolve enabled\n"))
+
+    revs = list(revs) + kwargs.pop('rev', [])
+    revs = set(scmutil.revrange(repo, revs))
+    revs = repo.revs("(%ld)::", revs)
+    kwargs['rev'] = []
+    kwargs['new'] = []
+    kwargs['succ'] = []
+    kwargs['biject'] = False
+    return cmdprune(ui, repo, *revs, **kwargs)
+
 @command('^touch',
     [('r', 'rev', [], 'revision to update'),
      ('D', 'duplicate', False,
@@ -2233,6 +2618,11 @@
         raise util.Abort(_("cannot fold non-linear revisions "
                            "(multiple heads given)"))
     head = repo[heads.first()]
+    disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt)
+    if disallowunstable:
+        if repo.revs("(%ld::) - %ld", revs, revs):
+            raise util.Abort(_("cannot fold chain not ending with a head "\
+                               "or with branching"))
     wlock = lock = None
     try:
         wlock = repo.wlock()
@@ -2299,8 +2689,12 @@
 @eh.extsetup
 def oldevolveextsetup(ui):
     for cmd in ['kill', 'uncommit', 'touch', 'fold']:
-        entry = extensions.wrapcommand(cmdtable, cmd,
-                                       warnobserrors)
+        try:
+            entry = extensions.wrapcommand(cmdtable, cmd,
+                                           warnobserrors)
+        except error.UnknownCommand:
+            # Commands may be disabled
+            continue
 
     entry = cmdutil.findcmd('commit', commands.table)[1]
     entry[1].append(('o', 'obsolete', [],
@@ -2329,38 +2723,19 @@
         topic = 'OBSEXC'
     ui.progress(topic, *args, **kwargs)
 
-if getattr(exchange, '_pushdiscoveryobsmarkers', None) is not None:
-    @eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers')
-    def _pushdiscoveryobsmarkers(orig, pushop):
-        if (obsolete._enabled
-            and pushop.repo.obsstore
-            and 'obsolete' in pushop.remote.listkeys('namespaces')):
-            repo = pushop.repo
-            obsexcmsg(repo.ui, "computing relevant nodes\n")
-            revs = list(repo.revs('::%ln', pushop.futureheads))
-            unfi = repo.unfiltered()
-            cl = unfi.changelog
-            if not pushop.remote.capable('_evoext_obshash_0'):
-                # do not trust core yet
-                # return orig(pushop)
-                nodes = [cl.node(r) for r in revs]
-                if nodes:
-                    obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n"
-                                       % len(nodes))
-                    pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes)
-                else:
-                    obsexcmsg(repo.ui, "markers already in sync\n")
-                    pushop.outobsmarkers = []
-                    pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes)
-                return
-
-            common = []
-            obsexcmsg(repo.ui, "looking for common markers in %i nodes\n"
-                               % len(revs))
-            commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads))
-            common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs)
-
-            revs = list(unfi.revs('%ld - (::%ln)', revs, common))
+@eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers')
+def _pushdiscoveryobsmarkers(orig, pushop):
+    if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt)
+        and pushop.repo.obsstore
+        and 'obsolete' in pushop.remote.listkeys('namespaces')):
+        repo = pushop.repo
+        obsexcmsg(repo.ui, "computing relevant nodes\n")
+        revs = list(repo.revs('::%ln', pushop.futureheads))
+        unfi = repo.unfiltered()
+        cl = unfi.changelog
+        if not pushop.remote.capable('_evoext_obshash_0'):
+            # do not trust core yet
+            # return orig(pushop)
             nodes = [cl.node(r) for r in revs]
             if nodes:
                 obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n"
@@ -2369,12 +2744,30 @@
             else:
                 obsexcmsg(repo.ui, "markers already in sync\n")
                 pushop.outobsmarkers = []
+                pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes)
+            return
+
+        common = []
+        obsexcmsg(repo.ui, "looking for common markers in %i nodes\n"
+                           % len(revs))
+        commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads))
+        common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs)
+
+        revs = list(unfi.revs('%ld - (::%ln)', revs, common))
+        nodes = [cl.node(r) for r in revs]
+        if nodes:
+            obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n"
+                               % len(nodes))
+            pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes)
+        else:
+            obsexcmsg(repo.ui, "markers already in sync\n")
+            pushop.outobsmarkers = []
 
 @eh.wrapfunction(wireproto, 'capabilities')
 def discocapabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
-    if obsolete._enabled:
+    if obsolete.isenabled(repo, obsolete.exchangeopt):
         caps += ' _evoext_obshash_0'
     return caps
 
@@ -2534,7 +2927,7 @@
     pushop.ui.debug('try to push obsolete markers to remote\n')
     repo = pushop.repo
     remote = pushop.remote
-    if (obsolete._enabled  and repo.obsstore and
+    if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and
         'obsolete' in remote.listkeys('namespaces')):
         markers = pushop.outobsmarkers
         if not markers:
@@ -2609,28 +3002,7 @@
     caps.add('_evoext_pushobsmarkers_0')
     return caps
 
-@eh.addattr(localrepo.localpeer, 'evoext_pushobsmarkers_0')
-def local_pushobsmarkers(peer, obsfile):
-    data = obsfile.read()
-    tr = lock = None
-    try:
-        lock = peer._repo.lock()
-        tr = peer._repo.transaction('pushkey: obsolete markers')
-        new = peer._repo.obsstore.mergemarkers(tr, data)
-        if new is not None:
-            obsexcmsg(peer._repo.ui, "%i obsolescence markers added\n" % new, True)
-        tr.close()
-    finally:
-        lockmod.release(tr, lock)
-    peer._repo.hook('evolve_pushobsmarkers')
-
-def srv_pushobsmarkers(repo, proto):
-    """wireprotocol command"""
-    fp = StringIO()
-    proto.redirect()
-    proto.getfile(fp)
-    data = fp.getvalue()
-    fp.close()
+def _pushobsmarkers(repo, data):
     tr = lock = None
     try:
         lock = repo.lock()
@@ -2642,6 +3014,20 @@
     finally:
         lockmod.release(tr, lock)
     repo.hook('evolve_pushobsmarkers')
+
+@eh.addattr(localrepo.localpeer, 'evoext_pushobsmarkers_0')
+def local_pushobsmarkers(peer, obsfile):
+    data = obsfile.read()
+    _pushobsmarkers(peer._repo, data)
+
+def srv_pushobsmarkers(repo, proto):
+    """wireprotocol command"""
+    fp = StringIO()
+    proto.redirect()
+    proto.getfile(fp)
+    data = fp.getvalue()
+    fp.close()
+    _pushobsmarkers(repo, data)
     return wireproto.pushres(0)
 
 def _buildpullobsmarkersboundaries(pullop):
@@ -2674,34 +3060,32 @@
             kwargs['evo_obscommon'] = common
     return ret
 
-if getattr(exchange, '_getbundleobsmarkerpart', None) is not None:
-    @eh.wrapfunction(exchange, '_getbundleobsmarkerpart')
-    def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs):
-        if 'evo_obscommon' not in 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.unfiltered().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, '_getbundleobsmarkerpart')
+def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs):
+    if 'evo_obscommon' not in 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.unfiltered().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):
-    if not obsolete._enabled:
+    if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
         return None
     if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']):
         return None
@@ -2871,21 +3255,20 @@
 _bestformat = max(obsolete.formats.keys())
 
 
-if getattr(obsolete, '_checkinvalidmarkers', None) is not None:
-    @eh.wrapfunction(obsolete, '_checkinvalidmarkers')
-    def _checkinvalidmarkers(orig, markers):
-        """search for marker with invalid data and raise error if needed
-
-        Exist as a separated function to allow the evolve extension for a more
-        subtle handling.
-        """
-        if 'debugobsconvert' in sys.argv:
-            return
-        for mark in markers:
-            if node.nullid in mark[1]:
-                raise util.Abort(_('bad obsolescence marker detected: '
-                                   'invalid successors nullid'),
-                                 hint=_('You should run `hg debugobsconvert`'))
+@eh.wrapfunction(obsolete, '_checkinvalidmarkers')
+def _checkinvalidmarkers(orig, markers):
+    """search for marker with invalid data and raise error if needed
+
+    Exist as a separated function to allow the evolve extension for a more
+    subtle handling.
+    """
+    if 'debugobsconvert' in sys.argv:
+        return
+    for mark in markers:
+        if node.nullid in mark[1]:
+            raise util.Abort(_('bad obsolescence marker detected: '
+                               'invalid successors nullid'),
+                             hint=_('You should run `hg debugobsconvert`'))
 
 @command(
     'debugobsconvert',
@@ -2920,7 +3303,7 @@
 def capabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
-    if obsolete._enabled:
+    if obsolete.isenabled(repo, obsolete.exchangeopt):
         caps += ' _evoext_pushobsmarkers_0'
         caps += ' _evoext_pullobsmarkers_0'
         caps += ' _evoext_obshash_0'
@@ -2941,3 +3324,16 @@
     def newcap(repo, proto):
         return capabilities(oldcap, repo, proto)
     wireproto.commands['capabilities'] = (newcap, args)
+
+def _helploader():
+    return help.gettext(evolutionhelptext)
+
+@eh.uisetup
+def _setuphelp(ui):
+    for entry in help.helptable:
+        if entry[0] == "evolution":
+            break
+    else:
+        help.helptable.append((["evolution"], _("Safely Rewriting History"),
+                      _helploader))
+        help.helptable.sort()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/inhibit.py	Tue Jun 23 15:32:47 2015 -0700
@@ -0,0 +1,227 @@
+"""reduce the changesets evolution feature scope for early and noob friendly ui
+
+the full scale changeset evolution have some massive bleeding edge and it is
+very easy for people not very intimate with the concept to end up in intricate
+situation. in order to get some of the benefit sooner, this extension is
+disabling some of the less polished aspect of evolution. it should gradually
+get thinner and thinner as changeset evolution will get more polished. this
+extension is only recommended for large scale organisations. individual user
+should probably stick on using evolution in its current state, understand its
+concept and provide feedback
+
+This extension provides the ability to "inhibit" obsolescence markers. obsolete 
+revision can be cheaply brought back to life that way. 
+However as the inhibitor are not fitting in an append only model, this is 
+incompatible with sharing mutable history.
+"""
+from mercurial import localrepo
+from mercurial import obsolete
+from mercurial import extensions
+from mercurial import cmdutil
+from mercurial import error
+from mercurial import scmutil
+from mercurial import commands
+from mercurial import lock as lockmod
+from mercurial import bookmarks
+from mercurial.i18n import _
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+def reposetup(ui, repo):
+
+    class obsinhibitedrepo(repo.__class__):
+
+        @localrepo.storecache('obsinhibit')
+        def _obsinhibit(self):
+            # XXX we should make sure it is invalidated by transaction failure
+            obsinhibit = set()
+            raw = self.sopener.tryread('obsinhibit')
+            for i in xrange(0, len(raw), 20):
+                obsinhibit.add(raw[i:i+20])
+            return obsinhibit
+
+        def commit(self, *args, **kwargs):
+            newnode = super(obsinhibitedrepo, self).commit(*args, **kwargs)
+            if newnode is not None:
+                _inhibitmarkers(repo, [newnode])
+            return newnode
+
+    repo.__class__ = obsinhibitedrepo
+
+def _update(orig, ui, repo, *args, **kwargs):
+    """
+    When moving to a commit we want to inhibit any obsolete commit affecting
+    the changeset we are updating to. In other words we don't want any visible
+    commit to be obsolete.
+    """
+    wlock = None
+    try:
+        # Evolve is running a hook on lock release to display a warning message 
+        # if the workind dir's parent is obsolete.
+        # We take the lock here to make sure that we inhibit the parent before
+        # that hook get a chance to run.
+        wlock = repo.wlock()
+        res = orig(ui, repo, *args, **kwargs)
+        newhead = repo['.'].node()
+        _inhibitmarkers(repo, [newhead])
+        return res
+    finally:
+        lockmod.release(wlock)
+
+def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
+    """ Add inhibition markers to every obsolete bookmarks """
+    repo = bkmstoreinst._repo
+    bkmstorenodes = [repo[v].node() for v in bkmstoreinst.values()]
+    _inhibitmarkers(repo, bkmstorenodes)
+    return orig(bkmstoreinst, *args, **kwargs)
+
+def _bookmark(orig, ui, repo, *bookmarks, **opts):
+    """ Add a -D option to the bookmark command, map it to prune -B """
+    haspruneopt = opts.get('prune', False)
+    if not haspruneopt:
+        return orig(ui, repo, *bookmarks, **opts)
+
+    # Call prune -B
+    evolve = extensions.find('evolve')
+    optsdict = {
+        'new': [],
+        'succ': [],
+        'rev': [],
+        'bookmark': bookmarks[0],
+        'keep': None,
+        'biject': False,
+    }
+    evolve.cmdprune(ui, repo, **optsdict)
+
+# obsolescence inhibitor
+########################
+
+def _schedulewrite(tr, obsinhibit):
+    """Make sure on disk content will be updated on transaction commit"""
+    def writer(fp):
+        """Serialize the inhibited list to disk.
+        """
+        raw = ''.join(obsinhibit)
+        fp.write(raw)
+    tr.addfilegenerator('obsinhibit', ('obsinhibit',), writer)
+    tr.hookargs['obs_inbihited'] = '1'
+
+def _filterpublic(repo, nodes):
+    """filter out inhibitor on public changeset
+
+    Public changesets are already immune to obsolescence"""
+    getrev = repo.changelog.nodemap.get
+    getphase = repo._phasecache.phase
+    return (n for n in repo._obsinhibit
+            if getrev(n) is not None and getphase(repo, getrev(n)))
+
+def _inhibitmarkers(repo, nodes):
+    """add marker inhibitor for all obsolete revision under <nodes>
+
+    Content of <nodes> and all mutable ancestors are considered. Marker for
+    obsolete revision only are created.
+    """
+    newinhibit = repo.set('::%ln and obsolete()', nodes)
+    if newinhibit:
+        lock = tr = None
+        try:
+            lock = repo.lock()
+            tr = repo.transaction('obsinhibit')
+            repo._obsinhibit.update(c.node() for c in newinhibit)
+            _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit))
+            repo.invalidatevolatilesets()
+            tr.close()
+        finally:
+            lockmod.release(tr, lock)
+
+def _deinhibitmarkers(repo, nodes):
+    """lift obsolescence inhibition on a set of nodes
+
+    This will be triggered when inhibited nodes received new obsolescence
+    markers. Otherwise the new obsolescence markers would also be inhibited.
+    """
+    deinhibited = repo._obsinhibit & set(nodes)
+    if deinhibited:
+        tr = repo.transaction('obsinhibit')
+        try:
+            repo._obsinhibit -= deinhibited
+            _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit))
+            repo.invalidatevolatilesets()
+            tr.close()
+        finally:
+            tr.release()
+
+def _createmarkers(orig, repo, relations, flag=0, date=None, metadata=None):
+    """wrap markers create to make sure we de-inhibit target nodes"""
+    # wrapping transactio to unify the one in each function
+    tr = repo.transaction('add-obsolescence-marker')
+    try:
+        orig(repo, relations, flag, date, metadata)
+        precs = (r[0].node() for r in relations)
+        _deinhibitmarkers(repo, precs)
+        tr.close()
+    finally:
+        tr.release()
+
+def transactioncallback(orig, repo, *args, **kwargs):
+    """ Wrap localrepo.transaction to inhibit new obsolete changes """
+    def inhibitposttransaction(transaction):
+        # At the end of the transaction we catch all the new visible and
+        # obsolete commit to inhibit them
+        visibleobsolete = repo.revs('obsolete() - hidden()')
+        ignoreset = set(getattr(repo, '_rebaseset', []))
+        visibleobsolete = list(r for r in visibleobsolete if r not in ignoreset)
+        if visibleobsolete:
+            _inhibitmarkers(repo, [repo[r].node() for r in visibleobsolete])
+    transaction = orig(repo, *args, **kwargs)
+    transaction.addpostclose('inhibitposttransaction', inhibitposttransaction)
+    return transaction
+
+def extsetup(ui):
+    # lets wrap the computation of the obsolete set
+    # We apply inhibition there
+    obsfunc = obsolete.cachefuncs['obsolete']
+    def _computeobsoleteset(repo):
+        """remove any inhibited nodes from the obsolete set
+
+        This will trickle down to other part of mercurial (hidden, log, etc)"""
+        obs = obsfunc(repo)
+        getrev = repo.changelog.nodemap.get
+        for n in repo._obsinhibit:
+            obs.discard(getrev(n))
+        return obs
+    try:
+        extensions.find('directaccess')
+    except KeyError:
+        errormsg = _('Cannot use inhibit without the direct access extension')
+        raise error.Abort(errormsg)
+
+    # Wrapping this to inhibit obsolete revs resulting from a transaction
+    extensions.wrapfunction(localrepo.localrepository,
+                            'transaction', transactioncallback)
+
+    obsolete.cachefuncs['obsolete'] = _computeobsoleteset
+    # wrap create marker to make it able to lift the inhibition
+    extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers)
+    # drop divergence computation since it is incompatible with "light revive"
+    obsolete.cachefuncs['divergent'] = lambda repo: set()
+    # drop bumped computation since it is incompatible with "light revive"
+    obsolete.cachefuncs['bumped'] = lambda repo: set()
+    # wrap update to make sure that no obsolete commit is visible after an
+    # update
+    extensions.wrapcommand(commands.table, 'update', _update)
+    # There are two ways to save bookmark changes during a transation, we
+    # wrap both to add inhibition markers.
+    extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged)
+    extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged)
+    # Add bookmark -D option
+    entry = extensions.wrapcommand(commands.table, 'bookmark', _bookmark)
+    entry[1].append(('D','prune',None,
+                    _('delete the bookmark and prune the commits underneath')))
+
+@command('debugobsinhibit', [], '')
+def cmddebugobsinhibit(ui, repo, *revs):
+    """inhibit obsolescence markers effect on a set of revs"""
+    nodes = (repo[r].node() for r in scmutil.revrange(repo, revs))
+    _inhibitmarkers(repo, nodes)
--- a/hgext/obsolete.py	Tue Jun 23 15:32:15 2015 -0700
+++ b/hgext/obsolete.py	Tue Jun 23 15:32:47 2015 -0700
@@ -14,8 +14,6 @@
 
 try:
     from mercurial import obsolete
-    if not obsolete._enabled:
-        obsolete._enabled = True
 except ImportError:
     raise util.Abort('Obsolete extension requires Mercurial 2.3 (or later)')
 
@@ -40,6 +38,10 @@
     """
     if not repo.local():
         return
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if not evolveopts:
+        evolveopts = 'all'
+        ui.setconfig('experimental', 'evolution', evolveopts)
     for arg in sys.argv:
         if 'debugc' in arg:
             break
--- a/hgext/pushexperiment.py	Tue Jun 23 15:32:15 2015 -0700
+++ b/hgext/pushexperiment.py	Tue Jun 23 15:32:47 2015 -0700
@@ -49,7 +49,8 @@
 
 def syncpush(orig, repo, remote):
     """wraper for obsolete.syncpush to use the fast way if possible"""
-    if not (obsolete._enabled and repo.obsstore):
+    if not (obsolete.isenabled(repo, obsolete.exchangeopt) and
+            repo.obsstore):
         return
     if remote.capable('_push_experiment_pushobsmarkers_0'):
         return # already pushed before changeset
@@ -75,7 +76,7 @@
     """push wrapped that call the wire protocol command"""
     if not remote.canpush():
         raise util.Abort(_("destination does not support push"))
-    if (obsolete._enabled and repo.obsstore
+    if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore
         and remote.capable('_push_experiment_pushobsmarkers_0')):
         # push marker early to limit damage of pushing too early.
         try:
@@ -94,7 +95,7 @@
 def capabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
-    if obsolete._enabled:
+    if obsolete.isenabled(repo, obsolete.exchangeopt):
         caps += ' _push_experiment_pushobsmarkers_0'
     caps += ' _push_experiment_notifypushend_0'
     return caps
--- a/hgext/simple4server.py	Tue Jun 23 15:32:15 2015 -0700
+++ b/hgext/simple4server.py	Tue Jun 23 15:32:47 2015 -0700
@@ -12,7 +12,6 @@
 buglink = 'http://bz.selenic.com/'
 
 import mercurial.obsolete
-mercurial.obsolete._enabled = True
 
 import struct
 from mercurial import util
@@ -31,8 +30,6 @@
 gboptslist = gboptsmap = None
 try:
     from mercurial import obsolete
-    if not obsolete._enabled:
-        obsolete._enabled = True
     from mercurial import wireproto
     gboptslist = getattr(wireproto, 'gboptslist', None)
     gboptsmap = getattr(wireproto, 'gboptsmap', None)
@@ -247,7 +244,7 @@
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
     advertise = repo.ui.configbool('__temporary__', 'advertiseobsolete', True)
-    if obsolete._enabled and advertise:
+    if obsolete.isenabled(repo, obsolete.exchangeopt) and advertise:
         caps += ' _evoext_pushobsmarkers_0'
         caps += ' _evoext_pullobsmarkers_0'
         caps += ' _evoext_obshash_0'
@@ -302,3 +299,8 @@
     extensions.wrapfunction(pushkey, '_nslist', _nslist)
     pushkey._namespaces['namespaces'] = (lambda *x: False, pushkey._nslist)
 
+def reposetup(ui, repo):
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if not evolveopts:
+        evolveopts = 'all'
+        ui.setconfig('experimental', 'evolution', evolveopts)
--- a/setup.py	Tue Jun 23 15:32:15 2015 -0700
+++ b/setup.py	Tue Jun 23 15:32:47 2015 -0700
@@ -1,6 +1,7 @@
 # Copied from histedit setup.py
 # Credit to Augie Fackler <durin42@gmail.com>
 
+import os
 from distutils.core import setup
 from os.path import dirname, join
 
@@ -14,6 +15,14 @@
           if "'" in line:
             return line.split("'")[1]
 
+py_modules = [
+    'hgext.evolve',
+]
+
+if os.environ.get('INCLUDE_INHIBIT'):
+    py_modules.append('hgext.inhibit')
+    py_modules.append('hgext.directaccess')
+
 setup(
     name='hg-evolve',
     version=get_version('hgext/evolve.py'),
@@ -25,5 +34,5 @@
     long_description=open('README').read(),
     keywords='hg mercurial',
     license='GPLv2+',
-    py_modules=['hgext.evolve'],
+    py_modules=py_modules
 )
--- a/tests/killdaemons.py	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/killdaemons.py	Tue Jun 23 15:32:47 2015 -0700
@@ -1,25 +1,91 @@
 #!/usr/bin/env python
 
-import os, time, errno, signal
+import os, sys, time, errno, signal
+
+if os.name =='nt':
+    import ctypes
+
+    def _check(ret, expectederr=None):
+        if ret == 0:
+            winerrno = ctypes.GetLastError()
+            if winerrno == expectederr:
+                return True
+            raise ctypes.WinError(winerrno)
 
-# Kill off any leftover daemon processes
-try:
-    fp = open(os.environ['DAEMON_PIDS'])
-    for line in fp:
+    def kill(pid, logfn, tryhard=True):
+        logfn('# Killing daemon process %d' % pid)
+        PROCESS_TERMINATE = 1
+        PROCESS_QUERY_INFORMATION = 0x400
+        SYNCHRONIZE = 0x00100000
+        WAIT_OBJECT_0 = 0
+        WAIT_TIMEOUT = 258
+        handle = ctypes.windll.kernel32.OpenProcess(
+                PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
+                False, pid)
+        if handle == 0:
+            _check(0, 87) # err 87 when process not found
+            return # process not found, already finished
         try:
-            pid = int(line)
-        except ValueError:
-            continue
+            r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
+            if r == WAIT_OBJECT_0:
+                pass # terminated, but process handle still available
+            elif r == WAIT_TIMEOUT:
+                _check(ctypes.windll.kernel32.TerminateProcess(handle, -1))
+            else:
+                _check(r)
+
+            # TODO?: forcefully kill when timeout
+            #        and ?shorter waiting time? when tryhard==True
+            r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
+                                                       # timeout = 100 ms
+            if r == WAIT_OBJECT_0:
+                pass # process is terminated
+            elif r == WAIT_TIMEOUT:
+                logfn('# Daemon process %d is stuck')
+            else:
+                _check(r) # any error
+        except: #re-raises
+            ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error
+            raise
+        _check(ctypes.windll.kernel32.CloseHandle(handle))
+
+else:
+    def kill(pid, logfn, tryhard=True):
         try:
             os.kill(pid, 0)
+            logfn('# Killing daemon process %d' % pid)
             os.kill(pid, signal.SIGTERM)
-            for i in range(10):
-                time.sleep(0.05)
+            if tryhard:
+                for i in range(10):
+                    time.sleep(0.05)
+                    os.kill(pid, 0)
+            else:
+                time.sleep(0.1)
                 os.kill(pid, 0)
+            logfn('# Daemon process %d is stuck - really killing it' % pid)
             os.kill(pid, signal.SIGKILL)
         except OSError, err:
             if err.errno != errno.ESRCH:
                 raise
-    fp.close()
-except IOError:
-    pass
+
+def killdaemons(pidfile, tryhard=True, remove=False, logfn=None):
+    if not logfn:
+        logfn = lambda s: s
+    # Kill off any leftover daemon processes
+    try:
+        fp = open(pidfile)
+        for line in fp:
+            try:
+                pid = int(line)
+            except ValueError:
+                continue
+            kill(pid, logfn, tryhard)
+        fp.close()
+        if remove:
+            os.unlink(pidfile)
+    except IOError:
+        pass
+
+if __name__ == '__main__':
+    path, = sys.argv[1:]
+    killdaemons(path)
--- a/tests/test-amend.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-amend.t	Tue Jun 23 15:32:47 2015 -0700
@@ -115,6 +115,7 @@
   branch: foo
   commit: 1 unknown (clean)
   update: (current)
+  phases: 3 draft
 
 Check the help
   $ hg amend -h
--- a/tests/test-corrupt.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-corrupt.t	Tue Jun 23 15:32:47 2015 -0700
@@ -110,8 +110,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 2 changes to 2 files
-  pushing 2 obsolescence markers (* bytes) (glob)
-  2 obsolescence markers added
+  2 new obsolescence markers
   $ hg -R ../other verify
   checking changesets
   checking manifests
--- a/tests/test-evolve-bumped.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-evolve-bumped.t	Tue Jun 23 15:32:47 2015 -0700
@@ -1,6 +1,11 @@
   $ hg init public
   $ cd public
   $ echo a > a
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
   $ hg commit -A -m init
   adding a
   $ cd ..
@@ -11,6 +16,8 @@
   $ cat >> .hg/hgrc <<EOF
   > [extensions]
   > evolve = $evolvepath
+  > [ui]
+  > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n
   > [phases]
   > publish = false
   > EOF
@@ -32,12 +39,7 @@
   adding file changes
   added 1 changesets with 1 changes to 1 files
   $ hg log -r 'draft()'
-  changeset:   1:4d1169d82e47
-  tag:         tip
-  user:        alice
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     modify a
-  
+  1:4d1169d82e47@default(draft) modify a
 
   $ cd ../bob
   $ hg pull ../private
@@ -47,15 +49,9 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pull obsolescence markers
   (run 'hg update' to get a working copy)
   $ hg log -r 'draft()'
-  changeset:   1:4d1169d82e47
-  tag:         tip
-  user:        alice
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     modify a
-  
+  1:4d1169d82e47@default(draft) modify a
   $ hg push ../public
   pushing to ../public
   searching for changes
@@ -71,12 +67,59 @@
   pulling from ../public
   searching for changes
   no changes found
-  pull obsolescence markers
   1 new bumped changesets
 
-  $ hg evolve -a
+  $ hg evolve -a -A --bumped
   recreate:[2] tweak a
   atop:[1] modify a
   computing new diff
   committed as 4d1169d82e47
   working directory is now at 4d1169d82e47
+
+Bumped Merge changeset:
+-----------------------
+
+We currently cannot automatically solve bumped changeset that is the
+product of a merge, we add a test for it.
+
+  $ mkcommit _a
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit _b
+  created new head
+  $ mkcommit _c
+  $ hg log -G
+  @  5:eeaf70969381@default(draft) add _c
+  |
+  o  4:6612fc0ddeb6@default(draft) add _b
+  |
+  | o  3:154ad198ff4a@default(draft) add _a
+  |/
+  o  1:4d1169d82e47@default(public) modify a
+  |
+  o  0:d3873e73d99e@default(public) init
+  
+  $ hg merge 3
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg commit -m "merge"
+  $ hg commit --amend -m "New message"
+  $ hg phase --public 551127da2a8a --hidden
+  1 new bumped changesets
+  $ hg log -G
+  @    7:b28e84916d8c@default(draft) New message
+  |\
+  +---o  6:551127da2a8a@default(public) merge
+  | |/
+  | o  5:eeaf70969381@default(public) add _c
+  | |
+  | o  4:6612fc0ddeb6@default(public) add _b
+  | |
+  o |  3:154ad198ff4a@default(public) add _a
+  |/
+  o  1:4d1169d82e47@default(public) modify a
+  |
+  o  0:d3873e73d99e@default(public) init
+  
+  $ hg evolve --all --bumped
+  skipping b28e84916d8c : we do not handle merge yet
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-order.t	Tue Jun 23 15:32:47 2015 -0700
@@ -0,0 +1,263 @@
+evolve --rev reordering
+-----------------------
+
+  $ cat >> $HGRCPATH <<EOF
+  > [defaults]
+  > amend=-d "0 0"
+  > fold=-d "0 0"
+  > [web]
+  > push_ssl = false
+  > allow_push = *
+  > [phases]
+  > publish = False
+  > [diff]
+  > git = 1
+  > unified = 0
+  > [ui]
+  > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n
+  > [extensions]
+  > hgext.graphlog=
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+
+  $ mkstack() {
+  >    # Creates a stack of commit based on $1 with messages from $2, $3 ..
+  >    hg update "$1" -C
+  >    shift
+  >    mkcommits $*
+  > }
+
+  $ mkcommits() {
+  >   for i in $@; do mkcommit $i ; done
+  > }
+
+Initial setup
+  $ hg init testrevorder
+  $ cd testrevorder
+  $ mkcommits p _a _b _c
+  $ hg phase --public 0
+  $ hg up 'desc(_a)'
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo "aaa" > _a
+  $ hg amend
+  2 new unstable changesets
+  $ hg log -G
+  @  5:12d519679175@default(draft) add _a
+  |
+  | o  3:4d156641b718@default(draft) add _c
+  | |
+  | o  2:4d7242ebb004@default(draft) add _b
+  | |
+  | x  1:2d73fcd7f07d@default(draft) add _a
+  |/
+  o  0:f92638be10c7@default(public) add p
+  
+
+evolve --rev reorders the rev to solve instability, trivial case 2 revs wrong order
+  $ hg evolve --rev 'desc(_c) + desc(_b)'
+  move:[2] add _b
+  atop:[5] add _a
+  move:[3] add _c
+  atop:[6] add _b
+  working directory is now at 52b8f9b04f83
+
+evolve --rev reorders the rev to solve instability. Harder case, obsolescence
+accross three stacks in growing rev numbers.
+  $ hg up "desc(_c)"
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ mkcommit _d
+  $ hg up "desc(_a)"
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ hg amend -m "aprime"
+  3 new unstable changesets
+  $ hg evolve --rev "desc(_b)"
+  move:[6] add _b
+  atop:[9] aprime
+  working directory is now at 476c9c052aae
+  $ hg up "desc(_b) - obsolete()"
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg amend -m "bprime"
+  $ hg up "desc(aprime)"
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg amend -m "asecond"
+  1 new unstable changesets
+  $ hg log -G
+  @  12:9a584314f3f3@default(draft) asecond
+  |
+  | o  11:a59c79776f7c@default(draft) bprime
+  | |
+  | x  9:81a687b96d4d@default(draft) aprime
+  |/
+  | o  8:464731bc0ed0@default(draft) add _d
+  | |
+  | o  7:52b8f9b04f83@default(draft) add _c
+  | |
+  | x  6:59476c3836ef@default(draft) add _b
+  | |
+  | x  5:12d519679175@default(draft) add _a
+  |/
+  o  0:f92638be10c7@default(public) add p
+  
+  $ hg evolve --rev "unstable()"
+  move:[11] bprime
+  atop:[12] asecond
+  move:[7] add _c
+  atop:[13] bprime
+  move:[8] add _d
+  atop:[14] add _c
+  working directory is now at 225d2cc5d3fc
+  $ hg log -G
+  @  15:225d2cc5d3fc@default(draft) add _d
+  |
+  o  14:0fc229278e4d@default(draft) add _c
+  |
+  o  13:c3741b9eafae@default(draft) bprime
+  |
+  o  12:9a584314f3f3@default(draft) asecond
+  |
+  o  0:f92638be10c7@default(public) add p
+  
+
+Evolve --rev more complex case: two sets of stacks one with prune an no successor, the other one
+partially solvable
+ 
+First set of stack:
+  $ mkstack "desc(_d)" c1_ c2_ c3_ c4_ >/dev/null
+  $ mkstack "desc(_d)" c1prime c2prime >/dev/null
+  $ mkstack "desc(_d)" c1second >/dev/null
+  $ hg prune "desc(c1_)" -s "desc(c1prime)"
+  1 changesets pruned
+  3 new unstable changesets
+  $ hg prune "desc(c2_)" -s "desc(c2prime)"
+  1 changesets pruned
+  $ hg prune "desc(c1prime)" -s "desc(c1second)"
+  1 changesets pruned
+  1 new unstable changesets
+  $ hg log -G -r "desc(_d)::"
+  @  22:a329855d0bc1@default(draft) add c1second
+  |
+  | o  21:072276ece1bf@default(draft) add c2prime
+  | |
+  | x  20:f137acd06692@default(draft) add c1prime
+  |/
+  | o  19:0a1d9b2ce733@default(draft) add c4_
+  | |
+  | o  18:e2874f41c56c@default(draft) add c3_
+  | |
+  | x  17:3247c33339fa@default(draft) add c2_
+  | |
+  | x  16:df322257c182@default(draft) add c1_
+  |/
+  o  15:225d2cc5d3fc@default(draft) add _d
+  |
+
+Second set of stack with no successor for b2_:
+  $ mkstack "desc(_d)" b1_ b2_ b3_ b4_ >/dev/null
+  $ mkstack "desc(_d)" b1prime b3prime >/dev/null
+  $ hg prune "desc(b1_)" -s "desc(b1prime)"
+  1 changesets pruned
+  3 new unstable changesets
+  $ hg prune "desc(b3_)" -s "desc(b3prime)"
+  1 changesets pruned
+  $ hg prune "desc(b2_)"
+  1 changesets pruned
+
+  $ hg log -G -r "desc(_d)::"
+  @  28:ba4c348b6d5e@default(draft) add b3prime
+  |
+  o  27:8fe985f5d0aa@default(draft) add b1prime
+  |
+  | o  26:1d9ba2e75c93@default(draft) add b4_
+  | |
+  | x  25:aec6a9657b6c@default(draft) add b3_
+  | |
+  | x  24:a69b58575918@default(draft) add b2_
+  | |
+  | x  23:3564eb18e448@default(draft) add b1_
+  |/
+  | o  22:a329855d0bc1@default(draft) add c1second
+  |/
+  | o  21:072276ece1bf@default(draft) add c2prime
+  | |
+  | x  20:f137acd06692@default(draft) add c1prime
+  |/
+  | o  19:0a1d9b2ce733@default(draft) add c4_
+  | |
+  | o  18:e2874f41c56c@default(draft) add c3_
+  | |
+  | x  17:3247c33339fa@default(draft) add c2_
+  | |
+  | x  16:df322257c182@default(draft) add c1_
+  |/
+  o  15:225d2cc5d3fc@default(draft) add _d
+  |
+
+Solve the full second stack and only part of the first one
+  $ echo "(desc(_d)::) - desc(c3_)"
+  (desc(_d)::) - desc(c3_)
+  $ hg evolve --rev "(desc(_d)::) - desc(c3_)"
+  cannot solve instability of 0a1d9b2ce733, skipping
+  move:[21] add c2prime
+  atop:[22] add c1second
+  move:[26] add b4_
+  atop:[28] add b3prime
+  working directory is now at 4897c8ed7645
+
+Cleanup
+  $ hg evolve --rev "(desc(_d)::)"
+  move:[18] add c3_
+  atop:[29] add c2prime
+  move:[19] add c4_
+  atop:[31] add c3_
+  working directory is now at 4ee8feb52325
+  $ hg log -G -r "desc(_d)::"
+  @  32:4ee8feb52325@default(draft) add c4_
+  |
+  o  31:08a530ce67e1@default(draft) add c3_
+  |
+  | o  30:4897c8ed7645@default(draft) add b4_
+  | |
+  o |  29:3abc7618dd5f@default(draft) add c2prime
+  | |
+  | o  28:ba4c348b6d5e@default(draft) add b3prime
+  | |
+  | o  27:8fe985f5d0aa@default(draft) add b1prime
+  | |
+  o |  22:a329855d0bc1@default(draft) add c1second
+  |/
+  o  15:225d2cc5d3fc@default(draft) add _d
+  |
+
+Test multiple revision with some un-evolvable because parent is splitted
+------------------------------------------------------------------------
+
+  $ hg up 'desc(c2prime)'
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ mkcommit c3part1
+  created new head
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [29] add c2prime
+  $ mkcommit c3part2
+  created new head
+  $ hg prune -s 'desc(c3part1)' 'desc(c3_)'
+  1 changesets pruned
+  1 new unstable changesets
+  $ hg prune -s 'desc(c3part2)' 'desc(c3_)'
+  1 changesets pruned
+  2 new divergent changesets
+  $ hg up 'desc(b3prime)'
+  2 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ hg amend -m 'b3second'
+  1 new unstable changesets
+  $ hg evolve --rev 'unstable()'
+  move:[30] add b4_
+  atop:[35] b3second
+  skipping 08a530ce67e1: divergent rewriting. can't choose destination
+  working directory is now at a51a8a82fdba
+
--- a/tests/test-evolve.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-evolve.t	Tue Jun 23 15:32:47 2015 -0700
@@ -22,10 +22,65 @@
   >    hg ci -m "add $1"
   > }
 
+  $ mkstack() {
+  >    # Creates a stack of commit based on $1 with messages from $2, $3 ..
+  >    hg update $1 -C
+  >    shift
+  >    mkcommits $*
+  > }
+
   $ glog() {
   >   hg glog --template '{rev}:{node|short}@{branch}({phase}) {desc|firstline}\n' "$@"
   > }
 
+  $ shaof() {
+  >   hg log -T {node} -r "first(desc($1))"
+  > }
+
+  $ mkcommits() {
+  >   for i in $@; do mkcommit $i ; done
+  > }
+
+Test the evolution test topic is installed
+
+  $ hg help evolution
+  Safely Rewriting History
+  """"""""""""""""""""""""
+  
+      Obsolescence markers make it possible to mark changesets that have been
+      deleted or superset in a new version of the changeset.
+  
+      Unlike the previous way of handling such changes, by stripping the old
+      changesets from the repository, obsolescence markers can be propagated
+      between repositories. This allows for a safe and simple way of exchanging
+      mutable history and altering it after the fact. Changeset phases are
+      respected, such that only draft and secret changesets can be altered (see
+      "hg hg phases" for details).
+  
+      Obsolescence is tracked using "obsolete markers", a piece of metadata
+      tracking which changesets have been made obsolete, potential successors
+      for a given changeset, the moment the changeset was marked as obsolete,
+      and the user who performed the rewriting operation. The markers are stored
+      separately from standard changeset data can be exchanged without any of
+      the precursor changesets, preventing unnecessary exchange of obsolescence
+      data.
+  
+      The complete set of obsolescence markers describes a history of changeset
+      modifications that is orthogonal to the repository history of file
+      modifications. This changeset history allows for detection and automatic
+      resolution of edge cases arising from multiple users rewriting the same
+      part of history concurrently.
+  
+      Current feature status
+      ======================
+  
+      This feature is still in development.  If you see this help, you have
+      enable an extension that turned this feature on.
+  
+      Obsolescence markers will be exchanged between repositories that
+      explicitly assert support for the obsolescence feature (this can currently
+      only be done via an extension).
+
 various init
 
   $ hg init local
@@ -69,9 +124,9 @@
   $ hg id -n
   5
   $ hg kill .
-  1 changesets pruned
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at fbb94e3a0ecf
+  1 changesets pruned
   $ hg qlog
   4 - fbb94e3a0ecf add e (draft)
   3 - 47d2a3944de8 add d (draft)
@@ -82,9 +137,9 @@
 test multiple kill
 
   $ hg kill 4 -r 3
-  2 changesets pruned
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at 7c3bad9141dc
+  2 changesets pruned
   $ hg qlog
   2 - 4538525df7e2 add c (draft)
   1 - 7c3bad9141dc add b (public)
@@ -97,9 +152,9 @@
   $ echo 4 > g
   $ hg add g
   $ hg kill .
-  1 changesets pruned
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at 7c3bad9141dc
+  1 changesets pruned
   $ hg st
   A g
 
@@ -317,7 +372,7 @@
   |
   o  0	: base - test
   
-  $ hg evolve --any --traceback
+  $ hg evolve --any --traceback --bumped
   recreate:[8] another feature that rox
   atop:[7] another feature (child of ba0ec09b1bab)
   computing new diff
@@ -419,7 +474,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pull obsolescence markers
   $ cd alpha
 
   $ cat << EOF > A
@@ -476,8 +530,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pull obsolescence markers
-  2 obsolescence markers added
+  2 new obsolescence markers
   (run 'hg update' to get a working copy)
   $ hg up
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -866,9 +919,20 @@
 
   $ hg up 8
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G --template '{rev} [{branch}] {desc|firstline}\n'
+  o  10 [default] a1__
+  |
+  | o  9 [mybranch] a3
+  | |
+  | @  8 [mybranch] a2
+  | |
+  | x  7 [default] a1_
+  |/
+  o  0 [default] a0
+  
   $ hg evolve
-  nothing to evolve here
-  (2 troubled changesets, do you want --any ?)
+  nothing to evolve on current working copy parent
+  (2 other unstable in the repository, do you want --any or --rev)
   [2]
 
 
@@ -887,3 +951,291 @@
   working directory is now at f37ed7a60f43
   $ ls .hg/bookmarks*
   .hg/bookmarks
+
+Possibility to select what trouble to solve first, asking for bumped before
+divergent
+  $ hg up 10
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg revert -r 11 --all
+  reverting a
+  $ hg log -G --template '{rev} [{branch}] {desc|firstline}\n'
+  o  11 [mybranch] a2
+  |
+  @  10 [default] a1__
+  |
+  | o  9 [mybranch] a3
+  | |
+  | x  8 [mybranch] a2
+  | |
+  | x  7 [default] a1_
+  |/
+  o  0 [default] a0
+  
+  $ echo "hello world" > newfile
+  $ hg add newfile
+  $ hg commit -m "add new file bumped" -o 11
+  $ hg phase --public --hidden 11
+  1 new bumped changesets
+  $ hg glog
+  @  12	: add new file bumped - test
+  |
+  | o  11	: a2 - test
+  |/
+  o  10	testbookmark: a1__ - test
+  |
+  | o  9	: a3 - test
+  | |
+  | x  8	: a2 - test
+  | |
+  | x  7	: a1_ - test
+  |/
+  o  0	: a0 - test
+  
+
+Now we have a bumped and an unstable changeset, we solve the bumped first
+normally the unstable changeset would be solve first
+
+  $ hg glog
+  @  12	: add new file bumped - test
+  |
+  | o  11	: a2 - test
+  |/
+  o  10	testbookmark: a1__ - test
+  |
+  | o  9	: a3 - test
+  | |
+  | x  8	: a2 - test
+  | |
+  | x  7	: a1_ - test
+  |/
+  o  0	: a0 - test
+  
+  $ hg evolve -r 12 --bumped
+  recreate:[12] add new file bumped
+  atop:[11] a2
+  computing new diff
+  committed as d66b1e328488
+  working directory is now at d66b1e328488
+  $ hg evolve --any
+  move:[9] a3
+  atop:[13] bumped update to f37ed7a60f43:
+  working directory is now at 7d2ce5f38f9b
+Check that we can resolve troubles in a revset with more than one commit
+  $ hg up 14 -C
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ mkcommit gg
+  $ hg up 14 
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit gh
+  created new head
+  $ hg up 14 
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ printf "newline\nnewline\n" >> a
+  $ hg glog
+  o  16	: add gh - test
+  |
+  | o  15	: add gg - test
+  |/
+  @  14	: a3 - test
+  |
+  o  13	: bumped update to f37ed7a60f43: - test
+  |
+  o  11	: a2 - test
+  |
+  o  10	testbookmark: a1__ - test
+  |
+  o  0	: a0 - test
+  
+  $ hg amend
+  2 new unstable changesets
+  $ hg glog
+  @  18	: a3 - test
+  |
+  | o  16	: add gh - test
+  | |
+  | | o  15	: add gg - test
+  | |/
+  | x  14	: a3 - test
+  |/
+  o  13	: bumped update to f37ed7a60f43: - test
+  |
+  o  11	: a2 - test
+  |
+  o  10	testbookmark: a1__ - test
+  |
+  o  0	: a0 - test
+  
+
+Evolving an empty revset should do nothing
+  $ hg evolve --rev "16 and 15"
+  set of specified revisions is empty
+  [1]
+
+  $ hg evolve --rev "14::" --bumped
+  no bumped changesets in specified revisions
+  (do you want to use --unstable)
+  [2]
+  $ hg evolve --rev "14::" --unstable
+  move:[15] add gg
+  atop:[18] a3
+  move:[16] add gh
+  atop:[18] a3
+  working directory is now at db3d894869b0
+  $ hg glog
+  @  20	: add gh - test
+  |
+  | o  19	: add gg - test
+  |/
+  o  18	: a3 - test
+  |
+  o  13	: bumped update to f37ed7a60f43: - test
+  |
+  o  11	: a2 - test
+  |
+  o  10	testbookmark: a1__ - test
+  |
+  o  0	: a0 - test
+  
+Check hg evolve --rev on singled out commit
+
+
+  $ hg up 19 -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit j1
+  $ mkcommit j2
+  $ mkcommit j3
+  $ hg up .^^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo "hello" > j4
+  $ hg add j4
+  $ hg amend
+  2 new unstable changesets
+  $ glog -r "18::"
+  @  25:4c0bc042ef3b@default(draft) add j1
+  |
+  | o  23:c70048fd3350@default(draft) add j3
+  | |
+  | o  22:714e60ca57b7@default(draft) add j2
+  | |
+  | x  21:b430835af718@default(draft) add j1
+  |/
+  | o  20:db3d894869b0@default(draft) add gh
+  | |
+  o |  19:10ffdd7e3cc9@default(draft) add gg
+  |/
+  o  18:0bb66d4c1968@default(draft) a3
+  |
+
+  $ hg evolve --rev 23 --any
+  abort: cannot specify both "--rev" and "--any"
+  [255]
+  $ hg evolve --rev 23
+  cannot solve instability of c70048fd3350, skipping
+
+Check that uncommit respects the allowunstable option
+With only createmarkers we can only uncommit on a head
+  $ cat >> $HGRCPATH <<EOF
+  > [experimental]
+  > evolution=createmarkers, allnewcommands
+  > EOF
+  $ hg up 4c0bc042ef3b^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg uncommit --all
+  abort: cannot uncommit in the middle of a stack
+  [255]
+  $ hg up 4c0bc042ef3b
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg uncommit --all
+  new changeset is empty
+  (use "hg prune ." to remove it)
+  $ glog -r "18::"
+  @  26:04b32348803e@default(draft) add j1
+  |
+  | o  23:c70048fd3350@default(draft) add j3
+  | |
+  | o  22:714e60ca57b7@default(draft) add j2
+  | |
+  | x  21:b430835af718@default(draft) add j1
+  |/
+  | o  20:db3d894869b0@default(draft) add gh
+  | |
+  o |  19:10ffdd7e3cc9@default(draft) add gg
+  |/
+  o  18:0bb66d4c1968@default(draft) a3
+  |
+
+Check that prune respects the allowunstable option
+  $ hg up -C .
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg up 20
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg evolve --all
+  nothing to evolve on current working copy parent
+  (2 other unstable in the repository, do you want --any or --rev)
+  [2]
+  $ hg evolve --all --any
+  move:[22] add j2
+  atop:[26] add j1
+  move:[23] add j3
+  atop:[27] add j2
+  working directory is now at 920a35e8dbd0
+  $ glog -r "18::"
+  @  28:920a35e8dbd0@default(draft) add j3
+  |
+  o  27:31e050d895dd@default(draft) add j2
+  |
+  o  26:04b32348803e@default(draft) add j1
+  |
+  | o  20:db3d894869b0@default(draft) add gh
+  | |
+  o |  19:10ffdd7e3cc9@default(draft) add gg
+  |/
+  o  18:0bb66d4c1968@default(draft) a3
+  |
+  $ hg up 19
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ mkcommit c5_
+  created new head
+  $ hg prune '26 + 27'
+  abort: cannot prune in the middle of a stack
+  [255]
+  $ hg prune '19::28'
+  abort: cannot prune in the middle of a stack
+  [255]
+  $ hg prune '26::'
+  3 changesets pruned
+  $ glog -r "18::"
+  @  29:5a6c53544778@default(draft) add c5_
+  |
+  | o  20:db3d894869b0@default(draft) add gh
+  | |
+  o |  19:10ffdd7e3cc9@default(draft) add gg
+  |/
+  o  18:0bb66d4c1968@default(draft) a3
+  |
+
+Check that fold respects the allowunstable option
+  $ hg up 0bb66d4c1968
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ mkcommit unstableifparentisfolded
+  created new head
+  $ glog -r "18::"
+  @  30:30ecefd67c0a@default(draft) add unstableifparentisfolded
+  |
+  | o  29:5a6c53544778@default(draft) add c5_
+  | |
+  +---o  20:db3d894869b0@default(draft) add gh
+  | |
+  | o  19:10ffdd7e3cc9@default(draft) add gg
+  |/
+  o  18:0bb66d4c1968@default(draft) a3
+  |
+
+  $ hg fold --exact "19 + 18"
+  abort: cannot fold chain not ending with a head or with branching
+  [255]
+  $ hg fold --exact "18::29"
+  abort: cannot fold chain not ending with a head or with branching
+  [255]
+  $ hg fold --exact "19::"
+  2 changesets folded
--- a/tests/test-exchange-D2.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-exchange-D2.t	Tue Jun 23 15:32:47 2015 -0700
@@ -37,9 +37,9 @@
   created new head
   $ hg debugobsolete `getid 'desc(A0)'` `getid 'desc(A1)'`
   $ hg prune --date '0 0' .
-  1 changesets pruned
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at a9bdc8b26820
+  1 changesets pruned
   $ hg strip --hidden -q 'desc(A1)'
   $ hg log -G --hidden
   x  28b51eb45704 (draft): A0
--- a/tests/test-exchange-D3.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-exchange-D3.t	Tue Jun 23 15:32:47 2015 -0700
@@ -39,9 +39,9 @@
   $ mkcommit A1
   $ hg debugobsolete `getid 'desc(A0)'` `getid 'desc(A1)'`
   $ hg prune -d '0 0' .
-  1 changesets pruned
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at 35b183996678
+  1 changesets pruned
   $ hg strip --hidden -q 'desc(A1)'
   $ hg log -G --hidden
   @  35b183996678 (draft): B
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-inhibit.t	Tue Jun 23 15:32:47 2015 -0700
@@ -0,0 +1,757 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > logtemplate = {rev}:{node|short} {desc}\n
+  > [experimental]
+  > prunestrip=True
+  > evolution=createmarkers
+  > [extensions]
+  > rebase=
+  > strip=
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+  $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH
+  $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+
+  $ hg init inhibit
+  $ cd inhibit
+  $ mkcommit cA
+  $ mkcommit cB
+  $ mkcommit cC
+  $ mkcommit cD
+  $ hg up 'desc(cA)'
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ mkcommit cE
+  created new head
+  $ mkcommit cG
+  $ mkcommit cH
+  $ mkcommit cJ
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  3:2db36d8066ff add cD
+  | |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+
+plain prune
+
+  $ hg strip 1::
+  3 changesets pruned
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg debugobsinhibit --hidden 1::
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  3:2db36d8066ff add cD
+  | |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg strip --hidden 1::
+  3 changesets pruned
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+
+after amend
+
+  $ echo babar > cJ
+  $ hg commit --amend
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg debugobsinhibit --hidden 18214586bf78
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+
+and no divergence
+
+  $ hg summary
+  parent: 9:55c73a90e4b4 tip
+   add cJ
+  branch: default
+  commit: (clean)
+  update: 1 new changesets, 2 branch heads (merge)
+  phases: 6 draft
+
+check public revision got cleared
+(when adding the second inhibitor, the first one is removed because it is public)
+
+  $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//'
+  20 .hg/store/obsinhibit
+  $ hg strip 7
+  1 changesets pruned
+  $ hg debugobsinhibit --hidden 18214586bf78
+  $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//'
+  20 .hg/store/obsinhibit
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg phase --public 7
+  $ hg strip 9
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory now at cf5c4f4554ce
+  1 changesets pruned
+  $ hg log -G
+  o  7:18214586bf78 add cJ
+  |
+  @  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg debugobsinhibit --hidden 55c73a90e4b4
+  $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//'
+  20 .hg/store/obsinhibit
+  $ hg log -G
+  o  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  @  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+Update should inhibit all related unstable commits
+
+  $ hg update 2 --hidden
+  2 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ hg log -G
+  o  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | @  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+
+  $ hg update 9
+  4 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg strip --hidden 1::
+  3 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+
+Bookmark should inhibit all related unstable commits
+  $ hg bookmark -r 2 book1  --hidden
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+
+Removing a bookmark with bookmark -D should prune the changes underneath
+that are not reachable from another bookmark or head
+
+  $ hg bookmark -r 1 book2
+  $ hg bookmark -D book1  --config experimental.evolution=createmarkers #--config to make sure prune is not registered as a command.
+  bookmark 'book1' deleted
+  1 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg bookmark -D book2
+  bookmark 'book2' deleted
+  1 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+Test that direct access make changesets visible
+
+  $ hg export 2db36d8066ff 02bcbc3f6e56
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82
+  # Parent  7df62a38b9bf9daf968de235043ba88a8ef43393
+  add cD
+  
+  diff -r 7df62a38b9bf -r 2db36d8066ff cD
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/cD	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +cD
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 02bcbc3f6e56fb2928efec2c6e24472720bf5511
+  # Parent  54ccbc537fc2d6845a5d61337c1cfb80d1d2815e
+  add cB
+  
+  diff -r 54ccbc537fc2 -r 02bcbc3f6e56 cB
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/cB	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +cB
+
+But only with hash
+
+  $ hg export 2db36d8066ff::
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82
+  # Parent  7df62a38b9bf9daf968de235043ba88a8ef43393
+  add cD
+  
+  diff -r 7df62a38b9bf -r 2db36d8066ff cD
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/cD	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +cD
+
+  $ hg export 1 3
+  abort: hidden revision '1'!
+  (use --hidden to access hidden revisions)
+  [255]
+
+
+With severals hidden sha, rebase of one hidden stack onto another one:
+  $ hg update -C 0
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ mkcommit cK
+  created new head
+  $ mkcommit cL
+  $ hg update -C 9
+  4 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -G
+  o  11:53a94305e133 add cL
+  |
+  o  10:ad78ff7d621f add cK
+  |
+  | @  9:55c73a90e4b4 add cJ
+  | |
+  | | o  7:18214586bf78 add cJ
+  | |/
+  | o  6:cf5c4f4554ce add cH
+  | |
+  | o  5:5419eb264a33 add cG
+  | |
+  | o  4:98065434e5c6 add cE
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg strip --hidden 10:
+  2 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg rebase -s 10 -d 3 
+  abort: hidden revision '3'!
+  (use --hidden to access hidden revisions)
+  [255]
+  $ hg rebase -r ad78ff7d621f -r 53a94305e133 -d  2db36d8066ff
+  Warning: accessing hidden changesets 2db36d8066ff for write operation
+  Warning: accessing hidden changesets ad78ff7d621f for write operation
+  Warning: accessing hidden changesets 53a94305e133 for write operation
+  rebasing 10:ad78ff7d621f "add cK"
+  rebasing 11:53a94305e133 "add cL"
+  $ hg log -G
+  o  13:2f7b7704d714 add cL
+  |
+  o  12:fe1634cbe235 add cK
+  |
+  | @  9:55c73a90e4b4 add cJ
+  | |
+  | | o  7:18214586bf78 add cJ
+  | |/
+  | o  6:cf5c4f4554ce add cH
+  | |
+  | o  5:5419eb264a33 add cG
+  | |
+  | o  4:98065434e5c6 add cE
+  | |
+  o |  3:2db36d8066ff add cD
+  | |
+  o |  2:7df62a38b9bf add cC
+  | |
+  o |  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+Check that amending in the middle of a stack does not show obsolete revs
+Since we are doing operation in the middle of the stack we cannot just
+have createmarkers as we are creating instability
+
+  $ cat >> $HGRCPATH <<EOF
+  > [experimental]
+  > evolution=all
+  > EOF
+
+  $ hg strip --hidden 1::
+  5 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg up 7
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ mkcommit cL
+  $ mkcommit cM
+  $ mkcommit cN
+  $ hg log -G
+  @  16:a438c045eb37 add cN
+  |
+  o  15:2d66e189f5b5 add cM
+  |
+  o  14:d66ccb8c5871 add cL
+  |
+  | o  9:55c73a90e4b4 add cJ
+  | |
+  o |  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg up 15
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "mmm" >> cM
+  $ hg amend
+  $ hg log -G
+  @  18:210589181b14 add cM
+  |
+  | o  16:a438c045eb37 add cN
+  | |
+  | o  15:2d66e189f5b5 add cM
+  |/
+  o  14:d66ccb8c5871 add cL
+  |
+  | o  9:55c73a90e4b4 add cJ
+  | |
+  o |  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+Check that rebasing a commit twice makes the commit visible again
+
+  $ hg rebase -d 18 -r 16 --keep
+  rebasing 16:a438c045eb37 "add cN"
+  $ hg log -r 14:: -G
+  o  19:104eed5354c7 add cN
+  |
+  @  18:210589181b14 add cM
+  |
+  | o  16:a438c045eb37 add cN
+  | |
+  | o  15:2d66e189f5b5 add cM
+  |/
+  o  14:d66ccb8c5871 add cL
+  |
+  $ hg strip -r 104eed5354c7
+  1 changesets pruned
+  $ hg rebase -d 18 -r 16 --keep
+  rebasing 16:a438c045eb37 "add cN"
+  $ hg log -r 14:: -G
+  o  19:104eed5354c7 add cN
+  |
+  @  18:210589181b14 add cM
+  |
+  | o  16:a438c045eb37 add cN
+  | |
+  | o  15:2d66e189f5b5 add cM
+  |/
+  o  14:d66ccb8c5871 add cL
+  |
+
+Test prunestrip
+
+  $ hg book foo -r 104eed5354c7
+  $ hg strip -r 210589181b14
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory now at d66ccb8c5871
+  2 changesets pruned
+  $ hg log -r 14:: -G -T '{rev}:{node|short} {desc|firstline} {bookmarks}\n'
+  o  16:a438c045eb37 add cN
+  |
+  o  15:2d66e189f5b5 add cM
+  |
+  @  14:d66ccb8c5871 add cL foo
+  |
+
+Check that --hidden used with inhibit does not hide every obsolete commit
+We show the log before and after a log -G --hidden, they should be the same
+  $ hg log -G
+  o  16:a438c045eb37 add cN
+  |
+  o  15:2d66e189f5b5 add cM
+  |
+  @  14:d66ccb8c5871 add cL
+  |
+  | o  9:55c73a90e4b4 add cJ
+  | |
+  o |  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg log -G --hidden
+  x  19:104eed5354c7 add cN
+  |
+  x  18:210589181b14 add cM
+  |
+  | x  17:b3c3274523f9 temporary amend commit for 2d66e189f5b5
+  | |
+  | | o  16:a438c045eb37 add cN
+  | |/
+  | o  15:2d66e189f5b5 add cM
+  |/
+  @  14:d66ccb8c5871 add cL
+  |
+  | x  13:2f7b7704d714 add cL
+  | |
+  | x  12:fe1634cbe235 add cK
+  | |
+  | | x  11:53a94305e133 add cL
+  | | |
+  | | x  10:ad78ff7d621f add cK
+  | | |
+  | | | o  9:55c73a90e4b4 add cJ
+  | | | |
+  +-------x  8:e84f73d9ad36 temporary amend commit for 18214586bf78
+  | | | |
+  o-----+  7:18214586bf78 add cJ
+   / / /
+  | | o  6:cf5c4f4554ce add cH
+  | | |
+  | | o  5:5419eb264a33 add cG
+  | | |
+  | | o  4:98065434e5c6 add cE
+  | |/
+  x |  3:2db36d8066ff add cD
+  | |
+  x |  2:7df62a38b9bf add cC
+  | |
+  x |  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+
+  $ hg log -G
+  o  16:a438c045eb37 add cN
+  |
+  o  15:2d66e189f5b5 add cM
+  |
+  @  14:d66ccb8c5871 add cL
+  |
+  | o  9:55c73a90e4b4 add cJ
+  | |
+  o |  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+ 
+check that pruning and inhibited node does not confuse anything
+
+  $ hg up --hidden 210589181b14
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg strip --bundle 210589181b14
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/inhibit/.hg/strip-backup/210589181b14-e09c7b88-backup.hg (glob)
+  $ hg unbundle .hg/strip-backup/210589181b14-e09c7b88-backup.hg # restore state
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 1 changes to 2 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+
+ Only allow direct access and check that evolve works like before
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > inhibit=!
+  > EOF
+
+  $ hg up 15
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory parent is obsolete!
+  $ echo "CM" > cM
+  $ hg amend
+  $ hg log -G
+  @  21:721c3c279519 add cM
+  |
+  | o  16:a438c045eb37 add cN
+  | |
+  | x  15:2d66e189f5b5 add cM
+  |/
+  o  14:d66ccb8c5871 add cL
+  |
+  o  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > EOF
+  $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH
+
+Empty commit
+  $ hg amend
+  nothing changed
+  [1]
+
+Directaccess should load after some extensions precised in the conf
+With no extension specified:
+
+  $ cat >$TESTTMP/test_extension.py  << EOF
+  > from mercurial import extensions
+  > def uisetup(ui):
+  >   print extensions._order
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > testextension=$TESTTMP/test_extension.py
+  > EOF
+  $ hg id
+  ['rebase', 'strip', 'evolve', 'directaccess', 'inhibit', 'testextension']
+  721c3c279519 tip
+
+With test_extension specified:
+  $ cat >> $HGRCPATH << EOF
+  > [directaccess]
+  > loadsafter=testextension
+  > EOF
+  $ hg id
+  ['rebase', 'strip', 'evolve', 'inhibit', 'testextension', 'directaccess']
+  721c3c279519 tip
+
+Inhibit should not work without directaccess
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > directaccess=!
+  > testextension=!
+  > EOF
+  $ hg up 15
+  abort: Cannot use inhibit without the direct access extension
+  [255]
+  $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH
+  $ cd ..
+
+
+hg push should not allow directaccess unless forced with --hidden
+We copy the inhibhit repo to inhibit2 and make some changes to push to inhibit
+
+  $ cp -r inhibit inhibit2
+  $ pwd=$(pwd)
+  $ cd inhibit
+  $ mkcommit pk
+  $ hg id
+  003a4735afde tip
+  $ echo "OO" > pk
+  $ hg amend
+  $ hg id
+  71eb4f100663 tip
+
+Hidden commits cannot be pushed without --hidden
+  $ hg push -r 003a4735afde file://$pwd/inhibit2
+  pushing to file://$TESTTMP/inhibit2
+  abort: hidden revision '003a4735afde'!
+  (use --hidden to access hidden revisions)
+  [255]
+
+Visible commits can still be pushed
+  $ hg push -r 71eb4f100663 file://$pwd/inhibit2
+  pushing to file://$TESTTMP/inhibit2
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
--- a/tests/test-obsolete.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-obsolete.t	Tue Jun 23 15:32:47 2015 -0700
@@ -183,8 +183,7 @@
   adding manifests
   adding file changes
   added 5 changesets with 5 changes to 5 files (+1 heads)
-  pushing 2 obsolescence markers (* bytes) (glob)
-  2 obsolescence markers added
+  2 new obsolescence markers
   $ hg -R ../other-new verify
   checking changesets
   checking manifests
@@ -238,8 +237,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pushing 3 obsolescence markers (* bytes) (glob)
-  1 obsolescence markers added
+  1 new obsolescence markers
   $ qlog -R ../other-new
   5
   - 95de7fc6918d
@@ -261,8 +259,6 @@
   pushing to ../other-new
   searching for changes
   no changes found
-  pushing 3 obsolescence markers (* bytes) (glob)
-  0 obsolescence markers added
   [1]
 
   $ hg up --hidden -q .^ # 3
@@ -278,9 +274,8 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re)
-  pull obsolescence markers
-  1 obsolescence markers added
-  (run 'hg heads' to see heads, 'hg merge' to merge)
+  1 new obsolescence markers
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
   $ qlog -R ../other-new
   6
   - 909a0fb57e5d
@@ -369,9 +364,8 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re)
-  pull obsolescence markers
-  1 obsolescence markers added
-  (run 'hg heads' to see heads, 'hg merge' to merge)
+  1 new obsolescence markers
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
 
   $ hg up -q 7 # to check rollback update behavior
   $ qlog
@@ -394,6 +388,7 @@
   branch: default
   commit: 1 deleted, 2 unknown (clean)
   update: 2 new changesets, 2 branch heads (merge)
+  phases: 4 draft
   unstable: 1 changesets
   $ qlog
   6
@@ -543,8 +538,7 @@
   adding manifests
   adding file changes
   added 2 changesets with 1 changes to [12] files (re)
-  pushing 7 obsolescence markers (* bytes) (glob)
-  3 obsolescence markers added
+  3 new obsolescence markers
   $ hg up -q 10
   $ mkcommit "obsol_d'''"
   created new head
@@ -556,8 +550,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pushing 8 obsolescence markers (* bytes) (glob)
-  1 obsolescence markers added
+  1 new obsolescence markers
   $ cd ..
 
 check bumped detection
@@ -669,6 +662,7 @@
   branch: default
   commit: (clean)
   update: (2|9|11) new changesets, (3|9|10) branch heads \(merge\) (re)
+  phases: 3 draft
   bumped: 1 changesets
   $ hg debugobsolete `getid a7a6f2b5d8a5` `getid 50f11e5e3a63`
   $ hg log -r 'divergent()'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-options.t	Tue Jun 23 15:32:47 2015 -0700
@@ -0,0 +1,30 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > logtemplate={rev}:{node|short}[{bookmarks}] ({obsolete}/{phase}) {desc|firstline}\n
+  > [extensions]
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+
+  $ hg init repo
+  $ cd repo
+  $ mkcommit a
+  $ mkcommit b
+
+test disabling commands
+
+  $ cat >> .hg/hgrc <<EOF
+  > [experimental]
+  > evolution=createmarkers
+  >   allowunstable
+  >   exchange
+  > EOF
+  $ hg prune | head -n 2
+  hg: unknown command 'prune'
+  Mercurial Distributed SCM
+  
--- a/tests/test-prune.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-prune.t	Tue Jun 23 15:32:47 2015 -0700
@@ -38,9 +38,9 @@
 prune current and tip changeset
 
   $ hg prune --user blah --date '1979-12-15' .
-  1 changesets pruned
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at 47d2a3944de8
+  1 changesets pruned
   $ hg bookmark
    * BABAR                     3:47d2a3944de8
   $ hg debugobsolete
@@ -59,9 +59,9 @@
 pruning multiple changeset at once
 
   $ hg prune 2:
-  2 changesets pruned
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
   working directory now at 1f0dee641bb7
+  2 changesets pruned
   $ hg debugobsolete
   9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'}
   7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob)
@@ -120,9 +120,9 @@
   $ hg up 'desc("add ee")'
   4 files updated, 0 files merged, 4 files removed, 0 files unresolved
   $ hg prune 'desc("add ee")' -s 'desc("add nE")'
-  1 changesets pruned
   4 files updated, 0 files merged, 4 files removed, 0 files unresolved
   working directory now at 6e8148413dd5
+  1 changesets pruned
   $ hg debugobsolete
   9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'}
   7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob)
@@ -210,9 +210,9 @@
   $ mkcommit n2
 
   $ hg prune 'desc("add n1")::desc("add n2")' -s 'desc("add nD")::desc("add nE")' --biject
-  2 changesets pruned
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   working directory now at 1f0dee641bb7
+  2 changesets pruned
   $ hg debugobsolete
   9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'}
   7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob)
@@ -225,6 +225,35 @@
   cb7f8f706a6532967b98cf8583a81baab79a0fa7 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 (*) {'user': 'test'} (glob)
   21b6f2f1cece8c10326e575dd38239189d467190 6e8148413dd541855b72a920a90c06fca127c7e7 0 (*) {'user': 'test'} (glob)
 
+test hg strip replacement
+
+  $ hg up 10
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ mkcommit n1
+  created new head
+  $ mkcommit n2
+  $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r .
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory now at c7e58696a948
+  1 changesets pruned
+  $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r . --bundle
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/c7e58696a948-69ca36d3-backup.hg (glob)
+
+test hg prune --keep
+  $ mkcommit n1
+  created new head
+  $ hg diff -r .^
+  diff -r aa96dc3f04c2 n1
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/n1	* +0000 (glob)
+  @@ -0,0 +1,1 @@
+  +n1
+  $ hg prune -r . --keep
+  1 changesets pruned
+  $ hg status
+  ? n1
+
 test hg prune -B bookmark
 yoinked from test-mq-strip.t
 
@@ -245,11 +274,11 @@
   [255]
   $ hg tag --remove --local a
   $ hg prune -B todelete
-  1 changesets pruned
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (leaving bookmark todelete)
   working directory now at d62d843c9a01
   bookmark 'todelete' deleted
+  1 changesets pruned
   $ hg id -ir dcbb326fdec2
   abort: hidden revision 'dcbb326fdec2'!
   (use --hidden to access hidden revisions)
@@ -260,8 +289,8 @@
      B                         10:ff43616e5d0f
      delete                    6:2702dd0c91e7
   $ hg prune -B delete
+  bookmark 'delete' deleted
   3 changesets pruned
-  bookmark 'delete' deleted
   $ hg tag --remove --local c
   $ hg id -ir 6:2702dd0c91e7
   abort: hidden revision '6'!
@@ -332,3 +361,12 @@
   |/
   o  0:1ea73414a91b[] (stable/draft) r0
   
+  $ hg book CELESTE
+  $ hg prune -r . --keep
+  1 changesets pruned
+  $ hg book
+     B                         8:d62d843c9a01
+   * CELESTE                   8:d62d843c9a01
+     r10                       8:d62d843c9a01
+     rg                        8:d62d843c9a01
+
--- a/tests/test-sharing.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-sharing.t	Tue Jun 23 15:32:47 2015 -0700
@@ -46,7 +46,6 @@
   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
@@ -88,8 +87,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pull obsolescence markers
-  2 obsolescence markers added
+  2 new obsolescence markers
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Figure SG03
@@ -140,8 +138,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pushing 4 obsolescence markers (* bytes) (glob)
-  4 obsolescence markers added
+  4 new obsolescence markers
 
 Now that the fix is public, we cannot amend it any more.
   $ hg amend -m 'fix bug 37'
@@ -161,8 +158,6 @@
   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()'
 
@@ -196,8 +191,6 @@
   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
@@ -213,8 +206,7 @@
   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
+  2 new obsolescence markers
   updating bookmark bug15
   $ hg -R ../review bookmarks
      bug15                     3:cbdfbd5a5db2
@@ -241,8 +233,6 @@
   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
@@ -259,8 +249,7 @@
   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
+  2 new obsolescence markers
   updating bookmark featureX
 
 Bob receives second review, amends, and pushes to public:
@@ -274,8 +263,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pushing 8 obsolescence markers (* bytes) (glob)
-  4 obsolescence markers added
+  4 new obsolescence markers
   $ hg -R ../public bookmarks
   no bookmarks set
   $ hg push ../review
@@ -286,8 +274,7 @@
   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
+  2 new obsolescence markers
   updating bookmark featureX
   $ hg -R ../review bookmarks
      bug15                     3:cbdfbd5a5db2
@@ -357,8 +344,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pull obsolescence markers
-  4 obsolescence markers added
+  4 new obsolescence markers
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg log -G -q -r 'head()'
   o  5:540ba8f317e6
@@ -388,8 +374,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pushing 11 obsolescence markers (* bytes) (glob)
-  3 obsolescence markers added
+  3 new obsolescence markers
   $ hg push ../review
   pushing to ../review
   searching for changes
@@ -397,8 +382,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 0 changes to 1 files
-  pushing 11 obsolescence markers (* bytes) (glob)
-  1 obsolescence markers added
+  1 new obsolescence markers
   updating bookmark bug15
 
 Figure SG08: review and public changesets after Alice pushes.
@@ -460,8 +444,6 @@
   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)'
@@ -489,8 +471,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pull obsolescence markers
-  2 obsolescence markers added
+  2 new obsolescence markers
   (run 'hg heads' to see heads, 'hg merge' to merge)
   2 new divergent changesets
 
@@ -511,7 +492,7 @@
   7:e3f99ce9d9cd  draft  fix bug 24 (v2 by alice)
 
 Use evolve to fix the divergence.
-  $ HGMERGE=internal:other hg evolve
+  $ HGMERGE=internal:other hg evolve --divergent
   merge:[6] fix bug 24 (v2 by bob)
   with: [7] fix bug 24 (v2 by alice)
   base: [4] fix bug 24 (v1)
--- a/tests/test-simple4server-bundle2.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-simple4server-bundle2.t	Tue Jun 23 15:32:47 2015 -0700
@@ -72,9 +72,9 @@
 ===================
 
   $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon
+  capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob)
   $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol)
+  * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
 
   $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
@@ -134,13 +134,13 @@
   obsolete	
   phases	
   $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon
+  capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob)
   $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol)
+  * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
 
   $ echo '[__temporary__]' >> server/.hg/hgrc
   $ echo 'advertiseobsolete=False' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
@@ -148,13 +148,9 @@
   bookmarks	
   namespaces	
   phases	
-  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
-  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol)
 
   $ echo 'advertiseobsolete=True' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
@@ -163,7 +159,8 @@
   namespaces	
   obsolete	
   phases	
+
   $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon
+  capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob)
   $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol)
+  * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
--- a/tests/test-simple4server.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-simple4server.t	Tue Jun 23 15:32:47 2015 -0700
@@ -7,6 +7,8 @@
   > allow_push = *
   > [phases]
   > publish = False
+  > [experimental]
+  > bundle2-exp=False
   > [extensions]
   > EOF
 
@@ -72,9 +74,9 @@
 ===================
 
   $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon
+  capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob)
   $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol)
+  * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
 
   $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
@@ -135,13 +137,13 @@
   obsolete	
   phases	
   $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon
+  capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob)
   $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol)
+  * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
 
   $ echo '[__temporary__]' >> server/.hg/hgrc
   $ echo 'advertiseobsolete=False' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
@@ -149,13 +151,13 @@
   bookmarks	
   namespaces	
   phases	
-  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
-  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol)
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello | grep _evoext_pushobsmarkers_0
+  [1]
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities | grep _evoext_pushobsmarkers_0
+  [1]
 
   $ echo 'advertiseobsolete=True' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
@@ -165,6 +167,6 @@
   obsolete	
   phases	
   $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
-  capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon
+  capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob)
   $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol)
+  * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
--- a/tests/test-stabilize-order.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-stabilize-order.t	Tue Jun 23 15:32:47 2015 -0700
@@ -153,8 +153,8 @@
   $ hg up 9
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg evolve -v
-  nothing to evolve here
-  (1 troubled changesets, do you want --any ?)
+  nothing to evolve on current working copy parent
+  (1 other unstable in the repository, do you want --any or --rev)
   [2]
   $ hg evolve --any -v
   move:[9] addc
@@ -180,5 +180,68 @@
   o  0:c471ef929e6a@default(draft) addroot
   
   $ hg evolve --any -v
-  no troubled changesets
+  no unstable changesets to evolve
   [1]
+
+Ambiguous evolution
+  $ echo a > k
+  $ hg add k
+  $ hg ci -m firstambiguous
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo a > l
+  $ hg add l
+  $ hg ci -m secondambiguous
+  created new head
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg commit --amend -m "newmessage"
+  2 new unstable changesets
+  $ hg log -G
+  @  changeset:   15:49773ccde390
+  |  tag:         tip
+  |  parent:      11:036cf654e942
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     newmessage
+  |
+  | o  changeset:   14:a9892777b519
+  | |  parent:      12:e99ecf51c867
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     secondambiguous
+  | |
+  | | o  changeset:   13:0b6e26b2472d
+  | |/   user:        test
+  | |    date:        Thu Jan 01 00:00:00 1970 +0000
+  | |    summary:     firstambiguous
+  | |
+  | x  changeset:   12:e99ecf51c867
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     addc
+  |
+  o  changeset:   11:036cf654e942
+  |  parent:      7:005fe5914f78
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     addb
+  |
+  o  changeset:   7:005fe5914f78
+  |  parent:      0:c471ef929e6a
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     adda
+  |
+  o  changeset:   0:c471ef929e6a
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     addroot
+  
+  $ hg evolve
+  abort: multiple evolve candidates
+  (select one of *, * with --rev) (glob)
+  [255]
+
+
+
--- a/tests/test-stabilize-result.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-stabilize-result.t	Tue Jun 23 15:32:47 2015 -0700
@@ -158,20 +158,20 @@
 
 Stabilize!
 
-  $ hg evolve --any --dry-run
+  $ hg evolve --any --dry-run --bumped
   recreate:[12] newer a
   atop:[8] newer a
   hg rebase --rev (73b15c7566e9|d5c7ef82d003) --dest 66719795a494; (re)
   hg update 1cf0aacfd363;
   hg revert --all --rev (73b15c7566e9|d5c7ef82d003); (re)
   hg commit --msg "bumped update to %s" (no-eol)
-  $ hg evolve --any --confirm
+  $ hg evolve --any --confirm --bumped
   recreate:[12] newer a
   atop:[8] newer a
   perform evolve? [Ny] n
   abort: evolve aborted by user
   [255]
-  $ echo y | hg evolve --any --confirm --config ui.interactive=True
+  $ echo y | hg evolve --any --confirm --config ui.interactive=True --bumped
   recreate:[12] newer a
   atop:[8] newer a
   perform evolve? [Ny] y
@@ -248,14 +248,14 @@
 
 Stabilize it
 
-  $ hg evolve -qn --confirm
+  $ hg evolve -qn --confirm --divergent
   merge:[19] More addition
   with: [17] More addition
   base: [15] More addition
   perform evolve? [Ny] n
   abort: evolve aborted by user
   [255]
-  $ echo y | hg evolve -qn --confirm --config ui.interactive=True
+  $ echo y | hg evolve -qn --confirm --config ui.interactive=True --divergent
   merge:[19] More addition
   with: [17] More addition
   base: [15] More addition
@@ -266,7 +266,7 @@
   hg up -C 3932c176bbaa &&
   hg revert --all --rev tip &&
   hg commit -m "`hg log -r eacc9c8240fe --template={desc}`";
-  $ hg evolve -v
+  $ hg evolve -v --divergent
   merge:[19] More addition
   with: [17] More addition
   base: [15] More addition
@@ -306,6 +306,7 @@
   branch: default
   commit: (clean)
   update: 2 new changesets, 2 branch heads (merge)
+  phases: 3 draft
   $ hg export .
   # HG changeset patch
   # User test
@@ -343,14 +344,14 @@
   $ hg phase 'divergent()'
   21: draft
   24: draft
-  $ hg evolve -qn
+  $ hg evolve -qn --divergent
   hg update -c 0b336205a5d0 &&
   hg merge f344982e63c4 &&
   hg commit -m "auto merge resolving conflict between 0b336205a5d0 and f344982e63c4"&&
   hg up -C 3932c176bbaa &&
   hg revert --all --rev tip &&
   hg commit -m "`hg log -r 0b336205a5d0 --template={desc}`";
-  $ hg evolve
+  $ hg evolve --divergent
   merge:[24] More addition (2)
   with: [21] More addition
   base: [15] More addition
--- a/tests/test-tutorial.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-tutorial.t	Tue Jun 23 15:32:47 2015 -0700
@@ -224,7 +224,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pull obsolescence markers
   (run 'hg heads' to see heads, 'hg merge' to merge)
 
 I now have a new heads. Note that this remote head is immutable
@@ -289,9 +288,9 @@
 not fit well in my standard shopping list)
 
   $ hg prune . # "." is for working directory parent
-  1 changesets pruned
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory now at 41aff6a42b75
+  1 changesets pruned
 
 The silly changeset is gone.
 
@@ -406,8 +405,7 @@
   adding manifests
   adding file changes
   added 3 changesets with 3 changes to 1 files
-  pushing 6 obsolescence markers (* bytes) (glob)
-  6 obsolescence markers added
+  6 new obsolescence markers
 
 for simplicity sake we get the bathroom change in line again
 
@@ -528,8 +526,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pull obsolescence markers
-  1 obsolescence markers added
+  1 new obsolescence markers
   (run 'hg update' to get a working copy)
   $ hg log -G
   o  75954b8cd933 (public): bathroom stuff
@@ -586,8 +583,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pull obsolescence markers
-  1 obsolescence markers added
+  1 new obsolescence markers
   (run 'hg update' to get a working copy)
   $ hg log -G
   o  75954b8cd933 (draft): bathroom stuff
@@ -647,8 +643,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  pull obsolescence markers
-  0 obsolescence markers added
   (run 'hg heads' to see heads, 'hg merge' to merge)
   1 new unstable changesets
 
@@ -738,8 +732,7 @@
   adding manifests
   adding file changes
   added 2 changesets with 2 changes to 1 files (+1 heads)
-  pushing 10 obsolescence markers (* bytes) (glob)
-  3 obsolescence markers added
+  3 new obsolescence markers
 
 remote get a warning that current working directory is based on an obsolete changeset
 
@@ -748,8 +741,6 @@
   pulling from $TESTTMP/local (glob)
   searching for changes
   no changes found
-  pull obsolescence markers
-  0 obsolescence markers added
   working directory parent is obsolete!
 
 now let's see where we are, and update to the successor
@@ -780,8 +771,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  pull obsolescence markers
-  0 obsolescence markers added
   (run 'hg update' to get a working copy)
   $ hg log -G
   o  99f039c5ec9e (draft): SPAM SPAM SPAM
@@ -804,9 +793,9 @@
 In the mean time I noticed you can't buy animals in a super market and I prune the animal changeset:
 
   $ hg prune ee942144f952
-  1 changesets pruned
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory now at a44c85f957d3
+  1 changesets pruned
   1 new unstable changesets
 
 
--- a/tests/test-uncommit.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-uncommit.t	Tue Jun 23 15:32:47 2015 -0700
@@ -138,7 +138,6 @@
 
   $ hg branch foo
   marked working directory as branch foo
-  (branches are permanent and global, did you want a bookmark?)
   $ hg mv ff f
   $ hg mv h i
   $ hg rm j
--- a/tests/test-userguide.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-userguide.t	Tue Jun 23 15:32:47 2015 -0700
@@ -66,9 +66,9 @@
   $ echo 'debug hack' >> file1.c
   $ hg commit -m 'debug hack'
   $ hg prune .
-  1 changesets pruned
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory now at 934359450037
+  1 changesets pruned
   $ hg parents --template '{rev}:{node|short}  {desc|firstline}\n'
   3:934359450037  implement feature Y
   $ hg --hidden shortlog -G -r 3:
@@ -219,7 +219,7 @@
   |
   o  18:1f33e68b18b9  draft  useful work
   |
-  $ hg evolve -q --all
+  $ hg evolve -q --all --any
   $ hg --hidden shortlog -G -r 18::
   @  21:4393e5877437  draft  more work
   |
@@ -251,7 +251,7 @@
   $ hg status
   M file2.c
   $ hg revert file2.c
-  $ hg evolve --all
+  $ hg evolve --all --any
   move:[23] fix bug 67
   atop:[24] fix bug 53
   working directory is now at 0d972d6888e6
@@ -300,7 +300,7 @@
   |/
   o  25:0d972d6888e6  draft  fix bug 67
   |
-  $ hg evolve --all
+  $ hg evolve --all --any
   move:[27] new feature
   atop:[28] fix a bug
   working directory is now at 166c1c368ab6
--- a/tests/test-wireproto-bundle1.t	Tue Jun 23 15:32:15 2015 -0700
+++ b/tests/test-wireproto-bundle1.t	Tue Jun 23 15:32:47 2015 -0700
@@ -50,7 +50,6 @@
   adding manifests
   adding file changes
   added 2 changesets with 2 changes to 2 files
-  pull obsolescence markers
   (run 'hg update' to get a working copy)
   $ hg push -R ../other
   pushing to ssh://user@dummy/server
@@ -70,8 +69,7 @@
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files (+1 heads)
-  pushing 2 obsolescence markers (* bytes) (glob)
-  remote: 2 obsolescence markers added
+  remote: 2 new obsolescence markers
   $ hg push
   pushing to ssh://user@dummy/server
   searching for changes
@@ -88,9 +86,8 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re)
-  pull obsolescence markers
-  2 obsolescence markers added
-  (run 'hg heads' to see heads)
+  2 new obsolescence markers
+  (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg -R ../other pull
   pulling from ssh://user@dummy/server
   searching for changes