test-compat: merge mercurial-4.2 into mercurial-4.1 mercurial-4.1
authorPierre-Yves David <pierre-yves.david@octobus.net>
Mon, 11 Dec 2017 18:30:15 +0100
branchmercurial-4.1
changeset 3289 bd99cb54712b
parent 3288 b714709afe6a (diff)
parent 3275 875de4816c5b (current diff)
child 3382 f14ad1e83896
test-compat: merge mercurial-4.2 into mercurial-4.1
tests/test-discovery-obshashrange.t
tests/test-evolve-obshistory.t
tests/test-prev-next.t
tests/test-topic-stack-complex.t
tests/test-topic-stack.t
tests/test-wireproto.t
--- a/CHANGELOG	Mon Dec 11 09:33:32 2017 +0100
+++ b/CHANGELOG	Mon Dec 11 18:30:15 2017 +0100
@@ -1,6 +1,22 @@
 Changelog
 =========
 
+7.1.0 - in progress
+-------------------
+
+  * verbosity: respect --quiet for prev, next and summary
+  * note: add a `-n/--note` flag to all history rewritting commands
+  * obslog: shows the obsmarkers notes
+  * obsdiscover: Improved stable range slice for the experimental obshashrange
+                 (client and server need to upgrade to this version)
+
+topic (0.6.0)
+
+  * add a new 'serverminitopic' extension for minimal server support
+    (see `hg help -e serverminitopic` for details)
+  * add a new config option `experimental.topic-mode.server` using which a
+    server can reject draft changesets without topic
+
 7.0.2 - in progress
 -------------------
 
@@ -8,7 +24,9 @@
 
 Topic (0.5.2)
 
+  * fix behavior of `hg stack` in cases of split
   * makes code more resilient to partiel initialization
+  * avoid over wrapping inside of long living process
 
 7.0.1 -- 2017-11-14
 -------------------
--- a/hgext3rd/evolve/__init__.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/__init__.py	Mon Dec 11 18:30:15 2017 +0100
@@ -831,17 +831,10 @@
         raise
 
 def summaryhook(ui, repo):
-    def write(fmt, count):
-        s = fmt % count
-        if count:
-            ui.write(s)
-        else:
-            ui.note(s)
-
     state = _evolvestateread(repo)
     if state is not None:
         # i18n: column positioning for "hg summary"
-        ui.write(_('evolve: (evolve --continue)\n'))
+        ui.status(_('evolve: (evolve --continue)\n'))
 
 @eh.extsetup
 def obssummarysetup(ui):
@@ -2031,7 +2024,8 @@
             finally:
                 lockmod.release(tr, lock)
 
-    displayer.show(target)
+    if not repo.ui.quiet:
+        displayer.show(target)
 
 def _findprevtarget(repo, displayer, movebookmark=False, topic=True):
     target = bookmark = None
@@ -2174,7 +2168,8 @@
                         tr.close()
                     finally:
                         lockmod.release(tr, lock)
-            displayer.show(c)
+            if not ui.quiet:
+                displayer.show(c)
             result = 0
         elif children:
             ui.warn(_("ambiguous next changeset:\n"))
--- a/hgext3rd/evolve/cmdrewrite.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/cmdrewrite.py	Mon Dec 11 18:30:15 2017 +0100
@@ -49,6 +49,21 @@
 
 # option added by evolve
 
+def _checknotesize(ui, opts):
+    """ make sure note is of valid format """
+
+    note = opts.get('note')
+    if not note:
+        return
+
+    if not compat.isobsnotesupported():
+        ui.warn(_("current hg version does not support storing"
+                  " note in obsmarker\n"))
+    if len(note) > 255:
+        raise error.Abort(_("cannot store a note of more than 255 bytes"))
+    if '\n' in note:
+        raise error.Abort(_("note cannot contain a newline"))
+
 def _resolveoptions(ui, opts):
     """modify commit options dict to handle related options
 
@@ -80,6 +95,7 @@
      ('', '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')),
     ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt,
     _('[OPTION]... [FILE]...'))
 def amend(ui, repo, *pats, **opts):
@@ -98,6 +114,7 @@
 
     Returns 0 on success, 1 if nothing changed.
     """
+    _checknotesize(ui, opts)
     opts = opts.copy()
     if opts.get('extract'):
         return uncommit(ui, repo, *pats, **opts)
@@ -288,6 +305,7 @@
     [('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')),
+     ('n', 'note', '', _('store a note on uncommit')),
      ] + commands.walkopts + commitopts + commitopts2 + commitopts3,
     _('[OPTION]... [NAME]'))
 def uncommit(ui, repo, *pats, **opts):
@@ -313,6 +331,7 @@
     Return 0 if changed files are uncommitted.
     """
 
+    _checknotesize(ui, opts)
     _resolveoptions(ui, opts) # process commitopts3
     interactive = opts.get('interactive')
     wlock = lock = tr = None
@@ -365,7 +384,12 @@
                 raise error.Abort(_('nothing to uncommit'),
                                   hint=_("use --all to uncommit all files"))
 
-        obsolete.createmarkers(repo, [(old, (repo[newid],))])
+        # metadata to be stored in obsmarker
+        metadata = {}
+        if opts.get('note'):
+            metadata['note'] = opts['note']
+
+        obsolete.createmarkers(repo, [(old, (repo[newid],))], metadata=metadata)
         phases.retractboundary(repo, tr, oldphase, [newid])
         with repo.dirstate.parentchange():
             repo.dirstate.setparents(newid, node.nullid)
@@ -477,7 +501,8 @@
     '^fold|squash',
     [('r', 'rev', [], _("revision to fold")),
      ('', 'exact', None, _("only fold specified revisions")),
-     ('', 'from', None, _("fold revisions linearly to working copy parent"))
+     ('', 'from', None, _("fold revisions linearly to working copy parent")),
+     ('n', 'note', '', _('store a note on fold')),
     ] + commitopts + commitopts2 + commitopts3,
     _('hg fold [OPTION]... [-r] REV'))
 def fold(ui, repo, *revs, **opts):
@@ -517,6 +542,7 @@
 
          hg fold foo::@ --exact
     """
+    _checknotesize(ui, opts)
     _resolveoptions(ui, opts)
     revs = list(revs)
     revs.extend(opts['rev'])
@@ -572,6 +598,10 @@
                 commitopts['message'] = "\n".join(msgs)
                 commitopts['edit'] = True
 
+            metadata = {}
+            if opts.get('note'):
+                metadata['note'] = opts['note']
+
             newid, unusedvariable = rewriteutil.rewrite(repo, root, allctx,
                                                         head,
                                                         [root.p1().node(),
@@ -579,7 +609,7 @@
                                                         commitopts=commitopts)
             phases.retractboundary(repo, tr, targetphase, [newid])
             obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                   for ctx in allctx])
+                                   for ctx in allctx], metadata=metadata)
             tr.close()
         finally:
             tr.release()
@@ -593,6 +623,7 @@
     'metaedit',
     [('r', 'rev', [], _("revision to edit")),
      ('', 'fold', None, _("also fold specified revisions into one")),
+     ('n', 'note', '', _('store a note on metaedit')),
     ] + commitopts + commitopts2 + commitopts3,
     _('hg metaedit [OPTION]... [-r] [REV]'))
 def metaedit(ui, repo, *revs, **opts):
@@ -624,6 +655,7 @@
        See :hg:`help phases` for more about draft revisions, and
        :hg:`help revsets` for more about the `draft()` and `only()` keywords.
     """
+    _checknotesize(ui, opts)
     _resolveoptions(ui, opts)
     revs = list(revs)
     revs.extend(opts['rev'])
@@ -697,9 +729,15 @@
             if created:
                 if p1.rev() in revs:
                     newp1 = newid
+                # metadata to be stored on obsmarker
+                metadata = {}
+                if opts.get('note'):
+                    metadata['note'] = opts['note']
+
                 phases.retractboundary(repo, tr, targetphase, [newid])
                 obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                              for ctx in allctx])
+                                              for ctx in allctx],
+                                       metadata=metadata)
             else:
                 ui.status(_("nothing changed\n"))
             tr.close()
@@ -736,6 +774,7 @@
      ('s', 'succ', [], _("successor changeset")),
      ('r', 'rev', [], _("revisions to prune")),
      ('k', 'keep', None, _("does not modify working copy during prune")),
+     ('n', 'note', '', _('store a note on prune')),
      ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")),
      ('', 'fold', False,
       _("record a fold (multiple precursors, one successors)")),
@@ -769,6 +808,7 @@
     must acknowledge it by passing ``--split``. Similarly, when you prune multiple
     changesets with a single successor, you must pass the ``--fold`` option.
     """
+    _checknotesize(ui, opts)
     revs = scmutil.revrange(repo, list(revs) + opts.get('rev'))
     succs = opts['new'] + opts['succ']
     bookmarks = set(opts.get('bookmark'))
@@ -884,6 +924,10 @@
         if bookmarks:
             rewriteutil.deletebookmark(repo, repomarks, bookmarks)
 
+        # store note in metadata
+        if opts.get('note'):
+            metadata['note'] = opts['note']
+
         # create markers
         obsolete.createmarkers(repo, relations, metadata=metadata)
 
@@ -913,6 +957,7 @@
 @eh.command(
     '^split',
     [('r', 'rev', [], _("revision to split")),
+     ('n', 'note', '', _("store a note on split")),
     ] + commitopts + commitopts2 + commitopts3,
     _('hg split [OPTION]... [-r] REV'))
 def cmdsplit(ui, repo, *revs, **opts):
@@ -923,6 +968,7 @@
 
     Use --rev to split a given changeset instead.
     """
+    _checknotesize(ui, opts)
     _resolveoptions(ui, opts)
     tr = wlock = lock = None
     newcommits = []
@@ -992,7 +1038,11 @@
             bmupdate(tip.node())
             if bookactive is not None:
                 bookmarksmod.activate(repo, bookactive)
-            obsolete.createmarkers(repo, [(repo[rev], newcommits)])
+            metadata = {}
+            if opts.get('note'):
+                metadata['note'] = opts['note']
+            obsolete.createmarkers(repo, [(repo[rev], newcommits)],
+                                   metadata=metadata)
         tr.close()
     finally:
         # Restore the old branch
@@ -1003,6 +1053,7 @@
 @eh.command(
     'touch',
     [('r', 'rev', [], 'revision to update'),
+     ('n', 'note', '', _('store a note on touch')),
      ('D', 'duplicate', False,
       'do not mark the new revision as successor of the old one'),
      ('A', 'allowdivergence', False,
@@ -1015,6 +1066,7 @@
 
     This is used to "resurrect" changesets
     """
+    _checknotesize(ui, opts)
     duplicate = opts['duplicate']
     allowdivergence = opts['allowdivergence']
     revs = list(revs)
@@ -1078,7 +1130,11 @@
             newmapping[ctx.node()] = new
 
             if not duplicate:
-                obsolete.createmarkers(repo, [(ctx, (repo[new],))])
+                metadata = {}
+                if opts.get('note'):
+                    metadata['note'] = opts['note']
+                obsolete.createmarkers(repo, [(ctx, (repo[new],))],
+                                       metadata=metadata)
             phases.retractboundary(repo, tr, ctx.phase(), [new])
             if ctx in repo[None].parents():
                 with repo.dirstate.parentchange():
--- a/hgext3rd/evolve/compat.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/compat.py	Mon Dec 11 18:30:15 2017 +0100
@@ -97,6 +97,14 @@
             bookmarks[name] = node
     bookmarks.recordchange(tr)
 
+def isobsnotesupported():
+    # hack to know obsnote is supported. The patches for obsnote support was
+    # pushed before the obsfateprinter patches, so this will serve as a good
+    # check
+    if not obsutil:
+        return False
+    return util.safehasattr(obsutil, 'obsfateprinter')
+
 # Evolution renaming compat
 
 TROUBLES = {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/depthcache.py	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,228 @@
+from __future__ import absolute_import
+
+import array
+import weakref
+
+from mercurial import (
+    localrepo,
+    node as nodemod,
+    util,
+    scmutil,
+)
+
+from . import (
+    error,
+    exthelper,
+    genericcaches,
+)
+
+from mercurial.i18n import _
+
+eh = exthelper.exthelper()
+
+def simpledepth(repo, rev):
+    """simple but obviously right implementation of depth"""
+    return len(repo.revs('::%d', rev))
+
+@eh.command(
+    'debugdepth',
+    [
+        ('r', 'rev', [], 'revs to print depth for'),
+        ('', 'method', 'cached', "one of 'simple', 'cached', 'compare'"),
+    ],
+    _('REVS'))
+def debugdepth(ui, repo, **opts):
+    """display depth of REVS
+    """
+    revs = scmutil.revrange(repo, opts['rev'])
+    method = opts['method']
+    if method == 'cached':
+        cache = repo.depthcache
+        cache.save(repo)
+    for r in revs:
+        ctx = repo[r]
+        if method == 'simple':
+            depth = simpledepth(repo, r)
+        elif method == 'cached':
+            depth = cache.get(r)
+        elif method == 'compare':
+            simple = simpledepth(repo, r)
+            cached = cache.get(r)
+            if simple != cached:
+                raise error.Abort('depth differ for revision %s: %d != %d'
+                                  % (ctx, simple, cached))
+        else:
+            raise error.Abort('unknown method "%s"' % method)
+        ui.write('%s %d\n' % (ctx, depth))
+
+@eh.reposetup
+def setupcache(ui, repo):
+
+    class depthcacherepo(repo.__class__):
+
+        @localrepo.unfilteredpropertycache
+        def depthcache(self):
+            cache = depthcache()
+            cache.update(self)
+            return cache
+
+        @localrepo.unfilteredmethod
+        def destroyed(self):
+            if 'obsstore' in vars(self):
+                self.depthcache.clear()
+            super(depthcacherepo, self).destroyed()
+
+        if util.safehasattr(repo, 'updatecaches'):
+            @localrepo.unfilteredmethod
+            def updatecaches(self, tr=None):
+                if (repo.ui.configbool('experimental', 'obshashrange',
+                                       False)
+                        and repo.ui.configbool('experimental',
+                                               'obshashrange.warm-cache',
+                                               True)):
+                    self.depthcache.update(repo)
+                    self.depthcache.save(repo)
+                super(depthcacherepo, self).updatecaches(tr)
+
+        else:
+            def transaction(self, *args, **kwargs):
+                tr = super(depthcacherepo, self).transaction(*args, **kwargs)
+                reporef = weakref.ref(self)
+
+                def _warmcache(tr):
+                    repo = reporef()
+                    if repo is None:
+                        return
+                    repo = repo.unfiltered()
+                    # As pointed in 'obscache.update', we could have the changelog
+                    # and the obsstore in charge of updating the cache when new
+                    # items goes it. The tranaction logic would then only be
+                    # involved for the 'pending' and final writing on disk.
+                    self.obsstore.obscache.update(repo)
+                    self.obsstore.obscache.save(repo)
+
+                if (repo.ui.configbool('experimental', 'obshashrange',
+                                       False)
+                        and repo.ui.configbool('experimental',
+                                               'obshashrange.warm-cache',
+                                               True)):
+                    tr.addpostclose('warmcache-depthcache', _warmcache)
+                return tr
+
+    repo.__class__ = depthcacherepo
+
+class depthcache(genericcaches.changelogsourcebase):
+
+    _filepath = 'evoext-depthcache-00'
+    _cachename = 'evo-ext-depthcache'
+
+    def __init__(self):
+        super(depthcache, self).__init__()
+        self._data = array.array('l')
+
+    def get(self, rev):
+        if len(self._data) <= rev:
+            raise error.ProgrammingError('depthcache must be warmed before use')
+        return self._data[rev]
+
+    def _updatefrom(self, repo, data):
+        """compute the rev of one revision, assert previous revision has an hot cache
+        """
+        cl = repo.unfiltered().changelog
+        total = len(data)
+
+        def progress(pos, rev):
+            repo.ui.progress('updating depth cache',
+                             pos, 'rev %s' % rev, 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, '')
+
+    def _depth(self, changelog, rev):
+        cl = changelog
+        p1, p2 = cl.parentrevs(rev)
+        if p1 == nodemod.nullrev:
+            # root case
+            return 1
+        elif p2 == nodemod.nullrev:
+            # linear commit case
+            return self.get(p1) + 1
+        # merge case, must find the amount of exclusive content
+        depth_p1 = self.get(p1)
+        depth_p2 = self.get(p2)
+        # computing depth of a merge
+        ancnodes = cl.commonancestorsheads(cl.node(p1), cl.node(p2))
+        if not ancnodes:
+            # unrelated branch, (no common root)
+            revdepth = depth_p1 + depth_p2 + 1
+        elif len(ancnodes) == 1:
+            # one unique branch point:
+            # we can compute depth without any walk
+            ancrev = cl.rev(ancnodes[0])
+            depth_anc = self.get(ancrev)
+            revdepth = depth_p1 + (depth_p2 - depth_anc) + 1
+        else:
+            # we pick the parent that is that is
+            # * the deepest (less changeset outside of it),
+            # * lowest revs because more chance to have descendant of other "above"
+            parents = [(p1, depth_p1), (p2, depth_p2)]
+            parents.sort(key=lambda x: (x[1], -x[0]))
+            revdepth = parents[1][1]
+            revdepth += len(cl.findmissingrevs(common=[parents[1][0]],
+                                               heads=[parents[0][0]]))
+            revdepth += 1 # the merge revision
+        return revdepth
+
+    # cache internal logic
+
+    def clear(self, reset=False):
+        """invalidate the cache content
+
+        if 'reset' is passed, we detected a strip and the cache will have to be
+        recomputed.
+
+        Subclasses MUST overide this method to actually affect the cache data.
+        """
+        super(depthcache, self).clear()
+        self._data = array.array('l')
+
+    # crude version of a cache, to show the kind of information we have to store
+
+    def load(self, repo):
+        """load data from disk"""
+        assert repo.filtername is None
+
+        if util.safehasattr(repo, 'cachevfs'):
+            data = repo.cachevfs.tryread(self._filepath)
+        else:
+            data = repo.vfs.tryread('cache/' + self._filepath)
+        self._data = array.array('l')
+        if not data:
+            self._cachekey = self.emptykey
+        else:
+            headerdata = data[:self._cachekeysize]
+            self._cachekey = self._deserializecachekey(headerdata)
+            self._data.fromstring(data[self._cachekeysize:])
+        self._ondiskkey = self._cachekey
+
+    def save(self, repo):
+        """save the data to disk
+
+        Format is pretty simple, we serialise the cache key and then drop the
+        bytearray.
+        """
+        if self._cachekey is None or self._cachekey == self._ondiskkey:
+            return
+
+        if util.safehasattr(repo, 'cachevfs'):
+            cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True)
+        else:
+            cachefile = repo.vfs('cache/' + self._filepath, 'w', atomictemp=True)
+        headerdata = self._serializecachekey()
+        cachefile.write(headerdata)
+        cachefile.write(self._data.tostring())
+        cachefile.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/genericcaches.py	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,188 @@
+# cache.py - utilities for caching
+#
+# Copyright 2017 Octobus SAS <contact@octobus.net>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+from __future__ import absolute_import
+
+import abc
+import struct
+import time
+import os
+
+from mercurial import (
+    node,
+    pycompat,
+    util,
+)
+
+# prior to hg-4.2 there are not util.timer
+if util.safehasattr(util, 'timer'):
+    timer = util.timer
+elif util.safehasattr(time, "perf_counter"):
+    timer = time.perf_counter
+elif getattr(pycompat, 'osname', os.name) == 'nt':
+    timer = time.clock
+else:
+    timer = time.time
+
+class incrementalcachebase(object):
+    """base class for incremental cache from append only source
+
+    There are multiple append only data source we might want to cache
+    computation from. One of the common pattern is to track the state of the
+    file and update the cache with the extra data (eg: branchmap-cache tracking
+    changelog). This pattern also needs to detect when a the source is striped
+
+    The overall pattern is similar whatever the actual source is. This class
+    introduces the basic patterns.
+    """
+
+    __metaclass__ = abc.ABCMeta
+
+    # default key used for an empty cache
+    emptykey = ()
+
+    _cachekeyspec = '' # used for serialization
+    _cachename = None # used for debug message
+
+    @abc.abstractmethod
+    def __init__(self):
+        super(incrementalcachebase, self).__init__()
+        self._cachekey = None
+
+    @util.propertycache
+    def _cachekeystruct(self):
+        # dynamic property to help subclass to change it
+        return struct.Struct('>' + self._cachekeyspec)
+
+    @util.propertycache
+    def _cachekeysize(self):
+        # dynamic property to help subclass to change it
+        return self._cachekeystruct.size
+
+    @abc.abstractmethod
+    def _updatefrom(self, repo, data):
+        """override this method to update you date from incrementally read data.
+
+        Content of <data> will depends of the sources.
+        """
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def clear(self, reset=False):
+        """invalidate the cache content
+
+        if 'reset' is passed, we detected a strip and the cache will have to be
+        recomputed.
+
+        Subclasses MUST overide this method to actually affect the cache data.
+        """
+        if reset:
+            self._cachekey = self.emptykey if reset else None
+        else:
+            self._cachekey = None
+
+    @abc.abstractmethod
+    def load(self, repo):
+        """Load data from disk
+
+        Subclasses MUST restore the "cachekey" attribute while doing so.
+        """
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def _fetchupdatedata(self, repo):
+        """Check the source for possible changes and return necessary data
+
+        The return is a tree elements tuple: reset, data, cachekey
+
+        * reset: `True` when a strip is detected and cache need to be reset
+        * data: new data to take in account, actual type depends of the source
+        * cachekey: the cache key covering <data> and precious covered data
+        """
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def _updatesummary(self, data):
+        """return a small string to be included in debug output"""
+        raise NotImplementedError
+
+    # Useful "public" function (no need to override them)
+
+    def update(self, repo):
+        """update the cache with new repository data
+
+        The update will be incremental when possible"""
+        repo = repo.unfiltered()
+
+        # If we do not have any data, try loading from disk
+        if self._cachekey is None:
+            self.load(repo)
+
+        reset, data, newkey = self._fetchupdatedata(repo)
+        if newkey == self._cachekey:
+            return
+        if reset or self._cachekey is None:
+            repo.ui.log('cache', 'strip detected, %s cache reset\n'
+                        % self._cachename)
+            self.clear(reset=True)
+
+        starttime = timer()
+        self._updatefrom(repo, data)
+        duration = timer() - starttime
+        summary = self._updatesummary(data)
+        repo.ui.log('cache', 'updated %s in %.4f seconds (%s)\n',
+                    self._cachename, duration, summary)
+
+        self._cachekey = newkey
+
+    def _serializecachekey(self):
+        """provide a bytes version of the cachekey"""
+        return self._cachekeystruct.pack(*self._cachekey)
+
+    def _deserializecachekey(self, data):
+        """read the cachekey from bytes"""
+        return self._cachekeystruct.unpack(data)
+
+class changelogsourcebase(incrementalcachebase):
+    """an abstract class for cache sourcing data from the changelog
+
+    For this purpose it use a cache key covering changelog content.
+    The cache key parts are: (tiprev, tipnode)
+    """
+
+    __metaclass__ = abc.ABCMeta
+
+    # default key used for an empty cache
+    emptykey = (0, node.nullid)
+    _cachekeyspec = 'i20s'
+    _cachename = None # used for debug message
+
+    # Useful "public" function (no need to override them)
+
+    def _fetchchangelogdata(self, cachekey, cl):
+        """use a cachekey to fetch incremental data
+
+        Exists as its own method to help subclass to reuse it."""
+        tiprev = len(cl) - 1
+        tipnode = cl.node(tiprev)
+        newkey = (tiprev, tipnode)
+        tiprev = len(cl) - 1
+        if newkey == cachekey:
+            return False, [], newkey
+        keyrev, keynode = cachekey
+        if tiprev < keyrev or cl.node(keyrev) != keynode:
+            revs = ()
+            if len(cl):
+                revs = list(cl.revs(stop=tiprev))
+            return True, revs, newkey
+        else:
+            return False, list(cl.revs(start=keyrev + 1, stop=tiprev)), newkey
+
+    def _fetchupdatedata(self, repo):
+        return self._fetchchangelogdata(self._cachekey, repo.changelog)
+
+    def _updatesummary(self, data):
+        return '%ir' % len(data)
--- a/hgext3rd/evolve/metadata.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/metadata.py	Mon Dec 11 18:30:15 2017 +0100
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-__version__ = '7.0.2.dev'
+__version__ = '7.1.0.dev'
 testedwith = '4.1.3 4.2.3 4.3.2 4.4.1'
 minimumhgversion = '4.1'
 buglink = 'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obsdiscovery.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/obsdiscovery.py	Mon Dec 11 18:30:15 2017 +0100
@@ -273,7 +273,7 @@
     rangelength = repo.stablerange.rangelength
     depthrev = repo.stablerange.depthrev
     if opts['subranges']:
-        ranges = stablerange.subrangesclosure(repo, revs)
+        ranges = stablerange.subrangesclosure(repo, repo.stablerange, revs)
     else:
         ranges = [(r, 0) for r in revs]
     headers = ('rev', 'node', 'index', 'size', 'depth', 'obshash')
@@ -451,6 +451,7 @@
             newrevs.extend(revs)
             revs = newrevs
 
+        repo.depthcache.update(repo)
         # warm the cache for the new revs
         for r in revs:
             _obshashrange(repo, (r, 0))
--- a/hgext3rd/evolve/obshistory.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/obshistory.py	Mon Dec 11 18:30:15 2017 +0100
@@ -499,6 +499,11 @@
     fm.write('date', '(%s)', fm.formatdate(date),
              label="evolve.date")
 
+    # initial support for showing note
+    if metadata.get('note'):
+        fm.plain('\n    note: ')
+        fm.write('note', "%s", metadata['note'], label="evolve.note")
+
     # Patch display
     if opts.get('patch'):
         _patchavailable = patchavailable(node, repo, marker)
@@ -787,8 +792,15 @@
         verb = 'split'
     return {'verb': verb}
 
+# Use a more advanced version of obsfateverb that uses effect-flag
+if util.safehasattr(obsutil, 'obsfateverb'):
+
+    @eh.wrapfunction(obsutil, 'obsfateverb')
+    def obsfateverb(orig, *args, **kwargs):
+        return _successorsetverb(*args, **kwargs)['verb']
+
 # Hijack callers of successorsetverb
-if util.safehasattr(obsutil, 'obsfateprinter'):
+elif util.safehasattr(obsutil, 'obsfateprinter'):
 
     @eh.wrapfunction(obsutil, 'obsfateprinter')
     def obsfateprinter(orig, successors, markers, ui):
--- a/hgext3rd/evolve/stablerange.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/evolve/stablerange.py	Mon Dec 11 18:30:15 2017 +0100
@@ -7,7 +7,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import collections
+import abc
 import heapq
 import math
 import os
@@ -16,8 +16,6 @@
 import weakref
 
 from mercurial import (
-    commands,
-    cmdutil,
     error,
     localrepo,
     node as nodemod,
@@ -29,10 +27,12 @@
 from mercurial.i18n import _
 
 from . import (
+    stablesort,
     exthelper,
 )
 
 eh = exthelper.exthelper()
+eh.merge(stablesort.eh)
 
 # prior to hg-4.2 there are not util.timer
 if util.safehasattr(util, 'timer'):
@@ -44,106 +44,6 @@
 else:
     timer = time.time
 
-##################################
-### Stable topological sorting ###
-##################################
-@eh.command(
-    'debugstablesort',
-    [
-        ('', 'rev', [], 'heads to start from'),
-    ] + commands.formatteropts,
-    _(''))
-def debugstablesort(ui, repo, **opts):
-    """display the ::REVS set topologically sorted in a stable way
-    """
-    revs = scmutil.revrange(repo, opts['rev'])
-    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
-    for r in stablesort(repo, revs):
-        ctx = repo[r]
-        displayer.show(ctx)
-        displayer.flush(ctx)
-    displayer.close()
-
-def stablesort(repo, revs, mergecallback=None):
-    """return '::revs' topologically sorted in "stable" order
-
-    This is a depth first traversal starting from 'nullrev', using node as a
-    tie breaker.
-    """
-    # Various notes:
-    #
-    # * Bitbucket is used dates as tie breaker, that might be a good idea.
-    #
-    # * It seemds we can traverse in the same order from (one) head to bottom,
-    #   if we the following record data for each merge:
-    #
-    #  - highest (stablesort-wise) common ancestors,
-    #  - order of parents (tablesort-wise)
-    cl = repo.changelog
-    parents = cl.parentrevs
-    nullrev = nodemod.nullrev
-    n = cl.node
-    # step 1: We need a parents -> children mapping for 2 reasons.
-    #
-    # * we build the order from nullrev to tip
-    #
-    # * we need to detect branching
-    children = collections.defaultdict(list)
-    for r in cl.ancestors(revs, inclusive=True):
-        p1, p2 = parents(r)
-        children[p1].append(r)
-        if p2 != nullrev:
-            children[p2].append(r)
-    # step two: walk back up
-    # * pick lowest node in case of branching
-    # * stack disregarded part of the branching
-    # * process merge when both parents are yielded
-
-    # track what changeset has been
-    seen = [0] * (max(revs) + 2)
-    seen[-1] = True # nullrev is known
-    # starts from repository roots
-    # reuse the list form the mapping as we won't need it again anyway
-    stack = children[nullrev]
-    if not stack:
-        return []
-    if 1 < len(stack):
-        stack.sort(key=n, reverse=True)
-
-    # list of rev, maybe we should yield, but since we built a children mapping we are 'O(N)' already
-    result = []
-
-    current = stack.pop()
-    while current is not None or stack:
-        if current is None:
-            # previous iteration reached a merge or an unready merge,
-            current = stack.pop()
-            if seen[current]:
-                current = None
-                continue
-        p1, p2 = parents(current)
-        if not (seen[p1] and seen[p2]):
-            # we can't iterate on this merge yet because other child is not
-            # yielded yet (and we are topo sorting) we can discard it for now
-            # because it will be reached from the other child.
-            current = None
-            continue
-        assert not seen[current]
-        seen[current] = True
-        result.append(current) # could be yield, cf earlier comment
-        if mergecallback is not None and p2 != nullrev:
-            mergecallback(result, current)
-        cs = children[current]
-        if not cs:
-            current = None
-        elif 1 == len(cs):
-            current = cs[0]
-        else:
-            cs.sort(key=n, reverse=True)
-            current = cs.pop() # proceed on smallest
-            stack.extend(cs)   # stack the rest for later
-    assert len(result) == len(set(result))
-    return result
 
 #################################
 ### Stable Range computation  ###
@@ -153,12 +53,12 @@
     """return highest power of two lower than 'i'"""
     return 2 ** int(math.log(i - 1, 2))
 
-def subrangesclosure(repo, heads):
+def subrangesclosure(repo, stablerange, heads):
     """set of all standard subrange under heads
 
     This is intended for debug purposes. Range are returned from largest to
     smallest in terms of number of revision it contains."""
-    subranges = repo.stablerange.subranges
+    subranges = stablerange.subranges
     toproceed = [(r, 0, ) for r in heads]
     ranges = set(toproceed)
     while toproceed:
@@ -169,16 +69,25 @@
                 toproceed.append(r)
     ranges = list(ranges)
     n = repo.changelog.node
-    rangelength = repo.stablerange.rangelength
+    rangelength = stablerange.rangelength
     ranges.sort(key=lambda r: (-rangelength(repo, r), n(r[0])))
     return ranges
 
+_stablerangemethodmap = {
+    'branchpoint': lambda repo: repo.stablerange,
+    'basic-branchpoint': lambda repo: stablerangebasic(),
+    'basic-mergepoint': lambda repo: stablerangedummy_mergepoint(),
+    'mergepoint': lambda repo: stablerange_mergepoint(),
+}
+
 @eh.command(
     'debugstablerange',
     [
-        ('', 'rev', [], 'operate on (rev, 0) ranges for rev in REVS'),
+        ('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"')
     ],
     _(''))
 def debugstablerange(ui, repo, **opts):
@@ -189,20 +98,8 @@
     """
     short = nodemod.short
     revs = scmutil.revrange(repo, opts['rev'])
-    # prewarm depth cache
-    unfi = repo.unfiltered()
-    node = unfi.changelog.node
-    stablerange = unfi.stablerange
-    depth = stablerange.depthrev
-    length = stablerange.rangelength
-    subranges = stablerange.subranges
     if not revs:
         raise error.Abort('no revisions specified')
-    repo.stablerange.warmup(repo, max(revs))
-    if opts['subranges']:
-        ranges = subrangesclosure(repo, revs)
-    else:
-        ranges = [(r, 0) for r in revs]
     if ui.verbose:
         template = '%s-%d (%d, %d, %d)'
 
@@ -222,6 +119,25 @@
                 short(node(rangeid[0])),
                 rangeid[1],
             )
+    # prewarm depth cache
+    unfi = repo.unfiltered()
+    node = unfi.changelog.node
+
+    method = opts['method']
+    getstablerange = _stablerangemethodmap.get(method)
+    if getstablerange is None:
+        raise error.Abort('unknown stable sort method: "%s"' % method)
+
+    stablerange = getstablerange(unfi)
+    depth = stablerange.depthrev
+    length = stablerange.rangelength
+    subranges = stablerange.subranges
+    stablerange.warmup(repo, max(revs))
+
+    if opts['subranges']:
+        ranges = subrangesclosure(unfi, stablerange, revs)
+    else:
+        ranges = [(r, 0) for r in revs]
 
     for r in ranges:
         subs = subranges(unfi, r)
@@ -241,14 +157,181 @@
         else:
             ui.status('%s - %s\n' % (rstr, subsstr))
 
-class stablerange(object):
+class abstractstablerange(object):
+    """The official API for a stablerange"""
+
+    __metaclass__ = abc.ABCMeta
+
+    @abc.abstractmethod
+    def subranges(self, repo, rangeid):
+        """return the stable sub-ranges of a rangeid"""
+        raise NotImplemented()
+
+    @abc.abstractmethod
+    def revsfromrange(self, repo, rangeid):
+        """return revision contained in a range"""
+        raise NotImplemented()
+
+    @abc.abstractmethod
+    def depthrev(self, repo, rev):
+        """depth a revision"""
+        # Exist to allow basic implementation to ignore the depthcache
+        # Could be demoted to _depthrev.
+        raise NotImplemented()
+
+    @abc.abstractmethod
+    def warmup(self, repo, upto=None):
+        """warmup the stable range cache"""
+        raise NotImplemented()
+
+    @abc.abstractmethod
+    def rangelength(self, repo, rangeid):
+        """number of revision in <range>"""
+        raise NotImplemented()
+
+    def _slicepoint(self, repo, rangeid):
+        """find the standard slicing point for a range"""
+        rangedepth = self.depthrev(repo, rangeid[0])
+        step = _hlp2(rangedepth)
+        standard_start = 0
+        while standard_start < rangeid[1] and 0 < step:
+            if standard_start + step < rangedepth:
+                standard_start += step
+            step //= 2
+        if rangeid[1] == standard_start:
+            slicesize = _hlp2(self.rangelength(repo, rangeid))
+            slicepoint = rangeid[1] + slicesize
+        else:
+            assert standard_start < rangedepth
+            slicepoint = standard_start
+        return slicepoint
+
+class stablerangebasic(abstractstablerange):
+    """a very dummy implementation of stablerange
+
+    the implementation is here to lay down the basic algorithm in the stable
+    range in a inefficient but easy to read manners. It should be used by test
+    to validate output."""
+
+    __metaclass__ = abc.ABCMeta
+
+    def _sortfunction(self, repo, headrev):
+        return stablesort.stablesort_branchpoint(repo, [headrev])
+
+    def warmup(self, repo, upto=None):
+        # no cache to warm for basic implementation
+        pass
+
+    def depthrev(self, repo, rev):
+        """depth a revision"""
+        return len(repo.revs('::%d', rev))
+
+    def revsfromrange(self, repo, rangeid):
+        """return revision contained in a range
+
+        The range `(<head>, <skip>)` contains all revisions stable-sorted from
+        <head>, skipping the <index>th lower revisions.
+        """
+        headrev, index = rangeid[0], rangeid[1]
+        revs = self._sortfunction(repo, headrev)
+        return revs[index:]
+
+    def rangelength(self, repo, rangeid):
+        """number of revision in <range>"""
+        return len(self.revsfromrange(repo, rangeid))
+
+    def subranges(self, repo, rangeid):
+        """return the stable sub-ranges of a rangeid"""
+        headrev, index = rangeid[0], rangeid[1]
+        if self.rangelength(repo, rangeid) == 1:
+            return []
+        slicepoint = self._slicepoint(repo, rangeid)
+
+        # search for range defining the lower set of revision
+        #
+        # we walk the lower set from the top following the stable order of the
+        # current "head" of the lower range.
+        #
+        # As soon as the revision in the lowerset diverges from the one in the
+        # range being generated, we emit the range and start a new one.
+        result = []
+        lowerrevs = self.revsfromrange(repo, rangeid)[:(slicepoint - index)]
+        head = None
+        headrange = None
+        skip = None
+        for rev in lowerrevs[::-1]:
+            if head is None:
+                head = rev
+                headrange = self.revsfromrange(repo, (head, 0))
+                skip = self.depthrev(repo, rev) - 1
+            elif rev != headrange[skip - 1]:
+                result.append((head, skip))
+                head = rev
+                headrange = self.revsfromrange(repo, (head, 0))
+                skip = self.depthrev(repo, rev) - 1
+            else:
+                skip -= 1
+        result.append((head, skip))
+
+        result.reverse()
+
+        # top part is trivial
+        top = (headrev, slicepoint)
+        result.append(top)
+
+        # double check the result
+        initialrevs = self.revsfromrange(repo, rangeid)
+        subrangerevs = sum((self.revsfromrange(repo, sub) for sub in result),
+                           [])
+        assert initialrevs == subrangerevs
+        return result
+
+class stablerangedummy_mergepoint(stablerangebasic):
+    """a very dummy implementation of stablerange use 'mergepoint' sorting
+    """
+
+    def _sortfunction(self, repo, headrev):
+        return stablesort.stablesort_mergepoint_head_basic(repo, [headrev])
+
+class stablerangecached(abstractstablerange):
+    """an implementation of stablerange using caching"""
+
+    __metaclass__ = abc.ABCMeta
+
+    def depthrev(self, repo, rev):
+        return repo.depthcache.get(rev)
+
+    def rangelength(self, repo, rangeid):
+        """number of revision in <range>"""
+        headrev, index = rangeid[0], rangeid[1]
+        return self.depthrev(repo, headrev) - index
+
+class stablerange_mergepoint(stablerangecached, stablerangebasic):
+    """Stablerange implementation using 'mergepoint' based sorting
+    """
+
+    def __init__(self):
+        self._sortcache = stablesort.stablesortcache()
+        super(stablerange_mergepoint, self).__init__()
+
+    def _sortfunction(self, repo, headrev):
+        return self._sortcache.get(repo, headrev)
+
+    def revsfromrange(self, repo, rangeid):
+        """return revision contained in a range
+
+        The range `(<head>, <skip>)` contains all revisions stable-sorted from
+        <head>, skipping the <index>th lower revisions.
+        """
+        limit = self.rangelength(repo, rangeid)
+        return self._sortcache.get(repo, rangeid[0], limit=limit)
+
+class stablerange(stablerangecached):
 
     def __init__(self, lrusize=2000):
         # The point up to which we have data in cache
         self._tiprev = None
         self._tipnode = None
-        # cache the 'depth' of a changeset, the size of '::rev'
-        self._depthcache = {}
         # cache the standard stable subranges or a range
         self._subrangescache = {}
         # To slices merge, we need to walk their descendant in reverse stable
@@ -271,6 +354,7 @@
     def warmup(self, repo, upto=None):
         """warm the cache up"""
         repo = repo.unfiltered()
+        repo.depthcache.update(repo)
         cl = repo.changelog
         # subrange should be warmed from head to range to be able to benefit
         # from revsfromrange cache. otherwise each merge will trigger its own
@@ -337,44 +421,6 @@
         repo.ui.log('evoext-cache', 'updated stablerange cache in %.4f seconds\n',
                     duration)
 
-    def depthrev(self, repo, rev):
-        repo = repo.unfiltered()
-        cl = repo.changelog
-        depth = self._getdepth
-        nullrev = nodemod.nullrev
-        stack = [rev]
-        while stack:
-            revdepth = None
-            current = stack[-1]
-            revdepth = depth(current)
-            if revdepth is not None:
-                stack.pop()
-                continue
-            p1, p2 = self._parents(current, cl.parentrevs)
-            if p1 == nullrev:
-                # root case
-                revdepth = 1
-            elif p2 == nullrev:
-                # linear commit case
-                parentdepth = depth(p1)
-                if parentdepth is None:
-                    stack.append(p1)
-                else:
-                    revdepth = parentdepth + 1
-            else:
-                # merge case
-                revdepth = self._depthmerge(cl, current, p1, p2, stack)
-            if revdepth is not None:
-                self._setdepth(current, revdepth)
-                stack.pop()
-        # actual_depth = len(list(cl.ancestors([rev], inclusive=True)))
-        # assert revdepth == actual_depth, (rev, revdepth, actual_depth)
-        return revdepth
-
-    def rangelength(self, repo, rangeid):
-        headrev, index = rangeid[0], rangeid[1]
-        return self.depthrev(repo, headrev) - index
-
     def subranges(self, repo, rangeid):
         cached = self._getsub(rangeid)
         if cached is not None:
@@ -399,8 +445,9 @@
             if allrevs is None:
                 allrevs = self._getrevsfrommerge(repo, headrev)
                 if allrevs is None:
-                    allrevs = stablesort(repo, [headrev],
-                                         mergecallback=self._filestablesortcache)
+                    mc = self._filestablesortcache
+                    sorting = stablesort.stablesort_branchpoint
+                    allrevs = sorting(repo, [headrev], mergecallback=mc)
                 self._stablesortcache[headrev] = allrevs
             # takes from index
             revs = allrevs[index:]
@@ -415,18 +462,6 @@
             self._parentscache[rev] = parents
         return parents
 
-    def _getdepth(self, rev):
-        """utility function used to access the depth cache
-
-        This mostly exist to help the on disk persistence."""
-        return self._depthcache.get(rev)
-
-    def _setdepth(self, rev, value):
-        """utility function used to set the depth cache
-
-        This mostly exist to help the on disk persistence."""
-        self._depthcache[rev] = value
-
     def _getsub(self, rev):
         """utility function used to access the subranges cache
 
@@ -489,63 +524,18 @@
             expected = len(revs) - 1
             # Since we do warmup properly, we can expect the cache to be hot
             # for everythin under the merge we investigate
-            cache = self._depthcache
+            cache = repo.depthcache
             # note: we cannot do a binary search because element under the
             # inherited point might have mismatching depth because of inner
             # branching.
             for rev in i:
-                if cache[rev] == expected:
+                if cache.get(rev) == expected:
                     break
                 expected -= 1
             value = (expected - 1, rev)
             self._inheritancecache[merge] = value
         return value
 
-    def _depthmerge(self, cl, rev, p1, p2, stack):
-        # sub method to simplify the main 'depthrev' one
-        revdepth = None
-        depth = self._getdepth
-        depth_p1 = depth(p1)
-        depth_p2 = depth(p2)
-        missingparent = False
-        if depth_p1 is None:
-            stack.append(p1)
-            missingparent = True
-        if depth_p2 is None:
-            stack.append(p2)
-            missingparent = True
-        if missingparent:
-            return None
-        # computin depth of a merge
-        # XXX the common ancestors heads could be cached
-        ancnodes = cl.commonancestorsheads(cl.node(p1), cl.node(p2))
-        ancrevs = [cl.rev(a) for a in ancnodes]
-        anyunkown = False
-        ancdepth = []
-        for r in ancrevs:
-            d = depth(r)
-            if d is None:
-                anyunkown = True
-                stack.append(r)
-            ancdepth.append((r, d))
-        if anyunkown:
-            return None
-        if not ancrevs:
-            # unrelated branch, (no common root)
-            revdepth = depth_p1 + depth_p2 + 1
-        elif len(ancrevs) == 1:
-            # one unique branch point:
-            # we can compute depth without any walk
-            depth_anc = ancdepth[0][1]
-            revdepth = depth_p1 + (depth_p2 - depth_anc) + 1
-        else:
-            # multiple ancestors, we pick one that is
-            # * the deepest (less changeset outside of it),
-            # * lowest revs because more chance to have descendant of other "above"
-            anc, revdepth = max(ancdepth, key=lambda x: (x[1], -x[0]))
-            revdepth += len(cl.findmissingrevs(common=[anc], heads=[rev]))
-        return revdepth
-
     def _subranges(self, repo, rangeid):
         if self.rangelength(repo, rangeid) == 1:
             return []
@@ -592,22 +582,6 @@
         # look like we found a relevent parentrange with no cache yet
         return reurange
 
-    def _slicepoint(self, repo, rangeid):
-        rangedepth = self.depthrev(repo, rangeid[0])
-        step = _hlp2(rangedepth)
-        standard_start = 0
-        while standard_start < rangeid[1] and 0 < step:
-            if standard_start + step < rangedepth:
-                standard_start += step
-            step //= 2
-        if rangeid[1] == standard_start:
-            slicesize = _hlp2(self.rangelength(repo, rangeid))
-            slicepoint = rangeid[1] + slicesize
-        else:
-            assert standard_start < rangedepth
-            slicepoint = standard_start
-        return slicepoint
-
     def _slicesrangeat(self, repo, rangeid, globalindex):
         p1, p2 = self._parents(rangeid[0], repo.changelog.parentrevs)
         if p2 == nodemod.nullrev:
@@ -635,7 +609,6 @@
 
     def _slicesrangeatmerge(self, repo, rangeid, globalindex):
         localindex = globalindex - rangeid[1]
-        cl = repo.changelog
 
         result = []
         allrevs = self.revsfromrange(repo, rangeid)
@@ -646,49 +619,24 @@
             # revision we needs
             result.append((bottomrevs[-1], rangeid[1]))
         else:
-            parentrevs = cl.parentrevs
-            parents = self._parents
-            bheads = set(bottomrevs)
-            du = bheads.difference_update
-            reachableroots = repo.changelog.reachableroots
-            minrev = min(bottomrevs)
-            for r in bottomrevs:
-                du(parents(r, parentrevs))
-            for h in bheads:
-                # reachable roots is fast because is C
-                #
-                # It is worth noting that will use this kind of filtering from
-                # "h" multiple time in a warming run. So using "ancestors" and
-                # caching that should be faster. But python code filtering on
-                # the ancestors end up being slower.
-                hrevs = reachableroots(minrev, [h], bottomrevs, True)
-                start = self.depthrev(repo, h) - len(hrevs)
-                entry = (h, start)
-                result.append(entry)
+            head = None
+            headrange = None
+            skip = None
+            for rev in bottomrevs[::-1]:
+                if head is None:
+                    head = rev
+                    headrange = self.revsfromrange(repo, (head, 0))
+                    skip = self.depthrev(repo, rev) - 1
+                elif rev != headrange[skip - 1]:
+                    result.append((head, skip))
+                    head = rev
+                    headrange = self.revsfromrange(repo, (head, 0))
+                    skip = self.depthrev(repo, rev) - 1
+                else:
+                    skip -= 1
+            result.append((head, skip))
 
-            # Talking about python code being slow, the following code is an
-            # alternative implementation.
-            #
-            # It complexity is better since is does a single traversal on the
-            # bottomset. However since it is all python it end up being
-            # slower.
-            # I'm keeping it here as an inspiration for a future C version
-            # branches = []
-            # for current in reversed(bottomrevs):
-            #     ps = parents(current, parentrevs)
-            #     found = False
-            #     for brevs, bexpect in branches:
-            #         if current in bexpect:
-            #             found = True
-            #             brevs.append(current)
-            #             bexpect.discard(current)
-            #             bexpect.update(ps)
-            #     if not found:
-            #         branches.append(([current], set(ps)))
-            # for revs, __ in reversed(branches):
-            #     head = revs[0]
-            #     index = self.depthrev(repo, head) - len(revs)
-            #     result.append((head, index))
+            result.reverse()
 
         # top part is trivial
         top = (rangeid[0], globalindex)
@@ -704,7 +652,6 @@
                          tiprev        INTEGER NOT NULL,
                          tipnode       BLOB    NOT NULL
                         );""",
-    "CREATE TABLE depth(rev INTEGER NOT NULL PRIMARY KEY, depth INTEGER NOT NULL);",
     """CREATE TABLE range(rev INTEGER  NOT NULL,
                           idx INTEGER NOT NULL,
                           PRIMARY KEY(rev, idx));""",
@@ -719,19 +666,15 @@
     );""",
     "CREATE INDEX subranges_index ON subranges (suprev, supidx);",
     "CREATE INDEX range_index ON range (rev, idx);",
-    "CREATE INDEX depth_index ON depth (rev);"
 ]
 _newmeta = "INSERT INTO meta (schemaversion, tiprev, tipnode) VALUES (?,?,?);"
 _updatemeta = "UPDATE meta SET tiprev = ?, tipnode = ?;"
-_updatedepth = "INSERT INTO depth(rev, depth) VALUES (?,?);"
 _updaterange = "INSERT INTO range(rev, idx) VALUES (?,?);"
 _updatesubranges = """INSERT
                        INTO subranges(listidx, suprev, supidx, subrev, subidx)
                        VALUES (?,?,?,?,?);"""
 _queryexist = "SELECT name FROM sqlite_master WHERE type='table' AND name='meta';"
 _querymeta = "SELECT schemaversion, tiprev, tipnode FROM meta;"
-_querydepth = "SELECT depth FROM depth WHERE rev = ?;"
-_batchdepth = "SELECT rev, depth FROM depth;"
 _queryrange = "SELECT * FROM range WHERE (rev = ? AND idx = ?);"
 _querysubranges = """SELECT subrev, subidx
                      FROM subranges
@@ -740,20 +683,18 @@
 
 class sqlstablerange(stablerange):
 
-    _schemaversion = 0
+    _schemaversion = 1
 
     def __init__(self, repo):
         lrusize = repo.ui.configint('experimental', 'obshashrange.lru-size',
                                     2000)
         super(sqlstablerange, self).__init__(lrusize=lrusize)
         self._vfs = repo.vfs
-        self._path = repo.vfs.join('cache/evoext_stablerange_v0.sqlite')
+        self._path = repo.vfs.join('cache/evoext_stablerange_v1.sqlite')
         self._cl = repo.unfiltered().changelog # (okay to keep an old one)
         self._ondisktiprev = None
         self._ondisktipnode = None
-        self._unsaveddepth = {}
         self._unsavedsubranges = {}
-        self._fulldepth = False
 
     def warmup(self, repo, upto=None):
         self._con # make sure the data base is loaded
@@ -774,22 +715,6 @@
             repo.ui.warn('(cache will not be saved)\n')
             super(sqlstablerange, self).warmup(repo, upto)
 
-    def _getdepth(self, rev):
-        cache = self._depthcache
-        if rev not in cache and rev <= self._ondisktiprev and self._con is not None:
-            value = None
-            result = self._con.execute(_querydepth, (rev,)).fetchone()
-            if result is not None:
-                value = result[0]
-            # in memory caching of the value
-            cache[rev] = value
-        return cache.get(rev)
-
-    def _setdepth(self, rev, depth):
-        assert rev not in self._unsaveddepth
-        self._unsaveddepth[rev] = depth
-        super(sqlstablerange, self)._setdepth(rev, depth)
-
     def _getsub(self, rangeid):
         cache = self._subrangescache
         if rangeid not in cache and rangeid[0] <= self._ondisktiprev and self._con is not None:
@@ -806,10 +731,6 @@
         self._unsavedsubranges[rangeid] = value
         super(sqlstablerange, self)._setsub(rangeid, value)
 
-    def _inheritancepoint(self, *args, **kwargs):
-        self._loaddepth()
-        return super(sqlstablerange, self)._inheritancepoint(*args, **kwargs)
-
     def _db(self):
         try:
             util.makedirs(self._vfs.dirname(self._path))
@@ -845,7 +766,8 @@
 
     def _save(self, repo):
         repo = repo.unfiltered()
-        if not (self._unsavedsubranges or self._unsaveddepth):
+        repo.depthcache.save(repo)
+        if not self._unsavedsubranges:
             return # no new data
 
         if self._con is None:
@@ -884,26 +806,12 @@
             ]
             con.execute(_updatemeta, meta)
 
-        self._savedepth(con, repo)
         self._saverange(con, repo)
         con.commit()
         self._ondisktiprev = self._tiprev
         self._ondisktipnode = self._tipnode
-        self._unsaveddepth.clear()
         self._unsavedsubranges.clear()
 
-    def _savedepth(self, con, repo):
-        repo = repo.unfiltered()
-        data = self._unsaveddepth.items()
-        con.executemany(_updatedepth, data)
-
-    def _loaddepth(self):
-        """batch load all data about depth"""
-        if not (self._fulldepth or self._con is None):
-            result = self._con.execute(_batchdepth)
-            self._depthcache.update(result.fetchall())
-            self._fulldepth = True
-
     def _saverange(self, con, repo):
         repo = repo.unfiltered()
         data = []
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/stablesort.py	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,395 @@
+# Code dedicated to the computation of stable sorting
+#
+# These stable sorting are used stable ranges
+#
+# Copyright 2017 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import collections
+
+from mercurial import (
+    commands,
+    cmdutil,
+    error,
+    node as nodemod,
+    scmutil,
+)
+
+from mercurial.i18n import _
+
+from . import (
+    depthcache,
+    exthelper,
+)
+
+eh = exthelper.exthelper()
+eh.merge(depthcache.eh)
+
+def _mergepoint_tie_breaker(repo):
+    """the key use to tie break merge parent
+
+    Exists as a function to help playing with different approaches.
+
+    Possible other factor are:
+        * depth of node,
+        * number of exclusive merges,
+        * number of jump points.
+        * <insert-your-idea>
+    """
+    return repo.changelog.node
+
+@eh.command(
+    '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)')
+    ] + commands.formatteropts,
+    _(''))
+def debugstablesort(ui, repo, **opts):
+    """display the ::REVS set topologically sorted in a stable way
+    """
+    revs = scmutil.revrange(repo, opts['rev'])
+
+    method = opts['method']
+    sorting = _methodmap.get(method)
+    if sorting is None:
+        valid_method = ', '.join(sorted(_methodmap))
+        raise error.Abort('unknown sorting method: "%s"' % method,
+                          hint='pick one of: %s' % valid_method)
+
+    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
+    kwargs = {}
+    if opts['limit']:
+        kwargs['limit'] = int(opts['limit'])
+    for r in sorting(repo, revs, **kwargs):
+        ctx = repo[r]
+        displayer.show(ctx)
+        displayer.flush(ctx)
+    displayer.close()
+
+def stablesort_branchpoint(repo, revs, mergecallback=None):
+    """return '::revs' topologically sorted in "stable" order
+
+    This is a depth first traversal starting from 'nullrev', using node as a
+    tie breaker.
+    """
+    # Various notes:
+    #
+    # * Bitbucket is used dates as tie breaker, that might be a good idea.
+    #
+    # * It seemds we can traverse in the same order from (one) head to bottom,
+    #   if we the following record data for each merge:
+    #
+    #  - highest (stablesort-wise) common ancestors,
+    #  - order of parents (tablesort-wise)
+    cl = repo.changelog
+    parents = cl.parentrevs
+    nullrev = nodemod.nullrev
+    n = cl.node
+    # step 1: We need a parents -> children mapping for 2 reasons.
+    #
+    # * we build the order from nullrev to tip
+    #
+    # * we need to detect branching
+    children = collections.defaultdict(list)
+    for r in cl.ancestors(revs, inclusive=True):
+        p1, p2 = parents(r)
+        children[p1].append(r)
+        if p2 != nullrev:
+            children[p2].append(r)
+    # step two: walk back up
+    # * pick lowest node in case of branching
+    # * stack disregarded part of the branching
+    # * process merge when both parents are yielded
+
+    # track what changeset has been
+    seen = [0] * (max(revs) + 2)
+    seen[-1] = True # nullrev is known
+    # starts from repository roots
+    # reuse the list form the mapping as we won't need it again anyway
+    stack = children[nullrev]
+    if not stack:
+        return []
+    if 1 < len(stack):
+        stack.sort(key=n, reverse=True)
+
+    # list of rev, maybe we should yield, but since we built a children mapping we are 'O(N)' already
+    result = []
+
+    current = stack.pop()
+    while current is not None or stack:
+        if current is None:
+            # previous iteration reached a merge or an unready merge,
+            current = stack.pop()
+            if seen[current]:
+                current = None
+                continue
+        p1, p2 = parents(current)
+        if not (seen[p1] and seen[p2]):
+            # we can't iterate on this merge yet because other child is not
+            # yielded yet (and we are topo sorting) we can discard it for now
+            # because it will be reached from the other child.
+            current = None
+            continue
+        assert not seen[current]
+        seen[current] = True
+        result.append(current) # could be yield, cf earlier comment
+        if mergecallback is not None and p2 != nullrev:
+            mergecallback(result, current)
+        cs = children[current]
+        if not cs:
+            current = None
+        elif 1 == len(cs):
+            current = cs[0]
+        else:
+            cs.sort(key=n, reverse=True)
+            current = cs.pop() # proceed on smallest
+            stack.extend(cs)   # stack the rest for later
+    assert len(result) == len(set(result))
+    return result
+
+def stablesort_mergepoint_multirevs(repo, revs):
+    """return '::revs' topologically sorted in "stable" order
+
+    This is a depth first traversal starting from 'revs' (toward root), using node as a
+    tie breaker.
+    """
+    cl = repo.changelog
+    tiebreaker = _mergepoint_tie_breaker(repo)
+    if not revs:
+        return []
+    elif len(revs) == 1:
+        heads = list(revs)
+    else:
+        # keeps heads only
+        heads = sorted(repo.revs('heads(%ld::%ld)', revs, revs), key=tiebreaker)
+
+    results = []
+    while heads:
+        h = heads.pop()
+        if revs:
+            bound = cl.findmissingrevs(common=heads, heads=[h])
+        else:
+            bound = cl.ancestors([h], inclusive=True)
+        results.append(stablesort_mergepoint_bounded(repo, h, bound))
+    if len(results) == 1:
+        return results[0]
+    finalresults = []
+    for r in results[::-1]:
+        finalresults.extend(r)
+    return finalresults
+
+def stablesort_mergepoint_bounded(repo, head, revs):
+    """return 'revs' topologically sorted in "stable" order.
+
+    The 'revs' set MUST have 'head' as its one and unique head.
+    """
+    # Various notes:
+    #
+    # * Bitbucket is using dates as tie breaker, that might be a good idea.
+    cl = repo.changelog
+    parents = cl.parentrevs
+    nullrev = nodemod.nullrev
+    tiebreaker = _mergepoint_tie_breaker(repo)
+    # step 1: We need a parents -> children mapping to detect dependencies
+    children = collections.defaultdict(set)
+    parentmap = {}
+    for r in revs:
+        p1, p2 = parents(r)
+        children[p1].add(r)
+        if p2 != nullrev:
+            children[p2].add(r)
+            parentmap[r] = tuple(sorted((p1, p2), key=tiebreaker))
+        elif p1 != nullrev:
+            parentmap[r] = (p1,)
+        else:
+            parentmap[r] = ()
+    # step two: walk again,
+    stack = [head]
+    resultset = set()
+    result = []
+
+    def add(current):
+        resultset.add(current)
+        result.append(current)
+
+    while stack:
+        current = stack.pop()
+        add(current)
+        parents = parentmap[current]
+        for p in parents:
+            if 1 < len(children[p]) and not children[p].issubset(resultset):
+                # we need other children to be yield first
+                continue
+            if p in revs:
+                stack.append(p)
+
+    result.reverse()
+    assert len(result) == len(resultset)
+    return result
+
+def stablesort_mergepoint_head_basic(repo, revs, limit=None):
+    heads = repo.revs('heads(%ld)', revs)
+    if not heads:
+        return []
+    elif 2 < len(heads):
+        raise error.Abort('cannot use head based merging, %d heads found'
+                          % len(heads))
+    head = heads.first()
+    revs = stablesort_mergepoint_bounded(repo, head, repo.revs('::%d', head))
+    if limit is None:
+        return revs
+    return revs[-limit:]
+
+def stablesort_mergepoint_head_debug(repo, revs, limit=None):
+    heads = repo.revs('heads(%ld)', revs)
+    if not heads:
+        return []
+    elif 2 < len(heads):
+        raise error.Abort('cannot use head based merging, %d heads found'
+                          % len(heads))
+    head = heads.first()
+    revs = stablesort_mergepoint_head(repo, head)
+    if limit is None:
+        return revs
+    return revs[-limit:]
+
+def stablesort_mergepoint_head(repo, head):
+    """return '::rev' topologically sorted in "stable" order
+
+    This is a depth first traversal starting from 'rev' (toward root), using node as a
+    tie breaker.
+    """
+    cl = repo.changelog
+    parents = cl.parentrevs
+    tiebreaker = _mergepoint_tie_breaker(repo)
+
+    top = [head]
+    mid = []
+    bottom = []
+
+    ps = [p for p in parents(head) if p is not nodemod.nullrev]
+    while len(ps) == 1:
+        top.append(ps[0])
+        ps = [p for p in parents(ps[0]) if p is not nodemod.nullrev]
+    top.reverse()
+    if len(ps) == 2:
+        ps.sort(key=tiebreaker)
+
+        # get the part from the highest parent. This is the part that changes
+        mid_revs = repo.revs('only(%d, %d)', ps[1], ps[0])
+        if mid_revs:
+            mid = stablesort_mergepoint_bounded(repo, ps[1], mid_revs)
+
+        # And follow up with part othe parent we can inherit from
+        bottom_revs = cl.ancestors([ps[0]], inclusive=True)
+        bottom = stablesort_mergepoint_bounded(repo, ps[0], bottom_revs)
+
+    return bottom + mid + top
+
+def stablesort_mergepoint_head_cached(repo, revs, limit=None):
+    heads = repo.revs('heads(%ld)', revs)
+    if not heads:
+        return []
+    elif 2 < len(heads):
+        raise error.Abort('cannot use head based merging, %d heads found'
+                          % len(heads))
+    head = heads.first()
+    cache = stablesortcache()
+    return cache.get(repo, head, limit=limit)
+
+class stablesortcache(object):
+
+    def get(self, repo, rev, limit=None):
+        result = []
+        for r in self._revsfrom(repo, rev):
+            result.append(r)
+            if limit is not None and limit <= len(result):
+                break
+        result.reverse()
+        return result
+
+    def _revsfrom(self, repo, head):
+        tiebreaker = _mergepoint_tie_breaker(repo)
+        cl = repo.changelog
+        parentsfunc = cl.parentrevs
+
+        def parents(rev):
+            return [p for p in parentsfunc(rev) if p is not nodemod.nullrev]
+
+        current = head
+        previous_current = None
+
+        while current is not None:
+            assert current is not previous_current
+            yield current
+            previous_current = current
+
+            ps = parents(current)
+            if not ps:
+                current = None # break
+            if len(ps) == 1:
+                current = ps[0]
+            elif len(ps) == 2:
+                lower_parent, higher_parent = sorted(ps, key=tiebreaker)
+
+                for rev in self._process_exclusive_side(lower_parent,
+                                                        higher_parent,
+                                                        cl.findmissingrevs,
+                                                        parents,
+                                                        tiebreaker):
+                    yield rev
+
+                current = lower_parent
+
+    def _process_exclusive_side(self, lower, higher, findmissingrevs, parents, tiebreaker):
+
+        exclusive = findmissingrevs(common=[lower],
+                                    heads=[higher])
+
+        stack = []
+        seen = set()
+        children = collections.defaultdict(set)
+        if not exclusive:
+            current = None
+        else:
+            current = higher
+            bound = set(exclusive)
+            for r in exclusive:
+                for p in parents(r):
+                    children[p].add(r)
+
+        previous_current = None
+        while current is not None:
+            assert current is not previous_current
+            yield current
+            seen.add(current)
+            previous_current = current
+
+            ps = parents(current)
+
+            usable_parents = [p for p in ps
+                              if (p in bound and children[p].issubset(seen))]
+            if not usable_parents:
+                if stack:
+                    current = stack.pop()
+                else:
+                    current = None
+            elif len(usable_parents) == 1:
+                    current = usable_parents[0]
+            else:
+                lower_parent, higher_parent = sorted(usable_parents, key=tiebreaker)
+                stack.append(lower_parent)
+                current = higher_parent
+
+_methodmap = {
+    'branchpoint': stablesort_branchpoint,
+    'basic-mergepoint': stablesort_mergepoint_multirevs,
+    'basic-headstart': stablesort_mergepoint_head_basic,
+    'headstart': stablesort_mergepoint_head_debug,
+    'headcached': stablesort_mergepoint_head_cached,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/serverminitopic.py	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,226 @@
+"""enable a minimal verison of topic for server
+
+Non publishing repository will see topic as "branch:topic" in the branch field.
+
+In addition to adding the extensions, the feature must be manually enabled in the config:
+
+    [experimental]
+    server-mini-topic = yes
+"""
+import hashlib
+import contextlib
+
+from mercurial import (
+    branchmap,
+    context,
+    encoding,
+    extensions,
+    node,
+    registrar,
+    util,
+    wireproto,
+)
+
+if util.safehasattr(registrar, 'configitem'):
+
+    configtable = {}
+    configitem = registrar.configitem(configtable)
+    configitem('experimental', 'server-mini-topic',
+               default=False,
+    )
+
+def hasminitopic(repo):
+    """true if minitopic is enabled on the repository
+
+    (The value is cached on the repository)
+    """
+    enabled = getattr(repo, '_hasminitopic', None)
+    if enabled is None:
+        enabled = (repo.ui.configbool('experimental', 'server-mini-topic')
+                   and not repo.publishing())
+        repo._hasminitopic = enabled
+    return enabled
+
+### make topic visible though "ctx.branch()"
+
+class topicchangectx(context.changectx):
+    """a sunclass of changectx that add topic to the branch name"""
+
+    def branch(self):
+        branch = super(topicchangectx, self).branch()
+        if hasminitopic(self._repo) and self.phase():
+            topic = self._changeset.extra.get('topic')
+            if topic is not None:
+                topic = encoding.tolocal(topic)
+                branch = '%s:%s' % (branch, topic)
+        return branch
+
+### avoid caching topic data in rev-branch-cache
+
+class revbranchcacheoverlay(object):
+    """revbranch mixin that don't use the cache for non public changeset"""
+
+    def _init__(self, *args, **kwargs):
+        super(revbranchcacheoverlay, self).__init__(*args, **kwargs)
+        if 'branchinfo' in vars(self):
+            del self.branchinfo
+
+    def branchinfo(self, rev):
+        """return branch name and close flag for rev, using and updating
+        persistent cache."""
+        phase = self._repo._phasecache.phase(self, rev)
+        if phase:
+            ctx = self._repo[rev]
+            return ctx.branch(), ctx.closesbranch()
+        return super(revbranchcacheoverlay, self).branchinfo(rev)
+
+def reposetup(ui, repo):
+    """install a repo class with a special revbranchcache"""
+
+    if hasminitopic(repo):
+        repo = repo.unfiltered()
+
+        class minitopicrepo(repo.__class__):
+            """repository subclass that install the modified cache"""
+
+            def revbranchcache(self):
+                if self._revbranchcache is None:
+                    cache = super(minitopicrepo, self).revbranchcache()
+
+                    class topicawarerbc(revbranchcacheoverlay, cache.__class__):
+                        pass
+                    cache.__class__ = topicawarerbc
+                    if 'branchinfo' in vars(cache):
+                        del cache.branchinfo
+                    self._revbranchcache = cache
+                return self._revbranchcache
+
+        repo.__class__ = minitopicrepo
+
+### topic aware branch head cache
+
+def _phaseshash(repo, maxrev):
+    """uniq ID for a phase matching a set of rev"""
+    revs = set()
+    cl = repo.changelog
+    fr = cl.filteredrevs
+    nm = cl.nodemap
+    for roots in repo._phasecache.phaseroots[1:]:
+        for n in roots:
+            r = nm.get(n)
+            if r not in fr and r < maxrev:
+                revs.add(r)
+    key = node.nullid
+    revs = sorted(revs)
+    if revs:
+        s = hashlib.sha1()
+        for rev in revs:
+            s.update('%s;' % rev)
+        key = s.digest()
+    return key
+
+# needed to prevent reference used for 'super()' call using in branchmap.py to
+# no go into cycle. (yes, URG)
+_oldbranchmap = branchmap.branchcache
+
+@contextlib.contextmanager
+def oldbranchmap():
+    previous = branchmap.branchcache
+    try:
+        branchmap.branchcache = _oldbranchmap
+        yield
+    finally:
+        branchmap.branchcache = previous
+
+_publiconly = set([
+    'base',
+    'immutable',
+])
+
+def mighttopic(repo):
+    return hasminitopic(repo) and repo.filtername not in _publiconly
+
+class _topiccache(branchmap.branchcache): # combine me with branchmap.branchcache
+
+    def __init__(self, *args, **kwargs):
+        # super() call may fail otherwise
+        with oldbranchmap():
+            super(_topiccache, self).__init__(*args, **kwargs)
+        self.phaseshash = None
+
+    def copy(self):
+        """return an deep copy of the branchcache object"""
+        new = self.__class__(self, self.tipnode, self.tiprev, self.filteredhash,
+                             self._closednodes)
+        new.phaseshash = self.phaseshash
+        return new
+
+    def validfor(self, repo):
+        """Is the cache content valid regarding a repo
+
+        - False when cached tipnode is unknown or if we detect a strip.
+        - True when cache is up to date or a subset of current repo."""
+        valid = super(_topiccache, self).validfor(repo)
+        if not valid:
+            return False
+        elif not mighttopic(repo) and self.phaseshash is None:
+            # phasehash at None means this is a branchmap
+            # coming from a public only set
+            return True
+        else:
+            try:
+                valid = self.phaseshash == _phaseshash(repo, self.tiprev)
+                return valid
+            except IndexError:
+                return False
+
+    def write(self, repo):
+        # we expect (hope) mutable set to be small enough to be that computing
+        # it all the time will be fast enough
+        if not mighttopic(repo):
+            super(_topiccache, self).write(repo)
+
+    def update(self, repo, revgen):
+        """Given a branchhead cache, self, that may have extra nodes or be
+        missing heads, and a generator of nodes that are strictly a superset of
+        heads missing, this function updates self to be correct.
+        """
+        super(_topiccache, self).update(repo, revgen)
+        if mighttopic(repo):
+            self.phaseshash = _phaseshash(repo, self.tiprev)
+
+def wrapread(orig, repo):
+    # Avoiding to write cache for filter where topic applies is a good step,
+    # but we need to also avoid reading it. Existing branchmap cache might
+    # exists before the turned the feature on.
+    if mighttopic(repo):
+        return None
+    return orig(repo)
+
+# advertise topic capabilities
+
+def wireprotocaps(orig, repo, proto):
+    caps = orig(repo, proto)
+    if hasminitopic(repo):
+        caps.append('topics')
+    return caps
+
+# wrap the necessary bit
+
+def wrapclass(container, oldname, new):
+    old = getattr(container, oldname)
+    if not issubclass(old, new):
+        targetclass = new
+        # check if someone else already wrapped the class and handle that
+        if not issubclass(new, old):
+            class targetclass(new, old):
+                pass
+        setattr(container, oldname, targetclass)
+    current = getattr(container, oldname)
+    assert issubclass(current, new), (current, new, targetclass)
+
+def uisetup(ui):
+    wrapclass(context, 'changectx', topicchangectx)
+    wrapclass(branchmap, 'branchcache', _topiccache)
+    extensions.wrapfunction(branchmap, 'read', wrapread)
+    extensions.wrapfunction(wireproto, '_capabilities', wireprotocaps)
--- a/hgext3rd/topic/__init__.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/topic/__init__.py	Mon Dec 11 18:30:15 2017 +0100
@@ -174,13 +174,16 @@
               'topic.active': 'green',
              }
 
-__version__ = '0.5.2.dev'
+__version__ = '0.6.0.dev'
 
 testedwith = '4.1.3 4.2.3 4.3.3 4.4.1'
 minimumhgversion = '4.1'
 buglink = 'https://bz.mercurial-scm.org/'
 
 if util.safehasattr(registrar, 'configitem'):
+
+    from mercurial import configitems
+
     configtable = {}
     configitem = registrar.configitem(configtable)
 
@@ -199,6 +202,9 @@
     configitem('_internal', 'keep-topic',
                default=False,
     )
+    configitem('experimental', 'topic-mode.server',
+               default=configitems.dynamicdefault,
+    )
 
     def extsetup(ui):
         # register config that strictly belong to other code (thg, core, etc)
@@ -223,6 +229,7 @@
         return ''
     return self.extra().get(constants.extrakey, '')
 context.basectx.topic = _contexttopic
+
 def _contexttopicidx(self):
     topic = self.topic()
     if not topic:
@@ -318,6 +325,10 @@
 
     cmdutil.summaryhooks.add('topic', summaryhook)
 
+    # Wrap workingctx extra to return the topic name
+    extensions.wrapfunction(context.workingctx, '__init__', wrapinit)
+    # Wrap changelog.add to drop empty topic
+    extensions.wrapfunction(changelog.changelog, 'add', wrapadd)
 
 def reposetup(ui, repo):
     if not isinstance(repo, localrepo.localrepository):
@@ -421,7 +432,19 @@
                     origvalidator(tr2)
                 tr.validator = validator
 
-            if (repo.ui.configbool('experimental', 'topic.publish-bare-branch')
+            topicmodeserver = repo.ui.config('experimental',
+                                             'topic-mode.server', 'ignore')
+            ispush = (desc.startswith('push') or desc.startswith('serve'))
+            if (topicmodeserver != 'ignore' and ispush):
+                origvalidator = tr.validator
+
+                def validator(tr2):
+                    repo = reporef()
+                    flow.rejectuntopicedchangeset(repo, tr2)
+                    return origvalidator(tr2)
+                tr.validator = validator
+
+            elif (repo.ui.configbool('experimental', 'topic.publish-bare-branch')
                     and (desc.startswith('push')
                          or desc.startswith('serve'))
                     ):
@@ -467,10 +490,6 @@
         repo.names.addnamespace(namespaces.namespace(
             'topics', 'topic', namemap=_namemap, nodemap=_nodemap,
             listnames=lambda repo: repo.topics))
-    # Wrap workingctx extra to return the topic name
-    extensions.wrapfunction(context.workingctx, '__init__', wrapinit)
-    # Wrap changelog.add to drop empty topic
-    extensions.wrapfunction(changelog.changelog, 'add', wrapadd)
 
 def wrapinit(orig, self, repo, *args, **kwargs):
     orig(self, repo, *args, **kwargs)
--- a/hgext3rd/topic/flow.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/topic/flow.py	Mon Dec 11 18:30:15 2017 +0100
@@ -30,6 +30,32 @@
         nodes = [cl.node(r) for r in topublish]
         repo._phasecache.advanceboundary(repo, tr, phases.public, nodes)
 
+def rejectuntopicedchangeset(repo, tr):
+    """Reject the push if there are changeset without topic"""
+    if 'node' not in tr.hookargs: # no new revs
+        return
+
+    startnode = node.bin(tr.hookargs['node'])
+
+    mode = repo.ui.config('experimental', 'topic-mode.server', 'ignore')
+
+    untopiced = repo.revs('not public() and (%n:) - hidden() - topic()', startnode)
+    if untopiced:
+        num = len(untopiced)
+        fnode = repo[untopiced.first()].hex()[:10]
+        if num == 1:
+            msg = _("%s") % fnode
+        else:
+            msg = _("%s and %d more") % (fnode, num - 1)
+        if mode == 'warning':
+            fullmsg = _("pushed draft changeset without topic: %s\n")
+            repo.ui.warn(fullmsg % msg)
+        elif mode == 'enforce':
+            fullmsg = _("rejecting draft changesets: %s")
+            raise error.Abort(fullmsg % msg)
+        else:
+            repo.ui.warn(_("unknown 'topic-mode.server': %s\n" % mode))
+
 def wrappush(orig, repo, remote, *args, **kwargs):
     """interpret the --publish flag and pass it to the push operation"""
     newargs = kwargs.copy()
--- a/hgext3rd/topic/stack.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/hgext3rd/topic/stack.py	Mon Dec 11 18:30:15 2017 +0100
@@ -12,7 +12,11 @@
     obsolete,
     util,
 )
-from .evolvebits import builddependencies, _singlesuccessor
+from .evolvebits import (
+    _singlesuccessor,
+    MultipleSuccessorsError,
+    builddependencies,
+)
 
 short = node.short
 
@@ -285,7 +289,15 @@
         p1 = ctx.p1()
         p2 = ctx.p2()
         if p1.obsolete():
-            p1 = repo[_singlesuccessor(repo, p1)]
+            try:
+                p1 = repo[_singlesuccessor(repo, p1)]
+            except MultipleSuccessorsError as e:
+                successors = e.successorssets
+                if len(successors) > 1:
+                    # case of divergence which we don't handle yet
+                    raise
+                p1 = repo[successors[0][-1]]
+
         if p2.node() != node.nullid:
             entries.append((idxmap.get(p1.rev()), False, p1))
             entries.append((idxmap.get(p2.rev()), False, p2))
--- a/setup.py	Mon Dec 11 09:33:32 2017 +0100
+++ b/setup.py	Mon Dec 11 18:30:15 2017 +0100
@@ -19,6 +19,7 @@
     return get_metadata()['minimumhgversion']
 
 py_modules = [
+    'hgext3rd.serverminitopic',
 ]
 py_packages = [
     'hgext3rd',
--- a/tests/test-amend.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-amend.t	Mon Dec 11 18:30:15 2017 +0100
@@ -18,10 +18,18 @@
   $ hg branch foo
   marked working directory as branch foo
   (branches are permanent and global, did you want a bookmark?)
-  $ hg amend -d '0 0'
+  $ hg amend -d '0 0' -n "this a note on the obsmarker and supported for hg>=4.4"
+  current hg version does not support storing note in obsmarker
   $ hg debugobsolete
   07f4944404050f47db2e5c5071e0e84e7a27bba9 6a022cbb61d5ba0f03f98ff2d36319dfea1034ae 0 (*) {'ef1': '*', 'user': 'test'} (glob)
   b2e32ffb533cbe1d5759638c0cd4e8abc43b2738 0 {07f4944404050f47db2e5c5071e0e84e7a27bba9} (*) {'ef1': '*', 'user': 'test'} (glob)
+
+  $ hg obslog
+  @  6a022cbb61d5 (2) adda
+  |
+  x  07f494440405 (0) adda
+       rewritten(branch) as 6a022cbb61d5 by test (*) (glob)
+  
   $ hg branch
   foo
   $ hg branches
@@ -147,6 +155,7 @@
       --close-branch        mark a branch as closed, hiding it from the branch
                             list
    -s --secret              use the secret phase for committing
+   -n --note VALUE          store a note on amend
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -m --message TEXT        use text as commit message
--- a/tests/test-corrupt.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-corrupt.t	Mon Dec 11 18:30:15 2017 +0100
@@ -101,6 +101,7 @@
   
 
   $ hg prune --fold -n -1 -- -2 -3
+  current hg version does not support storing note in obsmarker
   2 changesets pruned
   $ hg push ../other
   pushing to ../other
--- a/tests/test-discovery-obshashrange.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-discovery-obshashrange.t	Mon Dec 11 18:30:15 2017 +0100
@@ -50,6 +50,8 @@
   * @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
   * @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (8r, 0o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (8r, 0o) (glob)
   * @0000000000000000000000000000000000000000 (*)> debugbuilddag .+7 exited 0 after *.?? seconds (glob)
@@ -92,6 +94,8 @@
   * @0000000000000000000000000000000000000000 (*)> debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 66f7d451a68b85ed82ff5fcc254daf50c74144bd (glob)
   * @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
   * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
   * @0000000000000000000000000000000000000000 (*)> debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 66f7d451a68b85ed82ff5fcc254daf50c74144bd exited 0 after *.?? seconds (glob)
@@ -102,6 +106,8 @@
   * @0000000000000000000000000000000000000000 (*)> debugobsolete cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d (glob)
   * @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
   * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 2o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
   * @0000000000000000000000000000000000000000 (*)> debugobsolete cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d exited 0 after *.?? seconds (glob)
@@ -112,6 +118,8 @@
   * @0000000000000000000000000000000000000000 (*)> debugobsolete eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd (glob)
   * @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
   * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 2o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
   * @0000000000000000000000000000000000000000 (*)> debugobsolete eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd exited 0 after *.?? seconds (glob)
@@ -153,6 +161,8 @@
   (run 'hg update' to get a working copy)
   $ hg -R ../server blackbox
   * @0000000000000000000000000000000000000000 (*)> debugobshashrange --subranges --rev tip (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated stablerange cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> debugobshashrange --subranges --rev tip exited 0 after *.?? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
@@ -168,6 +178,8 @@
   * @0000000000000000000000000000000000000000 (*)> pull --rev 4 (glob)
   * @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (5r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated stablerange cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (5r, 3o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (5r, 3o) (glob)
@@ -251,6 +263,7 @@
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (1r, 0o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated stablerange cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
@@ -313,15 +326,18 @@
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (1r, 0o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated served branch cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 0o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> commit -m foo exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete ffffffffffffffffffffffffffffffffffffffff 45f8b879de922f6a6e620ba04205730335b6fc7e (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete ffffffffffffffffffffffffffffffffffffffff 45f8b879de922f6a6e620ba04205730335b6fc7e exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> push -f --debug (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 0/5 mismatch - 1 obshashrange queries in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> push -f --debug exited 0 after *.?? seconds (glob)
@@ -424,6 +440,7 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (2r, 0o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated served branch cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (2r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (2r, 3o) (glob)
@@ -570,6 +587,7 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (1r, 0o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated served branch cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
@@ -611,6 +629,8 @@
   (run 'hg update' to get a working copy)
   $ hg blackbox
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-obshashrange cache reset (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (8r, 12o) (glob)
@@ -619,6 +639,7 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 0/8 mismatch - 1 obshashrange queries in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-obscache cache reset (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (9r, 12o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
@@ -728,9 +749,12 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip -r desc("foo") (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> saved backup bundle to $TESTTMP/client/.hg/strip-backup/45f8b879de92-94c82517-backup.hg (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obshashrange cache reset (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-depthcache cache reset (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (5r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (5r, 13o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obscache cache reset (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (5r, 13o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (3r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated stablerange cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (3r, 0o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (3r, 0o) (glob)
@@ -744,6 +768,7 @@
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> pull (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> obsdiscovery, 0/8 mismatch - 1 obshashrange queries in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (1r, 0o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated stablerange cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 0o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated served branch cache in *.???? seconds (glob)
--- a/tests/test-evolve-obshistory-complex.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-evolve-obshistory-complex.t	Mon Dec 11 18:30:15 2017 +0100
@@ -71,9 +71,21 @@
   4 new unstable changesets
   $ hg fold --exact -r 3 -r 4 --date "0 0" -m "fold1"
   2 changesets folded
-  $ hg fold --exact -r 5 -r 6 --date "0 0" -m "fold2"
+  $ hg fold --exact -r 5 -r 6 --date "0 0" -m "fold2" -n "folding changesets to test"
+  current hg version does not support storing note in obsmarker
   2 changesets folded
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg obslog -r .
+  @    100cc25b765f (9) fold2
+  |\
+  x |  0da815c333f6 (5) E
+   /     rewritten(description, content) as 100cc25b765f by test (*) (glob)
+  |        note: folding changesets to test
+  |
+  x  d9f908fde1a1 (6) F
+       rewritten(description, parent, content) as 100cc25b765f by test (*) (glob)
+         note: folding changesets to test
+  
   $ hg log -G 
   @  changeset:   9:100cc25b765f
   |  tag:         tip
@@ -297,7 +309,8 @@
 
   $ hg prune -s 12 -r 11
   1 changesets pruned
-  $ hg prune -s 14 -r 13
+  $ hg prune -s 14 -r 13 -n "this is a note stored in obsmarker in prune"
+  current hg version does not support storing note in obsmarker
   1 changesets pruned
   $ hg log -G
   @  changeset:   15:d4a000f63ee9
@@ -398,12 +411,14 @@
   | | | | | |
   | +-------x  d0f33db50670 (13) fold1
   | | | | |      rewritten(description, parent, content) as ec31316faa9d by test (*) (glob)
+  | | | | |        note: this is a note stored in obsmarker in prune
   | | | | |
   +---x | |  e036916b63ea (11) fold0
   | |  / /     rewritten(description, parent, content) as 7b3290f6e0a0 by test (*) (glob)
   | | | |
   | | x |  0da815c333f6 (5) E
   | |  /     rewritten(description, content) as 100cc25b765f by test (*) (glob)
+  | | |        note: folding changesets to test
   | | |
   x | |    b868bc49b0a4 (7) fold0
   |\ \ \     rewritten(parent, content) as 19e14c8397fc, e036916b63ea by test (*) (glob)
@@ -413,6 +428,7 @@
   | | | | |
   | | | | x  d9f908fde1a1 (6) F
   | | | |      rewritten(description, parent, content) as 100cc25b765f by test (*) (glob)
+  | | | |        note: folding changesets to test
   | | | |
   x | | |  2a34000d3544 (1) A
    / / /     rewritten(description, content) as b868bc49b0a4 by test (*) (glob)
--- a/tests/test-evolve-obshistory.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-evolve-obshistory.t	Mon Dec 11 18:30:15 2017 +0100
@@ -312,7 +312,7 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     ROOT
   
-  $ hg split -r 'desc(A0)' -d "0 0" << EOF
+  $ hg split -r 'desc(A0)' -n "testing split" -d "0 0" << EOF
   > y
   > y
   > n
@@ -320,6 +320,7 @@
   > y
   > y
   > EOF
+  current hg version does not support storing note in obsmarker
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   adding a
   adding b
@@ -377,6 +378,7 @@
   $ hg obslog 471597cad322 --hidden --patch
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
   $ hg obslog 471597cad322 --hidden --no-graph -Tjson | python -m json.tool
@@ -392,6 +394,7 @@
                       "parent",
                       "content"
                   ],
+                  "note": "testing split",
                   "succnodes": [
                       "337fec4d2edc",
                       "f257fde29c7a"
@@ -412,6 +415,7 @@
   |
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 With the all option, it should show the three changesets
@@ -422,6 +426,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 Check that debugobshistory on the second successor after split show
@@ -431,6 +436,7 @@
   |
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 With the all option, it should show the three changesets
@@ -441,6 +447,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 Obslog with all option all should also works on the splitted commit
@@ -451,6 +458,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 Check that debugobshistory on both successors after split show
@@ -462,6 +470,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
   $ hg update 471597cad322
--- a/tests/test-metaedit.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-metaedit.t	Mon Dec 11 18:30:15 2017 +0100
@@ -137,7 +137,13 @@
   abort: editing multiple revisions without --fold is not currently supported
   [255]
 
-  $ HGEDITOR=cat hg metaedit '.^::.' --fold
+  $ HGEDITOR=cat hg metaedit '.^::.' --fold --note 'folding changesets using metaedit,
+  > and newlines'
+  current hg version does not support storing note in obsmarker
+  abort: note cannot contain a newline
+  [255]
+  $ HGEDITOR=cat hg metaedit '.^::.' --fold --note "folding changesets using metaedit"
+  current hg version does not support storing note in obsmarker
   HG: This is a fold of 2 changesets.
   HG: Commit message of changeset 7.
   
@@ -164,6 +170,21 @@
   |
   ~
 
+  $ hg obslog -r .
+  @    a08d35fd7d9d (10) E
+  |\
+  x |  212b2a2b87cd (9) F
+  | |    rewritten(description, user, parent, content) as a08d35fd7d9d by test (*) (glob)
+  | |      note: folding changesets using metaedit
+  | |
+  | x  c2bd843aa246 (7) E
+  |      rewritten(description, content) as a08d35fd7d9d by test (*) (glob)
+  |        note: folding changesets using metaedit
+  |
+  x  587528abfffe (8) F
+       rewritten(user) as 212b2a2b87cd by test (*) (glob)
+  
+
 no new commit is created here because the date is the same
   $ HGEDITOR=cat hg metaedit
   E
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-minitopic.t	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,238 @@
+  $ . $TESTDIR/testlib/common.sh
+
+setup
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > share=
+  > blackbox=
+  > [web]
+  > allow_push = *
+  > push_ssl = no
+  > [phases]
+  > publish = False
+  > [paths]
+  > enabled = http://localhost:$HGPORT/
+  > disabled = http://localhost:$HGPORT2/
+  > EOF
+
+  $ hg init ./server-enabled
+  $ cat >> server-enabled/.hg/hgrc << EOF
+  > [extensions]
+  > serverminitopic=
+  > [experimental]
+  > server-mini-topic = yes
+  > EOF
+
+  $ hg share ./server-enabled ./server-disabled
+  updating working directory
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat >> server-disabled/.hg/hgrc << EOF
+  > [extensions]
+  > serverminitopic=
+  > [experimental]
+  > server-mini-topic = no
+  > EOF
+
+  $ hg init client-disabled
+  $ hg init client-enabled
+  $ cat >> client-enabled/.hg/hgrc << EOF
+  > [extensions]
+  > topic=
+  > EOF
+
+  $ hg serve -R server-enabled -p $HGPORT -d --pid-file hg1.pid --errorlog hg1.error
+  $ cat hg1.pid > $DAEMON_PIDS
+  $ hg serve -R server-disabled -p $HGPORT2 -d --pid-file hg2.pid --errorlog hg2.error
+  $ cat hg2.pid >> $DAEMON_PIDS
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities | grep -o topics
+  topics
+  $ curl --silent http://localhost:$HGPORT2/?cmd=capabilities | grep -o topics
+  [1]
+
+Pushing first changesets to the servers
+--------------------------------------
+
+  $ cd client-enabled
+  $ mkcommit c_A0
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  $ mkcommit c_B0
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
+
+Pushing new head
+----------------
+
+  $ hg up 'desc("c_A0")'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit c_C0
+  created new head
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  abort: push creates new remote head 22c9514ed811!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: push creates new remote head 22c9514ed811!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  default 0ab6d544d0efd629fda056601cfe95e73d1af210
+  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  default 0ab6d544d0efd629fda056601cfe95e73d1af210
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
+
+Pushing new topic
+-----------------
+
+  $ hg merge
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ mkcommit c_D0
+  $ hg log -G
+  @    changeset:   3:9c660cf97499
+  |\   tag:         tip
+  | |  parent:      2:22c9514ed811
+  | |  parent:      1:0ab6d544d0ef
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_D0
+  | |
+  | o  changeset:   2:22c9514ed811
+  | |  parent:      0:14faebcf9752
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_C0
+  | |
+  o |  changeset:   1:0ab6d544d0ef
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     c_B0
+  |
+  o  changeset:   0:14faebcf9752
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     c_A0
+  
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 2 changesets with 2 changes to 2 files
+  $ hg up 'desc("c_C0")'
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg topic topic_A
+  marked working directory as topic: topic_A
+  $ mkcommit c_E0
+  active topic 'topic_A' grew its first changeset
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: push creates new remote head f31af349535e!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files (+1 heads)
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  default 9c660cf97499ae01ccb6894880455c6ffa4b19cf
+  default%3Atopic_A f31af349535e413b6023f11b51a6afccf4139180
+  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  default 9c660cf97499ae01ccb6894880455c6ffa4b19cf f31af349535e413b6023f11b51a6afccf4139180
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
+
+Pushing new head to a topic
+---------------------------
+
+  $ hg up 'desc("c_D0")'
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg topic topic_A
+  marked working directory as topic: topic_A
+  $ mkcommit c_F0
+  $ hg log -G
+  @  changeset:   5:82c5842e0472
+  |  tag:         tip
+  |  topic:       topic_A
+  |  parent:      3:9c660cf97499
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c_F0
+  |
+  | o  changeset:   4:f31af349535e
+  | |  topic:       topic_A
+  | |  parent:      2:22c9514ed811
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_E0
+  | |
+  o |  changeset:   3:9c660cf97499
+  |\|  parent:      2:22c9514ed811
+  | |  parent:      1:0ab6d544d0ef
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_D0
+  | |
+  | o  changeset:   2:22c9514ed811
+  | |  parent:      0:14faebcf9752
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_C0
+  | |
+  o |  changeset:   1:0ab6d544d0ef
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     c_B0
+  |
+  o  changeset:   0:14faebcf9752
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     c_A0
+  
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  abort: push creates new remote head 82c5842e0472 on branch 'default:topic_A'!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  default 9c660cf97499ae01ccb6894880455c6ffa4b19cf
+  default%3Atopic_A f31af349535e413b6023f11b51a6afccf4139180 82c5842e047215160763f81ae93ae42c65b20a63
+  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  default f31af349535e413b6023f11b51a6afccf4139180 82c5842e047215160763f81ae93ae42c65b20a63
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
--- a/tests/test-prev-next.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-prev-next.t	Mon Dec 11 18:30:15 2017 +0100
@@ -4,7 +4,7 @@
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
 
-hg prev -B should move active bookmark
+hg prev & next move to parent/child
   $ hg init test-repo
   $ cd test-repo
   $ touch a
@@ -13,6 +13,18 @@
   $ touch b
   $ hg add b
   $ hg commit -m 'added b'
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [0] added a
+  $ hg next
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  [1] added b
+
+hg prev & next respect --quiet
+  $ hg prev -q
+  $ hg next -q
+
+hg prev -B should move active bookmark
   $ hg bookmark mark
   $ hg bookmarks
    * mark                      1:6e742c9127b3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-stablerange-branchpoint.t	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,731 @@
+Test for stable ordering capabilities
+=====================================
+
+  $ . $TESTDIR/testlib/pythonpath.sh
+
+  $ cat << EOF >> $HGRCPATH
+  > [extensions]
+  > hgext3rd.evolve =
+  > [ui]
+  > logtemplate = "{rev} {node|short} {desc} {tags}\n"
+  > [defaults]
+  > debugstablerange = --method branchpoint
+  > EOF
+
+Simple linear test
+==================
+
+  $ hg init repo_linear
+  $ cd repo_linear
+  $ hg debugbuilddag '.+6'
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  c8d03c1b5e94 6
+  f69452c5b1af 7
+  $ hg debugstablerange --verify --verbose --subranges --rev 1
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 1 > 1.range
+
+bigger subset reuse most of the previous one
+
+  $ hg debugstablerange --verify --verbose --subranges --rev 4
+  bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 4 > 4.range
+  $ diff -u 1.range 4.range
+  --- 1.range	* (glob)
+  +++ 4.range	* (glob)
+  @@ -1,3 +1,9 @@
+  +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  +01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  +2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  +bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  [1]
+
+Using a range not ending on 2**N boundary
+we fall back on 2**N as much as possible
+
+  $ hg debugstablerange --verify --verbose --subranges --rev 5
+  c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  c8d03c1b5e94-5 (5, 6, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 5 > 5.range
+  $ diff -u 4.range 5.range
+  --- 4.range	* (glob)
+  +++ 5.range	* (glob)
+  @@ -1,9 +1,11 @@
+  -bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  +c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2)
+   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  +c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1)
+   01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+   2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+   bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  +c8d03c1b5e94-5 (5, 6, 1) [leaf] - 
+  [1]
+
+Even two unperfect range overlap a lot
+
+  $ hg debugstablerange --verify --verbose --subranges --rev tip
+  f69452c5b1af-0 (6, 7, 7) [complete] - 2dc09a01254d-0 (3, 4, 4), f69452c5b1af-4 (6, 7, 3)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  f69452c5b1af-4 (6, 7, 3) [complete] - c8d03c1b5e94-4 (5, 6, 2), f69452c5b1af-6 (6, 7, 1)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  c8d03c1b5e94-5 (5, 6, 1) [leaf] - 
+  f69452c5b1af-6 (6, 7, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev tip > tip.range
+  $ diff -u 5.range tip.range
+  --- 5.range	* (glob)
+  +++ tip.range	* (glob)
+  @@ -1,5 +1,6 @@
+  -c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2)
+  +f69452c5b1af-0 (6, 7, 7) [complete] - 2dc09a01254d-0 (3, 4, 4), f69452c5b1af-4 (6, 7, 3)
+   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +f69452c5b1af-4 (6, 7, 3) [complete] - c8d03c1b5e94-4 (5, 6, 2), f69452c5b1af-6 (6, 7, 1)
+   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+   c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1)
+  @@ -9,3 +10,4 @@
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+   bebd167eb94d-4 (4, 5, 1) [leaf] - 
+   c8d03c1b5e94-5 (5, 6, 1) [leaf] - 
+  +f69452c5b1af-6 (6, 7, 1) [leaf] - 
+  [1]
+
+  $ cd ..
+
+Case with merge
+===============
+
+Simple case: branching is on a boundary
+--------------------------------------------
+
+  $ hg init repo_merge_split_on_boundary
+  $ cd repo_merge_split_on_boundary
+  $ hg debugbuilddag '.:base
+  > +3:left
+  > <base+3:right
+  > <left/right:merge
+  > +2:head
+  > '
+  $ hg log -G
+  o  9 0338daf18215 r9 head tip
+  |
+  o  8 71b32fcf3f71 r8
+  |
+  o    7 5f18015f9110 r7 merge
+  |\
+  | o  6 a2f58e9c1e56 r6 right
+  | |
+  | o  5 3a367db1fabc r5
+  | |
+  | o  4 e7bd5218ca15 r4
+  | |
+  o |  3 2dc09a01254d r3 left
+  | |
+  o |  2 01241442b3c2 r2
+  | |
+  o |  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0 base
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  e7bd5218ca15 2
+  3a367db1fabc 3
+  a2f58e9c1e56 4
+  5f18015f9110 8
+  71b32fcf3f71 9
+  0338daf18215 10
+
+Each of the linear branch reuse range internally
+
+(left branch)
+
+  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2'
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' > left-2.range
+  $ hg debugstablerange --verify --verbose --subranges --rev left
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'left' > left.range
+  $ diff -u left-2.range left.range
+  --- left-2.range	* (glob)
+  +++ left.range	* (glob)
+  @@ -1,3 +1,7 @@
+  +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  +01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  +2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  [1]
+
+(right branch)
+
+  $ hg debugstablerange --verify --verbose --subranges --rev right~2
+  e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  e7bd5218ca15-1 (4, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'right~2' > right-2.range
+  $ hg debugstablerange --verify --verbose --subranges --rev right
+  a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2)
+  a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1)
+  e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  3a367db1fabc-2 (5, 3, 1) [leaf] - 
+  a2f58e9c1e56-3 (6, 4, 1) [leaf] - 
+  e7bd5218ca15-1 (4, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'right' > right.range
+  $ diff -u right-2.range right.range
+  --- right-2.range	* (glob)
+  +++ right.range	* (glob)
+  @@ -1,3 +1,7 @@
+  +a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2)
+  +a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1)
+   e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  +3a367db1fabc-2 (5, 3, 1) [leaf] - 
+  +a2f58e9c1e56-3 (6, 4, 1) [leaf] - 
+   e7bd5218ca15-1 (4, 2, 1) [leaf] - 
+  [1]
+
+The merge reuse as much of the slicing created for one of the branch
+
+  $ hg debugstablerange --verify --verbose --subranges --rev merge
+  5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  3a367db1fabc-1 (5, 3, 2) [complete] - e7bd5218ca15-1 (4, 2, 1), 3a367db1fabc-2 (5, 3, 1)
+  5f18015f9110-6 (7, 8, 2) [complete] - a2f58e9c1e56-3 (6, 4, 1), 5f18015f9110-7 (7, 8, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  3a367db1fabc-2 (5, 3, 1) [leaf] - 
+  5f18015f9110-7 (7, 8, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  a2f58e9c1e56-3 (6, 4, 1) [leaf] - 
+  e7bd5218ca15-1 (4, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'merge' > merge.range
+  $ diff -u left.range merge.range
+  --- left.range	* (glob)
+  +++ merge.range	* (glob)
+  @@ -1,7 +1,15 @@
+  +5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4)
+   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2)
+   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  +3a367db1fabc-1 (5, 3, 2) [complete] - e7bd5218ca15-1 (4, 2, 1), 3a367db1fabc-2 (5, 3, 1)
+  +5f18015f9110-6 (7, 8, 2) [complete] - a2f58e9c1e56-3 (6, 4, 1), 5f18015f9110-7 (7, 8, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+   01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+   2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  +3a367db1fabc-2 (5, 3, 1) [leaf] - 
+  +5f18015f9110-7 (7, 8, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  +a2f58e9c1e56-3 (6, 4, 1) [leaf] - 
+  +e7bd5218ca15-1 (4, 2, 1) [leaf] - 
+  [1]
+  $ diff -u right.range merge.range
+  --- right.range	* (glob)
+  +++ merge.range	* (glob)
+  @@ -1,7 +1,15 @@
+  -a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2)
+  -a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1)
+  -e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
+  +5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4)
+  +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2)
+  +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  +3a367db1fabc-1 (5, 3, 2) [complete] - e7bd5218ca15-1 (4, 2, 1), 3a367db1fabc-2 (5, 3, 1)
+  +5f18015f9110-6 (7, 8, 2) [complete] - a2f58e9c1e56-3 (6, 4, 1), 5f18015f9110-7 (7, 8, 1)
+  +66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  +01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  +2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   3a367db1fabc-2 (5, 3, 1) [leaf] - 
+  +5f18015f9110-7 (7, 8, 1) [leaf] - 
+  +66f7d451a68b-1 (1, 2, 1) [leaf] - 
+   a2f58e9c1e56-3 (6, 4, 1) [leaf] - 
+   e7bd5218ca15-1 (4, 2, 1) [leaf] - 
+  [1]
+  $ cd ..
+
+slice create multiple heads
+---------------------------
+
+  $ hg init repo_merge_split_heads
+  $ cd repo_merge_split_heads
+  $ hg debugbuilddag '.:base
+  > +4:left
+  > <base+5:right
+  > <left/right:merge
+  > +2:head
+  > '
+  $ hg debugbuilddag '.:base
+  > +3:left
+  > <base+3:right
+  > <left/right:merge
+  > +2:head
+  > '
+  abort: repository is not empty
+  [255]
+  $ hg log -G
+  o  12 e6b8d5b46647 r12 head tip
+  |
+  o  11 485383494a89 r11
+  |
+  o    10 8aca7f8c9bd2 r10 merge
+  |\
+  | o  9 f4b7da68b467 r9 right
+  | |
+  | o  8 857477a9aebb r8
+  | |
+  | o  7 42b07e8da27d r7
+  | |
+  | o  6 b9bc20507e0b r6
+  | |
+  | o  5 de561312eff4 r5
+  | |
+  o |  4 bebd167eb94d r4 left
+  | |
+  o |  3 2dc09a01254d r3
+  | |
+  o |  2 01241442b3c2 r2
+  | |
+  o |  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0 base
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  de561312eff4 2
+  b9bc20507e0b 3
+  42b07e8da27d 4
+  857477a9aebb 5
+  f4b7da68b467 6
+  8aca7f8c9bd2 11
+  485383494a89 12
+  e6b8d5b46647 13
+
+Each of the linear branch reuse range internally
+
+(left branch)
+
+  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2'
+  01241442b3c2-0 (2, 3, 3) [complete] - 66f7d451a68b-0 (1, 2, 2), 01241442b3c2-2 (2, 3, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' > left-2.range
+  $ hg debugstablerange --verify --verbose --subranges --rev left
+  bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'left' > left.range
+  $ diff -u left-2.range left.range
+  --- left-2.range	* (glob)
+  +++ left.range	* (glob)
+  @@ -1,5 +1,9 @@
+  -01241442b3c2-0 (2, 3, 3) [complete] - 66f7d451a68b-0 (1, 2, 2), 01241442b3c2-2 (2, 3, 1)
+  +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+   01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  +2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  +bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  [1]
+
+(right branch)
+
+  $ hg debugstablerange --verify --verbose --subranges --rev right~2
+  42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+  de561312eff4-1 (5, 2, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'right~2' > right-2.range
+  $ hg debugstablerange --verify --verbose --subranges --rev right
+  f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
+  42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
+  f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  857477a9aebb-4 (8, 5, 1) [leaf] - 
+  b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+  de561312eff4-1 (5, 2, 1) [leaf] - 
+  f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'right' > right.range
+  $ diff -u right-2.range right.range
+  --- right-2.range	* (glob)
+  +++ right.range	* (glob)
+  @@ -1,7 +1,11 @@
+  +f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
+   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+   de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
+  +f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+   42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  +857477a9aebb-4 (8, 5, 1) [leaf] - 
+   b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+   de561312eff4-1 (5, 2, 1) [leaf] - 
+  +f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  [1]
+
+In this case, the bottom of the split will have multiple heads,
+
+So we'll create more than 1 subrange out of it.
+
+We are still able to reuse one of the branch however
+
+  $ hg debugstablerange --verify --verbose --subranges --rev merge
+  8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
+  bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
+  8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  857477a9aebb-4 (8, 5, 1) [leaf] - 
+  8aca7f8c9bd2-10 (10, 11, 1) [leaf] - 
+  b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+  bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  de561312eff4-1 (5, 2, 1) [leaf] - 
+  f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'merge' > merge.range
+  $ diff -u left.range merge.range
+  --- left.range	* (glob)
+  +++ merge.range	* (glob)
+  @@ -1,9 +1,20 @@
+  +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
+   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
+  +8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
+   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  +42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  +f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+   01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+   2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  +42b07e8da27d-3 (7, 4, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  +857477a9aebb-4 (8, 5, 1) [leaf] - 
+  +8aca7f8c9bd2-10 (10, 11, 1) [leaf] - 
+  +b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+   bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  +de561312eff4-1 (5, 2, 1) [leaf] - 
+  +f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  [1]
+  $ diff -u right.range merge.range
+  --- right.range	* (glob)
+  +++ merge.range	* (glob)
+  @@ -1,11 +1,20 @@
+  -f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
+  -42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
+  +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
+  +8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
+  +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  -de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
+  +66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+   f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+  +01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  +2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  +66f7d451a68b-1 (1, 2, 1) [leaf] - 
+   857477a9aebb-4 (8, 5, 1) [leaf] - 
+  +8aca7f8c9bd2-10 (10, 11, 1) [leaf] - 
+   b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+  +bebd167eb94d-4 (4, 5, 1) [leaf] - 
+   de561312eff4-1 (5, 2, 1) [leaf] - 
+   f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  [1]
+
+Range above the merge, reuse subrange from the merge
+
+  $ hg debugstablerange --verify --verbose --subranges --rev tip
+  e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), e6b8d5b46647-8 (12, 13, 5)
+  bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1)
+  2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2)
+  42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
+  2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+  42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  485383494a89-10 (11, 12, 2) [complete] - 8aca7f8c9bd2-10 (10, 11, 1), 485383494a89-11 (11, 12, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+  01241442b3c2-2 (2, 3, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2dc09a01254d-3 (3, 4, 1) [leaf] - 
+  42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  485383494a89-11 (11, 12, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  857477a9aebb-4 (8, 5, 1) [leaf] - 
+  8aca7f8c9bd2-10 (10, 11, 1) [leaf] - 
+  b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+  bebd167eb94d-4 (4, 5, 1) [leaf] - 
+  de561312eff4-1 (5, 2, 1) [leaf] - 
+  e6b8d5b46647-12 (12, 13, 1) [leaf] - 
+  f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  $ hg debugstablerange --verify --verbose --subranges --rev 'tip' > tip.range
+  $ diff -u merge.range tip.range
+  --- merge.range	* (glob)
+  +++ tip.range	* (glob)
+  @@ -1,20 +1,24 @@
+  -8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
+  +e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), e6b8d5b46647-8 (12, 13, 5)
+   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
+  +e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1)
+   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
+  +485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2)
+   42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
+  -8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
+   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
+   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  +485383494a89-10 (11, 12, 2) [complete] - 8aca7f8c9bd2-10 (10, 11, 1), 485383494a89-11 (11, 12, 1)
+   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+   f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
+   01241442b3c2-2 (2, 3, 1) [leaf] - 
+   1ea73414a91b-0 (0, 1, 1) [leaf] - 
+   2dc09a01254d-3 (3, 4, 1) [leaf] - 
+   42b07e8da27d-3 (7, 4, 1) [leaf] - 
+  +485383494a89-11 (11, 12, 1) [leaf] - 
+   66f7d451a68b-1 (1, 2, 1) [leaf] - 
+   857477a9aebb-4 (8, 5, 1) [leaf] - 
+   8aca7f8c9bd2-10 (10, 11, 1) [leaf] - 
+   b9bc20507e0b-2 (6, 3, 1) [leaf] - 
+   bebd167eb94d-4 (4, 5, 1) [leaf] - 
+   de561312eff4-1 (5, 2, 1) [leaf] - 
+  +e6b8d5b46647-12 (12, 13, 1) [leaf] - 
+   f4b7da68b467-5 (9, 6, 1) [leaf] - 
+  [1]
+
+  $ cd ..
+
+Tests range with criss cross merge in the graph
+===============================================
+
+  $ hg init repo_criss_cross
+  $ cd repo_criss_cross
+  $ hg debugbuilddag '
+  > ..:g   # 2 nodes, tagged "g"
+  > <2.:h   # another node base one -2 -> 0, tagged "h"
+  > *1/2:m # merge -1 and -2 (1, 2), tagged "m"
+  > <2+2:i # 2 nodes based on -2, tag head as "i"
+  > .:c    # 1 node tagged "c"
+  > <m+3:a # 3 nodes base on the "m" tag
+  > <2.:b  # 1 node based on -2; tagged "b"
+  > <m+2:d # 2 nodes from "m" tagged "d"
+  > <2.:e  # 1 node based on -2, tagged "e"
+  > <m+1:f # 1 node based on "m" tagged "f"
+  > <i/f   # merge "i" and "f"
+  > '
+  $ hg log -G
+  o    15 1d8d22637c2d r15 tip
+  |\
+  | o  14 43227190fef8 r14 f
+  | |
+  | | o  13 b4594d867745 r13 e
+  | | |
+  | | | o  12 e46a4836065c r12 d
+  | | |/
+  | | o  11 bab5d5bf48bd r11
+  | |/
+  | | o  10 ff43616e5d0f r10 b
+  | | |
+  | | | o  9 dcbb326fdec2 r9 a
+  | | |/
+  | | o  8 d62d843c9a01 r8
+  | | |
+  | | o  7 e7d9710d9fc6 r7
+  | |/
+  +---o  6 2702dd0c91e7 r6 c
+  | |
+  o |  5 f0f3ef9a6cd5 r5 i
+  | |
+  o |  4 4c748ffd1a46 r4
+  | |
+  | o  3 2b6d669947cd r3 m
+  |/|
+  o |  2 fa942426a6fd r2 h
+  | |
+  | o  1 66f7d451a68b r1 g
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+  4c748ffd1a46 3
+  f0f3ef9a6cd5 4
+  2702dd0c91e7 5
+  e7d9710d9fc6 5
+  d62d843c9a01 6
+  dcbb326fdec2 7
+  ff43616e5d0f 7
+  bab5d5bf48bd 5
+  e46a4836065c 6
+  b4594d867745 6
+  43227190fef8 5
+  1d8d22637c2d 8
+  $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
+  1d8d22637c2d-0 (15, 8, 8) [complete] - 2b6d669947cd-0 (3, 4, 4), 1d8d22637c2d-4 (15, 8, 4)
+  dcbb326fdec2-0 (9, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), dcbb326fdec2-4 (9, 7, 3)
+  ff43616e5d0f-0 (10, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), ff43616e5d0f-4 (10, 7, 3)
+  b4594d867745-0 (13, 6, 6) [complete] - 2b6d669947cd-0 (3, 4, 4), b4594d867745-4 (13, 6, 2)
+  e46a4836065c-0 (12, 6, 6) [complete] - 2b6d669947cd-0 (3, 4, 4), e46a4836065c-4 (12, 6, 2)
+  2702dd0c91e7-0 (6, 5, 5) [complete] - f0f3ef9a6cd5-0 (5, 4, 4), 2702dd0c91e7-4 (6, 5, 1)
+  1d8d22637c2d-4 (15, 8, 4) [complete] - 43227190fef8-4 (14, 5, 1), 4c748ffd1a46-2 (4, 3, 1), 1d8d22637c2d-6 (15, 8, 2)
+  2b6d669947cd-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2b6d669947cd-2 (3, 4, 2)
+  f0f3ef9a6cd5-0 (5, 4, 4) [complete] - fa942426a6fd-0 (2, 2, 2), f0f3ef9a6cd5-2 (5, 4, 2)
+  dcbb326fdec2-4 (9, 7, 3) [complete] - d62d843c9a01-4 (8, 6, 2), dcbb326fdec2-6 (9, 7, 1)
+  ff43616e5d0f-4 (10, 7, 3) [complete] - d62d843c9a01-4 (8, 6, 2), ff43616e5d0f-6 (10, 7, 1)
+  1d8d22637c2d-6 (15, 8, 2) [complete] - f0f3ef9a6cd5-3 (5, 4, 1), 1d8d22637c2d-7 (15, 8, 1)
+  2b6d669947cd-2 (3, 4, 2) [complete] - fa942426a6fd-1 (2, 2, 1), 2b6d669947cd-3 (3, 4, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  b4594d867745-4 (13, 6, 2) [complete] - bab5d5bf48bd-4 (11, 5, 1), b4594d867745-5 (13, 6, 1)
+  d62d843c9a01-4 (8, 6, 2) [complete] - e7d9710d9fc6-4 (7, 5, 1), d62d843c9a01-5 (8, 6, 1)
+  e46a4836065c-4 (12, 6, 2) [complete] - bab5d5bf48bd-4 (11, 5, 1), e46a4836065c-5 (12, 6, 1)
+  f0f3ef9a6cd5-2 (5, 4, 2) [complete] - 4c748ffd1a46-2 (4, 3, 1), f0f3ef9a6cd5-3 (5, 4, 1)
+  fa942426a6fd-0 (2, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), fa942426a6fd-1 (2, 2, 1)
+  1d8d22637c2d-7 (15, 8, 1) [leaf] - 
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  2702dd0c91e7-4 (6, 5, 1) [leaf] - 
+  2b6d669947cd-3 (3, 4, 1) [leaf] - 
+  43227190fef8-4 (14, 5, 1) [leaf] - 
+  4c748ffd1a46-2 (4, 3, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  b4594d867745-5 (13, 6, 1) [leaf] - 
+  bab5d5bf48bd-4 (11, 5, 1) [leaf] - 
+  d62d843c9a01-5 (8, 6, 1) [leaf] - 
+  dcbb326fdec2-6 (9, 7, 1) [leaf] - 
+  e46a4836065c-5 (12, 6, 1) [leaf] - 
+  e7d9710d9fc6-4 (7, 5, 1) [leaf] - 
+  f0f3ef9a6cd5-3 (5, 4, 1) [leaf] - 
+  fa942426a6fd-1 (2, 2, 1) [leaf] - 
+  ff43616e5d0f-6 (10, 7, 1) [leaf] - 
+  $ cd ..
+
+Tests range where a toprange is rooted on a merge
+=================================================
+
+  $ hg init slice_on_merge
+  $ cd slice_on_merge
+  $ hg debugbuilddag '
+  > ..:a   # 2 nodes, tagged "a"
+  > <2..:b   # another branch with two node based on 0, tagged b
+  > *a/b:m # merge -1 and -2 (1, 2), tagged "m"
+  > '
+  $ hg log -G
+  o    4 f37e476fba9a r4 m tip
+  |\
+  | o  3 36315563e2fa r3 b
+  | |
+  | o  2 fa942426a6fd r2
+  | |
+  o |  1 66f7d451a68b r1 a
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  36315563e2fa 3
+  f37e476fba9a 5
+  $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
+  f37e476fba9a-0 (4, 5, 5) [complete] - 66f7d451a68b-0 (1, 2, 2), 36315563e2fa-1 (3, 3, 2), f37e476fba9a-4 (4, 5, 1)
+  36315563e2fa-1 (3, 3, 2) [complete] - fa942426a6fd-1 (2, 2, 1), 36315563e2fa-2 (3, 3, 1)
+  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
+  1ea73414a91b-0 (0, 1, 1) [leaf] - 
+  36315563e2fa-2 (3, 3, 1) [leaf] - 
+  66f7d451a68b-1 (1, 2, 1) [leaf] - 
+  f37e476fba9a-4 (4, 5, 1) [leaf] - 
+  fa942426a6fd-1 (2, 2, 1) [leaf] - 
+
--- a/tests/test-stablerange.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-stablerange.t	Mon Dec 11 18:30:15 2017 +0100
@@ -8,6 +8,8 @@
   > hgext3rd.evolve =
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
+  > [defaults]
+  > debugstablerange = --method mergepoint
   > EOF
 
 Simple linear test
@@ -16,6 +18,14 @@
   $ hg init repo_linear
   $ cd repo_linear
   $ hg debugbuilddag '.+6'
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  c8d03c1b5e94 6
+  f69452c5b1af 7
   $ hg debugstablerange --verify --verbose --subranges --rev 1
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
@@ -156,6 +166,17 @@
   |/
   o  0 1ea73414a91b r0 base
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  e7bd5218ca15 2
+  3a367db1fabc 3
+  a2f58e9c1e56 4
+  5f18015f9110 8
+  71b32fcf3f71 9
+  0338daf18215 10
 
 Each of the linear branch reuse range internally
 
@@ -327,6 +348,20 @@
   |/
   o  0 1ea73414a91b r0 base
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  de561312eff4 2
+  b9bc20507e0b 3
+  42b07e8da27d 4
+  857477a9aebb 5
+  f4b7da68b467 6
+  8aca7f8c9bd2 11
+  485383494a89 12
+  e6b8d5b46647 13
 
 Each of the linear branch reuse range internally
 
@@ -414,15 +449,14 @@
 We are still able to reuse one of the branch however
 
   $ hg debugstablerange --verify --verbose --subranges --rev merge
-  8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3)
+  8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
-  42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
   8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
-  de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
   f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
   01241442b3c2-2 (2, 3, 1) [leaf] - 
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
@@ -439,16 +473,15 @@
   $ diff -u left.range merge.range
   --- left.range	* (glob)
   +++ merge.range	* (glob)
-  @@ -1,9 +1,21 @@
-  +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3)
+  @@ -1,9 +1,20 @@
+  +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
    bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
    2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
-  +42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  +42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
   +8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
    2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
   +42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
    66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
-  +de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
   +f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
    01241442b3c2-2 (2, 3, 1) [leaf] - 
    1ea73414a91b-0 (0, 1, 1) [leaf] - 
@@ -465,17 +498,18 @@
   $ diff -u right.range merge.range
   --- right.range	* (glob)
   +++ merge.range	* (glob)
-  @@ -1,11 +1,21 @@
+  @@ -1,11 +1,20 @@
   -f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
-  +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3)
+  -42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
   +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
-   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  +42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
   +8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
   +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
    42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
+  -de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
   +66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
-   de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
    f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
   +01241442b3c2-2 (2, 3, 1) [leaf] - 
    1ea73414a91b-0 (0, 1, 1) [leaf] - 
@@ -493,17 +527,16 @@
 Range above the merge, reuse subrange from the merge
 
   $ hg debugstablerange --verify --verbose --subranges --rev tip
-  e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), e6b8d5b46647-8 (12, 13, 5)
+  e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), e6b8d5b46647-8 (12, 13, 5)
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
-  42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
   485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2)
+  42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
   485383494a89-10 (11, 12, 2) [complete] - 8aca7f8c9bd2-10 (10, 11, 1), 485383494a89-11 (11, 12, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
-  de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
   f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
   01241442b3c2-2 (2, 3, 1) [leaf] - 
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
@@ -522,22 +555,21 @@
   $ diff -u merge.range tip.range
   --- merge.range	* (glob)
   +++ tip.range	* (glob)
-  @@ -1,10 +1,12 @@
-  -8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3)
-  +e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), e6b8d5b46647-8 (12, 13, 5)
+  @@ -1,20 +1,24 @@
+  -8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
+  +e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), e6b8d5b46647-8 (12, 13, 5)
    bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   +e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1)
    2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
-   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
+  +485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2)
+   42b07e8da27d-1 (7, 4, 3) [complete] - de561312eff4-1 (5, 2, 1), 42b07e8da27d-2 (7, 4, 2)
   -8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1)
-  +485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2)
    2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
    42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
   +485383494a89-10 (11, 12, 2) [complete] - 8aca7f8c9bd2-10 (10, 11, 1), 485383494a89-11 (11, 12, 1)
    66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
-   de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
    f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1)
-  @@ -12,10 +14,12 @@
+   01241442b3c2-2 (2, 3, 1) [leaf] - 
    1ea73414a91b-0 (0, 1, 1) [leaf] - 
    2dc09a01254d-3 (3, 4, 1) [leaf] - 
    42b07e8da27d-3 (7, 4, 1) [leaf] - 
@@ -605,6 +637,23 @@
   |/
   o  0 1ea73414a91b r0
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+  4c748ffd1a46 3
+  f0f3ef9a6cd5 4
+  2702dd0c91e7 5
+  e7d9710d9fc6 5
+  d62d843c9a01 6
+  dcbb326fdec2 7
+  ff43616e5d0f 7
+  bab5d5bf48bd 5
+  e46a4836065c 6
+  b4594d867745 6
+  43227190fef8 5
+  1d8d22637c2d 8
   $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
   1d8d22637c2d-0 (15, 8, 8) [complete] - 2b6d669947cd-0 (3, 4, 4), 1d8d22637c2d-4 (15, 8, 4)
   dcbb326fdec2-0 (9, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), dcbb326fdec2-4 (9, 7, 3)
@@ -612,7 +661,7 @@
   b4594d867745-0 (13, 6, 6) [complete] - 2b6d669947cd-0 (3, 4, 4), b4594d867745-4 (13, 6, 2)
   e46a4836065c-0 (12, 6, 6) [complete] - 2b6d669947cd-0 (3, 4, 4), e46a4836065c-4 (12, 6, 2)
   2702dd0c91e7-0 (6, 5, 5) [complete] - f0f3ef9a6cd5-0 (5, 4, 4), 2702dd0c91e7-4 (6, 5, 1)
-  1d8d22637c2d-4 (15, 8, 4) [complete] - 4c748ffd1a46-2 (4, 3, 1), 43227190fef8-4 (14, 5, 1), 1d8d22637c2d-6 (15, 8, 2)
+  1d8d22637c2d-4 (15, 8, 4) [complete] - 43227190fef8-4 (14, 5, 1), 4c748ffd1a46-2 (4, 3, 1), 1d8d22637c2d-6 (15, 8, 2)
   2b6d669947cd-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2b6d669947cd-2 (3, 4, 2)
   f0f3ef9a6cd5-0 (5, 4, 4) [complete] - fa942426a6fd-0 (2, 2, 2), f0f3ef9a6cd5-2 (5, 4, 2)
   dcbb326fdec2-4 (9, 7, 3) [complete] - d62d843c9a01-4 (8, 6, 2), dcbb326fdec2-6 (9, 7, 1)
@@ -664,10 +713,15 @@
   |/
   o  0 1ea73414a91b r0
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  36315563e2fa 3
+  f37e476fba9a 5
   $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
-  f37e476fba9a-0 (4, 5, 5) [complete] - 66f7d451a68b-0 (1, 2, 2), 36315563e2fa-0 (3, 3, 3), f37e476fba9a-4 (4, 5, 1)
+  f37e476fba9a-0 (4, 5, 5) [complete] - 36315563e2fa-0 (3, 3, 3), 66f7d451a68b-1 (1, 2, 1), f37e476fba9a-4 (4, 5, 1)
   36315563e2fa-0 (3, 3, 3) [complete] - fa942426a6fd-0 (2, 2, 2), 36315563e2fa-2 (3, 3, 1)
-  66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   fa942426a6fd-0 (2, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), fa942426a6fd-1 (2, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   36315563e2fa-2 (3, 3, 1) [leaf] - 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-stablesort-branchpoint-criss-cross.t	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,816 @@
+Test for stable ordering capabilities
+=====================================
+
+  $ . $TESTDIR/testlib/pythonpath.sh
+
+  $ cat << EOF >> $HGRCPATH
+  > [extensions]
+  > hgext3rd.evolve =
+  > [ui]
+  > logtemplate = "{rev} {node|short} {desc} {tags}\n"
+  > [alias]
+  > showsort = debugstablesort --template="{node|short}\n" --method branchpoint
+  > EOF
+
+  $ checktopo () {
+  >     seen='null';
+  >     for node in `hg showsort --rev "$1"`; do
+  >         echo "=== checking $node ===";
+  >         hg log --rev "($seen) and $node::";
+  >         seen="${seen}+${node}";
+  >     done;
+  > }
+
+  $ cat << EOF >> random_rev.py
+  > import random
+  > import sys
+  > 
+  > loop = int(sys.argv[1])
+  > var = int(sys.argv[2])
+  > for x in range(loop):
+  >     print(x + random.randint(0, var))
+  > EOF
+
+Check criss cross merge
+=======================
+
+  $ hg init crisscross_A
+  $ cd crisscross_A
+  $ hg debugbuilddag '
+  > ...:base         # create some base
+  > # criss cross #1: simple
+  > +3:AbaseA      # "A" branch for CC "A"
+  > <base+2:AbaseB # "B" branch for CC "B"
+  > <AbaseA/AbaseB:AmergeA
+  > <AbaseB/AbaseA:AmergeB
+  > <AmergeA/AmergeB:Afinal
+  > # criss cross #2:multiple closes ones
+  > .:BbaseA
+  > <AmergeB:BbaseB
+  > <BbaseA/BbaseB:BmergeA
+  > <BbaseB/BbaseA:BmergeB
+  > <BmergeA/BmergeB:BmergeC
+  > <BmergeB/BmergeA:BmergeD
+  > <BmergeC/BmergeD:Bfinal
+  > # criss cross #2:many branches
+  > <Bfinal.:CbaseA
+  > <Bfinal+2:CbaseB
+  > <Bfinal.:CbaseC
+  > <Bfinal+5:CbaseD
+  > <Bfinal.:CbaseE
+  > <CbaseA/CbaseB+7:CmergeA
+  > <CbaseA/CbaseC:CmergeB
+  > <CbaseA/CbaseD.:CmergeC
+  > <CbaseA/CbaseE:CmergeD
+  > <CbaseB/CbaseA+2:CmergeE
+  > <CbaseB/CbaseC:CmergeF
+  > <CbaseB/CbaseD.:CmergeG
+  > <CbaseB/CbaseE:CmergeH
+  > <CbaseC/CbaseA.:CmergeI
+  > <CbaseC/CbaseB:CmergeJ
+  > <CbaseC/CbaseD+5:CmergeK
+  > <CbaseC/CbaseE+2:CmergeL
+  > <CbaseD/CbaseA:CmergeM
+  > <CbaseD/CbaseB...:CmergeN
+  > <CbaseD/CbaseC:CmergeO
+  > <CbaseD/CbaseE:CmergeP
+  > <CbaseE/CbaseA:CmergeQ
+  > <CbaseE/CbaseB..:CmergeR
+  > <CbaseE/CbaseC.:CmergeS
+  > <CbaseE/CbaseD:CmergeT
+  > <CmergeA/CmergeG:CmergeWA
+  > <CmergeB/CmergeF:CmergeWB
+  > <CmergeC/CmergeE:CmergeWC
+  > <CmergeD/CmergeH:CmergeWD
+  > <CmergeT/CmergeI:CmergeWE
+  > <CmergeS/CmergeJ:CmergeWF
+  > <CmergeR/CmergeK:CmergeWG
+  > <CmergeQ/CmergeL:CmergeWH
+  > <CmergeP/CmergeM:CmergeWI
+  > <CmergeO/CmergeN:CmergeWJ
+  > <CmergeO/CmergeN:CmergeWK
+  > <CmergeWA/CmergeWG:CmergeXA
+  > <CmergeWB/CmergeWH:CmergeXB
+  > <CmergeWC/CmergeWI:CmergeXC
+  > <CmergeWD/CmergeWJ:CmergeXD
+  > <CmergeWE/CmergeWK:CmergeXE
+  > <CmergeWF/CmergeWA:CmergeXF
+  > <CmergeXA/CmergeXF:CmergeYA
+  > <CmergeXB/CmergeXE:CmergeYB
+  > <CmergeXC/CmergeXD:CmergeYC
+  > <CmergeYA/CmergeYB:CmergeZA
+  > <CmergeYC/CmergeYB:CmergeZB
+  > <CmergeZA/CmergeZB:Cfinal
+  > '
+  $ hg log -G
+  o    94 01f771406cab r94 Cfinal tip
+  |\
+  | o    93 84d6ec6a8e21 r93 CmergeZB
+  | |\
+  o | |  92 721ba7c5f4ff r92 CmergeZA
+  |\| |
+  | | o    91 8ae32c3ed670 r91 CmergeYC
+  | | |\
+  | o \ \    90 8b79544bb56d r90 CmergeYB
+  | |\ \ \
+  o \ \ \ \    89 041e1188f5f1 r89 CmergeYA
+  |\ \ \ \ \
+  | o \ \ \ \    88 2472d042ec95 r88 CmergeXF
+  | |\ \ \ \ \
+  | | | | o \ \    87 c7d3029bf731 r87 CmergeXE
+  | | | | |\ \ \
+  | | | | | | | o    86 469c700e9ed8 r86 CmergeXD
+  | | | | | | | |\
+  | | | | | | o \ \    85 28be96b80dc1 r85 CmergeXC
+  | | | | | | |\ \ \
+  | | | o \ \ \ \ \ \    84 dbde319d43a3 r84 CmergeXB
+  | | | |\ \ \ \ \ \ \
+  o | | | | | | | | | |  83 b3cf98c3d587 r83 CmergeXA
+  |\| | | | | | | | | |
+  | | | | | | o | | | |    82 1da228afcf06 r82 CmergeWK
+  | | | | | | |\ \ \ \ \
+  | | | | | | +-+-------o  81 0bab31f71a21 r81 CmergeWJ
+  | | | | | | | | | | |
+  | | | | | | | | | o |    80 cd345198cf12 r80 CmergeWI
+  | | | | | | | | | |\ \
+  | | | | o \ \ \ \ \ \ \    79 82238c0bc950 r79 CmergeWH
+  | | | | |\ \ \ \ \ \ \ \
+  o \ \ \ \ \ \ \ \ \ \ \ \    78 89a0fe204177 r78 CmergeWG
+  |\ \ \ \ \ \ \ \ \ \ \ \ \
+  | | | o \ \ \ \ \ \ \ \ \ \    77 97d19fc5236f r77 CmergeWF
+  | | | |\ \ \ \ \ \ \ \ \ \ \
+  | | | | | | | | o \ \ \ \ \ \    76 37ad3ab0cddf r76 CmergeWE
+  | | | | | | | | |\ \ \ \ \ \ \
+  | | | | | | | | | | | | | | | o    75 790cdfecd168 r75 CmergeWD
+  | | | | | | | | | | | | | | | |\
+  | | | | | | | | | | | | o \ \ \ \    74 698970a2480b r74 CmergeWC
+  | | | | | | | | | | | | |\ \ \ \ \
+  | | | | | o \ \ \ \ \ \ \ \ \ \ \ \    73 31d7b43cc321 r73 CmergeWB
+  | | | | | |\ \ \ \ \ \ \ \ \ \ \ \ \
+  | | o \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \    72 eed373b0090d r72 CmergeWA
+  | | |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
+  | | | | | | | | | | | o \ \ \ \ \ \ \ \    71 4f3b41956174 r71 CmergeT
+  | | | | | | | | | | | |\ \ \ \ \ \ \ \ \
+  | | | | | o | | | | | | | | | | | | | | |  70 c3c7fa726f88 r70 CmergeS
+  | | | | | | | | | | | | | | | | | | | | |
+  | | | | | o-------------+ | | | | | | | |  69 d917f77a6439 r69
+  | | | | | | | | | | | | | | | | | | | | |
+  | o | | | | | | | | | | | | | | | | | | |  68 fac9e582edd1 r68 CmergeR
+  | | | | | | | | | | | | | | | | | | | | |
+  | o | | | | | | | | | | | | | | | | | | |  67 e4cfd6264623 r67
+  | | | | | | | | | | | | | | | | | | | | |
+  | o---------------------+ | | | | | | | |  66 d99e0f7dad5b r66
+  | | | | | | | | | | | | | | | | | | | | |
+  | | | | | | | | | o-----+ | | | | | | | |  65 c713eae2d31f r65 CmergeQ
+  | | | | | | | | | | | | | | | | | | | | |
+  | | | | | | | | | | | +-+-----------o | |  64 b33fd5ad4c0c r64 CmergeP
+  | | | | | | | | | | | | | | | | | |  / /
+  | | | | | +-----------+-----o | | | / /  63 bf6593f7e073 r63 CmergeO
+  | | | | | | | | | | | | | |  / / / / /
+  | | | | | | | | | | | | | o | | | | |  62 3871506da61e r62 CmergeN
+  | | | | | | | | | | | | | | | | | | |
+  | | | | | | | | | | | | | o | | | | |  61 c84da74cf586 r61
+  | | | | | | | | | | | | | | | | | | |
+  | | | | | | | | | | | | | o | | | | |  60 5eec91b12a58 r60
+  | | | | | | | | | | | | | | | | | | |
+  | +-------------------+---o | | | | |  59 0484d39906c8 r59
+  | | | | | | | | | | | | |  / / / / /
+  | | | | | | | | | +---+-------o / /  58 29141354a762 r58 CmergeM
+  | | | | | | | | | | | | | | |  / /
+  | | | | | | | | o | | | | | | | |  57 e7135b665740 r57 CmergeL
+  | | | | | | | | | | | | | | | | |
+  | | | | | | | | o | | | | | | | |  56 c7c1497fc270 r56
+  | | | | | | | | | | | | | | | | |
+  | | | | | +-----o-------+ | | | |  55 76151e8066e1 r55
+  | | | | | | | |  / / / / / / / /
+  o | | | | | | | | | | | | | | |  54 9a67238ad1c4 r54 CmergeK
+  | | | | | | | | | | | | | | | |
+  o | | | | | | | | | | | | | | |  53 c37e7cd9f2bd r53
+  | | | | | | | | | | | | | | | |
+  o | | | | | | | | | | | | | | |  52 0d153e3ad632 r52
+  | | | | | | | | | | | | | | | |
+  o | | | | | | | | | | | | | | |  51 97ac964e34b7 r51
+  | | | | | | | | | | | | | | | |
+  o | | | | | | | | | | | | | | |  50 900dd066a072 r50
+  | | | | | | | | | | | | | | | |
+  o---------+---------+ | | | | |  49 673f5499c8c2 r49
+   / / / / / / / / / / / / / / /
+  +-----o / / / / / / / / / / /  48 8ecb28746ec4 r48 CmergeJ
+  | | | |/ / / / / / / / / / /
+  | | | | | | | o | | | | | |  47 d6c9e2d27f14 r47 CmergeI
+  | | | | | | | | | | | | | |
+  | | | +-------o | | | | | |  46 bfcfd9a61e84 r46
+  | | | | | | |/ / / / / / /
+  +---------------+-------o  45 40553f55397e r45 CmergeH
+  | | | | | | | | | | | |
+  | | o | | | | | | | | |  44 d94da36be176 r44 CmergeG
+  | | | | | | | | | | | |
+  +---o---------+ | | | |  43 4b39f229a0ce r43
+  | |  / / / / / / / / /
+  +---+---o / / / / / /  42 43fc0b77ff07 r42 CmergeF
+  | | | |  / / / / / /
+  | | | | | | | | o |  41 88eace5ce682 r41 CmergeE
+  | | | | | | | | | |
+  | | | | | | | | o |  40 d928b4e8a515 r40
+  | | | | | | | | | |
+  +-------+-------o |  39 88714f4125cb r39
+  | | | | | | | |  /
+  | | | | +---+---o  38 e3e6738c56ce r38 CmergeD
+  | | | | | | | |
+  | | | | | | | o  37 32b41ca704e1 r37 CmergeC
+  | | | | | | | |
+  | | | | +-+---o  36 01e29e20ea3f r36
+  | | | | | | |
+  | | | o | | |  35 1f4a19f83a29 r35 CmergeB
+  | | |/|/ / /
+  | o | | | |  34 722d1b8b8942 r34 CmergeA
+  | | | | | |
+  | o | | | |  33 47c836a1f13e r33
+  | | | | | |
+  | o | | | |  32 2ea3fbf151b5 r32
+  | | | | | |
+  | o | | | |  31 0c3f2ba59eb7 r31
+  | | | | | |
+  | o | | | |  30 f3441cd3e664 r30
+  | | | | | |
+  | o | | | |  29 b9c3aa92fba5 r29
+  | | | | | |
+  | o | | | |  28 3bdb00d5c818 r28
+  | | | | | |
+  | o---+ | |  27 2bd677d0f13a r27
+  |/ / / / /
+  | | | | o  26 de05b9c29ec7 r26 CbaseE
+  | | | | |
+  | | | o |  25 ad46a4a0fc10 r25 CbaseD
+  | | | | |
+  | | | o |  24 a457569c5306 r24
+  | | | | |
+  | | | o |  23 f2bdd828a3aa r23
+  | | | | |
+  | | | o |  22 5ce588c2b7c5 r22
+  | | | | |
+  | | | o |  21 17b6e6bac221 r21
+  | | | |/
+  | o---+  20 b115c694654e r20 CbaseC
+  |  / /
+  o | |  19 884936b34999 r19 CbaseB
+  | | |
+  o---+  18 9729470d9329 r18
+   / /
+  o /  17 4f5078f7da8a r17 CbaseA
+  |/
+  o    16 3e1560705803 r16 Bfinal
+  |\
+  | o    15 55bf3fdb634f r15 BmergeD
+  | |\
+  o---+  14 39bab1cb1cbe r14 BmergeC
+  |/ /
+  | o    13 f7c6e7bfbcd0 r13 BmergeB
+  | |\
+  o---+  12 26f59ee8b1d7 r12 BmergeA
+  |/ /
+  | o  11 3e2da24aee59 r11 BbaseA
+  | |
+  | o  10 5ba9a53052ed r10 Afinal
+  |/|
+  o |    9 07c648efceeb r9 AmergeB BbaseB
+  |\ \
+  +---o  8 c81423bf5a24 r8 AmergeA
+  | |/
+  | o  7 65eb34ffc3a8 r7 AbaseB
+  | |
+  | o  6 0c1445abb33d r6
+  | |
+  o |  5 c8d03c1b5e94 r5 AbaseA
+  | |
+  o |  4 bebd167eb94d r4
+  | |
+  o |  3 2dc09a01254d r3
+  |/
+  o  2 01241442b3c2 r2 base
+  |
+  o  1 66f7d451a68b r1
+  |
+  o  0 1ea73414a91b r0
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  c8d03c1b5e94 6
+  0c1445abb33d 4
+  65eb34ffc3a8 5
+  c81423bf5a24 9
+  07c648efceeb 9
+  5ba9a53052ed 11
+  3e2da24aee59 12
+  26f59ee8b1d7 13
+  f7c6e7bfbcd0 13
+  39bab1cb1cbe 15
+  55bf3fdb634f 15
+  3e1560705803 17
+  4f5078f7da8a 18
+  9729470d9329 18
+  884936b34999 19
+  b115c694654e 18
+  17b6e6bac221 18
+  5ce588c2b7c5 19
+  f2bdd828a3aa 20
+  a457569c5306 21
+  ad46a4a0fc10 22
+  de05b9c29ec7 18
+  2bd677d0f13a 21
+  3bdb00d5c818 22
+  b9c3aa92fba5 23
+  f3441cd3e664 24
+  0c3f2ba59eb7 25
+  2ea3fbf151b5 26
+  47c836a1f13e 27
+  722d1b8b8942 28
+  1f4a19f83a29 20
+  01e29e20ea3f 24
+  32b41ca704e1 25
+  e3e6738c56ce 20
+  88714f4125cb 21
+  d928b4e8a515 22
+  88eace5ce682 23
+  43fc0b77ff07 21
+  4b39f229a0ce 25
+  d94da36be176 26
+  40553f55397e 21
+  bfcfd9a61e84 20
+  d6c9e2d27f14 21
+  8ecb28746ec4 21
+  673f5499c8c2 24
+  900dd066a072 25
+  97ac964e34b7 26
+  0d153e3ad632 27
+  c37e7cd9f2bd 28
+  9a67238ad1c4 29
+  76151e8066e1 20
+  c7c1497fc270 21
+  e7135b665740 22
+  29141354a762 24
+  0484d39906c8 25
+  5eec91b12a58 26
+  c84da74cf586 27
+  3871506da61e 28
+  bf6593f7e073 24
+  b33fd5ad4c0c 24
+  c713eae2d31f 20
+  d99e0f7dad5b 21
+  e4cfd6264623 22
+  fac9e582edd1 23
+  d917f77a6439 20
+  c3c7fa726f88 21
+  4f3b41956174 24
+  eed373b0090d 36
+  31d7b43cc321 24
+  698970a2480b 31
+  790cdfecd168 24
+  37ad3ab0cddf 29
+  97d19fc5236f 25
+  89a0fe204177 36
+  82238c0bc950 25
+  cd345198cf12 27
+  0bab31f71a21 31
+  1da228afcf06 31
+  b3cf98c3d587 49
+  dbde319d43a3 31
+  28be96b80dc1 36
+  469c700e9ed8 37
+  c7d3029bf731 38
+  2472d042ec95 43
+  041e1188f5f1 55
+  8b79544bb56d 48
+  8ae32c3ed670 48
+  721ba7c5f4ff 77
+  84d6ec6a8e21 65
+  01f771406cab 95
+
+Basic check
+-----------
+
+  $ hg showsort --rev 'Afinal'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  0c1445abb33d
+  65eb34ffc3a8
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  07c648efceeb
+  c81423bf5a24
+  5ba9a53052ed
+  $ checktopo Afinal
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking 01241442b3c2 ===
+  === checking 0c1445abb33d ===
+  === checking 65eb34ffc3a8 ===
+  === checking 2dc09a01254d ===
+  === checking bebd167eb94d ===
+  === checking c8d03c1b5e94 ===
+  === checking 07c648efceeb ===
+  === checking c81423bf5a24 ===
+  === checking 5ba9a53052ed ===
+  $ hg showsort --rev 'AmergeA'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  0c1445abb33d
+  65eb34ffc3a8
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  c81423bf5a24
+  $ checktopo AmergeA
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking 01241442b3c2 ===
+  === checking 0c1445abb33d ===
+  === checking 65eb34ffc3a8 ===
+  === checking 2dc09a01254d ===
+  === checking bebd167eb94d ===
+  === checking c8d03c1b5e94 ===
+  === checking c81423bf5a24 ===
+  $ hg showsort --rev 'AmergeB'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  0c1445abb33d
+  65eb34ffc3a8
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  07c648efceeb
+  $ checktopo AmergeB
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking 01241442b3c2 ===
+  === checking 0c1445abb33d ===
+  === checking 65eb34ffc3a8 ===
+  === checking 2dc09a01254d ===
+  === checking bebd167eb94d ===
+  === checking c8d03c1b5e94 ===
+  === checking 07c648efceeb ===
+
+close criss cross
+  $ hg showsort --rev 'Bfinal'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  0c1445abb33d
+  65eb34ffc3a8
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  07c648efceeb
+  c81423bf5a24
+  5ba9a53052ed
+  3e2da24aee59
+  26f59ee8b1d7
+  f7c6e7bfbcd0
+  39bab1cb1cbe
+  55bf3fdb634f
+  3e1560705803
+  $ checktopo Bfinal
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking 01241442b3c2 ===
+  === checking 0c1445abb33d ===
+  === checking 65eb34ffc3a8 ===
+  === checking 2dc09a01254d ===
+  === checking bebd167eb94d ===
+  === checking c8d03c1b5e94 ===
+  === checking 07c648efceeb ===
+  === checking c81423bf5a24 ===
+  === checking 5ba9a53052ed ===
+  === checking 3e2da24aee59 ===
+  === checking 26f59ee8b1d7 ===
+  === checking f7c6e7bfbcd0 ===
+  === checking 39bab1cb1cbe ===
+  === checking 55bf3fdb634f ===
+  === checking 3e1560705803 ===
+
+many branches criss cross
+
+  $ hg showsort --rev 'Cfinal'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  0c1445abb33d
+  65eb34ffc3a8
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  07c648efceeb
+  c81423bf5a24
+  5ba9a53052ed
+  3e2da24aee59
+  26f59ee8b1d7
+  f7c6e7bfbcd0
+  39bab1cb1cbe
+  55bf3fdb634f
+  3e1560705803
+  17b6e6bac221
+  5ce588c2b7c5
+  f2bdd828a3aa
+  a457569c5306
+  ad46a4a0fc10
+  4f5078f7da8a
+  01e29e20ea3f
+  32b41ca704e1
+  29141354a762
+  9729470d9329
+  884936b34999
+  0484d39906c8
+  5eec91b12a58
+  c84da74cf586
+  3871506da61e
+  2bd677d0f13a
+  3bdb00d5c818
+  b9c3aa92fba5
+  f3441cd3e664
+  0c3f2ba59eb7
+  2ea3fbf151b5
+  47c836a1f13e
+  722d1b8b8942
+  4b39f229a0ce
+  d94da36be176
+  eed373b0090d
+  88714f4125cb
+  d928b4e8a515
+  88eace5ce682
+  698970a2480b
+  b115c694654e
+  1f4a19f83a29
+  43fc0b77ff07
+  31d7b43cc321
+  673f5499c8c2
+  900dd066a072
+  97ac964e34b7
+  0d153e3ad632
+  c37e7cd9f2bd
+  9a67238ad1c4
+  8ecb28746ec4
+  bf6593f7e073
+  0bab31f71a21
+  1da228afcf06
+  bfcfd9a61e84
+  d6c9e2d27f14
+  de05b9c29ec7
+  40553f55397e
+  4f3b41956174
+  37ad3ab0cddf
+  c7d3029bf731
+  76151e8066e1
+  c7c1497fc270
+  e7135b665740
+  b33fd5ad4c0c
+  cd345198cf12
+  28be96b80dc1
+  c713eae2d31f
+  82238c0bc950
+  dbde319d43a3
+  8b79544bb56d
+  d917f77a6439
+  c3c7fa726f88
+  97d19fc5236f
+  2472d042ec95
+  d99e0f7dad5b
+  e4cfd6264623
+  fac9e582edd1
+  89a0fe204177
+  b3cf98c3d587
+  041e1188f5f1
+  721ba7c5f4ff
+  e3e6738c56ce
+  790cdfecd168
+  469c700e9ed8
+  8ae32c3ed670
+  84d6ec6a8e21
+  01f771406cab
+  $ checktopo Cfinal
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking 01241442b3c2 ===
+  === checking 0c1445abb33d ===
+  === checking 65eb34ffc3a8 ===
+  === checking 2dc09a01254d ===
+  === checking bebd167eb94d ===
+  === checking c8d03c1b5e94 ===
+  === checking 07c648efceeb ===
+  === checking c81423bf5a24 ===
+  === checking 5ba9a53052ed ===
+  === checking 3e2da24aee59 ===
+  === checking 26f59ee8b1d7 ===
+  === checking f7c6e7bfbcd0 ===
+  === checking 39bab1cb1cbe ===
+  === checking 55bf3fdb634f ===
+  === checking 3e1560705803 ===
+  === checking 17b6e6bac221 ===
+  === checking 5ce588c2b7c5 ===
+  === checking f2bdd828a3aa ===
+  === checking a457569c5306 ===
+  === checking ad46a4a0fc10 ===
+  === checking 4f5078f7da8a ===
+  === checking 01e29e20ea3f ===
+  === checking 32b41ca704e1 ===
+  === checking 29141354a762 ===
+  === checking 9729470d9329 ===
+  === checking 884936b34999 ===
+  === checking 0484d39906c8 ===
+  === checking 5eec91b12a58 ===
+  === checking c84da74cf586 ===
+  === checking 3871506da61e ===
+  === checking 2bd677d0f13a ===
+  === checking 3bdb00d5c818 ===
+  === checking b9c3aa92fba5 ===
+  === checking f3441cd3e664 ===
+  === checking 0c3f2ba59eb7 ===
+  === checking 2ea3fbf151b5 ===
+  === checking 47c836a1f13e ===
+  === checking 722d1b8b8942 ===
+  === checking 4b39f229a0ce ===
+  === checking d94da36be176 ===
+  === checking eed373b0090d ===
+  === checking 88714f4125cb ===
+  === checking d928b4e8a515 ===
+  === checking 88eace5ce682 ===
+  === checking 698970a2480b ===
+  === checking b115c694654e ===
+  === checking 1f4a19f83a29 ===
+  === checking 43fc0b77ff07 ===
+  === checking 31d7b43cc321 ===
+  === checking 673f5499c8c2 ===
+  === checking 900dd066a072 ===
+  === checking 97ac964e34b7 ===
+  === checking 0d153e3ad632 ===
+  === checking c37e7cd9f2bd ===
+  === checking 9a67238ad1c4 ===
+  === checking 8ecb28746ec4 ===
+  === checking bf6593f7e073 ===
+  === checking 0bab31f71a21 ===
+  === checking 1da228afcf06 ===
+  === checking bfcfd9a61e84 ===
+  === checking d6c9e2d27f14 ===
+  === checking de05b9c29ec7 ===
+  === checking 40553f55397e ===
+  === checking 4f3b41956174 ===
+  === checking 37ad3ab0cddf ===
+  === checking c7d3029bf731 ===
+  === checking 76151e8066e1 ===
+  === checking c7c1497fc270 ===
+  === checking e7135b665740 ===
+  === checking b33fd5ad4c0c ===
+  === checking cd345198cf12 ===
+  === checking 28be96b80dc1 ===
+  === checking c713eae2d31f ===
+  === checking 82238c0bc950 ===
+  === checking dbde319d43a3 ===
+  === checking 8b79544bb56d ===
+  === checking d917f77a6439 ===
+  === checking c3c7fa726f88 ===
+  === checking 97d19fc5236f ===
+  === checking 2472d042ec95 ===
+  === checking d99e0f7dad5b ===
+  === checking e4cfd6264623 ===
+  === checking fac9e582edd1 ===
+  === checking 89a0fe204177 ===
+  === checking b3cf98c3d587 ===
+  === checking 041e1188f5f1 ===
+  === checking 721ba7c5f4ff ===
+  === checking e3e6738c56ce ===
+  === checking 790cdfecd168 ===
+  === checking 469c700e9ed8 ===
+  === checking 8ae32c3ed670 ===
+  === checking 84d6ec6a8e21 ===
+  === checking 01f771406cab ===
+
+Test stability of this mess
+---------------------------
+
+  $ hg log -r tip
+  94 01f771406cab r94 Cfinal tip
+  $ hg showsort --rev 'all()' > ../crisscross.source.order
+  $ cd ..
+
+  $ hg clone crisscross_A crisscross_random --rev 0
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd crisscross_random
+  $ for x in `python ../random_rev.py 50 44`; do
+  >   # using python to benefit from the random seed
+  >   hg pull -r $x --quiet
+  > done;
+  $ hg pull --quiet
+
+  $ hg showsort --rev 'all()' > ../crisscross.random.order
+  $ python "$RUNTESTDIR/md5sum.py" ../crisscross.*.order
+  d9aab0d1907d5cf64d205a8b9036e959  ../crisscross.random.order
+  d9aab0d1907d5cf64d205a8b9036e959  ../crisscross.source.order
+  $ diff -u ../crisscross.*.order
+  $ hg showsort --rev 'all()'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  0c1445abb33d
+  65eb34ffc3a8
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  07c648efceeb
+  c81423bf5a24
+  5ba9a53052ed
+  3e2da24aee59
+  26f59ee8b1d7
+  f7c6e7bfbcd0
+  39bab1cb1cbe
+  55bf3fdb634f
+  3e1560705803
+  17b6e6bac221
+  5ce588c2b7c5
+  f2bdd828a3aa
+  a457569c5306
+  ad46a4a0fc10
+  4f5078f7da8a
+  01e29e20ea3f
+  32b41ca704e1
+  29141354a762
+  9729470d9329
+  884936b34999
+  0484d39906c8
+  5eec91b12a58
+  c84da74cf586
+  3871506da61e
+  2bd677d0f13a
+  3bdb00d5c818
+  b9c3aa92fba5
+  f3441cd3e664
+  0c3f2ba59eb7
+  2ea3fbf151b5
+  47c836a1f13e
+  722d1b8b8942
+  4b39f229a0ce
+  d94da36be176
+  eed373b0090d
+  88714f4125cb
+  d928b4e8a515
+  88eace5ce682
+  698970a2480b
+  b115c694654e
+  1f4a19f83a29
+  43fc0b77ff07
+  31d7b43cc321
+  673f5499c8c2
+  900dd066a072
+  97ac964e34b7
+  0d153e3ad632
+  c37e7cd9f2bd
+  9a67238ad1c4
+  8ecb28746ec4
+  bf6593f7e073
+  0bab31f71a21
+  1da228afcf06
+  bfcfd9a61e84
+  d6c9e2d27f14
+  de05b9c29ec7
+  40553f55397e
+  4f3b41956174
+  37ad3ab0cddf
+  c7d3029bf731
+  76151e8066e1
+  c7c1497fc270
+  e7135b665740
+  b33fd5ad4c0c
+  cd345198cf12
+  28be96b80dc1
+  c713eae2d31f
+  82238c0bc950
+  dbde319d43a3
+  8b79544bb56d
+  d917f77a6439
+  c3c7fa726f88
+  97d19fc5236f
+  2472d042ec95
+  d99e0f7dad5b
+  e4cfd6264623
+  fac9e582edd1
+  89a0fe204177
+  b3cf98c3d587
+  041e1188f5f1
+  721ba7c5f4ff
+  e3e6738c56ce
+  790cdfecd168
+  469c700e9ed8
+  8ae32c3ed670
+  84d6ec6a8e21
+  01f771406cab
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-stablesort-branchpoint.t	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,846 @@
+Test for stable ordering capabilities
+=====================================
+
+  $ . $TESTDIR/testlib/pythonpath.sh
+
+  $ cat << EOF >> $HGRCPATH
+  > [extensions]
+  > hgext3rd.evolve =
+  > [ui]
+  > logtemplate = "{rev} {node|short} {desc} {tags}\n"
+  > [alias]
+  > showsort = debugstablesort --template="{node|short}\n" --method branchpoint
+  > EOF
+
+
+
+  $ checktopo () {
+  >     seen='null';
+  >     for node in `hg showsort --rev "$1"`; do
+  >         echo "=== checking $node ===";
+  >         hg log --rev "($seen) and $node::";
+  >         seen="${seen}+${node}";
+  >     done;
+  > }
+
+  $ cat << EOF >> random_rev.py
+  > import random
+  > import sys
+  > 
+  > loop = int(sys.argv[1])
+  > var = int(sys.argv[2])
+  > for x in range(loop):
+  >     print(x + random.randint(0, var))
+  > EOF
+
+Basic tests
+===========
+(no criss cross merge)
+
+Smoke tests
+-----------
+
+Starts with a "simple case"
+
+  $ hg init repo_A
+  $ cd repo_A
+  $ hg debugbuilddag '
+  > ..:g   # 2 nodes, tagged "g"
+  > <2.:h   # another node base one -2 -> 0, tagged "h"
+  > *1/2:m # merge -1 and -2 (1, 2), tagged "m"
+  > <2+2:i # 2 nodes based on -2, tag head as "i"
+  > .:c    # 1 node tagged "c"
+  > <m+3:a # 3 nodes base on the "m" tag
+  > <2.:b  # 1 node based on -2; tagged "b"
+  > <m+2:d # 2 nodes from "m" tagged "d"
+  > <2.:e  # 1 node based on -2, tagged "e"
+  > <m+1:f # 1 node based on "m" tagged "f"
+  > <i/f   # merge "i" and "f"
+  > '
+  $ hg log -G
+  o    15 1d8d22637c2d r15 tip
+  |\
+  | o  14 43227190fef8 r14 f
+  | |
+  | | o  13 b4594d867745 r13 e
+  | | |
+  | | | o  12 e46a4836065c r12 d
+  | | |/
+  | | o  11 bab5d5bf48bd r11
+  | |/
+  | | o  10 ff43616e5d0f r10 b
+  | | |
+  | | | o  9 dcbb326fdec2 r9 a
+  | | |/
+  | | o  8 d62d843c9a01 r8
+  | | |
+  | | o  7 e7d9710d9fc6 r7
+  | |/
+  +---o  6 2702dd0c91e7 r6 c
+  | |
+  o |  5 f0f3ef9a6cd5 r5 i
+  | |
+  o |  4 4c748ffd1a46 r4
+  | |
+  | o  3 2b6d669947cd r3 m
+  |/|
+  o |  2 fa942426a6fd r2 h
+  | |
+  | o  1 66f7d451a68b r1 g
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+  4c748ffd1a46 3
+  f0f3ef9a6cd5 4
+  2702dd0c91e7 5
+  e7d9710d9fc6 5
+  d62d843c9a01 6
+  dcbb326fdec2 7
+  ff43616e5d0f 7
+  bab5d5bf48bd 5
+  e46a4836065c 6
+  b4594d867745 6
+  43227190fef8 5
+  1d8d22637c2d 8
+  $ hg showsort --rev 'all()' --traceback
+  1ea73414a91b
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  43227190fef8
+  bab5d5bf48bd
+  b4594d867745
+  e46a4836065c
+  e7d9710d9fc6
+  d62d843c9a01
+  dcbb326fdec2
+  ff43616e5d0f
+  4c748ffd1a46
+  f0f3ef9a6cd5
+  1d8d22637c2d
+  2702dd0c91e7
+
+Verify the topological order
+----------------------------
+
+Check we we did not issued a node before on ancestor
+
+output of log should be empty
+
+  $ checktopo 'all()'
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking fa942426a6fd ===
+  === checking 2b6d669947cd ===
+  === checking 43227190fef8 ===
+  === checking bab5d5bf48bd ===
+  === checking b4594d867745 ===
+  === checking e46a4836065c ===
+  === checking e7d9710d9fc6 ===
+  === checking d62d843c9a01 ===
+  === checking dcbb326fdec2 ===
+  === checking ff43616e5d0f ===
+  === checking 4c748ffd1a46 ===
+  === checking f0f3ef9a6cd5 ===
+  === checking 1d8d22637c2d ===
+  === checking 2702dd0c91e7 ===
+
+Check stability
+===============
+
+have repo with changesets in orders
+
+  $ cd ..
+  $ hg -R repo_A log -G > A.log
+  $ hg clone repo_A repo_B --rev 5
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo_B pull --rev 13
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R repo_B pull --rev 14
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ hg -R repo_B pull
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 0 changes to 0 files (+3 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ hg -R repo_B log -G
+  o    15 1d8d22637c2d r15 tip
+  |\
+  | | o  14 e46a4836065c r12
+  | | |
+  | | | o  13 ff43616e5d0f r10
+  | | | |
+  | | | | o  12 dcbb326fdec2 r9
+  | | | |/
+  | | | o  11 d62d843c9a01 r8
+  | | | |
+  | | | o  10 e7d9710d9fc6 r7
+  | | | |
+  +-------o  9 2702dd0c91e7 r6
+  | | | |
+  | o---+  8 43227190fef8 r14
+  |  / /
+  | +---o  7 b4594d867745 r13
+  | | |
+  | o |  6 bab5d5bf48bd r11
+  | |/
+  | o    5 2b6d669947cd r3
+  | |\
+  | | o  4 66f7d451a68b r1
+  | | |
+  @ | |  3 f0f3ef9a6cd5 r5
+  | | |
+  o | |  2 4c748ffd1a46 r4
+  |/ /
+  o /  1 fa942426a6fd r2
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ hg -R repo_B debugdepth -r 'all()'
+  1ea73414a91b 1
+  fa942426a6fd 2
+  4c748ffd1a46 3
+  f0f3ef9a6cd5 4
+  66f7d451a68b 2
+  2b6d669947cd 4
+  bab5d5bf48bd 5
+  b4594d867745 6
+  43227190fef8 5
+  2702dd0c91e7 5
+  e7d9710d9fc6 5
+  d62d843c9a01 6
+  dcbb326fdec2 7
+  ff43616e5d0f 7
+  e46a4836065c 6
+  1d8d22637c2d 8
+  $ hg -R repo_B log -G > B.log
+
+  $ hg clone repo_A repo_C --rev 10
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo_C pull --rev 12
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R repo_C pull --rev 15
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ hg -R repo_C pull
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 0 changes to 0 files (+3 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ hg -R repo_C log -G
+  o  15 b4594d867745 r13 tip
+  |
+  | o  14 dcbb326fdec2 r9
+  | |
+  | | o  13 2702dd0c91e7 r6
+  | | |
+  | | | o  12 1d8d22637c2d r15
+  | | |/|
+  | | | o  11 43227190fef8 r14
+  | | | |
+  | | o |  10 f0f3ef9a6cd5 r5
+  | | | |
+  | | o |  9 4c748ffd1a46 r4
+  | | | |
+  +-------o  8 e46a4836065c r12
+  | | | |
+  o-----+  7 bab5d5bf48bd r11
+   / / /
+  +-----@  6 ff43616e5d0f r10
+  | | |
+  o | |  5 d62d843c9a01 r8
+  | | |
+  o---+  4 e7d9710d9fc6 r7
+   / /
+  | o  3 2b6d669947cd r3
+  |/|
+  o |  2 fa942426a6fd r2
+  | |
+  | o  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ hg -R repo_C log -G > C.log
+
+  $ hg clone repo_A repo_D --rev 2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo_D pull --rev 10
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 0 changes to 0 files
+  (run 'hg update' to get a working copy)
+  $ hg -R repo_D pull --rev 15
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R repo_D pull
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 0 changes to 0 files (+4 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ hg -R repo_D log -G
+  o  15 b4594d867745 r13 tip
+  |
+  | o  14 e46a4836065c r12
+  |/
+  o  13 bab5d5bf48bd r11
+  |
+  | o  12 dcbb326fdec2 r9
+  | |
+  | | o  11 2702dd0c91e7 r6
+  | | |
+  | | | o  10 1d8d22637c2d r15
+  | | |/|
+  +-----o  9 43227190fef8 r14
+  | | |
+  | | o  8 f0f3ef9a6cd5 r5
+  | | |
+  | | o  7 4c748ffd1a46 r4
+  | | |
+  | +---o  6 ff43616e5d0f r10
+  | | |
+  | o |  5 d62d843c9a01 r8
+  | | |
+  | o |  4 e7d9710d9fc6 r7
+  |/ /
+  o |  3 2b6d669947cd r3
+  |\|
+  o |  2 66f7d451a68b r1
+  | |
+  | @  1 fa942426a6fd r2
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ hg -R repo_D log -G > D.log
+
+check the log output are different
+
+  $ python "$RUNTESTDIR/md5sum.py" *.log
+  55919ebc9c02f28070cf3255b1690f8c  A.log
+  c6244b76a60d0707767dc71780e544f3  B.log
+  4d8b08b8c50ecbdd2460a62e5852d84d  C.log
+  0f327003593b50b9591bea8ee28acb81  D.log
+
+bug stable ordering should be identical
+---------------------------------------
+
+  $ repos="A B C D "
+
+for 'all()'
+
+  $ for x in $repos; do
+  >     echo $x
+  >     hg -R repo_$x showsort --rev 'all()' > ${x}.all.order;
+  > done
+  A
+  B
+  C
+  D
+
+  $ python "$RUNTESTDIR/md5sum.py" *.all.order
+  0c6b2e6f15249c0359b0f93e28c5bd1c  A.all.order
+  0c6b2e6f15249c0359b0f93e28c5bd1c  B.all.order
+  0c6b2e6f15249c0359b0f93e28c5bd1c  C.all.order
+  0c6b2e6f15249c0359b0f93e28c5bd1c  D.all.order
+
+one specific head
+
+  $ for x in $repos; do
+  >     hg -R repo_$x showsort --rev 'b4594d867745' > ${x}.b4594d867745.order;
+  > done
+
+  $ python "$RUNTESTDIR/md5sum.py" *.b4594d867745.order
+  5c40900a22008f24eab8dfe2f30ad79f  A.b4594d867745.order
+  5c40900a22008f24eab8dfe2f30ad79f  B.b4594d867745.order
+  5c40900a22008f24eab8dfe2f30ad79f  C.b4594d867745.order
+  5c40900a22008f24eab8dfe2f30ad79f  D.b4594d867745.order
+
+one secific heads, that is a merge
+
+  $ for x in $repos; do
+  >     hg -R repo_$x showsort --rev '1d8d22637c2d' > ${x}.1d8d22637c2d.order;
+  > done
+
+  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.order
+  77dc20a6f86db9103df8edaae9ad2754  A.1d8d22637c2d.order
+  77dc20a6f86db9103df8edaae9ad2754  B.1d8d22637c2d.order
+  77dc20a6f86db9103df8edaae9ad2754  C.1d8d22637c2d.order
+  77dc20a6f86db9103df8edaae9ad2754  D.1d8d22637c2d.order
+
+changeset that are not heads
+
+  $ for x in $repos; do
+  >     hg -R repo_$x showsort --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.order;
+  > done
+
+  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  A.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  D.non-heads.order
+
+Check with different subset
+
+  $ hg clone repo_A repo_E --rev "43227190fef8"
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo_E pull --rev e7d9710d9fc6
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+
+  $ hg clone repo_A repo_F --rev "1d8d22637c2d"
+  adding changesets
+  adding manifests
+  adding file changes
+  added 8 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo_F pull --rev d62d843c9a01
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+
+  $ hg clone repo_A repo_G --rev "e7d9710d9fc6"
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo_G pull --rev 43227190fef8
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R repo_G pull --rev 2702dd0c91e7
+  pulling from $TESTTMP/repo_A (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+
+  $ for x in E F G; do
+  >     hg -R repo_$x showsort --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.order;
+  > done
+
+  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  A.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  D.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  E.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  F.non-heads.order
+  94e0ea8cdade135dabde4ec5e9954329  G.non-heads.order
+
+Multiple recursions
+===================
+
+  $ hg init recursion_A
+  $ cd recursion_A
+  $ hg debugbuilddag '
+  > .:base
+  > +3:A
+  > <base.:B
+  > +2/A:C
+  > <A+2:D
+  > <B./D:E
+  > +3:F
+  > <C+3/E
+  > +2
+  > '
+  $ hg log -G
+  o  20 160a7a0adbf4 r20 tip
+  |
+  o  19 1c645e73dbc6 r19
+  |
+  o    18 0496f0a6a143 r18
+  |\
+  | o  17 d64d500024d1 r17
+  | |
+  | o  16 4dbf739dd63f r16
+  | |
+  | o  15 9fff0871d230 r15
+  | |
+  | | o  14 4bbfc6078919 r14 F
+  | | |
+  | | o  13 013b27f11536 r13
+  | | |
+  +---o  12 a66b68853635 r12
+  | |
+  o |    11 001194dd78d5 r11 E
+  |\ \
+  | o |  10 6ee532b68cfa r10
+  | | |
+  o | |  9 529dfc5bb875 r9 D
+  | | |
+  o | |  8 abf57d94268b r8
+  | | |
+  +---o  7 5f18015f9110 r7 C
+  | | |
+  | | o  6 a2f58e9c1e56 r6
+  | | |
+  | | o  5 3a367db1fabc r5
+  | |/
+  | o  4 e7bd5218ca15 r4 B
+  | |
+  o |  3 2dc09a01254d r3 A
+  | |
+  o |  2 01241442b3c2 r2
+  | |
+  o |  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0 base
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  e7bd5218ca15 2
+  3a367db1fabc 3
+  a2f58e9c1e56 4
+  5f18015f9110 8
+  abf57d94268b 5
+  529dfc5bb875 6
+  6ee532b68cfa 3
+  001194dd78d5 9
+  a66b68853635 10
+  013b27f11536 11
+  4bbfc6078919 12
+  9fff0871d230 9
+  4dbf739dd63f 10
+  d64d500024d1 11
+  0496f0a6a143 16
+  1c645e73dbc6 17
+  160a7a0adbf4 18
+  $ hg showsort --rev 'all()'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  3a367db1fabc
+  a2f58e9c1e56
+  5f18015f9110
+  9fff0871d230
+  4dbf739dd63f
+  d64d500024d1
+  6ee532b68cfa
+  001194dd78d5
+  0496f0a6a143
+  1c645e73dbc6
+  160a7a0adbf4
+  a66b68853635
+  013b27f11536
+  4bbfc6078919
+  $ checktopo 'all()'
+  === checking 1ea73414a91b ===
+  === checking 66f7d451a68b ===
+  === checking 01241442b3c2 ===
+  === checking 2dc09a01254d ===
+  === checking abf57d94268b ===
+  === checking 529dfc5bb875 ===
+  === checking e7bd5218ca15 ===
+  === checking 3a367db1fabc ===
+  === checking a2f58e9c1e56 ===
+  === checking 5f18015f9110 ===
+  === checking 9fff0871d230 ===
+  === checking 4dbf739dd63f ===
+  === checking d64d500024d1 ===
+  === checking 6ee532b68cfa ===
+  === checking 001194dd78d5 ===
+  === checking 0496f0a6a143 ===
+  === checking 1c645e73dbc6 ===
+  === checking 160a7a0adbf4 ===
+  === checking a66b68853635 ===
+  === checking 013b27f11536 ===
+  === checking 4bbfc6078919 ===
+  $ hg showsort --rev 'all()' > ../multiple.source.order
+  $ hg log -r tip
+  20 160a7a0adbf4 r20 tip
+  $ cd ..
+
+  $ hg clone recursion_A recursion_random --rev 0
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files
+  updating to branch default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd recursion_random
+  $ for x in `python ../random_rev.py 15 5`; do
+  >   # using python to benefit from the random seed
+  >   hg pull -r $x --quiet
+  > done;
+  $ hg pull --quiet
+  $ hg showsort --rev 'all()' > ../multiple.random.order
+  $ python "$RUNTESTDIR/md5sum.py" ../multiple.*.order
+  6ff802a0a5f0a3ddd82b25f860238fbd  ../multiple.random.order
+  6ff802a0a5f0a3ddd82b25f860238fbd  ../multiple.source.order
+  $ hg showsort --rev 'all()'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  3a367db1fabc
+  a2f58e9c1e56
+  5f18015f9110
+  9fff0871d230
+  4dbf739dd63f
+  d64d500024d1
+  6ee532b68cfa
+  001194dd78d5
+  0496f0a6a143
+  1c645e73dbc6
+  160a7a0adbf4
+  a66b68853635
+  013b27f11536
+  4bbfc6078919
+  $ cd ..
+
+
+Test behavior with oedipus merges
+=================================
+
+  $ hg init recursion_oedipus
+  $ cd recursion_oedipus
+  $ echo base > base
+  $ hg add base
+  $ hg ci -m base
+  $ hg branch foo
+  marked working directory as branch foo
+  (branches are permanent and global, did you want a bookmark?)
+  $ echo foo1 > foo1
+  $ hg add foo1
+  $ hg ci -m foo1
+  $ echo foo2 > foo2
+  $ hg add foo2
+  $ hg ci -m foo2
+  $ hg up default
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg merge foo
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m oedipus_merge
+  $ echo default1 > default1
+  $ hg add default1
+  $ hg ci -m default1
+  $ hg log -G 
+  @  4 7f2454f6b04f default1 tip
+  |
+  o    3 ed776db7ed63 oedipus_merge
+  |\
+  | o  2 0dedbcd995b6 foo2
+  | |
+  | o  1 47da0f2c25e2 foo1
+  |/
+  o  0 d20a80d4def3 base
+  
+  $ hg debugdepth -r 'all()'
+  d20a80d4def3 1
+  47da0f2c25e2 2
+  0dedbcd995b6 3
+  ed776db7ed63 4
+  7f2454f6b04f 5
+  $ hg showsort --rev '.'
+  d20a80d4def3
+  47da0f2c25e2
+  0dedbcd995b6
+  ed776db7ed63
+  7f2454f6b04f
+
+Merge two branches with their own independant internal merge.
+-------------------------------------------------------------
+
+  $ hg init subbranch
+  $ cd subbranch
+  $ hg debugbuilddag '
+  > .:base
+  > +3:leftBranch
+  > +2:leftA
+  > <leftBranch.+2:leftB
+  > /leftA:leftMerge
+  > <base+2:rightBranch
+  > +4:rightA
+  > <rightBranch.+1:rightB
+  > /rightA:rightMerge
+  > +3/leftMerge
+  > '
+  $ hg log -G
+  o    22 56526aefbff4 r22 tip
+  |\
+  | o  21 d4422659bc40 r21
+  | |
+  | o  20 6a97ef856f90 r20
+  | |
+  | o  19 5648bbf0e38b r19
+  | |
+  | o    18 4442c125b80d r18 rightMerge
+  | |\
+  | | o  17 65e683dd6db4 r17 rightB
+  | | |
+  | | o  16 5188cf52b7b7 r16
+  | | |
+  | o |  15 191bac7bf37c r15 rightA
+  | | |
+  | o |  14 5cb8e6902ff3 r14
+  | | |
+  | o |  13 448a7ac3ab1f r13
+  | | |
+  | o |  12 ee222cc71ce6 r12
+  | |/
+  | o  11 e5c0d969abc4 r11 rightBranch
+  | |
+  | o  10 7cc044fdf4a7 r10
+  | |
+  o |    9 9f6c364a3574 r9 leftMerge
+  |\ \
+  | o |  8 588f0bc87ecd r8 leftB
+  | | |
+  | o |  7 e2317cea05f7 r7
+  | | |
+  | o |  6 c2c595bcd4c6 r6
+  | | |
+  o | |  5 c8d03c1b5e94 r5 leftA
+  | | |
+  o | |  4 bebd167eb94d r4
+  |/ /
+  o |  3 2dc09a01254d r3 leftBranch
+  | |
+  o |  2 01241442b3c2 r2
+  | |
+  o |  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0 base
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  c8d03c1b5e94 6
+  c2c595bcd4c6 5
+  e2317cea05f7 6
+  588f0bc87ecd 7
+  9f6c364a3574 10
+  7cc044fdf4a7 2
+  e5c0d969abc4 3
+  ee222cc71ce6 4
+  448a7ac3ab1f 5
+  5cb8e6902ff3 6
+  191bac7bf37c 7
+  5188cf52b7b7 4
+  65e683dd6db4 5
+  4442c125b80d 10
+  5648bbf0e38b 11
+  6a97ef856f90 12
+  d4422659bc40 13
+  56526aefbff4 23
+  $ hg showsort --rev 'tip'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  bebd167eb94d
+  c8d03c1b5e94
+  c2c595bcd4c6
+  e2317cea05f7
+  588f0bc87ecd
+  9f6c364a3574
+  7cc044fdf4a7
+  e5c0d969abc4
+  5188cf52b7b7
+  65e683dd6db4
+  ee222cc71ce6
+  448a7ac3ab1f
+  5cb8e6902ff3
+  191bac7bf37c
+  4442c125b80d
+  5648bbf0e38b
+  6a97ef856f90
+  d4422659bc40
+  56526aefbff4
+  $ cd ..
--- a/tests/test-stablesort-criss-cross.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-stablesort-criss-cross.t	Mon Dec 11 18:30:15 2017 +0100
@@ -9,7 +9,7 @@
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
   > [alias]
-  > showsort = debugstablesort --template="{node|short}\n"
+  > showsort = debugstablesort --template="{node|short}\n" --method headcached
   > EOF
 
   $ checktopo () {
@@ -293,6 +293,102 @@
   |
   o  0 1ea73414a91b r0
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  c8d03c1b5e94 6
+  0c1445abb33d 4
+  65eb34ffc3a8 5
+  c81423bf5a24 9
+  07c648efceeb 9
+  5ba9a53052ed 11
+  3e2da24aee59 12
+  26f59ee8b1d7 13
+  f7c6e7bfbcd0 13
+  39bab1cb1cbe 15
+  55bf3fdb634f 15
+  3e1560705803 17
+  4f5078f7da8a 18
+  9729470d9329 18
+  884936b34999 19
+  b115c694654e 18
+  17b6e6bac221 18
+  5ce588c2b7c5 19
+  f2bdd828a3aa 20
+  a457569c5306 21
+  ad46a4a0fc10 22
+  de05b9c29ec7 18
+  2bd677d0f13a 21
+  3bdb00d5c818 22
+  b9c3aa92fba5 23
+  f3441cd3e664 24
+  0c3f2ba59eb7 25
+  2ea3fbf151b5 26
+  47c836a1f13e 27
+  722d1b8b8942 28
+  1f4a19f83a29 20
+  01e29e20ea3f 24
+  32b41ca704e1 25
+  e3e6738c56ce 20
+  88714f4125cb 21
+  d928b4e8a515 22
+  88eace5ce682 23
+  43fc0b77ff07 21
+  4b39f229a0ce 25
+  d94da36be176 26
+  40553f55397e 21
+  bfcfd9a61e84 20
+  d6c9e2d27f14 21
+  8ecb28746ec4 21
+  673f5499c8c2 24
+  900dd066a072 25
+  97ac964e34b7 26
+  0d153e3ad632 27
+  c37e7cd9f2bd 28
+  9a67238ad1c4 29
+  76151e8066e1 20
+  c7c1497fc270 21
+  e7135b665740 22
+  29141354a762 24
+  0484d39906c8 25
+  5eec91b12a58 26
+  c84da74cf586 27
+  3871506da61e 28
+  bf6593f7e073 24
+  b33fd5ad4c0c 24
+  c713eae2d31f 20
+  d99e0f7dad5b 21
+  e4cfd6264623 22
+  fac9e582edd1 23
+  d917f77a6439 20
+  c3c7fa726f88 21
+  4f3b41956174 24
+  eed373b0090d 36
+  31d7b43cc321 24
+  698970a2480b 31
+  790cdfecd168 24
+  37ad3ab0cddf 29
+  97d19fc5236f 25
+  89a0fe204177 36
+  82238c0bc950 25
+  cd345198cf12 27
+  0bab31f71a21 31
+  1da228afcf06 31
+  b3cf98c3d587 49
+  dbde319d43a3 31
+  28be96b80dc1 36
+  469c700e9ed8 37
+  c7d3029bf731 38
+  2472d042ec95 43
+  041e1188f5f1 55
+  8b79544bb56d 48
+  8ae32c3ed670 48
+  721ba7c5f4ff 77
+  84d6ec6a8e21 65
+  01f771406cab 95
 
 Basic check
 -----------
@@ -420,21 +516,15 @@
   39bab1cb1cbe
   55bf3fdb634f
   3e1560705803
-  17b6e6bac221
-  5ce588c2b7c5
-  f2bdd828a3aa
-  a457569c5306
-  ad46a4a0fc10
-  4f5078f7da8a
-  01e29e20ea3f
-  32b41ca704e1
-  29141354a762
   9729470d9329
   884936b34999
-  0484d39906c8
-  5eec91b12a58
-  c84da74cf586
-  3871506da61e
+  b115c694654e
+  8ecb28746ec4
+  de05b9c29ec7
+  d917f77a6439
+  c3c7fa726f88
+  97d19fc5236f
+  4f5078f7da8a
   2bd677d0f13a
   3bdb00d5c818
   b9c3aa92fba5
@@ -443,55 +533,61 @@
   2ea3fbf151b5
   47c836a1f13e
   722d1b8b8942
+  17b6e6bac221
+  5ce588c2b7c5
+  f2bdd828a3aa
+  a457569c5306
+  ad46a4a0fc10
   4b39f229a0ce
   d94da36be176
   eed373b0090d
-  88714f4125cb
-  d928b4e8a515
-  88eace5ce682
-  698970a2480b
-  b115c694654e
-  1f4a19f83a29
-  43fc0b77ff07
-  31d7b43cc321
+  2472d042ec95
   673f5499c8c2
   900dd066a072
   97ac964e34b7
   0d153e3ad632
   c37e7cd9f2bd
   9a67238ad1c4
-  8ecb28746ec4
-  bf6593f7e073
-  0bab31f71a21
-  1da228afcf06
-  bfcfd9a61e84
-  d6c9e2d27f14
-  de05b9c29ec7
-  40553f55397e
-  4f3b41956174
-  37ad3ab0cddf
-  c7d3029bf731
-  76151e8066e1
-  c7c1497fc270
-  e7135b665740
-  b33fd5ad4c0c
-  cd345198cf12
-  28be96b80dc1
-  c713eae2d31f
-  82238c0bc950
-  dbde319d43a3
-  8b79544bb56d
-  d917f77a6439
-  c3c7fa726f88
-  97d19fc5236f
-  2472d042ec95
   d99e0f7dad5b
   e4cfd6264623
   fac9e582edd1
   89a0fe204177
   b3cf98c3d587
   041e1188f5f1
+  0484d39906c8
+  5eec91b12a58
+  c84da74cf586
+  3871506da61e
+  bf6593f7e073
+  1da228afcf06
+  4f3b41956174
+  bfcfd9a61e84
+  d6c9e2d27f14
+  37ad3ab0cddf
+  c7d3029bf731
+  1f4a19f83a29
+  43fc0b77ff07
+  31d7b43cc321
+  c713eae2d31f
+  76151e8066e1
+  c7c1497fc270
+  e7135b665740
+  82238c0bc950
+  dbde319d43a3
+  8b79544bb56d
   721ba7c5f4ff
+  01e29e20ea3f
+  32b41ca704e1
+  88714f4125cb
+  d928b4e8a515
+  88eace5ce682
+  698970a2480b
+  29141354a762
+  b33fd5ad4c0c
+  cd345198cf12
+  28be96b80dc1
+  0bab31f71a21
+  40553f55397e
   e3e6738c56ce
   790cdfecd168
   469c700e9ed8
@@ -516,21 +612,15 @@
   === checking 39bab1cb1cbe ===
   === checking 55bf3fdb634f ===
   === checking 3e1560705803 ===
-  === checking 17b6e6bac221 ===
-  === checking 5ce588c2b7c5 ===
-  === checking f2bdd828a3aa ===
-  === checking a457569c5306 ===
-  === checking ad46a4a0fc10 ===
-  === checking 4f5078f7da8a ===
-  === checking 01e29e20ea3f ===
-  === checking 32b41ca704e1 ===
-  === checking 29141354a762 ===
   === checking 9729470d9329 ===
   === checking 884936b34999 ===
-  === checking 0484d39906c8 ===
-  === checking 5eec91b12a58 ===
-  === checking c84da74cf586 ===
-  === checking 3871506da61e ===
+  === checking b115c694654e ===
+  === checking 8ecb28746ec4 ===
+  === checking de05b9c29ec7 ===
+  === checking d917f77a6439 ===
+  === checking c3c7fa726f88 ===
+  === checking 97d19fc5236f ===
+  === checking 4f5078f7da8a ===
   === checking 2bd677d0f13a ===
   === checking 3bdb00d5c818 ===
   === checking b9c3aa92fba5 ===
@@ -539,61 +629,179 @@
   === checking 2ea3fbf151b5 ===
   === checking 47c836a1f13e ===
   === checking 722d1b8b8942 ===
+  === checking 17b6e6bac221 ===
+  === checking 5ce588c2b7c5 ===
+  === checking f2bdd828a3aa ===
+  === checking a457569c5306 ===
+  === checking ad46a4a0fc10 ===
   === checking 4b39f229a0ce ===
   === checking d94da36be176 ===
   === checking eed373b0090d ===
-  === checking 88714f4125cb ===
-  === checking d928b4e8a515 ===
-  === checking 88eace5ce682 ===
-  === checking 698970a2480b ===
-  === checking b115c694654e ===
-  === checking 1f4a19f83a29 ===
-  === checking 43fc0b77ff07 ===
-  === checking 31d7b43cc321 ===
+  === checking 2472d042ec95 ===
   === checking 673f5499c8c2 ===
   === checking 900dd066a072 ===
   === checking 97ac964e34b7 ===
   === checking 0d153e3ad632 ===
   === checking c37e7cd9f2bd ===
   === checking 9a67238ad1c4 ===
-  === checking 8ecb28746ec4 ===
-  === checking bf6593f7e073 ===
-  === checking 0bab31f71a21 ===
-  === checking 1da228afcf06 ===
-  === checking bfcfd9a61e84 ===
-  === checking d6c9e2d27f14 ===
-  === checking de05b9c29ec7 ===
-  === checking 40553f55397e ===
-  === checking 4f3b41956174 ===
-  === checking 37ad3ab0cddf ===
-  === checking c7d3029bf731 ===
-  === checking 76151e8066e1 ===
-  === checking c7c1497fc270 ===
-  === checking e7135b665740 ===
-  === checking b33fd5ad4c0c ===
-  === checking cd345198cf12 ===
-  === checking 28be96b80dc1 ===
-  === checking c713eae2d31f ===
-  === checking 82238c0bc950 ===
-  === checking dbde319d43a3 ===
-  === checking 8b79544bb56d ===
-  === checking d917f77a6439 ===
-  === checking c3c7fa726f88 ===
-  === checking 97d19fc5236f ===
-  === checking 2472d042ec95 ===
   === checking d99e0f7dad5b ===
   === checking e4cfd6264623 ===
   === checking fac9e582edd1 ===
   === checking 89a0fe204177 ===
   === checking b3cf98c3d587 ===
   === checking 041e1188f5f1 ===
+  === checking 0484d39906c8 ===
+  === checking 5eec91b12a58 ===
+  === checking c84da74cf586 ===
+  === checking 3871506da61e ===
+  === checking bf6593f7e073 ===
+  === checking 1da228afcf06 ===
+  === checking 4f3b41956174 ===
+  === checking bfcfd9a61e84 ===
+  === checking d6c9e2d27f14 ===
+  === checking 37ad3ab0cddf ===
+  === checking c7d3029bf731 ===
+  === checking 1f4a19f83a29 ===
+  === checking 43fc0b77ff07 ===
+  === checking 31d7b43cc321 ===
+  === checking c713eae2d31f ===
+  === checking 76151e8066e1 ===
+  === checking c7c1497fc270 ===
+  === checking e7135b665740 ===
+  === checking 82238c0bc950 ===
+  === checking dbde319d43a3 ===
+  === checking 8b79544bb56d ===
   === checking 721ba7c5f4ff ===
+  === checking 01e29e20ea3f ===
+  === checking 32b41ca704e1 ===
+  === checking 88714f4125cb ===
+  === checking d928b4e8a515 ===
+  === checking 88eace5ce682 ===
+  === checking 698970a2480b ===
+  === checking 29141354a762 ===
+  === checking b33fd5ad4c0c ===
+  === checking cd345198cf12 ===
+  === checking 28be96b80dc1 ===
+  === checking 0bab31f71a21 ===
+  === checking 40553f55397e ===
   === checking e3e6738c56ce ===
   === checking 790cdfecd168 ===
   === checking 469c700e9ed8 ===
   === checking 8ae32c3ed670 ===
   === checking 84d6ec6a8e21 ===
   === checking 01f771406cab ===
+  $ hg showsort --rev 'Cfinal' --limit 72
+  c3c7fa726f88
+  97d19fc5236f
+  4f5078f7da8a
+  2bd677d0f13a
+  3bdb00d5c818
+  b9c3aa92fba5
+  f3441cd3e664
+  0c3f2ba59eb7
+  2ea3fbf151b5
+  47c836a1f13e
+  722d1b8b8942
+  17b6e6bac221
+  5ce588c2b7c5
+  f2bdd828a3aa
+  a457569c5306
+  ad46a4a0fc10
+  4b39f229a0ce
+  d94da36be176
+  eed373b0090d
+  2472d042ec95
+  673f5499c8c2
+  900dd066a072
+  97ac964e34b7
+  0d153e3ad632
+  c37e7cd9f2bd
+  9a67238ad1c4
+  d99e0f7dad5b
+  e4cfd6264623
+  fac9e582edd1
+  89a0fe204177
+  b3cf98c3d587
+  041e1188f5f1
+  0484d39906c8
+  5eec91b12a58
+  c84da74cf586
+  3871506da61e
+  bf6593f7e073
+  1da228afcf06
+  4f3b41956174
+  bfcfd9a61e84
+  d6c9e2d27f14
+  37ad3ab0cddf
+  c7d3029bf731
+  1f4a19f83a29
+  43fc0b77ff07
+  31d7b43cc321
+  c713eae2d31f
+  76151e8066e1
+  c7c1497fc270
+  e7135b665740
+  82238c0bc950
+  dbde319d43a3
+  8b79544bb56d
+  721ba7c5f4ff
+  01e29e20ea3f
+  32b41ca704e1
+  88714f4125cb
+  d928b4e8a515
+  88eace5ce682
+  698970a2480b
+  29141354a762
+  b33fd5ad4c0c
+  cd345198cf12
+  28be96b80dc1
+  0bab31f71a21
+  40553f55397e
+  e3e6738c56ce
+  790cdfecd168
+  469c700e9ed8
+  8ae32c3ed670
+  84d6ec6a8e21
+  01f771406cab
+  $ hg showsort --rev 'Cfinal' --limit 33
+  bfcfd9a61e84
+  d6c9e2d27f14
+  37ad3ab0cddf
+  c7d3029bf731
+  1f4a19f83a29
+  43fc0b77ff07
+  31d7b43cc321
+  c713eae2d31f
+  76151e8066e1
+  c7c1497fc270
+  e7135b665740
+  82238c0bc950
+  dbde319d43a3
+  8b79544bb56d
+  721ba7c5f4ff
+  01e29e20ea3f
+  32b41ca704e1
+  88714f4125cb
+  d928b4e8a515
+  88eace5ce682
+  698970a2480b
+  29141354a762
+  b33fd5ad4c0c
+  cd345198cf12
+  28be96b80dc1
+  0bab31f71a21
+  40553f55397e
+  e3e6738c56ce
+  790cdfecd168
+  469c700e9ed8
+  8ae32c3ed670
+  84d6ec6a8e21
+  01f771406cab
+  $ hg showsort --rev 'Cfinal' --limit 4
+  469c700e9ed8
+  8ae32c3ed670
+  84d6ec6a8e21
+  01f771406cab
 
 Test stability of this mess
 ---------------------------
@@ -619,8 +827,8 @@
 
   $ hg showsort --rev 'all()' > ../crisscross.random.order
   $ python "$RUNTESTDIR/md5sum.py" ../crisscross.*.order
-  d9aab0d1907d5cf64d205a8b9036e959  ../crisscross.random.order
-  d9aab0d1907d5cf64d205a8b9036e959  ../crisscross.source.order
+  56271e05099a227fc7c0d6a434c24f0e  ../crisscross.random.order
+  56271e05099a227fc7c0d6a434c24f0e  ../crisscross.source.order
   $ diff -u ../crisscross.*.order
   $ hg showsort --rev 'all()'
   1ea73414a91b
@@ -640,21 +848,15 @@
   39bab1cb1cbe
   55bf3fdb634f
   3e1560705803
-  17b6e6bac221
-  5ce588c2b7c5
-  f2bdd828a3aa
-  a457569c5306
-  ad46a4a0fc10
-  4f5078f7da8a
-  01e29e20ea3f
-  32b41ca704e1
-  29141354a762
   9729470d9329
   884936b34999
-  0484d39906c8
-  5eec91b12a58
-  c84da74cf586
-  3871506da61e
+  b115c694654e
+  8ecb28746ec4
+  de05b9c29ec7
+  d917f77a6439
+  c3c7fa726f88
+  97d19fc5236f
+  4f5078f7da8a
   2bd677d0f13a
   3bdb00d5c818
   b9c3aa92fba5
@@ -663,55 +865,61 @@
   2ea3fbf151b5
   47c836a1f13e
   722d1b8b8942
+  17b6e6bac221
+  5ce588c2b7c5
+  f2bdd828a3aa
+  a457569c5306
+  ad46a4a0fc10
   4b39f229a0ce
   d94da36be176
   eed373b0090d
-  88714f4125cb
-  d928b4e8a515
-  88eace5ce682
-  698970a2480b
-  b115c694654e
-  1f4a19f83a29
-  43fc0b77ff07
-  31d7b43cc321
+  2472d042ec95
   673f5499c8c2
   900dd066a072
   97ac964e34b7
   0d153e3ad632
   c37e7cd9f2bd
   9a67238ad1c4
-  8ecb28746ec4
-  bf6593f7e073
-  0bab31f71a21
-  1da228afcf06
-  bfcfd9a61e84
-  d6c9e2d27f14
-  de05b9c29ec7
-  40553f55397e
-  4f3b41956174
-  37ad3ab0cddf
-  c7d3029bf731
-  76151e8066e1
-  c7c1497fc270
-  e7135b665740
-  b33fd5ad4c0c
-  cd345198cf12
-  28be96b80dc1
-  c713eae2d31f
-  82238c0bc950
-  dbde319d43a3
-  8b79544bb56d
-  d917f77a6439
-  c3c7fa726f88
-  97d19fc5236f
-  2472d042ec95
   d99e0f7dad5b
   e4cfd6264623
   fac9e582edd1
   89a0fe204177
   b3cf98c3d587
   041e1188f5f1
+  0484d39906c8
+  5eec91b12a58
+  c84da74cf586
+  3871506da61e
+  bf6593f7e073
+  1da228afcf06
+  4f3b41956174
+  bfcfd9a61e84
+  d6c9e2d27f14
+  37ad3ab0cddf
+  c7d3029bf731
+  1f4a19f83a29
+  43fc0b77ff07
+  31d7b43cc321
+  c713eae2d31f
+  76151e8066e1
+  c7c1497fc270
+  e7135b665740
+  82238c0bc950
+  dbde319d43a3
+  8b79544bb56d
   721ba7c5f4ff
+  01e29e20ea3f
+  32b41ca704e1
+  88714f4125cb
+  d928b4e8a515
+  88eace5ce682
+  698970a2480b
+  29141354a762
+  b33fd5ad4c0c
+  cd345198cf12
+  28be96b80dc1
+  0bab31f71a21
+  40553f55397e
   e3e6738c56ce
   790cdfecd168
   469c700e9ed8
--- a/tests/test-stablesort.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-stablesort.t	Mon Dec 11 18:30:15 2017 +0100
@@ -9,7 +9,8 @@
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
   > [alias]
-  > showsort = debugstablesort --template="{node|short}\n"
+  > showsort = debugstablesort --template="{node|short}\n" --method basic-mergepoint
+  > showsorthead = debugstablesort --template="{node|short}\n" --method headcached
   > EOF
 
 
@@ -90,22 +91,111 @@
   |/
   o  0 1ea73414a91b r0
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+  4c748ffd1a46 3
+  f0f3ef9a6cd5 4
+  2702dd0c91e7 5
+  e7d9710d9fc6 5
+  d62d843c9a01 6
+  dcbb326fdec2 7
+  ff43616e5d0f 7
+  bab5d5bf48bd 5
+  e46a4836065c 6
+  b4594d867745 6
+  43227190fef8 5
+  1d8d22637c2d 8
   $ hg showsort --rev 'all()' --traceback
   1ea73414a91b
   66f7d451a68b
   fa942426a6fd
   2b6d669947cd
   43227190fef8
+  4c748ffd1a46
+  f0f3ef9a6cd5
+  1d8d22637c2d
+  2702dd0c91e7
   bab5d5bf48bd
   b4594d867745
-  e46a4836065c
   e7d9710d9fc6
   d62d843c9a01
   dcbb326fdec2
+  e46a4836065c
   ff43616e5d0f
+  $ hg showsorthead --rev 1d8d22637c2d
+  1ea73414a91b
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  43227190fef8
+  4c748ffd1a46
+  f0f3ef9a6cd5
+  1d8d22637c2d
+  $ hg showsorthead --rev 1d8d22637c2d --l 4
+  43227190fef8
   4c748ffd1a46
   f0f3ef9a6cd5
   1d8d22637c2d
+  $ hg showsorthead --rev b4594d867745
+  1ea73414a91b
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  bab5d5bf48bd
+  b4594d867745
+  $ hg showsorthead --rev b4594d867745 --limit 3
+  2b6d669947cd
+  bab5d5bf48bd
+  b4594d867745
+  $ hg showsorthead --rev e46a4836065c
+  1ea73414a91b
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  bab5d5bf48bd
+  e46a4836065c
+  $ hg showsorthead --rev e46a4836065c --limit 2
+  bab5d5bf48bd
+  e46a4836065c
+  $ hg showsorthead --rev ff43616e5d0f
+  1ea73414a91b
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  e7d9710d9fc6
+  d62d843c9a01
+  ff43616e5d0f
+  $ hg showsorthead --rev ff43616e5d0f --limit 6
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  e7d9710d9fc6
+  d62d843c9a01
+  ff43616e5d0f
+  $ hg showsorthead --rev dcbb326fdec2
+  1ea73414a91b
+  66f7d451a68b
+  fa942426a6fd
+  2b6d669947cd
+  e7d9710d9fc6
+  d62d843c9a01
+  dcbb326fdec2
+  $ hg showsorthead --rev dcbb326fdec2 --limit 4
+  2b6d669947cd
+  e7d9710d9fc6
+  d62d843c9a01
+  dcbb326fdec2
+  $ hg showsorthead --rev 2702dd0c91e7
+  1ea73414a91b
+  fa942426a6fd
+  4c748ffd1a46
+  f0f3ef9a6cd5
+  2702dd0c91e7
+  $ hg showsorthead --rev 2702dd0c91e7 --limit 2
+  f0f3ef9a6cd5
   2702dd0c91e7
 
 Verify the topological order
@@ -121,17 +211,17 @@
   === checking fa942426a6fd ===
   === checking 2b6d669947cd ===
   === checking 43227190fef8 ===
-  === checking bab5d5bf48bd ===
-  === checking b4594d867745 ===
-  === checking e46a4836065c ===
-  === checking e7d9710d9fc6 ===
-  === checking d62d843c9a01 ===
-  === checking dcbb326fdec2 ===
-  === checking ff43616e5d0f ===
   === checking 4c748ffd1a46 ===
   === checking f0f3ef9a6cd5 ===
   === checking 1d8d22637c2d ===
   === checking 2702dd0c91e7 ===
+  === checking bab5d5bf48bd ===
+  === checking b4594d867745 ===
+  === checking e7d9710d9fc6 ===
+  === checking d62d843c9a01 ===
+  === checking dcbb326fdec2 ===
+  === checking e46a4836065c ===
+  === checking ff43616e5d0f ===
 
 Check stability
 ===============
@@ -204,6 +294,23 @@
   |/
   o  0 1ea73414a91b r0
   
+  $ hg -R repo_B debugdepth -r 'all()'
+  1ea73414a91b 1
+  fa942426a6fd 2
+  4c748ffd1a46 3
+  f0f3ef9a6cd5 4
+  66f7d451a68b 2
+  2b6d669947cd 4
+  bab5d5bf48bd 5
+  b4594d867745 6
+  43227190fef8 5
+  2702dd0c91e7 5
+  e7d9710d9fc6 5
+  d62d843c9a01 6
+  dcbb326fdec2 7
+  ff43616e5d0f 7
+  e46a4836065c 6
+  1d8d22637c2d 8
   $ hg -R repo_B log -G > B.log
 
   $ hg clone repo_A repo_C --rev 10
@@ -363,10 +470,10 @@
   D
 
   $ python "$RUNTESTDIR/md5sum.py" *.all.order
-  0c6b2e6f15249c0359b0f93e28c5bd1c  A.all.order
-  0c6b2e6f15249c0359b0f93e28c5bd1c  B.all.order
-  0c6b2e6f15249c0359b0f93e28c5bd1c  C.all.order
-  0c6b2e6f15249c0359b0f93e28c5bd1c  D.all.order
+  4f54f623da142833149055fb83022a7e  A.all.order
+  4f54f623da142833149055fb83022a7e  B.all.order
+  4f54f623da142833149055fb83022a7e  C.all.order
+  4f54f623da142833149055fb83022a7e  D.all.order
 
 one specific head
 
@@ -384,6 +491,8 @@
 
   $ for x in $repos; do
   >     hg -R repo_$x showsort --rev '1d8d22637c2d' > ${x}.1d8d22637c2d.order;
+  >     hg -R repo_$x showsorthead --rev '1d8d22637c2d' > ${x}.1d8d22637c2d.orderhead;
+  >     hg -R repo_$x showsorthead --rev '1d8d22637c2d' --limit 4 > ${x}.1d8d22637c2d.orderhead-4;
   > done
 
   $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.order
@@ -391,11 +500,23 @@
   77dc20a6f86db9103df8edaae9ad2754  B.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  C.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  D.1d8d22637c2d.order
+  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.orderhead
+  77dc20a6f86db9103df8edaae9ad2754  A.1d8d22637c2d.orderhead
+  77dc20a6f86db9103df8edaae9ad2754  B.1d8d22637c2d.orderhead
+  77dc20a6f86db9103df8edaae9ad2754  C.1d8d22637c2d.orderhead
+  77dc20a6f86db9103df8edaae9ad2754  D.1d8d22637c2d.orderhead
+  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.orderhead-4
+  ea12ffc0007e1b4b911d09dd478881f3  A.1d8d22637c2d.orderhead-4
+  ea12ffc0007e1b4b911d09dd478881f3  B.1d8d22637c2d.orderhead-4
+  ea12ffc0007e1b4b911d09dd478881f3  C.1d8d22637c2d.orderhead-4
+  ea12ffc0007e1b4b911d09dd478881f3  D.1d8d22637c2d.orderhead-4
 
 changeset that are not heads
 
   $ for x in $repos; do
   >     hg -R repo_$x showsort --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.order;
+  >     hg -R repo_$x showsorthead --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.orderhead;
+  >     hg -R repo_$x showsorthead --rev 'e7d9710d9fc6+43227190fef8' --limit 6 > ${x}.non-heads.orderhead-6;
   > done
 
   $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
@@ -403,6 +524,11 @@
   94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  D.non-heads.order
+  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  A.non-heads.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  B.non-heads.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  C.non-heads.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  D.non-heads.orderhead
 
 Check with different subset
 
@@ -464,6 +590,8 @@
 
   $ for x in E F G; do
   >     hg -R repo_$x showsort --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.order;
+  >     hg -R repo_$x showsort --rev 'e7d9710d9fc6' > ${x}.non-head-A.orderhead;
+  >     hg -R repo_$x showsort --rev '43227190fef8' > ${x}.non-head-B.orderhead;
   > done
 
   $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
@@ -474,6 +602,14 @@
   94e0ea8cdade135dabde4ec5e9954329  E.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  F.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  G.non-heads.order
+  $ python "$RUNTESTDIR/md5sum.py" *.non-head-A.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  E.non-head-A.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  F.non-head-A.orderhead
+  1e5ce05b507a058c5dac3d7de9ae8feb  G.non-head-A.orderhead
+  $ python "$RUNTESTDIR/md5sum.py" *.non-head-B.orderhead
+  4b07febabfee9528aedcea156a7d7071  E.non-head-B.orderhead
+  4b07febabfee9528aedcea156a7d7071  F.non-head-B.orderhead
+  4b07febabfee9528aedcea156a7d7071  G.non-head-B.orderhead
 
 Multiple recursions
 ===================
@@ -534,6 +670,28 @@
   |/
   o  0 1ea73414a91b r0 base
   
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  e7bd5218ca15 2
+  3a367db1fabc 3
+  a2f58e9c1e56 4
+  5f18015f9110 8
+  abf57d94268b 5
+  529dfc5bb875 6
+  6ee532b68cfa 3
+  001194dd78d5 9
+  a66b68853635 10
+  013b27f11536 11
+  4bbfc6078919 12
+  9fff0871d230 9
+  4dbf739dd63f 10
+  d64d500024d1 11
+  0496f0a6a143 16
+  1c645e73dbc6 17
+  160a7a0adbf4 18
   $ hg showsort --rev 'all()'
   1ea73414a91b
   66f7d451a68b
@@ -542,17 +700,68 @@
   abf57d94268b
   529dfc5bb875
   e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
+  3a367db1fabc
+  a2f58e9c1e56
+  5f18015f9110
+  9fff0871d230
+  4dbf739dd63f
+  d64d500024d1
+  0496f0a6a143
+  1c645e73dbc6
+  160a7a0adbf4
+  a66b68853635
+  013b27f11536
+  4bbfc6078919
+  $ hg showsorthead --rev '160a7a0adbf4'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
   3a367db1fabc
   a2f58e9c1e56
   5f18015f9110
   9fff0871d230
   4dbf739dd63f
   d64d500024d1
-  6ee532b68cfa
-  001194dd78d5
+  0496f0a6a143
+  1c645e73dbc6
+  160a7a0adbf4
+  $ hg showsorthead --rev '160a7a0adbf4' --limit 7
+  5f18015f9110
+  9fff0871d230
+  4dbf739dd63f
+  d64d500024d1
   0496f0a6a143
   1c645e73dbc6
   160a7a0adbf4
+  $ hg showsorthead --rev '4bbfc6078919'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
+  a66b68853635
+  013b27f11536
+  4bbfc6078919
+  $ hg showsorthead --rev '4bbfc6078919' --limit 10
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
   a66b68853635
   013b27f11536
   4bbfc6078919
@@ -564,14 +773,14 @@
   === checking abf57d94268b ===
   === checking 529dfc5bb875 ===
   === checking e7bd5218ca15 ===
+  === checking 6ee532b68cfa ===
+  === checking 001194dd78d5 ===
   === checking 3a367db1fabc ===
   === checking a2f58e9c1e56 ===
   === checking 5f18015f9110 ===
   === checking 9fff0871d230 ===
   === checking 4dbf739dd63f ===
   === checking d64d500024d1 ===
-  === checking 6ee532b68cfa ===
-  === checking 001194dd78d5 ===
   === checking 0496f0a6a143 ===
   === checking 1c645e73dbc6 ===
   === checking 160a7a0adbf4 ===
@@ -579,6 +788,8 @@
   === checking 013b27f11536 ===
   === checking 4bbfc6078919 ===
   $ hg showsort --rev 'all()' > ../multiple.source.order
+  $ hg showsorthead --rev '160a7a0adbf4' > ../160a7a0adbf4.source.orderhead
+  $ hg showsorthead --rev '4bbfc6078919' > ../4bbfc6078919.source.orderhead
   $ hg log -r tip
   20 160a7a0adbf4 r20 tip
   $ cd ..
@@ -597,9 +808,17 @@
   > done;
   $ hg pull --quiet
   $ hg showsort --rev 'all()' > ../multiple.random.order
+  $ hg showsorthead --rev '160a7a0adbf4' > ../160a7a0adbf4.random.orderhead
+  $ hg showsorthead --rev '4bbfc6078919' > ../4bbfc6078919.random.orderhead
   $ python "$RUNTESTDIR/md5sum.py" ../multiple.*.order
-  6ff802a0a5f0a3ddd82b25f860238fbd  ../multiple.random.order
-  6ff802a0a5f0a3ddd82b25f860238fbd  ../multiple.source.order
+  a6547220a9f004c975e365d9561639dd  ../multiple.random.order
+  a6547220a9f004c975e365d9561639dd  ../multiple.source.order
+  $ python "$RUNTESTDIR/md5sum.py" ../160a7a0adbf4.*.orderhead
+  48d8911f53869b32e29da26c56e95119  ../160a7a0adbf4.random.orderhead
+  48d8911f53869b32e29da26c56e95119  ../160a7a0adbf4.source.orderhead
+  $ python "$RUNTESTDIR/md5sum.py" ../4bbfc6078919.*.orderhead
+  3732305a333d59ec50b91db0f5ab696e  ../4bbfc6078919.random.orderhead
+  3732305a333d59ec50b91db0f5ab696e  ../4bbfc6078919.source.orderhead
   $ hg showsort --rev 'all()'
   1ea73414a91b
   66f7d451a68b
@@ -608,20 +827,52 @@
   abf57d94268b
   529dfc5bb875
   e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
   3a367db1fabc
   a2f58e9c1e56
   5f18015f9110
   9fff0871d230
   4dbf739dd63f
   d64d500024d1
-  6ee532b68cfa
-  001194dd78d5
   0496f0a6a143
   1c645e73dbc6
   160a7a0adbf4
   a66b68853635
   013b27f11536
   4bbfc6078919
+  $ hg showsorthead --rev '160a7a0adbf4'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
+  3a367db1fabc
+  a2f58e9c1e56
+  5f18015f9110
+  9fff0871d230
+  4dbf739dd63f
+  d64d500024d1
+  0496f0a6a143
+  1c645e73dbc6
+  160a7a0adbf4
+  $ hg showsorthead --rev '4bbfc6078919'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  abf57d94268b
+  529dfc5bb875
+  e7bd5218ca15
+  6ee532b68cfa
+  001194dd78d5
+  a66b68853635
+  013b27f11536
+  4bbfc6078919
   $ cd ..
 
 
@@ -662,9 +913,186 @@
   |/
   o  0 d20a80d4def3 base
   
+  $ hg debugdepth -r 'all()'
+  d20a80d4def3 1
+  47da0f2c25e2 2
+  0dedbcd995b6 3
+  ed776db7ed63 4
+  7f2454f6b04f 5
   $ hg showsort --rev '.'
   d20a80d4def3
   47da0f2c25e2
   0dedbcd995b6
   ed776db7ed63
   7f2454f6b04f
+  $ hg showsorthead --rev '.'
+  d20a80d4def3
+  47da0f2c25e2
+  0dedbcd995b6
+  ed776db7ed63
+  7f2454f6b04f
+
+  $ cd ..
+
+Merge two branches with their own independant internal merge.
+-------------------------------------------------------------
+
+  $ hg init subbranch
+  $ cd subbranch
+  $ hg debugbuilddag '
+  > .:base
+  > +3:leftBranch
+  > +2:leftA
+  > <leftBranch.+2:leftB
+  > /leftA:leftMerge
+  > <base+2:rightBranch
+  > +4:rightA
+  > <rightBranch.+1:rightB
+  > /rightA:rightMerge
+  > +3/leftMerge
+  > '
+  $ hg log -G
+  o    22 56526aefbff4 r22 tip
+  |\
+  | o  21 d4422659bc40 r21
+  | |
+  | o  20 6a97ef856f90 r20
+  | |
+  | o  19 5648bbf0e38b r19
+  | |
+  | o    18 4442c125b80d r18 rightMerge
+  | |\
+  | | o  17 65e683dd6db4 r17 rightB
+  | | |
+  | | o  16 5188cf52b7b7 r16
+  | | |
+  | o |  15 191bac7bf37c r15 rightA
+  | | |
+  | o |  14 5cb8e6902ff3 r14
+  | | |
+  | o |  13 448a7ac3ab1f r13
+  | | |
+  | o |  12 ee222cc71ce6 r12
+  | |/
+  | o  11 e5c0d969abc4 r11 rightBranch
+  | |
+  | o  10 7cc044fdf4a7 r10
+  | |
+  o |    9 9f6c364a3574 r9 leftMerge
+  |\ \
+  | o |  8 588f0bc87ecd r8 leftB
+  | | |
+  | o |  7 e2317cea05f7 r7
+  | | |
+  | o |  6 c2c595bcd4c6 r6
+  | | |
+  o | |  5 c8d03c1b5e94 r5 leftA
+  | | |
+  o | |  4 bebd167eb94d r4
+  |/ /
+  o |  3 2dc09a01254d r3 leftBranch
+  | |
+  o |  2 01241442b3c2 r2
+  | |
+  o |  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0 base
+  
+  $ hg debugdepth -r 'all()'
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+  2dc09a01254d 4
+  bebd167eb94d 5
+  c8d03c1b5e94 6
+  c2c595bcd4c6 5
+  e2317cea05f7 6
+  588f0bc87ecd 7
+  9f6c364a3574 10
+  7cc044fdf4a7 2
+  e5c0d969abc4 3
+  ee222cc71ce6 4
+  448a7ac3ab1f 5
+  5cb8e6902ff3 6
+  191bac7bf37c 7
+  5188cf52b7b7 4
+  65e683dd6db4 5
+  4442c125b80d 10
+  5648bbf0e38b 11
+  6a97ef856f90 12
+  d4422659bc40 13
+  56526aefbff4 23
+  $ hg showsort --rev 'tip'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  c2c595bcd4c6
+  e2317cea05f7
+  588f0bc87ecd
+  bebd167eb94d
+  c8d03c1b5e94
+  9f6c364a3574
+  7cc044fdf4a7
+  e5c0d969abc4
+  ee222cc71ce6
+  448a7ac3ab1f
+  5cb8e6902ff3
+  191bac7bf37c
+  5188cf52b7b7
+  65e683dd6db4
+  4442c125b80d
+  5648bbf0e38b
+  6a97ef856f90
+  d4422659bc40
+  56526aefbff4
+  $ hg showsorthead --rev 'tip'
+  1ea73414a91b
+  66f7d451a68b
+  01241442b3c2
+  2dc09a01254d
+  c2c595bcd4c6
+  e2317cea05f7
+  588f0bc87ecd
+  bebd167eb94d
+  c8d03c1b5e94
+  9f6c364a3574
+  7cc044fdf4a7
+  e5c0d969abc4
+  ee222cc71ce6
+  448a7ac3ab1f
+  5cb8e6902ff3
+  191bac7bf37c
+  5188cf52b7b7
+  65e683dd6db4
+  4442c125b80d
+  5648bbf0e38b
+  6a97ef856f90
+  d4422659bc40
+  56526aefbff4
+  $ hg showsorthead --rev 'tip' --limit 12
+  e5c0d969abc4
+  ee222cc71ce6
+  448a7ac3ab1f
+  5cb8e6902ff3
+  191bac7bf37c
+  5188cf52b7b7
+  65e683dd6db4
+  4442c125b80d
+  5648bbf0e38b
+  6a97ef856f90
+  d4422659bc40
+  56526aefbff4
+  $ hg showsorthead --rev 'tip' --limit 7
+  5188cf52b7b7
+  65e683dd6db4
+  4442c125b80d
+  5648bbf0e38b
+  6a97ef856f90
+  d4422659bc40
+  56526aefbff4
+  $ hg showsorthead --rev 'tip' --limit 3
+  6a97ef856f90
+  d4422659bc40
+  56526aefbff4
+  $ cd ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-flow-reject-untopiced.t	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,174 @@
+Testing the config option for rejecting draft changeset without topic
+The config option is "experimental.topic-mode.server"
+
+  $ . "$TESTDIR/testlib/topic_setup.sh"
+
+Creating a server repo
+
+  $ hg init server
+  $ cd server
+  $ cat <<EOF >>.hg/hgrc
+  > [phases]
+  > publish=False
+  > [experimental]
+  > topic-mode.server = enforce
+  > EOF
+
+  $ hg topic server
+  marked working directory as topic: server
+  $ for ch in a b c; do echo foo > $ch; hg ci -Aqm "Added "$ch; done
+  $ hg ph -p 0
+
+  $ hg log -G -T "{rev}:{node|short}\n{desc}  {topics}"
+  @  2:a7b96f87a214
+  |  Added c  server
+  o  1:d6a8197e192a
+  |  Added b  server
+  o  0:49a3f206c9ae
+     Added a
+
+  $ cd ..
+
+Creating a client repo
+
+  $ hg clone server client
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd client
+  $ hg up server
+  switching to topic server
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G -T "{rev}:{node|short}\n{desc}  {topics}"
+  @  2:a7b96f87a214
+  |  Added c  server
+  o  1:d6a8197e192a
+  |  Added b  server
+  o  0:49a3f206c9ae
+     Added a
+
+  $ hg topic
+   * server (2 changesets)
+
+Create a changeset without topic
+
+  $ hg topic --clear
+  $ echo foo > d
+  $ hg ci -Aqm "added d"
+
+  $ hg log -G -T "{rev}:{node|short}\n{desc}  {topics}"
+  @  3:4e8b0e0237c6
+  |  added d
+  o  2:a7b96f87a214
+  |  Added c  server
+  o  1:d6a8197e192a
+  |  Added b  server
+  o  0:49a3f206c9ae
+     Added a
+
+Push a draft changeset without topic
+
+  $ hg push ../server  --new-branch
+  pushing to ../server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  transaction abort!
+  rollback completed
+  abort: rejecting draft changesets: 4e8b0e0237
+  [255]
+
+  $ hg push ../server -f
+  pushing to ../server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  transaction abort!
+  rollback completed
+  abort: rejecting draft changesets: 4e8b0e0237
+  [255]
+
+Grow the stack with more changesets having topic
+
+  $ hg topic client
+  marked working directory as topic: client
+  $ for ch in e f g; do echo foo > $ch; hg ci -Aqm "Added "$ch; done;
+
+  $ hg log -G -T "{rev}:{node|short}\n{desc}  {topics}"
+  @  6:42c4ac86452a
+  |  Added g  client
+  o  5:3dc456efb491
+  |  Added f  client
+  o  4:18a516babc60
+  |  Added e  client
+  o  3:4e8b0e0237c6
+  |  added d
+  o  2:a7b96f87a214
+  |  Added c  server
+  o  1:d6a8197e192a
+  |  Added b  server
+  o  0:49a3f206c9ae
+     Added a
+
+Pushing multiple changeset with some having topics and some not
+
+  $ hg push ../server --new-branch
+  pushing to ../server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files
+  transaction abort!
+  rollback completed
+  abort: rejecting draft changesets: 4e8b0e0237
+  [255]
+
+Testing case when both experimental.topic-mode.server and
+experimental.topic.publish-bare-branch are set
+
+  $ cd ../server
+  $ echo 'topic.publish-bare-branch=True' >> .hg/hgrc
+  $ cd ../client
+  $ hg push ../server --new-branch
+  pushing to ../server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files
+  transaction abort!
+  rollback completed
+  abort: rejecting draft changesets: 4e8b0e0237
+  [255]
+
+Turning the changeset public and testing push
+
+  $ hg phase -r 3 -p
+  $ hg log -G -T "{rev}:{node|short}\n{desc}  {topics}"
+  @  6:42c4ac86452a
+  |  Added g  client
+  o  5:3dc456efb491
+  |  Added f  client
+  o  4:18a516babc60
+  |  Added e  client
+  o  3:4e8b0e0237c6
+  |  added d
+  o  2:a7b96f87a214
+  |  Added c
+  o  1:d6a8197e192a
+  |  Added b
+  o  0:49a3f206c9ae
+     Added a
+
+  $ hg push ../server
+  pushing to ../server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files
+  active topic 'server' is now empty
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-stack-complex.t	Mon Dec 11 18:30:15 2017 +0100
@@ -0,0 +1,225 @@
+Testing `hg stack` on complex cases when we have multiple successors because of
+divergence, split etc.
+  $ . "$TESTDIR/testlib/topic_setup.sh"
+
+Setup
+
+  $ cat << EOF >> $HGRCPATH
+  > [experimental]
+  > evolution = all
+  > [ui]
+  > interactive = True
+  > [defaults]
+  > split = -d "0 "0
+  > [extensions]
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+  $ hg init test
+  $ cd test
+  $ echo foo > foo
+  $ hg add foo
+  $ hg ci -m "Added foo"
+  $ hg phase -r . --public
+  $ hg topic foo
+  marked working directory as topic: foo
+  $ echo a > a
+  $ echo b > b
+  $ hg ci -Aqm "Added a and b"
+  $ echo c > c
+  $ echo d > d
+  $ hg ci -Aqm "Added c and d"
+  $ echo e > e
+  $ echo f > f
+  $ hg ci -Aqm "Added e and f"
+  $ hg log -G
+  @  changeset:   3:f1d3649d6a8b
+  |  tag:         tip
+  |  topic:       foo
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     Added e and f
+  |
+  o  changeset:   2:8e8251e8193b
+  |  topic:       foo
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     Added c and d
+  |
+  o  changeset:   1:002b85930b9c
+  |  topic:       foo
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     Added a and b
+  |
+  o  changeset:   0:f3603c09ac10
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     Added foo
+  
+
+Testing in case of split within the topic
+
+  $ hg stack
+  ### topic: foo
+  ### target: default (branch)
+  t3@ Added e and f (current)
+  t2: Added c and d
+  t1: Added a and b
+  t0^ Added foo (base)
+  $ hg prev
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  [2] Added c and d
+
+  $ echo 0 > num
+  $ cat > editor.sh << '__EOF__'
+  > NUM=$(cat num)
+  > NUM=`expr "$NUM" + 1`
+  > echo "$NUM" > num
+  > echo "split$NUM" > "$1"
+  > __EOF__
+  $ export HGEDITOR="\"sh\" \"editor.sh\""
+
+  $ hg split << EOF
+  > y
+  > y
+  > n
+  > y
+  > EOF
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  adding c
+  adding d
+  diff --git a/c b/c
+  new file mode 100644
+  examine changes to 'c'? [Ynesfdaq?] y
+  
+  @@ -0,0 +1,1 @@
+  +c
+  record change 1/2 to 'c'? [Ynesfdaq?] y
+  
+  diff --git a/d b/d
+  new file mode 100644
+  examine changes to 'd'? [Ynesfdaq?] n
+  
+  Done splitting? [yN] y
+
+  $ hg stack
+  ### topic: foo
+  ### target: default (branch)
+  t4$ Added e and f (unstable)
+  t3@ split2 (current)
+  t2: split1
+  t1: Added a and b
+  t0^ Added foo (base)
+
+  $ hg log -G
+  @  changeset:   5:5ccee6da565e
+  |  tag:         tip
+  |  topic:       foo
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     split2
+  |
+  o  changeset:   4:f26c1b9addde
+  |  topic:       foo
+  |  parent:      1:002b85930b9c
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     split1
+  |
+  | o  changeset:   3:f1d3649d6a8b
+  | |  topic:       foo
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  trouble:     unstable
+  | |  summary:     Added e and f
+  | |
+  | x  changeset:   2:8e8251e8193b
+  |/   topic:       foo
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     Added c and d
+  |
+  o  changeset:   1:002b85930b9c
+  |  topic:       foo
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     Added a and b
+  |
+  o  changeset:   0:f3603c09ac10
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     Added foo
+  
+
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [4] split1
+  $ echo foo > c
+  $ hg diff
+  diff -r f26c1b9addde c
+  --- a/c	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/c	* (glob)
+  @@ -1,1 +1,1 @@
+  -c
+  +foo
+
+  $ hg amend
+  1 new unstable changesets
+
+  $ hg log -G
+  @  changeset:   7:7d9445714d83
+  |  tag:         tip
+  |  topic:       foo
+  |  parent:      1:002b85930b9c
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     split1
+  |
+  | o  changeset:   5:5ccee6da565e
+  | |  topic:       foo
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  trouble:     unstable
+  | |  summary:     split2
+  | |
+  | x  changeset:   4:f26c1b9addde
+  |/   topic:       foo
+  |    parent:      1:002b85930b9c
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     split1
+  |
+  | o  changeset:   3:f1d3649d6a8b
+  | |  topic:       foo
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  trouble:     unstable
+  | |  summary:     Added e and f
+  | |
+  | x  changeset:   2:8e8251e8193b
+  |/   topic:       foo
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     Added c and d
+  |
+  o  changeset:   1:002b85930b9c
+  |  topic:       foo
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     Added a and b
+  |
+  o  changeset:   0:f3603c09ac10
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     Added foo
+  
+
+  $ hg stack
+  ### topic: foo (2 heads)
+  ### target: default (branch), 2 behind
+  t4$ Added e and f (unstable)
+  t3$ split2 (unstable)
+  t2@ split1 (current)
+  t1: Added a and b
+  t0^ Added foo (base)
--- a/tests/test-touch.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-touch.t	Mon Dec 11 18:30:15 2017 +0100
@@ -125,3 +125,8 @@
   1 changesets pruned
   $ hg touch 14 --hidden
   1 new unstable changesets
+  $ hg obslog -r 14 --hidden
+  x  [0-9a-f]{12} (.*) move (re)
+       pruned by test (*) (glob)
+       rewritten(.*) as [0-9a-f]{12} by test (.*) (re)
+  
--- a/tests/test-tutorial.t	Mon Dec 11 09:33:32 2017 +0100
+++ b/tests/test-tutorial.t	Mon Dec 11 18:30:15 2017 +0100
@@ -936,6 +936,7 @@
   
    -a --all                 uncommit all changes when no arguments given
    -r --rev VALUE           revert commit content to REV instead
+   -n --note VALUE          store a note on uncommit
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -m --message TEXT        use text as commit message
@@ -975,6 +976,7 @@
    -r --rev VALUE [+] revision to fold
       --exact         only fold specified revisions
       --from          fold revisions linearly to working copy parent
+   -n --note VALUE    store a note on fold
    -m --message TEXT  use text as commit message
    -l --logfile FILE  read commit message from file
    -d --date DATE     record the specified date as commit date