changeset 5626:aea931d59fc0 mercurial-5.5

test-compat: merge stable into mercurial-5.5 # no-check-commit
author Anton Shestakov <av6@dwimlabs.net>
date Sat, 31 Oct 2020 17:47:08 +0800
parents 0d1ed042d0f5 (current diff) 782cbadb123f (diff)
children ce6f570918fd
files
diffstat 85 files changed, 3172 insertions(+), 1826 deletions(-) [+]
line wrap: on
line diff
--- a/.gitlab-ci.yml	Mon Oct 26 18:32:22 2020 +0100
+++ b/.gitlab-ci.yml	Sat Oct 31 17:47:08 2020 +0800
@@ -1,54 +1,73 @@
-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_branch=$(tests/testlib/map-hg-rev.sh "$(hg identify --branch)")
+    - hg -R /ci/repos/mercurial/ update "$hg_branch"
+    - hg_rev=$(hg log -R /ci/repos/mercurial/ -r . -T '{node}')
+    - echo testing with mercurial branch="$hg_branch", revision="$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"
-        - echo testing with mercurial branch="$hg_rev", revision="$(hg log -R /ci/repos/mercurial/ -r . -T '{node}')"
+        - *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"
-        - echo testing with mercurial branch="$hg_rev", revision="$(hg log -R /ci/repos/mercurial/ -r . -T '{node}')"
-        - (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"
-        - echo testing with mercurial branch="$hg_rev", revision="$(hg log -R /ci/repos/mercurial/ -r . -T '{node}')"
-        - (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"
-        - echo testing with mercurial branch="$hg_rev", revision="$(hg log -R /ci/repos/mercurial/ -r . -T '{node}')"
-        - (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"
-        - echo testing with mercurial branch="$hg_rev", revision="$(hg log -R /ci/repos/mercurial/ -r . -T '{node}')"
-        - (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	Mon Oct 26 18:32:22 2020 +0100
+++ b/.gitlab/issue_templates/new-version.md	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/CHANGELOG	Sat Oct 31 17:47:08 2020 +0800
@@ -1,12 +1,32 @@
 Changelog
 =========
 
-10.0.3 - in progress
+10.1.0 -- 2020-10-31
 --------------------
 
-  * evolve: specific the source of config override for `server.bundle1=no`
+  * compatibility with Mercurial 5.6
+
+  * numerous minor changes to packaging, Makefile, README moved to README.rst
+
+  * evolve: various improvements to content-divergence resolution
+  * evolve: fix various issues with --continue when solving content-divergence
+  * evolve: specify the source of config override for `server.bundle1=no`
+  * evolve: avoid leaving mergestate after instability resolution
+  * evolve: while resolving conflicts, the evolved node will no longer be a
+    dirstate parent (won't show up in `hg parents` and not as `@` in `hg log -G`,
+    but it will show up as `%` with hg >= 5.4)
+
   * metaedit: update bookmark location when applicable
 
+  * rewind: add a --dry-run flag
+  * rewind: properly record rewind of splits as folds
+
+topic (0.20.0)
+
+  * stack: support foo#stack relation revset (hg-5.4+ only)
+  * merge: add a experimental.topic.linear-merge option to allow oedipus merges
+    in some cases
+
 10.0.2 -- 2020-09-08
 --------------------
 
--- a/MANIFEST.in	Mon Oct 26 18:32:22 2020 +0100
+++ b/MANIFEST.in	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/Makefile	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ /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 installation instructions at the `format-source`_ repo.
-
-.. _`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	Sat Oct 31 17:47:08 2020 +0800
@@ -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 installation instructions at the `format-source`_ repo.
+
+.. _`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	Mon Oct 26 18:32:22 2020 +0100
+++ b/debian/changelog	Sat Oct 31 17:47:08 2020 +0800
@@ -170,9 +170,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	Mon Oct 26 18:32:22 2020 +0100
+++ b/debian/control	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/debian/docs	Sat Oct 31 17:47:08 2020 +0800
@@ -1,2 +1,2 @@
 html
-README
+README.rst
--- a/debian/rules	Mon Oct 26 18:32:22 2020 +0100
+++ b/debian/rules	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/debian/source/options	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/debian/test-blacklist	Sat Oct 31 17:47:08 2020 +0800
@@ -1,4 +1,1 @@
 test-drop.t
-test-inhibit.t
-test-simple4server.t
-tests/test-simple4server-bundle2.t
--- a/docs/makefile	Mon Oct 26 18:32:22 2020 +0100
+++ b/docs/makefile	Sat Oct 31 17:47:08 2020 +0800
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/troubles-handling.rst	Sat Oct 31 17:47:08 2020 +0800
@@ -0,0 +1,425 @@
+###########################################################
+Possible troubles in rewriting history and their resolution
+###########################################################
+
+.. Copyright 2020 Sushil Khanchi <sushilkhanchi97@gmail.com>
+..                Octobus SAS          <contact@octobus.net>
+
+
+Rewriting history, and especially rewriting draft history that have been
+exchanged can lead to "unstable" situation.
+
+This document is intended for developer of the changeset evolution concept. It
+cover through the technical aspects of each "instability" a changeset can get
+into. It aims at building an exhaustive list of each cases and sub-cases and the
+status of automatic resolution for these cases.
+
+Public changeset are part of the permanent history and are never considered
+unstable.
+
+.. contents:: :depth: 4
+
+******
+Orphan
+******
+
+Basics
+======
+
+A changeset is orphan when there is at least one obsolete ancestor.
+
+As a resolution, we need to find a new appropriate parents for the
+changeset and rebase the orphan there.
+
+If the parents of an orphan changeset are orphan themself, they will have to be
+"stabilised" fist.
+
+Cause of trouble
+================
+
+Orphan can appears because the user locally rewrite changeset with descendants.
+In this case the orphan are created when the command run. They are few real use
+case for such action and the user interface should focus on discouraging it.
+
+Orphan can also happens when the users created new changeset on draft that got
+rewritten in another repository. The orphan are then "discovered" when the
+obsolescence information of the ancestors is pulled in the local repository.
+This is the most common way to create phase divergences.
+
+Source of Complexity
+====================
+
+There can be different situations we need to take care of when dealing with
+resolution of an orphan changeset, like:
+
+* parents might not be obsolete (yet) and orphan themself. They will need to be
+  resolved first.
+* obsolete parent has conflicting rewrites (content-divergence), so there might
+  not be an obvious "good spot" to rebase the changeset too.
+* obsolete parent could have been prune, so it has not direct successors,
+* obsolete parent was split in multiple changesets. This comes with multiple
+  variants:
+
+  * successors could be linear of spread across multiple branches,
+  * successors could have been reordered after the initial split,
+  * Some of the successors could have been pruned.
+* the orphan changeset can be a merge and orphan may come any numbers of parents
+* rebasing might lead to conflict.
+
+Details of Sub cases
+====================
+
+.. contents::
+   :local:
+
+O-A: Linear changeset (one parent)
+----------------------------------
+
+O-A1: parent has a single successors
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+**Stabilisation Strategy**
+
+Solution is Clear.
+
+Relocate the orphan changeset onto the single successor of obsolete parent.
+
+**Current Support Level**
+
+Good: current implementation is expected to perform the planned stabilisation.
+
+O-A2: parent is pruned (no successors)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Find nearest "not-pruned" ancestor and treat it (or its successor, if apply) as
+a resolution parent. (re-run Orphan resolution starting from these parent).
+In some cases, there can be multiple "not-pruned" ancestor and it could be ambiguous
+for us to decide which one to pick.
+
+O-A2.1: set of "not-pruned" ancestor has single head
+""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+
+**Stabilisation Strategy**
+
+This comes under `O-A3` and `O-A4` cases (since we re-run orphan resolution
+assuming "not-pruned" ancestor as parent)
+
+**Current Support Level**
+
+Good: current implementation is expected to perform the planned stabilisation.
+
+O-A2.2: set of "not-pruned" ancestor has multiple heads
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+If a merge is pruned, we have multiple "not-pruned" ancestor for its immediate
+child to stabilise.
+
+
+**Stabilisation Strategy**
+
+XXX: yet to decide resolution
+
+**Current Support Level**
+
+O-A3: parent has multiple successors sets (divergence case)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+**Stabilisation Strategy**
+
+XXX: yet to decide resolution
+
+**Current Support Level**
+
+O-A4: parent is split into multiple successors
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+O-A4.1: successors of parent are all on the same topological branch
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+
+**Stabilisation Strategy**
+
+Pick highest one as resolution parent.
+
+**Current Support Level**
+
+Good: current implementation is expected to perform the planned stabilisation.
+
+O-A4.2: parent is split into multiple topological branches (at least 2 heads)
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+In this case, the destination can be ambiguous.
+
+**Stabilisation Strategy**
+
+prompt user to choose one.
+
+(could we do better in some case?)
+
+**Current Support Level**
+
+Current implementation is expected to perform the planned stabilisation.
+
+O-M: Parent are Merge (multiple parent)
+---------------------------------------
+
+O-M1: Only one parent is obsolete
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+XXX Simple case are probably as good as any `O-A` case. However special case are
+probably ignored right now (e.g: successors of the obsolete parent is linear with
+the other parent).
+
+O-M2: both parent are obsolete
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+XXX currently we evolve one after the other.
+
+****************
+Phase-Divergence
+****************
+
+Basics
+======
+
+
+It appears when a rewritten changeset got published. A phase-divergent changeset
+has a public predecessor.
+
+Solving phase divergence is done by extracting the changes made between the
+public predecessors and the latest evolution of the changesets into a new
+changesets, descendants of the public predecessors.
+
+Cause of trouble
+================
+
+It can appear if a user locally change the phase of an obsolete changeset
+(which has successors) to public.
+
+Phase-divergence can also happens when the users rewrite draft changeset that got
+published in another repository. The phase divergence then "discovered" when the
+publishing information of the predecessors is pulled in the local repository.
+
+Source of Complexity
+====================
+
+* public version is a merge
+* phase-divergent  changeset is a merge
+* public version was split (linear, over multiple topo branches, etc…)
+* phase-divergent version was folded with others
+* rebasing might lead to conflict.
+
+Details of Sub cases
+====================
+
+.. contents::
+   :local:
+
+#TODO: yet to document
+
+******************
+Content-Divergence
+******************
+
+Basics
+======
+
+
+Independent rewrites of same changeset leads to content-divergence. So an
+obsolete changeset have multiple "sets" of successors. And the content-divergent
+changeset have some predecessors in common without the situation being the
+result of a split.
+
+To stabilise the situation, we need to "merge" the changes introduced in each
+side of the divergence and create a new changeset, that will supersede both of
+the unstable one. The merged information can be both about file content and
+metadata (author, parent, etc).
+
+In practice there are a lot of corner case where this is "not that simple".
+
+Cause of trouble
+================
+
+It can appear locally if a user independently rewrite the same changeset multiple
+times.
+
+Content-divergence can also happens when the users rewrite draft changesets that got
+rewritten in another repository as well. The content divergence then "revealed"
+when the rewriting information is shared (pulled/pushed) with another repository.
+
+Source of Complexity
+====================
+
+
+Before we perform a 3-way merge between the divergent changesets and common
+predecessor (which acts as the 3-way merge base), there are some situations we need to take care of, like:
+
+* if divergent changesets moved, check which side moved in which direction,
+  and proceed accordingly
+* they moved on different unrelated branches
+* divergent changeset can be orphan as well
+* one side of divergence is in public phase
+* divergent changeset is part of a split or fold
+
+Details of resolution
+=====================
+
+Resolution of content-divergence can be understand by dividing it in stages:
+
+Special case: If one of the divergent changeset is part of a split or fold, we don't handle
+it right now. But when we are going to support it, it will probably have its own logic and
+seperate from generic resolution.
+
+Generic resolution:
+
+The first stage of solving content-divergence is to find the changeset where the resolution
+changeset will be based on (which we call the resolution parent or the target parent) and
+relocate the divergent changesets on the resolution parent, if apply.
+
+In second stage, we deal with merging files of two divergent changesets using the 3-way merge.
+
+In third stage, we deal with merging the metadata changes and creating the resultant changeset.
+
+Details of Sub cases
+====================
+
+.. contents::
+   :local:
+
+D-S: One of the divergent changeset is part of a split or fold
+--------------------------------------------------------------
+
+XXX: yet to decide resolution
+
+D-A: Finding the resolution parent
+----------------------------------
+
+This section is responsible to decide where the resultant changeset should live. If it's unable to
+find the resolution parent, we abort the content-divergence resolution. Following are the possible
+situations between the two divergent changesets and current support for each situation.
+
+D-A1: one of the divergent changeset is public
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the parent of public changeset as resolution parent.
+
+Note: After we resolve content-divergence in this case, the resultant changeset will be
+phase-divergent with the public side; so then we resolve phase-divergence to completely
+resolve the instability.
+
+D-A2: both are on the same parent
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the parent (or its successor, if apply) as resolution parent.
+
+D-A3: both are on different parents
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+D-A3.1: one side moved
+""""""""""""""""""""""
+
+Set the parent of moved changeset as resolution parent.
+
+D-A3.2: both moved forward; same branch
+"""""""""""""""""""""""""""""""""""""""
+
+Set the parent of ahead one as resolution parent.
+As most obvious is that, ahead one has the latest changes.
+
+D-A3.3: both moved backward; same branch
+""""""""""""""""""""""""""""""""""""""""
+
+XXX: yet to decide resolution
+
+D-A3.4: both moved opposite; same branch
+""""""""""""""""""""""""""""""""""""""""
+
+XXX: yet to decide resolution
+
+D-A3.5: both moved; one moved on different unrelated branch
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+XXX: yet to decide resolution
+
+D-A3.6: both moved on two different unrelated branch
+""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+XXX: yet to decide resolution
+
+
+D-B: Merging files of divergent changesets
+------------------------------------------
+
+While merging the files, there are few sub-cases that could arise
+like file content conflict, rename information conflict, multiple newest
+predecessor. Let's see them in detail.
+
+D-B1: File content conflict
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If there is a conflict in merging content of files we let the user to
+resolve the conflict.
+
+D-B2: Rename information conflict
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+XXX: yet to explore
+
+D-B3: Multiple newest predecessor
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+XXX: yet to explore
+
+
+D-C: Merging metadata changes and create resultant changeset
+------------------------------------------------------------
+
+While merging the metadata, there are few sub-cases that could arise
+like branch name conflict, commit message conflict, commit author conflict.
+If everything goes fine, we create the resultant changeset as a resolution of
+content-divergence.
+
+D-C1: Branch name conflict
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Stabilisation Strategy**
+
+Merge branch names using the 3-way merge. If unable to merge,
+we prompt the user to select a branch.
+
+Possible improvement: If we are solving a long streak of divergence, each
+side having the same branch, we can probably infer the correct resolution
+from previous resolution.
+
+**Current Support Level**
+
+Good: current implementation is expected to perform the planned stabilisation.
+
+D-C2: Commit description conflict
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Stabilisation Strategy**
+
+Merge commit description using text merge tool from user.
+
+Possible improvement: They are currently no way to "interrupt" and resume that
+conflict resolution. Having some way to do that would be nice.
+
+**Current Support Level**
+
+Good: current implementation is expected to perform the planned stabilisation.
+
+D-C3: Commit author conflict
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Stabilisation Strategy**
+
+Merge authors using 3-way merge. If unable to merge we concatenate
+the two, separated by comma. (for e.g. "John Doe, Malcolm X")
+
+**Current Support Level**
+
+Good: current implementation is expected to perform the planned stabilisation.
--- a/hgext3rd/evolve/cmdrewrite.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/cmdrewrite.py	Sat Oct 31 17:47:08 2020 +0800
@@ -173,7 +173,7 @@
         compat.cleanupnodes(repo, replacements, operation=b'amend',
                             metadata=metadata)
         phases.retractboundary(repo, tr, old.phase(), [newnode])
-        hg.updaterepo(repo, newnode, True)
+        compat.clean_update(repo[newnode])
 
 def _editandapply(ui, repo, pats, old, p1, fp, diffopts):
     newnode = None
@@ -513,7 +513,7 @@
                             metadata=metadata)
         phases.retractboundary(repo, tr, oldphase, [newid])
         if opts.get('revert'):
-            hg.updaterepo(repo, newid, True)
+            compat.clean_update(repo[newid])
         else:
             with repo.dirstate.parentchange(), compat.parentchange(repo):
                 movedirstate(repo, repo[newid], match)
@@ -874,9 +874,17 @@
                     metadata[b'note'] = opts['note']
 
                 phases.retractboundary(repo, tr, targetphase, [newid])
-                replacements = {tuple(ctx.node() for ctx in allctx): (newid,)}
-                compat.cleanupnodes(repo, replacements, operation=b"metaedit",
-                                    metadata=metadata)
+                # Use this condition as a proxy since the commit we care about
+                # (b99903534e06) didn't change any signatures.
+                if util.safehasattr(scmutil, 'nullrev'):
+                    mapping = {tuple(ctx.node() for ctx in allctx): (newid,)}
+                    scmutil.cleanupnodes(repo, mapping, operation=b"metaedit",
+                                         metadata=metadata)
+                else:
+                    # hg <= 4.7 (b99903534e06)
+                    mapping = {ctx.node(): (newid,) for ctx in allctx}
+                    scmutil.cleanupnodes(repo, mapping, operation=b"metaedit",
+                                         metadata=metadata)
             else:
                 ui.status(_(b"nothing changed\n"))
             tr.close()
@@ -1478,7 +1486,7 @@
     pctxnode = pickstate[b'oldpctx']
     ui.status(_(b"aborting pick, updating to %s\n") %
               node.hex(pctxnode)[:12])
-    hg.updaterepo(repo, pctxnode, True)
+    compat.clean_update(repo[pctxnode])
     pickstate.delete()
     return 0
 
--- a/hgext3rd/evolve/compat.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/compat.py	Sat Oct 31 17:47:08 2020 +0800
@@ -12,6 +12,7 @@
 from mercurial import (
     context,
     copies,
+    hg,
     merge as mergemod,
     obsolete,
     pycompat,
@@ -387,6 +388,30 @@
         if util.safehasattr(repo, '_quick_access_changeid_invalidate'):
             repo._quick_access_changeid_invalidate()
 
+if util.safehasattr(mergemod, '_update'):
+    def _update(*args, **kwargs):
+        return mergemod._update(*args, **kwargs)
+else:
+    # hg <= 5.5 (2c86b9587740)
+    def _update(*args, **kwargs):
+        return mergemod.update(*args, **kwargs)
+
+if (util.safehasattr(mergemod, '_update')
+    and util.safehasattr(mergemod, 'update')):
+
+    def update(ctx):
+        mergemod.update(ctx)
+
+    def clean_update(ctx):
+        mergemod.clean_update(ctx)
+else:
+    # hg <= 5.5 (c1b603cdc95a)
+    def update(ctx):
+        hg.updaterepo(ctx.repo(), ctx.node(), overwrite=False)
+
+    def clean_update(ctx):
+        hg.updaterepo(ctx.repo(), ctx.node(), overwrite=True)
+
 def cleanupnodes(repo, replacements, operation, moves=None, metadata=None):
     # Use this condition as a proxy since the commit we care about
     # (b99903534e06) didn't change any signatures.
--- a/hgext3rd/evolve/evolvecmd.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/evolvecmd.py	Sat Oct 31 17:47:08 2020 +0800
@@ -56,7 +56,7 @@
 abortmessage = _(b"see `hg help evolve.interrupted`\n")
 
 def _solveone(ui, repo, ctx, evolvestate, dryrun, confirm,
-              progresscb, category, lastsolved=None, stacktmplt=False):
+              progresscb, category, stacktmplt=False):
     """Resolve the troubles affecting one revision
 
     returns a tuple (bool, newnode) where,
@@ -68,16 +68,14 @@
     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,
-                                lastsolved=lastsolved)
+        result = _solveorphan(ui, repo, ctx, evolvestate, displayer,
+                              dryrun, confirm, progresscb)
     elif b'phasedivergent' == category:
         result = _solvephasedivergence(ui, repo, ctx, evolvestate,
                                        displayer, dryrun, confirm,
@@ -89,8 +87,8 @@
         assert False, b"unknown trouble category: %s" % (category)
     return result
 
-def _solveunstable(ui, repo, orig, evolvestate, displayer, dryrun=False,
-                   confirm=False, progresscb=None, lastsolved=None):
+def _solveorphan(ui, repo, orig, evolvestate, displayer, dryrun=False,
+                 confirm=False, progresscb=None):
     """ Tries to stabilize the changeset orig which is orphan.
 
     returns a tuple (bool, newnode) where,
@@ -109,7 +107,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 +118,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:
@@ -148,6 +146,9 @@
     if not ui.quiet or confirm:
         repo.ui.write(_(b'move:'), label=b'evolve.operation')
         displayer.show(orig)
+        # lastsolved: keep track of successor of last troubled cset we evolved
+        # to confirm that if atop msg should be suppressed to remove redundancy
+        lastsolved = evolvestate.get(b'lastsolved')
         if lastsolved is None or target != repo[lastsolved]:
             repo.ui.write(_(b'atop:'))
             displayer.show(target)
@@ -257,8 +258,8 @@
     # like reverting to the bumped node.
     wctx = context.overlayworkingctx(repo)
     wctx.setbase(prec)
-    merge.update(repo, bumped.node(), ancestor=prec, mergeancestor=True,
-                 branchmerge=False, force=False, wc=wctx)
+    compat._update(repo, bumped.node(), ancestor=prec, mergeancestor=True,
+                   branchmerge=False, force=False, wc=wctx)
     if not wctx.isempty():
         text = b'%s update to %s:\n\n' % (TROUBLES['PHASEDIVERGENT'], prec)
         text += bumped.description()
@@ -285,45 +286,9 @@
         repo.dirstate.setparents(newid, nodemod.nullid)
     return (True, replacementnode)
 
-def _prepcontentdivresolution(ui, repo, divergent, other, evolvestate):
+def _pickresolutionparent(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
-    # encounter conflicts after relocation is done while solving
-    # content-divergence and if the user calls `hg evolve --stop`, we need to
-    # strip that relocated commit. However if `--all` is passed, we need to
-    # reset this value for each content-divergence resolution which we are doing
-    # below.
-    evolvestate[b'relocated-other'] = None
-    evolvestate[b'relocating-other'] = False
-    evolvestate[b'relocated-div'] = None
-    evolvestate[b'relocating-div'] = False
-    evolvestate[b'relocation-req'] = False
-    # in case or relocation we get a new other node, we need to store the old
-    # other for purposes like `--abort` or `--stop`
-    evolvestate[b'old-other'] = None
-    evolvestate[b'old-divergent'] = None
-
-    def swapnodes(div, other):
-        div, other = other, div
-        evolvestate[b'divergent'] = div.node()
-        evolvestate[b'other-divergent'] = other.node()
-        return div, other
-    # haspubdiv: to keep track if we are solving public content-divergence
-    haspubdiv = False
-    if not (divergent.mutable() and other.mutable()):
-        haspubdiv = True
-        # for simplicity, we keep public one to local side while merging
-        # (as divergent is kept at local side, pinning public -> divergent)
-        if divergent.mutable():
-            publicdiv = other
-            divergent, other = swapnodes(divergent, other)
-        else:
-            publicdiv = divergent
-        evolvestate[b'public-divergent'] = publicdiv.node()
 
     # we don't handle merge content-divergent changesets yet
     if len(other.parents()) > 1:
@@ -336,19 +301,39 @@
         ui.write_err(hint)
         return (False, b".")
 
+    if not (divergent.mutable() and other.mutable()):
+        # Public divergence: we keep public one to local side while merging
+        # When public branch is behind to the mutable branch, for now we
+        # relocate mutable cset to public one's side in every case.
+        #
+        # This behaviour might be sub optimal when ancestors of mutable
+        # cset has changes its relocated descendant rely on.
+        #
+        # Otherwise, we are going to rebase the "behind" branch up to the new
+        # brancmap level.
+        publicdiv = divergent if other.mutable() else other
+        resolutionparent = publicdiv.p1().node()
+        evolvestate[b'public-divergent'] = publicdiv.node()
+        return (True, resolutionparent)
+
     otherp1 = succsotherp1 = other.p1().rev()
     divp1 = succsdivp1 = divergent.p1().rev()
 
-    # finding single successors of otherp1 and divp1
+    # finding single successors of divp1 and otherp1
+    try:
+        succsdivp1 = utility._singlesuccessor(repo, divergent.p1())
+    except utility.MultipleSuccessorsError:
+        msg = _(b"skipping %s: ambiguous orphan resolution parent for "
+                b"%s\n") % (divergent, divergent)
+        ui.write_err(msg)
+        return (False, b".")
     try:
         succsotherp1 = utility._singlesuccessor(repo, other.p1())
     except utility.MultipleSuccessorsError:
-        pass
-
-    try:
-        succsdivp1 = utility._singlesuccessor(repo, divergent.p1())
-    except utility.MultipleSuccessorsError:
-        pass
+        msg = _(b"skipping %s: ambiguous orphan resolution parent for "
+                b"%s\n") % (divergent, other)
+        ui.write_err(msg)
+        return (False, b".")
 
     # the changeset on which resolution changeset will be based on
     resolutionparent = repo[succsdivp1].node()
@@ -373,8 +358,6 @@
     # make it exclusive set
     divonly = set(divonly) - {divergent.rev()}
     otheronly = set(otheronly) - {other.rev()}
-    # is relocation of one of the changeset required
-    relocatereq = False
 
     # testing how both the divergent changesets are arranged, there can be 4
     # possible cases here:
@@ -392,6 +375,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:
@@ -400,40 +386,23 @@
             # to do in this case
             pass
         if otheronly:
-            relocatereq = True
-            if not haspubdiv:
-                # can't swap when public divergence, as public can't move
-                divergent, other = swapnodes(divergent, other)
-                resolutionparent = repo[succsotherp1].node()
+            resolutionparent = repo[succsotherp1].node()
         elif divonly:
-            relocatereq = True
+            pass
         else:
-            # no extra cset on either side; so not considering relocation
+            # no extra cset on either side
             pass
     elif succsotherp1 in gca and succsdivp1 not in gca:
-        relocatereq = True
         pass
     elif succsdivp1 in gca and succsotherp1 not in gca:
-        relocatereq = True
-
-        # When public branch is behind to the mutable branch, for now we
-        # relocate mutable cset to public one's side in every case.
-        #
-        # This behaviour might be sub optimal when ancestors of mutable
-        # cset has changes its relocated descendant rely on.
-        #
-        # Otherwise, we are going to rebase the "behind" branch up to the new
-        # brancmap level.
-        if not haspubdiv:
-            divergent, other = swapnodes(divergent, other)
-            resolutionparent = divergent.p1().node()
+        resolutionparent = repo[succsotherp1].node()
     else:
         msg = _(b"skipping %s: have a different parent than %s "
                 b"(not handled yet)\n") % (divergent, other)
         hint = _(b"| %(d)s, %(o)s are not based on the same changeset.\n"
-                 b"| With the current state of its implementation, \n"
+                 b"| With the current state of its implementation,\n"
                  b"| evolve does not work in that case.\n"
-                 b"| rebase one of them next to the other and run \n"
+                 b"| rebase one of them next to the other and run\n"
                  b"| this command again.\n"
                  b"| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n"
                  b"| - or:     hg rebase --dest 'p1(%(o)s)' -r %(d)s\n"
@@ -442,7 +411,7 @@
         ui.write_err(hint)
         return (False, b".")
 
-    return (True, divergent, other, resolutionparent, relocatereq)
+    return (True, resolutionparent)
 
 def _relocatedivergent(repo, orig, dest, evolvestate):
     """relocates a divergent commit and saves the evolve state"""
@@ -482,19 +451,39 @@
         ui.write_err(msg)
         return (False, b".")
     other = others[0]
+    evolvestate[b'divergent'] = divergent.node()
     evolvestate[b'other-divergent'] = other.node()
     evolvestate[b'base'] = base.node()
+    evolvestate[b'orig-divergent'] = divergent.node()
+
+    # sometimes we will relocate a node in case of different parents and we can
+    # encounter conflicts after relocation is done while solving
+    # content-divergence and if the user calls `hg evolve --stop`, we need to
+    # strip that relocated commit. However if `--all` is passed, we need to
+    # reset this value for each content-divergence resolution which we are doing
+    # below.
+    evolvestate[b'relocate-div'] = False
+    evolvestate[b'relocate-other'] = False
+    evolvestate[b'relocated-other'] = None
+    evolvestate[b'relocating-other'] = False
+    evolvestate[b'relocated-div'] = None
+    evolvestate[b'relocating-div'] = False
+    # in case or relocation we get a new other node, we need to store the old
+    # other for purposes like `--abort` or `--stop`
+    evolvestate[b'old-other'] = None
+    evolvestate[b'old-divergent'] = None
 
     # setup everything before merging two content-divergent csets
-    datatoproceed = _prepcontentdivresolution(ui, repo, divergent, other,
-                                              evolvestate)
-    if not datatoproceed[0]:
+    picked, resolutionparent = _pickresolutionparent(ui, repo, divergent,
+                                                     other, evolvestate)
+    if not picked:
         return (False, b".")
-    divergent, other, resolutionparent, relocatereq = datatoproceed[1:]
+    evolvestate[b'resolutionparent'] = resolutionparent
 
-    if relocatereq:
-        evolvestate['relocation-req'] = True
-    evolvestate[b'resolutionparent'] = resolutionparent
+    if divergent.p1().node() != resolutionparent:
+        evolvestate[b'relocate-div'] = True
+    if other.p1().node() != resolutionparent:
+        evolvestate[b'relocate-other'] = True
 
     if not ui.quiet or confirm:
         ui.write(_(b'merge:'), label=b'evolve.operation')
@@ -515,55 +504,45 @@
         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:
-        msg = _(b"ambiguous orphan resolution parent for %s")
-        raise error.Abort(msg % divergent.hex()[:12])
-    # relocate divergent cset to its obsolete parent's successsor
-    if succsdivp1 != divergent.p1().rev():
+    # relocate divergent cset to resolution parent
+    if evolvestate[b'relocate-div']:
         evolvestate[b'relocating-div'] = True
         ui.status(_(b'rebasing "divergent" content-divergent changeset %s on'
-                    b' %s\n' % (divergent, repo[succsdivp1])))
-        newdivergent = _relocatedivergent(repo, divergent, repo[succsdivp1],
+                    b' %s\n' % (divergent, repo[resolutionparent])))
+        newdivergent = _relocatedivergent(repo, divergent, repo[resolutionparent],
                                           evolvestate)
         evolvestate[b'old-divergent'] = divergent.node()
+        evolvestate[b'relocating-div'] = False
+        evolvestate[b'relocated-div'] = newdivergent
+        evolvestate[b'temprevs'].append(newdivergent)
+        evolvestate[b'divergent'] = newdivergent
         divergent = repo[newdivergent]
-        evolvestate[b'relocating-div'] = False
-        evolvestate[b'relocated-div'] = divergent.node()
-        evolvestate[b'temprevs'].append(divergent.node())
-        evolvestate[b'divergent'] = divergent.node()
 
-    # 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:
+    # relocate the other divergent to resolution parent
+    if evolvestate[b'relocate-other']:
         # relocating will help us understand during the time of conflicts that
         # whether conflicts occur at reloacting or they occured at merging
         # content divergent changesets
         evolvestate[b'relocating-other'] = True
         ui.status(_(b'rebasing "other" content-divergent changeset %s on'
-                    b' %s\n' % (other, divergent.p1())))
-        newother = _relocatedivergent(repo, other, divergent.p1(), evolvestate)
+                    b' %s\n' % (other, repo[resolutionparent])))
+        newother = _relocatedivergent(repo, other, repo[resolutionparent], evolvestate)
         evolvestate[b'old-other'] = other.node()
-        other = repo[newother]
         evolvestate[b'relocating-other'] = False
-        evolvestate[b'relocated-other'] = other.node()
-        evolvestate[b'temprevs'].append(other.node())
-        evolvestate[b'other-divergent'] = other.node()
+        evolvestate[b'relocated-other'] = newother
+        evolvestate[b'temprevs'].append(newother)
+        evolvestate[b'other-divergent'] = newother
+        other = repo[newother]
 
-    _mergecontentdivergents(repo, progresscb, divergent, other, base,
+    localside, otherside = divergent, other
+    if not otherside.mutable():
+        localside, otherside = other, divergent
+    _mergecontentdivergents(repo, progresscb, localside, otherside, base,
                             evolvestate)
     res, newnode = _completecontentdivergent(ui, repo, progresscb, divergent,
                                              other, base, evolvestate)
-    haspubdiv = not (divergent.mutable() and other.mutable())
-    if not haspubdiv:
+    haspublicdiv = not (divergent.mutable() and other.mutable())
+    if not haspublicdiv:
         return (res, newnode)
     else:
         publicdiv = repo[evolvestate[b'public-divergent']]
@@ -579,7 +558,7 @@
         if newnode == publicdiv.node():
             # case 2)
             pubstr = bytes(publicdiv)
-            othstr = bytes(other)
+            othstr = bytes(otherside)
             msg = _(b'content divergence resolution between %s '
                     b'(public) and %s has same content as %s, '
                     b'discarding %s\n')
@@ -591,26 +570,26 @@
         bumped = repo[newnode]
         return _resolvephasedivergent(ui, repo, prec=prec, bumped=bumped)
 
-def _mergecontentdivergents(repo, progresscb, divergent, other, base,
+def _mergecontentdivergents(repo, progresscb, local, other, base,
                             evolvestate):
-    if divergent not in repo[None].parents():
+    assert local != other
+    if local not in repo[None].parents():
         repo.ui.note(_(b"updating to \"local\" side of the conflict: %s\n") %
-                     divergent.hex()[:12])
-        hg.updaterepo(repo, divergent.node(), False)
+                     local.hex()[:12])
+        compat.update(local)
     # merging the two content-divergent changesets
     repo.ui.note(_(b"merging \"other\" %s changeset '%s'\n") %
                  (TROUBLES['CONTENTDIVERGENT'], other.hex()[:12]))
     if progresscb:
         progresscb()
     with state.saver(evolvestate):
-        mergeancestor = repo.changelog.isancestor(divergent.node(),
-                                                  other.node())
-        stats = merge.update(repo,
-                             other.node(),
-                             branchmerge=True,
-                             force=False,
-                             ancestor=base.node(),
-                             mergeancestor=mergeancestor)
+        mergeancestor = repo.changelog.isancestor(local.node(), other.node())
+        stats = compat._update(repo,
+                               other.node(),
+                               branchmerge=True,
+                               force=False,
+                               ancestor=base.node(),
+                               mergeancestor=mergeancestor)
         hg._showstats(repo, stats)
 
         # conflicts while merging content-divergent changesets
@@ -633,13 +612,12 @@
     resparent = evolvestate[b'resolutionparent']
 
     # whether we are solving public divergence
-    haspubdiv = False
+    haspublicdiv = False
     if evolvestate.get(b'public-divergent'):
-        haspubdiv = True
+        haspublicdiv = True
         publicnode = evolvestate[b'public-divergent']
         publicdiv = repo[publicnode]
-        othernode = evolvestate[b'other-divergent']
-        otherdiv = repo[othernode]
+        otherdiv = other if other.mutable() else divergent
 
         with repo.dirstate.parentchange(), compat.parentchange(repo):
             cmdrewrite.movedirstate(repo, repo[publicnode])
@@ -651,6 +629,7 @@
             # no changes, create markers to resolve divergence
             obsolete.createmarkers(repo, [(otherdiv, (publicdiv,))],
                                    operation=b'evolve')
+            compat.mergestate.clean(repo)
             return (True, publicnode)
 
     with repo.dirstate.parentchange(), compat.parentchange(repo):
@@ -676,6 +655,9 @@
         else:
             date = max(divergent.date(), other.date())
 
+    localside, otherside = divergent, other
+    if not otherside.mutable():
+        localside, otherside = other, divergent
     # We really want a new commit in order to avoid obsmarker cycles (otherwise
     # divergence resolutions done in separate repos may create markers in the
     # opposite directions). For that reason, we set ui.allowemptycommit and
@@ -685,20 +667,20 @@
         {(b'ui', b'allowemptycommit'): b'true'}, b'evolve'
     ):
         extra = {
-            b'divergence_source_local': divergent.hex(),
-            b'divergence_source_other': other.hex()
+            b'divergence_source_local': localside.hex(),
+            b'divergence_source_other': otherside.hex()
         }
         newnode = repo.commit(text=desc, user=user, date=date, extra=extra)
     new = repo[newnode]
-    hg.updaterepo(repo, new.rev(), False)
-    if haspubdiv and publicdiv == divergent:
+    compat.update(new)
+    if haspublicdiv and publicdiv == divergent:
         bypassphase(repo, (divergent, new), operation=b'evolve')
     else:
         obsolete.createmarkers(repo, [(divergent, (new,))],
                                operation=b'evolve')
 
     # creating markers and moving phases post-resolution
-    if haspubdiv and publicdiv == other:
+    if haspublicdiv and publicdiv == other:
         bypassphase(repo, (other, new), operation=b'evolve')
     else:
         obsolete.createmarkers(repo, [(other, (new,))], operation=b'evolve')
@@ -948,19 +930,34 @@
 
     tr = repo.currenttransaction()
     assert tr is not None
-    r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
-    if r.unresolvedcount: # some conflict
-        with repo.dirstate.parentchange(), compat.parentchange(repo):
-            repo.setparents(dest.node(), orig.node())
-            repo.dirstate.write(tr)
+    if repo._activebookmark:
+        repo.ui.status(_(b"(leaving bookmark %s)\n") % repo._activebookmark)
+    bookmarksmod.deactivate(repo)
+    nodenew = _relocatecommit(repo, orig, dest, pctx, keepbranch, commitmsg)
+    _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate)
+    return nodenew
+
+def _relocatecommit(repo, orig, dest, pctx, keepbranch, commitmsg):
+    if repo[b'.'].rev() != dest.rev():
+        compat._update(repo, dest, branchmerge=False, force=True)
+    if keepbranch:
+        repo.dirstate.setbranch(orig.branch())
+    if util.safehasattr(repo, 'currenttopic'):
+        # uurrgs
+        # there no other topic setter yet
+        if not orig.topic() and repo.vfs.exists(b'topic'):
+            repo.vfs.unlink(b'topic')
+        else:
+            with repo.vfs.open(b'topic', b'w') as f:
+                f.write(orig.topic())
+
+    stats = merge.graft(repo, orig, pctx, [b'destination', b'evolving'], True)
+
+    if stats.unresolvedcount: # some conflict
         hint = _(b"see 'hg help evolve.interrupted'")
         raise error.InterventionRequired(_(b"unresolved merge conflicts"),
                                          hint=hint)
-    nodenew = _relocatecommit(repo, orig, commitmsg)
-    _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate)
-    return nodenew
 
-def _relocatecommit(repo, orig, commitmsg):
     if commitmsg is None:
         commitmsg = orig.description()
     extra = dict(orig.extra())
@@ -978,55 +975,27 @@
                            date=orig.date(), extra=extra)
 
 def _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate):
-    destbookmarks = repo.nodebookmarks(dest.node())
-    nodesrc = orig.node()
-    oldbookmarks = repo.nodebookmarks(nodesrc)
-    bmchanges = []
-
     if nodenew is not None:
         obsolete.createmarkers(repo, [(orig, (repo[nodenew],))],
                                operation=b'evolve')
-        for book in oldbookmarks:
-            bmchanges.append((book, nodenew))
-            evolvestate[b'bookmarkchanges'].append((book, nodesrc))
+        bmdest = nodenew
     else:
         if category == b'orphan':
             repo.ui.status(_(b"evolution of %d:%s created no changes "
                              b"to commit\n") % (orig.rev(), orig))
         obsolete.createmarkers(repo, [(orig, ())], operation=b'evolve')
         # Behave like rebase, move bookmarks to dest
+        bmdest = dest.node()
+
+    nodesrc = orig.node()
+    oldbookmarks = repo.nodebookmarks(nodesrc)
+    if oldbookmarks:
+        bmchanges = []
         for book in oldbookmarks:
             evolvestate[b'bookmarkchanges'].append((book, nodesrc))
-            bmchanges.append((book, dest.node()))
-    for book in destbookmarks: # restore bookmark that rebase move
-        bmchanges.append((book, dest.node()))
-    if bmchanges:
+            bmchanges.append((book, bmdest))
         repo._bookmarks.applychanges(repo, tr, bmchanges)
 
-def _evolvemerge(repo, orig, dest, pctx, keepbranch):
-    """Used by the evolve function to merge dest on top of pctx.
-    return the same tuple as merge.graft"""
-    if repo[b'.'].rev() != dest.rev():
-        merge.update(repo,
-                     dest,
-                     branchmerge=False,
-                     force=True)
-    if repo._activebookmark:
-        repo.ui.status(_(b"(leaving bookmark %s)\n") % repo._activebookmark)
-    bookmarksmod.deactivate(repo)
-    if keepbranch:
-        repo.dirstate.setbranch(orig.branch())
-    if util.safehasattr(repo, 'currenttopic'):
-        # uurrgs
-        # there no other topic setter yet
-        if not orig.topic() and repo.vfs.exists(b'topic'):
-            repo.vfs.unlink(b'topic')
-        else:
-            with repo.vfs.open(b'topic', b'w') as f:
-                f.write(orig.topic())
-
-    return merge.graft(repo, orig, pctx, [b'destination', b'evolving'], True)
-
 instabilities_map = {
     b'contentdivergent': b"content-divergent",
     b'phasedivergent': b"phase-divergent"
@@ -1423,7 +1392,7 @@
         # obsolete (perhaps made obsolete by the current `hg evolve`)
         unfi = repo.unfiltered()
         succ = utility._singlesuccessor(repo, unfi[startnode])
-        hg.updaterepo(repo, repo[succ].node(), False)
+        compat.update(repo[succ])
     if repo[b'.'].node() != startnode:
         ui.status(_(b'working directory is now at %s\n') % repo[b'.'])
 
@@ -1641,7 +1610,7 @@
         # `hg next --evolve` in play
         if evolvestate[b'command'] != b'evolve':
             pctx = repo[b'.']
-            hg.updaterepo(repo, pctx.node(), True)
+            compat.clean_update(pctx)
             ui.status(_(b'evolve aborted\n'))
             ui.status(_(b'working directory is now at %s\n')
                       % pctx.hex()[:12])
@@ -1687,18 +1656,13 @@
                      b'bookmarkchanges': [], b'temprevs': [], b'obsmarkers': [],
                      b'update': shouldupdate}
         evolvestate.addopts(stateopts)
-        # lastsolved: keep track of successor of last troubled cset we evolved
-        # to confirm that if atop msg should be suppressed to remove redundancy
-        lastsolved = None
 
         activetopic = getattr(repo, 'currenttopic', b'')
         tr = repo.transaction(b"evolve")
         with util.acceptintervention(tr):
             for rev in revs:
-                lastsolved = _solveonerev(ui, repo, rev, evolvestate,
-                                          activetopic, dryrunopt,
-                                          confirmopt, progresscb,
-                                          targetcat, lastsolved)
+                _solveonerev(ui, repo, rev, evolvestate, activetopic, dryrunopt,
+                             confirmopt, progresscb, targetcat)
                 seen += 1
 
         if showprogress:
@@ -1707,7 +1671,7 @@
     _cleanup(ui, repo, startnode, shouldupdate)
 
 def _solveonerev(ui, repo, rev, evolvestate, activetopic, dryrunopt, confirmopt,
-                 progresscb, targetcat, lastsolved):
+                 progresscb, targetcat):
     """solves one trouble, including orphan merges
 
     Like _solveone(), this solves one trouble. Unlike _solveone(), it
@@ -1724,10 +1688,10 @@
     progresscb()
     ret = _solveone(ui, repo, curctx, evolvestate, dryrunopt,
                     confirmopt, progresscb, targetcat,
-                    lastsolved=lastsolved, stacktmplt=stacktmplt)
+                    stacktmplt=stacktmplt)
     if ret[0]:
         evolvestate[b'replacements'][curctx.node()] = ret[1]
-        lastsolved = ret[1]
+        evolvestate[b'lastsolved'] = ret[1]
     else:
         evolvestate[b'skippedrevs'].append(curctx.node())
 
@@ -1739,12 +1703,11 @@
                         stacktmplt=stacktmplt)
         if ret[0]:
             evolvestate[b'replacements'][curctx.node()] = ret[1]
-            lastsolved = ret[1]
+            evolvestate[b'lastsolved'] = ret[1]
         else:
             evolvestate[b'skippedrevs'].append(curctx.node())
 
         evolvestate[b'orphanmerge'] = False
-    return lastsolved
 
 def solveobswdp(ui, repo, opts):
     """this function updates to the successor of obsolete wdir parent"""
@@ -1798,20 +1761,20 @@
         if olddiv:
             with repo.wlock(), repo.lock():
                 repo = repo.unfiltered()
-                hg.updaterepo(repo, olddiv, True)
+                pctx = repo[olddiv]
+                compat.clean_update(pctx)
                 repair.strip(ui, repo, strips, False)
                 updated = True
-                pctx = repo[olddiv]
         elif oldother:
             with repo.wlock(), repo.lock():
                 repo = repo.unfiltered()
-                hg.updaterepo(repo, oldother, True)
+                pctx = repo[oldother]
+                compat.clean_update(pctx)
                 repair.strip(ui, repo, strips, False)
                 updated = True
-                pctx = repo[oldother]
     if not updated:
         pctx = repo[b'.']
-        hg.updaterepo(repo, pctx.node(), True)
+        compat.clean_update(pctx)
     ui.status(_(b'stopped the interrupted evolve\n'))
     ui.status(_(b'working directory is now at %s\n') % pctx)
 
@@ -1872,7 +1835,7 @@
                            for c in repo.set(b'roots(%ld)', evolvedrevs)]
 
         # updating the working directory
-        hg.updaterepo(repo, startnode, True)
+        compat.clean_update(repo[startnode])
 
         # Strip from the first evolved revision
         if evolvedrevs:
@@ -1900,7 +1863,7 @@
         evolvestate.load()
         if evolvestate[b'command'] != b'evolve':
             pctx = repo[b'.']
-            hg.updaterepo(repo, pctx.node(), True)
+            compat.clean_update(pctx)
             ui.status(_(b'evolve aborted\n'))
             ui.status(_(b'working directory is now at %s\n')
                       % pctx.hex()[:12])
@@ -1940,10 +1903,6 @@
     category = evolvestate[b'category']
     confirm = evolvestate[b'confirm']
     unfi = repo.unfiltered()
-    # lastsolved: keep track of successor of last troubled cset we
-    # evolved to confirm that if atop msg should be suppressed to remove
-    # redundancy
-    lastsolved = None
     activetopic = getattr(repo, 'currenttopic', b'')
     tr = repo.transaction(b"evolve")
     with util.acceptintervention(tr):
@@ -1963,11 +1922,10 @@
                 and curctx.node() not in evolvestate[b'skippedrevs']):
                 newnode = _solveone(ui, repo, curctx, evolvestate, False,
                                     confirm, progresscb, category,
-                                    lastsolved=lastsolved,
                                     stacktmplt=stacktmplt)
                 if newnode[0]:
                     evolvestate[b'replacements'][curctx.node()] = newnode[1]
-                    lastsolved = newnode[1]
+                    evolvestate[b'lastsolved'] = newnode[1]
                 else:
                     evolvestate[b'skippedrevs'].append(curctx.node())
             seen += 1
@@ -1976,44 +1934,47 @@
     """function to continue the interrupted content-divergence resolution."""
     tr = repo.transaction(b'evolve')
     with util.acceptintervention(tr):
-        divergent = evolvestate[b'divergent']
-        base = evolvestate[b'base']
         repo = repo.unfiltered()
+        divergent = repo[evolvestate[b'divergent']]
+        other = repo[evolvestate[b'other-divergent']]
+        assert divergent != other
+        base = repo[evolvestate[b'base']]
+        resolutionparent = repo[evolvestate.get(b'resolutionparent')]
         if evolvestate[b'relocating-div']:
             newdiv = _completerelocation(ui, repo, evolvestate)
             current = repo[evolvestate[b'current']]
             obsolete.createmarkers(repo, [(current, (repo[newdiv],))],
                                    operation=b'evolve')
-            evolvestate[b'old-divergent'] = repo[divergent].node()
+            evolvestate[b'old-divergent'] = divergent.node()
             evolvestate[b'relocating-div'] = False
             evolvestate[b'relocated-div'] = newdiv
             evolvestate[b'temprevs'].append(newdiv)
             evolvestate[b'divergent'] = newdiv
+            divergent = repo[newdiv]
 
-            relocatereq = evolvestate[b'relocation-req']
-            if relocatereq:
+            if evolvestate[b'relocate-other']:
                 divergent = repo[evolvestate[b'divergent']]
-                other = repo[evolvestate[b'other-divergent']]
                 evolvestate[b'relocating-other'] = True
                 ui.status(_(b'rebasing "other" content-divergent changeset %s on'
-                            b' %s\n' % (other, divergent.p1())))
-                newother = _relocatedivergent(repo, other, divergent.p1(),
+                            b' %s\n' % (other, resolutionparent)))
+                newother = _relocatedivergent(repo, other, resolutionparent,
                                               evolvestate)
                 evolvestate[b'old-other'] = other.node()
+                evolvestate[b'relocating-other'] = False
+                evolvestate[b'relocated-other'] = newother
+                evolvestate[b'temprevs'].append(newother)
+                evolvestate[b'other-divergent'] = newother
                 other = repo[newother]
-                evolvestate[b'relocating-other'] = False
-                evolvestate[b'relocated-other'] = other.node()
-                evolvestate[b'temprevs'].append(other.node())
-                evolvestate[b'other-divergent'] = other.node()
-                # continue the resolution by merging the content-divergence
-                _mergecontentdivergents(repo, progresscb,
-                                        divergent,
-                                        repo[newother],
-                                        repo[base],
-                                        evolvestate)
+            # continue the resolution by merging the content-divergent csets
+            localside, otherside = divergent, other
+            if not otherside.mutable():
+                localside, otherside = other, divergent
+            _mergecontentdivergents(repo, progresscb, localside, otherside,
+                                    base, evolvestate)
 
         if evolvestate[b'relocating-other']:
             newother = _completerelocation(ui, repo, evolvestate)
+            evolvestate[b'old-other'] = other.node()
             current = repo[evolvestate[b'current']]
             obsolete.createmarkers(repo, [(current, (repo[newother],))],
                                    operation=b'evolve')
@@ -2021,20 +1982,17 @@
             evolvestate[b'relocated-other'] = newother
             evolvestate[b'temprevs'].append(newother)
             evolvestate[b'other-divergent'] = newother
-            # continue the resolution by merging the content-divergence
-            _mergecontentdivergents(repo, progresscb,
-                                    repo[divergent],
-                                    repo[newother],
-                                    repo[base],
-                                    evolvestate)
+            other = repo[newother]
+            # continue the resolution by merging the content-divergent csets
+            localside, otherside = divergent, other
+            if not otherside.mutable():
+                localside, otherside = other, divergent
+            _mergecontentdivergents(repo, progresscb, localside, otherside,
+                                    base, evolvestate)
 
-        divergent = evolvestate[b'divergent']
-        other = evolvestate[b'other-divergent']
         res, newnode = _completecontentdivergent(ui, repo, progresscb,
-                                                 repo[divergent],
-                                                 repo[other],
-                                                 repo[base],
-                                                 evolvestate)
+                                                 divergent, other,
+                                                 base, evolvestate)
         origdivergent = evolvestate[b'orig-divergent']
         evolvestate[b'replacements'][origdivergent] = newnode
         ret = (res, newnode)
@@ -2145,7 +2103,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	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/metadata.py	Sat Oct 31 17:47:08 2020 +0800
@@ -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.3.dev'
-testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5'
+__version__ = b'10.1.0'
+testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6'
 minimumhgversion = b'4.6'
 buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/rewind.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/rewind.py	Sat Oct 31 17:47:08 2020 +0800
@@ -6,10 +6,11 @@
 from mercurial import (
     cmdutil,
     error,
-    hg,
+    node as nodemod,
     obsolete,
     obsutil,
     scmutil,
+    util,
 )
 
 from mercurial.utils import dateutil
@@ -17,9 +18,9 @@
 from mercurial.i18n import _
 
 from . import (
+    compat,
     exthelper,
     rewriteutil,
-    compat,
 )
 
 eh = exthelper.exthelper()
@@ -36,6 +37,8 @@
       _(b"rewind these revisions to their predecessors"), _(b'REV')),
      (b'k', b'keep', None,
       _(b"do not modify working directory during rewind")),
+     (b'n', b'dry-run', False,
+      _(b'do not perform actions, just print what would be done')),
      ],
     _(b'[--as-divergence] [--exact] [--keep] [--to REV]... [--from REV]...'),
     helpbasic=True,
@@ -88,25 +91,31 @@
         # 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:
-                ctx = unfi[rev]
-                ssets = obsutil.successorssets(repo, ctx.node(), cache=sscache)
-                if 1 < len(ssets):
-                    msg = _(b'rewind confused by divergence on %s') % ctx
-                    hint = _(b'solve divergence first or use "--as-divergence"')
-                    raise error.Abort(msg, hint=hint)
-                if ssets and ssets[0]:
-                    for succ in ssets[0]:
-                        successorsmap[succ].add(ctx.node())
+        for rev in targets:
+            ctx = unfi[rev]
+            ssets = obsutil.successorssets(repo, ctx.node(), cache=sscache)
+            if not opts['as_divergence'] and len(ssets) > 1:
+                msg = _(b'rewind confused by divergence on %s') % ctx
+                hint = _(b'solve divergence first or use "--as-divergence"')
+                raise error.Abort(msg, hint=hint)
+            for sset in ssets:
+                for succ in sset:
+                    successorsmap[succ].add(ctx.node())
+
+        if opts['as_divergence']:
+            successorsmap = {}
+
+        if opts['dry_run']:
+            ui.status(dryrun(unfi, targets, successorsmap, opts))
+            return
 
         # Check that we can rewind these changesets
         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)
 
@@ -120,10 +129,21 @@
                 relationships.append(rel)
                 if wctxp.node() == source:
                     update_target = newdest[-1]
+            # Use this condition as a proxy since the commit we care about
+            # (b99903534e06) didn't change any signatures.
+            if util.safehasattr(scmutil, 'nullrev'):
+                # hg <= 4.7 (b99903534e06)
+                destmap = util.sortdict()
+                for src, dest in relationships:
+                    destmap.setdefault(dest, []).append(src)
+                relationships = [
+                    (tuple(src), dest)
+                    for dest, src in destmap.items()
+                ]
             obsolete.createmarkers(unfi, relationships, operation=b'rewind')
             if update_target is not None:
                 if opts.get('keep'):
-                    hg.updaterepo(repo, oldctx, True)
+                    compat.clean_update(oldctx)
 
                     # This is largely the same as the implementation in
                     # strip.stripcmd() and cmdrewrite.cmdprune().
@@ -160,36 +180,36 @@
                         else:
                             cmdutil.revert(repo.ui, repo, oldctx, **revertopts)
                 else:
-                    hg.updaterepo(repo, update_target, False)
+                    compat.update(repo[update_target])
 
-    repo.ui.status(_(b'rewinded to %d changesets\n') % len(rewinded))
-    if relationships:
-        repo.ui.status(_(b'(%d changesets obsoleted)\n') % len(relationships))
+    ui.status(_(b'rewound to %d changesets\n') % len(targets))
+    if successorsmap:
+        ui.status(_(b'(%d changesets obsoleted)\n') % len(successorsmap))
     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.
@@ -224,3 +244,51 @@
                            flag=identicalflag, operation=b'rewind')
 
     return new
+
+def formatstatus(sources, destinations, asdiv=False):
+    """format a status line for one group of changesets for rewind
+
+    sources is a tuple of current successors, or None in case of rewinding to
+    an earlier version of a pruned commit or when --as-divergence is used.
+
+    destinations is a tuple of predecessors to rewind to.
+    """
+    if sources:
+        return b'rewinding %s to %d changesets: %s\n' % (
+            b' '.join(nodemod.short(src) for src in sources),
+            len(destinations),
+            b' '.join(nodemod.short(dest) for dest in destinations)
+        )
+    elif asdiv:
+        msg = b'recreating %d changesets: %s\n'
+    else:
+        msg = b'rewinding a pruned commit to %d changesets: %s\n'
+    return msg % (
+        len(destinations),
+        b' '.join(nodemod.short(dest) for dest in destinations)
+    )
+
+def dryrun(unfi, targets, successorsmap, opts):
+    """explain what would rewind do, given targets, successorsmap and opts
+
+    Returns a bytestring with one line per predecessors<->successors relation.
+    """
+    cl = unfi.changelog
+    todo = b''
+    rsm = collections.defaultdict(set) # reverse successors map
+    for src, destinations in successorsmap.items():
+        for dest in destinations:
+            rsm[dest].add(src)
+    if rsm and successorsmap:
+        if len(rsm) < len(successorsmap):
+            for dest in sorted(rsm, key=cl.rev):
+                sources = sorted(rsm[dest], key=cl.rev)
+                todo += formatstatus(sources, (dest,))
+        else:
+            for src in sorted(successorsmap, key=cl.rev):
+                destinations = sorted(successorsmap[src], key=cl.rev)
+                todo += formatstatus((src,), destinations)
+    else:
+        destinations = [unfi[rev].node() for rev in sorted(targets)]
+        todo = formatstatus(None, destinations, opts['as_divergence'])
+    return todo
--- a/hgext3rd/evolve/stablesort.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/stablesort.py	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/templatekw.py	Sat Oct 31 17:47:08 2020 +0800
@@ -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/evolve/utility.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/evolve/utility.py	Sat Oct 31 17:47:08 2020 +0800
@@ -209,8 +209,11 @@
     return revs[intidx - 1]
 
 def mergeusers(ui, base, divergent, other):
-    """ merges the users from two divergent changesets using three-way merge
-    and return the user that will be used as the author of resolved cset"""
+    """Return the merged user from two divergent changesets.
+
+    Perform merge using 3-way merge. If unable to merge, concatenate
+    the two users.
+    """
     baseuser = base.user()
     divuser = divergent.user()
     othuser = other.user()
--- a/hgext3rd/topic/__init__.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/topic/__init__.py	Sat Oct 31 17:47:08 2020 +0800
@@ -123,6 +123,36 @@
 
     [experimental]
     topic.server-gate-topic-changesets = yes
+
+Explicitly merging in the target branch
+=======================================
+
+By default, Mercurial will not let your merge a topic into its target branch if
+that topic is already based on the head of that branch. In other word,
+Mercurial will not let your create a merge that will eventually have two
+parents in the same branches, one parent being the ancestors of the other
+parent. This behavior can be lifted using the following config::
+
+    [experimental]
+    topic.linear-merge = allow-from-bare-branch
+
+When this option is set to `allow-from-bare-branch`, it is possible to merge a
+topic branch from a bare branch (commit an active topic (eg: public one))
+regardless of the topology. The result would typically looks like that::
+
+   @    summary: resulting merge commit
+   |\\   branch:  my-branch
+   | |
+   | o  summary: some more change in a topic, the merge "target"
+   | |  branch:  my-branch
+   | |  topic:   my-topic
+   | |
+   | o  summary: some change in a topic
+   |/   branch:  my-branch
+   |    topic:   my-topic
+   |
+   o    summary: previous head of the branch, the merge "source"
+   |    branch:  my-branch
 """
 
 from __future__ import absolute_import
@@ -202,9 +232,9 @@
               b'topic.active': b'green',
               }
 
-__version__ = b'0.19.3.dev'
+__version__ = b'0.20.0'
 
-testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5'
+testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6'
 minimumhgversion = b'4.6'
 buglink = b'https://bz.mercurial-scm.org/'
 
@@ -239,6 +269,9 @@
     configitem(b'experimental', b'topic.server-gate-topic-changesets',
                default=False,
     )
+    configitem(b'experimental', b'topic.linear-merge',
+               default="reject",
+    )
 
     def extsetup(ui):
         # register config that strictly belong to other code (thg, core, etc)
@@ -353,7 +386,11 @@
                      _(b"topic to push"), _(b'TOPIC')))
 
     extensions.wrapfunction(cmdutil, 'buildcommittext', committextwrap)
-    extensions.wrapfunction(merge, 'update', mergeupdatewrap)
+    if util.safehasattr(merge, '_update'):
+        extensions.wrapfunction(merge, '_update', mergeupdatewrap)
+    else:
+        # hg <= 5.5 (2c86b9587740)
+        extensions.wrapfunction(merge, 'update', mergeupdatewrap)
     # We need to check whether t0 or b0 or s0 is passed to override the default update
     # behaviour of changing topic and I can't find a better way
     # to do that as scmutil.revsingle returns the rev number and hence we can't
@@ -1284,7 +1321,37 @@
     isrebase = False
     ist0 = False
     try:
-        ret = orig(repo, node, branchmerge, force, *args, **kwargs)
+        mergemode = repo.ui.config(b'experimental', b'topic.linear-merge')
+
+        cleanup = lambda: None
+        oldrepo = repo
+        if mergemode == b'allow-from-bare-branch' and not repo[None].topic():
+            unfi = repo.unfiltered()
+            oldrepo = repo
+            old = unfi.__class__
+
+            class overridebranch(old):
+                def __getitem__(self, rev):
+                    ret = super(overridebranch, self).__getitem__(rev)
+                    if rev == node:
+                        b = ret.branch()
+                        t = ret.topic()
+                        if t:
+                            ret.branch = lambda: b'%s//%s' % (b, t)
+                    return ret
+            unfi.__class__ = overridebranch
+            if repo.filtername is not None:
+                repo = unfi.filtered(repo.filtername)
+
+            def cleanup():
+                unfi.__class__ = old
+
+        try:
+            ret = orig(repo, node, branchmerge, force, *args, **kwargs)
+        finally:
+            cleanup()
+            repo = oldrepo
+
         if not hastopicext(repo):
             return ret
         # The mergeupdatewrap function makes the destination's topic as the
--- a/hgext3rd/topic/discovery.py	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/topic/discovery.py	Sat Oct 31 17:47:08 2020 +0800
@@ -10,6 +10,7 @@
     error,
     exchange,
     extensions,
+    scmutil,
     util,
 )
 from . import (
@@ -186,7 +187,7 @@
             continue
         oldheads = [repo[n].rev() for n in b[1]]
         newheads = _filter_obsolete_heads(repo, oldheads)
-        data[b[0]] = len(newheads)
+        data[b[0]] = newheads
     return data
 
 def handlecheckheads(orig, op, inpart):
@@ -211,15 +212,27 @@
             repo.invalidatecaches()
             finalheads = _nbheads(repo)
             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)
+                newheads = finalheads.pop(branch, [])
+                if len(oldnb) < len(newheads):
+                    cl = repo.changelog
+                    newheads = sorted(set(newheads).difference(oldnb))
+                    heads = scmutil.nodesummaries(repo, [cl.node(r) for r in newheads])
+                    msg = _(
+                        b"push creates new heads on branch '%s': %s"
+                        % (branch, heads)
+                    )
                     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"'
-                            % branch)
-                    raise error.Abort(msg)
+                if 1 < len(newnb):
+                    cl = repo.changelog
+                    heads = scmutil.nodesummaries(repo, [cl.node(r) for r in newnb])
+                    msg = _(
+                        b"push creates new branch '%s' with multiple heads: %s"
+                        % (branch, heads)
+                    )
+                    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	Mon Oct 26 18:32:22 2020 +0100
+++ b/hgext3rd/topic/revset.py	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/setup.py	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/hghaveaddon.py	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-amend-patch.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-check-compat-strings.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-check-setup-manifest.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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
--- a/tests/test-evolve-abort-orphan.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-abort-orphan.t	Sat Oct 31 17:47:08 2020 +0800
@@ -111,12 +111,6 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     added c
   
-  changeset:   4:c41c793e0ef1
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  instability: orphan
-  summary:     added d
-  
 #if abortcommand
 when in dry-run mode
   $ hg abort --dry-run
@@ -209,7 +203,7 @@
   |   () draft
   | *  6:e83de241f751 added d
   | |   () draft
-  | @  5:e93a9161a274 added c
+  | %  5:e93a9161a274 added c
   | |   () draft
   | x  2:b1661037fa25 added b
   | |   () draft
@@ -303,7 +297,7 @@
   |/    () draft
   @  7:807e8e2ca559 added a
   |   () draft
-  | @  6:8f20d4390c21 foo to a
+  | %  6:8f20d4390c21 foo to a
   | |   () draft
   | | *  5:bcb1c47f8520 added d
   | | |   () draft
@@ -367,7 +361,7 @@
   |/    () draft
   @  7:807e8e2ca559 added a
   |   () draft
-  | @  6:8f20d4390c21 foo to a
+  | %  6:8f20d4390c21 foo to a
   | |   () draft
   | | *  5:bcb1c47f8520 added d
   | | |   () draft
@@ -452,7 +446,7 @@
   |/    () draft
   @  7:807e8e2ca559 added a
   |   () draft
-  | @  6:8f20d4390c21 foo to a
+  | %  6:8f20d4390c21 foo to a
   | |   () draft
   | x  1:2f913b0c9220 added a
   |/    () draft
@@ -520,7 +514,7 @@
   |   (bm1) draft
   o  4:a0086c17bfc7 added a
   |   () draft
-  | @  3:17509928e5bf added c
+  | %  3:17509928e5bf added c
   | |   () draft
   | x  2:9f0c80a55ddc added b
   | |   () draft
--- a/tests/test-evolve-abort-phasediv.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-abort-phasediv.t	Sat Oct 31 17:47:08 2020 +0800
@@ -123,14 +123,6 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     added c
   
-  changeset:   6:ddba58020bc0
-  tag:         tip
-  parent:      1:c7586e2a9264
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  instability: phase-divergent
-  summary:     added d
-  
 
   $ hg abort
   evolve aborted
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-content-divergent-case-A1.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Sat Oct 31 17:47:08 2020 +0800
@@ -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:[4] C
+  with: [5] C
+  base: [3] C
+  rebasing "divergent" content-divergent changeset 710d96992b40 on f6fbb35d8ac9
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 8f91b97f6f9a
+
+  $ hg log -G
+  @  7:8f91b97f6f9a (draft): C
+  |
+  o  2:f6fbb35d8ac9 (draft): B
+  |
+  o  1:f5bc6836db60 (draft): A
+  |
+  o  0:a9bdc8b26820 (public): O
+  
+  $ hg evolve -l
+
+  $ hg log -pl1
+  7:8f91b97f6f9a (draft): C 
+  diff -r f6fbb35d8ac9 -r 8f91b97f6f9a 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	Sat Oct 31 17:47:08 2020 +0800
@@ -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:[5] D
+  with: [6] D
+  base: [4] D
+  rebasing "divergent" content-divergent changeset 5d3fd66cb347 on d90aa47aa5d3
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at b2ef9cafa8ac
+
+  $ hg log -G
+  @  8:b2ef9cafa8ac (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-content-divergent-corner-cases.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-content-divergent-corner-cases.t	Sat Oct 31 17:47:08 2020 +0800
@@ -236,10 +236,10 @@
 
   $ cat >$TESTTMP/test_extension.py  << EOF
   > from mercurial import merge
-  > origupdate = merge.update
+  > origupdate = merge._update
   > def newupdate(*args, **kwargs):
   >   return origupdate(*args, **kwargs)
-  > merge.update = newupdate
+  > merge._update = newupdate
   > EOF
   $ cat >> $HGRCPATH << EOF
   > [extensions]
--- a/tests/test-evolve-content-divergent-interrupted.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-content-divergent-interrupted.t	Sat Oct 31 17:47:08 2020 +0800
@@ -198,6 +198,7 @@
   with: [7] added d
   base: [4] added d
   rebasing "divergent" content-divergent changeset e49523854bc8 on 4566502c0483
+  rebasing "other" content-divergent changeset f0f1694f123e on 4566502c0483
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -206,7 +207,7 @@
   [1]
 
   $ hg evolve --abort
-  1 new orphan changesets
+  2 new orphan changesets
   2 new content-divergent changesets
   evolve aborted
   working directory is now at 491e10505bae
@@ -303,10 +304,10 @@
       () draft
 
   $ hg evolve --content-divergent
-  merge:[7] added d
-  with: [6] added d
+  merge:[6] added d
+  with: [7] added d
   base: [4] added d
-  rebasing "other" content-divergent changeset 69bdd23a9b0d on ca1b80f7960a
+  rebasing "divergent" content-divergent changeset 69bdd23a9b0d on ca1b80f7960a
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
--- a/tests/test-evolve-content-divergent-relocation.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-content-divergent-relocation.t	Sat Oct 31 17:47:08 2020 +0800
@@ -184,12 +184,12 @@
       () [default] draft
 
   $ hg evolve --content-divergent
-  merge:[13] added x
-  with: [12] added foo to x
+  merge:[12] added foo to x
+  with: [13] added x
   base: [11] added x
-  rebasing "other" content-divergent changeset 5cf74a13db18 on be5a8b9faa8a
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at 4e844ab5e814
+  rebasing "divergent" content-divergent changeset 5cf74a13db18 on be5a8b9faa8a
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 618f8bd245a8
 
   $ hg exp
   # HG changeset patch
@@ -197,11 +197,11 @@
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
   # Branch bar
-  # Node ID 4e844ab5e8142d26fb1f0990486379f2410b0358
+  # Node ID 618f8bd245a8d1892954eb49a88a6ec5e500a5b5
   # Parent  be5a8b9faa8af54f115aa168a2c8564acb40c37d
   added foo to x
   
-  diff -r be5a8b9faa8a -r 4e844ab5e814 x
+  diff -r be5a8b9faa8a -r 618f8bd245a8 x
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/x	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
@@ -210,7 +210,7 @@
 The above `hg exp` and the following log call demonstrates that message, content
 and branch change is preserved in case of relocation
   $ hg glog
-  @  15:4e844ab5e814 added foo to x
+  @  15:618f8bd245a8 added foo to x
   |   () [bar] draft
   o  10:be5a8b9faa8a added d
   |   () [default] draft
@@ -234,10 +234,10 @@
   801b5920c7ea8d4ebdbc9cfc1e79e665dea2f211 5cf74a13db180e33dc2df8cd2aa70b21252a2a64 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'amend', 'user': 'test'}
   801b5920c7ea8d4ebdbc9cfc1e79e665dea2f211 45e15d6e88f5bd23ba360dff0c7591eca2d99f43 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
   5cf74a13db180e33dc2df8cd2aa70b21252a2a64 911c21adca136894a2b35f0a58fae7ee94fa5e61 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
-  45e15d6e88f5bd23ba360dff0c7591eca2d99f43 4e844ab5e8142d26fb1f0990486379f2410b0358 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'evolve', 'user': 'test'}
-  911c21adca136894a2b35f0a58fae7ee94fa5e61 4e844ab5e8142d26fb1f0990486379f2410b0358 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'evolve', 'user': 'test'}
+  911c21adca136894a2b35f0a58fae7ee94fa5e61 618f8bd245a8d1892954eb49a88a6ec5e500a5b5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'evolve', 'user': 'test'}
+  45e15d6e88f5bd23ba360dff0c7591eca2d99f43 618f8bd245a8d1892954eb49a88a6ec5e500a5b5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'evolve', 'user': 'test'}
   $ hg obslog --all
-  @    4e844ab5e814 (15) added foo to x
+  @    618f8bd245a8 (15) added foo to x
   |\     rewritten(description, branch, content) from 45e15d6e88f5 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |    rewritten from 911c21adca13 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
@@ -283,11 +283,11 @@
   $ hg amend
 
   $ hg glog
-  @  19:0faa43228ee7 added y
+  @  19:bfe170c9c964 added y
   |   () [bar] draft
   | *  17:9c32d35206fb added y
   | |   () [default] draft
-  o |  15:4e844ab5e814 added foo to x
+  o |  15:618f8bd245a8 added foo to x
   | |   () [bar] draft
   o |  10:be5a8b9faa8a added d
   | |   () [default] draft
@@ -301,10 +301,10 @@
       () [default] draft
 
   $ hg evolve --content-divergent
-  merge:[19] added y
-  with: [17] added y
+  merge:[17] added y
+  with: [19] added y
   base: [16] added y
-  rebasing "other" content-divergent changeset 9c32d35206fb on 4e844ab5e814
+  rebasing "divergent" content-divergent changeset 9c32d35206fb on 618f8bd245a8
   merging y
   warning: conflicts while merging y! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -317,12 +317,12 @@
   (no more unresolved files)
   continue: hg evolve --continue
   $ hg evolve --continue
-  working directory is now at ba11a9f47a2e
+  working directory is now at 7411ed2cf7cf
 
   $ hg glog
-  @  21:ba11a9f47a2e added y
+  @  21:7411ed2cf7cf added y
   |   () [bar] draft
-  o  15:4e844ab5e814 added foo to x
+  o  15:618f8bd245a8 added foo to x
   |   () [bar] draft
   o  10:be5a8b9faa8a added d
   |   () [default] draft
@@ -346,29 +346,29 @@
   801b5920c7ea8d4ebdbc9cfc1e79e665dea2f211 5cf74a13db180e33dc2df8cd2aa70b21252a2a64 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'amend', 'user': 'test'}
   801b5920c7ea8d4ebdbc9cfc1e79e665dea2f211 45e15d6e88f5bd23ba360dff0c7591eca2d99f43 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
   5cf74a13db180e33dc2df8cd2aa70b21252a2a64 911c21adca136894a2b35f0a58fae7ee94fa5e61 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
-  45e15d6e88f5bd23ba360dff0c7591eca2d99f43 4e844ab5e8142d26fb1f0990486379f2410b0358 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'evolve', 'user': 'test'}
-  911c21adca136894a2b35f0a58fae7ee94fa5e61 4e844ab5e8142d26fb1f0990486379f2410b0358 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'evolve', 'user': 'test'}
+  911c21adca136894a2b35f0a58fae7ee94fa5e61 618f8bd245a8d1892954eb49a88a6ec5e500a5b5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'evolve', 'user': 'test'}
+  45e15d6e88f5bd23ba360dff0c7591eca2d99f43 618f8bd245a8d1892954eb49a88a6ec5e500a5b5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'evolve', 'user': 'test'}
   ecf1d3992eb4d9700d441013fc4e89014692b461 9c32d35206fb5c3bf0ac814d410914d54a959a87 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-  ecf1d3992eb4d9700d441013fc4e89014692b461 1bb9315961377c18863281be7634ec8ff9b73ea9 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '68', 'operation': 'rebase', 'user': 'test'}
-  1bb9315961377c18863281be7634ec8ff9b73ea9 0faa43228ee7fb9c993f076cef7e2ac14c85954b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-  9c32d35206fb5c3bf0ac814d410914d54a959a87 0fc12918c8daa12ca83ce1e2a38e252b063b6276 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
-  0faa43228ee7fb9c993f076cef7e2ac14c85954b ba11a9f47a2e4a3380c4d5f80f64de41645a6681 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'evolve', 'user': 'test'}
-  0fc12918c8daa12ca83ce1e2a38e252b063b6276 ba11a9f47a2e4a3380c4d5f80f64de41645a6681 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '72', 'operation': 'evolve', 'user': 'test'}
+  ecf1d3992eb4d9700d441013fc4e89014692b461 491e9c6e22a4f265fad54d2060b9c2fa45f4301d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '68', 'operation': 'rebase', 'user': 'test'}
+  491e9c6e22a4f265fad54d2060b9c2fa45f4301d bfe170c9c96484157a071cd74e400426376c5e0e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
+  9c32d35206fb5c3bf0ac814d410914d54a959a87 7c47d5c3b6f5fb934723cdeb45c5819760988e1d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
+  7c47d5c3b6f5fb934723cdeb45c5819760988e1d 7411ed2cf7cfbc23f17711a72570787569177d69 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '72', 'operation': 'evolve', 'user': 'test'}
+  bfe170c9c96484157a071cd74e400426376c5e0e 7411ed2cf7cfbc23f17711a72570787569177d69 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'evolve', 'user': 'test'}
   $ hg obslog -r . --all
-  @    ba11a9f47a2e (21) added y
-  |\     amended(content) from 0faa43228ee7 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
-  | |    rewritten(branch, content) from 0fc12918c8da using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  @    7411ed2cf7cf (21) added y
+  |\     rewritten(branch, content) from 7c47d5c3b6f5 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    amended(content) from bfe170c9c964 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
-  x |  0faa43228ee7 (19) added y
-  | |    amended(content) from 1bb931596137 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
-  | |
-  | x  0fc12918c8da (20) added y
+  x |  7c47d5c3b6f5 (20) added y
   | |    rebased(parent) from 9c32d35206fb using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
-  x |  1bb931596137 (18) added y
+  | x  bfe170c9c964 (19) added y
+  | |    amended(content) from 491e9c6e22a4 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |
+  | x  491e9c6e22a4 (18) added y
   | |    rewritten(branch, parent) from ecf1d3992eb4 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
-  | x  9c32d35206fb (17) added y
+  x |  9c32d35206fb (17) added y
   |/     amended(content) from ecf1d3992eb4 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  ecf1d3992eb4 (16) added y
@@ -380,11 +380,11 @@
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 0fc12918c8daa12ca83ce1e2a38e252b063b6276
-  # Parent  4e844ab5e8142d26fb1f0990486379f2410b0358
+  # Node ID 7c47d5c3b6f5fb934723cdeb45c5819760988e1d
+  # Parent  618f8bd245a8d1892954eb49a88a6ec5e500a5b5
   added y
   
-  diff -r 4e844ab5e814 -r 0fc12918c8da y
+  diff -r 618f8bd245a8 -r 7c47d5c3b6f5 y
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
@@ -394,9 +394,9 @@
 ----------------------------------------------------------------------
 
   $ hg glog
-  @  21:ba11a9f47a2e added y
+  @  21:7411ed2cf7cf added y
   |   () [bar] draft
-  o  15:4e844ab5e814 added foo to x
+  o  15:618f8bd245a8 added foo to x
   |   () [bar] draft
   o  10:be5a8b9faa8a added d
   |   () [default] draft
@@ -436,13 +436,13 @@
   $ hg amend
 
   $ hg glog
-  @  25:ad2ec16eca1e added z
+  @  25:40a21b3496bc added z
   |   () [bar] draft
   | *  23:9bc2ace42175 added z
   | |   () [default] draft
-  o |  21:ba11a9f47a2e added y
+  o |  21:7411ed2cf7cf added y
   | |   () [bar] draft
-  o |  15:4e844ab5e814 added foo to x
+  o |  15:618f8bd245a8 added foo to x
   | |   () [bar] draft
   o |  10:be5a8b9faa8a added d
   | |   () [default] draft
@@ -456,10 +456,10 @@
       () [default] draft
 
   $ hg evolve --content-divergent --any
-  merge:[25] added z
-  with: [23] added z
+  merge:[23] added z
+  with: [25] added z
   base: [22] added z
-  rebasing "other" content-divergent changeset 9bc2ace42175 on ba11a9f47a2e
+  rebasing "divergent" content-divergent changeset 9bc2ace42175 on 7411ed2cf7cf
   merging y
   warning: conflicts while merging y! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
@@ -467,16 +467,16 @@
   [1]
 
   $ hg diff
-  diff -r ba11a9f47a2e y
+  diff -r 7411ed2cf7cf y
   --- a/y	Thu Jan 01 00:00:00 1970 +0000
   +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +1,5 @@
-  +<<<<<<< destination: ba11a9f47a2e bar - test: added y
+  +<<<<<<< destination: 7411ed2cf7cf bar - test: added y
    watbar
   +=======
   +foo
   +>>>>>>> evolving:    9bc2ace42175 - test: added z
-  diff -r ba11a9f47a2e z
+  diff -r 7411ed2cf7cf z
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/z	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
@@ -491,35 +491,41 @@
   evolving 23:9bc2ace42175 "added z"
   merging y
   warning: conflicts while merging y! (edit, then use 'hg resolve --mark')
-  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 1 files unresolved
   unresolved merge conflicts
   (see 'hg help evolve.interrupted')
   [1]
 
   $ hg diff
-  diff -r ad2ec16eca1e y
+  diff -r 635c0edd2e45 y
   --- a/y	Thu Jan 01 00:00:00 1970 +0000
   +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +1,5 @@
-  +<<<<<<< local: ad2ec16eca1e bar - test: added z
-   watbar
+  +<<<<<<< local: 635c0edd2e45 - test: added z
+   foo
   +=======
-  +foo
-  +>>>>>>> other: a7326a078527 - test: added z
+  +watbar
+  +>>>>>>> other: 40a21b3496bc bar - test: added z
+  diff -r 635c0edd2e45 z
+  --- a/z	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/z	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,1 @@
+  -z
+  +bar
 
   $ echo foo > y
   $ hg resolve -m
   (no more unresolved files)
   continue: hg evolve --continue
   $ hg evolve --continue
-  working directory is now at 45e55c9ab3d0
+  working directory is now at 664febd074c2
 
   $ hg glog
-  @  27:45e55c9ab3d0 added z
+  @  27:664febd074c2 added z
   |   () [bar] draft
-  o  21:ba11a9f47a2e added y
+  o  21:7411ed2cf7cf added y
   |   () [bar] draft
-  o  15:4e844ab5e814 added foo to x
+  o  15:618f8bd245a8 added foo to x
   |   () [bar] draft
   o  10:be5a8b9faa8a added d
   |   () [default] draft
@@ -538,17 +544,17 @@
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
   # Branch bar
-  # Node ID 45e55c9ab3d0b4746161fcbe5b6c062eb4647fdb
-  # Parent  ba11a9f47a2e4a3380c4d5f80f64de41645a6681
+  # Node ID 664febd074c2f9c5c4e03045dd688e93360f297c
+  # Parent  7411ed2cf7cfbc23f17711a72570787569177d69
   added z
   
-  diff -r ba11a9f47a2e -r 45e55c9ab3d0 y
+  diff -r 7411ed2cf7cf -r 664febd074c2 y
   --- a/y	Thu Jan 01 00:00:00 1970 +0000
   +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +1,1 @@
   -watbar
   +foo
-  diff -r ba11a9f47a2e -r 45e55c9ab3d0 z
+  diff -r 7411ed2cf7cf -r 664febd074c2 z
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/z	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
@@ -565,36 +571,36 @@
   801b5920c7ea8d4ebdbc9cfc1e79e665dea2f211 5cf74a13db180e33dc2df8cd2aa70b21252a2a64 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'amend', 'user': 'test'}
   801b5920c7ea8d4ebdbc9cfc1e79e665dea2f211 45e15d6e88f5bd23ba360dff0c7591eca2d99f43 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
   5cf74a13db180e33dc2df8cd2aa70b21252a2a64 911c21adca136894a2b35f0a58fae7ee94fa5e61 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
-  45e15d6e88f5bd23ba360dff0c7591eca2d99f43 4e844ab5e8142d26fb1f0990486379f2410b0358 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'evolve', 'user': 'test'}
-  911c21adca136894a2b35f0a58fae7ee94fa5e61 4e844ab5e8142d26fb1f0990486379f2410b0358 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'evolve', 'user': 'test'}
+  911c21adca136894a2b35f0a58fae7ee94fa5e61 618f8bd245a8d1892954eb49a88a6ec5e500a5b5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'evolve', 'user': 'test'}
+  45e15d6e88f5bd23ba360dff0c7591eca2d99f43 618f8bd245a8d1892954eb49a88a6ec5e500a5b5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '73', 'operation': 'evolve', 'user': 'test'}
   ecf1d3992eb4d9700d441013fc4e89014692b461 9c32d35206fb5c3bf0ac814d410914d54a959a87 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-  ecf1d3992eb4d9700d441013fc4e89014692b461 1bb9315961377c18863281be7634ec8ff9b73ea9 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '68', 'operation': 'rebase', 'user': 'test'}
-  1bb9315961377c18863281be7634ec8ff9b73ea9 0faa43228ee7fb9c993f076cef7e2ac14c85954b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-  9c32d35206fb5c3bf0ac814d410914d54a959a87 0fc12918c8daa12ca83ce1e2a38e252b063b6276 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
-  0faa43228ee7fb9c993f076cef7e2ac14c85954b ba11a9f47a2e4a3380c4d5f80f64de41645a6681 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'evolve', 'user': 'test'}
-  0fc12918c8daa12ca83ce1e2a38e252b063b6276 ba11a9f47a2e4a3380c4d5f80f64de41645a6681 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '72', 'operation': 'evolve', 'user': 'test'}
+  ecf1d3992eb4d9700d441013fc4e89014692b461 491e9c6e22a4f265fad54d2060b9c2fa45f4301d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '68', 'operation': 'rebase', 'user': 'test'}
+  491e9c6e22a4f265fad54d2060b9c2fa45f4301d bfe170c9c96484157a071cd74e400426376c5e0e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
+  9c32d35206fb5c3bf0ac814d410914d54a959a87 7c47d5c3b6f5fb934723cdeb45c5819760988e1d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'evolve', 'user': 'test'}
+  7c47d5c3b6f5fb934723cdeb45c5819760988e1d 7411ed2cf7cfbc23f17711a72570787569177d69 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '72', 'operation': 'evolve', 'user': 'test'}
+  bfe170c9c96484157a071cd74e400426376c5e0e 7411ed2cf7cfbc23f17711a72570787569177d69 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'evolve', 'user': 'test'}
   2048a66e8834bda866dcc8c479f091897816833e 9bc2ace42175da7380251fca97730f62ff5b9185 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-  2048a66e8834bda866dcc8c479f091897816833e ee19d6cc67048a1a7469268068a1413611720975 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '68', 'operation': 'rebase', 'user': 'test'}
-  ee19d6cc67048a1a7469268068a1413611720975 ad2ec16eca1eac4cf5045b496efa6205652e9a02 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-  9bc2ace42175da7380251fca97730f62ff5b9185 a7326a0785271a7bef2e812a9a742fb4256b4a3c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'evolve', 'user': 'test'}
-  ad2ec16eca1eac4cf5045b496efa6205652e9a02 45e55c9ab3d0b4746161fcbe5b6c062eb4647fdb 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'evolve', 'user': 'test'}
-  a7326a0785271a7bef2e812a9a742fb4256b4a3c 45e55c9ab3d0b4746161fcbe5b6c062eb4647fdb 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '72', 'operation': 'evolve', 'user': 'test'}
+  2048a66e8834bda866dcc8c479f091897816833e 8bf0130be95ef72377e39232335531426c2abcf9 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '68', 'operation': 'rebase', 'user': 'test'}
+  8bf0130be95ef72377e39232335531426c2abcf9 40a21b3496bc55fd0c0ac92d81b2930cfa4d4bef 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
+  9bc2ace42175da7380251fca97730f62ff5b9185 635c0edd2e45de215b2061b30aae5168708238d3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'evolve', 'user': 'test'}
+  635c0edd2e45de215b2061b30aae5168708238d3 664febd074c2f9c5c4e03045dd688e93360f297c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '72', 'operation': 'evolve', 'user': 'test'}
+  40a21b3496bc55fd0c0ac92d81b2930cfa4d4bef 664febd074c2f9c5c4e03045dd688e93360f297c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'evolve', 'user': 'test'}
   $ hg obslog --all
-  @    45e55c9ab3d0 (27) added z
-  |\     rewritten(branch, content) from a7326a078527 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
-  | |    amended(content) from ad2ec16eca1e using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  @    664febd074c2 (27) added z
+  |\     amended(content) from 40a21b3496bc using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |    rewritten(branch, content) from 635c0edd2e45 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
-  x |  a7326a078527 (26) added z
+  x |  40a21b3496bc (25) added z
+  | |    amended(content) from 8bf0130be95e using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  | |
+  | x  635c0edd2e45 (26) added z
   | |    rewritten(parent, content) from 9bc2ace42175 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
-  | x  ad2ec16eca1e (25) added z
-  | |    amended(content) from ee19d6cc6704 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
+  x |  8bf0130be95e (24) added z
+  | |    rewritten(branch, parent) from 2048a66e8834 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
-  x |  9bc2ace42175 (23) added z
-  | |    amended(content) from 2048a66e8834 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
-  | |
-  | x  ee19d6cc6704 (24) added z
-  |/     rewritten(branch, parent) from 2048a66e8834 using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
+  | x  9bc2ace42175 (23) added z
+  |/     amended(content) from 2048a66e8834 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   |
   x  2048a66e8834 (22) added z
   
--- a/tests/test-evolve-content-divergent-stack.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-content-divergent-stack.t	Sat Oct 31 17:47:08 2020 +0800
@@ -149,30 +149,189 @@
   with: [10] added b
   base: [2] added b
   rebasing "divergent" content-divergent changeset d5f148423c16 on df93a529fa42
+  rebasing "other" content-divergent changeset 88516dccf68a on df93a529fa42
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merge:[7] added c
   with: [11] added c
   base: [3] added c
-  rebasing "divergent" content-divergent changeset 3ce4be6d8e5e on 365f419742a3
+  rebasing "divergent" content-divergent changeset 3ce4be6d8e5e on aca5a88a1692
+  rebasing "other" content-divergent changeset 9e5fb1d5b955 on aca5a88a1692
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merge:[8] added d
   with: [12] added d
   base: [4] added d
-  rebasing "divergent" content-divergent changeset c72d2885eb51 on a18171c7d9e7
+  rebasing "divergent" content-divergent changeset c72d2885eb51 on 67e04919c9a4
+  rebasing "other" content-divergent changeset 15c781f93cac on 67e04919c9a4
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory is now at df93a529fa42
 
   $ hg glog
-  o  19:59d49942cf47 added d
+  o  22:e72164a86fb4 added d
   |   () [default] draft
-  o  17:a18171c7d9e7 added c
+  o  19:67e04919c9a4 added c
   |   () [default] draft
-  o  15:365f419742a3 added b
+  o  16:aca5a88a1692 added b
   |   () [default] draft
   @  13:df93a529fa42 watbar to a
   |   () [default] draft
   o  0:8fa14d15e168 added hgignore
       () [default] draft
+
+Testing --continue case when relocating "divergent"
+---------------------------------------------------
+When no relocation is required for "other", but "divergent"
+hit merge conflict in relocation. This test makes sure that
+content of two divergent csets merged correctly after the
+merge conflict.
+
+  $ hg log -r "desc('added c')" -p
+  changeset:   19:67e04919c9a4
+  parent:      16:aca5a88a1692
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     added c
+  
+  diff -r aca5a88a1692 -r 67e04919c9a4 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 @@
+  +foo
+  
+  $ hg strip -r 17: --hidden
+  saved backup bundle to $TESTTMP/stacktest/.hg/strip-backup/f1cd8f167491-3817ebd7-backup.hg
+  4 new orphan changesets
+  4 new content-divergent changesets
+  $ hg glog
+  o  16:aca5a88a1692 added b
+  |   () [default] draft
+  @  13:df93a529fa42 watbar to a
+  |   () [default] draft
+  | *  12:15c781f93cac added d
+  | |   () [default] draft
+  | *  11:9e5fb1d5b955 added c
+  | |   () [default] draft
+  | x  10:88516dccf68a added b
+  | |   () [default] draft
+  | x  9:82b74d5dc678 watbar to a
+  |/    () [default] draft
+  | *  8:c72d2885eb51 added d
+  | |   () [default] draft
+  | *  7:3ce4be6d8e5e added c
+  | |   () [default] draft
+  | x  6:d5f148423c16 added b
+  | |   () [default] draft
+  | x  5:8e222f257bbf watbar to a
+  |/    () [default] draft
+  o  0:8fa14d15e168 added hgignore
+      () [default] draft
+  $ hg next
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  [16] added b
+  $ echo conflict > c
+  $ hg amend -A
+  adding c
+
+add some changes on "other" side, to check later that merging performed correctly
+  $ hg up -r 11
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo new_file > newfile
+  $ hg amend -Am "added c and newfile"
+  adding newfile
+  $ hg glog
+  @  18:2ecfb60af48a added c and newfile
+  |   () [default] draft
+  | o  17:5907cbc074a0 added b
+  | |   () [default] draft
+  | o  13:df93a529fa42 watbar to a
+  | |   () [default] draft
+  | | *  12:15c781f93cac added d
+  | | |   () [default] draft
+  +---x  11:9e5fb1d5b955 added c
+  | |     () [default] draft
+  x |  10:88516dccf68a added b
+  | |   () [default] draft
+  x |  9:82b74d5dc678 watbar to a
+  |/    () [default] draft
+  | *  8:c72d2885eb51 added d
+  | |   () [default] draft
+  | *  7:3ce4be6d8e5e added c
+  | |   () [default] draft
+  | x  6:d5f148423c16 added b
+  | |   () [default] draft
+  | x  5:8e222f257bbf watbar to a
+  |/    () [default] draft
+  o  0:8fa14d15e168 added hgignore
+      () [default] draft
+
+  $ hg evolve --content-divergent -r 7
+  merge:[7] added c
+  with: [18] added c and newfile
+  base: [3] added c
+  rebasing "divergent" content-divergent changeset 3ce4be6d8e5e on 5907cbc074a0
+  merging c
+  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+  $ echo c > c
+  $ hg res -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+
+  $ hg evolve --continue
+  evolving 7:3ce4be6d8e5e "added c"
+  rebasing "other" content-divergent changeset 2ecfb60af48a on 5907cbc074a0
+  merging c
+  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+  $ hg diff
+  diff -r 5907cbc074a0 c
+  --- a/c	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/c	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,5 @@
+  +<<<<<<< destination: 5907cbc074a0 - test: added b
+   conflict
+  +=======
+  +foo
+  +>>>>>>> evolving:    2ecfb60af48a - test: added c and newfile
+  diff -r 5907cbc074a0 newfile
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/newfile	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +new_file
+
+  $ echo c > c
+  $ hg res -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+  $ hg evolve -c
+  evolving 18:2ecfb60af48a "added c and newfile"
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 7e8d59a0286a
+
+  $ hg log -p -l1
+  changeset:   21:7e8d59a0286a
+  tag:         tip
+  parent:      17:5907cbc074a0
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     added c and newfile
+  
+  diff -r 5907cbc074a0 -r 7e8d59a0286a c
+  --- a/c	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/c	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +1,1 @@
+  -conflict
+  +c
+  diff -r 5907cbc074a0 -r 7e8d59a0286a newfile
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/newfile	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +new_file
+  
+
 Resolving content-divergence of a stack with different parents
 ---------------------------------------------------------
 
@@ -311,39 +470,39 @@
       () [default] draft
 
   $ hg evolve --all --content-divergent
-  merge:[10] added a
-  with: [5] watbar to a
+  merge:[5] watbar to a
+  with: [10] added a
   base: [1] added a
-  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[11] added b
-  with: [6] added b
+  rebasing "divergent" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[6] added b
+  with: [11] added b
   base: [2] added b
-  rebasing "divergent" content-divergent changeset 6eb54b5af3fb on ebb749d2ace8
-  rebasing "other" content-divergent changeset d5f148423c16 on ebb749d2ace8
+  rebasing "divergent" content-divergent changeset d5f148423c16 on 7e67dfb7ee31
+  rebasing "other" content-divergent changeset 6eb54b5af3fb on 7e67dfb7ee31
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[12] added c
-  with: [7] added c
+  merge:[7] added c
+  with: [12] added c
   base: [3] added c
-  rebasing "divergent" content-divergent changeset 8ed612937375 on 9437917ebe8d
-  rebasing "other" content-divergent changeset 3ce4be6d8e5e on 9437917ebe8d
+  rebasing "divergent" content-divergent changeset 3ce4be6d8e5e on 80cec1b1c90f
+  rebasing "other" content-divergent changeset 8ed612937375 on 80cec1b1c90f
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[13] added d
-  with: [8] added d
+  merge:[8] added d
+  with: [13] added d
   base: [4] added d
-  rebasing "divergent" content-divergent changeset d45f050514c2 on 8e574c12775a
-  rebasing "other" content-divergent changeset c72d2885eb51 on 8e574c12775a
+  rebasing "divergent" content-divergent changeset c72d2885eb51 on 7e370616fb2b
+  rebasing "other" content-divergent changeset d45f050514c2 on 7e370616fb2b
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at ebb749d2ace8
+  working directory is now at 7e67dfb7ee31
 
   $ hg glog
-  o  24:5ab9b88fbead added d
+  o  24:469255caf534 added d
   |   () [default] draft
-  o  21:8e574c12775a added c
+  o  21:7e370616fb2b added c
   |   () [default] draft
-  o  18:9437917ebe8d added b
+  o  18:80cec1b1c90f added b
   |   () [default] draft
-  @  15:ebb749d2ace8 watbar to a
+  @  15:7e67dfb7ee31 watbar to a
   |   () [default] draft
   o  9:2228e3b74514 add newfile
   |   () [default] draft
@@ -355,30 +514,43 @@
 
   $ hg strip 14: --hidden
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  saved backup bundle to $TESTTMP/stackrepo1/.hg/strip-backup/ebb749d2ace8-0585ef5a-backup.hg
+  saved backup bundle to $TESTTMP/stackrepo1/.hg/strip-backup/7e67dfb7ee31-ff5c6a6d-backup.hg
   8 new content-divergent changesets
 
 Prepare repo to have merge conflicts
   $ hg up -r "max(desc('added a'))"
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg evolve -r . --content-divergent
-  merge:[10] added a
-  with: [5] watbar to a
+  merge:[5] watbar to a
+  with: [10] added a
   base: [1] added a
-  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  rebasing "divergent" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   6 new orphan changesets
-  working directory is now at ebb749d2ace8
+  working directory is now at 7e67dfb7ee31
   $ echo b_conflict > b
   $ hg amend -A
   adding b
 
 Let's try to evolve stack
   $ hg evolve --content-divergent
-  merge:[11] added b
-  with: [6] added b
+  merge:[6] added b
+  with: [11] added b
   base: [2] added b
-  rebasing "divergent" content-divergent changeset 6eb54b5af3fb on 73a960350ce9
+  rebasing "divergent" content-divergent changeset d5f148423c16 on c758af982013
+  merging b
+  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+  unresolved merge conflicts
+  (see 'hg help evolve.interrupted')
+  [1]
+
+  $ echo b > b
+  $ hg res -m
+  (no more unresolved files)
+  continue: hg evolve --continue
+  $ hg evolve --continue
+  evolving 6:d5f148423c16 "added b"
+  rebasing "other" content-divergent changeset 6eb54b5af3fb on c758af982013
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
@@ -391,41 +563,28 @@
   continue: hg evolve --continue
   $ hg evolve --continue
   evolving 11:6eb54b5af3fb "added b"
-  rebasing "other" content-divergent changeset d5f148423c16 on 73a960350ce9
-  merging b
-  warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
-  unresolved merge conflicts
-  (see 'hg help evolve.interrupted')
-  [1]
-
-  $ echo b > b
-  $ hg res -m
-  (no more unresolved files)
-  continue: hg evolve --continue
-  $ hg evolve --continue
-  evolving 6:d5f148423c16 "added b"
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[12] added c
-  with: [7] added c
+  merge:[7] added c
+  with: [12] added c
   base: [3] added c
-  rebasing "divergent" content-divergent changeset 8ed612937375 on 1b637ec00f24
-  rebasing "other" content-divergent changeset 3ce4be6d8e5e on 1b637ec00f24
+  rebasing "divergent" content-divergent changeset 3ce4be6d8e5e on 1a79fc84e761
+  rebasing "other" content-divergent changeset 8ed612937375 on 1a79fc84e761
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[13] added d
-  with: [8] added d
+  merge:[8] added d
+  with: [13] added d
   base: [4] added d
-  rebasing "divergent" content-divergent changeset d45f050514c2 on fbfadbe143d8
-  rebasing "other" content-divergent changeset c72d2885eb51 on fbfadbe143d8
+  rebasing "divergent" content-divergent changeset c72d2885eb51 on 6c228f1e5409
+  rebasing "other" content-divergent changeset d45f050514c2 on 6c228f1e5409
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at 73a960350ce9
+  working directory is now at c758af982013
   $ hg glog
-  o  25:bf2836f524a3 added d
+  o  25:957008d45543 added d
   |   () [default] draft
-  o  22:fbfadbe143d8 added c
+  o  22:6c228f1e5409 added c
   |   () [default] draft
-  o  19:1b637ec00f24 added b
+  o  19:1a79fc84e761 added b
   |   () [default] draft
-  @  16:73a960350ce9 watbar to a
+  @  16:c758af982013 watbar to a
   |   () [default] draft
   o  9:2228e3b74514 add newfile
   |   () [default] draft
@@ -436,7 +595,7 @@
 --------------------------------------------------------------
   $ hg strip 14: --hidden
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  saved backup bundle to $TESTTMP/stackrepo1/.hg/strip-backup/ebb749d2ace8-c2c1e937-backup.hg
+  saved backup bundle to $TESTTMP/stackrepo1/.hg/strip-backup/c758af982013-0af4fee9-backup.hg
   8 new content-divergent changesets
 
 Insert conflicting changes in between the stack of content-div csets
@@ -469,16 +628,16 @@
   
 Now let's try to evolve stack
   $ hg evolve --content-divergent
-  merge:[10] added a
-  with: [5] watbar to a
+  merge:[5] watbar to a
+  with: [10] added a
   base: [1] added a
-  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  rebasing "divergent" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merge:[14] added b
   with: [17] added b
   base: [2] added b
-  rebasing "divergent" content-divergent changeset 2a955e808c53 on ebb749d2ace8
-  rebasing "other" content-divergent changeset 509103439e5e on ebb749d2ace8
+  rebasing "divergent" content-divergent changeset 2a955e808c53 on 7e67dfb7ee31
+  rebasing "other" content-divergent changeset 509103439e5e on 7e67dfb7ee31
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -570,16 +729,16 @@
      summary:     added hgignore
   
   $ hg evolve --content-divergent
-  merge:[10] added a
-  with: [5] watbar to a
+  merge:[5] watbar to a
+  with: [10] added a
   base: [1] added a
-  rebasing "other" content-divergent changeset 8e222f257bbf on 2228e3b74514
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  rebasing "divergent" content-divergent changeset 8e222f257bbf on 2228e3b74514
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merge:[14] added b
   with: [17] added b
   base: [2] added b
-  rebasing "divergent" content-divergent changeset 2a955e808c53 on ebb749d2ace8
-  rebasing "other" content-divergent changeset 509103439e5e on ebb749d2ace8
+  rebasing "divergent" content-divergent changeset 2a955e808c53 on 7e67dfb7ee31
+  rebasing "other" content-divergent changeset 509103439e5e on 7e67dfb7ee31
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -593,7 +752,7 @@
   stopped the interrupted evolve
   working directory is now at 2a955e808c53
   $ hg log -G
-  o  changeset:   21:ebb749d2ace8
+  o  changeset:   21:7e67dfb7ee31
   |  tag:         tip
   |  parent:      9:2228e3b74514
   |  user:        test
@@ -641,7 +800,7 @@
   +---x  changeset:   10:c04ff147ef79
   | |    user:        test
   | |    date:        Thu Jan 01 00:00:00 1970 +0000
-  | |    obsolete:    rewritten using evolve as 21:ebb749d2ace8
+  | |    obsolete:    rewritten using evolve as 21:7e67dfb7ee31
   | |    summary:     added a
   | |
   o |  changeset:   9:2228e3b74514
@@ -654,7 +813,7 @@
   |/   parent:      0:8fa14d15e168
   |    user:        test
   |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    obsolete:    rebased using evolve as 21:ebb749d2ace8
+  |    obsolete:    rebased using evolve as 21:7e67dfb7ee31
   |    summary:     watbar to a
   |
   o  changeset:   0:8fa14d15e168
@@ -663,7 +822,7 @@
      summary:     added hgignore
   
   $ hg obslog -r 'desc("watbar to a")' --all
-  o    ebb749d2ace8 (21) watbar to a
+  o    7e67dfb7ee31 (21) watbar to a
   |\     rewritten from 186bdc2cdfa2 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |    rewritten(description, content) from c04ff147ef79 using evolve by test (Thu Jan 01 00:00:00 1970 +0000)
   | |
@@ -699,8 +858,8 @@
   merge:[14] added b
   with: [17] added b
   base: [2] added b
-  rebasing "divergent" content-divergent changeset 2a955e808c53 on ebb749d2ace8
-  rebasing "other" content-divergent changeset 509103439e5e on ebb749d2ace8
+  rebasing "divergent" content-divergent changeset 2a955e808c53 on 7e67dfb7ee31
+  rebasing "other" content-divergent changeset 509103439e5e on 7e67dfb7ee31
   merging b
   warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -716,16 +875,16 @@
   merge:[15] added c
   with: [18] added c
   base: [3] added c
-  rebasing "divergent" content-divergent changeset 48b0f803817a on 32bceb5bc91e
-  rebasing "other" content-divergent changeset eaf34afe4df3 on 32bceb5bc91e
+  rebasing "divergent" content-divergent changeset 48b0f803817a on ddfcba2aac91
+  rebasing "other" content-divergent changeset eaf34afe4df3 on ddfcba2aac91
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merge:[16] added d
   with: [19] added d
   base: [4] added d
-  rebasing "divergent" content-divergent changeset 91c8ccb9c241 on a87eb9f90df6
-  rebasing "other" content-divergent changeset c351be27f199 on a87eb9f90df6
+  rebasing "divergent" content-divergent changeset 91c8ccb9c241 on bb396302d792
+  rebasing "other" content-divergent changeset c351be27f199 on bb396302d792
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  working directory is now at 32bceb5bc91e
+  working directory is now at ddfcba2aac91
 
   $ hg evolve -l
 
@@ -852,16 +1011,16 @@
   
 
   $ hg evolve --content-divergent
-  merge:[6] b
-  with: [3] b
+  merge:[3] b
+  with: [6] b
   base: [1] b
-  rebasing "other" content-divergent changeset 5b2d00df9c4e on 980f7dc84c29
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[7] c
-  with: [4] c
+  rebasing "divergent" content-divergent changeset 5b2d00df9c4e on 980f7dc84c29
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[4] c
+  with: [7] c
   base: [2] c
-  rebasing "divergent" content-divergent changeset ef4885dea3da on 6813d99f0a9d
-  rebasing "other" content-divergent changeset fef59171875e on 6813d99f0a9d
+  rebasing "divergent" content-divergent changeset fef59171875e on bfba946a2829
+  rebasing "other" content-divergent changeset ef4885dea3da on bfba946a2829
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Expected result:
@@ -869,15 +1028,15 @@
 Changeset with description "c" only adds file "c" with content "c".
 
   $ hg glog -l2 -p
-  o  12:d5a8159e883f c
-  |   () [default] draftdiff -r 6813d99f0a9d -r d5a8159e883f c
+  o  12:a5abd6c7f9d8 c
+  |   () [default] draftdiff -r bfba946a2829 -r a5abd6c7f9d8 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 @@
   |  +c
   |
-  o  9:6813d99f0a9d b
-  |   () [default] draftdiff -r 980f7dc84c29 -r 6813d99f0a9d b
+  o  9:bfba946a2829 b
+  |   () [default] draftdiff -r 980f7dc84c29 -r bfba946a2829 b
   ~  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
      +++ b/b	Thu Jan 01 00:00:00 1970 +0000
      @@ -0,0 +1,1 @@
@@ -955,24 +1114,24 @@
       () [default] draft
 
   $ hg evolve -r 4+5 --content-divergent
-  merge:[8] added foo
-  with: [4] added foo
+  merge:[4] added foo
+  with: [8] added foo
   base: [1] added foo
-  rebasing "other" content-divergent changeset 8a2d93492f59 on 9703820a7d5b
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merge:[9] added bar and car
-  with: [5] added bar and car
+  rebasing "divergent" content-divergent changeset 8a2d93492f59 on 9703820a7d5b
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merge:[5] added bar and car
+  with: [9] added bar and car
   base: [2] added bar and car
-  rebasing "divergent" content-divergent changeset 7dd5b9d42ef3 on 586526ab82c5
-  rebasing "other" content-divergent changeset f4ed107810a7 on 586526ab82c5
+  rebasing "divergent" content-divergent changeset f4ed107810a7 on 3e0693d8f69b
+  rebasing "other" content-divergent changeset 7dd5b9d42ef3 on 3e0693d8f69b
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   2 new orphan changesets
-  working directory is now at 586526ab82c5
+  working directory is now at 3e0693d8f69b
 
   $ hg glog
-  o  15:e905bb19fd7e added bar and car
+  o  15:5382795441b8 added bar and car
   |   () [default] draft
-  @  12:586526ab82c5 added foo
+  @  12:3e0693d8f69b added foo
   |   () [default] draft
   | *  10:9a1f460df8b5 added dar
   | |   () [default] draft
@@ -991,17 +1150,33 @@
   o  0:bde1d2b6b5e5 added base
       () [default] draft
 
+  $ hg reb -r 10 -d 7
+  rebasing 10:9a1f460df8b5 "added dar"
+  $ hg up 0 -q
+  $ echo alpha > alpha
+  $ hg ci -Am "added alpha"
+  adding alpha
+  created new head
+  $ hg reb -r 6 -d 'desc("added alpha")'
+  rebasing 6:57a3f8edf065 "added dar"
+  $ hg evolve --content-divergent
+  skipping 8b68d5104188: have a different parent than cf9a46e19942 (not handled yet)
+  | 8b68d5104188, cf9a46e19942 are not based on the same changeset.
+  | With the current state of its implementation,
+  | evolve does not work in that case.
+  | rebase one of them next to the other and run
+  | this command again.
+  | - either: hg rebase --dest 'p1(8b68d5104188)' -r cf9a46e19942
+  | - or:     hg rebase --dest 'p1(cf9a46e19942)' -r 8b68d5104188
+
+  $ hg strip -r 16:
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/multiplesuccs2/.hg/strip-backup/8b68d5104188-e72c2390-backup.hg
+  2 new orphan changesets
   $ echo bar > bar; hg ci -Aqm "added bar"
   $ echo car > car; hg ci -Aqm "added car"
   $ hg prune --split -s 16+17 -r 15
   1 changesets pruned
 
   $ hg evolve --content-divergent
-  skipping 57a3f8edf065: have a different parent than 9a1f460df8b5 (not handled yet)
-  | 57a3f8edf065, 9a1f460df8b5 are not based on the same changeset.
-  | With the current state of its implementation, 
-  | evolve does not work in that case.
-  | rebase one of them next to the other and run 
-  | this command again.
-  | - either: hg rebase --dest 'p1(57a3f8edf065)' -r 9a1f460df8b5
-  | - or:     hg rebase --dest 'p1(9a1f460df8b5)' -r 57a3f8edf065
+  skipping 57a3f8edf065: ambiguous orphan resolution parent for 57a3f8edf065
--- a/tests/test-evolve-cycles.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-cycles.t	Sat Oct 31 17:47:08 2020 +0800
@@ -358,7 +358,7 @@
 
 Check the json output is valid in this case
 
-  $ hg obslog "desc(D)" --hidden --no-graph -Tjson | python -m json.tool
+  $ hg obslog "desc(D)" --hidden --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -559,7 +559,7 @@
       }
   ]
 
-  $ hg obslog "desc(D)" --hidden --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog "desc(D)" --hidden --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
--- a/tests/test-evolve-interrupted.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-interrupted.t	Sat Oct 31 17:47:08 2020 +0800
@@ -52,7 +52,7 @@
   $ hg l
   @  2 apricot and blueberry
   
-  %  1 banana
+  *  1 banana
   |
   x  0 apricot
   
@@ -80,7 +80,7 @@
   $ hg l
   @  2 apricot and blueberry
   
-  %  1 banana
+  *  1 banana
   |
   x  0 apricot
   
--- a/tests/test-evolve-issue5958.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-issue5958.t	Sat Oct 31 17:47:08 2020 +0800
@@ -86,9 +86,50 @@
   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
+  rebasing "other" content-divergent changeset 08bc7ba82799 on a24ed8ad918c
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory is now at d519bd54a945
+
+  $ hg log -G
+  @  changeset:   8:d519bd54a945
+  |  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, 2 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/issue5958/.hg/strip-backup/d519bd54a945-5f6b2f65-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: [7] add foo.txt
+  base: [1] add foo.txt
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory is now at d519bd54a945
+  $ hg log -G
+  @  changeset:   8:d519bd54a945
+  |  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-then-fold.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-amend-then-fold.t	Sat Oct 31 17:47:08 2020 +0800
@@ -185,7 +185,7 @@
   |
   x  0dec01379d3b (2) B0
   
-  $ hg obslog eb5a0daa2192 --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog eb5a0daa2192 --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -277,7 +277,7 @@
           "shortdescription": "A0"
       }
   ]
-  $ hg obslog eb5a0daa2192 --no-graph -Tjson | python -m json.tool
+  $ hg obslog eb5a0daa2192 --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
--- a/tests/test-evolve-obshistory-amend.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-amend.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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,29 +133,29 @@
   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
   
-  $ hg obslog 4ae3a4151de9 --no-graph -Tjson | python -m json.tool
+  $ hg obslog 4ae3a4151de9 --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -188,7 +194,7 @@
   $ hg obslog --hidden --patch 471f378eab4c
   x  471f378eab4c (1) A0
   
-  $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | python -m json.tool
+  $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
--- a/tests/test-evolve-obshistory-content-divergent.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-content-divergent.t	Sat Oct 31 17:47:08 2020 +0800
@@ -137,7 +137,7 @@
        reworded(description) as 65b757b745b9 using amend by test (Thu Jan 01 00:00:00 1970 +0000)
        reworded(description) as fdf9bde5129a using amend by test (Thu Jan 01 00:00:00 1970 +0000)
   
-  $ hg obslog --hidden 471f378eab4c --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog --hidden 471f378eab4c --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -188,7 +188,7 @@
           "shortdescription": "A0"
       }
   ]
-  $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | python -m json.tool
+  $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -315,7 +315,7 @@
   |
   x  471f378eab4c (1) A0
   
-  $ hg obslog '65b757b745b9+fdf9bde5129a' --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog '65b757b745b9+fdf9bde5129a' --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -376,7 +376,7 @@
           "shortdescription": "A1"
       }
   ]
-  $ hg obslog '65b757b745b9+fdf9bde5129a' --no-graph -Tjson | python -m json.tool
+  $ hg obslog '65b757b745b9+fdf9bde5129a' --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
--- a/tests/test-evolve-obshistory-fold.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-fold.t	Sat Oct 31 17:47:08 2020 +0800
@@ -166,7 +166,7 @@
          +B0
   
   
-  $ hg obslog eb5a0daa2192 --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog eb5a0daa2192 --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -241,7 +241,7 @@
    /
   x  471f378eab4c (1) A0
   
-  $ hg obslog eb5a0daa2192 --no-graph -Tjson | python -m json.tool
+  $ hg obslog eb5a0daa2192 --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
--- a/tests/test-evolve-obshistory-lots-of-splits.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-lots-of-splits.t	Sat Oct 31 17:47:08 2020 +0800
@@ -225,7 +225,7 @@
   |
   x  de7290d8b885 (1) A0
   
-  $ hg obslog de7290d8b885 --hidden --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog de7290d8b885 --hidden --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -273,7 +273,7 @@
   |
   x  de7290d8b885 (1) A0
   
-  $ hg obslog c7f044602e9b --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog c7f044602e9b --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -312,7 +312,7 @@
           "shortdescription": "A0"
       }
   ]
-  $ hg obslog c7f044602e9b --no-graph -Tjson | python -m json.tool
+  $ hg obslog c7f044602e9b --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
--- a/tests/test-evolve-obshistory-phase-divergent.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-phase-divergent.t	Sat Oct 31 17:47:08 2020 +0800
@@ -105,7 +105,7 @@
   |
   @  471f378eab4c (1) A0
   
-  $ hg obslog --hidden 471f378eab4c --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog --hidden 471f378eab4c --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -135,7 +135,7 @@
           "shortdescription": "A0"
       }
   ]
-  $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | python -m json.tool
+  $ hg obslog --hidden 471f378eab4c --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -186,7 +186,7 @@
   |
   @  471f378eab4c (1) A0
   
-  $ hg obslog 'fdf9bde5129a+471f378eab4c' --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog 'fdf9bde5129a+471f378eab4c' --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
@@ -221,7 +221,7 @@
           "shortdescription": "A0"
       }
   ]
-  $ hg obslog 'fdf9bde5129a+471f378eab4c' --no-graph -Tjson | python -m json.tool
+  $ hg obslog 'fdf9bde5129a+471f378eab4c' --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
--- a/tests/test-evolve-obshistory-prune.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-prune.t	Sat Oct 31 17:47:08 2020 +0800
@@ -67,7 +67,7 @@
        pruned using prune by test (Thu Jan 01 00:00:00 1970 +0000)
          (No patch available, no predecessors)
   
-  $ hg obslog 'desc(B0)' --hidden --no-graph -Tjson | python -m json.tool
+  $ hg obslog 'desc(B0)' --hidden --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -94,7 +94,7 @@
   $ hg obslog 'desc(A0)' --patch
   @  471f378eab4c (1) A0
   
-  $ hg obslog 'desc(A0)' --no-graph -Tjson | python -m json.tool
+  $ hg obslog 'desc(A0)' --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
--- a/tests/test-evolve-obshistory-split.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-obshistory-split.t	Sat Oct 31 17:47:08 2020 +0800
@@ -109,7 +109,7 @@
          note: testing split
          (No patch available, too many successors (2))
   
-  $ hg obslog 471597cad322 --hidden --no-graph --no-origin -Tjson | python -m json.tool
+  $ hg obslog 471597cad322 --hidden --no-graph --no-origin -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [
@@ -144,7 +144,7 @@
           "shortdescription": "A0"
       }
   ]
-  $ hg obslog 471597cad322 --hidden --no-graph -Tjson | python -m json.tool
+  $ hg obslog 471597cad322 --hidden --no-graph -Tjson | "$PYTHON" -m json.tool
   [
       {
           "markers": [],
--- a/tests/test-evolve-order.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-order.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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-phase.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-phase.t	Sat Oct 31 17:47:08 2020 +0800
@@ -103,7 +103,7 @@
   $ hg glog
   @  3 - 87495ea7c9ec b (draft)
   |
-  | @  2 - 13833940840c c (secret)
+  | %  2 - 13833940840c c (secret)
   | |
   | x  1 - 1e6c11564562 b (draft)
   |/
--- a/tests/test-evolve-progress.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-progress.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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
@@ -171,7 +171,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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-public-content-divergent-corner-cases.t	Sat Oct 31 17:47:08 2020 +0800
@@ -388,10 +388,10 @@
   
 
   $ hg evolve --content-divergent --any --update
-  merge:[5] added d
-  with: [4] added c e
+  merge:[4] added c e
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset e568fd1029bb on 155349b645be
+  rebasing "divergent" content-divergent changeset e568fd1029bb on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
@@ -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-public-content-divergent-discard.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-public-content-divergent-discard.t	Sat Oct 31 17:47:08 2020 +0800
@@ -89,8 +89,8 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[4] added c
-  with: [3] added ch
+  merge:[3] added ch
+  with: [4] added c
   base: [2] added c
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   other divergent changeset 90522bccf499 has same content as local f7c1071f1e7c and differs by "description" only, discarding 90522bccf499
@@ -180,10 +180,10 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[5] added d
-  with: [4] added dh
+  merge:[4] added dh
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset 5acd58ef5066 on 155349b645be
+  rebasing "divergent" content-divergent changeset 5acd58ef5066 on 155349b645be
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   other divergent changeset ae3429430ef1 has same content as local e800202333a4 and differs by "description" only, discarding ae3429430ef1
   content divergence resolution between e800202333a4 (public) and ae3429430ef1 has same content as e800202333a4, discarding ae3429430ef1
@@ -265,8 +265,8 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[4] added c
-  with: [3] added ch
+  merge:[3] added ch
+  with: [4] added c
   base: [2] added c
   merging ch
   warning: conflicts while merging ch! (edit, then use 'hg resolve --mark')
@@ -380,10 +380,10 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[5] added d
-  with: [4] added dh
+  merge:[4] added dh
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset f89a8e2f86ac on 155349b645be
+  rebasing "divergent" content-divergent changeset f89a8e2f86ac on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
@@ -485,10 +485,10 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[5] added d
-  with: [4] added dh
+  merge:[4] added dh
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset db0b7bba0aae on 155349b645be
+  rebasing "divergent" content-divergent changeset db0b7bba0aae on 155349b645be
   merging dh
   warning: conflicts while merging dh! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -509,6 +509,7 @@
 
   $ hg par
   changeset:   5:e800202333a4
+  tag:         tip
   parent:      2:155349b645be
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -589,10 +590,10 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[5] added d
-  with: [4] added dh
+  merge:[4] added dh
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset 67b19bbd770f on 155349b645be
+  rebasing "divergent" content-divergent changeset 67b19bbd770f on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
@@ -626,6 +627,7 @@
 
   $ hg par
   changeset:   5:e800202333a4
+  tag:         tip
   parent:      2:155349b645be
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
--- a/tests/test-evolve-public-content-divergent-main.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-public-content-divergent-main.t	Sat Oct 31 17:47:08 2020 +0800
@@ -353,10 +353,10 @@
       public
   
   $ hg evolve --content-divergent --any --update
-  merge:[5] added d
-  with: [4] added d c e
+  merge:[4] added d c e
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset f31bcc378766 on 155349b645be
+  rebasing "divergent" content-divergent changeset f31bcc378766 on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
@@ -483,10 +483,10 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[5] added d
-  with: [4] added d
+  merge:[4] added d
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset 9411ad1fe615 on 155349b645be
+  rebasing "divergent" content-divergent changeset 9411ad1fe615 on 155349b645be
   merging d
   warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -577,10 +577,10 @@
       public
   
   $ hg evolve --content-divergent --any
-  merge:[5] added d
-  with: [4] added c e
+  merge:[4] added c e
+  with: [5] added d
   base: [3] added d
-  rebasing "other" content-divergent changeset 3c17c7afaf6e on 155349b645be
+  rebasing "divergent" content-divergent changeset 3c17c7afaf6e on 155349b645be
   merging c
   warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
   unresolved merge conflicts
--- a/tests/test-evolve-serveronly-bundle2.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-serveronly-bundle2.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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-templates.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve-templates.t	Sat Oct 31 17:47:08 2020 +0800
@@ -158,11 +158,11 @@
   
 
   $ hg log -GT '{successors|json}\n'
-  o  ""
+  o  []
   |
   | @  [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
   |/
-  o  ""
+  o  []
   
 
   $ hg up 'desc(A1)' --hidden
--- a/tests/test-evolve.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-evolve.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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
@@ -1401,7 +1401,7 @@
   |
   o  32:6ec468e4cb98@default(draft) amended
   |
-  | @  31:5be050657ca5@default(draft) will cause conflict at evolve
+  | %  31:5be050657ca5@default(draft) will cause conflict at evolve
   | |
   | x  30:748126f98ff1@default(draft) will be evolved safely
   | |
--- a/tests/test-import.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-import.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-issue-6028.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-minitopic.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-rewind.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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
@@ -46,26 +41,23 @@
 
   $ cd ..
 
-Test rewinding to single changesets
-====================================
+Rewinding to a single changeset
+===============================
 
-  $ 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
 ---------------------------------------------
 
-Setup
-`````
-
-Update to an unrelated changeset
+update to an unrelated changeset
 
   $ hg up 'desc("c_ROOT")'
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
 
-Prune the head
+prune the head
 
   $ hg prune -r 'desc("c_B0")'
   1 changesets pruned
@@ -82,11 +74,15 @@
      summary:     c_ROOT
   
 
-Actual rewind
-`````````````
+target selection
+
+  $ hg rewind --hidden --to 'desc("c_B0")' --dry-run
+  rewinding a pruned commit to 1 changesets: 7e594302a05d
+
+actual rewind
 
   $ 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'}
@@ -149,24 +145,26 @@
      summary:     c_ROOT
   
 
-Other independant rewind create a different revision
-----------------------------------------------------------
-
-setup
-`````
+Other independent rewind creates a different revision
+-----------------------------------------------------
 
 note: we use "default-date" to make it a "different rewind"
 
   $ echo '[devel]' >> $HGRCPATH
   $ echo 'default-date = 1 0' >> $HGRCPATH
 
-Actual rewind
-`````````````
-
   $ hg prune 'desc("c_B0")'
   1 changesets pruned
+
+target selection
+
+  $ hg rewind --hidden --to 'min(desc("c_B0"))' --dry-run
+  rewinding a pruned commit to 1 changesets: 7e594302a05d
+
+actual rewind
+
   $ 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 +244,14 @@
   
   $ cd ..
 
-rewind a simple amend - creating content-divergence
+  $ 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
@@ -277,12 +273,18 @@
      summary:     c_ROOT
   
 
-Actual rewind
-`````````````
+target selection
+
+  $ hg rewind --from 'desc("c_B1")' --as-divergence --dry-run
+  recreating 1 changesets: 7e594302a05d
+  $ hg rewind --hidden --to 'desc("c_B0")' --as-divergence --dry-run
+  recreating 1 changesets: 7e594302a05d
+
+actual rewind
 
   $ 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'}
@@ -359,8 +361,17 @@
      summary:     c_ROOT
   
 
-Cleanup
-```````
+rewind --dry-run output when rewinding with relevant divergence
+
+  $ hg rewind --to 'min(desc("c_B0"))' --hidden --dry-run
+  abort: rewind confused by divergence on 7e594302a05d
+  (solve divergence first or use "--as-divergence")
+  [255]
+  $ hg rewind --to 'min(desc("c_B0"))' --hidden --as-divergence --dry-run
+  recreating 1 changesets: 7e594302a05d
+
+cleanup
+
   $ hg prune 'max(desc("c_B0"))'
   1 changesets pruned
   $ hg log -G
@@ -381,13 +392,20 @@
      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 successor
+---------------------------------------------------------------
+
+target selection
 
-rewind a simple amend - obsoleting the current latest successors
-----------------------------------------------------------------
+  $ hg rewind --hidden --to 'min(desc("c_B0"))' --dry-run
+  rewinding 25c8f5ab0c3b to 1 changesets: 7e594302a05d
 
+actual rewind
+
+  $ 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 +495,18 @@
   
   $ cd ..
 
-rewind a simple split
----------------------
-
-Setup
-`````
-
-  $ hg clone rewind-testing-base rewind-testing-split-fold
+  $ 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-split-fold
+  $ cd rewind-testing-single-split-fold
+
+Rewind a simple split
+---------------------
 
   $ echo C > C
   $ echo D > D
-  $ hg add C D
-  $ hg ci -m 'c_CD0'
-  $ hg split << EOF
+  $ hg ci -qAm 'c_CD0'
+  $ hg split --config ui.interactive=yes << EOF
   > y
   > f
   > d
@@ -566,28 +580,34 @@
   x  49fb7d900906 (3) c_CD0
   
 
-Actual rewind
-`````````````
+target selection
+
+  $ hg rewind --from 'desc("c_CD0")' --dry-run
+  rewinding a0316c4c5417 9576e80d6851 to 1 changesets: 49fb7d900906
+  $ hg rewind --hidden --to 'min(desc("c_CD0"))' --dry-run
+  rewinding a0316c4c5417 9576e80d6851 to 1 changesets: 49fb7d900906
+
+actual rewind
 
   $ 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
   49fb7d900906b0a3d329e90da4dcb0a7582d3b6e a0316c4c54179357e71d068fb8884678ebc7c351 9576e80d6851ce79cd535e2dc5fa01b444d89a39 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '12', 'operation': 'split', 'user': 'test'}
   49fb7d900906b0a3d329e90da4dcb0a7582d3b6e 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 4 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '2', 'operation': 'rewind', 'user': 'test'}
-  9576e80d6851ce79cd535e2dc5fa01b444d89a39 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '14', 'operation': 'rewind', 'user': 'test'}
-  a0316c4c54179357e71d068fb8884678ebc7c351 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '10', 'operation': 'rewind', 'user': 'test'}
+  9576e80d6851ce79cd535e2dc5fa01b444d89a39 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '14', 'fold-id': 'eeda726b', 'fold-idx': '1', 'fold-size': '2', 'operation': 'rewind', 'user': 'test'}
+  a0316c4c54179357e71d068fb8884678ebc7c351 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '10', 'fold-id': 'eeda726b', 'fold-idx': '2', 'fold-size': '2', 'operation': 'rewind', 'user': 'test'}
   $ hg obslog --no-origin
   @    4535d0af405c (6) c_CD0
   |\
   | \
   | |\
   | x |  9576e80d6851 (5) c_CD0
-  |/ /     rewritten(meta, parent, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  |/ /     folded(meta, parent, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
   | |
   | x  a0316c4c5417 (4) c_CD0
-  |/     rewritten(meta, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  |/     folded(meta, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
   |
   x  49fb7d900906 (3) c_CD0
        meta-changed(meta) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
@@ -596,8 +616,7 @@
   $ hg obslog
   @    4535d0af405c (6) c_CD0
   |\     meta-changed(meta) from 49fb7d900906 using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
-  | |    rewritten(meta, parent, content) from 9576e80d6851 using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
-  | |    rewritten(meta, content) from a0316c4c5417 using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  | |    folded(meta, parent, content) from 9576e80d6851, a0316c4c5417 using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
   | |
   | \
   | |\
@@ -633,26 +652,42 @@
      summary:     c_ROOT
   
 
-rewind a fold
+Rewind a fold
 -------------
 
-setup
-`````
-
   $ echo 'default-date = 3 0' >> $HGRCPATH
 
-Actual Rewind
-`````````````
+target selection
+
+  $ hg rewind --from . --hidden --dry-run
+  rewinding 4535d0af405c to 2 changesets: a0316c4c5417 9576e80d6851
+
+  $ hg rewind --to '9576e80d6851+a0316c4c5417' --hidden --dry-run
+  rewinding 4535d0af405c to 2 changesets: a0316c4c5417 9576e80d6851
+  $ hg rewind --to '9576e80d6851' --hidden --dry-run
+  rewinding 4535d0af405c to 2 changesets: a0316c4c5417 9576e80d6851
+
+XXX this should also give us 2 changesets
+
+  $ hg rewind --to 'a0316c4c5417' --hidden --dry-run
+  rewinding 4535d0af405c to 1 changesets: a0316c4c5417
+
+  $ hg rewind --to '9576e80d6851' --exact --hidden --dry-run
+  rewinding 4535d0af405c to 1 changesets: 9576e80d6851
+  $ hg rewind --to 'a0316c4c5417' --exact --hidden --dry-run
+  rewinding 4535d0af405c to 1 changesets: a0316c4c5417
+
+actual rewind
 
   $ 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
   49fb7d900906b0a3d329e90da4dcb0a7582d3b6e a0316c4c54179357e71d068fb8884678ebc7c351 9576e80d6851ce79cd535e2dc5fa01b444d89a39 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '12', 'operation': 'split', 'user': 'test'}
   49fb7d900906b0a3d329e90da4dcb0a7582d3b6e 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 4 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '2', 'operation': 'rewind', 'user': 'test'}
-  9576e80d6851ce79cd535e2dc5fa01b444d89a39 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '14', 'operation': 'rewind', 'user': 'test'}
-  a0316c4c54179357e71d068fb8884678ebc7c351 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '10', 'operation': 'rewind', 'user': 'test'}
+  9576e80d6851ce79cd535e2dc5fa01b444d89a39 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '14', 'fold-id': 'eeda726b', 'fold-idx': '1', 'fold-size': '2', 'operation': 'rewind', 'user': 'test'}
+  a0316c4c54179357e71d068fb8884678ebc7c351 4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '10', 'fold-id': 'eeda726b', 'fold-idx': '2', 'fold-size': '2', 'operation': 'rewind', 'user': 'test'}
   a0316c4c54179357e71d068fb8884678ebc7c351 e76375de0bfc9c59bdd91067c901f3eed7d6c8fe 4 (Thu Jan 01 00:00:03 1970 +0000) {'ef1': '2', 'operation': 'rewind', 'user': 'test'}
   9576e80d6851ce79cd535e2dc5fa01b444d89a39 95d72d892df7fec59107e10914c5729bdf03665f 4 (Thu Jan 01 00:00:03 1970 +0000) {'ef1': '6', 'operation': 'rewind', 'user': 'test'}
   4535d0af405c1bf35f37b35f26ec6f9acfa6fe0b e76375de0bfc9c59bdd91067c901f3eed7d6c8fe 95d72d892df7fec59107e10914c5729bdf03665f 0 (Thu Jan 01 00:00:03 1970 +0000) {'ef1': '14', 'operation': 'rewind', 'user': 'test'}
@@ -665,11 +700,11 @@
   |\| |    split(meta, parent, content) as 95d72d892df7, e76375de0bfc using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   | | |
   | x |  9576e80d6851 (5) c_CD0
-  |/ /     rewritten(meta, parent, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  |/ /     folded(meta, parent, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
   | |      rewritten(meta, parent) as 95d72d892df7 using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   | |
   | x  a0316c4c5417 (4) c_CD0
-  |/     rewritten(meta, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  |/     folded(meta, content) as 4535d0af405c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
   |      meta-changed(meta) as e76375de0bfc using rewind by test (Thu Jan 01 00:00:03 1970 +0000)
   |
   x  49fb7d900906 (3) c_CD0
@@ -706,8 +741,8 @@
   
   $ cd ..
 
-Test rewinding stack
-====================
+Rewinding a stack
+=================
 
   $ hg clone rewind-testing-base rewind-testing-stack
   updating to branch default
@@ -717,15 +752,12 @@
 Rewinding the top of the stack only
 -----------------------------------
 
-setup
-`````
-
   $ hg up 'desc("c_A0")'
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ 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
@@ -773,12 +805,16 @@
      summary:     c_ROOT
   
 
-Actual rewind
-`````````````
+target selection
+
+  $ hg rewind --hidden --to 'min(desc(c_B0))' --exact --dry-run
+  rewinding a65fceb2324a to 1 changesets: 7e594302a05d
+
+actual rewind
 
   $ 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
@@ -833,17 +869,18 @@
      summary:     c_ROOT
   
 
-Testing default argument (and cleanup)
-``````````````````````````````````````
+Testing the defaults
+--------------------
 
-rewind with no argument should be equivalent to `--from .`
+rewind with no arguments should be equivalent to `--from .`
 
   $ echo 'default-date = 4 0' >> $HGRCPATH
-  $ hg rewind --from '.'
-  rewinded to 1 changesets
+  $ hg rewind --dry-run
+  rewinding ac979e0aac4e to 1 changesets: a65fceb2324a
+  $ 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 +924,17 @@
 Automatically rewinding the full stack (with --to)
 --------------------------------------------------
 
+target selection
+
+  $ hg rewind --hidden --to 'predecessors(.)' --dry-run
+  rewinding d952d1794ff6 to 1 changesets: 579f120ba918
+  rewinding a5dd64adbb2a to 1 changesets: ac979e0aac4e
+
+actual rewind
+
+  $ 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
@@ -942,9 +988,17 @@
 Automatically rewinding the full stack (with --from)
 ----------------------------------------------------
 
+target selection
+
+  $ hg rewind --from '.' --dry-run
+  rewinding 9c28b7ed3951 to 1 changesets: d952d1794ff6
+  rewinding 3f2d8862657d to 1 changesets: a5dd64adbb2a
+
+actual rewind
+
   $ 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 +1083,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 +1137,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-stabilize-conflict.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-stabilize-conflict.t	Sat Oct 31 17:47:08 2020 +0800
@@ -141,7 +141,7 @@
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     babar count up to ten
   |
-  | @  changeset:   4:71c18f70c34f
+  | %  changeset:   4:71c18f70c34f
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  instability: orphan
@@ -234,7 +234,7 @@
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     babar count up to ten
   |
-  | @  changeset:   6:1836b91c6c1d
+  | %  changeset:   6:1836b91c6c1d
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  instability: orphan
--- a/tests/test-stablesort-branchpoint-criss-cross.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-stablesort-branchpoint-criss-cross.t	Sat Oct 31 17:47:08 2020 +0800
@@ -21,16 +21,6 @@
   >     done;
   > }
 
-  $ cat << EOF >> random_rev.py
-  > import random
-  > import sys
-  > 
-  > loop = int(sys.argv[1])
-  > var = int(sys.argv[2])
-  > for x in range(loop):
-  >     print(x + random.randint(0, var))
-  > EOF
-
 Check criss cross merge
 =======================
 
@@ -708,14 +698,14 @@
   updating to branch default
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd crisscross_random
-  $ for x in `python ../random_rev.py 50 44`; do
+  $ for x in `"$PYTHON" "$TESTDIR/testlib/random-revs.py" 50 44`; do
   >   # using python to benefit from the random seed
   >   hg pull -r $x --quiet
   > done;
   $ hg pull --quiet
 
   $ hg showsort --rev 'all()' > ../crisscross.random.order
-  $ python "$RUNTESTDIR/md5sum.py" ../crisscross.*.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" ../crisscross.*.order
   d9aab0d1907d5cf64d205a8b9036e959  ../crisscross.random.order
   d9aab0d1907d5cf64d205a8b9036e959  ../crisscross.source.order
   $ diff -u ../crisscross.*.order
--- a/tests/test-stablesort-branchpoint.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-stablesort-branchpoint.t	Sat Oct 31 17:47:08 2020 +0800
@@ -23,16 +23,6 @@
   >     done;
   > }
 
-  $ cat << EOF >> random_rev.py
-  > import random
-  > import sys
-  > 
-  > loop = int(sys.argv[1])
-  > var = int(sys.argv[2])
-  > for x in range(loop):
-  >     print(x + random.randint(0, var))
-  > EOF
-
 Basic tests
 ===========
 (no criss cross merge)
@@ -386,7 +376,7 @@
 
 check the log output are different
 
-  $ python "$RUNTESTDIR/md5sum.py" *.log
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.log
   55919ebc9c02f28070cf3255b1690f8c  A.log
   c6244b76a60d0707767dc71780e544f3  B.log
   4d8b08b8c50ecbdd2460a62e5852d84d  C.log
@@ -408,7 +398,7 @@
   C
   D
 
-  $ python "$RUNTESTDIR/md5sum.py" *.all.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.all.order
   0c6b2e6f15249c0359b0f93e28c5bd1c  A.all.order
   0c6b2e6f15249c0359b0f93e28c5bd1c  B.all.order
   0c6b2e6f15249c0359b0f93e28c5bd1c  C.all.order
@@ -420,7 +410,7 @@
   >     hg -R repo_$x showsort --rev 'b4594d867745' > ${x}.b4594d867745.order;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.b4594d867745.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.b4594d867745.order
   5c40900a22008f24eab8dfe2f30ad79f  A.b4594d867745.order
   5c40900a22008f24eab8dfe2f30ad79f  B.b4594d867745.order
   5c40900a22008f24eab8dfe2f30ad79f  C.b4594d867745.order
@@ -432,7 +422,7 @@
   >     hg -R repo_$x showsort --rev '1d8d22637c2d' > ${x}.1d8d22637c2d.order;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  A.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  B.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  C.1d8d22637c2d.order
@@ -444,7 +434,7 @@
   >     hg -R repo_$x showsort --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.order;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  A.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
@@ -519,7 +509,7 @@
   >     hg -R repo_$x showsort --rev 'e7d9710d9fc6+43227190fef8' > ${x}.non-heads.order;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  A.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
@@ -667,13 +657,13 @@
   updating to branch default
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd recursion_random
-  $ for x in `python ../random_rev.py 15 5`; do
+  $ for x in `"$PYTHON" "$TESTDIR/testlib/random-revs.py" 15 5`; do
   >   # using python to benefit from the random seed
   >   hg pull -r $x --quiet
   > done;
   $ hg pull --quiet
   $ hg showsort --rev 'all()' > ../multiple.random.order
-  $ python "$RUNTESTDIR/md5sum.py" ../multiple.*.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" ../multiple.*.order
   6ff802a0a5f0a3ddd82b25f860238fbd  ../multiple.random.order
   6ff802a0a5f0a3ddd82b25f860238fbd  ../multiple.source.order
   $ hg showsort --rev 'all()'
--- a/tests/test-stablesort-criss-cross.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-stablesort-criss-cross.t	Sat Oct 31 17:47:08 2020 +0800
@@ -21,16 +21,6 @@
   >     done;
   > }
 
-  $ cat << EOF >> random_rev.py
-  > import random
-  > import sys
-  > 
-  > loop = int(sys.argv[1])
-  > var = int(sys.argv[2])
-  > for x in range(loop):
-  >     print(x + random.randint(0, var))
-  > EOF
-
 Check criss cross merge
 =======================
 
@@ -820,14 +810,14 @@
   updating to branch default
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd crisscross_random
-  $ for x in `python ../random_rev.py 50 44`; do
+  $ for x in `"$PYTHON" "$TESTDIR/testlib/random-revs.py" 50 44`; do
   >   # using python to benefit from the random seed
   >   hg pull -r $x --quiet
   > done;
   $ hg pull --quiet
 
   $ hg showsort --rev 'all()' > ../crisscross.random.order
-  $ python "$RUNTESTDIR/md5sum.py" ../crisscross.*.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" ../crisscross.*.order
   0ace2b2a63ec70b3b63b661aaee69878  ../crisscross.random.order
   0ace2b2a63ec70b3b63b661aaee69878  ../crisscross.source.order
   $ diff -u ../crisscross.*.order
--- a/tests/test-stablesort.t	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-stablesort.t	Sat Oct 31 17:47:08 2020 +0800
@@ -24,16 +24,6 @@
   >     done;
   > }
 
-  $ cat << EOF >> random_rev.py
-  > import random
-  > import sys
-  > 
-  > loop = int(sys.argv[1])
-  > var = int(sys.argv[2])
-  > for x in range(loop):
-  >     print(x + random.randint(0, var))
-  > EOF
-
 Basic tests
 ===========
 (no criss cross merge)
@@ -459,7 +449,7 @@
 
 check the log output are different
 
-  $ python "$RUNTESTDIR/md5sum.py" *.log
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.log
   55919ebc9c02f28070cf3255b1690f8c  A.log
   c6244b76a60d0707767dc71780e544f3  B.log
   4d8b08b8c50ecbdd2460a62e5852d84d  C.log
@@ -481,7 +471,7 @@
   C
   D
 
-  $ python "$RUNTESTDIR/md5sum.py" *.all.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.all.order
   25e89e555b56ed9e7b51827d58073b77  A.all.order
   25e89e555b56ed9e7b51827d58073b77  B.all.order
   25e89e555b56ed9e7b51827d58073b77  C.all.order
@@ -493,7 +483,7 @@
   >     hg -R repo_$x showsort --rev 'b4594d867745' > ${x}.b4594d867745.order;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.b4594d867745.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.b4594d867745.order
   5c40900a22008f24eab8dfe2f30ad79f  A.b4594d867745.order
   5c40900a22008f24eab8dfe2f30ad79f  B.b4594d867745.order
   5c40900a22008f24eab8dfe2f30ad79f  C.b4594d867745.order
@@ -507,17 +497,17 @@
   >     hg -R repo_$x showsorthead --rev '1d8d22637c2d' --limit 4 > ${x}.1d8d22637c2d.orderhead-4;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  A.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  B.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  C.1d8d22637c2d.order
   77dc20a6f86db9103df8edaae9ad2754  D.1d8d22637c2d.order
-  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.orderhead
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.orderhead
   77dc20a6f86db9103df8edaae9ad2754  A.1d8d22637c2d.orderhead
   77dc20a6f86db9103df8edaae9ad2754  B.1d8d22637c2d.orderhead
   77dc20a6f86db9103df8edaae9ad2754  C.1d8d22637c2d.orderhead
   77dc20a6f86db9103df8edaae9ad2754  D.1d8d22637c2d.orderhead
-  $ python "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.orderhead-4
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.1d8d22637c2d.orderhead-4
   ea12ffc0007e1b4b911d09dd478881f3  A.1d8d22637c2d.orderhead-4
   ea12ffc0007e1b4b911d09dd478881f3  B.1d8d22637c2d.orderhead-4
   ea12ffc0007e1b4b911d09dd478881f3  C.1d8d22637c2d.orderhead-4
@@ -531,12 +521,12 @@
   >     hg -R repo_$x showsorthead --rev 'e7d9710d9fc6+43227190fef8' --limit 6 > ${x}.non-heads.orderhead-6;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  A.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  D.non-heads.order
-  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.orderhead
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-heads.orderhead
   1e5ce05b507a058c5dac3d7de9ae8feb  A.non-heads.orderhead
   4b07febabfee9528aedcea156a7d7071  B.non-heads.orderhead
   1e5ce05b507a058c5dac3d7de9ae8feb  C.non-heads.orderhead
@@ -613,7 +603,7 @@
   >     hg -R repo_$x showsort --rev '43227190fef8' > ${x}.non-head-B.orderhead;
   > done
 
-  $ python "$RUNTESTDIR/md5sum.py" *.non-heads.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  A.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  B.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  C.non-heads.order
@@ -621,11 +611,11 @@
   94e0ea8cdade135dabde4ec5e9954329  E.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  F.non-heads.order
   94e0ea8cdade135dabde4ec5e9954329  G.non-heads.order
-  $ python "$RUNTESTDIR/md5sum.py" *.non-head-A.orderhead
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-head-A.orderhead
   1e5ce05b507a058c5dac3d7de9ae8feb  E.non-head-A.orderhead
   1e5ce05b507a058c5dac3d7de9ae8feb  F.non-head-A.orderhead
   1e5ce05b507a058c5dac3d7de9ae8feb  G.non-head-A.orderhead
-  $ python "$RUNTESTDIR/md5sum.py" *.non-head-B.orderhead
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" *.non-head-B.orderhead
   4b07febabfee9528aedcea156a7d7071  E.non-head-B.orderhead
   4b07febabfee9528aedcea156a7d7071  F.non-head-B.orderhead
   4b07febabfee9528aedcea156a7d7071  G.non-head-B.orderhead
@@ -822,7 +812,7 @@
   updating to branch default
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd recursion_random
-  $ for x in `python ../random_rev.py 15 5`; do
+  $ for x in `"$PYTHON" "$TESTDIR/testlib/random-revs.py" 15 5`; do
   >   # using python to benefit from the random seed
   >   hg pull -r $x --quiet
   > done;
@@ -830,13 +820,13 @@
   $ hg showsort --rev 'all()' > ../multiple.random.order
   $ hg showsorthead --rev '160a7a0adbf4' > ../160a7a0adbf4.random.orderhead
   $ hg showsorthead --rev '4bbfc6078919' > ../4bbfc6078919.random.orderhead
-  $ python "$RUNTESTDIR/md5sum.py" ../multiple.*.order
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" ../multiple.*.order
   19b28a58af0311b99159f60944a57213  ../multiple.random.order
   19b28a58af0311b99159f60944a57213  ../multiple.source.order
-  $ python "$RUNTESTDIR/md5sum.py" ../160a7a0adbf4.*.orderhead
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" ../160a7a0adbf4.*.orderhead
   905b0bab9155f65ed2e220382b94e9b9  ../160a7a0adbf4.random.orderhead
   905b0bab9155f65ed2e220382b94e9b9  ../160a7a0adbf4.source.orderhead
-  $ python "$RUNTESTDIR/md5sum.py" ../4bbfc6078919.*.orderhead
+  $ "$PYTHON" "$RUNTESTDIR/md5sum.py" ../4bbfc6078919.*.orderhead
   3732305a333d59ec50b91db0f5ab696e  ../4bbfc6078919.random.orderhead
   3732305a333d59ec50b91db0f5ab696e  ../4bbfc6078919.source.orderhead
   $ hg showsort --rev 'all()'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-merge.t	Sat Oct 31 17:47:08 2020 +0800
@@ -0,0 +1,175 @@
+==========================================================
+Testing the ability to control merge behavior around topic
+==========================================================
+
+Especially, we want to test mode allowing the creation of merge that will
+eventually become "oedipus" merge.
+
+  $ . "$TESTDIR/testlib/topic_setup.sh"
+  $ . "$TESTDIR/testlib/common.sh"
+
+
+
+Setup a test repository
+=======================
+
+  $ hg init test-repo
+  $ cd test-repo
+  $ mkcommit root
+  $ mkcommit default-1
+  $ mkcommit default-2
+  $ hg up 'desc("default-2")'
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg topic test-topic
+  marked working directory as topic: test-topic
+  $ mkcommit topic-1
+  active topic 'test-topic' grew its first changeset
+  (see 'hg help topics' for more information)
+  $ mkcommit topic-2
+  $ hg up null
+  0 files updated, 0 files merged, 5 files removed, 0 files unresolved
+  $ hg log -G
+  o  changeset:   4:c3ec1ef2bf00
+  |  tag:         tip
+  |  topic:       test-topic
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     topic-2
+  |
+  o  changeset:   3:3300cececc85
+  |  topic:       test-topic
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     topic-1
+  |
+  o  changeset:   2:b05d997f9ab0
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     default-2
+  |
+  o  changeset:   1:ccab697ce421
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     default-1
+  |
+  o  changeset:   0:1e4be0697311
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     root
+  
+
+Test that the merge is rejected by default
+==========================================
+
+from the topic
+
+  $ hg up test-topic
+  switching to topic test-topic
+  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge default
+  abort: merging with a working directory ancestor has no effect
+  [255]
+
+from the bare branch
+
+  $ hg up default
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg merge test-topic
+  abort: nothing to merge
+  (use 'hg update' or check 'hg heads')
+  [255]
+
+Test that the merge is rejected if set to reject
+================================================
+
+Actually, this also work with any unvalid value, but:
+
+- the default value might change in the future,
+- this make sure we read the config right.
+
+Same result when setting the config to be strict
+
+  $ cat >> .hg/hgrc << EOF
+  > [experimental]
+  > topic.linear-merge = reject
+  > EOF
+
+from the topic
+
+  $ hg up test-topic
+  switching to topic test-topic
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge default
+  abort: merging with a working directory ancestor has no effect
+  [255]
+
+from the bare branch
+
+  $ hg up default
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg merge test-topic
+  abort: nothing to merge
+  (use 'hg update' or check 'hg heads')
+  [255]
+
+Test that the merge is accepted if configured to allow them
+===========================================================
+
+  $ cat >> .hg/hgrc << EOF
+  > [experimental]
+  > topic.linear-merge = allow-from-bare-branch
+  > EOF
+
+from the topic, this is rejected since the resulting merge would be in the topic itself
+
+  $ hg up test-topic
+  switching to topic test-topic
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge default
+  abort: merging with a working directory ancestor has no effect
+  [255]
+
+from the bare branch this is allowed since the resulting merge will be in the branch
+
+  $ hg up default
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg merge test-topic --traceback
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "How dreadful the knowledge of the truth can be when there's no help in truth."
+  $ hg log -G
+  @    changeset:   5:42ca2e8cb810
+  |\   tag:         tip
+  | |  parent:      2:b05d997f9ab0
+  | |  parent:      4:c3ec1ef2bf00
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     How dreadful the knowledge of the truth can be when there's no help in truth.
+  | |
+  | o  changeset:   4:c3ec1ef2bf00
+  | |  topic:       test-topic
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     topic-2
+  | |
+  | o  changeset:   3:3300cececc85
+  |/   topic:       test-topic
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     topic-1
+  |
+  o  changeset:   2:b05d997f9ab0
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     default-2
+  |
+  o  changeset:   1:ccab697ce421
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     default-1
+  |
+  o  changeset:   0:1e4be0697311
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     root
+  
--- a/tests/test-topic-push-concurrent-on.t	Mon Oct 26 18:32:22 2020 +0100
+++ /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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-topic-push.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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 new heads on branch 'default': bbd9d6199b88
   [255]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-shell-prompt.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-topic-stack.t	Sat Oct 31 17:47:08 2020 +0800
@@ -104,7 +104,7 @@
   s2(e629654d7050): c_d
   s1(8522f9e3fee9): c_c
   s0(ea705abc4f51)^ c_b (base)
-  $ hg stack -Tjson | python -m json.tool
+  $ hg stack -Tjson | "$PYTHON" -m json.tool
   [
       {
           "desc": "c_f",
@@ -157,7 +157,7 @@
           "symbol": "^"
       }
   ]
-  $ hg stack -v -Tjson | python -m json.tool
+  $ hg stack -v -Tjson | "$PYTHON" -m json.tool
   [
       {
           "desc": "c_f",
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-topic-tutorial.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-topic.t	Sat Oct 31 17:47:08 2020 +0800
@@ -134,6 +134,36 @@
     [experimental]
     topic.server-gate-topic-changesets = yes
   
+  Explicitly merging in the target branch
+  =======================================
+  
+  By default, Mercurial will not let your merge a topic into its target branch
+  if that topic is already based on the head of that branch. In other word,
+  Mercurial will not let your create a merge that will eventually have two
+  parents in the same branches, one parent being the ancestors of the other
+  parent. This behavior can be lifted using the following config:
+  
+    [experimental]
+    topic.linear-merge = allow-from-bare-branch
+  
+  When this option is set to 'allow-from-bare-branch', it is possible to merge a
+  topic branch from a bare branch (commit an active topic (eg: public one))
+  regardless of the topology. The result would typically looks like that:
+  
+    @    summary: resulting merge commit
+    |\   branch:  my-branch
+    | |
+    | o  summary: some more change in a topic, the merge "target"
+    | |  branch:  my-branch
+    | |  topic:   my-topic
+    | |
+    | o  summary: some change in a topic
+    |/   branch:  my-branch
+    |    topic:   my-topic
+    |
+    o    summary: previous head of the branch, the merge "source"
+    |    branch:  my-branch
+  
   list of commands:
   
   Change organization:
@@ -928,6 +958,10 @@
     ^ 3
   s1: 0
 
+  $ tlog 'tip#s'
+  0: 
+  4: 
+
   $ tlog 'tip#stack[0]'
   $ tlog 'tip#stack[1]'
   0: 
@@ -946,6 +980,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]'
@@ -996,6 +1035,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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-unstability-resolution-result.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ b/tests/test-wireproto.t	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Mon Oct 26 18:32:22 2020 +0100
+++ /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	Sat Oct 31 17:47:08 2020 +0800
@@ -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	Sat Oct 31 17:47:08 2020 +0800
@@ -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'
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testlib/random-revs.py	Sat Oct 31 17:47:08 2020 +0800
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+"""
+This simple script outputs a sequence of numbers separated by newlines. The
+amount of numbers and their approximate values can be controlled by two command
+line arguments.
+
+Usage: $0 COUNT MAXADD. COUNT will determine the amount of numbers printed, and
+MAXADD will limit the value that will be added to each of those numbers.
+"""
+
+from __future__ import print_function
+
+import random
+import sys
+
+def main():
+    count = int(sys.argv[1])
+    maxadd = int(sys.argv[2])
+    for x in range(count):
+        print(x + random.randint(0, maxadd))
+
+if __name__ == '__main__':
+    main()