test-compat: merge mercurial-4.7 into mercurial-4.6 mercurial-4.6
authorAnton Shestakov <av6@dwimlabs.net>
Sat, 29 Feb 2020 15:18:00 +0700
branchmercurial-4.6
changeset 5163 1752a2e7a226
parent 5162 a7fb433faab6 (diff)
parent 5104 f159c32bbf2d (current diff)
child 5284 0471b9ddd0b2
test-compat: merge mercurial-4.7 into mercurial-4.6
tests/test-amend.t
tests/test-evolve-content-divergent-basic.t
tests/test-evolve-content-divergent-corner-cases.t
tests/test-evolve-content-divergent-first-changeset.t
tests/test-evolve-content-divergent-interrupted.t
tests/test-evolve-content-divergent-meta.t
tests/test-evolve-content-divergent-relocation.t
tests/test-evolve-obshistory-amend-then-fold.t
tests/test-evolve-obshistory-amend.t
tests/test-evolve-obshistory-content-divergent.t
tests/test-evolve-obshistory-fold.t
tests/test-evolve-obshistory-lots-of-splits.t
tests/test-evolve-obshistory-phase-divergent.t
tests/test-evolve-obshistory-prune.t
tests/test-evolve-obshistory-split.t
tests/test-evolve-phase-divergence.t
tests/test-evolve-public-content-divergent-corner-cases.t
tests/test-evolve-public-content-divergent-main.t
tests/test-evolve-templates.t
tests/test-evolve.t
tests/test-obsolete.t
tests/test-prune.t
tests/test-topic-stack-complex.t
tests/test-topic.t
tests/test-touch.t
tests/test-uncommit.t
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitlab/issue_templates/new-version.md	Sat Feb 29 15:18:00 2020 +0700
@@ -0,0 +1,29 @@
+This is the actual check list for releasing evolve version X.Y.Z
+
+More details in the [README file](README#L179).
+
+Preparation
+
+* [ ] merges with compatibility branches
+* [ ] compatibility tests are happy
+* [ ] update `testedwith` field
+* [ ] make sure the changelog is up to data
+* [ ] [prepare an announcement for the list/website](https://mypads.framapad.org/mypads/?/mypads/group/octobus-public-5d3rw470w/pad/view/evolution-announce-iswcp7qx)
+* [ ] sanity check install and sdist targets of setup.py
+
+Releasing
+
+* [ ] add the current date to the changelog
+* [ ] update the `__version__` field of all relevant extensions
+* [ ] create a new Debian entry
+* [ ] tag the version
+* [ ] move the `@` bookmark on the new tag
+* [ ] push/publish the tag to the main repository
+* [ ] upload the tarball to PyPI
+* [ ] add `.dev` to the `__version__` field
+* [ ] merge stable into default
+* [ ] push the result on the main repo
+* [ ] send the announcement by email to evolve-testers@mercurial-scm.org
+* [ ] send the announcement by email to mercurial@mercurial-scm.org
+* [ ] publish the announcement as a blog on https://octobus.net/blog/
+* [ ] tweet about it
--- a/.hgtags	Thu Jan 30 14:32:10 2020 +0700
+++ b/.hgtags	Sat Feb 29 15:18:00 2020 +0700
@@ -83,3 +83,5 @@
 6f37fdad7ac123ca0a76872ac4639bd1f3c248f7 9.1.0
 40795751be1c329ec23202ecd79ba45623e1df66 9.2.0
 d989bab720e5f7638a9e58fda76cf3f4b40d0611 9.2.1
+ef41094c34e162eb32ef24bf66d9776d1112751a 9.2.2
+583dc6ef3eb21fbf6574021136f32b8a1163506c 9.3.0
--- a/CHANGELOG	Thu Jan 30 14:32:10 2020 +0700
+++ b/CHANGELOG	Sat Feb 29 15:18:00 2020 +0700
@@ -1,25 +1,48 @@
 Changelog
 =========
 
-9.2.2 - in progress
+9.3.0 -- 2020-03-04
 -------------------
 
-  * pick: don't create any successors when there were no changes (issue6093),
-  * metaedit: don't change commit date by default (issue5994),
-  * rewind: preserve date
-  * py3: fix setup.py --version
-  * py3: fix documentation generation
-  * py3: fix some exception formatting
+  * compat: compatibility with Mercurial 5.3
+  * compat: drop compatibility with Mercurial 4.5
+  * compat: cleanup old compatibility code for Mercurial < 4.5
+
+  * evolve: extensive cleanup of functions, template keywords and compatibility
+    code related to obsfate and successorssets
+  * evolve: add content divergence checking to the standard pre-rewrite check
+  * evolve: improve the message associated with content divergence
+  * evolve: correctly handle --continue and --stop when relocating
+    content-divergent changesets
+
+  * exchange: dropped more bundle-1 related dead code
+
+  * help: categorizing evolve and topic commands
+
+  * obslog: make templatable
+  * obslog: show folds and use more specific verbs when possible
+
+  * topic: add a `experimental.topic.server-gate-topic-changesets` config
+
+9.2.2 -- 2020-01-31
+-------------------
+
+  * amend: cleany abort when both `--patch` and `--extract` are passed
+  * compatibility for changes in upcoming Mercurial 5.3
   * debian: allow to build with python 3
-  * topic: add more options to command synopsis string
-  * evolve: use more often seen metavariables in command synopsis strings
   * documentation: update text and add missing figures
-  * amend: cleany abort when both `--patch` and `--extract` are passed
   * evolve: also merge the date field when solving content-divergence
-  * compatibility for changes in upcoming Mercurial 5.3
   * evolve: fix content-divergence resolution when p1 is null (issue6201)
   * evolve: make sure divergence resolution doesn't undo changes (issue6203)
   * evolve: preserve date when resolving content-divergence (issue6202)
+  * evolve: use more often seen metavariables in command synopsis strings
+  * metaedit: don't change commit date by default (issue5994),
+  * pick: don't create any successors when there were no changes (issue6093),
+  * py3: fix documentation generation
+  * py3: fix setup.py --version
+  * py3: fix some exception formatting
+  * rewind: preserve date
+  * topic: add more options to command synopsis string
   * topic: fix a bug in logic of choosing destination for hg update
   * topic: fix a bug in logic to choose destination when no active topic
 
--- a/MANIFEST.in	Thu Jan 30 14:32:10 2020 +0700
+++ b/MANIFEST.in	Sat Feb 29 15:18:00 2020 +0700
@@ -1,23 +1,17 @@
-exclude contrib
+prune contrib
+prune debian
+prune .gitlab
+prune hgext3rd/evolve/hack
+
 exclude .hg-format-source
-recursive-exclude contrib *
-exclude hgext3rd/evolve/hack
 exclude .gitlab-ci.yml
-recursive-exclude hgext3rd/evolve/hack *
 exclude hgext3rd/evolve/legacy.py
 exclude Makefile
 exclude tests/test-drop.t
 exclude tests/test-inhibit.t
 exclude tests/test-oldconvert.t
-exclude docs/tutorial/.netlify
+include CHANGELOG
 include COPYING
-include docs/figures/hgview-example.png
-include docs/makefile
-include docs/*.py
-include docs/README
-include docs/*.rst
-include docs/static/*.svg
-include docs/tutorials/*.t
 include hgext3rd/__init__.py
 include hgext3rd/evolve/*.py
 include hgext3rd/evolve/thirdparty/*.py
@@ -25,19 +19,19 @@
 include hgext3rd/topic/README
 include MANIFEST.in
 include README
-include CHANGELOG
 include setup.py
 include tests/*.py
 include tests/*.sh
 include tests/testlib/*.sh
 include tests/*.t
-recursive-include docs/tutorial *.rst
-recursive-include docs/tutorial *.css
+
+exclude docs/tutorial/.netlify
+include docs/makefile
+include docs/*.py
+include docs/README
+include docs/*.rst
+include docs/static/*.svg
+include docs/tutorials/*.t
 include docs/tutorial/standalone.html
-recursive-include docs/tutorial *.js
-recursive-include docs/tutorial *.md
-recursive-include docs/tutorial *.py
-recursive-include docs/tutorial *.sh
-recursive-include docs/tutorial *.t
-prune debian
-recursive-include docs/figures *.svg
+recursive-include docs/figures *.svg *.png
+recursive-include docs/tutorial *.css *.js *.md *.py *.rst *.sh *.t
--- a/README	Thu Jan 30 14:32:10 2020 +0700
+++ b/README	Sat Feb 29 15:18:00 2020 +0700
@@ -117,7 +117,7 @@
 .. _evolution: https://bz.mercurial-scm.org/buglist.cgi?component=evolution&query_format=advanced&resolution=---
 
 The recommended way is to create Merge Request on
-https://dev.heptapod.net/mercurial/evolve. To do so, create an account and
+https://foss.heptapod.net/mercurial/evolve. To do so, create an account and
 request access. You'll then be able to create topic based merge request.
 
 Alternatively, you can use the patchbomb extension to send email to `mercurial
@@ -175,3 +175,61 @@
     byteify-strings = python3 ~/workspace/octobus/mercurial-devel/contrib/byteify-strings.py --dictiter --treat-as-kwargs kwargs opts commitopts TROUBLES --allow-attr-methods
     byteify-strings:mode.input = file
     byteify-strings:mode.output = pipe
+
+Release Checklist
+=================
+
+* make sure the tests are happy on all supported versions,
+
+  You can use the `contrib/merge-test-compat.sh` to merge with the test
+  compatibility branches.
+
+* make sure there is no code difference between the compat branches and stable
+  (no diff within `hgext3rd/`),
+
+* update the `testedwith` variable for all extensions (remove '.dev'):
+
+  - hgext3rd/evolve/metadata.py
+  - hgext3rd/topic/__init__.py
+  - hgext3rd/pullbundle.py
+
+* make sure the changelog is up to date,
+
+* add a date to the changelog entry for the target version,
+
+* update the `__version__` field of all relevant extensions:
+
+  - hgext3rd/evolve/metadata.py
+  - hgext3rd/topic/__init__.py
+  - hgext3rd/pullbundle.py (if touched)
+
+* create a new Debian entry:
+
+  - debchange --newversion x.y.z-1 "new upstream release"
+  - debchange --release
+
+* sanity check install and sdist targets of setup.py:
+
+  - python setup.py install --home=$(mktemp -d)
+  - python setup.py sdist
+
+* tag the commit,
+
+* push and publish the tag,
+
+* upload the tarball to PyPI,
+
+* make an announcement on evolve-testers@mercurial-scm.org (possibly on
+  mercurial@mercurial-scm.org too),
+
+* bump versions of all extensions and add '.dev' (see existing commits as an
+  example):
+
+  - hgext3rd/evolve/metadata.py
+  - hgext3rd/topic/__init__.py
+  - hgext3rd/pullbundle.py
+
+  The version we use on the stable branch during development should be
+  `x.y.z+1.dev`. The version of the default branch should be `x.y+1.0.dev`.
+
+* merge stable into default.
--- a/debian/changelog	Thu Jan 30 14:32:10 2020 +0700
+++ b/debian/changelog	Sat Feb 29 15:18:00 2020 +0700
@@ -1,4 +1,16 @@
-mercurial-evolve (9.2.1-1.1) UNRELEASED; urgency=medium
+mercurial-evolve (9.3.0-1) unstable; urgency=medium
+
+  * new upstream release
+
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Wed, 04 Mar 2020 16:47:58 +0100
+
+mercurial-evolve (9.2.2-1) unstable; urgency=medium
+
+  * new upstream release
+
+ -- Anton Shestakov <av6@dwimlabs.net>  Fri, 31 Jan 2020 14:10:11 +0700
+
+mercurial-evolve (9.2.1-1.1) unstable; urgency=medium
 
   * build with pybuild and sphinxdoc
   * update debhelper compat to 10
--- a/hgext3rd/evolve/__init__.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/__init__.py	Sat Feb 29 15:18:00 2020 +0700
@@ -251,7 +251,6 @@
 """.strip()
 
 import sys
-import struct
 
 try:
     from mercurial import registrar
@@ -326,9 +325,6 @@
               b'evolve.operation': b'bold'
               }
 
-_pack = struct.pack
-_unpack = struct.unpack
-
 aliases, entry = cmdutil.findcmd(b'commit', commands.table)
 
 # This extension contains the following code
@@ -465,7 +461,8 @@
     _alias, statuscmd = cmdutil.findcmd(b'status', commands.table)
     pstatusopts = [o for o in statuscmd[1] if o[1] != b'rev']
 
-    @eh.command(b'pstatus', pstatusopts)
+    @eh.command(b'pstatus', pstatusopts,
+                **compat.helpcategorykwargs('CATEGORY_WORKING_DIRECTORY'))
     def pstatus(ui, repo, *args, **kwargs):
         """show status combining committed and uncommited changes
 
@@ -480,7 +477,8 @@
     _alias, diffcmd = cmdutil.findcmd(b'diff', commands.table)
     pdiffopts = [o for o in diffcmd[1] if o[1] != b'rev']
 
-    @eh.command(b'pdiff', pdiffopts)
+    @eh.command(b'pdiff', pdiffopts,
+                **compat.helpcategorykwargs('CATEGORY_WORKING_DIRECTORY'))
     def pdiff(ui, repo, *args, **kwargs):
         """show diff combining committed and uncommited changes
 
@@ -705,10 +703,10 @@
         debugcommand = b"hg evolve --list --content-divergent"
         basemsg = _(b"%s has diverged, use '%s' to resolve the issue")
         solvemsg = basemsg % (shortnode, debugcommand)
-    elif reason == b'superseed':
+    elif reason == b'superseded':
         msg = _(b"use 'hg evolve' to update to its successor: %s")
         solvemsg = msg % successors[0]
-    elif reason == b'superseed_split':
+    elif reason == b'superseded_split':
         msg = _(b"use 'hg evolve' to update to its tipmost successor: %s")
 
         if len(successors) <= 2:
@@ -736,10 +734,10 @@
             rev = repo[scmutil.revsingle(unfilteredrepo, changeid)]
             reason, successors = obshistory._getobsfateandsuccs(unfilteredrepo, rev.node())
 
-            # Be more precise in case the revision is superseed
-            if reason == b'superseed':
+            # Be more precise in case the revision is superseded
+            if reason == b'superseded':
                 reason = _(b"successor: %s") % successors[0]
-            elif reason == b'superseed_split':
+            elif reason == b'superseded_split':
                 if len(successors) <= 2:
                     reason = _(b"successors: %s") % b", ".join(successors)
                 else:
@@ -947,7 +945,7 @@
 def _findprevtarget(repo, displayer, movebookmark=False, topic=True):
     target = bookmark = None
     wkctx = repo[None]
-    p1 = wkctx.parents()[0]
+    p1 = wkctx.p1()
     parents = p1.parents()
     currenttopic = _getcurrenttopic(repo)
 
@@ -989,7 +987,8 @@
      (b'n', b'dry-run', False,
         _(b'do not perform actions, just print what would be done'))],
     b'[OPTION]...',
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_WORKING_DIRECTORY'))
 def cmdprevious(ui, repo, **opts):
     """update to parent revision
 
@@ -1023,15 +1022,14 @@
         target, bookmark = _findprevtarget(repo, displayer,
                                            opts.get('move_bookmark'), topic)
         if target is not None:
-            backup = repo.ui.backupconfig(b'_internal', b'keep-topic')
-            try:
-                if topic and _getcurrenttopic(repo) != _gettopic(target):
-                    repo.ui.setconfig(b'_internal', b'keep-topic', b'yes',
-                                      source=b'topic-extension')
+            configoverride = util.nullcontextmanager()
+            if topic and _getcurrenttopic(repo) != _gettopic(target):
+                configoverride = repo.ui.configoverride({
+                    (b'_internal', b'keep-topic'): b'yes'
+                }, source=b'topic-extension')
+            with configoverride:
                 _prevupdate(repo, displayer, target, bookmark, dryrunopt,
                             mergeopt)
-            finally:
-                repo.ui.restoreconfig(backup)
             return 0
         else:
             return 1
@@ -1048,7 +1046,8 @@
      (b'n', b'dry-run', False,
       _(b'do not perform actions, just print what would be done'))],
     b'[OPTION]...',
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_WORKING_DIRECTORY'))
 def cmdnext(ui, repo, **opts):
     """update to next child revision
 
@@ -1339,8 +1338,16 @@
         if entry[0] == b"evolution":
             break
     else:
-        help.helptable.append(([b"evolution"], _(b"Safely Rewriting History"),
-                               _helploader))
+        if util.safehasattr(help, 'TOPIC_CATEGORY_CONCEPTS'):
+            help.helptable.append(([b"evolution"],
+                                   _(b"Safely Rewriting History"),
+                                   _helploader,
+                                   help.TOPIC_CATEGORY_CONCEPTS))
+        else:
+            # hg <= 4.7 (c303d65d2e34)
+            help.helptable.append(([b"evolution"],
+                                   _(b"Safely Rewriting History"),
+                                   _helploader))
         help.helptable.sort()
 
 evolvestateversion = 0
--- a/hgext3rd/evolve/cmdrewrite.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/cmdrewrite.py	Sat Feb 29 15:18:00 2020 +0700
@@ -26,7 +26,6 @@
     merge,
     node,
     obsolete,
-    obsutil,
     patch,
     phases,
     pycompat,
@@ -36,11 +35,6 @@
 
 from mercurial.i18n import _
 
-try:
-    from mercurial.utils.dateutil import datestr
-except ImportError: # hg <= 4.5
-    from mercurial.util import datestr
-
 from . import (
     compat,
     state,
@@ -56,6 +50,7 @@
 commitopts2 = commands.commitopts2
 mergetoolopts = commands.mergetoolopts
 stringio = util.stringio
+precheck_contentdiv = rewriteutil.precheck_contentdiv
 
 # option added by evolve
 
@@ -66,9 +61,6 @@
     if not note:
         return
 
-    if not compat.isobsnotesupported():
-        ui.warn(_(b"current hg version does not support storing"
-                  b" note in obsmarker\n"))
     if len(note) > 255:
         raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
     if b'\n' in note:
@@ -109,7 +101,8 @@
      (b'n', b'note', b'', _(b'store a note on amend'), _(b'TEXT')),
      ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt,
     _(b'[OPTION]... [FILE]...'),
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_COMMITTING'))
 def amend(ui, repo, *pats, **opts):
     """combine a changeset with updates and replace it with a new one
 
@@ -274,7 +267,7 @@
     fp.write(b"# HG changeset patch\n")
     fp.write(b"# User %s\n" % ctx.user())
     fp.write(b"# Date %d %d\n" % ctx.date())
-    fp.write(b"#      %s\n" % datestr(ctx.date()))
+    fp.write(b"#      %s\n" % compat.datestr(ctx.date()))
     if branch and branch != b'default':
         fp.write(b"# Branch %s\n" % branch)
     fp.write(b"# Node ID %s\n" % node.hex(nodeval))
@@ -458,7 +451,8 @@
      (b'', b'revert', False, _(b'discard working directory changes after uncommit')),
      (b'n', b'note', b'', _(b'store a note on uncommit'), _(b'TEXT')),
      ] + commands.walkopts + commitopts + commitopts2 + commitopts3,
-    _(b'[OPTION]... [FILE]...'))
+    _(b'[OPTION]... [FILE]...'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def uncommit(ui, repo, *pats, **opts):
     """move changes from parent revision to working directory
 
@@ -694,7 +688,8 @@
      (b'n', b'note', b'', _(b'store a note on fold'), _(b'TEXT')),
      ] + commitopts + commitopts2 + commitopts3,
     _(b'hg fold [OPTION]... [-r] REV...'),
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def fold(ui, repo, *revs, **opts):
     """fold multiple revisions into a single one
 
@@ -806,9 +801,17 @@
                                                          p2.node()],
                                                         commitopts=commitopts)
             phases.retractboundary(repo, tr, targetphase, [newid])
-            replacements = {ctx.node(): [newid] for ctx in allctx}
-            scmutil.cleanupnodes(repo, replacements, operation=b"fold",
-                                 metadata=metadata)
+            # Use this condition as a proxy since the commit we care about
+            # (b99903534e06) didn't change any signatures.
+            if util.safehasattr(scmutil, 'nullrev'):
+                replacements = {tuple(ctx.node() for ctx in allctx): [newid]}
+                scmutil.cleanupnodes(repo, replacements, operation=b"fold",
+                                     metadata=metadata)
+            else:
+                # hg <= 4.7 (b99903534e06)
+                replacements = {ctx.node(): [newid] for ctx in allctx}
+                scmutil.cleanupnodes(repo, replacements, operation=b"fold",
+                                     metadata=metadata)
             tr.close()
         finally:
             tr.release()
@@ -824,7 +827,8 @@
      (b'', b'fold', None, _(b"also fold specified revisions into one")),
      (b'n', b'note', b'', _(b'store a note on metaedit'), _(b'TEXT')),
      ] + commitopts + commitopts2 + commitopts3,
-    _(b'hg metaedit [OPTION]... [[-r] REV]...'))
+    _(b'hg metaedit [OPTION]... [[-r] REV]...'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def metaedit(ui, repo, *revs, **opts):
     """edit commit information
 
@@ -934,9 +938,15 @@
                     metadata[b'note'] = opts['note']
 
                 phases.retractboundary(repo, tr, targetphase, [newid])
-                obsolete.createmarkers(repo, [(ctx, (repo[newid],))
-                                              for ctx in allctx],
-                                       metadata=metadata, operation=b"metaedit")
+                # Use this condition as a proxy since the commit we care about
+                # (6335c0de80fa) didn't change any signatures.
+                if not util.safehasattr(context, 'bin'):
+                    obsolete.createmarkers(repo, [(tuple(allctx), (repo[newid],))],
+                                           metadata=metadata, operation=b"metaedit")
+                else:
+                    # hg <= 4.7 (6335c0de80fa)
+                    obsolete.createmarkers(repo, [(ctx, (repo[newid],)) for ctx in allctx],
+                                           metadata=metadata, operation=b"metaedit")
             else:
                 ui.status(_(b"nothing changed\n"))
             tr.close()
@@ -982,7 +992,8 @@
      (b'B', b'bookmark', [], _(b"remove revs only reachable from given"
                                b" bookmark"), _(b'BOOKMARK'))] + metadataopts,
     _(b'[OPTION]... [-r] REV...'),
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 # XXX -U  --noupdate option to prevent wc update and or bookmarks update ?
 def cmdprune(ui, repo, *revs, **opts):
     """mark changesets as obsolete or succeeded by another changeset
@@ -1092,7 +1103,7 @@
                 newnode = wdp
 
                 while newnode in precs or newnode.obsolete():
-                    newnode = newnode.parents()[0]
+                    newnode = newnode.p1()
         else:
             # no need to update anywhere as wdp is not related to revs
             # being pruned
@@ -1159,6 +1170,11 @@
                 if not dest.obsolete() and dest.node() not in replacements:
                     moves[ctx.node()] = dest.node()
                     break
+        if len(sucs) == 1 and len(precs) > 1 and fold:
+            # hg <= 4.7 (b99903534e06)
+            # Using a proxy condition to let people wrap cleanupnodes()
+            if util.safehasattr(scmutil, 'nullrev'):
+                replacements = {tuple(p.node() for p in precs): [s.node() for s in sucs]}
         scmutil.cleanupnodes(repo, replacements, operation=b"prune", moves=moves,
                              metadata=metadata)
 
@@ -1176,7 +1192,8 @@
      (b'n', b'note', b'', _(b"store a note on split"), _(b'TEXT')),
      ] + commitopts + commitopts2 + commitopts3,
     _(b'hg split [OPTION]... [-r REV] [FILE]...'),
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def cmdsplit(ui, repo, *pats, **opts):
     """split a changeset into smaller changesets
 
@@ -1353,7 +1370,8 @@
       b'mark the new revision as successor of the old one potentially creating '
       b'divergence')],
     # allow to choose the seed ?
-    _(b'[OPTION]... [-r] REV...'))
+    _(b'[OPTION]... [-r] REV...'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def touch(ui, repo, *revs, **opts):
     """create successors identical to their predecessors but the changeset ID
 
@@ -1371,7 +1389,11 @@
 
     duplicate = opts['duplicate']
     if not duplicate:
-        rewriteutil.precheck(repo, revs, b'touch')
+        # Override allowdivergence=true because we'll do our own checking later
+        # instead
+        overrides = {(b'experimental', b'evolution.allowdivergence'): b"true"}
+        with ui.configoverride(overrides, b'touch'):
+            rewriteutil.precheck(repo, revs, b'touch')
     tmpl = utility.shorttemplate
     displayer = compat.changesetdisplayer(ui, repo, {b'template': tmpl})
     with repo.wlock(), repo.lock(), repo.transaction(b'touch'):
@@ -1393,27 +1415,7 @@
         p2 = newmapping.get(p2, p2)
 
         if not (duplicate or allowdivergence):
-            # If reviving this cset creates divergence, let's ask user
-            # what to do: create divergence or duplicate
-
-            # We need to check two cases that can cause divergence:
-            # case 1: the rev being revived has a non-obsolete successor (easily
-            #     detected by successorssets)
-            sset = obsutil.successorssets(repo, ctx.node())
-            nodivergencerisk = (len(sset) == 0
-                                or (len(sset) == 1
-                                    and len(sset[0]) == 1
-                                    and repo[sset[0][0]].rev() == ctx.rev()
-                                ))
-            if nodivergencerisk:
-                # case 2: one of the precursors of the rev being revived has a
-                #     non-obsolete successor (we need divergentsets for this)
-                from . import evolvecmd
-                if evolvecmd.divergentsets(repo, ctx):
-                    nodivergencerisk = False
-            if nodivergencerisk:
-                duplicate = False
-            else:
+            if precheck_contentdiv(repo, ctx):
                 displayer.show(ctx)
                 index = ui.promptchoice(
                     _(b"reviving this changeset will create divergence"
@@ -1421,9 +1423,7 @@
                       b" (d)uplicate the changeset? $$ &Allowdivergence $$ "
                       b"&Duplicate"), 0)
                 choice = [b'allowdivergence', b'duplicate'][index]
-                if choice == b'allowdivergence':
-                    duplicate = False
-                else:
+                if choice == b'duplicate':
                     duplicate = True
 
         updates = []
@@ -1454,7 +1454,8 @@
      (b'c', b'continue', False, b'continue interrupted pick'),
      (b'a', b'abort', False, b'abort interrupted pick'),
      ] + mergetoolopts,
-    _(b'[OPTION]... [-r] REV'))
+    _(b'[OPTION]... [-r] REV'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def cmdpick(ui, repo, *revs, **opts):
     """move a commit on the top of working directory parent and updates to it."""
 
--- a/hgext3rd/evolve/compat.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/compat.py	Sat Feb 29 15:18:00 2020 +0700
@@ -13,11 +13,10 @@
 from mercurial import (
     context,
     copies,
-    encoding,
     mdiff,
     obsolete,
-    obsutil,
     pycompat,
+    registrar,
     repair,
     scmutil,
     util,
@@ -41,9 +40,9 @@
     changesetdiffer = logcmdutil.changesetdiffer
 except (AttributeError, ImportError):
     from mercurial import cmdutil
-    changesetdisplayer = cmdutil.show_changeset
-    changesetprinter = cmdutil.changeset_printer
-    displaygraph = cmdutil.displaygraph
+    changesetdisplayer = cmdutil.show_changeset  # pytype: disable=module-attr
+    changesetprinter = cmdutil.changeset_printer  # pytype: disable=module-attr
+    displaygraph = cmdutil.displaygraph  # pytype: disable=module-attr
     changesetdiffer = None
 
 # hg <= 5.3 (c21aca51b392)
@@ -51,7 +50,7 @@
     from mercurial import pathutil
     dirs = pathutil.dirs
 except (AttributeError, ImportError):
-    dirs = util.dirs
+    dirs = util.dirs  # pytype: disable=module-attr
 
 from . import (
     exthelper,
@@ -59,14 +58,6 @@
 
 eh = exthelper.exthelper()
 
-def isobsnotesupported():
-    # hack to know obsnote is supported. The patches for obsnote support was
-    # pushed before the obsfateprinter patches, so this will serve as a good
-    # check
-    if not obsutil:
-        return False
-    return util.safehasattr(obsutil, 'obsfateprinter')
-
 # Evolution renaming compat
 
 TROUBLES = {
@@ -75,6 +66,7 @@
     r'PHASEDIVERGENT': b'phase-divergent',
 }
 
+# hg <= 4.6 (bec1212eceaa)
 if util.safehasattr(uimod.ui, 'makeprogress'):
     def progress(ui, topic, pos, item=b"", unit=b"", total=None):
         progress = ui.makeprogress(topic, unit, total)
@@ -96,7 +88,7 @@
 
 def memfilectx(repo, ctx, fctx, flags, copied, path):
     # XXX Would it be better at the module level?
-    varnames = context.memfilectx.__init__.__code__.co_varnames
+    varnames = context.memfilectx.__init__.__code__.co_varnames  # pytype: disable=attribute-error
 
     if r"copysource" in varnames:
         mctx = context.memfilectx(repo, ctx, fctx.path(), fctx.data(),
@@ -108,12 +100,12 @@
         mctx = context.memfilectx(repo, ctx, fctx.path(), fctx.data(),
                                   islink=b'l' in flags,
                                   isexec=b'x' in flags,
-                                  copied=copied.get(path))
+                                  copied=copied.get(path))  # pytype: disable=wrong-keyword-args
     else:
         mctx = context.memfilectx(repo, fctx.path(), fctx.data(),
                                   islink=b'l' in flags,
                                   isexec=b'x' in flags,
-                                  copied=copied.get(path))
+                                  copied=copied.get(path))  # pytype: disable=wrong-keyword-args
     return mctx
 
 def strdiff(a, b, fn1, fn2):
@@ -136,14 +128,17 @@
 
 # date related
 
+# hg <= 4.5 (c6061cadb400)
 try:
     import mercurial.utils.dateutil
+    datestr = mercurial.utils.dateutil.datestr
     makedate = mercurial.utils.dateutil.makedate
     parsedate = mercurial.utils.dateutil.parsedate
 except ImportError:
     import mercurial.util
-    makedate = mercurial.util.makedate
-    parsedate = mercurial.util.parsedate
+    datestr = mercurial.util.datestr  # pytype: disable=module-attr
+    makedate = mercurial.util.makedate  # pytype: disable=module-attr
+    parsedate = mercurial.util.parsedate  # pytype: disable=module-attr
 
 def wireprotocommand(exthelper, name, args=b'', permission=b'pull'):
     try:
@@ -174,7 +169,7 @@
 # scmutil.bookmarkrevs
 # This change is a part of 4.7 cycle, so drop this when we drop support for 4.6
 try:
-    bmrevset = repair.stripbmrevset
+    bmrevset = repair.stripbmrevset  # pytype: disable=module-attr
 except AttributeError:
     bmrevset = scmutil.bookmarkrevs
 
@@ -220,9 +215,9 @@
 
     # hg < 4.8 compat (dc50121126ae)
     try:
-        limit = copies._findlimit(repo, c1, c2)
+        limit = copies._findlimit(repo, c1, c2)  # pytype: disable=module-attr
     except (AttributeError, TypeError):
-        limit = copies._findlimit(repo, c1.rev(), c2.rev())
+        limit = copies._findlimit(repo, c1.rev(), c2.rev())  # pytype: disable=module-attr
     if limit is None:
         # no common ancestor, no copies
         return {}, {}, {}, {}, {}
@@ -263,11 +258,11 @@
     bothnew = sorted(addedinm1 & addedinm2)
     if tca == base:
         # unmatched file from base
-        u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
+        u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2)  # pytype: disable=module-attr
         u1u, u2u = u1r, u2r
     else:
         # unmatched file from base (DAG rotation in the graft case)
-        u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
+        u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2,  # pytype: disable=module-attr
                                              baselabel=b'base')
         # unmatched file from topological common ancestors (no DAG rotation)
         # need to recompute this for directory move handling when grafting
@@ -276,18 +271,18 @@
             m1f = m1.filesnotin(mta, repo.narrowmatch())
             m2f = m2.filesnotin(mta, repo.narrowmatch())
             baselabel = b'topological common ancestor'
-            u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1f, m2f,
+            u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1f, m2f,  # pytype: disable=module-attr
                                                  baselabel=baselabel)
         else:
-            u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
+            u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1.filesnotin(mta),  # pytype: disable=module-attr
                                                  m2.filesnotin(mta),
                                                  baselabel=b'topological common ancestor')
 
     for f in u1u:
-        copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
+        copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)  # pytype: disable=module-attr
 
     for f in u2u:
-        copies._checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
+        copies._checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)  # pytype: disable=module-attr
 
     copy = dict(data1[b'copy'])
     copy.update(data2[b'copy'])
@@ -295,10 +290,10 @@
     fullcopy.update(data2[b'fullcopy'])
 
     if dirtyc1:
-        copies._combinecopies(data2[b'incomplete'], data1[b'incomplete'], copy, diverge,
+        copies._combinecopies(data2[b'incomplete'], data1[b'incomplete'], copy, diverge,  # pytype: disable=module-attr
                               incompletediverge)
     else:
-        copies._combinecopies(data1[b'incomplete'], data2[b'incomplete'], copy, diverge,
+        copies._combinecopies(data1[b'incomplete'], data2[b'incomplete'], copy, diverge,  # pytype: disable=module-attr
                               incompletediverge)
 
     renamedelete = {}
@@ -334,19 +329,19 @@
              b'incompletediverge': bothincompletediverge
              }
     for f in bothnew:
-        copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
-        copies._checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
+        copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)  # pytype: disable=module-attr
+        copies._checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)  # pytype: disable=module-attr
 
     if dirtyc1 and dirtyc2:
         pass
     elif dirtyc1:
         # incomplete copies may only be found on the "dirty" side for bothnew
         assert not both2[b'incomplete']
-        remainder = copies._combinecopies({}, both1[b'incomplete'], copy, bothdiverge,
+        remainder = copies._combinecopies({}, both1[b'incomplete'], copy, bothdiverge,  # pytype: disable=module-attr
                                           bothincompletediverge)
     elif dirtyc2:
         assert not both1[b'incomplete']
-        remainder = copies._combinecopies({}, both2[b'incomplete'], copy, bothdiverge,
+        remainder = copies._combinecopies({}, both2[b'incomplete'], copy, bothdiverge,  # pytype: disable=module-attr
                                           bothincompletediverge)
     else:
         # incomplete copies and divergences can't happen outside grafts
@@ -444,85 +439,14 @@
 if not fixupstreamed:
     copies._fullcopytracing = fixedcopytracing
 
-if not util.safehasattr(obsutil, "_succs"):
-    class _succs(list):
-        """small class to represent a successors with some metadata about it"""
-
-        def __init__(self, *args, **kwargs):
-            super(_succs, self).__init__(*args, **kwargs)
-            self.markers = set()
-
-        def copy(self):
-            new = _succs(self)
-            new.markers = self.markers.copy()
-            return new
-
-        @util.propertycache
-        def _set(self):
-            # immutable
-            return set(self)
-
-        def canmerge(self, other):
-            return self._set.issubset(other._set)
-else:
-    from mercurial.obsutil import _succs
-
-def wrap_succs(succs):
-    """ Wrap old data format of successorsets (tuple) only if if's not yet a
-    _succs instance
-    """
-
-    if not util.safehasattr(succs, "markers"):
-        return _succs(succs)
-    else:
-        return succs
-
-if not util.safehasattr(obsutil, "markersdates"):
-    MARKERS_DATE_COMPAT = True
-else:
-    MARKERS_DATE_COMPAT = False
-
-def markersdates(markers):
-    """returns the list of dates for a list of markers
-    """
-    if MARKERS_DATE_COMPAT is False:
-        return obsutil.markersdates(markers)
-
-    return [m[4] for m in markers]
-
-if not util.safehasattr(obsutil, "markersusers"):
-    MARKERS_USERS_COMPAT = True
-else:
-    MARKERS_USERS_COMPAT = False
-
-def markersusers(markers):
-    """ Returns a sorted list of markers users without duplicates
-    """
-    if MARKERS_USERS_COMPAT is False:
-        return obsutil.markersusers(markers)
-
-    markersmeta = [dict(m[3]) for m in markers]
-    users = set(encoding.tolocal(meta[b'user']) for meta in markersmeta
-                if meta.get(b'user'))
-
-    return sorted(users)
-
-if not util.safehasattr(obsutil, "markersoperations"):
-    MARKERS_OPERATIONS_COMPAT = True
-else:
-    MARKERS_OPERATIONS_COMPAT = False
-
-def markersoperations(markers):
-    """ Returns a sorted list of markers operations without duplicates
-    """
-    if MARKERS_OPERATIONS_COMPAT is False:
-        return obsutil.markersoperations(markers)
-
-    markersmeta = [dict(m[3]) for m in markers]
-    operations = set(meta.get(b'operation') for meta in markersmeta
-                     if meta.get(b'operation'))
-
-    return sorted(operations)
+# help category compatibility
+# hg <= 4.7 (c303d65d2e34)
+def helpcategorykwargs(categoryname):
+    """Backwards-compatible specification of the helpategory argument."""
+    category = getattr(registrar.command, categoryname, None)
+    if not category:
+        return {}
+    return {'helpcategory': category}
 
 # nodemap.get and index.[has_node|rev|get_rev]
 # hg <= 5.3 (02802fa87b74)
--- a/hgext3rd/evolve/depthcache.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/depthcache.py	Sat Feb 29 15:18:00 2020 +0700
@@ -16,6 +16,12 @@
     scmutil,
 )
 
+try:
+    from mercurial.utils.stringutil import forcebytestr
+except ImportError:
+    # hg <= 4.5 (f99d64e8a4e4)
+    from mercurial.util import forcebytestr
+
 from . import (
     compat,
     error,
@@ -206,5 +212,5 @@
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
-            repo.ui.log(b'depthcache', b'could not write update %s\n' % exc)
-            repo.ui.debug(b'depthcache: could not write update %s\n' % exc)
+            repo.ui.log(b'depthcache', b'could not write update %s\n' % forcebytestr(exc))
+            repo.ui.debug(b'depthcache: could not write update %s\n' % forcebytestr(exc))
--- a/hgext3rd/evolve/evolvecmd.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/evolvecmd.py	Sat Feb 29 15:18:00 2020 +0700
@@ -16,7 +16,6 @@
     cmdutil,
     commands,
     context,
-    copies,
     error,
     encoding,
     hg,
@@ -62,7 +61,7 @@
     returns a tuple (bool, newnode) where,
         bool: a boolean value indicating whether the instability was solved
         newnode: if bool is True, then the newnode of the resultant commit
-                 formed. newnode can be node, when resolution led to no new
+                 formed. newnode can be None, when resolution led to no new
                  commit. If bool is False, this is ".".
     """
     tr = repo.currenttransaction()
@@ -123,43 +122,28 @@
         ui.warn(_(b"cannot solve instability of %s, skipping\n") % orig)
         return (False, b".")
     obs = pctx
-    newer = obsutil.successorssets(repo, obs.node())
-    # search of a parent which is not killed, but also isn't the orig
-    while not newer or newer == [()] or newer[0][0] == orig.node():
-        ui.debug(b"stabilize target %s is plain dead,"
-                 b" trying to stabilize on its parent\n" %
-                 obs)
-        obs = obs.parents()[0]
-        newer = obsutil.successorssets(repo, obs.node())
-    if len(newer) > 1:
-        msg = _(b"skipping %s: divergent rewriting. can't choose "
-                b"destination\n") % obs
-        ui.write_err(msg)
-        return (False, b".")
-    targets = newer[0]
-    assert targets
-    if len(targets) > 1:
-        # split target, figure out which one to pick, are they all in line?
-        targetrevs = [repo[r].rev() for r in targets]
-        roots = repo.revs(b'roots(%ld)', targetrevs)
-        heads = repo.revs(b'heads(%ld)', targetrevs)
-        if len(roots) > 1 or len(heads) > 1:
-            cheader = _(b"ancestor '%s' split over multiple topological"
-                        b" branches.\nchoose an evolve destination:") % orig
-            selectedrev = utility.revselectionprompt(ui, repo, list(heads),
-                                                     cheader)
-            if selectedrev is None:
+    try:
+        newer = utility._singlesuccessor(repo, obs)
+        # search of a parent which isn't the orig
+        while repo[newer].node() == orig.node():
+            obs = obs.parents()[0]
+            newer = utility._singlesuccessor(repo, obs)
+    except utility.MultipleSuccessorsError as exc:
+        if exc.divergenceflag:
+            msg = _(b"skipping %s: divergent rewriting. can't choose "
+                    b"destination\n") % obs
+            ui.write_err(msg)
+            return (False, b".")
+        if exc.splitflag:
+            splitsucc = utility.picksplitsuccessor(ui, repo, obs, orig)
+            if not splitsucc[0]:
                 msg = _(b"could not solve instability, "
                         b"ambiguous destination: "
                         b"parent split across two branches\n")
                 ui.write_err(msg)
                 return (False, b".")
-            target = repo[selectedrev]
-        else:
-            target = repo[heads.first()]
-    else:
-        target = targets[0]
-    target = repo[target]
+            newer = splitsucc[1]
+    target = repo[newer]
     if not ui.quiet or confirm:
         repo.ui.write(_(b'move:'), label=b'evolve.operation')
         displayer.show(orig)
@@ -179,8 +163,8 @@
         if progresscb:
             progresscb()
         with state.saver(evolvestate, {b'current': orig.node()}):
-            newid = relocate(repo, orig, target, evolvestate, pctx,
-                             keepbranch, b'orphan')
+            newid = _relocate(repo, orig, target, evolvestate, pctx,
+                              keepbranch, b'orphan')
             return (True, newid)
 
 def _solvephasedivergence(ui, repo, bumped, evolvestate, displayer,
@@ -238,8 +222,8 @@
             _(b'rebasing to destination parent: %s\n') % prec.p1())
         with state.saver(evolvestate, {b'current': bumped.hex(),
                                        b'precursor': prec.hex()}):
-            newnode = relocate(repo, bumped, prec.p1(), evolvestate,
-                               category=b'phasedivergent')
+            newnode = _relocate(repo, bumped, prec.p1(), evolvestate,
+                                category=b'phasedivergent')
             if newnode is not None:
                 new = repo[newnode]
                 obsolete.createmarkers(repo, [(bumped, (new,))],
@@ -300,17 +284,9 @@
         repo.dirstate.setparents(newid, nodemod.nullid)
     return (True, replacementnode)
 
-def _solvedivergent(ui, repo, divergent, evolvestate, displayer, dryrun=False,
-                    confirm=False, progresscb=None):
-    """tries to solve content-divergence of a changeset
-
-    returns a tuple (bool, newnode) where,
-        bool: a boolean value indicating whether the instability was solved
-        newnode: if bool is True, then the newnode of the resultant commit
-                 formed. newnode can be node, when resolution led to no new
-                 commit. If bool is False, this is ".".
-    """
-    repo = repo.unfiltered()
+def _prepcontentdivresolution(ui, repo, divergent, other, evolvestate):
+    """ if relocation required, decide which divergent cset will be relocated
+    to the other side"""
     divergent = repo[divergent.rev()]
     evolvestate[b'divergent'] = divergent.node()
     evolvestate[b'orig-divergent'] = divergent.node()
@@ -320,32 +296,15 @@
     # strip that relocated commit. However if `--all` is passed, we need to
     # reset this value for each content-divergence resolution which we are doing
     # below.
-    evolvestate[b'relocated'] = None
-    evolvestate[b'relocating'] = False
+    evolvestate[b'relocated-other'] = None
+    evolvestate[b'relocating-other'] = False
+    evolvestate[b'relocated-div'] = None
+    evolvestate[b'relocating-div'] = False
+    evolvestate[b'relocation-req'] = False
     # in case or relocation we get a new other node, we need to store the old
     # other for purposes like `--abort` or `--stop`
     evolvestate[b'old-other'] = None
-    base, others = divergentdata(divergent)
-
-    # we don't handle split in content-divergence yet
-    if len(others) > 1:
-        othersstr = b"[%s]" % (b','.join([bytes(i) for i in others]))
-        msg = _(b"skipping %s: %s with a changeset that got split"
-                b" into multiple ones:\n"
-                b"|[%s]\n"
-                b"| This is not handled by automatic evolution yet\n"
-                b"| You have to fallback to manual handling with commands "
-                b"such as:\n"
-                b"| - hg touch -D\n"
-                b"| - hg prune\n"
-                b"| \n"
-                b"| You should contact your local evolution Guru for help.\n"
-                ) % (divergent, TROUBLES['CONTENTDIVERGENT'], othersstr)
-        ui.write_err(msg)
-        return (False, b".")
-    other = others[0]
-    evolvestate[b'other-divergent'] = other.node()
-    evolvestate[b'base'] = base.node()
+    evolvestate[b'old-divergent'] = None
 
     def swapnodes(div, other):
         div, other = other, div
@@ -364,6 +323,7 @@
         else:
             publicdiv = divergent
         evolvestate[b'public-divergent'] = publicdiv.node()
+
     # we don't handle merge content-divergent changesets yet
     if len(other.parents()) > 1:
         msg = _(b"skipping %s: %s changeset can't be "
@@ -375,32 +335,32 @@
         ui.write_err(hint)
         return (False, b".")
 
-    otherp1 = other.p1().rev()
-    divp1 = divergent.p1().rev()
+    otherp1 = succsotherp1 = other.p1().rev()
+    divp1 = succsdivp1 = divergent.p1().rev()
 
     # finding single successors of otherp1 and divp1
     try:
-        otherp1 = utility._singlesuccessor(repo, other.p1())
+        succsotherp1 = utility._singlesuccessor(repo, other.p1())
     except utility.MultipleSuccessorsError:
         pass
 
     try:
-        divp1 = utility._singlesuccessor(repo, divergent.p1())
+        succsdivp1 = utility._singlesuccessor(repo, divergent.p1())
     except utility.MultipleSuccessorsError:
         pass
 
     # the changeset on which resolution changeset will be based on
-    resolutionparent = repo[divp1].node()
+    resolutionparent = repo[succsdivp1].node()
 
     # the nullrev has to be handled specially because -1 is overloaded to both
     # mean nullrev (this meaning is used for the result of changectx.rev(), as
     # called above) and the tipmost revision (this meaning is used for the %d
     # format specifier, as used below)
-    if nodemod.nullrev in (otherp1, divp1):
-        # nullrev is the only possible ancestor if otherp1 or divp1 is nullrev
+    if nodemod.nullrev in (succsotherp1, succsdivp1):
+        # nullrev is the only possible ancestor if succsotherp1 or succsdivp1 is nullrev
         gca = [nodemod.nullrev]
     else:
-        gca = repo.revs(b"ancestor(%d, %d)" % (otherp1, divp1))
+        gca = repo.revs(b"ancestor(%d, %d)" % (succsotherp1, succsdivp1))
     # divonly: non-obsolete csets which are topological ancestor of "divergent"
     # but not "other"
     divonly = repo.revs(b"only(%d, %d) - obsolete()" % (divergent.rev(),
@@ -428,32 +388,31 @@
     # for 2) we will relocate one which is behind to the parent of ahead one and
     # then solve the content-divergence the way we solve 1)
     # for 3) and 4), we still have to decide
-    if otherp1 in gca and divp1 in gca:
-        if otherp1 == other.p1().rev() and divp1 == divergent.p1().rev():
-            # both are on the same parents
+    if otherp1 == divp1:
+        # both are on the same parents
+        pass
+    elif succsotherp1 in gca and succsdivp1 in gca:
+        # both are not on the same parent but have same parents's succs.
+        if otheronly and divonly:
+            # case: we have visible csets on both side diverging from
+            # tca of "divergent" and "other". We still need to decide what
+            # to do in this case
             pass
+        if otheronly:
+            relocatereq = True
+            if not haspubdiv:
+                # can't swap when public divergence, as public can't move
+                divergent, other = swapnodes(divergent, other)
+                resolutionparent = repo[succsotherp1].node()
+        elif divonly:
+            relocatereq = True
         else:
-            # both are not on the same parent but have same parents's succs.
-            if otheronly and divonly:
-                # case: we have visible csets on both side diverging from
-                # tca of "divergent" and "other". We still need to decide what
-                # to do in this case
-                pass
-            if otheronly:
-                relocatereq = True
-                if not haspubdiv:
-                    # can't swap when public divergence, as public can't move
-                    divergent, other = swapnodes(divergent, other)
-                    resolutionparent = repo[otherp1].node()
-            elif divonly:
-                relocatereq = True
-            else:
-                # no extra cset on either side; so not considering relocation
-                pass
-    elif otherp1 in gca and divp1 not in gca:
+            # no extra cset on either side; so not considering relocation
+            pass
+    elif succsotherp1 in gca and succsdivp1 not in gca:
         relocatereq = True
         pass
-    elif divp1 in gca and otherp1 not in gca:
+    elif succsdivp1 in gca and succsotherp1 not in gca:
         relocatereq = True
 
         # When public branch is behind to the mutable branch, for now we
@@ -482,6 +441,52 @@
         ui.write_err(hint)
         return (False, b".")
 
+    return (True, divergent, other, resolutionparent, relocatereq)
+
+def _solvedivergent(ui, repo, divergent, evolvestate, displayer, dryrun=False,
+                    confirm=False, progresscb=None):
+    """tries to solve content-divergence of a changeset
+
+    returns a tuple (bool, newnode) where,
+        bool: a boolean value indicating whether the instability was solved
+        newnode: if bool is True, then the newnode of the resultant commit
+                 formed. newnode can be node, when resolution led to no new
+                 commit. If bool is False, this is ".".
+    """
+    repo = repo.unfiltered()
+    base, others = divergentdata(divergent)
+
+    # we don't handle split in content-divergence yet
+    if len(others) > 1:
+        othersstr = b"[%s]" % (b','.join([bytes(i) for i in others]))
+        msg = _(b"skipping %s: %s with a changeset that got split"
+                b" into multiple ones:\n"
+                b"|[%s]\n"
+                b"| This is not handled by automatic evolution yet\n"
+                b"| You have to fallback to manual handling with commands "
+                b"such as:\n"
+                b"| - hg touch -D\n"
+                b"| - hg prune\n"
+                b"| \n"
+                b"| You should contact your local evolution Guru for help.\n"
+                ) % (divergent, TROUBLES['CONTENTDIVERGENT'], othersstr)
+        ui.write_err(msg)
+        return (False, b".")
+    other = others[0]
+    evolvestate[b'other-divergent'] = other.node()
+    evolvestate[b'base'] = base.node()
+
+    # setup everything before merging two content-divergent csets
+    datatoproceed = _prepcontentdivresolution(ui, repo, divergent, other,
+                                              evolvestate)
+    if not datatoproceed[0]:
+        return (False, b".")
+    divergent, other, resolutionparent, relocatereq = datatoproceed[1:]
+
+    if relocatereq:
+        evolvestate['relocation-req'] = True
+    evolvestate[b'resolutionparent'] = resolutionparent
+
     if not ui.quiet or confirm:
         ui.write(_(b'merge:'), label=b'evolve.operation')
         displayer.show(divergent)
@@ -502,19 +507,26 @@
                   % divergent))
         return (False, b".")
 
+    if relocatereq:
+        evolvestate[b'relocation-req'] = True
+    evolvestate[b'resolutionparent'] = resolutionparent
     try:
         succsdivp1 = utility._singlesuccessor(repo, divergent.p1())
     except utility.MultipleSuccessorsError:
-        msg = _(b"ambiguous orphan resolution parent for %b")
+        msg = _(b"ambiguous orphan resolution parent for %s")
         raise error.Abort(msg % divergent.hex()[:12])
     # relocate divergent cset to its obsolete parent's successsor
     if succsdivp1 != divergent.p1().rev():
+        evolvestate[b'relocating-div'] = True
         ui.status(_(b'rebasing "divergent" content-divergent changeset %s on'
                     b' %s\n' % (divergent, repo[succsdivp1])))
         with state.saver(evolvestate, {b'current': divergent.node()}):
-            newdivergent = relocate(repo, divergent, repo[succsdivp1],
-                                    evolvestate, keepbranch=True)
+            newdivergent = _relocate(repo, divergent, repo[succsdivp1],
+                                     evolvestate, keepbranch=True)
+        evolvestate[b'old-divergent'] = divergent.node()
         divergent = repo[newdivergent]
+        evolvestate[b'relocating-div'] = False
+        evolvestate[b'relocated-div'] = divergent.node()
         evolvestate[b'temprevs'].append(divergent.node())
         evolvestate[b'divergent'] = divergent.node()
 
@@ -522,22 +534,21 @@
     if relocatereq and other == divergent.p1():
         relocatereq = False
 
-    evolvestate[b'resolutionparent'] = resolutionparent
     # relocate the other divergent if required
     if relocatereq:
         # relocating will help us understand during the time of conflicts that
         # whether conflicts occur at reloacting or they occured at merging
         # content divergent changesets
-        evolvestate[b'relocating'] = True
+        evolvestate[b'relocating-other'] = True
         ui.status(_(b'rebasing "other" content-divergent changeset %s on'
                     b' %s\n' % (other, divergent.p1())))
         with state.saver(evolvestate, {b'current': other.node()}):
-            newother = relocate(repo, other, divergent.p1(), evolvestate,
-                                keepbranch=True)
+            newother = _relocate(repo, other, divergent.p1(), evolvestate,
+                                 keepbranch=True)
         evolvestate[b'old-other'] = other.node()
         other = repo[newother]
-        evolvestate[b'relocating'] = False
-        evolvestate[b'relocated'] = other.node()
+        evolvestate[b'relocating-other'] = False
+        evolvestate[b'relocated-other'] = other.node()
         evolvestate[b'temprevs'].append(other.node())
         evolvestate[b'other-divergent'] = other.node()
 
@@ -545,9 +556,11 @@
                             evolvestate)
     res, newnode = _completecontentdivergent(ui, repo, progresscb, divergent,
                                              other, base, evolvestate)
+    haspubdiv = not (divergent.mutable() and other.mutable())
     if not haspubdiv:
         return (res, newnode)
     else:
+        publicdiv = repo[evolvestate[b'public-divergent']]
         # we have content-divergence with a public cset:
         # after performing content divergence resolution steps, possbile cases:
         # 1) merging results in a new node:
@@ -607,7 +620,6 @@
     # resume resolution
     if progresscb:
         progresscb()
-    emtpycommitallowed = repo.ui.backupconfig(b'ui', b'allowemptycommit')
     tr = repo.currenttransaction()
     assert tr is not None
     # whether to store the obsmarker in the evolvestate
@@ -635,68 +647,64 @@
             obsolete.createmarkers(repo, [(otherdiv, (publicdiv,))],
                                    operation=b'evolve')
             return (True, publicnode)
-    try:
-        with repo.dirstate.parentchange(), compat.parentchange(repo):
-            repo.dirstate.setparents(resparent, nodemod.nullid)
-
-        dirstatedance(repo, divergent, resparent, None)
 
-        # merge the branches
-        mergebranches(repo, divergent, other, base)
-        # merge the commit messages
-        desc = mergecommitmessages(ui, base.description(),
-                                   divergent.description(),
-                                   other.description())
-        user = utility.mergeusers(ui, base, divergent, other)
+    with repo.dirstate.parentchange(), compat.parentchange(repo):
+        repo.dirstate.setparents(resparent, nodemod.nullid)
+
+    dirstatedance(repo, divergent, resparent, None)
 
-        # new node if any formed as the replacement
-        newnode = None
+    # merge the branches
+    mergebranches(repo, divergent, other, base)
+    # merge the commit messages
+    desc = mergecommitmessages(ui, base.description(),
+                               divergent.description(),
+                               other.description())
+    user = utility.mergeusers(ui, base, divergent, other)
 
-        mergehook(repo, base, divergent, other)
+    mergehook(repo, base, divergent, other)
 
-        date = divergent.date()
-        if other.date() != divergent.date():
-            basedate = base.date()
-            if other.date() == basedate:
-                date = divergent.date()
-            elif divergent.date() == basedate:
-                date = other.date()
-            else:
-                date = max(divergent.date(), other.date())
+    date = divergent.date()
+    if other.date() != divergent.date():
+        basedate = base.date()
+        if other.date() == basedate:
+            date = divergent.date()
+        elif divergent.date() == basedate:
+            date = other.date()
+        else:
+            date = max(divergent.date(), other.date())
 
-        newnode = repo.commit(text=desc, user=user, date=date)
-        if newnode == divergent.node() or newnode is None:
-            # no changes
-            new = divergent
-            storemarker = True
-            repo.ui.status(_(b"nothing changed\n"))
-            hg.updaterepo(repo, divergent.rev(), False)
+    # new node if any formed as the replacement
+    newnode = repo.commit(text=desc, user=user, date=date)
+    if newnode == divergent.node() or newnode is None:
+        # no changes
+        new = divergent
+        storemarker = True
+        repo.ui.status(_(b"nothing changed\n"))
+        hg.updaterepo(repo, divergent.rev(), False)
+    else:
+        new = repo[newnode]
+        newnode = new.node()
+        hg.updaterepo(repo, new.rev(), False)
+        if haspubdiv and publicdiv == divergent:
+            bypassphase(repo, (divergent, new), operation=b'evolve')
         else:
-            new = repo[newnode]
-            newnode = new.node()
-            hg.updaterepo(repo, new.rev(), False)
-            if haspubdiv and publicdiv == divergent:
-                bypassphase(repo, (divergent, new), operation=b'evolve')
-            else:
-                obsolete.createmarkers(repo, [(divergent, (new,))],
-                                       operation=b'evolve')
+            obsolete.createmarkers(repo, [(divergent, (new,))],
+                                   operation=b'evolve')
 
-        # creating markers and moving phases post-resolution
-        if haspubdiv and publicdiv == other:
-            bypassphase(repo, (other, new), operation=b'evolve')
-        else:
-            obsolete.createmarkers(repo, [(other, (new,))], operation=b'evolve')
-        if storemarker:
-            # storing the marker in the evolvestate
-            # we just store the precursors and successor pair for now, we might
-            # want to store more data and serialize obsmarker in a better way in
-            # future
-            evolvestate[b'obsmarkers'].append((other.node(), new.node()))
+    # creating markers and moving phases post-resolution
+    if haspubdiv and publicdiv == other:
+        bypassphase(repo, (other, new), operation=b'evolve')
+    else:
+        obsolete.createmarkers(repo, [(other, (new,))], operation=b'evolve')
+    if storemarker:
+        # storing the marker in the evolvestate
+        # we just store the precursors and successor pair for now, we might
+        # want to store more data and serialize obsmarker in a better way in
+        # future
+        evolvestate[b'obsmarkers'].append((other.node(), new.node()))
 
-        phases.retractboundary(repo, tr, other.phase(), [new.node()])
-        return (True, newnode)
-    finally:
-        repo.ui.restoreconfig(emtpycommitallowed)
+    phases.retractboundary(repo, tr, other.phase(), [new.node()])
+    return (True, newnode)
 
 def warnmetadataloss(repo, local, other):
     """warn the user for the metadata being lost while resolving
@@ -954,8 +962,8 @@
     ordering.extend(sorted(dependencies))
     return ordering
 
-def relocate(repo, orig, dest, evolvestate, pctx=None, keepbranch=False,
-             category=None):
+def _relocate(repo, orig, dest, evolvestate, pctx=None, keepbranch=False,
+              category=None):
     """rewrites the orig rev on dest rev
 
     returns the node of new commit which is formed
@@ -1009,14 +1017,6 @@
         with repo.dirstate.parentchange(), compat.parentchange(repo):
             repo.setparents(dest.node(), orig.node())
             repo.dirstate.write(tr)
-            # fix up dirstate for copies and renames
-            if util.safehasattr(copies, 'graftcopies'):
-                copies.graftcopies(repo[None], dest, orig.p1())
-            else:
-                # hg <= 5.2 (2f0a44c69e07)
-                copies.duplicatecopies(repo, repo[None], dest.rev(),
-                                       orig.p1().rev())
-            dirstatedance(repo, dest, orig.node(), None)
         hint = _(b"see 'hg help evolve.interrupted'")
         raise error.InterventionRequired(_(b"unresolved merge conflicts"),
                                          hint=hint)
@@ -1032,16 +1032,14 @@
         del extra[b'branch']
     extra[b'rebase_source'] = orig.hex()
 
-    backup = repo.ui.backupconfig(b'phases', b'new-commit')
-    try:
-        targetphase = max(orig.phase(), phases.draft)
-        repo.ui.setconfig(b'phases', b'new-commit', targetphase, b'evolve')
+    targetphase = max(orig.phase(), phases.draft)
+    configoverride = repo.ui.configoverride({
+        (b'phases', b'new-commit'): targetphase
+    }, source=b'evolve')
+    with configoverride:
         # Commit might fail if unresolved files exist
-        nodenew = repo.commit(text=commitmsg, user=orig.user(),
-                              date=orig.date(), extra=extra)
-    finally:
-        repo.ui.restoreconfig(backup)
-    return nodenew
+        return repo.commit(text=commitmsg, user=orig.user(),
+                           date=orig.date(), extra=extra)
 
 def _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate):
     destbookmarks = repo.nodebookmarks(dest.node())
@@ -1050,7 +1048,7 @@
     bmchanges = []
 
     if nodenew is not None:
-        obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))],
+        obsolete.createmarkers(repo, [(orig, (repo[nodenew],))],
                                operation=b'evolve')
         for book in oldbookmarks:
             bmchanges.append((book, nodenew))
@@ -1059,7 +1057,7 @@
         if category == b'orphan':
             repo.ui.status(_(b"evolution of %d:%s created no changes "
                              b"to commit\n") % (orig.rev(), orig))
-        obsolete.createmarkers(repo, [(repo[nodesrc], ())], operation=b'evolve')
+        obsolete.createmarkers(repo, [(orig, ())], operation=b'evolve')
         # Behave like rebase, move bookmarks to dest
         for book in oldbookmarks:
             evolvestate[b'bookmarkchanges'].append((book, nodesrc))
@@ -1544,7 +1542,8 @@
                               b' in the repo')),
      ] + mergetoolopts,
     _(b'[OPTIONS]...'),
-    helpbasic=True
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT')
 )
 def evolve(ui, repo, **opts):
     """solve troubled changesets in your repository
@@ -1847,15 +1846,29 @@
     """logic for handling of `hg evolve --stop`"""
     updated = False
     pctx = None
+    divrelocated = evolvestate.get(b'relocated-div')
+    otherrelocated = evolvestate.get(b'relocated-other')
+    strips = []
+    if divrelocated:
+        strips.append(divrelocated)
+    if otherrelocated:
+        strips.append(otherrelocated)
     if (evolvestate[b'command'] == b'evolve'
         and evolvestate[b'category'] == b'contentdivergent'
-        and evolvestate[b'relocated']):
+        and strips):
         oldother = evolvestate[b'old-other']
-        if oldother:
+        olddiv = evolvestate[b'old-divergent']
+        if olddiv:
+            with repo.wlock(), repo.lock():
+                repo = repo.unfiltered()
+                hg.updaterepo(repo, olddiv, True)
+                repair.strip(ui, repo, strips, False)
+                updated = True
+                pctx = repo[olddiv]
+        elif oldother:
             with repo.wlock(), repo.lock():
                 repo = repo.unfiltered()
                 hg.updaterepo(repo, oldother, True)
-                strips = [evolvestate[b'relocated']]
                 repair.strip(ui, repo, strips, False)
                 updated = True
                 pctx = repo[oldother]
@@ -2029,13 +2042,47 @@
         divergent = evolvestate[b'divergent']
         base = evolvestate[b'base']
         repo = repo.unfiltered()
-        if evolvestate[b'relocating']:
+        if evolvestate[b'relocating-div']:
+            newdiv = _completerelocation(ui, repo, evolvestate)
+            current = repo[evolvestate[b'current']]
+            obsolete.createmarkers(repo, [(current, (repo[newdiv],))],
+                                   operation=b'evolve')
+            evolvestate[b'old-divergent'] = repo[divergent].node()
+            evolvestate[b'relocating-div'] = False
+            evolvestate[b'relocated-div'] = newdiv
+            evolvestate[b'temprevs'].append(newdiv)
+            evolvestate[b'divergent'] = newdiv
+
+            relocatereq = evolvestate[b'relocation-req']
+            if relocatereq:
+                divergent = repo[evolvestate[b'divergent']]
+                other = repo[evolvestate[b'other-divergent']]
+                evolvestate[b'relocating-other'] = True
+                ui.status(_(b'rebasing "other" content-divergent changeset %s on'
+                            b' %s\n' % (other, divergent.p1())))
+                with state.saver(evolvestate, {b'current': other.node()}):
+                    newother = _relocate(repo, other, divergent.p1(),
+                                         evolvestate, keepbranch=True)
+                evolvestate[b'old-other'] = other.node()
+                other = repo[newother]
+                evolvestate[b'relocating-other'] = False
+                evolvestate[b'relocated-other'] = other.node()
+                evolvestate[b'temprevs'].append(other.node())
+                evolvestate[b'other-divergent'] = other.node()
+                # continue the resolution by merging the content-divergence
+                _mergecontentdivergents(repo, progresscb,
+                                        divergent,
+                                        repo[newother],
+                                        repo[base],
+                                        evolvestate)
+
+        if evolvestate[b'relocating-other']:
             newother = _completerelocation(ui, repo, evolvestate)
             current = repo[evolvestate[b'current']]
             obsolete.createmarkers(repo, [(current, (repo[newother],))],
                                    operation=b'evolve')
-            evolvestate[b'relocating'] = False
-            evolvestate[b'relocated'] = newother
+            evolvestate[b'relocating-other'] = False
+            evolvestate[b'relocated-other'] = newother
             evolvestate[b'temprevs'].append(newother)
             evolvestate[b'other-divergent'] = newother
             # continue the resolution by merging the content-divergence
@@ -2045,18 +2092,19 @@
                                     repo[base],
                                     evolvestate)
 
+        divergent = evolvestate[b'divergent']
         other = evolvestate[b'other-divergent']
-        ret = _completecontentdivergent(ui, repo, progresscb,
-                                        repo[divergent],
-                                        repo[other],
-                                        repo[base],
-                                        evolvestate)
+        res, newnode = _completecontentdivergent(ui, repo, progresscb,
+                                                 repo[divergent],
+                                                 repo[other],
+                                                 repo[base],
+                                                 evolvestate)
         origdivergent = evolvestate[b'orig-divergent']
-        evolvestate[b'replacements'][origdivergent] = ret[1]
+        evolvestate[b'replacements'][origdivergent] = newnode
+        ret = (res, newnode)
         # logic to continue the public content-divergent
         publicnode = evolvestate.get(b'public-divergent')
         if publicnode:
-            res, newnode = ret
             if not res:
                 # no need to proceed for phase divergence resolution step
                 pass
@@ -2141,7 +2189,7 @@
 
     ctxparents = orig.parents()
     if len(ctxparents) == 2:
-        currentp1 = repo.dirstate.parents()[0]
+        currentp1 = repo.dirstate.p1()
         p1obs = ctxparents[0].obsolete()
         p2obs = ctxparents[1].obsolete()
         # asumming that the parent of current wdir is successor of one
@@ -2171,7 +2219,7 @@
             pass
     else:
         with repo.dirstate.parentchange(), compat.parentchange(repo):
-            repo.dirstate.setparents(repo.dirstate.parents()[0], nodemod.nullid)
+            repo.dirstate.setparents(repo.dirstate.p1(), nodemod.nullid)
 
     with repo.ui.configoverride(overrides, b'evolve-continue'):
         node = repo.commit(text=message, user=user,
--- a/hgext3rd/evolve/exthelper.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/exthelper.py	Sat Feb 29 15:18:00 2020 +0700
@@ -50,7 +50,7 @@
         @eh.command('mynewcommand',
             [('r', 'rev', [], _('operate on these revisions'))],
             _('-r REV...'),
-            helpcategory=command.CATEGORY_XXX)
+            **compat.helpcategorykwargs('CATEGORY_XXX'))
         def newcommand(ui, repo, *revs, **opts):
             # implementation goes here
 
--- a/hgext3rd/evolve/firstmergecache.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/firstmergecache.py	Sat Feb 29 15:18:00 2020 +0700
@@ -16,6 +16,12 @@
     node as nodemod,
 )
 
+try:
+    from mercurial.utils.stringutil import forcebytestr
+except ImportError:
+    # hg <= 4.5 (f99d64e8a4e4)
+    from mercurial.util import forcebytestr
+
 from . import (
     compat,
     error,
@@ -143,5 +149,5 @@
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
-            repo.ui.log(b'firstmergecache', b'could not write update %s\n' % exc)
-            repo.ui.debug(b'firstmergecache: could not write update %s\n' % exc)
+            repo.ui.log(b'firstmergecache', b'could not write update %s\n' % forcebytestr(exc))
+            repo.ui.debug(b'firstmergecache: could not write update %s\n' % forcebytestr(exc))
--- a/hgext3rd/evolve/genericcaches.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/genericcaches.py	Sat Feb 29 15:18:00 2020 +0700
@@ -14,7 +14,7 @@
     util,
 )
 
-class incrementalcachebase(object):
+class incrementalcachebase(object):  # pytype: disable=ignored-metaclass
     """base class for incremental cache from append only source
 
     There are multiple append only data source we might want to cache
@@ -133,7 +133,7 @@
         """read the cachekey from bytes"""
         return self._cachekeystruct.unpack(data)
 
-class changelogsourcebase(incrementalcachebase):
+class changelogsourcebase(incrementalcachebase):  # pytype: disable=ignored-metaclass
     """an abstract class for cache sourcing data from the changelog
 
     For this purpose it use a cache key covering changelog content.
--- a/hgext3rd/evolve/metadata.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/metadata.py	Sat Feb 29 15:18:00 2020 +0700
@@ -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__ = b'9.2.2.dev'
-testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0 5.1'
-minimumhgversion = b'4.5'
+__version__ = b'9.3.1.dev'
+testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3'
+minimumhgversion = b'4.6'
 buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obscache.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/obscache.py	Sat Feb 29 15:18:00 2020 +0700
@@ -19,6 +19,12 @@
     util,
 )
 
+try:
+    from mercurial.utils.stringutil import forcebytestr
+except ImportError:
+    # hg <= 4.5 (f99d64e8a4e4)
+    from mercurial.util import forcebytestr
+
 from . import (
     compat,
     exthelper,
@@ -411,8 +417,8 @@
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
-            repo.ui.log(b'obscache', b'could not write update %s\n' % exc)
-            repo.ui.debug(b'obscache: could not write update %s\n' % exc)
+            repo.ui.log(b'obscache', b'could not write update %s\n' % forcebytestr(exc))
+            repo.ui.debug(b'obscache: could not write update %s\n' % forcebytestr(exc))
 
     def load(self, repo):
         """load data from disk"""
--- a/hgext3rd/evolve/obsexchange.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/obsexchange.py	Sat Feb 29 15:18:00 2020 +0700
@@ -7,17 +7,11 @@
 
 from __future__ import absolute_import
 
-try:
-    from StringIO import StringIO
-except ImportError:
-    from io import StringIO
-
 from mercurial import (
     bundle2,
     error,
     exchange,
     extensions,
-    lock as lockmod,
     node,
     obsolete,
     pushkey,
@@ -35,7 +29,6 @@
 eh = exthelper.exthelper()
 eh.merge(obsdiscovery.eh)
 obsexcmsg = utility.obsexcmsg
-obsexcprg = utility.obsexcprg
 
 eh.configitem(b'experimental', b'verbose-obsolescence-exchange', False)
 
@@ -156,82 +149,6 @@
         return _obscommon_capabilities(oldcap, repo, proto)
     wireprotov1server.commands[b'capabilities'] = (newcap, args)
 
-def _pushobsmarkers(repo, data):
-    tr = lock = None
-    try:
-        lock = repo.lock()
-        tr = repo.transaction(b'pushkey: obsolete markers')
-        new = repo.obsstore.mergemarkers(tr, data)
-        if new is not None:
-            obsexcmsg(repo.ui, b"%i obsolescence markers added\n" % new, True)
-        tr.close()
-    finally:
-        lockmod.release(tr, lock)
-    repo.hook(b'evolve_pushobsmarkers')
-
-def srv_pushobsmarkers(repo, proto):
-    """wireprotocol command"""
-    fp = StringIO()
-    proto.redirect()
-    proto.getfile(fp)
-    data = fp.getvalue()
-    fp.close()
-    _pushobsmarkers(repo, data)
-    try:
-        from mercurial import wireprototypes
-        wireprototypes.pushres # force demandimport
-    except (ImportError, AttributeError):
-        from mercurial import wireproto as wireprototypes
-    return wireprototypes.pushres(0)
-
-def _getobsmarkersstream(repo, heads=None, common=None):
-    """Get a binary stream for all markers relevant to `::<heads> - ::<common>`
-    """
-    revset = b''
-    args = []
-    repo = repo.unfiltered()
-    if heads is None:
-        revset = b'all()'
-    elif heads:
-        revset += b"(::%ln)"
-        args.append(heads)
-    else:
-        assert False, b'pulling no heads?'
-    if common:
-        revset += b' - (::%ln)'
-        args.append(common)
-    nodes = [c.node() for c in repo.set(revset, *args)]
-    markers = repo.obsstore.relevantmarkers(nodes)
-    obsdata = StringIO()
-    for chunk in obsolete.encodemarkers(markers, True):
-        obsdata.write(chunk)
-    obsdata.seek(0)
-    return obsdata
-
-def srv_pullobsmarkers(repo, proto, others):
-    """serves a binary stream of markers.
-
-    Serves relevant to changeset between heads and common. The stream is prefix
-    by a -string- representation of an integer. This integer is the size of the
-    stream."""
-    try:
-        from mercurial import wireprototypes, wireprotov1server
-        wireprototypes.pushres # force demandimport
-    except (ImportError, AttributeError):
-        from mercurial import wireproto as wireprototypes
-        wireprotov1server = wireprototypes
-    opts = wireprotov1server.options(b'', [b'heads', b'common'], others)
-    for k, v in opts.items():
-        if k in (b'heads', b'common'):
-            opts[k] = wireprototypes.decodelist(v)
-    obsdata = _getobsmarkersstream(repo, **opts)
-    finaldata = StringIO()
-    obsdata = obsdata.getvalue()
-    finaldata.write(b'%20i' % len(obsdata))
-    finaldata.write(obsdata)
-    finaldata.seek(0)
-    return wireprototypes.streamres(reader=finaldata, v1compressible=True)
-
 abortmsg = b"won't exchange obsmarkers through pushkey"
 hint = b"upgrade your client or server to use the bundle2 protocol"
 
--- a/hgext3rd/evolve/obshistory.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/obshistory.py	Sat Feb 29 15:18:00 2020 +0700
@@ -42,6 +42,10 @@
         item.default = True
         efd.clear()
 
+@eh.extsetup
+def addtouchnoise(ui):
+    obsutil.METABLACKLIST.append(re.compile(br'^__touch-noise__$'))
+
 @eh.command(
     b'obslog|olog',
     [(b'G', b'graph', True, _(b"show the revision DAG")),
@@ -50,7 +54,8 @@
      (b'p', b'patch', False, _(b'show the patch between two obs versions')),
      (b'f', b'filternonlocal', False, _(b'filter out non local commits')),
      ] + commands.formatteropts,
-    _(b'hg olog [OPTION]... [[-r] REV]...'))
+    _(b'hg olog [OPTION]... [[-r] REV]...'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_NAVIGATION'))
 def cmdobshistory(ui, repo, *revs, **opts):
     """show the obsolescence history of the specified revisions
 
@@ -85,55 +90,47 @@
         revs = [b'.']
     revs = scmutil.revrange(repo, revs)
 
+    # Use the default template unless the user provided one, but not if
+    # -f was given, because that doesn't work with templates yet. Note
+    # that --no-graph doesn't support -f (it ignores it), so we also
+    # don't use templating with --no-graph.
+    if not opts['template'] and not (opts['filternonlocal'] and opts['graph']):
+        opts['template'] = DEFAULT_TEMPLATE
+
     if opts['graph']:
         return _debugobshistorygraph(ui, repo, revs, opts)
 
     revs.reverse()
     _debugobshistoryrevs(ui, repo, revs, opts)
 
-def _successorsandmarkers(repo, ctx):
-    """compute the raw data needed for computing obsfate
-    Returns a list of dict, one dict per successors set
-    """
-    ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
-
-    # closestsuccessors returns an empty list for pruned revisions, remap it
-    # into a list containing an empty list for future processing
-    if ssets == []:
-        ssets = [[]]
-
-    # Try to recover pruned markers
-    succsmap = repo.obsstore.successors
-    fullsuccessorsets = [] # successor set + markers
-    for sset in ssets:
-        if sset:
-            fullsuccessorsets.append(compat.wrap_succs(sset))
-        else:
-            # successorsset return an empty set() when ctx or one of its
-            # successors is pruned.
-            # In this case, walk the obs-markers tree again starting with ctx
-            # and find the relevant pruning obs-makers, the ones without
-            # successors.
-            # Having these markers allow us to compute some information about
-            # its fate, like who pruned this changeset and when.
-
-            # XXX we do not catch all prune markers (eg rewritten then pruned)
-            # (fix me later)
-            foundany = False
-            for mark in succsmap.get(ctx.node(), ()):
-                if not mark[1]:
-                    foundany = True
-                    sset = compat._succs()
-                    sset.markers.add(mark)
-                    fullsuccessorsets.append(sset)
-            if not foundany:
-                fullsuccessorsets.append(compat._succs())
-
-    values = []
-    for sset in fullsuccessorsets:
-        values.append({b'successors': sset, b'markers': sset.markers})
-
-    return values
+TEMPLATE_MISSING_NODE = b"""{label("evolve.node evolve.missing_change_ctx", node|short)}"""
+TEMPLATE_PRESENT_NODE = b"""{label("evolve.node", node|short)} {label("evolve.rev", "({rev})")} {label("evolve.short_description", desc|firstline)}"""
+TEMPLATE_FIRST_LINE = b"""{if(rev, "%(presentnode)s", "%(missingnode)s")}""" % {
+    b"presentnode": TEMPLATE_PRESENT_NODE,
+    b"missingnode": TEMPLATE_MISSING_NODE
+}
+TEMPLATE_VERB = b"""{label("evolve.verb", verb)}"""
+TEMPLATE_SUCCNODES = b"""{label("evolve.node", join(succnodes % "{succnode|short}", ", "))}"""
+TEMPLATE_REWRITE = b"""{if(succnodes, "%(verb)s{if(effects, "({join(effects, ", ")})")} as %(succnodes)s", "pruned")}""" % {
+    b"verb": TEMPLATE_VERB,
+    b"succnodes": TEMPLATE_SUCCNODES
+}
+TEMPLATE_OPERATION = b"""{if(operation, "using {label("evolve.operation", operation)}")}"""
+TEMPLATE_USER = b"""by {label("evolve.user", user)}"""
+TEMPLATE_DATE = b"""{label("evolve.date", "({date(date, "%a %b %d %H:%M:%S %Y %1%2")})")}"""
+TEMPLATE_NOTE = b"""{if(note, "\n    note: {label("evolve.note", note)}")}"""
+TEMPLATE_PATCH = b"""{if(patch, "{patch}")}{if(nopatchreason, "\n(No patch available, {nopatchreason})")}"""
+DEFAULT_TEMPLATE = (b"""%(firstline)s
+{markers %% "  {separate(" ", "%(rewrite)s", "%(operation)s", "%(user)s", "%(date)s")}%(note)s{indent(descdiff, "    ")}{indent("%(patch)s", "    ")}\n"}
+""") % {
+    b"firstline": TEMPLATE_FIRST_LINE,
+    b"rewrite": TEMPLATE_REWRITE,
+    b"operation": TEMPLATE_OPERATION,
+    b"user": TEMPLATE_USER,
+    b"date": TEMPLATE_DATE,
+    b"note": TEMPLATE_NOTE,
+    b"patch": TEMPLATE_PATCH,
+}
 
 class obsmarker_printer(compat.changesetprinter):
     """show (available) information about a node
@@ -178,23 +175,25 @@
                 succs = sorted(succs)
 
                 for successor in succs:
-                    _debugobshistorydisplaymarker(markerfm, successor,
+                    _debugobshistorydisplaymarker(self.ui, markerfm, successor,
                                                   ctx.node(), self.repo,
                                                   self._includediff)
 
             else:
-                r = _successorsandmarkers(self.repo, ctx)
+                r = obsutil.successorsandmarkers(self.repo, ctx)
+                if r is None:
+                    r = []
 
                 for succset in sorted(r):
                     markers = succset[b"markers"]
                     if not markers:
                         continue
                     successors = succset[b"successors"]
-                    _debugobshistorydisplaysuccsandmarkers(markerfm, successors, markers, ctx.node(), self.repo, self._includediff)
+                    _debugobshistorydisplaysuccsandmarkers(self.ui, markerfm, successors, markers, ctx.node(), self.repo, self._includediff)
 
             markerfm.end()
 
-            markerfm.plain(b'\n')
+            fm.plain(b'\n')
             fm.end()
 
             self.hunk[ctx.node()] = self.ui.popbuffer()
@@ -454,8 +453,9 @@
         markerfm = fm.nested(b"markers")
         for successor in sorted(succs):
             includediff = opts and opts.get("patch")
-            _debugobshistorydisplaymarker(markerfm, successor, ctxnode, unfi, includediff)
+            _debugobshistorydisplaymarker(ui, markerfm, successor, ctxnode, unfi, includediff)
         markerfm.end()
+        fm.plain(b'\n')
 
         precs = precursors.get(ctxnode, ())
         for p in sorted(precs):
@@ -477,12 +477,12 @@
         shortdescription = shortdescription.splitlines()[0]
 
     fm.startitem()
-    fm.write(b'node', b'%s', bytes(ctx),
-             label=b"evolve.node")
+    fm.context(ctx=ctx)
+    fm.data(node=ctx.hex())
+    fm.plain(b'%s' % bytes(ctx), label=b"evolve.node")
     fm.plain(b' ')
 
-    fm.write(b'rev', b'(%d)', ctx.rev(),
-             label=b"evolve.rev")
+    fm.plain(b'(%d)' % ctx.rev(), label=b"evolve.rev")
     fm.plain(b' ')
 
     fm.write(b'shortdescription', b'%s', shortdescription,
@@ -490,84 +490,44 @@
     fm.plain(b'\n')
 
 def _debugobshistorydisplaymissingctx(fm, nodewithoutctx):
-    hexnode = nodemod.short(nodewithoutctx)
     fm.startitem()
-    fm.write(b'node', b'%s', hexnode,
+    fm.data(node=nodemod.hex(nodewithoutctx))
+    fm.plain(nodemod.short(nodewithoutctx),
              label=b"evolve.node evolve.missing_change_ctx")
     fm.plain(b'\n')
 
-def _debugobshistorydisplaymarker(fm, marker, node, repo, includediff=False):
+def _debugobshistorydisplaymarker(ui, fm, marker, node, repo, includediff=False):
     succnodes = marker[1]
     date = marker[4]
     metadata = dict(marker[3])
 
     fm.startitem()
-    fm.plain(b'  ')
-
-    # Detect pruned revisions
-    if len(succnodes) == 0:
-        verb = b'pruned'
-    else:
-        verb = b'rewritten'
 
-    fm.write(b'verb', b'%s', verb,
-             label=b"evolve.verb")
+    verb = _successorsetverb(succnodes, [marker])[b"verb"]
 
-    effectflag = metadata.get(b'ef1')
-    if effectflag is not None:
-        try:
-            effectflag = int(effectflag)
-        except ValueError:
-            effectflag = None
-    if effectflag:
-        effect = []
+    fm.data(verb=verb)
 
-        # XXX should be a dict
-        if effectflag & DESCCHANGED:
-            effect.append(b'description')
-        if effectflag & METACHANGED:
-            effect.append(b'meta')
-        if effectflag & USERCHANGED:
-            effect.append(b'user')
-        if effectflag & DATECHANGED:
-            effect.append(b'date')
-        if effectflag & BRANCHCHANGED:
-            effect.append(b'branch')
-        if effectflag & PARENTCHANGED:
-            effect.append(b'parent')
-        if effectflag & DIFFCHANGED:
-            effect.append(b'content')
-
-        if effect:
-            fmteffect = fm.formatlist(effect, b'effect', sep=b', ')
-            fm.write(b'effect', b'(%s)', fmteffect)
+    effects = _markerseffects([marker])
+    if effects:
+        fmteffect = fm.formatlist(effects, b'effect', sep=b', ')
+        fm.write(b'effects', b'(%s)', fmteffect)
 
     if len(succnodes) > 0:
-        fm.plain(b' as ')
-
-        shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
-        nodes = fm.formatlist(shortsnodes, b'succnodes', sep=b', ')
-        fm.write(b'succnodes', b'%s', nodes,
-                 label=b"evolve.node")
+        hexnodes = (nodemod.hex(succnode) for succnode in sorted(succnodes))
+        nodes = fm.formatlist(hexnodes, b'succnode')
+        fm.write(b'succnodes', b'%s', nodes)
 
     operation = metadata.get(b'operation')
     if operation:
-        fm.plain(b' using ')
-        fm.write(b'operation', b'%s', operation, label=b"evolve.operation")
-
-    fm.plain(b' by ')
+        fm.data(operation=operation)
 
-    fm.write(b'user', b'%s', metadata[b'user'],
-             label=b"evolve.user")
-    fm.plain(b' ')
+    fm.data(user=metadata[b'user'])
 
-    fm.write(b'date', b'(%s)', fm.formatdate(date),
-             label=b"evolve.date")
+    fm.data(date=date)
 
     # initial support for showing note
     if metadata.get(b'note'):
-        fm.plain(b'\n    note: ')
-        fm.write(b'note', b"%s", metadata[b'note'], label=b"evolve.note")
+        fm.data(note=metadata[b'note'])
 
     # Patch display
     if includediff is True:
@@ -592,15 +552,16 @@
                 def tolist(text):
                     return [text]
 
-                fm.plain(b"\n")
+                ui.pushbuffer(labeled=True)
+                ui.write(b"\n")
 
                 for chunk, label in patch.difflabel(tolist, descriptionpatch):
                     chunk = chunk.strip(b'\t')
-                    if chunk and chunk != b'\n':
-                        fm.plain(b'    ')
-                    fm.write(b'desc-diff', b'%s', chunk, label=label)
+                    ui.write(chunk, label=label)
+                fm.write(b'descdiff', b'%s', ui.popbuffer())
 
             # Content patch
+            ui.pushbuffer(labeled=True)
             diffopts = patch.diffallopts(repo.ui, {})
             matchfn = scmutil.matchall(repo)
             firstline = True
@@ -608,23 +569,18 @@
             for chunk, label in patch.diffui(repo, node, succ, matchfn,
                                              opts=diffopts):
                 if firstline:
-                    fm.plain(b'\n')
+                    ui.write(b'\n')
                     firstline = False
                 if linestart:
-                    fm.plain(b'    ')
                     linestart = False
                 if chunk == b'\n':
                     linestart = True
-                fm.write(b'patch', b'%s', chunk, label=label)
+                ui.write(chunk, label=label)
+            fm.data(patch=ui.popbuffer())
         else:
-            nopatch = b"    (No patch available, %s)" % _patchavailable[1]
-            fm.plain(b"\n")
-            # TODO: should be in json too
-            fm.plain(nopatch)
+            fm.data(nopatchreason=_patchavailable[1])
 
-    fm.plain(b"\n")
-
-def _debugobshistorydisplaysuccsandmarkers(fm, succnodes, markers, node, repo, includediff=False):
+def _debugobshistorydisplaysuccsandmarkers(ui, fm, succnodes, markers, node, repo, includediff=False):
     """
     This function is a duplication of _debugobshistorydisplaymarker modified
     to accept multiple markers as input.
@@ -632,54 +588,26 @@
     fm.startitem()
     fm.plain(b'  ')
 
-    # Detect pruned revisions
     verb = _successorsetverb(succnodes, markers)[b"verb"]
 
     fm.write(b'verb', b'%s', verb,
              label=b"evolve.verb")
 
-    # Effect flag
-    metadata = [dict(marker[3]) for marker in markers]
-    ef1 = [data.get(b'ef1') for data in metadata]
-
-    effectflag = 0
-    for ef in ef1:
-        if ef:
-            effectflag |= int(ef)
-
-    if effectflag:
-        effect = []
-
-        # XXX should be a dict
-        if effectflag & DESCCHANGED:
-            effect.append(b'description')
-        if effectflag & METACHANGED:
-            effect.append(b'meta')
-        if effectflag & USERCHANGED:
-            effect.append(b'user')
-        if effectflag & DATECHANGED:
-            effect.append(b'date')
-        if effectflag & BRANCHCHANGED:
-            effect.append(b'branch')
-        if effectflag & PARENTCHANGED:
-            effect.append(b'parent')
-        if effectflag & DIFFCHANGED:
-            effect.append(b'content')
-
-        if effect:
-            fmteffect = fm.formatlist(effect, b'effect', sep=b', ')
-            fm.write(b'effect', b'(%s)', fmteffect)
+    effects = _markerseffects(markers)
+    if effects:
+        fmteffect = fm.formatlist(effects, b'effect', sep=b', ')
+        fm.write(b'effects', b'(%s)', fmteffect)
 
     if len(succnodes) > 0:
         fm.plain(b' as ')
 
         shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
-        nodes = fm.formatlist(shortsnodes, b'succnodes', sep=b', ')
+        nodes = fm.formatlist(shortsnodes, b'succnode', sep=b', ')
         fm.write(b'succnodes', b'%s', nodes,
                  label=b"evolve.node")
 
     # Operations
-    operations = compat.markersoperations(markers)
+    operations = obsutil.markersoperations(markers)
     if operations:
         fm.plain(b' using ')
         fm.write(b'operation', b'%s', b", ".join(operations), label=b"evolve.operation")
@@ -687,13 +615,13 @@
     fm.plain(b' by ')
 
     # Users
-    users = compat.markersusers(markers)
+    users = obsutil.markersusers(markers)
     fm.write(b'user', b'%s', b", ".join(users),
              label=b"evolve.user")
     fm.plain(b' ')
 
     # Dates
-    dates = compat.markersdates(markers)
+    dates = obsutil.markersdates(markers)
     if dates:
         min_date = min(dates)
         max_date = max(dates)
@@ -732,15 +660,18 @@
                 def tolist(text):
                     return [text]
 
-                fm.plain(b"\n")
+                ui.pushbuffer(labeled=True)
+                ui.write(b"\n")
 
                 for chunk, label in patch.difflabel(tolist, descriptionpatch):
                     chunk = chunk.strip(b'\t')
                     if chunk and chunk != b'\n':
-                        fm.plain(b'    ')
-                    fm.write(b'desc-diff', b'%s', chunk, label=label)
+                        ui.write(b'    ')
+                    ui.write(chunk, label=label)
+                fm.write(b'descdiff', b'%s', ui.popbuffer())
 
             # Content patch
+            ui.pushbuffer(labeled=True)
             diffopts = patch.diffallopts(repo.ui, {})
             matchfn = scmutil.matchall(repo)
             firstline = True
@@ -748,50 +679,21 @@
             for chunk, label in patch.diffui(repo, node, succ, matchfn,
                                              opts=diffopts):
                 if firstline:
-                    fm.plain(b'\n')
+                    ui.write(b'\n')
                     firstline = False
                 if linestart:
-                    fm.plain(b'    ')
+                    ui.write(b'    ')
                     linestart = False
                 if chunk == b'\n':
                     linestart = True
-                fm.write(b'patch', b'%s', chunk, label=label)
+                ui.write(chunk, label=label)
+            fm.write(b'patch', b'%s', ui.popbuffer())
         else:
-            nopatch = b"    (No patch available, %s)" % _patchavailable[1]
-            fm.plain(b"\n")
-            # TODO: should be in json too
-            fm.plain(nopatch)
+            fm.write(b'nopatchreason', b"\n    (No patch available, %s)",
+                     _patchavailable[1])
 
     fm.plain(b"\n")
 
-# logic around storing and using effect flags
-DESCCHANGED = 1 << 0 # action changed the description
-METACHANGED = 1 << 1 # action change the meta
-PARENTCHANGED = 1 << 2 # action change the parent
-DIFFCHANGED = 1 << 3 # action change diff introduced by the changeset
-USERCHANGED = 1 << 4 # the user changed
-DATECHANGED = 1 << 5 # the date changed
-BRANCHCHANGED = 1 << 6 # the branch changed
-
-METABLACKLIST = [
-    re.compile(br'^__touch-noise__$'),
-    re.compile(br'^branch$'),
-    re.compile(br'^.*-source$'),
-    re.compile(br'^.*_source$'),
-    re.compile(br'^source$'),
-]
-
-def ismetablacklisted(metaitem):
-    """ Check that the key of a meta item (extrakey, extravalue) does not
-    match at least one of the blacklist pattern
-    """
-    metakey = metaitem[0]
-    for pattern in METABLACKLIST:
-        if pattern.match(metakey):
-            return False
-
-    return True
-
 def _prepare_hunk(hunk):
     """Drop all information but the username and patch"""
     cleanunk = []
@@ -810,40 +712,14 @@
         return None
     return _prepare_hunk(lines)
 
-def _getobsfate(successorssets):
-    """ Compute a changeset obsolescence fate based on his successorssets.
-    Successors can be the tipmost ones or the immediate ones.
-    Returns one fate in the following list:
-    - pruned
-    - diverged
-    - superseed
-    - superseed_split
-    """
-
-    if len(successorssets) == 0:
-        # The commit has been pruned
-        return b'pruned'
-    elif len(successorssets) > 1:
-        return b'diverged'
-    else:
-        # No divergence, only one set of successors
-        successors = successorssets[0]
-
-        if len(successors) == 1:
-            return b'superseed'
-        else:
-            return b'superseed_split'
-
-def _getobsfateandsuccs(repo, revnode, successorssets=None):
+def _getobsfateandsuccs(repo, revnode):
     """ Return a tuple containing:
-    - the reason a revision is obsolete (diverged, pruned or superseed)
+    - the reason a revision is obsolete (diverged, pruned or superseded)
     - the list of successors short node if the revision is neither pruned
     or has diverged
     """
-    if successorssets is None:
-        successorssets = obsutil.successorssets(repo, revnode)
-
-    fate = _getobsfate(successorssets)
+    successorssets = obsutil.successorssets(repo, revnode)
+    fate = obsutil._getobsfate(successorssets)
 
     # Apply node.short if we have no divergence
     if len(successorssets) == 1:
@@ -855,42 +731,69 @@
 
     return (fate, successors)
 
-def _successorsetdates(successorset, markers):
-    """returns the max date and the min date of the markers list
-    """
+EFFECTMAPPING = util.sortdict([
+    (obsutil.DESCCHANGED, b'description'),
+    (obsutil.METACHANGED, b'meta'),
+    (obsutil.USERCHANGED, b'user'),
+    (obsutil.DATECHANGED, b'date'),
+    (obsutil.BRANCHCHANGED, b'branch'),
+    (obsutil.PARENTCHANGED, b'parent'),
+    (obsutil.DIFFCHANGED, b'content'),
+])
 
-    if not markers:
-        return {}
-
-    dates = [m[4] for m in markers]
+def _markerseffects(markers):
+    """ Return a list of effects as strings based on effect flags in markers
 
-    return {
-        b'min_date': min(dates),
-        b'max_date': max(dates)
-    }
-
-def _successorsetusers(successorset, markers):
-    """ Returns a sorted list of markers users without duplicates
+    Return None if verb cannot be more precise than just "rewritten", i.e. when
+    markers collectively have more than one effect in the flags.
     """
-    if not markers:
-        return {}
+    metadata = [dict(marker[3]) for marker in markers]
+    ef1 = [data.get(b'ef1') for data in metadata]
+    effects = []
 
-    # Check that user is present in meta
-    markersmeta = [dict(m[3]) for m in markers]
-    users = set(meta.get(b'user') for meta in markersmeta if meta.get(b'user'))
+    combined = 0
+    for ef in ef1:
+        if ef:
+            combined |= int(ef)
 
-    return {b'users': sorted(users)}
+    if combined:
+        for key, value in EFFECTMAPPING.items():
+            if combined & key:
+                effects.append(value)
+
+    return effects
 
 VERBMAPPING = {
-    DESCCHANGED: b"reworded",
-    METACHANGED: b"meta-changed",
-    USERCHANGED: b"reauthored",
-    DATECHANGED: b"date-changed",
-    BRANCHCHANGED: b"branch-changed",
-    PARENTCHANGED: b"rebased",
-    DIFFCHANGED: b"amended"
+    obsutil.DESCCHANGED: b"reworded",
+    obsutil.METACHANGED: b"meta-changed",
+    obsutil.USERCHANGED: b"reauthored",
+    obsutil.DATECHANGED: b"date-changed",
+    obsutil.BRANCHCHANGED: b"branch-changed",
+    obsutil.PARENTCHANGED: b"rebased",
+    obsutil.DIFFCHANGED: b"amended"
 }
 
+def _markerspreciseverb(markers):
+    """ Return a more precise verb based on effect flags in markers
+
+    Return None if verb cannot be more precise than just "rewritten", i.e. when
+    markers collectively have more than one effect in the flags.
+    """
+    metadata = [dict(marker[3]) for marker in markers]
+
+    if len(metadata) == 1 and metadata[0].get(b'fold-id') is not None:
+        return b'folded'
+
+    ef1 = [data.get(b'ef1') for data in metadata]
+    if all(ef1):
+        combined = 0
+        for ef in ef1:
+            combined |= int(ef)
+
+        # Combined will be in VERBMAPPING only if one bit is set
+        if combined in VERBMAPPING:
+            return VERBMAPPING[combined]
+
 def _successorsetverb(successorset, markers):
     """ Return the verb summarizing the successorset
     """
@@ -898,19 +801,7 @@
     if not successorset:
         verb = b'pruned'
     elif len(successorset) == 1:
-        # Check for effect flag
-
-        metadata = [dict(marker[3]) for marker in markers]
-        ef1 = [data.get(b'ef1') for data in metadata]
-
-        if all(ef1):
-            combined = 0
-            for ef in ef1:
-                combined |= int(ef)
-
-            # Combined will be in VERBMAPPING only of one bit is set
-            if combined in VERBMAPPING:
-                verb = VERBMAPPING[combined]
+        verb = _markerspreciseverb(markers)
 
         if verb is None:
             verb = b'rewritten'
@@ -919,101 +810,6 @@
     return {b'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)[b'verb']
-
-# Hijack callers of successorsetverb
-elif util.safehasattr(obsutil, 'obsfateprinter'):
-
-    @eh.wrapfunction(obsutil, 'obsfateprinter')
-    def obsfateprinter(orig, successors, markers, ui):
-
-        def closure(successors):
-            return _successorsetverb(successors, markers)[b'verb']
-
-        if not util.safehasattr(obsutil, 'successorsetverb'):
-            return orig(successors, markers, ui)
-
-        # Save the old value
-        old = obsutil.successorsetverb
-
-        try:
-            # Replace by own
-            obsutil.successorsetverb = closure
-
-            # Call the orig
-            result = orig(successors, markers, ui)
-
-            # And return result
-            return result
-        finally:
-            # Replace the old one
-            obsutil.successorsetverb = old
-
-FORMATSSETSFUNCTIONS = [
-    _successorsetdates,
-    _successorsetusers,
-    _successorsetverb
-]
-
-def successorsetallmarkers(successorset, pathscache):
-    """compute all successors of a successorset.
-
-    pathscache must contains all successors starting from selected nodes
-    or revision. This way, iterating on each successor, we can take all
-    precursors and have the subgraph of all obsmarkers between roots to
-    successors.
-    """
-
-    markers = set()
-    seen = set()
-
-    for successor in successorset:
-        stack = [successor]
-
-        while stack:
-            element = stack.pop()
-            seen.add(element)
-            for prec, mark in pathscache.get(element, []):
-                if prec not in seen:
-                    # Process element precursors
-                    stack.append(prec)
-
-                if mark not in markers:
-                    markers.add(mark)
-
-    return markers
-
-def preparesuccessorset(successorset, rawmarkers):
-    """ For a successor set, get all related markers, compute the set of user,
-    the min date and the max date
-    """
-    hex = nodemod.hex
-
-    successorset = [hex(n) for n in successorset]
-
-    # hex the binary nodes in the markers
-    markers = []
-    for m in rawmarkers:
-        hexprec = hex(m[0])
-        hexsucs = tuple(hex(n) for n in m[1])
-        hexparents = None
-        if m[5] is not None:
-            hexparents = tuple(hex(n) for n in m[5])
-        newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
-        markers.append(newmarker)
-
-    # Format basic data
-    data = {
-        b"successors": sorted(successorset),
-        b"markers": sorted(markers)
-    }
-
-    # Call an extensible list of functions to override or add new data
-    for function in FORMATSSETSFUNCTIONS:
-        data.update(function(successorset, markers))
-
-    return data
+@eh.wrapfunction(obsutil, 'obsfateverb')
+def obsfateverb(orig, *args, **kwargs):
+    return _successorsetverb(*args, **kwargs)[b'verb']
--- a/hgext3rd/evolve/rewind.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/rewind.py	Sat Feb 29 15:18:00 2020 +0700
@@ -36,7 +36,8 @@
       _(b"do not modify working directory during rewind")),
      ],
     _(b'[--as-divergence] [--exact] [--keep] [--to REV]... [--from REV]...'),
-    helpbasic=True)
+    helpbasic=True,
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_MANAGEMENT'))
 def rewind(ui, repo, **opts):
     """rewind a stack of changesets to a previous state
 
--- a/hgext3rd/evolve/rewriteutil.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/rewriteutil.py	Sat Feb 29 15:18:00 2020 +0700
@@ -21,7 +21,7 @@
     lock as lockmod,
     node,
     obsolete,
-    phases,
+    obsutil,
     revset,
     util,
 )
@@ -76,6 +76,31 @@
         msg %= (action, len(newunstable))
         hint = _(b"see 'hg help evolution.instability'")
         raise error.Abort(msg, hint=hint)
+    divrisk = revs_hascontentdivrisk(repo, revs)
+    allowdivergence = repo.ui.configbool(b'experimental',
+                                         b'evolution.allowdivergence')
+    if divrisk and not allowdivergence:
+        localdiv = repo[divrisk[0]]
+        otherdiv, base = repo[divrisk[1][0]], repo[divrisk[1][1]]
+        msg = _(b"%s of %s creates content-divergence "
+                b"with %s") % (action, localdiv, otherdiv)
+        if localdiv.rev() != base.rev():
+            msg += _(b', from %s') % base
+        hint = _(b"add --verbose for details or see "
+                 b"'hg help evolution.instability'")
+        if repo.ui.verbose:
+            if localdiv.rev() != base.rev():
+                msg += _(b'\n    changeset %s is an evolution of '
+                         b'changeset %s') % (localdiv, base)
+            msg += _(b'\n    changeset %s already have a successors as '
+                     b'changeset %s\n'
+                     b'    rewriting changeset %s would create '
+                     b'"content-divergence"\n'
+                     b'    set experimental.evolution.allowdivergence=True to '
+                     b'overwrite this check') % (base, otherdiv, localdiv)
+            hint = _(b"see 'hg help evolution.instability' for details "
+                     b"on content-divergence")
+        raise error.Abort(msg, hint=hint)
 
 def bookmarksupdater(repo, oldid, tr):
     """Return a callable update(newid) updating the current bookmark
@@ -88,6 +113,14 @@
             repo._bookmarks.applychanges(repo, tr, bmchanges)
     return updatebookmarks
 
+def revs_hascontentdivrisk(repo, revs):
+    obsrevs = repo.revs(b'%ld and obsolete()', revs)
+    for r in obsrevs:
+        div = precheck_contentdiv(repo, repo[r])
+        if div:
+            return [r, div]
+    return []
+
 def disallowednewunstable(repo, revs):
     """Check that editing <revs> will not create disallowed unstable
 
@@ -105,9 +138,6 @@
     if len(roots) > 1:
         raise error.Abort(_(b"cannot fold non-linear revisions "
                             b"(multiple roots given)"))
-    root = repo[roots.first()]
-    if root.phase() <= phases.public:
-        raise error.Abort(_(b"cannot fold public revisions"))
     heads = repo.revs(b'heads(%ld)', revs)
     if len(heads) > 1:
         raise error.Abort(_(b"cannot fold non-linear revisions "
@@ -124,6 +154,7 @@
             hint = _(b'set experimental.evolution.allowdivergence=yes'
                      b' to allow folding them')
             raise error.Abort(msg, hint=hint)
+    root = repo[roots.first()]
     # root's p1 is already used as the target ctx p1
     baseparents -= {root.p1().rev()}
     p2 = repo[baseparents.first()]
@@ -254,3 +285,31 @@
         return newid, created
     finally:
         lockmod.release(tr, lock, wlock)
+
+def precheck_contentdiv(repo, ctx):
+    """return divergent revision if rewriting an obsolete cset (ctx) will
+    create divergence"""
+    # We need to check two cases that can cause divergence:
+    # case 1: the rev being rewritten has a non-obsolete successor (easily
+    #     detected by successorssets)
+    divergent = [] # contains [divergent_cset, common_precursor]
+    sset = obsutil.successorssets(repo, ctx.node())
+    nodivergencerisk = (len(sset) == 0
+                        or (len(sset) == 1
+                            and len(sset[0]) == 1
+                            and repo[sset[0][0]].rev() == ctx.rev()
+                        ))
+    if nodivergencerisk:
+        # case 2: one of the precursors of the rev being revived has a
+        #     non-obsolete successor (we need divergentsets for this)
+        from . import evolvecmd
+        divsets = evolvecmd.divergentsets(repo, ctx)
+        if divsets:
+            nsuccset = divsets[0][b'divergentnodes']
+            divergent.append(nsuccset[0])
+            prec = divsets[0][b'commonprecursor']
+            divergent.append(prec)
+    else:
+        divergent.append(sset[0][0])
+        divergent.append(ctx.node())
+    return divergent
--- a/hgext3rd/evolve/stablerangecache.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/stablerangecache.py	Sat Feb 29 15:18:00 2020 +0700
@@ -22,6 +22,12 @@
     util,
 )
 
+try:
+    from mercurial.utils.stringutil import forcebytestr
+except ImportError:
+    # hg <= 4.5 (f99d64e8a4e4)
+    from mercurial.util import forcebytestr
+
 from . import (
     compat,
     exthelper,
@@ -282,8 +288,8 @@
             if r'_con' in vars(self):
                 del self._con
             self._unsavedsubranges.clear()
-            repo.ui.log(b'evoext-cache', b'error while saving new data: %s' % exc)
-            repo.ui.debug(b'evoext-cache: error while saving new data: %s' % exc)
+            repo.ui.log(b'evoext-cache', b'error while saving new data: %s' % forcebytestr(exc))
+            repo.ui.debug(b'evoext-cache: error while saving new data: %s' % forcebytestr(exc))
 
     def _trysave(self, repo):
         repo = repo.unfiltered()
--- a/hgext3rd/evolve/stablesort.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/stablesort.py	Sat Feb 29 15:18:00 2020 +0700
@@ -259,6 +259,12 @@
     scmutil,
 )
 
+try:
+    from mercurial.utils.stringutil import forcebytestr
+except ImportError:
+    # hg <= 4.5 (f99d64e8a4e4)
+    from mercurial.util import forcebytestr
+
 from mercurial.i18n import _
 
 from . import (
@@ -932,8 +938,8 @@
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
-            repo.ui.log(b'stablesortcache', b'could not write update %s\n' % exc)
-            repo.ui.debug(b'stablesortcache: could not write update %s\n' % exc)
+            repo.ui.log(b'stablesortcache', b'could not write update %s\n' % forcebytestr(exc))
+            repo.ui.debug(b'stablesortcache: could not write update %s\n' % forcebytestr(exc))
 
 @eh.reposetup
 def setupcache(ui, repo):
--- a/hgext3rd/evolve/templatekw.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/templatekw.py	Sat Feb 29 15:18:00 2020 +0700
@@ -11,7 +11,6 @@
 from . import (
     error,
     exthelper,
-    obshistory
 )
 
 from mercurial import (
@@ -24,7 +23,7 @@
 ### template keywords
 
 if util.safehasattr(templatekw, 'compatlist'):
-    @eh.templatekeyword(b'instabilities', requires=set([b'ctx', b'templ']))
+    @eh.templatekeyword(b'instabilities', requires={b'ctx', b'templ'})
     def showinstabilities(context, mapping):
         """List of strings. Evolution instabilities affecting the changeset
         (zero or more of "orphan", "content-divergent" or "phase-divergent")."""
@@ -33,7 +32,7 @@
                                      ctx.instabilities(),
                                      plural=b'instabilities')
 
-    @eh.templatekeyword(b'troubles', requires=set([b'ctx', b'templ']))
+    @eh.templatekeyword(b'troubles', requires={b'ctx', b'templ'})
     def showtroubles(context, mapping):   # legacy name for instabilities
         ctx = context.resource(mapping, b'ctx')
         return templatekw.compatlist(context, mapping, b'trouble',
@@ -87,192 +86,6 @@
     except error.Abort: # no easy way to avoid ui raising Abort here :-/
         return None
 
-def obsfatedefaulttempl(ui):
-    """ Returns a dict with the default templates for obs fate
-    """
-    # Prepare templates
-    verbtempl = b'{verb}'
-    usertempl = b'{if(users, " by {join(users, ", ")}")}'
-    succtempl = b'{if(successors, " as ")}{successors}' # Bypass if limitation
-    datetempleq = b' (at {min_date|isodate})'
-    datetemplnoteq = b' (between {min_date|isodate} and {max_date|isodate})'
-    datetempl = b'{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' % (datetempleq, datetemplnoteq)
-
-    optionalusertempl = usertempl
-    username = _getusername(ui)
-    if username is not None:
-        optionalusertempl = (b'{ifeq(join(users, "\0"), "%s", "", "%s")}'
-                             % (username, usertempl))
-
-    # Assemble them
-    return {
-        b'obsfate_quiet': verbtempl + succtempl,
-        b'obsfate': verbtempl + succtempl + optionalusertempl,
-        b'obsfate_verbose': verbtempl + succtempl + usertempl + datetempl,
-    }
-
-def obsfatedata(repo, ctx):
-    """compute the raw data needed for computing obsfate
-    Returns a list of dict
-    """
-    if not ctx.obsolete():
-        return None
-
-    successorssets, pathcache = closestsuccessors(repo, ctx.node())
-
-    # closestsuccessors returns an empty list for pruned revisions, remap it
-    # into a list containing en empty list for future processing
-    if successorssets == []:
-        successorssets = [[]]
-
-    succsmap = repo.obsstore.successors
-    fullsuccessorsets = [] # successor set + markers
-    for sset in successorssets:
-        if sset:
-            markers = obshistory.successorsetallmarkers(sset, pathcache)
-            fullsuccessorsets.append((sset, markers))
-        else:
-            # XXX we do not catch all prune markers (eg rewritten then pruned)
-            # (fix me later)
-            foundany = False
-            for mark in succsmap.get(ctx.node(), ()):
-                if not mark[1]:
-                    foundany = True
-                    fullsuccessorsets.append((sset, [mark]))
-            if not foundany:
-                fullsuccessorsets.append(([], []))
-
-    values = []
-    for sset, rawmarkers in fullsuccessorsets:
-        raw = obshistory.preparesuccessorset(sset, rawmarkers)
-        values.append(raw)
-
-    return values
-
-def obsfatelineprinter(obsfateline, ui):
-    quiet = ui.quiet
-    verbose = ui.verbose
-    normal = not verbose and not quiet
-
-    # Build the line step by step
-    line = []
-
-    # Verb
-    line.append(obsfateline[b'verb'])
-
-    # Successors
-    successors = obsfateline[b"successors"]
-
-    if successors:
-        fmtsuccessors = map(lambda s: s[:12], successors)
-        line.append(b" as %s" % b", ".join(fmtsuccessors))
-
-    # Users
-    if (verbose or normal) and b'users' in obsfateline:
-        users = obsfateline[b'users']
-
-        if not verbose:
-            # If current user is the only user, do not show anything if not in
-            # verbose mode
-            username = _getusername(ui)
-            if len(users) == 1 and users[0] == username:
-                users = None
-
-        if users:
-            line.append(b" by %s" % b", ".join(users))
-
-    # Date
-    if verbose:
-        min_date = obsfateline[b'min_date']
-        max_date = obsfateline[b'max_date']
-
-        if min_date == max_date:
-            fmtmin_date = util.datestr(min_date, b'%Y-%m-%d %H:%M %1%2')
-            line.append(b" (at %s)" % fmtmin_date)
-        else:
-            fmtmin_date = util.datestr(min_date, b'%Y-%m-%d %H:%M %1%2')
-            fmtmax_date = util.datestr(max_date, b'%Y-%m-%d %H:%M %1%2')
-            line.append(b" (between %s and %s)" % (fmtmin_date, fmtmax_date))
-
-    return b"".join(line)
-
-def obsfateprinter(obsfate, ui, prefix=b""):
-    lines = []
-    for raw in obsfate:
-        lines.append(obsfatelineprinter(raw, ui))
-
-    if prefix:
-        lines = [prefix + line for line in lines]
-
-    return b"\n".join(lines)
-
-if not util.safehasattr(templatekw, 'obsfateverb'): # <= hg-4.5
-    @eh.templatekeyword(b"obsfatedata")
-    def showobsfatedata(repo, ctx, **args):
-        # Get the needed obsfate data
-        values = obsfatedata(repo, ctx)
-
-        if values is None:
-            return templatekw.showlist(b"obsfatedata", [], args)
-
-        return _showobsfatedata(repo, ctx, values, **args)
-
-def _showobsfatedata(repo, ctx, values, **args):
-
-    # Format each successorset successors list
-    for raw in values:
-        # As we can't do something like
-        # "{join(map(nodeshort, successors), ', '}" in template, manually
-        # create a correct textual representation
-        gen = b', '.join(n[:12] for n in raw[b'successors'])
-
-        makemap = lambda x: {b'successor': x}
-        joinfmt = lambda d: b"%s" % d[b'successor']
-        raw[b'successors'] = templatekw._hybrid(gen, raw[b'successors'], makemap,
-                                                joinfmt)
-
-    # And then format them
-    # Insert default obsfate templates
-    args[b'templ'].cache.update(obsfatedefaulttempl(repo.ui))
-
-    if repo.ui.quiet:
-        name = b"obsfate_quiet"
-    elif repo.ui.verbose:
-        name = b"obsfate_verbose"
-    elif repo.ui.debugflag:
-        name = b"obsfate_debug"
-    else:
-        name = b"obsfate"
-
-    # Format a single value
-    def fmt(d):
-        nargs = args.copy()
-        nargs.update(d[name])
-        templ = args[b'templ']
-        # HG 4.6
-        if hasattr(templ, "generate"):
-            return templ.generate(name, nargs)
-        else:
-            return args[b'templ'](name, **nargs)
-
-    # Generate a good enough string representation using templater
-    gen = []
-    for d in values:
-        chunk = fmt({name: d})
-        chunkstr = []
-
-        # Empty the generator
-        try:
-            while True:
-                chunkstr.append(next(chunk))
-        except StopIteration:
-            pass
-
-        gen.append(b"".join(chunkstr))
-    gen = b"; ".join(gen)
-
-    return templatekw._hybrid(gen, values, lambda x: {name: x}, fmt)
-
 # copy from mercurial.obsolete with a small change to stop at first known changeset.
 
 def directsuccessorssets(repo, initialnode, cache=None):
--- a/hgext3rd/evolve/utility.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/evolve/utility.py	Sat Feb 29 15:18:00 2020 +0700
@@ -13,10 +13,6 @@
 
 from mercurial.node import nullrev
 
-from . import (
-    compat,
-)
-
 shorttemplate = b"[{label('evolve.rev', rev)}] {desc|firstline}\n"
 stacktemplate = b"""[{label('evolve.rev', if(topicidx, "s{topicidx}", rev))}] {desc|firstline}\n"""
 
@@ -27,12 +23,6 @@
     if important or verbose:
         ui.status(message)
 
-def obsexcprg(ui, *args, **kwargs):
-    topic = b'obsmarkers exchange'
-    if ui.configbool(b'experimental', b'verbose-obsolescence-exchange'):
-        topic = b'OBSEXC'
-    compat.progress(ui, topic, *args, **kwargs)
-
 def filterparents(parents):
     """filter nullrev parents
 
@@ -85,6 +75,8 @@
 
     def __init__(self, successorssets):
         self.successorssets = successorssets
+        self.divergenceflag = len(successorssets) > 1
+        self.splitflag = len(successorssets[0]) > 1
 
 def builddependencies(repo, revs):
     """returns dependency graphs giving an order to solve instability of revs
@@ -126,13 +118,41 @@
         ui.debug(b"stabilize target %s is plain dead,"
                  b" trying to stabilize on its parent\n" %
                  obs)
-        obs = obs.parents()[0]
+        obs = obs.p1()
         newer = obsutil.successorssets(repo, obs.node())
     if len(newer) > 1 or len(newer[0]) > 1:
         raise MultipleSuccessorsError(newer)
 
     return repo[newer[0][0]].rev()
 
+def picksplitsuccessor(ui, repo, ctx, evolvecand):
+    """choose a successor of ctx from split targets
+
+    Choose highest one if all successors are in a topological branch. And if
+    they are split over multiple topological branches, we ask user to choose
+    an evolve destination.
+
+    Return (True, succ) unless split targets are split over multiple
+    topological branches and user didn't choose any evolve destination,
+    in which case return (False, '.')
+    """
+    targets = obsutil.successorssets(repo, ctx.node())[0]
+    assert targets
+    targetrevs = [repo[r].rev() for r in targets]
+    roots = repo.revs(b'roots(%ld)', targetrevs)
+    heads = repo.revs(b'heads(%ld)', targetrevs)
+    if len(roots) > 1 or len(heads) > 1:
+        cheader = (_(b"ancestor '%s' split over multiple topological"
+                     b" branches.\nchoose an evolve destination:") %
+                   evolvecand)
+        selectedrev = revselectionprompt(ui, repo, list(heads), cheader)
+        if selectedrev is None:
+            return (False, '.')
+        succ = repo[selectedrev]
+    else:
+        succ = repo[heads.first()]
+    return (True, repo[succ].rev())
+
 def _successorrevs(repo, ctx):
     try:
         return {_singlesuccessor(repo, ctx)}
--- a/hgext3rd/pullbundle.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/pullbundle.py	Sat Feb 29 15:18:00 2020 +0700
@@ -87,6 +87,7 @@
     node as nodemod,
     registrar,
     scmutil,
+    ui as uimod,
     util,
 )
 
@@ -508,7 +509,7 @@
     if 1 < min_cache:
         repo.ui.write(b"  not caching ranges smaller than %d changesets\n" % min_cache)
     for i in range(count):
-        repo.ui.progress(b'gathering data', i, total=count)
+        progress(repo.ui, b'gathering data', i, total=count)
         outgoing = takeonesample(repo, actionrevs)
         ranges = sliceoutgoing(repo, outgoing)
         hitranges = 0
@@ -532,7 +533,7 @@
                  hitranges,
                  )
         pullstats.append(stats)
-    repo.ui.progress(b'gathering data', None)
+    progress(repo.ui, b'gathering data', None)
 
     sizes = []
     changesmissing = []
@@ -591,6 +592,7 @@
 
 def takeonesample(repo, revs):
     node = repo.changelog.node
+    revs = list(revs)
     pulled = random.sample(revs, max(4, len(revs) // 1000))
     pulled = repo.revs(b'%ld::%ld', pulled, pulled)
     nodes = [node(r) for r in pulled]
@@ -610,19 +612,32 @@
         b'max': data[-1],
     }
 
-STATSFORMAT = b"""{name}:
-  min: {min}
-  10%: {10%}
-  25%: {25%}
-  50%: {50%}
-  75%: {75%}
-  90%: {90%}
-  95%: {95%}
-  max: {max}
+STATSFORMAT = b"""%(name)s:
+  min: %(min)r
+  10%%: %(10%)r
+  25%%: %(25%)r
+  50%%: %(50%)r
+  75%%: %(75%)r
+  90%%: %(90%)r
+  95%%: %(95%)r
+  max: %(max)r
 """
 
 def fmtdist(name, data):
-    return STATSFORMAT.format(name=name, **data)
+    data[b'name'] = name
+    return STATSFORMAT % data
+
+# hg <= 4.6 (bec1212eceaa)
+if util.safehasattr(uimod.ui, 'makeprogress'):
+    def progress(ui, topic, pos, item=b"", unit=b"", total=None):
+        progress = ui.makeprogress(topic, unit, total)
+        if pos is not None:
+            progress.update(pos, item=item)
+        else:
+            progress.complete()
+else:
+    def progress(ui, topic, pos, item=b"", unit=b"", total=None):
+        ui.progress(topic, pos, item, unit, total)
 
 # nodemap.get and index.[has_node|rev|get_rev]
 # hg <= 5.3 (02802fa87b74)
--- a/hgext3rd/serverminitopic.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/serverminitopic.py	Sat Feb 29 15:18:00 2020 +0700
@@ -23,10 +23,11 @@
     util,
 )
 
+# hg <= 4.5 (b4d85bc122bd)
 try:
-    from mercurial import wireproto
+    from mercurial import wireproto  # pytype: disable=import-error
     wireproto.branchmap
-except ImportError: # <= hg-4.5
+except ImportError:
     from mercurial import wireprotov1server as wireproto
 
 if util.safehasattr(registrar, 'configitem'):
--- a/hgext3rd/topic/__init__.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/topic/__init__.py	Sat Feb 29 15:18:00 2020 +0700
@@ -110,6 +110,19 @@
     [experimental]
     topic.allow-publish = no
 
+Server side visibility
+======================
+
+Serving changesets with topics to clients without topic extension can get
+confusing. Such clients will have multiple anonymous heads without a clear way
+to distinguish them. They will also "lose" the canonical heads of the branch.
+
+To avoid this confusion, server can be configured to only serve changesets with
+topics to clients with the topic extension (version 9.3+). This might become
+the default in future::
+
+    [experimental]
+    topic.server-gate-topic-changesets = yes
 """
 
 from __future__ import absolute_import
@@ -135,6 +148,7 @@
     namespaces,
     node,
     obsolete,
+    obsutil,
     patch,
     phases,
     pycompat,
@@ -154,6 +168,7 @@
     flow,
     randomname,
     revset as topicrevset,
+    server,
     stack,
     topicmap,
 )
@@ -187,10 +202,10 @@
               b'topic.active': b'green',
               }
 
-__version__ = b'0.17.2.dev'
+__version__ = b'0.18.1.dev'
 
-testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0 5.1'
-minimumhgversion = b'4.5'
+testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3'
+minimumhgversion = b'4.6'
 buglink = b'https://bz.mercurial-scm.org/'
 
 if util.safehasattr(registrar, 'configitem'):
@@ -221,6 +236,9 @@
     configitem(b'experimental', b'topic-mode.server',
                default=configitems.dynamicdefault,
     )
+    configitem(b'experimental', b'topic.server-gate-topic-changesets',
+               default=False,
+    )
 
     def extsetup(ui):
         # register config that strictly belong to other code (thg, core, etc)
@@ -363,6 +381,8 @@
     # Wrap changelog.add to drop empty topic
     extensions.wrapfunction(changelog.changelog, 'add', wrapadd)
 
+    server.setupserver(ui)
+
 def reposetup(ui, repo):
     if not isinstance(repo, localrepo.localrepository):
         return # this can be a peer in the ssh case (puzzling)
@@ -384,14 +404,14 @@
             return caps
 
         def commit(self, *args, **kwargs):
-            backup = self.ui.backupconfig(b'ui', b'allowemptycommit')
-            try:
-                if self.currenttopic != self[b'.'].topic():
-                    # bypass the core "nothing changed" logic
-                    self.ui.setconfig(b'ui', b'allowemptycommit', True)
+            configoverride = util.nullcontextmanager()
+            if self.currenttopic != self[b'.'].topic():
+                # bypass the core "nothing changed" logic
+                configoverride = self.ui.configoverride({
+                    (b'ui', b'allowemptycommit'): True
+                })
+            with configoverride:
                 return super(topicrepo, self).commit(*args, **kwargs)
-            finally:
-                self.ui.restoreconfig(backup)
 
         def commitctx(self, ctx, *args, **kwargs):
             topicfilter = topicmap.topicfilter(self.filtername)
@@ -559,7 +579,7 @@
                         msg = _(b"active topic '%s' grew its first changeset\n%s")
                         ui.status(msg % (ct, hint))
                     else:
-                        msg = _(b"active topic '%s' grew its %s first changesets\n%s")
+                        msg = _(b"active topic '%s' grew its %d first changesets\n%s")
                         ui.status(msg % (ct, csetcount, hint))
 
             tr.addpostclose(b'signalcurrenttopicempty', currenttopicempty)
@@ -634,7 +654,8 @@
         (b'', b'age', False, b'show when you last touched the topics'),
         (b'', b'current', None, b'display the current topic only'),
     ] + commands.formatteropts,
-    _(b'hg topics [OPTION]... [-r REV]... [TOPIC]'))
+    _(b'hg topics [OPTION]... [-r REV]... [TOPIC]'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_ORGANIZATION'))
 def topics(ui, repo, topic=None, **opts):
     """View current topic, set current topic, change topic for a set of revisions, or see all topics.
 
@@ -777,7 +798,8 @@
         (b'c', b'children', None,
             _(b'display data about children outside of the stack'))
     ] + commands.formatteropts,
-    _(b'hg stack [TOPIC]'))
+    _(b'hg stack [TOPIC]'),
+    **compat.helpcategorykwargs('CATEGORY_CHANGE_NAVIGATION'))
 def cmdstack(ui, repo, topic=b'', **opts):
     """list all changesets in a topic and other information
 
@@ -1115,7 +1137,7 @@
                 user = repo[revs].user()
             # looking on the markers also to get more information and accurate
             # last touch time.
-            obsmarkers = compat.getmarkers(repo, [repo[revs].node()])
+            obsmarkers = obsutil.getmarkers(repo, [repo[revs].node()])
             for marker in obsmarkers:
                 rt = marker.date()
                 if rt[0] > maxtime[0]:
@@ -1258,14 +1280,13 @@
 def checkt0(orig, ui, repo, node=None, rev=None, *args, **kwargs):
 
     thezeros = set([b't0', b'b0', b's0'])
-    backup = repo.ui.backupconfig(b'_internal', b'keep-topic')
-    try:
-        if node in thezeros or rev in thezeros:
-            repo.ui.setconfig(b'_internal', b'keep-topic', b'yes',
-                              source=b'topic-extension')
+    configoverride = util.nullcontextmanager()
+    if node in thezeros or rev in thezeros:
+        configoverride = repo.ui.configoverride({
+            (b'_internal', b'keep-topic'): b'yes'
+        }, source=b'topic-extension')
+    with configoverride:
         return orig(ui, repo, node=node, rev=rev, *args, **kwargs)
-    finally:
-        repo.ui.restoreconfig(backup)
 
 def _fixrebase(loaded):
     if not loaded:
--- a/hgext3rd/topic/compat.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/topic/compat.py	Sat Feb 29 15:18:00 2020 +0700
@@ -8,25 +8,11 @@
 from __future__ import absolute_import
 
 from mercurial import (
-    obsolete,
     pycompat,
+    registrar,
     util,
 )
 
-getmarkers = None
-successorssets = None
-try:
-    from mercurial import obsutil
-    getmarkers = getattr(obsutil, 'getmarkers', None)
-    successorssets = getattr(obsutil, 'successorssets', None)
-except ImportError:
-    pass
-
-if getmarkers is None:
-    getmarkers = obsolete.getmarkers
-if successorssets is None:
-    successorssets = obsolete.successorssets
-
 if pycompat.ispy3:
     def branchmapitems(branchmap):
         return branchmap.items()
@@ -36,6 +22,15 @@
         return branchmap.iteritems()
     # py3-transform: on
 
+# help category compatibility
+# hg <= 4.7 (c303d65d2e34)
+def helpcategorykwargs(categoryname):
+    """Backwards-compatible specification of the helpategory argument."""
+    category = getattr(registrar.command, categoryname, None)
+    if not category:
+        return {}
+    return {'helpcategory': category}
+
 # nodemap.get and index.[has_node|rev|get_rev]
 # hg <= 5.3 (02802fa87b74)
 def getgetrev(cl):
--- a/hgext3rd/topic/discovery.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/topic/discovery.py	Sat Feb 29 15:18:00 2020 +0700
@@ -17,10 +17,11 @@
     compat,
 )
 
+# hg <= 4.5 (b4d85bc122bd)
 try:
-    from mercurial import wireproto
+    from mercurial import wireproto  # pytype: disable=import-error
     wireproto.branchmap
-except (AttributeError, ImportError): # <= hg-4.5
+except (AttributeError, ImportError):
     from mercurial import wireprotov1server as wireproto
 
 def _headssummary(orig, pushop, *args, **kwargs):
--- a/hgext3rd/topic/evolvebits.py	Thu Jan 30 14:32:10 2020 +0700
+++ b/hgext3rd/topic/evolvebits.py	Sat Feb 29 15:18:00 2020 +0700
@@ -81,7 +81,7 @@
         ui.debug(b"stabilize target %s is plain dead,"
                  b" trying to stabilize on its parent\n" %
                  obs)
-        obs = obs.parents()[0]
+        obs = obs.p1()
         newer = obsutil.successorssets(repo, obs.node())
     if 1 < len(newer):
         # divergence case
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/topic/server.py	Sat Feb 29 15:18:00 2020 +0700
@@ -0,0 +1,108 @@
+# topic/server.py - server specific behavior with topic
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+from mercurial import (
+    extensions,
+    repoview,
+    wireprototypes,
+    wireprotov1peer,
+    wireprotov1server,
+)
+
+
+try:
+    from mercurial.utils import (
+        repoviewutil,
+    )
+    repoviewutil.subsettable
+except (AttributeError, ImportError):
+    # hg <= 4.8
+    from mercurial import branchmap as repoviewutil
+
+from . import (
+    common,
+    constants,
+)
+
+### Visibility restriction
+#
+# Serving draft changesets with topics to clients without topic extension can
+# confuse them, because they won't see the topic label and will consider them
+# normal anonymous heads. Instead we have the option to not serve changesets
+# with topics to clients without topic support.
+#
+# To achieve this, we alter the behavior of the standard `heads` commands and
+# introduce a new `heads` command that only clients with topic will know about.
+
+# compat version of the wireprotocommand decorator, taken from evolve compat
+
+FILTERNAME = b'served-no-topic'
+
+def computeunservedtopic(repo, visibilityexceptions=None):
+    assert not repo.changelog.filteredrevs
+    filteredrevs = repoview.filtertable[b'served'](repo, visibilityexceptions).copy()
+    mutable = repoview.filtertable[b'immutable'](repo, visibilityexceptions)
+    consider = mutable - filteredrevs
+    cl = repo.changelog
+    extrafiltered = set()
+    for r in consider:
+        if cl.changelogrevision(r).extra.get(constants.extrakey, b''):
+            extrafiltered.add(r)
+    if extrafiltered:
+        extrafiltered = set(repo.revs('%ld::%ld', extrafiltered, consider))
+        filteredrevs = frozenset(filteredrevs | extrafiltered)
+    return filteredrevs
+
+def wireprotocommand(name, args=b'', permission=b'pull'):
+    try:
+        from mercurial.wireprotov1server import wireprotocommand
+    except (ImportError, AttributeError):
+        # hg <= 4.6 (b4d85bc122bd)
+        from mercurial.wireproto import wireprotocommand
+    return wireprotocommand(name, args, permission=permission)
+
+def wrapheads(orig, repo, proto):
+    """wrap head to hide topic^W draft changeset to old client"""
+    hidetopics = repo.ui.configbool(b'experimental', b'topic.server-gate-topic-changesets')
+    if common.hastopicext(repo) and hidetopics:
+        h = repo.filtered(FILTERNAME).heads()
+        return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + b'\n')
+    return orig(repo, proto)
+
+def topicheads(repo, proto):
+    """Same as the normal wireprotocol command, but accessing with a different end point."""
+    h = repo.heads()
+    return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + b'\n')
+
+def wireprotocaps(orig, repo, proto):
+    """advertise the new topic specific `head` command for client with topic"""
+    caps = orig(repo, proto)
+    if common.hastopicext(repo) and repo.peer().capable(b'topics'):
+        caps.append(b'_exttopics_heads')
+    return caps
+
+def setupserver(ui):
+    extensions.wrapfunction(wireprotov1server, 'heads', wrapheads)
+    wireprotov1server.commands.pop(b'heads')
+    wireprotocommand(b'heads', permission=b'pull')(wireprotov1server.heads)
+    wireprotocommand(b'_exttopics_heads', permission=b'pull')(topicheads)
+    extensions.wrapfunction(wireprotov1server, '_capabilities', wireprotocaps)
+
+    class topicpeerexecutor(wireprotov1peer.peerexecutor):
+
+        def callcommand(self, command, args):
+            if command == b'heads':
+                if self._peer.capable(b'_exttopics_heads'):
+                    command = b'_exttopics_heads'
+                    if getattr(self._peer, '_exttopics_heads', None) is None:
+                        self._peer._exttopics_heads = self._peer.heads
+            s = super(topicpeerexecutor, self)
+            return s.callcommand(command, args)
+
+    wireprotov1peer.peerexecutor = topicpeerexecutor
+
+    if FILTERNAME not in repoview.filtertable:
+        repoview.filtertable[FILTERNAME] = computeunservedtopic
+        repoviewutil.subsettable[FILTERNAME] = b'immutable'
+        repoviewutil.subsettable[b'served'] = FILTERNAME
--- a/tests/test-amend-patch.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-amend-patch.t	Sat Feb 29 15:18:00 2020 +0700
@@ -88,7 +88,7 @@
   @  af624b221c0c (1) added a
   |
   x  f7ad41964313 (0) added a
-       rewritten(content) as af624b221c0c using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as af624b221c0c using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff --git a/a b/a
          --- a/a
          +++ b/a
--- a/tests/test-amend.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-amend.t	Sat Feb 29 15:18:00 2020 +0700
@@ -25,7 +25,7 @@
   @  6a022cbb61d5 (1) adda
   |
   x  07f494440405 (0) adda
-       rewritten(branch) as 6a022cbb61d5 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       branch-changed(branch) as 6a022cbb61d5 using amend 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
@@ -121,6 +121,9 @@
   update: (current)
   phases: 3 draft
 
+setting the user after we have performed the test with no username
+  $ HGUSER=test
+
 Check the help
   $ hg amend -h
   hg amend [OPTION]... [FILE]...
@@ -170,3 +173,61 @@
   $ hg amend --patch --extract
   abort: cannot use both --patch and --extract
   [255]
+
+  $ cd ..
+
+Check the pre-check logic for content-divergence
+  $ hg init precheckrepo
+  $ cd precheckrepo
+  $ echo a > a
+  $ hg ci -Am "added a"
+  adding a
+  $ echo newchanges > a
+  $ hg amend
+  $ hg up 0 --hidden -q
+  updating to a hidden changeset 9092f1db7931
+  (hidden revision '9092f1db7931' was rewritten as: aafaf407b00d)
+  working directory parent is obsolete! (9092f1db7931)
+
+when rewritting an already rewritten changeset (i.e cset being rewritten will
+be the base of divergence)
+  $ hg amend -m "i am gonna create divergence"
+  abort: amend of 9092f1db7931 creates content-divergence with aafaf407b00d
+  (add --verbose for details or see 'hg help evolution.instability')
+  [255]
+  $ hg amend -m "i am gonna create divergence" --verbose
+  abort: amend of 9092f1db7931 creates content-divergence with aafaf407b00d
+      changeset 9092f1db7931 already have a successors as changeset aafaf407b00d
+      rewriting changeset 9092f1db7931 would create "content-divergence"
+      set experimental.evolution.allowdivergence=True to overwrite this check
+  (see 'hg help evolution.instability' for details on content-divergence)
+  [255]
+
+when rewritting a cset which has a predecessor with non-obsolete successor
+
+to prepare the repo
+1) create content-divergence
+2) remove divergence by pruning one of the divergent cset
+  $ echo edited_a > a
+  $ hg ci -m "edited a"
+  1 new orphan changesets
+  $ hg debugobsolete $(hg id -ir 0 --debug) $(hg id -ir 2 --debug)
+  2 new content-divergent changesets
+
+remove divergence by pruning one side of divergenence
+  $ hg debugobsolete $(hg id -ir 2 --debug)
+  obsoleted 1 changesets
+
+  $ hg evolve -l
+  $ hg amend -m "i am gonna create divergence"
+  abort: amend of f8c05838af90 creates content-divergence with aafaf407b00d, from 9092f1db7931
+  (add --verbose for details or see 'hg help evolution.instability')
+  [255]
+  $ hg amend -m "i am gonna create divergence" --verbose
+  abort: amend of f8c05838af90 creates content-divergence with aafaf407b00d, from 9092f1db7931
+      changeset f8c05838af90 is an evolution of changeset 9092f1db7931
+      changeset 9092f1db7931 already have a successors as changeset aafaf407b00d
+      rewriting changeset f8c05838af90 would create "content-divergence"
+      set experimental.evolution.allowdivergence=True to overwrite this check
+  (see 'hg help evolution.instability' for details on content-divergence)
+  [255]
--- a/tests/test-evolve-content-divergent-basic.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-basic.t	Sat Feb 29 15:18:00 2020 +0700
@@ -21,6 +21,8 @@
   > unified = 0
   > [ui]
   > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline} [{instabilities}]\n
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
@@ -998,7 +1000,7 @@
   @  8c2a6aae4bc5 (5) E4
   |
   x    aae293050980 (4) <<<<<<< divergent
-  |\     rewritten(description) as 8c2a6aae4bc5 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |\     reworded(description) as 8c2a6aae4bc5 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   | |      diff -r aae293050980 -r 8c2a6aae4bc5 changeset-description
   | |      --- a/changeset-description
   | |      +++ b/changeset-description
--- a/tests/test-evolve-content-divergent-corner-cases.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-corner-cases.t	Sat Feb 29 15:18:00 2020 +0700
@@ -21,6 +21,8 @@
   > unified = 0
   > [ui]
   > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline} [{instabilities}]\n
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
--- a/tests/test-evolve-content-divergent-first-changeset.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-first-changeset.t	Sat Feb 29 15:18:00 2020 +0700
@@ -1,6 +1,10 @@
   $ . $TESTDIR/testlib/pythonpath.sh
-  $ echo "[extensions]" >> $HGRCPATH
-  $ echo "evolve=" >> $HGRCPATH
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > evolve=
+  > [experimental]
+  > evolution.allowdivergence = True
+  > EOF
 
 This test file tests the case of content-divergence resolution of changesets
 that have the null revision as the parent.
--- a/tests/test-evolve-content-divergent-interrupted.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-interrupted.t	Sat Feb 29 15:18:00 2020 +0700
@@ -24,6 +24,8 @@
   > publish = False
   > [alias]
   > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
@@ -239,11 +241,11 @@
   | @  491e10505bae (10) added c
   | |
   | x  7398f702a162 (9) added c
-  |/     rewritten(content) as 491e10505bae using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |/     amended(content) as 491e10505bae using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  ca1b80f7960a (3) added c
-       rewritten(content) as 2ba73e31f264 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
-       rewritten(content) as 7398f702a162 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 2ba73e31f264 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 7398f702a162 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ cd ..
 
--- a/tests/test-evolve-content-divergent-meta.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-meta.t	Sat Feb 29 15:18:00 2020 +0700
@@ -12,8 +12,11 @@
   > glog = log -GT "{rev}:{node|short} {desc|firstline}\n {phase} {instabilities}\n\n"
   > [phases]
   > publish = False
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > rebase =
+  > strip =
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
 
@@ -157,3 +160,213 @@
   user:        baruser, foouser
 
   $ cd ..
+
+Test the content-divergence resolution involving date update
+------------------------------------------------------------
+
+  $ hg init divergingdate
+  $ cd divergingdate
+  $ unset HGUSER
+  $ echo "[ui]" >> ./.hg/hgrc
+  $ echo "username = test" >> ./.hg/hgrc
+
+  $ echo hi > r0
+  $ hg ci -qAm 'add r0'
+  $ echo hi > foo.txt
+  $ hg ci -qAm 'add foo.txt'
+  $ hg metaedit -r . -d '0 2'
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+date: updated on both side to the same value
+
+  $ echo hi > bar.txt
+  $ hg add -q bar.txt
+  $ hg amend -q
+  $ hg metaedit -r 1 -d '0 1' --hidden
+  2 new content-divergent changesets
+  $ hg log -G
+  *  changeset:   4:c17bf400a278
+  |  tag:         tip
+  |  parent:      0:a24ed8ad918c
+  |  user:        test
+  |  date:        Wed Dec 31 23:59:59 1969 -0000
+  |  instability: content-divergent
+  |  summary:     add foo.txt
+  |
+  | @  changeset:   3:a25dd7af6cf6
+  |/   parent:      0:a24ed8ad918c
+  |    user:        test
+  |    date:        Wed Dec 31 23:59:58 1969 -0000
+  |    instability: content-divergent
+  |    summary:     add foo.txt
+  |
+  o  changeset:   0:a24ed8ad918c
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add r0
+  
+  $ hg evolve --list --rev .
+  a25dd7af6cf6: add foo.txt
+    content-divergent: c17bf400a278 (draft) (precursor cc71ffbc7c00)
+  
+  $ hg log --hidden -r cc71ffbc7c00 -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  1 cc71ffbc7c00 1970-01-01 00:00 +0000: date-changed using metaedit as 4:c17bf400a278; date-changed using metaedit as 2:0065551bd38f
+  $ hg log -r 'desc("add foo.txt")' -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  3 a25dd7af6cf6 1969-12-31 23:59 -0000: 
+  4 c17bf400a278 1969-12-31 23:59 -0000: 
+  $ hg evolve --content-divergent
+  merge:[3] add foo.txt
+  with: [4] add foo.txt
+  base: [1] add foo.txt
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 6c144bb30333
+  $ hg log -r 'desc("add foo.txt")' -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  5 6c144bb30333 1969-12-31 23:59 -0000: 
+
+date: updated one one side to an older value
+
+  $ hg strip .
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/divergingdate/.hg/strip-backup/6c144bb30333-72e26b88-backup.hg
+  2 new content-divergent changesets
+  $ hg up tip
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg amend --date "0 3"
+  $ hg log -G
+  @  changeset:   5:6189a9adfff0
+  |  tag:         tip
+  |  parent:      0:a24ed8ad918c
+  |  user:        test
+  |  date:        Wed Dec 31 23:59:57 1969 -0000
+  |  instability: content-divergent
+  |  summary:     add foo.txt
+  |
+  | *  changeset:   3:a25dd7af6cf6
+  |/   parent:      0:a24ed8ad918c
+  |    user:        test
+  |    date:        Wed Dec 31 23:59:58 1969 -0000
+  |    instability: content-divergent
+  |    summary:     add foo.txt
+  |
+  o  changeset:   0:a24ed8ad918c
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add r0
+  
+  $ hg evolve --list -r .
+  6189a9adfff0: add foo.txt
+    content-divergent: a25dd7af6cf6 (draft) (precursor cc71ffbc7c00)
+  
+  $ hg log -r cc71ffbc7c00+6189a9adfff0+a25dd7af6cf6 --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  1 cc71ffbc7c00 1970-01-01 00:00 +0000: date-changed using metaedit as 4:c17bf400a278; date-changed using metaedit as 2:0065551bd38f
+  5 6189a9adfff0 1969-12-31 23:59 -0000: 
+  3 a25dd7af6cf6 1969-12-31 23:59 -0000: 
+  $ hg evolve --content-divergent
+  merge:[3] add foo.txt
+  with: [5] add foo.txt
+  base: [1] add foo.txt
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 806d0024c04d
+  $ hg log -r . --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  6 806d0024c04d 1969-12-31 23:59 -0000: 
+
+date: updated one side to an newer value
+
+  $ hg strip .
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/divergingdate/.hg/strip-backup/806d0024c04d-24cb28ad-backup.hg
+  2 new content-divergent changesets
+  $ hg update a25dd7af6cf6 --hidden
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg amend --date "120 0"
+  $ hg log -G
+  @  changeset:   6:5199d0bc13d4
+  |  tag:         tip
+  |  parent:      0:a24ed8ad918c
+  |  user:        test
+  |  date:        Thu Jan 01 00:02:00 1970 +0000
+  |  instability: content-divergent
+  |  summary:     add foo.txt
+  |
+  | *  changeset:   5:6189a9adfff0
+  |/   parent:      0:a24ed8ad918c
+  |    user:        test
+  |    date:        Wed Dec 31 23:59:57 1969 -0000
+  |    instability: content-divergent
+  |    summary:     add foo.txt
+  |
+  o  changeset:   0:a24ed8ad918c
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add r0
+  
+  $ hg evolve --list -r .
+  5199d0bc13d4: add foo.txt
+    content-divergent: 6189a9adfff0 (draft) (precursor cc71ffbc7c00)
+  
+  $ hg up 6189a9adfff0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -r cc71ffbc7c00+6189a9adfff0+5199d0bc13d4 --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  1 cc71ffbc7c00 1970-01-01 00:00 +0000: date-changed using metaedit as 4:c17bf400a278; date-changed using metaedit as 2:0065551bd38f
+  5 6189a9adfff0 1969-12-31 23:59 -0000: 
+  6 5199d0bc13d4 1970-01-01 00:02 +0000: 
+  $ hg evolve --content-divergent
+  merge:[5] add foo.txt
+  with: [6] add foo.txt
+  base: [1] add foo.txt
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 51e08ac59670
+  $ hg log -r . --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  7 51e08ac59670 1970-01-01 00:02 +0000: 
+
+date: updated each side to a different value, newer should win
+
+  $ hg strip .
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/divergingdate/.hg/strip-backup/51e08ac59670-d8a3c2ca-backup.hg
+  2 new content-divergent changesets
+  $ hg up tip
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg amend --date "235 0"
+  $ hg update 6189a9adfff0 --hidden
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg amend --date "784 0"
+  $ hg log -G
+  @  changeset:   8:75254fb3164b
+  |  tag:         tip
+  |  parent:      0:a24ed8ad918c
+  |  user:        test
+  |  date:        Thu Jan 01 00:13:04 1970 +0000
+  |  instability: content-divergent
+  |  summary:     add foo.txt
+  |
+  | *  changeset:   7:5421a7efcc6e
+  |/   parent:      0:a24ed8ad918c
+  |    user:        test
+  |    date:        Thu Jan 01 00:03:55 1970 +0000
+  |    instability: content-divergent
+  |    summary:     add foo.txt
+  |
+  o  changeset:   0:a24ed8ad918c
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add r0
+  
+  $ hg evolve --list -r .
+  75254fb3164b: add foo.txt
+    content-divergent: 5421a7efcc6e (draft) (precursor cc71ffbc7c00)
+  
+  $ hg log -r 6189a9adfff0+5421a7efcc6e+75254fb3164b --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  5 6189a9adfff0 1969-12-31 23:59 -0000: date-changed using amend as 8:75254fb3164b
+  7 5421a7efcc6e 1970-01-01 00:03 +0000: 
+  8 75254fb3164b 1970-01-01 00:13 +0000: 
+  $ hg evolve --content-divergent
+  merge:[7] add foo.txt
+  with: [8] add foo.txt
+  base: [1] add foo.txt
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at ab7c0a425dc9
+  $ hg log -r . --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
+  9 ab7c0a425dc9 1970-01-01 00:13 +0000: 
+
+  $ cd ..
--- a/tests/test-evolve-content-divergent-relocation.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-relocation.t	Sat Feb 29 15:18:00 2020 +0700
@@ -288,16 +288,16 @@
   | |    rewritten(branch, content) as 7bbcf24ddecf using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   | x  b4575ed6fcfc (19) added y
-  | |    rewritten(content) as 7bbcf24ddecf using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    amended(content) as 7bbcf24ddecf using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  2a9f6ccbdeba (17) added y
-  | |    rewritten(parent) as 48f745db3f53 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    rebased(parent) as 48f745db3f53 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   | x  96b677f01b81 (18) added y
-  |/     rewritten(content) as b4575ed6fcfc using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |/     amended(content) as b4575ed6fcfc using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  fc6ad2bac162 (16) added y
-       rewritten(content) as 2a9f6ccbdeba using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 2a9f6ccbdeba using amend by test (Thu Jan 01 00:00:00 1970 +0000)
        rewritten(branch, parent) as 96b677f01b81 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
 
--- a/tests/test-evolve-content-divergent-stack.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-content-divergent-stack.t	Sat Feb 29 15:18:00 2020 +0700
@@ -10,6 +10,7 @@
   > [phases]
   > publish = False
   > [extensions]
+  > strip =
   > rebase =
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
@@ -348,6 +349,386 @@
   |   () [default] draft
   o  0:8fa14d15e168 added hgignore
       () [default] draft
+
+when "divergent" and "other" both hit merge conflict in relocating
+------------------------------------------------------------------
+
+  $ hg strip 14: --hidden
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/stackrepo1/.hg/strip-backup/74fbf3e6a0b6-f3612603-backup.hg
+  8 new content-divergent changesets
+
+Prepare repo to have merge conflicts
+  $ hg up -r "max(desc('added a'))"
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg evolve -r . --content-divergent
+  merge:[10] added a
+  with: [5] watbar to a
+  base: [1] added a
+  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  6 new orphan changesets
+  working directory is now at 74fbf3e6a0b6
+  $ echo b_conflict > b
+  $ hg amend -A
+  adding b
+
+Let's try to evolve stack
+  $ hg evolve --content-divergent
+  merge:[11] added b
+  with: [6] added b
+  base: [2] added b
+  rebasing "divergent" content-divergent changeset 6eb54b5af3fb on 119989a4317e
+  merging b
+  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+
+  $ echo b > b
+  $ hg res -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+  $ hg evolve --continue
+  evolving 11:6eb54b5af3fb "added b"
+  rebasing "other" content-divergent changeset d5f148423c16 on 119989a4317e
+  merging b
+  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+
+  $ echo b > b
+  $ hg res -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+  $ hg evolve --continue
+  evolving 6:d5f148423c16 "added b"
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[12] added c
+  with: [7] added c
+  base: [3] added c
+  rebasing "divergent" content-divergent changeset 8ed612937375 on 646bd3372ee7
+  rebasing "other" content-divergent changeset 3ce4be6d8e5e on 646bd3372ee7
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[13] added d
+  with: [8] added d
+  base: [4] added d
+  rebasing "divergent" content-divergent changeset d45f050514c2 on 67abc597e636
+  rebasing "other" content-divergent changeset c72d2885eb51 on 67abc597e636
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 119989a4317e
+  $ hg glog
+  o  25:5e2572194f59 added d
+  |   () [default] draft
+  o  22:67abc597e636 added c
+  |   () [default] draft
+  o  19:646bd3372ee7 added b
+  |   () [default] draft
+  @  16:119989a4317e watbar to a
+  |   () [default] draft
+  o  9:2228e3b74514 add newfile
+  |   () [default] draft
+  o  0:8fa14d15e168 added hgignore
+      () [default] draft
+
+when relocating "other" hit merge conflict but not "divergent"
+--------------------------------------------------------------
+  $ hg strip 14: --hidden
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/stackrepo1/.hg/strip-backup/74fbf3e6a0b6-15474722-backup.hg
+  8 new content-divergent changesets
+
+Insert conflicting changes in between the stack of content-div csets
+  $ hg up -r "max(desc('added b'))"
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo b_diverging_local > b
+  $ hg amend
+  2 new orphan changesets
+  $ hg evolve
+  move:[12] added c
+  atop:[14] added b
+  move:[13] added d
+  $ hg up -r d5f148423c16
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo b_diverging_other > b
+  $ hg amend
+  2 new orphan changesets
+  $ hg evolve
+  move:[7] added c
+  atop:[17] added b
+  move:[8] added d
+
+  $ hg log -r tip
+  changeset:   19:c351be27f199
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  instability: content-divergent
+  summary:     added d
+  
+Now let's try to evolve stack
+  $ hg evolve --content-divergent
+  merge:[10] added a
+  with: [5] watbar to a
+  base: [1] added a
+  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[14] added b
+  with: [17] added b
+  base: [2] added b
+  rebasing "divergent" content-divergent changeset 2a955e808c53 on 74fbf3e6a0b6
+  rebasing "other" content-divergent changeset 509103439e5e on 74fbf3e6a0b6
+  merging b
+  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  4 new orphan changesets
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+
+As now we have interrupted evolution of stack of content-divergent cset (when
+relocation of "divergent" also included) let's test --abort and --stop 
+test --abort:
+  $ hg evolve --abort
+  2 new content-divergent changesets
+  evolve aborted
+  working directory is now at 509103439e5e
+
+confirm that tip is same as it was before we started --content-div resolution
+  $ hg log -r tip
+  changeset:   19:c351be27f199
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  instability: content-divergent
+  summary:     added d
+  
+test --stop:
+  $ hg log -G
+  *  changeset:   19:c351be27f199
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  instability: content-divergent
+  |  summary:     added d
+  |
+  *  changeset:   18:eaf34afe4df3
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  instability: content-divergent
+  |  summary:     added c
+  |
+  @  changeset:   17:509103439e5e
+  |  parent:      5:8e222f257bbf
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  instability: content-divergent
+  |  summary:     added b
+  |
+  | *  changeset:   16:91c8ccb9c241
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: content-divergent
+  | |  summary:     added d
+  | |
+  | *  changeset:   15:48b0f803817a
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: content-divergent
+  | |  summary:     added c
+  | |
+  | *  changeset:   14:2a955e808c53
+  | |  parent:      10:c04ff147ef79
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: content-divergent
+  | |  summary:     added b
+  | |
+  | *  changeset:   10:c04ff147ef79
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: content-divergent
+  | |  summary:     added a
+  | |
+  | o  changeset:   9:2228e3b74514
+  | |  parent:      0:8fa14d15e168
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     add newfile
+  | |
+  * |  changeset:   5:8e222f257bbf
+  |/   parent:      0:8fa14d15e168
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    instability: content-divergent
+  |    summary:     watbar to a
+  |
+  o  changeset:   0:8fa14d15e168
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     added hgignore
+  
+  $ hg evolve --content-divergent
+  merge:[10] added a
+  with: [5] watbar to a
+  base: [1] added a
+  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[14] added b
+  with: [17] added b
+  base: [2] added b
+  rebasing "divergent" content-divergent changeset 2a955e808c53 on 74fbf3e6a0b6
+  rebasing "other" content-divergent changeset 509103439e5e on 74fbf3e6a0b6
+  merging b
+  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  4 new orphan changesets
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+
+  $ hg evolve --stop
+  2 new orphan changesets
+  stopped the interrupted evolve
+  working directory is now at 2a955e808c53
+  $ hg log -G
+  o  changeset:   21:74fbf3e6a0b6
+  |  tag:         tip
+  |  parent:      9:2228e3b74514
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     watbar to a
+  |
+  | *  changeset:   19:c351be27f199
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: orphan, content-divergent
+  | |  summary:     added d
+  | |
+  | *  changeset:   18:eaf34afe4df3
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: orphan, content-divergent
+  | |  summary:     added c
+  | |
+  | *  changeset:   17:509103439e5e
+  | |  parent:      5:8e222f257bbf
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  instability: orphan, content-divergent
+  | |  summary:     added b
+  | |
+  | | *  changeset:   16:91c8ccb9c241
+  | | |  user:        test
+  | | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | | |  instability: orphan, content-divergent
+  | | |  summary:     added d
+  | | |
+  | | *  changeset:   15:48b0f803817a
+  | | |  user:        test
+  | | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | | |  instability: orphan, content-divergent
+  | | |  summary:     added c
+  | | |
+  | | @  changeset:   14:2a955e808c53
+  | | |  parent:      10:c04ff147ef79
+  | | |  user:        test
+  | | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | | |  instability: orphan, content-divergent
+  | | |  summary:     added b
+  | | |
+  +---x  changeset:   10:c04ff147ef79
+  | |    user:        test
+  | |    date:        Thu Jan 01 00:00:00 1970 +0000
+  | |    obsolete:    rewritten using evolve as 21:74fbf3e6a0b6
+  | |    summary:     added a
+  | |
+  o |  changeset:   9:2228e3b74514
+  | |  parent:      0:8fa14d15e168
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     add newfile
+  | |
+  | x  changeset:   5:8e222f257bbf
+  |/   parent:      0:8fa14d15e168
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    obsolete:    rebased using evolve as 21:74fbf3e6a0b6
+  |    summary:     watbar to a
+  |
+  o  changeset:   0:8fa14d15e168
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     added hgignore
+  
+  $ hg obslog -r 'desc("watbar to a")' --all
+  o    74fbf3e6a0b6 (21) watbar to a
+  |\
+  x |  186bdc2cdfa2 (20) watbar to a
+  | |    rewritten as 74fbf3e6a0b6 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |
+  | x  c04ff147ef79 (10) added a
+  | |    rewritten(description, content) as 74fbf3e6a0b6 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |
+  x |  8e222f257bbf (5) watbar to a
+  |/     rebased(parent) as 186bdc2cdfa2 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  x  c7586e2a9264 (1) added a
+       rewritten(description, content) as 8e222f257bbf using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as c04ff147ef79 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+  $ hg obslog -r 'desc("added b")' --all
+  @  2a955e808c53 (14) added b
+  |
+  | *  509103439e5e (17) added b
+  | |
+  x |  6eb54b5af3fb (11) added b
+  | |    amended(content) as 2a955e808c53 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |
+  | x  d5f148423c16 (6) added b
+  |/     amended(content) as 509103439e5e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |
+  x  b1661037fa25 (2) added b
+       rebased(parent) as 6eb54b5af3fb using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as d5f148423c16 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  
+
+Again, let's evolve the stack
+  $ hg evolve --content-divergent
+  merge:[14] added b
+  with: [17] added b
+  base: [2] added b
+  rebasing "divergent" content-divergent changeset 2a955e808c53 on 74fbf3e6a0b6
+  rebasing "other" content-divergent changeset 509103439e5e on 74fbf3e6a0b6
+  merging b
+  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+
+  $ echo foo > b
+  $ hg res -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+  $ hg evolve --continue
+  merge:[15] added c
+  with: [18] added c
+  base: [3] added c
+  rebasing "divergent" content-divergent changeset 48b0f803817a on 4e29776e83a5
+  rebasing "other" content-divergent changeset eaf34afe4df3 on 4e29776e83a5
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[16] added d
+  with: [19] added d
+  base: [4] added d
+  rebasing "divergent" content-divergent changeset 91c8ccb9c241 on 77126af93a25
+  rebasing "other" content-divergent changeset c351be27f199 on 77126af93a25
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 4e29776e83a5
+
+  $ hg evolve -l
+
   $ cd ..
 
 Make sure that content-divergent resolution doesn't undo a change (issue6203)
--- a/tests/test-evolve-continue.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-continue.t	Sat Feb 29 15:18:00 2020 +0700
@@ -434,3 +434,31 @@
   M c
   A d
   ? c.orig
+
+  $ cd ..
+  $ hg init transitive-renames
+  $ cd transitive-renames
+  $ echo 1 > a
+  $ echo 1 > b
+  $ hg ci -Aqm initial
+  $ echo 2 > a
+  $ hg mv b c
+  $ hg ci -m 'rename b to c'
+  $ echo 3 > a
+  $ hg mv c d
+  $ hg ci -m 'rename c to d'
+  $ hg prev -q
+  $ echo 2b > a
+  $ hg amend -q
+  1 new orphan changesets
+  $ hg ev -q
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+  $ hg st -C
+  M a
+  A d
+    c
+  R c
+  ? a.orig
--- a/tests/test-evolve-cycles.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-cycles.t	Sat Feb 29 15:18:00 2020 +0700
@@ -264,7 +264,7 @@
   | |      rewritten(description, parent, content) as c473644ee0e9 using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  a8df460dbbfe (3) C
-  | |    rewritten(description, parent, content) as 2a34000d3544, 868d2e0eb19c using prune by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    split(description, parent, content) as 2a34000d3544, 868d2e0eb19c using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  c473644ee0e9 (2) B
   | |    rewritten(description, parent, content) as a8df460dbbfe using prune by test (Thu Jan 01 00:00:00 1970 +0000)
@@ -284,7 +284,7 @@
   | |      rewritten(description, parent, content) as c473644ee0e9 using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  a8df460dbbfe (3) C
-  | |    rewritten(description, parent, content) as 2a34000d3544, 868d2e0eb19c using prune by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    split(description, parent, content) as 2a34000d3544, 868d2e0eb19c using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  c473644ee0e9 (2) B
   | |    rewritten(description, parent, content) as a8df460dbbfe using prune by test (Thu Jan 01 00:00:00 1970 +0000)
@@ -300,21 +300,20 @@
                       *, (glob)
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "prune",
                   "succnodes": [
-                      "0da815c333f6"
+                      "0da815c333f6364b46c86b0a897c00eb617397b6"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "868d2e0eb19c",
-          "rev": 4,
+          "node": "868d2e0eb19c2b55a2894d37e1c435c221384d48",
           "shortdescription": "D"
       },
       {
@@ -324,21 +323,20 @@
                       *, (glob)
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "prune",
                   "succnodes": [
-                      "868d2e0eb19c"
+                      "868d2e0eb19c2b55a2894d37e1c435c221384d48"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "d9f908fde1a1",
-          "rev": 6,
+          "node": "d9f908fde1a10ad198a462a3ec8b440bb397fc9c",
           "shortdescription": "F"
       },
       {
@@ -348,21 +346,20 @@
                       *, (glob)
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "prune",
                   "succnodes": [
-                      "d9f908fde1a1"
+                      "d9f908fde1a10ad198a462a3ec8b440bb397fc9c"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "0da815c333f6",
-          "rev": 5,
+          "node": "0da815c333f6364b46c86b0a897c00eb617397b6",
           "shortdescription": "E"
       },
       {
@@ -372,22 +369,21 @@
                       *, (glob)
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "prune",
                   "succnodes": [
-                      "2a34000d3544",
-                      "868d2e0eb19c"
+                      "2a34000d35446022104f7a091c06fe21ff2b5912",
+                      "868d2e0eb19c2b55a2894d37e1c435c221384d48"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "split"
               }
           ],
-          "node": "a8df460dbbfe",
-          "rev": 3,
+          "node": "a8df460dbbfe9ef0c1e5ab4fff02e9514672e379",
           "shortdescription": "C"
       },
       {
@@ -397,21 +393,20 @@
                       *, (glob)
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "prune",
                   "succnodes": [
-                      "a8df460dbbfe"
+                      "a8df460dbbfe9ef0c1e5ab4fff02e9514672e379"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "c473644ee0e9",
-          "rev": 2,
+          "node": "c473644ee0e988d7f537e31423831bbc409f12f7",
           "shortdescription": "B"
       },
       {
@@ -421,21 +416,20 @@
                       *, (glob)
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "prune",
                   "succnodes": [
-                      "c473644ee0e9"
+                      "c473644ee0e988d7f537e31423831bbc409f12f7"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "2a34000d3544",
-          "rev": 1,
+          "node": "2a34000d35446022104f7a091c06fe21ff2b5912",
           "shortdescription": "A"
       }
   ]
--- a/tests/test-evolve-effectflags.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-effectflags.t	Sat Feb 29 15:18:00 2020 +0700
@@ -34,7 +34,7 @@
   @  fdf9bde5129a (2) A1
   |
   x  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "desc(A0)"
   changeset:   1:471f378eab4c
@@ -58,7 +58,7 @@
   @  5485c92d3433 (4) B0
   |
   x  ef4a313b1e0a (3) B0
-       rewritten(user) as 5485c92d3433 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reauthored(user) as 5485c92d3433 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "ef4a313b1e0a"
   changeset:   3:ef4a313b1e0a
@@ -82,7 +82,7 @@
   @  4dd84345082e (6) B1
   |
   x  2ef0680ff450 (5) B1
-       rewritten(date) as 4dd84345082e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       date-changed(date) as 4dd84345082e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "2ef0680ff450"
   changeset:   5:2ef0680ff450
@@ -109,7 +109,7 @@
   @  14a01456e057 (8) B2
   |
   x  bd3db8264cee (7) B2
-       rewritten(branch) as 14a01456e057 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       branch-changed(branch) as 14a01456e057 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "bd3db8264cee"
   changeset:   7:bd3db8264cee
@@ -138,7 +138,7 @@
   @  da86aa2f19a3 (11) D0
   |
   x  c85eff83a034 (10) D0
-       rewritten(parent) as da86aa2f19a3 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as da86aa2f19a3 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "c85eff83a034"
   changeset:   10:c85eff83a034
@@ -163,7 +163,7 @@
   @  75781fdbdbf5 (13) E0
   |
   x  ebfe0333e0d9 (12) E0
-       rewritten(content) as 75781fdbdbf5 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 75781fdbdbf5 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "ebfe0333e0d9"
   changeset:   12:ebfe0333e0d9
@@ -244,7 +244,7 @@
   o  e509e2eb3df5 (19) H1
   |
   x  b57fed8d8322 (17) H1
-       rewritten(parent) as e509e2eb3df5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as e509e2eb3df5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "b57fed8d8322"
   changeset:   17:b57fed8d8322
@@ -269,7 +269,7 @@
   @  12c6238b5e37 (22) I0
   |
   x  2f599e54c1c6 (21) I0
-       rewritten(meta) as 12c6238b5e37 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       meta-changed(meta) as 12c6238b5e37 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log --hidden -r "2f599e54c1c6"
   changeset:   21:2f599e54c1c6
--- a/tests/test-evolve-issue5958.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-issue5958.t	Sat Feb 29 15:18:00 2020 +0700
@@ -22,8 +22,6 @@
 (Make changes in unrelated files so that we don't have any merge conflicts
 during the rebase, but the two touched revisions aren't identical)
 
-date: updated on both side to the same value
-
   $ echo hi > bar.txt
   $ hg add -q bar.txt
   $ hg amend -q
@@ -72,24 +70,15 @@
   | |    rewritten(parent, content) as 08bc7ba82799 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  a25dd7af6cf6 (3) add foo.txt
-  | |    rewritten(content) as 1d1fc409af98 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    amended(content) as 1d1fc409af98 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  0065551bd38f (2) add foo.txt
-  |/     rewritten(content) as a25dd7af6cf6 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |/     amended(content) as a25dd7af6cf6 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  cc71ffbc7c00 (1) add foo.txt
-       rewritten(date) as 0065551bd38f using metaedit by test (Thu Jan 01 00:00:00 1970 +0000)
-       rewritten(date) as c17bf400a278 using metaedit by test (Thu Jan 01 00:00:00 1970 +0000)
+       date-changed(date) as 0065551bd38f using metaedit by test (Thu Jan 01 00:00:00 1970 +0000)
+       date-changed(date) as c17bf400a278 using metaedit by test (Thu Jan 01 00:00:00 1970 +0000)
   
-  $ hg evolve --list --rev .
-  08bc7ba82799: add foo.txt
-    content-divergent: c17bf400a278 (draft) (precursor cc71ffbc7c00)
-  
-  $ hg log --hidden -r cc71ffbc7c00 -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  1 cc71ffbc7c00 1970-01-01 00:00 +0000: date-changed using metaedit as 4:c17bf400a278; date-changed using metaedit as 2:0065551bd38f
-  $ hg log -r 'desc("add foo.txt")' -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  4 c17bf400a278 1969-12-31 23:59 -0000: 
-  6 08bc7ba82799 1969-12-31 23:59 -0000: 
   $ hg evolve --content-divergent
   merge:[6] add foo.txt
   with: [4] add foo.txt
@@ -97,160 +86,3 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   1 new orphan changesets
   working directory is now at 459c64f7eaad
-  $ hg log -r 'desc("add foo.txt")' -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  4 c17bf400a278 1969-12-31 23:59 -0000: rewritten using evolve as 7:459c64f7eaad
-  7 459c64f7eaad 1969-12-31 23:59 -0000: 
-
-date: updated one one side to an older value
-
-  $ hg evolve -r .
-  move:[7] add foo.txt
-  atop:[0] add r0
-  working directory is now at 545776b4e79f
-  $ hg update --hidden --rev 'predecessors(.)'
-  updating to a hidden changeset 459c64f7eaad
-  (hidden revision '459c64f7eaad' was rewritten as: 545776b4e79f)
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (459c64f7eaad)
-  (use 'hg evolve' to update to its successor: 545776b4e79f)
-  $ hg amend --date "0 3"
-  1 new orphan changesets
-  2 new content-divergent changesets
-  $ hg rebase -r . -d 0
-  rebasing 9:c117f15338e6 "add foo.txt" (tip)
-  $ hg log -G
-  @  changeset:   10:7a09c7a39546
-  |  tag:         tip
-  |  parent:      0:a24ed8ad918c
-  |  user:        test
-  |  date:        Wed Dec 31 23:59:57 1969 -0000
-  |  instability: content-divergent
-  |  summary:     add foo.txt
-  |
-  | *  changeset:   8:545776b4e79f
-  |/   parent:      0:a24ed8ad918c
-  |    user:        test
-  |    date:        Wed Dec 31 23:59:58 1969 -0000
-  |    instability: content-divergent
-  |    summary:     add foo.txt
-  |
-  o  changeset:   0:a24ed8ad918c
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add r0
-  
-  $ hg evolve --list -r .
-  7a09c7a39546: add foo.txt
-    content-divergent: 545776b4e79f (draft) (precursor 459c64f7eaad)
-  
-  $ hg log -r 459c64f7eaad+7a09c7a39546+545776b4e79f --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  7 459c64f7eaad 1969-12-31 23:59 -0000: date-changed using amend as 9:c117f15338e6; rebased using evolve as 8:545776b4e79f
-  10 7a09c7a39546 1969-12-31 23:59 -0000: 
-  8 545776b4e79f 1969-12-31 23:59 -0000: 
-  $ hg evolve --content-divergent
-  merge:[8] add foo.txt
-  with: [10] add foo.txt
-  base: [7] add foo.txt
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at 39c4200c0d94
-  $ hg log -r . --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  11 39c4200c0d94 1969-12-31 23:59 -0000: 
-
-date: updated one side to an newer value
-
-  $ hg update --hidden --rev 'predecessors(.)'
-  updating to a hidden changeset 7a09c7a39546
-  (hidden revision '7a09c7a39546' was rewritten as: 39c4200c0d94)
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (7a09c7a39546)
-  (use 'hg evolve' to update to its successor: 39c4200c0d94)
-  $ hg amend --date "120 0"
-  2 new content-divergent changesets
-  $ hg log -G
-  @  changeset:   12:da3be3d72fe2
-  |  tag:         tip
-  |  parent:      0:a24ed8ad918c
-  |  user:        test
-  |  date:        Thu Jan 01 00:02:00 1970 +0000
-  |  instability: content-divergent
-  |  summary:     add foo.txt
-  |
-  | *  changeset:   11:39c4200c0d94
-  |/   parent:      0:a24ed8ad918c
-  |    user:        test
-  |    date:        Wed Dec 31 23:59:57 1969 -0000
-  |    instability: content-divergent
-  |    summary:     add foo.txt
-  |
-  o  changeset:   0:a24ed8ad918c
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add r0
-  
-  $ hg evolve --list -r .
-  da3be3d72fe2: add foo.txt
-    content-divergent: 39c4200c0d94 (draft) (precursor 7a09c7a39546)
-  
-  $ hg up 39c4200c0d94
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log -r 7a09c7a39546+39c4200c0d94+da3be3d72fe2 --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  10 7a09c7a39546 1969-12-31 23:59 -0000: date-changed using amend as 12:da3be3d72fe2; rewritten using evolve as 11:39c4200c0d94
-  11 39c4200c0d94 1969-12-31 23:59 -0000: 
-  12 da3be3d72fe2 1970-01-01 00:02 +0000: 
-  $ hg evolve --content-divergent
-  merge:[11] add foo.txt
-  with: [12] add foo.txt
-  base: [10] add foo.txt
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at 06cde6010a51
-  $ hg log -r . --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  13 06cde6010a51 1970-01-01 00:02 +0000: 
-
-date: updated each side to a different value, newer should win
-
-  $ hg amend --date "235 0"
-  $ hg update --hidden --rev 'predecessors(.)'
-  updating to a hidden changeset 06cde6010a51
-  (hidden revision '06cde6010a51' was rewritten as: a7412ff9bfb3)
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory parent is obsolete! (06cde6010a51)
-  (use 'hg evolve' to update to its successor: a7412ff9bfb3)
-  $ hg amend --date "784 0"
-  2 new content-divergent changesets
-  $ hg log -G
-  @  changeset:   15:e3077936ec52
-  |  tag:         tip
-  |  parent:      0:a24ed8ad918c
-  |  user:        test
-  |  date:        Thu Jan 01 00:13:04 1970 +0000
-  |  instability: content-divergent
-  |  summary:     add foo.txt
-  |
-  | *  changeset:   14:a7412ff9bfb3
-  |/   parent:      0:a24ed8ad918c
-  |    user:        test
-  |    date:        Thu Jan 01 00:03:55 1970 +0000
-  |    instability: content-divergent
-  |    summary:     add foo.txt
-  |
-  o  changeset:   0:a24ed8ad918c
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add r0
-  
-  $ hg evolve --list -r .
-  e3077936ec52: add foo.txt
-    content-divergent: a7412ff9bfb3 (draft) (precursor 06cde6010a51)
-  
-  $ hg log -r 39c4200c0d94+a7412ff9bfb3+e3077936ec52 --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  11 39c4200c0d94 1969-12-31 23:59 -0000: date-changed using evolve as 13:06cde6010a51
-  14 a7412ff9bfb3 1970-01-01 00:03 +0000: 
-  15 e3077936ec52 1970-01-01 00:13 +0000: 
-  $ hg evolve --content-divergent
-  merge:[14] add foo.txt
-  with: [15] add foo.txt
-  base: [13] add foo.txt
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at 1a39f3901288
-  $ hg log -r . --hidden -T '{rev} {node|short} {date|isodate}: {join(obsfate, "; ")}\n'
-  16 1a39f3901288 1970-01-01 00:13 +0000: 
--- a/tests/test-evolve-issue6097.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-issue6097.t	Sat Feb 29 15:18:00 2020 +0700
@@ -89,7 +89,7 @@
   @  bb847d1d3a5f (4) apricot and coconut
   |
   x  2868fe6df617 (3) apricot and coconut
-  |    rewritten(parent) as bb847d1d3a5f using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    rebased(parent) as bb847d1d3a5f using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  32acf8fb1b23 (2) apricot and coconut
   |    rewritten(parent, content) as 2868fe6df617 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
--- a/tests/test-evolve-obshistory-amend-then-fold.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-amend-then-fold.t	Sat Feb 29 15:18:00 2020 +0700
@@ -105,7 +105,7 @@
   |      (No patch available, changesets rebased)
   |
   x  0dec01379d3b (2) B0
-       rewritten(description) as b7ea6d14e664 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as b7ea6d14e664 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 0dec01379d3b -r b7ea6d14e664 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -139,7 +139,7 @@
   |      (No patch available, changesets rebased)
   |
   x  0dec01379d3b (2) B0
-       rewritten(description) as b7ea6d14e664 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as b7ea6d14e664 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 0dec01379d3b -r b7ea6d14e664 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -152,8 +152,7 @@
   [
       {
           "markers": [],
-          "node": "eb5a0daa2192",
-          "rev": 4,
+          "node": "eb5a0daa21923bbf8caeb2c42085b9e463861fd0",
           "shortdescription": "C0"
       },
       {
@@ -161,23 +160,22 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       *, (glob)
                       *, (glob)
                       "content"
                   ],
                   "operation": "fold",
                   "succnodes": [
-                      "eb5a0daa2192"
+                      "eb5a0daa21923bbf8caeb2c42085b9e463861fd0"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "b7ea6d14e664",
-          "rev": 3,
+          "node": "b7ea6d14e664bdc8922221f7992631b50da3fb07",
           "shortdescription": "B1"
       },
       {
@@ -185,21 +183,20 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "b7ea6d14e664"
+                      "b7ea6d14e664bdc8922221f7992631b50da3fb07"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               }
           ],
-          "node": "0dec01379d3b",
-          "rev": 2,
+          "node": "0dec01379d3be6318c470ead31b1fe7ae7cb53d5",
           "shortdescription": "B0"
       },
       {
@@ -207,22 +204,21 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "content"
                   ],
                   "operation": "fold",
                   "succnodes": [
-                      "eb5a0daa2192"
+                      "eb5a0daa21923bbf8caeb2c42085b9e463861fd0"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
--- a/tests/test-evolve-obshistory-amend.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-amend.t	Sat Feb 29 15:18:00 2020 +0700
@@ -92,8 +92,9 @@
 
   $ hg obslog --no-graph --patch 4ae3a4151de9
   4ae3a4151de9 (2) A1
+  
   471f378eab4c (1) A0
-    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)    
       diff -r 471f378eab4c -r 4ae3a4151de9 changeset-description
       --- a/changeset-description
       +++ b/changeset-description
@@ -102,15 +103,39 @@
       +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
-      
+  
+  
 
+Test that content diff works with templating
+  $ hg obslog --color=debug --patch 4ae3a4151de9 \
+  > -T '{node|short} {desc|firstline}\n{markers % "patch:\n```{patch}```\n"}'
+  @  4ae3a4151de9 A1
+  |
+  x  471f378eab4c A0
+     patch:
+     ```
+     [diff.diffline|diff -r 471f378eab4c -r 4ae3a4151de9 A0]
+     [diff.file_a|--- a/A0	Thu Jan 01 00:00:00 1970 +0000]
+     [diff.file_b|+++ b/A0	Thu Jan 01 00:00:00 1970 +0000]
+     [diff.hunk|@@ -1,1 +1,2 @@]
+      A0
+     [diff.inserted|+42]
+     ```
+
+  $ hg obslog 4ae3a4151de9 --graph -T'{label("log.summary", desc|firstline)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}'
+  @  A1
+  |
+  x  A0 at 0 0 by test
+  
+
+Check that the same thing works with the old {shortdescription} form
   $ hg obslog 4ae3a4151de9 --graph -T'{label("log.summary", shortdescription)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}'
   @  A1
   |
@@ -120,8 +145,7 @@
   [
       {
           "markers": [],
-          "node": "4ae3a4151de9",
-          "rev": 2,
+          "node": "4ae3a4151de9aa872113f0b196e28323308981e8",
           "shortdescription": "A1"
       },
       {
@@ -129,22 +153,21 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "content"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "4ae3a4151de9"
+                      "4ae3a4151de9aa872113f0b196e28323308981e8"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
@@ -175,22 +198,21 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       *, (glob)
                       "content"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "4ae3a4151de9"
+                      "4ae3a4151de9aa872113f0b196e28323308981e8"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
@@ -220,15 +242,19 @@
   
   $ hg obslog -R $TESTTMP/server --no-graph --patch 4ae3a4151de9
   4ae3a4151de9 (1) A1
+  
   471f378eab4c
-    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, context is not local)
+  
 
   $ hg obslog -R $TESTTMP/server --no-graph -f --patch 4ae3a4151de9
   4ae3a4151de9 (1) A1
+  
   471f378eab4c
-    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, context is not local)
+  
 
 Amend two more times
 ====================
@@ -303,7 +329,7 @@
   @  92210308515b (4) A3
   |
   x  4f1685185907 (3) A2
-  |    rewritten(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    reworded(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |      diff -r 4f1685185907 -r 92210308515b changeset-description
   |      --- a/changeset-description
   |      +++ b/changeset-description
@@ -316,7 +342,7 @@
   |
   |
   x  4ae3a4151de9 (2) A1
-  |    rewritten(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    reworded(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |      diff -r 4ae3a4151de9 -r 4f1685185907 changeset-description
   |      --- a/changeset-description
   |      +++ b/changeset-description
@@ -347,6 +373,50 @@
          +42
   
   
+Test that description diff works with templating
+  $ hg obslog --color=debug --patch 92210308515b \
+  > -T '{node|short} {desc|firstline}\n{markers % "description diff:\n```{descdiff}```\n"}'
+  @  92210308515b A3
+  |
+  x  4f1685185907 A2
+  |  description diff:
+  |  ```
+  |  [diff.diffline|diff -r 4f1685185907 -r 92210308515b changeset-description]
+  |  [diff.file_a|--- a/changeset-description]
+  |  [diff.file_b|+++ b/changeset-description]
+  |  [diff.hunk|@@ -1,3 +1,3 @@]
+  |  [diff.deleted|-A2]
+  |  [diff.inserted|+A3]
+  |
+  |  [diff.deleted|-Better better commit message]
+  |  [diff.inserted|+Better better better commit message]
+  |  ```
+  x  4ae3a4151de9 A1
+  |  description diff:
+  |  ```
+  |  [diff.diffline|diff -r 4ae3a4151de9 -r 4f1685185907 changeset-description]
+  |  [diff.file_a|--- a/changeset-description]
+  |  [diff.file_b|+++ b/changeset-description]
+  |  [diff.hunk|@@ -1,3 +1,3 @@]
+  |  [diff.deleted|-A1]
+  |  [diff.inserted|+A2]
+  |
+  |  [diff.deleted|-Better commit message]
+  |  [diff.inserted|+Better better commit message]
+  |  ```
+  x  471f378eab4c A0
+     description diff:
+     ```
+     [diff.diffline|diff -r 471f378eab4c -r 4ae3a4151de9 changeset-description]
+     [diff.file_a|--- a/changeset-description]
+     [diff.file_b|+++ b/changeset-description]
+     [diff.hunk|@@ -1,1 +1,3 @@]
+     [diff.deleted|-A0]
+     [diff.inserted|+A1]
+     [diff.inserted|+]
+     [diff.inserted|+Better commit message]
+     ```
+
 Check the output on the server
 ------------------------------
 
@@ -354,11 +424,11 @@
   o  92210308515b (2) A3
   |
   x  4f1685185907
-  |    rewritten(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    reworded(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |      (No patch available, context is not local)
   |
   x  4ae3a4151de9 (1) A1
-  |    rewritten(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    reworded(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |      (No patch available, successor is unknown locally)
   |
   x  471f378eab4c
@@ -383,24 +453,32 @@
   
   $ hg obslog -R $TESTTMP/server --no-graph --patch 92210308515b
   92210308515b (2) A3
+  
   4f1685185907
-    rewritten(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    reworded(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, context is not local)
+  
   4ae3a4151de9 (1) A1
-    rewritten(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    reworded(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, successor is unknown locally)
+  
   471f378eab4c
-    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, context is not local)
+  
 
   $ hg obslog -R $TESTTMP/server --no-graph -f --patch 92210308515b
   92210308515b (2) A3
+  
   4f1685185907
-    rewritten(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    reworded(description) as 92210308515b using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, context is not local)
+  
   4ae3a4151de9 (1) A1
-    rewritten(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    reworded(description) as 4f1685185907 using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, successor is unknown locally)
+  
   471f378eab4c
-    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+    rewritten(description, content) as 4ae3a4151de9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)        
       (No patch available, context is not local)
+  
--- a/tests/test-evolve-obshistory-complex.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-complex.t	Sat Feb 29 15:18:00 2020 +0700
@@ -387,7 +387,7 @@
   *    7b3290f6e0a0 (12) fold1
   |\
   x |    d15d0ffc75f6 (8) fold1
-  |\ \     rewritten(parent, content) as 7b3290f6e0a0, d0f33db50670 using split by test (Thu Jan 01 00:00:00 1970 +0000)
+  |\ \     split(parent, content) as 7b3290f6e0a0, d0f33db50670 using split by test (Thu Jan 01 00:00:00 1970 +0000)
   | | |
   | | x  e036916b63ea (11) fold0
   | | |    rewritten(description, parent, content) as 7b3290f6e0a0 using prune by test (Thu Jan 01 00:00:00 1970 +0000)
@@ -399,7 +399,7 @@
    /     rewritten(description, content) as d15d0ffc75f6 using fold by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x    b868bc49b0a4 (7) fold0
-  |\     rewritten(parent, content) as 19e14c8397fc, e036916b63ea using split by test (Thu Jan 01 00:00:00 1970 +0000)
+  |\     split(parent, content) as 19e14c8397fc, e036916b63ea using split by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  2a34000d3544 (1) A
    /     rewritten(description, content) as b868bc49b0a4 using fold by test (Thu Jan 01 00:00:00 1970 +0000)
@@ -419,7 +419,7 @@
   | | | | *  ec31316faa9d (14) fold2
   | | | |/|
   | | | x |    100cc25b765f (9) fold2
-  | | | |\ \     rewritten(parent, content) as d4a000f63ee9, ec31316faa9d using split by test (Thu Jan 01 00:00:00 1970 +0000)
+  | | | |\ \     split(parent, content) as d4a000f63ee9, ec31316faa9d using split by test (Thu Jan 01 00:00:00 1970 +0000)
   | | | | | |
   | +-------x  d0f33db50670 (13) fold1
   | | | | |      rewritten(description, parent, content) as ec31316faa9d using prune by test (Thu Jan 01 00:00:00 1970 +0000)
@@ -433,10 +433,10 @@
   | | |        note: folding changesets to test
   | | |
   x | |    b868bc49b0a4 (7) fold0
-  |\ \ \     rewritten(parent, content) as 19e14c8397fc, e036916b63ea using split by test (Thu Jan 01 00:00:00 1970 +0000)
+  |\ \ \     split(parent, content) as 19e14c8397fc, e036916b63ea using split by test (Thu Jan 01 00:00:00 1970 +0000)
   | | | |
   | | x |    d15d0ffc75f6 (8) fold1
-  | | |\ \     rewritten(parent, content) as 7b3290f6e0a0, d0f33db50670 using split by test (Thu Jan 01 00:00:00 1970 +0000)
+  | | |\ \     split(parent, content) as 7b3290f6e0a0, d0f33db50670 using split by test (Thu Jan 01 00:00:00 1970 +0000)
   | | | | |
   | | | | x  d9f908fde1a1 (6) F
   | | | |      rewritten(description, parent, content) as 100cc25b765f using fold by test (Thu Jan 01 00:00:00 1970 +0000)
--- a/tests/test-evolve-obshistory-content-divergent.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-content-divergent.t	Sat Feb 29 15:18:00 2020 +0700
@@ -5,6 +5,10 @@
 ============
 
   $ . $TESTDIR/testlib/obshistory_setup.sh
+  $ cat >> $HGRCPATH << EOF
+  > [experimental]
+  > evolution.allowdivergence = True
+  > EOF
 
 Test output with content-divergence
 ===================================
@@ -78,7 +82,7 @@
 Check that debugobshistory on the divergent revision show both destinations
   $ hg obslog --hidden 471f378eab4c --patch
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -86,7 +90,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -103,7 +107,7 @@
   | *  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -111,7 +115,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -127,36 +131,35 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "65b757b745b9"
+                      "65b757b745b935093c87a2bccd877521cccffcbd"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               },
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "fdf9bde5129a"
+                      "fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
@@ -166,7 +169,7 @@
   *  fdf9bde5129a (2) A1
   |
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -174,7 +177,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -191,7 +194,7 @@
   | *  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -199,7 +202,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -214,7 +217,7 @@
   @  65b757b745b9 (3) A2
   |
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -222,7 +225,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -238,7 +241,7 @@
   | *  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -246,7 +249,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -263,7 +266,7 @@
   | *  fdf9bde5129a (2) A1
   |/
   x  471f378eab4c (1) A0
-       rewritten(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r 65b757b745b9 changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -271,7 +274,7 @@
          -A0
          +A2
   
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -284,8 +287,7 @@
   [
       {
           "markers": [],
-          "node": "65b757b745b9",
-          "rev": 3,
+          "node": "65b757b745b935093c87a2bccd877521cccffcbd",
           "shortdescription": "A2"
       },
       {
@@ -293,42 +295,40 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "65b757b745b9"
+                      "65b757b745b935093c87a2bccd877521cccffcbd"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               },
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "fdf9bde5129a"
+                      "fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       },
       {
           "markers": [],
-          "node": "fdf9bde5129a",
-          "rev": 2,
+          "node": "fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e",
           "shortdescription": "A1"
       }
   ]
--- a/tests/test-evolve-obshistory-fold.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-fold.t	Sat Feb 29 15:18:00 2020 +0700
@@ -172,8 +172,7 @@
   [
       {
           "markers": [],
-          "node": "eb5a0daa2192",
-          "rev": 3,
+          "node": "eb5a0daa21923bbf8caeb2c42085b9e463861fd0",
           "shortdescription": "C0"
       },
       {
@@ -181,22 +180,21 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "content"
                   ],
                   "operation": "fold",
                   "succnodes": [
-                      "eb5a0daa2192"
+                      "eb5a0daa21923bbf8caeb2c42085b9e463861fd0"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       },
       {
@@ -204,23 +202,22 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description",
                       "parent",
                       "content"
                   ],
                   "operation": "fold",
                   "succnodes": [
-                      "eb5a0daa2192"
+                      "eb5a0daa21923bbf8caeb2c42085b9e463861fd0"
                   ],
                   "user": "test",
                   "verb": "rewritten"
               }
           ],
-          "node": "0dec01379d3b",
-          "rev": 2,
+          "node": "0dec01379d3be6318c470ead31b1fe7ae7cb53d5",
           "shortdescription": "B0"
       }
   ]
--- a/tests/test-evolve-obshistory-lots-of-splits.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-lots-of-splits.t	Sat Feb 29 15:18:00 2020 +0700
@@ -162,7 +162,7 @@
 
   $ hg obslog de7290d8b885 --hidden --patch
   x  de7290d8b885 (1) A0
-       rewritten(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, too many successors (4))
   
   $ hg obslog de7290d8b885 --hidden --all --patch
@@ -175,7 +175,7 @@
   | o  f257fde29c7a (3) A0
   |/
   x  de7290d8b885 (1) A0
-       rewritten(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, too many successors (4))
   
   $ hg obslog de7290d8b885 --hidden --no-graph -Tjson | python -m json.tool
@@ -185,25 +185,24 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "parent",
                       "content"
                   ],
                   "operation": "split",
                   "succnodes": [
-                      "1ae8bc733a14",
-                      "337fec4d2edc",
-                      "c7f044602e9b",
-                      "f257fde29c7a"
+                      "1ae8bc733a14e374f11767d2ad128d4c891dc43f",
+                      "337fec4d2edcf0e7a467e35f818234bc620068b5",
+                      "c7f044602e9bd5dec6528b33114df3d0221e6359",
+                      "f257fde29c7a847c9b607f6e958656d0df0fb15c"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "split"
               }
           ],
-          "node": "de7290d8b885",
-          "rev": 1,
+          "node": "de7290d8b885925115bb9e88887252dfc20ef2a8",
           "shortdescription": "A0"
       }
   ]
@@ -211,15 +210,14 @@
   @  c7f044602e9b (5) A0
   |
   x  de7290d8b885 (1) A0
-       rewritten(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, too many successors (4))
   
   $ hg obslog c7f044602e9b --no-graph -Tjson | python -m json.tool
   [
       {
           "markers": [],
-          "node": "c7f044602e9b",
-          "rev": 5,
+          "node": "c7f044602e9bd5dec6528b33114df3d0221e6359",
           "shortdescription": "A0"
       },
       {
@@ -227,25 +225,24 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "parent",
                       "content"
                   ],
                   "operation": "split",
                   "succnodes": [
-                      "1ae8bc733a14",
-                      "337fec4d2edc",
-                      "c7f044602e9b",
-                      "f257fde29c7a"
+                      "1ae8bc733a14e374f11767d2ad128d4c891dc43f",
+                      "337fec4d2edcf0e7a467e35f818234bc620068b5",
+                      "c7f044602e9bd5dec6528b33114df3d0221e6359",
+                      "f257fde29c7a847c9b607f6e958656d0df0fb15c"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "split"
               }
           ],
-          "node": "de7290d8b885",
-          "rev": 1,
+          "node": "de7290d8b885925115bb9e88887252dfc20ef2a8",
           "shortdescription": "A0"
       }
   ]
@@ -260,7 +257,7 @@
   | o  f257fde29c7a (3) A0
   |/
   x  de7290d8b885 (1) A0
-       rewritten(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, too many successors (4))
   
   $ hg obslog 5 --all --patch
@@ -273,7 +270,7 @@
   | o  f257fde29c7a (3) A0
   |/
   x  de7290d8b885 (1) A0
-       rewritten(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 1ae8bc733a14, 337fec4d2edc, c7f044602e9b, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, too many successors (4))
   
   $ hg update de7290d8b885
--- a/tests/test-evolve-obshistory-phase-divergent.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-phase-divergent.t	Sat Feb 29 15:18:00 2020 +0700
@@ -69,7 +69,7 @@
 Check that debugobshistory on the divergent revision show both destinations
   $ hg obslog --hidden 471f378eab4c --patch
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -84,7 +84,7 @@
   *  fdf9bde5129a (2) A1
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -100,21 +100,20 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "fdf9bde5129a"
+                      "fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
@@ -124,7 +123,7 @@
   *  fdf9bde5129a (2) A1
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -139,7 +138,7 @@
   *  fdf9bde5129a (2) A1
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -154,7 +153,7 @@
   *  fdf9bde5129a (2) A1
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -168,7 +167,7 @@
   *  fdf9bde5129a (2) A1
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -183,7 +182,7 @@
   *  fdf9bde5129a (2) A1
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -196,30 +195,28 @@
   [
       {
           "markers": [],
-          "node": "fdf9bde5129a",
-          "rev": 2,
+          "node": "fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e",
           "shortdescription": "A1"
       },
       {
           "markers": [
               {
                   "date": [
-                      0,
+                      0.0,
                       0
                   ],
-                  "effect": [
+                  "effects": [
                       "description"
                   ],
                   "operation": "amend",
                   "succnodes": [
-                      "fdf9bde5129a"
+                      "fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "reworded"
               }
           ],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
--- a/tests/test-evolve-obshistory-prune.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-prune.t	Sat Feb 29 15:18:00 2020 +0700
@@ -75,15 +75,14 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
                   "operation": "prune",
                   "user": "test",
                   "verb": "pruned"
               }
           ],
-          "node": "0dec01379d3b",
-          "rev": 2,
+          "node": "0dec01379d3be6318c470ead31b1fe7ae7cb53d5",
           "shortdescription": "B0"
       }
   ]
@@ -94,8 +93,7 @@
   [
       {
           "markers": [],
-          "node": "471f378eab4c",
-          "rev": 1,
+          "node": "471f378eab4c5e25f6c77f785b27c936efb22874",
           "shortdescription": "A0"
       }
   ]
--- a/tests/test-evolve-obshistory-split.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory-split.t	Sat Feb 29 15:18:00 2020 +0700
@@ -101,7 +101,7 @@
 Check that debugobshistory on split commit show both targets
   $ hg obslog 471597cad322 --hidden --patch
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -112,24 +112,23 @@
               {
                   "date": [
                       *, (glob)
-                      0 (glob)
+                      0
                   ],
-                  "effect": [
+                  "effects": [
                       "parent",
                       "content"
                   ],
                   "note": "testing split",
                   "operation": "split",
                   "succnodes": [
-                      "337fec4d2edc",
-                      "f257fde29c7a"
+                      "337fec4d2edcf0e7a467e35f818234bc620068b5",
+                      "f257fde29c7a847c9b607f6e958656d0df0fb15c"
                   ],
                   "user": "test",
-                  "verb": "rewritten"
+                  "verb": "split"
               }
           ],
-          "node": "471597cad322",
-          "rev": 1,
+          "node": "471597cad322d1f659bb169751be9133dad92ef3",
           "shortdescription": "A0"
       }
   ]
@@ -139,7 +138,7 @@
   o  337fec4d2edc (2) A0
   |
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -150,7 +149,7 @@
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -160,7 +159,7 @@
   @  f257fde29c7a (3) A0
   |
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -171,7 +170,7 @@
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -182,7 +181,7 @@
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -194,7 +193,7 @@
   | @  f257fde29c7a (3) A0
   |/
   x  471597cad322 (1) A0
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, too many successors (2))
   
@@ -216,7 +215,7 @@
   o  f257fde29c7a (2) A0
   |
   x  471597cad322
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, context is not local)
   
@@ -229,7 +228,7 @@
   | o  f257fde29c7a (2) A0
   |/
   x  471597cad322
-       rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
          note: testing split
          (No patch available, context is not local)
   
@@ -240,14 +239,18 @@
   
   $ hg obslog -R $TESTTMP/server --no-graph -f --all --patch tip
   f257fde29c7a (2) A0
+  
   471597cad322
-    rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
-      note: testing split
+    split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+      note: testing split        
       (No patch available, context is not local)
+  
 
   $ hg obslog -R $TESTTMP/server --no-graph -f --all --patch tip
   f257fde29c7a (2) A0
+  
   471597cad322
-    rewritten(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
-      note: testing split
+    split(parent, content) as 337fec4d2edc, f257fde29c7a using split by test (Thu Jan 01 00:00:00 1970 +0000)
+      note: testing split        
       (No patch available, context is not local)
+  
--- a/tests/test-evolve-obshistory.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-obshistory.t	Sat Feb 29 15:18:00 2020 +0700
@@ -97,7 +97,7 @@
   @  7a230b46bf61 (3) A2
   |
   x  fdf9bde5129a (2) A1
-  |    rewritten(description) as 7a230b46bf61 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    reworded(description) as 7a230b46bf61 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |      diff -r fdf9bde5129a -r 7a230b46bf61 changeset-description
   |      --- a/changeset-description
   |      +++ b/changeset-description
@@ -107,7 +107,7 @@
   |
   |
   x  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 471f378eab4c -r fdf9bde5129a changeset-description
          --- a/changeset-description
          +++ b/changeset-description
@@ -136,11 +136,11 @@
   o  7a230b46bf61 (2) A2
   |
   x  fdf9bde5129a
-  |    rewritten(description) as 7a230b46bf61 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    reworded(description) as 7a230b46bf61 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |      (No patch available, context is not local)
   |
   @  471f378eab4c (1) A0
-       rewritten(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, successor is unknown locally)
   
 
@@ -161,10 +161,19 @@
   o  [evolve.node|7a230b46bf61] [evolve.rev|(2)] [evolve.short_description|A2]
   |
   x  [evolve.node evolve.missing_change_ctx|fdf9bde5129a]
-  |    [evolve.verb|rewritten](description) as [evolve.node|7a230b46bf61] using [evolve.operation|amend] by [evolve.user|test] [evolve.date|(Thu Jan 01 00:00:00 1970 +0000)]
+  |    [evolve.verb|reworded](description) as [evolve.node|7a230b46bf61] using [evolve.operation|amend] by [evolve.user|test] [evolve.date|(Thu Jan 01 00:00:00 1970 +0000)]
   |      (No patch available, context is not local)
   |
   @  [evolve.node|471f378eab4c] [evolve.rev|(1)] [evolve.short_description|A0]
-       [evolve.verb|rewritten](description) as [evolve.node|fdf9bde5129a] using [evolve.operation|amend] by [evolve.user|test] [evolve.date|(Thu Jan 01 00:00:00 1970 +0000)]
+       [evolve.verb|reworded](description) as [evolve.node|fdf9bde5129a] using [evolve.operation|amend] by [evolve.user|test] [evolve.date|(Thu Jan 01 00:00:00 1970 +0000)]
          (No patch available, successor is unknown locally)
   
+
+  $ hg obslog 7a230b46bf61 --graph \
+  > -T '{node|short} {rev} {desc|firstline}\n{markers % "rewritten using {operation}"}\n'
+  o  7a230b46bf61 2 A2
+  |
+  x  fdf9bde5129a
+  |  rewritten using amend
+  @  471f378eab4c 1 A0
+     rewritten using amend
--- a/tests/test-evolve-order.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-order.t	Sat Feb 29 15:18:00 2020 +0700
@@ -15,6 +15,8 @@
   > unified = 0
   > [ui]
   > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
--- a/tests/test-evolve-orphan-merge.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-orphan-merge.t	Sat Feb 29 15:18:00 2020 +0700
@@ -134,11 +134,18 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     merging a and b
   
+
+Clean up to prepare for next test case
+  $ hg prune -r 64370c9805e7 -r 3d41537b44ca -r 968d205ba4d8
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  working directory is now at 8fa14d15e168
+  3 changesets pruned
+
 2) When merging both the parents resulted in conflicts
 ------------------------------------------------------
 
   $ hg up 8fa14d15e168
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo foo > c
   $ hg ci -Aqm "foo to c"
   $ hg prev
@@ -152,24 +159,6 @@
   |   () draft
   | o  8:1c165c673853 foo to c
   |/    () draft
-  | o    7:968d205ba4d8 merging a and b
-  | |\    () draft
-  +---o  6:3d41537b44ca added a
-  | |     () draft
-  | o  4:64370c9805e7 added b
-  |/    () draft
-  o  0:8fa14d15e168 added hgignore
-      () draft
-
-Prune old test changesets to have clear graph view
-  $ hg prune -r 64370c9805e7 -r 3d41537b44ca -r 968d205ba4d8
-  3 changesets pruned
-
-  $ hg glog
-  @  9:d0f84b25d4e3 bar to c
-  |   () draft
-  | o  8:1c165c673853 foo to c
-  |/    () draft
   o  0:8fa14d15e168 added hgignore
       () draft
 
@@ -529,17 +518,13 @@
 case where merge commit becomes orphan with its ancestors pruned up until a
 point where the other parent of merge is the first non-pruned ancestor.
 
-Note: allowing an empty commit here to have the same output on all hg versions.
-In newer versions or Mercurial this command would not create a new commit.
-hg <= 5.2 (32d11a23c9cf)
-
-  $ hg evolve -r . --config ui.allowemptycommit=true
+  $ hg evolve -r .
   move:[28] merged l and x
   atop:[0] added hgignore
-  working directory is now at * (glob)
+  working directory is now at b61ba77b924a
 
   $ hg glog
-  @  29:* merged l and x (glob)
+  @  29:b61ba77b924a merged l and x
   |   () draft
   o  0:8fa14d15e168 added hgignore
       () draft
--- a/tests/test-evolve-phase-divergence.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-phase-divergence.t	Sat Feb 29 15:18:00 2020 +0700
@@ -161,7 +161,7 @@
   @  98bb3a6cfe1a (2) tweak a
   |
   o  4d1169d82e47 (1) modify a
-       rewritten(description) as 98bb3a6cfe1a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reworded(description) as 98bb3a6cfe1a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
 Solving the phase divergence using evolve command
 --------------------------------------------------
@@ -581,7 +581,7 @@
   @  334e300d6db5 (10) added x to x
   |
   o  2352021b3785 (9) added x to x
-       rewritten(parent) as 334e300d6db5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as 334e300d6db5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg glog -r .^::
   @  10:334e300d6db5 added x to x
@@ -629,10 +629,10 @@
   |    pruned using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  334e300d6db5 (10) added x to x
-  |    rewritten(parent) as b1a0e143e32b using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    rebased(parent) as b1a0e143e32b using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   @  2352021b3785 (9) added x to x
-       rewritten(parent) as 334e300d6db5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as 334e300d6db5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
 
   $ hg exp
@@ -712,10 +712,10 @@
   @  13015a180eee (14) y to y and foobar to foo
   |
   x  211ab84d1689 (13) y to y and foobar to foo
-  |    rewritten(content) as 13015a180eee using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    amended(content) as 13015a180eee using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   o  dc88f5aa9bc9 (12) y to y and foobar to foo
-       rewritten(parent) as 211ab84d1689 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as 211ab84d1689 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
 Resolving divergence using `hg evolve`
 -------------------------------------
@@ -784,10 +784,10 @@
   @  5fd38c0de46e (19) added l to l
   |
   x  2bfd56949cf0 (18) added l to l
-  |    rewritten(content) as 5fd38c0de46e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    amended(content) as 5fd38c0de46e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  f3794e5a91dc (17) added l to l
-       rewritten(parent) as 2bfd56949cf0 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as 2bfd56949cf0 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
 
   $ hg phase -r f3794e5a91dc --public --hidden
@@ -1028,10 +1028,10 @@
   | |    rewritten(description, parent, content) as 89ba615ea1ec using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   | x  e1154ec0206a (3) added n
-  |/     rewritten(parent) as ee4af146c5cf using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |/     rebased(parent) as ee4af146c5cf using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   o  a51bce62c219 (1) added m and n
-       rewritten(description, parent, content) as 4f25cd9cd2bf, e1154ec0206a using prune by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(description, parent, content) as 4f25cd9cd2bf, e1154ec0206a using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   
 
 XXX: not sure this is the correct
@@ -1158,7 +1158,7 @@
   |/     rewritten(description, parent, content) as 26f564f94bcc using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   o  90859808ece6 (1) added p and q
-       rewritten(description, content) as a47263294745, bb87595f9a77 using prune by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(description, content) as a47263294745, bb87595f9a77 using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   
 
 XXX: not sure this is correct
@@ -1289,13 +1289,13 @@
   | |    rewritten(description, parent, content) as 88b0dae5369a using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   | x  52ca78bb98c7 (4) added n
-  | |    rewritten(parent) as 98dad8812511 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    rebased(parent) as 98dad8812511 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   | x  e1154ec0206a (3) added n
-  |/     rewritten(content) as 52ca78bb98c7 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |/     amended(content) as 52ca78bb98c7 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   o  a51bce62c219 (1) added m and n
-       rewritten(description, parent, content) as 4f25cd9cd2bf, e1154ec0206a using prune by test (Thu Jan 01 00:00:00 1970 +0000)
+       split(description, parent, content) as 4f25cd9cd2bf, e1154ec0206a using prune by test (Thu Jan 01 00:00:00 1970 +0000)
   
 
 XXX: not sure this is correct
--- a/tests/test-evolve-public-content-divergent-corner-cases.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-public-content-divergent-corner-cases.t	Sat Feb 29 15:18:00 2020 +0700
@@ -12,6 +12,8 @@
   > glog = log -GT "{rev}:{node|short} {desc|firstline}\n {phase} {instabilities}\n\n"
   > [phases]
   > publish = False
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > rebase =
   > EOF
@@ -370,7 +372,7 @@
   | *  e568fd1029bb (4) added c e
   |/
   x  9150fe93bec6 (3) added d
-       rewritten(content) as 93cd84bbdaca using prune by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 93cd84bbdaca using prune by test (Thu Jan 01 00:00:00 1970 +0000)
          diff -r 9150fe93bec6 -r 93cd84bbdaca d
          --- a/d	Thu Jan 01 00:00:00 1970 +0000
          +++ b/d	Thu Jan 01 00:00:00 1970 +0000
@@ -756,9 +758,9 @@
   | |    rewritten as a52ac76b45f5 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
   x |  8ae8db670b4a (10) c_F
-  |/     rewritten(parent) as 6a87ed4aa317 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  |/     rebased(parent) as 6a87ed4aa317 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  77ccbf8d837e (6) c_F
-       rewritten(parent) as 8ae8db670b4a using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
-       rewritten(parent) as a52ac76b45f5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as 8ae8db670b4a using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+       rebased(parent) as a52ac76b45f5 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   
--- a/tests/test-evolve-public-content-divergent-discard.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-public-content-divergent-discard.t	Sat Feb 29 15:18:00 2020 +0700
@@ -17,6 +17,8 @@
   > glog = log -GT "{rev}:{node|short} {desc|firstline}\n {phase} {instabilities}\n\n"
   > [phases]
   > publish = False
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > rebase =
   > EOF
--- a/tests/test-evolve-public-content-divergent-main.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-public-content-divergent-main.t	Sat Feb 29 15:18:00 2020 +0700
@@ -15,6 +15,8 @@
   > glog = log -GT "{rev}:{node|short} {desc|firstline}\n {phase} {instabilities}\n\n"
   > [phases]
   > publish = False
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > rebase =
   > EOF
--- a/tests/test-evolve-templates.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve-templates.t	Sat Feb 29 15:18:00 2020 +0700
@@ -1,4 +1,4 @@
-This test file test the various templates for precursors and successors.
+This test file test the various templates for predecessors and successors.
 
 Global setup
 ============
@@ -9,12 +9,14 @@
   > interactive = true
   > [phases]
   > publish=False
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > evolve =
   > [alias]
   > tlog = log -G -T '{node|short}\
-  >     {if(precursors, "\n  Precursors: {precursors}")}\
-  >     {if(precursors, "\n  semi-colon: {join(precursors, "; ")}")}\
+  >     {if(predecessors, "\n  Predecessors: {predecessors}")}\
+  >     {if(predecessors, "\n  semi-colon: {join(predecessors, "; ")}")}\
   >     {if(successors, "\n  Successors: {successors}")}\
   >     {if(successors, "\n  semi-colon: {join(successors, "; ")}")}\
   >     {if(obsfate, "\n  Fate: {join(obsfate, "\n  Fate: ")}\n")}\n'
@@ -69,19 +71,19 @@
   working directory parent is obsolete! (471f378eab4c)
   (use 'hg evolve' to update to its successor: d004c8f274b9)
 
-Precursors template should show current revision as it is the working copy
+Predecessors template should show current revision as it is the working copy
   $ hg olog tip
   o  d004c8f274b9 (3) A2
   |
   x  a468dc9b3633 (2) A1
-  |    rewritten(description) as d004c8f274b9 using amend by test2 (Thu Apr 19 04:25:21 2001 +0000)
+  |    reworded(description) as d004c8f274b9 using amend by test2 (Thu Apr 19 04:25:21 2001 +0000)
   |
   @  471f378eab4c (1) A0
        rewritten(description, content) as a468dc9b3633 using amend by test (Fri Feb 13 23:31:30 2009 +0000)
   
   $ hg tlog
   o  d004c8f274b9
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | @  471f378eab4c
   |/     Successors: 3:d004c8f274b9
@@ -139,7 +141,7 @@
 
 (check json)
 
-  $ hg log -GT '{precursors|json}\n'
+  $ hg log -GT '{predecessors|json}\n'
   o  ["471f378eab4c5e25f6c77f785b27c936efb22874"]
   |
   | @  []
@@ -162,10 +164,10 @@
   working directory parent is obsolete! (a468dc9b3633)
   (use 'hg evolve' to update to its successor: d004c8f274b9)
 
-Precursors template should show current revision as it is the working copy
+Predecessors template should show current revision as it is the working copy
   $ hg tlog
   o  d004c8f274b9
-  |    Precursors: 2:a468dc9b3633
+  |    Predecessors: 2:a468dc9b3633
   |    semi-colon: 2:a468dc9b3633
   | @  a468dc9b3633
   |/     Successors: 3:d004c8f274b9
@@ -174,14 +176,14 @@
   |
   o  ea207398892e
   
-Precursors template should show the precursor as we force its display with
+Predecessors template should show the precursor as we force its display with
 --hidden  
   $ hg tlog --hidden
   o  d004c8f274b9
-  |    Precursors: 2:a468dc9b3633
+  |    Predecessors: 2:a468dc9b3633
   |    semi-colon: 2:a468dc9b3633
   | @  a468dc9b3633
-  |/     Precursors: 1:471f378eab4c
+  |/     Predecessors: 1:471f378eab4c
   |      semi-colon: 1:471f378eab4c
   |      Successors: 3:d004c8f274b9
   |      semi-colon: 3:d004c8f274b9
@@ -211,10 +213,10 @@
   
   $ hg tlog --hidden
   @  d004c8f274b9
-  |    Precursors: 2:a468dc9b3633
+  |    Predecessors: 2:a468dc9b3633
   |    semi-colon: 2:a468dc9b3633
   | x  a468dc9b3633
-  |/     Precursors: 1:471f378eab4c
+  |/     Predecessors: 1:471f378eab4c
   |      semi-colon: 1:471f378eab4c
   |      Successors: 3:d004c8f274b9
   |      semi-colon: 3:d004c8f274b9
@@ -338,13 +340,13 @@
   working directory parent is obsolete! (471597cad322)
   (use 'hg evolve' to update to its tipmost successor: 337fec4d2edc, f257fde29c7a)
 
-Precursors template should show current revision as it is the working copy
+Predecessors template should show current revision as it is the working copy
   $ hg tlog
   o  f257fde29c7a
-  |    Precursors: 1:471597cad322
+  |    Predecessors: 1:471597cad322
   |    semi-colon: 1:471597cad322
   o  337fec4d2edc
-  |    Precursors: 1:471597cad322
+  |    Predecessors: 1:471597cad322
   |    semi-colon: 1:471597cad322
   | @  471597cad322
   |/     Successors: 2:337fec4d2edc 3:f257fde29c7a
@@ -367,7 +369,7 @@
   $ hg up f257fde29c7a
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-Precursors template should not show a precursor as it's not displayed in the
+Predecessors template should not show a precursor as it's not displayed in the
 log
   $ hg tlog
   @  f257fde29c7a
@@ -376,14 +378,14 @@
   |
   o  ea207398892e
   
-Precursors template should show the precursor as we force its display with
+Predecessors template should show the precursor as we force its display with
 --hidden
   $ hg tlog --hidden
   @  f257fde29c7a
-  |    Precursors: 1:471597cad322
+  |    Predecessors: 1:471597cad322
   |    semi-colon: 1:471597cad322
   o  337fec4d2edc
-  |    Precursors: 1:471597cad322
+  |    Predecessors: 1:471597cad322
   |    semi-colon: 1:471597cad322
   | x  471597cad322
   |/     Successors: 2:337fec4d2edc 3:f257fde29c7a
@@ -469,10 +471,10 @@
   working directory parent is obsolete! (471f378eab4c)
   (use 'hg evolve' to update to its successor: eb5a0daa2192)
 
-Precursors template should show current revision as it is the working copy
+Predecessors template should show current revision as it is the working copy
   $ hg tlog
   o  eb5a0daa2192
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | @  471f378eab4c
   |/     Successors: 3:eb5a0daa2192
@@ -496,11 +498,11 @@
   working directory parent is obsolete! (0dec01379d3b)
   (use 'hg evolve' to update to its successor: eb5a0daa2192)
 
-Precursors template should show both precursors as they should be both
+Predecessors template should show both predecessors as they should be both
 displayed
   $ hg tlog
   o  eb5a0daa2192
-  |    Precursors: 2:0dec01379d3b 1:471f378eab4c
+  |    Predecessors: 2:0dec01379d3b 1:471f378eab4c
   |    semi-colon: 2:0dec01379d3b; 1:471f378eab4c
   | @  0dec01379d3b
   | |    Successors: 3:eb5a0daa2192
@@ -529,18 +531,18 @@
   $ hg up 'desc(C0)'
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-Precursors template should not show precursors as it's not displayed in the
+Predecessors template should not show predecessors as it's not displayed in the
 log
   $ hg tlog
   @  eb5a0daa2192
   |
   o  ea207398892e
   
-Precursors template should show both precursors as we force its display with
+Predecessors template should show both predecessors as we force its display with
 --hidden
   $ hg tlog --hidden
   @  eb5a0daa2192
-  |    Precursors: 2:0dec01379d3b 1:471f378eab4c
+  |    Predecessors: 2:0dec01379d3b 1:471f378eab4c
   |    semi-colon: 2:0dec01379d3b; 1:471f378eab4c
   | x  0dec01379d3b
   | |    Successors: 3:eb5a0daa2192
@@ -644,13 +646,13 @@
   working directory parent is obsolete! (471f378eab4c)
   (471f378eab4c has diverged, use 'hg evolve --list --content-divergent' to resolve the issue)
 
-Precursors template should show current revision as it is the working copy
+Predecessors template should show current revision as it is the working copy
   $ hg tlog
   *  019fadeab383
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | *  fdf9bde5129a
-  |/     Precursors: 1:471f378eab4c
+  |/     Predecessors: 1:471f378eab4c
   |      semi-colon: 1:471f378eab4c
   | @  471f378eab4c
   |/     Successors: 2:fdf9bde5129a; 4:019fadeab383
@@ -673,7 +675,7 @@
 
   $ hg up 'desc(A1)'
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-Precursors template should not show precursors as it's not displayed in the
+Predecessors template should not show predecessors as it's not displayed in the
 log
   $ hg tlog
   *  019fadeab383
@@ -690,20 +692,20 @@
   |/
   o  ea207398892e
   
-Precursors template should a precursor as we force its display with --hidden
+Predecessors template should a precursor as we force its display with --hidden
   $ hg tlog --hidden
   *  019fadeab383
-  |    Precursors: 3:65b757b745b9
+  |    Predecessors: 3:65b757b745b9
   |    semi-colon: 3:65b757b745b9
   | x  65b757b745b9
-  |/     Precursors: 1:471f378eab4c
+  |/     Predecessors: 1:471f378eab4c
   |      semi-colon: 1:471f378eab4c
   |      Successors: 4:019fadeab383
   |      semi-colon: 4:019fadeab383
   |      Fate: reworded using amend as 4:019fadeab383
   |
   | @  fdf9bde5129a
-  |/     Precursors: 1:471f378eab4c
+  |/     Predecessors: 1:471f378eab4c
   |      semi-colon: 1:471f378eab4c
   | x  471f378eab4c
   |/     Successors: 2:fdf9bde5129a; 3:65b757b745b9
@@ -809,7 +811,7 @@
   (use 'hg evolve' to update to its successor: eb5a0daa2192)
   $ hg tlog
   o  eb5a0daa2192
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | @  471f378eab4c
   |/     Successors: 4:eb5a0daa2192
@@ -834,7 +836,7 @@
   (use 'hg evolve' to update to its successor: eb5a0daa2192)
   $ hg tlog
   o  eb5a0daa2192
-  |    Precursors: 2:0dec01379d3b 1:471f378eab4c
+  |    Predecessors: 2:0dec01379d3b 1:471f378eab4c
   |    semi-colon: 2:0dec01379d3b; 1:471f378eab4c
   | @  0dec01379d3b
   | |    Successors: 4:eb5a0daa2192
@@ -868,7 +870,7 @@
   (use 'hg evolve' to update to its successor: eb5a0daa2192)
   $ hg tlog
   o  eb5a0daa2192
-  |    Precursors: 1:471f378eab4c 3:b7ea6d14e664
+  |    Predecessors: 1:471f378eab4c 3:b7ea6d14e664
   |    semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
   | @  b7ea6d14e664
   | |    Successors: 4:eb5a0daa2192
@@ -903,10 +905,10 @@
   
   $ hg tlog --hidden
   @  eb5a0daa2192
-  |    Precursors: 1:471f378eab4c 3:b7ea6d14e664
+  |    Predecessors: 1:471f378eab4c 3:b7ea6d14e664
   |    semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
   | x  b7ea6d14e664
-  | |    Precursors: 2:0dec01379d3b
+  | |    Predecessors: 2:0dec01379d3b
   | |    semi-colon: 2:0dec01379d3b
   | |    Successors: 4:eb5a0daa2192
   | |    semi-colon: 4:eb5a0daa2192
@@ -1032,7 +1034,7 @@
 
   $ hg tlog
   o  7a230b46bf61
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | @  471f378eab4c
   |/     Successors: 2:7a230b46bf61
@@ -1063,7 +1065,7 @@
   
   $ hg tlog --hidden
   @  7a230b46bf61
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | x  471f378eab4c
   |/     Successors: 2:7a230b46bf61
@@ -1127,10 +1129,10 @@
   (use 'hg evolve' to update to its parent successor)
   $ hg tlog
   o  f897c6137566
-  |    Precursors: 2:0dec01379d3b
+  |    Predecessors: 2:0dec01379d3b
   |    semi-colon: 2:0dec01379d3b
   | @  0dec01379d3b
-  | |    Precursors: 1:471f378eab4c
+  | |    Predecessors: 1:471f378eab4c
   | |    semi-colon: 1:471f378eab4c
   | |    Successors: 3:f897c6137566; 1:471f378eab4c
   | |    semi-colon: 3:f897c6137566; 1:471f378eab4c
@@ -1138,7 +1140,7 @@
   | |    Fate: rewritten as 1:471f378eab4c
   | |
   | x  471f378eab4c
-  |/     Precursors: 2:0dec01379d3b
+  |/     Predecessors: 2:0dec01379d3b
   |      semi-colon: 2:0dec01379d3b
   |      Successors: 2:0dec01379d3b
   |      semi-colon: 2:0dec01379d3b
@@ -1163,7 +1165,7 @@
   (use 'hg evolve' to update to its parent successor)
   $ hg tlog
   o  f897c6137566
-  |    Precursors: 1:471f378eab4c
+  |    Predecessors: 1:471f378eab4c
   |    semi-colon: 1:471f378eab4c
   | @  471f378eab4c
   |/     Fate: pruned
@@ -1193,10 +1195,10 @@
   
   $ hg tlog --hidden
   o  f897c6137566
-  |    Precursors: 2:0dec01379d3b
+  |    Predecessors: 2:0dec01379d3b
   |    semi-colon: 2:0dec01379d3b
   | x  0dec01379d3b
-  | |    Precursors: 1:471f378eab4c
+  | |    Predecessors: 1:471f378eab4c
   | |    semi-colon: 1:471f378eab4c
   | |    Successors: 3:f897c6137566; 1:471f378eab4c
   | |    semi-colon: 3:f897c6137566; 1:471f378eab4c
@@ -1204,7 +1206,7 @@
   | |    Fate: rewritten as 1:471f378eab4c
   | |
   | x  471f378eab4c
-  |/     Precursors: 2:0dec01379d3b
+  |/     Predecessors: 2:0dec01379d3b
   |      semi-colon: 2:0dec01379d3b
   |      Successors: 2:0dec01379d3b
   |      semi-colon: 2:0dec01379d3b
@@ -1379,10 +1381,10 @@
 
   $ hg tlog
   @  0b997eb7ceee
-  |    Precursors: 6:4a004186e638
+  |    Predecessors: 6:4a004186e638
   |    semi-colon: 6:4a004186e638
   | *  b18bc8331526
-  |/     Precursors: 6:4a004186e638
+  |/     Predecessors: 6:4a004186e638
   |      semi-colon: 6:4a004186e638
   | *  ba2ed02b0c9a
   | |
@@ -1416,16 +1418,16 @@
   
   $ hg tlog --hidden
   @  0b997eb7ceee
-  |    Precursors: 6:4a004186e638
+  |    Predecessors: 6:4a004186e638
   |    semi-colon: 6:4a004186e638
   | *  b18bc8331526
-  |/     Precursors: 6:4a004186e638
+  |/     Predecessors: 6:4a004186e638
   |      semi-colon: 6:4a004186e638
   | *  ba2ed02b0c9a
-  | |    Precursors: 4:9bd10a0775e4
+  | |    Predecessors: 4:9bd10a0775e4
   | |    semi-colon: 4:9bd10a0775e4
   | x  4a004186e638
-  |/     Precursors: 4:9bd10a0775e4
+  |/     Predecessors: 4:9bd10a0775e4
   |      semi-colon: 4:9bd10a0775e4
   |      Successors: 8:b18bc8331526; 9:0b997eb7ceee
   |      semi-colon: 8:b18bc8331526; 9:0b997eb7ceee
@@ -1433,7 +1435,7 @@
   |      Fate: reworded using amend as 9:0b997eb7ceee
   |
   *  dd800401bd8c
-  |    Precursors: 4:9bd10a0775e4
+  |    Predecessors: 4:9bd10a0775e4
   |    semi-colon: 4:9bd10a0775e4
   | x  9bd10a0775e4
   |/     Successors: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
@@ -1441,10 +1443,10 @@
   |      Fate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
   |
   o  f897c6137566
-  |    Precursors: 2:0dec01379d3b
+  |    Predecessors: 2:0dec01379d3b
   |    semi-colon: 2:0dec01379d3b
   | x  0dec01379d3b
-  | |    Precursors: 1:471f378eab4c
+  | |    Predecessors: 1:471f378eab4c
   | |    semi-colon: 1:471f378eab4c
   | |    Successors: 3:f897c6137566; 1:471f378eab4c
   | |    semi-colon: 3:f897c6137566; 1:471f378eab4c
@@ -1452,7 +1454,7 @@
   | |    Fate: rewritten as 1:471f378eab4c
   | |
   | x  471f378eab4c
-  |/     Precursors: 2:0dec01379d3b
+  |/     Predecessors: 2:0dec01379d3b
   |      semi-colon: 2:0dec01379d3b
   |      Successors: 2:0dec01379d3b
   |      semi-colon: 2:0dec01379d3b
@@ -1495,16 +1497,16 @@
   rebasing 7:ba2ed02b0c9a "Add A,B,C"
   $ hg tlog
   *  eceed8f98ffc
-  |    Precursors: 4:9bd10a0775e4
+  |    Predecessors: 4:9bd10a0775e4
   |    semi-colon: 4:9bd10a0775e4
   | *  0b997eb7ceee
-  | |    Precursors: 4:9bd10a0775e4
+  | |    Predecessors: 4:9bd10a0775e4
   | |    semi-colon: 4:9bd10a0775e4
   * |  b18bc8331526
-  |/     Precursors: 4:9bd10a0775e4
+  |/     Predecessors: 4:9bd10a0775e4
   |      semi-colon: 4:9bd10a0775e4
   *  dd800401bd8c
-  |    Precursors: 4:9bd10a0775e4
+  |    Predecessors: 4:9bd10a0775e4
   |    semi-colon: 4:9bd10a0775e4
   | @  9bd10a0775e4
   |/     Successors: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc; 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
--- a/tests/test-evolve.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-evolve.t	Sat Feb 29 15:18:00 2020 +0700
@@ -790,10 +790,10 @@
           median length:             91
           mean length:               90
       format v0:
-          smallest length:           * (glob)
-          longer length:             * (glob)
-          median length:             * (glob)
-          mean length:               * (glob)
+          smallest length:          * (glob)
+          longer length:            * (glob)
+          median length:            * (glob)
+          mean length:              * (glob)
   disconnected clusters:              1
           any known node:             1
           smallest length:           10
--- a/tests/test-extension-isolation.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-extension-isolation.t	Sat Feb 29 15:18:00 2020 +0700
@@ -131,6 +131,7 @@
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-no-ext | egrep 'topics|evoext'
   [1]
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-topic | egrep 'topics|evoext'
+    _exttopics_heads
     topics
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-no-ext | egrep 'topics|evoext'
   [1]
@@ -143,11 +144,13 @@
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-both | egrep 'topics|evoext'
     _evoext_getbundle_obscommon
     _evoext_obshashrange_v1
+    _exttopics_heads
     topics
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-evo | egrep 'topics|evoext'
     _evoext_getbundle_obscommon
     _evoext_obshashrange_v1
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-topic | egrep 'topics|evoext'
+    _exttopics_heads
     topics
   $ hg debugcapabilities http://$LOCALIP:$HGPORT/repo-evo | egrep 'topics|evoext'
     _evoext_getbundle_obscommon
--- a/tests/test-fold.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-fold.t	Sat Feb 29 15:18:00 2020 +0700
@@ -95,6 +95,10 @@
   $ hg fold --from -r 'desc("r5")'
   3 changesets folded
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg debugobsolete -r 'desc("r5")' --exclusive
+  4de32a90b66cd083ebf3c00b41277aa7abca51dd 198b5c405d01a50c41a81a00fc61677b81981a5f 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '37', 'operation': 'fold', 'user': 'test'}
+  c8d03c1b5e94af74b772900c58259d2e08917735 198b5c405d01a50c41a81a00fc61677b81981a5f 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '33', 'operation': 'fold', 'user': 'test'}
+  f69452c5b1af6cbaaa56ef50cf94fff5bcc6ca23 198b5c405d01a50c41a81a00fc61677b81981a5f 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '37', 'operation': 'fold', 'user': 'test'}
 
 Checking whether the bookmarks are moved or not
 
@@ -405,8 +409,8 @@
 
   $ hg fold --exact -r 'desc("A")::desc("B")' -m 'second fold' \
   >         --config experimental.evolution.allowdivergence=no
-  abort: folding obsolete revisions may cause divergence
-  (set experimental.evolution.allowdivergence=yes to allow folding them)
+  abort: fold of 4b34ecfb0d56 creates content-divergence with fcfd42a7fa46
+  (add --verbose for details or see 'hg help evolution.instability')
   [255]
 
 but if we allow divergence, this should work and should create new content-divergent changesets
--- a/tests/test-metaedit.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-metaedit.t	Sat Feb 29 15:18:00 2020 +0700
@@ -144,8 +144,11 @@
   |        note: folding changesets using metaedit
   |
   x  587528abfffe (7) F
-       rewritten(user) as 212b2a2b87cd using metaedit by test (Thu Jan 01 00:00:00 1970 +0000)
+       reauthored(user) as 212b2a2b87cd using metaedit by test (Thu Jan 01 00:00:00 1970 +0000)
   
+  $ hg debugobsolete --rev . --exclusive
+  212b2a2b87cdbae992f001e9baba64db389fbce7 a08d35fd7d9d0f8cb33d5bd2074e9bafb5cbc70f 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '29', 'note': 'folding changesets using metaedit', 'operation': 'metaedit', 'user': 'test'}
+  c2bd843aa2468b30bb56d69d4f5fef95b85986f2 a08d35fd7d9d0f8cb33d5bd2074e9bafb5cbc70f 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '9', 'note': 'folding changesets using metaedit', 'operation': 'metaedit', 'user': 'test'}
 
 no new commit is created here because the date is the same
   $ HGEDITOR=cat hg metaedit
@@ -205,6 +208,8 @@
   1 changesets folded
   $ hg log -r "tip" --template '{rev}: {author}\n'
   12: foobar3
+  $ hg debugobsolete --rev 'tip' --exclusive
+  f3d001339afd30d27fcd91e713274a78233528c8 07a6525ddaf5de1ab33352806abb5724a0954f3f 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '16', 'operation': 'metaedit', 'user': 'foobar3'}
 
 working on merge commits too
 
--- a/tests/test-obsolete.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-obsolete.t	Sat Feb 29 15:18:00 2020 +0700
@@ -717,7 +717,7 @@
   [2]
   $ hg olog
   @  0d3f46688ccc (3) add obsol_c
-  |    rewritten(parent) as 2033b4e49474 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    rebased(parent) as 2033b4e49474 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   |    rewritten as 725c380fe99b by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  4538525df7e2 (2) add c
--- a/tests/test-pullbundle.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-pullbundle.t	Sat Feb 29 15:18:00 2020 +0700
@@ -1289,3 +1289,14 @@
   02-e469a7aa5cce57653b6b02ff46c80b2d94d62629-0000000912skip-0000000016size.hg
   02-e74670ea99533967c5d90da3ddbc0318cc1fd502-0000001280skip-0000000256size.hg
   02-fb6c210a224903e81e5a8d2ee099cb0c9526ba8c-0000001512skip-0000000004size.hg
+
+  $ hg debugpullbundlecacheoverlap -R server 'all()' | grep -v '^  '
+  gathering 100 sample pulls within 2131 revisions
+  pull size:
+  non-cached changesets:
+  ratio of cached changesets:
+  bundle count:
+  ratio of cached bundles:
+  changesets served:
+  size of cached bundles:
+  hit on cached bundles:
--- a/tests/test-push-checkheads-pruned-B6.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-push-checkheads-pruned-B6.t	Sat Feb 29 15:18:00 2020 +0700
@@ -9,7 +9,7 @@
 This case is part of a series of tests checking this behavior.
 
 Category B: simple case involving pruned changesets
-TestCase 6: single changesets, pruned then superseeded (on a new changeset)
+TestCase 6: single changesets, pruned then superseded (on a new changeset)
 
 .. old-state:
 ..
--- a/tests/test-push-checkheads-pruned-B7.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-push-checkheads-pruned-B7.t	Sat Feb 29 15:18:00 2020 +0700
@@ -9,7 +9,7 @@
 This case is part of a series of tests checking this behavior.
 
 Category B: simple case involving pruned changesets
-TestCase 7: single changesets, pruned then superseeded (on an existing changeset)
+TestCase 7: single changesets, pruned then superseded (on an existing changeset)
 
 .. old-state:
 ..
--- a/tests/test-push-checkheads-unpushed-D6.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-push-checkheads-unpushed-D6.t	Sat Feb 29 15:18:00 2020 +0700
@@ -9,7 +9,7 @@
 This case is part of a series of tests checking this behavior.
 
 Category D: remote head is "obs-affected" locally, but result is not part of the push
-TestCase 6: single changeset, superseeded then pruned (on a new changeset unpushed) changeset
+TestCase 6: single changeset, superseded then pruned (on a new changeset unpushed) changeset
 
 This is a partial push variation of case B-6
 
--- a/tests/test-push-checkheads-unpushed-D7.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-push-checkheads-unpushed-D7.t	Sat Feb 29 15:18:00 2020 +0700
@@ -9,7 +9,7 @@
 This case is part of a series of tests checking this behavior.
 
 Category D: remote head is "obs-affected" locally, but result is not part of the push
-TestCase 7: single changesets, superseeded multiple time then pruned (on a new changeset unpushed) changeset
+TestCase 7: single changesets, superseded multiple time then pruned (on a new changeset unpushed) changeset
 
 This is a partial push variation of B6
 
--- a/tests/test-rewind.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-rewind.t	Sat Feb 29 15:18:00 2020 +0700
@@ -95,7 +95,7 @@
   |
   x  7e594302a05d (2) c_B0
        pruned using prune by test (Thu Jan 01 00:00:00 1970 +0000)
-       rewritten(meta) as 073989a581cf using rewind by test (Thu Jan 01 00:00:00 1970 +0000)
+       meta-changed(meta) as 073989a581cf using rewind by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg log -G
   o  changeset:   3:073989a581cf
@@ -173,8 +173,8 @@
   |/
   x  7e594302a05d (2) c_B0
        pruned using prune by test (Thu Jan 01 00:00:00 1970 +0000)
-       rewritten(meta) as 073989a581cf using rewind by test (Thu Jan 01 00:00:00 1970 +0000)
-       rewritten(meta) as b13b032065ef using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
+       meta-changed(meta) as 073989a581cf using rewind by test (Thu Jan 01 00:00:00 1970 +0000)
+       meta-changed(meta) as b13b032065ef using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
   
   $ hg log -G
   o  changeset:   4:b13b032065ef
@@ -273,7 +273,7 @@
   |
   x  7e594302a05d (2) c_B0
        rewritten(description, content) as 25c8f5ab0c3b using amend by test (Thu Jan 01 00:00:01 1970 +0000)
-       rewritten(meta) as b13b032065ef using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
+       meta-changed(meta) as b13b032065ef using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
   
   $ hg log -G
   *  changeset:   4:b13b032065ef
@@ -380,8 +380,8 @@
   |
   x  7e594302a05d (2) c_B0
        rewritten(description, content) as 25c8f5ab0c3b using amend by test (Thu Jan 01 00:00:01 1970 +0000)
-       rewritten(meta) as 837cd997bb05 using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
-       rewritten(meta) as b13b032065ef using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
+       meta-changed(meta) as 837cd997bb05 using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+       meta-changed(meta) as b13b032065ef using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
   
   $ hg log -G
   @  changeset:   5:837cd997bb05
@@ -517,7 +517,7 @@
   | o  a0316c4c5417 (4) c_CD0
   |/
   x  49fb7d900906 (3) c_CD0
-       rewritten(parent, content) as 9576e80d6851, a0316c4c5417 using split by test (Thu Jan 01 00:00:02 1970 +0000)
+       split(parent, content) as 9576e80d6851, a0316c4c5417 using split by test (Thu Jan 01 00:00:02 1970 +0000)
   
 
 Actual rewind
@@ -544,8 +544,8 @@
   |/     rewritten(meta, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
   |
   x  49fb7d900906 (3) c_CD0
-       rewritten(meta) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
-       rewritten(parent, content) as 9576e80d6851, a0316c4c5417 using split by test (Thu Jan 01 00:00:02 1970 +0000)
+       meta-changed(meta) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+       split(parent, content) as 9576e80d6851, a0316c4c5417 using split by test (Thu Jan 01 00:00:02 1970 +0000)
   
   $ hg log -G
   @  changeset:   6:4535d0af405c
@@ -600,7 +600,7 @@
   +---o  e76375de0bfc (7) c_CD0
   | | |
   x---+  4535d0af405c (6) c_CD0
-  |\| |    rewritten(meta, parent, content) as 95d72d892df7, e76375de0bfc using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
+  |\| |    split(meta, parent, content) as 95d72d892df7, e76375de0bfc using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   | | |
   | x |  9576e80d6851 (5) c_CD0
   |/ /     rewritten(meta, parent, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
@@ -608,11 +608,11 @@
   | |
   | x  a0316c4c5417 (4) c_CD0
   |/     rewritten(meta, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
-  |      rewritten(meta) as e76375de0bfc using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
+  |      meta-changed(meta) as e76375de0bfc using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  49fb7d900906 (3) c_CD0
-       rewritten(meta) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
-       rewritten(parent, content) as 9576e80d6851, a0316c4c5417 using split by test (Thu Jan 01 00:00:02 1970 +0000)
+       meta-changed(meta) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+       split(parent, content) as 9576e80d6851, a0316c4c5417 using split by test (Thu Jan 01 00:00:02 1970 +0000)
   
   $ hg log -G
   @  changeset:   8:95d72d892df7
@@ -679,7 +679,7 @@
   |      rewritten(description, content) as d952d1794ff6 using amend by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  7e594302a05d (2) c_B0
-       rewritten(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
+       rebased(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
   
   $ hg log -G
   @  changeset:   4:a65fceb2324a
@@ -720,8 +720,8 @@
   |/     rewritten(meta, parent) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  7e594302a05d (2) c_B0
-       rewritten(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
-       rewritten(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
+       rebased(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
+       meta-changed(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   
   $ hg log -G
   @  changeset:   5:ac979e0aac4e
@@ -794,12 +794,12 @@
   |/|    rewritten(meta, parent) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
   | |
   x |  a65fceb2324a (4) c_B0
-  |/     rewritten(meta) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
+  |/     meta-changed(meta) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
   |      rewritten(meta, parent) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  7e594302a05d (2) c_B0
-       rewritten(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
-       rewritten(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
+       rebased(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
+       meta-changed(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   
 Automatically rewinding the full stack (with --to)
 --------------------------------------------------
@@ -830,12 +830,12 @@
   | |    rewritten(meta, parent) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
   | |
   x |  a65fceb2324a (4) c_B0
-  |/     rewritten(meta) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
+  |/     meta-changed(meta) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
   |      rewritten(meta, parent) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  7e594302a05d (2) c_B0
-       rewritten(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
-       rewritten(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
+       rebased(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
+       meta-changed(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   
   $ hg log -G
   @  changeset:   8:3f2d8862657d
@@ -894,12 +894,12 @@
   | |    rewritten(meta, parent) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
   | |
   | x  a65fceb2324a (4) c_B0
-  |/     rewritten(meta) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
+  |/     meta-changed(meta) as a5dd64adbb2a using rewind by test (Thu Jan 01 00:00:04 1970 +0000)
   |      rewritten(meta, parent) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  7e594302a05d (2) c_B0
-       rewritten(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
-       rewritten(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
+       rebased(parent) as a65fceb2324a using evolve by test (Thu Jan 01 00:00:03 1970 +0000)
+       meta-changed(meta) as ac979e0aac4e using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   
   $ hg log -G
   @  changeset:   10:d36d6d267714
@@ -1010,7 +1010,7 @@
   |
   x  30704102d912 (1) initial
        rewritten(description, content) as 2ea5be2f8751 using amend by test (Thu Jan 01 00:00:06 1970 +0000)
-       rewritten(meta) as b4c97fddc16a using rewind by test (Thu Jan 01 00:00:06 1970 +0000)
+       meta-changed(meta) as b4c97fddc16a using rewind by test (Thu Jan 01 00:00:06 1970 +0000)
   
   $ hg glf --hidden
   @  3: initial (a b)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-server.t	Sat Feb 29 15:18:00 2020 +0700
@@ -0,0 +1,200 @@
+  $ . "$TESTDIR/testlib/topic_setup.sh"
+
+  $ cat << EOF >> $HGRCPATH
+  > [experimental]
+  > evolution = all
+  > topic.publish-bare-branch = yes
+  > topic.server-gate-topic-changesets = yes
+  > 
+  > [extensions]
+  > evolve =
+  > 
+  > [phases]
+  > publish = no
+  > 
+  > [ui]
+  > ssh = "$PYTHON" "$RUNTESTDIR/dummyssh"
+  > EOF
+
+  $ hg init server
+  $ cd server
+
+  $ echo a > a
+  $ hg commit -qAm root
+  $ hg phase --public -r 'all()'
+
+  $ cd ..
+
+  $ hg clone ssh://user@dummy/server client-topic1
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets 6569b5a81c7e
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone ssh://user@dummy/server client-topic2
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets 6569b5a81c7e
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg version -v -R client-topic1
+  warning: --repository ignored
+  Mercurial Distributed SCM (*) (glob)
+  (see https://mercurial-scm.org for more information)
+  
+  Copyright (C) 2005-* Matt Mackall and others (glob)
+  This is free software; see the source for copying conditions. There is NO
+  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  
+  Enabled extensions:
+  
+    rebase  internal  
+    topic   external  * (glob)
+    evolve  external  * (glob)
+  $ hg clone ssh://user@dummy/server client-plain
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets 6569b5a81c7e
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat << EOF >> client-plain/.hg/hgrc
+  > [extensions]
+  > topic = !
+  > EOF
+  $ hg version -v -R client-plain
+  warning: --repository ignored
+  Mercurial Distributed SCM (*) (glob)
+  (see https://mercurial-scm.org for more information)
+  
+  Copyright (C) 2005-* Matt Mackall and others (glob)
+  This is free software; see the source for copying conditions. There is NO
+  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  
+  Enabled extensions:
+  
+    rebase  internal  
+    evolve  external  * (glob)
+
+Make two commits, with and without a topic, and push them to the server
+
+  $ cd client-topic1
+  $ echo b > b
+  $ hg topic some-work
+  marked working directory as topic: some-work
+  $ hg commit -Am 'adding b (topic)'
+  adding b
+  active topic 'some-work' grew its first changeset
+  (see 'hg help topics' for more information)
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo c > c
+  $ hg commit -Am 'adding c (no topic)'
+  adding c
+  $ hg up some-work
+  switching to topic some-work
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg topic --clear
+  $ echo d > d
+  $ hg commit -Am 'adding d (no topic)'
+  adding d
+  created new head
+  (consider using topic for lightweight branches. See 'hg help topic')
+  $ hg log -r 'all() - 0'
+  changeset:   1:2a2e8b3520f2
+  topic:       some-work
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding b (topic)
+  
+  changeset:   2:b46feb4d24f9
+  parent:      0:6569b5a81c7e
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding c (no topic)
+  
+  changeset:   3:be22ca6e89ea
+  tag:         tip
+  parent:      1:2a2e8b3520f2
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding d (no topic)
+  
+
+(disable the bare publishing to put a bare draft on the server)
+
+  $ cat << EOF >> ../server/.hg/hgrc
+  > [experimental]
+  > topic.publish-bare-branch = no
+  > EOF
+  $ hg push --force
+  pushing to ssh://user@dummy/server
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 3 changesets with 3 changes to 3 files (+1 heads)
+  $ cd ..
+  $ hg --cwd server phase -r 'tip'
+  3: draft
+
+Clients with topic can exchange draft changesets both with and without a topic through the server
+
+  $ hg --cwd client-topic2 pull
+  pulling from ssh://user@dummy/server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 3 changes to 3 files (+1 heads)
+  new changesets 2a2e8b3520f2:be22ca6e89ea
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg --cwd client-topic2 log -r 'all() - 0'
+  changeset:   1:2a2e8b3520f2
+  topic:       some-work
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding b (topic)
+  
+  changeset:   2:b46feb4d24f9
+  parent:      0:6569b5a81c7e
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding c (no topic)
+  
+  changeset:   3:be22ca6e89ea
+  tag:         tip
+  parent:      1:2a2e8b3520f2
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding d (no topic)
+  
+
+Client without topic only sees draft changesets if they don't have a topic
+
+  $ hg --cwd client-plain pull
+  pulling from ssh://user@dummy/server
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets b46feb4d24f9
+  (run 'hg update' to get a working copy)
+  $ hg --cwd client-plain phase -r 'all() - 0'
+  1: draft
+  $ hg --cwd client-plain log -r 'all() - 0'
+  changeset:   1:b46feb4d24f9
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     adding c (no topic)
+  
--- a/tests/test-topic-stack-complex.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-topic-stack-complex.t	Sat Feb 29 15:18:00 2020 +0700
@@ -7,6 +7,7 @@
   $ cat << EOF >> $HGRCPATH
   > [experimental]
   > evolution = all
+  > evolution.allowdivergence = True
   > [ui]
   > interactive = True
   > [extensions]
--- a/tests/test-topic-stack.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-topic-stack.t	Sat Feb 29 15:18:00 2020 +0700
@@ -947,16 +947,16 @@
   | @  e7ea874afbd5 (22) c_G
   |/
   x  b24bab30ac12 (20) c_G
-  |    rewritten(parent, content) as dde94df880e9, e7ea874afbd5 using split by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    split(parent, content) as dde94df880e9, e7ea874afbd5 using split by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  907f7d3c2333 (18) c_G
-  |    rewritten(content) as b24bab30ac12 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    amended(content) as b24bab30ac12 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  3ab2eedae500 (13) c_G
-  |    rewritten(parent) as 907f7d3c2333 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    rebased(parent) as 907f7d3c2333 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  c7d60a180d05 (6) c_G
-       rewritten(user) as 3ab2eedae500 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+       reauthored(user) as 3ab2eedae500 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
   $ hg export .
   # HG changeset patch
--- a/tests/test-topic.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-topic.t	Sat Feb 29 15:18:00 2020 +0700
@@ -120,6 +120,20 @@
     [experimental]
     topic.allow-publish = no
   
+  Server side visibility
+  ======================
+  
+  Serving changesets with topics to clients without topic extension can get
+  confusing. Such clients will have multiple anonymous heads without a clear way
+  to distinguish them. They will also "lose" the canonical heads of the branch.
+  
+  To avoid this confusion, server can be configured to only serve changesets
+  with topics to clients with the topic extension (version 9.3+). This might
+  become the default in future:
+  
+    [experimental]
+    topic.server-gate-topic-changesets = yes
+  
   list of commands:
   
    stack         list all changesets in a topic and other information
--- a/tests/test-touch.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-touch.t	Sat Feb 29 15:18:00 2020 +0700
@@ -221,4 +221,9 @@
   (a)llow divergence or (d)uplicate the changeset?  a
   2 new content-divergent changesets
 
+But -A allows divergence
+
+  $ hg touch -r "desc('modified c')" --hidden -A
+  1 new content-divergent changesets
+
   $ cd ..
--- a/tests/test-uncommit-interactive.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-uncommit-interactive.t	Sat Feb 29 15:18:00 2020 +0700
@@ -168,7 +168,7 @@
   @  678a59e5ff90 (3) another one
   |
   x  f70fb463d5bf (1) another one
-       rewritten(content) as 678a59e5ff90 using uncommit by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 678a59e5ff90 using uncommit by test (Thu Jan 01 00:00:00 1970 +0000)
          note: note on amend --extract
   
 The unselected part should be in the diff
@@ -300,11 +300,11 @@
   @  46e35360be47 (5) another one
   |
   x  678a59e5ff90 (3) another one
-  |    rewritten(content) as 46e35360be47 using uncommit by test (Thu Jan 01 00:00:00 1970 +0000)
+  |    amended(content) as 46e35360be47 using uncommit 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 using uncommit by test (Thu Jan 01 00:00:00 1970 +0000)
+       amended(content) as 678a59e5ff90 using uncommit by test (Thu Jan 01 00:00:00 1970 +0000)
          note: note on amend --extract
   
 
--- a/tests/test-uncommit.t	Thu Jan 30 14:32:10 2020 +0700
+++ b/tests/test-uncommit.t	Sat Feb 29 15:18:00 2020 +0700
@@ -1,4 +1,6 @@
   $ cat >> $HGRCPATH <<EOF
+  > [experimental]
+  > evolution.allowdivergence = True
   > [extensions]
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH