changeset 5481:565194775c41

branching: merge with stable
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 25 Aug 2020 10:12:29 +0200
parents f2d919aa64d6 (diff) 9242d0b5f74d (current diff)
children 3435822cd758
files CHANGELOG hgext3rd/evolve/rewind.py
diffstat 55 files changed, 1490 insertions(+), 1141 deletions(-) [+]
line wrap: on
line diff
--- a/.gitlab-ci.yml	Fri Aug 21 23:04:03 2020 +0800
+++ b/.gitlab-ci.yml	Tue Aug 25 10:12:29 2020 +0200
@@ -1,49 +1,71 @@
-flake8:
-    image: octobus/ci-py2-hgext3rd
-    script:
-        - hg files -0 'set:(**.py or grep("^#!.*python")) - removed()' -X hgext3rd/evolve/thirdparty | xargs -0 flake8
+.prepare_hg: &prepare_hg
+    - hg pull -R /ci/repos/mercurial/
+    - hg_rev=$(tests/testlib/map-hg-rev.sh "$(hg identify --branch)")
+    - hg -R /ci/repos/mercurial/ update "$hg_rev"
 
 pytype:
     image: octobus/ci-py3-hgext3rd
     script:
-        - hg pull -R /ci/repos/mercurial/
-        - hg_rev=$(tests/testlib/map-hg-rev.sh "$(hg log -r . -T '{branch}')")
-        - hg -R /ci/repos/mercurial/ update "$hg_rev"
+        - *prepare_hg
+        - pytype --version
         - jobs=$(python -c 'import multiprocessing; print multiprocessing.cpu_count()')
         - pytype -P /ci/repos/mercurial/:hgext3rd -k hgext3rd -x hgext3rd/evolve/thirdparty -j $jobs || true
     when: manual
 
-tests-py2-cext:
-    image: octobus/ci-py2-hgext3rd
+variables:
+    PY: py2
+    PYTHON: python
+    RUNTEST_ARGS: ""
+    TEST_HGMODULEPOLICY: "allow"
+
+.runtests_template: &runtests
+    image: octobus/ci-$PY-hgext3rd
+    before_script:
+        - (cd tests; ls -1 test-check-*.t > /tmp/check-tests.txt)
     script:
-        - hg pull -R /ci/repos/mercurial/
-        - hg_rev=$(tests/testlib/map-hg-rev.sh "$(hg log -r . -T '{branch}')")
-        - hg -R /ci/repos/mercurial/ update "$hg_rev"
-        - (cd tests; /ci/repos/mercurial/tests/run-tests.py --color=always)
+        - *prepare_hg
+        - ("$PYTHON" --version)
+        - (cd tests; set -x; HGMODULEPOLICY="$TEST_HGMODULEPOLICY" "$PYTHON" /ci/repos/mercurial/tests/run-tests.py --color=always $RUNTEST_ARGS)
+
+checks-py2:
+    <<: *runtests
+    variables:
+        RUNTEST_ARGS: "--test-list /tmp/check-tests.txt"
+
+checks-py3:
+    <<: *runtests
+    variables:
+        PY: py3
+        PYTHON: python3
+        RUNTEST_ARGS: "--test-list /tmp/check-tests.txt"
+
+tests-py2-cext:
+    <<: *runtests
+    variables:
+        RUNTEST_ARGS: "--no-rust --blacklist /tmp/check-tests.txt"
+        TEST_HGMODULEPOLICY: "c"
 
 tests-py2-pure:
-    image: octobus/ci-py2-hgext3rd
-    script:
-        - hg pull -R /ci/repos/mercurial/
-        - hg_rev=$(tests/testlib/map-hg-rev.sh "$(hg log -r . -T '{branch}')")
-        - hg -R /ci/repos/mercurial/ update "$hg_rev"
-        - (cd tests; /ci/repos/mercurial/tests/run-tests.py --color=always --pure)
+    <<: *runtests
+    variables:
+        RUNTEST_ARGS: "--pure --blacklist /tmp/check-tests.txt"
+        TEST_HGMODULEPOLICY: "py"
 
 tests-py3-cext:
-    image: octobus/ci-py3-hgext3rd
-    script:
-        - hg pull -R /ci/repos/mercurial/
-        - hg_rev=$(tests/testlib/map-hg-rev.sh "$(hg log -r . -T '{branch}')")
-        - hg -R /ci/repos/mercurial/ update "$hg_rev"
-        - (cd tests; python3 /ci/repos/mercurial/tests/run-tests.py --color=always)
+    <<: *runtests
+    variables:
+        PY: py3
+        PYTHON: python3
+        RUNTEST_ARGS: "--no-rust --blacklist /tmp/check-tests.txt"
+        TEST_HGMODULEPOLICY: "c"
 
 tests-py3-pure:
-    image: octobus/ci-py3-hgext3rd
-    script:
-        - hg pull -R /ci/repos/mercurial/
-        - hg_rev=$(tests/testlib/map-hg-rev.sh "$(hg log -r . -T '{branch}')")
-        - hg -R /ci/repos/mercurial/ update "$hg_rev"
-        - (cd tests; python3 /ci/repos/mercurial/tests/run-tests.py --color=always --pure)
+    <<: *runtests
+    variables:
+        PY: py3
+        PYTHON: python3
+        RUNTEST_ARGS: "--pure --blacklist /tmp/check-tests.txt"
+        TEST_HGMODULEPOLICY: "py"
 
 doc:
     image: octobus/ci-py2-evolve-doc
--- a/.gitlab/issue_templates/new-version.md	Fri Aug 21 23:04:03 2020 +0800
+++ b/.gitlab/issue_templates/new-version.md	Tue Aug 25 10:12:29 2020 +0200
@@ -1,6 +1,6 @@
 This is the actual check list for releasing evolve version X.Y.Z
 
-More details in the [README file](README#L179).
+More details in the [README.rst file](README.rst#L199).
 
 Preparation
 
--- a/CHANGELOG	Fri Aug 21 23:04:03 2020 +0800
+++ b/CHANGELOG	Tue Aug 25 10:12:29 2020 +0200
@@ -1,6 +1,15 @@
 Changelog
 =========
 
+10.1.0 - in progress
+--------------------
+
+  * numerous minor changes to packaging, README moved to README.rst
+
+topic (0.20.0)
+
+  * stack: support foo#stack relation revset (hg-5.4+ only)
+
 10.0.2 - in progress
 --------------------
 
--- a/MANIFEST.in	Fri Aug 21 23:04:03 2020 +0800
+++ b/MANIFEST.in	Tue Aug 25 10:12:29 2020 +0200
@@ -2,7 +2,7 @@
 include COPYING
 include hgext3rd/topic/README
 include MANIFEST.in
-include README
+include README.rst
 include setup.py
 recursive-include tests *.py *.sh *.t
 
@@ -22,7 +22,6 @@
 exclude .hg-format-source
 exclude Makefile
 exclude tests/test-drop.t
-exclude tests/test-inhibit.t
 exclude tests/test-oldconvert.t
 
 prune contrib
--- a/Makefile	Fri Aug 21 23:04:03 2020 +0800
+++ b/Makefile	Tue Aug 25 10:12:29 2020 +0200
@@ -1,9 +1,19 @@
-VERSION=$(shell python setup.py --version)
+PYTHON ?= python
+VERSION = $(shell python setup.py --version)
+TESTFLAGS ?= $(shell echo $$HGTESTFLAGS)
+HGTESTS = $(HGROOT)/tests
 
-PYTHON=python
+.PHONY: help
+help:
+	@echo 'Commonly used make targets:'
+	@echo '  deb-prepare        - prepare the build of a debian package'
+	@echo '  install-home       - install with setup.py install --home=$$HOME ($(HOME))'
+	@echo '  tests              - run all tests in the automatic test suite'
 
+.PHONY: all
 all: help
 
+.PHONY: deb-prepare
 deb-prepare:
 	python setup.py sdist --dist-dir ..
 	mv -f ../hg-evolve-$(VERSION).tar.gz ../mercurial-evolve_$(VERSION).orig.tar.gz
@@ -13,42 +23,23 @@
 	cp -r debian/ ../mercurial-evolve_$(VERSION).orig/
 	@cd ../mercurial-evolve_$(VERSION).orig && echo 'debian build directory ready at' `pwd`
 
+.PHONY: install-home
 install-home:
 	$(PYTHON) setup.py install --home="$(HOME)" --prefix="" --force
 
-# test targets
-TESTFLAGS ?= $(shell echo $$HGTESTFLAGS)
-
-HGTESTS=$(HGROOT)/tests
+.PHONY: doc
+doc:
+	$(MAKE) -C docs
 
-help:
-	@echo 'Commonly used make targets:'
-	@echo '  deb-prepare        - prepare the build of a debian package'
-	@echo '  tests              - run all tests in the automatic test suite'
-	@echo '  all-version-tests - run all tests against many hg versions'
-	@echo '  tests-%s           - run all tests in the specified hg version'
-
-all: help
-
+.PHONY: _check_hgroot
 _check_hgroot:
 ifeq ($(HGROOT),)
 	$(error HGROOT is not set to the root of the hg source tree)
 endif
 
+.PHONY: tests
 tests: _check_hgroot
 	cd tests && $(PYTHON) $(HGTESTS)/run-tests.py $(TESTFLAGS)
 
-# /!\ run outside of the compatibility branch output test will likely fails
-
 test-%: _check_hgroot
 	cd tests && $(PYTHON) $(HGTESTS)/run-tests.py $(TESTFLAGS) $@
-
-tests-%: _check_hgroot
-	hg -R $(HGROOT) checkout $$(echo $@ | sed s/tests-//) && \
-	(cd $(HGROOT) ; $(MAKE) clean ) && \
-	cd tests && $(PYTHON) $(HGTESTS)/run-tests.py $(TESTFLAGS)
-
-# build a script to extract declared version
-all-version-tests: tests-@
-
-.PHONY: tests all-version-tests
--- a/README	Fri Aug 21 23:04:03 2020 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-=============================
-Mutable History For Mercurial
-=============================
-
-Evolve Extension
-=================
-
-This package supplies the ``evolve`` extension for Mercurial,
-
-**The full implementation of the changeset evolution concept is still in
-progress.**  Please subscribe to the `evolve-testers mailing list
-<https://www.mercurial-scm.org/mailman/listinfo/evolve-testers>`_ to stay up to
-date with changes.
-
-This extension:
-
-* enables the "changeset evolution" feature of Mercurial core,
-
-* provides a set of commands to mutate your history,
-
-* issues several warning messages when troubles from some mutable appears in
-  your repository,
-
-* provides an ``hg evolve`` command to deal with such "troubles",
-
-* improves performance of obsolescence marker exchanges and discovery during
-  push and pull.
-
-Documentation
--------------
-
-We recommend reading the documentation first. An online version is
-available here:
-
-    https://www.mercurial-scm.org/doc/evolution/
-
-How to Install
-==============
-
-Using Pip
----------
-
-You can install the latest evolution version usin pip::
-
-    $ pip install --user hg-evolve
-
-Then just enable it in you hgrc::
-
-    $ hg config --edit # adds the two line below:
-    [extensions]
-    evolve =
-
-From Source
------------
-
-To install a local version from source::
-
-    $ hg clone https://www.mercurial-scm.org/repo/evolve/
-    $ cd evolve
-    $ pip install --user .
-
-Then just enable it in you hgrc::
-
-    $ hg config --edit # adds the two line below:
-    [extensions]
-    evolve =
-
-Documentation lives in ``doc/``.
-
-Server Only Setup
-=================
-
-It is possible to enable a smaller subset of the extensions aimed at server
-serving repository. It skips the additions of the new commands and local UI
-messages that might add performance overheads. To use the server only
-extension, install the package and use::
-
-    $ hg config --edit # adds the two line below:
-    [extensions]
-    evolve.serveronly =
-
-Extension Purpose
-=================
-
-The goal of this extension is to provide an appropriate place for code and
-concept related to changeset evolution to mature. In this extension we allow
-for hackier code, unlocking quick experimentation and faster iterations.
-
-In addition, the evolve extensions support a wider set of Mercurial version,
-allowing us to reach a larger user base for feedback. The Evolve extension is
-not tight to the Mercurial release cycle and can release new feature and bug
-fix at a higher rate if necessary.
-
-Once a concept is ready enough, its implementation is moved into Mercurial
-core. The maturation period helped us to get a clearer picture of what was
-needed. During the upstreaming process, we can use this clearer picture to
-clean up the code and upgrade it to an appropriate quality for Mercurial core.
-
-Python 3 support
-================
-
-Mercurial announced beta support for Python 3 starting with its 5.0 release.
-Since 9.1.0, ``evolve`` has beta support for Python 3.6+.
-
-Support will stay in beta while Mercurial's support for Python 3 remains in
-beta and until it is a bit more battle-tested.
-
-How to Contribute
-=================
-
-Discussion happens on the #hg-evolve IRC on freenode_.
-
-.. _freenode: https://freenode.net/
-
-Bugs are to be reported on the mercurial's bug tracker (component: `evolution`_):
-
-.. _evolution: https://bz.mercurial-scm.org/buglist.cgi?component=evolution&query_format=advanced&resolution=---
-
-The recommended way is to create Merge Request on
-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
-devel <https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel>`_.
-Please make sure to use the evolve-ext flag when doing so. You can use a
-command like this::
-
-    $ hg email --to mercurial-devel@mercurial-scm.org --flag evolve-ext --rev '<your patches>'
-
-
-For guidelines on the patch description, see the `official Mercurial guideline`_.
-
-.. _`official Mercurial guideline`: https://mercurial-scm.org/wiki/ContributingChanges#Patch_descriptions
-
-Please don't forget to update and run the tests when you fix a bug or
-add a feature. To run the tests, you need a working copy of Mercurial,
-say in $HGSRC::
-
-    $ cd tests
-    $ python $HGSRC/tests/run-tests.py
-
-When certain blocks of code need to cope with API changes in core Mercurial,
-they should have comments in the ``hg <= x.y (commit hash)`` format. For
-example, if a function needs another code path because of changes introduced in
-02802fa87b74 that was first included in Mercurial 5.3, then the comment should
-be::
-
-    # hg <= 5.2 (02802fa87b74)
-
-See also tests/test-check-compat-strings.t.
-
-Branch policy
--------------
-
-The evolve tests are highly impacted by changes in core. To deal with this, we
-use named branches.
-
-There are two main branches: "stable" and "default". Tests on these branches
-are supposed to pass with the corresponding "default" and "stable" branch from
-core Mercurial. The documentation is built from the tip of stable.
-
-In addition, we have compatibility branches to check tests on older versions of
-Mercurial. They are the "mercurial-x.y" branches. They are used to apply
-expected test changes only, no code changes should happen there.
-
-Test output changes from a changeset in core should add the following line to
-their patch description:
-
-CORE-TEST-OUTPUT-UPDATE: <CORE-NODE-ID>
-
-
-Format-source config
-====================
-
-Format source helps smooth out the pain of merging after auto-formatting.
-Follow the instructions for install here:
-
-.. _`format-source`: https://bitbucket.org/octobus/format-source
-
-Then update both your global and repo config files::
-
-    $ hg config -l # add the lines below
-    [extensions]
-    formatsource =
-
-    [format-source]
-    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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.rst	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,255 @@
+=============================
+Mutable History For Mercurial
+=============================
+
+Evolve Extension
+================
+
+This package supplies the evolve extension for Mercurial,
+
+**The full implementation of the changeset evolution concept is still in
+progress.**  Please subscribe to the `evolve-testers mailing list
+<https://www.mercurial-scm.org/mailman/listinfo/evolve-testers>`_ to stay up to
+date with changes.
+
+This extension:
+
+* enables the "`changeset evolution`_" feature of core Mercurial,
+
+* provides a set of commands to rewrite history in a distributed way,
+
+* issues various warning messages when "troubles" from changeset evolution
+  appear in your repository,
+
+* provides an ``hg evolve`` command to deal with such troubles,
+
+* improves performance of obsolescence marker exchange and discovery during
+  push and pull.
+
+.. _`changeset evolution`: https://www.mercurial-scm.org/wiki/ChangesetEvolution
+
+Documentation
+-------------
+
+We recommend reading the documentation first. An online version is available
+here:
+
+    https://www.mercurial-scm.org/doc/evolution/
+
+Source of the documentation can be found in ``docs/``.
+
+How to Install
+==============
+
+Using Pip
+---------
+
+You can install the latest released version using pip::
+
+    $ pip install --user hg-evolve
+
+Then enable it in your hgrc::
+
+    $ hg config --edit # add these two lines:
+    [extensions]
+    evolve =
+
+From Source
+-----------
+
+To install a local version from source::
+
+    $ hg clone https://www.mercurial-scm.org/repo/evolve/
+    $ cd evolve
+    # optionally `hg update <target revision>`
+    $ pip install --user .
+
+Then enable it in your hgrc::
+
+    $ hg config --edit # add these two lines:
+    [extensions]
+    evolve =
+
+It's also possible to use evolve without installing it, in which case you will
+need to provide the full path to ``hgext3rd/evolve/``, for example::
+
+    [extensions]
+    evolve = ~/evolve/hgext3rd/evolve
+
+Server-only Setup
+=================
+
+It is possible to enable a smaller subset of the features aimed at servers that
+simply serve repositories::
+
+    $ hg config --edit # add these two lines:
+    [extensions]
+    evolve.serveronly =
+
+It skips the additions of the new commands and local UI messages that might add
+performance overhead.
+
+Extension Purpose
+=================
+
+The goal of this extension is to provide an appropriate place for code and
+concepts related to `changeset evolution`_ to mature. In this extension we
+allow hackier code, unlocking quick experimentation and faster iterations.
+
+In addition, evolve extension supports a wide range of Mercurial versions,
+allowing us to reach a larger user base for feedback. The extension is not tied
+to the Mercurial release cycle and can release new features and bug fixes at a
+higher rate if necessary.
+
+Once a concept is deemed ready, its implementation is moved into core
+Mercurial. The maturation period helped us to get a clearer picture of what was
+needed. During the upstreaming process, we can use this clearer picture to
+clean up the code and upgrade it to an appropriate quality for core Mercurial.
+
+Python 3 Support
+================
+
+Mercurial announced official `support for Python 3`_ starting with its 5.2
+release. Since 9.3.0, evolve has official support for Python 3.6+.
+
+.. _`support for Python 3`: https://www.mercurial-scm.org/wiki/Python3
+
+How to Contribute
+=================
+
+Discussion happens on the #hg-evolve IRC on freenode_.
+
+.. _freenode: https://freenode.net/
+
+Bugs are to be reported on the Mercurial's bug tracker (component:
+`evolution`_).
+
+.. _evolution: https://bz.mercurial-scm.org/buglist.cgi?component=evolution&query_format=advanced&resolution=---
+
+The recommended way to submit a patch is to create a Merge Request on
+https://foss.heptapod.net/mercurial/evolve. To do so, create an account and
+request access. You'll then be able to create a topic-based merge request.
+
+Alternatively, you can use the patchbomb extension to send email to `mercurial
+devel <https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel>`_.
+Please make sure to use the evolve-ext flag when doing so. You can use a
+command like this::
+
+    $ hg email --to mercurial-devel@mercurial-scm.org --flag evolve-ext --rev '<your patches>'
+
+For guidelines on the patch description, see the `official Mercurial guideline`_.
+
+.. _`official Mercurial guideline`: https://mercurial-scm.org/wiki/ContributingChanges#Patch_descriptions
+
+Please don't forget to update and run the tests when you fix a bug or add a
+feature. To run the tests, you need a working copy of Mercurial, say in
+$HGSRC::
+
+    $ cd tests
+    $ python $HGSRC/tests/run-tests.py
+
+When certain blocks of code need to cope with API changes in core Mercurial,
+they should have comments in the ``hg <= x.y (commit hash)`` format. For
+example, if a function needs another code path because of changes introduced in
+02802fa87b74 that was first included in Mercurial 5.3, then the comment should
+be::
+
+    # hg <= 5.2 (02802fa87b74)
+
+See also tests/test-check-compat-strings.t.
+
+Branch policy
+-------------
+
+The evolve tests are highly impacted by changes in core Mercurial. To deal with
+this, we use named branches.
+
+There are two main branches: "stable" and "default". Tests on these branches
+are supposed to pass with the corresponding "default" and "stable" branch from
+core Mercurial. The documentation is built from the tip of stable.
+
+In addition, we have compatibility branches to check tests on older versions of
+Mercurial. They are the "mercurial-x.y" branches. They are used to apply
+expected test changes only, no code changes should happen there.
+
+Test output changes from a changeset in core should add the following line to
+their patch description::
+
+    CORE-TEST-OUTPUT-UPDATE: <changeset hash>
+
+Format-source config
+====================
+
+Format source helps smooth out the pain of merging after auto-formatting.
+Follow the instructions for install here:
+
+.. _`format-source`: https://foss.heptapod.net/mercurial/format-source
+
+Then update your per-repo config file::
+
+    $ hg config --local --edit # add these lines:
+    [extensions]
+    formatsource =
+
+    [format-source]
+    byteify-strings = python3 ~/hg/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	Fri Aug 21 23:04:03 2020 +0800
+++ b/debian/changelog	Tue Aug 25 10:12:29 2020 +0200
@@ -164,9 +164,9 @@
  -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Mon, 23 Oct 2017 15:41:03 +0200
 
 mercurial-evolve (6.7.1-1) unstable; urgency=medium
- 
+
    * new upstream release
- 
+
  -- Pierre-Yves David <pierre-yves.david@ens-lyon.org>  Tue, 10 Oct 2017 16:03:23 +0200
 
 mercurial-evolve (6.7.0-1) unstable; urgency=medium
--- a/debian/control	Fri Aug 21 23:04:03 2020 +0800
+++ b/debian/control	Tue Aug 25 10:12:29 2020 +0200
@@ -5,21 +5,21 @@
 Uploaders:
  Julien Cristau <julien.cristau@logilab.fr>,
  Pierre-Yves David <pierre-yves.david@logilab.fr>,
-Standards-Version: 3.9.3
+Standards-Version: 4.3.0
 Build-Depends:
  mercurial (>= 4.6),
  python,
  python3,
  debhelper (>= 10),
  dh-python,
- python-sphinx (>= 1.0.8),
+ python-sphinx,
  imagemagick,
  librsvg2-bin,
- wget,
- sphinx-common,
-X-Python-Version: >= 2.7
+ graphviz,
 X-Python3-Version: >= 3.6
 Homepage: https://www.mercurial-scm.org/doc/evolution/
+Vcs-Browser: https://www.mercurial-scm.org/repo/evolve/
+Vcs-Hg: https://www.mercurial-scm.org/repo/evolve/
 
 Package: mercurial-evolve
 Architecture: all
@@ -36,5 +36,3 @@
  .
  This extension provides several commands to mutate history and deal with issues
  it may raise.
-
-
--- a/debian/docs	Fri Aug 21 23:04:03 2020 +0800
+++ b/debian/docs	Tue Aug 25 10:12:29 2020 +0200
@@ -1,2 +1,2 @@
 html
-README
+README.rst
--- a/debian/rules	Fri Aug 21 23:04:03 2020 +0800
+++ b/debian/rules	Tue Aug 25 10:12:29 2020 +0200
@@ -30,12 +30,7 @@
 	find debian -path '*/hgext3rd/__init__.py' -delete
 	dh_python3
 
-override_dh_auto_clean: clean-docs
+override_dh_auto_clean:
 	dh_auto_clean
 	rm -f tests/*.err
-
-clean-docs:
-	rm -rf html
-	rm -f docs/static/logo-evolve.ico
-	rm -f docs/tutorials/tutorial.rst
-	rm -f docs/tutorials/topic-tutorial.rst
+	$(MAKE) -C docs clean
--- a/debian/source/options	Fri Aug 21 23:04:03 2020 +0800
+++ b/debian/source/options	Tue Aug 25 10:12:29 2020 +0200
@@ -1,1 +1,1 @@
-extend-diff-ignore = "^(contrib/|.gitlab-ci.yml|.hg-format-source|MANIFEST|Makefile|docs/tutorial/.netlify|hgext3rd/evolve/hack/|tests/.testtimes)"
+extend-diff-ignore = "^(contrib/|\.gitlab/|\.gitlab-ci.yml|\.hg-format-source|MANIFEST|Makefile|docs/tutorial/\.netlify|hgext3rd/evolve/hack/|tests/\.testtimes|tests/test-drop\.t|tests/test-oldconvert\.t)"
--- a/debian/test-blacklist	Fri Aug 21 23:04:03 2020 +0800
+++ b/debian/test-blacklist	Tue Aug 25 10:12:29 2020 +0200
@@ -1,4 +1,1 @@
 test-drop.t
-test-inhibit.t
-test-simple4server.t
-tests/test-simple4server-bundle2.t
--- a/docs/makefile	Fri Aug 21 23:04:03 2020 +0800
+++ b/docs/makefile	Tue Aug 25 10:12:29 2020 +0200
@@ -1,5 +1,6 @@
 SPHINXBUILD ?= sphinx-build
 
+.PHONY: all
 all: tutorials/tutorial.rst tutorials/topic-tutorial.rst static/logo-evolve.ico
 	$(SPHINXBUILD) . ../html/
 
@@ -10,4 +11,11 @@
 	python test2rst.py tutorials/
 
 static/logo-evolve.ico: static/logo-evolve.svg
-	convert -resize 36x36 static/logo-evolve.svg static/logo-evolve.ico
+	convert -resize 36x36 $< $@
+
+.PHONY: clean
+clean:
+	rm -rf ../html
+	rm -f static/logo-evolve.ico
+	rm -f tutorials/tutorial.rst
+	rm -f tutorials/topic-tutorial.rst
--- a/hgext3rd/evolve/evolvecmd.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/evolve/evolvecmd.py	Tue Aug 25 10:12:29 2020 +0200
@@ -68,12 +68,11 @@
     tr = repo.currenttransaction()
     assert tr is not None
     displayer = None
+    template = shorttemplate
     if stacktmplt:
-        displayer = logcmdutil.changesetdisplayer(ui, repo,
-                                                  {b'template': stacktemplate})
-    else:
-        displayer = logcmdutil.changesetdisplayer(ui, repo,
-                                                  {b'template': shorttemplate})
+        template = stacktemplate
+    displayer = logcmdutil.changesetdisplayer(ui, repo,
+                                              {b'template': template})
     if b'orphan' == category:
         result = _solveunstable(ui, repo, ctx, evolvestate, displayer,
                                 dryrun, confirm, progresscb,
@@ -109,7 +108,7 @@
             keepbranch = orig.p2().branch() != orig.branch()
         elif not p2obs and p1obs:
             pass
-        else:
+        elif p1obs and p2obs:
             # store that we are resolving an orphan merge with both parents
             # obsolete and proceed with first parent
             evolvestate[b'orphanmerge'] = True
@@ -120,7 +119,7 @@
             keepbranch = orig.p2().branch() != orig.branch()
 
     if not pctx.obsolete():
-        ui.warn(_(b"cannot solve instability of %s, skipping\n") % orig)
+        ui.warn(_(b"skipping %s, consider including orphan ancestors\n") % orig)
         return (False, b".")
     obs = pctx
     try:
@@ -288,7 +287,6 @@
 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()
     # sometimes we will relocate a node in case of different parents and we can
@@ -392,6 +390,9 @@
     if otherp1 == divp1:
         # both are on the same parents
         pass
+    elif divergent == other.p1():
+        # both are in parent-child relation
+        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:
@@ -493,7 +494,7 @@
     divergent, other, resolutionparent, relocatereq = datatoproceed[1:]
 
     if relocatereq:
-        evolvestate['relocation-req'] = True
+        evolvestate[b'relocation-req'] = True
     evolvestate[b'resolutionparent'] = resolutionparent
 
     if not ui.quiet or confirm:
@@ -515,10 +516,6 @@
         ui.write((b'hg commit -m "`hg log -r %s --template={desc}`";\n'
                   % 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:
@@ -538,10 +535,6 @@
         evolvestate[b'temprevs'].append(divergent.node())
         evolvestate[b'divergent'] = divergent.node()
 
-    # Sometimes we already have the other cset where we want it
-    if relocatereq and other == divergent.p1():
-        relocatereq = False
-
     # relocate the other divergent if required
     if relocatereq:
         # relocating will help us understand during the time of conflicts that
@@ -2215,7 +2208,7 @@
                 repo.dirstate.setparents(ctxparents[0].node(),
                                          currentp1)
 
-        else:
+        elif p1obs and p2obs:
             # both the parents were obsoleted, if orphanmerge is set, we
             # are processing the second parent first (to keep parent order)
             if evolvestate.get(b'orphanmerge'):
--- a/hgext3rd/evolve/metadata.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/evolve/metadata.py	Tue Aug 25 10:12:29 2020 +0200
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-__version__ = b'10.0.2.dev'
+__version__ = b'10.1.0.dev'
 testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5'
 minimumhgversion = b'4.6'
 buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/rewind.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/evolve/rewind.py	Tue Aug 25 10:12:29 2020 +0200
@@ -88,10 +88,10 @@
         # stay on the safe side: prevent local case in case we need to upgrade
         cmdutil.bailifchanged(repo)
 
-        rewinded = _select_rewinded(repo, opts)
+        targets = _select_rewind_targets(repo, opts)
 
         if not opts['as_divergence']:
-            for rev in rewinded:
+            for rev in targets:
                 ctx = unfi[rev]
                 ssets = obsutil.successorssets(repo, ctx.node(), cache=sscache)
                 if 1 < len(ssets):
@@ -106,7 +106,7 @@
         with repo.transaction(b'rewind'):
             oldctx = repo[b'.']
 
-            for rev in sorted(rewinded):
+            for rev in sorted(targets):
                 ctx = unfi[rev]
                 rewindmap[ctx.node()] = _revive_revision(unfi, rev, rewindmap)
 
@@ -162,34 +162,34 @@
                 else:
                     hg.updaterepo(repo, update_target, False)
 
-    repo.ui.status(_(b'rewinded to %d changesets\n') % len(rewinded))
+    ui.status(_(b'rewound to %d changesets\n') % len(targets))
     if relationships:
-        repo.ui.status(_(b'(%d changesets obsoleted)\n') % len(relationships))
+        ui.status(_(b'(%d changesets obsoleted)\n') % len(relationships))
     if update_target is not None and not opts.get('keep'):
         ui.status(_(b'working directory is now at %s\n') % repo[b'.'])
 
-def _select_rewinded(repo, opts):
-    """select the revision we shoudl rewind to
+def _select_rewind_targets(repo, opts):
+    """select the revisions we should rewind to
     """
     unfi = repo.unfiltered()
-    rewinded = set()
+    targets = set()
     revsto = opts.get('to')
     revsfrom = opts.get('from')
     if not (revsto or revsfrom):
         revsfrom.append(b'.')
     if revsto:
-        rewinded.update(scmutil.revrange(repo, revsto))
+        targets.update(scmutil.revrange(repo, revsto))
     if revsfrom:
         succs = scmutil.revrange(repo, revsfrom)
-        rewinded.update(unfi.revs(b'predecessors(%ld)', succs))
+        targets.update(unfi.revs(b'predecessors(%ld)', succs))
 
-    if not rewinded:
+    if not targets:
         raise error.Abort(b'no revision to rewind to')
 
     if not opts['exact']:
-        rewinded = unfi.revs(b'obsolete() and ::%ld', rewinded)
+        targets = unfi.revs(b'obsolete() and ::%ld', targets)
 
-    return sorted(rewinded)
+    return sorted(targets)
 
 def _revive_revision(unfi, rev, rewindmap):
     """rewind a single revision rev.
--- a/hgext3rd/evolve/stablesort.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/evolve/stablesort.py	Tue Aug 25 10:12:29 2020 +0200
@@ -61,7 +61,7 @@
     pl = stablemin(parents(m))
     ph = stablemax(parents(m))
     stablesort(::m) == stablesort(pl)
-                       + [i for in in stablesort(ph) if in ph % pl]
+                       + [i for i in stablesort(ph) if i in ph % pl]
                        + m
 
 This is visible in the example above:
@@ -113,7 +113,7 @@
   stablesort(::F) = [A, B, C, E, F]
   stablesort(::G) - ::F = [A, B, C, D, E, G] - [A, B, C, E, F] = [D, G]
 
-In this order, we reuse all of `stablesort(::H)`, but the subset of
+In this order, we reuse all of `stablesort(::F)`, but the subset of
 `stablesort(::G)` we reuse is not contiguous, we had to skip over 'E' that is
 already contained inside ::F.
 
--- a/hgext3rd/evolve/templatekw.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/evolve/templatekw.py	Tue Aug 25 10:12:29 2020 +0200
@@ -9,7 +9,6 @@
 """
 
 from . import (
-    error,
     exthelper,
     obshistory,
 )
@@ -23,15 +22,6 @@
 
 ### template keywords
 
-@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")."""
-    ctx = context.resource(mapping, b'ctx')
-    return templatekw.compatlist(context, mapping, b'instability',
-                                 ctx.instabilities(),
-                                 plural=b'instabilities')
-
 @eh.templatekeyword(b'troubles', requires={b'ctx', b'templ'})
 def showtroubles(context, mapping):   # legacy name for instabilities
     ctx = context.resource(mapping, b'ctx')
@@ -60,12 +50,6 @@
 else:
     templatekw.keywords[b"precursors"] = _sp
 
-
-def closestsuccessors(repo, nodeid):
-    """ returns the closest visible successors sets instead.
-    """
-    return directsuccessorssets(repo, nodeid)
-
 _ss = templatekw.showsuccessorssets
 if util.safehasattr(_ss, '_requires'):
     def showsuccessors(context, mapping):
@@ -75,98 +59,3 @@
     _tk(showsuccessors)
 else:
     templatekw.keywords[b"successors"] = _ss
-
-def _getusername(ui):
-    """the default username in the config or None"""
-    try:
-        return ui.username()
-    except error.Abort: # no easy way to avoid ui raising Abort here :-/
-        return None
-
-# copy from mercurial.obsolete with a small change to stop at first known changeset.
-
-def directsuccessorssets(repo, initialnode, cache=None):
-    """return set of all direct successors of initial nodes
-    """
-
-    succmarkers = repo.obsstore.successors
-
-    # Stack of nodes we search successors sets for
-    toproceed = [initialnode]
-    # set version of above list for fast loop detection
-    # element added to "toproceed" must be added here
-    stackedset = set(toproceed)
-
-    pathscache = {}
-
-    if cache is None:
-        cache = {}
-    while toproceed:
-        current = toproceed[-1]
-        if current in cache:
-            stackedset.remove(toproceed.pop())
-        elif current != initialnode and current in repo:
-            # We have a valid direct successors.
-            cache[current] = [(current,)]
-        elif current not in succmarkers:
-            if current in repo:
-                # We have a valid last successors.
-                cache[current] = [(current,)]
-            else:
-                # Final obsolete version is unknown locally.
-                # Do not count that as a valid successors
-                cache[current] = []
-        else:
-            for mark in sorted(succmarkers[current]):
-                for suc in mark[1]:
-                    if suc not in cache:
-                        if suc in stackedset:
-                            # cycle breaking
-                            cache[suc] = []
-                        else:
-                            # case (3) If we have not computed successors sets
-                            # of one of those successors we add it to the
-                            # `toproceed` stack and stop all work for this
-                            # iteration.
-                            pathscache.setdefault(suc, []).append((current, mark))
-                            toproceed.append(suc)
-                            stackedset.add(suc)
-                            break
-                else:
-                    continue
-                break
-            else:
-                succssets = []
-                for mark in sorted(succmarkers[current]):
-                    # successors sets contributed by this marker
-                    markss = [[]]
-                    for suc in mark[1]:
-                        # cardinal product with previous successors
-                        productresult = []
-                        for prefix in markss:
-                            for suffix in cache[suc]:
-                                newss = list(prefix)
-                                for part in suffix:
-                                    # do not duplicated entry in successors set
-                                    # first entry wins.
-                                    if part not in newss:
-                                        newss.append(part)
-                                productresult.append(newss)
-                        markss = productresult
-                    succssets.extend(markss)
-                # remove duplicated and subset
-                seen = []
-                final = []
-                candidate = sorted(((set(s), s) for s in succssets if s),
-                                   key=lambda x: len(x[1]), reverse=True)
-                for setversion, listversion in candidate:
-                    for seenset in seen:
-                        if setversion.issubset(seenset):
-                            break
-                    else:
-                        final.append(listversion)
-                        seen.append(setversion)
-                final.reverse() # put small successors set first
-                cache[current] = final
-
-    return cache[initialnode], pathscache
--- a/hgext3rd/topic/__init__.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/topic/__init__.py	Tue Aug 25 10:12:29 2020 +0200
@@ -202,7 +202,7 @@
               b'topic.active': b'green',
               }
 
-__version__ = b'0.19.2.dev'
+__version__ = b'0.20.0.dev'
 
 testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5'
 minimumhgversion = b'4.6'
--- a/hgext3rd/topic/discovery.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/topic/discovery.py	Tue Aug 25 10:12:29 2020 +0200
@@ -213,13 +213,15 @@
             for branch, oldnb in tr._prepushheads.items():
                 newnb = finalheads.pop(branch, 0)
                 if oldnb < newnb:
-                    msg = _(b'push create a new head on branch "%s"' % branch)
+                    msg = _(b"push creates a new head on branch '%s'" % branch)
                     raise error.Abort(msg)
             for branch, newnb in finalheads.items():
                 if 1 < newnb:
-                    msg = _(b'push create more than 1 head on new branch "%s"'
+                    msg = _(b"push creates new branch '%s' with multiple heads"
                             % branch)
-                    raise error.Abort(msg)
+                    hint = _(b"merge or see 'hg help push' for details about "
+                             b"pushing new heads")
+                    raise error.Abort(msg, hint=hint)
 
     def validator(tr):
         _validate(tr)
--- a/hgext3rd/topic/revset.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/hgext3rd/topic/revset.py	Tue Aug 25 10:12:29 2020 +0200
@@ -107,8 +107,10 @@
         branch = repo[None].branch()
     return revset.baseset(stack.stack(repo, branch=branch, topic=topic)[1:]) & subset
 
+# x#y[z] revset operator support (no support for older version)
+# hg <= 4.8 (e54bfde922f2)
 if util.safehasattr(revset, 'subscriptrelations'):
-    def stackrel(repo, subset, x, rel, z, order):
+    def stacksubrel(repo, subset, x, rel, z, order):
         """This is a revset-flavored implementation of stack aliases.
 
         The syntax is: rev#stack[n] or rev#s[n]. Plenty of logic is borrowed
@@ -160,15 +162,25 @@
 
         return subset & revset.baseset(revs)
 
-    revset.subscriptrelations[b'stack'] = stackrel
-    revset.subscriptrelations[b's'] = stackrel
+    revset.subscriptrelations[b'stack'] = stacksubrel
+    revset.subscriptrelations[b's'] = stacksubrel
 
-    def topicrel(repo, subset, x, *args):
+    def topicsubrel(repo, subset, x, *args):
         subset &= topicset(repo, subset, x)
         # not using revset.generationssubrel directly because it was renamed
         # hg <= 5.3 (8859de3e83dc)
         generationssubrel = revset.subscriptrelations[b'generations']
         return generationssubrel(repo, subset, x, *args)
 
-    revset.subscriptrelations[b'topic'] = topicrel
-    revset.subscriptrelations[b't'] = topicrel
+    revset.subscriptrelations[b'topic'] = topicsubrel
+    revset.subscriptrelations[b't'] = topicsubrel
+
+    # x#y revset operator support (no support for older version)
+    # hg <= 5.3 (eca82eb9d777)
+    if util.safehasattr(revset, 'relations'):
+        def stackrel(repo, subset, x, rel, order):
+            z = (b'rangeall', None)
+            return stacksubrel(repo, subset, x, rel, z, order)
+
+        revset.relations[b'stack'] = stackrel
+        revset.relations[b's'] = stackrel
--- a/setup.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/setup.py	Tue Aug 25 10:12:29 2020 +0200
@@ -1,4 +1,3 @@
-import os
 from distutils.core import setup
 from os.path import dirname, join
 
@@ -34,10 +33,6 @@
 
 py_versions = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4'
 
-if os.environ.get('INCLUDE_INHIBIT'):
-    py_modules.append('hgext3rd.evolve.hack.inhibit')
-    py_modules.append('hgext3rd.evolve.hack.directaccess')
-
 setup(
     name='hg-evolve',
     version=get_version(),
@@ -47,7 +42,7 @@
     maintainer_email='pierre-yves.david@ens-lyon.org',
     url='https://www.mercurial-scm.org/doc/evolution/',
     description='Flexible evolution of Mercurial history.',
-    long_description=open(join(dirname(__file__), 'README')).read(),
+    long_description=open(join(dirname(__file__), 'README.rst')).read(),
     keywords='hg mercurial',
     license='GPLv2+',
     py_modules=py_modules,
--- a/tests/hghaveaddon.py	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/hghaveaddon.py	Tue Aug 25 10:12:29 2020 +0200
@@ -23,3 +23,8 @@
         return False
     else:
         return True
+
+@hghave.check("check-manifest", "check-manifest MANIFEST.in checking tool")
+def has_check_manifest():
+    return hghave.matchoutput('check-manifest --version 2>&1',
+                              br'check-manifest version')
--- a/tests/test-amend-patch.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-amend-patch.t	Tue Aug 25 10:12:29 2020 +0200
@@ -256,13 +256,9 @@
   @@ -0,0 +1,1 @@
   +Gello
 
-  $ hg status
+  $ hg status -A
   ? editor.sh
-
-  $ ls -A
-  .hg
-  changedfile
-  editor.sh
+  C changedfile
 
 Handling both deletions and additions
 -------------------------------------
--- a/tests/test-check-compat-strings.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-check-compat-strings.t	Tue Aug 25 10:12:29 2020 +0200
@@ -5,4 +5,5 @@
   > evolution = all
   > EOF
 
-  $ $TESTDIR/testlib/check-compat-strings.sh "$TESTDIR/../hgext3rd/" "$RUNTESTDIR/.."
+  $ "$PYTHON" "$TESTDIR/testlib/check-compat-strings.py" \
+  > "$TESTDIR/../hgext3rd/" "$RUNTESTDIR/.."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-debian.t	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,12 @@
+Enable obsolescence to avoid the warning issue when obsmarkers are found
+
+  $ cat << EOF >> $HGRCPATH
+  > [experimental]
+  > evolution = all
+  > EOF
+
+  $ "$PYTHON" "$TESTDIR/testlib/check-min-versions.py" \
+  > "$TESTDIR/../hgext3rd/evolve/metadata.py" "$TESTDIR/../debian/control"
+
+  $ "$PYTHON" "$TESTDIR/testlib/check-min-versions.py" \
+  > "$TESTDIR/../hgext3rd/topic/__init__.py" "$TESTDIR/../debian/control"
--- a/tests/test-check-setup-manifest.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-check-setup-manifest.t	Tue Aug 25 10:12:29 2020 +0200
@@ -1,12 +1,5 @@
-#require test-repo
+#require test-repo check-manifest
 
-  $ checkcm() {
-  >   if ! (which check-manifest > /dev/null); then
-  >     echo skipped: missing tool: check-manifest;
-  >     exit 80;
-  >   fi;
-  > };
-  $ checkcm
   $ cat << EOF >> $HGRCPATH
   > [experimental]
   > evolution=all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-content-divergent-case-A1.t	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,106 @@
+===============================================
+Testing content-divergence resolution: Case A.1
+===============================================
+
+Independent rewrites of same changeset can lead to content-divergence. In most
+common cases, it can occur when multiple users rewrite the same changeset
+independently and push it.
+
+This test belongs to a series of tests checking the resolution of content-divergent
+changesets.
+
+Category A: no parents are obsolete
+Testcase 1: no conflict: independent metadata changes only
+
+A.1 No content changes on any side
+==================================
+
+.. (Divergence reason):
+..    local: changed the description of changeset
+..    other: changed the date of changeset
+.. Where we show that since there is no content change in divergent changesets
+.. we should only merge the metadata of changesets in a 3-way merge
+..
+.. (local):
+..
+..   A ø⇠○ A'
+..     |/
+..     ● O
+..
+.. (other):
+..
+..   A ø⇠○ A''
+..     |/
+..     ● O
+..
+.. (Resolution):
+..
+..     ○ A'''
+..     |
+..     ● O
+..
+
+Setup
+-----
+  $ . $TESTDIR/testlib/content-divergence-util.sh
+  $ setuprepos A.1
+  creating test repo for test case A.1
+  - upstream
+  - local
+  - other
+  cd into `local` and proceed with env setup
+
+initial
+
+  $ cd local
+  $ mkcommit A0
+  $ hg push -q
+  $ hg amend -m "A1"
+  $ hg log -G --hidden
+  @  2:0d8c87cec5fc (draft): A1
+  |
+  | x  1:28b51eb45704 (draft): A0
+  |/
+  o  0:a9bdc8b26820 (public): O
+  
+  $ cd ../other
+  $ hg pull -uq
+  $ hg amend -d '0 1'
+  $ hg push -q
+
+  $ cd ../local
+  $ hg pull -q
+  2 new content-divergent changesets
+
+Actual test of resolution
+-------------------------
+  $ hg evolve -l
+  0d8c87cec5fc: A1
+    content-divergent: ece7459c388a (draft) (precursor 28b51eb45704)
+  
+  ece7459c388a: A0
+    content-divergent: 0d8c87cec5fc (draft) (precursor 28b51eb45704)
+  
+  $ hg log -G --hidden
+  *  3:ece7459c388a (draft): A0 [content-divergent]
+  |
+  | @  2:0d8c87cec5fc (draft): A1 [content-divergent]
+  |/
+  | x  1:28b51eb45704 (draft): A0
+  |/
+  o  0:a9bdc8b26820 (public): O
+  
+
+  $ hg evolve --content-divergent
+  merge:[2] A1
+  with: [3] A0
+  base: [1] A0
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 36c6c1f70ad9
+
+  $ hg log -G
+  @  4:36c6c1f70ad9 (draft): A1
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve -l
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-content-divergent-case-A2.t	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,121 @@
+===============================================
+Testing content-divergence resolution: Case A.2
+===============================================
+
+Independent rewrites of same changeset can lead to content-divergence. In most
+common cases, it can occur when multiple users rewrite the same changeset
+independently and push it.
+
+This test belongs to a series of tests checking the resolution of content-divergent
+changesets.
+
+Category A: no parents are obsolete
+Testcase 2: no conflict: both sides amended content changes (non-conflicting changes in same file)
+
+A.2 both sides amended content changes
+======================================
+
+.. (Divergence reason):
+..    local: amended some content changes in same file as "other" but non-conflicting
+..    other: amended some content changes in same file as "local" but non-conflicting
+.. Where we show that since both side amended some content changes without any relocation,
+.. the most reasonable behaviour is to simply perform 3-way merge.
+..
+.. (local):
+..
+..   A ø⇠○ A'
+..     |/
+..     ● O
+..
+.. (other):
+..
+..   A ø⇠○ A''
+..     |/
+..     ● O
+..
+.. (Resolution):
+..
+..     ○ A'''
+..     |
+..     ● O
+..
+
+Setup
+-----
+  $ . $TESTDIR/testlib/content-divergence-util.sh
+  $ setuprepos A.2
+  creating test repo for test case A.2
+  - upstream
+  - local
+  - other
+  cd into `local` and proceed with env setup
+
+initial
+
+  $ cd local
+  $ mkcommit A0
+  $ hg push -q
+  $ sed -i '1ifoo' A0
+  $ hg amend -m "A1"
+  $ hg log -G --hidden
+  @  2:e1f7c24563ba (draft): A1
+  |
+  | x  1:28b51eb45704 (draft): A0
+  |/
+  o  0:a9bdc8b26820 (public): O
+  
+  $ cd ../other
+  $ hg pull -uq
+  $ echo bar >> A0
+  $ hg amend
+  $ hg push -q
+
+  $ cd ../local
+  $ hg pull -q
+  2 new content-divergent changesets
+
+Actual test of resolution
+-------------------------
+  $ hg evolve -l
+  e1f7c24563ba: A1
+    content-divergent: 5fbe90f37421 (draft) (precursor 28b51eb45704)
+  
+  5fbe90f37421: A0
+    content-divergent: e1f7c24563ba (draft) (precursor 28b51eb45704)
+  
+  $ hg log -G --hidden
+  *  3:5fbe90f37421 (draft): A0 [content-divergent]
+  |
+  | @  2:e1f7c24563ba (draft): A1 [content-divergent]
+  |/
+  | x  1:28b51eb45704 (draft): A0
+  |/
+  o  0:a9bdc8b26820 (public): O
+  
+
+  $ hg evolve --content-divergent
+  merge:[2] A1
+  with: [3] A0
+  base: [1] A0
+  merging A0
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 414367a5568a
+
+  $ hg log -Gp
+  @  4:414367a5568a (draft): A1
+  |  diff -r a9bdc8b26820 -r 414367a5568a A0
+  |  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  |  +++ b/A0	Thu Jan 01 00:00:00 1970 +0000
+  |  @@ -0,0 +1,3 @@
+  |  +foo
+  |  +A0
+  |  +bar
+  |
+  o  0:a9bdc8b26820 (public): O
+     diff -r 000000000000 -r a9bdc8b26820 O
+     --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+     +++ b/O	Thu Jan 01 00:00:00 1970 +0000
+     @@ -0,0 +1,1 @@
+     +O
+  
+  $ hg evolve -l
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-content-divergent-case-A3.t	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,201 @@
+===============================================
+Testing content-divergence resolution: Case A.3
+===============================================
+
+Independent rewrites of same changeset can lead to content-divergence. In most
+common cases, it can occur when multiple users rewrite the same changeset
+independently and push it.
+
+This test belongs to a series of tests checking the resolution of content-divergent
+changesets.
+
+Category A: no parents are obsolete
+Testcase 3: one side relocated forward and other amended content changes
+Variants:
+# a: "local" is rebased forward
+# b: "other" is rebased forward
+
+A.3 Relocated forward; other side amended content changes
+=========================================================
+
+.. (Divergence reason):
+..    local: relocated the changeset forward in the graph
+..    other: amended some content changes
+.. Where we show that since one side amended some changes and other just relocated,
+.. the most reasonable behaviour is to relocate the amended one to the same parent as
+.. relocated one and perform 3-way merge.
+..
+.. (local):
+..
+..    C ø⇠○ C'
+..      | |
+..      | ○ B
+..      \ |
+..        ○ A
+..        |
+..        ● O
+..
+.. (other):
+..
+..      C ø⇠○ C''
+..        | |
+..   B ○  | |
+..      \ | /
+..      A ○
+..        |
+..        ● O
+..
+.. (Resolution):
+..
+..     ○ C'''
+..     |
+..     ○ B
+..     |
+..     ○ A
+..     |
+..     ● O
+..
+
+Setup
+-----
+  $ . $TESTDIR/testlib/content-divergence-util.sh
+  $ setuprepos A.3
+  creating test repo for test case A.3
+  - upstream
+  - local
+  - other
+  cd into `local` and proceed with env setup
+
+initial
+
+  $ cd upstream
+  $ mkcommit A
+  $ mkcommit B
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [1] A
+  $ mkcommit C
+  created new head
+  $ cd ../local
+  $ hg pull -qu
+  $ hg rebase -r 'desc(C)' -d 'desc(B)'
+  rebasing 3:928c8849ec01 "C" (tip)
+
+  $ cd ../other
+  $ hg pull -qu
+  $ echo newC > C
+  $ hg amend
+  $ hg push -q
+
+  $ cd ../local
+  $ hg push -fq
+  2 new content-divergent changesets
+  $ hg pull -q
+  2 new content-divergent changesets
+
+Actual test of resolution
+-------------------------
+
+Variant_a: when "local" is rebased forward
+------------------------------------------
+  $ hg evolve -l
+  384129981c4b: C
+    content-divergent: 710d96992b40 (draft) (precursor 928c8849ec01)
+  
+  710d96992b40: C
+    content-divergent: 384129981c4b (draft) (precursor 928c8849ec01)
+  
+  $ hg log -G --hidden
+  *  5:710d96992b40 (draft): C [content-divergent]
+  |
+  | @  4:384129981c4b (draft): C [content-divergent]
+  | |
+  +---x  3:928c8849ec01 (draft): C
+  | |
+  | o  2:f6fbb35d8ac9 (draft): B
+  |/
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+
+  $ hg evolve --content-divergent
+  merge:[4] C
+  with: [5] C
+  base: [3] C
+  rebasing "other" content-divergent changeset 710d96992b40 on f6fbb35d8ac9
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 3ad062d48137
+
+  $ hg log -G
+  @  7:3ad062d48137 (draft): C
+  |
+  o  2:f6fbb35d8ac9 (draft): B
+  |
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg log -pl 1
+  7:3ad062d48137 (draft): C 
+  diff -r f6fbb35d8ac9 -r 3ad062d48137 C
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/C	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +newC
+  
+  $ hg evolve -l
+
+Variant_b: when "other" is rebased forward
+------------------------------------------
+
+  $ cd ../other
+  $ hg pull -q
+  2 new content-divergent changesets
+  $ hg evolve -l
+  710d96992b40: C
+    content-divergent: 384129981c4b (draft) (precursor 928c8849ec01)
+  
+  384129981c4b: C
+    content-divergent: 710d96992b40 (draft) (precursor 928c8849ec01)
+  
+  $ hg log -G --hidden
+  *  5:384129981c4b (draft): C [content-divergent]
+  |
+  | @  4:710d96992b40 (draft): C [content-divergent]
+  | |
+  | | x  3:928c8849ec01 (draft): C
+  | |/
+  o |  2:f6fbb35d8ac9 (draft): B
+  |/
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve --content-divergent
+  merge:[5] C
+  with: [4] C
+  base: [3] C
+  rebasing "other" content-divergent changeset 710d96992b40 on f6fbb35d8ac9
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 3ad062d48137
+
+  $ hg log -G
+  @  7:3ad062d48137 (draft): C
+  |
+  o  2:f6fbb35d8ac9 (draft): B
+  |
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve -l
+
+  $ hg log -pl1
+  7:3ad062d48137 (draft): C 
+  diff -r f6fbb35d8ac9 -r 3ad062d48137 C
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/C	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +newC
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-content-divergent-case-A4.t	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,197 @@
+===============================================
+Testing content-divergence resolution: Case A.4
+===============================================
+
+Independent rewrites of same changeset can lead to content-divergence. In most
+common cases, it can occur when multiple users rewrite the same changeset
+independently and push it.
+
+This test belongs to a series of tests checking the resolution of content-divergent
+changesets.
+
+Category A: no parents are obsolete
+Testcase 4: both sides relocated forward to different location but aligned
+Variants:
+# a: local is ahead of other
+# b: other is ahead of local
+
+A.4 Both relocated forward
+==========================
+
+.. (Divergence reason):
+..    local: relocated the changeset forward in the graph but on the same topo branch
+..    other: relocated the changeset forward in the graph but on the same topo branch
+.. Where we show that since both changesets relocated forward in the graph,
+.. we can assume the reason for ahead one being ahead is that it's rebased on the latest changes and
+.. its parent should be the resolution parent. So, we will relocate the other changeset to resolution
+.. parent and perform 3-way merge.
+..
+.. (local):
+..
+..    D ø⇠○ D'
+..      | |
+..      | ○ C
+..      | |
+..      | ○ B
+..      \ |
+..        ○ A
+..        |
+..        ● O
+..
+.. (other):
+..
+..    D ø⇠○ D''
+..      | |  ○ C
+..      | | /
+..      | ○ B
+..      \ |
+..        ○ A
+..        |
+..        ● O
+..
+.. (Resolution):
+..     ○ D'''
+..     |
+..     ○ C
+..     |
+..     ○ B
+..     |
+..     ○ A
+..     |
+..     ● O
+..
+
+Setup
+-----
+  $ . $TESTDIR/testlib/content-divergence-util.sh
+  $ setuprepos A.4
+  creating test repo for test case A.4
+  - upstream
+  - local
+  - other
+  cd into `local` and proceed with env setup
+
+initial
+
+  $ cd upstream
+  $ mkcommit A
+  $ mkcommit B
+  $ mkcommit C
+  $ hg co -r 'desc(A)'
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ mkcommit D
+  created new head
+  $ cd ../local
+  $ hg pull -qu
+  $ hg rebase -r 'desc(D)' -d 'desc(C)'
+  rebasing 4:6a418860e87b "D" (tip)
+
+  $ cd ../other
+  $ hg pull -qu
+  $ hg rebase -r 'desc(D)' -d 'desc(B)'
+  rebasing 4:6a418860e87b "D" (tip)
+  $ hg push -q
+
+  $ cd ../local
+  $ hg push -q
+  2 new content-divergent changesets
+  $ hg pull -q
+  2 new content-divergent changesets
+
+Actual test of resolution
+-------------------------
+
+Variant_a: when local is ahead of other
+---------------------------------------
+
+  $ hg evolve -l
+  d203ddccc9cc: D
+    content-divergent: 5d3fd66cb347 (draft) (precursor 6a418860e87b)
+  
+  5d3fd66cb347: D
+    content-divergent: d203ddccc9cc (draft) (precursor 6a418860e87b)
+  
+  $ hg log -G --hidden
+  *  6:5d3fd66cb347 (draft): D [content-divergent]
+  |
+  | @  5:d203ddccc9cc (draft): D [content-divergent]
+  | |
+  | | x  4:6a418860e87b (draft): D
+  | | |
+  | o |  3:d90aa47aa5d3 (draft): C
+  |/ /
+  o /  2:f6fbb35d8ac9 (draft): B
+  |/
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve --content-divergent
+  merge:[5] D
+  with: [6] D
+  base: [4] D
+  rebasing "other" content-divergent changeset 5d3fd66cb347 on d90aa47aa5d3
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 898f6bbfb21e
+
+  $ hg log -G
+  @  8:898f6bbfb21e (draft): D
+  |
+  o  3:d90aa47aa5d3 (draft): C
+  |
+  o  2:f6fbb35d8ac9 (draft): B
+  |
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve -l
+
+Variant_b: when other is ahead of local
+---------------------------------------
+
+  $ cd ../other
+  $ hg pull -q
+  2 new content-divergent changesets
+
+  $ hg evolve -l
+  5d3fd66cb347: D
+    content-divergent: d203ddccc9cc (draft) (precursor 6a418860e87b)
+  
+  d203ddccc9cc: D
+    content-divergent: 5d3fd66cb347 (draft) (precursor 6a418860e87b)
+  
+  $ hg log -G --hidden
+  *  6:d203ddccc9cc (draft): D [content-divergent]
+  |
+  | @  5:5d3fd66cb347 (draft): D [content-divergent]
+  | |
+  | | x  4:6a418860e87b (draft): D
+  | | |
+  o | |  3:d90aa47aa5d3 (draft): C
+  |/ /
+  o /  2:f6fbb35d8ac9 (draft): B
+  |/
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve --content-divergent
+  merge:[6] D
+  with: [5] D
+  base: [4] D
+  rebasing "other" content-divergent changeset 5d3fd66cb347 on d90aa47aa5d3
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 898f6bbfb21e
+
+  $ hg log -G
+  @  8:898f6bbfb21e (draft): D
+  |
+  o  3:d90aa47aa5d3 (draft): C
+  |
+  o  2:f6fbb35d8ac9 (draft): B
+  |
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
--- a/tests/test-evolve-issue5958.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve-issue5958.t	Tue Aug 25 10:12:29 2020 +0200
@@ -86,9 +86,49 @@
   x  cc71ffbc7c00 (1) add foo.txt
   
   $ hg evolve --content-divergent
-  merge:[6] add foo.txt
-  with: [4] add foo.txt
+  merge:[4] add foo.txt
+  with: [6] add foo.txt
   base: [1] add foo.txt
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  1 new orphan changesets
-  working directory is now at 5899f25049de
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 17e66b2fbf1b
+
+  $ hg log -G
+  @  changeset:   7:17e66b2fbf1b
+  |  tag:         tip
+  |  parent:      0:a24ed8ad918c
+  |  user:        test
+  |  date:        Wed Dec 31 23:59:58 1969 -0000
+  |  summary:     add foo.txt
+  |
+  o  changeset:   0:a24ed8ad918c
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add r0
+  
+test that resolution is consistent and independent of divergent changeset we initiate the resolution from
+
+  $ hg strip . --config extensions.strip=
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/issue5958/.hg/strip-backup/17e66b2fbf1b-080f2943-backup.hg
+  2 new content-divergent changesets
+  $ hg up 4
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg evolve --content-divergent
+  merge:[4] add foo.txt
+  with: [6] add foo.txt
+  base: [1] add foo.txt
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 17e66b2fbf1b
+  $ hg log -G
+  @  changeset:   7:17e66b2fbf1b
+  |  tag:         tip
+  |  parent:      0:a24ed8ad918c
+  |  user:        test
+  |  date:        Wed Dec 31 23:59:58 1969 -0000
+  |  summary:     add foo.txt
+  |
+  o  changeset:   0:a24ed8ad918c
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add r0
+  
--- a/tests/test-evolve-obshistory-amend.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve-obshistory-amend.t	Tue Aug 25 10:12:29 2020 +0200
@@ -5,6 +5,12 @@
 
   $ . $TESTDIR/testlib/obshistory_setup.sh
 
+  $ cat >> $HGRCPATH << EOF
+  > [templates]
+  > logdates = '{ifeq(min(dates), max(dates), "at {max(dates)|hgdate}", "between {min(dates)|hgdate} and {max(dates)|hgdate}")}'
+  > logmarkers = '{if(markers, join(markers % "{logdates} by {users} "))}'
+  > EOF
+
 Test output on amended commit
 =============================
 
@@ -127,24 +133,24 @@
   x  471f378eab4c A0
   
 
-  $ hg obslog 4ae3a4151de9 --graph --no-origin -T'{label("log.summary", desc|firstline)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}'
+  $ hg obslog 4ae3a4151de9 --graph --no-origin -T '{desc|firstline} {logmarkers}'
   @  A1
   |
   x  A0 at 0 0 by test
   
-  $ hg obslog 4ae3a4151de9 --graph -T'{label("log.summary", desc|firstline)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}'
+  $ hg obslog 4ae3a4151de9 --graph -T '{desc|firstline} {logmarkers}'
   @  A1 at 0 0 by test
   |
   x  A0
   
 
 Check that the same thing works with the old {shortdescription} form
-  $ hg obslog 4ae3a4151de9 --graph --no-origin -T'{label("log.summary", shortdescription)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}'
+  $ hg obslog 4ae3a4151de9 --graph --no-origin -T '{shortdescription} {logmarkers}'
   @  A1
   |
   x  A0 at 0 0 by test
   
-  $ hg obslog 4ae3a4151de9 --graph -T'{label("log.summary", shortdescription)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}'
+  $ hg obslog 4ae3a4151de9 --graph -T '{shortdescription} {logmarkers}'
   @  A1 at 0 0 by test
   |
   x  A0
--- a/tests/test-evolve-order.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve-order.t	Tue Aug 25 10:12:29 2020 +0200
@@ -199,7 +199,7 @@
   $ echo "(desc(_d)::) - desc(c3_)"
   (desc(_d)::) - desc(c3_)
   $ hg evolve --rev "(desc(_d)::) - desc(c3_)"
-  cannot solve instability of d096a2437fd0, skipping
+  skipping d096a2437fd0, consider including orphan ancestors
   move:[20] add c2prime
   atop:[21] add c1second
   move:[25] add b4_
--- a/tests/test-evolve-progress.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve-progress.t	Tue Aug 25 10:12:29 2020 +0200
@@ -33,7 +33,7 @@
    ancestor: a87874c6ec31, local: fd0a2402f834+, remote: 4f60c78b6d58
    a: remote is newer -> g
   getting a
-  updating: a 1/2 files (50.00%)
+  updating: a 1/1 files (100.00%)
   committing files:
   a
   committing manifest
@@ -61,7 +61,7 @@
    ancestor: 769574b07a96, local: 53c0008d98a0+, remote: 22782fddc0ab
    b: remote is newer -> g
   getting b
-  updating: b 1/2 files (50.00%)
+  updating: b 1/1 files (100.00%)
   committing files:
   b
   committing manifest
@@ -94,7 +94,7 @@
    ancestor: fd0a2402f834, local: 152c368c622b+, remote: 5f16d91ecde0
    a: remote is newer -> g
   getting a
-  updating: a 1/2 files (50.00%)
+  updating: a 1/1 files (100.00%)
   committing files:
   a
   committing manifest
@@ -170,7 +170,7 @@
    ancestor: 53c0008d98a0, local: b2de95304e32+, remote: 385376d04062
    b: remote is newer -> g
   getting b
-  updating: b 1/2 files (50.00%)
+  updating: b 1/1 files (100.00%)
   committing files:
   b
   committing manifest
--- a/tests/test-evolve-public-content-divergent-corner-cases.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve-public-content-divergent-corner-cases.t	Tue Aug 25 10:12:29 2020 +0200
@@ -660,7 +660,7 @@
 
   $ hg evolve --list
 
-  $ hg log -G --patch
+  $ hg log -G --patch -l2
   @  changeset:   8:a52ac76b45f5
   |  tag:         tip
   |  user:        test
@@ -675,70 +675,15 @@
   |
   o  changeset:   7:c7d2d47c7240
   |  parent:      2:eb1b4e1205b8
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     c_E
-  |
-  |  diff -r eb1b4e1205b8 -r c7d2d47c7240 c_E
-  |  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  |  +++ b/c_E	Thu Jan 01 00:00:00 1970 +0000
-  |  @@ -0,0 +1,1 @@
-  |  +c_E
-  |
-  | o  changeset:   4:dbb960d6c97c
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     c_D
-  | |
-  | |  diff -r abb77b893f28 -r dbb960d6c97c c_D
-  | |  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  | |  +++ b/c_D	Thu Jan 01 00:00:00 1970 +0000
-  | |  @@ -0,0 +1,1 @@
-  | |  +c_D
-  | |
-  | o  changeset:   3:abb77b893f28
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     c_C
-  |
-  |    diff -r eb1b4e1205b8 -r abb77b893f28 c_C
-  |    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  |    +++ b/c_C	Thu Jan 01 00:00:00 1970 +0000
-  |    @@ -0,0 +1,1 @@
-  |    +c_C
-  |
-  o  changeset:   2:eb1b4e1205b8
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     c_B
-  |
-  |  diff -r e31751786014 -r eb1b4e1205b8 c_B
-  |  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  |  +++ b/c_B	Thu Jan 01 00:00:00 1970 +0000
-  |  @@ -0,0 +1,1 @@
-  |  +c_B
-  |
-  o  changeset:   1:e31751786014
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     c_A
-  |
-  |  diff -r 1e4be0697311 -r e31751786014 c_A
-  |  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  |  +++ b/c_A	Thu Jan 01 00:00:00 1970 +0000
-  |  @@ -0,0 +1,1 @@
-  |  +c_A
-  |
-  o  changeset:   0:1e4be0697311
-     user:        test
+  ~  user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     root
+     summary:     c_E
   
-     diff -r 000000000000 -r 1e4be0697311 root
+     diff -r eb1b4e1205b8 -r c7d2d47c7240 c_E
      --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-     +++ b/root	Thu Jan 01 00:00:00 1970 +0000
+     +++ b/c_E	Thu Jan 01 00:00:00 1970 +0000
      @@ -0,0 +1,1 @@
-     +root
+     +c_E
   
   $ hg export tip
   # HG changeset patch
--- a/tests/test-evolve-serveronly-bundle2.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve-serveronly-bundle2.t	Tue Aug 25 10:12:29 2020 +0200
@@ -85,12 +85,12 @@
 Capacity testing
 ===================
 
-  $ curl -s http://localhost:$HGPORT/?cmd=hello
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=hello
   capabilities: _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (glob)
-  $ curl -s http://localhost:$HGPORT/?cmd=capabilities
-  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (no-eol) (glob)
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=capabilities
+  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (glob)
 
-  $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ hg debugpushkey http://localhost:$HGPORT namespaces
   bookmarks	
   namespaces	
   obsolete	
@@ -145,15 +145,15 @@
 ===========================================
 (used by bitbucket to select which repo use evolve)
 
-  $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ hg debugpushkey http://localhost:$HGPORT namespaces
   bookmarks	
   namespaces	
   obsolete	
   phases	
-  $ curl -s http://localhost:$HGPORT/?cmd=hello
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=hello
   capabilities: _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (glob)
-  $ curl -s http://localhost:$HGPORT/?cmd=capabilities
-  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (no-eol) (glob)
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=capabilities
+  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (glob)
 
   $ echo '[experimental]' >> server/.hg/hgrc
   $ echo 'evolution=!' >> server/.hg/hgrc
@@ -161,7 +161,7 @@
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ hg debugpushkey http://localhost:$HGPORT namespaces
   bookmarks	
   namespaces	
   phases	
@@ -171,16 +171,16 @@
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ hg debugpushkey http://localhost:$HGPORT namespaces
   bookmarks	
   namespaces	
   obsolete	
   phases	
 
-  $ curl -s http://localhost:$HGPORT/?cmd=hello
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=hello
   capabilities: _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (glob)
-  $ curl -s http://localhost:$HGPORT/?cmd=capabilities
-  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (no-eol) (glob)
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=capabilities
+  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch * (glob)
 
 Test obshashrange discover
 ===========================================
--- a/tests/test-evolve.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-evolve.t	Tue Aug 25 10:12:29 2020 +0200
@@ -1231,7 +1231,7 @@
   abort: cannot specify both "--rev" and "--any"
   [255]
   $ hg evolve --rev 89e4f7e8feb5
-  cannot solve instability of 89e4f7e8feb5, skipping
+  skipping 89e4f7e8feb5, consider including orphan ancestors
 
 Check that uncommit respects the allowunstable option
 With only createmarkers we can only uncommit on a head
--- a/tests/test-import.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-import.t	Tue Aug 25 10:12:29 2020 +0200
@@ -1,9 +1,3 @@
-
-This feature requires mercurial 3.0
-(and the `only()` revset is 3.0 specific)
-
-  $ (hg help revset | grep '"only(' > /dev/null) || exit 80
-
 Test creation of obsolescence marker by path import
 
   $ hg init auto-obsolete
--- a/tests/test-issue-6028.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-issue-6028.t	Tue Aug 25 10:12:29 2020 +0200
@@ -165,6 +165,27 @@
 
 evolve creates an obsolete changeset above as 11
 
+  $ hg evolve -l
+  61eff7f7bb6c: aa
+    orphan: 487c225d33af (obsolete parent)
+  
+  2b5a7b3298d9: merged bb
+    orphan: 3f6f25057afb (obsolete parent)
+  
+  c920dd828523: merged aa
+    orphan: 61eff7f7bb6c (orphan parent)
+  
   $ hg evolve -r .
-  cannot solve instability of c920dd828523, skipping
-  cannot solve instability of c920dd828523, skipping
+  skipping c920dd828523, consider including orphan ancestors
+
+test that the suggestion works
+
+  $ hg evolve -r 'parents(".")::'
+  move:[7] aa
+  atop:[1] a
+  switching to topic a
+  move:[11] merged aa
+  move:[10] merged bb
+  working directory is now at bdbfa73836d6
+
+  $ hg evolve -l
--- a/tests/test-minitopic.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-minitopic.t	Tue Aug 25 10:12:29 2020 +0200
@@ -45,9 +45,9 @@
   $ hg serve -R server-disabled -p $HGPORT2 -d --pid-file hg2.pid --errorlog hg2.error
   $ cat hg2.pid >> $DAEMON_PIDS
 
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities | grep -o topics
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=capabilities | grep -o topics
   topics
-  $ curl --silent http://localhost:$HGPORT2/?cmd=capabilities | grep -o topics
+  $ hg debugdownload http://localhost:$HGPORT2/?cmd=capabilities | grep -o topics
   [1]
 
 Pushing first changesets to the servers
@@ -95,9 +95,9 @@
   (merge or see 'hg help push' for details about pushing new heads)
   [255]
 
-  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=branchmap | sort
   default 0ab6d544d0efd629fda056601cfe95e73d1af210
-  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  $ hg debugdownload http://localhost:$HGPORT2/?cmd=branchmap | sort
   default 0ab6d544d0efd629fda056601cfe95e73d1af210
   $ cat $TESTTMP/hg1.error
   $ cat $TESTTMP/hg2.error
@@ -162,10 +162,10 @@
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files (+1 heads)
 
-  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=branchmap | sort
   default 9c660cf97499ae01ccb6894880455c6ffa4b19cf
   default%3Atopic_A f31af349535e413b6023f11b51a6afccf4139180
-  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  $ hg debugdownload http://localhost:$HGPORT2/?cmd=branchmap | sort
   default 9c660cf97499ae01ccb6894880455c6ffa4b19cf f31af349535e413b6023f11b51a6afccf4139180
   $ cat $TESTTMP/hg1.error
   $ cat $TESTTMP/hg2.error
@@ -231,10 +231,10 @@
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
 
-  $ curl --silent http://localhost:$HGPORT/?cmd=branchmap | sort
+  $ hg debugdownload http://localhost:$HGPORT/?cmd=branchmap | sort
   default 9c660cf97499ae01ccb6894880455c6ffa4b19cf
   default%3Atopic_A f31af349535e413b6023f11b51a6afccf4139180 82c5842e047215160763f81ae93ae42c65b20a63
-  $ curl --silent http://localhost:$HGPORT2/?cmd=branchmap | sort
+  $ hg debugdownload http://localhost:$HGPORT2/?cmd=branchmap | sort
   default f31af349535e413b6023f11b51a6afccf4139180 82c5842e047215160763f81ae93ae42c65b20a63
   $ cat $TESTTMP/hg1.error
   $ cat $TESTTMP/hg2.error
--- a/tests/test-rewind.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-rewind.t	Tue Aug 25 10:12:29 2020 +0200
@@ -1,14 +1,11 @@
-This test file test the rewind command in several situations.
+  $ . $TESTDIR/testlib/common.sh
 
 Global setup
 ============
 
-  $ . $TESTDIR/testlib/common.sh
-  $ cat >> $HGRCPATH <<EOF
-  > [ui]
-  > interactive = true
+  $ cat >> $HGRCPATH << EOF
   > [phases]
-  > publish=False
+  > publish = false
   > [alias]
   > glf = log -GT "{rev}: {desc} ({files})"
   > [extensions]
@@ -18,14 +15,12 @@
   $ hg init rewind-testing-base
   $ cd rewind-testing-base
   $ echo a > root
-  $ hg add root
-  $ hg ci -m 'c_ROOT'
+  $ hg ci -qAm 'c_ROOT'
   $ echo a > A
-  $ hg add A
-  $ hg ci -m 'c_A0'
+  $ hg ci -qAm 'c_A0'
   $ echo a > B
-  $ hg add B
-  $ hg ci -m 'c_B0'
+  $ hg ci -qAm 'c_B0'
+
   $ hg log -G
   @  changeset:   2:7e594302a05d
   |  tag:         tip
@@ -49,10 +44,10 @@
 Test rewinding to single changesets
 ====================================
 
-  $ hg clone rewind-testing-base rewind-testing-simple-prune
+  $ hg clone rewind-testing-base rewind-testing-single-prunes
   updating to branch default
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cd rewind-testing-simple-prune
+  $ cd rewind-testing-single-prunes
 
 Prune changeset unrelated to the working copy
 ---------------------------------------------
@@ -86,7 +81,7 @@
 `````````````
 
   $ hg rewind --hidden --to 'desc("c_B0")'
-  rewinded to 1 changesets
+  rewound to 1 changesets
   $ hg debugobsolete
   7e594302a05d3769b27be88fc3cdfd39d7498498 0 {579f120ba91885449adc92eedf48ef3569742cee} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'prune', 'user': 'test'}
   7e594302a05d3769b27be88fc3cdfd39d7498498 073989a581cf430a844192364fa37606357cbbc2 4 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '2', 'operation': 'rewind', 'user': 'test'}
@@ -166,7 +161,7 @@
   $ hg prune 'desc("c_B0")'
   1 changesets pruned
   $ hg rewind --hidden --to 'min(desc("c_B0"))'
-  rewinded to 1 changesets
+  rewound to 1 changesets
   $ hg debugobsolete
   7e594302a05d3769b27be88fc3cdfd39d7498498 0 {579f120ba91885449adc92eedf48ef3569742cee} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'prune', 'user': 'test'}
   7e594302a05d3769b27be88fc3cdfd39d7498498 073989a581cf430a844192364fa37606357cbbc2 4 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '2', 'operation': 'rewind', 'user': 'test'}
@@ -246,16 +241,17 @@
   
   $ cd ..
 
+  $ hg clone rewind-testing-base rewind-testing-single-amends
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd rewind-testing-single-amends
+
 rewind a simple amend - creating content-divergence
 ---------------------------------------------------
 
 Setup
 `````
 
-  $ hg clone rewind-testing-base rewind-testing-single-rewrite
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cd rewind-testing-single-rewrite
   $ echo BB > B
   $ hg amend -m 'c_B1'
   $ hg log -G
@@ -282,7 +278,7 @@
 
   $ hg rewind --hidden --to 'desc("c_B0")' --as-divergence
   2 new content-divergent changesets
-  rewinded to 1 changesets
+  rewound to 1 changesets
   $ hg debugobsolete
   7e594302a05d3769b27be88fc3cdfd39d7498498 25c8f5ab0c3bb569ec672570f1a901be4c6f032b 0 (Thu Jan 01 00:00:01 1970 +0000) {'ef1': '9', 'operation': 'amend', 'user': 'test'}
   7e594302a05d3769b27be88fc3cdfd39d7498498 b13b032065ef57a68d9a4cead38ba0f34f95529b 4 (Thu Jan 01 00:00:01 1970 +0000) {'ef1': '2', 'operation': 'rewind', 'user': 'test'}
@@ -381,13 +377,13 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     c_ROOT
   
-  $ echo 'default-date = 2 0' >> $HGRCPATH
 
 rewind a simple amend - obsoleting the current latest successors
 ----------------------------------------------------------------
 
+  $ echo 'default-date = 2 0' >> $HGRCPATH
   $ hg rewind --hidden --to 'min(desc("c_B0"))'
-  rewinded to 1 changesets
+  rewound to 1 changesets
   (1 changesets obsoleted)
   working directory is now at 837cd997bb05
   $ hg debugobsolete
@@ -477,22 +473,22 @@
   
   $ cd ..
 
+  $ hg clone rewind-testing-base rewind-testing-single-split-fold
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd rewind-testing-single-split-fold
+
 rewind a simple split
 ---------------------
 
 Setup
 `````
 
-  $ hg clone rewind-testing-base rewind-testing-split-fold
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cd rewind-testing-split-fold
-
   $ echo C > C
   $ echo D > D
   $ hg add C D
   $ hg ci -m 'c_CD0'
-  $ hg split << EOF
+  $ hg split --config ui.interactive=yes << EOF
   > y
   > f
   > d
@@ -570,7 +566,7 @@
 `````````````
 
   $ hg rewind --hidden --to 'min(desc("c_CD0"))'
-  rewinded to 1 changesets
+  rewound to 1 changesets
   (2 changesets obsoleted)
   working directory is now at 4535d0af405c
   $ hg debugobsolete
@@ -645,7 +641,7 @@
 `````````````
 
   $ hg rewind --to '9576e80d6851+a0316c4c5417' --hidden
-  rewinded to 2 changesets
+  rewound to 2 changesets
   (1 changesets obsoleted)
   working directory is now at 95d72d892df7
   $ hg debugobsolete
@@ -725,7 +721,7 @@
   $ echo AA >> A
   $ hg amend -m 'c_A1'
   1 new orphan changesets
-  $ hg evolve --all --update
+  $ hg evolve --update
   move:[2] c_B0
   atop:[3] c_A1
   working directory is now at a65fceb2324a
@@ -778,7 +774,7 @@
 
   $ hg rewind --hidden --to 'min(desc(c_B0))' --exact
   1 new orphan changesets
-  rewinded to 1 changesets
+  rewound to 1 changesets
   (1 changesets obsoleted)
   working directory is now at ac979e0aac4e
   $ hg debugobsolete
@@ -839,11 +835,10 @@
 rewind with no argument should be equivalent to `--from .`
 
   $ echo 'default-date = 4 0' >> $HGRCPATH
-  $ hg rewind --from '.'
-  rewinded to 1 changesets
+  $ hg rewind
+  rewound to 1 changesets
   (1 changesets obsoleted)
   working directory is now at a5dd64adbb2a
-  $ echo 'default-date = 5 0' >> $HGRCPATH
   $ hg log -G
   @  changeset:   6:a5dd64adbb2a
   |  tag:         tip
@@ -887,8 +882,9 @@
 Automatically rewinding the full stack (with --to)
 --------------------------------------------------
 
+  $ echo 'default-date = 5 0' >> $HGRCPATH
   $ hg rewind --hidden --to 'predecessors(.)'
-  rewinded to 2 changesets
+  rewound to 2 changesets
   (2 changesets obsoleted)
   working directory is now at 3f2d8862657d
   $ hg debugobsolete
@@ -943,8 +939,8 @@
 ----------------------------------------------------
 
   $ echo 'default-date = 6 0' >> $HGRCPATH
-  $ hg rewind --hidden --from '.'
-  rewinded to 2 changesets
+  $ hg rewind --from '.'
+  rewound to 2 changesets
   (2 changesets obsoleted)
   working directory is now at d36d6d267714
   $ hg debugobsolete
@@ -1029,7 +1025,7 @@
   A B
 
   $ hg rewind --from .
-  rewinded to 1 changesets
+  rewound to 1 changesets
   (1 changesets obsoleted)
   working directory is now at 9d325190bd87
   $ hg st --change .
@@ -1083,7 +1079,7 @@
 Clean wdir
 
   $ hg rewind --keep --to 'desc("initial")' --hidden
-  rewinded to 1 changesets
+  rewound to 1 changesets
   (1 changesets obsoleted)
   $ hg debugobsolete
   30704102d912d9d35a3d51400c4c93ad1e8bc7f3 2ea5be2f87510c7d26d5866dec83b57a7d939c4a 0 (Thu Jan 01 00:00:06 1970 +0000) {'ef1': '9', 'operation': 'amend', 'user': 'test'}
--- a/tests/test-topic-push-concurrent-on.t	Fri Aug 21 23:04:03 2020 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,432 +0,0 @@
-# same as test-topic-push but with the concurrent push feature on
-
-  $ . "$TESTDIR/testlib/topic_setup.sh"
-
-  $ cat << EOF >> $HGRCPATH
-  > [ui]
-  > logtemplate = {rev} {branch} {get(namespaces, "topics")} {phase} {desc|firstline}\n
-  > ssh = "$PYTHON" "$RUNTESTDIR/dummyssh"
-  > [server]
-  > concurrent-push-mode=check-related
-  > EOF
-
-  $ hg init main
-  $ hg init draft
-  $ cat << EOF >> draft/.hg/hgrc
-  > [phases]
-  > publish=False
-  > EOF
-  $ hg clone main client
-  updating to branch default
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cat << EOF >> client/.hg/hgrc
-  > [paths]
-  > draft=../draft
-  > EOF
-
-
-Testing core behavior to make sure we did not break anything
-============================================================
-
-Pushing a first changeset
-
-  $ cd client
-  $ echo aaa > aaa
-  $ hg add aaa
-  $ hg commit -m 'CA'
-  $ hg outgoing -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  @  0 default  draft CA
-  
-  $ hg push
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-
-Pushing two heads
-
-  $ echo aaa > bbb
-  $ hg add bbb
-  $ hg commit -m 'CB'
-  $ echo aaa > ccc
-  $ hg up 'desc(CA)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg add ccc
-  $ hg commit -m 'CC'
-  created new head
-  (consider using topic for lightweight branches. See 'hg help topic')
-  $ hg outgoing -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  @  2 default  draft CC
-  
-  o  1 default  draft CB
-  
-  $ hg push
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  abort: push creates new remote head 9fe81b7f425d!
-  (merge or see 'hg help push' for details about pushing new heads)
-  [255]
-  $ hg outgoing -r 'desc(CB)' -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  o  1 default  draft CB
-  
-  $ hg push -r 'desc(CB)'
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files
-
-Pushing a new branch
-
-  $ hg branch mountain
-  marked working directory as branch mountain
-  (branches are permanent and global, did you want a bookmark?)
-  $ hg commit --amend
-  $ hg outgoing -G
-  comparing with $TESTTMP/main (glob)
-  searching for changes
-  @  3 mountain  draft CC
-  
-  $ hg push 
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  abort: push creates new remote branches: mountain!
-  (use 'hg push --new-branch' to create new remote branches)
-  [255]
-  $ hg push --new-branch
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  1 new obsolescence markers
-
-Including on non-publishing
-
-  $ hg push --new-branch draft
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 3 files (+1 heads)
-  1 new obsolescence markers
-
-Testing topic behavior
-======================
-
-Local peer tests
-----------------
-
-  $ hg up -r 'desc(CA)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic babar
-  marked working directory as topic: babar
-  $ echo aaa > ddd
-  $ hg add ddd
-  $ hg commit -m 'CD'
-  active topic 'babar' grew its first changeset
-  (see 'hg help topics' for more information)
-  $ hg log -G # keep track of phase because I saw some strange bug during developement
-  @  4 default babar draft CD
-  |
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a non publishing server should not be seen as a new head
-
-  $ hg push draft
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  $ hg log -G
-  @  4 default babar draft CD
-  |
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a publishing server should be seen as a new head
-
-  $ hg push
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  abort: push creates new remote head 67f579af159d!
-  (merge or see 'hg help push' for details about pushing new heads)
-  [255]
-  $ hg log -G
-  @  4 default babar draft CD
-  |
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-wireprotocol tests
-------------------
-
-  $ hg up -r 'desc(CA)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic celeste
-  marked working directory as topic: celeste
-  $ echo aaa > eee
-  $ hg add eee
-  $ hg commit -m 'CE'
-  active topic 'celeste' grew its first changeset
-  (see 'hg help topics' for more information)
-  $ hg log -G # keep track of phase because I saw some strange bug during developement
-  @  5 default celeste draft CE
-  |
-  | o  4 default babar draft CD
-  |/
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a non publishing server without topic -> new head
-
-  $ cat << EOF >> ../draft/.hg/hgrc
-  > [extensions]
-  > topic=!
-  > EOF
-  $ hg push ssh://user@dummy/draft
-  pushing to ssh://user@dummy/draft
-  searching for changes
-  abort: push creates new remote head 84eaf32db6c3!
-  (merge or see 'hg help push' for details about pushing new heads)
-  [255]
-  $ hg log -G
-  @  5 default celeste draft CE
-  |
-  | o  4 default babar draft CD
-  |/
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a non publishing server should not be seen as a new head
-
-  $ printf "topic=" >> ../draft/.hg/hgrc
-  $ hg config extensions.topic >> ../draft/.hg/hgrc
-  $ hg push ssh://user@dummy/draft
-  pushing to ssh://user@dummy/draft
-  searching for changes
-  remote: adding changesets
-  remote: adding manifests
-  remote: adding file changes
-  remote: added 1 changesets with 1 changes to 1 files (+1 heads)
-  $ hg log -G
-  @  5 default celeste draft CE
-  |
-  | o  4 default babar draft CD
-  |/
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Pushing a new topic to a publishing server should be seen as a new head
-
-  $ hg push ssh://user@dummy/main
-  pushing to ssh://user@dummy/main
-  searching for changes
-  abort: push creates new remote head 67f579af159d!
-  (merge or see 'hg help push' for details about pushing new heads)
-  [255]
-  $ hg log -G
-  @  5 default celeste draft CE
-  |
-  | o  4 default babar draft CD
-  |/
-  | o  3 mountain  public CC
-  |/
-  | o  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Check that we reject multiple head on the same topic
-----------------------------------------------------
-
-  $ hg up 'desc(CB)'
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg topic babar
-  marked working directory as topic: babar
-  $ echo aaa > fff
-  $ hg add fff
-  $ hg commit -m 'CF'
-  $ hg log -G
-  @  6 default babar draft CF
-  |
-  | o  5 default celeste draft CE
-  | |
-  | | o  4 default babar draft CD
-  | |/
-  | | o  3 mountain  public CC
-  | |/
-  o |  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-  $ hg push draft
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  abort: push creates new remote head f0bc62a661be on branch 'default:babar'!
-  (merge or see 'hg help push' for details about pushing new heads)
-  [255]
-
-Multiple head on a branch merged in a topic changesets
-------------------------------------------------------------------------
-
-
-  $ hg up 'desc(CA)'
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ echo aaa > ggg
-  $ hg add ggg
-  $ hg commit -m 'CG'
-  created new head
-  (consider using topic for lightweight branches. See 'hg help topic')
-  $ hg up 'desc(CF)'
-  switching to topic babar
-  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg merge 'desc(CG)'
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-  $ hg commit -m 'CM'
-  $ hg log -G
-  @    8 default babar draft CM
-  |\
-  | o  7 default  draft CG
-  | |
-  o |  6 default babar draft CF
-  | |
-  | | o  5 default celeste draft CE
-  | |/
-  | | o  4 default babar draft CD
-  | |/
-  | | o  3 mountain  public CC
-  | |/
-  o |  1 default  public CB
-  |/
-  o  0 default  public CA
-  
-
-Reject when pushing to draft
-
-  $ hg push draft -r .
-  pushing to $TESTTMP/draft (glob)
-  searching for changes
-  abort: push creates new remote head 4937c4cad39e!
-  (merge or see 'hg help push' for details about pushing new heads)
-  [255]
-
-
-Reject when pushing to publishing
-
-  $ hg push -r .
-  pushing to $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 2 changes to 2 files
-
-  $ cd ..
-
-Test phase move
-==================================
-
-setup, two repo knowns about two small topic branch
-
-  $ hg init repoA
-  $ hg clone repoA repoB
-  updating to branch default
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cat << EOF >> repoA/.hg/hgrc
-  > [phases]
-  > publish=False
-  > EOF
-  $ cat << EOF >> repoB/.hg/hgrc
-  > [phases]
-  > publish=False
-  > EOF
-  $ cd repoA
-  $ echo aaa > base
-  $ hg add base
-  $ hg commit -m 'CBASE'
-  $ echo aaa > aaa
-  $ hg add aaa
-  $ hg topic topicA
-  marked working directory as topic: topicA
-  $ hg commit -m 'CA'
-  active topic 'topicA' grew its first changeset
-  (see 'hg help topics' for more information)
-  $ hg up 'desc(CBASE)'
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ echo aaa > bbb
-  $ hg add bbb
-  $ hg topic topicB
-  marked working directory as topic: topicB
-  $ hg commit -m 'CB'
-  active topic 'topicB' grew its first changeset
-  (see 'hg help topics' for more information)
-  $ cd ..
-  $ hg push -R repoA repoB
-  pushing to repoB
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 3 changesets with 3 changes to 3 files (+1 heads)
-  $ hg log -G -R repoA
-  @  2 default topicB draft CB
-  |
-  | o  1 default topicA draft CA
-  |/
-  o  0 default  draft CBASE
-  
-
-We turn different topic to public on each side,
-
-  $ hg -R repoA phase --public topicA
-  $ hg -R repoB phase --public topicB
-
-Pushing should complain because it create to heads on default
-
-  $ hg push -R repoA repoB
-  pushing to repoB
-  searching for changes
-  no changes found
-  abort: push create a new head on branch "default"
-  [255]
--- a/tests/test-topic-push.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-topic-push.t	Tue Aug 25 10:12:29 2020 +0200
@@ -1,3 +1,5 @@
+#testcases concurrent-strict concurrent-check-related
+
   $ . "$TESTDIR/testlib/topic_setup.sh"
 
   $ cat << EOF >> $HGRCPATH
@@ -6,18 +8,32 @@
   > ssh = "$PYTHON" "$RUNTESTDIR/dummyssh"
   > EOF
 
+#if concurrent-strict
+  $ cat << EOF >> $HGRCPATH
+  > [server]
+  > concurrent-push-mode = strict
+  > EOF
+#endif
+
+#if concurrent-check-related
+  $ cat << EOF >> $HGRCPATH
+  > [server]
+  > concurrent-push-mode = check-related
+  > EOF
+#endif
+
   $ hg init main
   $ hg init draft
   $ cat << EOF >> draft/.hg/hgrc
   > [phases]
-  > publish=False
+  > publish = no
   > EOF
   $ hg clone main client
   updating to branch default
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat << EOF >> client/.hg/hgrc
   > [paths]
-  > draft=../draft
+  > draft = ../draft
   > EOF
 
 
@@ -92,7 +108,7 @@
   searching for changes
   @  3 mountain  draft CC
   
-  $ hg push 
+  $ hg push
   pushing to $TESTTMP/main (glob)
   searching for changes
   abort: push creates new remote branches: mountain!
@@ -279,7 +295,7 @@
 
   $ cat << EOF >> ../draft/.hg/hgrc
   > [extensions]
-  > topic=!
+  > topic = !
   > EOF
   $ hg push ssh://user@dummy/draft
   pushing to ssh://user@dummy/draft
@@ -301,8 +317,7 @@
 
 Pushing a new topic to a non publishing server should not be seen as a new head
 
-  $ printf "topic=" >> ../draft/.hg/hgrc
-  $ hg config extensions.topic >> ../draft/.hg/hgrc
+  $ echo 'topic =' >> ../draft/.hg/hgrc
   $ hg push ssh://user@dummy/draft
   pushing to ssh://user@dummy/draft
   searching for changes
@@ -342,8 +357,8 @@
   o  0 default  public CA
   
 
-Check that we reject multiple head on the same topic
-----------------------------------------------------
+Check that we reject multiple heads on the same topic
+-----------------------------------------------------
 
   $ hg up 'desc(CB)'
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -373,9 +388,8 @@
   (merge or see 'hg help push' for details about pushing new heads)
   [255]
 
-Multiple head on a branch merged in a topic changesets
-------------------------------------------------------------------------
-
+Multiple heads on a branch merged into a topic branch
+-----------------------------------------------------
 
   $ hg up 'desc(CA)'
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
@@ -418,7 +432,6 @@
   (merge or see 'hg help push' for details about pushing new heads)
   [255]
 
-
 Reject when pushing to publishing
 
   $ hg push -r .
@@ -432,9 +445,9 @@
   $ cd ..
 
 Test phase move
-==================================
+===============
 
-setup, two repo knowns about two small topic branch
+setup, two repos have two small topic branches
 
   $ hg init repoA
   $ hg clone repoA repoB
@@ -442,11 +455,11 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat << EOF >> repoA/.hg/hgrc
   > [phases]
-  > publish=False
+  > publish = no
   > EOF
   $ cat << EOF >> repoB/.hg/hgrc
   > [phases]
-  > publish=False
+  > publish = no
   > EOF
   $ cd repoA
   $ echo aaa > base
@@ -484,16 +497,16 @@
   o  0 default  draft CBASE
   
 
-We turn different topic to public on each side,
+We turn a different topic to public on each side,
 
   $ hg -R repoA phase --public topicA
   $ hg -R repoB phase --public topicB
 
-Pushing should complain because it create to heads on default
+Pushing should complain because it would create two heads on default
 
   $ hg push -R repoA repoB
   pushing to repoB
   searching for changes
   no changes found
-  abort: push create a new head on branch "default"
+  abort: push creates a new head on branch 'default'
   [255]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-shell-prompt.t	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,29 @@
+  $ . $TESTDIR/testlib/topic_setup.sh
+
+Code logically equivalent to the following is used in Zsh to show the branch
+and topic (if set) in the prompt. If the format of the files is changed in a
+way that it breaks the test, a mail should be sent to zsh-workers@zsh.org.
+
+  $ get_branch_like_zsh() {
+  >     branchfile=".hg/branch"
+  >     topicfile=".hg/topic"
+  >     if [ -r "${branchfile}" ] ; then
+  >         r_branch=$(cat "${branchfile}")
+  >     fi
+  >     if [ -f "${topicfile}" ] && [ -r "${topicfile}" ] && [ -s "${topicfile}" ] ; then
+  >         IFS= read -r REPLY < ${topicfile}
+  >         r_branch=${r_branch}:${REPLY}
+  >     fi
+  >     echo $r_branch
+  > }
+
+  $ hg init
+  $ hg branch branch -q
+  $ get_branch_like_zsh
+  branch
+  $ hg topic topic -q
+  $ get_branch_like_zsh
+  branch:topic
+  $ hg topic --clear -q
+  $ get_branch_like_zsh
+  branch
--- a/tests/test-topic-stack.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-topic-stack.t	Tue Aug 25 10:12:29 2020 +0200
@@ -385,6 +385,14 @@
   hg: parse error: stack takes no arguments, it works on current topic
   [255]
 
+Stack relation:
+
+  $ hg log -r 'foo#stack'
+  2 default {foo} draft c_c
+  4 default {foo} draft c_e
+  5 default {foo} draft c_f
+  6 default {foo} draft c_d
+
 Stack relation subscript:
 
   $ hg log -r 'foo#stack[0]'
--- a/tests/test-topic-tutorial.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-topic-tutorial.t	Tue Aug 25 10:12:29 2020 +0200
@@ -332,6 +332,7 @@
       		penwidth=2.0];
       }
 #endif
+
 And updating back to the topic reactivates it:
 
   $ hg update food
@@ -1516,6 +1517,7 @@
       		penwidth=2.0];
       }
 #endif
+
 Multi-headed stack
 ------------------
 
--- a/tests/test-topic.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-topic.t	Tue Aug 25 10:12:29 2020 +0200
@@ -910,6 +910,10 @@
     ^ 3
   s1: 0
 
+  $ tlog 'tip#s'
+  0: 
+  4: 
+
   $ tlog 'tip#stack[0]'
   $ tlog 'tip#stack[1]'
   0: 
@@ -928,6 +932,11 @@
   s1: 1
   s0^ 0 (base)
 
+  $ tlog 'featureA#s'
+  1: featureA
+  2: featureA
+  3: featureA
+
   $ tlog 'featureA#s[0]'
   0: 
   $ tlog 'featureA#s[0:0]'
@@ -978,6 +987,7 @@
   ### target: default (branch)
   (stack is empty)
   s0^ 4 (base current)
+  $ tlog 'wdir()#s'
   $ tlog 'wdir()#s[0]'
   4: 
 
--- a/tests/test-unstability-resolution-result.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-unstability-resolution-result.t	Tue Aug 25 10:12:29 2020 +0200
@@ -66,10 +66,9 @@
 
 Test evolve of orphan that run into conflict
 
-  $ ls -A
-  .hg
-  a
-  b
+  $ hg status -A
+  C a
+  C b
   $ hg pdiff a
   diff -r 07f494440405 a
   --- a/a	Thu Jan 01 00:00:00 1970 +0000
--- a/tests/test-wireproto.t	Fri Aug 21 23:04:03 2020 +0800
+++ b/tests/test-wireproto.t	Tue Aug 25 10:12:29 2020 +0200
@@ -194,8 +194,9 @@
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log  --config experimental.evolution.obsdiscovery=no
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl -s http://localhost:$HGPORT/?cmd=capabilities
-  _evoext_getbundle_obscommon _evoext_obshashrange_v1 batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Acheckheads%3Drelated%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache%0Astream%3Dv2 changegroupsubset compression=*zlib getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1,sparserevlog unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (no-eol) (glob)
+  $ hg debugcapabilities http://localhost:$HGPORT | grep _evoext
+    _evoext_getbundle_obscommon
+    _evoext_obshashrange_v1
 
 Check we cannot use pushkey for marker exchange anymore
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testlib/check-compat-strings.py	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+"""
+This script finds compatibility-related comments with a node hash specified
+in all files in a given directory (. by default) and looks up the hash in a
+repo (~/hg by default) to determine if each of the comments is correct and,
+if not, it suggests the correct release. This can prevent accidentally
+removing a piece of code that was misattributed to a different (earlier)
+release of core hg.
+
+Usage: $0 WDIR HGREPO where WDIR is usually evolve/hgext3rd/ and HGREPO is
+the place with core Mercurial repo (not just checkout). Said repo has to be
+sufficiently up-to-date, otherwise this script may not work correctly.
+"""
+
+from __future__ import print_function
+
+import argparse
+import os
+import re
+from subprocess import check_output
+
+def grepall(workdir, linere):
+    for root, dirs, files in os.walk(workdir):
+        for fname in files:
+            if not fname.endswith('.py'):
+                continue
+            path = os.path.join(root, fname)
+            with open(path, 'r') as src:
+                for lineno, line in enumerate(src, 1):
+                    for groups in linere.findall(line):
+                        yield path, lineno, line, groups
+
+def main():
+    ap = argparse.ArgumentParser()
+    ap.add_argument('workdir', nargs='?', default='.')
+    ap.add_argument('hgdir', nargs='?', default=os.path.expanduser('~/hg'))
+
+    opts = ap.parse_args()
+
+    linere = re.compile(r'hg <= ([0-9.]+) \(([0-9a-f+]+)\)')
+    basecmd = ['hg', '--cwd', opts.hgdir, 'log', '-T', '{tags}']
+    hgenv = {'HGPLAIN': '1', 'HGENCODING': 'UTF-8'}
+    relcache = {}
+
+    for path, lineno, line, match in grepall(opts.workdir, linere):
+        expected, revset = match
+
+        if revset not in relcache:
+            tagrevset = 'max(tag("re:^[0-9]\\.[0-9]$") - (%s)::)' % revset
+            cmd = basecmd + ['-r', tagrevset]
+            relcache[revset] = check_output(cmd, env=hgenv).decode('UTF-8')
+
+        lastrel = relcache[revset]
+
+        if lastrel != expected:
+            print('%s:%d:%s' % (path, lineno, line.rstrip('\r\n')))
+            print('\\ actual last major release without %s is %s'
+                  % (revset, lastrel))
+            print()
+
+if __name__ == '__main__':
+    main()
--- a/tests/testlib/check-compat-strings.sh	Fri Aug 21 23:04:03 2020 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-unset GREP_OPTIONS
-
-# This script finds compatibility-related comments with a node hash specified
-# in all files in a given directory (. by default) and looks up the hash in a
-# repo (~/hg by default) to determine if each of the comments is correct and,
-# if not, it suggests the correct release. This can prevent accidentally
-# removing a piece of code that was misattributed to a different (earlier)
-# release of core hg.
-
-# Usage: $0 WDIR HGREPO where WDIR is usually evolve/hgext3rd/ and HGREPO is
-# the place with core Mercurial repo (not just checkout). Said repo has to be
-# sufficiently up-to-date, otherwise this script may not work correctly.
-
-workdir=${1:-'.'}
-hgdir=${2:-~/hg}
-grep -Ern 'hg <= [0-9.]+ \([0-9a-f+]+\)' "$workdir" | while read -r line; do
-    bashre='hg <= ([0-9.]+) \(([0-9a-f+]+)\)'
-    if [[ $line =~ $bashre ]]; then
-        expected=${BASH_REMATCH[1]}
-        revset=${BASH_REMATCH[2]}
-        tagrevset="max(tag('re:^[0-9]\\.[0-9]$') - ($revset)::)"
-        lastrel=$(HGPLAIN=1 hg --cwd "$hgdir" log -r "$tagrevset" -T '{tags}')
-        if [[ "$lastrel" != "$expected" ]]; then
-            echo "$line"
-            echo "actual last major release without $revset is $lastrel"
-            echo
-        fi
-    fi
-done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testlib/check-min-versions.py	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+"""
+This script takes `minimumhgversion` and `testedwith` values from the first
+file (e.g. hgext3rd/evolve/metadata.py) and mercurial package versions from the
+second file (e.g. debian/control) and compares them using a set of rules to
+find any issues.
+
+Rules:
+
+    - if `minimumhgversion` is a feature release, the first version from
+      `testedwith` should belong to that feature release
+
+    - if `minimumhgversion` is a bugfix release, the first version from
+      `testedwith` should be the same
+
+    - mercurial package versions (from both Depends and Build-Depends sections)
+      should match `minimumhgversion`
+
+Usage: $0 MFILE CFILE where MFILE contains extension metadata and CFILE is a
+debian/control file.
+"""
+
+from __future__ import print_function
+
+import argparse
+import os
+import re
+
+def grepall(workdir, linere):
+    for root, dirs, files in os.walk(workdir):
+        for fname in files:
+            if not fname.endswith('.py'):
+                continue
+            path = os.path.join(root, fname)
+            with open(path, 'r') as src:
+                for line in src:
+                    for groups in linere.findall(line):
+                        yield path, groups
+
+def main():
+    ap = argparse.ArgumentParser()
+    ap.add_argument('mfile', help='.py file with `testedwith`')
+    ap.add_argument('cfile', help='debian/control file')
+
+    opts = ap.parse_args()
+
+    mre = re.compile(r"^minimumhgversion = b'([0-9.]+)'")
+    mtre = re.compile(r"^testedwith = b'([0-9.]+) ")
+    minversion = ''
+    mintestedversion = ''
+
+    with open(opts.mfile, 'r') as src:
+        for line in src:
+            if not minversion:
+                groups = mre.findall(line)
+                if groups:
+                    minversion = groups[0]
+            if not mintestedversion:
+                groups = mtre.findall(line)
+                if groups:
+                    mintestedversion = groups[0]
+
+    if not minversion:
+        print('could not find `minimumhgversion` variable in %s' % opts.mfile)
+
+    if not mintestedversion:
+        print('could not find `testedwith` variable in %s' % opts.mfile)
+
+    if minversion.count('.') > 1:
+        # `minversion` is a bugfix release
+        if minversion != mintestedversion:
+            print('`minimumhgversion` is a bugfix release, the first version '
+                  'in `testedwith` should be the same: %s and %s'
+                  % (minversion, mintestedversion))
+
+    else:
+        # matching X.Y to determine the "feature release" version
+        frelre = re.compile(r"([0-9]+).([0-9]+)")
+
+        if frelre.findall(minversion) != frelre.findall(mintestedversion):
+            print('the first version in `testedwith` does not belong to the '
+                  'same feature release as `minimumhgversion`: %s and %s'
+                  % (mintestedversion, minversion))
+
+    cre = re.compile(r"^ mercurial \(>= ([0-9.]+)\)")
+    depversion = ''
+
+    with open(opts.cfile, 'r') as src:
+        for line in src:
+            groups = cre.findall(line)
+            if groups:
+                depversion = groups[0]
+                if minversion != depversion:
+                    print('versions differ: %s from `minimumhgversion`, %s '
+                          'from dependencies' % (minversion, depversion))
+
+    if not depversion:
+        print('could not find dependency on mercurial in %s' % opts.cfile)
+
+if __name__ == '__main__':
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testlib/content-divergence-util.sh	Tue Aug 25 10:12:29 2020 +0200
@@ -0,0 +1,36 @@
+#!/bin/sh
+# setup config and various utility to test content-divergence resolution
+
+. $TESTDIR/testlib/common.sh
+
+cat >> $HGRCPATH <<EOF
+[ui]
+# simpler log output
+logtemplate ="{rev}:{node|short} ({phase}): {desc|firstline} {if(troubles, '[{troubles}]')}\n"
+
+[phases]
+# non publishing server
+publish=False
+
+[extensions]
+evolve=
+rebase=
+EOF
+
+setuprepos() {
+    echo creating test repo for test case $1
+    mkdir $1
+    cd $1
+
+    echo - upstream
+    hg init upstream
+    cd upstream
+    mkcommit O
+    hg phase --public .
+    cd ..
+    echo - local
+    hg clone -q upstream local
+    echo - other
+    hg clone -q upstream other
+    echo 'cd into `local` and proceed with env setup'
+}