branching: merge stable into default
authorPierre-Yves David <pierre-yves.david@octobus.net>
Sun, 25 Jun 2017 18:37:42 +0200
changeset 2660 a51970c07492
parent 2657 58719183d383 (diff)
parent 2659 166ca0aba0ea (current diff)
child 2661 914757c70217
branching: merge stable into default
README
--- a/README	Sun Jun 25 16:37:56 2017 +0200
+++ b/README	Sun Jun 25 18:37:42 2017 +0200
@@ -121,6 +121,18 @@
 Changelog
 =========
 
+6.5.0 - in progress
+-------------------
+
+ - obslog: gain a --patch flag to display changes introduced by the evolution
+  (Currently limited to in simple case only)
+
+ - stack: also show the unstable status for the current changeset (issue5553)
+ - stack: properly abort when and unknown topic is requested,
+ - topic: changing topic on revs no longer adds extra instability (issue5441)
+ - topic: topics: rename '--change' flag to '--rev' flag,
+ - topic: multiple large performance improvements,
+
 6.4.1 - in progress
 -------------------
 
--- a/hgext3rd/evolve/metadata.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/evolve/metadata.py	Sun Jun 25 18:37:42 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.4.1.dev'
+__version__ = '6.5.0.dev'
 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/obshistory.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/evolve/obshistory.py	Sun Jun 25 18:37:42 2017 +0200
@@ -14,6 +14,8 @@
     commands,
     error,
     graphmod,
+    mdiff,
+    patch,
     obsolete,
     node as nodemod,
     scmutil,
@@ -31,7 +33,8 @@
     'obslog|olog',
     [('G', 'graph', True, _("show the revision DAG")),
      ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
-     ('a', 'all', False, _('show all related changesets, not only precursors'))
+     ('a', 'all', False, _('show all related changesets, not only precursors')),
+     ('p', 'patch', False, _('show the patch between two obs versions'))
     ] + commands.formatteropts,
     _('hg olog [OPTION]... [REV]'))
 def cmdobshistory(ui, repo, *revs, **opts):
@@ -72,7 +75,7 @@
 
     fm = ui.formatter('debugobshistory', opts)
     revs.reverse()
-    _debugobshistorysingle(fm, repo, revs)
+    _debugobshistoryrevs(fm, repo, revs, opts)
 
     fm.end()
 
@@ -91,15 +94,17 @@
             fm = self.ui.formatter('debugobshistory', props)
             _debugobshistorydisplaynode(fm, self.repo, changenode)
 
+            # Succs markers
             succs = self.repo.obsstore.successors.get(changenode, ())
+            succs = sorted(succs)
 
             markerfm = fm.nested("debugobshistory.markers")
-            for successor in sorted(succs):
-                _debugobshistorydisplaymarker(markerfm, self.repo, successor)
+            for successor in succs:
+                _debugobshistorydisplaymarker(markerfm, successor,
+                                              ctx.node(), self.repo, self.diffopts)
             markerfm.end()
 
             markerfm.plain('\n')
-
             self.hunk[ctx.node()] = self.ui.popbuffer()
         else:
             ### graph output is buffered only
@@ -112,6 +117,67 @@
         '''
         pass
 
+def patchavailable(node, repo, marker):
+    if node not in repo:
+        return False, "context is not local"
+
+    successors = marker[1]
+
+    if len(successors) == 0:
+        return False, "no successors"
+    elif len(successors) > 1:
+        return False, "too many successors (%d)" % len(successors)
+
+    succ = successors[0]
+
+    if succ not in repo:
+        return False, "succ is unknown locally"
+
+    # Check that both node and succ have the same parents
+    nodep1, nodep2 = repo[node].p1(), repo[node].p2()
+    succp1, succp2 = repo[succ].p1(), repo[succ].p2()
+
+    if nodep1 != succp1 or nodep2 != succp2:
+        return False, "changesets rebased"
+
+    return True, succ
+
+def _indent(content, indent=4):
+    extra = ' ' * indent
+    return "".join(extra + line for line in content.splitlines(True))
+
+def getmarkercontentpatch(repo, node, succ):
+    # Todo get the ops from the cmd
+    diffopts = patch.diffallopts(repo.ui, {})
+    matchfn = scmutil.matchall(repo)
+
+    repo.ui.pushbuffer()
+    cmdutil.diffordiffstat(repo.ui, repo, diffopts, node, succ,
+                           match=matchfn, stat=False)
+    buffer = repo.ui.popbuffer()
+
+    return _indent(buffer)
+
+def getmarkerdescriptionpatch(repo, base, succ):
+    basectx = repo[base]
+    succctx = repo[succ]
+    # description are stored without final new line,
+    # add one to avoid ugly diff
+    basedesc = basectx.description() + '\n'
+    succdesc = succctx.description() + '\n'
+
+    # fake file name
+    basename = "%s-changeset-description" % basectx
+    succname = "%s-changeset-description" % succctx
+
+    d = mdiff.unidiff(basedesc, '', succdesc, '', basename, succname)
+    uheaders, hunks = d
+
+    # Copied from patch.diff
+    text = ''.join(sum((list(hlines) for hrange, hlines in hunks), []))
+    patch = "\n".join(uheaders + [text])
+    return _indent(patch)
+
 class missingchangectx(object):
     ''' a minimal object mimicking changectx for change contexts
     references by obs markers but not available locally '''
@@ -275,17 +341,22 @@
     return sorted(seen), nodesucc, nodeprec
 
 def _debugobshistorygraph(ui, repo, revs, opts):
-    displayer = obsmarker_printer(ui, repo.unfiltered(), None, opts, buffered=True)
+    matchfn = None
+    if opts.get('patch'):
+        matchfn = scmutil.matchall(repo)
+
+    displayer = obsmarker_printer(ui, repo.unfiltered(), matchfn, opts, buffered=True)
     edges = graphmod.asciiedges
     walker = _obshistorywalker(repo.unfiltered(), revs, opts.get('all', False))
     cmdutil.displaygraph(ui, repo, walker, displayer, edges)
 
-def _debugobshistorysingle(fm, repo, revs):
-    """ Display the obsolescence history for a single revision
+def _debugobshistoryrevs(fm, repo, revs, opts):
+    """ Display the obsolescence history for revset
     """
     precursors = repo.obsstore.precursors
     successors = repo.obsstore.successors
     nodec = repo.changelog.node
+    unfi = repo.unfiltered()
     nodes = [nodec(r) for r in revs]
 
     seen = set(nodes)
@@ -293,13 +364,13 @@
     while nodes:
         ctxnode = nodes.pop()
 
-        _debugobshistorydisplaynode(fm, repo, ctxnode)
+        _debugobshistorydisplaynode(fm, unfi, ctxnode)
 
         succs = successors.get(ctxnode, ())
 
         markerfm = fm.nested("debugobshistory.markers")
         for successor in sorted(succs):
-            _debugobshistorydisplaymarker(markerfm, repo, successor)
+            _debugobshistorydisplaymarker(markerfm, successor, ctxnode, repo, opts)
         markerfm.end()
 
         precs = precursors.get(ctxnode, ())
@@ -310,8 +381,8 @@
                 nodes.append(p[0])
 
 def _debugobshistorydisplaynode(fm, repo, node):
-    if node in repo.unfiltered():
-        _debugobshistorydisplayctx(fm, repo.unfiltered()[node])
+    if node in repo:
+        _debugobshistorydisplayctx(fm, repo[node])
     else:
         _debugobshistorydisplaymissingctx(fm, node)
 
@@ -338,7 +409,7 @@
              label="evolve.node evolve.missing_change_ctx")
     fm.plain('\n')
 
-def _debugobshistorydisplaymarker(fm, repo, marker):
+def _debugobshistorydisplaymarker(fm, marker, node, repo, opts):
     succnodes = marker[1]
     date = marker[4]
     metadata = dict(marker[3])
@@ -401,6 +472,30 @@
         fm.write('debugobshistory.succnodes', '%s', nodes,
                  label="evolve.node")
 
+    # Patch display
+    if opts.get('patch'):
+        _patchavailable = patchavailable(node, repo, marker)
+
+        if _patchavailable[0] is True:
+            succ = _patchavailable[1]
+
+            # Description patch
+            descriptionpatch = getmarkerdescriptionpatch(repo, node, succ)
+            if descriptionpatch:
+                fm.plain("\n")
+                fm.plain(descriptionpatch)
+
+            # Content patch
+            contentpatch = getmarkercontentpatch(repo, node, succ)
+            if contentpatch:
+                fm.plain("\n")
+                fm.plain(contentpatch)
+        else:
+            patch = "    (No patch available yet, %s)" % _patchavailable[1]
+            fm.plain("\n")
+            # TODO: should be in json too
+            fm.plain(patch)
+
     fm.plain("\n")
 
 # logic around storing and using effect flags
--- a/hgext3rd/topic/__init__.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/__init__.py	Sun Jun 25 18:37:42 2017 +0200
@@ -10,7 +10,7 @@
 Compared to bookmark, topic is reference carried by each changesets of the
 series instead of just the single head revision.  Topic are quite similar to
 the way named branch work, except they eventualy fade away when the changeset
-becomes part of the immutable history.  Changeset can below to both a topic and
+becomes part of the immutable history. Changeset can belong to both a topic and
 a named branch, but as long as it is mutable, its topic identity will prevail.
 As a result, default destination for 'update', 'merge', etc...  will take topic
 into account. When a topic is active these operations will only consider other
@@ -21,10 +21,11 @@
 There is currently two commands to be used with that extension: 'topics' and
 'stack'.
 
-The 'hg topics' command is used to set the current topic and list existing one.
-'hg topics --verbose' will list various information related to each topic.
+The 'hg topics' command is used to set the current topic, change and list
+existing one. 'hg topics --verbose' will list various information related to
+each topic.
 
-The 'stack' will show you in formation about the stack of commit belonging to
+The 'stack' will show you information about the stack of commit belonging to
 your current topic.
 
 Topic is offering you aliases reference to changeset in your current topic
@@ -55,12 +56,12 @@
 
 from mercurial.i18n import _
 from mercurial import (
-    branchmap,
     cmdutil,
     commands,
     context,
     error,
     extensions,
+    hg,
     localrepo,
     lock,
     merge,
@@ -114,7 +115,9 @@
 
 testedwith = '4.0.2 4.1.3 4.2'
 
-def _contexttopic(self):
+def _contexttopic(self, force=False):
+    if not (force or self.mutable()):
+        return ''
     return self.extra().get(constants.extrakey, '')
 context.basectx.topic = _contexttopic
 
@@ -135,8 +138,8 @@
         return [repo[r].node()]
     if name not in repo.topics:
         return []
-    return [ctx.node() for ctx in
-            repo.set('not public() and extra(topic, %s)', name)]
+    node = repo.changelog.node
+    return [node(rev) for rev in repo.revs('topic(%s)', name)]
 
 def _nodemap(repo, node):
     ctx = repo[node]
@@ -167,6 +170,8 @@
     if not isinstance(repo, localrepo.localrepository):
         return # this can be a peer in the ssh case (puzzling)
 
+    repo = repo.unfiltered()
+
     if repo.ui.config('experimental', 'thg.displaynames', None) is None:
         repo.ui.setconfig('experimental', 'thg.displaynames', 'topics',
                           source='topic-extension')
@@ -189,6 +194,11 @@
                 self.ui.restoreconfig(backup)
 
         def commitctx(self, ctx, error=None):
+            topicfilter = topicmap.topicfilter(self.filtername)
+            if topicfilter != self.filtername:
+                other = repo.filtered(topicmap.topicfilter(repo.filtername))
+                other.commitctx(ctx, error=error)
+
             if isinstance(ctx, context.workingcommitctx):
                 current = self.currenttopic
                 if current:
@@ -199,8 +209,7 @@
                 not self.currenttopic):
                 # we are amending and need to remove a topic
                 del ctx.extra()[constants.extrakey]
-            with topicmap.usetopicmap(self):
-                return super(topicrepo, self).commitctx(ctx, error=error)
+            return super(topicrepo, self).commitctx(ctx, error=error)
 
         @property
         def topics(self):
@@ -217,23 +226,21 @@
         def currenttopic(self):
             return self.vfs.tryread('topic')
 
-        def branchmap(self, topic=True):
-            if not topic:
-                super(topicrepo, self).branchmap()
-            with topicmap.usetopicmap(self):
-                branchmap.updatecache(self)
-            return self._topiccaches[self.filtername]
+        # overwritten at the instance level by topicmap.py
+        _autobranchmaptopic = True
 
-        def destroyed(self, *args, **kwargs):
-            with topicmap.usetopicmap(self):
-                return super(topicrepo, self).destroyed(*args, **kwargs)
+        def branchmap(self, topic=None):
+            if topic is None:
+                topic = getattr(repo, '_autobranchmaptopic', False)
+            topicfilter = topicmap.topicfilter(self.filtername)
+            if not topic or topicfilter == self.filtername:
+                return super(topicrepo, self).branchmap()
+            return self.filtered(topicfilter).branchmap()
 
         def invalidatevolatilesets(self):
             # XXX we might be able to move this to something invalidated less often
             super(topicrepo, self).invalidatevolatilesets()
             self._topics = None
-            if '_topiccaches' in vars(self.unfiltered()):
-                self.unfiltered()._topiccaches.clear()
 
         def peer(self):
             peer = super(topicrepo, self).peer()
@@ -254,83 +261,32 @@
 
 @command('topics [TOPIC]', [
         ('', 'clear', False, 'clear active topic if any'),
-        ('', 'change', '', 'revset of existing revisions to change topic'),
+        ('r', 'rev', '', 'revset of existing revisions', _('REV')),
         ('l', 'list', False, 'show the stack of changeset in the topic'),
     ] + commands.formatteropts)
-def topics(ui, repo, topic='', clear=False, change=None, list=False, **opts):
+def topics(ui, repo, topic='', clear=False, rev=None, list=False, **opts):
     """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."""
     if list:
-        if clear or change:
-            raise error.Abort(_("cannot use --clear or --change with --list"))
+        if clear or rev:
+            raise error.Abort(_("cannot use --clear or --rev with --list"))
         if not topic:
             topic = repo.currenttopic
         if not topic:
             raise error.Abort(_('no active topic to list'))
         return stack.showstack(ui, repo, topic, opts)
 
-    if change:
+    if rev:
         if not obsolete.isenabled(repo, obsolete.createmarkersopt):
-            raise error.Abort(_('must have obsolete enabled to use --change'))
-        if not topic and not clear:
+            raise error.Abort(_('must have obsolete enabled to change topics'))
+        if clear:
+            topic = None
+        elif not topic:
             raise error.Abort('changing topic requires a topic name or --clear')
-        if any(not c.mutable() for c in repo.set('%r and public()', change)):
+        if any(not c.mutable() for c in repo.set('%r and public()', rev)):
             raise error.Abort("can't change topic of a public change")
-        rewrote = 0
-        needevolve = False
-        l = repo.lock()
-        txn = repo.transaction('rewrite-topics')
-        try:
-            for c in repo.set('%r', change):
-                def filectxfn(repo, ctx, path):
-                    try:
-                        return c[path]
-                    except error.ManifestLookupError:
-                        return None
-                fixedextra = dict(c.extra())
-                ui.debug('old node id is %s\n' % node.hex(c.node()))
-                ui.debug('origextra: %r\n' % fixedextra)
-                newtopic = None if clear else topic
-                oldtopic = fixedextra.get(constants.extrakey, None)
-                if oldtopic == newtopic:
-                    continue
-                if clear:
-                    del fixedextra[constants.extrakey]
-                else:
-                    fixedextra[constants.extrakey] = topic
-                if 'amend_source' in fixedextra:
-                    # TODO: right now the commitctx wrapper in
-                    # topicrepo overwrites the topic in extra if
-                    # amend_source is set to support 'hg commit
-                    # --amend'. Support for amend should be adjusted
-                    # to not be so invasive.
-                    del fixedextra['amend_source']
-                ui.debug('changing topic of %s from %s to %s\n' % (
-                    c, oldtopic, newtopic))
-                ui.debug('fixedextra: %r\n' % fixedextra)
-                mc = context.memctx(
-                    repo, (c.p1().node(), c.p2().node()), c.description(),
-                    c.files(), filectxfn,
-                    user=c.user(), date=c.date(), extra=fixedextra)
-                newnode = repo.commitctx(mc)
-                ui.debug('new node id is %s\n' % node.hex(newnode))
-                needevolve = needevolve or (len(c.children()) > 0)
-                obsolete.createmarkers(repo, [(c, (repo[newnode],))])
-                rewrote += 1
-            txn.close()
-        except:
-            try:
-                txn.abort()
-            finally:
-                repo.invalidate()
-            raise
-        finally:
-            lock.release(txn, l)
-        ui.status('changed topic on %d changes\n' % rewrote)
-        if needevolve:
-            evolvetarget = 'topic(%s)' % topic if topic else 'not topic()'
-            ui.status('please run hg evolve --rev "%s" now\n' % evolvetarget)
+        _changetopics(ui, repo, rev, topic)
     if clear:
         if repo.vfs.exists('topic'):
             repo.vfs.unlink('topic')
@@ -353,6 +309,82 @@
         raise error.Abort(_('no active topic to list'))
     return stack.showstack(ui, repo, topic, opts)
 
+def _changetopics(ui, repo, revset, newtopic):
+    rewrote = 0
+    needevolve = False
+    wl = l = txn = None
+    try:
+        wl = repo.wlock()
+        l = repo.lock()
+        txn = repo.transaction('rewrite-topics')
+        p1 = None
+        p2 = None
+        successors = {}
+        for c in repo.set('%r', revset):
+            def filectxfn(repo, ctx, path):
+                try:
+                    return c[path]
+                except error.ManifestLookupError:
+                    return None
+            fixedextra = dict(c.extra())
+            ui.debug('old node id is %s\n' % node.hex(c.node()))
+            ui.debug('origextra: %r\n' % fixedextra)
+            oldtopic = fixedextra.get(constants.extrakey, None)
+            if oldtopic == newtopic:
+                continue
+            if newtopic is None:
+                del fixedextra[constants.extrakey]
+            else:
+                fixedextra[constants.extrakey] = newtopic
+            fixedextra[constants.changekey] = c.hex()
+            if 'amend_source' in fixedextra:
+                # TODO: right now the commitctx wrapper in
+                # topicrepo overwrites the topic in extra if
+                # amend_source is set to support 'hg commit
+                # --amend'. Support for amend should be adjusted
+                # to not be so invasive.
+                del fixedextra['amend_source']
+            ui.debug('changing topic of %s from %s to %s\n' % (
+                c, oldtopic, newtopic))
+            ui.debug('fixedextra: %r\n' % fixedextra)
+            # While changing topic of set of linear commits, make sure that
+            # we base our commits on new parent rather than old parent which
+            # was obsoleted while changing the topic
+            p1 = c.p1().node()
+            p2 = c.p2().node()
+            if p1 in successors:
+                p1 = successors[p1]
+            if p2 in successors:
+                p2 = successors[p2]
+            mc = context.memctx(
+                repo, (p1, p2), c.description(),
+                c.files(), filectxfn,
+                user=c.user(), date=c.date(), extra=fixedextra)
+            newnode = repo.commitctx(mc)
+            successors[c.node()] = newnode
+            ui.debug('new node id is %s\n' % node.hex(newnode))
+            needevolve = needevolve or (len(c.children()) > 0)
+            obsolete.createmarkers(repo, [(c, (repo[newnode],))])
+            rewrote += 1
+        # move the working copy too
+        wctx = repo[None]
+        # in-progress merge is a bit too complexe for now.
+        if len(wctx.parents()) == 1:
+            newid = successors.get(wctx.p1().node())
+            if newid is not None:
+                # this provide some useless output we shoudl eventually suppress
+                #
+                # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+                hg.update(repo, newid)
+        txn.close()
+    finally:
+        lock.release(txn, l, wl)
+        repo.invalidate()
+    ui.status('changed topic on %d changes\n' % rewrote)
+    if needevolve:
+        evolvetarget = 'topic(%s)' % newtopic if newtopic else 'not topic()'
+        ui.status('please run hg evolve --rev "%s" now\n' % evolvetarget)
+
 def _listtopics(ui, repo, opts):
     fm = ui.formatter('bookmarks', opts)
     activetopic = repo.currenttopic
--- a/hgext3rd/topic/constants.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/constants.py	Sun Jun 25 18:37:42 2017 +0200
@@ -1,1 +1,2 @@
 extrakey = 'topic'
+changekey = '_rewrite_noise'
--- a/hgext3rd/topic/destination.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/destination.py	Sun Jun 25 18:37:42 2017 +0200
@@ -89,14 +89,14 @@
     # but that is expensive
     #
     # we should write plain code instead
-    with topicmap.usetopicmap(repo):
-        tmap = repo.branchmap()
-        if branch not in tmap:
-            return []
-        elif all:
-            return tmap.branchheads(branch)
-        else:
-            return [tmap.branchtip(branch)]
+
+    tmap = topicmap.gettopicrepo(repo).branchmap()
+    if branch not in tmap:
+        return []
+    elif all:
+        return tmap.branchheads(branch)
+    else:
+        return [tmap.branchtip(branch)]
 
 def modsetup(ui):
     """run a uisetup time to install all destinations wrapping"""
--- a/hgext3rd/topic/discovery.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/discovery.py	Sun Jun 25 18:37:42 2017 +0200
@@ -4,7 +4,6 @@
 
 from mercurial.i18n import _
 from mercurial import (
-    branchmap,
     bundle2,
     discovery,
     error,
@@ -13,15 +12,15 @@
     wireproto,
 )
 
-from . import topicmap
-
 def _headssummary(orig, *args):
     # In mercurial < 4.2, we receive repo, remote and outgoing as arguments
     if len(args) == 3:
+        pushoparg = False
         repo, remote, outgoing = args
 
     # In mercurial > 4.3, we receive the pushop as arguments
     elif len(args) == 1:
+        pushoparg = True
         pushop = args[0]
         repo = pushop.repo.unfiltered()
         remote = pushop.remote
@@ -33,38 +32,44 @@
                   or bool(remote.listkeys('phases').get('publishing', False)))
     if publishing or not remote.capable('topics'):
         return orig(*args)
-    oldrepo = repo.__class__
-    oldbranchcache = branchmap.branchcache
-    oldfilename = branchmap._filename
-    try:
-        class repocls(repo.__class__):
-            def __getitem__(self, key):
-                ctx = super(repocls, self).__getitem__(key)
-                oldbranch = ctx.branch
+
+    class repocls(repo.__class__):
+        def __getitem__(self, key):
+            ctx = super(repocls, self).__getitem__(key)
+            oldbranch = ctx.branch
+
+            def branch():
+                branch = oldbranch()
+                topic = ctx.topic()
+                if topic:
+                    branch = "%s:%s" % (branch, topic)
+                return branch
 
-                def branch():
-                    branch = oldbranch()
-                    topic = ctx.topic()
-                    if topic:
-                        branch = "%s:%s" % (branch, topic)
-                    return branch
+            ctx.branch = branch
+            return ctx
 
-                ctx.branch = branch
-                return ctx
-
+    oldrepo = repo.__class__
+    try:
         repo.__class__ = repocls
-        branchmap.branchcache = topicmap.topiccache
-        branchmap._filename = topicmap._filename
-        summary = orig(*args)
+        unxx = repo.filtered('unfiltered-topic')
+        repo.unfiltered = lambda: unxx
+        if pushoparg:
+            try:
+                pushop.repo = repo
+                summary = orig(pushop)
+            finally:
+                pushop.repo = repo
+        else:
+            summary = orig(repo, remote, outgoing)
         for key, value in summary.iteritems():
             if ':' in key: # This is a topic
                 if value[0] is None and value[1]:
                     summary[key] = ([value[1].pop(0)], ) + value[1:]
         return summary
     finally:
+        if 'unfiltered' in vars(repo):
+            del repo.unfiltered
         repo.__class__ = oldrepo
-        branchmap.branchcache = oldbranchcache
-        branchmap._filename = oldfilename
 
 def wireprotobranchmap(orig, repo, proto):
     oldrepo = repo.__class__
--- a/hgext3rd/topic/revset.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/revset.py	Sun Jun 25 18:37:42 2017 +0200
@@ -3,6 +3,7 @@
 from mercurial.i18n import _
 from mercurial import (
     error,
+    phases,
     revset,
     util,
 )
@@ -37,9 +38,17 @@
         _kind, _pattern, matcher = mkmatcher(topic)
     else:
         matcher = lambda t: bool(t)
-    drafts = subset.filter(lambda r: repo[r].mutable())
-    return drafts.filter(
-        lambda r: matcher(repo[r].extra().get(constants.extrakey, '')))
+    mutable = repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
+
+    rawchange = repo.changelog.changelogrevision
+    key = constants.extrakey
+
+    def matchtopic(r):
+        topic = rawchange(r).extra.get(key)
+        if topic is None:
+            return False
+        return matcher(topic)
+    return (subset & mutable).filter(matchtopic)
 
 def ngtipset(repo, subset, x):
     """`ngtip([branch])`
--- a/hgext3rd/topic/stack.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/stack.py	Sun Jun 25 18:37:42 2017 +0200
@@ -22,6 +22,10 @@
     return ' '.join(prefix % suffix for suffix in labelssuffix)
 
 def showstack(ui, repo, topic, opts):
+
+    if topic not in repo.topics:
+        raise error.Abort(_('cannot resolve "%s": no such topic found') % topic)
+
     fm = ui.formatter('topicstack', opts)
     prev = None
     entries = []
@@ -69,19 +73,22 @@
         states = []
         iscurrentrevision = repo.revs('%d and parents()', ctx.rev())
 
-        if iscurrentrevision:
-            states.append('current')
-
         if not isentry:
             symbol = '^'
             # "base" is kind of a "ghost" entry
             # skip other label for them (no current, no unstable)
             states = ['base']
-        elif iscurrentrevision:
-            symbol = '@'
-        elif repo.revs('%d and unstable()', ctx.rev()):
+        elif ctx.unstable():
+            # current revision can be unstable also, so in that case show both
+            # the states and the symbol '@' (issue5553)
+            if iscurrentrevision:
+                states.append('current')
+                symbol = '@'
             symbol = '$'
             states.append('unstable')
+        elif iscurrentrevision:
+            states.append('current')
+            symbol = '@'
         else:
             symbol = ':'
             states.append('clean')
--- a/hgext3rd/topic/topicmap.py	Sun Jun 25 16:37:56 2017 +0200
+++ b/hgext3rd/topic/topicmap.py	Sun Jun 25 18:37:42 2017 +0200
@@ -1,27 +1,63 @@
 import contextlib
 import hashlib
 
-from mercurial.node import hex, bin, nullid
+from mercurial.node import nullid
 from mercurial import (
     branchmap,
     changegroup,
     cmdutil,
-    encoding,
-    error,
     extensions,
-    scmutil,
+    repoview,
 )
 
-def _filename(repo):
-    """name of a branchcache file for a given repo or repoview"""
-    filename = "cache/topicmap"
-    if repo.filtername:
-        filename = '%s-%s' % (filename, repo.filtername)
-    return filename
+basefilter = set(['base', 'immutable'])
+def topicfilter(name):
+    """return a "topic" version of a filter level"""
+    if name in basefilter:
+        return name
+    elif name is None:
+        return None
+    elif name.endswith('-topic'):
+        return name
+    else:
+        return name + '-topic'
+
+def istopicfilter(filtername):
+    if filtername is None:
+        return False
+    return filtername.endswith('-topic')
+
+def gettopicrepo(repo):
+    filtername = topicfilter(repo.filtername)
+    if filtername == repo.filtername:
+        return repo
+    return repo.filtered(filtername)
 
-oldbranchcache = branchmap.branchcache
+def _setuptopicfilter(ui):
+    """extend the filter related mapping with topic related one"""
+    funcmap = repoview.filtertable
+    partialmap = branchmap.subsettable
+
+    # filter level not affected by topic that we should not override
+
+    for plainname in list(funcmap):
+        newfilter = topicfilter(plainname)
+        if newfilter == plainname:
+            continue
+
+        def revsfunc(repo, name=plainname):
+            return repoview.filterrevs(repo, name)
+
+        base = topicfilter(partialmap[plainname])
+
+        if newfilter not in funcmap:
+            funcmap[newfilter] = revsfunc
+            partialmap[newfilter] = base
+    funcmap['unfiltered-topic'] = lambda repo: frozenset()
+    partialmap['unfiltered-topic'] = 'visible-topic'
 
 def _phaseshash(repo, maxrev):
+    """uniq ID for a phase matching a set of rev"""
     revs = set()
     cl = repo.changelog
     fr = cl.filteredrevs
@@ -40,61 +76,65 @@
         key = s.digest()
     return key
 
-@contextlib.contextmanager
-def usetopicmap(repo):
-    """use awful monkey patching to ensure topic map usage
+def modsetup(ui):
+    """call at uisetup time to install various wrappings"""
+    _setuptopicfilter(ui)
+    _wrapbmcache(ui)
+    extensions.wrapfunction(changegroup.cg1unpacker, 'apply', cgapply)
+    extensions.wrapfunction(cmdutil, 'commitstatus', commitstatus)
 
-    During the extend of the context block, The topicmap should be used and
-    updated instead of the branchmap."""
-    oldbranchcache = branchmap.branchcache
-    oldfilename = branchmap._filename
-    oldread = branchmap.read
-    oldcaches = getattr(repo, '_branchcaches', {})
-    try:
-        branchmap.branchcache = topiccache
-        branchmap._filename = _filename
-        branchmap.read = readtopicmap
-        repo._branchcaches = getattr(repo, '_topiccaches', {})
-        yield
-        repo._topiccaches = repo._branchcaches
-    finally:
-        repo._branchcaches = oldcaches
-        branchmap.branchcache = oldbranchcache
-        branchmap._filename = oldfilename
-        branchmap.read = oldread
-
-def cgapply(orig, repo, *args, **kwargs):
+def cgapply(orig, self, repo, *args, **kwargs):
     """make sure a topicmap is used when applying a changegroup"""
-    with usetopicmap(repo):
-        return orig(repo, *args, **kwargs)
+    other = repo.filtered(topicfilter(repo.filtername))
+    return orig(self, other, *args, **kwargs)
 
 def commitstatus(orig, repo, node, branch, bheads=None, opts=None):
     # wrap commit status use the topic branch heads
     ctx = repo[node]
     if ctx.topic() and ctx.branch() == branch:
-        bheads = repo.branchheads("%s:%s" % (branch, ctx.topic()))
+        subbranch = "%s:%s" % (branch, ctx.topic())
+        bheads = repo.branchheads("%s:%s" % (subbranch, ctx.topic()))
     return orig(repo, node, branch, bheads=bheads, opts=opts)
 
-class topiccache(oldbranchcache):
+def _wrapbmcache(ui):
+    class topiccache(_topiccache, branchmap.branchcache):
+        pass
+    branchmap.branchcache = topiccache
+    extensions.wrapfunction(branchmap, 'updatecache', _wrapupdatebmcache)
+
+def _wrapupdatebmcache(orig, repo):
+    previous = getattr(repo, '_autobranchmaptopic', False)
+    try:
+        repo._autobranchmaptopic = False
+        return orig(repo)
+    finally:
+        repo._autobranchmaptopic = previous
+
+# needed to prevent reference used for 'super()' call using in branchmap.py to
+# no go into cycle. (yes, URG)
+_oldbranchmap = branchmap.branchcache
+
+@contextlib.contextmanager
+def oldbranchmap():
+    previous = branchmap.branchcache
+    try:
+        branchmap.branchcache = _oldbranchmap
+        yield
+    finally:
+        branchmap.branchcache = previous
+
+class _topiccache(object): # combine me with branchmap.branchcache
 
     def __init__(self, *args, **kwargs):
-        otherbranchcache = branchmap.branchcache
-        try:
-            # super() call may fail otherwise
-            branchmap.branchcache = oldbranchcache
-            super(topiccache, self).__init__(*args, **kwargs)
-            if self.filteredhash is None:
-                self.filteredhash = nullid
-            self.phaseshash = nullid
-        finally:
-            branchmap.branchcache = otherbranchcache
+        # super() call may fail otherwise
+        with oldbranchmap():
+            super(_topiccache, self).__init__(*args, **kwargs)
+        self.phaseshash = None
 
     def copy(self):
         """return an deep copy of the branchcache object"""
-        new = topiccache(self, self.tipnode, self.tiprev, self.filteredhash,
-                         self._closednodes)
-        if self.filteredhash is None:
-            self.filteredhash = nullid
+        new = self.__class__(self, self.tipnode, self.tiprev, self.filteredhash,
+                             self._closednodes)
         new.phaseshash = self.phaseshash
         return new
 
@@ -104,144 +144,61 @@
         Raise KeyError for unknown branch.'''
         if topic:
             branch = '%s:%s' % (branch, topic)
-        return super(topiccache, self).branchtip(branch)
+        return super(_topiccache, self).branchtip(branch)
 
     def branchheads(self, branch, closed=False, topic=''):
         if topic:
             branch = '%s:%s' % (branch, topic)
-        return super(topiccache, self).branchheads(branch, closed=closed)
+        return super(_topiccache, self).branchheads(branch, closed=closed)
 
     def validfor(self, repo):
         """Is the cache content valid regarding a repo
 
         - False when cached tipnode is unknown or if we detect a strip.
         - True when cache is up to date or a subset of current repo."""
-        # This is copy paste of mercurial.branchmap.branchcache.validfor in
-        # 69077c65919d With a small changes to the cache key handling to
-        # include phase information that impact the topic cache.
-        #
-        # All code changes should be flagged on site.
-        try:
-            if (self.tipnode == repo.changelog.node(self.tiprev)):
-                fh = scmutil.filteredhash(repo, self.tiprev)
-                if fh is None:
-                    fh = nullid
-                if ((self.filteredhash == fh)
-                    and (self.phaseshash == _phaseshash(repo, self.tiprev))):
-                    return True
+        valid = super(_topiccache, self).validfor(repo)
+        if not valid:
             return False
-        except IndexError:
-            return False
+        elif not istopicfilter(repo.filtername) or self.phaseshash is None:
+            # phasehash at None means this is a branchmap
+            # come from non topic thing
+            return True
+        else:
+            try:
+                valid = self.phaseshash == _phaseshash(repo, self.tiprev)
+                return valid
+            except IndexError:
+                return False
 
     def write(self, repo):
-        # This is copy paste of mercurial.branchmap.branchcache.write in
-        # 69077c65919d With a small changes to the cache key handling to
-        # include phase information that impact the topic cache.
-        #
-        # All code changes should be flagged on site.
-        try:
-            f = repo.vfs(_filename(repo), "w", atomictemp=True)
-            cachekey = [hex(self.tipnode), str(self.tiprev)]
-            # [CHANGE] we need a hash in all cases
-            assert self.filteredhash is not None
-            cachekey.append(hex(self.filteredhash))
-            cachekey.append(hex(self.phaseshash))
-            f.write(" ".join(cachekey) + '\n')
-            nodecount = 0
-            for label, nodes in sorted(self.iteritems()):
-                for node in nodes:
-                    nodecount += 1
-                    if node in self._closednodes:
-                        state = 'c'
-                    else:
-                        state = 'o'
-                    f.write("%s %s %s\n" % (hex(node), state,
-                                            encoding.fromlocal(label)))
-            f.close()
-            repo.ui.log('branchcache',
-                        'wrote %s branch cache with %d labels and %d nodes\n',
-                        repo.filtername, len(self), nodecount)
-        except (IOError, OSError, error.Abort) as inst:
-            repo.ui.debug("couldn't write branch cache: %s\n" % inst)
-            # Abort may be raise by read only opener
-            pass
+        # we expect mutable set to be small enough to be that computing it all
+        # the time will be fast enough
+        if not istopicfilter(repo.filtername):
+            super(_topiccache, self).write(repo)
 
     def update(self, repo, revgen):
         """Given a branchhead cache, self, that may have extra nodes or be
         missing heads, and a generator of nodes that are strictly a superset of
         heads missing, this function updates self to be correct.
         """
-        oldgetbranchinfo = repo.revbranchcache().branchinfo
+        if not istopicfilter(repo.filtername):
+            return super(_topiccache, self).update(repo, revgen)
+        unfi = repo.unfiltered()
+        oldgetbranchinfo = unfi.revbranchcache().branchinfo
+
+        def branchinfo(r):
+            info = oldgetbranchinfo(r)
+            topic = ''
+            ctx = unfi[r]
+            if ctx.mutable():
+                topic = ctx.topic()
+            branch = info[0]
+            if topic:
+                branch = '%s:%s' % (branch, topic)
+            return (branch, info[1])
         try:
-            def branchinfo(r):
-                info = oldgetbranchinfo(r)
-                topic = ''
-                ctx = repo[r]
-                if ctx.mutable():
-                    topic = ctx.topic()
-                branch = info[0]
-                if topic:
-                    branch = '%s:%s' % (branch, topic)
-                return (branch, info[1])
-            repo.revbranchcache().branchinfo = branchinfo
-            super(topiccache, self).update(repo, revgen)
-            if self.filteredhash is None:
-                self.filteredhash = nullid
+            unfi.revbranchcache().branchinfo = branchinfo
+            super(_topiccache, self).update(repo, revgen)
             self.phaseshash = _phaseshash(repo, self.tiprev)
         finally:
-            repo.revbranchcache().branchinfo = oldgetbranchinfo
-
-def readtopicmap(repo):
-    # This is copy paste of mercurial.branchmap.read in 69077c65919d
-    # With a small changes to the cache key handling to include phase
-    # information that impact the topic cache.
-    #
-    # All code changes should be flagged on site.
-    try:
-        f = repo.vfs(_filename(repo))
-        lines = f.read().split('\n')
-        f.close()
-    except (IOError, OSError):
-        return None
-
-    try:
-        cachekey = lines.pop(0).split(" ", 2)
-        last, lrev = cachekey[:2]
-        last, lrev = bin(last), int(lrev)
-        filteredhash = bin(cachekey[2]) # [CHANGE] unconditional filteredhash
-        partial = topiccache(tipnode=last, tiprev=lrev,
-                             filteredhash=filteredhash)
-        partial.phaseshash = bin(cachekey[3]) # [CHANGE] read phaseshash
-        if not partial.validfor(repo):
-            # invalidate the cache
-            raise ValueError('tip differs')
-        cl = repo.changelog
-        for l in lines:
-            if not l:
-                continue
-            node, state, label = l.split(" ", 2)
-            if state not in 'oc':
-                raise ValueError('invalid branch state')
-            label = encoding.tolocal(label.strip())
-            node = bin(node)
-            if not cl.hasnode(node):
-                raise ValueError('node %s does not exist' % hex(node))
-            partial.setdefault(label, []).append(node)
-            if state == 'c':
-                partial._closednodes.add(node)
-    except KeyboardInterrupt:
-        raise
-    except Exception as inst:
-        if repo.ui.debugflag:
-            msg = 'invalid branchheads cache'
-            if repo.filtername is not None:
-                msg += ' (%s)' % repo.filtername
-            msg += ': %s\n'
-            repo.ui.debug(msg % inst)
-        partial = None
-    return partial
-
-def modsetup(ui):
-    """call at uisetup time to install various wrappings"""
-    extensions.wrapfunction(changegroup.cg1unpacker, 'apply', cgapply)
-    extensions.wrapfunction(cmdutil, 'commitstatus', commitstatus)
+            unfi.revbranchcache().branchinfo = oldgetbranchinfo
--- a/tests/test-discovery-obshashrange.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-discovery-obshashrange.t	Sun Jun 25 18:37:42 2017 +0200
@@ -34,23 +34,9 @@
   * @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 evo-ext-obshashrange in *.???? seconds (8r, 0o) (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)
@@ -166,10 +152,10 @@
   $ 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 base branch cache in *.???? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> wrote base branch cache with 1 labels and 1 nodes (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)
@@ -206,7 +192,7 @@
   taking quick initial sample
   query 2; still undecided: 5, sample size is: 5
   sending known command
-  2 total queries
+  2 total queries in *.????s (glob)
   preparing listkeys for "phases"
   sending listkeys command
   received listkey for "phases": 58 bytes
@@ -238,7 +224,7 @@
   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-bundle: no-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
@@ -248,12 +234,12 @@
   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 stablerange cache in *.???? seconds (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> obscache is out of date, falling back to slower obsstore version (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 (*)> updated evo-ext-obscache in *.???? seconds (1r, 1o) (glob)
   * @0000000000000000000000000000000000000000 (*)> 1 incoming changes - new heads: 45f8b879de92 (glob)
   * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> blackbox (glob)
@@ -310,10 +296,11 @@
   * @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 (*)> obscache is out of date, falling back to slower obsstore version (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated served branch cache in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (1r, 0o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> commit -m foo exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete ffffffffffffffffffffffffffffffffffffffff 45f8b879de922f6a6e620ba04205730335b6fc7e (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
@@ -322,6 +309,7 @@
   * @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 (*)> found 1 common and 1 unknown server heads, 2 roundtrips in *.????s (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)
@@ -342,7 +330,7 @@
   * @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 (*)> push exited 1 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
   $ rm .hg/blackbox.log
   $ hg debugobsolete | sort
@@ -421,13 +409,12 @@
   * @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 (*)> updated served branch cache in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (2r, 3o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 2 incoming changes - new heads: f69452c5b1af (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r 6 exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
@@ -567,17 +554,18 @@
   * @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 stablerange cache in *.???? seconds (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (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 (*)> updated evo-ext-obscache in *.???? seconds (1r, 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 (*)> strip detected, evo-ext-obscache cache reset (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (8r, 12o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> rollback exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
   $ rm .hg/blackbox.log
@@ -617,11 +605,9 @@
   * @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 (*)> updated evo-ext-obscache in *.???? seconds (1r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 1 incoming changes - new heads: 4de32a90b66c (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
@@ -695,10 +681,12 @@
   pulling from ssh://user@dummy/server
   searching for changes
   OBSEXC: looking for common markers in 8 nodes
+  OBSEXC: request obsmarkers for 1 common nodes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
+  2 new obsolescence markers
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg log -G
   o  8 45f8b879de92 foo tip
@@ -728,9 +716,9 @@
   * @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 (*)> updated evo-ext-obshashrange in *.???? seconds (5r, 11o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obscache cache reset (glob)
-  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (5r, 13o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (5r, 11o) (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)
@@ -742,12 +730,13 @@
   * @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 (*)> obsdiscovery, 1/8 mismatch - 1 obshashrange queries in *.???? seconds (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 (*)> obshashcache reset - new markers affect cached ranges (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 2o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 2 nodes (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (1r, 2o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> 1 incoming changes - new heads: 45f8b879de92 (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> pull exited 0 after *.?? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G (glob)
--- a/tests/test-drop.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-drop.t	Sun Jun 25 18:37:42 2017 +0200
@@ -263,4 +263,3 @@
      summary:     add base
   
   ============ obsmark ============
-  87ea30a976fdf235bf096f04899cb02a903873e2 0 {34b6c051bf1f78db6aef400776de5cb964470207} (*) {'ef1': '0', 'user': 'test'} (glob)
--- a/tests/test-evolve-obshistory.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-evolve-obshistory.t	Sun Jun 25 18:37:42 2017 +0200
@@ -55,11 +55,26 @@
   
 Actual test
 -----------
-  $ hg obslog 4ae3a4151de9
+  $ hg obslog --patch 4ae3a4151de9
   @  4ae3a4151de9 (3) A1
   |
   x  471f378eab4c (1) A0
-       rewritten(description, content) by test (*) as 4ae3a4151de9 (glob)
+       rewritten(description, content) by test (Thu Jan 01 00:00:00 1970 +0000) as 4ae3a4151de9
+         --- a/471f378eab4c-changeset-description
+         +++ b/4ae3a4151de9-changeset-description
+         @@ -1,1 +1,3 @@
+         -A0
+         +A1
+         +
+         +Better commit message
+  
+         diff -r 471f378eab4c -r 4ae3a4151de9 A0
+         --- a/A0	Thu Jan 01 00:00:00 1970 +0000
+         +++ b/A0	Thu Jan 01 00:00:00 1970 +0000
+         @@ -1,1 +1,2 @@
+          A0
+         +42
+  
   
   $ hg obslog 4ae3a4151de9 --no-graph -Tjson | python -m json.tool
   [
@@ -92,9 +107,24 @@
           "debugobshistory.shortdescription": "A0"
       }
   ]
-  $ hg obslog --hidden 471f378eab4c
+  $ hg obslog --hidden --patch 471f378eab4c
   x  471f378eab4c (1) A0
        rewritten(description, content) by test (*) as 4ae3a4151de9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/4ae3a4151de9-changeset-description
+         @@ -1,1 +1,3 @@
+         -A0
+         +A1
+         +
+         +Better commit message
+  
+         diff -r 471f378eab4c -r 4ae3a4151de9 A0
+         --- a/A0	Thu Jan 01 00:00:00 1970 +0000
+         +++ b/A0	Thu Jan 01 00:00:00 1970 +0000
+         @@ -1,1 +1,2 @@
+          A0
+         +42
+  
   
   $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | python -m json.tool
   [
@@ -183,9 +213,10 @@
 Actual test
 -----------
 
-  $ hg obslog 'desc(B0)' --hidden
+  $ hg obslog 'desc(B0)' --hidden --patch
   x  0dec01379d3b (2) B0
        pruned by test (*) (glob)
+         (No patch available yet, no successors)
   
   $ hg obslog 'desc(B0)' --hidden --no-graph -Tjson | python -m json.tool
   [
@@ -205,7 +236,7 @@
           "debugobshistory.shortdescription": "B0"
       }
   ]
-  $ hg obslog 'desc(A0)'
+  $ hg obslog 'desc(A0)' --patch
   @  471f378eab4c (1) A0
   
   $ hg obslog 'desc(A0)' --no-graph -Tjson | python -m json.tool
@@ -316,9 +347,10 @@
 -----------
 
 Check that debugobshistory on splitted commit show both targets
-  $ hg obslog 471597cad322 --hidden
+  $ hg obslog 471597cad322 --hidden --patch
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
   $ hg obslog 471597cad322 --hidden --no-graph -Tjson | python -m json.tool
   [
@@ -348,56 +380,62 @@
   ]
 Check that debugobshistory on the first successor after split show
 the revision plus the splitted one
-  $ hg obslog 337fec4d2edc
+  $ hg obslog 337fec4d2edc --patch
   o  337fec4d2edc (2) A0
   |
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
 With the all option, it should show the three changesets
-  $ hg obslog --all 337fec4d2edc
+  $ hg obslog --all 337fec4d2edc --patch
   o  337fec4d2edc (2) A0
   |
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
 Check that debugobshistory on the second successor after split show
 the revision plus the splitted one
-  $ hg obslog f257fde29c7a
+  $ hg obslog f257fde29c7a --patch
   @  f257fde29c7a (3) A0
   |
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
 With the all option, it should show the three changesets
-  $ hg obslog f257fde29c7a --all
+  $ hg obslog f257fde29c7a --all --patch
   o  337fec4d2edc (2) A0
   |
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
 Obslog with all option all should also works on the splitted commit
-  $ hg obslog -a 471597cad322 --hidden
+  $ hg obslog -a 471597cad322 --hidden --patch
   o  337fec4d2edc (2) A0
   |
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
 Check that debugobshistory on both successors after split show
 a coherent graph
-  $ hg obslog 'f257fde29c7a+337fec4d2edc'
+  $ hg obslog 'f257fde29c7a+337fec4d2edc' --patch
   o  337fec4d2edc (2) A0
   |
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) by test (*) as 337fec4d2edc, f257fde29c7a (glob)
+         (No patch available yet, too many successors (2))
   
   $ hg update 471597cad322
   abort: hidden revision '471597cad322'!
@@ -561,11 +599,12 @@
 Actual test
 -----------
 
-  $ hg obslog de7290d8b885 --hidden
+  $ hg obslog de7290d8b885 --hidden --patch
   x  de7290d8b885 (1) A0
        rewritten(parent, content) by test (*) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a (glob)
+         (No patch available yet, too many successors (4))
   
-  $ hg obslog de7290d8b885 --hidden --all
+  $ hg obslog de7290d8b885 --hidden --all --patch
   o  1ae8bc733a14 (4) A0
   |
   | o  337fec4d2edc (2) A0
@@ -576,6 +615,7 @@
   |/
   x  de7290d8b885 (1) A0
        rewritten(parent, content) by test (*) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a (glob)
+         (No patch available yet, too many successors (4))
   
   $ hg obslog de7290d8b885 --hidden --no-graph -Tjson | python -m json.tool
   [
@@ -605,11 +645,12 @@
           "debugobshistory.shortdescription": "A0"
       }
   ]
-  $ hg obslog c7f044602e9b
+  $ hg obslog c7f044602e9b --patch
   @  c7f044602e9b (5) A0
   |
   x  de7290d8b885 (1) A0
        rewritten(parent, content) by test (*) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a (glob)
+         (No patch available yet, too many successors (4))
   
   $ hg obslog c7f044602e9b --no-graph -Tjson | python -m json.tool
   [
@@ -646,7 +687,7 @@
       }
   ]
 Check that debugobshistory on all heads show a coherent graph
-  $ hg obslog 2::5
+  $ hg obslog 2::5 --patch
   o  1ae8bc733a14 (4) A0
   |
   | o  337fec4d2edc (2) A0
@@ -657,8 +698,9 @@
   |/
   x  de7290d8b885 (1) A0
        rewritten(parent, content) by test (*) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a (glob)
+         (No patch available yet, too many successors (4))
   
-  $ hg obslog 5 --all
+  $ hg obslog 5 --all --patch
   o  1ae8bc733a14 (4) A0
   |
   | o  337fec4d2edc (2) A0
@@ -669,6 +711,7 @@
   |/
   x  de7290d8b885 (1) A0
        rewritten(parent, content) by test (*) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a (glob)
+         (No patch available yet, too many successors (4))
   
   $ hg update de7290d8b885
   abort: hidden revision 'de7290d8b885'!
@@ -738,46 +781,98 @@
 
 Check that debugobshistory on the first folded revision show only
 the revision with the target
-  $ hg obslog --hidden 471f378eab4c
+  $ hg obslog --hidden 471f378eab4c --patch
   x  471f378eab4c (1) A0
        rewritten(description, content) by test (*) as eb5a0daa2192 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/eb5a0daa2192-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +C0
+  
+         diff -r 471f378eab4c -r eb5a0daa2192 B0
+         --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+         +++ b/B0	Thu Jan 01 00:00:00 1970 +0000
+         @@ -0,0 +1,1 @@
+         +B0
+  
   
 Check that with all option, all changesets are shown
-  $ hg obslog --hidden --all 471f378eab4c
+  $ hg obslog --hidden --all 471f378eab4c --patch
   @    eb5a0daa2192 (3) C0
   |\
   x |  0dec01379d3b (2) B0
    /     rewritten(description, parent, content) by test (*) as eb5a0daa2192 (glob)
+  |        (No patch available yet, changesets rebased)
   |
   x  471f378eab4c (1) A0
        rewritten(description, content) by test (*) as eb5a0daa2192 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/eb5a0daa2192-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +C0
+  
+         diff -r 471f378eab4c -r eb5a0daa2192 B0
+         --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+         +++ b/B0	Thu Jan 01 00:00:00 1970 +0000
+         @@ -0,0 +1,1 @@
+         +B0
+  
   
 Check that debugobshistory on the second folded revision show only
 the revision with the target
-  $ hg obslog --hidden 0dec01379d3b
+  $ hg obslog --hidden 0dec01379d3b --patch
   x  0dec01379d3b (2) B0
        rewritten(description, parent, content) by test (*) as eb5a0daa2192 (glob)
+         (No patch available yet, changesets rebased)
   
 Check that with all option, all changesets are shown
-  $ hg obslog --hidden --all 0dec01379d3b
+  $ hg obslog --hidden --all 0dec01379d3b --patch
   @    eb5a0daa2192 (3) C0
   |\
   x |  0dec01379d3b (2) B0
    /     rewritten(description, parent, content) by test (*) as eb5a0daa2192 (glob)
+  |        (No patch available yet, changesets rebased)
   |
   x  471f378eab4c (1) A0
        rewritten(description, content) by test (*) as eb5a0daa2192 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/eb5a0daa2192-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +C0
+  
+         diff -r 471f378eab4c -r eb5a0daa2192 B0
+         --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+         +++ b/B0	Thu Jan 01 00:00:00 1970 +0000
+         @@ -0,0 +1,1 @@
+         +B0
+  
   
 Check that debugobshistory on the successor revision show a coherent
 graph
-  $ hg obslog eb5a0daa2192
+  $ hg obslog eb5a0daa2192 --patch
   @    eb5a0daa2192 (3) C0
   |\
   x |  0dec01379d3b (2) B0
    /     rewritten(description, parent, content) by test (*) as eb5a0daa2192 (glob)
+  |        (No patch available yet, changesets rebased)
   |
   x  471f378eab4c (1) A0
        rewritten(description, content) by test (*) as eb5a0daa2192 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/eb5a0daa2192-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +C0
+  
+         diff -r 471f378eab4c -r eb5a0daa2192 B0
+         --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+         +++ b/B0	Thu Jan 01 00:00:00 1970 +0000
+         @@ -0,0 +1,1 @@
+         +B0
+  
   
   $ hg obslog eb5a0daa2192 --no-graph -Tjson | python -m json.tool
   [
@@ -917,21 +1012,45 @@
 -----------
 
 Check that debugobshistory on the divergent revision show both destinations
-  $ hg obslog --hidden 471f378eab4c
+  $ hg obslog --hidden 471f378eab4c --patch
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
 
 Check that with all option, every changeset is shown
-  $ hg obslog --hidden --all 471f378eab4c
+  $ hg obslog --hidden --all 471f378eab4c --patch
   @  65b757b745b9 (3) A2
   |
   | o  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
   $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | python -m json.tool
   [
@@ -973,53 +1092,113 @@
   ]
 Check that debugobshistory on the first diverged revision show the revision
 and the diverent one
-  $ hg obslog fdf9bde5129a
+  $ hg obslog fdf9bde5129a --patch
   o  fdf9bde5129a (2) A1
   |
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
 
 Check that all option show all of them
-  $ hg obslog fdf9bde5129a -a
+  $ hg obslog fdf9bde5129a -a --patch
   @  65b757b745b9 (3) A2
   |
   | o  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
 Check that debugobshistory on the second diverged revision show the revision
 and the diverent one
-  $ hg obslog 65b757b745b9
+  $ hg obslog 65b757b745b9 --patch
   @  65b757b745b9 (3) A2
   |
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
 Check that all option show all of them
-  $ hg obslog 65b757b745b9 -a
+  $ hg obslog 65b757b745b9 -a --patch
   @  65b757b745b9 (3) A2
   |
   | o  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
 Check that debugobshistory on the both diverged revision show a coherent
 graph
-  $ hg obslog '65b757b745b9+fdf9bde5129a'
+  $ hg obslog '65b757b745b9+fdf9bde5129a' --patch
   @  65b757b745b9 (3) A2
   |
   | o  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as 65b757b745b9 (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/65b757b745b9-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A2
+  
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
   $ hg obslog '65b757b745b9+fdf9bde5129a' --no-graph -Tjson | python -m json.tool
   [
@@ -1151,30 +1330,68 @@
  -----------
 
 Check that debugobshistory on head show a coherent graph
-  $ hg obslog eb5a0daa2192
+  $ hg obslog eb5a0daa2192 --patch
   @    eb5a0daa2192 (4) C0
   |\
   x |  471f378eab4c (1) A0
    /     rewritten(description, content) by test (*) as eb5a0daa2192 (glob)
+  |        --- a/471f378eab4c-changeset-description
+  |        +++ b/eb5a0daa2192-changeset-description
+  |        @@ -1,1 +1,1 @@
+  |        -A0
+  |        +C0
+  |
+  |        diff -r 471f378eab4c -r eb5a0daa2192 B0
+  |        --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  |        +++ b/B0	Thu Jan 01 00:00:00 1970 +0000
+  |        @@ -0,0 +1,1 @@
+  |        +B0
+  |
   |
   x  b7ea6d14e664 (3) B1
   |    rewritten(description, parent, content) by test (*) as eb5a0daa2192 (glob)
+  |      (No patch available yet, changesets rebased)
   |
   x  0dec01379d3b (2) B0
        rewritten(description) by test (*) as b7ea6d14e664 (glob)
+         --- a/0dec01379d3b-changeset-description
+         +++ b/b7ea6d14e664-changeset-description
+         @@ -1,1 +1,1 @@
+         -B0
+         +B1
+  
   
 Check that obslog on ROOT with all option show everything
-  $ hg obslog 1 --hidden --all
+  $ hg obslog 1 --hidden --all --patch
   @    eb5a0daa2192 (4) C0
   |\
   x |  471f378eab4c (1) A0
    /     rewritten(description, content) by test (*) as eb5a0daa2192 (glob)
+  |        --- a/471f378eab4c-changeset-description
+  |        +++ b/eb5a0daa2192-changeset-description
+  |        @@ -1,1 +1,1 @@
+  |        -A0
+  |        +C0
+  |
+  |        diff -r 471f378eab4c -r eb5a0daa2192 B0
+  |        --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  |        +++ b/B0	Thu Jan 01 00:00:00 1970 +0000
+  |        @@ -0,0 +1,1 @@
+  |        +B0
+  |
   |
   x  b7ea6d14e664 (3) B1
   |    rewritten(description, parent, content) by test (*) as eb5a0daa2192 (glob)
+  |      (No patch available yet, changesets rebased)
   |
   x  0dec01379d3b (2) B0
        rewritten(description) by test (*) as b7ea6d14e664 (glob)
+         --- a/0dec01379d3b-changeset-description
+         +++ b/b7ea6d14e664-changeset-description
+         @@ -1,1 +1,1 @@
+         -B0
+         +B1
+  
   
   $ hg obslog eb5a0daa2192 --no-graph -Tjson | python -m json.tool
   [
@@ -1340,14 +1557,26 @@
  Actual test
  -----------
 
-  $ hg obslog 7a230b46bf61
+  $ hg obslog 7a230b46bf61 --patch
   @  7a230b46bf61 (3) A2
   |
   x  fdf9bde5129a (2) A1
   |    rewritten(description) by test (*) as 7a230b46bf61 (glob)
+  |      --- a/fdf9bde5129a-changeset-description
+  |      +++ b/7a230b46bf61-changeset-description
+  |      @@ -1,1 +1,1 @@
+  |      -A1
+  |      +A2
+  |
   |
   x  471f378eab4c (1) A0
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         --- a/471f378eab4c-changeset-description
+         +++ b/fdf9bde5129a-changeset-description
+         @@ -1,1 +1,1 @@
+         -A0
+         +A1
+  
   
   $ cd $TESTTMP/local-remote-markers-2
   $ hg pull
@@ -1363,21 +1592,25 @@
   (use 'hg evolve' to update to its successor: 7a230b46bf61)
 Check that debugobshistory works with markers pointing to missing local
 changectx
-  $ hg obslog 7a230b46bf61
+  $ hg obslog 7a230b46bf61 --patch
   o  7a230b46bf61 (2) A2
   |
   x  fdf9bde5129a
   |    rewritten(description) by test (*) as 7a230b46bf61 (glob)
+  |      (No patch available yet, context is not local)
   |
   @  471f378eab4c (1) A0
        rewritten(description) by test (*) as fdf9bde5129a (glob)
+         (No patch available yet, succ is unknown locally)
   
-  $ hg obslog 7a230b46bf61 --color=debug
+  $ hg obslog 7a230b46bf61 --color=debug --patch
   o  [evolve.node|7a230b46bf61] [evolve.rev|(2)] [evolve.short_description|A2]
   |
   x  [evolve.node evolve.missing_change_ctx|fdf9bde5129a]
   |    [evolve.verb|rewritten](description) by [evolve.user|test] [evolve.date|(*)] as [evolve.node|7a230b46bf61] (glob)
+  |      (No patch available yet, context is not local)
   |
   @  [evolve.node|471f378eab4c] [evolve.rev|(1)] [evolve.short_description|A0]
        [evolve.verb|rewritten](description) by [evolve.user|test] [evolve.date|(*)] as [evolve.node|fdf9bde5129a] (glob)
+         (No patch available yet, succ is unknown locally)
   
--- a/tests/test-evolve-templates.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-evolve-templates.t	Sun Jun 25 18:37:42 2017 +0200
@@ -76,10 +76,10 @@
   o  d004c8f274b9 (4) A2
   |
   x  a468dc9b3633 (3) A1
-  |    rewritten(description) by test2 (*) as d004c8f274b9 (glob)
+  |    rewritten(description) by test2 (Thu Apr 19 04:25:21 2001 +0000) as d004c8f274b9
   |
   @  471f378eab4c (1) A0
-       rewritten(description, content) by test1 (*) as a468dc9b3633 (glob)
+       rewritten(description, content) by test1 (Fri Feb 13 23:31:30 2009 +0000) as a468dc9b3633
   
   $ hg tlog
   o  d004c8f274b9
@@ -113,7 +113,7 @@
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten by test1, test2 as d004c8f274b9 (between * and *) (glob)
+  |/     Obsfate: rewritten by test1, test2 as d004c8f274b9 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000)
   |
   o  ea207398892e
   
@@ -180,7 +180,7 @@
   o  d004c8f274b9
   |
   | @  a468dc9b3633
-  |/     Obsfate: rewritten by test2 as d004c8f274b9 (at *) (glob)
+  |/     Obsfate: rewritten by test2 as d004c8f274b9 (at 2001-04-19 04:25 +0000)
   |
   o  ea207398892e
   
@@ -222,13 +222,13 @@
   @  d004c8f274b9
   |
   | x  a468dc9b3633
-  |/     Obsfate: rewritten by test2 as d004c8f274b9 (at *) (glob)
+  |/     Obsfate: rewritten by test2 as d004c8f274b9 (at 2001-04-19 04:25 +0000)
   |
   | x  f137d23bb3e1
-  | |    Obsfate: pruned by test1 (at *) (glob)
+  | |    Obsfate: pruned by test1 (at 2009-02-13 23:31 +0000)
   | |
   | x  471f378eab4c
-  |/     Obsfate: rewritten by test1 as a468dc9b3633 (at *) (glob)
+  |/     Obsfate: rewritten by test1 as a468dc9b3633 (at 2009-02-13 23:31 +0000)
   |
   o  ea207398892e
   
@@ -236,11 +236,11 @@
   $ hg fatelogjson --hidden
   @  d004c8f274b9 ""
   |
-  | x  a468dc9b3633 [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["ef1", "1"], ["user", "test2"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], "users": ["test2"], "verb": "rewritten"}] (glob)
+  | x  a468dc9b3633 [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["ef1", "1"], ["user", "test2"]], [987654321.0, 0], null]], "max_date": [987654321.0, 0], "min_date": [987654321.0, 0], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], "users": ["test2"], "verb": "rewritten"}]
   |/
-  | x  f137d23bb3e1 [{"markers": [["f137d23bb3e11dc1daeb6264fac9cb2433782e15", [], 0, [["ef1", "0"], ["user", "test1"]], [*, *], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]], "max_date": [*, *], "min_date": [*, *], "successors": [], "users": ["test1"], "verb": "pruned"}] (glob)
+  | x  f137d23bb3e1 [{"markers": [["f137d23bb3e11dc1daeb6264fac9cb2433782e15", [], 0, [["ef1", "0"], ["user", "test1"]], [1234567890.0, 0], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]], "max_date": [1234567890.0, 0], "min_date": [1234567890.0, 0], "successors": [], "users": ["test1"], "verb": "pruned"}]
   | |
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["ef1", "9"], ["user", "test1"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], "users": ["test1"], "verb": "rewritten"}] (glob)
+  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["ef1", "9"], ["user", "test1"]], [1234567890.0, 0], null]], "max_date": [1234567890.0, 0], "min_date": [1234567890.0, 0], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], "users": ["test1"], "verb": "rewritten"}]
   |/
   o  ea207398892e ""
   
@@ -405,7 +405,7 @@
   |
   o  337fec4d2edc ""
   |
-  | x  471597cad322 [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["ef1", "12"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], "users": ["test"], "verb": "split"}] (glob)
+  | x  471597cad322 [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["ef1", "12"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], "users": ["test"], "verb": "split"}]
   |/
   o  ea207398892e ""
   
@@ -570,9 +570,9 @@
   $ hg fatelogjson --hidden
   @  eb5a0daa2192 ""
   |
-  | x  0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "13"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "13"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
   | |
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "9"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "9"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
   |/
   o  ea207398892e ""
   
@@ -734,11 +734,11 @@
   $ hg fatelogjson --hidden
   o  019fadeab383 ""
   |
-  | x  65b757b745b9 [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["ef1", "1"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  65b757b745b9 [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], "users": ["test"], "verb": "rewritten"}]
   |/
   | @  fdf9bde5129a ""
   |/
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], "users": ["test"], "verb": "rewritten"}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["ef1", "1"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], "users": ["test"], "verb": "rewritten"}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"], "users": ["test"], "verb": "rewritten"}]
   |/
   o  ea207398892e ""
   
@@ -947,11 +947,11 @@
   $ hg fatelogjson --hidden
   @  eb5a0daa2192 ""
   |
-  | x  b7ea6d14e664 [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "13"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  b7ea6d14e664 [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "13"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
   | |
-  | | x  0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["ef1", "1"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | | x  0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], "users": ["test"], "verb": "rewritten"}]
   | |/
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "9"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["ef1", "9"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}]
   |/
   o  ea207398892e ""
   
@@ -1057,7 +1057,7 @@
   o  7a230b46bf61
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten by test as 7a230b46bf61 (between * and *) (glob)
+  |/     Obsfate: rewritten by test as 7a230b46bf61 (at 1970-01-01 00:00 +0000)
   |
   o  ea207398892e
   
@@ -1088,7 +1088,7 @@
   @  7a230b46bf61
   |
   | x  471f378eab4c
-  |/     Obsfate: rewritten by test as 7a230b46bf61 (between * and *) (glob)
+  |/     Obsfate: rewritten by test as 7a230b46bf61 (at 1970-01-01 00:00 +0000)
   |
   o  ea207398892e
   
@@ -1096,7 +1096,7 @@
   $ hg fatelogjson --hidden
   @  7a230b46bf61 ""
   |
-  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["user", "test"]], [*, *], null], ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e", ["7a230b46bf61e50b30308c6cfd7bd1269ef54702"], 0, [["ef1", "1"], ["user", "test"]], [*, *], null]], "max_date": [*, *], "min_date": [*, *], "successors": ["7a230b46bf61e50b30308c6cfd7bd1269ef54702"], "users": ["test"], "verb": "rewritten"}] (glob)
+  | x  471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null], ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e", ["7a230b46bf61e50b30308c6cfd7bd1269ef54702"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["7a230b46bf61e50b30308c6cfd7bd1269ef54702"], "users": ["test"], "verb": "rewritten"}]
   |/
   o  ea207398892e ""
   
@@ -1131,7 +1131,7 @@
   
   $ hg fatelog -v
   @  471f378eab4c
-  |    Obsfate: pruned by test (at *) (glob)
+  |    Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
   |
   o  ea207398892e
   
--- a/tests/test-inhibit.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-inhibit.t	Sun Jun 25 18:37:42 2017 +0200
@@ -686,6 +686,7 @@
   adding manifests
   adding file changes
   added 2 changesets with 1 changes to 2 files (+1 heads)
+  3 new obsolescence markers
   (run 'hg heads .' to see heads, 'hg merge' to merge)
 
  Only allow direct access and check that evolve works like before
--- a/tests/test-obsolete.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-obsolete.t	Sun Jun 25 18:37:42 2017 +0200
@@ -455,6 +455,7 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up 
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  updated to "159dfc9fa5d3: add obsol_d''"
   1 other heads for branch "default"
   $ hg id -n
   8
--- a/tests/test-sharing.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-sharing.t	Sun Jun 25 18:37:42 2017 +0200
@@ -89,6 +89,7 @@
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  updated to "60ffde5765c5: fix bug 37"
   1 other heads for branch "default"
 
 Figure SG03
@@ -120,6 +121,7 @@
   $ cd ../test-repo
   $ hg update
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  updated to "de6151c48e1c: fix bug 37"
   1 other heads for branch "default"
   $ hg shortlog --hidden -G
   @  4:de6151c48e1c  draft  fix bug 37
--- a/tests/test-topic-stack.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-topic-stack.t	Sun Jun 25 18:37:42 2017 +0200
@@ -188,7 +188,7 @@
   ### topic: foo
   ### branch: default
   t4$ c_f (unstable)
-  t3@ c_e (current)
+  t3$ c_e (current unstable)
   t2: c_d
   t1: c_c
     ^ c_b
@@ -196,7 +196,7 @@
   [topic.stack.summary.topic|### topic: [topic.active|foo]]
   [topic.stack.summary.branches|### branch: default]
   [topic.stack.index topic.stack.index.unstable|t4][topic.stack.state topic.stack.state.unstable|$] [topic.stack.desc topic.stack.desc.unstable|c_f][topic.stack.state topic.stack.state.unstable| (unstable)]
-  [topic.stack.index topic.stack.index.current|t3][topic.stack.state topic.stack.state.current|@] [topic.stack.desc topic.stack.desc.current|c_e][topic.stack.state topic.stack.state.current| (current)]
+  [topic.stack.index topic.stack.index.current topic.stack.index.unstable|t3][topic.stack.state topic.stack.state.current topic.stack.state.unstable|$] [topic.stack.desc topic.stack.desc.current topic.stack.desc.unstable|c_e][topic.stack.state topic.stack.state.current topic.stack.state.unstable| (current unstable)]
   [topic.stack.index topic.stack.index.clean|t2][topic.stack.state topic.stack.state.clean|:] [topic.stack.desc topic.stack.desc.clean|c_d]
   [topic.stack.index topic.stack.index.clean|t1][topic.stack.state topic.stack.state.clean|:] [topic.stack.desc topic.stack.desc.clean|c_c]
     [topic.stack.state topic.stack.state.base|^] [topic.stack.desc topic.stack.desc.base|c_b]
@@ -319,3 +319,11 @@
   t2@ c_D (current)
   t1: c_c
     ^ c_b
+
+Trying to list non existing topic
+  $ hg stack thisdoesnotexist
+  abort: cannot resolve "thisdoesnotexist": no such topic found
+  [255]
+  $ hg topic --list thisdoesnotexist
+  abort: cannot resolve "thisdoesnotexist": no such topic found
+  [255]
--- a/tests/test-topic.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-topic.t	Sun Jun 25 18:37:42 2017 +0200
@@ -22,9 +22,9 @@
   
   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
+      --clear   clear active topic if any
+   -r --rev REV revset of existing revisions
+   -l --list    show the stack of changeset in the topic
   
   (some details hidden, use --verbose to show complete help)
   $ hg topics
@@ -32,8 +32,8 @@
 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
+  $ hg topics --config experimental.evolution= --rev . bob
+  abort: must have obsolete enabled to change topics
   [255]
 
 Create some changes:
@@ -542,21 +542,21 @@
   $ hg topics
      fran
 Changing topic fails if we don't give a topic
-  $ hg topic --change 9
+  $ hg topic --rev 9
   abort: changing topic requires a topic name or --clear
   [255]
 
 Can't change topic of a public change
-  $ hg topic --change 1:: --clear
+  $ hg topic --rev 1:: --clear
   abort: can't change topic of a public change
   [255]
 
 Can clear topics
-  $ hg topic --change 9 --clear
+  $ hg topic --rev 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
+  o  changeset:   11:0beca5ab56c3
   |  tag:         tip
   |  parent:      3:a53952faf762
   |  user:        test
@@ -577,11 +577,11 @@
   rebasing 10:4073470c35e1 "fran?"
 
 Can add a topic to an existing change
-  $ hg topic --change 11 wat
+  $ hg topic --rev 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
+  o  changeset:   13:686a642006db
   |  tag:         tip
   |  topic:       wat
   |  parent:      3:a53952faf762
@@ -589,7 +589,7 @@
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     start on fran
   |
-  | @  changeset:   12:d9e32f4c4806
+  | @  changeset:   12:18b70b8de1f0
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  trouble:     unstable
@@ -600,17 +600,17 @@
 bonus deps in the testsuite.
 
   $ hg rebase -d tip -s .
-  rebasing 12:d9e32f4c4806 "fran?"
+  rebasing 12:18b70b8de1f0 "fran?"
 
   $ hg log -Gr 'draft()'
-  @  changeset:   14:cf24ad8bbef5
+  @  changeset:   14:503497a14c3e
   |  tag:         tip
   |  topic:       wat
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     fran?
   |
-  o  changeset:   13:d91cd8fd490e
+  o  changeset:   13:686a642006db
   |  topic:       wat
   |  parent:      3:a53952faf762
   |  user:        test
@@ -623,15 +623,15 @@
   $ hg topic watwat
   $ hg ci --amend
   $ hg log -Gr 'draft()'
-  @  changeset:   16:893ffcf66c1f
+  @  changeset:   16:024d9c69f30f
   |  tag:         tip
   |  topic:       watwat
-  |  parent:      13:d91cd8fd490e
+  |  parent:      13:686a642006db
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     fran?
   |
-  o  changeset:   13:d91cd8fd490e
+  o  changeset:   13:686a642006db
   |  topic:       wat
   |  parent:      3:a53952faf762
   |  user:        test
@@ -644,13 +644,114 @@
   $ hg topic --clear
   $ hg ci --amend
   $ hg log -r .
-  changeset:   18:a13639e22b65
+  changeset:   18:573ab57c6de2
   tag:         tip
-  parent:      13:d91cd8fd490e
+  parent:      13:686a642006db
   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
+Reading the same topic with topic --rev should work:
+  $ hg topic --rev . watwat
+  switching to topic watwat
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   changed topic on 1 changes
+
+Testing issue5441
+  $ hg co 19
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -Gr 'draft()'
+  @  changeset:   19:2539ecb09b37
+  |  tag:         tip
+  |  topic:       watwat
+  |  parent:      13:686a642006db
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     fran?
+  |
+  o  changeset:   13:686a642006db
+  |  topic:       wat
+  |  parent:      3:a53952faf762
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     start on fran
+  |
+
+  $ hg topics --rev '13::19' changewat
+  switching to topic changewat
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  changed topic on 2 changes
+  please run hg evolve --rev "topic(changewat)" now
+  $ hg log -Gr 'draft()'
+  @  changeset:   21:993d145391f5
+  |  tag:         tip
+  |  topic:       changewat
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     fran?
+  |
+  o  changeset:   20:ceba5be9d56f
+  |  topic:       changewat
+  |  parent:      3:a53952faf762
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     start on fran
+  |
+
+Case with branching:
+
+  $ hg up changewat
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg up t1
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo gamma >> gamma
+  $ hg ci -m gamma
+  $ hg log -Gr 'draft()'
+  @  changeset:   22:0d3d805542b4
+  |  tag:         tip
+  |  topic:       changewat
+  |  parent:      20:ceba5be9d56f
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     gamma
+  |
+  | o  changeset:   21:993d145391f5
+  |/   topic:       changewat
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     fran?
+  |
+  o  changeset:   20:ceba5be9d56f
+  |  topic:       changewat
+  |  parent:      3:a53952faf762
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     start on fran
+  |
+  $ hg topics --rev 't1::' changewut
+  switching to topic changewut
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  changed topic on 3 changes
+  please run hg evolve --rev "topic(changewut)" now
+  $ hg log -Gr 'draft()'
+  @  changeset:   25:729ed5717393
+  |  tag:         tip
+  |  topic:       changewut
+  |  parent:      23:62e49f09f883
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     gamma
+  |
+  | o  changeset:   24:658ae31a0c05
+  |/   topic:       changewut
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     fran?
+  |
+  o  changeset:   23:62e49f09f883
+  |  topic:       changewut
+  |  parent:      3:a53952faf762
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     start on fran
+  |
--- a/tests/test-wireproto.t	Sun Jun 25 16:37:56 2017 +0200
+++ b/tests/test-wireproto.t	Sun Jun 25 18:37:42 2017 +0200
@@ -136,7 +136,7 @@
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 0 changes to 1 files (+1 heads)
-  remote: obsmarker-exchange: 227 bytes received
+  remote: obsmarker-exchange: 226 bytes received
   remote: 1 new obsolescence markers
   $ hg -R ../other pull
   pulling from ssh://user@dummy/server
@@ -145,7 +145,7 @@
   adding manifests
   adding file changes
   added 1 changesets with 0 changes to 1 files (+1 heads)
-  obsmarker-exchange: 227 bytes received
+  obsmarker-exchange: 226 bytes received
   1 new obsolescence markers
   (run 'hg heads' to see heads)
 
@@ -169,7 +169,7 @@
   (skipping discovery of obsolescence markers, will exchange everything)
   (controled by 'experimental.evolution.obsdiscovery' configuration)
   no changes found
-  remote: obsmarker-exchange: 377 bytes received
+  remote: obsmarker-exchange: 376 bytes received
   [1]
   $ hg -R ../other pull --config experimental.evolution.obsdiscovery=no
   pulling from ssh://user@dummy/server
@@ -177,6 +177,6 @@
   no changes found
   (skipping discovery of obsolescence markers, will exchange everything)
   (controled by 'experimental.evolution.obsdiscovery' configuration)
-  obsmarker-exchange: 377 bytes received
+  obsmarker-exchange: 376 bytes received
 
   $ cd ..