changeset 3704:a0c39e8d2c29 stable

brancing: merge next release into default We are about to release hg-evolve 8.0.0
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 23 Apr 2018 11:04:27 +0200
parents c912eaf29eec (current diff) f6979d64b9fb (diff)
children 47e6776c2ef0
files hgext3rd/evolve/checkheads.py tests/test-discovery-obshashrange.t tests/test-evolve-bumped.t
diffstat 48 files changed, 4046 insertions(+), 1151 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Fri Mar 23 09:08:21 2018 -0700
+++ b/CHANGELOG	Mon Apr 23 11:04:27 2018 +0200
@@ -1,6 +1,25 @@
 Changelog
 =========
 
+8.0.0 - in progress
+-------------------
+
+  * drop support for Mercurial 4.1 and 4.2
+  * `--obsolete` and `--old-obsolete` flags for `hg graft` are dropped
+  * evolve: a new `--abort` flag which aborts an interrupted evolve
+            resolving orphans
+  * `hg evolve` now return 0 if there is nothing to evolve
+  * amend: a new `--patch` flag to make changes to wdir parent by editing patch
+
+  * fixed some memory leak issue
+
+  * templatekw: remove `obsfatedata` templatekw. Individuals fields are
+    available in core as single template functions.
+
+  * fixed issue 5833 and 5832
+
+  * topic: restring name to letter, '-', '_' and '.'
+
 7.3.0 -- 2018-03-21
 ---------------------
 
--- a/docs/from-mq.rst	Fri Mar 23 09:08:21 2018 -0700
+++ b/docs/from-mq.rst	Mon Apr 23 11:04:27 2018 +0200
@@ -125,7 +125,7 @@
 
 You can also decide to do it manually using::
 
-  $ hg graft -O <old-version>
+  $ hg grab <old-version>
 
 or::
 
--- a/hgext3rd/evolve/__init__.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/__init__.py	Mon Apr 23 11:04:27 2018 +0200
@@ -32,7 +32,7 @@
 backported to older version of Mercurial by this extension. Some older
 experimental protocol are also supported for a longer time in the extensions to
 help people transitioning. (The extensions is currently compatible down to
-Mercurial version 4.1).
+Mercurial version 4.3).
 
 New Config::
 
@@ -78,11 +78,11 @@
   #   auto: warm cache at the end of server side transaction (default).
   obshashrange.warm-cache = 'auto'
 
-The initial cache warming might be a bit slow. To make sure it is build you
-can run one of the following commands in your repository::
+    $ hg debugupdatecache
 
-    $ hg debugupdatecache # mercurial 4.3 and above
-    $ hg debugobshashrange --rev 'head() # mercurial 4.2 and below
+It is recommended to enable the blackbox extension. It gathers useful data about
+the experiment. It is shipped with Mercurial so no extra install is needed::
+    $ hg debugupdatecache
 
 It is recommended to enable the blackbox extension. It gathers useful data about
 the experiment. It is shipped with Mercurial so no extra install is needed::
@@ -299,7 +299,6 @@
 from mercurial.node import nullid
 
 from . import (
-    checkheads,
     compat,
     debugcmd,
     cmdrewrite,
@@ -357,7 +356,6 @@
 eh.merge(debugcmd.eh)
 eh.merge(evolvecmd.eh)
 eh.merge(obsexchange.eh)
-eh.merge(checkheads.eh)
 eh.merge(safeguard.eh)
 eh.merge(obscache.eh)
 eh.merge(obshistory.eh)
@@ -509,7 +507,7 @@
 
 @eh.uisetup
 def _installalias(ui):
-    if ui.config('alias', 'odiff') is None:
+    if ui.config('alias', 'odiff', None) is None:
         ui.setconfig('alias', 'odiff',
                      "diff --hidden --rev 'limit(precursors(.),1)' --rev .",
                      'evolve')
@@ -722,9 +720,8 @@
 
     ui.warn("(%s)\n" % solvemsg)
 
-if util.safehasattr(context, '_filterederror'):
-    # if < hg-4.2 we do not update the message
-    @eh.wrapfunction(context, '_filterederror')
+if util.safehasattr(context, '_filterederror'): # <= hg-4.5
+    @eh.wrapfunction(scmutil, '_filterederror')
     def evolve_filtererror(original, repo, changeid):
         """build an exception to be raised about a filtered changeid
 
@@ -733,10 +730,10 @@
         if repo.filtername.startswith('visible'):
 
             unfilteredrepo = repo.unfiltered()
-            rev = unfilteredrepo[changeid]
+            rev = repo[scmutil.revsingle(unfilteredrepo, changeid)]
             reason, successors = obshistory._getobsfateandsuccs(unfilteredrepo, rev.node())
 
-            # Be more precise in cqse the revision is superseed
+            # Be more precise in case the revision is superseed
             if reason == 'superseed':
                 reason = _("successor: %s") % successors[0]
             elif reason == 'superseed_split':
@@ -880,25 +877,36 @@
                     _('mark the old node as obsoleted by '
                       'the created commit')))
 
+def _getnodefrompatch(patch, dest):
+    patchnode = patch.get('nodeid')
+    if patchnode is not None:
+        dest['node'] = node.bin(patchnode)
+
 @eh.wrapfunction(mercurial.cmdutil, 'tryimportone')
 def tryimportone(orig, ui, repo, hunk, parents, opts, *args, **kwargs):
-    extracted = patch.extract(ui, hunk)
-    expected = extracted.get('nodeid')
-    if expected is not None:
-        expected = node.bin(expected)
-    oldextract = patch.extract
-    try:
-        patch.extract = lambda ui, hunk: extracted
+    expected = {'node': None}
+    if not util.safehasattr(hunk, 'get'): # hg < 4.6
+        oldextract = patch.extract
+
+        def extract(*args, **kwargs):
+            ret = oldextract(*args, **kwargs)
+            _getnodefrompatch(ret, expected)
+            return ret
+        try:
+            patch.extract = extract
+            ret = orig(ui, repo, hunk, parents, opts, *args, **kwargs)
+        finally:
+            patch.extract = oldextract
+    else:
+        _getnodefrompatch(hunk, expected)
         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):
+    if (opts['obsolete'] and None not in (created, expected['node'])
+        and created != expected['node']):
             tr = repo.transaction('import-obs')
             try:
                 metadata = {'user': ui.username()}
-                repo.obsstore.create(tr, expected, (created,),
+                repo.obsstore.create(tr, expected['node'], (created,),
                                      metadata=metadata)
                 tr.close()
             finally:
@@ -973,7 +981,7 @@
                 tr = repo.transaction('previous')
                 if bookmark is not None:
                     bmchanges = [(bookmark, target.node())]
-                    compat.bookmarkapplychanges(repo, tr, bmchanges)
+                    repo._bookmarks.applychanges(repo, tr, bmchanges)
                 else:
                     bookmarksmod.deactivate(repo)
                 tr.close()
@@ -1166,7 +1174,8 @@
     """logic for hg next command to evolve and update to an aspiring children"""
 
     cmdutil.bailifchanged(repo)
-    evolvestate = state.cmdstate(repo, opts={'command': 'next'})
+    evolvestate = state.cmdstate(repo, opts={'command': 'next',
+                                             'bookmarkchanges': []})
     result = evolvecmd._solveone(ui, repo, repo[aspchildren],
                                  evolvestate, opts.get('dry_run'), False,
                                  lambda: None, category='orphan')
@@ -1194,7 +1203,7 @@
                 tr = repo.transaction('next')
                 if shouldmove:
                     bmchanges = [(bm, children.node())]
-                    compat.bookmarkapplychanges(repo, tr, bmchanges)
+                    repo._bookmarks.applychanges(repo, tr, bmchanges)
                 else:
                     bookmarksmod.deactivate(repo)
                 tr.close()
@@ -1218,14 +1227,14 @@
             obsoleted = repo.set('%lr', obsoleted)
         result = orig(ui, repo, *arg, **kwargs)
         if not result: # commit succeeded
-            new = repo['-1']
+            new = repo['tip']
             oldbookmarks = []
             markers = []
             for old in obsoleted:
                 oldbookmarks.extend(repo.nodebookmarks(old.node()))
                 markers.append((old, (new,)))
             if markers:
-                compat.createmarkers(repo, markers, operation="amend")
+                obsolete.createmarkers(repo, markers, operation="amend")
             bmchanges = []
             for book in oldbookmarks:
                 bmchanges.append((book, new.node()))
@@ -1235,7 +1244,7 @@
                 if not lock:
                     lock = repo.lock()
                 tr = repo.transaction('commit')
-                compat.bookmarkapplychanges(repo, tr, bmchanges)
+                repo._bookmarks.applychanges(repo, tr, bmchanges)
                 tr.close()
         return result
     finally:
@@ -1266,33 +1275,6 @@
     kwargs['biject'] = False
     return cmdrewrite.cmdprune(ui, repo, *revs, **kwargs)
 
-@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.vfs.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']:
@@ -1306,11 +1288,6 @@
     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)")))
 
 @eh.wrapfunction(obsolete, '_checkinvalidmarkers')
 def _checkinvalidmarkers(orig, markers):
@@ -1374,7 +1351,7 @@
 
 @eh.uisetup
 def setupevolveunfinished(ui):
-    data = ('evolvestate', True, False, _('evolve in progress'),
+    data = ('evolvestate', False, False, _('evolve in progress'),
             _("use 'hg evolve --continue' or 'hg update -C .' to abort"))
     cmdutil.unfinishedstates.append(data)
 
--- a/hgext3rd/evolve/checkheads.py	Fri Mar 23 09:08:21 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-# Code dedicated to the postprocessing new heads check with obsolescence
-#
-# Copyright 2017 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-import functools
-
-from mercurial import (
-    discovery,
-    error,
-    extensions,
-    node as nodemod,
-    phases,
-    util,
-)
-
-from mercurial.i18n import _
-
-from . import exthelper
-
-nullid = nodemod.nullid
-short = nodemod.short
-_headssummary = discovery._headssummary
-_oldheadssummary = discovery._oldheadssummary
-_nowarnheads = discovery._nowarnheads
-
-eh = exthelper.exthelper()
-
-@eh.uisetup
-def setupcheckheadswrapper(ui):
-    if not util.safehasattr(discovery, '_postprocessobsolete'):
-        # hg-4.2+ has all the code natively
-        extensions.wrapfunction(discovery, 'checkheads',
-                                checkheadsfulloverlay)
-
-# have dedicated wrapper to keep the rest as close as core as possible
-def checkheadsfulloverlay(orig, pushop):
-    if pushop.repo.obsstore:
-        return corecheckheads(pushop)
-    else:
-        return orig(pushop)
-
-# copied from mercurial.discovery.checkheads as in a5bad127128d (4.1)
-#
-# The only differences are:
-# * the _postprocessobsolete section have been extracted,
-# * minor test adjustment to please flake8
-def corecheckheads(pushop):
-    """Check that a push won't add any outgoing head
-
-    raise Abort error and display ui message as needed.
-    """
-
-    repo = pushop.repo.unfiltered()
-    remote = pushop.remote
-    outgoing = pushop.outgoing
-    remoteheads = pushop.remoteheads
-    newbranch = pushop.newbranch
-    inc = bool(pushop.incoming)
-
-    # Check for each named branch if we're creating new remote heads.
-    # To be a remote head after push, node must be either:
-    # - unknown locally
-    # - a local outgoing head descended from update
-    # - a remote head that's known locally and not
-    #   ancestral to an outgoing head
-    if remoteheads == [nullid]:
-        # remote is empty, nothing to check.
-        return
-
-    if remote.capable('branchmap'):
-        headssum = _headssummary(repo, remote, outgoing)
-    else:
-        headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
-    newbranches = [branch for branch, heads in headssum.iteritems()
-                   if heads[0] is None]
-    # 1. Check for new branches on the remote.
-    if newbranches and not newbranch:  # new branch requires --new-branch
-        branchnames = ', '.join(sorted(newbranches))
-        raise error.Abort(_("push creates new remote branches: %s!")
-                          % branchnames,
-                          hint=_("use 'hg push --new-branch' to create"
-                                 " new remote branches"))
-
-    # 2. Find heads that we need not warn about
-    nowarnheads = _nowarnheads(pushop)
-
-    # 3. Check for new heads.
-    # If there are more heads after the push than before, a suitable
-    # error message, depending on unsynced status, is displayed.
-    errormsg = None
-    # If there is no obsstore, allfuturecommon won't be used, so no
-    # need to compute it.
-    if repo.obsstore:
-        allmissing = set(outgoing.missing)
-        cctx = repo.set('%ld', outgoing.common)
-        allfuturecommon = set(c.node() for c in cctx)
-        allfuturecommon.update(allmissing)
-    for branch, heads in sorted(headssum.iteritems()):
-        remoteheads, newheads, unsyncedheads = heads
-        candidate_newhs = set(newheads)
-        # add unsynced data
-        if remoteheads is None:
-            oldhs = set()
-        else:
-            oldhs = set(remoteheads)
-        oldhs.update(unsyncedheads)
-        candidate_newhs.update(unsyncedheads)
-        dhs = None # delta heads, the new heads on branch
-        if not repo.obsstore:
-            discardedheads = set()
-            newhs = candidate_newhs
-        else:
-            newhs, discardedheads = _postprocessobsolete(pushop,
-                                                         allfuturecommon,
-                                                         candidate_newhs)
-        unsynced = sorted(h for h in unsyncedheads if h not in discardedheads)
-        if unsynced:
-            if None in unsynced:
-                # old remote, no heads data
-                heads = None
-            elif len(unsynced) <= 4 or repo.ui.verbose:
-                heads = ' '.join(short(h) for h in unsynced)
-            else:
-                heads = (' '.join(short(h) for h in unsynced[:4]) +
-                         ' ' + _("and %s others") % (len(unsynced) - 4))
-            if heads is None:
-                repo.ui.status(_("remote has heads that are "
-                                 "not known locally\n"))
-            elif branch is None:
-                repo.ui.status(_("remote has heads that are "
-                                 "not known locally: %s\n") % heads)
-            else:
-                repo.ui.status(_("remote has heads on branch '%s' that are "
-                                 "not known locally: %s\n") % (branch, heads))
-        if remoteheads is None:
-            if len(newhs) > 1:
-                dhs = list(newhs)
-                if errormsg is None:
-                    errormsg = (_("push creates new branch '%s' "
-                                  "with multiple heads") % (branch))
-                    hint = _("merge or"
-                             " see 'hg help push' for details about"
-                             " pushing new heads")
-        elif len(newhs) > len(oldhs):
-            # remove bookmarked or existing remote heads from the new heads list
-            dhs = sorted(newhs - nowarnheads - oldhs)
-        if dhs:
-            if errormsg is None:
-                if branch not in ('default', None):
-                    errormsg = _("push creates new remote head %s "
-                                 "on branch '%s'!") % (short(dhs[0]), branch)
-                elif repo[dhs[0]].bookmarks():
-                    errormsg = (_("push creates new remote head %s "
-                                  "with bookmark '%s'!")
-                                % (short(dhs[0]), repo[dhs[0]].bookmarks()[0]))
-                else:
-                    errormsg = _("push creates new remote head %s!"
-                                 ) % short(dhs[0])
-                if unsyncedheads:
-                    hint = _("pull and merge or"
-                             " see 'hg help push' for details about"
-                             " pushing new heads")
-                else:
-                    hint = _("merge or"
-                             " see 'hg help push' for details about"
-                             " pushing new heads")
-            if branch is None:
-                repo.ui.note(_("new remote heads:\n"))
-            else:
-                repo.ui.note(_("new remote heads on branch '%s':\n") % branch)
-            for h in dhs:
-                repo.ui.note((" %s\n") % short(h))
-    if errormsg:
-        raise error.Abort(errormsg, hint=hint)
-
-def _postprocessobsolete(pushop, futurecommon, candidate):
-    """post process the list of new heads with obsolescence information
-
-    Exist as a subfunction to contains the complexity and allow extensions to
-    experiment with smarter logic.
-    Returns (newheads, discarded_heads) tuple
-    """
-    # remove future heads which are actually obsoleted by another
-    # pushed element:
-    #
-    # known issue
-    #
-    # * We "silently" skip processing on all changeset unknown locally
-    #
-    # * if <nh> is public on the remote, it won't be affected by obsolete
-    #     marker and a new is created
-    repo = pushop.repo
-    unfi = repo.unfiltered()
-    tonode = unfi.changelog.node
-    public = phases.public
-    getphase = unfi._phasecache.phase
-    ispublic = (lambda r: getphase(unfi, r) == public)
-    hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, futurecommon)
-    successorsmarkers = unfi.obsstore.successors
-    newhs = set()
-    discarded = set()
-    # I leave the print in the code because they are so handy at debugging
-    # and I keep getting back to this piece of code.
-    #
-    localcandidate = set()
-    unknownheads = set()
-    for h in candidate:
-        if h in unfi:
-            localcandidate.add(h)
-        else:
-            if successorsmarkers.get(h) is not None:
-                msg = ('checkheads: remote head unknown locally has'
-                       ' local marker: %s\n')
-                repo.ui.debug(msg % nodemod.hex(h))
-            unknownheads.add(h)
-    if len(localcandidate) == 1:
-        return unknownheads | set(candidate), set()
-    while localcandidate:
-        nh = localcandidate.pop()
-        # run this check early to skip the revset on the whole branch
-        if (nh in futurecommon
-                or unfi[nh].phase() <= public):
-            newhs.add(nh)
-            continue
-        # XXX there is a corner case if there is a merge in the branch. we
-        # might end up with -more- heads.  However, these heads are not "added"
-        # by the push, but more by the "removal" on the remote so I think is a
-        # okay to ignore them,
-        branchrevs = unfi.revs('only(%n, (%ln+%ln))',
-                               nh, localcandidate, newhs)
-        branchnodes = [tonode(r) for r in branchrevs]
-
-        # The branch will still exist on the remote if
-        # * any part of it is public,
-        # * any part of it is considered part of the result by previous logic,
-        # * if we have no markers to push to obsolete it.
-        if (any(ispublic(r) for r in branchrevs)
-                or any(n in futurecommon for n in branchnodes)
-                or any(not hasoutmarker(n) for n in branchnodes)):
-            newhs.add(nh)
-        else:
-            discarded.add(nh)
-    newhs |= unknownheads
-    return newhs, discarded
-
-def pushingmarkerfor(obsstore, pushset, node):
-    """True if some markers are to be pushed for node
-
-    We cannot just look in to the pushed obsmarkers from the pushop because
-    discover might have filtered relevant markers. In addition listing all
-    markers relevant to all changeset in the pushed set would be too expensive.
-
-    The is probably some cache opportunity in this function. but it would
-    requires a two dimentions stack.
-    """
-    successorsmarkers = obsstore.successors
-    stack = [node]
-    seen = set(stack)
-    while stack:
-        current = stack.pop()
-        if current in pushset:
-            return True
-        markers = successorsmarkers.get(current, ())
-        # markers fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
-        for m in markers:
-            nexts = m[1] # successors
-            if not nexts: # this is a prune marker
-                nexts = m[5] # parents
-            for n in nexts:
-                if n not in seen:
-                    seen.add(n)
-                    stack.append(n)
-    return False
--- a/hgext3rd/evolve/cmdrewrite.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/cmdrewrite.py	Mon Apr 23 11:04:27 2018 +0200
@@ -11,6 +11,7 @@
 
 from __future__ import absolute_import
 
+import contextlib
 import random
 
 from mercurial import (
@@ -25,6 +26,7 @@
     merge,
     node,
     obsolete,
+    obsutil,
     patch,
     phases,
     scmutil,
@@ -33,6 +35,11 @@
 
 from mercurial.i18n import _
 
+try:
+    from mercurial.utils.dateutil import datestr
+except ImportError: # hg <= 4.5
+    from mercurial.util import datestr
+
 from . import (
     compat,
     state,
@@ -94,6 +101,7 @@
      ('a', 'all', False, _("match all files")),
      ('e', 'edit', False, _('invoke editor on commit messages')),
      ('', 'extract', False, _('extract changes from the commit to the working copy')),
+     ('', 'patch', False, _('make changes to wdir parent by editing patch')),
      ('', 'close-branch', None,
       _('mark a branch as closed, hiding it from the branch list')),
      ('s', 'secret', None, _('use the secret phase for committing')),
@@ -118,6 +126,8 @@
     """
     _checknotesize(ui, opts)
     opts = opts.copy()
+    if opts.get('patch'):
+        return amendpatch(ui, repo, *pats, **opts)
     if opts.get('extract'):
         return uncommit(ui, repo, *pats, **opts)
     else:
@@ -140,6 +150,152 @@
         finally:
             lockmod.release(lock, wlock)
 
+def amendpatch(ui, repo, *pats, **opts):
+    """logic for --patch flag of `hg amend` command."""
+    lock = wlock = tr = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+        tr = repo.transaction('amend')
+        cmdutil.bailifchanged(repo)
+        # first get the patch
+        old = repo['.']
+        p1 = old.p1()
+        rewriteutil.precheck(repo, [old.rev()], 'amend')
+        bookmarkupdater = rewriteutil.bookmarksupdater(repo, old.node(), tr)
+        diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
+        diffopts.nodates = True
+        diffopts.git = True
+        fp = stringio()
+        _writectxmetadata(repo, old, fp)
+        matcher = scmutil.match(old, pats, opts)
+        for chunk, label in patch.diffui(repo, p1.node(), old.node(),
+                                         match=matcher,
+                                         opts=diffopts):
+                fp.write(chunk)
+        newnode = _editandapply(ui, repo, pats, old, p1, fp, diffopts)
+        if newnode == old.node():
+            raise error.Abort(_("nothing changed"))
+        metadata = {}
+        if opts.get('note'):
+            metadata['note'] = opts['note']
+        obsolete.createmarkers(repo, [(old, (repo[newnode],))],
+                               metadata=metadata, operation='amend')
+        phases.retractboundary(repo, tr, old.phase(), [newnode])
+        hg.updaterepo(repo, newnode, True)
+        bookmarkupdater(newnode)
+        tr.close()
+    finally:
+        tr.release()
+        lockmod.release(lock, wlock)
+
+def _editandapply(ui, repo, pats, old, p1, fp, diffopts):
+    RETRYCHOICE = _('try to fix the patch (yn)?$$ &Yes $$ &No')
+    newnode = None
+    while newnode is None:
+        fp.seek(0)
+        previous_patch = fp.getvalue()
+        newpatch = ui.edit(fp.getvalue(), old.user(), action="diff")
+
+        afp = stringio()
+        afp.write(newpatch)
+        if pats:
+            # write rest of the files in the patch
+            restmatcher = scmutil.match(old, [], opts={'exclude': pats})
+            for chunk, label in patch.diffui(repo, p1.node(), old.node(),
+                                             match=restmatcher,
+                                             opts=diffopts):
+                    afp.write(chunk)
+
+        user_patch = afp.getvalue()
+        if user_patch == previous_patch:
+            raise error.Abort(_("patch unchanged"))
+        afp.seek(0)
+        # write the patch to repo and get the newnode
+        try:
+            newnode = _writepatch(ui, repo, old, afp)
+        except patch.PatchError as err:
+            ui.write_err(_("failed to apply edited patch: %s\n") % err)
+            defaultchoice = 0 # yes
+            if not ui.interactive:
+                defaultchoice = 1 # no
+            if ui.promptchoice(RETRYCHOICE, default=defaultchoice):
+                raise error.Abort(_("Could not apply amended path"))
+            else:
+                # consider a third choice where we restore the original patch
+                fp = stringio()
+                fp.write(user_patch)
+    return newnode
+
+def _writepatch(ui, repo, old, fp):
+    """utility function to use filestore and patchrepo to apply a patch to the
+    repository with metadata being extracted from the patch"""
+    metadata = patch.extract(ui, fp)
+    if util.safehasattr(metadata, 'get'): # < hg-4.6
+        @contextlib.contextmanager
+        def patchcontext():
+            yield metadata
+        patchcontext = patchcontext()
+    else:
+        patchcontext = metadata
+    pold = old.p1()
+
+    with patchcontext as metadata:
+        # store the metadata from the patch to variables
+        parents = (metadata.get('p1'), metadata.get('p2'))
+        date = metadata.get('date') or old.date()
+        branch = metadata.get('branch') or old.branch()
+        user = metadata.get('user') or old.user()
+        # XXX: we must extract extras from the patchfile too
+        extra = old.extra()
+        message = metadata.get('message') or old.description()
+        store = patch.filestore()
+        fp.seek(0)
+        try:
+            files = set()
+            # beware: next line may raise a PatchError to be handled by the caller
+            # of this function
+            patch.patchrepo(ui, repo, pold, store, fp, 1, '',
+                            files=files, eolmode=None)
+
+            memctx = context.memctx(repo, parents, message, files=files,
+                                    filectxfn=store,
+                                    user=user,
+                                    date=date,
+                                    branch=branch,
+                                    extra=extra)
+            newcm = memctx.commit()
+        finally:
+            store.close()
+    return newcm
+
+def _writectxmetadata(repo, ctx, fp):
+    nodeval = scmutil.binnode(ctx)
+    parents = [p.node() for p in ctx.parents() if p]
+    branch = ctx.branch()
+    if parents:
+        prev = parents[0]
+    else:
+        prev = node.nullid
+
+    fp.write("# HG changeset patch\n")
+    fp.write("# User %s\n" % ctx.user())
+    fp.write("# Date %d %d\n" % ctx.date())
+    fp.write("#      %s\n" % datestr(ctx.date()))
+    if branch and branch != 'default':
+        fp.write("# Branch %s\n" % branch)
+    fp.write("# Node ID %s\n" % node.hex(nodeval))
+    fp.write("# Parent  %s\n" % node.hex(prev))
+    if len(parents) > 1:
+        fp.write("# Parent  %s\n" % node.hex(parents[1]))
+
+    for headerid in cmdutil.extraexport:
+        header = cmdutil.extraexportmap[headerid](1, ctx)
+        if header is not None:
+            fp.write('# %s\n' % header)
+    fp.write(ctx.description().rstrip())
+    fp.write("\n\n")
+
 def _touchedbetween(repo, source, dest, match=None):
     touched = set()
     for files in repo.status(source, dest, match=match)[:3]:
@@ -389,8 +545,8 @@
         if opts.get('note'):
             metadata['note'] = opts['note']
 
-        compat.createmarkers(repo, [(old, (repo[newid],))], metadata=metadata,
-                             operation="uncommit")
+        obsolete.createmarkers(repo, [(old, (repo[newid],))], metadata=metadata,
+                               operation="uncommit")
         phases.retractboundary(repo, tr, oldphase, [newid])
         if opts.get('revert'):
             hg.updaterepo(repo, newid, True)
@@ -428,7 +584,7 @@
     fp.seek(0)
     newnode = _patchtocommit(ui, repo, old, fp)
     # creating obs marker temp -> ()
-    compat.createmarkers(repo, [(repo[tempnode], ())], operation="uncommit")
+    obsolete.createmarkers(repo, [(repo[tempnode], ())], operation="uncommit")
     return newnode
 
 def _createtempcommit(ui, repo, old, match):
@@ -612,9 +768,9 @@
                                                          root.p2().node()],
                                                         commitopts=commitopts)
             phases.retractboundary(repo, tr, targetphase, [newid])
-            compat.createmarkers(repo, [(ctx, (repo[newid],))
-                                 for ctx in allctx], metadata=metadata,
-                                 operation="fold")
+            obsolete.createmarkers(repo, [(ctx, (repo[newid],))
+                                   for ctx in allctx], metadata=metadata,
+                                   operation="fold")
             # move bookmarks from old nodes to the new one
             # XXX: we should make rewriteutil.rewrite() handle such cases
             for ctx in allctx:
@@ -745,9 +901,9 @@
                     metadata['note'] = opts['note']
 
                 phases.retractboundary(repo, tr, targetphase, [newid])
-                compat.createmarkers(repo, [(ctx, (repo[newid],))
-                                            for ctx in allctx],
-                                     metadata=metadata, operation="metaedit")
+                obsolete.createmarkers(repo, [(ctx, (repo[newid],))
+                                              for ctx in allctx],
+                                       metadata=metadata, operation="metaedit")
             else:
                 ui.status(_("nothing changed\n"))
             tr.close()
@@ -923,7 +1079,7 @@
                 if movebookmark:
                     bookmarksmod.deactivate(repo)
                     bmchanges = [(bookactive, newnode.node())]
-                    compat.bookmarkapplychanges(repo, tr, bmchanges)
+                    repo._bookmarks.applychanges(repo, tr, bmchanges)
                 commands.update(ui, repo, newnode.rev())
                 ui.status(_('working directory now at %s\n')
                           % ui.label(str(newnode), 'evolve.node'))
@@ -939,8 +1095,8 @@
             metadata['note'] = opts['note']
 
         # create markers
-        compat.createmarkers(repo, relations, metadata=metadata,
-                             operation="prune")
+        obsolete.createmarkers(repo, relations, metadata=metadata,
+                               operation="prune")
 
         # informs that changeset have been pruned
         ui.status(_('%i changesets pruned\n') % len(precs))
@@ -1053,8 +1209,8 @@
             metadata = {}
             if opts.get('note'):
                 metadata['note'] = opts['note']
-            compat.createmarkers(repo, [(repo[rev], newcommits)],
-                                 metadata=metadata, operation="split")
+            obsolete.createmarkers(repo, [(repo[rev], newcommits)],
+                                   metadata=metadata, operation="split")
         tr.close()
     finally:
         # Restore the old branch
@@ -1113,7 +1269,7 @@
             if not (duplicate or allowdivergence):
                 # The user hasn't yet decided what to do with the revived
                 # cset, let's ask
-                sset = compat.successorssets(repo, ctx.node())
+                sset = obsutil.successorssets(repo, ctx.node())
                 nodivergencerisk = (len(sset) == 0 or
                                     (len(sset) == 1 and
                                      len(sset[0]) == 1 and
@@ -1145,8 +1301,8 @@
                 metadata = {}
                 if opts.get('note'):
                     metadata['note'] = opts['note']
-                compat.createmarkers(repo, [(ctx, (repo[new],))],
-                                     metadata=metadata, operation="touch")
+                obsolete.createmarkers(repo, [(ctx, (repo[new],))],
+                                       metadata=metadata, operation="touch")
             phases.retractboundary(repo, tr, ctx.phase(), [new])
             if ctx in repo[None].parents():
                 with repo.dirstate.parentchange():
@@ -1200,7 +1356,7 @@
                        origctx.description().split("\n", 1)[0]))
             stats = merge.graft(repo, origctx, origctx.p1(), ['local',
                                                               'destination'])
-            if stats[3]:
+            if compat.hasconflict(stats):
                 grabstate.addopts({'orignode': origctx.node(),
                                    'oldpctx': pctx.node()})
                 grabstate.save()
@@ -1237,7 +1393,7 @@
         if grabstate:
             grabstate.delete()
         newctx = repo[newnode] if newnode else pctx
-        compat.createmarkers(repo, [(origctx, (newctx,))], operation="grab")
+        obsolete.createmarkers(repo, [(origctx, (newctx,))], operation="grab")
 
         if newnode is None:
             ui.warn(_("note: grab of %d:%s created no changes to commit\n") %
--- a/hgext3rd/evolve/compat.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/compat.py	Mon Apr 23 11:04:27 2018 +0200
@@ -8,33 +8,17 @@
 
 import inspect
 
-import functools
-
 from mercurial import (
-    copies,
     context,
-    hg,
     mdiff,
     obsolete,
+    obsutil,
     revset,
     util,
-    wireproto,
+    vfs as vfsmod,
 )
 from mercurial.hgweb import hgweb_mod
 
-# hg < 4.2 compat
-try:
-    from mercurial import vfs as vfsmod
-    vfsmod.vfs
-except ImportError:
-    from mercurial import scmutil as vfsmod
-
-try:
-    from mercurial import obsutil
-    obsutil.closestpredecessors
-except ImportError:
-    obsutil = None
-
 # hg < 4.6 compat (c8e2d6ed1f9e)
 try:
     from mercurial import logcmdutil
@@ -53,89 +37,6 @@
 
 eh = exthelper.exthelper()
 
-# Wrap obsolete.creatmarkers and make it accept but ignore "operation" argument
-# for hg < 4.3
-originalcreatemarkers = obsolete.createmarkers
-while isinstance(originalcreatemarkers, functools.partial):
-    originalcreatemarkers = originalcreatemarkers.func
-if originalcreatemarkers.__code__.co_argcount < 6:
-    def createmarkers(repo, relations, flag=0, date=None, metadata=None,
-                      operation=None):
-        return obsolete.createmarkers(repo, relations, flag, date, metadata)
-else:
-    def createmarkers(*args, **kwargs):
-        return obsolete.createmarkers(*args, **kwargs)
-
-if not util.safehasattr(hg, '_copycache'):
-    # exact copy of relevantmarkers as in Mercurial-176d1a0ce385
-    # this fixes relevant markers computation for version < hg-4.3
-    @eh.wrapfunction(obsolete.obsstore, 'relevantmarkers')
-    def relevantmarkers(orig, self, nodes):
-        """return a set of all obsolescence markers relevant to a set of nodes.
-
-        "relevant" to a set of nodes mean:
-
-        - marker that use this changeset as successor
-        - prune marker of direct children on this changeset
-        - recursive application of the two rules on precursors of these markers
-
-        It is a set so you cannot rely on order.
-
-        Backport of mercurial changeset 176d1a0ce385 for version < 4.3
-        """
-
-        pendingnodes = set(nodes)
-        seenmarkers = set()
-        seennodes = set(pendingnodes)
-        precursorsmarkers = self.predecessors
-        succsmarkers = self.successors
-        children = self.children
-        while pendingnodes:
-            direct = set()
-            for current in pendingnodes:
-                direct.update(precursorsmarkers.get(current, ()))
-                pruned = [m for m in children.get(current, ()) if not m[1]]
-                direct.update(pruned)
-                pruned = [m for m in succsmarkers.get(current, ()) if not m[1]]
-                direct.update(pruned)
-            direct -= seenmarkers
-            pendingnodes = set([m[0] for m in direct])
-            seenmarkers |= direct
-            pendingnodes -= seennodes
-            seennodes |= pendingnodes
-        return seenmarkers
-
-# successors set move from mercurial.obsolete to mercurial.obsutil in 4.3
-def successorssets(*args, **kwargs):
-    func = getattr(obsutil, 'successorssets', None)
-    if func is None:
-        func = obsolete.successorssets
-    return func(*args, **kwargs)
-
-# allprecursors set move from mercurial.obsolete to mercurial.obsutil in 4.3
-# allprecursors  was renamed into allpredecessors in 4.4
-def allprecursors(*args, **kwargs):
-    func = getattr(obsutil, 'allpredecessors', None)
-    if func is None:
-        func = getattr(obsutil, 'allprecursors', None)
-        if func is None:
-            func = obsolete.allprecursors
-    return func(*args, **kwargs)
-
-# compatibility layer for mercurial < 4.3
-def bookmarkapplychanges(repo, tr, changes):
-    """Apply a list of changes to bookmarks
-    """
-    bookmarks = repo._bookmarks
-    if util.safehasattr(bookmarks, 'applychanges'):
-        return bookmarks.applychanges(repo, tr, changes)
-    for name, node in changes:
-        if node is None:
-            del bookmarks[name]
-        else:
-            bookmarks[name] = node
-    bookmarks.recordchange(tr)
-
 def isobsnotesupported():
     # hack to know obsnote is supported. The patches for obsnote support was
     # pushed before the obsfateprinter patches, so this will serve as a good
@@ -221,20 +122,6 @@
 if not util.safehasattr(obsolete, '_computephasedivergentset'):
     obsolete._computephasedivergentset = obsolete.cachefor('phasedivergent')(obsolete._computebumpedset)
 
-def startpager(ui, cmd):
-    """function to start a pager in case ui.pager() exists"""
-    if util.safehasattr(ui, 'pager'):
-        ui.pager(cmd)
-
-def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
-    # cannot use anything else until 4.3 support is dropped.
-    assert wctx.rev() is None
-    if copies.duplicatecopies.__code__.co_argcount < 5:
-        # pre 4.4 duplicatecopies compat
-        copies.duplicatecopies(repo, rev, fromrev, skiprev=skiprev)
-    else:
-        copies.duplicatecopies(repo, wctx, rev, fromrev, skiprev=skiprev)
-
 def memfilectx(repo, ctx, fctx, flags, copied, path):
     # XXX Would it be better at the module level?
     varnames = context.memfilectx.__init__.__code__.co_varnames
@@ -284,14 +171,31 @@
     parsedate = mercurial.util.parsedate
 
 def wireprotocommand(exthelper, name, args='', permission='pull'):
+    try:
+        # Since b4d85bc1
+        from mercurial.wireprotov1server import wireprotocommand
+        return wireprotocommand(name, args, permission=permission)
+    except (ImportError, AttributeError):
+        from mercurial import wireproto
+
     if 3 <= len(wireproto.wireprotocommand.func_defaults):
         return wireproto.wireprotocommand(name, args, permission=permission)
 
-    else:
-        # <= hg-4.5 permission must be registered in dictionnary
-        def decorator(func):
-            @eh.extsetup
-            def install(ui):
-                hgweb_mod.perms[name] = permission
-                wireproto.commands[name] = (func, args)
-        return decorator
+    # <= hg-4.5 permission must be registered in dictionnary
+    def decorator(func):
+        @eh.extsetup
+        def install(ui):
+            hgweb_mod.perms[name] = permission
+            wireproto.commands[name] = (func, args)
+    return decorator
+
+# mercurial <= 4.5 do not have the updateresult object
+try:
+    from mercurial.merge import updateresult
+except (ImportError, AttributeError):
+    updateresult = None
+
+def hasconflict(upres):
+    if updateresult is None:
+        return bool(upres[-1])
+    return bool(upres.unresolvedcount)
--- a/hgext3rd/evolve/evolvecmd.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/evolvecmd.py	Mon Apr 23 11:04:27 2018 +0200
@@ -23,7 +23,9 @@
     merge,
     node,
     obsolete,
+    obsutil,
     phases,
+    repair,
     scmutil,
     util,
 )
@@ -45,7 +47,6 @@
 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
 
 eh = exthelper.exthelper()
-_bookmarksupdater = rewriteutil.bookmarksupdater
 mergetoolopts = commands.mergetoolopts
 
 def _solveone(ui, repo, ctx, evolvestate, dryrun, confirm,
@@ -67,8 +68,8 @@
             result = _solveunstable(ui, repo, ctx, evolvestate,
                                     dryrun, confirm, progresscb)
         elif 'phasedivergent' == category:
-            result = _solvebumped(ui, repo, ctx, evolvestate,
-                                  dryrun, confirm, progresscb)
+            result = _solvephasedivergence(ui, repo, ctx, evolvestate,
+                                           dryrun, confirm, progresscb)
         elif 'contentdivergent' == category:
             result = _solvedivergent(ui, repo, ctx, evolvestate,
                                      dryrun, confirm, progresscb)
@@ -113,14 +114,14 @@
         ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
         return (False, '')
     obs = pctx
-    newer = compat.successorssets(repo, obs.node())
+    newer = obsutil.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 = compat.successorssets(repo, obs.node())
+        newer = obsutil.successorssets(repo, obs.node())
     if len(newer) > 1:
         msg = _("skipping %s: divergent rewriting. can't choose "
                 "destination\n") % obs
@@ -169,7 +170,8 @@
         if progresscb:
             progresscb()
         try:
-            newid = relocate(repo, orig, target, pctx, keepbranch)
+            newid = relocate(repo, orig, target, evolvestate, pctx,
+                             keepbranch, 'orphan')
             return (True, newid)
         except MergeFailure:
             ops = {'current': orig.node()}
@@ -178,12 +180,12 @@
             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"))
+                  " or use 'hg evolve --abort' to abort\n"))
             raise
 
-def _solvebumped(ui, repo, bumped, evolvestate, dryrun=False, confirm=False,
-                 progresscb=None):
-    """Stabilize a bumped changeset
+def _solvephasedivergence(ui, repo, bumped, evolvestate, dryrun=False,
+                          confirm=False, progresscb=None):
+    """Stabilize a phase divergent changeset
 
     returns a tuple (bool, newnode) where,
         bool: a boolean value indicating whether the instability was solved
@@ -224,28 +226,51 @@
         return (False, '')
     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)
+
+    # Checking for whether the phase-divergent changeset has common parents as
+    # it's precursors. Phase-divergent changeset and precursor having different
+    # parents is a result of when the changeset is rebased, grabbed, histedit or
+    # evolved or any other operation which can change parent. In such cases,
+    # when parents are not same, we first rebase the divergent changeset onto
+    # parent or precursor and then perform later steps
     if not list(repo.set('parents(%d) and parents(%d)', bumped.rev(), prec.rev())):
         # 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())
+            tmpid = relocate(repo, bumped, prec.p1(), evolvestate,
+                             category='phasedivergent')
             if tmpid is not None:
                 tmpctx = repo[tmpid]
-                compat.createmarkers(repo, [(bumped, (tmpctx,))],
-                                     operation='evolve')
+                obsolete.createmarkers(repo, [(bumped, (tmpctx,))],
+                                       operation='evolve')
         except MergeFailure:
-            repo.vfs.write('graftstate', bumped.hex() + '\n')
+            evolvestate['current'] = bumped.hex()
+            evolvestate['precursor'] = prec.hex()
+            evolvestate.save()
             repo.ui.write_err(_('evolution failed!\n'))
-            msg = _("fix conflict and run 'hg evolve --continue'\n")
+            msg = _("fix conflict then run 'hg evolve --continue' or "
+                    "use `hg evolve --abort`\n")
             repo.ui.write_err(msg)
             raise
+
+    return _resolvephasedivergent(ui, repo, prec, bumped, tmpctx)
+
+def _resolvephasedivergent(ui, repo, prec, bumped, tmpctx=None):
+
+    tr = repo.currenttransaction()
+    assert tr is not None
+    bmupdate = _bookmarksupdater(repo, bumped.node(), tr)
+    newid = None
+
+    # function to update the bookmark from the rebased changeset to new resolved
+    # changeset
+    rebasedbmupdate = None
+    if tmpctx and tmpctx.node() != bumped.node():
+        rebasedbmupdate = _bookmarksupdater(repo, tmpctx.node(), tr)
+        bumped = tmpctx
+
     # Create the new commit context
     repo.ui.status(_('computing new diff\n'))
     files = set()
@@ -261,6 +286,7 @@
         if precvalue != val:
             files.add(key)
     files.update(precmanifest)  # add missing files
+
     # commit it
     if files: # something to commit!
         def filectxfn(repo, ctx, path):
@@ -284,13 +310,16 @@
 
         newid = repo.commitctx(new)
     if newid is None:
-        compat.createmarkers(repo, [(tmpctx, ())], operation='evolve')
+        obsolete.createmarkers(repo, [(tmpctx, ())], operation='evolve')
         newid = prec.node()
     else:
         phases.retractboundary(repo, tr, bumped.phase(), [newid])
-        compat.createmarkers(repo, [(tmpctx, (repo[newid],))],
-                             flag=obsolete.bumpedfix, operation='evolve')
+        obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
+                               flag=obsolete.bumpedfix, operation='evolve')
     bmupdate(newid)
+    # if rebased happened, update bookmarks from there too
+    if rebasedbmupdate:
+        rebasedbmupdate(newid)
     repo.ui.status(_('committed as %s\n') % node.short(newid))
     # reroute the working copy parent to the new changeset
     with repo.dirstate.parentchange():
@@ -383,10 +412,9 @@
                          ancestor=base.node(),
                          mergeancestor=True)
     hg._showstats(repo, stats)
-    if stats[3]:
+    if compat.hasconflict(stats):
         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:
@@ -412,7 +440,7 @@
             # no changes
         else:
             new = repo['.']
-        compat.createmarkers(repo, [(other, (new,))], operation='evolve')
+        obsolete.createmarkers(repo, [(other, (new,))], operation='evolve')
         phases.retractboundary(repo, tr, other.phase(), [new.node()])
         return (True, new.node())
     finally:
@@ -456,7 +484,8 @@
     ordering.extend(sorted(dependencies))
     return ordering
 
-def relocate(repo, orig, dest, pctx=None, keepbranch=False):
+def relocate(repo, orig, dest, evolvestate, pctx=None, keepbranch=False,
+             category=None):
     """rewrites the orig rev on dest rev
 
     returns the node of new commit which is formed
@@ -481,16 +510,17 @@
     sha1s = re.findall(sha1re, commitmsg)
     unfi = repo.unfiltered()
     for sha1 in sha1s:
-        ctx = None
-        try:
-            ctx = unfi[sha1]
-        except error.RepoLookupError:
+        if util.safehasattr(scmutil, 'resolvehexnodeidprefix'): # > hg-4.6
+            fullnode = scmutil.resolvehexnodeidprefix(unfi, sha1)
+        else:
+            fullnode = unfi.changelog.index.partialmatch(sha1)
+        if fullnode is None:
             continue
-
+        ctx = unfi[fullnode]
         if not ctx.obsolete():
             continue
 
-        successors = compat.successorssets(repo, ctx.node(), cache)
+        successors = obsutil.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.
@@ -505,7 +535,7 @@
     assert tr is not None
     try:
         r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
-        if r[-1]: # some conflict
+        if compat.hasconflict(r): # some conflict
             raise error.Abort(_('unresolved merge conflicts '
                                 '(see hg help resolve)'))
         nodenew = _relocatecommit(repo, orig, commitmsg)
@@ -514,14 +544,14 @@
             repo.setparents(repo['.'].node(), node.nullid)
             repo.dirstate.write(tr)
             # fix up dirstate for copies and renames
-            compat.duplicatecopies(repo, repo[None], dest.rev(), orig.p1().rev())
+            copies.duplicatecopies(repo, repo[None], dest.rev(), orig.p1().rev())
 
         class LocalMergeFailure(MergeFailure, exc.__class__):
             pass
         exc.__class__ = LocalMergeFailure
         tr.close() # to keep changes in this transaction (e.g. dirstate)
         raise
-    _finalizerelocate(repo, orig, dest, nodenew, tr)
+    _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate)
     return nodenew
 
 def _relocatecommit(repo, orig, commitmsg):
@@ -543,26 +573,31 @@
         repo.ui.restoreconfig(backup)
     return nodenew
 
-def _finalizerelocate(repo, orig, dest, nodenew, tr):
+def _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate):
     destbookmarks = repo.nodebookmarks(dest.node())
     nodesrc = orig.node()
     oldbookmarks = repo.nodebookmarks(nodesrc)
     bmchanges = []
 
     if nodenew is not None:
-        compat.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))],
-                             operation='evolve')
+        obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))],
+                               operation='evolve')
         for book in oldbookmarks:
             bmchanges.append((book, nodenew))
+            evolvestate['bookmarkchanges'].append((book, nodesrc))
     else:
-        compat.createmarkers(repo, [(repo[nodesrc], ())], operation='evolve')
+        if category == 'orphan':
+            repo.ui.status(_("evolution of %d:%s created no changes "
+                             "to commit\n") % (orig.rev(), orig))
+        obsolete.createmarkers(repo, [(repo[nodesrc], ())], operation='evolve')
         # Behave like rebase, move bookmarks to dest
         for book in oldbookmarks:
+            evolvestate['bookmarkchanges'].append((book, nodesrc))
             bmchanges.append((book, dest.node()))
     for book in destbookmarks: # restore bookmark that rebase move
         bmchanges.append((book, dest.node()))
     if bmchanges:
-        compat.bookmarkapplychanges(repo, tr, bmchanges)
+        repo._bookmarks.applychanges(repo, tr, bmchanges)
 
 def _evolvemerge(repo, orig, dest, pctx, keepbranch):
     """Used by the evolve function to merge dest on top of pctx.
@@ -651,7 +686,7 @@
     """
     repo = ctx._repo.unfiltered()
     for base in repo.set('reverse(allprecursors(%d))', ctx.rev()):
-        newer = compat.successorssets(ctx._repo, base.node())
+        newer = obsutil.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:
@@ -699,7 +734,7 @@
     tovisit = list(parents(rev))
     while tovisit:
         r = tovisit.pop()
-        succsets = compat.successorssets(repo, tonode(r))
+        succsets = obsutil.successorssets(repo, tonode(r))
         if not succsets:
             tovisit.extend(parents(r))
         else:
@@ -719,6 +754,7 @@
     unselectedcategories = [c for c in troublecategories if c != targetcat]
     msg = None
     hint = None
+    retoverride = None
 
     troubled = {
         "orphan": repo.revs("orphan()"),
@@ -805,14 +841,20 @@
                     hint = hintmap['any+' + ('+'.join(othertroubles))]
                 else:
                     msg = _("no troubled changesets")
+                    # Exit with a 0 (success) status in this case.
+                    retoverride = 0
 
     assert msg is not None
     ui.write_err("%s\n" % msg)
     if hint:
         ui.write_err("(%s)\n" % hint)
-        return 2
+        ret = 2
     else:
-        return 1
+        ret = 1
+
+    if retoverride is not None:
+        return retoverride
+    return ret
 
 def _preparelistctxs(items, condition):
     return [item.hex() for item in items if condition(item)]
@@ -916,6 +958,9 @@
         if opts['stop']:
             raise error.Abort(_('cannot specify both "--stop" and'
                                 ' "--continue"'))
+        if opts['abort']:
+            raise error.Abort(_('cannot specify both "--abort" and'
+                                ' "--continue"'))
 
     if opts['stop']:
         if opts['any']:
@@ -924,6 +969,16 @@
             raise error.Abort(_('cannot specify both "--all" and "--stop"'))
         if opts['rev']:
             raise error.Abort(_('cannot specify both "--rev" and "--stop"'))
+        if opts['abort']:
+            raise error.Abort(_('cannot specify both "--abort" and "--stop"'))
+
+    if opts['abort']:
+        if opts['any']:
+            raise error.Abort(_('cannot specify both "--any" and "--abort"'))
+        if opts['all']:
+            raise error.Abort(_('cannot specify both "--all" and "--abort"'))
+        if opts['rev']:
+            raise error.Abort(_('cannot specify both "--rev" and "--abort"'))
 
     if opts['rev']:
         if opts['any']:
@@ -965,11 +1020,11 @@
     """Compute sets of commits divergent with a given one"""
     cache = {}
     base = {}
-    for n in compat.allprecursors(repo.obsstore, [ctx.node()]):
+    for n in obsutil.allpredecessors(repo.obsstore, [ctx.node()]):
         if n == ctx.node():
             # a node can't be a base for divergence with itself
             continue
-        nsuccsets = compat.successorssets(repo, n, cache)
+        nsuccsets = obsutil.successorssets(repo, n, cache)
         for nsuccset in nsuccsets:
             if ctx.node() in nsuccset:
                 # we are only interested in *other* successor sets
@@ -1007,7 +1062,9 @@
                            'current  working directory and its descendants')),
      ('c', 'continue', False, _('continue an interrupted evolution')),
      ('', 'stop', False, _('stop the interrupted evolution')),
-     ('l', 'list', False, 'provide details on troubled changesets in the repo'),
+     ('', 'abort', False, _('abort the interrupted evolution')),
+     ('l', 'list', False, _('provide details on troubled changesets'
+                            ' in the repo')),
     ] + mergetoolopts,
     _('[OPTIONS]...')
 )
@@ -1091,13 +1148,14 @@
     confirmopt = opts['confirm']
     revopt = opts['rev']
     stopopt = opts['stop']
+    abortopt = opts['abort']
 
     troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
     specifiedcategories = [t.replace('_', '')
                            for t in troublecategories
                            if opts[t]]
     if opts['list']:
-        compat.startpager(ui, 'evolve')
+        ui.pager('evolve')
         listtroubles(ui, repo, specifiedcategories, **opts)
         return
 
@@ -1166,6 +1224,11 @@
         ui.status(_('working directory is now at %s\n') % pctx)
         evolvestate.delete()
         return
+    elif abortopt:
+        if not evolvestate:
+            raise error.Abort(_('no interrupted evolve to stop'))
+        evolvestate.load()
+        return abortevolve(ui, repo, evolvestate)
     else:
         cmdutil.bailifchanged(repo)
 
@@ -1181,9 +1244,11 @@
             revs = _orderrevs(repo, revs)
 
         # cbor does not know how to serialize sets, using list for skippedrevs
-        stateopts = {'category': targetcat, 'replacements': {}, 'revs': revs,
-                     'confirm': confirmopt, 'startnode': startnode.node(),
-                     'skippedrevs': [], 'command': 'evolve', 'orphanmerge': False}
+        stateopts = {'category': targetcat, 'replacements': {},
+                     'revs': list(revs), 'confirm': confirmopt,
+                     'startnode': startnode.node(), 'skippedrevs': [],
+                     'command': 'evolve', 'orphanmerge': False,
+                     'bookmarkchanges': []}
         evolvestate.addopts(stateopts)
         for rev in revs:
             curctx = repo[rev]
@@ -1211,78 +1276,86 @@
     progresscb()
     _cleanup(ui, repo, startnode, showprogress)
 
+def abortevolve(ui, repo, evolvestate):
+    """ logic for handling of `hg evolve --abort`"""
+
+    with repo.wlock(), repo.lock():
+        evolvedctx = []
+        # boolean value to say whether we should strip or not
+        cleanup = True
+        startnode = evolvestate['startnode']
+        for old, new in evolvestate['replacements'].iteritems():
+            evolvedctx.append(repo[new[0]])
+        evolvedrevs = [c.rev() for c in evolvedctx]
+
+        # checking if phase changed of any of the evolved rev
+        immutable = [c for c in evolvedctx if not c.mutable()]
+        if immutable:
+            repo.ui.warn(_("cannot clean up public changesets: %s\n")
+                         % ', '.join(str(c) for c in immutable),
+                         hint=_("see 'hg help phases' for details"))
+            cleanup = False
+
+        # checking no new changesets are created on evolved revs
+        descendants = set()
+        if evolvedrevs:
+            descendants = set(repo.changelog.descendants(evolvedrevs))
+        if descendants - set(evolvedrevs):
+            repo.ui.warn(_("warning: new changesets detected on destination "
+                           "branch\n"))
+            cleanup = False
+
+        if cleanup:
+            if evolvedrevs:
+                strippoints = [c.node()
+                               for c in repo.set('roots(%ld)', evolvedrevs)]
+
+            # updating the working directory
+            hg.updaterepo(repo, startnode, True)
+
+            # Strip from the first evolved revision
+            if evolvedrevs:
+                # no backup of evolved cset versions needed
+                repair.strip(repo.ui, repo, strippoints, False)
+
+            tr = None
+            try:
+                tr = repo.transaction('evolve')
+                # restoring bookmarks at there original place
+                bmchanges = evolvestate['bookmarkchanges']
+                if bmchanges:
+                    repo._bookmarks.applychanges(repo, tr, bmchanges)
+                tr.close()
+            finally:
+                tr.release()
+
+            evolvestate.delete()
+            ui.status(_('evolve aborted\n'))
+            ui.status(_('working directory is now at %s\n')
+                      % node.hex(startnode)[:12])
+        else:
+            raise error.Abort(_("unable to abort interrupted evolve, use 'hg "
+                                "evolve --stop' to stop evolve"))
+
 def continueevolve(ui, repo, evolvestate, progresscb):
     """logic for handling of `hg evolve --continue`"""
-    orig = repo[evolvestate['current']]
+
     with repo.wlock(), repo.lock():
-        ctx = orig
-        source = ctx.extra().get('source')
-        extra = {}
-        if source:
-            extra['source'] = source
-            extra['intermediate-source'] = ctx.hex()
+        if (evolvestate['command'] == 'next' or
+            evolvestate['category'] == 'orphan'):
+            _completeorphan(ui, repo, evolvestate)
+        elif evolvestate['category'] == 'phasedivergent':
+            _completephasedivergent(ui, repo, evolvestate)
         else:
-            extra['source'] = ctx.hex()
-        user = ctx.user()
-        date = ctx.date()
-        message = ctx.description()
-        ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx,
-                                                message.split('\n', 1)[0]))
-        targetphase = max(ctx.phase(), phases.draft)
-        overrides = {('phases', 'new-commit'): targetphase}
-
-        ctxparents = orig.parents()
-        if len(ctxparents) == 2:
-            currentp1 = repo.dirstate.parents()[0]
-            p1obs = ctxparents[0].obsolete()
-            p2obs = ctxparents[1].obsolete()
-            # asumming that the parent of current wdir is successor of one
-            # of p1 or p2 of the original changeset
-            if p1obs and not p2obs:
-                # p1 is obsolete and p2 is not obsolete, current working
-                # directory parent should be successor of p1, so we should
-                # set dirstate parents to (succ of p1, p2)
-                with repo.dirstate.parentchange():
-                    repo.dirstate.setparents(currentp1,
-                                             ctxparents[1].node())
-            elif p2obs and not p1obs:
-                # p2 is obsolete and p1 is not obsolete, current working
-                # directory parent should be successor of p2, so we should
-                # set dirstate parents to (succ of p2, p1)
-                with repo.dirstate.parentchange():
-                    repo.dirstate.setparents(ctxparents[0].node(),
-                                             currentp1)
-
-            else:
-                # both the parents were obsoleted, if orphanmerge is set, we
-                # are processing the second parent first (to keep parent order)
-                if evolvestate.get('orphanmerge'):
-                    with repo.dirstate.parentchange():
-                        repo.dirstate.setparents(ctxparents[0].node(),
-                                                 currentp1)
-                pass
-
-        with repo.ui.configoverride(overrides, 'evolve-continue'):
-            node = repo.commit(text=message, user=user,
-                               date=date, extra=extra)
-
-        # resolving conflicts can lead to empty wdir and node can be None in
-        # those cases
-        newctx = repo[node] if node is not None else repo['.']
-        compat.createmarkers(repo, [(ctx, (newctx,))], operation='evolve')
+            repo.ui.status(_("continuing interrupted '%s' resolution is not yet"
+                             " supported\n") % evolvestate['category'])
+            return
 
         # make sure we are continuing evolve and not `hg next --evolve`
         if evolvestate['command'] == 'evolve':
-            evolvestate['replacements'][ctx.node()] = node
             category = evolvestate['category']
             confirm = evolvestate['confirm']
             unfi = repo.unfiltered()
-            if evolvestate['orphanmerge']:
-                # processing a merge changeset with both parents obsoleted,
-                # stabilized on second parent, insert in front of list to
-                # re-process to stabilize on first parent
-                evolvestate['revs'].insert(0, repo[node].rev())
-                evolvestate['orphanmerge'] = False
             for rev in evolvestate['revs']:
                 # XXX: prevent this lookup by storing nodes instead of revnums
                 curctx = unfi[rev]
@@ -1295,3 +1368,109 @@
                     else:
                         evolvestate['skippedrevs'].append(curctx.node())
         return
+
+def _completephasedivergent(ui, repo, evolvestate):
+    """function to complete the interrupted phase-divergence resolution.
+
+    First completes the relocation of the commit and then process resolving
+    phase-divergence"""
+
+    tr = None
+    try:
+        # need to start transaction for bookmark changes
+        tr = repo.transaction('evolve')
+        node = _completerelocation(ui, repo, evolvestate)
+        # resolving conflicts can lead to empty wdir and node can be None in
+        # those cases
+        ctx = repo[evolvestate['current']]
+        newctx = repo[node] if node is not None else repo['.']
+        obsolete.createmarkers(repo, [(ctx, (newctx,))], operation='evolve')
+
+        # now continuing the phase-divergence resolution part
+        prec = repo[evolvestate['precursor']]
+        _resolvephasedivergent(ui, repo, prec, ctx, newctx)
+        evolvestate['replacements'][ctx.node()] = node
+        tr.close()
+    finally:
+        tr.release()
+
+def _completeorphan(ui, repo, evolvestate):
+    """function to complete the interrupted orphan resolution"""
+
+    node = _completerelocation(ui, repo, evolvestate)
+    # resolving conflicts can lead to empty wdir and node can be None in
+    # those cases
+    ctx = repo[evolvestate['current']]
+    if node is None:
+        repo.ui.status(_("evolution of %d:%s created no changes"
+                         " to commit\n") % (ctx.rev(), ctx))
+    newctx = repo[node] if node is not None else repo['.']
+    obsolete.createmarkers(repo, [(ctx, (newctx,))], operation='evolve')
+
+    # make sure we are continuing evolve and not `hg next --evolve`
+    if evolvestate['command'] == 'evolve':
+        evolvestate['replacements'][ctx.node()] = node
+        if evolvestate['orphanmerge']:
+            # processing a merge changeset with both parents obsoleted,
+            # stabilized on second parent, insert in front of list to
+            # re-process to stabilize on first parent
+            evolvestate['revs'].insert(0, repo[node].rev())
+            evolvestate['orphanmerge'] = False
+
+def _completerelocation(ui, repo, evolvestate):
+    """function to complete the interrupted relocation of a commit
+    return the new node formed
+    """
+
+    orig = repo[evolvestate['current']]
+    ctx = orig
+    source = ctx.extra().get('source')
+    extra = {}
+    if source:
+        extra['source'] = source
+        extra['intermediate-source'] = ctx.hex()
+    else:
+        extra['source'] = ctx.hex()
+    user = ctx.user()
+    date = ctx.date()
+    message = ctx.description()
+    ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx,
+                                            message.split('\n', 1)[0]))
+    targetphase = max(ctx.phase(), phases.draft)
+    overrides = {('phases', 'new-commit'): targetphase}
+
+    ctxparents = orig.parents()
+    if len(ctxparents) == 2:
+        currentp1 = repo.dirstate.parents()[0]
+        p1obs = ctxparents[0].obsolete()
+        p2obs = ctxparents[1].obsolete()
+        # asumming that the parent of current wdir is successor of one
+        # of p1 or p2 of the original changeset
+        if p1obs and not p2obs:
+            # p1 is obsolete and p2 is not obsolete, current working
+            # directory parent should be successor of p1, so we should
+            # set dirstate parents to (succ of p1, p2)
+            with repo.dirstate.parentchange():
+                repo.dirstate.setparents(currentp1,
+                                         ctxparents[1].node())
+        elif p2obs and not p1obs:
+            # p2 is obsolete and p1 is not obsolete, current working
+            # directory parent should be successor of p2, so we should
+            # set dirstate parents to (succ of p2, p1)
+            with repo.dirstate.parentchange():
+                repo.dirstate.setparents(ctxparents[0].node(),
+                                         currentp1)
+
+        else:
+            # both the parents were obsoleted, if orphanmerge is set, we
+            # are processing the second parent first (to keep parent order)
+            if evolvestate.get('orphanmerge'):
+                with repo.dirstate.parentchange():
+                    repo.dirstate.setparents(ctxparents[0].node(),
+                                             currentp1)
+            pass
+
+    with repo.ui.configoverride(overrides, 'evolve-continue'):
+        node = repo.commit(text=message, user=user,
+                           date=date, extra=extra)
+    return node
--- a/hgext3rd/evolve/genericcaches.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/genericcaches.py	Mon Apr 23 11:04:27 2018 +0200
@@ -8,25 +8,12 @@
 
 import abc
 import struct
-import time
-import os
 
 from mercurial import (
     node,
-    pycompat,
     util,
 )
 
-# prior to hg-4.2 there are not util.timer
-if util.safehasattr(util, 'timer'):
-    timer = util.timer
-elif util.safehasattr(time, "perf_counter"):
-    timer = time.perf_counter
-elif getattr(pycompat, 'osname', os.name) == 'nt':
-    timer = time.clock
-else:
-    timer = time.time
-
 class incrementalcachebase(object):
     """base class for incremental cache from append only source
 
@@ -129,9 +116,9 @@
                         % self._cachename)
             self.clear(reset=True)
 
-        starttime = timer()
+        starttime = util.timer()
         self._updatefrom(repo, data)
-        duration = timer() - starttime
+        duration = util.timer() - starttime
         summary = self._updatesummary(data)
         repo.ui.log('cache', 'updated %s in %.4f seconds (%s)\n',
                     self._cachename, duration, summary)
--- a/hgext3rd/evolve/metadata.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/metadata.py	Mon Apr 23 11:04:27 2018 +0200
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-__version__ = '7.3.1.dev'
-testedwith = '4.1.3 4.2.3 4.3.2 4.4.2 4.5.2'
-minimumhgversion = '4.1'
+__version__ = '8.0.0.dev'
+testedwith = '4.3.2 4.4.2 4.5.2'
+minimumhgversion = '4.3'
 buglink = 'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obscache.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/obscache.py	Mon Apr 23 11:04:27 2018 +0200
@@ -9,23 +9,17 @@
 
 import errno
 import hashlib
-import os
 import struct
-import time
 import weakref
 
 from mercurial import (
-    error,
     localrepo,
     obsolete,
     phases,
-    pycompat,
     node,
     util,
 )
 
-from mercurial.i18n import _
-
 from . import (
     compat,
     exthelper,
@@ -33,17 +27,6 @@
 
 eh = exthelper.exthelper()
 
-# prior to hg-4.2 there are not util.timer
-if util.safehasattr(util, 'timer'):
-    timer = util.timer
-elif util.safehasattr(time, "perf_counter"):
-    timer = time.perf_counter
-elif getattr(pycompat, 'osname', os.name) == 'nt':
-    timer = time.clock
-else:
-    timer = time.time
-
-
 obsstorefilecache = localrepo.localrepository.obsstore
 
 # obsstore is a filecache so we have do to some spacial dancing
@@ -98,30 +81,6 @@
 
     return obsstore
 
-if obsolete._fm0readmarkers.__code__.co_argcount > 2:
-    # hg-4.3+ has the "offset" parameter, and _fm?readmarkers also have an
-    # extra "stop" parameter
-    # Note that _readmarkers is wrapped by @util.nogc, so its co_argcount is
-    # misleadingly 0. So we check _fm0readmarkers instead, which increased its
-    # argument count in the same changeset (5d3ba439).
-    _readmarkers = obsolete._readmarkers
-else:
-    # XXX copied as is from Mercurial 4.2 and added the "offset" parameters
-    @util.nogc
-    def _readmarkers(data, offset=None):
-        """Read and enumerate markers from raw data"""
-        off = 0
-        diskversion = struct.unpack('>B', data[off:off + 1])[0]
-        if offset is None:
-            off += 1
-        else:
-            assert 1 <= offset
-            off = offset
-        if diskversion not in obsolete.formats:
-            raise error.Abort(_('parsing obsolete marker: unknown version %r')
-                              % diskversion)
-        return diskversion, obsolete.formats[diskversion][0](data, off)
-
 def markersfrom(obsstore, byteoffset, firstmarker):
     if not firstmarker:
         return list(obsstore)
@@ -130,7 +89,7 @@
         return obsstore._all[firstmarker:]
     else:
         obsdata = obsstore.svfs.tryread('obsstore')
-        return _readmarkers(obsdata, byteoffset)[1]
+        return obsolete._readmarkers(obsdata, byteoffset)[1]
 
 
 class dualsourcecache(object):
@@ -224,11 +183,11 @@
             repo.ui.log('evoext-cache', 'strip detected, %s cache reset\n' % self._cachename)
             self.clear(reset=True)
 
-        starttime = timer()
+        starttime = util.timer()
         revs = list(revs)
         obsmarkers = list(obsmarkers)
         self._updatefrom(repo, revs, obsmarkers)
-        duration = timer() - starttime
+        duration = util.timer() - starttime
         repo.ui.log('evoext-cache', 'updated %s in %.4f seconds (%sr, %so)\n',
                     self._cachename, duration, len(revs), len(obsmarkers))
 
@@ -470,11 +429,7 @@
     """the set of obsolete revisions"""
     obs = set()
     repo = repo.unfiltered()
-    if util.safehasattr(repo._phasecache, 'getrevset'):
-        notpublic = repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
-    else:
-        # < hg-4.2 compat
-        notpublic = repo.revs("not public()")
+    notpublic = repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
     if notpublic:
         obscache = repo.obsstore.obscache
         # Since we warm the cache at the end of every transaction, the cache
@@ -527,8 +482,8 @@
             @localrepo.unfilteredmethod
             def updatecaches(self, tr=None, **kwargs):
                 super(obscacherepo, self).updatecaches(tr, **kwargs)
-                self.obsstore.obscache.update(repo)
-                self.obsstore.obscache.save(repo)
+                self.obsstore.obscache.update(self)
+                self.obsstore.obscache.save(self)
 
         else:
             def transaction(self, *args, **kwargs):
--- a/hgext3rd/evolve/obsdiscovery.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/obsdiscovery.py	Mon Apr 23 11:04:27 2018 +0200
@@ -24,10 +24,8 @@
 
 import hashlib
 import heapq
-import os
 import sqlite3
 import struct
-import time
 import weakref
 
 from mercurial import (
@@ -38,11 +36,9 @@
     localrepo,
     node,
     obsolete,
-    pycompat,
     scmutil,
     setdiscovery,
     util,
-    wireproto,
 )
 from mercurial.i18n import _
 
@@ -55,15 +51,14 @@
     stablerangecache,
 )
 
-# prior to hg-4.2 there are not util.timer
-if util.safehasattr(util, 'timer'):
-    timer = util.timer
-elif util.safehasattr(time, "perf_counter"):
-    timer = time.perf_counter
-elif getattr(pycompat, 'osname', os.name) == 'nt':
-    timer = time.clock
-else:
-    timer = time.time
+try: # < hg-4.6
+    from mercurial import wireproto as wireprototypes
+    wireprotov1server = wireprototypes
+    from mercurial.wireproto import wirepeer, encodelist, decodelist
+except (ImportError, AttributeError):
+    from mercurial import wireprototypes, wireprotov1server
+    from mercurial.wireprotov1peer import wirepeer
+    from mercurial.wireprototypes import encodelist, decodelist
 
 _pack = struct.pack
 _unpack = struct.unpack
@@ -146,7 +141,7 @@
                      initialsamplesize=100,
                      fullsamplesize=200):
     missing = set()
-    starttime = timer()
+    starttime = util.timer()
 
     heads = local.revs('heads(%ld)', probeset)
     local.stablerange.warmup(local)
@@ -236,7 +231,7 @@
                     unit=_("queries"))
     ui.progress(_("comparing obsmarker with other"), None)
     local.obsstore.rangeobshashcache.save(local)
-    duration = timer() - starttime
+    duration = util.timer() - starttime
     logmsg = ('obsdiscovery, %d/%d mismatch'
               ' - %d obshashrange queries in %.4f seconds\n')
     logmsg %= (len(missing), len(probeset), querycount, duration)
@@ -668,22 +663,22 @@
     index = _unpack(_indexformat, data[-_indexsize:])[0]
     return (headnode, index)
 
-@eh.addattr(wireproto.wirepeer, 'evoext_obshashrange_v1')
+@eh.addattr(wirepeer, 'evoext_obshashrange_v1')
 def peer_obshashrange_v0(self, ranges):
     binranges = [_encrange(r) for r in ranges]
-    encranges = wireproto.encodelist(binranges)
+    encranges = encodelist(binranges)
     d = self._call("evoext_obshashrange_v1", ranges=encranges)
     try:
-        return wireproto.decodelist(d)
+        return decodelist(d)
     except ValueError:
         self._abort(error.ResponseError(_("unexpected response:"), d))
 
 @compat.wireprotocommand(eh, 'evoext_obshashrange_v1', 'ranges')
 def srv_obshashrange_v1(repo, proto, ranges):
-    ranges = wireproto.decodelist(ranges)
+    ranges = decodelist(ranges)
     ranges = [_decrange(r) for r in ranges]
     hashes = _obshashrange_v0(repo, ranges)
-    return wireproto.encodelist(hashes)
+    return encodelist(hashes)
 
 def _useobshashrange(repo):
     base = repo.ui.configbool('experimental', 'obshashrange', False)
@@ -716,20 +711,20 @@
 
         # Compat hg 4.6+ (2f7290555c96)
         if bytesresponse:
-            from mercurial import wireprototypes
             caps = wireprototypes.bytesresponse(caps)
     return caps
 
 @eh.extsetup
 def obshashrange_extsetup(ui):
     ###
-    extensions.wrapfunction(wireproto, 'capabilities', _obshashrange_capabilities)
+    extensions.wrapfunction(wireprotov1server, 'capabilities',
+                            _obshashrange_capabilities)
     # wrap command content
-    oldcap, args = wireproto.commands['capabilities']
+    oldcap, args = wireprotov1server.commands['capabilities']
 
     def newcap(repo, proto):
         return _obshashrange_capabilities(oldcap, repo, proto)
-    wireproto.commands['capabilities'] = (newcap, args)
+    wireprotov1server.commands['capabilities'] = (newcap, args)
 
 #############################
 ### Tree Hash computation ###
@@ -827,30 +822,30 @@
 def local_obshash1(peer, nodes):
     return _obshash(peer._repo, nodes, version=1)
 
-@eh.addattr(wireproto.wirepeer, 'evoext_obshash')
+@eh.addattr(wirepeer, 'evoext_obshash')
 def peer_obshash(self, nodes):
-    d = self._call("evoext_obshash", nodes=wireproto.encodelist(nodes))
+    d = self._call("evoext_obshash", nodes=encodelist(nodes))
     try:
-        return wireproto.decodelist(d)
+        return decodelist(d)
     except ValueError:
         self._abort(error.ResponseError(_("unexpected response:"), d))
 
-@eh.addattr(wireproto.wirepeer, 'evoext_obshash1')
+@eh.addattr(wirepeer, 'evoext_obshash1')
 def peer_obshash1(self, nodes):
-    d = self._call("evoext_obshash1", nodes=wireproto.encodelist(nodes))
+    d = self._call("evoext_obshash1", nodes=encodelist(nodes))
     try:
-        return wireproto.decodelist(d)
+        return decodelist(d)
     except ValueError:
         self._abort(error.ResponseError(_("unexpected response:"), d))
 
 @compat.wireprotocommand(eh, 'evoext_obshash', 'nodes')
 def srv_obshash(repo, proto, nodes):
-    return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes)))
+    return encodelist(_obshash(repo, decodelist(nodes)))
 
 @compat.wireprotocommand(eh, 'evoext_obshash1', 'nodes')
 def srv_obshash1(repo, proto, nodes):
-    return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes),
-                                version=1))
+    return encodelist(_obshash(repo, decodelist(nodes),
+                      version=1))
 
 def _obshash_capabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
@@ -872,19 +867,19 @@
 
         # Compat hg 4.6+ (2f7290555c96)
         if bytesresponse:
-            from mercurial import wireprototypes
             caps = wireprototypes.bytesresponse(caps)
     return caps
 
 @eh.extsetup
 def obshash_extsetup(ui):
-    extensions.wrapfunction(wireproto, 'capabilities', _obshash_capabilities)
+    extensions.wrapfunction(wireprotov1server, 'capabilities',
+                            _obshash_capabilities)
     # wrap command content
-    oldcap, args = wireproto.commands['capabilities']
+    oldcap, args = wireprotov1server.commands['capabilities']
 
     def newcap(repo, proto):
         return _obshash_capabilities(oldcap, repo, proto)
-    wireproto.commands['capabilities'] = (newcap, args)
+    wireprotov1server.commands['capabilities'] = (newcap, args)
 
 ##########################################
 ###  trigger discovery during exchange ###
--- a/hgext3rd/evolve/obsexchange.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/obsexchange.py	Mon Apr 23 11:04:27 2018 +0200
@@ -24,15 +24,8 @@
     obsolete,
     pushkey,
     util,
-    wireproto,
 )
 
-try:
-    from mercurial import wireprotoserver
-    wireprotoserver.handlewsgirequest
-except (ImportError, AttributeError):
-    wireprotoserver = None
-
 from mercurial.hgweb import common as hgwebcommon
 
 from . import (
@@ -60,8 +53,15 @@
 
 @eh.uisetup
 def addgetbundleargs(self):
-    wireproto.gboptsmap['evo_obscommon'] = 'nodes'
-    wireproto.gboptsmap['evo_missing_nodes'] = 'nodes'
+    try:
+        from mercurial import wireprototypes
+        gboptsmap = wireprototypes.GETBUNDLE_ARGUMENTS
+    except (ImportError, AttributeError):
+        # <= hg 4.5
+        from mercurial import wireproto
+        gboptsmap = wireproto.gboptsmap
+    gboptsmap['evo_obscommon'] = 'nodes'
+    gboptsmap['evo_missing_nodes'] = 'nodes'
 
 @eh.wrapfunction(exchange, '_pullbundle2extraprepare')
 def _addobscommontob2pull(orig, pullop, kwargs):
@@ -132,7 +132,15 @@
 
 @eh.extsetup
 def extsetup_obscommon(ui):
-    wireproto.gboptsmap['evo_obscommon'] = 'nodes'
+    try:
+        from mercurial import wireprototypes, wireprotov1server
+        gboptsmap = wireprototypes.GETBUNDLE_ARGUMENTS
+    except (ImportError, AttributeError):
+        # <= hg 4.5
+        from mercurial import wireproto
+        gboptsmap = wireproto.gboptsmap
+        wireprotov1server = wireproto
+    gboptsmap['evo_obscommon'] = 'nodes'
 
     # wrap module content
     origfunc = exchange.getbundle2partsmapping['obsmarkers']
@@ -141,13 +149,14 @@
         return _getbundleobsmarkerpart(origfunc, *args, **kwargs)
     exchange.getbundle2partsmapping['obsmarkers'] = newfunc
 
-    extensions.wrapfunction(wireproto, 'capabilities', _obscommon_capabilities)
+    extensions.wrapfunction(wireprotov1server, 'capabilities',
+                            _obscommon_capabilities)
     # wrap command content
-    oldcap, args = wireproto.commands['capabilities']
+    oldcap, args = wireprotov1server.commands['capabilities']
 
     def newcap(repo, proto):
         return _obscommon_capabilities(oldcap, repo, proto)
-    wireproto.commands['capabilities'] = (newcap, args)
+    wireprotov1server.commands['capabilities'] = (newcap, args)
 
 def _pushobsmarkers(repo, data):
     tr = lock = None
@@ -170,7 +179,12 @@
     data = fp.getvalue()
     fp.close()
     _pushobsmarkers(repo, data)
-    return wireproto.pushres(0)
+    try:
+        from mercurial import wireprototypes
+        wireprototypes.pushres # force demandimport
+    except (ImportError, AttributeError):
+        from mercurial import wireproto as wireprototypes
+    return wireprototypes.pushres(0)
 
 def _getobsmarkersstream(repo, heads=None, common=None):
     """Get a binary stream for all markers relevant to `::<heads> - ::<common>`
@@ -202,17 +216,23 @@
     Serves relevant to changeset between heads and common. The stream is prefix
     by a -string- representation of an integer. This integer is the size of the
     stream."""
-    opts = wireproto.options('', ['heads', 'common'], others)
+    try:
+        from mercurial import wireprototypes, wireprotov1server
+        wireprototypes.pushres # force demandimport
+    except (ImportError, AttributeError):
+        from mercurial import wireproto as wireprototypes
+        wireprotov1server = wireprototypes
+    opts = wireprotov1server.options('', ['heads', 'common'], others)
     for k, v in opts.iteritems():
         if k in ('heads', 'common'):
-            opts[k] = wireproto.decodelist(v)
+            opts[k] = wireprototypes.decodelist(v)
     obsdata = _getobsmarkersstream(repo, **opts)
     finaldata = StringIO()
     obsdata = obsdata.getvalue()
     finaldata.write('%20i' % len(obsdata))
     finaldata.write(obsdata)
     finaldata.seek(0)
-    return wireproto.streamres(reader=finaldata, v1compressible=True)
+    return wireprototypes.streamres(reader=finaldata, v1compressible=True)
 
 abortmsg = "won't exchange obsmarkers through pushkey"
 hint = "upgrade your client or server to use the bundle2 protocol"
--- a/hgext3rd/evolve/obshistory.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/obshistory.py	Mon Apr 23 11:04:27 2018 +0200
@@ -83,7 +83,7 @@
 
     Returns 0 on success.
     """
-    compat.startpager(ui, 'obslog')
+    ui.pager('obslog')
     revs = list(revs) + opts['rev']
     if not revs:
         revs = ['.']
@@ -172,15 +172,11 @@
     succname = "changeset-description"
 
     d = compat.strdiff(basedesc, succdesc, basename, succname)
-    # mercurial 4.1 and before return the patch directly
-    if not isinstance(d, tuple):
-        patch = d
-    else:
-        uheaders, hunks = d
+    uheaders, hunks = d
 
-        # Copied from patch.diff
-        text = ''.join(sum((list(hlines) for hrange, hlines in hunks), []))
-        patch = "\n".join(uheaders + [text])
+    # Copied from patch.diff
+    text = ''.join(sum((list(hlines) for hrange, hlines in hunks), []))
+    patch = "\n".join(uheaders + [text])
 
     return patch
 
@@ -623,9 +619,6 @@
 def _getdifflines(iterdiff):
     """return a cleaned up lines"""
     try:
-        # XXX-COMPAT Mercurial 4.1 compat
-        if isinstance(iterdiff, list) and len(iterdiff) == 0:
-            return None
         lines = iterdiff.next()
     except StopIteration:
         return None
@@ -727,7 +720,7 @@
     or has diverged
     """
     if successorssets is None:
-        successorssets = compat.successorssets(repo, revnode)
+        successorssets = obsutil.successorssets(repo, revnode)
 
     fate = _getobsfate(successorssets)
 
--- a/hgext3rd/evolve/rewriteutil.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/rewriteutil.py	Mon Apr 23 11:04:27 2018 +0200
@@ -86,7 +86,7 @@
         oldbookmarks = repo.nodebookmarks(oldid)
         bmchanges = [(b, newid) for b in oldbookmarks]
         if bmchanges:
-            compat.bookmarkapplychanges(repo, tr, bmchanges)
+            repo._bookmarks.applychanges(repo, tr, bmchanges)
     return updatebookmarks
 
 def disallowednewunstable(repo, revs):
@@ -125,7 +125,7 @@
         bmchanges = []
         for bookmark in bookmarks:
             bmchanges.append((bookmark, None))
-        compat.bookmarkapplychanges(repo, tr, bmchanges)
+        repo._bookmarks.applychanges(repo, tr, bmchanges)
         tr.close()
         for bookmark in sorted(bookmarks):
             repo.ui.write(_("bookmark '%s' deleted\n") % bookmark)
--- a/hgext3rd/evolve/safeguard.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/safeguard.py	Mon Apr 23 11:04:27 2018 +0200
@@ -25,17 +25,17 @@
 
         def checkpush(self, pushop):
             super(noautopublishrepo, self).checkpush(pushop)
-            behavior = repo.ui.config('experimental', 'auto-publish', 'default')
+            behavior = self.ui.config('experimental', 'auto-publish', 'default')
             remotephases = pushop.remote.listkeys('phases')
             publishing = remotephases.get('publishing', False)
             if behavior in ('warn', 'abort') and publishing:
                 if pushop.revs is None:
-                    published = repo.filtered('served').revs("not public()")
+                    published = self.filtered('served').revs("not public()")
                 else:
-                    published = repo.revs("::%ln - public()", pushop.revs)
+                    published = self.revs("::%ln - public()", pushop.revs)
                 if published:
                     if behavior == 'warn':
-                        repo.ui.warn(_('%i changesets about to be published\n') % len(published))
+                        self.ui.warn(_('%i changesets about to be published\n') % len(published))
                     elif behavior == 'abort':
                         msg = _('push would publish 1 changesets')
                         hint = _("behavior controlled by 'experimental.auto-publish' config")
--- a/hgext3rd/evolve/stablerange.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/stablerange.py	Mon Apr 23 11:04:27 2018 +0200
@@ -11,13 +11,11 @@
 import functools
 import heapq
 import math
-import os
 import time
 
 from mercurial import (
     error,
     node as nodemod,
-    pycompat,
     scmutil,
     util,
 )
@@ -37,16 +35,6 @@
 eh.merge(stablesort.eh)
 eh.merge(firstmergecache.eh)
 
-# prior to hg-4.2 there are not util.timer
-if util.safehasattr(util, 'timer'):
-    timer = util.timer
-elif util.safehasattr(time, "perf_counter"):
-    timer = time.perf_counter
-elif getattr(pycompat, 'osname', os.name) == 'nt':
-    timer = time.clock
-else:
-    timer = time.time
-
 
 #################################
 ### Stable Range computation  ###
@@ -592,7 +580,7 @@
         #
         # we use the revnumber as an approximation for depth
         ui = repo.ui
-        starttime = timer()
+        starttime = util.timer()
 
         if upto is None:
             upto = len(cl) - 1
@@ -608,7 +596,8 @@
         rangeheap = []
         for idx, r in enumerate(revs):
             if not idx % 1000:
-                ui.progress(_("filling depth cache"), idx, total=nbrevs)
+                ui.progress(_("filling depth cache"), idx, total=nbrevs,
+                            unit=_("changesets"))
             # warm up depth
             self.depthrev(repo, r)
             rangeheap.append((-r, (r, 0)))
@@ -633,7 +622,8 @@
                     progress_new = time.time()
                     if (1 < progress_each) and (0.1 < progress_new - progress_last):
                         progress_each /= 10
-                    ui.progress(_("filling stablerange cache"), seen, total=nbrevs)
+                    ui.progress(_("filling stablerange cache"), seen,
+                                total=nbrevs, unit=_("changesets"))
                     progress_last = progress_new
                 seen += 1
                 original.remove(value) # might have been added from other source
@@ -647,7 +637,7 @@
         self._tiprev = upto
         self._tipnode = cl.node(upto)
 
-        duration = timer() - starttime
+        duration = util.timer() - starttime
         repo.ui.log('evoext-cache', 'updated stablerange cache in %.4f seconds\n',
                     duration)
 
--- a/hgext3rd/evolve/stablerangecache.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/stablerangecache.py	Mon Apr 23 11:04:27 2018 +0200
@@ -66,7 +66,8 @@
                     progress_new = time.time()
                     if (1 < progress_each) and (0.1 < progress_new - progress_last):
                         progress_each /= 10
-                    ui.progress(_("filling stablerange cache"), seen, total=total)
+                    ui.progress(_("filling stablerange cache"), seen,
+                                total=total, unit=_("changesets"))
                     progress_last = progress_new
                 seen += 1
                 original.remove(rangeid) # might have been added from other source
@@ -353,8 +354,8 @@
 
         @localrepo.unfilteredpropertycache
         def stablerange(self):
-            cache = mergepointsql(repo)
-            cache.update(repo)
+            cache = mergepointsql(self)
+            cache.update(self)
             return cache
 
         @localrepo.unfilteredmethod
--- a/hgext3rd/evolve/templatekw.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/templatekw.py	Mon Apr 23 11:04:27 2018 +0200
@@ -47,13 +47,8 @@
         """List of strings. Evolution troubles affecting the changeset
         (zero or more of "unstable", "divergent" or "bumped")."""
         ctx = args['ctx']
-        try:
-            # specify plural= explicitly to trigger TypeError on hg < 4.2
-            return templatekw.showlist('trouble', ctx.instabilities(), args,
-                                       plural='troubles')
-        except TypeError:
-            return templatekw.showlist('trouble', ctx.instabilities(), plural='troubles',
-                                       **args)
+        return templatekw.showlist('trouble', ctx.instabilities(), args,
+                                   plural='troubles')
 
 if util.safehasattr(templatekw, 'showpredecessors'):
     templatekw.keywords["precursors"] = templatekw.showpredecessors
@@ -91,12 +86,7 @@
         precursors = sorted(closestprecursors(repo, ctx.node()))
         precursors = [node.hex(p) for p in precursors]
 
-        # <= hg-4.1 requires an explicite gen.
-        # we can use None once the support is dropped
-        #
-        # They also requires an iterator instead of an iterable.
-        gen = iter(" ".join(p[:12] for p in precursors))
-        return templatekw._hybrid(gen.__iter__(), precursors, lambda x: {'precursor': x},
+        return templatekw._hybrid(None, precursors, lambda x: {'precursor': x},
                                   lambda d: d['precursor'][:12])
 
 def closestsuccessors(repo, nodeid):
@@ -260,8 +250,10 @@
 
     return "\n".join(lines)
 
-
-if util.safehasattr(templatekw, 'compatlist'):
+if util.safehasattr(templatekw, 'obsfateverb'):
+    # Individuals fragments are available in core
+    pass
+elif util.safehasattr(templatekw, 'compatlist'):
     @eh.templatekw('obsfatedata', requires=set(['ctx', 'templ']))
     def showobsfatedata(context, mapping):
         ctx = context.resource(mapping, 'ctx')
@@ -272,7 +264,7 @@
             return templatekw.compatlist(context, mapping, "obsfatedata", [])
         args = mapping.copy()
         args.pop('ctx')
-        args['templ'] = context.resource(mapping, 'templ')
+        args['templ'] = context
         return _showobsfatedata(repo, ctx, values, **args)
 else:
     # pre hg-4.6
@@ -317,7 +309,12 @@
     def fmt(d):
         nargs = args.copy()
         nargs.update(d[name])
-        return args['templ'](name, **nargs)
+        templ = args['templ']
+        # HG 4.6
+        if hasattr(templ, "generate"):
+            return templ.generate(name, nargs)
+        else:
+            return args['templ'](name, **nargs)
 
     # Generate a good enough string representation using templater
     gen = []
--- a/hgext3rd/evolve/utility.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/evolve/utility.py	Mon Apr 23 11:04:27 2018 +0200
@@ -7,14 +7,14 @@
 
 import collections
 
+from mercurial import (
+    obsutil,
+)
+
 from mercurial.i18n import _
 
 from mercurial.node import nullrev
 
-from . import (
-    compat,
-)
-
 shorttemplate = "[{label('evolve.rev', rev)}] {desc|firstline}\n"
 
 def obsexcmsg(ui, message, important=False):
@@ -103,7 +103,10 @@
             try:
                 succ = _singlesuccessor(repo, p)
             except MultipleSuccessorsError as exc:
-                dependencies[r] = exc.successorssets
+                tset = set()
+                for node in exc.successorssets[0]:
+                    tset.add(repo[node].rev())
+                dependencies[r] = tset
                 continue
             if succ in revs:
                 dependencies[r].add(succ)
@@ -119,14 +122,14 @@
         return p.rev()
     obs = repo[p]
     ui = repo.ui
-    newer = compat.successorssets(repo, obs.node())
+    newer = obsutil.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 = compat.successorssets(repo, obs.node())
+        newer = obsutil.successorssets(repo, obs.node())
     if len(newer) > 1 or len(newer[0]) > 1:
         raise MultipleSuccessorsError(newer)
 
--- a/hgext3rd/serverminitopic.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/serverminitopic.py	Mon Apr 23 11:04:27 2018 +0200
@@ -18,9 +18,14 @@
     node,
     registrar,
     util,
-    wireproto,
 )
 
+try:
+    from mercurial import wireproto
+    wireproto.branchmap
+except ImportError: # <= hg-4.5
+    from mercurial import wireprotov1server as wireproto
+
 if util.safehasattr(registrar, 'configitem'):
 
     configtable = {}
@@ -43,17 +48,14 @@
 
 ### make topic visible though "ctx.branch()"
 
-class topicchangectx(context.changectx):
-    """a sunclass of changectx that add topic to the branch name"""
-
-    def branch(self):
-        branch = super(topicchangectx, self).branch()
-        if hasminitopic(self._repo) and self.phase():
-            topic = self._changeset.extra.get('topic')
-            if topic is not None:
-                topic = encoding.tolocal(topic)
-                branch = '%s:%s' % (branch, topic)
-        return branch
+def topicbranch(orig, self):
+    branch = orig(self)
+    if hasminitopic(self._repo) and self.phase():
+        topic = self._changeset.extra.get('topic')
+        if topic is not None:
+            topic = encoding.tolocal(topic)
+            branch = '%s:%s' % (branch, topic)
+    return branch
 
 ### avoid caching topic data in rev-branch-cache
 
@@ -220,7 +222,7 @@
     assert issubclass(current, new), (current, new, targetclass)
 
 def uisetup(ui):
-    wrapclass(context, 'changectx', topicchangectx)
     wrapclass(branchmap, 'branchcache', _topiccache)
     extensions.wrapfunction(branchmap, 'read', wrapread)
     extensions.wrapfunction(wireproto, '_capabilities', wireprotocaps)
+    extensions.wrapfunction(context.changectx, 'branch', topicbranch)
--- a/hgext3rd/topic/__init__.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/topic/__init__.py	Mon Apr 23 11:04:27 2018 +0200
@@ -176,10 +176,10 @@
               'topic.active': 'green',
              }
 
-__version__ = '0.8.1.dev'
+__version__ = '0.9.0.dev'
 
-testedwith = '4.1.3 4.2.3 4.3.3 4.4.2 4.5.2'
-minimumhgversion = '4.1'
+testedwith = '4.3.3 4.4.2 4.5.2'
+minimumhgversion = '4.3'
 buglink = 'https://bz.mercurial-scm.org/'
 
 if util.safehasattr(registrar, 'configitem'):
@@ -594,7 +594,13 @@
         # Have some restrictions on the topic name just like bookmark name
         scmutil.checknewlabel(repo, topic, 'topic')
 
-    compat.startpager(ui, 'topics')
+        rmatch = re.match(br'[-_.\w]+', topic)
+        if not rmatch or rmatch.group(0) != topic:
+            helptxt = _("topic names can only consist of alphanumeric, '-'"
+                        " '_' and '.' characters")
+            raise error.Abort(_("invalid topic name: '%s'") % topic, hint=helptxt)
+
+    ui.pager('topics')
 
     if list:
         if clear or rev:
@@ -675,7 +681,7 @@
         topic = repo.currenttopic
     if topic is None:
         branch = repo[None].branch()
-    compat.startpager(ui, 'stack')
+    ui.pager('stack')
     return stack.showstack(ui, repo, branch=branch, topic=topic, opts=opts)
 
 @command('debugcb|debugconvertbookmark', [
@@ -879,7 +885,7 @@
     # create obsmarkers and move bookmarks
     # XXX we should be creating marker as we go instead of only at the end,
     # this makes the operations more modulars
-    compat.cleanupnodes(repo, successors, 'changetopics')
+    scmutil.cleanupnodes(repo, successors, 'changetopics')
 
     # move the working copy too
     wctx = repo[None]
@@ -1166,7 +1172,7 @@
         if node in thezeros or rev in thezeros:
             repo.ui.setconfig('_internal', 'keep-topic', 'yes',
                               source='topic-extension')
-        return orig(ui, repo, node, rev, *args, **kwargs)
+        return orig(ui, repo, node=node, rev=rev, *args, **kwargs)
     finally:
         repo.ui.restoreconfig(backup)
 
@@ -1178,17 +1184,22 @@
         if ctx.topic():
             extra[constants.extrakey] = ctx.topic()
 
-    def newmakeextrafn(orig, copiers):
-        return orig(copiers + [savetopic])
-
     def setrebaseconfig(orig, ui, repo, **opts):
         repo.ui.setconfig('experimental', 'topicrebase', 'yes',
                           source='topic-extension')
         return orig(ui, repo, **opts)
 
+    def new_init(orig, *args, **kwargs):
+        runtime = orig(*args, **kwargs)
+
+        if util.safehasattr(runtime, 'extrafns'):
+            runtime.extrafns.append(savetopic)
+
+        return runtime
+
     try:
         rebase = extensions.find("rebase")
-        extensions.wrapfunction(rebase, '_makeextrafn', newmakeextrafn)
+        extensions.wrapfunction(rebase.rebaseruntime, '__init__', new_init)
         # This exists to store in the config that rebase is running so that we can
         # update the topic according to rebase. This is a hack and should be removed
         # when we have better options.
--- a/hgext3rd/topic/compat.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/topic/compat.py	Mon Apr 23 11:04:27 2018 +0200
@@ -7,12 +7,8 @@
 """
 from __future__ import absolute_import
 
-import functools
-
 from mercurial import (
     obsolete,
-    scmutil,
-    util,
 )
 
 getmarkers = None
@@ -28,35 +24,3 @@
     getmarkers = obsolete.getmarkers
 if successorssets is None:
     successorssets = obsolete.successorssets
-
-# Wrap obsolete.creatmarkers and make it accept but ignore "operation" argument
-# for hg < 4.3
-originalcreatemarkers = obsolete.createmarkers
-while isinstance(originalcreatemarkers, functools.partial):
-    originalcreatemarkers = originalcreatemarkers.func
-if originalcreatemarkers.__code__.co_argcount < 6:
-    def createmarkers(repo, relations, flag=0, date=None, metadata=None,
-                      operation=None):
-        return obsolete.createmarkers(repo, relations, flag, date, metadata)
-else:
-    def createmarkers(*args, **kwargs):
-        return obsolete.createmarkers(*args, **kwargs)
-
-def startpager(ui, cmd):
-    """function to start a pager in case ui.pager() exists"""
-    try:
-        ui.pager(cmd)
-    except AttributeError:
-        pass
-
-def cleanupnodes(repo, replacements, operation, moves=None):
-    # create obsmarkers and move bookmarks
-    # XXX we should be creating marker as we go instead of only at the end,
-    # this makes the operations more modulars
-    if util.safehasattr(scmutil, 'cleanupnodes'):
-        scmutil.cleanupnodes(repo, replacements, 'changetopics',
-                             moves=moves)
-    else:
-        relations = [(repo[o], tuple(repo[n] for n in new))
-                     for (o, new) in replacements.iteritems()]
-        createmarkers(repo, relations, operation=operation)
--- a/hgext3rd/topic/discovery.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/topic/discovery.py	Mon Apr 23 11:04:27 2018 +0200
@@ -11,37 +11,30 @@
     exchange,
     extensions,
     util,
-    wireproto,
 )
 
-def _headssummary(orig, *args):
-    # In mercurial < 4.2, we receive repo, remote and outgoing as arguments
-    pushop = None
-    if len(args) == 3:
-        pushoparg = False
-        repo, remote, outgoing = args
+try:
+    from mercurial import wireproto
+    wireproto.branchmap
+except ImportError: # <= hg-4.5
+    from mercurial import wireprotov1server as wireproto
 
+def _headssummary(orig, pushop, *args, **kwargs):
     # In mercurial > 4.3, we receive the pushop as arguments
-    elif len(args) == 1:
-        pushoparg = True
-        pushop = args[0]
-        repo = pushop.repo.unfiltered()
-        remote = pushop.remote
-    else:
-        msg = 'topic-ext _headssummary() takes 1 or 3 arguments (%d given)'
-        raise TypeError(msg % len(args))
+    repo = pushop.repo.unfiltered()
+    remote = pushop.remote
 
     publishing = ('phases' not in remote.listkeys('namespaces')
                   or bool(remote.listkeys('phases').get('publishing', False)))
     if ((publishing or not remote.capable('topics'))
             and not getattr(pushop, 'publish', False)):
-        return orig(*args)
+        return orig(pushop, *args, **kwargs)
 
     publishedset = ()
     remotebranchmap = None
     origremotebranchmap = remote.branchmap
     # < hg-4.4 do not have a --publish flag anyway
-    if pushoparg and util.safehasattr(pushop, 'remotephases'):
+    if util.safehasattr(pushop, 'remotephases'):
         publishednode = [c.node() for c in pushop.outdatedphases]
         publishedset = repo.revs('ancestors(%ln + %ln)',
                                  publishednode,
@@ -109,11 +102,8 @@
             remote.branchmap = remotebranchmap
         unxx = repo.filtered('unfiltered-topic')
         repo.unfiltered = lambda: unxx
-        if pushoparg:
-            pushop.repo = repo
-            summary = orig(pushop)
-        else:
-            summary = orig(repo, remote, outgoing)
+        pushop.repo = repo
+        summary = orig(pushop)
         for key, value in summary.iteritems():
             if ':' in key: # This is a topic
                 if value[0] is None and value[1]:
--- a/hgext3rd/topic/evolvebits.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/topic/evolvebits.py	Mon Apr 23 11:04:27 2018 +0200
@@ -1,6 +1,8 @@
 import collections
 
-from . import compat
+from mercurial import (
+    obsutil,
+)
 
 # Copied from evolve 081605c2e9b6
 
@@ -73,14 +75,14 @@
         return p.rev()
     obs = repo[p]
     ui = repo.ui
-    newer = compat.successorssets(repo, obs.node())
+    newer = obsutil.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 = compat.successorssets(repo, obs.node())
+        newer = obsutil.successorssets(repo, obs.node())
     if 1 < len(newer):
         # divergence case
         # we should pick as arbitrary one
--- a/hgext3rd/topic/revset.py	Fri Mar 23 09:08:21 2018 -0700
+++ b/hgext3rd/topic/revset.py	Mon Apr 23 11:04:27 2018 +0200
@@ -15,7 +15,11 @@
 try:
     mkmatcher = revset._stringmatcher
 except AttributeError:
-    mkmatcher = util.stringmatcher
+    try:
+        from mercurial.utils import stringutil
+        mkmatcher = stringutil.stringmatcher
+    except (ImportError, AttributeError):
+        mkmatcher = util.stringmatcher
 
 revsetpredicate = registrar.revsetpredicate()
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-amend-patch.t	Mon Apr 23 11:04:27 2018 +0200
@@ -0,0 +1,1158 @@
+** Test for the `--patch` flag for `hg amend` command **
+
+Setup
+
+  $ cat >> $HGRCPATH << EOF
+  > [alias]
+  > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+  > [diff]
+  > git = 1
+  > [extensions]
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+Reposetup
+
+  $ hg init repo
+  $ cd repo
+  $ echo foo > a
+  $ hg ci -Aqm "added a"
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID f7ad4196431346de3c33c52e75374fba45e04313
+  # Parent  0000000000000000000000000000000000000000
+  added a
+  
+  diff --git a/a b/a
+  new file mode 100644
+  --- /dev/null
+  +++ b/a
+  @@ -0,0 +1,1 @@
+  +foo
+
+Testing of just changing the diff, not the patch metadata
+==========================================================
+
+Testing the normal case
+-----------------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID f7ad4196431346de3c33c52e75374fba45e04313
+  > # Parent  0000000000000000000000000000000000000000
+  > added a
+  > diff --git a/a b/a
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/a
+  > @@ -0,0 +1,1 @@
+  > +Gello
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+Making sure the amended commit is correct
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID af624b221c0c0bec5d74e2650180dd3eddcb7c42
+  # Parent  0000000000000000000000000000000000000000
+  added a
+  
+  diff --git a/a b/a
+  new file mode 100644
+  --- /dev/null
+  +++ b/a
+  @@ -0,0 +1,1 @@
+  +Gello
+
+  $ hg glog
+  @  1:af624b221c0c added a
+      () draft
+
+Obsolsence history is fine
+
+  $ hg obslog -p -r .
+  @  af624b221c0c (1) added a
+  |
+  x  f7ad41964313 (0) added a
+       rewritten(content) as af624b221c0c using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+         diff --git a/a b/a
+         --- a/a
+         +++ b/a
+         @@ -1,1 +1,1 @@
+         -foo
+         +Gello
+  
+  
+Diff and status are good too
+  $ hg diff
+  $ hg status
+  ? editor.sh
+  $ cat a
+  Gello
+
+Dirstate parents should be correctly set
+  $ hg parents
+  changeset:   1:af624b221c0c
+  tag:         tip
+  parent:      -1:000000000000
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     added a
+  
+Trying to amend with a wrong patch
+----------------------------------
+
+Having context which was is not present
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID c6ba250efbf73e671f2ca24b79db2c178ccbfff9
+  > # Parent  0000000000000000000000000000000000000000
+  > added a
+  > diff --git a/a b/a
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/a
+  > @@ -0,0 +1,1 @@
+  > I was not there before!
+  > +Gello
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+  failed to apply edited patch: bad hunk #1 @@ -0,0 +1,1 @@
+   (1 0 1 1)
+  try to fix the patch (yn)? y
+  abort: patch unchanged
+  [255]
+
+Having deletions which dont exists
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID af624b221c0c0bec5d74e2650180dd3eddcb7c42
+  > # Parent  0000000000000000000000000000000000000000
+  > added a
+  > diff --git a/a b/a
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/a
+  > @@ -0,0 +1,1 @@
+  > -I was not deleted before!
+  > +Gello
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+  failed to apply edited patch: bad hunk #1 @@ -0,0 +1,1 @@
+   (1 0 1 1)
+  try to fix the patch (yn)? y
+  abort: patch unchanged
+  [255]
+
+Changing the file mode using amend --patch
+------------------------------------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID af624b221c0c0bec5d74e2650180dd3eddcb7c42
+  > # Parent  0000000000000000000000000000000000000000
+  > added a
+  > diff --git a/a b/a
+  > new file mode 100755
+  > --- /dev/null
+  > +++ b/a
+  > @@ -0,0 +1,1 @@
+  > +Gello
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp --git
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 3a99e4b7ac73da799e20ae56914e3dd5b1a22d4d
+  # Parent  0000000000000000000000000000000000000000
+  added a
+  
+  diff --git a/a b/a
+  new file mode 100755
+  --- /dev/null
+  +++ b/a
+  @@ -0,0 +1,1 @@
+  +Gello
+
+Changing the file using amend --patch
+-------------------------------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 3a99e4b7ac73da799e20ae56914e3dd5b1a22d4d
+  > # Parent  0000000000000000000000000000000000000000
+  > added a
+  > diff --git a/changedfile b/changedfile
+  > new file mode 100755
+  > --- /dev/null
+  > +++ b/changedfile
+  > @@ -0,0 +1,1 @@
+  > +Gello
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID fc57c20be380f2878f4db139dad66d6cfb42ec62
+  # Parent  0000000000000000000000000000000000000000
+  added a
+  
+  diff --git a/changedfile b/changedfile
+  new file mode 100755
+  --- /dev/null
+  +++ b/changedfile
+  @@ -0,0 +1,1 @@
+  +Gello
+
+  $ hg status
+  ? editor.sh
+
+  $ ls
+  changedfile
+  editor.sh
+
+Handling both deletions and additions
+-------------------------------------
+
+  $ echo foobar > changedfile
+  $ hg ci -m "foobar to changedfile"
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 7969f70ffb81c3a6eee2d4f2f7032b694ce05349
+  # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  foobar to changedfile
+  
+  diff --git a/changedfile b/changedfile
+  --- a/changedfile
+  +++ b/changedfile
+  @@ -1,1 +1,1 @@
+  -Gello
+  +foobar
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 7969f70ffb81c3a6eee2d4f2f7032b694ce05349
+  > # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  > foobar to changedfile
+  > diff --git a/changedfile b/changedfile
+  > --- a/changedfile
+  > +++ b/changedfile
+  > @@ -1,1 +1,1 @@
+  > -Gello
+  > +foobar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+  abort: nothing changed
+  [255]
+
+Cannot change lines which are deleted
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 7969f70ffb81c3a6eee2d4f2f7032b694ce05349
+  > # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  > foobar to changedfile
+  > diff --git a/changedfile b/changedfile
+  > --- a/changedfile
+  > +++ b/changedfile
+  > @@ -1,1 +1,1 @@
+  > -Hello
+  > +foobar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+  patching file changedfile
+  Hunk #1 FAILED at 0
+  failed to apply edited patch: patch failed to apply
+  try to fix the patch (yn)? y
+  abort: patch unchanged
+  [255]
+
+Add more addition to the patch
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 7969f70ffb81c3a6eee2d4f2f7032b694ce05349
+  > # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  > foobar to changedfile
+  > diff --git a/changedfile b/changedfile
+  > --- a/changedfile
+  > +++ b/changedfile
+  > @@ -1,1 +1,2 @@
+  > -Gello
+  > +foobar
+  > +babar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 5d54400acb70b88f07128a1df497ed794b0b177b
+  # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  foobar to changedfile
+  
+  diff --git a/changedfile b/changedfile
+  --- a/changedfile
+  +++ b/changedfile
+  @@ -1,1 +1,2 @@
+  -Gello
+  +foobar
+  +babar
+
+  $ cat changedfile
+  foobar
+  babar
+
+Introduce files which were not there
+------------------------------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 5d54400acb70b88f07128a1df497ed794b0b177b
+  > # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  > foobar to changedfile
+  > diff --git a/changedfile b/changedfile
+  > --- a/changedfile
+  > +++ b/changedfile
+  > @@ -1,1 +1,2 @@
+  > -Gello
+  > +foobar
+  > +babar
+  > diff --git a/a b/a
+  > new file mode 100755
+  > --- /dev/null
+  > +++ b/a
+  > @@ -0,0 +1,1 @@
+  > +Gello
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID c3e29c061982c94418ce141d521434d6da76cd46
+  # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  foobar to changedfile
+  
+  diff --git a/a b/a
+  new file mode 100755
+  --- /dev/null
+  +++ b/a
+  @@ -0,0 +1,1 @@
+  +Gello
+  diff --git a/changedfile b/changedfile
+  --- a/changedfile
+  +++ b/changedfile
+  @@ -1,1 +1,2 @@
+  -Gello
+  +foobar
+  +babar
+
+Delete files which were not deleted in the first place
+------------------------------------------------------
+
+  $ echo Hello >> a
+  $ hg ci -m "hello to a"
+  $ hg glog
+  @  7:3d62c45a1699 hello to a
+  |   () draft
+  o  6:c3e29c061982 foobar to changedfile
+  |   () draft
+  o  3:fc57c20be380 added a
+      () draft
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 3d62c45a1699b11c7ecae573f013601712f2cc5f
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,2 @@
+  >  Gello
+  > +Hello
+  > diff --git a/changedfile b/changedfile
+  > deleted file mode 100755
+  > --- a/changedfile
+  > +++ /dev/null
+  > @@ -1,2 +0,0 @@
+  > -foobar
+  > -babar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID c9875799c53fb862c0dbaf01500459c9397373a4
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,2 @@
+   Gello
+  +Hello
+  diff --git a/changedfile b/changedfile
+  deleted file mode 100755
+  --- a/changedfile
+  +++ /dev/null
+  @@ -1,2 +0,0 @@
+  -foobar
+  -babar
+
+  $ hg status
+  ? editor.sh
+
+  $ cat changedfile
+  cat: changedfile: No such file or directory
+  [1]
+
+Testing sercret phase preservation during `hg amend --patch`
+------------------------------------------------------------
+
+  $ hg phase -r . --secret --force
+
+  $ hg phase -r .
+  8: secret
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID c9875799c53fb862c0dbaf01500459c9397373a4
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Hello
+  > +mello
+  > diff --git a/changedfile b/changedfile
+  > deleted file mode 100755
+  > --- a/changedfile
+  > +++ /dev/null
+  > @@ -1,2 +0,0 @@
+  > -foobar
+  > -babar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 4414485658e719a1f3d5e58bc8b2412385aa1592
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Hello
+  +mello
+  diff --git a/changedfile b/changedfile
+  deleted file mode 100755
+  --- a/changedfile
+  +++ /dev/null
+  @@ -1,2 +0,0 @@
+  -foobar
+  -babar
+
+  $ hg phase -r .
+  9: secret
+
+Testing bookmark movement on amend --patch
+------------------------------------------
+
+  $ hg bookmark foo
+  $ hg glog
+  @  9:4414485658e7 hello to a
+  |   (foo) secret
+  o  6:c3e29c061982 foobar to changedfile
+  |   () draft
+  o  3:fc57c20be380 added a
+      () draft
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 4414485658e719a1f3d5e58bc8b2412385aa1592
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Hello
+  > +bello
+  > diff --git a/changedfile b/changedfile
+  > deleted file mode 100755
+  > --- a/changedfile
+  > +++ /dev/null
+  > @@ -1,2 +0,0 @@
+  > -foobar
+  > -babar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 36454bda1fdb8e2e4fe07bb084eef378e29cba74
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Hello
+  +bello
+  diff --git a/changedfile b/changedfile
+  deleted file mode 100755
+  --- a/changedfile
+  +++ /dev/null
+  @@ -1,2 +0,0 @@
+  -foobar
+  -babar
+
+  $ hg glog
+  @  10:36454bda1fdb hello to a
+  |   (foo) secret
+  o  6:c3e29c061982 foobar to changedfile
+  |   () draft
+  o  3:fc57c20be380 added a
+      () draft
+
+Trying to amend --patch a public changeset
+------------------------------------------
+
+  $ hg phase -r . --public
+  $ hg glog
+  @  10:36454bda1fdb hello to a
+  |   (foo) public
+  o  6:c3e29c061982 foobar to changedfile
+  |   () public
+  o  3:fc57c20be380 added a
+      () public
+
+  $ HGEDITOR=cat hg amend --patch
+  abort: cannot amend public changesets: 36454bda1fdb
+  (see 'hg help phases' for details)
+  [255]
+
+  $ hg phase -r . --draft --force
+
+Trying on a dirty working directory
+-------------------------------------
+
+  $ echo bar > bar
+  $ hg add bar
+  $ HGEDITOR=cat hg amend --patch
+  abort: uncommitted changes
+  [255]
+
+  $ hg revert --all
+  forgetting bar
+
+Trying to pass filenames, only mentioned file names should be popped up in
+editor and rest should stay in the commit as they were
+--------------------------------------------------------------------------
+
+Checking the we pop-up with the files which were mentioned
+
+  $ HGEDITOR=cat hg amend --patch changedfile
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 36454bda1fdb8e2e4fe07bb084eef378e29cba74
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/changedfile b/changedfile
+  deleted file mode 100755
+  --- a/changedfile
+  +++ /dev/null
+  @@ -1,2 +0,0 @@
+  -foobar
+  -babar
+  abort: nothing changed
+  [255]
+
+  $ HGEDITOR=cat hg amend --patch a
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 36454bda1fdb8e2e4fe07bb084eef378e29cba74
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Hello
+  +bello
+  abort: nothing changed
+  [255]
+
+  $ HGEDITOR=cat hg amend --patch changedfile a
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 36454bda1fdb8e2e4fe07bb084eef378e29cba74
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Hello
+  +bello
+  diff --git a/changedfile b/changedfile
+  deleted file mode 100755
+  --- a/changedfile
+  +++ /dev/null
+  @@ -1,2 +0,0 @@
+  -foobar
+  -babar
+  abort: patch unchanged
+  [255]
+
+  $ HGEDITOR=cat hg amend --patch doesnotexists
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 36454bda1fdb8e2e4fe07bb084eef378e29cba74
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  abort: nothing changed
+  [255]
+
+Changing only one file
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 36454bda1fdb8e2e4fe07bb084eef378e29cba74
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Hello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch a
+
+file 'a' should be amended, rest of them should remain unchanged
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID ea175dcc4ee38c106db157975e006b4092444c65
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Hello
+  +betto
+  diff --git a/changedfile b/changedfile
+  deleted file mode 100755
+  --- a/changedfile
+  +++ /dev/null
+  @@ -1,2 +0,0 @@
+  -foobar
+  -babar
+
+  $ hg status
+  ? bar
+  ? editor.sh
+
+  $ hg diff
+
+Testing again with file 'changedfile'
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID ea175dcc4ee38c106db157975e006b4092444c65
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/changedfile b/changedfile
+  > --- a/changedfile
+  > +++ b/changedfile
+  > @@ -1,2 +1,1 @@
+  >  foobar
+  > -babar
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch changedfile
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 0e64d76c3519308c398a28192cb095d48b29aede
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Hello
+  +betto
+  diff --git a/changedfile b/changedfile
+  --- a/changedfile
+  +++ b/changedfile
+  @@ -1,2 +1,1 @@
+   foobar
+  -babar
+
+  $ hg diff
+  $ hg status
+  ? bar
+  ? editor.sh
+
+Dropping a file from commit by removing related hunks
+------------------------------------------------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 0e64d76c3519308c398a28192cb095d48b29aede
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Kello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 944e9f65fa55fdb2de98577c9d8ab30de0927d8e
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Kello
+  +betto
+
+The part which was dropped from the patch will not be there in working directory
+too
+  $ hg diff
+
+  $ hg status
+  ? bar
+  ? editor.sh
+
+Changing metadata of a patch by editing patch content
+======================================================
+
+Changing user
+-------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User RandomUser
+  > # Date 0 0
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 944e9f65fa55fdb2de98577c9d8ab30de0927d8e
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Kello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User RandomUser
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 5ded18a8c333a55da4b0e051162457cfe5d85558
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Kello
+  +betto
+
+Changing Date
+-------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User RandomUser
+  > # Date 123456 1200
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Node ID 944e9f65fa55fdb2de98577c9d8ab30de0927d8e
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Kello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User RandomUser
+  # Date 123456 1200
+  #      Fri Jan 02 09:57:36 1970 -0020
+  # Node ID e2312ddcd8756665075a60bd05431ddca3c45050
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Kello
+  +betto
+
+Changing branch
+---------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User RandomUser
+  > # Date 123456 1200
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Branch stable
+  > # Node ID 944e9f65fa55fdb2de98577c9d8ab30de0927d8e
+  > # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Kello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User RandomUser
+  # Date 123456 1200
+  #      Fri Jan 02 09:57:36 1970 -0020
+  # Branch stable
+  # Node ID ddc61a4058687b2dd4a316f4b5fe7d52a35b702a
+  # Parent  c3e29c061982c94418ce141d521434d6da76cd46
+  hello to a
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,3 @@
+   Gello
+  +Kello
+  +betto
+
+Changing parent (this should be fun)
+------------------------------------
+
+  $ hg glog
+  @  16:ddc61a405868 hello to a
+  |   (foo) draft
+  o  6:c3e29c061982 foobar to changedfile
+  |   () public
+  o  3:fc57c20be380 added a
+      () public
+
+  $ hg log -r .^^ -T '{node}'
+  fc57c20be380f2878f4db139dad66d6cfb42ec62 (no-eol)
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User RandomUser
+  > # Date 123456 1200
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Branch stable
+  > # Node ID 944e9f65fa55fdb2de98577c9d8ab30de0927d8e
+  > # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  > hello to a
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,3 @@
+  >  Gello
+  > +Kello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User RandomUser
+  # Date 123456 1200
+  #      Fri Jan 02 09:57:36 1970 -0020
+  # Branch stable
+  # Node ID b763f7cb2302f2efa1275e2a9202655872c9567f
+  # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  hello to a
+  
+  diff --git a/a b/a
+  new file mode 100755
+  --- /dev/null
+  +++ b/a
+  @@ -0,0 +1,3 @@
+  +Gello
+  +Kello
+  +betto
+
+  $ hg glog
+  @  17:b763f7cb2302 hello to a
+  |   (foo) draft
+  | o  6:c3e29c061982 foobar to changedfile
+  |/    () public
+  o  3:fc57c20be380 added a
+      () public
+
+Changing the commit desciption
+-------------------------------
+
+  $ cat > editor.sh <<EOF
+  > #!/bin/sh
+  > cat > \$1 <<ENDOF
+  > # HG changeset patch
+  > # User RandomUser
+  > # Date 123456 1200
+  > #      Thu Jan 01 00:00:00 1970 +0000
+  > # Branch stable
+  > # Node ID 944e9f65fa55fdb2de98577c9d8ab30de0927d8e
+  > # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  > I am a message which is testing change of message
+  > diff --git a/a b/a
+  > new file mode 100755
+  > --- /dev/null
+  > +++ b/a
+  > @@ -0,0 +1,3 @@
+  > +Gello
+  > +Kello
+  > +betto
+  > ENDOF
+  > EOF
+
+  $ HGEDITOR="sh ./editor.sh" hg amend --patch
+
+  $ hg exp
+  # HG changeset patch
+  # User RandomUser
+  # Date 123456 1200
+  #      Fri Jan 02 09:57:36 1970 -0020
+  # Branch stable
+  # Node ID f14ecd7121e63915ac93edbad7f60f605e62dd52
+  # Parent  fc57c20be380f2878f4db139dad66d6cfb42ec62
+  I am a message which is testing change of message
+  
+  diff --git a/a b/a
+  new file mode 100755
+  --- /dev/null
+  +++ b/a
+  @@ -0,0 +1,3 @@
+  +Gello
+  +Kello
+  +betto
+
+Changing the Node ID of the patch
+---------------------------------
+
+Nothing happens in that case we dont care about the node ID. Look the above 3-4
+tests to realize I was testing that too.
--- a/tests/test-amend.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-amend.t	Mon Apr 23 11:04:27 2018 +0200
@@ -149,6 +149,7 @@
    -a --all                 match all files
    -e --edit                invoke editor on commit messages
       --extract             extract changes from the commit to the working copy
+      --patch               make changes to wdir parent by editing patch
       --close-branch        mark a branch as closed, hiding it from the branch
                             list
    -s --secret              use the secret phase for committing
--- a/tests/test-discovery-obshashrange.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-discovery-obshashrange.t	Mon Apr 23 11:04:27 2018 +0200
@@ -183,6 +183,7 @@
   remote: * (glob)
   remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v1 batch * (glob)
   remote: 1
+  sending protocaps command
   preparing listkeys for "phases"
   sending listkeys command
   received listkey for "phases": 58 bytes
@@ -318,8 +319,9 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending hello command (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending between command (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> remote: * (glob)
-  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v1 batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps changegroupsubset getbundle known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v1 batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> remote: 1 (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending protocaps command (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> preparing listkeys for "phases" (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending listkeys command (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> received listkey for "phases": 58 bytes (glob)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-abort.t	Mon Apr 23 11:04:27 2018 +0200
@@ -0,0 +1,498 @@
+Tests for the --abort flag for `hg evolve` command
+==================================================
+
+Setup
+=====
+
+  $ cat >> $HGRCPATH <<EOF
+  > [phases]
+  > publish = False
+  > [alias]
+  > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+  > [extensions]
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+  $ hg init abortrepo
+  $ cd abortrepo
+  $ echo ".*\.orig" > .hgignore
+  $ hg add .hgignore
+  $ hg ci -m "added hgignore"
+  $ for ch in a b c d; do echo foo > $ch; hg add $ch; hg ci -qm "added "$ch; done;
+
+  $ hg glog
+  @  4:c41c793e0ef1 added d
+  |   () draft
+  o  3:ca1b80f7960a added c
+  |   () draft
+  o  2:b1661037fa25 added b
+  |   () draft
+  o  1:c7586e2a9264 added a
+  |   () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+Testing --abort when no evolve is interrupted
+=============================================
+
+  $ hg evolve --abort
+  abort: no interrupted evolve to stop
+  [255]
+
+Testing with wrong combination of flags
+=======================================
+
+  $ hg evolve --abort --continue
+  abort: cannot specify both "--abort" and "--continue"
+  [255]
+
+  $ hg evolve --abort --stop
+  abort: cannot specify both "--abort" and "--stop"
+  [255]
+
+  $ hg evolve --abort --rev 3
+  abort: cannot specify both "--rev" and "--abort"
+  [255]
+
+  $ hg evolve --abort --any
+  abort: cannot specify both "--any" and "--abort"
+  [255]
+
+  $ hg evolve --abort --all
+  abort: cannot specify both "--all" and "--abort"
+  [255]
+
+Normal testingw when no rev was evolved
+========================================
+
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [3] added c
+
+  $ echo babar > d
+  $ hg add d
+  $ hg amend
+  1 new orphan changesets
+
+  $ hg evolve --all
+  move:[4] added d
+  atop:[5] added c
+  merging d
+  warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+
+  $ hg evolve --abort
+  evolve aborted
+  working directory is now at e93a9161a274
+
+  $ hg glog
+  @  5:e93a9161a274 added c
+  |   () draft
+  | *  4:c41c793e0ef1 added d
+  | |   () draft
+  | x  3:ca1b80f7960a added c
+  |/    () draft
+  o  2:b1661037fa25 added b
+  |   () draft
+  o  1:c7586e2a9264 added a
+  |   () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg diff
+
+  $ hg status
+
+cleaning up things for next testing
+
+  $ hg evolve --all
+  move:[4] added d
+  atop:[5] added c
+  merging d
+  warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+  $ echo foo > d
+  $ hg resolve -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+  $ hg evolve --continue
+  evolving 4:c41c793e0ef1 "added d"
+  working directory is now at e83de241f751
+
+  $ hg up .^^^
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+
+When there are evolved revisions but on a single branch
+=======================================================
+
+  $ echo bar > c
+  $ hg add c
+  $ hg amend
+  3 new orphan changesets
+
+  $ hg evolve --all
+  move:[2] added b
+  atop:[7] added a
+  move:[5] added c
+  atop:[8] added b
+  merging c
+  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+
+  $ hg glog
+  @  8:0c41ec482070 added b
+  |   () draft
+  o  7:125af0ed8cae added a
+  |   () draft
+  | *  6:e83de241f751 added d
+  | |   () draft
+  | *  5:e93a9161a274 added c
+  | |   () draft
+  | x  2:b1661037fa25 added b
+  | |   () draft
+  | x  1:c7586e2a9264 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg evolve --abort
+  1 new orphan changesets
+  evolve aborted
+  working directory is now at 125af0ed8cae
+
+  $ hg glog
+  @  7:125af0ed8cae added a
+  |   () draft
+  | *  6:e83de241f751 added d
+  | |   () draft
+  | *  5:e93a9161a274 added c
+  | |   () draft
+  | *  2:b1661037fa25 added b
+  | |   () draft
+  | x  1:c7586e2a9264 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ cd ..
+
+Testing when evolved revs are on multiple branches
+==================================================
+
+  $ hg init repotwo
+  $ cd repotwo
+  $ echo ".*\.orig" > .hgignore
+  $ hg add .hgignore
+  $ hg ci -m "added hgignore"
+  $ echo a > a
+  $ hg ci -Aqm "added a"
+  $ for ch in b c; do echo $ch > $ch; hg add $ch; hg ci -m "added "$ch; done;
+  $ hg up .^^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ for ch in c d; do echo $ ch > $ch; hg add $ch; hg ci -m "added "$ch; done;
+  created new head
+  $ hg up .^^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo foo > a
+  $ hg ci -m "foo to a"
+  created new head
+
+  $ hg glog
+  @  6:8f20d4390c21 foo to a
+  |   () draft
+  | o  5:bcb1c47f8520 added d
+  | |   () draft
+  | o  4:86d2603075a3 added c
+  |/    () draft
+  | o  3:17509928e5bf added c
+  | |   () draft
+  | o  2:9f0c80a55ddc added b
+  |/    () draft
+  o  1:2f913b0c9220 added a
+  |   () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg prev
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  [1] added a
+  $ echo aa > a
+  $ hg amend
+  5 new orphan changesets
+
+  $ hg evolve --all
+  move:[2] added b
+  atop:[7] added a
+  move:[4] added c
+  atop:[7] added a
+  move:[6] foo to a
+  atop:[7] added a
+  merging a
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+
+  $ hg glog
+  o  9:7f8e8bd9f0b6 added c
+  |   () draft
+  | o  8:db3b42ef4da7 added b
+  |/    () draft
+  @  7:807e8e2ca559 added a
+  |   () draft
+  | *  6:8f20d4390c21 foo to a
+  | |   () draft
+  | | *  5:bcb1c47f8520 added d
+  | | |   () draft
+  | | x  4:86d2603075a3 added c
+  | |/    () draft
+  | | *  3:17509928e5bf added c
+  | | |   () draft
+  | | x  2:9f0c80a55ddc added b
+  | |/    () draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg evolve --abort
+  2 new orphan changesets
+  evolve aborted
+  working directory is now at 807e8e2ca559
+
+  $ hg glog
+  @  7:807e8e2ca559 added a
+  |   () draft
+  | *  6:8f20d4390c21 foo to a
+  | |   () draft
+  | | *  5:bcb1c47f8520 added d
+  | | |   () draft
+  | | *  4:86d2603075a3 added c
+  | |/    () draft
+  | | *  3:17509928e5bf added c
+  | | |   () draft
+  | | *  2:9f0c80a55ddc added b
+  | |/    () draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg status
+
+  $ hg diff
+
+Testing when user created a new changesets on top of evolved revisions
+======================================================================
+
+  $ hg evolve --all
+  move:[2] added b
+  atop:[7] added a
+  move:[4] added c
+  atop:[7] added a
+  move:[6] foo to a
+  atop:[7] added a
+  merging a
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+  $ hg glog
+  o  9:7f8e8bd9f0b6 added c
+  |   () draft
+  | o  8:db3b42ef4da7 added b
+  |/    () draft
+  @  7:807e8e2ca559 added a
+  |   () draft
+  | *  6:8f20d4390c21 foo to a
+  | |   () draft
+  | | *  5:bcb1c47f8520 added d
+  | | |   () draft
+  | | x  4:86d2603075a3 added c
+  | |/    () draft
+  | | *  3:17509928e5bf added c
+  | | |   () draft
+  | | x  2:9f0c80a55ddc added b
+  | |/    () draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ echo foo > a
+  $ hg resolve -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+
+  $ cd ..
+  $ hg init clonerepo
+  $ cd repotwo
+  $ hg push ../clonerepo --force
+  pushing to ../clonerepo
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 10 changesets with 8 changes to 5 files (+4 heads)
+  3 new obsolescence markers
+  3 new orphan changesets
+  $ cd ../clonerepo
+  $ hg up 7f8e8bd9f0b6
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo bar > bar
+  $ hg add bar
+  $ hg ci -m "made an new commit on evolved rev"
+
+  $ hg push ../repotwo --force
+  pushing to ../repotwo
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  $ cd ../repotwo
+  $ hg evolve --abort
+  warning: new changesets detected on destination branch
+  abort: unable to abort interrupted evolve, use 'hg evolve --stop' to stop evolve
+  [255]
+
+  $ hg evolve --stop
+  stopped the interrupted evolve
+  working directory is now at 807e8e2ca559
+
+Testing when the evolved revision turned public due to some other user actions
+==============================================================================
+
+  $ hg evolve --all
+  move:[3] added c
+  atop:[8] added b
+  move:[5] added d
+  atop:[9] added c
+  move:[6] foo to a
+  atop:[7] added a
+  merging a
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+
+  $ hg glog
+  o  12:1c476940790a added d
+  |   () draft
+  | o  11:c10a55eb0cc6 added c
+  | |   () draft
+  +---o  10:48eca1ed5478 made an new commit on evolved rev
+  | |     () draft
+  o |  9:7f8e8bd9f0b6 added c
+  | |   () draft
+  | o  8:db3b42ef4da7 added b
+  |/    () draft
+  @  7:807e8e2ca559 added a
+  |   () draft
+  | *  6:8f20d4390c21 foo to a
+  | |   () draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg phase -r 1c476940790a --public
+
+  $ hg evolve --abort
+  cannot clean up public changesets: 1c476940790a
+  abort: unable to abort interrupted evolve, use 'hg evolve --stop' to stop evolve
+  [255]
+
+  $ hg evolve --stop
+  stopped the interrupted evolve
+  working directory is now at 807e8e2ca559
+
+  $ cd ..
+
+Testing that bookmark should be moved back when doing `hg evolve --abort`
+=========================================================================
+
+  $ hg init repothree
+  $ cd repothree
+  $ echo ".*\.orig" > .hgignore
+  $ hg add .hgignore
+  $ hg ci -m "added hgignore"
+  $ for ch in a b c; do echo $ch > $ch; hg add $ch; hg ci -m "added "$ch; done;
+
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg bookmark bm1
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  (leaving bookmark bm1)
+  $ echo foo > c
+  $ hg add c
+  $ hg amend
+  2 new orphan changesets
+
+  $ hg glog
+  @  4:a0086c17bfc7 added a
+  |   () draft
+  | *  3:17509928e5bf added c
+  | |   () draft
+  | *  2:9f0c80a55ddc added b
+  | |   (bm1) draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg evolve --all
+  move:[2] added b
+  atop:[4] added a
+  move:[3] added c
+  atop:[5] added b
+  merging c
+  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+  evolve failed!
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+
+  $ hg glog
+  @  5:c1f4718020e3 added b
+  |   (bm1) draft
+  o  4:a0086c17bfc7 added a
+  |   () draft
+  | *  3:17509928e5bf added c
+  | |   () draft
+  | x  2:9f0c80a55ddc added b
+  | |   () draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
+
+  $ hg evolve --abort
+  1 new orphan changesets
+  evolve aborted
+  working directory is now at a0086c17bfc7
+
+  $ hg glog
+  @  4:a0086c17bfc7 added a
+  |   () draft
+  | *  3:17509928e5bf added c
+  | |   () draft
+  | *  2:9f0c80a55ddc added b
+  | |   (bm1) draft
+  | x  1:2f913b0c9220 added a
+  |/    () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
--- a/tests/test-evolve-bumped.t	Fri Mar 23 09:08:21 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-  $ hg init public
-  $ cd public
-  $ echo a > a
-  $ mkcommit() {
-  >    echo "$1" > "$1"
-  >    hg add "$1"
-  >    hg ci -m "add $1"
-  > }
-  $ hg commit -A -m init
-  adding a
-  $ cd ..
-
-  $ evolvepath=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/
-  $ hg clone -U public private
-  $ cd private
-  $ cat >> .hg/hgrc <<EOF
-  > [extensions]
-  > evolve = $evolvepath
-  > [ui]
-  > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n
-  > [phases]
-  > publish = false
-  > EOF
-  $ cd ..
-
-  $ cp -a private alice
-  $ cp -a private bob
-
-  $ cd alice
-  $ hg update
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo a >> a
-  $ hg commit -u alice -m 'modify a'
-  $ hg push ../private
-  pushing to ../private
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-  $ hg log -r 'draft()'
-  1:4d1169d82e47@default(draft) modify a
-
-  $ cd ../bob
-  $ hg pull ../private
-  pulling from ../private
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-  new changesets 4d1169d82e47
-  (run 'hg update' to get a working copy)
-  $ hg log -r 'draft()'
-  1:4d1169d82e47@default(draft) modify a
-  $ hg push ../public
-  pushing to ../public
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-  $ hg log -r 'draft()'
-
-  $ cd ../alice
-  $ hg amend -m 'tweak a'
-  $ hg pull ../public
-  pulling from ../public
-  searching for changes
-  no changes found
-  1 new phase-divergent changesets
-
-  $ hg evolve -a -A --phase-divergent
-  recreate:[2] tweak a
-  atop:[1] modify a
-  computing new diff
-  committed as 4d1169d82e47
-  working directory is now at 4d1169d82e47
-
-Bumped Merge changeset:
------------------------
-
-We currently cannot automatically solve bumped changeset that is the
-product of a merge, we add a test for it.
-
-  $ mkcommit _a
-  $ hg up .^
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ mkcommit _b
-  created new head
-  $ mkcommit _c
-  $ hg log -G
-  @  5:eeaf70969381@default(draft) add _c
-  |
-  o  4:6612fc0ddeb6@default(draft) add _b
-  |
-  | o  3:154ad198ff4a@default(draft) add _a
-  |/
-  o  1:4d1169d82e47@default(public) modify a
-  |
-  o  0:d3873e73d99e@default(public) init
-  
-  $ hg merge 3
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-  $ hg commit -m "merge"
-  $ hg commit --amend -m "New message"
-  $ hg phase --public 551127da2a8a --hidden
-  1 new phase-divergent changesets
-  $ hg log -G
-  @    7:b28e84916d8c@default(draft) New message
-  |\
-  +---o  6:551127da2a8a@default(public) merge
-  | |/
-  | o  5:eeaf70969381@default(public) add _c
-  | |
-  | o  4:6612fc0ddeb6@default(public) add _b
-  | |
-  o |  3:154ad198ff4a@default(public) add _a
-  |/
-  o  1:4d1169d82e47@default(public) modify a
-  |
-  o  0:d3873e73d99e@default(public) init
-  
-  $ hg evolve --all --phase-divergent
-  skipping b28e84916d8c : we do not handle merge yet
--- a/tests/test-evolve-continue.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve-continue.t	Mon Apr 23 11:04:27 2018 +0200
@@ -59,7 +59,7 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -118,7 +118,7 @@
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -128,9 +128,9 @@
   continue: hg evolve --continue
   $ hg diff
 
-XXX: maybe we should add a message here about evolve resulting in no commit
   $ hg evolve --continue
   evolving 7:ad0a59d83efe "added e"
+  evolution of 7:ad0a59d83efe created no changes to commit
 
   $ hg glog
   @  8:00a5c774cc37 added d
@@ -159,7 +159,7 @@
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -247,7 +247,7 @@
   merging f
   warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -264,7 +264,7 @@
   merging h
   warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -312,7 +312,7 @@
   merging g
   warning: conflicts while merging g! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -364,7 +364,7 @@
   merging g
   warning: conflicts while merging g! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
   $ echo foo > g
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-issue5832.t	Mon Apr 23 11:04:27 2018 +0200
@@ -0,0 +1,281 @@
+Test for issue 5832 present at https://bz.mercurial-scm.org/show_bug.cgi?id=5832
+================================================================================
+
+Setup
+=====
+
+  $ cat >> $HGRCPATH <<EOF
+  > [phases]
+  > publish = False
+  > [alias]
+  > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+  > [extensions]
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+  $ hg init issue5832
+  $ cd issue5832
+
+  $ echo base > base
+  $ hg ci -Aqm "added base"
+
+  $ echo a > a
+  $ hg ci -Aqm "added a"
+
+  $ echo b > b
+  $ hg ci -Aqm "added b"
+
+  $ hg up .^^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo c > c
+  $ echo d > d
+  $ hg ci -Aqm "added c and d"
+
+  $ hg merge
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "merge commit"
+
+  $ hg glog
+  @    4:b9b387427a53 merge commit
+  |\    () draft
+  | o  3:9402371b436e added c and d
+  | |   () draft
+  o |  2:a1da0651488c added b
+  | |   () draft
+  o |  1:1b24879c5c3c added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+
+  $ hg up 1b24879c5c3c
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ echo foo > a
+  $ hg amend
+  2 new orphan changesets
+
+  $ hg up bde1d2b6b5e5
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo c > c
+  $ hg ci -Aqm "added c"
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo d > d
+  $ hg ci -Aqm "added d"
+  $ hg glog
+  @  7:5841d7cf9893 added d
+  |   () draft
+  | o  6:62fb70414f99 added c
+  |/    () draft
+  | o  5:7014ec2829cd added a
+  |/    () draft
+  | *    4:b9b387427a53 merge commit
+  | |\    () draft
+  +---o  3:9402371b436e added c and d
+  | |     () draft
+  | *  2:a1da0651488c added b
+  | |   () draft
+  | x  1:1b24879c5c3c added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+
+  $ hg prune -r 9402371b436e --succ 62fb70414f99 --succ 5841d7cf9893 --split
+  1 changesets pruned
+
+  $ hg glog
+  @  7:5841d7cf9893 added d
+  |   () draft
+  | o  6:62fb70414f99 added c
+  |/    () draft
+  | o  5:7014ec2829cd added a
+  |/    () draft
+  | *    4:b9b387427a53 merge commit
+  | |\    () draft
+  +---x  3:9402371b436e added c and d
+  | |     () draft
+  | *  2:a1da0651488c added b
+  | |   () draft
+  | x  1:1b24879c5c3c added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+
+Checking what evolve is trying to do
+
+  $ hg evolve --dry-run --any --all
+  move:[2] added b
+  atop:[5] added a
+  hg rebase -r a1da0651488c -d 7014ec2829cd
+  could not solve instability, ambiguous destination: parent split across two branches
+
+Resolving instability using `hg evolve`
+
+  $ hg evolve --any --all --config ui.interactive=True <<EOF
+  > 0
+  > EOF
+  move:[2] added b
+  atop:[5] added a
+  move:[4] merge commit
+  atop:[8] added b
+  ancestor '7235ef625ea3' split over multiple topological branches.
+  choose an evolve destination:
+  0: [62fb70414f99] added c
+  1: [5841d7cf9893] added d
+  q: quit the prompt
+  enter the index of the revision you want to select: 0
+  move:[9] merge commit
+  atop:[6] added c
+  working directory is now at 28a0775ac832
+
+  $ hg glog
+  @    10:28a0775ac832 merge commit
+  |\    () draft
+  | o  8:2baf8bae7ea4 added b
+  | |   () draft
+  | | o  7:5841d7cf9893 added d
+  | | |   () draft
+  o---+  6:62fb70414f99 added c
+   / /    () draft
+  o /  5:7014ec2829cd added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+
+  $ cd ..
+
+Test for issue5833 present at https://bz.mercurial-scm.org/show_bug.cgi?id=5833
+===============================================================================
+
+  $ hg init issue5833
+  $ cd issue5833
+  $ echo base > base
+  $ hg ci -Aqm "added base"
+
+  $ echo a > a
+  $ hg ci -Aqm "added a"
+
+  $ echo b > b
+  $ hg ci -Aqm "added b"
+
+  $ hg up .^^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo c > c
+  $ echo d > d
+  $ hg ci -Aqm "added c and d"
+
+  $ hg merge
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "merge commit"
+
+  $ hg glog
+  @    4:b9b387427a53 merge commit
+  |\    () draft
+  | o  3:9402371b436e added c and d
+  | |   () draft
+  o |  2:a1da0651488c added b
+  | |   () draft
+  o |  1:1b24879c5c3c added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+ 
+  $ hg up bde1d2b6b5e5
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ echo l > l
+  $ hg ci -Aqm "added l"
+  $ hg grab -r 1b24879c5c3c
+  grabbing 1:1b24879c5c3c "added a"
+  2 new orphan changesets
+
+  $ hg up bde1d2b6b5e5
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo c > c
+  $ hg ci -Aqm "added c"
+  $ hg up .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo d > d
+  $ hg ci -Aqm "added d"
+
+  $ hg glog
+  @  8:5841d7cf9893 added d
+  |   () draft
+  | o  7:62fb70414f99 added c
+  |/    () draft
+  | o  6:5568b87b1491 added a
+  | |   () draft
+  | o  5:0a6281e212fe added l
+  |/    () draft
+  | *    4:b9b387427a53 merge commit
+  | |\    () draft
+  +---o  3:9402371b436e added c and d
+  | |     () draft
+  | *  2:a1da0651488c added b
+  | |   () draft
+  | x  1:1b24879c5c3c added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+
+  $ hg prune -r 9402371b436e --succ 5841d7cf9893 --succ 62fb70414f99 --split
+  1 changesets pruned
+
+  $ hg glog
+  @  8:5841d7cf9893 added d
+  |   () draft
+  | o  7:62fb70414f99 added c
+  |/    () draft
+  | o  6:5568b87b1491 added a
+  | |   () draft
+  | o  5:0a6281e212fe added l
+  |/    () draft
+  | *    4:b9b387427a53 merge commit
+  | |\    () draft
+  +---x  3:9402371b436e added c and d
+  | |     () draft
+  | *  2:a1da0651488c added b
+  | |   () draft
+  | x  1:1b24879c5c3c added a
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
+
+  $ hg evolve --any --all --dry-run
+  move:[2] added b
+  atop:[6] added a
+  hg rebase -r a1da0651488c -d 5568b87b1491
+  could not solve instability, ambiguous destination: parent split across two branches
+
+  $ hg evolve --any --all --config ui.interactive=True <<EOF
+  > 1
+  > EOF
+  move:[2] added b
+  atop:[6] added a
+  move:[4] merge commit
+  atop:[9] added b
+  ancestor 'cdf2ea1b9312' split over multiple topological branches.
+  choose an evolve destination:
+  0: [62fb70414f99] added c
+  1: [5841d7cf9893] added d
+  q: quit the prompt
+  enter the index of the revision you want to select: 1
+  move:[10] merge commit
+  atop:[8] added d
+  working directory is now at 460e6e72b7f9
+
+  $ hg glog
+  @    11:460e6e72b7f9 merge commit
+  |\    () draft
+  | o  9:da76bb7cd904 added b
+  | |   () draft
+  o |  8:5841d7cf9893 added d
+  | |   () draft
+  +---o  7:62fb70414f99 added c
+  | |     () draft
+  | o  6:5568b87b1491 added a
+  | |   () draft
+  | o  5:0a6281e212fe added l
+  |/    () draft
+  o  0:bde1d2b6b5e5 added base
+      () draft
--- a/tests/test-evolve-obshistory.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve-obshistory.t	Mon Apr 23 11:04:27 2018 +0200
@@ -201,8 +201,8 @@
       }
   ]
   $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions; successor: 4ae3a4151de9)
+  abort: hidden revision '471f378eab4c' was rewritten as: 4ae3a4151de9!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden "desc(A0)"
   updating to a hidden changeset 471f378eab4c
@@ -304,8 +304,8 @@
   $ hg up 1
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up 0dec01379d3b
-  abort: hidden revision '0dec01379d3b'!
-  (use --hidden to access hidden revisions; pruned)
+  abort: hidden revision '0dec01379d3b' is pruned!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg up --hidden -r 'desc(B0)'
   updating to a hidden changeset 0dec01379d3b
@@ -503,8 +503,8 @@
          (No patch available, too many successors (2))
   
   $ hg update 471597cad322
-  abort: hidden revision '471597cad322'!
-  (use --hidden to access hidden revisions; successors: 337fec4d2edc, f257fde29c7a)
+  abort: hidden revision '471597cad322' was split as: 337fec4d2edc, f257fde29c7a!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden 'min(desc(A0))'
   updating to a hidden changeset 471597cad322
@@ -784,8 +784,8 @@
          (No patch available, too many successors (4))
   
   $ hg update de7290d8b885
-  abort: hidden revision 'de7290d8b885'!
-  (use --hidden to access hidden revisions; successors: 337fec4d2edc, f257fde29c7a and 2 more)
+  abort: hidden revision 'de7290d8b885' was split as: 337fec4d2edc, f257fde29c7a and 2 more!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden 'min(desc(A0))'
   updating to a hidden changeset de7290d8b885
@@ -1009,8 +1009,8 @@
       }
   ]
   $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions; successor: eb5a0daa2192)
+  abort: hidden revision '471f378eab4c' was rewritten as: eb5a0daa2192!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden 'desc(A0)'
   updating to a hidden changeset 471f378eab4c
@@ -1019,8 +1019,8 @@
   working directory parent is obsolete! (471f378eab4c)
   (use 'hg evolve' to update to its successor: eb5a0daa2192)
   $ hg update 0dec01379d3b
-  abort: hidden revision '0dec01379d3b'!
-  (use --hidden to access hidden revisions; successor: eb5a0daa2192)
+  abort: hidden revision '0dec01379d3b' was rewritten as: eb5a0daa2192!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden 'desc(B0)'
   updating to a hidden changeset 0dec01379d3b
@@ -1356,8 +1356,8 @@
       }
   ]
   $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions; diverged)
+  abort: hidden revision '471f378eab4c' has diverged!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden 'desc(A0)'
   updating to a hidden changeset 471f378eab4c
@@ -1587,8 +1587,8 @@
       }
   ]
   $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions; successor: eb5a0daa2192)
+  abort: hidden revision '471f378eab4c' was rewritten as: eb5a0daa2192!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg update --hidden 'desc(A0)'
   updating to a hidden changeset 471f378eab4c
--- a/tests/test-evolve-orphan-merge.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve-orphan-merge.t	Mon Apr 23 11:04:27 2018 +0200
@@ -220,7 +220,7 @@
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -277,7 +277,7 @@
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-phase-divergence.t	Mon Apr 23 11:04:27 2018 +0200
@@ -0,0 +1,1232 @@
+** Test for handling of phase divergent changesets by `hg evolve` **
+====================================================================
+
+  $ cat >> $HGRCPATH <<EOF
+  > [alias]
+  > glog = log -GT "{rev}:{node|short} {desc|firstline}\n ({bookmarks}) {phase}"
+  > [extensions]
+  > rebase =
+  > EOF
+
+Setting up a public repo
+------------------------
+
+  $ hg init public
+  $ cd public
+  $ echo a > a
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+  $ hg commit -A -m init
+  adding a
+  $ cd ..
+
+  $ evolvepath=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/
+
+Setting up a private non-publishing repo
+----------------------------------------
+
+  $ hg clone -U public private
+  $ cd private
+  $ cat >> .hg/hgrc <<EOF
+  > [extensions]
+  > evolve = $evolvepath
+  > [ui]
+  > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n
+  > [phases]
+  > publish = false
+  > EOF
+  $ cd ..
+
+Setting up couple of more instances of private repo
+---------------------------------------------------
+
+  $ cp -a private alice
+  $ cp -a private bob
+
+Creating a phase-divergence changeset
+-------------------------------------
+
+Alice creating a draft changeset and pushing to main private repo
+
+  $ cd alice
+  $ hg update
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo a >> a
+  $ hg commit -u alice -m 'modify a'
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  $ hg glog
+  @  1:4d1169d82e47 modify a
+  |   () draft
+  o  0:d3873e73d99e init
+      () public
+
+Bob pulling from private repo and pushing to the main public repo making the
+changeset public
+
+  $ cd ../bob
+  $ hg pull ../private
+  pulling from ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets 4d1169d82e47
+  (run 'hg update' to get a working copy)
+
+  $ hg glog
+  o  1:4d1169d82e47 modify a
+  |   () draft
+  o  0:d3873e73d99e init
+      () public
+
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+  $ hg glog
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+*But* Alice decided to amend the changeset she had and then pulling from public
+repo creating phase-divergent changeset locally
+
+  $ cd ../alice
+  $ hg amend -m 'tweak a'
+
+XXX: pull should tell us how to see what is the new phase-divergent changeset
+  $ hg pull ../public
+  pulling from ../public
+  searching for changes
+  no changes found
+  1 new phase-divergent changesets
+
+  $ hg glog
+  @  2:98bb3a6cfe1a tweak a
+  |   () draft
+  | o  1:4d1169d82e47 modify a
+  |/    () public
+  o  0:d3873e73d99e init
+      () public
+
+Using evolve --list to list phase-divergent changesets
+------------------------------------------------------
+
+  $ hg evolve --list
+  98bb3a6cfe1a: tweak a
+    phase-divergent: 4d1169d82e47 (immutable precursor)
+  
+
+
+XXX-Pulkit: Trying to see instability on public changeset
+
+XXX-Pulkit: this is not helpful
+
+XXX-Marmoute: public changeset "instable themself"
+XXX-Marmoute: I'm not sure if we store this information and it is useful to show it.
+XXX-Marmoute: We should maybe point the user toward `hg obslog` instead`
+  $ hg evolve -r 4d1169d8 --list
+  4d1169d82e47: modify a
+  
+
+Understanding phasedivergence using obslog
+------------------------------------------
+
+XXX: There must be mention of phase-divergence here
+  $ hg obslog -r . --all
+  @  98bb3a6cfe1a (2) tweak a
+  |
+  o  4d1169d82e47 (1) modify a
+       rewritten(description) as 98bb3a6cfe1a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+Solving the phase divergence using evolve command
+--------------------------------------------------
+
+(We do not solve evolution other than orphan by default because it turned out
+it was too confusing for users. We used to behave this way, but having multiple
+possible outcome to evolve end up scaring people)
+
+  $ hg evolve
+  nothing to evolve on current working copy parent
+  (do you want to use --phase-divergent)
+  [2]
+
+testing the --confirm option
+  $ hg evolve --phase-divergent --confirm <<EOF
+  > n
+  > EOF
+  recreate:[2] tweak a
+  atop:[1] modify a
+  perform evolve? [Ny] n
+  abort: evolve aborted by user
+  [255]
+
+testing the --dry-run option
+
+  $ hg evolve --phase-divergent --dry-run
+  recreate:[2] tweak a
+  atop:[1] modify a
+  hg rebase --rev 98bb3a6cfe1a --dest d3873e73d99e;
+  hg update 4d1169d82e47;
+  hg revert --all --rev 98bb3a6cfe1a;
+  hg commit --msg "phase-divergent update to 98bb3a6cfe1a"
+
+XXX: evolve should have mentioned that draft commit is just obsoleted in favour
+of public one. From the message it looks like a new commit is created.
+
+  $ hg evolve --phase-divergent
+  recreate:[2] tweak a
+  atop:[1] modify a
+  computing new diff
+  committed as 4d1169d82e47
+  working directory is now at 4d1169d82e47
+
+  $ hg glog
+  @  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+Syncying every repo with the new state
+--------------------------------------
+
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  no changes found
+  2 new obsolescence markers
+  [1]
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  no changes found
+  2 new obsolescence markers
+  [1]
+  $ hg push ../bob
+  pushing to ../bob
+  searching for changes
+  no changes found
+  2 new obsolescence markers
+  [1]
+
+Creating more phase-divergence where a new resolution commit will be formed and
+also testing bookmark movement
+--------------------------------------------------------------------------------
+
+Alice created a commit and push to private non-publishing repo
+
+  $ echo foo > foo
+  $ hg add foo
+  $ hg ci -m "added foo to foo"
+  $ hg glog
+  @  3:aa071e5554e3 added foo to foo
+  |   () draft
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Bob pulled from the private repo and pushed that to publishing repo
+
+  $ cd ../bob
+  $ hg pull ../private
+  pulling from ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets aa071e5554e3
+  (run 'hg update' to get a working copy)
+
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Alice amended that changeset and then pulled from publishing repo creating
+phase-divergence
+
+  $ cd ../alice
+  $ echo bar >> foo
+  $ hg amend -m "added bar to foo"
+  $ hg bookmark bm
+
+  $ hg pull ../public
+  pulling from ../public
+  searching for changes
+  no changes found
+  1 new phase-divergent changesets
+
+  $ hg glog
+  @  4:d47f2b37ed82 added bar to foo
+  |   (bm) draft
+  | o  3:aa071e5554e3 added foo to foo
+  |/    () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+Resolving the new phase-divergence changeset using `hg evolve`
+--------------------------------------------------------------
+
+XXX: this should have popped up for a new commit message of the changeset or an
+option should be there
+
+XXX: we should document what should user expect where running this, writing this
+test I have to go through code base to understand what will be the behavior
+
+  $ hg evolve --phase-divergent
+  recreate:[4] added bar to foo
+  atop:[3] added foo to foo
+  computing new diff
+  committed as 3d62500c673d
+  working directory is now at 3d62500c673d
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 3d62500c673dd1c88bb09a73e86d0210aed6fcb6
+  # Parent  aa071e5554e36080a36cfd24accd5a71e3320f1e
+  phase-divergent update to aa071e5554e3:
+  
+  added bar to foo
+  
+  diff -r aa071e5554e3 -r 3d62500c673d foo
+  --- a/foo	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/foo	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,2 @@
+   foo
+  +bar
+
+XXX: the commit message is not best one, we should give option to user to modify
+the commit message
+
+  $ hg glog
+  @  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   (bm) draft
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+  $ hg obslog -r . --all
+  @  3d62500c673d (5) phase-divergent update to aa071e5554e3:
+  |
+  x  d47f2b37ed82 (4) added bar to foo
+  |    rewritten(description, parent, content) as 3d62500c673d using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  o  aa071e5554e3 (3) added foo to foo
+       rewritten(description, content) as d47f2b37ed82 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+
+Syncing all other repositories
+------------------------------
+
+These pushed should not be turned to quiet mode as the output is very helpful to
+make sure everything is working fine
+
+  $ hg push ../bob
+  pushing to ../bob
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
+
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
+
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
+
+Creating a phasedivergence changeset where the divergent changeset changed in a
+way that we rebase that on old public changeset, there will be conflicts, but
+the `hg evolve` command handles it very well and uses `hg revert` logic to
+prevent any conflicts
+-------------------------------------------------------------------------------
+
+Alice creates one more changeset and pushes to private repo
+
+  $ echo bar > bar
+  $ hg ci -Aqm "added bar to bar"
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Bob pulls from private and pushes to public repo
+  $ cd ../bob
+
+  $ hg pull ../private
+  pulling from ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets b756eb10ea73
+  (run 'hg update' to get a working copy)
+
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Alice amends the changeset and then pull from public creating phase-divergence
+
+  $ cd ../alice
+  $ echo foo > bar
+  $ hg amend -m "foo to bar"
+
+  $ hg pull ../public
+  pulling from ../public
+  searching for changes
+  no changes found
+  1 new phase-divergent changesets
+
+  $ hg glog
+  @  7:2c3560aedead foo to bar
+  |   (bm) draft
+  | o  6:b756eb10ea73 added bar to bar
+  |/    () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+Resolving the new phase-divergence changeset using `hg evolve`
+---------------------------------------------------------------
+
+  $ hg evolve --phase-divergent
+  recreate:[7] foo to bar
+  atop:[6] added bar to bar
+  computing new diff
+  committed as 502e73736632
+  working directory is now at 502e73736632
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 502e737366322886cf628276aa0a2796904453b4
+  # Parent  b756eb10ea73ee4ba69c998e64a5c6e1005d74b5
+  phase-divergent update to b756eb10ea73:
+  
+  foo to bar
+  
+  diff -r b756eb10ea73 -r 502e73736632 bar
+  --- a/bar	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/bar	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,1 @@
+  -bar
+  +foo
+
+  $ hg glog
+  @  8:502e73736632 phase-divergent update to b756eb10ea73:
+  |   (bm) draft
+  o  6:b756eb10ea73 added bar to bar
+  |   () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+Syncing all the repositories
+----------------------------
+
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
+
+Creating phase-divergence with divergent changeset and precursor having
+different parents
+-----------------------------------------------------------------------
+
+Alice creates a changeset and pushes to private repo
+
+  $ echo x > x
+  $ hg ci -Am "added x to x"
+  adding x
+
+  $ hg push ../private
+  pushing to ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Bob does what he always does, pull from private and push to public, he is acting
+as a CI service
+
+  $ cd ../bob
+  $ hg pull ../private
+  pulling from ../private
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files
+  2 new obsolescence markers
+  new changesets 502e73736632:2352021b3785
+  (run 'hg update' to get a working copy)
+  $ hg push ../public
+  pushing to ../public
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Alice like always dont care about Bob existence and rebases her changeset and
+then pull from public repo creating phase divergence
+
+  $ cd ../alice
+  $ hg rebase -r . -d .^^^
+  rebasing 9:2352021b3785 "added x to x" (bm tip)
+
+  $ hg pull ../public
+  pulling from ../public
+  searching for changes
+  no changes found
+  1 new phase-divergent changesets
+
+  $ hg obslog -r .
+  @  334e300d6db5 (10) added x to x
+  |
+  o  2352021b3785 (9) added x to x
+       rewritten(parent) as 334e300d6db5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+  $ hg glog -r .^::
+  @  10:334e300d6db5 added x to x
+  |   (bm) draft
+  | o  9:2352021b3785 added x to x
+  | |   () public
+  | o  8:502e73736632 phase-divergent update to b756eb10ea73:
+  | |   () public
+  | o  6:b756eb10ea73 added bar to bar
+  |/    () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  ~
+
+Using `hg evolve` to resolve phase-divergence
+---------------------------------------------
+
+  $ hg evolve --phase-divergent
+  recreate:[10] added x to x
+  atop:[9] added x to x
+  rebasing to destination parent: 502e73736632
+  (leaving bookmark bm)
+  computing new diff
+  committed as 2352021b3785
+  working directory is now at 2352021b3785
+
+XXX: we should move bookmark here
+  $ hg glog
+  @  9:2352021b3785 added x to x
+  |   (bm) public
+  o  8:502e73736632 phase-divergent update to b756eb10ea73:
+  |   () public
+  o  6:b756eb10ea73 added bar to bar
+  |   () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+  $ hg obslog -r . b1a0e143e32b --all --hidden
+  x  b1a0e143e32b (11) added x to x
+  |    pruned using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  x  334e300d6db5 (10) added x to x
+  |    rewritten(parent) as b1a0e143e32b using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  @  2352021b3785 (9) added x to x
+       rewritten(parent) as 334e300d6db5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 2352021b37851be226ebed109b0eb6eada918566
+  # Parent  502e737366322886cf628276aa0a2796904453b4
+  added x to x
+  
+  diff -r 502e73736632 -r 2352021b3785 x
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/x	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +x
+
+Creating divergence with parent and content change both but not resulting in
+conflicts
+-----------------------------------------------------------------------------
+
+Alice is tired of pushing and pulling and will create phase-divergence locally
+
+  $ hg glog
+  @  9:2352021b3785 added x to x
+  |   (bm) public
+  o  8:502e73736632 phase-divergent update to b756eb10ea73:
+  |   () public
+  o  6:b756eb10ea73 added bar to bar
+  |   () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+  $ echo y > y
+  $ echo foobar >> foo
+  $ hg add y
+  $ hg ci -m "y to y and foobar to foo"
+  $ hg rebase -r . -d .^^^
+  rebasing 12:dc88f5aa9bc9 "y to y and foobar to foo" (tip)
+
+  $ echo foo > y
+  $ hg amend
+
+Alice making the old changeset public to have content-divergence
+
+  $ hg phase -r dc88f5aa9bc9 --public --hidden
+  1 new phase-divergent changesets
+  $ hg glog
+  @  14:13015a180eee y to y and foobar to foo
+  |   () draft
+  | o  12:dc88f5aa9bc9 y to y and foobar to foo
+  | |   () public
+  | o  9:2352021b3785 added x to x
+  | |   (bm) public
+  | o  8:502e73736632 phase-divergent update to b756eb10ea73:
+  |/    () public
+  o  6:b756eb10ea73 added bar to bar
+  |   () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+  $ hg obslog -r .
+  @  13015a180eee (14) y to y and foobar to foo
+  |
+  x  211ab84d1689 (13) y to y and foobar to foo
+  |    rewritten(content) as 13015a180eee using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  o  dc88f5aa9bc9 (12) y to y and foobar to foo
+       rewritten(parent) as 211ab84d1689 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+Resolving divergence using `hg evolve`
+-------------------------------------
+
+  $ hg evolve --phase-divergent
+  recreate:[14] y to y and foobar to foo
+  atop:[12] y to y and foobar to foo
+  rebasing to destination parent: 2352021b3785
+  computing new diff
+  committed as 8c2bb6fb44e9
+  working directory is now at 8c2bb6fb44e9
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 8c2bb6fb44e9443c64b3a2a3d061272c8e25e6ce
+  # Parent  dc88f5aa9bc90a6418899d267d9524205dfb429b
+  phase-divergent update to dc88f5aa9bc9:
+  
+  y to y and foobar to foo
+  
+  diff -r dc88f5aa9bc9 -r 8c2bb6fb44e9 y
+  --- a/y	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/y	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,1 @@
+  -y
+  +foo
+
+  $ hg glog
+  @  16:8c2bb6fb44e9 phase-divergent update to dc88f5aa9bc9:
+  |   () draft
+  o  12:dc88f5aa9bc9 y to y and foobar to foo
+  |   () public
+  o  9:2352021b3785 added x to x
+  |   (bm) public
+  o  8:502e73736632 phase-divergent update to b756eb10ea73:
+  |   () public
+  o  6:b756eb10ea73 added bar to bar
+  |   () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+Creating divergence with parent and content change both which results in
+conflicts while rebasing on parent
+-----------------------------------------------------------------------------
+
+  $ echo l > l
+  $ hg ci -Aqm "added l to l"
+  $ hg rebase -r . -d .^^^^
+  rebasing 17:f3794e5a91dc "added l to l" (tip)
+  $ echo kl > l
+  $ echo foo > x
+  $ hg add x
+  $ hg amend
+
+  $ hg obslog -r .
+  @  5fd38c0de46e (19) added l to l
+  |
+  x  2bfd56949cf0 (18) added l to l
+  |    rewritten(content) as 5fd38c0de46e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  x  f3794e5a91dc (17) added l to l
+       rewritten(parent) as 2bfd56949cf0 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+
+  $ hg phase -r f3794e5a91dc --public --hidden
+  1 new phase-divergent changesets
+
+Resolution using `hg evolve --phase-divergent`
+----------------------------------------------
+
+  $ hg evolve --phase-divergent
+  recreate:[19] added l to l
+  atop:[17] added l to l
+  rebasing to destination parent: 8c2bb6fb44e9
+  merging x
+  warning: conflicts while merging x! (edit, then use 'hg resolve --mark')
+  evolution failed!
+  fix conflict then run 'hg evolve --continue' or use `hg evolve --abort`
+  abort: unresolved merge conflicts (see hg help resolve)
+  [255]
+
+  $ hg diff
+  diff -r 8c2bb6fb44e9 l
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/l	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +kl
+  diff -r 8c2bb6fb44e9 x
+  --- a/x	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/x	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,5 @@
+  +<<<<<<< destination: 8c2bb6fb44e9 - test: phase-divergent update to dc88f5aa9...
+   x
+  +=======
+  +foo
+  +>>>>>>> evolving:    5fd38c0de46e - test: added l to l
+
+  $ echo foo > x
+
+  $ hg resolve -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+
+  $ hg evolve --continue
+  evolving 19:5fd38c0de46e "added l to l"
+  computing new diff
+  committed as e3090241a10c
+  working directory is now at e3090241a10c
+
+  $ hg glog
+  @  21:e3090241a10c phase-divergent update to f3794e5a91dc:
+  |   () draft
+  o  17:f3794e5a91dc added l to l
+  |   () public
+  o  16:8c2bb6fb44e9 phase-divergent update to dc88f5aa9bc9:
+  |   () public
+  o  12:dc88f5aa9bc9 y to y and foobar to foo
+  |   () public
+  o  9:2352021b3785 added x to x
+  |   (bm) public
+  o  8:502e73736632 phase-divergent update to b756eb10ea73:
+  |   () public
+  o  6:b756eb10ea73 added bar to bar
+  |   () public
+  o  5:3d62500c673d phase-divergent update to aa071e5554e3:
+  |   () public
+  o  3:aa071e5554e3 added foo to foo
+  |   () public
+  o  1:4d1169d82e47 modify a
+  |   () public
+  o  0:d3873e73d99e init
+      () public
+
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID e3090241a10c320b6132e4673915fd6b19c0de39
+  # Parent  f3794e5a91dc1d4d36fee5c423386b19433a1f48
+  phase-divergent update to f3794e5a91dc:
+  
+  added l to l
+  
+  diff -r f3794e5a91dc -r e3090241a10c l
+  --- a/l	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/l	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,1 @@
+  -l
+  +kl
+  diff -r f3794e5a91dc -r e3090241a10c x
+  --- a/x	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/x	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,1 @@
+  -x
+  +foo
+
+Creating phase divergence when couple of changesets are folded into one
+------------------------------------------------------------------------
+
+  $ hg glog -r .
+  @  21:e3090241a10c phase-divergent update to f3794e5a91dc:
+  |   () draft
+  ~
+  $ echo f > f
+  $ hg ci -Aqm "added f"
+  $ echo g > g
+  $ hg ci -Aqm "added g"
+
+  $ hg fold -r . -r .^ --exact
+  2 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg evolve --list
+
+  $ hg phase -r 428f7900a969 --public --hidden
+  1 new phase-divergent changesets
+
+  $ hg glog -r f3794e5a91dc::
+  @  24:e450d05b7d27 added g
+  |   () draft
+  | o  23:428f7900a969 added g
+  | |   () public
+  | o  22:21ae52e414e6 added f
+  |/    () public
+  o  21:e3090241a10c phase-divergent update to f3794e5a91dc:
+  |   () public
+  o  17:f3794e5a91dc added l to l
+  |   () public
+  ~
+
+  $ hg evolve --list
+  e450d05b7d27: added g
+    phase-divergent: 21ae52e414e6 (immutable precursor)
+    phase-divergent: 428f7900a969 (immutable precursor)
+  
+Resolving phase divergence using `hg evolve`
+
+  $ hg evolve --phase-divergent --all
+  recreate:[24] added g
+  atop:[23] added g
+  rebasing to destination parent: 21ae52e414e6
+  computing new diff
+  committed as 428f7900a969
+  working directory is now at 428f7900a969
+
+  $ hg glog -r f3794e5a91dc::
+  @  23:428f7900a969 added g
+  |   () public
+  o  22:21ae52e414e6 added f
+  |   () public
+  o  21:e3090241a10c phase-divergent update to f3794e5a91dc:
+  |   () public
+  o  17:f3794e5a91dc added l to l
+  |   () public
+  ~
+
+When the public changesets is splitted causing phase-divergence
+---------------------------------------------------------------
+
+  $ echo m > m
+  $ echo n > n
+  $ hg ci -Aqm "added m and n"
+
+  $ hg glog -r 21ae52e414e6::
+  @  26:849cee0a874b added m and n
+  |   () draft
+  o  23:428f7900a969 added g
+  |   () public
+  o  22:21ae52e414e6 added f
+  |   () public
+  ~
+
+  $ hg prev
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  [23] added g
+  $ echo m > m
+  $ hg ci -Aqm "added m"
+  $ echo n > n
+  $ hg ci -Aqm "added n"
+
+  $ hg glog -r 428f7900a969::
+  @  28:63ccb8ea7cae added n
+  |   () draft
+  o  27:f313e2b90e70 added m
+  |   () draft
+  | o  26:849cee0a874b added m and n
+  |/    () draft
+  o  23:428f7900a969 added g
+  |   () public
+  ~
+
+  $ hg prune -r 849cee0a874b --succ f313e2b90e70 --succ 63ccb8ea7cae --split
+  1 changesets pruned
+
+  $ hg phase -r 849cee0a874b --hidden --public
+  2 new phase-divergent changesets
+
+  $ hg glog -r 428f7900a969::
+  @  28:63ccb8ea7cae added n
+  |   () draft
+  *  27:f313e2b90e70 added m
+  |   () draft
+  | o  26:849cee0a874b added m and n
+  |/    () public
+  o  23:428f7900a969 added g
+  |   () public
+  ~
+
+  $ hg evolve --all --phase-divergent
+  recreate:[27] added m
+  atop:[26] added m and n
+  computing new diff
+  committed as 870e1c3eddc3
+  1 new orphan changesets
+  recreate:[28] added n
+  atop:[26] added m and n
+  rebasing to destination parent: 428f7900a969
+  computing new diff
+  committed as 154b0179fb9b
+  working directory is now at 154b0179fb9b
+
+XXX: this is messy, we should solve things in better way
+  $ hg glog -r 428f7900a969:: --hidden
+  @  31:154b0179fb9b phase-divergent update to 849cee0a874b:
+  |   () draft
+  | x  30:1ebf33547a82 added n
+  | |   () draft
+  +---o  29:870e1c3eddc3 phase-divergent update to 849cee0a874b:
+  | |     () draft
+  | | x  28:63ccb8ea7cae added n
+  | | |   () draft
+  | | x  27:f313e2b90e70 added m
+  | |/    () draft
+  o |  26:849cee0a874b added m and n
+  |/    () public
+  o  23:428f7900a969 added g
+  |   () public
+  ~
+
+XXX: not sure this is the correct
+  $ hg exp 154b0179fb9b
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 154b0179fb9b53d2f853d6ba04740bb3d7a5cabe
+  # Parent  849cee0a874be7c4e75dfacb5ad72aa5696951ba
+  phase-divergent update to 849cee0a874b:
+  
+  added n
+  
+  diff -r 849cee0a874b -r 154b0179fb9b m
+  --- a/m	Thu Jan 01 00:00:00 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -m
+
+XXX: not sure this is correct
+  $ hg exp 870e1c3eddc3
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 870e1c3eddc34cc475e8e13d2fe1934210c1937e
+  # Parent  849cee0a874be7c4e75dfacb5ad72aa5696951ba
+  phase-divergent update to 849cee0a874b:
+  
+  added m
+  
+  diff -r 849cee0a874b -r 870e1c3eddc3 n
+  --- a/n	Thu Jan 01 00:00:00 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -n
+
+When the public changeset is splitted across various branches
+--------------------------------------------------------------
+
+  $ echo p > p
+  $ echo q > q
+  $ hg ci -Aqm "added p and q"
+
+  $ hg prev
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  [31] phase-divergent update to 849cee0a874b:
+  $ echo p > p
+  $ hg ci -Aqm "added p"
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [31] phase-divergent update to 849cee0a874b:
+  $ echo q > q
+  $ hg ci -Aqm "added q"
+
+  $ hg glog -r 154b0179fb9b::
+  @  34:e046341aa97c added q
+  |   () draft
+  | o  33:6f8c250eecff added p
+  |/    () draft
+  | o  32:8a70f55b2af3 added p and q
+  |/    () draft
+  o  31:154b0179fb9b phase-divergent update to 849cee0a874b:
+  |   () draft
+  ~
+
+  $ hg prune -r 8a70f55b2af3 --succ 6f8c250eecff --succ e046341aa97c --split
+  1 changesets pruned
+
+  $ hg phase -r 8a70f55b2af3 --public --hidden
+  2 new phase-divergent changesets
+
+  $ hg glog -r 154b0179fb9b::
+  @  34:e046341aa97c added q
+  |   () draft
+  | *  33:6f8c250eecff added p
+  |/    () draft
+  | o  32:8a70f55b2af3 added p and q
+  |/    () public
+  o  31:154b0179fb9b phase-divergent update to 849cee0a874b:
+  |   () public
+  ~
+
+  $ hg evolve --list
+  6f8c250eecff: added p
+    phase-divergent: 8a70f55b2af3 (immutable precursor)
+  
+  e046341aa97c: added q
+    phase-divergent: 8a70f55b2af3 (immutable precursor)
+  
+  $ hg evolve --all --phase-divergent
+  recreate:[33] added p
+  atop:[32] added p and q
+  computing new diff
+  committed as f3e41d89b3c5
+  recreate:[34] added q
+  atop:[32] added p and q
+  computing new diff
+  committed as 605c306d4f87
+  working directory is now at 605c306d4f87
+
+  $ hg glog -r 154b0179fb9b:: --hidden
+  @  36:605c306d4f87 phase-divergent update to 8a70f55b2af3:
+  |   () draft
+  | o  35:f3e41d89b3c5 phase-divergent update to 8a70f55b2af3:
+  |/    () draft
+  | x  34:e046341aa97c added q
+  | |   () draft
+  | | x  33:6f8c250eecff added p
+  | |/    () draft
+  o |  32:8a70f55b2af3 added p and q
+  |/    () public
+  o  31:154b0179fb9b phase-divergent update to 849cee0a874b:
+  |   () public
+  ~
+
+XXX: not sure this is correct
+  $ hg exp 605c306d4f87
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 605c306d4f87fccfdb5e7dd1c750b6d4f813defb
+  # Parent  8a70f55b2af35452916dc89401a5ecf6553646a5
+  phase-divergent update to 8a70f55b2af3:
+  
+  added q
+  
+  diff -r 8a70f55b2af3 -r 605c306d4f87 p
+  --- a/p	Thu Jan 01 00:00:00 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -p
+
+XXX: not sure this is correct
+  $ hg exp f3e41d89b3c5
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID f3e41d89b3c5f6ee49ccc734045856d7b025f048
+  # Parent  8a70f55b2af35452916dc89401a5ecf6553646a5
+  phase-divergent update to 8a70f55b2af3:
+  
+  added p
+  
+  diff -r 8a70f55b2af3 -r f3e41d89b3c5 q
+  --- a/q	Thu Jan 01 00:00:00 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -q
+
+Testing the evolution of a phase-divergent merge with no conflicts
+------------------------------------------------------------------
+
+  $ hg glog -r 154b0179fb9b::
+  @  36:605c306d4f87 phase-divergent update to 8a70f55b2af3:
+  |   () draft
+  | o  35:f3e41d89b3c5 phase-divergent update to 8a70f55b2af3:
+  |/    () draft
+  o  32:8a70f55b2af3 added p and q
+  |   () public
+  o  31:154b0179fb9b phase-divergent update to 849cee0a874b:
+  |   () public
+  ~
+
+  $ echo h > h
+  $ hg ci -Aqm "added h"
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [36] phase-divergent update to 8a70f55b2af3:
+  $ echo i > i
+  $ hg ci -Aqm "added i"
+  $ hg merge -r ef8c23f37b55
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "merge h and i"
+
+  $ hg glog -r 605c306d4f87::
+  @    39:12ebe0d625d7 merge h and i
+  |\    () draft
+  | o  38:9bb561db4230 added i
+  | |   () draft
+  o |  37:ef8c23f37b55 added h
+  |/    () draft
+  o  36:605c306d4f87 phase-divergent update to 8a70f55b2af3:
+  |   () draft
+  ~
+
+  $ hg up ef8c23f37b55
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg merge -r 9bb561db4230
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "merge h and i successor"
+  created new head
+  $ hg glog -r 605c306d4f87::
+  @    40:d2aeda868461 merge h and i successor
+  |\    () draft
+  +---o  39:12ebe0d625d7 merge h and i
+  | |/    () draft
+  | o  38:9bb561db4230 added i
+  | |   () draft
+  o |  37:ef8c23f37b55 added h
+  |/    () draft
+  o  36:605c306d4f87 phase-divergent update to 8a70f55b2af3:
+  |   () draft
+  ~
+
+  $ hg prune -r 12ebe0d625d7 --succ .
+  1 changesets pruned
+
+  $ hg phase 12ebe0d625d7 --hidden --public
+  1 new phase-divergent changesets
+
+Resolution of phase-divergent merge commit using `hg evolve`
+
+XXX: we should handle phase-divergent merges
+  $ hg evolve --phase-divergent
+  skipping d2aeda868461 : we do not handle merge yet
--- a/tests/test-evolve-phase.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve-phase.t	Mon Apr 23 11:04:27 2018 +0200
@@ -87,7 +87,7 @@
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
--- a/tests/test-evolve-stop.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve-stop.t	Mon Apr 23 11:04:27 2018 +0200
@@ -89,7 +89,7 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -137,7 +137,7 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -186,7 +186,7 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
   $ echo foo > d
@@ -239,7 +239,7 @@
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -278,7 +278,7 @@
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
@@ -356,7 +356,7 @@
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
--- a/tests/test-evolve-templates.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve-templates.t	Mon Apr 23 11:04:27 2018 +0200
@@ -19,7 +19,6 @@
   >     {if(successors, "\n  semi-colon: {join(successors, "; ")}")}\
   >     {if(obsfate, "\n  Fate: {join(obsfate, "\n  Fate: ")}\n")}\n'
   > fatelog = log -G -T '{node|short}\n{if(obsfate, "  Obsfate: {join(obsfate, "; ")}\n\n")}'
-  > fatelogjson = log -G -T '{node|short} {obsfatedata|json}\n'
   > EOF
 
 Test templates on amended commit
@@ -246,16 +245,6 @@
   o  ea207398892e
   
 
-  $ hg fatelogjson --hidden
-  @  d004c8f274b9 []
-  |
-  | x  a468dc9b3633 [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test2"]], [987654321.0, 0], null]], "max_date": [987654321.0, 0], "min_date": [987654321.0, 0], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], "users": ["test2"], "verb": "reworded"}]
-  |/
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["ef1", "9"], ["operation", "amend"], ["user", "test"]], [1234567890.0, 0], null]], "max_date": [1234567890.0, 0], "min_date": [1234567890.0, 0], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], "users": ["test"], "verb": "rewritten"}]
-  |/
-  o  ea207398892e []
-  
-
 Test templates with splitted commit
 ===================================
 
@@ -414,16 +403,6 @@
   o  ea207398892e
   
 
-  $ hg fatelogjson --hidden
-  @  f257fde29c7a []
-  |
-  o  337fec4d2edc []
-  |
-  | x  471597cad322 [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["ef1", "12"], ["operation", "split"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], "users": ["test"], "verb": "split"}]
-  |/
-  o  ea207398892e []
-  
-
 Test templates with folded commit
 ==============================
 
@@ -587,16 +566,6 @@
   o  ea207398892e
   
 
-  $ hg fatelogjson --hidden
-  @  eb5a0daa2192 []
-  |
-  | x  0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "13"], ["operation", "fold"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
-  | |
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "9"], ["operation", "fold"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
-  |/
-  o  ea207398892e []
-  
-
 Test templates with divergence
 ==============================
 
@@ -758,18 +727,6 @@
   o  ea207398892e
   
 
-  $ hg fatelogjson --hidden
-  *  019fadeab383 []
-  |
-  | x  65b757b745b9 [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], "users": ["test"], "verb": "reworded"}]
-  |/
-  | @  fdf9bde5129a []
-  |/
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], "users": ["test"], "verb": "reworded"}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"], "users": ["test"], "verb": "reworded"}]
-  |/
-  o  ea207398892e []
-  
-
 Test templates with amended + folded commit
 ===========================================
 
@@ -981,17 +938,6 @@
   |
   o  ea207398892e
   
-  $ hg fatelogjson --hidden
-  @  eb5a0daa2192 []
-  |
-  | x  b7ea6d14e664 [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "13"], ["operation", "fold"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
-  | |
-  | | x  0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], "users": ["test"], "verb": "reworded"}]
-  | |/
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "9"], ["operation", "fold"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
-  |/
-  o  ea207398892e []
-  
 
 Test template with pushed and pulled obs markers
 ==============================================
@@ -1134,14 +1080,6 @@
   |
   o  ea207398892e
   
-
-  $ hg fatelogjson --hidden
-  @  7a230b46bf61 []
-  |
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null], ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e", ["7a230b46bf61e50b30308c6cfd7bd1269ef54702"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["7a230b46bf61e50b30308c6cfd7bd1269ef54702"], "users": ["test"], "verb": "reworded"}]
-  |/
-  o  ea207398892e []
-  
  
 Test template with obsmarkers cycle
 ===================================
--- a/tests/test-evolve.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-evolve.t	Mon Apr 23 11:04:27 2018 +0200
@@ -574,7 +574,7 @@
 
   $ cd ..
 
-Test graft --obsolete/--old-obsolete
+Normal testing
 
   $ hg init test-graft
   $ cd test-graft
@@ -596,14 +596,16 @@
   |/
   o  0:8685c6d34325@default(draft) add 0
   
-  $ hg graft -r3 -O
-  grafting 3:0e84df4912da "add 3"
-  $ hg graft -r1 -o 2
+  $ hg grab -r3
+  grabbing 3:0e84df4912da "add 3"
+  $ hg graft -r1
   grafting 1:73d38bb17fd7 "add 1"
+  $ hg prune -r2 --succ .
+  1 changesets pruned
   $ glog --hidden
-  @  6:acb28cd497b7@default(draft) add 1
+  @  6:417185465d2c@default(draft) add 1
   |
-  o  5:0b9e50c35132@default(draft) add 3
+  o  5:fa455b5098e0@default(draft) add 3
   |
   o  4:ce341209337f@default(draft) add 4
   |
@@ -616,39 +618,37 @@
   o  0:8685c6d34325@default(draft) add 0
   
   $ hg debugobsolete
-  0e84df4912da4c7cad22a3b4fcfd58ddfb7c8ae9 0b9e50c35132ff548ec0065caea6a87e1ebcef32 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'amend', 'user': 'test'}
-  db038628b9e56f51a454c0da0c508df247b41748 acb28cd497b7f8767e01ef70f68697a959573c2d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'amend', 'user': 'test'}
+  0e84df4912da4c7cad22a3b4fcfd58ddfb7c8ae9 fa455b5098e0ce8c1871edf6369f32be7d8b4d1c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'grab', 'user': 'test'}
+  db038628b9e56f51a454c0da0c508df247b41748 417185465d2c68e575cff4cd6ed8a4047505ef24 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'prune', 'user': 'test'}
 
-Test graft --continue
+Test grab --continue
 
   $ hg up -qC 0
   $ echo 2 > 1
   $ hg ci -Am conflict 1
   created new head
   $ hg up -qC 6
-  $ hg graft -O 7
-  grafting 7:a5bfd90a2f29 "conflict" (tip)
+  $ hg grab -r 7
+  grabbing 7:a5bfd90a2f29 "conflict"
   merging 1
   warning: conflicts while merging 1! (edit, then use 'hg resolve --mark')
-  abort: unresolved conflicts, can't continue
-  (use 'hg resolve' and 'hg graft --continue')
-  [255]
+  unresolved merge conflicts (see hg help resolve)
+  [1]
   $ hg log -r7 --template '{rev}:{node|short} {obsolete}\n'
   7:a5bfd90a2f29 
   $ echo 3 > 1
   $ hg resolve -m 1
   (no more unresolved files)
-  continue: hg graft --continue
-  $ hg graft --continue -O
-  grafting 7:a5bfd90a2f29 "conflict" (tip)
+  continue: hg grab --continue
+  $ hg grab --continue
   $ glog --hidden
-  @  8:920e58bb443b@default(draft) conflict
+  @  8:fb2c0f0a0c54@default(draft) conflict
   |
   | x  7:a5bfd90a2f29@default(draft) conflict
   | |
-  o |  6:acb28cd497b7@default(draft) add 1
+  o |  6:417185465d2c@default(draft) add 1
   | |
-  o |  5:0b9e50c35132@default(draft) add 3
+  o |  5:fa455b5098e0@default(draft) add 3
   | |
   o |  4:ce341209337f@default(draft) add 4
   |/
@@ -661,18 +661,18 @@
   o  0:8685c6d34325@default(draft) add 0
   
   $ hg debugobsolete
-  0e84df4912da4c7cad22a3b4fcfd58ddfb7c8ae9 0b9e50c35132ff548ec0065caea6a87e1ebcef32 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'amend', 'user': 'test'}
-  db038628b9e56f51a454c0da0c508df247b41748 acb28cd497b7f8767e01ef70f68697a959573c2d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'amend', 'user': 'test'}
-  a5bfd90a2f29c7ccb8f917ff4e5013a9053d0a04 920e58bb443b73eea9d6d65570b4241051ea3229 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'amend', 'user': 'test'}
+  0e84df4912da4c7cad22a3b4fcfd58ddfb7c8ae9 fa455b5098e0ce8c1871edf6369f32be7d8b4d1c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'grab', 'user': 'test'}
+  db038628b9e56f51a454c0da0c508df247b41748 417185465d2c68e575cff4cd6ed8a4047505ef24 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'prune', 'user': 'test'}
+  a5bfd90a2f29c7ccb8f917ff4e5013a9053d0a04 fb2c0f0a0c54be4367988521bad2cbd33a540969 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'grab', 'user': 'test'}
 
 Test touch
 
   $ glog
-  @  8:920e58bb443b@default(draft) conflict
+  @  8:fb2c0f0a0c54@default(draft) conflict
   |
-  o  6:acb28cd497b7@default(draft) add 1
+  o  6:417185465d2c@default(draft) add 1
   |
-  o  5:0b9e50c35132@default(draft) add 3
+  o  5:fa455b5098e0@default(draft) add 3
   |
   o  4:ce341209337f@default(draft) add 4
   |
@@ -684,9 +684,9 @@
   $ glog
   @  9:*@default(draft) conflict (glob)
   |
-  o  6:acb28cd497b7@default(draft) add 1
+  o  6:417185465d2c@default(draft) add 1
   |
-  o  5:0b9e50c35132@default(draft) add 3
+  o  5:fa455b5098e0@default(draft) add 3
   |
   o  4:ce341209337f@default(draft) add 4
   |
@@ -698,9 +698,9 @@
   $ glog
   @  10:*@default(draft) conflict (glob)
   |
-  o  6:acb28cd497b7@default(draft) add 1
+  o  6:417185465d2c@default(draft) add 1
   |
-  o  5:0b9e50c35132@default(draft) add 3
+  o  5:fa455b5098e0@default(draft) add 3
   |
   o  4:ce341209337f@default(draft) add 4
   |
@@ -765,14 +765,14 @@
   $ hg olog | head -n 10 # hg touch makes the output unstable (fix it with devel option for more stable touch)
   @    d26d339c513f (12) add 4
   |\
-  x |    af636757ce3b (11) add 3
-  |\ \     rewritten(description, user, parent, content) as d26d339c513f using fold by test (Thu Jan 01 00:00:00 1970 +0000)
-  | | |
-  | \ \
-  | |\ \
-  | | | x  ce341209337f (4) add 4
-  | | |      rewritten(description, user, content) as d26d339c513f using fold by test (Thu Jan 01 00:00:00 1970 +0000)
-  | | |
+  x |  ce341209337f (4) add 4
+   /     rewritten(description, user, content) as d26d339c513f using fold by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  x    cf0c3904643c (11) add 3
+  |\     rewritten(description, user, parent, content) as d26d339c513f using fold by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |
+  | \
+  | |\
 
 Test obsstore stat
 
@@ -793,7 +793,7 @@
           smallest length:           90
           longer length:             92
           median length:             91
-          mean length:               91
+          mean length:               90
       format v0:
           smallest length:           * (glob)
           longer length:             * (glob)
@@ -1536,7 +1536,7 @@
   merging newfile
   warning: conflicts while merging newfile! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
--- a/tests/test-fold.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-fold.t	Mon Apr 23 11:04:27 2018 +0200
@@ -112,8 +112,8 @@
 (test inherited from test-evolve.t)
 
   $ hg fold --from 6 # want to run hg fold 6
-  abort: hidden revision '6'!
-  (use --hidden to access hidden revisions; successor: 198b5c405d01)
+  abort: hidden revision '6' was rewritten as: 198b5c405d01!
+  (use --hidden to access hidden revisions)
   [255]
 
   $ hg log -G
--- a/tests/test-issue-5720.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-issue-5720.t	Mon Apr 23 11:04:27 2018 +0200
@@ -62,7 +62,7 @@
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
--- a/tests/test-prune.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-prune.t	Mon Apr 23 11:04:27 2018 +0200
@@ -306,8 +306,8 @@
   bookmark 'todelete' deleted
   1 changesets pruned
   $ hg id -ir dcbb326fdec2
-  abort: hidden revision 'dcbb326fdec2'!
-  (use --hidden to access hidden revisions; pruned)
+  abort: hidden revision 'dcbb326fdec2' is pruned!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg id -ir d62d843c9a01
   d62d843c9a01
@@ -340,8 +340,8 @@
   3 changesets pruned
   $ hg tag --remove --local c
   $ hg id -ir 6:2702dd0c91e7
-  abort: hidden revision '6'!
-  (use --hidden to access hidden revisions; pruned)
+  abort: hidden revision '6' is pruned!
+  (use --hidden to access hidden revisions)
   [255]
 
   $ hg debugobsstorestat
--- a/tests/test-stabilize-conflict.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-stabilize-conflict.t	Mon Apr 23 11:04:27 2018 +0200
@@ -129,7 +129,7 @@
   merging babar
   warning: conflicts while merging babar! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
   $ hg resolve -l
@@ -223,7 +223,7 @@
   was merge successful (yn)? n
   merging babar failed!
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
   $ hg resolve -l
--- a/tests/test-stabilize-order.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-stabilize-order.t	Mon Apr 23 11:04:27 2018 +0200
@@ -129,7 +129,6 @@
   
   $ hg evolve -v
   no troubled changesets
-  [1]
 
 Test behavior with --any
 
--- a/tests/test-stabilize-result.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-stabilize-result.t	Mon Apr 23 11:04:27 2018 +0200
@@ -40,6 +40,8 @@
   perform evolve? [Ny] y
   hg rebase -r cce2c55b8965 -d fb9d051ec0a4
   resolving manifests
+  evolution of 2:cce2c55b8965 created no changes to commit
+
   $ glog --hidden
   @  3:fb9d051ec0a4@default(draft) bk:[changea] changea
   |
@@ -80,7 +82,7 @@
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
   evolve failed!
-  fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+  fix conflict and run 'hg evolve --continue' or use 'hg evolve --abort' to abort
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
   $ hg revert -r "orphan()" a
@@ -173,10 +175,10 @@
   perform evolve? [Ny] y
   rebasing to destination parent: 66719795a494
   computing new diff
-  committed as 3d968e0b3097
-  working directory is now at 3d968e0b3097
+  committed as 8fc63fe1f297
+  working directory is now at 8fc63fe1f297
   $ glog
-  @  11:3d968e0b3097@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
+  @  11:8fc63fe1f297@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
   |
   | o  7:7bc2f5967f5e@default(draft) bk:[] add c
   | |
@@ -186,13 +188,32 @@
   |
   o  0:07f494440405@default(public) bk:[] adda
   
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 8fc63fe1f297f356d1156bbbbe865b9911efad74
+  # Parent  1cf0aacfd36310b18e403e1594871187e0364a82
+  phase-divergent update to 1cf0aacfd363:
+  
+  newer a
+  
+  diff -r 1cf0aacfd363 -r 8fc63fe1f297 a
+  --- a/a	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/a	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,3 +1,4 @@
+   a
+   a
+   newer a
+  +babar
 
 Stabilize divergent changesets with same parent
 ===============================================
 
   $ rm a.orig
   $ hg up 7bc2f5967f5e
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat << EOF >> a
   > flore
   > arthur
@@ -205,7 +226,7 @@
   $ glog
   @  12:3932c176bbaa@default(draft) bk:[] More addition
   |
-  | o  11:3d968e0b3097@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
+  | o  11:8fc63fe1f297@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
   | |
   o |  7:7bc2f5967f5e@default(draft) bk:[] add c
   | |
@@ -234,7 +255,7 @@
   |
   | *  13:d2f173e25686@default(draft) bk:[] More addition
   |/
-  | o  11:3d968e0b3097@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
+  | o  11:8fc63fe1f297@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
   | |
   o |  7:7bc2f5967f5e@default(draft) bk:[] add c
   | |
@@ -284,7 +305,7 @@
   $ glog
   @  15:f344982e63c4@default(draft) bk:[] More addition
   |
-  | o  11:3d968e0b3097@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
+  | o  11:8fc63fe1f297@default(draft) bk:[] phase-divergent update to 1cf0aacfd363:
   | |
   o |  7:7bc2f5967f5e@default(draft) bk:[] add c
   | |
--- a/tests/test-topic.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-topic.t	Mon Apr 23 11:04:27 2018 +0200
@@ -115,6 +115,24 @@
   abort: topic name cannot consist entirely of whitespaces
   [255]
 
+  $ hg topic 'a12#45'
+  abort: invalid topic name: 'a12#45'
+  (topic names can only consist of alphanumeric, '-' '_' and '.' characters)
+  [255]
+
+  $ hg topic 'foo bar'
+  abort: invalid topic name: 'foo bar'
+  (topic names can only consist of alphanumeric, '-' '_' and '.' characters)
+  [255]
+
+this is trying to list topic names
+  $ hg topic ''
+
+  $ hg topic '*12 B23'
+  abort: invalid topic name: '*12 B23'
+  (topic names can only consist of alphanumeric, '-' '_' and '.' characters)
+  [255]
+
 Test commit flag and help text
 
   $ echo stuff >> alpha
--- a/tests/test-userguide.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-userguide.t	Mon Apr 23 11:04:27 2018 +0200
@@ -38,8 +38,8 @@
   $ echo 'tweak feature Y' >> file1.c
   $ hg commit --amend -u alice -d '2 0' -m 'implement feature Y'
   $ hg shortlog -q -r fe0ecd3bd2a4
-  abort: hidden revision 'fe0ecd3bd2a4'!
-  (use --hidden to access hidden revisions; successor: 934359450037)
+  abort: hidden revision 'fe0ecd3bd2a4' was rewritten as: 934359450037!
+  (use --hidden to access hidden revisions)
   [255]
   $ hg --hidden shortlog -G
   @  2:934359450037  draft  implement feature Y
--- a/tests/test-wireproto.t	Fri Mar 23 09:08:21 2018 -0700
+++ b/tests/test-wireproto.t	Mon Apr 23 11:04:27 2018 +0200
@@ -195,7 +195,7 @@
   $ cat hg.pid >> $DAEMON_PIDS
 
   $ curl -s http://localhost:$HGPORT/?cmd=capabilities
-  _evoext_getbundle_obscommon batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps changegroupsubset compression=*zlib getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (no-eol) (glob)
+  _evoext_getbundle_obscommon batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=*zlib getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (no-eol) (glob)
 
 Check we cannot use pushkey for marker exchange anymore