changeset 4782:8fcdf221b046 mercurial-4.8

test-compat: merge mercurial-4.9 into mercurial-4.8
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 29 Jul 2019 14:43:12 +0200
parents 5ad6d92f125c (diff) 97f1a229dd99 (current diff)
children 5d50f3de4714 31c481934138
files tests/test-discovery-obshashrange.t tests/test-evolve-abort-orphan.t tests/test-evolve-stop-orphan.t tests/test-split.t tests/test-topic-stack.t tests/test-topic.t
diffstat 66 files changed, 875 insertions(+), 425 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Mon Jul 29 11:40:18 2019 +0200
+++ b/CHANGELOG	Mon Jul 29 14:43:12 2019 +0200
@@ -1,6 +1,18 @@
 Changelog
 =========
 
+9.1.0 - in progress
+-------------------
+
+  * evolve: use the same wording as core in case of unresolved conflict
+  * evolve: minor output message improvements
+  * evolve: improve `hg evolve --all` behavior when "." is obsolete
+  * topic: fix confusion in branch heads checking logic
+  * touch: now works on merge commit too
+  * rewind: fix behavior for merge commit
+  * fold: allow fold with merge commit
+  * metaedit: now also operates on merge commit
+
 9.0.1 - in progress
 -------------------
 
--- a/hgext3rd/evolve/__init__.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/__init__.py	Mon Jul 29 14:43:12 2019 +0200
@@ -278,6 +278,7 @@
     lock as lockmod,
     node,
     patch,
+    pycompat,
     revset,
     scmutil,
 )
@@ -298,7 +299,6 @@
     obshashtree,
     obshistory,
     rewind,
-    rewriteutil,
     safeguard,
     templatekw,
     utility,
@@ -329,10 +329,7 @@
 _pack = struct.pack
 _unpack = struct.unpack
 
-aliases, entry = cmdutil.findcmd('commit', commands.table)
-commitopts3 = cmdrewrite.commitopts3
-interactiveopt = cmdrewrite.interactiveopt
-rewrite = rewriteutil.rewrite
+aliases, entry = cmdutil.findcmd(b'commit', commands.table)
 
 # This extension contains the following code
 #
@@ -403,7 +400,7 @@
         repo.ui.setconfig('experimental', 'evolution', evolveopts, 'evolve')
     if obsolete.isenabled(repo, 'exchange'):
         # if no config explicitly set, disable bundle1
-        if not isinstance(repo.ui.config('server', 'bundle1'), str):
+        if not isinstance(repo.ui.config('server', 'bundle1'), bytes):
             repo.ui.setconfig('server', 'bundle1', False)
 
     class trdescrepo(repo.__class__):
@@ -433,8 +430,9 @@
             if not matchingevolvecommands:
                 raise error.Abort(_('unknown command: %s') % cmd)
             elif len(matchingevolvecommands) > 1:
-                msg = _('ambiguous command specification: "%s" matches %r')
-                raise error.Abort(msg % (cmd, matchingevolvecommands))
+                matchstr = ', '.join(matchingevolvecommands)
+                msg = _("ambiguous command specification: '%s' matches [%s]")
+                raise error.Abort(msg % (cmd, matchstr))
             else:
                 whitelist.add(matchingevolvecommands[0])
         for disabledcmd in set(cmdtable) - whitelist:
@@ -467,7 +465,7 @@
     _alias, statuscmd = cmdutil.findcmd('status', commands.table)
     pstatusopts = [o for o in statuscmd[1] if o[1] != 'rev']
 
-    @eh.command('pstatus', pstatusopts)
+    @eh.command(b'pstatus', pstatusopts)
     def pstatus(ui, repo, *args, **kwargs):
         """show status combining committed and uncommited changes
 
@@ -482,7 +480,7 @@
     _alias, diffcmd = cmdutil.findcmd('diff', commands.table)
     pdiffopts = [o for o in diffcmd[1] if o[1] != 'rev']
 
-    @eh.command('pdiff', pdiffopts)
+    @eh.command(b'pdiff', pdiffopts)
     def pdiff(ui, repo, *args, **kwargs):
         """show diff combining committed and uncommited changes
 
@@ -503,7 +501,7 @@
 
 ### Unstable revset symbol
 
-@eh.revsetpredicate('unstable()')
+@eh.revsetpredicate(b'unstable()')
 def revsetunstable(repo, subset, x):
     """Changesets with instabilities.
     """
@@ -516,7 +514,7 @@
     troubled.sort() # set is non-ordered, enforce order
     return subset & troubled
 
-@eh.revsetpredicate('troubled()')    # legacy name
+@eh.revsetpredicate(b'troubled()')    # legacy name
 def revsettroubled(repo, subset, x):
     return revsetunstable(repo, subset, x)
 
@@ -615,7 +613,7 @@
 
 
 ### XXX I'm not sure this revset is useful
-@eh.revsetpredicate('suspended()')
+@eh.revsetpredicate(b'suspended()')
 def revsetsuspended(repo, subset, x):
     """Obsolete changesets with non-obsolete descendants.
     """
@@ -625,7 +623,7 @@
     return subset & suspended
 
 
-@eh.revsetpredicate('predecessors(set)')
+@eh.revsetpredicate(b'predecessors(set)')
 def revsetpredecessors(repo, subset, x):
     """Immediate predecessors of changesets in set.
     """
@@ -635,12 +633,12 @@
     return subset & s
 
 
-@eh.revsetpredicate('precursors(set)')   # legacy name for predecessors
+@eh.revsetpredicate(b'precursors(set)')   # legacy name for predecessors
 def revsetprecursors(repo, subset, x):
     return revsetpredecessors(repo, subset, x)
 
 
-@eh.revsetpredicate('allpredecessors(set)')
+@eh.revsetpredicate(b'allpredecessors(set)')
 def revsetallpredecessors(repo, subset, x):
     """Transitive predecessors of changesets in set.
     """
@@ -650,12 +648,12 @@
     return subset & s
 
 
-@eh.revsetpredicate('allprecursors(set)')   # legacy name for allpredecessors
+@eh.revsetpredicate(b'allprecursors(set)')   # legacy name for allpredecessors
 def revsetallprecursors(repo, subset, x):
     return revsetallpredecessors(repo, subset, x)
 
 
-@eh.revsetpredicate('successors(set)')
+@eh.revsetpredicate(b'successors(set)')
 def revsetsuccessors(repo, subset, x):
     """Immediate successors of changesets in set.
     """
@@ -664,7 +662,7 @@
     s.sort()
     return subset & s
 
-@eh.revsetpredicate('allsuccessors(set)')
+@eh.revsetpredicate(b'allsuccessors(set)')
 def revsetallsuccessors(repo, subset, x):
     """Transitive successors of changesets in set.
     """
@@ -890,7 +888,7 @@
         ui = args[0]
         ui.warn(deprecationwarning)
         util.checksignature(fn)(*args, **kwargs)
-    newfn.__doc__ = deprecationwarning + ' (DEPRECATED)'
+    newfn.__doc__ = pycompat.sysstr(deprecationwarning + ' (DEPRECATED)')
     cmdwrapper = eh.command(oldalias, opts, synopsis)
     cmdwrapper(newfn)
 
@@ -983,14 +981,14 @@
     return target, bookmark
 
 @eh.command(
-    'previous',
-    [('B', 'move-bookmark', False,
-        _('move active bookmark after update')),
-     ('m', 'merge', False, _('bring uncommitted change along')),
-     ('', 'no-topic', False, _('ignore topic and move topologically')),
-     ('n', 'dry-run', False,
-        _('do not perform actions, just print what would be done'))],
-    '[OPTION]...',
+    b'previous',
+    [(b'B', b'move-bookmark', False,
+        _(b'move active bookmark after update')),
+     (b'm', b'merge', False, _(b'bring uncommitted change along')),
+     (b'', b'no-topic', False, _(b'ignore topic and move topologically')),
+     (b'n', b'dry-run', False,
+        _(b'do not perform actions, just print what would be done'))],
+    b'[OPTION]...',
     helpbasic=True)
 def cmdprevious(ui, repo, **opts):
     """update to parent revision
@@ -1041,15 +1039,15 @@
         lockmod.release(wlock)
 
 @eh.command(
-    'next',
-    [('B', 'move-bookmark', False,
-        _('move active bookmark after update')),
-     ('m', 'merge', False, _('bring uncommitted change along')),
-     ('', 'evolve', True, _('evolve the next changeset if necessary')),
-     ('', 'no-topic', False, _('ignore topic and move topologically')),
-     ('n', 'dry-run', False,
-      _('do not perform actions, just print what would be done'))],
-    '[OPTION]...',
+    b'next',
+    [(b'B', b'move-bookmark', False,
+        _(b'move active bookmark after update')),
+     (b'm', b'merge', False, _(b'bring uncommitted change along')),
+     (b'', b'evolve', True, _(b'evolve the next changeset if necessary')),
+     (b'', b'no-topic', False, _(b'ignore topic and move topologically')),
+     (b'n', b'dry-run', False,
+      _(b'do not perform actions, just print what would be done'))],
+    b'[OPTION]...',
     helpbasic=True)
 def cmdnext(ui, repo, **opts):
     """update to next child revision
@@ -1175,18 +1173,18 @@
     # making sure a next commit is formed
     if result[0] and result[1]:
         ui.status(_('working directory is now at %s\n')
-                  % ui.label(str(repo['.']), 'evolve.node'))
+                  % ui.label(bytes(repo['.']), 'evolve.node'))
     return 0
 
-def _updatetonext(ui, repo, children, displayer, opts):
+def _updatetonext(ui, repo, child, displayer, opts):
     """ logic for `hg next` command to update to children and move bookmarks if
     required """
     bm = repo._activebookmark
     shouldmove = opts.get('move_bookmark') and bm is not None
     if opts.get('dry_run'):
-        ui.write(_('hg update %s;\n') % children)
+        ui.write(_('hg update %s;\n') % child)
         if shouldmove:
-            ui.write(_('hg bookmark %s -r %s;\n') % (bm, children))
+            ui.write(_('hg bookmark %s -r %s;\n') % (bm, child))
     else:
         updatecheck = None
         # --merge is passed, we don't need to care about commands.update.check
@@ -1194,7 +1192,7 @@
         if opts['merge']:
             updatecheck = 'none'
         try:
-            ret = hg.updatetotally(ui, repo, children.node(), None,
+            ret = hg.updatetotally(ui, repo, child.node(), None,
                                    updatecheck=updatecheck)
         except error.Abort as exc:
             # replace the hint to mention about --merge option
@@ -1207,7 +1205,7 @@
                 lock = repo.lock()
                 tr = repo.transaction('next')
                 if shouldmove:
-                    bmchanges = [(bm, children.node())]
+                    bmchanges = [(bm, child.node())]
                     repo._bookmarks.applychanges(repo, tr, bmchanges)
                 else:
                     bookmarksmod.deactivate(repo)
@@ -1215,7 +1213,7 @@
             finally:
                 lockmod.release(tr, lock)
     if not ui.quiet:
-        displayer.show(children)
+        displayer.show(child)
     return 0
 
 @eh.wrapcommand('commit')
@@ -1302,9 +1300,9 @@
             raise error.Abort(msg, hint=hint)
 
 @eh.command(
-    'debugobsconvert',
-    [('', 'new-format', obsexchange._bestformat, _('Destination format for markers.'))],
-    '')
+    b'debugobsconvert',
+    [(b'', b'new-format', obsexchange._bestformat, _(b'Destination format for markers.'))],
+    b'')
 def debugobsconvert(ui, repo, new_format):
     origmarkers = repo.obsstore._all  # settle version
     if new_format == repo.obsstore._version:
@@ -1326,7 +1324,8 @@
             markers.append(m)
         ui.write(_('Old store is version %d, will rewrite in version %d\n') % (
             repo.obsstore._version, new_format))
-        map(f.write, obsolete.encodemarkers(markers, True, new_format))
+        for data in obsolete.encodemarkers(markers, True, new_format):
+            f.write(data)
         f.close()
     ui.write(_('Done!\n'))
 
@@ -1355,17 +1354,29 @@
 
 @eh.uisetup
 def setupevolveunfinished(ui):
-    estate = ('evolvestate', False, False, _('evolve in progress'),
-              _("use 'hg evolve --continue' or 'hg evolve --abort' to abort"))
-    cmdutil.unfinishedstates.append(estate)
-    pstate = ('pickstate', False, False, _('pick in progress'),
-              _("use 'hg pick --continue' or 'hg pick --abort' to abort"))
-    cmdutil.unfinishedstates.append(pstate)
+    if not util.safehasattr(cmdutil, 'unfinishedstates'):
+        from mercurial import state as statemod
+        _msg = _('To continue:    hg evolve --continue\n'
+                 'To abort:       hg evolve --abort\n'
+                 'To stop:        hg evolve --stop\n'
+                 '(also see `hg help evolve.interrupted`)')
+        statemod.addunfinished('evolve', fname='evolvestate',
+                               continueflag=True, stopflag=True,
+                               statushint=_msg)
+        statemod.addunfinished('pick', fname='pickstate', continueflag=True)
+    else:
+        # compat <= hg-5.0 (5f2f6912c9e6)
+        estate = ('evolvestate', False, False, _('evolve in progress'),
+                  _("use 'hg evolve --continue' or 'hg evolve --abort' to abort"))
+        cmdutil.unfinishedstates.append(estate)
+        pstate = ('pickstate', False, False, _('pick in progress'),
+                  _("use 'hg pick --continue' or 'hg pick --abort' to abort"))
+        cmdutil.unfinishedstates.append(pstate)
 
-    afterresolved = ('evolvestate', _('hg evolve --continue'))
-    pickresolved = ('pickstate', _('hg pick --continue'))
-    cmdutil.afterresolvedstates.append(afterresolved)
-    cmdutil.afterresolvedstates.append(pickresolved)
+        afterresolved = ('evolvestate', _('hg evolve --continue'))
+        pickresolved = ('pickstate', _('hg pick --continue'))
+        cmdutil.afterresolvedstates.append(afterresolved)
+        cmdutil.afterresolvedstates.append(pickresolved)
 
     if util.safehasattr(cmdutil, 'STATES'):
         statedata = ('evolve', cmdutil.fileexistspredicate('evolvestate'),
--- a/hgext3rd/evolve/cmdrewrite.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/cmdrewrite.py	Mon Jul 29 14:43:12 2019 +0200
@@ -29,6 +29,7 @@
     obsutil,
     patch,
     phases,
+    pycompat,
     scmutil,
     util,
 )
@@ -86,28 +87,28 @@
         opts['user'] = ui.username()
 
 commitopts3 = [
-    ('D', 'current-date', None,
-     _('record the current date as commit date')),
-    ('U', 'current-user', None,
-     _('record the current user as committer')),
+    (b'D', b'current-date', None,
+     _(b'record the current date as commit date')),
+    (b'U', b'current-user', None,
+     _(b'record the current user as committer')),
 ]
 
-interactiveopt = [['i', 'interactive', None, _('use interactive mode')]]
+interactiveopt = [[b'i', b'interactive', None, _(b'use interactive mode')]]
 
 @eh.command(
-    'amend|refresh',
-    [('A', 'addremove', None,
-      _('mark new/missing files as added/removed before committing')),
-     ('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')),
-     ('n', 'note', '', _('store a note on amend'), _('TEXT')),
-    ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt,
-    _('[OPTION]... [FILE]...'),
+    b'amend|refresh',
+    [(b'A', b'addremove', None,
+      _(b'mark new/missing files as added/removed before committing')),
+     (b'a', b'all', False, _(b"match all files")),
+     (b'e', b'edit', False, _(b'invoke editor on commit messages')),
+     (b'', b'extract', False, _(b'extract changes from the commit to the working copy')),
+     (b'', b'patch', False, _(b'make changes to wdir parent by editing patch')),
+     (b'', b'close-branch', None,
+      _(b'mark a branch as closed, hiding it from the branch list')),
+     (b's', b'secret', None, _(b'use the secret phase for committing')),
+     (b'n', b'note', b'', _(b'store a note on amend'), _(b'TEXT')),
+     ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt,
+    _(b'[OPTION]... [FILE]...'),
     helpbasic=True)
 def amend(ui, repo, *pats, **opts):
     """combine a changeset with updates and replace it with a new one
@@ -182,10 +183,7 @@
     while newnode is None:
         fp.seek(0)
         previous_patch = fp.getvalue()
-        if 5 <= len(ui.edit.im_func.func_defaults):
-            newpatch = ui.edit(fp.getvalue(), old.user(), action="diff")
-        else:
-            newpatch = ui.edit(fp.getvalue(), old.user())
+        newpatch = ui.edit(fp.getvalue(), old.user(), action="diff")
 
         afp = stringio()
         afp.write(newpatch)
@@ -320,7 +318,7 @@
 
     # Filter copies
     copied = copies.pathcopies(target, ctx)
-    copied = dict((dst, src) for dst, src in copied.iteritems()
+    copied = dict((dst, src) for dst, src in copied.items()
                   if dst in files)
 
     def filectxfn(repo, memctx, path, contentctx=ctx, redirect=newcontent):
@@ -441,22 +439,22 @@
             oldcopies[f] = src[0]
     oldcopies.update(copies)
     copies = dict((dst, oldcopies.get(src, src))
-                  for dst, src in oldcopies.iteritems())
+                  for dst, src in oldcopies.items())
     # Adjust the dirstate copies
-    for dst, src in copies.iteritems():
+    for dst, src in copies.items():
         if (src not in ctx or dst in ctx or ds[dst] != 'a'):
             src = None
         ds.copy(src, dst)
 
 @eh.command(
-    'uncommit',
-    [('a', 'all', None, _('uncommit all changes when no arguments given')),
-     ('i', 'interactive', False, _('interactive mode to uncommit (EXPERIMENTAL)')),
-     ('r', 'rev', '', _('revert commit content to REV instead'), _('REV')),
-     ('', 'revert', False, _('discard working directory changes after uncommit')),
-     ('n', 'note', '', _('store a note on uncommit'), _('TEXT')),
+    b'uncommit',
+    [(b'a', b'all', None, _(b'uncommit all changes when no arguments given')),
+     (b'i', b'interactive', False, _(b'interactive mode to uncommit (EXPERIMENTAL)')),
+     (b'r', b'rev', b'', _(b'revert commit content to REV instead'), _(b'REV')),
+     (b'', b'revert', False, _(b'discard working directory changes after uncommit')),
+     (b'n', b'note', b'', _(b'store a note on uncommit'), _(b'TEXT')),
      ] + commands.walkopts + commitopts + commitopts2 + commitopts3,
-    _('[OPTION]... [NAME]'))
+    _(b'[OPTION]... [NAME]'))
 def uncommit(ui, repo, *pats, **opts):
     """move changes from parent revision to working directory
 
@@ -511,7 +509,7 @@
         if disallowunstable and not onahead:
             raise error.Abort(_("cannot uncommit in the middle of a stack"))
 
-        match = scmutil.match(old, pats, opts)
+        match = scmutil.match(old, pats, pycompat.byteskwargs(opts))
 
         # Check all explicitly given files; abort if there's a problem.
         if match.files():
@@ -554,7 +552,7 @@
             if (pats or includeorexclude or opts.get('all')):
                 if not (opts['message'] or opts['logfile']):
                     opts['message'] = old.description()
-                message = cmdutil.logmessage(ui, opts)
+                message = cmdutil.logmessage(ui, pycompat.byteskwargs(opts))
                 newid = _commitfiltered(repo, old, match, target=rev,
                                         message=message, user=opts.get('user'),
                                         date=opts.get('date'))
@@ -668,7 +666,7 @@
             patch.patchrepo(ui, repo, pold, store, fp, 1, '',
                             files=files, eolmode=None)
         except patch.PatchError as err:
-            raise error.Abort(str(err))
+            raise error.Abort(pycompat.bytestr(err))
 
         finally:
             del fp
@@ -685,13 +683,13 @@
     return newcm
 
 @eh.command(
-    'fold|squash',
-    [('r', 'rev', [], _("revision to fold"), _('REV')),
-     ('', 'exact', None, _("only fold specified revisions")),
-     ('', 'from', None, _("fold revisions linearly to working copy parent")),
-     ('n', 'note', '', _('store a note on fold'), _('TEXT')),
-    ] + commitopts + commitopts2 + commitopts3,
-    _('hg fold [OPTION]... [-r] REV'),
+    b'fold|squash',
+    [(b'r', b'rev', [], _(b"revision to fold"), _(b'REV')),
+     (b'', b'exact', None, _(b"only fold specified revisions")),
+     (b'', b'from', None, _(b"fold revisions linearly to working copy parent")),
+     (b'n', b'note', b'', _(b'store a note on fold'), _(b'TEXT')),
+     ] + commitopts + commitopts2 + commitopts3,
+    _(b'hg fold [OPTION]... [-r] REV'),
     helpbasic=True)
 def fold(ui, repo, *revs, **opts):
     """fold multiple revisions into a single one
@@ -773,7 +771,7 @@
         wlock = repo.wlock()
         lock = repo.lock()
 
-        root, head = rewriteutil.foldcheck(repo, revs)
+        root, head, p2 = rewriteutil.foldcheck(repo, revs)
 
         tr = repo.transaction('fold')
         try:
@@ -785,7 +783,7 @@
                 commitopts['edit'] = False
             else:
                 msgs = ["HG: This is a fold of %d changesets." % len(allctx)]
-                msgs += ["HG: Commit message of changeset %s.\n\n%s\n" %
+                msgs += ["HG: Commit message of changeset %d.\n\n%s\n" %
                          (c.rev(), c.description()) for c in allctx]
                 commitopts['message'] = "\n".join(msgs)
                 commitopts['edit'] = True
@@ -794,10 +792,14 @@
             if opts.get('note'):
                 metadata['note'] = opts['note']
 
-            newid, unusedvariable = rewriteutil.rewrite(repo, root, allctx,
+            updates = allctx[:]
+            if p2 is not None and root.p2() != p2:
+                updates.append(p2)
+            commitopts = pycompat.byteskwargs(commitopts)
+            newid, unusedvariable = rewriteutil.rewrite(repo, root, updates,
                                                         head,
                                                         [root.p1().node(),
-                                                         root.p2().node()],
+                                                         p2.node()],
                                                         commitopts=commitopts)
             phases.retractboundary(repo, tr, targetphase, [newid])
             replacements = {ctx.node(): [newid] for ctx in allctx}
@@ -813,12 +815,12 @@
         lockmod.release(lock, wlock)
 
 @eh.command(
-    'metaedit',
-    [('r', 'rev', [], _("revision to edit"), _('REV')),
-     ('', 'fold', None, _("also fold specified revisions into one")),
-     ('n', 'note', '', _('store a note on metaedit'), _('TEXT')),
-    ] + commitopts + commitopts2 + commitopts3,
-    _('hg metaedit [OPTION]... [-r] [REV]'))
+    b'metaedit',
+    [(b'r', b'rev', [], _(b"revision to edit"), _(b'REV')),
+     (b'', b'fold', None, _(b"also fold specified revisions into one")),
+     (b'n', b'note', b'', _(b'store a note on metaedit'), _(b'TEXT')),
+     ] + commitopts + commitopts2 + commitopts3,
+    _(b'hg metaedit [OPTION]... [-r] [REV]'))
 def metaedit(ui, repo, *revs, **opts):
     """edit commit information
 
@@ -872,7 +874,7 @@
                                 'not currently supported'))
 
         if opts['fold']:
-            root, head = rewriteutil.foldcheck(repo, revs)
+            root, head, p2 = rewriteutil.foldcheck(repo, revs)
         else:
             if repo.revs("%ld and public()", revs):
                 raise error.Abort(_('cannot edit commit information for public '
@@ -886,6 +888,7 @@
                 hint %= repo[newunstable.first()]
                 raise error.Abort(msg, hint=hint)
             root = head = repo[revs.first()]
+            p2 = root.p2()
 
         wctx = repo[None]
         p1 = wctx.p1()
@@ -901,19 +904,23 @@
             else:
                 if opts['fold']:
                     msgs = ["HG: This is a fold of %d changesets." % len(allctx)]
-                    msgs += ["HG: Commit message of changeset %s.\n\n%s\n" %
+                    msgs += ["HG: Commit message of changeset %d.\n\n%s\n" %
                              (c.rev(), c.description()) for c in allctx]
                 else:
                     msgs = [head.description()]
                 commitopts['message'] = "\n".join(msgs)
                 commitopts['edit'] = True
 
+            updates = allctx[:]
+            if p2 is not None and (root.p2() != p2 or not opts['fold']):
+                updates.append(p2)
             # TODO: if the author and message are the same, don't create a new
             # hash. Right now we create a new hash because the date can be
             # different.
-            newid, created = rewriteutil.rewrite(repo, root, allctx, head,
+            commitopts = pycompat.byteskwargs(commitopts)
+            newid, created = rewriteutil.rewrite(repo, root, updates, head,
                                                  [root.p1().node(),
-                                                  root.p2().node()],
+                                                  p2.node()],
                                                  commitopts=commitopts)
             if created:
                 if p1.rev() in revs:
@@ -939,10 +946,10 @@
             hg.update(repo, newp1)
 
 metadataopts = [
-    ('d', 'date', '',
-     _('record the specified date in metadata'), _('DATE')),
-    ('u', 'user', '',
-     _('record the specified user in metadata'), _('USER')),
+    (b'd', b'date', b'',
+     _(b'record the specified date in metadata'), _(b'DATE')),
+    (b'u', b'user', b'',
+     _(b'record the specified user in metadata'), _(b'USER')),
 ]
 
 def _getmetadata(**opts):
@@ -956,22 +963,22 @@
     return metadata
 
 @eh.command(
-    'prune|obsolete',
-    [('n', 'new', [], _("successor changeset (DEPRECATED)")),
-     ('s', 'successor', [], _("successor changeset"), _('REV')),
-     ('r', 'rev', [], _("revisions to prune"), _('REV')),
-     ('k', 'keep', None, _("does not modify working copy during prune")),
-     ('n', 'note', '', _('store a note on prune'), _('TEXT')),
-     ('', 'pair', False, _("record a pairing, such as a rebase or divergence resolution "
-                           "(pairing multiple precursors to multiple successors)")),
-     ('', 'biject', False, _("alias to --pair (DEPRECATED)")),
-     ('', 'fold', False,
-      _("record a fold (multiple precursors, one successor)")),
-     ('', 'split', False,
-      _("record a split (one precursor, multiple successors)")),
-     ('B', 'bookmark', [], _("remove revs only reachable from given"
-                             " bookmark"), _('BOOKMARK'))] + metadataopts,
-    _('[OPTION] [-r] REV...'),
+    b'prune|obsolete',
+    [(b'n', b'new', [], _(b"successor changeset (DEPRECATED)")),
+     (b's', b'successor', [], _(b"successor changeset"), _(b'REV')),
+     (b'r', b'rev', [], _(b"revisions to prune"), _(b'REV')),
+     (b'k', b'keep', None, _(b"does not modify working copy during prune")),
+     (b'n', b'note', b'', _(b'store a note on prune'), _(b'TEXT')),
+     (b'', b'pair', False, _(b"record a pairing, such as a rebase or divergence resolution "
+                             b"(pairing multiple precursors to multiple successors)")),
+     (b'', b'biject', False, _(b"alias to --pair (DEPRECATED)")),
+     (b'', b'fold', False,
+      _(b"record a fold (multiple precursors, one successor)")),
+     (b'', b'split', False,
+      _(b"record a split (one precursor, multiple successors)")),
+     (b'B', b'bookmark', [], _(b"remove revs only reachable from given"
+                               b" bookmark"), _(b'BOOKMARK'))] + metadataopts,
+    _(b'[OPTION] [-r] REV...'),
     helpbasic=True)
 # XXX -U  --noupdate option to prevent wc update and or bookmarks update ?
 def cmdprune(ui, repo, *revs, **opts):
@@ -1010,7 +1017,8 @@
 
     options = [o for o in ('pair', 'fold', 'split') if opts.get(o)]
     if 1 < len(options):
-        raise error.Abort(_("can only specify one of %s") % ', '.join(options))
+        _opts = pycompat.sysbytes(', '.join(options))
+        raise error.Abort(_("can only specify one of %s") % _opts)
 
     if bookmarks:
         reachablefrombookmark = rewriteutil.reachablefrombookmark
@@ -1115,7 +1123,7 @@
                     repo._bookmarks.applychanges(repo, tr, bmchanges)
                 commands.update(ui, repo, newnode.hex())
                 ui.status(_('working directory is now at %s\n')
-                          % ui.label(str(newnode), 'evolve.node'))
+                          % ui.label(bytes(newnode), 'evolve.node'))
                 if movebookmark:
                     bookmarksmod.activate(repo, bookactive)
 
@@ -1153,12 +1161,12 @@
         lockmod.release(tr, lock, wlock)
 
 @eh.command(
-    'split',
-    [('i', 'interactive', True, _('use interactive mode')),
-     ('r', 'rev', [], _("revision to split"), _('REV')),
-     ('n', 'note', '', _("store a note on split"), _('TEXT')),
-    ] + commitopts + commitopts2 + commitopts3,
-    _('hg split [OPTION] [-r REV] [FILES]'),
+    b'split',
+    [(b'i', b'interactive', True, _(b'use interactive mode')),
+     (b'r', b'rev', [], _(b"revision to split"), _(b'REV')),
+     (b'n', b'note', b'', _(b"store a note on split"), _(b'TEXT')),
+     ] + commitopts + commitopts2 + commitopts3,
+    _(b'hg split [OPTION] [-r REV] [FILES]'),
     helpbasic=True)
 def cmdsplit(ui, repo, *pats, **opts):
     """split a changeset into smaller changesets
@@ -1178,7 +1186,7 @@
     newcommits = []
     iselect = opts.pop('interactive')
 
-    revs = opts.get('rev') or '.'
+    revs = opts.get('rev')
     if not revs:
         revarg = '.'
     elif len(revs) == 1:
@@ -1325,14 +1333,14 @@
         lockmod.release(tr, lock, wlock)
 
 @eh.command(
-    'touch',
-    [('r', 'rev', [], _('revision to update'), _('REV')),
-     ('n', 'note', '', _('store a note on touch'), _('TEXT')),
-     ('D', 'duplicate', False,
-      'do not mark the new revision as successor of the old one'),
-     ('A', 'allowdivergence', False,
-      'mark the new revision as successor of the old one potentially creating '
-      'divergence')],
+    b'touch',
+    [(b'r', b'rev', [], _(b'revision to update'), _(b'REV')),
+     (b'n', b'note', b'', _(b'store a note on touch'), _(b'TEXT')),
+     (b'D', b'duplicate', False,
+      b'do not mark the new revision as successor of the old one'),
+     (b'A', b'allowdivergence', False,
+      b'mark the new revision as successor of the old one potentially creating '
+      b'divergence')],
     # allow to choose the seed ?
     _('[-r] revs'))
 def touch(ui, repo, *revs, **opts):
@@ -1355,10 +1363,8 @@
         rewriteutil.precheck(repo, revs, 'touch')
     tmpl = utility.shorttemplate
     displayer = compat.changesetdisplayer(ui, repo, {'template': tmpl})
-    with repo.wlock(), repo.lock():
-        tr = repo.transaction('touch')
-        with util.acceptintervention(tr):
-            touchnodes(ui, repo, revs, displayer, **opts)
+    with repo.wlock(), repo.lock(), repo.transaction('touch'):
+        touchnodes(ui, repo, revs, displayer, **opts)
 
 def touchnodes(ui, repo, revs, displayer, **opts):
     duplicate = opts['duplicate']
@@ -1409,8 +1415,11 @@
                 else:
                     duplicate = True
 
+        updates = []
+        if len(ctx.parents()) > 1:
+            updates = ctx.parents()
         extradict = {'extra': extra}
-        new, unusedvariable = rewriteutil.rewrite(repo, ctx, [], ctx,
+        new, unusedvariable = rewriteutil.rewrite(repo, ctx, updates, ctx,
                                                   [p1, p2],
                                                   commitopts=extradict)
         # store touched version to help potential children
@@ -1429,12 +1438,12 @@
                 repo.dirstate.setparents(new, node.nullid)
 
 @eh.command(
-    'pick|grab',
-    [('r', 'rev', '', _('revision to pick'), _('REV')),
-     ('c', 'continue', False, 'continue interrupted pick'),
-     ('a', 'abort', False, 'abort interrupted pick'),
+    b'pick|grab',
+    [(b'r', b'rev', b'', _(b'revision to pick'), _(b'REV')),
+     (b'c', b'continue', False, b'continue interrupted pick'),
+     (b'a', b'abort', False, b'abort interrupted pick'),
     ] + mergetoolopts,
-    _('[-r] rev'))
+    _(b'[-r] rev'))
 def cmdpick(ui, repo, *revs, **opts):
     """move a commit on the top of working directory parent and updates to it."""
 
@@ -1448,8 +1457,7 @@
     if opts.get('rev'):
         revs.append(opts['rev'])
 
-    overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
-    with repo.wlock(), repo.lock(), ui.configoverride(overrides, 'pick'):
+    with repo.wlock(), repo.lock():
         pickstate = state.cmdstate(repo, path='pickstate')
         pctx = repo['.']
 
@@ -1471,8 +1479,10 @@
             ui.status(_('picking %d:%s "%s"\n') %
                       (origctx.rev(), origctx,
                        origctx.description().split("\n", 1)[0]))
-            stats = merge.graft(repo, origctx, origctx.p1(), ['local',
-                                                              'destination'])
+            overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
+            with ui.configoverride(overrides, 'pick'):
+                stats = merge.graft(repo, origctx, origctx.p1(),
+                                    ['local', 'destination'])
             if compat.hasconflict(stats):
                 pickstate.addopts({'orignode': origctx.node(),
                                    'oldpctx': pctx.node()})
--- a/hgext3rd/evolve/compat.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/compat.py	Mon Jul 29 14:43:12 2019 +0200
@@ -7,6 +7,7 @@
 """
 
 import inspect
+import array
 
 from mercurial import (
     context,
@@ -15,6 +16,7 @@
     mdiff,
     obsolete,
     obsutil,
+    pycompat,
     repair,
     scmutil,
     util,
@@ -22,6 +24,13 @@
 )
 from mercurial.hgweb import hgweb_mod
 
+if pycompat.ispy3:
+    arraytobytes = array.array.tobytes
+    arrayfrombytes = array.array.frombytes
+else:
+    arraytobytes = array.array.tostring
+    arrayfrombytes = array.array.fromstring
+
 # hg < 4.6 compat (c8e2d6ed1f9e)
 try:
     from mercurial import logcmdutil
@@ -105,9 +114,14 @@
     args = [a, '', b, '', fn1, fn2]
 
     # hg < 4.6 compat 8b6dd3922f70
-    argspec = inspect.getargspec(mdiff.unidiff)
+    if util.safehasattr(inspect, 'signature'):
+        signature = inspect.signature(mdiff.unidiff)
+        needsbinary = 'binary' in signature.parameters
+    else:
+        argspec = inspect.getargspec(mdiff.unidiff)
+        needsbinary = 'binary' in argspec.args
 
-    if 'binary' in argspec.args:
+    if needsbinary:
         args.append(False)
 
     return mdiff.unidiff(*args)
@@ -123,7 +137,7 @@
     makedate = mercurial.util.makedate
     parsedate = mercurial.util.parsedate
 
-def wireprotocommand(exthelper, name, args='', permission='pull'):
+def wireprotocommand(exthelper, name, args=b'', permission=b'pull'):
     try:
         # Since b4d85bc1
         from mercurial.wireprotov1server import wireprotocommand
@@ -371,7 +385,7 @@
 
     # examine each file copy for a potential directory move, which is
     # when all the files in a directory are moved to a new directory
-    for dst, src in fullcopy.iteritems():
+    for dst, src in fullcopy.items():
         dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
         if dsrc in invalid:
             # already seen to be uninteresting
--- a/hgext3rd/evolve/debugcmd.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/debugcmd.py	Mon Jul 29 14:43:12 2019 +0200
@@ -21,7 +21,7 @@
 
 eh = exthelper.exthelper()
 
-@eh.command('debugobsstorestat', [], '')
+@eh.command(b'debugobsstorestat', [], b'')
 def cmddebugobsstorestat(ui, repo):
     """print statistics about obsolescence markers in the repo"""
     def _updateclustermap(nodes, mark, clustersmap):
--- a/hgext3rd/evolve/depthcache.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/depthcache.py	Mon Jul 29 14:43:12 2019 +0200
@@ -35,12 +35,12 @@
     return len(repo.revs('::%d', rev))
 
 @eh.command(
-    'debugdepth',
+    b'debugdepth',
     [
-        ('r', 'rev', [], 'revs to print depth for'),
-        ('', 'method', 'cached', "one of 'simple', 'cached', 'compare'"),
+        (b'r', b'rev', [], b'revs to print depth for'),
+        (b'', b'method', b'cached', b"one of 'simple', 'cached', 'compare'"),
     ],
-    _('REVS'))
+    _(b'REVS'))
 def debugdepth(ui, repo, **opts):
     """display depth of REVS
     """
@@ -112,16 +112,17 @@
         cl = repo.unfiltered().changelog
         total = len(data)
 
-        def progress(pos, rev):
+        def progress(pos, rev=None):
+            revstr = '' if rev is None else ('rev %d' % rev)
             compat.progress(repo.ui, 'updating depth cache',
-                            pos, 'rev %s' % rev, unit='revision', total=total)
-        progress(0, '')
+                            pos, revstr, unit='revision', total=total)
+        progress(0)
         for idx, rev in enumerate(data, 1):
             assert rev == len(self._data), (rev, len(self._data))
             self._data.append(self._depth(cl, rev))
             if not (idx % 10000): # progress as a too high performance impact
                 progress(idx, rev)
-        progress(None, '')
+        progress(None)
 
     def _depth(self, changelog, rev):
         cl = changelog
@@ -185,7 +186,7 @@
         else:
             headerdata = data[:self._cachekeysize]
             self._cachekey = self._deserializecachekey(headerdata)
-            self._data.fromstring(data[self._cachekeysize:])
+            compat.arrayfrombytes(self._data, data[self._cachekeysize:])
         self._ondiskkey = self._cachekey
 
     def save(self, repo):
@@ -201,7 +202,7 @@
             cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True)
             headerdata = self._serializecachekey()
             cachefile.write(headerdata)
-            cachefile.write(self._data.tostring())
+            cachefile.write(compat.arraytobytes(self._data))
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
--- a/hgext3rd/evolve/evolvecmd.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/evolvecmd.py	Mon Jul 29 14:43:12 2019 +0200
@@ -26,6 +26,7 @@
     obsolete,
     obsutil,
     phases,
+    pycompat,
     repair,
     scmutil,
     simplemerge,
@@ -204,7 +205,7 @@
         msg = _('skipping %s : we do not handle merge yet\n') % bumped
         ui.write_err(msg)
         return (False, ".")
-    prec = repo.set('last(allpredecessors(%d) and public())', bumped.rev()).next()
+    prec = next(repo.set('last(allpredecessors(%d) and public())', bumped.rev()))
     # For now we deny target merge
     if len(prec.parents()) > 1:
         msg = _('skipping: %s: public version is a merge, '
@@ -217,7 +218,7 @@
         displayer.show(bumped)
         repo.ui.write(_('atop:'))
         displayer.show(prec)
-    if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
+    if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y':
         raise error.Abort(_('evolve aborted by user'))
     if dryrun:
         todo = 'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1())
@@ -336,7 +337,7 @@
 
     # we don't handle split in content-divergence yet
     if len(others) > 1:
-        othersstr = "[%s]" % (','.join([str(i) for i in others]))
+        othersstr = "[%s]" % (','.join([bytes(i) for i in others]))
         msg = _("skipping %s: %s with a changeset that got split"
                 " into multiple ones:\n"
                 "|[%s]\n"
@@ -542,8 +543,8 @@
             return (res, newnode)
         if newnode == publicdiv.node():
             # case 2)
-            pubstr = str(publicdiv)
-            othstr = str(other)
+            pubstr = bytes(publicdiv)
+            othstr = bytes(other)
             msg = _('content divergence resolution between %s '
                     '(public) and %s has same content as %s, '
                     'discarding %s\n')
@@ -578,8 +579,9 @@
     # conflicts while merging content-divergent changesets
     if compat.hasconflict(stats):
         evolvestate.save()
-        raise error.InterventionRequired(_("fix conflicts and see `hg help "
-                                           "evolve.interrupted`"))
+        hint = _("see 'hg help evolve.interrupted'")
+        raise error.InterventionRequired(_("unresolved merge conflicts"),
+                                         hint=hint)
 
 def _completecontentdivergent(ui, repo, progresscb, divergent, other,
                               base, evolvestate):
@@ -690,8 +692,8 @@
 
     if aspects:
         # warn user
-        locstr = str(local)
-        othstr = str(other)
+        locstr = bytes(local)
+        othstr = bytes(other)
         if 'close' in aspects:
             filteredasp = aspects - {'close'}
             if filteredasp:
@@ -984,8 +986,9 @@
             copies.duplicatecopies(repo, repo[None], dest.rev(),
                                    orig.p1().rev())
             dirstatedance(repo, dest, orig.node(), None)
-        raise error.InterventionRequired(_("fix conflicts and see `hg help "
-                                           "evolve.interrupted`"))
+        hint = _("see 'hg help evolve.interrupted'")
+        raise error.InterventionRequired(_("unresolved merge conflicts"),
+                                         hint=hint)
     nodenew = _relocatecommit(repo, orig, commitmsg)
     _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate)
     return nodenew
@@ -1089,7 +1092,7 @@
         if 1 < len(revs):
             msg = "multiple evolve candidates"
             hint = (_("select one of %s with --rev")
-                    % ', '.join([str(repo[r]) for r in sorted(revs)]))
+                    % ', '.join([bytes(repo[r]) for r in sorted(revs)]))
             raise error.Abort(msg, hint=hint)
     elif instabilities_map.get(targetcat, targetcat) in repo['.'].instabilities():
         revs = set([repo['.'].rev()])
@@ -1314,7 +1317,7 @@
     if opts.get('rev'):
         revs = scmutil.revrange(repo, opts.get('rev'))
 
-    fm = ui.formatter('evolvelist', opts)
+    fm = ui.formatter('evolvelist', pycompat.byteskwargs(opts))
     for rev in revs:
         ctx = repo[rev]
         unpars = _preparelistctxs(ctx.parents(), lambda p: p.orphan())
@@ -1476,7 +1479,7 @@
                 continue
             base[tuple(nsuccset)] = n
     divergence = []
-    for divset, b in base.iteritems():
+    for divset, b in base.items():
         divergence.append({
             'divergentnodes': divset,
             'commonprecursor': b
@@ -1593,8 +1596,8 @@
     situation:
 
       - `hg evolve --continue`:
-         fix all the conflicts using `hg resolve` and then run this to continue the
-         interrupted evolve
+         resolve all the conflicts using `hg resolve` and then run this to
+         continue the interrupted evolve
 
       - `hg evolve --stop`:
          stops the current interrupted evolve, keeping all the successful steps,
@@ -1620,25 +1623,23 @@
     abortopt = opts['abort']
     shouldupdate = opts['update']
 
-    troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
-    specifiedcategories = [t.replace('_', '')
-                           for t in troublecategories
-                           if opts[t]]
+    troublecategories = {
+        'phasedivergent': 'phase_divergent',
+        'contentdivergent': 'content_divergent',
+        'orphan': 'orphan',
+    }
+    specifiedcategories = [k for k, v in troublecategories.items() if opts[v]]
     if opts['list']:
         ui.pager('evolve')
         listtroubles(ui, repo, specifiedcategories, **opts)
         return
 
     targetcat = 'orphan'
-    has_some_opts = bool(revopt or anyopt or allopt or contopt or stopopt or abortopt)
     if 1 < len(specifiedcategories):
         msg = _('cannot specify more than one trouble category to solve (yet)')
         raise error.Abort(msg)
     elif len(specifiedcategories) == 1:
         targetcat = specifiedcategories[0]
-    elif repo['.'].obsolete() and not has_some_opts:
-        # if no args and parent is obsolete, update to successors
-        return solveobswdp(ui, repo, opts)
 
     ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
 
@@ -1665,7 +1666,7 @@
         return
     elif abortopt:
         if not evolvestate:
-            raise error.Abort(_('no interrupted evolve to stop'))
+            raise error.Abort(_('no interrupted evolve to abort'))
         evolvestate.load()
         # `hg next --evolve` in play
         if evolvestate['command'] != 'evolve':
@@ -1680,19 +1681,20 @@
     else:
         cmdutil.bailifchanged(repo)
 
+        obswdir = repo['.'].obsolete()
         revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat)
 
-        # Case: when wdir parent is obsolete and args passed.
-        # Handling it here otherwise `revs` set would change, after
-        # performing update to successor of obsolete wdir parent.
-        # (in case when user passes a revset related to wdir parent '.::')
-        if repo['.'].obsolete():
+        if not (revs or obswdir):
+            return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
+        obswdironly = not revs and obswdir
+
+        if obswdir:
             result = solveobswdp(ui, repo, opts)
             if result != 0 or result is True:
+                # return as solving obswdp wasn't successful
                 return result
-
-        if not revs:
-            return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
+        if obswdironly:
+            return 0
 
         # Progress handling
         seen = 1
@@ -1839,7 +1841,7 @@
         # boolean value to say whether we should strip or not
         cleanup = True
         startnode = evolvestate['startnode']
-        for old, new in evolvestate['replacements'].iteritems():
+        for old, new in evolvestate['replacements'].items():
             if new:
                 evolvedctx.append(repo[new])
         for temp in evolvestate['temprevs']:
@@ -1851,7 +1853,7 @@
         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),
+                         % ', '.join(bytes(c) for c in immutable),
                          hint=_("see 'hg help phases' for details"))
             cleanup = False
 
--- a/hgext3rd/evolve/exthelper.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/exthelper.py	Mon Jul 29 14:43:12 2019 +0200
@@ -111,7 +111,7 @@
         self._functionwrappers.extend(other._functionwrappers)
         self._duckpunchers.extend(other._duckpunchers)
         self.cmdtable.update(other.cmdtable)
-        for section, items in other.configtable.iteritems():
+        for section, items in other.configtable.items():
             if section in self.configtable:
                 self.configtable[section].update(items)
             else:
--- a/hgext3rd/evolve/firstmergecache.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/firstmergecache.py	Mon Jul 29 14:43:12 2019 +0200
@@ -74,16 +74,17 @@
         cl = repo.unfiltered().changelog
         total = len(data)
 
-        def progress(pos, rev):
+        def progress(pos, rev=None):
+            revstr = '' if rev is None else ('rev %d' % rev)
             compat.progress(repo.ui, 'updating firstmerge cache',
-                            pos, 'rev %s' % rev, unit='revision', total=total)
-        progress(0, '')
+                            pos, revstr, unit='revision', total=total)
+        progress(0)
         for idx, rev in enumerate(data, 1):
             assert rev == len(self._data), (rev, len(self._data))
             self._data.append(self._firstmerge(cl, rev))
             if not (idx % 10000): # progress as a too high performance impact
                 progress(idx, rev)
-        progress(None, '')
+        progress(None)
 
     def _firstmerge(self, changelog, rev):
         cl = changelog
@@ -122,7 +123,7 @@
         else:
             headerdata = data[:self._cachekeysize]
             self._cachekey = self._deserializecachekey(headerdata)
-            self._data.fromstring(data[self._cachekeysize:])
+            compat.arrayfrombytes(self._data, data[self._cachekeysize:])
         self._ondiskkey = self._cachekey
 
     def save(self, repo):
@@ -138,7 +139,7 @@
             cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True)
             headerdata = self._serializecachekey()
             cachefile.write(headerdata)
-            cachefile.write(self._data.tostring())
+            cachefile.write(compat.arraytobytes(self._data))
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
--- a/hgext3rd/evolve/legacy.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/legacy.py	Mon Jul 29 14:43:12 2019 +0200
@@ -139,7 +139,7 @@
 
                     oldmark['date'] = '%i %i' % tuple(oldmark['date'])
                     meta = dict((k.encode('utf-8'), v.encode('utf-8'))
-                                for k, v in oldmark.iteritems())
+                                for k, v in oldmark.items())
                     try:
                         succs = [bin(n) for n in oldsubjects]
                         succs = [n for n in succs if n != nullid]
--- a/hgext3rd/evolve/metadata.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/metadata.py	Mon Jul 29 14:43:12 2019 +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__ = '9.0.1.dev'
-testedwith = '4.5.2 4.6.2 4.7 4.8 4.9 5.0'
-minimumhgversion = '4.5'
-buglink = 'https://bz.mercurial-scm.org/'
+__version__ = b'9.1.0.dev'
+testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0'
+minimumhgversion = b'4.5'
+buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obscache.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/obscache.py	Mon Jul 29 14:43:12 2019 +0200
@@ -186,7 +186,7 @@
         obsmarkers = list(obsmarkers)
         self._updatefrom(repo, revs, obsmarkers)
         duration = util.timer() - starttime
-        repo.ui.log('evoext-cache', 'updated %s in %.4f seconds (%sr, %so)\n',
+        repo.ui.log('evoext-cache', 'updated %s in %.4f seconds (%dr, %do)\n',
                     self._cachename, duration, len(revs), len(obsmarkers))
 
         # update the key from the new data
--- a/hgext3rd/evolve/obsdiscovery.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/obsdiscovery.py	Mon Jul 29 14:43:12 2019 +0200
@@ -22,6 +22,7 @@
 import weakref
 
 from mercurial import (
+    encoding,
     error,
     exchange,
     extensions,
@@ -186,12 +187,12 @@
 ##############################
 
 @eh.command(
-    'debugobshashrange',
+    b'debugobshashrange',
     [
-        ('', 'rev', [], 'display obshash for all (rev, 0) range in REVS'),
-        ('', 'subranges', False, 'display all subranges'),
+        (b'', b'rev', [], b'display obshash for all (rev, 0) range in REVS'),
+        (b'', b'subranges', False, b'display all subranges'),
     ],
-    _(''))
+    _(b''))
 def debugobshashrange(ui, repo, **opts):
     """display the ::REVS set topologically sorted in a stable way
     """
@@ -465,15 +466,16 @@
         repo.depthcache.update(repo)
         total = len(revs)
 
-        def progress(pos, rev):
+        def progress(pos, rev=None):
+            revstr = '' if rev is None else ('rev %d' % rev)
             compat.progress(repo.ui, 'updating obshashrange cache',
-                            pos, 'rev %s' % rev, unit='revision', total=total)
+                            pos, revstr, unit='revision', total=total)
         # warm the cache for the new revs
-        progress(0, '')
+        progress(0)
         for idx, r in enumerate(revs):
             _obshashrange(repo, (r, 0))
             progress(idx, r)
-        progress(None, '')
+        progress(None)
 
         del self._updating
 
@@ -492,8 +494,9 @@
             util.makedirs(self._vfs.dirname(self._path))
         except OSError:
             return None
-        con = sqlite3.connect(self._path, timeout=30, isolation_level="IMMEDIATE")
-        con.text_factory = str
+        con = sqlite3.connect(encoding.strfromlocal(self._path), timeout=30,
+                              isolation_level="IMMEDIATE")
+        con.text_factory = bytes
         return con
 
     @util.propertycache
@@ -688,7 +691,7 @@
     except ValueError:
         self._abort(error.ResponseError(_("unexpected response:"), d))
 
-@compat.wireprotocommand(eh, 'evoext_obshashrange_v1', 'ranges')
+@compat.wireprotocommand(eh, b'evoext_obshashrange_v1', b'ranges')
 def srv_obshashrange_v1(repo, proto, ranges):
     ranges = decodelist(ranges)
     ranges = [_decrange(r) for r in ranges]
--- a/hgext3rd/evolve/obsexchange.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/obsexchange.py	Mon Jul 29 14:43:12 2019 +0200
@@ -221,7 +221,7 @@
         from mercurial import wireproto as wireprototypes
         wireprotov1server = wireprototypes
     opts = wireprotov1server.options('', ['heads', 'common'], others)
-    for k, v in opts.iteritems():
+    for k, v in opts.items():
         if k in ('heads', 'common'):
             opts[k] = wireprototypes.decodelist(v)
     obsdata = _getobsmarkersstream(repo, **opts)
--- a/hgext3rd/evolve/obshashtree.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/obshashtree.py	Mon Jul 29 14:43:12 2019 +0200
@@ -28,9 +28,9 @@
 # the obshash of its parents.  This is similar to what happend for changeset
 # node where the parent is used in the computation
 @eh.command(
-    'debugobsrelsethashtree',
-    [('', 'v0', None, 'hash on marker format "0"'),
-     ('', 'v1', None, 'hash on marker format "1" (default)')], _(''))
+    b'debugobsrelsethashtree',
+    [(b'', b'v0', None, b'hash on marker format "0"'),
+     (b'', b'v1', None, b'hash on marker format "1" (default)')], _(b''))
 def debugobsrelsethashtree(ui, repo, v0=False, v1=False):
     """display Obsolete markers, Relevant Set, Hash Tree
     changeset-node obsrelsethashtree-node
--- a/hgext3rd/evolve/obshistory.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/obshistory.py	Mon Jul 29 14:43:12 2019 +0200
@@ -16,6 +16,7 @@
     patch,
     obsutil,
     node as nodemod,
+    pycompat,
     scmutil,
     util,
 )
@@ -42,14 +43,14 @@
         efd.clear()
 
 @eh.command(
-    'obslog|olog',
-    [('G', 'graph', True, _("show the revision DAG")),
-     ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
-     ('a', 'all', False, _('show all related changesets, not only precursors')),
-     ('p', 'patch', False, _('show the patch between two obs versions')),
-     ('f', 'filternonlocal', False, _('filter out non local commits')),
-    ] + commands.formatteropts,
-    _('hg olog [OPTION]... [REV]'))
+    b'obslog|olog',
+    [(b'G', b'graph', True, _(b"show the revision DAG")),
+     (b'r', b'rev', [], _(b'show the specified revision or revset'), _(b'REV')),
+     (b'a', b'all', False, _(b'show all related changesets, not only precursors')),
+     (b'p', b'patch', False, _(b'show the patch between two obs versions')),
+     (b'f', b'filternonlocal', False, _(b'filter out non local commits')),
+     ] + commands.formatteropts,
+    _(b'hg olog [OPTION]... [REV]'))
 def cmdobshistory(ui, repo, *revs, **opts):
     """show the obsolescence history of the specified revisions
 
@@ -322,7 +323,7 @@
 
         # Filter out candidates, returns only nodes with all their successors
         # already shown
-        validcandidates = filter(isvalidcandidate, candidates)
+        validcandidates = list(filter(isvalidcandidate, candidates))
 
         # If we likely have a cycle
         if not validcandidates:
@@ -423,15 +424,18 @@
 
 def _debugobshistorygraph(ui, repo, revs, opts):
 
-    displayer = obsmarker_printer(ui, repo.unfiltered(), obspatch=True, diffopts=opts, buffered=True)
+    displayer = obsmarker_printer(ui, repo.unfiltered(), obspatch=True,
+                                  diffopts=pycompat.byteskwargs(opts),
+                                  buffered=True)
     edges = graphmod.asciiedges
-    walker = _obshistorywalker(repo.unfiltered(), revs, opts.get('all', False), opts.get('filternonlocal', False))
+    walker = _obshistorywalker(repo.unfiltered(), revs, opts.get('all', False),
+                               opts.get('filternonlocal', False))
     compat.displaygraph(ui, repo, walker, displayer, edges)
 
 def _debugobshistoryrevs(ui, repo, revs, opts):
     """ Display the obsolescence history for revset
     """
-    fm = ui.formatter('debugobshistory', opts)
+    fm = ui.formatter('debugobshistory', pycompat.byteskwargs(opts))
     precursors = repo.obsstore.predecessors
     successors = repo.obsstore.successors
     nodec = repo.changelog.node
@@ -473,7 +477,7 @@
         shortdescription = shortdescription.splitlines()[0]
 
     fm.startitem()
-    fm.write('node', '%s', str(ctx),
+    fm.write('node', '%s', bytes(ctx),
              label="evolve.node")
     fm.plain(' ')
 
@@ -793,7 +797,7 @@
 def _getdifflines(iterdiff):
     """return a cleaned up lines"""
     try:
-        lines = iterdiff.next()
+        lines = next(iterdiff)
     except StopIteration:
         return None
     return _prepare_hunk(lines)
--- a/hgext3rd/evolve/rewind.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/rewind.py	Mon Jul 29 14:43:12 2019 +0200
@@ -26,14 +26,14 @@
 identicalflag = 4
 
 @eh.command(
-    'rewind|undo',
-    [('', 'to', [], _("rewind to these revisions"), _('REV')),
-     ('', 'as-divergence', None, _("preserve current latest successors")),
-     ('', 'exact', None, _("only rewind explicitly selected revisions")),
-     ('', 'from', [],
-      _("rewind these revisions to their predecessors"), _('REV')),
-    ],
-    _(''),
+    b'rewind|undo',
+    [(b'', b'to', [], _(b"rewind to these revisions"), _(b'REV')),
+     (b'', b'as-divergence', None, _(b"preserve current latest successors")),
+     (b'', b'exact', None, _(b"only rewind explicitly selected revisions")),
+     (b'', b'from', [],
+      _(b"rewind these revisions to their predecessors"), _(b'REV')),
+     ],
+    _(b''),
     helpbasic=True)
 def rewind(ui, repo, **opts):
     """rewind a stack of changesets to a previous state
@@ -166,9 +166,12 @@
     p2 = ctx.p2().node()
     p2 = rewindmap.get(p2, p2)
 
+    updates = []
+    if len(ctx.parents()) > 1:
+        updates = ctx.parents()
     extradict = {'extra': extra}
 
-    new, unusedvariable = rewriteutil.rewrite(unfi, ctx, [], ctx,
+    new, unusedvariable = rewriteutil.rewrite(unfi, ctx, updates, ctx,
                                               [p1, p2],
                                               commitopts=extradict)
 
--- a/hgext3rd/evolve/rewriteutil.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/rewriteutil.py	Mon Jul 29 14:43:12 2019 +0200
@@ -113,7 +113,14 @@
         raise error.Abort(_("cannot fold non-linear revisions "
                             "(multiple heads given)"))
     head = repo[heads.first()]
-    return root, head
+    baseparents = repo.revs('parents(%ld) - %ld', revs, revs)
+    if len(baseparents) > 2:
+        raise error.Abort(_("cannot fold revisions that merge with more than "
+                            "one external changeset (not in revisions)"))
+    # root's p1 is already used as the target ctx p1
+    baseparents -= {root.p1().rev()}
+    p2 = repo[baseparents.first()]
+    return root, head, p2
 
 def deletebookmark(repo, repomarks, bookmarks):
     wlock = lock = tr = None
@@ -150,7 +157,7 @@
     # a revision we have to only delete the bookmark and not strip
     # anything. revsets cannot detect that case.
     nodetobookmarks = {}
-    for mark, bnode in repomarks.iteritems():
+    for mark, bnode in repomarks.items():
         nodetobookmarks.setdefault(bnode, []).append(mark)
     for marks in nodetobookmarks.values():
         if bookmarks.issuperset(marks):
@@ -171,8 +178,6 @@
         wlock = repo.wlock()
         lock = repo.lock()
         tr = repo.transaction('rewrite')
-        if len(old.parents()) > 1: # XXX remove this unnecessary limitation.
-            raise error.Abort(_('cannot amend merge changesets'))
         base = old.p1()
         updatebookmarks = bookmarksupdater(repo, old.node(), tr)
 
--- a/hgext3rd/evolve/serveronly.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/serveronly.py	Mon Jul 29 14:43:12 2019 +0200
@@ -23,8 +23,9 @@
         obscache,
         obsexchange,
     )
-except ValueError as exc:
-    if str(exc) != 'Attempted relative import in non-package':
+except (ValueError, ImportError) as exc:
+    if (isinstance(exc, ValueError)
+        and str(exc) != 'Attempted relative import in non-package'):
         raise
     # extension imported using direct path
     sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
@@ -59,5 +60,5 @@
         repo.ui.setconfig('experimental', 'evolution', evolveopts)
     if obsolete.isenabled(repo, 'exchange'):
         # if no config explicitly set, disable bundle1
-        if not isinstance(repo.ui.config('server', 'bundle1'), str):
+        if not isinstance(repo.ui.config('server', 'bundle1'), bytes):
             repo.ui.setconfig('server', 'bundle1', False)
--- a/hgext3rd/evolve/stablerange.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/stablerange.py	Mon Jul 29 14:43:12 2019 +0200
@@ -72,15 +72,15 @@
 }
 
 @eh.command(
-    'debugstablerange',
+    b'debugstablerange',
     [
-        ('r', 'rev', [], 'operate on (rev, 0) ranges for rev in REVS'),
-        ('', 'subranges', False, 'recursively display data for subranges too'),
-        ('', 'verify', False, 'checks subranges content (EXPENSIVE)'),
-        ('', 'method', 'branchpoint',
-         'method to use, one of "branchpoint", "mergepoint"')
+        (b'r', b'rev', [], b'operate on (rev, 0) ranges for rev in REVS'),
+        (b'', b'subranges', False, b'recursively display data for subranges too'),
+        (b'', b'verify', False, b'checks subranges content (EXPENSIVE)'),
+        (b'', b'method', b'branchpoint',
+         b'method to use, one of "branchpoint", "mergepoint"')
     ],
-    _(''))
+    _(b''))
 def debugstablerange(ui, repo, **opts):
     """display standard stable subrange for a set of ranges
 
@@ -502,7 +502,7 @@
             elif skips < size:
                 revs = walkfrom(jumphead)
                 next(revs)
-                for i in xrange(skips):
+                for i in range(skips):
                     jumphead = next(revs)
                     assert jumphead is not None
                 skipped = skips
@@ -764,7 +764,7 @@
         if value is None:
             revs = self.revsfromrange(repo, (merge, 0))
             i = reversed(revs)
-            i.next() # pop the merge
+            next(i) # pop the merge
             expected = len(revs) - 1
             # Since we do warmup properly, we can expect the cache to be hot
             # for everythin under the merge we investigate
--- a/hgext3rd/evolve/stablerangecache.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/stablerangecache.py	Mon Jul 29 14:43:12 2019 +0200
@@ -14,6 +14,7 @@
 import time
 
 from mercurial import (
+    encoding,
     error,
     localrepo,
     node as nodemod,
@@ -207,7 +208,11 @@
         # 1) check the in memory cache
         # 2) check the sqlcaches (and warm in memory cache we want we find)
         cache = self._subrangescache
-        if rangeid not in cache and rangeid[0] <= self._ondisktiprev and self._con is not None:
+        if (rangeid not in cache
+            and self._ondisktiprev is not None
+            and rangeid[0] <= self._ondisktiprev
+            and self._con is not None):
+
             value = None
             try:
                 result = self._con.execute(_queryrange, rangeid).fetchone()
@@ -234,8 +239,9 @@
             util.makedirs(self._vfs.dirname(self._path))
         except OSError:
             return None
-        con = sqlite3.connect(self._path, timeout=30, isolation_level="IMMEDIATE")
-        con.text_factory = str
+        con = sqlite3.connect(encoding.strfromlocal(self._path), timeout=30,
+                              isolation_level="IMMEDIATE")
+        con.text_factory = bytes
         return con
 
     @util.propertycache
--- a/hgext3rd/evolve/stablesort.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/stablesort.py	Mon Jul 29 14:43:12 2019 +0200
@@ -16,6 +16,7 @@
     localrepo,
     error,
     node as nodemod,
+    pycompat,
     scmutil,
 )
 
@@ -52,14 +53,14 @@
     return key
 
 @eh.command(
-    'debugstablesort',
+    b'debugstablesort',
     [
-        ('r', 'rev', [], 'heads to start from'),
-        ('', 'method', 'branchpoint', "method used for sorting, one of: "
-         "branchpoint, basic-mergepoint and basic-headstart"),
-        ('l', 'limit', '', 'number of revision display (default to all)')
+        (b'r', b'rev', [], b'heads to start from'),
+        (b'', b'method', b'branchpoint', b"method used for sorting, one of: "
+         b"branchpoint, basic-mergepoint and basic-headstart"),
+        (b'l', b'limit', b'', b'number of revision display (default to all)')
     ] + commands.formatteropts,
-    _(''))
+    _(b''))
 def debugstablesort(ui, repo, **opts):
     """display the ::REVS set topologically sorted in a stable way
     """
@@ -72,7 +73,8 @@
         raise error.Abort('unknown sorting method: "%s"' % method,
                           hint='pick one of: %s' % valid_method)
 
-    displayer = compat.changesetdisplayer(ui, repo, opts, buffered=True)
+    displayer = compat.changesetdisplayer(ui, repo, pycompat.byteskwargs(opts),
+                                          buffered=True)
     kwargs = {}
     if opts['limit']:
         kwargs['limit'] = int(opts['limit'])
@@ -434,7 +436,7 @@
 
         def popready(stack):
             """pop the top most ready item in the list"""
-            for idx in xrange(len(stack) - 1, -1, -1):
+            for idx in range(len(stack) - 1, -1, -1):
                 if children[stack[idx]].issubset(seen):
                     return stack.pop(idx)
             return None
@@ -549,7 +551,7 @@
         # merge revision
 
         def jumps():
-            for idx in xrange(index[rev - 1], index[rev]):
+            for idx in range(index[rev - 1], index[rev]):
                 i = idx * 3
                 yield tuple(data[i:i + 3])
         return jumps()
@@ -576,11 +578,11 @@
 
         total = len(data)
 
-        def progress(pos, rev):
+        def progress(pos, rev=None):
+            revstr = '' if rev is None else ('rev %d' % rev)
             compat.progress(repo.ui, 'updating stablesort cache',
-                            pos, 'rev %s' % rev, unit='revision', total=total)
+                            pos, revstr, unit='revision', total=total)
 
-        progress(0, '')
         for idx, rev in enumerate(data):
             parents = filterparents(repo.changelog.parentrevs(rev))
             if len(parents) <= 1:
@@ -594,7 +596,7 @@
                         break
             if not (idx % 1000): # progress as a too high performance impact
                 progress(idx, rev)
-        progress(None, '')
+        progress(None)
 
     def clear(self, reset=False):
         super(ondiskstablesortcache, self).clear()
@@ -620,9 +622,9 @@
             indexsizedata = data[offset:offset + S_INDEXSIZE.size]
             indexsize = S_INDEXSIZE.unpack(indexsizedata)[0]
             offset += S_INDEXSIZE.size
-            self._index.fromstring(data[offset:offset + indexsize])
+            compat.arrayfrombytes(self._index, data[offset:offset + indexsize])
             offset += indexsize
-            self._data.fromstring(data[offset:])
+            compat.arrayfrombytes(self._data, data[offset:])
         self._ondiskkey = self._cachekey
         pass
 
@@ -638,8 +640,8 @@
 
             # data to write
             headerdata = self._serializecachekey()
-            indexdata = self._index.tostring()
-            data = self._data.tostring()
+            indexdata = compat.arraytobytes(self._index)
+            data = compat.arraytobytes(self._data)
             indexsize = S_INDEXSIZE.pack(len(indexdata))
 
             # writing
--- a/hgext3rd/evolve/state.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/state.py	Mon Jul 29 14:43:12 2019 +0200
@@ -45,6 +45,8 @@
     def __nonzero__(self):
         return self.exists()
 
+    __bool__ = __nonzero__
+
     def __contains__(self, key):
         return key in self.opts
 
@@ -134,7 +136,7 @@
             elif rtype.lower():
                 repo.ui.debug('ignore evolve state record type %s' % rtype)
             else:
-                raise error.Abort(_('unknown evolvestate field type %r')
+                raise error.Abort(_("unknown evolvestate field type '%s'")
                                   % rtype, hint=_('upgrade your evolve'))
         return state
     finally:
--- a/hgext3rd/evolve/templatekw.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/templatekw.py	Mon Jul 29 14:43:12 2019 +0200
@@ -24,7 +24,7 @@
 ### template keywords
 
 if util.safehasattr(templatekw, 'compatlist'):
-    @eh.templatekeyword('instabilities', requires=set(['ctx', 'templ']))
+    @eh.templatekeyword(b'instabilities', requires=set([b'ctx', b'templ']))
     def showinstabilities(context, mapping):
         """List of strings. Evolution instabilities affecting the changeset
         (zero or more of "orphan", "content-divergent" or "phase-divergent")."""
@@ -33,14 +33,14 @@
                                      ctx.instabilities(),
                                      plural='instabilities')
 
-    @eh.templatekeyword('troubles', requires=set(['ctx', 'templ']))
+    @eh.templatekeyword(b'troubles', requires=set([b'ctx', b'templ']))
     def showtroubles(context, mapping):   # legacy name for instabilities
         ctx = context.resource(mapping, 'ctx')
         return templatekw.compatlist(context, mapping, 'trouble',
                                      ctx.instabilities(), plural='troubles')
 else:
     # older template API in hg < 4.6
-    @eh.templatekeyword('instabilities')
+    @eh.templatekeyword(b'instabilities')
     def showinstabilities(**args):
         """List of strings. Evolution instabilities affecting the changeset
         (zero or more of "orphan", "content-divergent" or "phase-divergent")."""
@@ -48,7 +48,7 @@
         return templatekw.showlist('instability', ctx.instabilities(), args,
                                    plural='instabilities')
 
-    @eh.templatekeyword('troubles')
+    @eh.templatekeyword(b'troubles')
     def showtroubles(**args):
         ctx = args['ctx']
         return templatekw.showlist('trouble', ctx.instabilities(), args,
@@ -59,10 +59,10 @@
     def showprecursors(context, mapping):
         return _sp(context, mapping)
     showprecursors.__doc__ = _sp._origdoc
-    _tk = templatekw.templatekeyword("precursors", requires=_sp._requires)
+    _tk = templatekw.templatekeyword(b"precursors", requires=_sp._requires)
     _tk(showprecursors)
 else:
-    templatekw.keywords["precursors"] = _sp
+    templatekw.keywords[b"precursors"] = _sp
 
 
 def closestsuccessors(repo, nodeid):
@@ -75,10 +75,10 @@
     def showsuccessors(context, mapping):
         return _ss(context, mapping)
     showsuccessors.__doc__ = _ss._origdoc
-    _tk = templatekw.templatekeyword("successors", requires=_ss._requires)
+    _tk = templatekw.templatekeyword(b"successors", requires=_ss._requires)
     _tk(showsuccessors)
 else:
-    templatekw.keywords["successors"] = _ss
+    templatekw.keywords[b"successors"] = _ss
 
 def _getusername(ui):
     """the default username in the config or None"""
@@ -207,7 +207,7 @@
     return "\n".join(lines)
 
 if not util.safehasattr(templatekw, 'obsfateverb'): # <= hg-4.5
-    @eh.templatekeyword("obsfatedata")
+    @eh.templatekeyword(b"obsfatedata")
     def showobsfatedata(repo, ctx, **args):
         # Get the needed obsfate data
         values = obsfatedata(repo, ctx)
@@ -264,7 +264,7 @@
         # Empty the generator
         try:
             while True:
-                chunkstr.append(chunk.next())
+                chunkstr.append(next(chunk))
         except StopIteration:
             pass
 
--- a/hgext3rd/evolve/thirdparty/cbor.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/evolve/thirdparty/cbor.py	Mon Jul 29 14:43:12 2019 +0200
@@ -193,7 +193,7 @@
                 parts.append(dumps(k, sort_keys=sort_keys))
                 parts.append(dumps(v, sort_keys=sort_keys))
         else:
-            for k,v in d.iteritems():
+            for k,v in d.items():
                 parts.append(dumps(k, sort_keys=sort_keys))
                 parts.append(dumps(v, sort_keys=sort_keys))
         return b''.join(parts)
--- a/hgext3rd/pullbundle.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/pullbundle.py	Mon Jul 29 14:43:12 2019 +0200
@@ -168,7 +168,7 @@
         slices = sliceoutgoing(repo, outgoing)
         end = util.timer()
         msg = _('pullbundle-cache: "missing" set sliced into %d subranges '
-                'in %s seconds\n')
+                'in %f seconds\n')
         repo.ui.write(msg % (len(slices), end - start))
         for sliceid, sliceout in slices:
             makeonecgpart(newpart, repo, sliceid, sliceout, version, source, bundlecaps,
@@ -507,7 +507,7 @@
                   % (count, len(actionrevs)))
     if 1 < min_cache:
         repo.ui.write("  not caching ranges smaller than %d changesets\n" % min_cache)
-    for i in xrange(count):
+    for i in range(count):
         repo.ui.progress('gathering data', i, total=count)
         outgoing = takeonesample(repo, actionrevs)
         ranges = sliceoutgoing(repo, outgoing)
--- a/hgext3rd/serverminitopic.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/serverminitopic.py	Mon Jul 29 14:43:12 2019 +0200
@@ -120,7 +120,7 @@
     if revs:
         s = hashlib.sha1()
         for rev in revs:
-            s.update('%s;' % rev)
+            s.update('%d;' % rev)
         key = s.digest()
     return key
 
--- a/hgext3rd/topic/__init__.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/__init__.py	Mon Jul 29 14:43:12 2019 +0200
@@ -137,6 +137,7 @@
     obsolete,
     patch,
     phases,
+    pycompat,
     registrar,
     scmutil,
     templatefilters,
@@ -186,11 +187,11 @@
               'topic.active': 'green',
              }
 
-__version__ = '0.15.1.dev'
+__version__ = b'0.16.0.dev'
 
-testedwith = '4.5.2 4.6.2 4.7 4.8 4.9 5.0'
-minimumhgversion = '4.5'
-buglink = 'https://bz.mercurial-scm.org/'
+testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0'
+minimumhgversion = b'4.5'
+buglink = b'https://bz.mercurial-scm.org/'
 
 if util.safehasattr(registrar, 'configitem'):
 
@@ -436,6 +437,15 @@
                 return super(topicrepo, self).branchmap()
             return self.filtered(topicfilter).branchmap()
 
+        def branchheads(self, branch=None, start=None, closed=False):
+            if branch is None:
+                branch = self[None].branch()
+            if self.currenttopic:
+                branch = "%s:%s" % (branch, self.currenttopic)
+            return super(topicrepo, self).branchheads(branch=branch,
+                                                      start=start,
+                                                      closed=closed)
+
         def invalidatevolatilesets(self):
             # XXX we might be able to move this to something invalidated less often
             super(topicrepo, self).invalidatevolatilesets()
@@ -537,7 +547,7 @@
                 csetcount = stack.stack(repo, topic=ct).changesetcount
                 empty = csetcount == 0
                 if empty and not ctwasempty:
-                    ui.status('active topic %r is now empty\n' % ct)
+                    ui.status("active topic '%s' is now empty\n" % ct)
                     trnames = getattr(tr, 'names', getattr(tr, '_names', ()))
                     if ('phase' in trnames
                             or any(n.startswith('push-response')
@@ -546,10 +556,10 @@
                 hint = _("(see 'hg help topics' for more information)\n")
                 if ctwasempty and not empty:
                     if csetcount == 1:
-                        msg = _('active topic %r grew its first changeset\n%s')
+                        msg = _("active topic '%s' grew its first changeset\n%s")
                         ui.status(msg % (ct, hint))
                     else:
-                        msg = _('active topic %r grew its %s first changesets\n%s')
+                        msg = _("active topic '%s' grew its %s first changesets\n%s")
                         ui.status(msg % (ct, csetcount, hint))
 
             tr.addpostclose('signalcurrenttopicempty', currenttopicempty)
@@ -563,13 +573,13 @@
             listnames=lambda repo: repo.topics))
 
 if post45template:
-    @templatekeyword('topic', requires={'ctx'})
+    @templatekeyword(b'topic', requires={b'ctx'})
     def topickw(context, mapping):
         """:topic: String. The topic of the changeset"""
         ctx = context.resource(mapping, 'ctx')
         return ctx.topic()
 
-    @templatekeyword('topicidx', requires={'ctx'})
+    @templatekeyword(b'topicidx', requires={b'ctx'})
     def topicidxkw(context, mapping):
         """:topicidx: Integer. Index of the changeset as a stack alias"""
         ctx = context.resource(mapping, 'ctx')
@@ -617,14 +627,14 @@
 # revset predicates are automatically registered at loading via this symbol
 revsetpredicate = topicrevset.revsetpredicate
 
-@command('topics', [
-        ('', 'clear', False, 'clear active topic if any'),
-        ('r', 'rev', [], 'revset of existing revisions', _('REV')),
-        ('l', 'list', False, 'show the stack of changeset in the topic'),
-        ('', 'age', False, 'show when you last touched the topics'),
-        ('', 'current', None, 'display the current topic only'),
+@command(b'topics', [
+        (b'', b'clear', False, b'clear active topic if any'),
+        (b'r', b'rev', [], b'revset of existing revisions', _(b'REV')),
+        (b'l', b'list', False, b'show the stack of changeset in the topic'),
+        (b'', b'age', False, b'show when you last touched the topics'),
+        (b'', b'current', None, b'display the current topic only'),
     ] + commands.formatteropts,
-    _('hg topics [TOPIC]'))
+    _(b'hg topics [TOPIC]'))
 def topics(ui, repo, topic=None, **opts):
     """View current topic, set current topic, change topic for a set of revisions, or see all topics.
 
@@ -702,7 +712,8 @@
             topic = repo.currenttopic
         if not topic:
             raise error.Abort(_('no active topic to list'))
-        return stack.showstack(ui, repo, topic=topic, opts=opts)
+        return stack.showstack(ui, repo, topic=topic,
+                               opts=pycompat.byteskwargs(opts))
 
     if touchedrevs:
         if not obsolete.isenabled(repo, obsolete.createmarkersopt):
@@ -752,7 +763,7 @@
         ui.write_err(_('no active topic\n'))
         ret = 1
     elif current:
-        fm = ui.formatter('topic', opts)
+        fm = ui.formatter('topic', pycompat.byteskwargs(opts))
         namemask = '%s\n'
         label = 'topic.active'
         fm.startitem()
@@ -782,7 +793,8 @@
     if topic is None:
         branch = repo[None].branch()
     ui.pager('stack')
-    return stack.showstack(ui, repo, branch=branch, topic=topic, opts=opts)
+    return stack.showstack(ui, repo, branch=branch, topic=topic,
+                           opts=pycompat.byteskwargs(opts))
 
 @command('debugcb|debugconvertbookmark', [
         ('b', 'bookmark', '', _('bookmark to convert to topic')),
@@ -804,7 +816,7 @@
     bmstore = repo._bookmarks
 
     nodetobook = {}
-    for book, revnode in bmstore.iteritems():
+    for book, revnode in bmstore.items():
         if nodetobook.get(revnode):
             nodetobook[revnode].append(book)
         else:
@@ -836,7 +848,7 @@
                 actions[(bookmark, revnum)] = targetrevs
 
         elif convertall:
-            for bmark, revnode in sorted(bmstore.iteritems()):
+            for bmark, revnode in sorted(bmstore.items()):
                 revnum = repo[revnode].rev()
                 if revnum in skipped:
                     continue
@@ -854,7 +866,7 @@
         if actions:
             try:
                 tr = repo.transaction('debugconvertbookmark')
-                for ((bmark, revnum), targetrevs) in sorted(actions.iteritems()):
+                for ((bmark, revnum), targetrevs) in sorted(actions.items()):
                     _applyconvertbmarktopic(ui, repo, targetrevs, revnum, bmark, tr)
                 tr.close()
             finally:
@@ -952,7 +964,7 @@
             # to not be so invasive.
             del fixedextra['amend_source']
         ui.debug('changing topic of %s from %s to %s\n' % (
-            c, oldtopic, newtopic))
+            c, oldtopic or '<none>', newtopic or '<none>'))
         ui.debug('fixedextra: %r\n' % fixedextra)
         # While changing topic of set of linear commits, make sure that
         # we base our commits on new parent rather than old parent which
@@ -997,7 +1009,7 @@
     return rewrote
 
 def _listtopics(ui, repo, opts):
-    fm = ui.formatter('topics', opts)
+    fm = ui.formatter('topics', pycompat.byteskwargs(opts))
     activetopic = repo.currenttopic
     namemask = '%s'
     if repo.topics:
--- a/hgext3rd/topic/compat.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/compat.py	Mon Jul 29 14:43:12 2019 +0200
@@ -9,6 +9,7 @@
 
 from mercurial import (
     obsolete,
+    pycompat,
 )
 
 getmarkers = None
@@ -24,3 +25,10 @@
     getmarkers = obsolete.getmarkers
 if successorssets is None:
     successorssets = obsolete.successorssets
+
+if pycompat.ispy3:
+    def branchmapitems(branchmap):
+        return branchmap.items()
+else:
+    def branchmapitems(branchmap):
+        return branchmap.iteritems()
--- a/hgext3rd/topic/discovery.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/discovery.py	Mon Jul 29 14:43:12 2019 +0200
@@ -14,6 +14,7 @@
 )
 from . import (
     common,
+    compat,
 )
 
 try:
@@ -49,7 +50,7 @@
     def remotebranchmap():
         # drop topic information from changeset about to be published
         result = collections.defaultdict(list)
-        for branch, heads in origremotebranchmap().iteritems():
+        for branch, heads in compat.branchmapitems(origremotebranchmap()):
             if ':' not in branch:
                 result[branch].extend(heads)
             else:
@@ -110,7 +111,7 @@
         repo.unfiltered = lambda: unxx
         pushop.repo = repo
         summary = orig(pushop)
-        for key, value in summary.iteritems():
+        for key, value in summary.items():
             if ':' in key: # This is a topic
                 if value[0] is None and value[1]:
                     summary[key] = ([value[1][0]], ) + value[1:]
@@ -171,12 +172,12 @@
         if repo is not None:
             repo.invalidatecaches()
             finalheads = _nbheads(repo)
-            for branch, oldnb in tr._prepushheads.iteritems():
+            for branch, oldnb in tr._prepushheads.items():
                 newnb = finalheads.pop(branch, 0)
                 if oldnb < newnb:
                     msg = _('push create a new head on branch "%s"' % branch)
                     raise error.Abort(msg)
-            for branch, newnb in finalheads.iteritems():
+            for branch, newnb in finalheads.items():
                 if 1 < newnb:
                     msg = _('push create more than 1 head on new branch "%s"'
                             % branch)
--- a/hgext3rd/topic/flow.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/flow.py	Mon Jul 29 14:43:12 2019 +0200
@@ -11,8 +11,13 @@
 
 from mercurial.i18n import _
 
+from . import (
+    compat,
+)
+
 def enforcesinglehead(repo, tr):
-    for name, heads in repo.filtered('visible').branchmap().iteritems():
+    branchmap = repo.filtered('visible').branchmap()
+    for name, heads in compat.branchmapitems(branchmap):
         if len(heads) > 1:
             hexs = [node.short(n) for n in heads]
             raise error.Abort(_('%d heads on "%s"') % (len(heads), name),
--- a/hgext3rd/topic/randomname.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/randomname.py	Mon Jul 29 14:43:12 2019 +0200
@@ -1005,6 +1005,9 @@
 ]
 
 def randomtopicname(ui):
+    # Re-implement random.choice() in the way it was written in Python 2.
+    def choice(things):
+        return things[int(len(things) * random.random())]
     if ui.configint("devel", "randomseed"):
         random.seed(ui.configint("devel", "randomseed"))
-    return random.choice(adjectives) + "-" + random.choice(animals)
+    return choice(adjectives) + "-" + choice(animals)
--- a/hgext3rd/topic/revset.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/revset.py	Mon Jul 29 14:43:12 2019 +0200
@@ -28,7 +28,7 @@
         return x[1]
     raise error.ParseError(err)
 
-@revsetpredicate('topic([string or set])')
+@revsetpredicate(b'topic([string or set])')
 def topicset(repo, subset, x):
     """All changesets with the specified topic or the topics of the given
     changesets. Without the argument, all changesets with any topic specified.
@@ -76,7 +76,7 @@
 
     return (subset & mutable).filter(matches)
 
-@revsetpredicate('ngtip([branch])')
+@revsetpredicate(b'ngtip([branch])')
 def ngtipset(repo, subset, x):
     """The untopiced tip.
 
@@ -89,7 +89,7 @@
         branch = repo['.'].branch()
     return subset & revset.baseset(destination.ngtip(repo, branch))
 
-@revsetpredicate('stack()')
+@revsetpredicate(b'stack()')
 def stackset(repo, subset, x):
     """All relevant changes in the current topic,
 
--- a/hgext3rd/topic/stack.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/stack.py	Mon Jul 29 14:43:12 2019 +0200
@@ -8,6 +8,7 @@
     error,
     node,
     phases,
+    pycompat,
     obsolete,
     util,
 )
@@ -204,7 +205,7 @@
                 return 0
             except error.ManyMergeDestAbort as exc:
                 # XXX we should make it easier for upstream to provide the information
-                self.behinderror = str(exc).split('-', 1)[0].rstrip()
+                self.behinderror = pycompat.bytestr(exc).split('-', 1)[0].rstrip()
                 return -1
         return 0
 
--- a/hgext3rd/topic/topicmap.py	Mon Jul 29 11:40:18 2019 +0200
+++ b/hgext3rd/topic/topicmap.py	Mon Jul 29 14:43:12 2019 +0200
@@ -80,7 +80,7 @@
     if revs:
         s = hashlib.sha1()
         for rev in revs:
-            s.update('%s;' % rev)
+            s.update('%d;' % rev)
         key = s.digest()
     return key
 
@@ -100,8 +100,7 @@
     # wrap commit status use the topic branch heads
     ctx = repo[node]
     if ctx.topic() and ctx.branch() == branch:
-        subbranch = "%s:%s" % (branch, ctx.topic())
-        bheads = repo.branchheads("%s:%s" % (subbranch, ctx.topic()))
+        bheads = repo.branchheads("%s:%s" % (branch, ctx.topic()))
 
     ret = orig(repo, node, branch, bheads=bheads, opts=opts)
 
--- a/tests/test-evolve-abort-orphan.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-abort-orphan.t	Mon Jul 29 14:43:12 2019 +0200
@@ -42,7 +42,7 @@
 =============================================
 
   $ hg evolve --abort
-  abort: no interrupted evolve to stop
+  abort: no interrupted evolve to abort
   [255]
 
 Testing with wrong combination of flags
@@ -85,7 +85,8 @@
   atop:[5] added c
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg parents
@@ -132,7 +133,8 @@
   atop:[5] added c
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ echo foo > d
   $ hg resolve -m
@@ -159,7 +161,8 @@
   move:[5] added c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
 testing that interrupted evolve shows up in morestatus
@@ -270,7 +273,8 @@
   atop:[7] added a
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg glog
@@ -334,7 +338,8 @@
   atop:[7] added a
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ hg glog
   o  9:7f8e8bd9f0b6 added c
@@ -411,7 +416,8 @@
   atop:[7] added a
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg glog
@@ -486,7 +492,8 @@
   move:[3] added c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg glog
@@ -532,7 +539,8 @@
   atop:[5] added b
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --abort
--- a/tests/test-evolve-abort-phasediv.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-abort-phasediv.t	Mon Jul 29 14:43:12 2019 +0200
@@ -87,7 +87,8 @@
   rebasing to destination parent: ca1b80f7960a
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
 testing that interrupted evolve shows up in morestatus
@@ -213,7 +214,8 @@
   rebasing to destination parent: b1661037fa25
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --abort
@@ -282,7 +284,8 @@
   rebasing to destination parent: b1661037fa25
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo watwat > c
@@ -297,7 +300,8 @@
   rebasing to destination parent: ca1b80f7960a
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --abort
--- a/tests/test-evolve-content-divergent-basic.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-content-divergent-basic.t	Mon Jul 29 14:43:12 2019 +0200
@@ -340,7 +340,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foobar > d
@@ -399,7 +400,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo watbar > d
@@ -609,7 +611,8 @@
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ cat > a <<EOF
--- a/tests/test-evolve-content-divergent-corner-cases.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-content-divergent-corner-cases.t	Mon Jul 29 14:43:12 2019 +0200
@@ -277,7 +277,8 @@
   rebasing "other" content-divergent changeset de4ea3103326 on 9150fe93bec6
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff --no-git --config diff.unified=3
--- a/tests/test-evolve-content-divergent-interrupted.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-content-divergent-interrupted.t	Mon Jul 29 14:43:12 2019 +0200
@@ -86,7 +86,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg status -v
@@ -197,7 +198,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --abort
@@ -303,7 +305,8 @@
   rebasing "other" content-divergent changeset 69bdd23a9b0d on ca1b80f7960a
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --abort
@@ -356,7 +359,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --abort
@@ -447,7 +451,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --stop
@@ -499,7 +504,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --stop
@@ -548,7 +554,8 @@
   rebasing "other" content-divergent changeset 8fd1c4bd144c on ca1b80f7960a
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
--- a/tests/test-evolve-content-divergent-relocation.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-content-divergent-relocation.t	Mon Jul 29 14:43:12 2019 +0200
@@ -255,7 +255,8 @@
   merging y
   warning: conflicts while merging y! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo watbar > y
@@ -389,7 +390,8 @@
   rebasing "other" content-divergent changeset 3f7a1f693080 on 7bbcf24ddecf
   merging y
   warning: conflicts while merging y! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
@@ -418,7 +420,8 @@
   merging y
   warning: conflicts while merging y! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
--- a/tests/test-evolve-continue.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-continue.t	Mon Jul 29 14:43:12 2019 +0200
@@ -58,7 +58,8 @@
   atop:[5] added c
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foo > d
@@ -118,7 +119,8 @@
   atop:[8] added d
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo bar > e
@@ -158,7 +160,8 @@
   atop:[9] added a
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foo > b
@@ -242,7 +245,8 @@
   move:[13] added f
   merging f
   warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foo > f
@@ -256,7 +260,8 @@
   move:[15] added h
   merging h
   warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foo > h
@@ -302,7 +307,8 @@
   perform evolve? [Ny] y
   merging g
   warning: conflicts while merging g! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foo > g
@@ -350,7 +356,8 @@
   atop:[24] added f
   merging g
   warning: conflicts while merging g! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ echo foo > g
   $ hg resolve -m
@@ -416,7 +423,8 @@
   atop:[5] added c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
 Status mentions file 'b' (copied from 'a') here, even though it wasn't
--- a/tests/test-evolve-issue5966.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-issue5966.t	Mon Jul 29 14:43:12 2019 +0200
@@ -57,7 +57,8 @@
   $ hg evolve -t :fail --rev 'first(orphan())'
   move:[2] banana
   atop:[4] apricot
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ hg evolve --list
   34a690fcf6ab: banana
--- a/tests/test-evolve-issue5967.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-issue5967.t	Mon Jul 29 14:43:12 2019 +0200
@@ -41,7 +41,8 @@
   atop:[2] apricot
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo apricot > a
--- a/tests/test-evolve-orphan-merge.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-orphan-merge.t	Mon Jul 29 14:43:12 2019 +0200
@@ -219,7 +219,8 @@
   atop:[11] foo to c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo FOObar > c
@@ -274,7 +275,8 @@
   atop:[13] foo to c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foobar > c
--- a/tests/test-evolve-phase-divergence.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-phase-divergence.t	Mon Jul 29 14:43:12 2019 +0200
@@ -807,7 +807,8 @@
   rebasing to destination parent: 8c2bb6fb44e9
   merging x
   warning: conflicts while merging x! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
--- a/tests/test-evolve-phase.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-phase.t	Mon Jul 29 14:43:12 2019 +0200
@@ -85,7 +85,8 @@
   atop:[3] b
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
--- a/tests/test-evolve-progress.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-progress.t	Mon Jul 29 14:43:12 2019 +0200
@@ -133,7 +133,8 @@
   picked tool ':merge' for a (binary False symlink False changedelete False)
   my a@f8d7d38c0a88+ other a@df5d742141b0 ancestor a@152c368c622b
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ echo resolved > a
   $ hg resolve -m a
--- a/tests/test-evolve-public-content-divergent-corner-cases.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-public-content-divergent-corner-cases.t	Mon Jul 29 14:43:12 2019 +0200
@@ -389,7 +389,8 @@
   rebasing "other" content-divergent changeset e568fd1029bb on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
@@ -419,7 +420,8 @@
   What do you want to do?
   use (c)hanged version, (d)elete, or leave (u)nresolved? u
   1 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg sum
--- a/tests/test-evolve-public-content-divergent-discard.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-public-content-divergent-discard.t	Mon Jul 29 14:43:12 2019 +0200
@@ -269,7 +269,8 @@
   merging ch
   warning: conflicts while merging ch! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
@@ -383,7 +384,8 @@
   rebasing "other" content-divergent changeset f89a8e2f86ac on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo c > c
@@ -488,7 +490,8 @@
   merging dh
   warning: conflicts while merging dh! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo dh > dh
@@ -591,7 +594,8 @@
   rebasing "other" content-divergent changeset 67b19bbd770f on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo c > c
@@ -604,7 +608,8 @@
   merging dh
   warning: conflicts while merging dh! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo dh > dh
--- a/tests/test-evolve-public-content-divergent-main.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-public-content-divergent-main.t	Mon Jul 29 14:43:12 2019 +0200
@@ -170,7 +170,8 @@
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo "I am foobar" > b
@@ -356,7 +357,8 @@
   rebasing "other" content-divergent changeset f31bcc378766 on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
@@ -486,7 +488,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo d > d
@@ -578,7 +581,8 @@
   rebasing "other" content-divergent changeset 3c17c7afaf6e on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
@@ -612,7 +616,8 @@
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   2 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo d > d
--- a/tests/test-evolve-stop-orphan.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-stop-orphan.t	Mon Jul 29 14:43:12 2019 +0200
@@ -90,7 +90,8 @@
   atop:[5] added c
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --stop
@@ -135,7 +136,8 @@
   atop:[5] added c
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
@@ -195,7 +197,8 @@
   atop:[5] added c
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ echo foo > d
   $ hg resolve -m
@@ -244,7 +247,8 @@
   move:[5] added c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg status
@@ -281,7 +285,8 @@
   atop:[9] added b
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ echo foobar > c
@@ -356,7 +361,8 @@
   move:[10] added c
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --stop
--- a/tests/test-evolve-stop-phasediv.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-stop-phasediv.t	Mon Jul 29 14:43:12 2019 +0200
@@ -84,7 +84,8 @@
   rebasing to destination parent: ca1b80f7960a
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ hg evolve --stop
--- a/tests/test-evolve-topic.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve-topic.t	Mon Jul 29 14:43:12 2019 +0200
@@ -426,7 +426,8 @@
   move:[s3] add hhh
   merging hhh
   warning: conflicts while merging hhh! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ echo "resolved hhh" > hhh
   $ hg resolve --mark hhh
@@ -438,3 +439,22 @@
   atop:[s3] add hhh
   move:[s5] add jjj
   working directory is now at 2c295936ac04
+
+Test to make sure that evolve don't crash with FilteredRepoLookupError when obsolete revs are in play:
+------------------------------------------------------------------------------------------------------
+
+update to obsolete revision
+  $ hg up -r 'min(desc("add fff"))' --hidden
+  switching to topic foo
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  updated to hidden changeset 6a6b7365c751
+  (hidden revision '6a6b7365c751' was rewritten as: 2c295936ac04)
+  working directory parent is obsolete! (6a6b7365c751)
+  (use 'hg evolve' to update to its successor: 2c295936ac04)
+
+Evolve:
+  $ hg evolve
+  update:[26] add fff
+  switching to topic bar
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 2c295936ac04
--- a/tests/test-evolve.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-evolve.t	Mon Jul 29 14:43:12 2019 +0200
@@ -1389,7 +1389,8 @@
   move:[31] will cause conflict at evolve
   merging newfile
   warning: conflicts while merging newfile! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
   $ glog -r "desc('add unstableifparentisfolded')::" --hidden
@@ -1441,7 +1442,6 @@
   update:[1] added a
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory is now at ab832e43dd5a
-  no troubled changesets
 
   $ hg log -GT "{rev}:{node|short} {desc} ({bookmarks})\n" --hidden
   @  1:ab832e43dd5a added a (book)
--- a/tests/test-fold.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-fold.t	Mon Jul 29 14:43:12 2019 +0200
@@ -7,9 +7,17 @@
   > fold=-d "0 0"
   > [extensions]
   > evolve=
+  > [alias]
+  > glog = log -GT "{rev}: {desc}"
+  > glf = log -GT "{rev}: {desc} ({files})"
   > [ui]
   > logtemplate = '{rev} - {node|short} {desc|firstline} [{author}] ({phase}) {bookmarks}\n'
   > EOF
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -qm "$1"
+  > }
 
   $ hg init fold-tests
   $ cd fold-tests/
@@ -270,3 +278,106 @@
 
   $ cd ..
 
+One merge commit
+
+  $ hg init fold-a-merge
+  $ cd fold-a-merge
+
+  $ mkcommit zebra
+
+  $ hg up null
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit apple
+  $ mkcommit banana
+
+  $ hg merge
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m merge
+
+  $ mkcommit coconut
+
+  $ hg glf
+  @  4: coconut (coconut)
+  |
+  o    3: merge ()
+  |\
+  | o  2: banana (banana)
+  | |
+  | o  1: apple (apple)
+  |
+  o  0: zebra (zebra)
+  
+
+now we merge some of the fruits
+
+  $ hg fold --exact -r 'desc("banana")::desc("coconut")' -m 'banana+coconut in a merge with zebra'
+  3 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg glf
+  @    5: banana+coconut in a merge with zebra (banana coconut)
+  |\
+  | o  1: apple (apple)
+  |
+  o  0: zebra (zebra)
+  
+
+let's go even further: zebra becomes a parent of the squashed fruit commit
+
+  $ hg fold --from -r 'desc("apple")' -m 'apple+banana+coconut is a child of zebra'
+  2 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg glf
+  @  6: apple+banana+coconut is a child of zebra (apple banana coconut)
+  |
+  o  0: zebra (zebra)
+  
+
+make sure zebra exists at tip and has expected contents
+
+  $ hg cat -r tip zebra
+  zebra
+
+  $ cd ..
+
+Multiple merge commits
+
+  $ hg init fold-many-merges
+  $ cd fold-many-merges
+
+  $ hg debugbuilddag '+3 *3 /3 /4 /4'
+  $ hg glog
+  o    6: r6
+  |\
+  | o    5: r5
+  | |\
+  | | o  4: r4
+  | |/|
+  | | o  3: r3
+  | | |
+  o | |  2: r2
+  |/ /
+  o /  1: r1
+  |/
+  o  0: r0
+  
+
+cannot fold 5 and 6 because they have 3 external parents in total: 1, 2, 4
+
+  $ hg fold --exact -r 5:6 -m r5+r6
+  abort: cannot fold revisions that merge with more than one external changeset (not in revisions)
+  [255]
+
+now many of the parents are included in the revisions to fold, only 0 and 3 are external
+
+  $ hg fold --exact -r 1+2+4+5+6 -m r1+r2+r4+r5+r6
+  5 changesets folded
+
+  $ hg glog
+  o    7: r1+r2+r4+r5+r6
+  |\
+  | o  3: r3
+  |/
+  o  0: r0
+  
+  $ cd ..
--- a/tests/test-issue-5720.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-issue-5720.t	Mon Jul 29 14:43:12 2019 +0200
@@ -61,7 +61,8 @@
   atop:[3] b
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
 
 Fix the conflict
--- a/tests/test-metaedit.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-metaedit.t	Mon Jul 29 14:43:12 2019 +0200
@@ -10,6 +10,7 @@
   > publish = False
   > [alias]
   > qlog = log --template='{rev} - {node|short} {desc} ({phase})\n'
+  > gluf = log -GT "{rev}: {desc|firstline} - {author|user} ({files})"
   > [diff]
   > git = 1
   > unified = 0
@@ -229,3 +230,54 @@
   1 changesets folded
   $ hg log -r "tip" --template '{rev}: {author}\n'
   12: foobar3
+
+working on merge commits too
+
+  $ hg up -q 11
+  $ hg merge -q 12
+  $ hg ci -m 'merge commit'
+  $ hg st --change .
+  A D
+  $ hg metaedit --user someone-else
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg st --change .
+  A D
+  $ hg gluf
+  @    14: merge commit - someone-else ()
+  |\
+  | o  12: D2 - foobar3 (D)
+  | |
+  o |  11: E - foobar2 (E F)
+  |/
+  o  3: C - test (C)
+  |
+  | o  2: B - test (B)
+  |/
+  o  1: A - test (A)
+  |
+  o  0: ROOT - test (ROOT)
+  
+  $ hg metaedit --user mr-squasher -r 3:14 --fold --message squashed
+  4 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg st --change .
+  A C
+  A D
+  A E
+  A F
+  $ hg gluf
+  @  15: squashed - mr-squasher (C D E F)
+  |
+  | o  2: B - test (B)
+  |/
+  o  1: A - test (A)
+  |
+  o  0: ROOT - test (ROOT)
+  
+  $ hg files
+  A
+  C
+  D
+  E
+  F
+  ROOT
--- a/tests/test-rewind.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-rewind.t	Mon Jul 29 14:43:12 2019 +0200
@@ -9,6 +9,8 @@
   > interactive = true
   > [phases]
   > publish=False
+  > [alias]
+  > glf = log -GT "{rev}: {desc} ({files})"
   > [extensions]
   > evolve =
   > EOF
@@ -935,3 +937,45 @@
   $ hg rewind
   abort: uncommitted changes
   [255]
+
+Merge commits
+-------------
+
+  $ hg up --clean .^
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo foo > foo
+  $ hg ci -qAm foo
+
+  $ hg merge
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m merge
+  $ hg st --change .
+  A B
+
+  $ echo bar > foo
+  $ hg amend -m 'merge, but foo is now bar'
+  $ hg st --change .
+  M foo
+  A B
+
+  $ hg rewind --from .
+  rewinded to 1 changesets
+  (1 changesets obsoleted)
+  working directory is now at 006fd8c2fed9
+  $ hg st --change .
+  A B
+
+  $ hg glf -r '. + allpredecessors(.) + parents(.)' --hidden
+  @    6: merge ()
+  |\
+  +---x  5: merge, but foo is now bar (foo)
+  | |/
+  +---x  4: merge ()
+  | |/
+  | o  3: foo (C foo)
+  | |
+  | ~
+  o  2: c_B0 (B)
+  |
+  ~
--- a/tests/test-stabilize-conflict.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-stabilize-conflict.t	Mon Jul 29 14:43:12 2019 +0200
@@ -128,7 +128,8 @@
   atop:[5] babar count up to ten
   merging babar
   warning: conflicts while merging babar! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ hg resolve -l
   U babar
@@ -220,7 +221,8 @@
    output file babar appears unchanged
   was merge successful (yn)? n
   merging babar failed!
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ hg resolve -l
   U babar
--- a/tests/test-topic-stack-data.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-topic-stack-data.t	Mon Jul 29 14:43:12 2019 +0200
@@ -116,7 +116,7 @@
    add foo_b
   branch: lake
   commit: (clean)
-  update: 2 new changesets (update)
+  update: (current)
   phases: 22 draft
   orphan: 3 changesets
   topic:  foo
--- a/tests/test-topic-stack.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-topic-stack.t	Mon Jul 29 14:43:12 2019 +0200
@@ -315,7 +315,7 @@
    c_d
   branch: default
   commit: (clean)
-  update: (current)
+  update: 2 new changesets (update)
   phases: 4 draft
   topic:  foo
 
--- a/tests/test-topic.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-topic.t	Mon Jul 29 14:43:12 2019 +0200
@@ -963,3 +963,27 @@
   abort: cannot use --age while setting a topic
   [255]
   $ cd ..
+
+Test that topics doesn't confuse branchheads checking logic
+-----------------------------------------------------------
+
+  $ hg init hgtags
+  $ cd hgtags
+  $ echo a > a
+  $ hg ci -Am "added a" --config experimental.topic-mode=default
+  adding a
+  $ echo b > b
+  $ hg ci -Am "added b" --config experimental.topic-mode=default
+  adding b
+
+  $ hg topic foo -r .
+  switching to topic foo
+  changed topic on 1 changesets to "foo"
+
+Try to put a tag on current rev which also has an active topic:
+  $ hg tag 1.0
+  $ hg tags
+  tip                                3:9efc5c3ac635
+  1.0                                2:3bbb3fdb2546
+
+  $ cd ..
--- a/tests/test-touch.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-touch.t	Mon Jul 29 14:43:12 2019 +0200
@@ -4,6 +4,8 @@
   > logtemplate={rev}:{node|short} {desc}\n
   > [defaults]
   > amend=-d "0 0"
+  > [alias]
+  > glog = log -GT "{rev}: {desc}"
   > [extensions]
   > hgext.rebase=
   > EOF
@@ -171,6 +173,34 @@
   [255]
   $ hg touch --duplicate 2
 
+Reviving merge commit
+
+  $ hg up 12
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg merge 15
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m merge
+  $ hg st --change .
+  A a
+  A b
+  $ hg prune -r .
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  working directory is now at * (glob)
+  1 changesets pruned
+  $ hg touch 16 --hidden
+  $ hg glog -r '12+15+17'
+  o    17: merge
+  |\
+  | o  15: ab
+  |
+  @  12: move
+  |
+  ~
+  $ hg st --change 17
+  A a
+  A b
+
   $ cd ..
 
 Make sure touch doesn't fail to warn about divergence (issue6107)
--- a/tests/test-tutorial.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-tutorial.t	Mon Jul 29 14:43:12 2019 +0200
@@ -1528,7 +1528,6 @@
   update:[8] animals
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory is now at 2a2b36e14660
-  no troubled changesets
 
 Relocating unstable change after prune
 ----------------------------------------------
--- a/tests/test-unstability-resolution-result.t	Mon Jul 29 11:40:18 2019 +0200
+++ b/tests/test-unstability-resolution-result.t	Mon Jul 29 14:43:12 2019 +0200
@@ -90,7 +90,8 @@
   atop:[5] changea
   merging a
   warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
-  fix conflicts and see `hg help evolve.interrupted`
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
   [1]
   $ hg revert -r "orphan()" a
   $ hg diff