changeset 2433:fd24f1c7accd mercurial-3.9

merge with future 6.2.0 The new 'olog' command is not working with 3.9 so we skill all mentions of it in the tests. This also apply to the topic extensions.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 18 May 2017 23:51:10 +0200
parents 765b2561fa90 (diff) ee321b87c548 (current diff)
children 37deace44b36 8fadd87da07e
files tests/test-check-setup-manifest.t tests/test-discovery-obshashrange.t tests/test-evolve-obshistory.t tests/test-evolve-topic.t tests/test-evolve.t tests/test-obsolete.t tests/test-prev-next.t tests/test-topic-dest.t tests/test-topic-push.t tests/test-topic-stack-data.t tests/test-topic-stack.t tests/test-topic-tutorial.t tests/test-topic.t
diffstat 26 files changed, 1533 insertions(+), 3553 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Wed May 03 13:23:36 2017 +0200
+++ b/.hgtags	Thu May 18 23:51:10 2017 +0200
@@ -48,3 +48,4 @@
 70694b2621ba9d919bc38303f8901e84caf5da0f 5.6.1
 165ad227993de4e7d819cc6c820d5b9f7b38b80d 6.0.0
 5ef112a6eb875633a7925cde61b7d2d9e65b3a56 6.0.1
+8510d3fd7c3b312dc731f4c29badc415d504558a 6.1.0
--- a/README	Wed May 03 13:23:36 2017 +0200
+++ b/README	Thu May 18 23:51:10 2017 +0200
@@ -112,7 +112,24 @@
 Changelog
 =========
 
-6.1.0 - in progress
+6.2.0 -- 2017-05-18
+-------------------
+
+ - olog: a new command to inspect the obs-history of a changeset (hg-4.0 + only),
+ - topic: have thg display topic name if possible,
+ - blackbox: log more information about discovery and cache computation,
+ - obscache: more efficient update in the (rare) case of a transaction adding
+   markers without changesets,
+ - obscache: fix more cache invalidation propagation,
+ - obscache: also enable the new cache (from 6.1.0) for 'evolve.server-only',
+ - obshashrange-cache: update incrementally in the (common) case of a
+   transaction not affecting existing range,
+ - obshashrange-cache: keep the cache warm after each transaction,
+ - topic: now requires Mercurial 4.0 or above,
+ - stack: now display if current revision is in bad state (issue5533),
+ - stack: fix json output to be valid json.
+
+6.1.0 -- 2017-05-03
 -------------------
 
  - improve message about obsolete working copy parent,
--- a/debian/changelog	Wed May 03 13:23:36 2017 +0200
+++ b/debian/changelog	Thu May 18 23:51:10 2017 +0200
@@ -1,14 +1,26 @@
-mercurial-evolve (6.0.1-1) UNRELEASED; urgency=medium
+mercurial-evolve (6.2.0-1) unstable; urgency=medium
+
+  * new upstream release
+
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Thu, 18 May 2017 22:24:10 +0200
+
+mercurial-evolve (6.1.0-1) unstable; urgency=medium
+
+  * New upstream release
+
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Wed, 03 May 2017 13:57:15 +0200
+
+mercurial-evolve (6.0.1-1) unstable; urgency=medium
 
   * New upstream version
 
- -- Pierre-Yves David <marmoute@nodosa.octopoid.net>  Thu, 20 Apr 2017 12:58:35 +0200
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Thu, 20 Apr 2017 12:58:35 +0200
 
 mercurial-evolve (6.0.0-1) unstable; urgency=medium
 
   * New Upstream Release
 
- -- Pierre-Yves David <marmoute@nodosa.octopoid.net>  Thu, 20 Apr 2017 12:58:03 +0200
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Thu, 20 Apr 2017 12:58:03 +0200
 
 mercurial-evolve (5.5.0-1) unstable; urgency=medium
 
--- a/hgext3rd/evolve/__init__.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/evolve/__init__.py	Thu May 18 23:51:10 2017 +0200
@@ -148,6 +148,7 @@
     obsexchange,
     safeguard,
     utility,
+    obshistory
 )
 
 __version__ = metadata.__version__
@@ -162,6 +163,15 @@
 
 obsexcmsg = utility.obsexcmsg
 
+colortable = {'evolve.node': 'yellow',
+              'evolve.user': 'green',
+              'evolve.rev': 'blue',
+              'evolve.short_description': '',
+              'evolve.date': 'cyan',
+              'evolve.current_rev': 'bold',
+              'evolve.verb': '',
+              }
+
 _pack = struct.pack
 _unpack = struct.unpack
 
@@ -180,6 +190,7 @@
 eh.merge(checkheads.eh)
 eh.merge(safeguard.eh)
 eh.merge(obscache.eh)
+eh.merge(obshistory.eh)
 uisetup = eh.final_uisetup
 extsetup = eh.final_extsetup
 reposetup = eh.final_reposetup
@@ -269,9 +280,6 @@
         ui.setconfig('alias', 'pstatus', 'status --rev .^', 'evolve')
     if ui.config('alias', 'pdiff', None) is None:
         ui.setconfig('alias', 'pdiff', 'diff --rev .^', 'evolve')
-    if ui.config('alias', 'olog', None) is None:
-        ui.setconfig('alias', 'olog', "log -r 'precursors(.)' --hidden",
-                     'evolve')
     if ui.config('alias', 'odiff', None) is None:
         ui.setconfig('alias', 'odiff',
                      "diff --hidden --rev 'limit(precursors(.),1)' --rev .",
@@ -1881,7 +1889,7 @@
     raise error.Abort("base of divergent changeset %s not found" % ctx,
                       hint='this case is not yet handled')
 
-shorttemplate = '[{rev}] {desc|firstline}\n'
+shorttemplate = "[{label('evolve.rev', rev)}] {desc|firstline}\n"
 
 @eh.command(
     '^previous',
@@ -2055,7 +2063,8 @@
                 result = _solveone(ui, repo, repo[aspchildren[0]], dryrunopt,
                                    False, lambda: None, category='unstable')
                 if not result:
-                    ui.status(_('working directory now at %s\n') % repo['.'])
+                    ui.status(_('working directory now at %s\n')
+                              % ui.label(repo['.'], 'evolve.node'))
                 return result
             return 1
         return result
@@ -2261,7 +2270,8 @@
                     repo._bookmarks[bookactive] = newnode.node()
                     repo._bookmarks.recordchange(tr)
                 commands.update(ui, repo, newnode.rev())
-                ui.status(_('working directory now at %s\n') % newnode)
+                ui.status(_('working directory now at %s\n')
+                          % ui.label(newnode, 'evolve.node'))
                 if movebookmark:
                     bookmarksmod.activate(repo, bookactive)
 
--- a/hgext3rd/evolve/metadata.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/evolve/metadata.py	Thu May 18 23:51:10 2017 +0200
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-__version__ = '6.1.0.dev'
-testedwith = '3.8.4 3.9.2 4.0.2 4.1.1 4.2'
+__version__ = '6.2.0'
+testedwith = '3.8.4 3.9.2 4.0.2 4.1.2 4.2'
 minimumhgversion = '3.8'
 buglink = 'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obscache.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/evolve/obscache.py	Thu May 18 23:51:10 2017 +0200
@@ -7,25 +7,42 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+import errno
 import hashlib
+import os
 import struct
+import time
 import weakref
-import errno
 
 from mercurial import (
+    error,
     localrepo,
     obsolete,
     phases,
+    pycompat,
     node,
     util,
 )
 
+from mercurial.i18n import _
+
 from . import (
     exthelper,
 )
 
 eh = exthelper.exthelper()
 
+# prior to hg-4.2 there are not util.timer
+if util.safehasattr(util, 'timer'):
+    timer = util.timer
+elif util.safehasattr(time, "perf_counter"):
+    timer = time.perf_counter
+elif getattr(pycompat, 'osname', os.name) == 'nt':
+    timer = time.clock
+else:
+    timer = time.time
+
+
 try:
     obsstorefilecache = localrepo.localrepository.obsstore
 except AttributeError:
@@ -76,91 +93,235 @@
             except (OSError, IOError) as e:
                 if e.errno != errno.ENOENT:
                     raise
-            key = hashlib.sha1(keydata).digest()
+            if keydata:
+                key = hashlib.sha1(keydata).digest()
+            else:
+                # reusing an existing "empty" value make it easier to define a
+                # default cachekey for 'no data'.
+                key = node.nullid
             return obsstoresize, key
 
     obsstore.__class__ = cachekeyobsstore
 
     return obsstore
 
-emptykey = (node.nullrev, node.nullid, 0, 0, node.nullid)
-
-def getcachekey(repo):
-    """get a cache key covering the changesets and obsmarkers content
-
-    IT contains the following data. Combined with 'upgradeneeded' it allows to
-    do iterative upgrade for cache depending of theses two data.
+# XXX copied as is from Mercurial 4.2 and added the "offset" parameters
+@util.nogc
+def _readmarkers(data, offset=None):
+    """Read and enumerate markers from raw data"""
+    off = 0
+    diskversion = struct.unpack('>B', data[off:off + 1])[0]
+    if offset is None:
+        off += 1
+    else:
+        assert 1 <= offset
+        off = offset
+    if diskversion not in obsolete.formats:
+        raise error.Abort(_('parsing obsolete marker: unknown version %r')
+                          % diskversion)
+    return diskversion, obsolete.formats[diskversion][0](data, off)
 
-    The cache key parts are"
-    - tip-rev,
-    - tip-node,
-    - obsstore-length (nb markers),
-    - obsstore-file-size (in bytes),
-    - obsstore "cache key"
-    """
-    assert repo.filtername is None
-    cl = repo.changelog
-    index, key = repo.obsstore.cachekey()
-    tiprev = len(cl) - 1
-    return (tiprev,
-            cl.node(tiprev),
-            len(repo.obsstore),
-            index,
-            key)
+def markersfrom(obsstore, byteoffset, firstmarker):
+    if not firstmarker:
+        return list(obsstore)
+    elif '_all' in vars(obsstore):
+        # if the data are in memory, just use that
+        return obsstore._all[firstmarker:]
+    else:
+        obsdata = obsstore.svfs.tryread('obsstore')
+        return _readmarkers(obsdata, byteoffset)[1]
 
-def upgradeneeded(repo, key):
-    """return (valid, start-rev, start-obs-idx)
 
-    'valid': is "False" if older cache value needs invalidation,
+class dualsourcecache(object):
+    """An abstract class for cache that needs both changelog and obsstore
 
-    'start-rev': first revision not in the cache. None if cache is up to date,
-
-    'start-obs-idx': index of the first obs-markers not in the cache. None is
-                     up to date.
+    This class handle the tracking of changelog and obsstore update. It provide
+    data to performs incremental update (see the 'updatefrom' function for
+    details).  This class can also detect stripping of the changelog or the
+    obsstore and can reset the cache in this cache (see the 'clear' function
+    for details).
     """
 
-    # XXX ideally, this function would return a bounded amount of changeset and
-    # obsmarkers and the associated new cache key. Otherwise we are exposed to
-    # a race condition between the time the cache is updated and the new cache
-    # key is computed. (however, we do not want to compute the full new cache
-    # key in all case because we want to skip reading the obsstore content. We
-    # could have a smarter implementation here.
+    # default key used for an empty cache
+    #
+    # The cache key covering the changesets and obsmarkers content
     #
-    # In pratice the cache is only updated after each transaction within a
-    # lock. So we should be fine. We could enforce this with a new repository
-    # requirement (or fix the race, that is not too hard).
-    invalid = (False, 0, 0)
-    if key is None:
-        return invalid
+    # The cache key parts are:
+    # - tip-rev,
+    # - tip-node,
+    # - obsstore-length (nb markers),
+    # - obsstore-file-size (in bytes),
+    # - obsstore "cache key"
+    emptykey = (node.nullrev, node.nullid, 0, 0, node.nullid)
+    _cachename = None # used for error message
+
+    def __init__(self):
+        super(dualsourcecache, self).__init__()
+        self._cachekey = None
+
+    def _updatefrom(self, repo, revs, obsmarkers):
+        """override this method to update your cache data incrementally
+
+        revs:      list of new revision in the changelog
+        obsmarker: list of new obsmarkers in the obsstore
+        """
+        raise NotImplementedError
+
+    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.
+        """
+        # /!\ IMPORTANT /!\
+        # You must overide this method to actually
+        if reset:
+            self._cachekey = self.emptykey if reset else None
+        else:
+            self._cachekey = None
+
+    def load(self, repo):
+        """Load data from disk
+
+        Do not forget to restore the "cachekey" attribute while doing so.
+        """
+        raise NotImplementedError
+
+    # Useful public function (no need to override them)
+
+    def uptodate(self, repo):
+        """return True if the cache content is up to date False otherwise
+
+        This method can be used to detect of the cache is lagging behind new
+        data in either changelog or obsstore.
+        """
+        if self._cachekey is None:
+            self.load(repo)
+        status = self._checkkey(repo.changelog, repo.obsstore)
+        return (status is not None
+                and status[0] == self._cachekey[0] # tiprev
+                and status[1] == self._cachekey[3]) # obssize
+
+    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)
+
+        assert repo.filtername is None
+        cl = repo.changelog
+
+        upgrade = self._upgradeneeded(repo)
+        if upgrade is None:
+            return
+
+        reset, revs, obsmarkers, obskeypair = upgrade
+        if reset or self._cachekey is None:
+            repo.ui.log('evoext-cache', 'strip detected, %s cache reset\n' % self._cachename)
+            self.clear(reset=True)
+
+        starttime = timer()
+        self._updatefrom(repo, revs, obsmarkers)
+        duration = timer() - starttime
+        repo.ui.log('evoext-cache', 'updated %s in %.4f seconds (%sr, %so)\n',
+                    self._cachename, duration, len(revs), len(obsmarkers))
 
-    ### Is the cache valid ?
-    keytiprev, keytipnode, keyobslength, keyobssize, keyobskey = key
-    # check for changelog strip
-    cl = repo.changelog
-    tiprev = len(cl) - 1
-    if (tiprev < keytiprev
-            or cl.node(keytiprev) != keytipnode):
-        return invalid
-    # check for obsstore strip
-    obssize, obskey = repo.obsstore.cachekey(index=keyobssize)
-    if obskey != keyobskey:
-        return invalid
+        # update the key from the new data
+        key = list(self._cachekey)
+        if revs:
+            key[0] = len(cl) - 1
+            key[1] = cl.node(key[0])
+        if obsmarkers:
+            key[2] += len(obsmarkers)
+            key[3], key[4] = obskeypair
+        self._cachekey = tuple(key)
+
+    # from here, there are internal function only
+
+    def _checkkey(self, changelog, obsstore):
+        """internal function"""
+        key = self._cachekey
+        if key is None:
+            return None
+
+        ### Is the cache valid ?
+        keytiprev, keytipnode, keyobslength, keyobssize, keyobskey = key
+        # check for changelog strip
+        tiprev = len(changelog) - 1
+        if (tiprev < keytiprev
+                or changelog.node(keytiprev) != keytipnode):
+            return None
+        # check for obsstore strip
+        obssize, obskey = obsstore.cachekey(index=keyobssize)
+        if obskey != keyobskey:
+            return None
+        if obssize != keyobssize:
+            # we want to return the obskey for the new size
+            __, obskey = obsstore.cachekey(index=obssize)
+        return tiprev, obssize, obskey
+
+    def _upgradeneeded(self, repo):
+        """return (valid, start-rev, start-obs-idx)
+
+        'valid': is "False" if older cache value needs invalidation,
+
+        'start-rev': first revision not in the cache. None if cache is up to date,
+
+        'start-obs-idx': index of the first obs-markers not in the cache. None is
+                         up to date.
+        """
 
-    ### cache is valid, is there anything to update
+        # We need to ensure we use the same changelog and obsstore through the
+        # processing. Otherwise some invalidation could update the object and their
+        # content after we computed the cache key.
+        cl = repo.changelog
+        obsstore = repo.obsstore
+        key = self._cachekey
+
+        reset = False
 
-    # any new changesets ?
-    startrev = None
-    if keytiprev < tiprev:
-        startrev = keytiprev + 1
+        status = self._checkkey(cl, obsstore)
+        if status is None:
+            reset = True
+            key = self.emptykey
+            obssize, obskey = obsstore.cachekey()
+            tiprev = len(cl) - 1
+        else:
+            tiprev, obssize, obskey = status
+
+        keytiprev, keytipnode, keyobslength, keyobssize, keyobskey = key
+
+        if not reset and keytiprev == tiprev and keyobssize == obssize:
+            return None # nothing to upgrade
 
-    # any new markers
-    startidx = None
-    if keyobssize < obssize:
-        startidx = keyobslength
+        ### cache is valid, is there anything to update
+
+        # any new changesets ?
+        revs = ()
+        if keytiprev < tiprev:
+            revs = list(cl.revs(start=keytiprev + 1, stop=tiprev))
 
-    return True, startrev, startidx
+        # any new markers
+        markers = ()
+        if keyobssize < obssize:
+            # XXX Three are a small race change here. Since the obsstore might have
+            # move forward between the time we computed the cache key and we access
+            # the data. To fix this we need so "up to" argument when fetching the
+            # markers here. Otherwise we might return more markers than covered by
+            # the cache key.
+            #
+            # In pratice the cache is only updated after each transaction within a
+            # lock. So we should be fine. We could enforce this with a new repository
+            # requirement (or fix the race, that is not too hard).
+            markers = markersfrom(obsstore, keyobssize, keyobslength)
 
-class obscache(object):
+        return reset, revs, markers, (obssize, obskey)
+
+
+class obscache(dualsourcecache):
     """cache the "does a rev" is the precursors of some obsmarkers data
 
     This is not directly holding the "is this revision obsolete" information,
@@ -197,16 +358,12 @@
     _filepath = 'cache/evoext-obscache-00'
     _headerformat = '>q20sQQ20s'
 
+    _cachename = 'evo-ext-obscache' # used for error message
+
     def __init__(self, repo):
+        super(obscache, self).__init__()
+        self._ondiskkey = None
         self._vfs = repo.vfs
-        # The cache key parts are"
-        # - tip-rev,
-        # - tip-node,
-        # - obsstore-length (nb markers),
-        # - obsstore-file-size (in bytes),
-        # - obsstore "cache key"
-        self._cachekey = None
-        self._ondiskkey = None
         self._data = bytearray()
 
     def get(self, rev):
@@ -215,90 +372,56 @@
         Make sure the cache has been updated to match the repository content before using it"""
         return self._data[rev]
 
-    def clear(self):
+    def clear(self, reset=False):
         """invalidate the cache content"""
-        self._cachekey = None
+        super(obscache, self).clear(reset=reset)
         self._data = bytearray()
 
-    def uptodate(self, repo):
-        if self._cachekey is None:
-            self.load(repo)
-        valid, startrev, startidx = upgradeneeded(repo, self._cachekey)
-        return (valid and startrev is None and startidx is None)
+    def _updatefrom(self, repo, revs, obsmarkers):
+        if revs:
+            self._updaterevs(repo, revs)
+        if obsmarkers:
+            self._updatemarkers(repo, obsmarkers)
+
+    def _updaterevs(self, repo, revs):
+        """update the cache with new revisions
+
+        Newly added changeset might be affected by obsolescence markers
+        we already have locally. So we needs to have some global
+        knowledge about the markers to handle that question.
+
+        Right now this requires parsing all markers in the obsstore. We could
+        imagine using various optimisation (eg: another cache, network
+        exchange, etc).
 
-    def update(self, repo):
-        """Iteratively update the cache with new repository data"""
-        # If we do not have any data, try loading from disk
-        if self._cachekey is None:
-            self.load(repo)
+        A possible approach to this is to build a set of all node used as
+        precursors in `obsstore._obscandidate`. If markers are not loaded yet,
+        we could initialize it by doing a quick scan through the obsstore data
+        and filling a (pre-sized) set. Doing so would be much faster than
+        parsing all the obsmarkers since we would access less data, not create
+        any object beside the nodes and not have to decode any complex data.
 
-        valid, startrev, startidx = upgradeneeded(repo, self._cachekey)
-        if not valid:
-            self.clear()
-
-        if startrev is None and startidx is None:
-            return
-
-        # process the new changesets
+        For now we stick to the simpler approach of paying the
+        performance cost on new changesets.
+        """
+        node = repo.changelog.node
+        succs = repo.obsstore.successors
+        for r in revs:
+            if node(r) in succs:
+                val = 1
+            else:
+                val = 0
+            self._data.append(val)
         cl = repo.changelog
-        if startrev is not None:
-            node = cl.node
-            # Note:
-            #
-            #  Newly added changeset might be affected by obsolescence markers
-            #  we already have locally. So we needs to have soem global
-            #  knowledge about the markers to handle that question. Right this
-            #  requires parsing all markers in the obsstore. However, we could
-            #  imagine using various optimisation (eg: bloom filter, other on
-            #  disk cache) to remove this full parsing.
-            #
-            #  For now we stick to the simpler approach or paying the
-            #  performance cost on new changesets.
-            succs = repo.obsstore.successors
-            for r in cl.revs(startrev):
-                if node(r) in succs:
-                    val = 1
-                else:
-                    val = 0
-                self._data.append(val)
         assert len(self._data) == len(cl), (len(self._data), len(cl))
 
-        # process the new obsmarkers
-        if startidx is not None:
-            rev = cl.nodemap.get
-            markers = repo.obsstore._all
-            # Note:
-            #
-            #   There are no actually needs to load the full obsstore here,
-            #   since we only read the latest ones.  We do it for simplicity in
-            #   the first implementation. Loading the full obsstore has a
-            #   performance cost and should go away in this case too. We have
-            #   two simples options for that:
-            #
-            #   1) provide and API to start reading markers from a byte offset
-            #      (we have that data in the cache key)
-            #
-            #   2) directly update the cache at a lower level, in the code
-            #      responsible for adding a markers.
-            #
-            #   Option 2 is probably a bit more invasive, but more solid on the long run
-
-            for i in xrange(startidx, len(repo.obsstore)):
-                r = rev(markers[i][0])
-                # If markers affect a newly added nodes, it would have been
-                # caught in the previous loop, (so we skip < startrev)
-                if r is not None and (startrev is None or r < startrev):
-                    self._data[r] = 1
-
-        assert repo._currentlock(repo._lockref) is not None
-        # XXX note that there are a potential race condition here, since the
-        # repo "might" have changed side the cache update above. However, this
-        # code will only be running in a lock so we ignore the issue for now.
-        #
-        # To work around this, 'upgradeneeded' should return a bounded amount
-        # of changeset and markers to read with their associated cachekey. see
-        # 'upgradeneeded' for detail.
-        self._cachekey = getcachekey(repo)
+    def _updatemarkers(self, repo, obsmarkers):
+        """update the cache with new markers"""
+        rev = repo.changelog.nodemap.get
+        for m in obsmarkers:
+            r = rev(m[0])
+            if r is not None:
+                self._data[r] = 1
 
     def save(self, repo):
         """save the data to disk"""
@@ -319,7 +442,7 @@
 
         data = repo.vfs.tryread(self._filepath)
         if not data:
-            self._cachekey = emptykey
+            self._cachekey = self.emptykey
             self._data = bytearray()
         else:
             headersize = struct.calcsize(self._headerformat)
@@ -339,7 +462,7 @@
     if notpublic:
         obscache = repo.obsstore.obscache
         # Since we warm the cache at the end of every transaction, the cache
-        # should be up to date. However a non-enabled client might have touced
+        # should be up to date. However a non-enabled client might have touched
         # the repository.
         #
         # Updating the cache without a lock is sloppy, so we fallback to the
@@ -348,17 +471,18 @@
         #
         # With the current implementation updating the cache will requires to
         # load the obsstore anyway. Once loaded, hitting the obsstore directly
-        # will be about as fast..
+        # will be about as fast...
         if not obscache.uptodate(repo):
             if repo.currenttransaction() is None:
-                repo.ui.log('evoext-obscache',
+                repo.ui.log('evoext-cache',
                             'obscache is out of date, '
                             'falling back to slower obsstore version\n')
                 repo.ui.debug('obscache is out of date')
                 return orig(repo)
             else:
-                # If a transaction is open, it is worthwhile to update and use the
-                # cache as it will be written on disk when the transaction close.
+                # If a transaction is open, it is worthwhile to update and use
+                # the cache, the lock prevent race and it will be written on
+                # disk when the transaction close.
                 obscache.update(repo)
         isobs = obscache.get
     for r in notpublic:
@@ -381,6 +505,7 @@
         def destroyed(self):
             if 'obsstore' in vars(self):
                 self.obsstore.obscache.clear()
+            super(obscacherepo, self).destroyed()
 
         def transaction(self, *args, **kwargs):
             tr = super(obscacherepo, self).transaction(*args, **kwargs)
@@ -391,11 +516,10 @@
                 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 saving
-                # logic.
+                # 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)
 
--- a/hgext3rd/evolve/obsdiscovery.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/evolve/obsdiscovery.py	Thu May 18 23:51:10 2017 +0200
@@ -24,8 +24,10 @@
 
 import hashlib
 import heapq
+import os
 import sqlite3
 import struct
+import time
 import weakref
 
 from mercurial import (
@@ -36,6 +38,7 @@
     localrepo,
     node,
     obsolete,
+    pycompat,
     scmutil,
     setdiscovery,
     util,
@@ -46,10 +49,21 @@
 
 from . import (
     exthelper,
+    obscache,
     utility,
     stablerange,
 )
 
+# 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
+
 _pack = struct.pack
 _unpack = struct.unpack
 _calcsize = struct.calcsize
@@ -214,6 +228,7 @@
                      initialsamplesize=100,
                      fullsamplesize=200):
     missing = set()
+    starttime = timer()
 
     heads = local.revs('heads(%ld)', probeset)
     local.stablerange.warmup(local)
@@ -241,6 +256,7 @@
         entry = (h, 0)
         addentry(entry)
 
+    local.obsstore.rangeobshashcache.update(local)
     querycount = 0
     ui.progress(_("comparing obsmarker with other"), querycount)
     overflow = []
@@ -300,6 +316,12 @@
         ui.progress(_("comparing obsmarker with other"), querycount)
     ui.progress(_("comparing obsmarker with other"), None)
     local.obsstore.rangeobshashcache.save(local)
+    duration = timer() - starttime
+    logmsg = ('obsdiscovery, %d/%d mismatch'
+              ' - %d obshashrange queries in %.4f seconds\n')
+    logmsg %= (len(missing), len(probeset), querycount, duration)
+    ui.log('evoext-obsdiscovery', logmsg)
+    ui.debug(logmsg)
     return sorted(missing)
 
 def _queryrange(ui, repo, remote, allentries):
@@ -342,6 +364,7 @@
     linetemplate = '%12d %12s %12d %12d %12d %12s\n'
     headertemplate = linetemplate.replace('d', 's')
     ui.status(headertemplate % headers)
+    repo.obsstore.rangeobshashcache.update(repo)
     for r in ranges:
         d = (r[0],
              s(cl.node(r[0])),
@@ -392,10 +415,11 @@
 
 _sqliteschema = [
     """CREATE TABLE meta(schemaversion INTEGER NOT NULL,
+                         tiprev        INTEGER NOT NULL,
+                         tipnode       BLOB    NOT NULL,
                          nbobsmarker   INTEGER NOT NULL,
-                         obstipdata    BLOB    NOT NULL,
-                         tiprev        INTEGER NOT NULL,
-                         tipnode       BLOB    NOT NULL
+                         obssize       BLOB    NOT NULL,
+                         obskey        BLOB    NOT NULL
                         );""",
     """CREATE TABLE obshashrange(rev     INTEGER NOT NULL,
                                  idx     INTEGER NOT NULL,
@@ -404,53 +428,132 @@
     "CREATE INDEX range_index ON obshashrange(rev, idx);",
 ]
 _queryexist = "SELECT name FROM sqlite_master WHERE type='table' AND name='meta';"
-_newmeta = """INSERT INTO meta (schemaversion, nbobsmarker, obstipdata, tiprev, tipnode)
-            VALUES (?,?,?,?,?);"""
+_clearmeta = """DELETE FROM meta;"""
+_newmeta = """INSERT INTO meta (schemaversion, tiprev, tipnode, nbobsmarker, obssize, obskey)
+            VALUES (?,?,?,?,?,?);"""
 _updateobshash = "INSERT INTO obshashrange(rev, idx, obshash) VALUES (?,?,?);"
-_querymeta = "SELECT schemaversion, nbobsmarker, obstipdata, tiprev, tipnode FROM meta;"
+_querymeta = "SELECT schemaversion, tiprev, tipnode, nbobsmarker, obssize, obskey FROM meta;"
 _queryobshash = "SELECT obshash FROM obshashrange WHERE (rev = ? AND idx = ?);"
 
-class _obshashcache(dict):
+_reset = "DELETE FROM obshashrange;"
+
+class _obshashcache(obscache.dualsourcecache):
 
-    _schemaversion = 0
+    _schemaversion = 1
+
+    _cachename = 'evo-ext-obshashrange' # used for error message
 
     def __init__(self, repo):
         super(_obshashcache, self).__init__()
-        self._path = repo.vfs.join('cache/evoext_obshashrange_v0.sqlite')
+        self._vfs = repo.vfs
+        self._path = repo.vfs.join('cache/evoext_obshashrange_v1.sqlite')
         self._new = set()
         self._valid = True
         self._repo = weakref.ref(repo.unfiltered())
         # cache status
         self._ondiskcachekey = None
+        self._data = {}
 
-    def clear(self):
-        self._valid = False
-        super(_obshashcache, self).clear()
+    def clear(self, reset=False):
+        super(_obshashcache, self).clear(reset=reset)
+        self._data.clear()
         self._new.clear()
+        if reset:
+            self._valid = False
+        if '_con' in vars(self):
+            del self._con
 
     def get(self, rangeid):
-        value = super(_obshashcache, self).get(rangeid)
+        # revision should be covered by the tiprev
+        #
+        # XXX there are issue with cache warming, we hack around it for now
+        if not getattr(self, '_updating', False):
+            if self._cachekey[0] < rangeid[0]:
+                msg = ('using unwarmed obshashrangecache (%s %s)'
+                       % (rangeid[0], self._cachekey[0]))
+                raise error.ProgrammingError(msg)
+
+        value = self._data.get(rangeid)
         if value is None and self._con is not None:
             nrange = (rangeid[0], rangeid[1])
             obshash = self._con.execute(_queryobshash, nrange).fetchone()
             if obshash is not None:
                 value = obshash[0]
+            self._data[rangeid] = value
         return value
 
     def __setitem__(self, rangeid, obshash):
         self._new.add(rangeid)
-        super(_obshashcache, self).__setitem__(rangeid, obshash)
+        self._data[rangeid] = obshash
+
+    def _updatefrom(self, repo, revs, obsmarkers):
+        """override this method to update your cache data incrementally
+
+        revs:      list of new revision in the changelog
+        obsmarker: list of new obsmarkers in the obsstore
+        """
+        # XXX for now, we'll not actually update the cache, but we'll be
+        # smarter at invalidating it.
+        #
+        # 1) new revisions does not get their entry updated (not update)
+        # 2) if we detect markers affecting non-new revision we reset the cache
+
+        self._updating = True
+
+        setrevs = set(revs)
+        rev = repo.changelog.nodemap.get
+        # if we have a new markers affecting a node already covered by the
+        # cache, we must abort.
+        affected = set()
+        for m in obsmarkers:
+            # check successors and parent
+            for l in (m[1], m[5]):
+                if l is None:
+                    continue
+                for p in l:
+                    r = rev(p)
+                    if r is not None and r not in setrevs:
+                        # XXX should check < min(setrevs) or tiprevs
+                        affected.add(r)
 
-    def _cachekey(self, repo):
-        # XXX for now the cache is very volatile, but this is still a win
-        nbobsmarker = len(repo.obsstore._all)
-        if nbobsmarker:
-            tipdata = obsolete._fm1encodeonemarker(repo.obsstore._all[-1])
-        else:
-            tipdata = node.nullid
-        tiprev = len(repo.changelog) - 1
-        tipnode = repo.changelog.node(tiprev)
-        return (self._schemaversion, nbobsmarker, tipdata, tiprev, tipnode)
+        if affected:
+            repo.ui.log('evoext-cache', 'obshashcache reset - '
+                        'new markers affect cached ranges\n')
+            # XXX the current reset is too strong we could just drop the affected range
+            con = self._con
+            if con is not None:
+                con.execute(_reset)
+            # rewarm the whole cache
+            stop = self._cachekey[0] # tiprev
+            if revs:
+                stop = max(revs)
+            if 0 <= stop:
+                revs = repo.changelog.revs(stop=stop)
+
+        # warm the cache for the new revs
+        for r in revs:
+            _obshashrange(repo, (r, 0))
+
+        del self._updating
+
+    @property
+    def _fullcachekey(self):
+        return (self._schemaversion, ) + self._cachekey
+
+    def load(self, repo):
+        if self._con is None:
+            self._cachekey = self.emptykey
+            self._ondiskcachekey = self.emptykey
+        assert self._cachekey is not None
+
+    def _db(self):
+        try:
+            util.makedirs(self._vfs.dirname(self._path))
+        except OSError:
+            return None
+        con = sqlite3.connect(self._path)
+        con.text_factory = str
+        return con
 
     @util.propertycache
     def _con(self):
@@ -459,25 +562,27 @@
         repo = self._repo()
         if repo is None:
             return None
-        cachekey = self._cachekey(repo)
-        con = sqlite3.connect(self._path)
-        con.text_factory = str
+        con = self._db()
+        if con is None:
+            return None
         cur = con.execute(_queryexist)
         if cur.fetchone() is None:
             self._valid = False
             return None
         meta = con.execute(_querymeta).fetchone()
-        if meta != cachekey:
+        if meta is None or meta[0] != self._schemaversion:
             self._valid = False
             return None
-        self._ondiskcachekey = meta
+        self._cachekey = self._ondiskcachekey = meta[1:]
         return con
 
     def save(self, repo):
+        if self._cachekey is None:
+            return
+        if self._cachekey == self._ondiskcachekey and not self._new:
+            return
         repo = repo.unfiltered()
         try:
-            if not self._new:
-                return
             with repo.lock():
                 self._save(repo)
         except error.LockError:
@@ -493,32 +598,38 @@
             if '_con' in vars(self):
                 del self._con
 
-            con = sqlite3.connect(self._path)
-            con.text_factory = str
+            con = self._db()
+            if con is None:
+                repo.ui.log('evoext-cache', 'unable to write obshashrange cache'
+                            ' - cannot create database')
+                return
             with con:
                 for req in _sqliteschema:
                     con.execute(req)
 
-                con.execute(_newmeta, self._cachekey(repo))
+                con.execute(_newmeta, self._fullcachekey)
         else:
             con = self._con
             if self._ondiskcachekey is not None:
                 meta = con.execute(_querymeta).fetchone()
-                if meta != self._ondiskcachekey:
+                if meta[1:] != self._ondiskcachekey:
                     # drifting is currently an issue because this means another
                     # process might have already added the cache line we are about
                     # to add. This will confuse sqlite
                     msg = _('obshashrange cache: skipping write, '
                             'database drifted under my feet\n')
-                    data = (meta[2], meta[1], self._ondisktiprev, self._ondisktipnode)
+                    data = (meta[2], meta[1], self._ondiskcachekey[0], self._ondiskcachekey[1])
                     repo.ui.warn(msg)
-        data = ((rangeid[0], rangeid[1], self[rangeid]) for rangeid in self._new)
+                    return
+        data = ((rangeid[0], rangeid[1], self.get(rangeid)) for rangeid in self._new)
         con.executemany(_updateobshash, data)
-        cachekey = self._cachekey(repo)
+        cachekey = self._fullcachekey
+        con.execute(_clearmeta) # remove the older entry
         con.execute(_newmeta, cachekey)
         con.commit()
         self._new.clear()
-        self._ondiskcachekey = cachekey
+        self._ondiskcachekey = self._cachekey
+        self._valid = True
 
 @eh.wrapfunction(obsolete.obsstore, '_addmarkers')
 def _addmarkers(orig, obsstore, *args, **kwargs):
@@ -549,10 +660,31 @@
     class obshashrepo(repo.__class__):
         @localrepo.unfilteredmethod
         def destroyed(self):
-            if 'stablerange' in vars(self):
-                del self.stablerange
+            if 'obsstore' in vars(self):
+                self.obsstore.rangeobshashcache.clear()
             super(obshashrepo, self).destroyed()
 
+        def transaction(self, *args, **kwargs):
+            tr = super(obshashrepo, self).transaction(*args, **kwargs)
+            reporef = weakref.ref(self)
+
+            def _warmcache(tr):
+                repo = reporef()
+                if repo is None:
+                    return
+                if not repo.ui.configbool('experimental', 'obshashrange', False):
+                    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.rangeobshashcache.update(repo)
+                self.obsstore.rangeobshashcache.save(repo)
+
+            tr.addpostclose('warmcache-20-obscacherange', _warmcache)
+            return tr
+
     repo.__class__ = obshashrepo
 
 ### wire protocol commands
@@ -570,6 +702,7 @@
         if maxrev is not None:
             repo.stablerange.warmup(repo, upto=maxrev)
     result = []
+    repo.obsstore.rangeobshashcache.update(repo)
     for r in ranges:
         if r[0] is None:
             result.append(node.wdirid)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/obshistory.py	Thu May 18 23:51:10 2017 +0200
@@ -0,0 +1,357 @@
+# Code dedicated to display and exploration of the obsolescence history
+#
+# This module content aims at being upstreamed enventually.
+#
+# 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 mercurial import (
+    cmdutil,
+    commands,
+    error,
+    graphmod,
+    node as nodemod,
+    scmutil,
+)
+
+from mercurial.i18n import _
+
+from . import (
+    exthelper,
+)
+
+eh = exthelper.exthelper()
+
+@eh.command(
+    'olog',
+    [('G', 'graph', True, _("show the revision DAG")),
+     ('r', 'rev', [], _('show the specified revision or revset'), _('REV'))
+    ] + commands.formatteropts,
+    _('hg olog [OPTION]... [REV]'))
+def cmdobshistory(ui, repo, *revs, **opts):
+    """show the obsolescence history of the specified revisions.
+
+    If no revision range is specified, we display the log for the current
+    working copy parent.
+
+    By default this command prints the selected revisions and all its
+    precursors. For precursors pointing on existing revisions in the repository,
+    it will display revisions node id, revision number and the first line of the
+    description. For precursors pointing on non existing revisions in the
+    repository (that can happen when exchanging obsolescence-markers), display
+    only the node id.
+
+    In both case, for each node, its obsolescence marker will be displayed with
+    the obsolescence operation (rewritten or pruned) in addition of the user and
+    date of the operation.
+
+    The output is a graph by default but can deactivated with the option '--no-
+    graph'.
+
+    'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
+    and '+' represents a fork where the changeset from the lines below is a
+    parent of the 'o' merge on the same line.
+
+    Paths in the DAG are represented with '|', '/' and so forth.
+
+    Returns 0 on success.
+    """
+    revs = list(revs) + opts['rev']
+    if not revs:
+        revs = ['.']
+    revs = scmutil.revrange(repo, revs)
+
+    if opts['graph']:
+        return _debugobshistorygraph(ui, repo, revs, opts)
+
+    fm = ui.formatter('debugobshistory', opts)
+    revs.reverse()
+    _debugobshistorysingle(fm, repo, revs)
+
+    fm.end()
+
+class obsmarker_printer(cmdutil.changeset_printer):
+    """show (available) information about a node
+
+    We display the node, description (if available) and various information
+    about obsolescence markers affecting it"""
+
+    def show(self, ctx, copies=None, matchfn=None, **props):
+        if self.buffered:
+            self.ui.pushbuffer(labeled=True)
+
+            changenode = ctx.node()
+
+            fm = self.ui.formatter('debugobshistory', props)
+            _debugobshistorydisplaynode(fm, self.repo, changenode)
+
+            succs = self.repo.obsstore.successors.get(changenode, ())
+
+            markerfm = fm.nested("debugobshistory.markers")
+            for successor in sorted(succs):
+                _debugobshistorydisplaymarker(markerfm, self.repo, successor)
+            markerfm.end()
+
+            markerfm.plain('\n')
+
+            self.hunk[ctx.node()] = self.ui.popbuffer()
+        else:
+            ### graph output is buffered only
+            msg = 'cannot be used outside of the graphlog (yet)'
+            raise error.ProgrammingError(msg)
+
+    def flush(self, ctx):
+        ''' changeset_printer has some logic around buffering data
+        in self.headers that we don't use
+        '''
+        pass
+
+class missingchangectx(object):
+    ''' a minimal object mimicking changectx for change contexts
+    references by obs markers but not available locally '''
+
+    def __init__(self, repo, nodeid):
+        self._repo = repo
+        self._node = nodeid
+
+    def node(self):
+        return self._node
+
+    def obsolete(self):
+        # If we don't have it locally, it's obsolete
+        return True
+
+def cyclic(graph):
+    """Return True if the directed graph has a cycle.
+    The graph must be represented as a dictionary mapping vertices to
+    iterables of neighbouring vertices. For example:
+
+    >>> cyclic({1: (2,), 2: (3,), 3: (1,)})
+    True
+    >>> cyclic({1: (2,), 2: (3,), 3: (4,)})
+    False
+
+    Taken from: https://codereview.stackexchange.com/a/86067
+
+    """
+    visited = set()
+    o = object()
+    path = [o]
+    path_set = set(path)
+    stack = [iter(graph)]
+    while stack:
+        for v in sorted(stack[-1]):
+            if v in path_set:
+                path_set.remove(o)
+                return path_set
+            elif v not in visited:
+                visited.add(v)
+                path.append(v)
+                path_set.add(v)
+                stack.append(iter(graph.get(v, ())))
+                break
+        else:
+            path_set.remove(path.pop())
+            stack.pop()
+    return False
+
+def _obshistorywalker(repo, revs):
+    """ Directly inspired by graphmod.dagwalker,
+    walk the obs marker tree and yield
+    (id, CHANGESET, ctx, [parentinfo]) tuples
+    """
+
+    # Get the list of nodes and links between them
+    candidates, nodesucc, nodeprec = _obshistorywalker_links(repo, revs)
+
+    # Shown, set of nodes presents in items
+    shown = set()
+
+    def isvalidcandidate(candidate):
+        """ Function to filter candidates, check the candidate succ are
+        in shown set
+        """
+        return nodesucc.get(candidate, set()).issubset(shown)
+
+    # While we have some nodes to show
+    while candidates:
+
+        # Filter out candidates, returns only nodes with all their successors
+        # already shown
+        validcandidates = filter(isvalidcandidate, candidates)
+
+        # If we likely have a cycle
+        if not validcandidates:
+            cycle = cyclic(nodesucc)
+            assert cycle
+
+            # Then choose a random node from the cycle
+            breaknode = sorted(cycle)[0]
+            # And display it by force
+            repo.ui.debug('obs-cycle detected, forcing display of %s\n'
+                          % nodemod.short(breaknode))
+            validcandidates = [breaknode]
+
+        # Display all valid candidates
+        for cand in sorted(validcandidates):
+            # Remove candidate from candidates set
+            candidates.remove(cand)
+            # And remove it from nodesucc in case of future cycle detected
+            try:
+                del nodesucc[cand]
+            except KeyError:
+                pass
+
+            shown.add(cand)
+
+            # Add the right changectx class
+            if cand in repo:
+                changectx = repo[cand]
+            else:
+                changectx = missingchangectx(repo, cand)
+
+            childrens = [(graphmod.PARENT, x) for x in nodeprec.get(cand, ())]
+            yield (cand, 'M', changectx, childrens)
+
+def _obshistorywalker_links(repo, revs):
+    """ Iterate the obs history tree starting from revs, traversing
+    each revision precursors recursively.
+    Return a tuple of:
+    - The list of node crossed
+    - The dictionnary of each node successors, values are a set
+    - The dictionnary of each node precursors, values are a list
+    """
+    precursors = repo.obsstore.precursors
+    nodec = repo.changelog.node
+
+    # Parents, set of parents nodes seen during walking the graph for node
+    nodesucc = dict()
+    # Childrens
+    nodeprec = dict()
+
+    nodes = [nodec(r) for r in revs]
+    seen = set(nodes)
+
+    # Iterate on each node
+    while nodes:
+        node = nodes.pop()
+
+        precs = precursors.get(node, ())
+
+        nodeprec[node] = []
+
+        for prec in sorted(precs):
+            precnode = prec[0]
+
+            # Mark node as prec successor
+            nodesucc.setdefault(precnode, set()).add(node)
+
+            # Mark precnode as node precursor
+            nodeprec[node].append(precnode)
+
+            # Add prec for future processing if not node already processed
+            if precnode not in seen:
+                seen.add(precnode)
+                nodes.append(precnode)
+
+    return sorted(seen), nodesucc, nodeprec
+
+def _debugobshistorygraph(ui, repo, revs, opts):
+    displayer = obsmarker_printer(ui, repo.unfiltered(), None, opts, buffered=True)
+    edges = graphmod.asciiedges
+    cmdutil.displaygraph(ui, repo, _obshistorywalker(repo.unfiltered(), revs), displayer, edges)
+
+def _debugobshistorysingle(fm, repo, revs):
+    """ Display the obsolescence history for a single revision
+    """
+    precursors = repo.obsstore.precursors
+    successors = repo.obsstore.successors
+    nodec = repo.changelog.node
+    nodes = [nodec(r) for r in revs]
+
+    seen = set(nodes)
+
+    while nodes:
+        ctxnode = nodes.pop()
+
+        _debugobshistorydisplaynode(fm, repo, ctxnode)
+
+        succs = successors.get(ctxnode, ())
+
+        markerfm = fm.nested("debugobshistory.markers")
+        for successor in sorted(succs):
+            _debugobshistorydisplaymarker(markerfm, repo, successor)
+        markerfm.end()
+
+        precs = precursors.get(ctxnode, ())
+        for p in sorted(precs):
+            # Only show nodes once
+            if p[0] not in seen:
+                seen.add(p[0])
+                nodes.append(p[0])
+
+def _debugobshistorydisplaynode(fm, repo, node):
+    if node in repo.unfiltered():
+        _debugobshistorydisplayctx(fm, repo.unfiltered()[node])
+    else:
+        _debugobshistorydisplaymissingctx(fm, node)
+
+def _debugobshistorydisplayctx(fm, ctx):
+    shortdescription = ctx.description().splitlines()[0]
+
+    fm.startitem()
+    fm.write('debugobshistory.node', '%s', str(ctx),
+             label="evolve.node")
+    fm.plain(' ')
+
+    fm.write('debugobshistory.rev', '(%d)', int(ctx),
+             label="evolve.rev")
+    fm.plain(' ')
+
+    fm.write('debugobshistory.shortdescription', '%s', shortdescription,
+             label="evolve.short_description")
+    fm.plain('\n')
+
+def _debugobshistorydisplaymissingctx(fm, nodewithoutctx):
+    hexnode = nodemod.short(nodewithoutctx)
+    fm.startitem()
+    fm.write('debugobshistory.node', '%s', hexnode,
+             label="evolve.node evolve.missing_change_ctx")
+    fm.plain('\n')
+
+def _debugobshistorydisplaymarker(fm, repo, marker):
+    succnodes = marker[1]
+    date = marker[4]
+    metadata = dict(marker[3])
+
+    fm.startitem()
+    fm.plain('  ')
+
+    # Detect pruned revisions
+    if len(succnodes) == 0:
+        verb = 'pruned'
+    else:
+        verb = 'rewritten'
+
+    fm.write('debugobshistory.verb', '%s', verb,
+             label="evolve.verb")
+    fm.plain(' by ')
+
+    fm.write('debugobshistory.marker_user', '%s', metadata['user'],
+             label="evolve.user")
+    fm.plain(' ')
+
+    fm.write('debugobshistory.marker_date', '(%s)', fm.formatdate(date),
+             label="evolve.date")
+
+    if len(succnodes) > 0:
+        fm.plain(' as ')
+
+        shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
+        nodes = fm.formatlist(shortsnodes, 'debugobshistory.succnodes', sep=', ')
+        fm.write('debugobshistory.succnodes', '%s', nodes,
+                 label="evolve.node")
+
+    fm.plain("\n")
--- a/hgext3rd/evolve/serveronly.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/evolve/serveronly.py	Thu May 18 23:51:10 2017 +0200
@@ -17,6 +17,7 @@
     from . import (
         exthelper,
         metadata,
+        obscache,
         obsexchange,
     )
 except ValueError as exc:
@@ -27,6 +28,7 @@
     from evolve import (
         exthelper,
         metadata,
+        obscache,
         obsexchange,
     )
 
@@ -36,6 +38,7 @@
 buglink = metadata.buglink
 
 eh = exthelper.exthelper()
+eh.merge(obscache.eh)
 eh.merge(obsexchange.eh)
 uisetup = eh.final_uisetup
 extsetup = eh.final_extsetup
--- a/hgext3rd/evolve/stablerange.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/evolve/stablerange.py	Thu May 18 23:51:10 2017 +0200
@@ -10,7 +10,9 @@
 import collections
 import heapq
 import math
+import os
 import sqlite3
+import time
 import weakref
 
 from mercurial import (
@@ -19,6 +21,7 @@
     error,
     localrepo,
     node as nodemod,
+    pycompat,
     scmutil,
     util,
 )
@@ -31,6 +34,16 @@
 
 eh = exthelper.exthelper()
 
+# prior to hg-4.2 there are not util.timer
+if util.safehasattr(util, 'timer'):
+    timer = util.timer
+elif util.safehasattr(time, "perf_counter"):
+    timer = time.perf_counter
+elif getattr(pycompat, 'osname', os.name) == 'nt':
+    timer = time.clock
+else:
+    timer = time.time
+
 ##################################
 ### Stable topological sorting ###
 ##################################
@@ -263,6 +276,7 @@
         #
         # we use the revnumber as an approximation for depth
         ui = repo.ui
+        starttime = timer()
 
         if upto is None:
             upto = len(cl) - 1
@@ -308,6 +322,10 @@
         self._tiprev = upto
         self._tipnode = cl.node(upto)
 
+        duration = timer() - starttime
+        repo.ui.log('evoext-cache', 'updated stablerange cache in %.4f seconds\n',
+                    duration)
+
     def depthrev(self, repo, rev):
         repo = repo.unfiltered()
         cl = repo.changelog
@@ -714,6 +732,7 @@
 
     def __init__(self, repo):
         super(sqlstablerange, self).__init__()
+        self._vfs = repo.vfs
         self._path = repo.vfs.join('cache/evoext_stablerange_v0.sqlite')
         self._cl = repo.unfiltered().changelog # (okay to keep an old one)
         self._ondisktiprev = None
@@ -777,10 +796,20 @@
         self._loaddepth()
         return super(sqlstablerange, self)._inheritancepoint(*args, **kwargs)
 
+    def _db(self):
+        try:
+            util.makedirs(self._vfs.dirname(self._path))
+        except OSError:
+            return None
+        con = sqlite3.connect(self._path)
+        con.text_factory = str
+        return con
+
     @util.propertycache
     def _con(self):
-        con = sqlite3.connect(self._path)
-        con.text_factory = str
+        con = self._db()
+        if con is None:
+            return None
         cur = con.execute(_queryexist)
         if cur.fetchone() is None:
             return None
@@ -810,8 +839,9 @@
             if '_con' in vars(self):
                 del self._con
 
-            con = sqlite3.connect(self._path)
-            con.text_factory = str
+            con = self._db()
+            if con is None:
+                return
             with con:
                 for req in _sqliteschema:
                     con.execute(req)
@@ -902,7 +932,7 @@
                     # new nodes !
                     repo.stablerange.warmup(repo)
 
-            tr.addpostclose('warmcache-stablerange', _warmcache)
+            tr.addpostclose('warmcache-10-stablerange', _warmcache)
             return tr
 
     repo.__class__ = stablerangerepo
--- a/hgext3rd/topic/__init__.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/topic/__init__.py	Thu May 18 23:51:10 2017 +0200
@@ -100,9 +100,13 @@
               'topic.stack.summary.behindcount': 'cyan',
               'topic.stack.summary.behinderror': 'red',
               'topic.stack.summary.headcount.multiple': 'yellow',
+              # default color to help log output and thg
+              # (first pick I could think off, update as needed
+              'log.topic': 'green_background',
+              'topic.active': 'green',
              }
 
-testedwith = '3.9'
+testedwith = '4.0.2 4.1.3 4.2'
 
 def _contexttopic(self):
     return self.extra().get(constants.extrakey, '')
@@ -157,6 +161,10 @@
     if not isinstance(repo, localrepo.localrepository):
         return # this can be a peer in the ssh case (puzzling)
 
+    if repo.ui.config('experimental', 'thg.displaynames', None) is None:
+        repo.ui.setconfig('experimental', 'thg.displaynames', 'topics',
+                          source='topic-extension')
+
     class topicrepo(repo.__class__):
 
         def _restrictcapabilities(self, caps):
--- a/hgext3rd/topic/stack.py	Wed May 03 13:23:36 2017 +0200
+++ b/hgext3rd/topic/stack.py	Thu May 18 23:51:10 2017 +0200
@@ -15,6 +15,12 @@
     trevs = repo.revs("topic(%s) - obsolete()", topic)
     return _orderrevs(repo, trevs)
 
+def labelsgen(prefix, labelssuffix):
+    """ Takes a label prefix and a list of suffixes. Returns a string of the prefix
+    formatted with each suffix separated with a space.
+    """
+    return ' '.join(prefix % suffix for suffix in labelssuffix)
+
 def showstack(ui, repo, topic, opts):
     fm = ui.formatter('topicstack', opts)
     prev = None
@@ -59,35 +65,44 @@
 
     # super crude initial version
     for idx, isentry, ctx in entries[::-1]:
+
+        states = []
+        iscurrentrevision = repo.revs('%d and parents()', ctx.rev())
+
+        if iscurrentrevision:
+            states.append('current')
+
         if not isentry:
             symbol = '^'
-            state = 'base'
-        elif repo.revs('%d and parents()', ctx.rev()):
+            # "base" is kind of a "ghost" entry
+            # skip other label for them (no current, no unstable)
+            states = ['base']
+        elif iscurrentrevision:
             symbol = '@'
-            state = 'current'
         elif repo.revs('%d and unstable()', ctx.rev()):
             symbol = '$'
-            state = 'unstable'
+            states.append('unstable')
         else:
             symbol = ':'
-            state = 'clean'
+            states.append('clean')
         fm.startitem()
         fm.data(isentry=isentry)
+
         if idx is None:
             fm.plain('  ')
         else:
             fm.write('topic.stack.index', 't%d', idx,
-                     label='topic.stack.index topic.stack.index.%s' % state)
+                     label='topic.stack.index ' + labelsgen('topic.stack.index.%s', states))
         fm.write('topic.stack.state.symbol', '%s', symbol,
-                 label='topic.stack.state topic.stack.state.%s' % state)
+                 label='topic.stack.state ' + labelsgen('topic.stack.state.%s', states))
         fm.plain(' ')
         fm.write('topic.stack.desc', '%s', ctx.description().splitlines()[0],
-                 label='topic.stack.desc topic.stack.desc.%s' % state)
-        fm.condwrite(state != 'clean' and idx is not None, 'topic.stack.state',
-                     ' (%s)', state,
-                     label='topic.stack.state topic.stack.state.%s' % state)
+                 label='topic.stack.desc ' + labelsgen('topic.stack.desc.%s', states))
+        fm.condwrite(states != ['clean'] and idx is not None, 'topic.stack.state',
+                     ' (%s)', fm.formatlist(states, 'topic.stack.state'),
+                     label='topic.stack.state ' + labelsgen('topic.stack.state.%s', states))
         fm.plain('\n')
-        fm.end()
+    fm.end()
 
 def stackdata(repo, topic):
     """get various data about a stack
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-commit.t	Thu May 18 23:51:10 2017 +0200
@@ -0,0 +1,22 @@
+#require test-repo
+
+Enable obsolescence to avoid the warning issue when obsmarker are found
+
+  $ cat << EOF >> $HGRCPATH
+  > [experimental]
+  > evolution=all
+  > EOF
+
+Go back in the hg repo
+
+  $ cd $TESTDIR/..
+
+  $ for node in `hg log --rev 'not public() and ::. and not desc("# no-check-commit")' --template '{node|short}\n'`; do
+  >    hg export $node | ${RUNTESTDIR}/../contrib/check-commit > ${TESTTMP}/check-commit.out
+  >    if [ $? -ne 0 ]; then
+  >        echo "Revision $node does not comply with rules"
+  >        echo '------------------------------------------------------'
+  >        cat ${TESTTMP}/check-commit.out
+  >        echo
+  >   fi
+  > done
--- a/tests/test-check-setup-manifest.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#require test-repo
-
-  $ checkcm() {
-  >   if ! (which check-manifest > /dev/null); then
-  >     echo skipped: missing tool: check-manifest;
-  >     exit 80;
-  >   fi;
-  > };
-  $ checkcm
-  $ cat << EOF >> $HGRCPATH
-  > [experimental]
-  > evolution=all
-  > EOF
-
-Run check manifest:
-
-  $ cd $TESTDIR/..
-  $ check-manifest
-  lists of files in version control and sdist match
--- a/tests/test-discovery-obshashrange.t	Wed May 03 13:23:36 2017 +0200
+++ b/tests/test-discovery-obshashrange.t	Thu May 18 23:51:10 2017 +0200
@@ -6,6 +6,9 @@
   $ cat << EOF >> $HGRCPATH
   > [extensions]
   > hgext3rd.evolve =
+  > blackbox =
+  > [defaults]
+  > blackbox = -l 100
   > [experimental]
   > obshashrange=1
   > verbose-obsolescence-exchange=1
@@ -17,7 +20,7 @@
   > EOF
 
   $ getid() {
-  >     hg log --hidden --template '{node}\n' --rev "$1"
+  >     hg log --hidden --template '{node}\n' --rev "$1" --config 'extensions.blackbox=!'
   > }
 
   $ hg init server
@@ -27,6 +30,31 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd server
   $ hg debugbuilddag '.+7'
+  $ hg blackbox
+  * @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugbuilddag .+7 (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
+  * @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 (*)> updated served branch cache in *.???? seconds (glob)
+  * @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 (*)> updated served branch cache in *.???? seconds (glob)
+  * @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 (*)> updated served branch cache in *.???? seconds (glob)
+  * @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 (*)> 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)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
   $ hg log -G
   o  7 4de32a90b66c r7 tip
   |
@@ -57,6 +85,39 @@
   dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
 
+  $ hg blackbox
+  * @0000000000000000000000000000000000000000 (*)> log -G (glob)
+  * @0000000000000000000000000000000000000000 (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @0000000000000000000000000000000000000000 (*)> log -G exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 66f7d451a68b85ed82ff5fcc254daf50c74144bd (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (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)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
   $ hg debugobshashrange --subranges --rev tip
            rev         node        index         size        depth      obshash
              7 4de32a90b66c            0            8            8 38d1e7ad86ea
@@ -88,10 +149,30 @@
   added 5 changesets with 0 changes to 0 files
   3 new obsolescence markers
   (run 'hg update' to get a working copy)
+  $ hg -R ../server blackbox
+  * @0000000000000000000000000000000000000000 (*)> debugobshashrange --subranges --rev tip (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated stablerange cache in *.???? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobshashrange --subranges --rev tip exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm ../server/.hg/blackbox.log
   $ hg -R ../server/ debugobsolete --rev ::4 | sort
   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 66f7d451a68b85ed82ff5fcc254daf50c74144bd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  $ rm ../server/.hg/blackbox.log
+  $ hg blackbox
+  * @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 (*)> 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)
+  * @0000000000000000000000000000000000000000 (*)> 5 incoming changes - new heads: bebd167eb94d (glob)
+  * @0000000000000000000000000000000000000000 (*)> pull --rev 4 exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
   $ hg debugobsolete | sort
   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 66f7d451a68b85ed82ff5fcc254daf50c74144bd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -106,17 +187,75 @@
   $ hg add foo
   $ hg commit -m foo
   $ hg debugobsolete ffffffffffffffffffffffffffffffffffffffff `getid '.'`
-  $ hg push -f
+  $ hg push -f --debug
   pushing to ssh://user@dummy/server
+  running python "*/dummyssh" user@dummy 'hg -R server serve --stdio' (glob)
+  sending hello command
+  sending between command
+  remote: 532
+  remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v0 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch branchmap bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps changegroupsubset getbundle httpheader=1024 known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
+  remote: 1
+  preparing listkeys for "phases"
+  sending listkeys command
+  received listkey for "phases": 58 bytes
+  query 1; heads
+  sending batch command
   searching for changes
+  taking quick initial sample
+  query 2; still undecided: 5, sample size is: 5
+  sending known command
+  2 total queries
+  preparing listkeys for "phases"
+  sending listkeys command
+  received listkey for "phases": 58 bytes
+  preparing listkeys for "namespaces"
+  sending listkeys command
+  received listkey for "namespaces": 40 bytes
   OBSEXC: computing relevant nodes
   OBSEXC: looking for common markers in 6 nodes
+  query 0; add more sample (target 100, current 1)
+  query 0; sample size is 9, largest range 5
+  sending evoext_obshashrange_v0 command
+  obsdiscovery, 0/5 mismatch - 1 obshashrange queries in *.???? seconds (glob)
   OBSEXC: computing markers relevant to 1 nodes
+  checking for updated bookmarks
+  preparing listkeys for "bookmarks"
+  sending listkeys command
+  received listkey for "bookmarks": 0 bytes
+  1 changesets found
+  list of changesets:
+  45f8b879de922f6a6e620ba04205730335b6fc7e
+  sending unbundle command
+  bundle2-output-bundle: "HG20", 4 parts total
+  bundle2-output-part: "replycaps" 172 bytes payload
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
+  bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
+  bundle2-output-part: "obsmarkers" streamed payload
   remote: adding changesets
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files (+1 heads)
   remote: 1 new obsolescence markers
+  bundle2-input-bundle: with-transaction
+  bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
+  bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
+  bundle2-input-part: "reply:obsmarkers" (params: 0 advisory) supported
+  bundle2-input-bundle: 2 parts total
+  preparing listkeys for "phases"
+  sending listkeys command
+  received listkey for "phases": 58 bytes
+  $ hg -R ../server blackbox
+  * @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
+  * @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 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)
+  * @0000000000000000000000000000000000000000 (*)> 1 incoming changes - new heads: 45f8b879de92 (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm ../server/.hg/blackbox.log
 
 testing push with extra local markers
 =====================================
@@ -145,6 +284,14 @@
   no changes found
   remote: 2 new obsolescence markers
   [1]
+  $ hg -R ../server blackbox
+  * @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 2o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 2o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm ../server/.hg/blackbox.log
   $ hg -R ../server/ debugobsolete --rev ::tip | sort
   111111111111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   22222222222222222bbbbbbbbbbbbb2222222222 2dc09a01254db841290af0538aa52f6f52c776e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -152,6 +299,46 @@
   bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   ffffffffffffffffffffffffffffffffffffffff 45f8b879de922f6a6e620ba04205730335b6fc7e 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  $ hg blackbox
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> up (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> up exited 0 after *.?? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> add foo (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> add foo exited 0 after *.?? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> commit -m foo (glob)
+  * @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-obshashrange in *.???? seconds (1r, 0o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> commit -m foo exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete ffffffffffffffffffffffffffffffffffffffff 45f8b879de922f6a6e620ba04205730335b6fc7e (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (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 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)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete 111111111111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete 111111111111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete 22222222222222222bbbbbbbbbbbbb2222222222 2dc09a01254db841290af0538aa52f6f52c776e3 (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete 22222222222222222bbbbbbbbbbbbb2222222222 2dc09a01254db841290af0538aa52f6f52c776e3 exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> push (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 2/6 mismatch - 1 obshashrange queries in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> push exited True after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
   $ hg debugobsolete | sort
   111111111111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   22222222222222222bbbbbbbbbbbbb2222222222 2dc09a01254db841290af0538aa52f6f52c776e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -190,6 +377,24 @@
   3 new obsolescence markers
   (run 'hg heads' to see heads, 'hg merge' to merge)
 
+  $ hg -R ../server blackbox
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete --rev ::tip (glob)
+  * @0000000000000000000000000000000000000000 (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R ../server/ debugobsolete --rev ::tip exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete aaaaaaa11111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R ../server debugobsolete aaaaaaa11111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> debugobsolete bbbbbbb2222222222bbbbbbbbbbbbb2222222222 bebd167eb94d257ace0e814aeb98e6972ed2970d (glob)
+  * @0000000000000000000000000000000000000000 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R ../server debugobsolete bbbbbbb2222222222bbbbbbbbbbbbb2222222222 bebd167eb94d257ace0e814aeb98e6972ed2970d exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
+  * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
+  $ rm ../server/.hg/blackbox.log
   $ hg -R ../server/ debugobsolete --rev '::6' | sort
   111111111111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   22222222222222222bbbbbbbbbbbbb2222222222 2dc09a01254db841290af0538aa52f6f52c776e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -199,6 +404,24 @@
   bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  $ hg blackbox
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r 6 (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 2/6 mismatch - 1 obshashrange queries in *.???? seconds (glob)
+  * @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 stablerange cache in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (2r, 3o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 3o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 2 incoming changes - new heads: f69452c5b1af (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r 6 exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
   $ hg debugobsolete --rev '::6' | sort
   111111111111111aaaaaaaaa1111111111111111 66f7d451a68b85ed82ff5fcc254daf50c74144bd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   22222222222222222bbbbbbbbbbbbb2222222222 2dc09a01254db841290af0538aa52f6f52c776e3 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -209,3 +432,336 @@
   cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
 
+Test cache behavior
+===================
+
+Adding markers affecting already used range:
+--------------------------------------------
+
+  $ hg debugobshashrange --subranges --rev 'heads(all())'
+           rev         node        index         size        depth      obshash
+             7 f69452c5b1af            0            7            7 000000000000
+             5 45f8b879de92            0            6            6 1643971dbe2d
+             3 2dc09a01254d            0            4            4 6be48f31976a
+             7 f69452c5b1af            4            3            7 000000000000
+             3 2dc09a01254d            2            2            4 9522069ae085
+             5 45f8b879de92            4            2            6 9c26c72819c0
+             1 66f7d451a68b            0            2            2 853c77a32154
+             6 c8d03c1b5e94            4            2            6 ec8a3e92c525
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 8a2acf8e1cde
+             5 45f8b879de92            5            1            6 1a0c08180b65
+             1 66f7d451a68b            1            1            2 853c77a32154
+             4 bebd167eb94d            4            1            5 20a2cc572e4b
+             6 c8d03c1b5e94            5            1            6 446c2dc3bce5
+             7 f69452c5b1af            6            1            7 000000000000
+  $ hg -R ../server debugobsolete aaaa333333333aaaaa333a3a3a3a3a3a3a3a3a3a `getid 'desc(r1)'`
+  $ hg -R ../server debugobsolete bb4b4b4b4b4b4b4b44b4b4b4b4b4b4b4b4b4b4b4 `getid 'desc(r3)'`
+  $ hg pull -r `getid 'desc(r6)'`
+  pulling from ssh://user@dummy/server
+  no changes found
+  OBSEXC: looking for common markers in 7 nodes
+  OBSEXC: request obsmarkers for 2 common nodes
+  2 new obsolescence markers
+  $ hg debugobshashrange --subranges --rev 'desc("r3")' -R ../server
+           rev         node        index         size        depth      obshash
+             3 2dc09a01254d            0            4            4 8932bf980bb4
+             3 2dc09a01254d            2            2            4 ce1937ca1278
+             1 66f7d451a68b            0            2            2 327c7dd73d29
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 26f996446ecb
+             1 66f7d451a68b            1            1            2 327c7dd73d29
+  $ hg debugobshashrange --subranges --rev 'desc("r3")'
+           rev         node        index         size        depth      obshash
+             3 2dc09a01254d            0            4            4 8932bf980bb4
+             3 2dc09a01254d            2            2            4 ce1937ca1278
+             1 66f7d451a68b            0            2            2 327c7dd73d29
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 26f996446ecb
+             1 66f7d451a68b            1            1            2 327c7dd73d29
+  $ hg blackbox
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev ::6 (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev ::6 exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r f69452c5b1af6cbaaa56ef50cf94fff5bcc6ca23 (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 2/7 mismatch - 1 obshashrange queries in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 2o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 2o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r f69452c5b1af6cbaaa56ef50cf94fff5bcc6ca23 exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev desc("r3") (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev desc("r3") exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
+
+Adding prune markers on existing changeset
+------------------------------------------
+
+  $ hg -R ../server debugobsolete --record-parents `getid 'desc(foo)'`
+  $ hg pull -r `getid 'desc(r4)'`
+  pulling from ssh://user@dummy/server
+  no changes found
+  OBSEXC: looking for common markers in 5 nodes
+  OBSEXC: request obsmarkers for 1 common nodes
+  1 new obsolescence markers
+  $ hg blackbox
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r bebd167eb94d257ace0e814aeb98e6972ed2970d (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 1/5 mismatch - 1 obshashrange queries in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r bebd167eb94d257ace0e814aeb98e6972ed2970d exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
+  $ hg debugobshashrange --subranges --rev 'heads(all())'
+           rev         node        index         size        depth      obshash
+             7 f69452c5b1af            0            7            7 000000000000
+             5 45f8b879de92            0            6            6 b8a4206b0fc6
+             3 2dc09a01254d            0            4            4 8932bf980bb4
+             7 f69452c5b1af            4            3            7 000000000000
+             3 2dc09a01254d            2            2            4 ce1937ca1278
+             5 45f8b879de92            4            2            6 31fc49d36a59
+             1 66f7d451a68b            0            2            2 327c7dd73d29
+             6 c8d03c1b5e94            4            2            6 89755fd39e6d
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 26f996446ecb
+             5 45f8b879de92            5            1            6 1a0c08180b65
+             1 66f7d451a68b            1            1            2 327c7dd73d29
+             4 bebd167eb94d            4            1            5 b21465ecb790
+             6 c8d03c1b5e94            5            1            6 446c2dc3bce5
+             7 f69452c5b1af            6            1            7 000000000000
+
+Recover after rollback
+
+  $ hg pull
+  pulling from ssh://user@dummy/server
+  searching for changes
+  OBSEXC: looking for common markers in 8 nodes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files
+  1 new obsolescence markers
+  (run 'hg update' to get a working copy)
+  $ hg rollback
+  repository tip rolled back to revision 7 (undo pull)
+  $ hg blackbox
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 0/8 mismatch - 1 obshashrange queries in *.???? seconds (glob)
+  * @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 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)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 1 incoming changes - new heads: 4de32a90b66c (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> rollback (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated base branch cache in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote base branch cache with 1 labels and 2 nodes (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> rollback exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
+  $ hg debugobshashrange --subranges --rev 'heads(all())'
+           rev         node        index         size        depth      obshash
+             7 f69452c5b1af            0            7            7 000000000000
+             5 45f8b879de92            0            6            6 b8a4206b0fc6
+             3 2dc09a01254d            0            4            4 8932bf980bb4
+             7 f69452c5b1af            4            3            7 000000000000
+             3 2dc09a01254d            2            2            4 ce1937ca1278
+             5 45f8b879de92            4            2            6 31fc49d36a59
+             1 66f7d451a68b            0            2            2 327c7dd73d29
+             6 c8d03c1b5e94            4            2            6 89755fd39e6d
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 26f996446ecb
+             5 45f8b879de92            5            1            6 1a0c08180b65
+             1 66f7d451a68b            1            1            2 327c7dd73d29
+             4 bebd167eb94d            4            1            5 b21465ecb790
+             6 c8d03c1b5e94            5            1            6 446c2dc3bce5
+             7 f69452c5b1af            6            1            7 000000000000
+  $ hg pull
+  pulling from ssh://user@dummy/server
+  searching for changes
+  OBSEXC: looking for common markers in 8 nodes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files
+  1 new obsolescence markers
+  (run 'hg update' to get a working copy)
+  $ hg blackbox
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) (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)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull (glob)
+  * @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 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)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 1 incoming changes - new heads: 4de32a90b66c (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
+  $ hg debugobshashrange --subranges --rev 'heads(all())'
+           rev         node        index         size        depth      obshash
+             8 4de32a90b66c            0            8            8 c7f1f7e9925b
+             5 45f8b879de92            0            6            6 b8a4206b0fc6
+             3 2dc09a01254d            0            4            4 8932bf980bb4
+             8 4de32a90b66c            4            4            8 c681c3e58c27
+             3 2dc09a01254d            2            2            4 ce1937ca1278
+             5 45f8b879de92            4            2            6 31fc49d36a59
+             8 4de32a90b66c            6            2            8 033544c939f0
+             1 66f7d451a68b            0            2            2 327c7dd73d29
+             6 c8d03c1b5e94            4            2            6 89755fd39e6d
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 26f996446ecb
+             5 45f8b879de92            5            1            6 1a0c08180b65
+             8 4de32a90b66c            7            1            8 033544c939f0
+             1 66f7d451a68b            1            1            2 327c7dd73d29
+             4 bebd167eb94d            4            1            5 b21465ecb790
+             6 c8d03c1b5e94            5            1            6 446c2dc3bce5
+             7 f69452c5b1af            6            1            7 000000000000
+
+Recover after stripping (in the middle of the repo)
+
+We strip a branch that is not the tip of the reporiosy so part of the affected
+revision are reapplied after the target is stripped.
+
+  $ hg log -G
+  o  8 4de32a90b66c r7 tip
+  |
+  o  7 f69452c5b1af r6
+  |
+  o  6 c8d03c1b5e94 r5
+  |
+  | @  5 45f8b879de92 foo
+  |/
+  o  4 bebd167eb94d r4
+  |
+  o  3 2dc09a01254d r3
+  |
+  o  2 01241442b3c2 r2
+  |
+  o  1 66f7d451a68b r1
+  |
+  o  0 1ea73414a91b r0
+  
+  $ hg --config extensions.strip= strip -r 'desc("foo")'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/45f8b879de92-94c82517-backup.hg (glob)
+  $ hg log -G
+  o  7 4de32a90b66c r7 tip
+  |
+  o  6 f69452c5b1af r6
+  |
+  o  5 c8d03c1b5e94 r5
+  |
+  @  4 bebd167eb94d r4
+  |
+  o  3 2dc09a01254d r3
+  |
+  o  2 01241442b3c2 r2
+  |
+  o  1 66f7d451a68b r1
+  |
+  o  0 1ea73414a91b r0
+  
+  $ hg pull
+  pulling from ssh://user@dummy/server
+  searching for changes
+  OBSEXC: looking for common markers in 8 nodes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg log -G
+  o  8 45f8b879de92 foo tip
+  |
+  | o  7 4de32a90b66c r7
+  | |
+  | o  6 f69452c5b1af r6
+  | |
+  | o  5 c8d03c1b5e94 r5
+  |/
+  @  4 bebd167eb94d r4
+  |
+  o  3 2dc09a01254d r3
+  |
+  o  2 01241442b3c2 r2
+  |
+  o  1 66f7d451a68b r1
+  |
+  o  0 1ea73414a91b r0
+  
+  $ hg blackbox
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev heads(all()) exited 0 after *.?? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G exited 0 after *.?? seconds (glob)
+  * @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 (*)> 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 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)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 1 nodes (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> 3 incoming changes - new heads: 4de32a90b66c (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> --config extensions.strip= strip -r desc("foo") exited 0 after *.?? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G exited 0 after *.?? seconds (glob)
+  * @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 stablerange cache in *.???? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 0o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated served branch cache in *.???? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> 1 incoming changes - new heads: 45f8b879de92 (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> pull exited 0 after *.?? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G exited 0 after *.?? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> blackbox (glob)
+  $ rm .hg/blackbox.log
+  $ hg debugobshashrange --subranges --rev 'heads(all())'
+           rev         node        index         size        depth      obshash
+             7 4de32a90b66c            0            8            8 c7f1f7e9925b
+             8 45f8b879de92            0            6            6 b8a4206b0fc6
+             3 2dc09a01254d            0            4            4 8932bf980bb4
+             7 4de32a90b66c            4            4            8 c681c3e58c27
+             3 2dc09a01254d            2            2            4 ce1937ca1278
+             8 45f8b879de92            4            2            6 31fc49d36a59
+             7 4de32a90b66c            6            2            8 033544c939f0
+             1 66f7d451a68b            0            2            2 327c7dd73d29
+             5 c8d03c1b5e94            4            2            6 89755fd39e6d
+             2 01241442b3c2            2            1            3 1ed3c61fb39a
+             0 1ea73414a91b            0            1            1 000000000000
+             3 2dc09a01254d            3            1            4 26f996446ecb
+             8 45f8b879de92            5            1            6 1a0c08180b65
+             7 4de32a90b66c            7            1            8 033544c939f0
+             1 66f7d451a68b            1            1            2 327c7dd73d29
+             4 bebd167eb94d            4            1            5 b21465ecb790
+             5 c8d03c1b5e94            5            1            6 446c2dc3bce5
+             6 f69452c5b1af            6            1            7 000000000000
+
--- a/tests/test-evolve-obshistory.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,514 +0,0 @@
-This test file test the various messages when accessing obsolete
-revisions.
-
-Global setup
-============
-
-  $ . $TESTDIR/testlib/common.sh
-  $ cat >> $HGRCPATH <<EOF
-  > [ui]
-  > interactive = true
-  > [phases]
-  > publish=False
-  > [extensions]
-  > evolve =
-  > EOF
-
-Test output on amended commit
-=============================
-
-Test setup
-----------
-
-  $ hg init $TESTTMP/local-amend
-  $ cd $TESTTMP/local-amend
-  $ mkcommit ROOT
-  $ mkcommit A0
-  $ echo 42 >> A0
-  $ hg amend -m "A1"
-  $ hg log --hidden -G
-  @  changeset:   3:a468dc9b3633
-  |  tag:         tip
-  |  parent:      0:ea207398892e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A1
-  |
-  | x  changeset:   2:f137d23bb3e1
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     temporary amend commit for 471f378eab4c
-  | |
-  | x  changeset:   1:471f378eab4c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-Actual test
------------
-
-  $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg update --hidden "desc(A0)"
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (471f378eab4c)
-  (use 'hg evolve' to update to its successor: a468dc9b3633)
-
-Test output with pruned commit
-==============================
-
-Test setup
-----------
-
-  $ hg init $TESTTMP/local-prune
-  $ cd $TESTTMP/local-prune
-  $ mkcommit ROOT
-  $ mkcommit A0 # 0
-  $ mkcommit B0 # 1
-  $ hg log --hidden -G
-  @  changeset:   2:0dec01379d3b
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     B0
-  |
-  o  changeset:   1:471f378eab4c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-  $ hg prune -r 'desc(B0)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  working directory now at 471f378eab4c
-  1 changesets pruned
-
-Actual test
------------
-
-  $ hg up 1
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg up 0dec01379d3b
-  abort: hidden revision '0dec01379d3b'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg up --hidden -r 'desc(B0)'
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (0dec01379d3b)
-  (use 'hg evolve' to update to its parent successor)
-
-Test output with splitted commit
-================================
-
-Test setup
-----------
-
-  $ hg init $TESTTMP/local-split
-  $ cd $TESTTMP/local-split
-  $ mkcommit ROOT
-  $ echo 42 >> a
-  $ echo 43 >> b
-  $ hg commit -A -m "A0"
-  adding a
-  adding b
-  $ hg log --hidden -G
-  @  changeset:   1:471597cad322
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-  $ hg split -r 'desc(A0)' -d "0 0" << EOF
-  > y
-  > y
-  > n
-  > n
-  > y
-  > y
-  > EOF
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  adding a
-  adding b
-  diff --git a/a b/a
-  new file mode 100644
-  examine changes to 'a'? [Ynesfdaq?] y
-  
-  @@ -0,0 +1,1 @@
-  +42
-  record change 1/2 to 'a'? [Ynesfdaq?] y
-  
-  diff --git a/b b/b
-  new file mode 100644
-  examine changes to 'b'? [Ynesfdaq?] n
-  
-  created new head
-  Done splitting? [yN] n
-  diff --git a/b b/b
-  new file mode 100644
-  examine changes to 'b'? [Ynesfdaq?] y
-  
-  @@ -0,0 +1,1 @@
-  +43
-  record this change to 'b'? [Ynesfdaq?] y
-  
-  no more change to split
-
-  $ hg log --hidden -G
-  @  changeset:   3:f257fde29c7a
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   2:337fec4d2edc
-  |  parent:      0:ea207398892e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  | x  changeset:   1:471597cad322
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-Actual test
------------
-
-  $ hg update 471597cad322
-  abort: hidden revision '471597cad322'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg update --hidden 'min(desc(A0))'
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (471597cad322)
-  (use 'hg evolve' to update to its tipmost successor: 337fec4d2edc, f257fde29c7a)
-
-Test output with lots of splitted commit
-========================================
-
-Test setup
-----------
-
-  $ hg init $TESTTMP/local-lots-split
-  $ cd $TESTTMP/local-lots-split
-  $ mkcommit ROOT
-  $ echo 42 >> a
-  $ echo 43 >> b
-  $ echo 44 >> c
-  $ echo 45 >> d
-  $ hg commit -A -m "A0"
-  adding a
-  adding b
-  adding c
-  adding d
-  $ hg log --hidden -G
-  @  changeset:   1:de7290d8b885
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-
-  $ hg split -r 'desc(A0)' -d "0 0" << EOF
-  > y
-  > y
-  > n
-  > n
-  > n
-  > n
-  > y
-  > y
-  > n
-  > n
-  > n
-  > y
-  > y
-  > n
-  > n
-  > y
-  > y
-  > EOF
-  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  adding a
-  adding b
-  adding c
-  adding d
-  diff --git a/a b/a
-  new file mode 100644
-  examine changes to 'a'? [Ynesfdaq?] y
-  
-  @@ -0,0 +1,1 @@
-  +42
-  record change 1/4 to 'a'? [Ynesfdaq?] y
-  
-  diff --git a/b b/b
-  new file mode 100644
-  examine changes to 'b'? [Ynesfdaq?] n
-  
-  diff --git a/c b/c
-  new file mode 100644
-  examine changes to 'c'? [Ynesfdaq?] n
-  
-  diff --git a/d b/d
-  new file mode 100644
-  examine changes to 'd'? [Ynesfdaq?] n
-  
-  created new head
-  Done splitting? [yN] n
-  diff --git a/b b/b
-  new file mode 100644
-  examine changes to 'b'? [Ynesfdaq?] y
-  
-  @@ -0,0 +1,1 @@
-  +43
-  record change 1/3 to 'b'? [Ynesfdaq?] y
-  
-  diff --git a/c b/c
-  new file mode 100644
-  examine changes to 'c'? [Ynesfdaq?] n
-  
-  diff --git a/d b/d
-  new file mode 100644
-  examine changes to 'd'? [Ynesfdaq?] n
-  
-  Done splitting? [yN] n
-  diff --git a/c b/c
-  new file mode 100644
-  examine changes to 'c'? [Ynesfdaq?] y
-  
-  @@ -0,0 +1,1 @@
-  +44
-  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] n
-  diff --git a/d b/d
-  new file mode 100644
-  examine changes to 'd'? [Ynesfdaq?] y
-  
-  @@ -0,0 +1,1 @@
-  +45
-  record this change to 'd'? [Ynesfdaq?] y
-  
-  no more change to split
-
-  $ hg log --hidden -G
-  @  changeset:   5:c7f044602e9b
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   4:1ae8bc733a14
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   3:f257fde29c7a
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   2:337fec4d2edc
-  |  parent:      0:ea207398892e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  | x  changeset:   1:de7290d8b885
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-Actual test
------------
-
-  $ hg update de7290d8b885
-  abort: hidden revision 'de7290d8b885'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg update --hidden 'min(desc(A0))'
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (de7290d8b885)
-  (use 'hg evolve' to update to its tipmost successor: 337fec4d2edc, f257fde29c7a and 2 more)
-
-Test output with folded commit
-==============================
-
-Test setup
-----------
-
-  $ hg init $TESTTMP/local-fold
-  $ cd $TESTTMP/local-fold
-  $ mkcommit ROOT
-  $ mkcommit A0
-  $ mkcommit B0
-  $ hg log --hidden -G
-  @  changeset:   2:0dec01379d3b
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     B0
-  |
-  o  changeset:   1:471f378eab4c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-  $ hg fold --exact -r 'desc(A0) + desc(B0)' --date "0 0" -m "C0"
-  2 changesets folded
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log --hidden -G
-  @  changeset:   3:eb5a0daa2192
-  |  tag:         tip
-  |  parent:      0:ea207398892e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     C0
-  |
-  | x  changeset:   2:0dec01379d3b
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     B0
-  | |
-  | x  changeset:   1:471f378eab4c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
- Actual test
- -----------
-
-  $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg update --hidden 'desc(A0)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  working directory parent is obsolete! (471f378eab4c)
-  (use 'hg evolve' to update to its successor: eb5a0daa2192)
-  $ hg update 0dec01379d3b
-  working directory parent is obsolete! (471f378eab4c)
-  (use 'hg evolve' to update to its successor: eb5a0daa2192)
-  abort: hidden revision '0dec01379d3b'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg update --hidden 'desc(B0)'
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (0dec01379d3b)
-  (use 'hg evolve' to update to its successor: eb5a0daa2192)
-
-Test output with divergence
-===========================
-
-Test setup
-----------
-
-  $ hg init $TESTTMP/local-divergence
-  $ cd $TESTTMP/local-divergence
-  $ mkcommit ROOT
-  $ mkcommit A0
-  $ hg amend -m "A1"
-  $ hg log --hidden -G
-  @  changeset:   2:fdf9bde5129a
-  |  tag:         tip
-  |  parent:      0:ea207398892e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A1
-  |
-  | x  changeset:   1:471f378eab4c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-  $ hg update --hidden 'desc(A0)'
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (471f378eab4c)
-  (use 'hg evolve' to update to its successor: fdf9bde5129a)
-  $ hg amend -m "A2"
-  2 new divergent changesets
-  $ hg log --hidden -G
-  @  changeset:   3:65b757b745b9
-  |  tag:         tip
-  |  parent:      0:ea207398892e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     A2
-  |
-  | o  changeset:   2:fdf9bde5129a
-  |/   parent:      0:ea207398892e
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A1
-  |
-  | x  changeset:   1:471f378eab4c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     A0
-  |
-  o  changeset:   0:ea207398892e
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     ROOT
-  
-Actual test
------------
-
-  $ hg update 471f378eab4c
-  abort: hidden revision '471f378eab4c'!
-  (use --hidden to access hidden revisions)
-  [255]
-  $ hg update --hidden 'desc(A0)'
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (471f378eab4c)
-  (471f378eab4c has diverged, use 'hg evolve -list --divergent' to resolve the issue)
--- a/tests/test-evolve-topic.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-
-Check we can find the topic extensions
-
-  $ cat >> $HGRCPATH <<EOF
-  > [defaults]
-  > amend=-d "0 0"
-  > fold=-d "0 0"
-  > [phases]
-  > publish = False
-  > [ui]
-  > logtemplate = {rev} - \{{get(namespaces, "topics")}} {node|short} {desc} ({phase})\n
-  > [diff]
-  > git = 1
-  > unified = 0
-  > [extensions]
-  > rebase = 
-  > EOF
-  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
-  $ echo "topic=$(echo $(dirname $TESTDIR))/hgext3rd/topic/" >> $HGRCPATH
-
-  $ mkcommit() {
-  >    echo "$1" > "$1"
-  >    hg add "$1"
-  >    hg ci -m "add $1"
-  > }
-
-Create a simple setup
-
-  $ hg init repoa
-  $ cd repoa
-  $ mkcommit aaa
-  $ mkcommit bbb
-  $ hg topic foo
-  $ mkcommit ccc
-  $ mkcommit ddd
-  $ mkcommit eee
-  $ mkcommit fff
-  $ hg topic bar
-  $ mkcommit ggg
-  $ mkcommit hhh
-  $ mkcommit iii
-  $ mkcommit jjj
-
-  $ hg log -G
-  @  9 - {bar} 1d964213b023 add jjj (draft)
-  |
-  o  8 - {bar} fcab990f3261 add iii (draft)
-  |
-  o  7 - {bar} b0c2554835ac add hhh (draft)
-  |
-  o  6 - {bar} c748293f1c1a add ggg (draft)
-  |
-  o  5 - {foo} 6a6b7365c751 add fff (draft)
-  |
-  o  4 - {foo} 3969ab847d9c add eee (draft)
-  |
-  o  3 - {foo} 4e3a154f38c7 add ddd (draft)
-  |
-  o  2 - {foo} cced9bac76e3 add ccc (draft)
-  |
-  o  1 - {} a4dbed0837ea add bbb (draft)
-  |
-  o  0 - {} 199cc73e9a0b add aaa (draft)
-  
-
-Test that evolve --all evolve the current topic
------------------------------------------------
-
-make a mess
-
-  $ hg up foo
-  switching to topic foo
-  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  $ hg topic -l 
-  ### topic: foo (?)
-  ### branch: default (?)
-  t4@ add fff (current)
-  t3: add eee
-  t2: add ddd
-  t1: add ccc
-    ^ add bbb
-  $ hg up 'desc(ddd)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ echo ddd >> ddd
-  $ hg amend
-  6 new unstable changesets
-  $ hg up 'desc(fff)'
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo fff >> fff
-  $ hg amend
-
-  $ hg log -G
-  @  13 - {foo} e104f49bab28 add fff (draft)
-  |
-  | o  11 - {foo} d9cacd156ffc add ddd (draft)
-  | |
-  | | o  9 - {bar} 1d964213b023 add jjj (draft)
-  | | |
-  | | o  8 - {bar} fcab990f3261 add iii (draft)
-  | | |
-  | | o  7 - {bar} b0c2554835ac add hhh (draft)
-  | | |
-  | | o  6 - {bar} c748293f1c1a add ggg (draft)
-  | | |
-  +---x  5 - {foo} 6a6b7365c751 add fff (draft)
-  | |
-  o |  4 - {foo} 3969ab847d9c add eee (draft)
-  | |
-  x |  3 - {foo} 4e3a154f38c7 add ddd (draft)
-  |/
-  o  2 - {foo} cced9bac76e3 add ccc (draft)
-  |
-  o  1 - {} a4dbed0837ea add bbb (draft)
-  |
-  o  0 - {} 199cc73e9a0b add aaa (draft)
-  
-
-Run evolve --all
-
-  $ hg evolve --all
-  move:[4] add eee
-  atop:[11] add ddd
-  move:[13] add fff
-  atop:[14] add eee
-  working directory is now at 070c5573d8f9
-  $ hg log -G
-  @  15 - {foo} 070c5573d8f9 add fff (draft)
-  |
-  o  14 - {foo} 42b49017ff90 add eee (draft)
-  |
-  o  11 - {foo} d9cacd156ffc add ddd (draft)
-  |
-  | o  9 - {bar} 1d964213b023 add jjj (draft)
-  | |
-  | o  8 - {bar} fcab990f3261 add iii (draft)
-  | |
-  | o  7 - {bar} b0c2554835ac add hhh (draft)
-  | |
-  | o  6 - {bar} c748293f1c1a add ggg (draft)
-  | |
-  | x  5 - {foo} 6a6b7365c751 add fff (draft)
-  | |
-  | x  4 - {foo} 3969ab847d9c add eee (draft)
-  | |
-  | x  3 - {foo} 4e3a154f38c7 add ddd (draft)
-  |/
-  o  2 - {foo} cced9bac76e3 add ccc (draft)
-  |
-  o  1 - {} a4dbed0837ea add bbb (draft)
-  |
-  o  0 - {} 199cc73e9a0b add aaa (draft)
-  
-
-Test that evolve does not loose topic information
--------------------------------------------------
-
-  $ hg evolve --rev 'topic(bar)'
-  move:[6] add ggg
-  atop:[15] add fff
-  move:[7] add hhh
-  atop:[16] add ggg
-  move:[8] add iii
-  atop:[17] add hhh
-  move:[9] add jjj
-  atop:[18] add iii
-  working directory is now at 9bf430c106b7
-  $ hg log -G
-  @  19 - {bar} 9bf430c106b7 add jjj (draft)
-  |
-  o  18 - {bar} d2dc89c57700 add iii (draft)
-  |
-  o  17 - {bar} 20bc4d02aa62 add hhh (draft)
-  |
-  o  16 - {bar} 16d6f664b17c add ggg (draft)
-  |
-  o  15 - {foo} 070c5573d8f9 add fff (draft)
-  |
-  o  14 - {foo} 42b49017ff90 add eee (draft)
-  |
-  o  11 - {foo} d9cacd156ffc add ddd (draft)
-  |
-  o  2 - {foo} cced9bac76e3 add ccc (draft)
-  |
-  o  1 - {} a4dbed0837ea add bbb (draft)
-  |
-  o  0 - {} 199cc73e9a0b add aaa (draft)
-  
-
-Tests next and prev behavior
-============================
-
-Basic move are restricted to the current topic
-
-  $ hg up foo
-  switching to topic foo
-  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  $ hg prev
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  [14] add eee
-  $ hg next
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  [15] add fff
-  $ hg next
-  no children on topic "foo"
-  do you want --no-topic
-  [1]
-  $ hg next --no-topic
-  switching to topic bar
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  [16] add ggg
-  $ hg prev
-  no parent in topic "bar"
-  (do you want --no-topic)
-  $ hg prev --no-topic
-  switching to topic foo
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  [15] add fff
--- a/tests/test-evolve.t	Wed May 03 13:23:36 2017 +0200
+++ b/tests/test-evolve.t	Thu May 18 23:51:10 2017 +0200
@@ -767,12 +767,6 @@
   $ hg debugrebuildstate
   $ hg st
 
-Test olog
-
-  $ hg olog
-  4	: add 4 - test
-  11	: add 3 - test
-
 Test obsstore stat
 
   $ hg debugobsstorestat
--- a/tests/test-obsolete.t	Wed May 03 13:23:36 2017 +0200
+++ b/tests/test-obsolete.t	Thu May 18 23:51:10 2017 +0200
@@ -1,3 +1,5 @@
+
+  $ . $TESTDIR/testlib/common.sh
   $ cat >> $HGRCPATH <<EOF
   > [web]
   > push_ssl = false
@@ -8,6 +10,7 @@
   > debugobsolete=debugobsolete -d '0 0'
   > [extensions]
   > hgext.rebase=
+  > color =
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
   $ mkcommit() {
@@ -15,10 +18,6 @@
   >    hg add "$1"
   >    hg ci -m "add $1"
   > }
-  $ getid() {
-  >    hg id --hidden --debug -ir "$1"
-  > }
-
   $ alias qlog="hg log --template='{rev}\n- {node|short}\n'"
   $ hg init local
   $ cd local
@@ -689,12 +688,6 @@
   [4] add obsol_c'
   [10] add obsol_c
   [2]
-  $ hg olog
-  changeset:   2:4538525df7e2
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add c
-  
 
 Check import reports new unstable changeset:
 
--- a/tests/test-prev-next.t	Wed May 03 13:23:36 2017 +0200
+++ b/tests/test-prev-next.t	Thu May 18 23:51:10 2017 +0200
@@ -1,6 +1,7 @@
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > hgext.graphlog=
+  > color =
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
 
@@ -47,6 +48,9 @@
   $ hg bookmarks
      mark                      1:6e742c9127b3
    * mark2                     0:a154386e50d1
+  $ hg next --dry-run --color=debug
+  hg update 1;
+  [[evolve.rev|1]] added b
   $ hg next
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   [1] added b
@@ -147,6 +151,9 @@
   $ hg next --evolve
   no children
   [1]
+  $ hg prev --dry-run --color=debug
+  hg update 1;
+  [[evolve.rev|1]] added b
   $ hg prev
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   [1] added b
--- a/tests/test-topic-dest.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,492 +0,0 @@
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-
-  $ hg init jungle
-  $ cd jungle
-  $ cat <<EOF >> .hg/hgrc
-  > [extensions]
-  > rebase=
-  > histedit=
-  > [phases]
-  > publish=false
-  > EOF
-  $ cat <<EOF >> $HGRCPATH
-  > [ui]
-  > logtemplate = '{rev} ({topics}) {desc}\n'
-  > EOF
-
-  $ for x in alpha beta gamma delta ; do
-  >   echo file $x >> $x
-  >   hg add $x
-  >   hg ci -m "c_$x"
-  > done
-
-Test NGTip feature
-==================
-
-Simple linear case
-
-  $ echo babar >> jungle
-  $ hg add jungle
-  $ hg ci -t elephant -m babar
-
-  $ hg log -G
-  @  4 (elephant) babar
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ hg log -r 'ngtip(.)'
-  3 () c_delta
-  $ hg log -r 'default'
-  3 () c_delta
-
-
-multiple heads with topic
-
-  $ hg up "desc('c_beta')"
-  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  $ echo zephir >> jungle
-  $ hg add jungle
-  $ hg ci -t monkey -m zephir
-  $ hg log -G
-  @  5 (monkey) zephir
-  |
-  | o  4 (elephant) babar
-  | |
-  | o  3 () c_delta
-  | |
-  | o  2 () c_gamma
-  |/
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ hg log -r 'ngtip(.)'
-  3 () c_delta
-  $ hg log -r 'default'
-  3 () c_delta
-
-one of the head is a valid tip
-
-  $ hg up "desc('c_delta')"
-  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ echo epsilon >> epsilon
-  $ hg add epsilon
-  $ hg ci -m "c_epsilon"
-  $ hg log -G
-  @  6 () c_epsilon
-  |
-  | o  5 (monkey) zephir
-  | |
-  +---o  4 (elephant) babar
-  | |
-  o |  3 () c_delta
-  | |
-  o |  2 () c_gamma
-  |/
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ hg log -r 'ngtip(.)'
-  6 () c_epsilon
-  $ hg log -r 'default'
-  6 () c_epsilon
-
-rebase destination
-==================
-
-rebase on branch ngtip
-
-  $ hg up elephant
-  switching to topic elephant
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg rebase
-  rebasing 4:cb7ae72f4a80 "babar"
-  $ hg log -G
-  @  7 (elephant) babar
-  |
-  o  6 () c_epsilon
-  |
-  | o  5 (monkey) zephir
-  | |
-  o |  3 () c_delta
-  | |
-  o |  2 () c_gamma
-  |/
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ hg up monkey
-  switching to topic monkey
-  1 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  $ hg rebase
-  rebasing 5:d832ddc604ec "zephir"
-  $ hg log -G
-  @  8 (monkey) zephir
-  |
-  | o  7 (elephant) babar
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-Rebase on other topic heads if any
-
-  $ hg up 'desc(c_delta)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ echo "General Huc" >> monkeyville
-  $ hg add monkeyville
-  $ hg ci -t monkey -m Huc
-  $ hg log -G
-  @  9 (monkey) Huc
-  |
-  | o  8 (monkey) zephir
-  | |
-  | | o  7 (elephant) babar
-  | |/
-  | o  6 () c_epsilon
-  |/
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ hg rebase
-  rebasing 9:d79a104e2902 "Huc" (tip)
-  $ hg log -G
-  @  10 (monkey) Huc
-  |
-  o  8 (monkey) zephir
-  |
-  | o  7 (elephant) babar
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-merge destination
-=================
-
-  $ hg up 'ngtip(default)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ hg up default
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo zeta >> zeta
-  $ hg add zeta
-  $ hg ci -m "c_zeta"
-  $ hg log -G
-  @  11 () c_zeta
-  |
-  | o  10 (monkey) Huc
-  | |
-  | o  8 (monkey) zephir
-  |/
-  | o  7 (elephant) babar
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ hg up elephant
-  switching to topic elephant
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg rebase -d 'desc(c_zeta)' # make sure tip is elsewhere
-  rebasing 7:8d0b77140b05 "babar"
-  $ hg up monkey
-  switching to topic monkey
-  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg merge
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-  $ hg topic
-     elephant
-   * monkey
-  $ hg ci -m 'merge with default'
-  $ hg topic
-     elephant
-   * monkey
-  $ hg log -G
-  @    13 (monkey) merge with default
-  |\
-  | | o  12 (elephant) babar
-  | |/
-  | o  11 () c_zeta
-  | |
-  o |  10 (monkey) Huc
-  | |
-  o |  8 (monkey) zephir
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-
-Check pull --rebase
--------------------
-
-(we broke it a some point)
-
-  $ cd ..
-  $ hg clone jungle other --rev '2'
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 3 files
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cd other
-  $ echo other > other
-  $ hg add other
-  $ hg ci -m 'c_other'
-  $ hg pull -r default --rebase
-  pulling from $TESTTMP/jungle (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 3 files (+1 heads)
-  rebasing 3:dbc48dd9e743 "c_other"
-  $ hg log -G
-  @  7 () c_other
-  |
-  o  6 () c_zeta
-  |
-  o  5 () c_epsilon
-  |
-  o  4 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-  $ cd ../jungle
-
-
-Default destination for update
-===============================
-
-initial setup
-
-  $ hg up elephant
-  switching to topic elephant
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ echo arthur >> jungle
-  $ hg ci -m arthur
-  $ echo pompadour >> jungle
-  $ hg ci -m pompadour
-  $ hg up 'roots(all())'
-  0 files updated, 0 files merged, 6 files removed, 0 files unresolved
-  $ hg log -G
-  o  15 (elephant) pompadour
-  |
-  o  14 (elephant) arthur
-  |
-  | o    13 (monkey) merge with default
-  | |\
-  o---+  12 (elephant) babar
-   / /
-  | o  11 () c_zeta
-  | |
-  o |  10 (monkey) Huc
-  | |
-  o |  8 (monkey) zephir
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  @  0 () c_alpha
-  
-
-testing default destination on a branch
-
-  $ hg up
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log -G
-  o  15 (elephant) pompadour
-  |
-  o  14 (elephant) arthur
-  |
-  | o    13 (monkey) merge with default
-  | |\
-  o---+  12 (elephant) babar
-   / /
-  | @  11 () c_zeta
-  | |
-  o |  10 (monkey) Huc
-  | |
-  o |  8 (monkey) zephir
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-extra setup for topic
-(making sure tip is not the topic)
-
-  $ hg up 'desc(c_zeta)'
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo 'eta' >> 'eta'
-  $ hg add 'eta'
-  $ hg commit -m 'c_eta'
-  $ hg log -G
-  @  16 () c_eta
-  |
-  | o  15 (elephant) pompadour
-  | |
-  | o  14 (elephant) arthur
-  | |
-  +---o  13 (monkey) merge with default
-  | | |
-  | o |  12 (elephant) babar
-  |/ /
-  o |  11 () c_zeta
-  | |
-  | o  10 (monkey) Huc
-  | |
-  | o  8 (monkey) zephir
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-Testing default destination for topic
-
-  $ hg up 'roots(topic(elephant))'
-  switching to topic elephant
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg up
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log -G
-  o  16 () c_eta
-  |
-  | @  15 (elephant) pompadour
-  | |
-  | o  14 (elephant) arthur
-  | |
-  +---o  13 (monkey) merge with default
-  | | |
-  | o |  12 (elephant) babar
-  |/ /
-  o |  11 () c_zeta
-  | |
-  | o  10 (monkey) Huc
-  | |
-  | o  8 (monkey) zephir
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-Testing default destination for topic
-
-  $ hg up 'p1(roots(topic(elephant)))'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic elephant
-  $ hg up
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log -G
-  o  16 () c_eta
-  |
-  | @  15 (elephant) pompadour
-  | |
-  | o  14 (elephant) arthur
-  | |
-  +---o  13 (monkey) merge with default
-  | | |
-  | o |  12 (elephant) babar
-  |/ /
-  o |  11 () c_zeta
-  | |
-  | o  10 (monkey) Huc
-  | |
-  | o  8 (monkey) zephir
-  |/
-  o  6 () c_epsilon
-  |
-  o  3 () c_delta
-  |
-  o  2 () c_gamma
-  |
-  o  1 () c_beta
-  |
-  o  0 () c_alpha
-  
-
-Default destination for histedit
-================================
-
-By default histedit should edit with the current topic only
-(even when based on other draft
-
-  $ hg phase 'desc(c_zeta)'
-  11: draft
-  $ HGEDITOR=cat hg histedit | grep pick
-  pick e44744d9ad73 12 babar
-  pick 38eea8439aee 14 arthur
-  pick 411315c48bdc 15 pompadour
-  #  p, pick = use commit
--- a/tests/test-topic-push.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,414 +0,0 @@
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-
-  $ cat << EOF >> $HGRCPATH
-  > [ui]
-  > logtemplate = {rev} {branch} {get(namespaces, "topics")} {phase} {desc|firstline}\n
-  > [ui]
-  > ssh =python "$RUNTESTDIR/dummyssh"
-  > EOF
-
-  $ hg init main
-  $ hg init draft
-  $ cat << EOF >> draft/.hg/hgrc
-  > [phases]
-  > publish=False
-  > EOF
-  $ hg clone main client
-  updating to branch default
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cat << EOF >> client/.hg/hgrc
-  > [paths]
-  > draft=../draft
-  > EOF
-
-
-Testing core behavior to make sure we did not break anything
-============================================================
-
-Pushing a first changeset
-
-  $ cd client
-  $ echo aaa > aaa
-  $ hg add aaa
-  $ hg commit -m 'CA'
-  $ hg outgoing -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  @  0 default  draft CA
-  
-  $ hg push
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-
-Pushing two heads
-
-  $ echo aaa > bbb
-  $ hg add bbb
-  $ hg commit -m 'CB'
-  $ echo aaa > ccc
-  $ hg up 'desc(CA)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg add ccc
-  $ hg commit -m 'CC'
-  created new head
-  $ hg outgoing -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  @  2 default  draft CC
-  
-  o  1 default  draft CB
-  
-  $ hg push
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  abort: push creates new remote head 9fe81b7f425d!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-  $ hg outgoing -r 'desc(CB)' -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  o  1 default  draft CB
-  
-  $ hg push -r 'desc(CB)'
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-
-Pushing a new branch
-
-  $ hg branch mountain
-  marked working directory as branch mountain
-  (branches are permanent and global, did you want a bookmark?)
-  $ hg commit --amend
-  $ hg outgoing -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  @  4 mountain  draft CC
-  
-  $ hg push 
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  abort: push creates new remote branches: mountain!
-  (use 'hg push --new-branch' to create new remote branches)
-  [255]
-  $ hg push --new-branch
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  2 new obsolescence markers
-
-Including on non-publishing
-
-  $ hg push --new-branch draft
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 3 files (+1 heads)
-  2 new obsolescence markers
-
-Testing topic behavior
-======================
-
-Local peer tests
-----------------
-
-  $ hg up -r 'desc(CA)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic babar
-  $ echo aaa > ddd
-  $ hg add ddd
-  $ hg commit -m 'CD'
-  $ hg log -G # keep track of phase because I saw some strange bug during developement
-  @  5 default babar draft CD
-  |
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a non publishing server should not be seen as a new head
-
-  $ hg push draft
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  $ hg log -G
-  @  5 default babar draft CD
-  |
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a publishing server should be seen as a new head
-
-  $ hg push
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  abort: push creates new remote head 67f579af159d!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-  $ hg log -G
-  @  5 default babar draft CD
-  |
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-wireprotocol tests
-------------------
-
-  $ hg up -r 'desc(CA)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic celeste
-  $ echo aaa > eee
-  $ hg add eee
-  $ hg commit -m 'CE'
-  $ hg log -G # keep track of phase because I saw some strange bug during developement
-  @  6 default celeste draft CE
-  |
-  | o  5 default babar draft CD
-  |/
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a non publishing server without topic -> new head
-
-  $ cat << EOF >> ../draft/.hg/hgrc
-  > [extensions]
-  > topic=!
-  > EOF
-  $ hg push ssh://user@dummy/draft
-  pushing to ssh://user@dummy/draft
-  searching for changes
-  abort: push creates new remote head 84eaf32db6c3!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-  $ hg log -G
-  @  6 default celeste draft CE
-  |
-  | o  5 default babar draft CD
-  |/
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a non publishing server should not be seen as a new head
-
-  $ printf "topic=" >> ../draft/.hg/hgrc
-  $ hg config extensions.topic >> ../draft/.hg/hgrc
-  $ hg push ssh://user@dummy/draft
-  pushing to ssh://user@dummy/draft
-  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)
-  $ hg log -G
-  @  6 default celeste draft CE
-  |
-  | o  5 default babar draft CD
-  |/
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a publishing server should be seen as a new head
-
-  $ hg push ssh://user@dummy/main
-  pushing to ssh://user@dummy/main
-  searching for changes
-  abort: push creates new remote head 67f579af159d!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-  $ hg log -G
-  @  6 default celeste draft CE
-  |
-  | o  5 default babar draft CD
-  |/
-  | o  4 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Check that we reject multiple head on the same topic
-----------------------------------------------------
-
-  $ hg up 'desc(CB)'
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic babar
-  $ echo aaa > fff
-  $ hg add fff
-  $ hg commit -m 'CF'
-  $ hg log -G
-  @  7 default babar draft CF
-  |
-  | o  6 default celeste draft CE
-  | |
-  | | o  5 default babar draft CD
-  | |/
-  | | o  4 mountain  public CC
-  | |/
-  o |  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-  $ hg push draft
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  abort: push creates new remote head f0bc62a661be on branch 'default:babar'!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-
-Multiple head on a branch merged in a topic changesets
-------------------------------------------------------------------------
-
-
-  $ hg up 'desc(CA)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ echo aaa > ggg
-  $ hg add ggg
-  $ hg commit -m 'CG'
-  created new head
-  $ hg up 'desc(CF)'
-  switching to topic babar
-  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg merge 'desc(CG)'
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-  $ hg commit -m 'CM'
-  $ hg log -G
-  @    9 default babar draft CM
-  |\
-  | o  8 default  draft CG
-  | |
-  o |  7 default babar draft CF
-  | |
-  | | o  6 default celeste draft CE
-  | |/
-  | | o  5 default babar draft CD
-  | |/
-  | | o  4 mountain  public CC
-  | |/
-  o |  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Reject when pushing to draft
-
-  $ hg push draft -r .
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  abort: push creates new remote head 4937c4cad39e!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-
-
-Reject when pushing to publishing
-
-  $ hg push -r .
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 2 changes to 2 files
-
-  $ cd ..
-
-Test phase move
-==================================
-
-setup, two repo knowns about two small topic branch
-
-  $ hg init repoA
-  $ hg clone repoA repoB
-  updating to branch default
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cat << EOF >> repoA/.hg/hgrc
-  > [phases]
-  > publish=False
-  > EOF
-  $ cat << EOF >> repoB/.hg/hgrc
-  > [phases]
-  > publish=False
-  > EOF
-  $ cd repoA
-  $ echo aaa > base
-  $ hg add base
-  $ hg commit -m 'CBASE'
-  $ echo aaa > aaa
-  $ hg add aaa
-  $ hg topic topicA
-  $ hg commit -m 'CA'
-  $ hg up 'desc(CBASE)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ echo aaa > bbb
-  $ hg add bbb
-  $ hg topic topicB
-  $ hg commit -m 'CB'
-  $ cd ..
-  $ hg push -R repoA repoB
-  pushing to repoB
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 3 files (+1 heads)
-  $ hg log -G -R repoA
-  @  2 default topicB draft CB
-  |
-  | o  1 default topicA draft CA
-  |/
-  o  0 default  draft CBASE
-  
-
-We turn different topic to public on each side,
-
-  $ hg -R repoA phase --public topicA
-  $ hg -R repoB phase --public topicB
-
-Pushing should complain because it create to heads on default
-
-  $ hg push -R repoA repoB
-  pushing to repoB
-  searching for changes
-  no changes found
-  abort: push create a new head on branch "default"
-  [255]
--- a/tests/test-topic-stack-data.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-Setup
-=====
-
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-
-  $ hg init test-list
-  $ cd test-list
-  $ cat <<EOF >> .hg/hgrc
-  > [phases]
-  > publish=false
-  > EOF
-  $ cat <<EOF >> $HGRCPATH
-  > [experimental]
-  > # disable the new graph style until we drop 3.7 support
-  > graphstyle.missing = |
-  > # turn evolution on
-  > evolution=all
-  > EOF
-
-
-  $ mkcommit() {
-  >    echo "$1" > "$1"
-  >    hg add "$1"
-  >    hg ci -m "add $1"
-  > }
-
-Build some basic graph
-----------------------
-
-  $ for x in base_a base_b base_c base_d base_e ; do
-  >   mkcommit $x
-  > done
-
-Add another branch with two heads
-
-  $ hg up 'desc(base_a)'
-  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  $ hg branch lake
-  marked working directory as branch lake
-  (branches are permanent and global, did you want a bookmark?)
-  $ mkcommit lake_a
-  $ mkcommit lake_b
-  $ hg up 'desc(lake_a)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ mkcommit lake_c
-  created new head
-
-
-Add some topics
----------------
-
-A simple topic that need rebasing
-
-  $ hg up 'desc(base_c)'
-  2 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ hg topic baz
-  $ mkcommit baz_a
-  $ mkcommit baz_b
-
-A simple topic with unstability
-
-  $ hg up 'desc(base_d)'
-  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ hg topic fuz
-  $ mkcommit fuz_a
-  $ mkcommit fuz_b
-  $ mkcommit fuz_c
-  $ hg up 'desc(fuz_a)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ hg commit --amend --message 'fuz1_a'
-
-A topic with multiple heads
-
-  $ hg up 'desc(base_e)'
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic bar
-  $ mkcommit bar_a
-  $ mkcommit bar_b
-  $ mkcommit bar_c
-  $ hg up 'desc(bar_b)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ mkcommit bar_d
-  $ mkcommit bar_e
-  $ hg up 'desc(bar_d)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg commit --amend --message 'bar1_d'
-
-topic 'foo' on the multi headed branch
-
-  $ hg up 'desc(lake_a)'
-  1 files updated, 0 files merged, 7 files removed, 0 files unresolved
-  $ hg topic foo
-  $ mkcommit foo_a
-  $ mkcommit foo_b
-
-Summary
--------
-
-  $ hg summary
-  parent: 21:3e54b49a3113 tip
-   add foo_b
-  branch: lake
-  commit: (clean)
-  update: 2 new changesets (update)
-  phases: 22 draft
-  unstable: 3 changesets
-  topic:  foo
-  $ hg log --graph -T '{desc} ({branch}) [{topic}]'
-  @  add foo_b (lake) []
-  |
-  o  add foo_a (lake) []
-  |
-  | o  bar1_d (default) []
-  | |
-  | | o  add bar_e (default) []
-  | | |
-  | | x  add bar_d (default) []
-  | |/
-  | | o  add bar_c (default) []
-  | |/
-  | o  add bar_b (default) []
-  | |
-  | o  add bar_a (default) []
-  | |
-  | | o  fuz1_a (default) []
-  | | |
-  | | | o  add fuz_c (default) []
-  | | | |
-  | | | o  add fuz_b (default) []
-  | | | |
-  | | | x  add fuz_a (default) []
-  | | |/
-  | | | o  add baz_b (default) []
-  | | | |
-  | | | o  add baz_a (default) []
-  | | | |
-  +-------o  add lake_c (lake) []
-  | | | |
-  +-------o  add lake_b (lake) []
-  | | | |
-  o | | |  add lake_a (lake) []
-  | | | |
-  | o | |  add base_e (default) []
-  | |/ /
-  | o /  add base_d (default) []
-  | |/
-  | o  add base_c (default) []
-  | |
-  | o  add base_b (default) []
-  |/
-  o  add base_a (default) []
-  
-
-Actual Testing
-==============
-
-basic output
-
-  $ hg topic
-     bar
-     baz
-   * foo
-     fuz
-
-quiet version
-
-  $ hg topic --quiet
-  bar
-  baz
-  foo
-  fuz
-
-verbose
-
-  $ hg topic --verbose
-     bar (on branch: default, 5 changesets, 1 troubled, 2 heads)
-     baz (on branch: default, 2 changesets, 2 behind)
-   * foo (on branch: lake, 2 changesets, ambiguous destination)
-     fuz (on branch: default, 3 changesets, 2 troubled, 1 behind)
-
-json
-
-  $ hg topic -T json
-  [
-   {
-    "active": false,
-    "topic": "bar"
-   },
-   {
-    "active": false,
-    "topic": "baz"
-   },
-   {
-    "active": true,
-    "topic": "foo"
-   },
-   {
-    "active": false,
-    "topic": "fuz"
-   }
-  ]
-
-json --verbose
-
-  $ hg topic -T json --verbose
-  [
-   {
-    "active": false,
-    "branches+": "default",
-    "changesetcount": 5,
-    "headcount": 2,
-    "topic": "bar",
-    "troubledcount": 1
-   },
-   {
-    "active": false,
-    "behindcount": 2,
-    "branches+": "default",
-    "changesetcount": 2,
-    "topic": "baz"
-   },
-   {
-    "active": true,
-    "behinderror": "ambiguous destination",
-    "branches+": "lake",
-    "changesetcount": 2,
-    "topic": "foo"
-   },
-   {
-    "active": false,
-    "behindcount": 1,
-    "branches+": "default",
-    "changesetcount": 3,
-    "topic": "fuz",
-    "troubledcount": 2
-   }
-  ]
-
-Also test this situation with 'hg stack'
-=======================================
-
-  $ hg stack bar
-  ### topic: bar (2 heads)
-  ### branch: default
-  t5: add bar_c
-  t2^ add bar_b (base)
-  t4$ add bar_e (unstable)
-  t3: bar1_d
-  t2: add bar_b
-  t1: add bar_a
-    ^ add base_e
-  $ hg stack baz
-  ### topic: baz
-  ### branch: default, 2 behind
-  t2: add baz_b
-  t1: add baz_a
-    ^ add base_c
-  $ hg stack foo
-  ### topic: foo
-  ### branch: lake, ambigious rebase destination
-  t2@ add foo_b (current)
-  t1: add foo_a
-    ^ add lake_a
-  $ hg stack fuz
-  ### topic: fuz
-  ### branch: default, 1 behind
-  t3$ add fuz_c (unstable)
-  t2$ add fuz_b (unstable)
-  t1: fuz1_a
-    ^ add base_d
--- a/tests/test-topic-stack.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +0,0 @@
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-
-Initial setup
-
-
-  $ cat << EOF >> $HGRCPATH
-  > [ui]
-  > logtemplate = {rev} {branch} \{{get(namespaces, "topics")}} {phase} {desc|firstline}\n
-  > [experimental]
-  > evolution=createmarkers,exchange,allowunstable
-  > EOF
-
-  $ hg init main
-  $ cd main
-  $ hg topic other
-  $ echo aaa > aaa
-  $ hg add aaa
-  $ hg commit -m c_a
-  $ echo aaa > bbb
-  $ hg add bbb
-  $ hg commit -m c_b
-  $ hg topic foo
-  $ echo aaa > ccc
-  $ hg add ccc
-  $ hg commit -m c_c
-  $ echo aaa > ddd
-  $ hg add ddd
-  $ hg commit -m c_d
-  $ echo aaa > eee
-  $ hg add eee
-  $ hg commit -m c_e
-  $ echo aaa > fff
-  $ hg add fff
-  $ hg commit -m c_f
-  $ hg log -G
-  @  5 default {foo} draft c_f
-  |
-  o  4 default {foo} draft c_e
-  |
-  o  3 default {foo} draft c_d
-  |
-  o  2 default {foo} draft c_c
-  |
-  o  1 default {other} draft c_b
-  |
-  o  0 default {other} draft c_a
-  
-
-Check that topic without any parent does not crash --list
----------------------------------------------------------
-
-  $ hg up other
-  switching to topic other
-  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  $ hg topic --list
-  ### topic: other
-  ### branch: default
-  t2@ c_b (current)
-  t1: c_a
-  $ hg phase --public 'topic("other")'
-  $ hg up foo
-  switching to topic foo
-  4 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-Simple test
------------
-
-'hg stack' list all changeset in the topic
-
-  $ hg topic
-   * foo
-  $ hg stack
-  ### topic: foo
-  ### branch: default
-  t4@ c_f (current)
-  t3: c_e
-  t2: c_d
-  t1: c_c
-    ^ c_b
-
-error case, nothing to list
-
-  $ hg topic --clear
-  $ hg stack
-  abort: no active topic to list
-  [255]
-
-Test "t#" reference
--------------------
-
-
-  $ hg up t2
-  abort: cannot resolve "t2": no active topic
-  [255]
-  $ hg topic foo
-  $ hg up t42
-  abort: cannot resolve "t42": topic "foo" has only 4 changesets
-  [255]
-  $ hg up t2
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ hg summary
-  parent: 3:e629654d7050 
-   c_d
-  branch: default
-  commit: (clean)
-  update: (current)
-  phases: 4 draft
-  topic:  foo
-
-Case with some of the topic unstable
-------------------------------------
-
-  $ echo bbb > ddd
-  $ hg commit --amend
-  $ hg log -G
-  @  7 default {foo} draft c_d
-  |
-  | o  5 default {foo} draft c_f
-  | |
-  | o  4 default {foo} draft c_e
-  | |
-  | x  3 default {foo} draft c_d
-  |/
-  o  2 default {foo} draft c_c
-  |
-  o  1 default {} public c_b
-  |
-  o  0 default {} public c_a
-  
-  $ hg topic --list
-  ### topic: foo
-  ### branch: default
-  t4$ c_f (unstable)
-  t3$ c_e (unstable)
-  t2@ c_d (current)
-  t1: c_c
-    ^ c_b
-
-Also test the revset:
-
-  $ hg log -r 'stack()'
-  2 default {foo} draft c_c
-  7 default {foo} draft c_d
-  4 default {foo} draft c_e
-  5 default {foo} draft c_f
-
-Case with multiple heads on the topic
--------------------------------------
-
-Make things linear again
-
-  $ hg rebase -s 'desc(c_e)' -d 'desc(c_d) - obsolete()'
-  rebasing 4:0f9ac936c87d "c_e"
-  rebasing 5:6559e6d93aea "c_f"
-  $ hg log -G
-  o  9 default {foo} draft c_f
-  |
-  o  8 default {foo} draft c_e
-  |
-  @  7 default {foo} draft c_d
-  |
-  o  2 default {foo} draft c_c
-  |
-  o  1 default {} public c_b
-  |
-  o  0 default {} public c_a
-  
-
-
-Create the second branch
-
-  $ hg up 'desc(c_d)'
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo aaa > ggg
-  $ hg add ggg
-  $ hg commit -m c_g
-  $ echo aaa > hhh
-  $ hg add hhh
-  $ hg commit -m c_h
-  $ hg log -G
-  @  11 default {foo} draft c_h
-  |
-  o  10 default {foo} draft c_g
-  |
-  | o  9 default {foo} draft c_f
-  | |
-  | o  8 default {foo} draft c_e
-  |/
-  o  7 default {foo} draft c_d
-  |
-  o  2 default {foo} draft c_c
-  |
-  o  1 default {} public c_b
-  |
-  o  0 default {} public c_a
-  
-
-Test output
-
-  $ hg top -l
-  ### topic: foo (2 heads)
-  ### branch: default
-  t6: c_f
-  t5: c_e
-  t2^ c_d (base)
-  t4@ c_h (current)
-  t3: c_g
-  t2: c_d
-  t1: c_c
-    ^ c_b
-
-Case with multiple heads on the topic with unstability involved
----------------------------------------------------------------
-
-We amend the message to make sure the display base pick the right changeset
-
-  $ hg up 'desc(c_d)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ echo ccc > ddd
-  $ hg commit --amend -m 'c_D' 
-  $ hg rebase -d . -s 'desc(c_g)'
-  rebasing 10:81264ae8a36a "c_g"
-  rebasing 11:fde5f5941642 "c_h"
-  $ hg log -G
-  o  15 default {foo} draft c_h
-  |
-  o  14 default {foo} draft c_g
-  |
-  @  13 default {foo} draft c_D
-  |
-  | o  9 default {foo} draft c_f
-  | |
-  | o  8 default {foo} draft c_e
-  | |
-  | x  7 default {foo} draft c_d
-  |/
-  o  2 default {foo} draft c_c
-  |
-  o  1 default {} public c_b
-  |
-  o  0 default {} public c_a
-  
-
-  $ hg topic --list
-  ### topic: foo (2 heads)
-  ### branch: default
-  t6$ c_f (unstable)
-  t5$ c_e (unstable)
-  t2^ c_D (base)
-  t4: c_h
-  t3: c_g
-  t2@ c_D (current)
-  t1: c_c
-    ^ c_b
--- a/tests/test-topic-tutorial.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,468 +0,0 @@
-==============
-Topic Tutorial
-==============
-
-.. This test file is also supposed to be able to compile as a rest file.
-
-
-.. Some Setup::
-
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-  $ hg init server
-  $ cd server
-  $ cat >> .hg/hgrc << EOF
-  > [ui]
-  > user= Shopping Master
-  > EOF
-  $ cat >> shopping << EOF
-  > Spam
-  > Whizzo butter
-  > Albatross
-  > Rat (rather a lot)
-  > Jugged fish
-  > Blancmange
-  > Salmon mousse
-  > EOF
-  $ hg commit -A -m "Shopping list"
-  adding shopping
-  $ cd ..
-  $ hg clone server client
-  updating to branch default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cd client
-  $ cat >> .hg/hgrc << EOF
-  > [ui]
-  > user= Tutorial User
-  > EOF
-
-Topic branches are lightweight branches which disappear when changes are
-finalized (move to the public phase). They can help users to organise and share
-their unfinished work.
-
-Topic Basics
-============
-
-Let's says we use Mercurial to manage our shopping list::
-
-  $ hg log --graph
-  @  changeset:   0:38da43f0a2ea
-     tag:         tip
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Shopping list
-  
-
-We are about to do some edition to this list and would like to do them within
-a topic. Creating a new topic is done using the ``topic`` command::
-
-  $ hg topic food
-
-As for named branch, our topic is active but it does not contains any changesets yet::
-
-  $ hg topic
-   * food
-  $ hg summary
-  parent: 0:38da43f0a2ea tip
-   Shopping list
-  branch: default
-  commit: (clean)
-  update: (current)
-  topic:  food
-  $ hg log --graph
-  @  changeset:   0:38da43f0a2ea
-     tag:         tip
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Shopping list
-  
-
-Our next commit will be part of the active topic::
-
-  $ cat >> shopping << EOF
-  > Egg
-  > Suggar
-  > Vinegar
-  > Oil
-  > EOF
-  $ hg commit -m "adding condiments"
-  $ hg log --graph --rev 'topic("food")'
-  @  changeset:   1:13900241408b
-  |  tag:         tip
-  ~  topic:       food
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     adding condiments
-  
-
-And future commit will be part of that topic too::
-
-  $ cat >> shopping << EOF
-  > Bananas
-  > Pear
-  > Apple
-  > EOF
-  $ hg commit -m "adding fruits"
-  $ hg log --graph --rev 'topic("food")'
-  @  changeset:   2:287de11b401f
-  |  tag:         tip
-  |  topic:       food
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding fruits
-  |
-  o  changeset:   1:13900241408b
-  |  topic:       food
-  ~  user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     adding condiments
-  
-
-We can get a compact view of the content of our topic using the ``stack`` command::
-
-  $ hg stack
-  ### topic: food
-  ### branch: default
-  t2@ adding fruits (current)
-  t1: adding condiments
-    ^ Shopping list
-
-The topic desactivate when we update away from it::
-
-  $ hg up default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg topic
-     food
-
-Note that ``default`` (name of the branch) now refers to the tipmost changeset of default without a topic::
-
-  $ hg log --graph
-  o  changeset:   2:287de11b401f
-  |  tag:         tip
-  |  topic:       food
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding fruits
-  |
-  o  changeset:   1:13900241408b
-  |  topic:       food
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding condiments
-  |
-  @  changeset:   0:38da43f0a2ea
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Shopping list
-  
-
-And updating back to the topic reactivate it::
-
-  $ hg up food
-  switching to topic food
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg topic
-   * food
-
-The name used for updating does not affect the activation of the topic, updating to a revision part of a topic will activate it in all cases::
-
-  $ hg up default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg up --rev 'desc("condiments")'
-  switching to topic food
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg topic
-   * food
-
-.. server side activity::
-
-  $ cd ../server/
-  $ cat > shopping << EOF
-  > T-Shirt
-  > Trousers
-  > Spam
-  > Whizzo butter
-  > Albatross
-  > Rat (rather a lot)
-  > Jugged fish
-  > Blancmange
-  > Salmon mousse
-  > EOF
-  $ hg commit -A -m "Adding clothes"
-  $ cd ../client
-
-Topic will also affect rebase and merge destination. Let's pull the latest update from the main server::
-
-  $ hg pull
-  pulling from $TESTTMP/server (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  (run 'hg heads' to see heads)
-  $ hg log -G
-  o  changeset:   3:6104862e8b84
-  |  tag:         tip
-  |  parent:      0:38da43f0a2ea
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Adding clothes
-  |
-  | o  changeset:   2:287de11b401f
-  | |  topic:       food
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     adding fruits
-  | |
-  | @  changeset:   1:13900241408b
-  |/   topic:       food
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     adding condiments
-  |
-  o  changeset:   0:38da43f0a2ea
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Shopping list
-  
-
-The topic head will not be considered when merge from the new head of the branch::
-
-  $ hg up default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg merge
-  abort: branch 'default' has one head - please merge with an explicit rev
-  (run 'hg heads' to see all heads)
-  [255]
-
-But the topic will see that branch head as a valid destination::
-
-  $ hg up food
-  switching to topic food
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg rebase
-  rebasing 1:13900241408b "adding condiments"
-  merging shopping
-  rebasing 2:287de11b401f "adding fruits"
-  merging shopping
-  $ hg log --graph
-  @  changeset:   5:2d50db8b5b4c
-  |  tag:         tip
-  |  topic:       food
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding fruits
-  |
-  o  changeset:   4:4011b46eeb33
-  |  topic:       food
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding condiments
-  |
-  o  changeset:   3:6104862e8b84
-  |  parent:      0:38da43f0a2ea
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Adding clothes
-  |
-  o  changeset:   0:38da43f0a2ea
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Shopping list
-  
-
-The topic information will fade out when we publish the changesets::
-
-  $ hg topic
-     food
-  $ hg push
-  pushing to $TESTTMP/server (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 2 changesets with 2 changes to 1 files
-  2 new obsolescence markers
-  $ hg topic
-  $ hg log --graph
-  @  changeset:   5:2d50db8b5b4c
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding fruits
-  |
-  o  changeset:   4:4011b46eeb33
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     adding condiments
-  |
-  o  changeset:   3:6104862e8b84
-  |  parent:      0:38da43f0a2ea
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Adding clothes
-  |
-  o  changeset:   0:38da43f0a2ea
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Shopping list
-  
-  $ hg up default
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-Working with Multiple Topics
-============================
-
-In the above example, topic are not bring much benefit since you only have one
-line of developement. Topic start to be more useful when you have to work on
-multiple features are the same time.
-
-We might go shopping in a hardware store in the same go, so let's add some
-tools to the shopping list withing a new topic::
-
-  $ hg topic tools
-  $ echo hammer >> shopping
-  $ hg ci -m 'Adding hammer'
-  $ echo saw >> shopping
-  $ hg ci -m 'Adding saw'
-  $ echo drill >> shopping
-  $ hg ci -m 'Adding drill'
-
-But are not sure to actually go in the hardward store, so in the meantime, we
-want to extend the list with drinks. We go back to the official default branch
-and start a new topic::
-
-  $ hg up default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg topic drinks
-  $ echo 'apple juice' >> shopping
-  $ hg ci -m 'Adding apple juice'
-  $ echo 'orange juice' >> shopping
-  $ hg ci -m 'Adding orange juice'
-
-We now have two topics::
-
-  $ hg topic
-   * drinks
-     tools
-
-The information ``hg stack`` command adapt to the active topic::
-
-  $ hg stack
-  ### topic: drinks
-  ### branch: default
-  t2@ Adding orange juice (current)
-  t1: Adding apple juice
-    ^ adding fruits
-  $ hg up tools
-  switching to topic tools
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg stack
-  ### topic: tools
-  ### branch: default
-  t3@ Adding drill (current)
-  t2: Adding saw
-  t1: Adding hammer
-    ^ adding fruits
-
-They are seen as independant branch by Mercurial. No rebase or merge betwen them will be attempted by default::
-
-  $ hg rebase
-  nothing to rebase
-  [1]
-
-.. server activity::
-
-  $ cd ../server
-  $ hg up
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ mv shopping foo
-  $ echo 'Coat' > shopping
-  $ cat foo >> shopping
-  $ hg ci -m 'add a coat'
-  $ echo 'Coat' > shopping
-  $ echo 'Shoes' >> shopping
-  $ cat foo >> shopping
-  $ hg rm foo
-  not removing foo: file is untracked
-  [1]
-  $ hg ci -m 'add a pair of shoes'
-  $ cd ../client
-
-Lets see what other people did in the mean time::
-
-  $ hg pull
-  pulling from $TESTTMP/server (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 2 changesets with 2 changes to 1 files (+1 heads)
-  (run 'hg heads' to see heads)
-
-There is new changes! We can simply use ``hg rebase`` to update our changeset on top of the latest::
-
-  $ hg rebase
-  rebasing 6:183984ef46d1 "Adding hammer"
-  merging shopping
-  rebasing 7:cffff85af537 "Adding saw"
-  merging shopping
-  rebasing 8:34255b455dac "Adding drill"
-  merging shopping
-
-But what about the other topic? You can use 'hg topic --verbose' to see information about them::
-
-  $ hg topic --verbose
-     drinks (on branch: default, 2 changesets, 2 behind)
-     tools  (on branch: default, 3 changesets)
-
-The "2 behind" is telling you that there is 2 new changesets on the named branch of the topic. You need to merge or rebase to incorporate them.
-
-Pushing that topic would create a new heads will be prevented::
-
-  $ hg push --rev drinks
-  pushing to $TESTTMP/server (glob)
-  searching for changes
-  abort: push creates new remote head 70dfa201ed73!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-
-
-Even after a rebase Pushing all active topics at the same time will complains about the multiple heads it would create on that branch::
-
-  $ hg rebase -b drinks
-  rebasing 9:8dfa45bd5e0c "Adding apple juice"
-  merging shopping
-  rebasing 10:70dfa201ed73 "Adding orange juice"
-  merging shopping
-  switching to topic tools
-  $ hg push
-  pushing to $TESTTMP/server (glob)
-  searching for changes
-  abort: push creates new remote head 4cd7c1591a67!
-  (merge or see "hg help push" for details about pushing new heads)
-  [255]
-
-Publishing only one of them is allowed (as long as it does not create a new branch head has we just saw in the previous case)::
-
-  $ hg push -r drinks
-  pushing to $TESTTMP/server (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 2 changesets with 2 changes to 1 files
-  2 new obsolescence markers
-
-The publishing topic has now vanished, and the one still draft is now marked as "behind"::
-
-  $ hg topic --verbose
-   * tools (on branch: default, 3 changesets, 2 behind)
-  $ hg stack
-  ### topic: tools
-  ### branch: default, 2 behind
-  t3@ Adding drill (current)
-  t2: Adding saw
-  t1: Adding hammer
-    ^ add a pair of shoes
-
--- a/tests/test-topic.t	Wed May 03 13:23:36 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,654 +0,0 @@
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-
-  $ hg init pinky
-  $ cd pinky
-  $ cat <<EOF >> .hg/hgrc
-  > [phases]
-  > publish=false
-  > EOF
-  $ cat <<EOF >> $HGRCPATH
-  > [experimental]
-  > # disable the new graph style until we drop 3.7 support
-  > graphstyle.missing = |
-  > EOF
-
-  $ hg help topics
-  hg topics [TOPIC]
-  
-  View current topic, set current topic, or see all topics.
-  
-      The --verbose version of this command display various information on the
-      state of each topic.
-  
-  options:
-  
-      --clear        clear active topic if any
-      --change VALUE revset of existing revisions to change topic
-   -l --list         show the stack of changeset in the topic
-  
-  (some details hidden, use --verbose to show complete help)
-  $ hg topics
-
-Test topics interaction with evolution:
-
-  $ hg topics --config experimental.evolution=
-  $ hg topics --config experimental.evolution= --change . bob
-  abort: must have obsolete enabled to use --change
-  [255]
-
-Create some changes:
-
-  $ for x in alpha beta gamma delta ; do
-  >   echo file $x >> $x
-  >   hg addremove
-  >   hg ci -m "Add file $x"
-  > done
-  adding alpha
-  adding beta
-  adding gamma
-  adding delta
-
-Still no topics
-  $ hg topics
-
-Test commit flag and help text
-
-  $ echo stuff >> alpha
-  $ HGEDITOR=cat hg ci -t topicflag
-  
-  
-  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
-  HG: Leave message empty to abort commit.
-  HG: --
-  HG: user: test
-  HG: topic 'topicflag'
-  HG: branch 'default'
-  HG: changed alpha
-  abort: empty commit message
-  [255]
-  $ hg revert alpha
-  $ hg topic
-   * topicflag
-
-Make a topic
-  $ hg topic narf
-  $ hg topics
-   * narf
-  $ echo topic work >> alpha
-  $ hg ci -m 'start on narf'
-  $ hg co .^
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg topic fran
-  $ hg topics
-   * fran
-     narf
-  $ echo >> fran work >> beta
-  $ hg ci -m 'start on fran'
-  $ hg co narf
-  switching to topic narf
-  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg topic
-     fran
-   * narf
-  $ hg log -r . -T '{topics}\n'
-  narf
-  $ echo 'narf!!!' >> alpha
-  $ hg ci -m 'narf!'
-  $ hg log -G
-  @  changeset:   6:7c34953036d6
-  |  tag:         tip
-  |  topic:       narf
-  |  parent:      4:fb147b0b417c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     narf!
-  |
-  | o  changeset:   5:0469d521db49
-  | |  topic:       fran
-  | |  parent:      3:a53952faf762
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     start on fran
-  | |
-  o |  changeset:   4:fb147b0b417c
-  |/   topic:       narf
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     start on narf
-  |
-  o  changeset:   3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file delta
-  |
-  o  changeset:   2:15d1eb11d2fa
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file gamma
-  |
-  o  changeset:   1:c692ea2c9224
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file beta
-  |
-  o  changeset:   0:c2b7d2f7d14b
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Add file alpha
-  
-
-Exchanging of topics:
-  $ cd ..
-  $ hg init brain
-  $ hg -R pinky push -r 4 brain
-  pushing to brain
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 5 changesets with 5 changes to 4 files
-
-Export
-
-  $ hg -R pinky export
-  # HG changeset patch
-  # User test
-  # Date 0 0
-  #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 7c34953036d6a36eae468c550d0592b89ee8bffc
-  # Parent  fb147b0b417c25ca15547cd945acf51cf8dcaf02
-  # EXP-Topic narf
-  narf!
-  
-  diff -r fb147b0b417c -r 7c34953036d6 alpha
-  --- a/alpha	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/alpha	Thu Jan 01 00:00:00 1970 +0000
-  @@ -1,2 +1,3 @@
-   file alpha
-   topic work
-  +narf!!!
-
-Import
-
-  $ hg -R pinky export > narf.diff
-  $ hg -R pinky --config extensions.strip= strip .
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  saved backup bundle to $TESTTMP/pinky/.hg/strip-backup/7c34953036d6-1ff3bae2-backup.hg (glob)
-  $ hg -R pinky import narf.diff
-  applying narf.diff
-  $ hg -R pinky log -r .
-  changeset:   6:7c34953036d6
-  tag:         tip
-  topic:       narf
-  parent:      4:fb147b0b417c
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     narf!
-  
-Now that we've pushed to brain, the work done on narf is no longer a
-draft, so we won't see that topic name anymore:
-
-  $ hg log -R pinky -G
-  @  changeset:   6:7c34953036d6
-  |  tag:         tip
-  |  topic:       narf
-  |  parent:      4:fb147b0b417c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     narf!
-  |
-  | o  changeset:   5:0469d521db49
-  | |  topic:       fran
-  | |  parent:      3:a53952faf762
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     start on fran
-  | |
-  o |  changeset:   4:fb147b0b417c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     start on narf
-  |
-  o  changeset:   3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file delta
-  |
-  o  changeset:   2:15d1eb11d2fa
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file gamma
-  |
-  o  changeset:   1:c692ea2c9224
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file beta
-  |
-  o  changeset:   0:c2b7d2f7d14b
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Add file alpha
-  
-  $ cd brain
-  $ hg co tip
-  4 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-Because the change is public, we won't inherit the topic from narf.
-
-  $ hg topic
-  $ echo what >> alpha
-  $ hg topic query
-  $ hg ci -m 'what is narf, pinky?'
-  $ hg log -Gl2
-  @  changeset:   5:c01515cfc331
-  |  tag:         tip
-  |  topic:       query
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     what is narf, pinky?
-  |
-  o  changeset:   4:fb147b0b417c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on narf
-  |
-  $ hg push -f ../pinky -r query
-  pushing to ../pinky
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  $ hg -R ../pinky log -Gl 4
-  o  changeset:   7:c01515cfc331
-  |  tag:         tip
-  |  topic:       query
-  |  parent:      4:fb147b0b417c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     what is narf, pinky?
-  |
-  | @  changeset:   6:7c34953036d6
-  |/   topic:       narf
-  |    parent:      4:fb147b0b417c
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     narf!
-  |
-  | o  changeset:   5:0469d521db49
-  | |  topic:       fran
-  | |  parent:      3:a53952faf762
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     start on fran
-  | |
-  o |  changeset:   4:fb147b0b417c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     start on narf
-  |
-  $ hg topics
-   * query
-  $ cd ../pinky
-  $ hg co query
-  switching to topic query
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo answer >> alpha
-  $ hg ci -m 'Narf is like `zort` or `poit`!'
-  $ hg merge narf
-  merging alpha
-  warning: conflicts while merging alpha! (edit, then use 'hg resolve --mark')
-  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
-  [1]
-  $ hg revert -r narf alpha
-  $ hg resolve -m alpha
-  (no more unresolved files)
-  $ hg topic narf
-  $ hg ci -m 'Finish narf'
-  $ hg topics
-     fran
-   * narf
-     query
-  $ hg debugnamecomplete # branch:topic here is a buggy side effect
-  default
-  default:fran
-  default:narf
-  default:query
-  fran
-  narf
-  query
-  tip
-  $ hg phase --public narf
-
-POSSIBLE BUG: narf topic stays alive even though we just made all
-narf commits public:
-
-  $ hg topics
-     fran
-   * narf
-  $ hg log -Gl 6
-  @    changeset:   9:ae074045b7a7
-  |\   tag:         tip
-  | |  parent:      8:54c943c1c167
-  | |  parent:      6:7c34953036d6
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     Finish narf
-  | |
-  | o  changeset:   8:54c943c1c167
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     Narf is like `zort` or `poit`!
-  | |
-  | o  changeset:   7:c01515cfc331
-  | |  parent:      4:fb147b0b417c
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     what is narf, pinky?
-  | |
-  o |  changeset:   6:7c34953036d6
-  |/   parent:      4:fb147b0b417c
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     narf!
-  |
-  | o  changeset:   5:0469d521db49
-  | |  topic:       fran
-  | |  parent:      3:a53952faf762
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     start on fran
-  | |
-  o |  changeset:   4:fb147b0b417c
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     start on narf
-  |
-  $ cd ../brain
-  $ hg topics
-   * query
-  $ hg pull ../pinky -r narf
-  pulling from ../pinky
-  abort: unknown revision 'narf'!
-  [255]
-  $ hg pull ../pinky -r default
-  pulling from ../pinky
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 1 files
-  (run 'hg update' to get a working copy)
-  $ hg topics
-   * query
-
-We can pull in the draft-phase change and we get the new topic
-
-  $ hg pull ../pinky
-  pulling from ../pinky
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  (run 'hg heads' to see heads)
-  $ hg topics
-     fran
-   * query
-  $ hg log -Gr 'draft()'
-  o  changeset:   9:0469d521db49
-  |  tag:         tip
-  |  topic:       fran
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-
-query is not an open topic, so when we clear the current topic it'll
-disappear:
-
-  $ hg topics --clear
-  $ hg topics
-     fran
-
---clear when we don't have an active topic isn't an error:
-
-  $ hg topics --clear
-
-Topic revset
-  $ hg log -r 'topic()' -G
-  o  changeset:   9:0469d521db49
-  |  tag:         tip
-  |  topic:       fran
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-  $ hg log -r 'not topic()' -G
-  o    changeset:   8:ae074045b7a7
-  |\   parent:      7:54c943c1c167
-  | |  parent:      6:7c34953036d6
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     Finish narf
-  | |
-  | o  changeset:   7:54c943c1c167
-  | |  parent:      5:c01515cfc331
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     Narf is like `zort` or `poit`!
-  | |
-  o |  changeset:   6:7c34953036d6
-  | |  parent:      4:fb147b0b417c
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     narf!
-  | |
-  | @  changeset:   5:c01515cfc331
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     what is narf, pinky?
-  |
-  o  changeset:   4:fb147b0b417c
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on narf
-  |
-  o  changeset:   3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file delta
-  |
-  o  changeset:   2:15d1eb11d2fa
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file gamma
-  |
-  o  changeset:   1:c692ea2c9224
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     Add file beta
-  |
-  o  changeset:   0:c2b7d2f7d14b
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     Add file alpha
-  
-No matches because narf is already closed:
-  $ hg log -r 'topic(narf)' -G
-This regexp should match the topic `fran`:
-  $ hg log -r 'topic("re:.ra.")' -G
-  o  changeset:   9:0469d521db49
-  |  tag:         tip
-  |  topic:       fran
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-Exact match on fran:
-  $ hg log -r 'topic(fran)' -G
-  o  changeset:   9:0469d521db49
-  |  tag:         tip
-  |  topic:       fran
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-
-Match current topic:
-  $ hg topic
-     fran
-  $ hg log -r 'topic(.)'
-(no output is expected)
-  $ hg co fran
-  switching to topic fran
-  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log -r 'topic(.)'
-  changeset:   9:0469d521db49
-  tag:         tip
-  topic:       fran
-  parent:      3:a53952faf762
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     start on fran
-  
-
-Deactivate the topic.
-  $ hg topics
-   * fran
-  $ hg topics --clear
-  $ echo fran? >> beta
-  $ hg ci -m 'fran?'
-  created new head
-  $ hg log -Gr 'draft()'
-  @  changeset:   10:4073470c35e1
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     fran?
-  |
-  o  changeset:   9:0469d521db49
-  |  topic:       fran
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-  $ hg topics
-     fran
-Changing topic fails if we don't give a topic
-  $ hg topic --change 9
-  abort: changing topic requires a topic name or --clear
-  [255]
-
-Can't change topic of a public change
-  $ hg topic --change 1:: --clear
-  abort: can't change topic of a public change
-  [255]
-
-Can clear topics
-  $ hg topic --change 9 --clear
-  changed topic on 1 changes
-  please run hg evolve --rev "not topic()" now
-  $ hg log -Gr 'draft() and not obsolete()'
-  o  changeset:   11:783930e1d79e
-  |  tag:         tip
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-  | @  changeset:   10:4073470c35e1
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     fran?
-  | |
-
-Normally you'd do this with evolve, but we'll use rebase to avoid
-bonus deps in the testsuite.
-
-  $ hg rebase -d tip -s .
-  rebasing 10:4073470c35e1 "fran?"
-
-Can add a topic to an existing change
-  $ hg topic --change 11 wat
-  changed topic on 1 changes
-  please run hg evolve --rev "topic(wat)" now
-  $ hg log -Gr 'draft() and not obsolete()'
-  o  changeset:   13:d91cd8fd490e
-  |  tag:         tip
-  |  topic:       wat
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-  | @  changeset:   12:d9e32f4c4806
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     fran?
-  | |
-
-Normally you'd do this with evolve, but we'll use rebase to avoid
-bonus deps in the testsuite.
-
-  $ hg rebase -d tip -s .
-  rebasing 12:d9e32f4c4806 "fran?"
-
-  $ hg log -Gr 'draft()'
-  @  changeset:   14:cf24ad8bbef5
-  |  tag:         tip
-  |  topic:       wat
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     fran?
-  |
-  o  changeset:   13:d91cd8fd490e
-  |  topic:       wat
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-
-Amend a topic
-
-  $ hg topic watwat
-  $ hg ci --amend
-  $ hg log -Gr 'draft()'
-  @  changeset:   16:893ffcf66c1f
-  |  tag:         tip
-  |  topic:       watwat
-  |  parent:      13:d91cd8fd490e
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     fran?
-  |
-  o  changeset:   13:d91cd8fd490e
-  |  topic:       wat
-  |  parent:      3:a53952faf762
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     start on fran
-  |
-
-Clear and amend:
-
-  $ hg topic --clear
-  $ hg ci --amend
-  $ hg log -r .
-  changeset:   18:a13639e22b65
-  tag:         tip
-  parent:      13:d91cd8fd490e
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     fran?
-  
-Readding the same topic with topic --change should work:
-  $ hg topic --change . watwat
-  changed topic on 1 changes