hgext/evolve.py
branchmercurial-3.9
changeset 2110 f1ffd093ef30
parent 1816 bb665c99562a
parent 2109 90ab79764ce4
child 2111 ec04eb4d2c6e
child 2261 3e339f6717c7
--- a/hgext/evolve.py	Tue Feb 28 17:04:58 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4187 +0,0 @@
-# Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
-#                Logilab SA        <contact@logilab.fr>
-#                Pierre-Yves David <pierre-yves.david@ens-lyon.org>
-#                Patrick Mezard <patrick@mezard.eu>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-'''extends Mercurial feature related to Changeset Evolution
-
-This extension provides several commands to mutate history and deal with
-resulting issues.
-
-It also:
-
-    - enables the "Changeset Obsolescence" feature of Mercurial,
-    - alters core commands and extensions that rewrite history to use
-      this feature,
-    - improves some aspect of the early implementation in Mercurial core
-'''
-
-__version__ = '5.6.0'
-testedwith = '3.4.3 3.5.2 3.6.2 3.7.3 3.8.1 3.9 4.0 4.1'
-buglink = 'https://bz.mercurial-scm.org/'
-
-
-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:`help 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 enabled 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
-try:
-    import StringIO as io
-    StringIO = io.StringIO
-except ImportError:
-    import io
-    StringIO = io.StringIO
-import re
-import collections
-import socket
-import errno
-import hashlib
-import struct
-sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
-
-import mercurial
-from mercurial import util
-from mercurial import repair
-
-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)
-except (ImportError, AttributeError):
-    gboptslist = gboptsmap = None
-
-# Flags for enabling optional parts of evolve
-commandopt = 'allnewcommands'
-
-from mercurial import bookmarks as bookmarksmod
-from mercurial import cmdutil
-from mercurial import commands
-from mercurial import context
-from mercurial import copies
-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
-from mercurial import merge
-from mercurial import node
-from mercurial import phases
-from mercurial import patch
-from mercurial import revset
-from mercurial import scmutil
-from mercurial import templatekw
-from mercurial.i18n import _
-from mercurial.commands import walkopts, commitopts, commitopts2, mergetoolopts
-from mercurial.node import nullid
-from mercurial import wireproto
-from mercurial import localrepo
-from mercurial.hgweb import hgweb_mod
-
-cmdtable = {}
-command = cmdutil.command(cmdtable)
-
-_pack = struct.pack
-_unpack = struct.unpack
-
-if gboptsmap is not None:
-    memfilectx = context.memfilectx
-elif gboptslist is not None:
-    oldmemfilectx = context.memfilectx
-    def memfilectx(repo, *args, **kwargs):
-        return oldmemfilectx(*args, **kwargs)
-else:
-    raise ImportError('evolve needs version %s or above' %
-                      min(testedwith.split()))
-
-aliases, entry = cmdutil.findcmd('commit', commands.table)
-hasinteractivemode = any(['interactive' in e for e in entry[1]])
-if hasinteractivemode:
-    interactiveopt = [['i', 'interactive', None, _('use interactive mode')]]
-else:
-    interactiveopt = []
-# This extension contains the following code
-#
-# - Extension Helper code
-# - Obsolescence cache
-# - ...
-# - Older format compat
-
-
-#####################################################################
-### Extension helper                                              ###
-#####################################################################
-
-class exthelper(object):
-    """Helper for modular extension setup
-
-    A single helper should be instantiated for each extension. Helper
-    methods are then used as decorators for various purpose.
-
-    All decorators return the original function and may be chained.
-    """
-
-    def __init__(self):
-        self._uicallables = []
-        self._extcallables = []
-        self._repocallables = []
-        self._revsetsymbols = []
-        self._templatekws = []
-        self._commandwrappers = []
-        self._extcommandwrappers = []
-        self._functionwrappers = []
-        self._duckpunchers = []
-
-    def final_uisetup(self, ui):
-        """Method to be used as the extension uisetup
-
-        The following operations belong here:
-
-        - Changes to ui.__class__ . The ui object that will be used to run the
-          command has not yet been created. Changes made here will affect ui
-          objects created after this, and in particular the ui that will be
-          passed to runcommand
-        - Command wraps (extensions.wrapcommand)
-        - Changes that need to be visible to other extensions: because
-          initialization occurs in phases (all extensions run uisetup, then all
-          run extsetup), a change made here will be visible to other extensions
-          during extsetup
-        - Monkeypatch or wrap function (extensions.wrapfunction) of dispatch
-          module members
-        - Setup of pre-* and post-* hooks
-        - pushkey setup
-        """
-        for cont, funcname, func in self._duckpunchers:
-            setattr(cont, funcname, func)
-        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:
-            c(ui)
-
-    def final_extsetup(self, ui):
-        """Method to be used as a the extension extsetup
-
-        The following operations belong here:
-
-        - Changes depending on the status of other extensions. (if
-          extensions.find('mq'))
-        - Add a global option to all commands
-        - Register revset functions
-        """
-        knownexts = {}
-        for name, symbol in self._revsetsymbols:
-            revset.symbols[name] = symbol
-        for name, kw in self._templatekws:
-            templatekw.keywords[name] = kw
-        for ext, command, wrapper, opts in self._extcommandwrappers:
-            if ext not in knownexts:
-                try:
-                    e = extensions.find(ext)
-                except KeyError:
-                    # Extension isn't enabled, so don't bother trying to wrap
-                    # it.
-                    continue
-                knownexts[ext] = e.cmdtable
-            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)
-
-    def final_reposetup(self, ui, repo):
-        """Method to be used as the extension reposetup
-
-        The following operations belong here:
-
-        - All hooks but pre-* and post-*
-        - Modify configuration variables
-        - Changes to repo.__class__, repo.dirstate.__class__
-        """
-        for c in self._repocallables:
-            c(ui, repo)
-
-    def uisetup(self, call):
-        """Decorated function will be executed during uisetup
-
-        example::
-
-            @eh.uisetup
-            def setupbabar(ui):
-                print 'this is uisetup!'
-        """
-        self._uicallables.append(call)
-        return call
-
-    def extsetup(self, call):
-        """Decorated function will be executed during extsetup
-
-        example::
-
-            @eh.extsetup
-            def setupcelestine(ui):
-                print 'this is extsetup!'
-        """
-        self._extcallables.append(call)
-        return call
-
-    def reposetup(self, call):
-        """Decorated function will be executed during reposetup
-
-        example::
-
-            @eh.reposetup
-            def setupzephir(ui, repo):
-                print 'this is reposetup!'
-        """
-        self._repocallables.append(call)
-        return call
-
-    def revset(self, symbolname):
-        """Decorated function is a revset symbol
-
-        The name of the symbol must be given as the decorator argument.
-        The symbol is added during `extsetup`.
-
-        example::
-
-            @eh.revset('hidden')
-            def revsetbabar(repo, subset, x):
-                args = revset.getargs(x, 0, 0, 'babar accept no argument')
-                return [r for r in subset if 'babar' in repo[r].description()]
-        """
-        def dec(symbol):
-            self._revsetsymbols.append((symbolname, symbol))
-            return symbol
-        return dec
-
-
-    def templatekw(self, keywordname):
-        """Decorated function is a template keyword
-
-        The name of the keyword must be given as the decorator argument.
-        The symbol is added during `extsetup`.
-
-        example::
-
-            @eh.templatekw('babar')
-            def kwbabar(ctx):
-                return 'babar'
-        """
-        def dec(keyword):
-            self._templatekws.append((keywordname, keyword))
-            return keyword
-        return dec
-
-    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.
-        The wrapping is installed during `uisetup`.
-
-        If the second option `extension` argument is provided, the wrapping
-        will be applied in the extension commandtable. This argument must be a
-        string that will be searched using `extension.find` if not found and
-        Abort error is raised. If the wrapping applies to an extension, it is
-        installed during `extsetup`.
-
-        example::
-
-            @eh.wrapcommand('summary')
-            def wrapsummary(orig, ui, repo, *args, **kwargs):
-                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, opts))
-            else:
-                self._extcommandwrappers.append((extension, command, wrapper,
-                    opts))
-            return wrapper
-        return dec
-
-    def wrapfunction(self, container, funcname):
-        """Decorated function is a function wrapper
-
-        This function takes two arguments, the container and the name of the
-        function to wrap. The wrapping is performed during `uisetup`.
-        (there is no extension support)
-
-        example::
-
-            @eh.function(discovery, 'checkheads')
-            def wrapfunction(orig, *args, **kwargs):
-                ui.note('His head smashed in and his heart cut out')
-                return orig(*args, **kwargs)
-        """
-        def dec(wrapper):
-            self._functionwrappers.append((container, funcname, wrapper))
-            return wrapper
-        return dec
-
-    def addattr(self, container, funcname):
-        """Decorated function is to be added to the container
-
-        This function takes two arguments, the container and the name of the
-        function to wrap. The wrapping is performed during `uisetup`.
-
-        example::
-
-            @eh.function(context.changectx, 'babar')
-            def babar(ctx):
-                return 'babar' in ctx.description
-        """
-        def dec(func):
-            self._duckpunchers.append((container, funcname, func))
-            return func
-        return dec
-
-eh = exthelper()
-uisetup = eh.final_uisetup
-extsetup = eh.final_extsetup
-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, 'evolve')
-
-@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.
-    evolvecommands = ui.configlist('experimental', 'evolutioncommands')
-    evolveopts = ui.configlist('experimental', 'evolution')
-    if evolveopts and (commandopt not in evolveopts and
-                       'all' not in evolveopts):
-        # We build whitelist containing the commands we want to enable
-        whitelist = set()
-        for cmd in evolvecommands:
-            matchingevolvecommands = [e for e in cmdtable.keys() if cmd in e]
-            if not matchingevolvecommands:
-                raise error.Abort(_('unknown command: %s') % cmd)
-            elif len(matchingevolvecommands) > 1:
-                msg = _('ambiguous command specification: "%s" matches %r')
-                raise error.Abort(msg % (cmd, matchingevolvecommands))
-            else:
-                whitelist.add(matchingevolvecommands[0])
-        for disabledcmd in set(cmdtable) - whitelist:
-            del cmdtable[disabledcmd]
-
-#####################################################################
-### experimental behavior                                         ###
-#####################################################################
-
-commitopts3 = [
-    ('D', 'current-date', None,
-     _('record the current date as commit date')),
-    ('U', 'current-user', None,
-     _('record the current user as committer')),
-]
-
-def _resolveoptions(ui, opts):
-    """modify commit options dict to handle related options
-
-    For now, all it does is figure out the commit date: respect -D unless
-    -d was supplied.
-    """
-    # N.B. this is extremely similar to setupheaderopts() in mq.py
-    if not opts.get('date') and opts.get('current_date'):
-        opts['date'] = '%d %d' % util.makedate()
-    if not opts.get('user') and opts.get('current_user'):
-        opts['user'] = ui.username()
-
-getrevs = obsolete.getrevs
-
-#####################################################################
-### Additional Utilities                                          ###
-#####################################################################
-
-# This section contains a lot of small utility function and method
-
-# - Function to create markers
-# - useful alias pstatus and pdiff (should probably go in evolve)
-# - "troubles" method on changectx
-# - function to travel through the obsolescence graph
-# - function to find useful changeset to stabilize
-
-
-### Useful alias
-
-@eh.uisetup
-def _installalias(ui):
-    if ui.config('alias', 'pstatus', None) is None:
-        ui.setconfig('alias', 'pstatus', 'status --rev .^', 'evolve')
-    if ui.config('alias', 'pdiff', None) is None:
-        ui.setconfig('alias', 'pdiff', 'diff --rev .^', 'evolve')
-    if ui.config('alias', 'olog', None) is None:
-        ui.setconfig('alias', 'olog', "log -r 'precursors(.)' --hidden",
-                     'evolve')
-    if ui.config('alias', 'odiff', None) is None:
-        ui.setconfig('alias', 'odiff',
-            "diff --hidden --rev 'limit(precursors(.),1)' --rev .",
-            'evolve')
-    if ui.config('alias', 'grab', None) is None:
-        if os.name == 'nt':
-            ui.setconfig('alias', 'grab',
-                "! " + util.hgexecutable() + " rebase --dest . --rev $@ && "
-                 + util.hgexecutable() + " up tip",
-                         'evolve')
-        else:
-            ui.setconfig('alias', 'grab',
-                "! $HG rebase --dest . --rev $@ && $HG up tip",
-                         'evolve')
-
-
-### Troubled revset symbol
-
-@eh.revset('troubled')
-def revsettroubled(repo, subset, x):
-    """``troubled()``
-    Changesets with troubles.
-    """
-    revset.getargs(x, 0, 0, 'troubled takes no arguments')
-    troubled = set()
-    troubled.update(getrevs(repo, 'unstable'))
-    troubled.update(getrevs(repo, 'bumped'))
-    troubled.update(getrevs(repo, 'divergent'))
-    troubled = revset.baseset(troubled)
-    troubled.sort() # set is non-ordered, enforce order
-    return subset & troubled
-
-### Obsolescence graph
-
-# XXX SOME MAJOR CLEAN UP TO DO HERE XXX
-
-def _precursors(repo, s):
-    """Precursor of a changeset"""
-    cs = set()
-    nm = repo.changelog.nodemap
-    markerbysubj = repo.obsstore.precursors
-    node = repo.changelog.node
-    for r in s:
-        for p in markerbysubj.get(node(r), ()):
-            pr = nm.get(p[0])
-            if pr is not None:
-                cs.add(pr)
-    cs -= repo.changelog.filteredrevs # nodemap has no filtering
-    return cs
-
-def _allprecursors(repo, s):  # XXX we need a better naming
-    """transitive precursors of a subset"""
-    node = repo.changelog.node
-    toproceed = [node(r) for r in s]
-    seen = set()
-    allsubjects = repo.obsstore.precursors
-    while toproceed:
-        nc = toproceed.pop()
-        for mark in allsubjects.get(nc, ()):
-            np = mark[0]
-            if np not in seen:
-                seen.add(np)
-                toproceed.append(np)
-    nm = repo.changelog.nodemap
-    cs = set()
-    for p in seen:
-        pr = nm.get(p)
-        if pr is not None:
-            cs.add(pr)
-    cs -= repo.changelog.filteredrevs # nodemap has no filtering
-    return cs
-
-def _successors(repo, s):
-    """Successors of a changeset"""
-    cs = set()
-    node = repo.changelog.node
-    nm = repo.changelog.nodemap
-    markerbyobj = repo.obsstore.successors
-    for r in s:
-        for p in markerbyobj.get(node(r), ()):
-            for sub in p[1]:
-                sr = nm.get(sub)
-                if sr is not None:
-                    cs.add(sr)
-    cs -= repo.changelog.filteredrevs # nodemap has no filtering
-    return cs
-
-def _allsuccessors(repo, s, haltonflags=0):  # XXX we need a better naming
-    """transitive successors of a subset
-
-    haltonflags allows to provide flags which prevent the evaluation of a
-    marker.  """
-    node = repo.changelog.node
-    toproceed = [node(r) for r in s]
-    seen = set()
-    allobjects = repo.obsstore.successors
-    while toproceed:
-        nc = toproceed.pop()
-        for mark in allobjects.get(nc, ()):
-            if mark[2] & haltonflags:
-                continue
-            for sub in mark[1]:
-                if sub == nullid:
-                    continue # should not be here!
-                if sub not in seen:
-                    seen.add(sub)
-                    toproceed.append(sub)
-    nm = repo.changelog.nodemap
-    cs = set()
-    for s in seen:
-        sr = nm.get(s)
-        if sr is not None:
-            cs.add(sr)
-    cs -= repo.changelog.filteredrevs # nodemap has no filtering
-    return cs
-
-
-
-
-#####################################################################
-### Extending revset and template                                 ###
-#####################################################################
-
-# this section add several useful revset symbol not yet in core.
-# they are subject to changes
-
-
-### XXX I'm not sure this revset is useful
-@eh.revset('suspended')
-def revsetsuspended(repo, subset, x):
-    """``suspended()``
-    Obsolete changesets with non-obsolete descendants.
-    """
-    revset.getargs(x, 0, 0, 'suspended takes no arguments')
-    suspended = revset.baseset(getrevs(repo, 'suspended'))
-    suspended.sort()
-    return subset & suspended
-
-
-@eh.revset('precursors')
-def revsetprecursors(repo, subset, x):
-    """``precursors(set)``
-    Immediate precursors of changesets in set.
-    """
-    s = revset.getset(repo, revset.fullreposet(repo), x)
-    s = revset.baseset(_precursors(repo, s))
-    s.sort()
-    return subset & s
-
-
-@eh.revset('allprecursors')
-def revsetallprecursors(repo, subset, x):
-    """``allprecursors(set)``
-    Transitive precursors of changesets in set.
-    """
-    s = revset.getset(repo, revset.fullreposet(repo), x)
-    s = revset.baseset(_allprecursors(repo, s))
-    s.sort()
-    return subset & s
-
-
-@eh.revset('successors')
-def revsetsuccessors(repo, subset, x):
-    """``successors(set)``
-    Immediate successors of changesets in set.
-    """
-    s = revset.getset(repo, revset.fullreposet(repo), x)
-    s = revset.baseset(_successors(repo, s))
-    s.sort()
-    return subset & s
-
-@eh.revset('allsuccessors')
-def revsetallsuccessors(repo, subset, x):
-    """``allsuccessors(set)``
-    Transitive successors of changesets in set.
-    """
-    s = revset.getset(repo, revset.fullreposet(repo), x)
-    s = revset.baseset(_allsuccessors(repo, s))
-    s.sort()
-    return subset & s
-
-### template keywords
-# XXX it does not handle troubles well :-/
-
-@eh.templatekw('obsolete')
-def obsoletekw(repo, ctx, templ, **args):
-    """:obsolete: String. Whether the changeset is ``obsolete``.
-    """
-    if ctx.obsolete():
-        return 'obsolete'
-    return ''
-
-@eh.templatekw('troubles')
-def showtroubles(repo, ctx, **args):
-    """:troubles: List of strings. Evolution troubles affecting the changeset
-    (zero or more of "unstable", "divergent" or "bumped")."""
-    return templatekw.showlist('trouble', ctx.troubles(), plural='troubles',
-                               **args)
-
-#####################################################################
-### Various trouble warning                                       ###
-#####################################################################
-
-# This section take care of issue warning to the user when troubles appear
-
-
-def _warnobsoletewc(ui, repo):
-    if repo['.'].obsolete():
-        ui.warn(_('working directory parent is obsolete!\n'))
-        if (not ui.quiet) and obsolete.isenabled(repo, commandopt):
-            ui.warn(_("(use 'hg evolve' to update to its successor)\n"))
-
-@eh.wrapcommand("update")
-@eh.wrapcommand("pull")
-def wrapmayobsoletewc(origfn, ui, repo, *args, **opts):
-    """Warn that the working directory parent is an obsolete changeset"""
-    def warnobsolete():
-        _warnobsoletewc(ui, repo)
-    wlock = None
-    try:
-        wlock = repo.wlock()
-        repo._afterlock(warnobsolete)
-        res = origfn(ui, repo, *args, **opts)
-    finally:
-        lockmod.release(wlock)
-    return res
-
-@eh.wrapcommand("parents")
-def wrapparents(origfn, ui, repo, *args, **opts):
-    res = origfn(ui, repo, *args, **opts)
-    _warnobsoletewc(ui, repo)
-    return res
-
-# XXX this could wrap transaction code
-# XXX (but this is a bit a layer violation)
-@eh.wrapcommand("commit")
-@eh.wrapcommand("import")
-@eh.wrapcommand("push")
-@eh.wrapcommand("pull")
-@eh.wrapcommand("graft")
-@eh.wrapcommand("phase")
-@eh.wrapcommand("unbundle")
-def warnobserrors(orig, ui, repo, *args, **kwargs):
-    """display warning is the command resulted in more instable changeset"""
-    # part of the troubled stuff may be filtered (stash ?)
-    # This needs a better implementation but will probably wait for core.
-    filtered = repo.changelog.filteredrevs
-    priorunstables = len(set(getrevs(repo, 'unstable')) - filtered)
-    priorbumpeds = len(set(getrevs(repo, 'bumped')) - filtered)
-    priordivergents = len(set(getrevs(repo, 'divergent')) - filtered)
-    ret = orig(ui, repo, *args, **kwargs)
-    # workaround phase stupidity
-    #phases._filterunknown(ui, repo.changelog, repo._phasecache.phaseroots)
-    filtered = repo.changelog.filteredrevs
-    newunstables = \
-        len(set(getrevs(repo, 'unstable')) - filtered) - priorunstables
-    newbumpeds = \
-        len(set(getrevs(repo, 'bumped')) - filtered) - priorbumpeds
-    newdivergents = \
-        len(set(getrevs(repo, 'divergent')) - filtered) - priordivergents
-    if newunstables > 0:
-        ui.warn(_('%i new unstable changesets\n') % newunstables)
-    if newbumpeds > 0:
-        ui.warn(_('%i new bumped changesets\n') % newbumpeds)
-    if newdivergents > 0:
-        ui.warn(_('%i new divergent changesets\n') % newdivergents)
-    return ret
-
-@eh.wrapfunction(mercurial.exchange, 'push')
-def push(orig, repo, *args, **opts):
-    """Add a hint for "hg evolve" when troubles make push fails
-    """
-    try:
-        return orig(repo, *args, **opts)
-    except error.Abort as ex:
-        hint = _("use 'hg evolve' to get a stable history "
-                 "or --force to ignore warnings")
-        if (len(ex.args) >= 1
-            and ex.args[0].startswith('push includes ')
-            and ex.hint is None):
-            ex.hint = hint
-        raise
-
-def summaryhook(ui, repo):
-    def write(fmt, count):
-        s = fmt % count
-        if count:
-            ui.write(s)
-        else:
-            ui.note(s)
-
-    # util.versiontuple was introduced in 3.6.2
-    if not util.safehasattr(util, 'versiontuple'):
-        nbunstable = len(getrevs(repo, 'unstable'))
-        nbbumped = len(getrevs(repo, 'bumped'))
-        nbdivergent = len(getrevs(repo, 'divergent'))
-        write('unstable: %i changesets\n', nbunstable)
-        write('bumped: %i changesets\n', nbbumped)
-        write('divergent: %i changesets\n', nbdivergent)
-    else:
-        # In 3.6.2, summary in core gained this feature, no need to display it
-        pass
-    state = _evolvestateread(repo)
-    if state is not None:
-        # i18n: column positioning for "hg summary"
-        ui.write(_('evolve: (evolve --continue)\n'))
-
-@eh.extsetup
-def obssummarysetup(ui):
-    cmdutil.summaryhooks.add('evolve', summaryhook)
-
-
-#####################################################################
-### Core Other extension compat                                   ###
-#####################################################################
-
-
-@eh.extsetup
-def _rebasewrapping(ui):
-    # warning about more obsolete
-    try:
-        rebase = extensions.find('rebase')
-        if rebase:
-            extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors)
-    except KeyError:
-        pass # rebase not found
-    try:
-        histedit = extensions.find('histedit')
-        if histedit:
-            extensions.wrapcommand(histedit.cmdtable, 'histedit', warnobserrors)
-    except KeyError:
-        pass # histedit not found
-
-#####################################################################
-### Old Evolve extension content                                  ###
-#####################################################################
-
-# XXX need clean up and proper sorting in other section
-
-### util function
-#############################
-
-### changeset rewriting logic
-#############################
-
-def rewrite(repo, old, updates, head, newbases, commitopts):
-    """Return (nodeid, created) where nodeid is the identifier of the
-    changeset generated by the rewrite process, and created is True if
-    nodeid was actually created. If created is False, nodeid
-    references a changeset existing before the rewrite call.
-    """
-    wlock = lock = tr = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        tr = repo.transaction('rewrite')
-        if len(old.parents()) > 1: #XXX remove this unnecessary limitation.
-            raise error.Abort(_('cannot amend merge changesets'))
-        base = old.p1()
-        updatebookmarks = _bookmarksupdater(repo, old.node(), tr)
-
-        # commit a new version of the old changeset, including the update
-        # collect all files which might be affected
-        files = set(old.files())
-        for u in updates:
-            files.update(u.files())
-
-        # Recompute copies (avoid recording a -> b -> a)
-        copied = copies.pathcopies(base, head)
-
-
-        # prune files which were reverted by the updates
-        def samefile(f):
-            if f in head.manifest():
-                a = head.filectx(f)
-                if f in base.manifest():
-                    b = base.filectx(f)
-                    return (a.data() == b.data()
-                            and a.flags() == b.flags())
-                else:
-                    return False
-            else:
-                return f not in base.manifest()
-        files = [f for f in files if not samefile(f)]
-        # commit version of these files as defined by head
-        headmf = head.manifest()
-        def filectxfn(repo, ctx, path):
-            if path in headmf:
-                fctx = head[path]
-                flags = fctx.flags()
-                mctx = memfilectx(repo, fctx.path(), fctx.data(),
-                                  islink='l' in flags,
-                                  isexec='x' in flags,
-                                  copied=copied.get(path))
-                return mctx
-            return None
-
-        message = cmdutil.logmessage(repo.ui, commitopts)
-        if not message:
-            message = old.description()
-
-        user = commitopts.get('user') or old.user()
-        date = commitopts.get('date') or None # old.date()
-        extra = dict(commitopts.get('extra', old.extra()))
-        extra['branch'] = head.branch()
-
-        new = context.memctx(repo,
-                             parents=newbases,
-                             text=message,
-                             files=files,
-                             filectxfn=filectxfn,
-                             user=user,
-                             date=date,
-                             extra=extra)
-
-        if commitopts.get('edit'):
-            new._text = cmdutil.commitforceeditor(repo, new, [])
-        revcount = len(repo)
-        newid = repo.commitctx(new)
-        new = repo[newid]
-        created = len(repo) != revcount
-        updatebookmarks(newid)
-
-        tr.close()
-        return newid, created
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-class MergeFailure(error.Abort):
-    pass
-
-def relocate(repo, orig, dest, pctx=None, keepbranch=False):
-    """rewrite <rev> on dest"""
-    if orig.rev() == dest.rev():
-        raise error.Abort(_('tried to relocate a node on top of itself'),
-                         hint=_("This shouldn't happen. If you still "
-                                "need to move changesets, please do so "
-                                "manually with nothing to rebase - working "
-                                "directory parent is also destination"))
-
-    if pctx is None:
-        if len(orig.parents()) == 2:
-            raise error.Abort(_("tried to relocate a merge commit without "
-                                "specifying which parent should be moved"),
-                              hint=_("Specify the parent by passing in pctx"))
-        pctx = orig.p1()
-
-    destbookmarks = repo.nodebookmarks(dest.node())
-    nodesrc = orig.node()
-    destphase = repo[nodesrc].phase()
-    commitmsg = orig.description()
-
-    cache = {}
-    sha1s = re.findall(sha1re, commitmsg)
-    unfi = repo.unfiltered()
-    for sha1 in sha1s:
-        ctx = None
-        try:
-            ctx = unfi[sha1]
-        except error.RepoLookupError:
-            continue
-
-        if not ctx.obsolete():
-            continue
-
-        successors = obsolete.successorssets(repo, ctx.node(), cache)
-
-        # We can't make any assumptions about how to update the hash if the
-        # cset in question was split or diverged.
-        if len(successors) == 1 and len(successors[0]) == 1:
-            newsha1 = node.hex(successors[0][0])
-            commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)])
-        else:
-            repo.ui.note(_('The stale commit message reference to %s could '
-                           'not be updated\n') % sha1)
-
-    tr = repo.currenttransaction()
-    assert tr is not None
-    try:
-        r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
-        if r[-1]:  #some conflict
-            raise error.Abort(
-                    'unresolved merge conflicts (see hg help resolve)')
-        nodenew = _relocatecommit(repo, orig, commitmsg)
-    except error.Abort as exc:
-        repo.dirstate.beginparentchange()
-        repo.setparents(repo['.'].node(), nullid)
-        writedirstate(repo.dirstate, tr)
-        # fix up dirstate for copies and renames
-        copies.duplicatecopies(repo, dest.rev(), orig.p1().rev())
-        repo.dirstate.endparentchange()
-        class LocalMergeFailure(MergeFailure, exc.__class__):
-            pass
-        exc.__class__ = LocalMergeFailure
-        tr.close() # to keep changes in this transaction (e.g. dirstate)
-        raise
-    oldbookmarks = repo.nodebookmarks(nodesrc)
-    _finalizerelocate(repo, orig, dest, nodenew, tr)
-    return nodenew
-
-def _bookmarksupdater(repo, oldid, tr):
-    """Return a callable update(newid) updating the current bookmark
-    and bookmarks bound to oldid to newid.
-    """
-    def updatebookmarks(newid):
-        dirty = False
-        oldbookmarks = repo.nodebookmarks(oldid)
-        if oldbookmarks:
-            for b in oldbookmarks:
-                repo._bookmarks[b] = newid
-            dirty = True
-        if dirty:
-            repo._bookmarks.recordchange(tr)
-    return updatebookmarks
-
-### bookmarks api compatibility layer ###
-def bmdeactivate(repo):
-    try:
-        return bookmarksmod.deactivate(repo)
-    except AttributeError:
-        return bookmarksmod.unsetcurrent(repo)
-def bmactivate(repo, book):
-    try:
-        return bookmarksmod.activate(repo, book)
-    except AttributeError:
-        return bookmarksmod.setcurrent(repo, book)
-
-def bmactive(repo):
-    try:
-        return repo._activebookmark
-    except AttributeError:
-        return repo._bookmarkcurrent
-
-### dirstate compatibility layer < hg 3.6
-
-def writedirstate(dirstate, tr):
-    if dirstate.write.func_code.co_argcount != 1: # mercurial 3.6 and above
-        return dirstate.write(tr)
-    return dirstate.write()
-
-
-
-### new command
-#############################
-metadataopts = [
-    ('d', 'date', '',
-     _('record the specified date in metadata'), _('DATE')),
-    ('u', 'user', '',
-     _('record the specified user in metadata'), _('USER')),
-]
-
-@eh.uisetup
-def _installimportobsolete(ui):
-    entry = cmdutil.findcmd('import', commands.table)[1]
-    entry[1].append(('', 'obsolete', False,
-                    _('mark the old node as obsoleted by '
-                      'the created commit')))
-
-@eh.wrapfunction(mercurial.cmdutil, 'tryimportone')
-def tryimportone(orig, ui, repo, hunk, parents, opts, *args, **kwargs):
-    extracted = patch.extract(ui, hunk)
-    if util.safehasattr(extracted, 'get'):
-        # mercurial 3.6 return a dictionary there
-        expected = extracted.get('nodeid')
-    else:
-        expected = extracted[5]
-    if expected is not None:
-        expected = node.bin(expected)
-    oldextract = patch.extract
-    try:
-        patch.extract = lambda ui, hunk: extracted
-        ret = orig(ui, repo, hunk, parents, opts, *args, **kwargs)
-    finally:
-        patch.extract = oldextract
-    created = ret[1]
-    if (opts['obsolete'] and None not in (created, expected)
-        and created != expected):
-            tr = repo.transaction('import-obs')
-            try:
-                metadata = {'user': ui.username()}
-                repo.obsstore.create(tr, expected, (created,),
-                                     metadata=metadata)
-                tr.close()
-            finally:
-                tr.release()
-    return ret
-
-
-def _deprecatealias(oldalias, newalias):
-    '''Deprecates an alias for a command in favour of another
-
-    Creates a new entry in the command table for the old alias. It creates a
-    wrapper that has its synopsis set to show that is has been deprecated.
-    The documentation will be replace with a pointer to the new alias.
-    If a user invokes the command a deprecation warning will be printed and
-    the command of the *new* alias will be invoked.
-
-    This function is loosely based on the extensions.wrapcommand function.
-    '''
-    try:
-        aliases, entry = cmdutil.findcmd(newalias, cmdtable)
-    except error.UnknownCommand:
-        # Commands may be disabled
-        return
-    for alias, e in cmdtable.items():
-        if e is entry:
-            break
-
-    synopsis = '(DEPRECATED)'
-    if len(entry) > 2:
-        fn, opts, _syn = entry
-    else:
-        fn, opts, = entry
-    deprecationwarning = _('%s have been deprecated in favor of %s\n') % (
-        oldalias, newalias)
-    def newfn(*args, **kwargs):
-        ui = args[0]
-        ui.warn(deprecationwarning)
-        util.checksignature(fn)(*args, **kwargs)
-    newfn.__doc__  = deprecationwarning
-    cmdwrapper = command(oldalias, opts, synopsis)
-    cmdwrapper(newfn)
-
-@eh.extsetup
-def deprecatealiases(ui):
-    _deprecatealias('gup', 'next')
-    _deprecatealias('gdown', 'previous')
-
-@command('debugrecordpruneparents', [], '')
-def cmddebugrecordpruneparents(ui, repo):
-    """add parent data to prune markers when possible
-
-    This command searches the repo for prune markers without parent information.
-    If the pruned node is locally known, it creates a new marker with parent
-    data.
-    """
-    pgop = 'reading markers'
-
-    # lock from the beginning to prevent race
-    wlock = lock = tr = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        tr = repo.transaction('recordpruneparents')
-        unfi = repo.unfiltered()
-        nm = unfi.changelog.nodemap
-        store = repo.obsstore
-        pgtotal = len(store._all)
-        for idx, mark in enumerate(list(store._all)):
-            if not mark[1]:
-                rev = nm.get(mark[0])
-                if rev is not None:
-                    ctx = unfi[rev]
-                    parents = tuple(p.node() for p in ctx.parents())
-                    before = len(store._all)
-                    store.create(tr, mark[0], mark[1], mark[2], mark[3],
-                                 parents=parents)
-                    if len(store._all) - before:
-                        ui.write(_('created new markers for %i\n') % rev)
-            ui.progress(pgop, idx, total=pgtotal)
-        tr.close()
-        ui.progress(pgop, None)
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-@command('debugobsstorestat', [], '')
-def cmddebugobsstorestat(ui, repo):
-    """print statistics about obsolescence markers in the 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
-
-    store = repo.obsstore
-    unfi = repo.unfiltered()
-    nm = unfi.changelog.nodemap
-    ui.write(_('markers total:              %9i\n') % len(store._all))
-    sucscount = [0, 0 , 0, 0]
-    known = 0
-    parentsdata = 0
-    metakeys = {}
-    # node -> cluster mapping
-    #   a cluster is a (set(nodes), set(markers)) tuple
-    clustersmap = {}
-    # same data using parent information
-    pclustersmap = {}
-    for mark in store:
-        if mark[0] in nm:
-            known += 1
-        nbsucs = len(mark[1])
-        sucscount[min(nbsucs, 3)] += 1
-        meta = mark[3]
-        for key, value in meta:
-            metakeys.setdefault(key, 0)
-            metakeys[key] += 1
-        meta = dict(meta)
-        parents = [meta.get('p1'), meta.get('p2')]
-        parents = [node.bin(p) for p in parents if p is not None]
-        if parents:
-            parentsdata += 1
-        # cluster handling
-        nodes = set(mark[1])
-        nodes.add(mark[0])
-        _updateclustermap(nodes, mark, clustersmap)
-        # same with parent data
-        nodes.update(parents)
-        _updateclustermap(nodes, mark, pclustersmap)
-
-    # freezing the result
-    for c in clustersmap.values():
-        fc = (frozenset(c[0]), frozenset(c[1]))
-        for n in fc[0]:
-            clustersmap[n] = fc
-    # same with parent data
-    for c in pclustersmap.values():
-        fc = (frozenset(c[0]), frozenset(c[1]))
-        for n in fc[0]:
-            pclustersmap[n] = fc
-    ui.write(('    for known precursors:   %9i\n' % known))
-    ui.write(('    with parents data:      %9i\n' % parentsdata))
-    # successors data
-    ui.write(('markers with no successors: %9i\n' % sucscount[0]))
-    ui.write(('              1 successors: %9i\n' % sucscount[1]))
-    ui.write(('              2 successors: %9i\n' % sucscount[2]))
-    ui.write(('    more than 2 successors: %9i\n' % sucscount[3]))
-    # meta data info
-    ui.write(('    available  keys:\n'))
-    for key in sorted(metakeys):
-        ui.write(('    %15s:        %9i\n' % (key, metakeys[key])))
-
-    allclusters = list(set(clustersmap.values()))
-    allclusters.sort(key=lambda x: len(x[1]))
-    ui.write(('disconnected clusters:      %9i\n' % len(allclusters)))
-
-    ui.write('        any known node:     %9i\n'
-             % len([c for c in allclusters
-                    if [n for n in c[0] if nm.get(n) is not None]]))
-    if allclusters:
-        nbcluster = len(allclusters)
-        ui.write(('        smallest length:    %9i\n' % len(allclusters[0][1])))
-        ui.write(('        longer length:      %9i\n'
-                 % len(allclusters[-1][1])))
-        median = len(allclusters[nbcluster//2][1])
-        ui.write(('        median length:      %9i\n' % median))
-        mean = sum(len(x[1]) for x in allclusters) // nbcluster
-        ui.write(('        mean length:        %9i\n' % mean))
-    allpclusters = list(set(pclustersmap.values()))
-    allpclusters.sort(key=lambda x: len(x[1]))
-    ui.write(('    using parents data:     %9i\n' % len(allpclusters)))
-    ui.write('        any known node:     %9i\n'
-             % len([c for c in allclusters
-                    if [n for n in c[0] if nm.get(n) is not None]]))
-    if allpclusters:
-        nbcluster = len(allpclusters)
-        ui.write(('        smallest length:    %9i\n'
-                 % len(allpclusters[0][1])))
-        ui.write(('        longer length:      %9i\n'
-                 % len(allpclusters[-1][1])))
-        median = len(allpclusters[nbcluster//2][1])
-        ui.write(('        median length:      %9i\n' % median))
-        mean = sum(len(x[1]) for x in allpclusters) // nbcluster
-        ui.write(('        mean length:        %9i\n' % mean))
-
-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:
-                othertroubles = []
-                for cat in unselectedcategories:
-                    if troubled[cat]:
-                        othertroubles.append(cat)
-                if othertroubles:
-                    hint = hintmap['any+'+('+'.join(othertroubles))]
-                else:
-                    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['.'])
-
-class MultipleSuccessorsError(RuntimeError):
-    """Exception raised by _singlesuccessor when multiple successor sets exists
-
-    The object contains the list of successorssets in its 'successorssets'
-    attribute to call to easily recover.
-    """
-
-    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:
-        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 or len(newer[0]) > 1:
-        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 as exc:
-                dependencies[r] = exc.successorssets
-                continue
-            if succ in revs:
-                dependencies[r].add(succ)
-                rdependencies[succ].add(r)
-    return dependencies, rdependencies
-
-def _dedupedivergents(repo, revs):
-    """Dedupe the divergents revs in revs to get one from each group with the
-    lowest revision numbers
-    """
-    repo = repo.unfiltered()
-    res = set()
-    # To not reevaluate divergents of the same group once one is encountered
-    discarded = set()
-    for rev in revs:
-        if rev in discarded:
-            continue
-        divergent = repo[rev]
-        base, others = divergentdata(divergent)
-        othersrevs = [o.rev() for o in others]
-        res.add(min([divergent.rev()] + othersrevs))
-        discarded.update(othersrevs)
-    return res
-
-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:
-            topic = getattr(repo, 'currenttopic', '')
-            if topic:
-                revs = repo.revs('topic(%s)', topic) & revs
-            elif targetcat == 'unstable':
-                revs = _aspiringdescendant(repo,
-                                           repo.revs('(.::) - obsolete()::'))
-                revs = set(revs)
-        if targetcat == 'divergent':
-            # Pick one divergent per group of divergents
-            revs = _dedupedivergents(repo, revs)
-    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
-
-    revs is a list of unstable 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 ensures the minimal number of stabilizations, 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
-
-def divergentsets(repo, ctx):
-    """Compute sets of commits divergent with a given one"""
-    cache = {}
-    succsets = {}
-    base = {}
-    for n in obsolete.allprecursors(repo.obsstore, [ctx.node()]):
-        if n == ctx.node():
-            # a node can't be a base for divergence with itself
-            continue
-        nsuccsets = obsolete.successorssets(repo, n, cache)
-        for nsuccset in nsuccsets:
-            if ctx.node() in nsuccset:
-                # we are only interested in *other* successor sets
-                continue
-            if tuple(nsuccset) in base:
-                # we already know the latest base for this divergency
-                continue
-            base[tuple(nsuccset)] = n
-    divergence = []
-    for divset, b in base.iteritems():
-        divergence.append({
-            'divergentnodes': divset,
-            'commonprecursor': b
-        })
-
-    return divergence
-
-def _preparelistctxs(items, condition):
-    return [item.hex() for item in items if condition(item)]
-
-def _formatctx(fm, ctx):
-    fm.data(node=ctx.hex())
-    fm.data(desc=ctx.description())
-    fm.data(date=ctx.date())
-    fm.data(user=ctx.user())
-
-def listtroubles(ui, repo, troublecategories, **opts):
-    """Print all the troubles for the repo (or given revset)"""
-    troublecategories = troublecategories or ['divergent', 'unstable', 'bumped']
-    showunstable = 'unstable' in troublecategories
-    showbumped = 'bumped' in troublecategories
-    showdivergent = 'divergent' in troublecategories
-
-    revs = repo.revs('+'.join("%s()" % t for t in troublecategories))
-    if opts.get('rev'):
-        revs = revs & repo.revs(opts.get('rev'))
-
-    fm = ui.formatter('evolvelist', opts)
-    for rev in revs:
-        ctx = repo[rev]
-        unpars = _preparelistctxs(ctx.parents(), lambda p: p.unstable())
-        obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete())
-        imprecs = _preparelistctxs(repo.set("allprecursors(%n)", ctx.node()),
-                                   lambda p: not p.mutable())
-        dsets = divergentsets(repo, ctx)
-
-        fm.startitem()
-        # plain formatter section
-        hashlen, desclen = 12, 60
-        desc = ctx.description()
-        if desc:
-            desc = desc.splitlines()[0]
-        desc = (desc[:desclen] + '...') if len(desc) > desclen else desc
-        fm.plain('%s: ' % ctx.hex()[:hashlen])
-        fm.plain('%s\n' % desc)
-        fm.data(node=ctx.hex(), rev=ctx.rev(), desc=desc, phase=ctx.phasestr())
-
-        for unpar in unpars if showunstable else []:
-            fm.plain('  unstable: %s (unstable parent)\n' % unpar[:hashlen])
-        for obspar in obspars if showunstable else []:
-            fm.plain('  unstable: %s (obsolete parent)\n' % obspar[:hashlen])
-        for imprec in imprecs if showbumped else []:
-            fm.plain('  bumped: %s (immutable precursor)\n' % imprec[:hashlen])
-
-        if dsets and showdivergent:
-            for dset in dsets:
-                fm.plain('  divergent: ')
-                first = True
-                for n in dset['divergentnodes']:
-                    t = "%s (%s)" if first else " %s (%s)"
-                    first = False
-                    fm.plain(t % (node.hex(n)[:hashlen], repo[n].phasestr()))
-                comprec = node.hex(dset['commonprecursor'])[:hashlen]
-                fm.plain(" (precursor %s)\n" % comprec)
-        fm.plain("\n")
-
-        # templater-friendly section
-        _formatctx(fm, ctx)
-        troubles = []
-        for unpar in unpars:
-            troubles.append({'troubletype': 'unstable', 'sourcenode': unpar,
-                             'sourcetype': 'unstableparent'})
-        for obspar in obspars:
-            troubles.append({'troubletype': 'unstable', 'sourcenode': obspar,
-                             'sourcetype': 'obsoleteparent'})
-        for imprec in imprecs:
-            troubles.append({'troubletype': 'bumped', 'sourcenode': imprec,
-                             'sourcetype': 'immutableprecursor'})
-        for dset in dsets:
-            divnodes = [{'node': node.hex(n),
-                         'phase': repo[n].phasestr(),
-                        } for n in dset['divergentnodes']]
-            troubles.append({'troubletype': 'divergent',
-                             'commonprecursor': node.hex(dset['commonprecursor']),
-                             'divergentnodes': divnodes})
-        fm.data(troubles=troubles)
-
-    fm.end()
-
-@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')),
-    ('l', 'list', False, 'provide details on troubled changesets in the repo'),
-    ] + mergetoolopts,
-    _('[OPTIONS]...'))
-def evolve(ui, repo, **opts):
-    """solve troubled changesets in your repository
-
-    Modifying history can lead to various types of troubled changesets:
-    unstable, bumped, or divergent. The evolve command resolves your troubles
-    by executing one of the following actions:
-
-    - update working copy to a successor
-    - rebase an unstable changeset
-    - extract the desired changes from a bumped changeset
-    - fuse divergent changesets back together
-
-    If you pass no arguments, evolve works in automatic mode: it will execute a
-    single action to reduce instability related to your working copy. There are
-    two cases for this action. First, if the parent of your working copy is
-    obsolete, evolve updates to the parent's successor. Second, if the working
-    copy parent is not obsolete but has obsolete predecessors, then evolve
-    determines if there is an unstable changeset that can be rebased onto the
-    working copy parent in order to reduce instability.
-    If so, evolve rebases that changeset. If not, evolve refuses to guess your
-    intention, and gives a hint about what you might want to do next.
-
-    Any time evolve creates a changeset, it updates the working copy to the new
-    changeset. (Currently, every successful evolve operation involves an update
-    as well; this may change in future.)
-
-    Automatic mode only handles common use cases. For example, it avoids taking
-    action in the case of ambiguity, and it ignores unstable changesets that
-    are not related to your working copy.
-    It also refuses to solve bumped or divergent changesets unless you explicity
-    request such behavior (see below).
-
-    Eliminating all instability around your working copy may require multiple
-    invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively
-    select and evolve all unstable changesets that can be rebased onto the
-    working copy parent.
-    This is more powerful than successive invocations, since ``--all`` handles
-    ambiguous cases (e.g. unstable changesets with multiple children) by
-    evolving all branches.
-
-    When your repository cannot be handled by automatic mode, you might need to
-    use ``--rev`` to specify a changeset to evolve. For example, if you have
-    an unstable changeset that is not related to the working copy parent,
-    you could use ``--rev`` to evolve it. Or, if some changeset has multiple
-    unstable children, evolve in automatic mode refuses to guess which one to
-    evolve; you have to use ``--rev`` in that case.
-
-    Alternately, ``--any`` makes evolve search for the next evolvable changeset
-    regardless of whether it is related to the working copy parent.
-
-    You can supply multiple revisions to evolve multiple troubled changesets
-    in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev
-    first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are
-    ``--rev`` and ``--any``.
-
-    ``hg evolve --any --all`` is useful for cleaning up instability across all
-    branches, letting evolve figure out the appropriate order and destination.
-
-    When you have troubled changesets that are not unstable, :hg:`evolve`
-    refuses to consider them unless you specify the category of trouble you
-    wish to resolve, with ``--bumped`` or ``--divergent``. These options are
-    currently mutually exclusive with each other and with ``--unstable``
-    (the default). You can combine ``--bumped`` or ``--divergent`` with
-    ``--rev``, ``--all``, or ``--any``.
-
-    You can also use the evolve command to list the troubles affecting your
-    repository by using the --list flag. You can choose to display only some
-    categories of troubles with the --unstable, --divergent or --bumped flags.
-    """
-
-    # Options
-    listopt = opts['list']
-    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]]
-    if listopt:
-        listtroubles(ui, repo, specifiedcategories, **opts)
-        return
-
-    targetcat = 'unstable'
-    if 1 < len(specifiedcategories):
-        msg = _('cannot specify more than one trouble category to solve (yet)')
-        raise error.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 as 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 error.Abort('cannot specify both "--any" and "--continue"')
-        if allopt:
-            raise error.Abort('cannot specify both "--all" and "--continue"')
-        state = _evolvestateread(repo)
-        if state is None:
-            raise error.Abort('no evolve to continue')
-        orig = repo[state['current']]
-        # XXX This is a terrible terrible hack, please get rid of it.
-        lock = repo.wlock()
-        try:
-            repo.opener.write('graftstate', orig.hex() + '\n')
-            try:
-                graftcmd = commands.table['graft'][0]
-                ret = graftcmd(ui, repo, old_obsolete=True, **{'continue': True})
-                _evolvestatedelete(repo)
-                return ret
-            finally:
-                util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
-        finally:
-            lock.release()
-    cmdutil.bailifchanged(repo)
-
-
-    if revopt and allopt:
-        raise error.Abort('cannot specify both "--rev" and "--all"')
-    if revopt and anyopt:
-        raise error.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 an unstable changeset"""
-    pctx = orig.p1()
-    if len(orig.parents()) == 2:
-        if not pctx.obsolete():
-            pctx = orig.p2()  # second parent is obsolete ?
-        elif orig.p2().obsolete():
-            hint = _("Redo the merge (%s) and use `hg prune <old> "
-                     "--succ <new>` to obsolete the old one") % orig.hex()[:12]
-            ui.warn(_("warning: no support for evolving merge changesets "
-                      "with two obsolete parents yet\n") +
-                    _("(%s)\n") % hint)
-            return False
-
-    if not pctx.obsolete():
-        ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
-        return False
-    obs = pctx
-    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:
-        # split target, figure out which one to pick, are they all in line?
-        targetrevs = [repo[r].rev() for r in targets]
-        roots = repo.revs('roots(%ld)', targetrevs)
-        heads = repo.revs('heads(%ld)', targetrevs)
-        if len(roots) > 1 or len(heads) > 1:
-            msg = "cannot solve split accross two branches\n"
-            ui.write_err(msg)
-            return 2
-        target = repo[heads.first()]
-    else:
-        target = targets[0]
-    displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
-    target = repo[target]
-    if not ui.quiet or confirm:
-        repo.ui.write(_('move:'))
-        displayer.show(orig)
-        repo.ui.write(_('atop:'))
-        displayer.show(target)
-    if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
-            raise error.Abort(_('evolve aborted by user'))
-    if progresscb: progresscb()
-    todo = 'hg rebase -r %s -d %s\n' % (orig, target)
-    if dryrun:
-        repo.ui.write(todo)
-    else:
-        repo.ui.note(todo)
-        if progresscb: progresscb()
-        keepbranch = orig.p1().branch() != orig.branch()
-        try:
-            relocate(repo, orig, target, pctx, keepbranch)
-        except MergeFailure:
-            _evolvestatewrite(repo, {'current': orig.node()})
-            repo.ui.write_err(_('evolve failed!\n'))
-            repo.ui.write_err(
-                _("fix conflict and run 'hg evolve --continue'"
-                  " or use 'hg update -C .' to abort\n"))
-            raise
-
-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:
-        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:
-        msg = _('skipping: %s: public version is a merge, '
-                'this is 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:
-        repo.ui.write(_('recreate:'))
-        displayer.show(bumped)
-        repo.ui.write(_('atop:'))
-        displayer.show(prec)
-    if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
-        raise error.Abort(_('evolve aborted by user'))
-    if dryrun:
-        todo = 'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1())
-        repo.ui.write(todo)
-        repo.ui.write(('hg update %s;\n' % prec))
-        repo.ui.write(('hg revert --all --rev %s;\n' % bumped))
-        repo.ui.write(('hg commit --msg "bumped update to %s"'))
-        return 0
-    if progresscb: progresscb()
-    newid = tmpctx = None
-    tmpctx = bumped
-    # Basic check for common parent. Far too complicated and fragile
-    tr = repo.currenttransaction()
-    assert tr is not None
-    bmupdate = _bookmarksupdater(repo, bumped.node(), tr)
-    if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)):
-        # Need to rebase the changeset at the right place
-        repo.ui.status(
-            _('rebasing to destination parent: %s\n') % prec.p1())
-        try:
-            tmpid = relocate(repo, bumped, prec.p1())
-            if tmpid is not None:
-                tmpctx = repo[tmpid]
-                obsolete.createmarkers(repo, [(bumped, (tmpctx,))])
-        except MergeFailure:
-            repo.opener.write('graftstate', bumped.hex() + '\n')
-            repo.ui.write_err(_('evolution failed!\n'))
-            repo.ui.write_err(
-                _("fix conflict and run 'hg evolve --continue'\n"))
-            raise
-    # Create the new commit context
-    repo.ui.status(_('computing new diff\n'))
-    files = set()
-    copied = copies.pathcopies(prec, bumped)
-    precmanifest = prec.manifest().copy()
-    # 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:
-            del precmanifest[key]
-        if precvalue != val:
-            files.add(key)
-    files.update(precmanifest)  # add missing files
-    # commit it
-    if files: # something to commit!
-        def filectxfn(repo, ctx, path):
-            if path in bumped:
-                fctx = bumped[path]
-                flags = fctx.flags()
-                mctx = memfilectx(repo, fctx.path(), fctx.data(),
-                                  islink='l' in flags,
-                                  isexec='x' in flags,
-                                  copied=copied.get(path))
-                return mctx
-            return None
-        text = 'bumped update to %s:\n\n' % prec
-        text += bumped.description()
-
-        new = context.memctx(repo,
-                             parents=[prec.node(), node.nullid],
-                             text=text,
-                             files=files,
-                             filectxfn=filectxfn,
-                             user=bumped.user(),
-                             date=bumped.date(),
-                             extra=bumped.extra())
-
-        newid = repo.commitctx(new)
-    if newid is None:
-        obsolete.createmarkers(repo, [(tmpctx, ())])
-        newid = prec.node()
-    else:
-        phases.retractboundary(repo, tr, bumped.phase(), [newid])
-        obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
-                               flag=obsolete.bumpedfix)
-    bmupdate(newid)
-    repo.ui.status(_('committed as %s\n') % node.short(newid))
-    # reroute the working copy parent to the new changeset
-    repo.dirstate.beginparentchange()
-    repo.dirstate.setparents(newid, node.nullid)
-    repo.dirstate.endparentchange()
-
-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]))
-        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 len(other.parents()) > 1:
-        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():
-        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:
-        ui.write(_('merge:'))
-        displayer.show(divergent)
-        ui.write(_('with: '))
-        displayer.show(other)
-        ui.write(_('base: '))
-        displayer.show(base)
-    if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y':
-        raise error.Abort(_('evolve aborted by user'))
-    if dryrun:
-        ui.write(('hg update -c %s &&\n' % divergent))
-        ui.write(('hg merge %s &&\n' % other))
-        ui.write(('hg commit -m "auto merge resolving conflict between '
-                 '%s and %s"&&\n' % (divergent, other)))
-        ui.write(('hg up -C %s &&\n' % base))
-        ui.write(('hg revert --all --rev tip &&\n'))
-        ui.write(('hg commit -m "`hg log -r %s --template={desc}`";\n'
-                 % divergent))
-        return
-    if divergent not in repo[None].parents():
-        repo.ui.status(_('updating to "local" conflict\n'))
-        hg.update(repo, divergent.rev())
-    repo.ui.note(_('merging divergent changeset\n'))
-    if progresscb: progresscb()
-    try:
-        stats = merge.update(repo,
-                             other.node(),
-                             branchmerge=True,
-                             force=False,
-                             ancestor=base.node(),
-                             mergeancestor=True)
-    except TypeError:
-        # Mercurial  < 43c00ca887d1 (3.7)
-        stats = merge.update(repo,
-                             other.node(),
-                             branchmerge=True,
-                             force=False,
-                             partial=None,
-                             ancestor=base.node(),
-                             mergeancestor=True)
-
-    hg._showstats(repo, stats)
-    if stats[3]:
-        repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
-                         "or 'hg update -C .' to abort\n"))
-    if stats[3] > 0:
-        raise error.Abort('merge conflict between several amendments '
-            '(this is not automated yet)',
-            hint="""/!\ You can try:
-/!\ * manual merge + resolve => new cset X
-/!\ * hg up to the parent of the amended changeset (which are named W and Z)
-/!\ * hg revert --all -r X
-/!\ * hg ci -m "same message as the amended changeset" => new cset Y
-/!\ * hg prune -n Y W Z
-""")
-    if progresscb: progresscb()
-    emtpycommitallowed = repo.ui.backupconfig('ui', 'allowemptycommit')
-    tr = repo.currenttransaction()
-    assert tr is not None
-    try:
-        repo.ui.setconfig('ui', 'allowemptycommit', True, 'evolve')
-        repo.dirstate.beginparentchange()
-        repo.dirstate.setparents(divergent.node(), node.nullid)
-        repo.dirstate.endparentchange()
-        oldlen = len(repo)
-        amend(ui, repo, message='', logfile='')
-        if oldlen == len(repo):
-            new = divergent
-            # no changes
-        else:
-            new = repo['.']
-        obsolete.createmarkers(repo, [(other, (new,))])
-        phases.retractboundary(repo, tr, other.phase(), [new.node()])
-    finally:
-        repo.ui.restoreconfig(emtpycommitallowed)
-
-def divergentdata(ctx):
-    """return base, other part of a conflict
-
-    This only return the first one.
-
-    XXX this woobly function won't survive XXX
-    """
-    repo = ctx._repo.unfiltered()
-    for base in repo.set('reverse(allprecursors(%d))', ctx):
-        newer = obsolete.successorssets(ctx._repo, base.node())
-        # drop filter and solution including the original ctx
-        newer = [n for n in newer if n and ctx.node() not in n]
-        if newer:
-            return base, tuple(ctx._repo[o] for o in newer[0])
-    raise error.Abort("base of divergent changeset %s not found" % ctx,
-                     hint='this case is not yet handled')
-
-
-
-shorttemplate = '[{rev}] {desc|firstline}\n'
-
-@command('^previous',
-         [('B', 'move-bookmark', False,
-             _('move active bookmark after update')),
-          ('', 'merge', False, _('bring uncommitted change along')),
-          ('', 'no-topic', False, _('ignore topic and move topologically')),
-          ('n', 'dry-run', False,
-             _('do not perform actions, just print what would be done'))],
-         '[OPTION]...')
-def cmdprevious(ui, repo, **opts):
-    """update to parent revision
-
-    Displays the summary line of the destination for clarity."""
-    wlock = None
-    dryrunopt = opts['dry_run']
-    if not dryrunopt:
-        wlock = repo.wlock()
-    try:
-        wkctx = repo[None]
-        wparents = wkctx.parents()
-        if len(wparents) != 1:
-            raise error.Abort('merge in progress')
-        if not opts['merge']:
-            try:
-                cmdutil.bailifchanged(repo)
-            except error.Abort as exc:
-                exc.hint = _('do you want --merge?')
-                raise
-
-        parents = wparents[0].parents()
-        topic = getattr(repo, 'currenttopic', '')
-        if topic and not opts.get("no_topic", False):
-            parents = [ctx for ctx in parents if ctx.topic() == topic]
-        displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
-        if not parents:
-            ui.warn(_('no parent in topic "%s"\n') % topic)
-            ui.warn(_('(do you want --no-topic)\n'))
-        elif len(parents) == 1:
-            p = parents[0]
-            bm = bmactive(repo)
-            shouldmove = opts.get('move_bookmark') and bm is not None
-            if dryrunopt:
-                ui.write(('hg update %s;\n' % p.rev()))
-                if shouldmove:
-                    ui.write(('hg bookmark %s -r %s;\n' % (bm, p.rev())))
-            else:
-                ret = hg.update(repo, p.rev())
-                if not ret:
-                    tr = lock = None
-                    try:
-                        lock = repo.lock()
-                        tr = repo.transaction('previous')
-                        if shouldmove:
-                            repo._bookmarks[bm] = p.node()
-                            repo._bookmarks.recordchange(tr)
-                        else:
-                            bmdeactivate(repo)
-                        tr.close()
-                    finally:
-                        lockmod.release(tr, lock)
-
-            displayer.show(p)
-            return 0
-        else:
-            for p in parents:
-                displayer.show(p)
-            ui.warn(_('multiple parents, explicitly update to one\n'))
-            return 1
-    finally:
-        lockmod.release(wlock)
-
-@command('^next',
-         [('B', 'move-bookmark', False,
-             _('move active bookmark after update')),
-          ('', 'merge', False, _('bring uncommitted change along')),
-          ('', 'evolve', False, _('evolve the next changeset if necessary')),
-          ('', 'no-topic', False, _('ignore topic and move topologically')),
-          ('n', 'dry-run', False,
-              _('do not perform actions, just print what would be done'))],
-              '[OPTION]...')
-def cmdnext(ui, repo, **opts):
-    """update to next child revision
-
-    Use the ``--evolve`` flag to evolve unstable children on demand.
-
-    Displays the summary line of the destination for clarity.
-    """
-    wlock = None
-    dryrunopt = opts['dry_run']
-    if not dryrunopt:
-        wlock = repo.wlock()
-    try:
-        wkctx = repo[None]
-        wparents = wkctx.parents()
-        if len(wparents) != 1:
-            raise error.Abort('merge in progress')
-        if not opts['merge']:
-            try:
-                cmdutil.bailifchanged(repo)
-            except error.Abort as exc:
-                exc.hint = _('do you want --merge?')
-                raise
-
-        children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()]
-        topic = getattr(repo, 'currenttopic', '')
-        filtered = []
-        if topic and not opts.get("no_topic", False):
-            filtered = [ctx for ctx in children if ctx.topic() != topic]
-            # XXX N-square membership on children
-            children = [ctx for ctx in children if ctx not in filtered]
-        displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
-        if len(children) == 1:
-            c = children[0]
-            bm = bmactive(repo)
-            shouldmove = opts.get('move_bookmark') and bm is not None
-            if dryrunopt:
-                ui.write(('hg update %s;\n' % c.rev()))
-                if shouldmove:
-                    ui.write(('hg bookmark %s -r %s;\n' % (bm, c.rev())))
-            else:
-                ret = hg.update(repo, c.rev())
-                if not ret:
-                    lock = tr = None
-                    try:
-                        lock = repo.lock()
-                        tr = repo.transaction('next')
-                        if shouldmove:
-                            repo._bookmarks[bm] = c.node()
-                            repo._bookmarks.recordchange(tr)
-                        else:
-                            bmdeactivate(repo)
-                        tr.close()
-                    finally:
-                        lockmod.release(tr, lock)
-            displayer.show(c)
-            result = 0
-        elif children:
-            ui.warn(_("ambigious next changeset:\n"))
-            for c in children:
-                displayer.show(c)
-            ui.warn(_('explicitly update to one of them\n'))
-            result = 1
-        else:
-            aspchildren = _aspiringchildren(repo, [repo['.'].rev()])
-            if topic:
-                filtered.extend(repo[c] for c in children
-                                if repo[c].topic() != topic)
-                # XXX N-square membership on children
-                aspchildren = [ctx for ctx in aspchildren if ctx not in filtered]
-            if not opts['evolve'] or not aspchildren:
-                if filtered:
-                    ui.warn(_('no children on topic "%s"\n') % topic)
-                    ui.warn(_('do you want --no-topic\n'))
-                else:
-                    ui.warn(_('no children\n'))
-                if aspchildren:
-                    msg = _('(%i unstable changesets to be evolved here, '
-                            'do you want --evolve?)\n')
-                    ui.warn(msg % len(aspchildren))
-                result = 1
-            elif 1 < len(aspchildren):
-                ui.warn(_("ambigious next (unstable) changeset:\n"))
-                for c in aspchildren:
-                    displayer.show(repo[c])
-                ui.warn(_("(run 'hg evolve --rev REV' on one of them)\n"))
-                return 1
-            else:
-                cmdutil.bailifchanged(repo)
-                result = _solveone(ui, repo, repo[aspchildren[0]], dryrunopt,
-                                   False, lambda:None, category='unstable')
-                if not result:
-                    ui.status(_('working directory now at %s\n') % repo['.'])
-                return result
-            return 1
-        return result
-    finally:
-        lockmod.release(wlock)
-
-def _reachablefrombookmark(repo, revs, bookmarks):
-    """filter revisions and bookmarks reachable from the given bookmark
-    yoinked from mq.py
-    """
-    repomarks = repo._bookmarks
-    if not bookmarks.issubset(repomarks):
-        raise error.Abort(_("bookmark '%s' not found") %
-            ','.join(sorted(bookmarks - set(repomarks.keys()))))
-
-    # If the requested bookmark is not the only one pointing to a
-    # a revision we have to only delete the bookmark and not strip
-    # anything. revsets cannot detect that case.
-    nodetobookmarks = {}
-    for mark, node in repomarks.iteritems():
-        nodetobookmarks.setdefault(node, []).append(mark)
-    for marks in nodetobookmarks.values():
-        if bookmarks.issuperset(marks):
-           if util.safehasattr(repair, 'stripbmrevset'):
-               rsrevs = repair.stripbmrevset(repo, marks[0])
-           else:
-               rsrevs = repo.revs("ancestors(bookmark(%s)) - "
-                                  "ancestors(head() and not bookmark(%s)) - "
-                                  "ancestors(bookmark() and not bookmark(%s)) - "
-                                  "obsolete()",
-                                  marks[0], marks[0], marks[0])
-           revs = set(revs)
-           revs.update(set(rsrevs))
-           revs = sorted(revs)
-    return repomarks, revs
-
-def _deletebookmark(repo, repomarks, bookmarks):
-    wlock = lock = tr = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        tr = repo.transaction('prune')
-        for bookmark in bookmarks:
-            del repomarks[bookmark]
-        repomarks.recordchange(tr)
-        tr.close()
-        for bookmark in sorted(bookmarks):
-            repo.ui.write(_("bookmark '%s' deleted\n") % bookmark)
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-
-
-def _getmetadata(**opts):
-    metadata = {}
-    date = opts.get('date')
-    user = opts.get('user')
-    if date:
-        metadata['date'] = '%i %i' % util.parsedate(date)
-    if user:
-        metadata['user'] = user
-    return metadata
-
-
-@command('^prune|obsolete',
-    [('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")),
-     ('', 'fold', False,
-        _("record a fold (multiple precursors, one successors)")),
-     ('', 'split', False,
-        _("record a split (on precursor, multiple successors)")),
-     ('B', 'bookmark', [], _("remove revs only reachable from given"
-                             " bookmark"))] + metadataopts,
-    _('[OPTION] [-r] REV...'))
-    # -U  --noupdate option to prevent wc update and or bookmarks update ?
-def cmdprune(ui, repo, *revs, **opts):
-    """hide changesets by marking them obsolete
-
-    Pruned changesets are obsolete with no successors. If they also have no
-    descendants, they are hidden (invisible to all commands).
-
-    Non-obsolete descendants of pruned changesets become "unstable". Use :hg:`evolve`
-    to handle this situation.
-
-    When you prune the parent of your working copy, Mercurial updates the working
-    copy to a non-obsolete parent.
-
-    You can use ``--succ`` to tell Mercurial that a newer version (successor) of the
-    pruned changeset exists. Mercurial records successor revisions in obsolescence
-    markers.
-
-    You can use the ``--biject`` option to specify a 1-1 mapping (bijection) between
-    revisions to pruned (precursor) and successor changesets. This option may be
-    removed in a future release (with the functionality provided automatically).
-
-    If you specify multiple revisions in ``--succ``, you are recording a "split" and
-    must acknowledge it by passing ``--split``. Similarly, when you prune multiple
-    changesets with a single successor, you must pass the ``--fold`` option.
-    """
-    revs = scmutil.revrange(repo, list(revs) + opts.get('rev'))
-    succs = opts['new'] + opts['succ']
-    bookmarks = set(opts.get('bookmark'))
-    metadata = _getmetadata(**opts)
-    biject = opts.get('biject')
-    fold = opts.get('fold')
-    split = opts.get('split')
-
-    options = [o for o in ('biject', 'fold', 'split') if opts.get(o)]
-    if 1 < len(options):
-        raise error.Abort(_("can only specify one of %s") % ', '.join(options))
-
-    if bookmarks:
-        repomarks, revs = _reachablefrombookmark(repo, revs, bookmarks)
-        if not revs:
-            # no revisions to prune - delete bookmark immediately
-            _deletebookmark(repo, repomarks, bookmarks)
-
-    if not revs:
-        raise error.Abort(_('nothing to prune'))
-
-    wlock = lock = tr = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        tr = repo.transaction('prune')
-        # defines pruned changesets
-        precs = []
-        revs.sort()
-        for p in revs:
-            cp = repo[p]
-            if not cp.mutable():
-                # note: createmarkers() would have raised something anyway
-                raise error.Abort('cannot prune immutable changeset: %s' % cp,
-                                 hint="see 'hg help phases' for details")
-            precs.append(cp)
-        if not precs:
-            raise error.Abort('nothing to prune')
-
-        if _disallowednewunstable(repo, revs):
-            raise error.Abort(_("cannot prune in the middle of a stack"),
-                        hint = _("new unstable changesets are not allowed"))
-
-        # defines successors changesets
-        sucs = scmutil.revrange(repo, succs)
-        sucs.sort()
-        sucs = tuple(repo[n] for n in sucs)
-        if not biject and len(sucs) > 1 and len(precs) > 1:
-            msg = "Can't use multiple successors for multiple precursors"
-            hint = _("use --biject to mark a series as a replacement"
-                     " for another")
-            raise error.Abort(msg, hint=hint)
-        elif biject and len(sucs) != len(precs):
-            msg = "Can't use %d successors for %d precursors" \
-                % (len(sucs), len(precs))
-            raise error.Abort(msg)
-        elif (len(precs) == 1 and len(sucs) > 1) and not split:
-            msg = "please add --split if you want to do a split"
-            raise error.Abort(msg)
-        elif len(sucs) == 1 and len(precs) > 1 and not fold:
-            msg = "please add --fold if you want to do a fold"
-            raise error.Abort(msg)
-        elif biject:
-            relations = [(p, (s,)) for p, s in zip(precs, sucs)]
-        else:
-            relations = [(p, sucs) for p in precs]
-
-        wdp = repo['.']
-
-        if len(sucs) == 1 and len(precs) == 1 and wdp in precs:
-            # '.' killed, so update to the successor
-            newnode = sucs[0]
-        else:
-            # update to an unkilled parent
-            newnode = wdp
-
-            while newnode in precs or newnode.obsolete():
-                newnode = newnode.parents()[0]
-
-
-        if newnode.node() != wdp.node():
-            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)
-                writedirstate(dirstate, tr)
-            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 bookmarks
-                if movebookmark:
-                    bmdeactivate(repo)
-                    repo._bookmarks[bookactive] = newnode.node()
-                    repo._bookmarks.recordchange(tr)
-                commands.update(ui, repo, newnode.rev())
-                ui.status(_('working directory now at %s\n') % newnode)
-                if movebookmark:
-                    bmactivate(repo, bookactive)
-
-        # update bookmarks
-        if bookmarks:
-            _deletebookmark(repo, repomarks, bookmarks)
-
-        # 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:
-            #
-            #   ldest = list(repo.set('max((::%d) - obsolete())', ctx))
-            #   if ldest:
-            #      c = ldest[0]
-            #
-            # but then revset took a lazy arrow in the knee and became much
-            # slower. The new forms makes as much sense and a much faster.
-            for dest in ctx.ancestors():
-                if not dest.obsolete():
-                    updatebookmarks = _bookmarksupdater(repo, ctx.node(), tr)
-                    updatebookmarks(dest.node())
-                    break
-
-        tr.close()
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-@command('amend|refresh',
-    [('A', 'addremove', None,
-     _('mark new/missing files as added/removed before committing')),
-    ('e', 'edit', False, _('invoke editor on commit messages')),
-    ('', 'close-branch', None,
-     _('mark a branch as closed, hiding it from the branch list')),
-    ('s', 'secret', None, _('use the secret phase for committing')),
-    ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt,
-    _('[OPTION]... [FILE]...'))
-def amend(ui, repo, *pats, **opts):
-    """combine a changeset with updates and replace it with a new one
-
-    Commits a new changeset incorporating both the changes to the given files
-    and all the changes from the current parent changeset into the repository.
-
-    See :hg:`commit` for details about committing changes.
-
-    If you don't specify -m, the parent's message will be reused.
-
-    Behind the scenes, Mercurial first commits the update as a regular child
-    of the current parent. Then it creates a new commit on the parent's parents
-    with the updated contents. Then it changes the working copy parent to this
-    new combined changeset. Finally, the old changeset and its update are hidden
-    from :hg:`log` (unless you use --hidden with log).
-
-    Returns 0 on success, 1 if nothing changed.
-    """
-    opts = opts.copy()
-    edit = opts.pop('edit', False)
-    log = opts.get('logfile')
-    opts['amend'] = True
-    if not (edit or opts['message'] or log):
-        opts['message'] = repo['.'].description()
-    _resolveoptions(ui, opts)
-    _alias, commitcmd = cmdutil.findcmd('commit', commands.table)
-    return commitcmd[0](ui, repo, *pats, **opts)
-
-
-def _touchedbetween(repo, source, dest, match=None):
-    touched = set()
-    for files in repo.status(source, dest, match=match)[:3]:
-        touched.update(files)
-    return touched
-
-def _commitfiltered(repo, ctx, match, target=None):
-    """Recommit ctx with changed files not in match. Return the new
-    node identifier, or None if nothing changed.
-    """
-    base = ctx.p1()
-    if target is None:
-        target = base
-    # ctx
-    initialfiles = _touchedbetween(repo, base, ctx)
-    if base == target:
-        affected = set(f for f in initialfiles if match(f))
-        newcontent = set()
-    else:
-        affected = _touchedbetween(repo, target, ctx, match=match)
-        newcontent = _touchedbetween(repo, target, base, match=match)
-    # The commit touchs all existing files
-    # + all file that needs a new content
-    # - the file affected bny uncommit with the same content than base.
-    files = (initialfiles - affected) | newcontent
-    if not newcontent and files == initialfiles:
-        return None
-
-    # Filter copies
-    copied = copies.pathcopies(target, ctx)
-    copied = dict((dst, src) for dst, src in copied.iteritems()
-                  if dst in files)
-    def filectxfn(repo, memctx, path, contentctx=ctx, redirect=newcontent):
-        if path in redirect:
-            return filectxfn(repo, memctx, path, contentctx=target, redirect=())
-        if path not in contentctx:
-            return None
-        fctx = contentctx[path]
-        flags = fctx.flags()
-        mctx = memfilectx(repo, fctx.path(), fctx.data(),
-                          islink='l' in flags,
-                          isexec='x' in flags,
-                          copied=copied.get(path))
-        return mctx
-
-    new = context.memctx(repo,
-                         parents=[base.node(), node.nullid],
-                         text=ctx.description(),
-                         files=files,
-                         filectxfn=filectxfn,
-                         user=ctx.user(),
-                         date=ctx.date(),
-                         extra=ctx.extra())
-    # commitctx always create a new revision, no need to check
-    newid = repo.commitctx(new)
-    return newid
-
-def _uncommitdirstate(repo, oldctx, match):
-    """Fix the dirstate after switching the working directory from
-    oldctx to a copy of oldctx not containing changed files matched by
-    match.
-    """
-    ctx = repo['.']
-    ds = repo.dirstate
-    copies = dict(ds.copies())
-    m, a, r = repo.status(oldctx.p1(), oldctx, match=match)[:3]
-    for f in m:
-        if ds[f] == 'r':
-            # modified + removed -> removed
-            continue
-        ds.normallookup(f)
-
-    for f in a:
-        if ds[f] == 'r':
-            # added + removed -> unknown
-            ds.drop(f)
-        elif ds[f] != 'a':
-            ds.add(f)
-
-    for f in r:
-        if ds[f] == 'a':
-            # removed + added -> normal
-            ds.normallookup(f)
-        elif ds[f] != 'r':
-            ds.remove(f)
-
-    # Merge old parent and old working dir copies
-    oldcopies = {}
-    for f in (m + a):
-        src = oldctx[f].renamed()
-        if src:
-            oldcopies[f] = src[0]
-    oldcopies.update(copies)
-    copies = dict((dst, oldcopies.get(src, src))
-                  for dst, src in oldcopies.iteritems())
-    # Adjust the dirstate copies
-    for dst, src in copies.iteritems():
-        if (src not in ctx or dst in ctx or ds[dst] != 'a'):
-            src = None
-        ds.copy(src, dst)
-
-@command('^uncommit',
-    [('a', 'all', None, _('uncommit all changes when no arguments given')),
-     ('r', 'rev', '', _('revert commit content to REV instead')),
-     ] + commands.walkopts,
-    _('[OPTION]... [NAME]'))
-def uncommit(ui, repo, *pats, **opts):
-    """move changes from parent revision to working directory
-
-    Changes to selected files in the checked out revision appear again as
-    uncommitted changed in the working directory. A new revision
-    without the selected changes is created, becomes the checked out
-    revision, and obsoletes the previous one.
-
-    The --include option specifies patterns to uncommit.
-    The --exclude option specifies patterns to keep in the commit.
-
-    The --rev argument let you change the commit file to a content of another
-    revision. It still does not change the content of your file in the working
-    directory.
-
-    Return 0 if changed files are uncommitted.
-    """
-
-    wlock = lock = tr = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        wctx = repo[None]
-        if len(wctx.parents()) <= 0:
-            raise error.Abort(_("cannot uncommit null changeset"))
-        if len(wctx.parents()) > 1:
-            raise error.Abort(_("cannot uncommit while merging"))
-        old = repo['.']
-        if old.phase() == phases.public:
-            raise error.Abort(_("cannot rewrite immutable changeset"))
-        if len(old.parents()) > 1:
-            raise error.Abort(_("cannot uncommit merge changeset"))
-        oldphase = old.phase()
-
-
-        rev = None
-        if opts.get('rev'):
-            rev = scmutil.revsingle(repo, opts.get('rev'))
-            ctx = repo[None]
-            if ctx.p1() == rev or ctx.p2() == rev:
-                raise error.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 error.Abort(_("cannot uncommit in the middle of a stack"))
-
-        # Recommit the filtered changeset
-        tr = repo.transaction('uncommit')
-        updatebookmarks = _bookmarksupdater(repo, old.node(), tr)
-        newid = None
-        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:
-            raise error.Abort(_('nothing to uncommit'),
-                             hint=_("use --all to uncommit all files"))
-        # Move local changes on filtered changeset
-        obsolete.createmarkers(repo, [(old, (repo[newid],))])
-        phases.retractboundary(repo, tr, oldphase, [newid])
-        repo.dirstate.beginparentchange()
-        repo.dirstate.setparents(newid, node.nullid)
-        _uncommitdirstate(repo, old, match)
-        repo.dirstate.endparentchange()
-        updatebookmarks(newid)
-        if not repo[newid].files():
-            ui.warn(_("new changeset is empty\n"))
-            ui.status(_("(use 'hg prune .' to remove it)\n"))
-        tr.close()
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-@eh.wrapcommand('commit')
-def commitwrapper(orig, ui, repo, *arg, **kwargs):
-    tr = None
-    if kwargs.get('amend', False):
-        wlock = lock = None
-    else:
-        wlock = repo.wlock()
-        lock = repo.lock()
-    try:
-        obsoleted = kwargs.get('obsolete', [])
-        if obsoleted:
-            obsoleted = repo.set('%lr', obsoleted)
-        result = orig(ui, repo, *arg, **kwargs)
-        if not result: # commit succeeded
-            new = repo['-1']
-            oldbookmarks = []
-            markers = []
-            for old in obsoleted:
-                oldbookmarks.extend(repo.nodebookmarks(old.node()))
-                markers.append((old, (new,)))
-            if markers:
-                obsolete.createmarkers(repo, markers)
-            for book in oldbookmarks:
-                repo._bookmarks[book] = new.node()
-            if oldbookmarks:
-                if not wlock:
-                    wlock = repo.wlock()
-                if not lock:
-                    lock = repo.lock()
-                tr = repo.transaction('commit')
-                repo._bookmarks.recordchange(tr)
-                tr.close()
-        return result
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-@command('^split',
-    [('r', 'rev', [], _("revision to split")),
-    ] + commitopts + commitopts2,
-    _('hg split [OPTION]... [-r] REV'))
-def cmdsplit(ui, repo, *revs, **opts):
-    """split a changeset into smaller changesets
-
-    By default, split the current revision by prompting for all its hunks to be
-    redistributed into new changesets.
-
-    Use --rev to split a given changeset instead.
-    """
-    tr = wlock = lock = None
-    newcommits = []
-
-    revarg = (list(revs) + opts.get('rev')) or ['.']
-    if len(revarg) != 1:
-        msg = _("more than one revset is given")
-        hnt = _("use either `hg split <rs>` or `hg split --rev <rs>`, not both")
-        raise error.Abort(msg, hint=hnt)
-
-    rev = scmutil.revsingle(repo, revarg[0])
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        cmdutil.bailifchanged(repo)
-        tr = repo.transaction('split')
-        ctx = repo[rev]
-        r = ctx.rev()
-        disallowunstable = not obsolete.isenabled(repo,
-                                                  obsolete.allowunstableopt)
-        if disallowunstable:
-            # XXX We should check head revs
-            if repo.revs("(%d::) - %d", rev, rev):
-                raise error.Abort(_("cannot split commit: %s not a head") % ctx)
-
-        if len(ctx.parents()) > 1:
-            raise error.Abort(_("cannot split merge commits"))
-        prev = ctx.p1()
-        bmupdate = _bookmarksupdater(repo, ctx.node(), tr)
-        bookactive = bmactive(repo)
-        if bookactive is not None:
-            repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo))
-        bmdeactivate(repo)
-        hg.update(repo, prev)
-
-        commands.revert(ui, repo, rev=r, all=True)
-        def haschanges():
-            modified, added, removed, deleted = repo.status()[:4]
-            return modified or added or removed or deleted
-        msg = ("HG: This is the original pre-split commit message. "
-               "Edit it as appropriate.\n\n")
-        msg += ctx.description()
-        opts['message'] = msg
-        opts['edit'] = True
-        while haschanges():
-            pats = ()
-            cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
-                             cmdutil.recordfilter, *pats, **opts)
-            # TODO: Does no seem like the best way to do this
-            # We should make dorecord return the newly created commit
-            newcommits.append(repo['.'])
-            if haschanges():
-                if ui.prompt('Done splitting? [yN]', default='n') == 'y':
-                    commands.commit(ui, repo, **opts)
-                    newcommits.append(repo['.'])
-                    break
-            else:
-                ui.status(_("no more change to split\n"))
-
-        if newcommits:
-            tip = repo[newcommits[-1]]
-            bmupdate(tip.node())
-            if bookactive is not None:
-                bmactivate(repo, bookactive)
-            obsolete.createmarkers(repo, [(repo[r], newcommits)])
-        tr.close()
-    finally:
-        lockmod.release(tr, 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,
-      'do not mark the new revision as successor of the old one'),
-     ('A', 'allowdivergence', False,
-      'mark the new revision as successor of the old one potentially creating '
-      'divergence')],
-    # allow to choose the seed ?
-    _('[-r] revs'))
-def touch(ui, repo, *revs, **opts):
-    """create successors that are identical to their predecessors except
-    for the changeset ID
-
-    This is used to "resurrect" changesets
-    """
-    duplicate = opts['duplicate']
-    allowdivergence = opts['allowdivergence']
-    revs = list(revs)
-    revs.extend(opts['rev'])
-    if not revs:
-        revs = ['.']
-    revs = scmutil.revrange(repo, revs)
-    if not revs:
-        ui.write_err('no revision to touch\n')
-        return 1
-    if not duplicate and repo.revs('public() and %ld', revs):
-        raise error.Abort("can't touch public revision")
-    displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
-    wlock = lock = tr = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        tr = repo.transaction('touch')
-        revs.sort() # ensure parent are run first
-        newmapping = {}
-        for r in revs:
-            ctx = repo[r]
-            extra = ctx.extra().copy()
-            extra['__touch-noise__'] = random.randint(0, 0xffffffff)
-            # search for touched parent
-            p1 = ctx.p1().node()
-            p2 = ctx.p2().node()
-            p1 = newmapping.get(p1, p1)
-            p2 = newmapping.get(p2, p2)
-
-            if not (duplicate or allowdivergence):
-                # The user hasn't yet decided what to do with the revived
-                # cset, let's ask
-                sset = obsolete.successorssets(repo, ctx.node())
-                nodivergencerisk = len(sset) == 0 or (
-                                    len(sset) == 1 and
-                                    len(sset[0]) == 1 and
-                                    repo[sset[0][0]].rev() == ctx.rev()
-                                   )
-                if nodivergencerisk:
-                    duplicate = False
-                else:
-                    displayer.show(ctx)
-                    index = ui.promptchoice(
-                        _("reviving this changeset will create divergence"
-                        " unless you make a duplicate.\n(a)llow divergence or"
-                        " (d)uplicate the changeset? $$ &Allowdivergence $$ "
-                        "&Duplicate"), 0)
-                    choice = ['allowdivergence', 'duplicate'][index]
-                    if choice == 'allowdivergence':
-                        duplicate = False
-                    else:
-                        duplicate = True
-
-            new, unusedvariable = rewrite(repo, ctx, [], ctx,
-                                          [p1, p2],
-                                          commitopts={'extra': extra})
-            # store touched version to help potential children
-            newmapping[ctx.node()] = new
-
-            if not duplicate:
-                obsolete.createmarkers(repo, [(ctx, (repo[new],))])
-            phases.retractboundary(repo, tr, ctx.phase(), [new])
-            if ctx in repo[None].parents():
-                repo.dirstate.beginparentchange()
-                repo.dirstate.setparents(new, node.nullid)
-                repo.dirstate.endparentchange()
-        tr.close()
-    finally:
-        lockmod.release(tr, lock, wlock)
-
-@command('^fold|squash',
-    [('r', 'rev', [], _("revision to fold")),
-     ('', 'exact', None, _("only fold specified revisions")),
-     ('', 'from', None, _("fold revisions linearly to working copy parent"))
-    ] + commitopts + commitopts2,
-    _('hg fold [OPTION]... [-r] REV'))
-def fold(ui, repo, *revs, **opts):
-    """fold multiple revisions into a single one
-
-    With --from, folds all the revisions linearly between the given revisions
-    and the parent of the working directory.
-
-    With --exact, folds only the specified revisions while ignoring the
-    parent of the working directory. In this case, the given revisions must
-    form a linear unbroken chain.
-
-    .. container:: verbose
-
-     Some examples:
-
-     - Fold the current revision with its parent::
-
-         hg fold --from .^
-
-     - Fold all draft revisions with working directory parent::
-
-         hg fold --from 'draft()'
-
-       See :hg:`help phases` for more about draft revisions and
-       :hg:`help revsets` for more about the `draft()` keyword
-
-     - Fold revisions between 3 and 6 with the working directory parent::
-
-         hg fold --from 3::6
-
-     - Fold revisions 3 and 4:
-
-        hg fold "3 + 4" --exact
-
-     - Only fold revisions linearly between foo and @::
-
-         hg fold foo::@ --exact
-    """
-    revs = list(revs)
-    revs.extend(opts['rev'])
-    if not revs:
-        raise error.Abort(_('no revisions specified'))
-
-    revs = scmutil.revrange(repo, revs)
-
-    if opts['from'] and opts['exact']:
-        raise error.Abort(_('cannot use both --from and --exact'))
-    elif opts['from']:
-        # Try to extend given revision starting from the working directory
-        extrevs = repo.revs('(%ld::.) or (.::%ld)', revs, revs)
-        discardedrevs = [r for r in revs if r not in extrevs]
-        if discardedrevs:
-            raise error.Abort(_("cannot fold non-linear revisions"),
-                               hint=_("given revisions are unrelated to parent "
-                                      "of working directory"))
-        revs = extrevs
-    elif opts['exact']:
-        # Nothing to do; "revs" is already set correctly
-        pass
-    else:
-        raise error.Abort(_('must specify either --from or --exact'))
-
-    if not revs:
-        raise error.Abort(_('specified revisions evaluate to an empty set'),
-                          hint=_('use different revision arguments'))
-    elif len(revs) == 1:
-        ui.write_err(_('single revision specified, nothing to fold\n'))
-        return 1
-
-    wlock = lock = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-
-        root, head = _foldcheck(repo, revs)
-
-        tr = repo.transaction('fold')
-        try:
-            commitopts = opts.copy()
-            allctx = [repo[r] for r in revs]
-            targetphase = max(c.phase() for c in allctx)
-
-            if commitopts.get('message') or commitopts.get('logfile'):
-                commitopts['edit'] = False
-            else:
-                msgs = ["HG: This is a fold of %d changesets." % len(allctx)]
-                msgs += ["HG: Commit message of changeset %s.\n\n%s\n" %
-                         (c.rev(), c.description()) for c in allctx]
-                commitopts['message'] =  "\n".join(msgs)
-                commitopts['edit'] = True
-
-            newid, unusedvariable = rewrite(repo, root, allctx, head,
-                                            [root.p1().node(),
-                                             root.p2().node()],
-                                            commitopts=commitopts)
-            phases.retractboundary(repo, tr, targetphase, [newid])
-            obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                 for ctx in allctx])
-            tr.close()
-        finally:
-            tr.release()
-        ui.status('%i changesets folded\n' % len(revs))
-        if repo['.'].rev() in revs:
-            hg.update(repo, newid)
-    finally:
-        lockmod.release(lock, wlock)
-
-@command('^metaedit',
-         [('r', 'rev', [], _("revision to edit")),
-         ('', 'fold', None, _("also fold specified revisions into one")),
-         ] + commitopts + commitopts2,
-         _('hg metaedit [OPTION]... [-r] [REV]'))
-def metaedit(ui, repo, *revs, **opts):
-    """edit commit information
-
-    Edits the commit information for the specified revisions. By default, edits
-    commit information for the working directory parent.
-
-    With --fold, also folds multiple revisions into one if necessary. In this
-    case, the given revisions must form a linear unbroken chain.
-
-    .. container:: verbose
-
-     Some examples:
-
-     - Edit the commit message for the working directory parent::
-
-         hg metaedit
-
-     - Change the username for the working directory parent::
-
-         hg metaedit --user 'New User <new-email@example.com>'
-
-     - Combine all draft revisions that are ancestors of foo but not of @ into
-       one::
-
-         hg metaedit --fold 'draft() and only(foo,@)'
-
-       See :hg:`help phases` for more about draft revisions, and
-       :hg:`help revsets` for more about the `draft()` and `only()` keywords.
-    """
-    revs = list(revs)
-    revs.extend(opts['rev'])
-    if not revs:
-        if opts['fold']:
-            raise error.Abort(_('revisions must be specified with --fold'))
-        revs = ['.']
-
-    wlock = lock = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-
-        revs = scmutil.revrange(repo, revs)
-        if not opts['fold'] and len(revs) > 1:
-            # TODO: handle multiple revisions. This is somewhat tricky because
-            # if we want to edit a series of commits:
-            #
-            #   a ---- b ---- c
-            #
-            # we need to rewrite a first, then directly rewrite b on top of the
-            # new a, then rewrite c on top of the new b. So we need to handle
-            # revisions in topological order.
-            raise error.Abort(_('editing multiple revisions without --fold is '
-                                'not currently supported'))
-
-        if opts['fold']:
-            root, head = _foldcheck(repo, revs)
-        else:
-            if repo.revs("%ld and public()", revs):
-                raise error.Abort(_('cannot edit commit information for public '
-                                    'revisions'))
-            newunstable = _disallowednewunstable(repo, revs)
-            if newunstable:
-                raise error.Abort(
-                    _('cannot edit commit information in the middle of a '\
-                    'stack'), hint=_('%s will become unstable and new unstable'\
-                    ' changes are not allowed') % repo[newunstable.first()])
-            root = head = repo[revs.first()]
-
-        wctx = repo[None]
-        p1 = wctx.p1()
-        tr = repo.transaction('metaedit')
-        newp1 = None
-        try:
-            commitopts = opts.copy()
-            allctx = [repo[r] for r in revs]
-            targetphase = max(c.phase() for c in allctx)
-
-            if commitopts.get('message') or commitopts.get('logfile'):
-                commitopts['edit'] = False
-            else:
-                if opts['fold']:
-                    msgs = ["HG: This is a fold of %d changesets." % len(allctx)]
-                    msgs += ["HG: Commit message of changeset %s.\n\n%s\n" %
-                             (c.rev(), c.description()) for c in allctx]
-                else:
-                    msgs = [head.description()]
-                commitopts['message'] =  "\n".join(msgs)
-                commitopts['edit'] = True
-
-            # TODO: if the author and message are the same, don't create a new
-            # hash. Right now we create a new hash because the date can be
-            # different.
-            newid, created = rewrite(repo, root, allctx, head,
-                                     [root.p1().node(), root.p2().node()],
-                                     commitopts=commitopts)
-            if created:
-                if p1.rev() in revs:
-                    newp1 = newid
-                phases.retractboundary(repo, tr, targetphase, [newid])
-                obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                              for ctx in allctx])
-            else:
-                ui.status(_("nothing changed\n"))
-            tr.close()
-        finally:
-            tr.release()
-
-        if opts['fold']:
-            ui.status('%i changesets folded\n' % len(revs))
-        if newp1 is not None:
-            hg.update(repo, newp1)
-    finally:
-        lockmod.release(lock, wlock)
-
-def _foldcheck(repo, revs):
-    roots = repo.revs('roots(%ld)', revs)
-    if len(roots) > 1:
-        raise error.Abort(_("cannot fold non-linear revisions "
-                           "(multiple roots given)"))
-    root = repo[roots.first()]
-    if root.phase() <= phases.public:
-        raise error.Abort(_("cannot fold public revisions"))
-    heads = repo.revs('heads(%ld)', revs)
-    if len(heads) > 1:
-        raise error.Abort(_("cannot fold non-linear revisions "
-                           "(multiple heads given)"))
-    head = repo[heads.first()]
-    if _disallowednewunstable(repo, revs):
-        raise error.Abort(_("cannot fold chain not ending with a head "\
-                            "or with branching"), hint = _("new unstable"\
-                            " changesets are not allowed"))
-    return root, head
-
-def _disallowednewunstable(repo, revs):
-    allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
-    if allowunstable:
-        return revset.baseset()
-    return repo.revs("(%ld::) - %ld", revs, revs)
-
-@eh.wrapcommand('graft')
-def graftwrapper(orig, ui, repo, *revs, **kwargs):
-    kwargs = dict(kwargs)
-    revs = list(revs) + kwargs.get('rev', [])
-    kwargs['rev'] = []
-    obsoleted = kwargs.setdefault('obsolete', [])
-
-    wlock = lock = None
-    try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        if kwargs.get('old_obsolete'):
-            if kwargs.get('continue'):
-                obsoleted.extend(repo.opener.read('graftstate').splitlines())
-            else:
-                obsoleted.extend(revs)
-        # convert obsolete target into revs to avoid alias joke
-        obsoleted[:] = [str(i) for i in repo.revs('%lr', obsoleted)]
-        if obsoleted and len(revs) > 1:
-
-            raise error.Abort(_('cannot graft multiple revisions while '
-                                'obsoleting (for now).'))
-
-        return commitwrapper(orig, ui, repo,*revs, **kwargs)
-    finally:
-        lockmod.release(lock, wlock)
-
-@eh.extsetup
-def oldevolveextsetup(ui):
-    for cmd in ['prune', 'uncommit', 'touch', 'fold']:
-        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', [],
-                     _("make commit obsolete this revision (DEPRECATED)")))
-    entry = cmdutil.findcmd('graft', commands.table)[1]
-    entry[1].append(('o', 'obsolete', [],
-                     _("make graft obsoletes this revision (DEPRECATED)")))
-    entry[1].append(('O', 'old-obsolete', False,
-                     _("make graft obsoletes its source (DEPRECATED)")))
-
-#####################################################################
-### Obsolescence marker exchange experimenation                   ###
-#####################################################################
-
-def obsexcmsg(ui, message, important=False):
-    verbose = ui.configbool('experimental', 'verbose-obsolescence-exchange',
-                             False)
-    if verbose:
-        message = 'OBSEXC: ' + message
-    if important or verbose:
-        ui.status(message)
-
-def obsexcprg(ui, *args, **kwargs):
-    topic = 'obsmarkers exchange'
-    if ui.configbool('experimental', 'verbose-obsolescence-exchange', False):
-        topic = 'OBSEXC'
-    ui.progress(topic, *args, **kwargs)
-
-@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"
-                                   % 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))
-        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.isenabled(repo, obsolete.exchangeopt):
-        caps += ' _evoext_obshash_0'
-    return caps
-
-@eh.extsetup
-def _installobsmarkersdiscovery(ui):
-    hgweb_mod.perms['evoext_obshash'] = 'pull'
-    hgweb_mod.perms['evoext_obshash1'] = 'pull'
-    # wrap command content
-    oldcap, args = wireproto.commands['capabilities']
-    def newcap(repo, proto):
-        return discocapabilities(oldcap, repo, proto)
-    wireproto.commands['capabilities'] = (newcap, args)
-    wireproto.commands['evoext_obshash'] = (srv_obshash, 'nodes')
-    wireproto.commands['evoext_obshash1'] = (srv_obshash1, 'nodes')
-    if getattr(exchange, '_pushdiscoveryobsmarkers', None) is None:
-        ui.warn(_('evolve: your mercurial version is too old\n'
-                  'evolve: (running in degraded mode, push will '
-                  'includes all markers)\n'))
-    else:
-        olddisco = exchange.pushdiscoverymapping['obsmarker']
-        def newdisco(pushop):
-            _pushdiscoveryobsmarkers(olddisco, pushop)
-        exchange.pushdiscoverymapping['obsmarker'] = newdisco
-
-### Set discovery START
-
-from mercurial import dagutil
-from mercurial import setdiscovery
-
-def _obshash(repo, nodes, version=0):
-    if version == 0:
-        hashs = _obsrelsethashtreefm0(repo)
-    elif version ==1:
-        hashs = _obsrelsethashtreefm1(repo)
-    else:
-        assert False
-    nm = repo.changelog.nodemap
-    revs = [nm.get(n) for n in nodes]
-    return [r is None and nullid or hashs[r][1] for r in revs]
-
-def srv_obshash(repo, proto, nodes):
-    return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes)))
-
-def srv_obshash1(repo, proto, nodes):
-    return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes),
-                                version=1))
-
-@eh.addattr(localrepo.localpeer, 'evoext_obshash')
-def local_obshash(peer, nodes):
-    return _obshash(peer._repo, nodes)
-
-@eh.addattr(localrepo.localpeer, 'evoext_obshash1')
-def local_obshash1(peer, nodes):
-    return _obshash(peer._repo, nodes, version=1)
-
-@eh.addattr(wireproto.wirepeer, 'evoext_obshash')
-def peer_obshash(self, nodes):
-    d = self._call("evoext_obshash", nodes=wireproto.encodelist(nodes))
-    try:
-        return wireproto.decodelist(d)
-    except ValueError:
-        self._abort(error.ResponseError(_("unexpected response:"), d))
-
-@eh.addattr(wireproto.wirepeer, 'evoext_obshash1')
-def peer_obshash1(self, nodes):
-    d = self._call("evoext_obshash1", nodes=wireproto.encodelist(nodes))
-    try:
-        return wireproto.decodelist(d)
-    except ValueError:
-        self._abort(error.ResponseError(_("unexpected response:"), d))
-
-def findcommonobsmarkers(ui, local, remote, probeset,
-                         initialsamplesize=100,
-                         fullsamplesize=200):
-    # from discovery
-    roundtrips = 0
-    cl = local.changelog
-    dag = dagutil.revlogdag(cl)
-    missing = set()
-    common = set()
-    undecided = set(probeset)
-    totalnb = len(undecided)
-    ui.progress(_("comparing with other"), 0, total=totalnb)
-    _takefullsample = setdiscovery._takefullsample
-    if remote.capable('_evoext_obshash_1'):
-        getremotehash = remote.evoext_obshash1
-        localhash = _obsrelsethashtreefm1(local)
-    else:
-        getremotehash = remote.evoext_obshash
-        localhash = _obsrelsethashtreefm0(local)
-
-    while undecided:
-
-        ui.note(_("sampling from both directions\n"))
-        if len(undecided) < fullsamplesize:
-            sample = set(undecided)
-        else:
-            sample = _takefullsample(dag, undecided, size=fullsamplesize)
-
-        roundtrips += 1
-        ui.progress(_("comparing with other"), totalnb - len(undecided),
-                    total=totalnb)
-        ui.debug("query %i; still undecided: %i, sample size is: %i\n"
-                 % (roundtrips, len(undecided), len(sample)))
-        # indices between sample and externalized version must match
-        sample = list(sample)
-        remotehash = getremotehash(dag.externalizeall(sample))
-
-        yesno = [localhash[ix][1] == remotehash[si]
-                 for si, ix in enumerate(sample)]
-
-        commoninsample = set(n for i, n in enumerate(sample) if yesno[i])
-        common.update(dag.ancestorset(commoninsample, common))
-
-        missinginsample = [n for i, n in enumerate(sample) if not yesno[i]]
-        missing.update(dag.descendantset(missinginsample, missing))
-
-        undecided.difference_update(missing)
-        undecided.difference_update(common)
-
-
-    ui.progress(_("comparing with other"), None)
-    result = dag.headsetofconnecteds(common)
-    ui.debug("%d total queries\n" % roundtrips)
-
-    if not result:
-        return set([nullid])
-    return dag.externalizeall(result)
-
-
-_pushkeyescape = getattr(obsolete, '_pushkeyescape', None)
-
-class pushobsmarkerStringIO(StringIO):
-    """hacky string io for progress"""
-
-    @util.propertycache
-    def length(self):
-        return len(self.getvalue())
-
-    def read(self, size=None):
-        obsexcprg(self.ui, self.tell(), unit=_("bytes"), total=self.length)
-        return StringIO.read(self, size)
-
-    def __iter__(self):
-        d = self.read(4096)
-        while d:
-            yield d
-            d = self.read(4096)
-
-@eh.wrapfunction(exchange, '_pushobsolete')
-def _pushobsolete(orig, pushop):
-    """utility function to push obsolete markers to a remote"""
-    stepsdone = getattr(pushop, 'stepsdone', None)
-    if stepsdone is not None:
-        if 'obsmarkers' in stepsdone:
-            return
-        stepsdone.add('obsmarkers')
-    if util.safehasattr(pushop, 'cgresult'):
-        cgresult = pushop.cgresult
-    else:
-        cgresult = pushop.ret
-    if cgresult == 0:
-        return
-    pushop.ui.debug('try to push obsolete markers to remote\n')
-    repo = pushop.repo
-    remote = pushop.remote
-    if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and
-        'obsolete' in remote.listkeys('namespaces')):
-        markers = pushop.outobsmarkers
-        if not markers:
-            obsexcmsg(repo.ui, "no marker to push\n")
-        elif remote.capable('_evoext_pushobsmarkers_0'):
-            obsdata = pushobsmarkerStringIO()
-            for chunk in obsolete.encodemarkers(markers, True):
-                obsdata.write(chunk)
-            obsdata.seek(0)
-            obsdata.ui = repo.ui
-            obsexcmsg(repo.ui, "pushing %i obsolescence markers (%i bytes)\n"
-                               % (len(markers), len(obsdata.getvalue())),
-                      True)
-            remote.evoext_pushobsmarkers_0(obsdata)
-            obsexcprg(repo.ui, None)
-        else:
-            rslts = []
-            remotedata = _pushkeyescape(markers).items()
-            totalbytes = sum(len(d) for k, d in remotedata)
-            sentbytes = 0
-            obsexcmsg(repo.ui, "pushing %i obsolescence markers in %i "
-                               "pushkey payload (%i bytes)\n"
-                               % (len(markers), len(remotedata), totalbytes),
-                      True)
-            for key, data in remotedata:
-                obsexcprg(repo.ui, sentbytes, item=key, unit=_("bytes"),
-                          total=totalbytes)
-                rslts.append(remote.pushkey('obsolete', key, '', data))
-                sentbytes += len(data)
-                obsexcprg(repo.ui, sentbytes, item=key, unit=_("bytes"),
-                          total=totalbytes)
-            obsexcprg(repo.ui, None)
-            if [r for r in rslts if not r]:
-                msg = _('failed to push some obsolete markers!\n')
-                repo.ui.warn(msg)
-        obsexcmsg(repo.ui, "DONE\n")
-
-
-@eh.addattr(wireproto.wirepeer, 'evoext_pushobsmarkers_0')
-def client_pushobsmarkers(self, obsfile):
-    """wireprotocol peer method"""
-    self.requirecap('_evoext_pushobsmarkers_0',
-                    _('push obsolete markers faster'))
-    ret, output = self._callpush('evoext_pushobsmarkers_0', obsfile)
-    for l in output.splitlines(True):
-        self.ui.status(_('remote: '), l)
-    return ret
-
-@eh.addattr(httppeer.httppeer, 'evoext_pushobsmarkers_0')
-def httpclient_pushobsmarkers(self, obsfile):
-    """httpprotocol peer method
-    (Cannot simply use _callpush as http is doing some special handling)"""
-    self.requirecap('_evoext_pushobsmarkers_0',
-                    _('push obsolete markers faster'))
-    try:
-        r = self._call('evoext_pushobsmarkers_0', data=obsfile)
-        vals = r.split('\n', 1)
-        if len(vals) < 2:
-            raise error.ResponseError(_("unexpected response:"), r)
-
-        for l in vals[1].splitlines(True):
-            if l.strip():
-                self.ui.status(_('remote: '), l)
-        return vals[0]
-    except socket.error as err:
-        if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
-            raise error.Abort(_('push failed: %s') % err.args[1])
-        raise error.Abort(err.args[1])
-
-@eh.wrapfunction(localrepo.localrepository, '_restrictcapabilities')
-def local_pushobsmarker_capabilities(orig, repo, caps):
-    caps = orig(repo, caps)
-    caps.add('_evoext_pushobsmarkers_0')
-    return caps
-
-def _pushobsmarkers(repo, data):
-    tr = lock = None
-    try:
-        lock = repo.lock()
-        tr = repo.transaction('pushkey: obsolete markers')
-        new = repo.obsstore.mergemarkers(tr, data)
-        if new is not None:
-            obsexcmsg(repo.ui, "%i obsolescence markers added\n" % new, True)
-        tr.close()
-    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):
-    """small funtion returning the argument for pull markers call
-    may to contains 'heads' and 'common'. skip the key for None.
-
-    Its a separed functio to play around with strategy for that."""
-    repo = pullop.repo
-    remote = pullop.remote
-    unfi = repo.unfiltered()
-    revs = unfi.revs('::(%ln - null)', pullop.common)
-    common = [nullid]
-    if remote.capable('_evoext_obshash_0'):
-        obsexcmsg(repo.ui, "looking for common markers in %i nodes\n"
-                           % len(revs))
-        common = findcommonobsmarkers(repo.ui, repo, remote, revs)
-    return {'heads': pullop.pulledsubset, 'common': common}
-
-@eh.uisetup
-def addgetbundleargs(self):
-    gboptsmap['evo_obscommon'] = 'nodes'
-
-@eh.wrapfunction(exchange, '_pullbundle2extraprepare')
-def _addobscommontob2pull(orig, pullop, kwargs):
-    ret = orig(pullop, kwargs)
-    if ('obsmarkers' in kwargs and
-        pullop.remote.capable('_evoext_getbundle_obscommon')):
-        boundaries = _buildpullobsmarkersboundaries(pullop)
-        common = boundaries['common']
-        if common != [nullid]:
-            kwargs['evo_obscommon'] = common
-    return ret
-
-@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.isenabled(pullop.repo, obsolete.exchangeopt):
-        return None
-    if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']):
-        return None
-    if 'obsmarkers' in getattr(pullop, 'stepsdone', []):
-        return None
-    wirepull = pullop.remote.capable('_evoext_pullobsmarkers_0')
-    if not wirepull:
-        return orig(pullop)
-    if 'obsolete' not in pullop.remote.listkeys('namespaces'):
-        return None # remote opted out of obsolescence marker exchange
-    tr = None
-    ui = pullop.repo.ui
-    boundaries = _buildpullobsmarkersboundaries(pullop)
-    if not set(boundaries['heads']) - set(boundaries['common']):
-        obsexcmsg(ui, "nothing to pull\n")
-        return None
-
-    obsexcmsg(ui, "pull obsolescence markers\n", True)
-    new = 0
-
-    if wirepull:
-        obsdata = pullop.remote.evoext_pullobsmarkers_0(**boundaries)
-        obsdata = obsdata.read()
-        if len(obsdata) > 5:
-            obsexcmsg(ui, "merging obsolescence markers (%i bytes)\n"
-                           % len(obsdata))
-            tr = pullop.gettransaction()
-            old = len(pullop.repo.obsstore._all)
-            pullop.repo.obsstore.mergemarkers(tr, obsdata)
-            new = len(pullop.repo.obsstore._all) - old
-            obsexcmsg(ui, "%i obsolescence markers added\n" % new, True)
-        else:
-            obsexcmsg(ui, "no unknown remote markers\n")
-        obsexcmsg(ui, "DONE\n")
-    if new:
-        pullop.repo.invalidatevolatilesets()
-    return tr
-
-def _getobsmarkersstream(repo, heads=None, common=None):
-    revset = ''
-    args = []
-    repo = repo.unfiltered()
-    if heads is None:
-        revset = 'all()'
-    elif heads:
-        revset += "(::%ln)"
-        args.append(heads)
-    else:
-        assert False, 'pulling no heads?'
-    if common:
-        revset += ' - (::%ln)'
-        args.append(common)
-    nodes = [c.node() for c in repo.set(revset, *args)]
-    markers = repo.obsstore.relevantmarkers(nodes)
-    obsdata = StringIO()
-    for chunk in obsolete.encodemarkers(markers, True):
-        obsdata.write(chunk)
-    obsdata.seek(0)
-    return obsdata
-
-@eh.addattr(wireproto.wirepeer, 'evoext_pullobsmarkers_0')
-def client_pullobsmarkers(self, heads=None, common=None):
-    self.requirecap('_evoext_pullobsmarkers_0', _('look up remote obsmarkers'))
-    opts = {}
-    if heads is not None:
-        opts['heads'] = wireproto.encodelist(heads)
-    if common is not None:
-        opts['common'] = wireproto.encodelist(common)
-    if util.safehasattr(self, '_callcompressable'):
-        f = self._callcompressable("evoext_pullobsmarkers_0", **opts)
-    else:
-        f = self._callstream("evoext_pullobsmarkers_0", **opts)
-        f = self._decompress(f)
-    length = int(f.read(20))
-    chunk = 4096
-    current = 0
-    data = StringIO()
-    ui = self.ui
-    obsexcprg(ui, current, unit=_("bytes"), total=length)
-    while current < length:
-        readsize = min(length - current, chunk)
-        data.write(f.read(readsize))
-        current += readsize
-        obsexcprg(ui, current, unit=_("bytes"), total=length)
-    obsexcprg(ui, None)
-    data.seek(0)
-    return data
-
-@eh.addattr(localrepo.localpeer, 'evoext_pullobsmarkers_0')
-def local_pullobsmarkers(self, heads=None, common=None):
-    return _getobsmarkersstream(self._repo, heads=heads, common=common)
-
-# The wireproto.streamres API changed, handling chunking and compression
-# directly. Handle either case.
-if util.safehasattr(wireproto.abstractserverproto, 'groupchunks'):
-    # We need to handle chunking and compression directly
-    def streamres(d, proto):
-        return wireproto.streamres(proto.groupchunks(d))
-else:
-    # Leave chunking and compression to streamres
-    def streamres(d, proto):
-        return wireproto.streamres(reader=d, v1compressible=True)
-
-def srv_pullobsmarkers(repo, proto, others):
-    opts = wireproto.options('', ['heads', 'common'], others)
-    for k, v in opts.iteritems():
-        if k in ('heads', 'common'):
-            opts[k] = wireproto.decodelist(v)
-    obsdata = _getobsmarkersstream(repo, **opts)
-    finaldata = StringIO()
-    obsdata = obsdata.getvalue()
-    finaldata.write('%20i' % len(obsdata))
-    finaldata.write(obsdata)
-    finaldata.seek(0)
-    return streamres(finaldata, proto)
-
-def _obsrelsethashtreefm0(repo):
-    return _obsrelsethashtree(repo, obsolete._fm0encodeonemarker)
-
-def _obsrelsethashtreefm1(repo):
-    return _obsrelsethashtree(repo, obsolete._fm1encodeonemarker)
-
-def _obsrelsethashtree(repo, encodeonemarker):
-    cache = []
-    unfi = repo.unfiltered()
-    markercache = {}
-    repo.ui.progress(_("preparing locally"), 0, total=len(unfi))
-    for i in unfi:
-        ctx = unfi[i]
-        entry = 0
-        sha = hashlib.sha1()
-        # add data from p1
-        for p in ctx.parents():
-            p = p.rev()
-            if p < 0:
-                p = nullid
-            else:
-                p = cache[p][1]
-            if p != nullid:
-                entry += 1
-                sha.update(p)
-        tmarkers = repo.obsstore.relevantmarkers([ctx.node()])
-        if tmarkers:
-            bmarkers = []
-            for m in tmarkers:
-                if not m in markercache:
-                    markercache[m] = encodeonemarker(m)
-                bmarkers.append(markercache[m])
-            bmarkers.sort()
-            for m in bmarkers:
-                entry += 1
-                sha.update(m)
-        if entry:
-            cache.append((ctx.node(), sha.digest()))
-        else:
-            cache.append((ctx.node(), nullid))
-        repo.ui.progress(_("preparing locally"), i, total=len(unfi))
-    repo.ui.progress(_("preparing locally"), None)
-    return cache
-
-@command('debugobsrelsethashtree',
-        [('', 'v0', None, 'hash on marker format "0"'),
-         ('', 'v1', None, 'hash on marker format "1" (default)')] , _(''))
-def debugobsrelsethashtree(ui, repo, v0=False, v1=False):
-    """display Obsolete markers, Relevant Set, Hash Tree
-    changeset-node obsrelsethashtree-node
-
-    It computed form the "orsht" of its parent and markers
-    relevant to the changeset itself."""
-    if v0 and v1:
-        raise error.Abort('cannot only specify one format')
-    elif v0:
-        treefunc = _obsrelsethashtreefm0
-    else:
-        treefunc = _obsrelsethashtreefm1
-
-    for chg, obs in treefunc(repo):
-        ui.status('%s %s\n' % (node.hex(chg), node.hex(obs)))
-
-_bestformat = max(obsolete.formats.keys())
-
-
-@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 error.Abort(_('bad obsolescence marker detected: '
-                               'invalid successors nullid'),
-                             hint=_('You should run `hg debugobsconvert`'))
-
-@command(
-    'debugobsconvert',
-    [('', 'new-format', _bestformat, _('Destination format for markers.'))],
-    '')
-def debugobsconvert(ui, repo, new_format):
-    origmarkers = repo.obsstore._all  # settle version
-    if new_format == repo.obsstore._version:
-        msg = _('New format is the same as the old format, not upgrading!')
-        raise error.Abort(msg)
-    f = repo.svfs('obsstore', 'wb', atomictemp=True)
-    known = set()
-    markers = []
-    for m in origmarkers:
-        # filter out invalid markers
-        if nullid in m[1]:
-            m = list(m)
-            m[1] = tuple(s for s in m[1] if s != nullid)
-            m = tuple(m)
-        if m in known:
-            continue
-        known.add(m)
-        markers.append(m)
-    ui.write(_('Old store is version %d, will rewrite in version %d\n') % (
-        repo.obsstore._version, new_format))
-    map(f.write, obsolete.encodemarkers(markers, True, new_format))
-    f.close()
-    ui.write(_('Done!\n'))
-
-
-@eh.wrapfunction(wireproto, 'capabilities')
-def capabilities(orig, repo, proto):
-    """wrapper to advertise new capability"""
-    caps = orig(repo, proto)
-    if obsolete.isenabled(repo, obsolete.exchangeopt):
-        caps += ' _evoext_pushobsmarkers_0'
-        caps += ' _evoext_pullobsmarkers_0'
-        caps += ' _evoext_obshash_0'
-        caps += ' _evoext_obshash_1'
-        caps += ' _evoext_getbundle_obscommon'
-    return caps
-
-
-@eh.extsetup
-def _installwireprotocol(ui):
-    localrepo.moderncaps.add('_evoext_pullobsmarkers_0')
-    hgweb_mod.perms['evoext_pushobsmarkers_0'] = 'push'
-    hgweb_mod.perms['evoext_pullobsmarkers_0'] = 'pull'
-    wireproto.commands['evoext_pushobsmarkers_0'] = (srv_pushobsmarkers, '')
-    wireproto.commands['evoext_pullobsmarkers_0'] = (srv_pullobsmarkers, '*')
-    # wrap command content
-    oldcap, args = wireproto.commands['capabilities']
-    def newcap(repo, proto):
-        return capabilities(oldcap, repo, proto)
-    wireproto.commands['capabilities'] = (newcap, args)
-
-# Mercurial >= 3.6 passes ui
-def _helploader(ui=None):
-    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()
-
-def _relocatecommit(repo, orig, commitmsg):
-    if commitmsg is None:
-        commitmsg = orig.description()
-    extra = dict(orig.extra())
-    if 'branch' in extra:
-        del extra['branch']
-    extra['rebase_source'] = orig.hex()
-
-    backup = repo.ui.backupconfig('phases', 'new-commit')
-    try:
-        targetphase = max(orig.phase(), phases.draft)
-        repo.ui.setconfig('phases', 'new-commit', targetphase, 'evolve')
-        # Commit might fail if unresolved files exist
-        nodenew = repo.commit(text=commitmsg, user=orig.user(),
-                              date=orig.date(), extra=extra)
-    finally:
-        repo.ui.restoreconfig(backup)
-    return nodenew
-
-def _finalizerelocate(repo, orig, dest, nodenew, tr):
-    destbookmarks = repo.nodebookmarks(dest.node())
-    nodesrc = orig.node()
-    destphase = repo[nodesrc].phase()
-    oldbookmarks = repo.nodebookmarks(nodesrc)
-    if nodenew is not None:
-        phases.retractboundary(repo, tr, destphase, [nodenew])
-        obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))])
-        for book in oldbookmarks:
-            repo._bookmarks[book] = nodenew
-    else:
-        obsolete.createmarkers(repo, [(repo[nodesrc], ())])
-        # Behave like rebase, move bookmarks to dest
-        for book in oldbookmarks:
-            repo._bookmarks[book] = dest.node()
-    for book in destbookmarks: # restore bookmark that rebase move
-        repo._bookmarks[book] = dest.node()
-    if oldbookmarks or destbookmarks:
-        repo._bookmarks.recordchange(tr)
-
-evolvestateversion = 0
-
-@eh.uisetup
-def setupevolveunfinished(ui):
-    data = ('evolvestate', True, False, _('evolve in progress'),
-           _("use 'hg evolve --continue' or 'hg update -C .' to abort"))
-    cmdutil.unfinishedstates.append(data)
-
-@eh.wrapfunction(hg, 'clean')
-def clean(orig, repo, *args, **kwargs):
-    ret = orig(repo, *args, **kwargs)
-    util.unlinkpath(repo.join('evolvestate'), ignoremissing=True)
-    return ret
-
-def _evolvestatewrite(repo, state):
-    # [version]
-    # [type][length][content]
-    #
-    # `version` is a 4 bytes integer (handled at higher level)
-    # `type` is a single character, `length` is a 4 byte integer, and
-    # `content` is an arbitrary byte sequence of length `length`.
-    f = repo.vfs('evolvestate', 'w')
-    try:
-        f.write(_pack('>I', evolvestateversion))
-        current = state['current']
-        key = 'C' # as in 'current'
-        format = '>sI%is' % len(current)
-        f.write(_pack(format, key, len(current), current))
-    finally:
-        f.close()
-
-def _evolvestateread(repo):
-    try:
-        f = repo.vfs('evolvestate')
-    except IOError as err:
-        if err.errno != errno.ENOENT:
-            raise
-        return None
-    try:
-        versionblob = f.read(4)
-        if len(versionblob) < 4:
-            repo.ui.debug('ignoring corrupted evolvestte (file contains %i bits)'
-                          % len(versionblob))
-            return None
-        version = _unpack('>I', versionblob)[0]
-        if version != evolvestateversion:
-            raise error.Abort(_('unknown evolvestate version %i')
-                                % version, hint=_('upgrade your evolve'))
-        records = []
-        data = f.read()
-        off = 0
-        end = len(data)
-        while off < end:
-            rtype = data[off]
-            off += 1
-            length = _unpack('>I', data[off:(off + 4)])[0]
-            off += 4
-            record = data[off:(off + length)]
-            off += length
-            if rtype == 't':
-                rtype, record = record[0], record[1:]
-            records.append((rtype, record))
-        state = {}
-        for rtype, rdata in records:
-            if rtype == 'C':
-                state['current'] = rdata
-            elif rtype.lower():
-                repo.ui.debug('ignore evolve state record type %s' % rtype)
-            else:
-                raise error.Abort(_('unknown evolvestate field type %r')
-                                  % rtype, hint=_('upgrade your evolve'))
-        return state
-    finally:
-        f.close()
-
-def _evolvestatedelete(repo):
-    util.unlinkpath(repo.join('evolvestate'), ignoremissing=True)
-
-def _evolvemerge(repo, orig, dest, pctx, keepbranch):
-    """Used by the evolve function to merge dest on top of pctx.
-    return the same tuple as merge.graft"""
-    if repo['.'].rev() != dest.rev():
-        #assert False
-        try:
-            merge.update(repo,
-                         dest,
-                         branchmerge=False,
-                         force=True)
-        except TypeError:
-            # Mercurial  < 43c00ca887d1 (3.7)
-            merge.update(repo,
-                         dest,
-                         branchmerge=False,
-                         force=True,
-                         partial=False)
-    if bmactive(repo):
-       repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo))
-    bmdeactivate(repo)
-    if keepbranch:
-       repo.dirstate.setbranch(orig.branch())
-    if util.safehasattr(repo, 'currenttopic'):
-        # uurrgs
-        # there no other topic setter yet
-        if not orig.topic() and repo.vfs.exists('topic'):
-                repo.vfs.unlink('topic')
-        else:
-            with repo.vfs.open('topic', 'w') as f:
-                f.write(orig.topic())
-
-    try:
-       r = merge.graft(repo, orig, pctx, ['local', 'graft'], True)
-    except TypeError:
-       # not using recent enough mercurial
-       if len(orig.parents()) == 2:
-           raise error.Abort(
-               _("no support for evolving merge changesets yet"),
-               hint=_("Redo the merge and use `hg prune <old> --succ "
-                      "<new>` to obsolete the old one"))
-
-       r = merge.graft(repo, orig, pctx, ['local', 'graft'])
-    return r