branching: merge with stable
authorPierre-Yves David <pierre-yves.david@octobus.net>
Tue, 05 Dec 2017 18:31:08 +0100
changeset 3232 c1d20598bc2b
parent 3231 996dabc4224b (current diff)
parent 3225 28fb347a5bf8 (diff)
child 3234 a18ca224e812
branching: merge with stable
CHANGELOG
hgext3rd/evolve/__init__.py
hgext3rd/evolve/cmdrewrite.py
hgext3rd/topic/__init__.py
tests/test-prev-next.t
tests/test-topic-stack.t
--- a/CHANGELOG	Wed Dec 06 02:08:37 2017 +0100
+++ b/CHANGELOG	Tue Dec 05 18:31:08 2017 +0100
@@ -1,6 +1,18 @@
 Changelog
 =========
 
+7.1.0 - in progress
+-------------------
+
+  * verbosity: respect --quiet for prev, next and summary
+  * note: add a `-n/--note` flag to all history rewritting commands
+  * obslog: shows the obsmarkers notes
+
+topic (0.6.0)
+
+  * add a new 'serverminitopic' extension for minimal server support
+    (see `hg help -e serverminitopic` for details)
+
 7.0.2 - in progress
 -------------------
 
--- a/hgext3rd/evolve/__init__.py	Wed Dec 06 02:08:37 2017 +0100
+++ b/hgext3rd/evolve/__init__.py	Tue Dec 05 18:31:08 2017 +0100
@@ -831,17 +831,10 @@
         raise
 
 def summaryhook(ui, repo):
-    def write(fmt, count):
-        s = fmt % count
-        if count:
-            ui.write(s)
-        else:
-            ui.note(s)
-
     state = _evolvestateread(repo)
     if state is not None:
         # i18n: column positioning for "hg summary"
-        ui.write(_('evolve: (evolve --continue)\n'))
+        ui.status(_('evolve: (evolve --continue)\n'))
 
 @eh.extsetup
 def obssummarysetup(ui):
@@ -2031,7 +2024,8 @@
             finally:
                 lockmod.release(tr, lock)
 
-    displayer.show(target)
+    if not repo.ui.quiet:
+        displayer.show(target)
 
 def _findprevtarget(repo, displayer, movebookmark=False, topic=True):
     target = bookmark = None
@@ -2174,7 +2168,8 @@
                         tr.close()
                     finally:
                         lockmod.release(tr, lock)
-            displayer.show(c)
+            if not ui.quiet:
+                displayer.show(c)
             result = 0
         elif children:
             ui.warn(_("ambiguous next changeset:\n"))
--- a/hgext3rd/evolve/cmdrewrite.py	Wed Dec 06 02:08:37 2017 +0100
+++ b/hgext3rd/evolve/cmdrewrite.py	Tue Dec 05 18:31:08 2017 +0100
@@ -49,6 +49,18 @@
 
 # option added by evolve
 
+def _checknotesize(opts):
+    """ make sure note is of valid format """
+
+    note = opts.get('note')
+    if not note:
+        return
+
+    if len(note) > 255:
+        raise error.Abort(_("cannot store a note of more than 255 bytes"))
+    if '\n' in note:
+        raise error.Abort(_("note cannot contain a newline"))
+
 def _resolveoptions(ui, opts):
     """modify commit options dict to handle related options
 
@@ -80,6 +92,7 @@
      ('', 'close-branch', None,
       _('mark a branch as closed, hiding it from the branch list')),
      ('s', 'secret', None, _('use the secret phase for committing')),
+     ('n', 'note', '', _('store a note on amend')),
     ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt,
     _('[OPTION]... [FILE]...'))
 def amend(ui, repo, *pats, **opts):
@@ -98,6 +111,7 @@
 
     Returns 0 on success, 1 if nothing changed.
     """
+    _checknotesize(opts)
     opts = opts.copy()
     if opts.get('extract'):
         return uncommit(ui, repo, *pats, **opts)
@@ -288,6 +302,7 @@
     [('a', 'all', None, _('uncommit all changes when no arguments given')),
      ('i', 'interactive', False, _('interactive mode to uncommit (EXPERIMENTAL)')),
      ('r', 'rev', '', _('revert commit content to REV instead')),
+     ('n', 'note', '', _('store a note on uncommit')),
      ] + commands.walkopts + commitopts + commitopts2 + commitopts3,
     _('[OPTION]... [NAME]'))
 def uncommit(ui, repo, *pats, **opts):
@@ -313,6 +328,7 @@
     Return 0 if changed files are uncommitted.
     """
 
+    _checknotesize(opts)
     _resolveoptions(ui, opts) # process commitopts3
     interactive = opts.get('interactive')
     wlock = lock = tr = None
@@ -365,7 +381,12 @@
                 raise error.Abort(_('nothing to uncommit'),
                                   hint=_("use --all to uncommit all files"))
 
-        obsolete.createmarkers(repo, [(old, (repo[newid],))])
+        # metadata to be stored in obsmarker
+        metadata = {}
+        if opts.get('note'):
+            metadata['note'] = opts['note']
+
+        obsolete.createmarkers(repo, [(old, (repo[newid],))], metadata=metadata)
         phases.retractboundary(repo, tr, oldphase, [newid])
         with repo.dirstate.parentchange():
             repo.dirstate.setparents(newid, node.nullid)
@@ -477,7 +498,8 @@
     '^fold|squash',
     [('r', 'rev', [], _("revision to fold")),
      ('', 'exact', None, _("only fold specified revisions")),
-     ('', 'from', None, _("fold revisions linearly to working copy parent"))
+     ('', 'from', None, _("fold revisions linearly to working copy parent")),
+     ('n', 'note', '', _('store a note on fold')),
     ] + commitopts + commitopts2 + commitopts3,
     _('hg fold [OPTION]... [-r] REV'))
 def fold(ui, repo, *revs, **opts):
@@ -517,6 +539,7 @@
 
          hg fold foo::@ --exact
     """
+    _checknotesize(opts)
     _resolveoptions(ui, opts)
     revs = list(revs)
     revs.extend(opts['rev'])
@@ -572,6 +595,10 @@
                 commitopts['message'] = "\n".join(msgs)
                 commitopts['edit'] = True
 
+            metadata = {}
+            if opts.get('note'):
+                metadata['note'] = opts['note']
+
             newid, unusedvariable = rewriteutil.rewrite(repo, root, allctx,
                                                         head,
                                                         [root.p1().node(),
@@ -579,7 +606,7 @@
                                                         commitopts=commitopts)
             phases.retractboundary(repo, tr, targetphase, [newid])
             obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                   for ctx in allctx])
+                                   for ctx in allctx], metadata=metadata)
             tr.close()
         finally:
             tr.release()
@@ -593,6 +620,7 @@
     'metaedit',
     [('r', 'rev', [], _("revision to edit")),
      ('', 'fold', None, _("also fold specified revisions into one")),
+     ('n', 'note', '', _('store a note on metaedit')),
     ] + commitopts + commitopts2 + commitopts3,
     _('hg metaedit [OPTION]... [-r] [REV]'))
 def metaedit(ui, repo, *revs, **opts):
@@ -624,6 +652,7 @@
        See :hg:`help phases` for more about draft revisions, and
        :hg:`help revsets` for more about the `draft()` and `only()` keywords.
     """
+    _checknotesize(opts)
     _resolveoptions(ui, opts)
     revs = list(revs)
     revs.extend(opts['rev'])
@@ -697,9 +726,15 @@
             if created:
                 if p1.rev() in revs:
                     newp1 = newid
+                # metadata to be stored on obsmarker
+                metadata = {}
+                if opts.get('note'):
+                    metadata['note'] = opts['note']
+
                 phases.retractboundary(repo, tr, targetphase, [newid])
                 obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                              for ctx in allctx])
+                                              for ctx in allctx],
+                                       metadata=metadata)
             else:
                 ui.status(_("nothing changed\n"))
             tr.close()
@@ -736,6 +771,7 @@
      ('s', 'succ', [], _("successor changeset")),
      ('r', 'rev', [], _("revisions to prune")),
      ('k', 'keep', None, _("does not modify working copy during prune")),
+     ('n', 'note', '', _('store a note on prune')),
      ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")),
      ('', 'fold', False,
       _("record a fold (multiple precursors, one successors)")),
@@ -769,6 +805,7 @@
     must acknowledge it by passing ``--split``. Similarly, when you prune multiple
     changesets with a single successor, you must pass the ``--fold`` option.
     """
+    _checknotesize(opts)
     revs = scmutil.revrange(repo, list(revs) + opts.get('rev'))
     succs = opts['new'] + opts['succ']
     bookmarks = set(opts.get('bookmark'))
@@ -884,6 +921,10 @@
         if bookmarks:
             rewriteutil.deletebookmark(repo, repomarks, bookmarks)
 
+        # store note in metadata
+        if opts.get('note'):
+            metadata['note'] = opts['note']
+
         # create markers
         obsolete.createmarkers(repo, relations, metadata=metadata)
 
@@ -913,6 +954,7 @@
 @eh.command(
     '^split',
     [('r', 'rev', [], _("revision to split")),
+     ('n', 'note', '', _("store a note on split")),
     ] + commitopts + commitopts2 + commitopts3,
     _('hg split [OPTION]... [-r] REV'))
 def cmdsplit(ui, repo, *revs, **opts):
@@ -923,6 +965,7 @@
 
     Use --rev to split a given changeset instead.
     """
+    _checknotesize(opts)
     _resolveoptions(ui, opts)
     tr = wlock = lock = None
     newcommits = []
@@ -984,7 +1027,11 @@
             bmupdate(tip.node())
             if bookactive is not None:
                 bookmarksmod.activate(repo, bookactive)
-            obsolete.createmarkers(repo, [(repo[rev], newcommits)])
+            metadata = {}
+            if opts.get('note'):
+                metadata['note'] = opts['note']
+            obsolete.createmarkers(repo, [(repo[rev], newcommits)],
+                                   metadata=metadata)
         tr.close()
     finally:
         lockmod.release(tr, lock, wlock)
@@ -992,6 +1039,7 @@
 @eh.command(
     'touch',
     [('r', 'rev', [], 'revision to update'),
+     ('n', 'note', '', _('store a note on touch')),
      ('D', 'duplicate', False,
       'do not mark the new revision as successor of the old one'),
      ('A', 'allowdivergence', False,
@@ -1006,6 +1054,7 @@
 
     This is used to "resurrect" changesets
     """
+    _checknotesize(opts)
     duplicate = opts['duplicate']
     allowdivergence = opts['allowdivergence']
     revs = list(revs)
@@ -1069,7 +1118,11 @@
             newmapping[ctx.node()] = new
 
             if not duplicate:
-                obsolete.createmarkers(repo, [(ctx, (repo[new],))])
+                metadata = {}
+                if opts.get('note'):
+                    metadata['note'] = opts['note']
+                obsolete.createmarkers(repo, [(ctx, (repo[new],))],
+                                       metadata=metadata)
             phases.retractboundary(repo, tr, ctx.phase(), [new])
             if ctx in repo[None].parents():
                 with repo.dirstate.parentchange():
--- a/hgext3rd/evolve/metadata.py	Wed Dec 06 02:08:37 2017 +0100
+++ b/hgext3rd/evolve/metadata.py	Tue Dec 05 18:31:08 2017 +0100
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-__version__ = '7.0.2.dev'
+__version__ = '7.1.0.dev'
 testedwith = '4.1.3 4.2.3 4.3.2 4.4.1'
 minimumhgversion = '4.1'
 buglink = 'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obshistory.py	Wed Dec 06 02:08:37 2017 +0100
+++ b/hgext3rd/evolve/obshistory.py	Tue Dec 05 18:31:08 2017 +0100
@@ -499,6 +499,11 @@
     fm.write('date', '(%s)', fm.formatdate(date),
              label="evolve.date")
 
+    # initial support for showing note
+    if metadata.get('note'):
+        fm.plain('\n    note: ')
+        fm.write('note', "%s", metadata['note'], label="evolve.note")
+
     # Patch display
     if opts.get('patch'):
         _patchavailable = patchavailable(node, repo, marker)
@@ -787,8 +792,15 @@
         verb = 'split'
     return {'verb': verb}
 
+# Use a more advanced version of obsfateverb that uses effect-flag
+if util.safehasattr(obsutil, 'obsfateverb'):
+
+    @eh.wrapfunction(obsutil, 'obsfateverb')
+    def obsfateverb(orig, *args, **kwargs):
+        return _successorsetverb(*args, **kwargs)['verb']
+
 # Hijack callers of successorsetverb
-if util.safehasattr(obsutil, 'obsfateprinter'):
+elif util.safehasattr(obsutil, 'obsfateprinter'):
 
     @eh.wrapfunction(obsutil, 'obsfateprinter')
     def obsfateprinter(orig, successors, markers, ui):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/serverminitopic.py	Tue Dec 05 18:31:08 2017 +0100
@@ -0,0 +1,226 @@
+"""enable a minimal verison of topic for server
+
+Non publishing repository will see topic as "branch:topic" in the branch field.
+
+In addition to adding the extensions, the feature must be manually enabled in the config:
+
+    [experimental]
+    server-mini-topic = yes
+"""
+import hashlib
+import contextlib
+
+from mercurial import (
+    branchmap,
+    context,
+    encoding,
+    extensions,
+    node,
+    registrar,
+    util,
+    wireproto,
+)
+
+if util.safehasattr(registrar, 'configitem'):
+
+    configtable = {}
+    configitem = registrar.configitem(configtable)
+    configitem('experimental', 'server-mini-topic',
+               default=False,
+    )
+
+def hasminitopic(repo):
+    """true if minitopic is enabled on the repository
+
+    (The value is cached on the repository)
+    """
+    enabled = getattr(repo, '_hasminitopic', None)
+    if enabled is None:
+        enabled = (repo.ui.configbool('experimental', 'server-mini-topic')
+                   and not repo.publishing())
+        repo._hasminitopic = enabled
+    return enabled
+
+### make topic visible though "ctx.branch()"
+
+class topicchangectx(context.changectx):
+    """a sunclass of changectx that add topic to the branch name"""
+
+    def branch(self):
+        branch = super(topicchangectx, self).branch()
+        if hasminitopic(self._repo) and self.phase():
+            topic = self._changeset.extra.get('topic')
+            if topic is not None:
+                topic = encoding.tolocal(topic)
+                branch = '%s:%s' % (branch, topic)
+        return branch
+
+### avoid caching topic data in rev-branch-cache
+
+class revbranchcacheoverlay(object):
+    """revbranch mixin that don't use the cache for non public changeset"""
+
+    def _init__(self, *args, **kwargs):
+        super(revbranchcacheoverlay, self).__init__(*args, **kwargs)
+        if 'branchinfo' in vars(self):
+            del self.branchinfo
+
+    def branchinfo(self, rev):
+        """return branch name and close flag for rev, using and updating
+        persistent cache."""
+        phase = self._repo._phasecache.phase(self, rev)
+        if phase:
+            ctx = self._repo[rev]
+            return ctx.branch(), ctx.closesbranch()
+        return super(revbranchcacheoverlay, self).branchinfo(rev)
+
+def reposetup(ui, repo):
+    """install a repo class with a special revbranchcache"""
+
+    if hasminitopic(repo):
+        repo = repo.unfiltered()
+
+        class minitopicrepo(repo.__class__):
+            """repository subclass that install the modified cache"""
+
+            def revbranchcache(self):
+                if self._revbranchcache is None:
+                    cache = super(minitopicrepo, self).revbranchcache()
+
+                    class topicawarerbc(revbranchcacheoverlay, cache.__class__):
+                        pass
+                    cache.__class__ = topicawarerbc
+                    if 'branchinfo' in vars(cache):
+                        del cache.branchinfo
+                    self._revbranchcache = cache
+                return self._revbranchcache
+
+        repo.__class__ = minitopicrepo
+
+### topic aware branch head cache
+
+def _phaseshash(repo, maxrev):
+    """uniq ID for a phase matching a set of rev"""
+    revs = set()
+    cl = repo.changelog
+    fr = cl.filteredrevs
+    nm = cl.nodemap
+    for roots in repo._phasecache.phaseroots[1:]:
+        for n in roots:
+            r = nm.get(n)
+            if r not in fr and r < maxrev:
+                revs.add(r)
+    key = node.nullid
+    revs = sorted(revs)
+    if revs:
+        s = hashlib.sha1()
+        for rev in revs:
+            s.update('%s;' % rev)
+        key = s.digest()
+    return key
+
+# needed to prevent reference used for 'super()' call using in branchmap.py to
+# no go into cycle. (yes, URG)
+_oldbranchmap = branchmap.branchcache
+
+@contextlib.contextmanager
+def oldbranchmap():
+    previous = branchmap.branchcache
+    try:
+        branchmap.branchcache = _oldbranchmap
+        yield
+    finally:
+        branchmap.branchcache = previous
+
+_publiconly = set([
+    'base',
+    'immutable',
+])
+
+def mighttopic(repo):
+    return hasminitopic(repo) and repo.filtername not in _publiconly
+
+class _topiccache(branchmap.branchcache): # combine me with branchmap.branchcache
+
+    def __init__(self, *args, **kwargs):
+        # super() call may fail otherwise
+        with oldbranchmap():
+            super(_topiccache, self).__init__(*args, **kwargs)
+        self.phaseshash = None
+
+    def copy(self):
+        """return an deep copy of the branchcache object"""
+        new = self.__class__(self, self.tipnode, self.tiprev, self.filteredhash,
+                             self._closednodes)
+        new.phaseshash = self.phaseshash
+        return new
+
+    def validfor(self, repo):
+        """Is the cache content valid regarding a repo
+
+        - False when cached tipnode is unknown or if we detect a strip.
+        - True when cache is up to date or a subset of current repo."""
+        valid = super(_topiccache, self).validfor(repo)
+        if not valid:
+            return False
+        elif not mighttopic(repo) and self.phaseshash is None:
+            # phasehash at None means this is a branchmap
+            # coming from a public only set
+            return True
+        else:
+            try:
+                valid = self.phaseshash == _phaseshash(repo, self.tiprev)
+                return valid
+            except IndexError:
+                return False
+
+    def write(self, repo):
+        # we expect (hope) mutable set to be small enough to be that computing
+        # it all the time will be fast enough
+        if not mighttopic(repo):
+            super(_topiccache, self).write(repo)
+
+    def update(self, repo, revgen):
+        """Given a branchhead cache, self, that may have extra nodes or be
+        missing heads, and a generator of nodes that are strictly a superset of
+        heads missing, this function updates self to be correct.
+        """
+        super(_topiccache, self).update(repo, revgen)
+        if mighttopic(repo):
+            self.phaseshash = _phaseshash(repo, self.tiprev)
+
+def wrapread(orig, repo):
+    # Avoiding to write cache for filter where topic applies is a good step,
+    # but we need to also avoid reading it. Existing branchmap cache might
+    # exists before the turned the feature on.
+    if mighttopic(repo):
+        return None
+    return orig(repo)
+
+# advertise topic capabilities
+
+def wireprotocaps(orig, repo, proto):
+    caps = orig(repo, proto)
+    if hasminitopic(repo):
+        caps.append('topics')
+    return caps
+
+# wrap the necessary bit
+
+def wrapclass(container, oldname, new):
+    old = getattr(container, oldname)
+    if not issubclass(old, new):
+        targetclass = new
+        # check if someone else already wrapped the class and handle that
+        if not issubclass(new, old):
+            class targetclass(new, old):
+                pass
+        setattr(container, oldname, targetclass)
+    current = getattr(container, oldname)
+    assert issubclass(current, new), (current, new, targetclass)
+
+def uisetup(ui):
+    wrapclass(context, 'changectx', topicchangectx)
+    wrapclass(branchmap, 'branchcache', _topiccache)
+    extensions.wrapfunction(branchmap, 'read', wrapread)
+    extensions.wrapfunction(wireproto, '_capabilities', wireprotocaps)
--- a/hgext3rd/topic/__init__.py	Wed Dec 06 02:08:37 2017 +0100
+++ b/hgext3rd/topic/__init__.py	Tue Dec 05 18:31:08 2017 +0100
@@ -174,7 +174,7 @@
               'topic.active': 'green',
              }
 
-__version__ = '0.5.2.dev'
+__version__ = '0.6.0.dev'
 
 testedwith = '4.1.3 4.2.3 4.3.3 4.4.1'
 minimumhgversion = '4.1'
--- a/setup.py	Wed Dec 06 02:08:37 2017 +0100
+++ b/setup.py	Tue Dec 05 18:31:08 2017 +0100
@@ -19,6 +19,7 @@
     return get_metadata()['minimumhgversion']
 
 py_modules = [
+    'hgext3rd.serverminitopic',
 ]
 py_packages = [
     'hgext3rd',
--- a/tests/test-amend.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-amend.t	Tue Dec 05 18:31:08 2017 +0100
@@ -18,9 +18,16 @@
   $ hg branch foo
   marked working directory as branch foo
   (branches are permanent and global, did you want a bookmark?)
-  $ hg amend -d '0 0'
+  $ hg amend -d '0 0' -n "this a note on the obsmarker and supported for hg>=4.4"
   $ hg debugobsolete
   07f4944404050f47db2e5c5071e0e84e7a27bba9 6a022cbb61d5ba0f03f98ff2d36319dfea1034ae 0 (*) {'ef1': '*', 'user': 'test'} (glob)
+  $ hg obslog
+  @  6a022cbb61d5 (1) adda
+  |
+  x  07f494440405 (0) adda
+       rewritten(branch) as 6a022cbb61d5 by test (Thu Jan 01 00:00:00 1970 +0000)
+         note: this a note on the obsmarker and supported for hg>=4.4
+  
   $ hg branch
   foo
   $ hg branches
@@ -145,6 +152,7 @@
       --close-branch        mark a branch as closed, hiding it from the branch
                             list
    -s --secret              use the secret phase for committing
+   -n --note VALUE          store a note on amend
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -m --message TEXT        use text as commit message
--- a/tests/test-evolve-obshistory-complex.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-evolve-obshistory-complex.t	Tue Dec 05 18:31:08 2017 +0100
@@ -71,9 +71,20 @@
   4 new orphan changesets
   $ hg fold --exact -r 3 -r 4 --date "0 0" -m "fold1"
   2 changesets folded
-  $ hg fold --exact -r 5 -r 6 --date "0 0" -m "fold2"
+  $ hg fold --exact -r 5 -r 6 --date "0 0" -m "fold2" -n "folding changesets to test"
   2 changesets folded
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg obslog -r .
+  @    100cc25b765f (9) fold2
+  |\
+  x |  0da815c333f6 (5) E
+   /     rewritten(description, content) as 100cc25b765f by test (Thu Jan 01 00:00:00 1970 +0000)
+  |        note: folding changesets to test
+  |
+  x  d9f908fde1a1 (6) F
+       rewritten(description, parent, content) as 100cc25b765f by test (Thu Jan 01 00:00:00 1970 +0000)
+         note: folding changesets to test
+  
   $ hg log -G 
   @  changeset:   9:100cc25b765f
   |  tag:         tip
@@ -305,7 +316,7 @@
 
   $ hg prune -s 12 -r 11
   1 changesets pruned
-  $ hg prune -s 14 -r 13
+  $ hg prune -s 14 -r 13 -n "this is a note stored in obsmarker in prune"
   1 changesets pruned
   $ hg log -G
   @  changeset:   15:d4a000f63ee9
@@ -410,12 +421,14 @@
   | | | | | |
   | +-------x  d0f33db50670 (13) fold1
   | | | | |      rewritten(description, parent, content) as ec31316faa9d by test (*) (glob)
+  | | | | |        note: this is a note stored in obsmarker in prune
   | | | | |
   +---x | |  e036916b63ea (11) fold0
   | |  / /     rewritten(description, parent, content) as 7b3290f6e0a0 by test (*) (glob)
   | | | |
   | | x |  0da815c333f6 (5) E
   | |  /     rewritten(description, content) as 100cc25b765f by test (*) (glob)
+  | | |        note: folding changesets to test
   | | |
   x | |    b868bc49b0a4 (7) fold0
   |\ \ \     rewritten(parent, content) as 19e14c8397fc, e036916b63ea by test (*) (glob)
@@ -425,6 +438,7 @@
   | | | | |
   | | | | x  d9f908fde1a1 (6) F
   | | | |      rewritten(description, parent, content) as 100cc25b765f by test (*) (glob)
+  | | | |        note: folding changesets to test
   | | | |
   x | | |  2a34000d3544 (1) A
    / / /     rewritten(description, content) as b868bc49b0a4 by test (*) (glob)
--- a/tests/test-evolve-obshistory.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-evolve-obshistory.t	Tue Dec 05 18:31:08 2017 +0100
@@ -308,7 +308,7 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     ROOT
   
-  $ hg split -r 'desc(A0)' -d "0 0" << EOF
+  $ hg split -r 'desc(A0)' -n "testing split" -d "0 0" << EOF
   > y
   > y
   > n
@@ -374,6 +374,7 @@
   $ hg obslog 471597cad322 --hidden --patch
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
   $ hg obslog 471597cad322 --hidden --no-graph -Tjson | python -m json.tool
@@ -389,6 +390,7 @@
                       "parent",
                       "content"
                   ],
+                  "note": "testing split",
                   "succnodes": [
                       "337fec4d2edc",
                       "f257fde29c7a"
@@ -409,6 +411,7 @@
   |
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 With the all option, it should show the three changesets
@@ -419,6 +422,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 Check that debugobshistory on the second successor after split show
@@ -428,6 +432,7 @@
   |
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 With the all option, it should show the three changesets
@@ -438,6 +443,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 Obslog with all option all should also works on the splitted commit
@@ -448,6 +454,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
 Check that debugobshistory on both successors after split show
@@ -459,6 +466,7 @@
   |/
   x  471597cad322 (1) A0
        rewritten(parent, content) as 337fec4d2edc, f257fde29c7a by test (*) (glob)
+         note: testing split
          (No patch available, too many successors (2))
   
   $ hg update 471597cad322
--- a/tests/test-metaedit.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-metaedit.t	Tue Dec 05 18:31:08 2017 +0100
@@ -134,7 +134,11 @@
   abort: editing multiple revisions without --fold is not currently supported
   [255]
 
-  $ HGEDITOR=cat hg metaedit '.^::.' --fold
+  $ HGEDITOR=cat hg metaedit '.^::.' --fold --note 'folding changesets using metaedit,
+  > and newlines'
+  abort: note cannot contain a newline
+  [255]
+  $ HGEDITOR=cat hg metaedit '.^::.' --fold --note "folding changesets using metaedit"
   HG: This is a fold of 2 changesets.
   HG: Commit message of changeset 6.
   
@@ -161,6 +165,21 @@
   |
   ~
 
+  $ hg obslog -r .
+  @    a08d35fd7d9d (9) E
+  |\
+  x |  212b2a2b87cd (8) F
+  | |    rewritten(description, user, parent, content) as a08d35fd7d9d by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |      note: folding changesets using metaedit
+  | |
+  | x  c2bd843aa246 (6) E
+  |      rewritten(description, content) as a08d35fd7d9d by test (Thu Jan 01 00:00:00 1970 +0000)
+  |        note: folding changesets using metaedit
+  |
+  x  587528abfffe (7) F
+       rewritten(user) as 212b2a2b87cd by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+
 no new commit is created here because the date is the same
   $ HGEDITOR=cat hg metaedit
   E
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-minitopic.t	Tue Dec 05 18:31:08 2017 +0100
@@ -0,0 +1,238 @@
+  $ . $TESTDIR/testlib/common.sh
+
+setup
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > share=
+  > blackbox=
+  > [web]
+  > allow_push = *
+  > push_ssl = no
+  > [phases]
+  > publish = False
+  > [paths]
+  > enabled = http://localhost:$HGPORT/
+  > disabled = http://localhost:$HGPORT2/
+  > EOF
+
+  $ hg init ./server-enabled
+  $ cat >> server-enabled/.hg/hgrc << EOF
+  > [extensions]
+  > serverminitopic=
+  > [experimental]
+  > server-mini-topic = yes
+  > EOF
+
+  $ hg share ./server-enabled ./server-disabled
+  updating working directory
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat >> server-disabled/.hg/hgrc << EOF
+  > [extensions]
+  > serverminitopic=
+  > [experimental]
+  > server-mini-topic = no
+  > EOF
+
+  $ hg init client-disabled
+  $ hg init client-enabled
+  $ cat >> client-enabled/.hg/hgrc << EOF
+  > [extensions]
+  > topic=
+  > EOF
+
+  $ hg serve -R server-enabled -p $HGPORT -d --pid-file hg1.pid --errorlog hg1.error
+  $ cat hg1.pid > $DAEMON_PIDS
+  $ hg serve -R server-disabled -p $HGPORT2 -d --pid-file hg2.pid --errorlog hg2.error
+  $ cat hg2.pid >> $DAEMON_PIDS
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities | grep -o topics
+  topics
+  $ curl --silent http://localhost:$HGPORT2/?cmd=capabilities | grep -o topics
+  [1]
+
+Pushing first changesets to the servers
+--------------------------------------
+
+  $ cd client-enabled
+  $ mkcommit c_A0
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  $ mkcommit c_B0
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
+
+Pushing new head
+----------------
+
+  $ hg up 'desc("c_A0")'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit c_C0
+  created new head
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  abort: push creates new remote head 22c9514ed811!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: push creates new remote head 22c9514ed811!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  default 0ab6d544d0efd629fda056601cfe95e73d1af210
+  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  default 0ab6d544d0efd629fda056601cfe95e73d1af210
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
+
+Pushing new topic
+-----------------
+
+  $ hg merge
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ mkcommit c_D0
+  $ hg log -G
+  @    changeset:   3:9c660cf97499
+  |\   tag:         tip
+  | |  parent:      2:22c9514ed811
+  | |  parent:      1:0ab6d544d0ef
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_D0
+  | |
+  | o  changeset:   2:22c9514ed811
+  | |  parent:      0:14faebcf9752
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_C0
+  | |
+  o |  changeset:   1:0ab6d544d0ef
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     c_B0
+  |
+  o  changeset:   0:14faebcf9752
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     c_A0
+  
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 2 changesets with 2 changes to 2 files
+  $ hg up 'desc("c_C0")'
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg topic topic_A
+  marked working directory as topic: topic_A
+  $ mkcommit c_E0
+  active topic 'topic_A' grew its first changeset
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: push creates new remote head f31af349535e!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files (+1 heads)
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  default 9c660cf97499ae01ccb6894880455c6ffa4b19cf
+  default%3Atopic_A f31af349535e413b6023f11b51a6afccf4139180
+  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  default 9c660cf97499ae01ccb6894880455c6ffa4b19cf f31af349535e413b6023f11b51a6afccf4139180
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
+
+Pushing new head to a topic
+---------------------------
+
+  $ hg up 'desc("c_D0")'
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg topic topic_A
+  marked working directory as topic: topic_A
+  $ mkcommit c_F0
+  $ hg log -G
+  @  changeset:   5:82c5842e0472
+  |  tag:         tip
+  |  topic:       topic_A
+  |  parent:      3:9c660cf97499
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c_F0
+  |
+  | o  changeset:   4:f31af349535e
+  | |  topic:       topic_A
+  | |  parent:      2:22c9514ed811
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_E0
+  | |
+  o |  changeset:   3:9c660cf97499
+  |\|  parent:      2:22c9514ed811
+  | |  parent:      1:0ab6d544d0ef
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_D0
+  | |
+  | o  changeset:   2:22c9514ed811
+  | |  parent:      0:14faebcf9752
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     c_C0
+  | |
+  o |  changeset:   1:0ab6d544d0ef
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     c_B0
+  |
+  o  changeset:   0:14faebcf9752
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     c_A0
+  
+  $ hg push enabled
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  abort: push creates new remote head 82c5842e0472 on branch 'default:topic_A'!
+  (merge or see 'hg help push' for details about pushing new heads)
+  [255]
+  $ hg push disabled
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+
+  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  default 9c660cf97499ae01ccb6894880455c6ffa4b19cf
+  default%3Atopic_A f31af349535e413b6023f11b51a6afccf4139180 82c5842e047215160763f81ae93ae42c65b20a63
+  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  default f31af349535e413b6023f11b51a6afccf4139180 82c5842e047215160763f81ae93ae42c65b20a63
+  $ cat $TESTTMP/hg1.error
+  $ cat $TESTTMP/hg2.error
--- a/tests/test-prev-next.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-prev-next.t	Tue Dec 05 18:31:08 2017 +0100
@@ -3,7 +3,7 @@
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
 
-hg prev -B should move active bookmark
+hg prev & next move to parent/child
   $ hg init test-repo
   $ cd test-repo
   $ touch a
@@ -12,6 +12,18 @@
   $ touch b
   $ hg add b
   $ hg commit -m 'added b'
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [0] added a
+  $ hg next
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  [1] added b
+
+hg prev & next respect --quiet
+  $ hg prev -q
+  $ hg next -q
+
+hg prev -B should move active bookmark
   $ hg bookmark mark
   $ hg bookmarks
    * mark                      1:6e742c9127b3
--- a/tests/test-topic-stack.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-topic-stack.t	Tue Dec 05 18:31:08 2017 +0100
@@ -864,13 +864,13 @@
   |    rewritten(parent, content) as dde94df880e9, e7ea874afbd5 by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  907f7d3c2333 (18) c_G
-  |    rewritten as b24bab30ac12 by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    rewritten(content) as b24bab30ac12 by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  3ab2eedae500 (13) c_G
-  |    rewritten as 907f7d3c2333 by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    rewritten(parent) as 907f7d3c2333 by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  c7d60a180d05 (6) c_G
-       rewritten as 3ab2eedae500 by test (Thu Jan 01 00:00:00 1970 +0000)
+       rewritten(user) as 3ab2eedae500 by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg export .
   # HG changeset patch
--- a/tests/test-touch.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-touch.t	Tue Dec 05 18:31:08 2017 +0100
@@ -151,5 +151,11 @@
 
   $ hg prune 13
   1 changesets pruned
-  $ hg touch 13 --hidden
+  $ hg touch 13 --hidden --note "testing with no successor"
   1 new orphan changesets
+  $ hg obslog -r 13 --hidden
+  x  [0-9a-f]{12} (.*) move (re)
+       pruned by test (Thu Jan 01 00:00:00 1970 +0000)
+       rewritten(.*) as [0-9a-f]{12} by test (.*) (re)
+         note: testing with no successor
+  
--- a/tests/test-tutorial.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-tutorial.t	Tue Dec 05 18:31:08 2017 +0100
@@ -937,6 +937,7 @@
   
    -a --all                 uncommit all changes when no arguments given
    -r --rev VALUE           revert commit content to REV instead
+   -n --note VALUE          store a note on uncommit
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -m --message TEXT        use text as commit message
@@ -976,6 +977,7 @@
    -r --rev VALUE [+] revision to fold
       --exact         only fold specified revisions
       --from          fold revisions linearly to working copy parent
+   -n --note VALUE    store a note on fold
    -m --message TEXT  use text as commit message
    -l --logfile FILE  read commit message from file
    -d --date DATE     record the specified date as commit date
--- a/tests/test-uncommit-interactive.t	Wed Dec 06 02:08:37 2017 +0100
+++ b/tests/test-uncommit-interactive.t	Tue Dec 05 18:31:08 2017 +0100
@@ -128,7 +128,7 @@
 Uncommit a chunk
 ================
 
-  $ hg amend --extract -i<<EOF
+  $ hg amend --extract -n "note on amend --extract" -i<<EOF
   > y
   > y
   > n
@@ -163,6 +163,14 @@
   +babar
   discard change 3/3 to 'a'? [Ynesfdaq?] n
   
+
+  $ hg obslog
+  @  678a59e5ff90 (3) another one
+  |
+  x  f70fb463d5bf (1) another one
+       rewritten(content) as 678a59e5ff90 by test (Thu Jan 01 00:00:00 1970 +0000)
+         note: note on amend --extract
+  
 The unselected part should be in the diff
 -----------------------------------------
 
@@ -220,7 +228,7 @@
    2
    3
 
-  $ hg uncommit -i<<EOF
+  $ hg uncommit -n "testing uncommit on dirty wdir" -i<<EOF
   > y
   > n
   > y
@@ -293,9 +301,11 @@
   |
   x  678a59e5ff90 (3) another one
   |    rewritten(content) as 46e35360be47 by test (Thu Jan 01 00:00:00 1970 +0000)
+  |      note: testing uncommit on dirty wdir
   |
   x  f70fb463d5bf (1) another one
        rewritten(content) as 678a59e5ff90 by test (Thu Jan 01 00:00:00 1970 +0000)
+         note: note on amend --extract
   
 
 Push the changes back to the commit and more commits for more testing