changeset 5463:8d28fa24f7cd mercurial-5.1

test-compat: merge mercurial-5.2 into mercurial-5.1
author Anton Shestakov <av6@dwimlabs.net>
date Fri, 31 Jul 2020 18:40:28 +0800
parents e8542915d0f7 (current diff) 33c33a373da6 (diff)
children 868e7bc03b5f 6a6f24398686
files tests/test-obsolete.t
diffstat 28 files changed, 383 insertions(+), 228 deletions(-) [+]
line wrap: on
line diff
--- a/.gitlab-ci.yml	Fri May 08 20:36:28 2020 +0800
+++ b/.gitlab-ci.yml	Fri Jul 31 18:40:28 2020 +0800
@@ -52,6 +52,7 @@
         - make
     variables:
         LANG: en_us.UTF-8
+        PYTHONPATH: "/ci/repos/mercurial:$PYTHONPATH"
     artifacts:
         paths:
             - html/*
--- a/.hgtags	Fri May 08 20:36:28 2020 +0800
+++ b/.hgtags	Fri Jul 31 18:40:28 2020 +0800
@@ -86,3 +86,4 @@
 ef41094c34e162eb32ef24bf66d9776d1112751a 9.2.2
 583dc6ef3eb21fbf6574021136f32b8a1163506c 9.3.0
 8d955635cf457aaa4810d77740721d4275001f74 9.3.1
+27d57ca8686590867e62e3d42c96bad84a5f56ef 10.0.0
--- a/CHANGELOG	Fri May 08 20:36:28 2020 +0800
+++ b/CHANGELOG	Fri Jul 31 18:40:28 2020 +0800
@@ -1,6 +1,23 @@
 Changelog
 =========
 
+10.0.1 - in progress
+--------------------
+
+  * compatibility with Mercurial 5.5
+
+  * evolve: update the template keywords section in `hg help -e evolve`
+
+  * obslog: make obslog --no-origin -f work with multiple successor sets
+
+topic (0.19.1)
+
+  * compatibility with Mercurial 5.5
+
+  * topic: hg push --topic does-not-exist now doesn't try to push unrelated
+    changesets and aborts instead
+  * topic: hg outgoing/push --topic . will use current topic
+
 10.0.0 -- 2020-05-09
 --------------------
 
--- a/contrib/check-compat-strings.sh	Fri May 08 20:36:28 2020 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-unset GREP_OPTIONS
-
-# This script finds compatibility-related comments with a node hash specified
-# in all files in a given directory (. by default) and looks up the hash in a
-# repo (~/hg by default) to determine if each of the comments is correct and,
-# if not, it suggests the correct release. This can prevent accidentally
-# removing a piece of code that was misattributed to a different (earlier)
-# release of core hg.
-
-# Usage: $0 WDIR HGREPO where WDIR is usually evolve/hgext3rd/ and HGREPO is
-# the place with core Mercurial repo (not just checkout). Said repo has to be
-# sufficiently up-to-date, otherwise this script may not work correctly.
-
-workdir=${1:-'.'}
-hgdir=${2:-~/hg}
-grep -Ern 'hg <= [0-9.]+ \([0-9a-f+]+\)' "$workdir" | while read -r line; do
-    bashre='hg <= ([0-9.]+) \(([0-9a-f+]+)\)'
-    if [[ $line =~ $bashre ]]; then
-        expected=${BASH_REMATCH[1]}
-        revset=${BASH_REMATCH[2]}
-        tagrevset="max(tag('re:^[0-9]\\.[0-9]$') - ($revset)::)"
-        lastrel=$(HGPLAIN=1 hg --cwd "$hgdir" log -r "$tagrevset" -T '{tags}')
-        if [[ "$lastrel" != "$expected" ]]; then
-            echo "$line"
-            echo "actual last major release without $revset is $lastrel"
-            echo
-        fi
-    fi
-done
--- a/hgext3rd/evolve/__init__.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/__init__.py	Fri Jul 31 18:40:28 2020 +0800
@@ -153,40 +153,33 @@
 you create will not cause interference with other clients or servers without
 the effect flag recording.
 
-Templates
-=========
+Template keywords
+=================
+
+Evolve provides one template keyword that helps explore obsolescence history:
 
-Evolve ship several templates that you can use to have a better visibility
-about your obs history:
+  - obsorigin, for each changeset display a line summarizing what changed
+    between the changeset and its predecessors. Depending on the verbosity
+    level (-q and -v) it displays the users that created the obsmarkers and the
+    date range of these operations.
 
-  - precursors, for each obsolete changeset show the closest visible
-    precursors.
-  - successors, for each obsolete changeset show the closest visible
-    successors. It is useful when your working directory is obsolete to see
-    what are its successors. This information can also be retrieved with the
-    obslog command and the --all option.
-  - obsfate, for each obsolete changeset display a line summarizing what
-    changed between the changeset and its successors. Depending on the
-    verbosity level (-q and -v) it display the changeset successors, the users
-    that created the obsmarkers and the date range of these changes.
+Evolve used to provide these template keywords, which since have been included
+in core Mercurial (see :hg:`help templates -v`):
+
+  - obsolete
+  - obsfate (including obsfatedata, see also obsfate* template functions)
 
-    The template itself is not complex, the data are basically a list of
-    successortset. Each successorset is a dict with these fields:
+For compatibility, this extension also provides the following aliases to
+template keywords from core Mercurial:
 
-      - "verb", how did the revision changed, pruned or rewritten for the moment
-      - "users" a sorted list of users that have create obs marker between current
-        changeset and one of its successor
-      - "min_date" the tiniest date of the first obs marker between current
-        changeset and one of its successor
-      - "max_date" the biggest date between current changeset and one of its
-        successor
-      - "successors" a sorted list of locally know successors node ids
-      - "markers" the raw list of changesets.
+  - precursors (deprecated, use predecessors instead)
+  - successors (deprecated, use successorssets instead)
+  - troubles (deprecated, use instabilities instead)
 """
 
 evolutionhelptext = b"""
 Obsolescence markers make it possible to mark changesets that have been
-deleted or superset in a new version of the changeset.
+deleted or superseded in a new version of the changeset.
 
 Unlike the previous way of handling such changes, by stripping the old
 changesets from the repository, obsolescence markers can be propagated
--- a/hgext3rd/evolve/compat.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/compat.py	Fri Jul 31 18:40:28 2020 +0800
@@ -12,6 +12,7 @@
 from mercurial import (
     context,
     copies,
+    merge as mergemod,
     obsolete,
     pycompat,
     registrar,
@@ -35,6 +36,13 @@
 except (AttributeError, ImportError):
     dirs = util.dirs  # pytype: disable=module-attr
 
+# hg <= 5.4 (b7808443ed6a)
+try:
+    from mercurial import mergestate as mergestatemod
+    mergestate = mergestatemod.mergestate
+except (AttributeError, ImportError):
+    mergestate = mergemod.mergestate  # pytype: disable=module-attr
+
 from . import (
     exthelper,
 )
--- a/hgext3rd/evolve/evolvecmd.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/evolvecmd.py	Fri Jul 31 18:40:28 2020 +0800
@@ -1981,7 +1981,7 @@
 def continueevolve(ui, repo, evolvestate):
     """logic for handling of `hg evolve --continue`"""
 
-    ms = merge.mergestate.read(repo)
+    ms = compat.mergestate.read(repo)
     mergeutil.checkunresolved(ms)
     if (evolvestate[b'command'] == b'next'
         or evolvestate[b'category'] == b'orphan'):
--- a/hgext3rd/evolve/metadata.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/metadata.py	Fri Jul 31 18:40:28 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.0'
+__version__ = b'10.0.1.dev'
 testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4'
 minimumhgversion = b'4.6'
 buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obsdiscovery.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/obsdiscovery.py	Fri Jul 31 18:40:28 2020 +0800
@@ -28,7 +28,7 @@
     exchange,
     extensions,
     localrepo,
-    node,
+    node as nodemod,
     obsolete,
     scmutil,
     store,
@@ -195,7 +195,7 @@
 def debugobshashrange(ui, repo, **opts):
     """display the ::REVS set topologically sorted in a stable way
     """
-    s = node.short
+    s = nodemod.short
     revs = scmutil.revrange(repo, opts['rev'])
     # prewarm depth cache
     if revs:
@@ -218,7 +218,7 @@
              r[1],
              rangelength(repo, r),
              depthrev(repo, r[0]),
-             node.short(_obshashrange(repo, r)))
+             s(_obshashrange(repo, r)))
         ui.status(linetemplate % d)
     repo.obsstore.rangeobshashcache.save(repo)
 
@@ -230,7 +230,7 @@
     if obshash is not None:
         return obshash
     pieces = []
-    nullid = node.nullid
+    nullid = nodemod.nullid
     if repo.stablerange.rangelength(repo, rangeid) == 1:
         rangenode = cl.node(rangeid[0])
         tmarkers = repo.obsstore.relevantmarkers([rangenode])
@@ -249,7 +249,7 @@
     # note: if there is only one subrange with actual data, we'll just
     # reuse the same hash.
     if not pieces:
-        obshash = node.nullid
+        obshash = nodemod.nullid
     elif len(pieces) != 1 or obshash is None:
         sha = hashlib.sha1()
         for p in pieces:
@@ -293,34 +293,35 @@
     """
     affected_nodes = set()
     known_markers = set(markers)
-    node_to_proceed = set()
-    marker_to_proceed = set(known_markers)
+    nodes_to_proceed = set()
+    markers_to_proceed = set(known_markers)
 
-    obsstore = repo.obsstore
+    successors = repo.obsstore.successors
+    predecessors = repo.obsstore.predecessors
 
-    while node_to_proceed or marker_to_proceed:
-        while marker_to_proceed:
-            m = marker_to_proceed.pop()
+    while nodes_to_proceed or markers_to_proceed:
+        while markers_to_proceed:
+            marker = markers_to_proceed.pop()
             # check successors and parent
-            if m[1]:
-                relevant = (m[1], )
+            if marker[1]:
+                relevant = (marker[1], )
             else: # prune case
-                relevant = ((m[0], ), m[5])
-            for l in relevant:
-                if l is None:
+                relevant = ((marker[0], ), marker[5])
+            for relnodes in relevant:
+                if relnodes is None:
                     continue
-                for n in l:
-                    if n not in affected_nodes:
-                        node_to_proceed.add(n)
-                    affected_nodes.add(n)
-        # marker_to_proceed is now empty:
-        if node_to_proceed:
-            n = node_to_proceed.pop()
+                for node in relnodes:
+                    if node not in affected_nodes:
+                        nodes_to_proceed.add(node)
+                    affected_nodes.add(node)
+        # markers_to_proceed is now empty:
+        if nodes_to_proceed:
+            node = nodes_to_proceed.pop()
             markers = set()
-            markers.update(obsstore.successors.get(n, ()))
-            markers.update(obsstore.predecessors.get(n, ()))
+            markers.update(successors.get(node, ()))
+            markers.update(predecessors.get(node, ()))
             markers -= known_markers
-            marker_to_proceed.update(markers)
+            markers_to_proceed.update(markers)
             known_markers.update(markers)
 
     return affected_nodes
@@ -669,7 +670,7 @@
     repo.obsstore.rangeobshashcache.update(repo)
     for r in ranges:
         if r[0] is None:
-            result.append(node.wdirid)
+            result.append(nodemod.wdirid)
         else:
             result.append(_obshashrange(repo, r))
     repo.obsstore.rangeobshashcache.save(repo)
@@ -843,13 +844,13 @@
     revs = unfi.revs(b'::(%ln - null) - %ld', pullop.common, filteredrevs)
     boundaries = {b'heads': pullop.pulledsubset}
     if not revs: # nothing common
-        boundaries[b'common'] = [node.nullid]
+        boundaries[b'common'] = [nodemod.nullid]
         return boundaries
 
     if not usediscovery(repo):
         # discovery disabled by users.
         repo.ui.status(obsdiscovery_skip_message)
-        boundaries[b'common'] = [node.nullid]
+        boundaries[b'common'] = [nodemod.nullid]
         return boundaries
 
     if bundle2 and _canobshashrange(repo, remote):
@@ -864,7 +865,7 @@
             common = repo.set("heads(only(%ld, %ln))", revs, missing)
             boundaries[b'common'] = [c.node() for c in common]
     else:
-        boundaries[b'common'] = [node.nullid]
+        boundaries[b'common'] = [nodemod.nullid]
     return boundaries
 
 # merge later for outer layer wrapping
--- a/hgext3rd/evolve/obsexchange.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/obsexchange.py	Fri Jul 31 18:40:28 2020 +0800
@@ -52,7 +52,7 @@
 
 ARGUMENTS_LIMIT = 200
 
-OVERFLOW_MSG = """obsmarkers differ for %d common nodes
+OVERFLOW_MSG = b"""obsmarkers differ for %d common nodes
 |
 | This might be too much for the remote HTTP server that doesn't accept
 | arguments through POST request. (config: experimental.httppostargs=yes)
--- a/hgext3rd/evolve/obshistory.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/evolve/obshistory.py	Fri Jul 31 18:40:28 2020 +0800
@@ -219,9 +219,14 @@
         r = obsutil.successorsandmarkers(repo, ctx)
         if r is None:
             r = []
-        for succset in sorted(r):
-            if succset[b'markers']:
-                yield (succset[b'successors'], succset[b'markers'])
+        # replacing dicts and sets, they can't be compared
+        data = [
+            (succset[b'successors'], tuple(sorted(succset[b'markers'])))
+            for succset in r
+        ]
+        for (nodes, markers) in sorted(data):
+            if markers:
+                yield (nodes, markers)
     else:
         markers = successors.get(ctx.node(), ())
         for marker in sorted(markers):
--- a/hgext3rd/pullbundle.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/pullbundle.py	Fri Jul 31 18:40:28 2020 +0800
@@ -190,7 +190,12 @@
         ms = missingrevs.copy()
         ss = []
     allslices = []
-    missingheads = [rev(n) for n in sorted(outgoing.missingheads, reverse=True)]
+    # hg <= 5.4 (c93dd9d9f1e6)
+    if util.safehasattr(outgoing, 'ancestorsof'):
+        missingheads = outgoing.ancestorsof
+    else:
+        missingheads = outgoing.missingheads
+    missingheads = [rev(n) for n in sorted(missingheads, reverse=True)]
     for head in missingheads:
         localslices = []
         localmissing = set(repo.revs(b'%ld and ::%d', missingrevs, head))
@@ -375,9 +380,11 @@
     return num and not num & (num - 1)
 
 def outgoingfromnodes(repo, nodes):
-    return discovery.outgoing(repo,
-                              missingroots=nodes,
-                              missingheads=nodes)
+    # hg <= 5.4 (c93dd9d9f1e6)
+    if r'ancestorsof' in discovery.outgoing.__init__.__code__.co_varnames:
+        return discovery.outgoing(repo, missingroots=nodes, ancestorsof=nodes)
+    else:
+        return discovery.outgoing(repo, missingroots=nodes, missingheads=nodes)
 
 # changegroup part construction
 
--- a/hgext3rd/serverminitopic.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/serverminitopic.py	Fri Jul 31 18:40:28 2020 +0800
@@ -33,6 +33,14 @@
                default=False,
     )
 
+# hg <= 5.4 (e2d17974a869)
+def nonpublicphaseroots(repo):
+    if util.safehasattr(repo._phasecache, 'nonpublicphaseroots'):
+        return repo._phasecache.nonpublicphaseroots(repo)
+    return set().union(
+        *[roots for roots in repo._phasecache.phaseroots[1:] if roots]
+    )
+
 def hasminitopic(repo):
     """true if minitopic is enabled on the repository
 
@@ -106,11 +114,10 @@
     cl = repo.changelog
     fr = cl.filteredrevs
     nm = cl.nodemap
-    for roots in repo._phasecache.phaseroots[1:]:
-        for n in roots:
-            r = nm.get(n)
-            if r not in fr and r < maxrev:
-                revs.add(r)
+    for n in nonpublicphaseroots(repo):
+        r = nm.get(n)
+        if r not in fr and r < maxrev:
+            revs.add(r)
     key = node.nullid
     revs = sorted(revs)
     if revs:
--- a/hgext3rd/topic/__init__.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/topic/__init__.py	Fri Jul 31 18:40:28 2020 +0800
@@ -202,7 +202,7 @@
               b'topic.active': b'green',
               }
 
-__version__ = b'0.19.0'
+__version__ = b'0.19.1.dev'
 
 testedwith = b'4.6.2 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4'
 minimumhgversion = b'4.6'
@@ -1269,7 +1269,11 @@
 
 def pushoutgoingwrap(orig, ui, repo, *args, **opts):
     if opts.get('topic'):
-        topicrevs = repo.revs(b'topic(%s) - obsolete()', opts['topic'])
+        topic = opts['topic']
+        if topic == b'.':
+            topic = repo.currenttopic
+        topic = b'literal:' + topic
+        topicrevs = repo.revs(b'topic(%s) - obsolete()', topic)
         opts.setdefault('rev', []).extend(topicrevs)
     return orig(ui, repo, *args, **opts)
 
--- a/hgext3rd/topic/compat.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/topic/compat.py	Fri Jul 31 18:40:28 2020 +0800
@@ -38,3 +38,11 @@
     if util.safehasattr(cl.index, 'get_rev'):
         return cl.index.get_rev
     return cl.nodemap.get
+
+# hg <= 5.4 (e2d17974a869)
+def nonpublicphaseroots(repo):
+    if util.safehasattr(repo._phasecache, 'nonpublicphaseroots'):
+        return repo._phasecache.nonpublicphaseroots(repo)
+    return set().union(
+        *[roots for roots in repo._phasecache.phaseroots[1:] if roots]
+    )
--- a/hgext3rd/topic/stack.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/topic/stack.py	Fri Jul 31 18:40:28 2020 +0800
@@ -44,10 +44,10 @@
     this area.
     """
     phasesets = repo._phasecache._phasesets
-    if not phasesets or None in phasesets[phases.draft:]:
+    if not phasesets:
         return repo.revs(b'(not public()) - obsolete()')
 
-    result = set.union(*phasesets[phases.draft:])
+    result = set.union(*[phasesets[phase] for phase in phases.trackedphases])
     result -= obsolete.getrevs(repo, b'obsolete')
     return result
 
--- a/hgext3rd/topic/topicmap.py	Fri May 08 20:36:28 2020 +0800
+++ b/hgext3rd/topic/topicmap.py	Fri Jul 31 18:40:28 2020 +0800
@@ -71,11 +71,10 @@
     cl = repo.changelog
     fr = cl.filteredrevs
     getrev = compat.getgetrev(cl)
-    for roots in repo._phasecache.phaseroots[1:]:
-        for n in roots:
-            r = getrev(n)
-            if r not in fr and r < maxrev:
-                revs.add(r)
+    for n in compat.nonpublicphaseroots(repo):
+        r = getrev(n)
+        if r not in fr and r < maxrev:
+            revs.add(r)
     key = nullid
     revs = sorted(revs)
     if revs:
--- a/tests/hghaveaddon.py	Fri May 08 20:36:28 2020 +0800
+++ b/tests/hghaveaddon.py	Fri Jul 31 18:40:28 2020 +0800
@@ -12,3 +12,14 @@
         except ImportError:
             return False
     return True
+
+@hghave.check("flake8", "Flake8 python linter")
+def has_flake8():
+    try:
+        import flake8
+
+        flake8.__version__
+    except ImportError:
+        return False
+    else:
+        return True
--- a/tests/test-check-compat-strings.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-check-compat-strings.t	Fri Jul 31 18:40:28 2020 +0800
@@ -5,4 +5,4 @@
   > evolution = all
   > EOF
 
-  $ $TESTDIR/../contrib/check-compat-strings.sh "$TESTDIR/../hgext3rd/" "$RUNTESTDIR/.."
+  $ $TESTDIR/testlib/check-compat-strings.sh "$TESTDIR/../hgext3rd/" "$RUNTESTDIR/.."
--- a/tests/test-check-flake8.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-check-flake8.t	Fri Jul 31 18:40:28 2020 +0800
@@ -1,12 +1,4 @@
-#require test-repo
-
-  $ checkflake8() {
-  >   if ! (which flake8 > /dev/null); then
-  >     echo skipped: missing tool: flake8;
-  >     exit 80;
-  >   fi;
-  > };
-  $ checkflake8
+#require test-repo flake8
 
 Copied from Mercurial core (60ee2593a270)
 
@@ -17,4 +9,4 @@
   $ hg files -0 'set:(**.py or grep("^#!.*python")) - removed()' \
   > -X hgext3rd/evolve/thirdparty \
   > 2>/dev/null \
-  > | xargs -0 flake8
+  > | xargs -0 "$PYTHON" -m flake8
--- a/tests/test-check-pyflakes.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-check-pyflakes.t	Fri Jul 31 18:40:28 2020 +0800
@@ -7,5 +7,7 @@
 run pyflakes on all tracked files ending in .py or without a file ending
 (skipping binary file random-seed)
 
-  $ hg locate -I 'set:(**.py or grep("^#!.*python")) - removed()' 2>/dev/null \
-  > | xargs pyflakes 2>/dev/null
+  $ hg files -0 'set:(**.py or grep("^#!.*python")) - removed()' \
+  > -X hgext3rd/evolve/thirdparty \
+  > 2>/dev/null \
+  > | xargs -0 "$PYTHON" -m pyflakes 2>/dev/null
--- a/tests/test-evolve-obshistory-content-divergent.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-evolve-obshistory-content-divergent.t	Fri Jul 31 18:40:28 2020 +0800
@@ -98,6 +98,11 @@
          +A1
   
   
+  $ hg obslog --hidden 'desc(A0)' --no-origin -f
+  x  471f378eab4c (1) A0
+       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)
+  
 
 Check that with all option, every changeset is shown
   $ hg obslog --hidden --all 471f378eab4c --patch
@@ -123,6 +128,15 @@
   |
   x  471f378eab4c (1) A0
   
+  $ hg obslog --hidden --all 'desc(A0)' --no-origin -f
+  @  65b757b745b9 (3) A2
+  |
+  | *  fdf9bde5129a (2) A1
+  |/
+  x  471f378eab4c (1) A0
+       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
   [
       {
--- a/tests/test-evolve-obshistory.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-evolve-obshistory.t	Fri Jul 31 18:40:28 2020 +0800
@@ -1,5 +1,4 @@
-This test file test the various messages when accessing obsolete
-revisions.
+Testing obslog and other commands accessing obsolete revisions.
 
 Global setup
 ============
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-wdir.t	Fri Jul 31 18:40:28 2020 +0800
@@ -0,0 +1,114 @@
+===============================================
+Testing evolution of obsolete working directory
+===============================================
+
+Pulling changes from other repos can make your working directory parent (wdir)
+obsolete, most probably because now it has a new successor. But there are
+other cases as well where it might be pruned with no successors or split
+in multiple changesets etc.
+
+This test file deals with all the possible cases for the evolution from an
+obsolete working directory parent.
+
+.. Case A: obsolete wdp with single successor
+..     Resolution : simply update to the successor
+..
+.. Case B: obsolete wdp with no successor (simply pruned)
+..     Resolution : update to a not-dead ancestor
+..
+.. Case C: obsolete wdp with multiple successor (divergence rewriting)
+..     Resolution : #TODO: not handled yet
+..
+.. Case D: obsolete wdp with multiple successor (split rewriting)
+..     Resolution : #TODO: not handled yet
+
+A. Obsolete wdp with single successor
+-------------------------------------
+
+Setup
+  $ . $TESTDIR/testlib/common.sh
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > evolve=
+  > rebase=
+  > [alias]
+  > glog = log --graph --template "{rev}:{node|short} ({phase}): {desc|firstline} {if(troubles, '[{troubles}]')}\n"
+  > EOF
+
+  $ hg init repo
+  $ cd repo
+  $ mkcommit c_A
+  $ mkcommit c_B
+  $ hg amend -m "u_B"
+  $ hg up -r 'desc(c_B)' --hidden
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  updated to hidden changeset 707ee88b2870
+  (hidden revision '707ee88b2870' was rewritten as: 9bf151312dec)
+  working directory parent is obsolete! (707ee88b2870)
+  (use 'hg evolve' to update to its successor: 9bf151312dec)
+
+  $ hg evolve
+  update:[2] u_B
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 9bf151312dec
+  $ hg glog
+  @  2:9bf151312dec (draft): u_B
+  |
+  o  0:9f0188af4c58 (draft): c_A
+  
+
+B. Obsolete wdp with no successor
+---------------------------------
+
+  $ hg prune .
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory is now at 9f0188af4c58
+  1 changesets pruned
+  $ hg up -r 'desc(c_B)' --hidden
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  updated to hidden changeset 707ee88b2870
+  (hidden revision '707ee88b2870' is pruned)
+  working directory parent is obsolete! (707ee88b2870)
+  (use 'hg evolve' to update to its parent successor)
+
+  $ hg evolve
+  update:[0] c_A
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory is now at 9f0188af4c58
+  $ hg glog
+  @  0:9f0188af4c58 (draft): c_A
+  
+
+C. Obsolete wdp with multiple successor (divergence rewriting)
+---------------------------------------------------------------
+
+  $ hg metaedit -r 'desc(u_B)' -d '0 1' --hidden
+  $ hg metaedit -r 'desc(c_B)' -d '0 1' --hidden
+  2 new content-divergent changesets
+  $ hg up -r 'min(desc(c_B))' --hidden
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  updated to hidden changeset 707ee88b2870
+  (hidden revision '707ee88b2870' has diverged)
+  working directory parent is obsolete! (707ee88b2870)
+  (707ee88b2870 has diverged, use 'hg evolve --list --content-divergent' to resolve the issue)
+
+  $ hg evolve
+  parent is obsolete with multiple successors:
+  [3] u_B
+  [4] c_B
+  [2]
+
+  $ hg glog
+  *  4:39e54eb7aa3c (draft): c_B [content-divergent]
+  |
+  | *  3:90624b574289 (draft): u_B [content-divergent]
+  |/
+  | @  1:707ee88b2870 (draft): c_B
+  |/
+  o  0:9f0188af4c58 (draft): c_A
+  
+
+D. Obsolete wdp with multiple successor (split rewriting)
+----------------------------------------------------------
+
+#TODO: yet to write tests for this case
--- a/tests/test-evolve.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-evolve.t	Fri Jul 31 18:40:28 2020 +0800
@@ -48,7 +48,7 @@
   """"""""""""""""""""""""
   
       Obsolescence markers make it possible to mark changesets that have been
-      deleted or superset in a new version of the changeset.
+      deleted or superseded in a new version of the changeset.
   
       Unlike the previous way of handling such changes, by stripping the old
       changesets from the repository, obsolescence markers can be propagated
--- a/tests/test-obsolete.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-obsolete.t	Fri Jul 31 18:40:28 2020 +0800
@@ -523,22 +523,17 @@
 
 should not rebase extinct changesets
 
-#excluded 'whole rebase set is extinct and ignored.' message not in core
-  $ hg rebase -b '3' -d 4 --traceback --config experimental.rebaseskipobsolete=0
-  rebasing 3:0d3f46688ccc "add obsol_c"
+  $ hg rebase -b '3' -d 4 --traceback
+  note: not rebasing 3:0d3f46688ccc "add obsol_c", already in destination as 4:725c380fe99b "add obsol_c'"
   rebasing 8:159dfc9fa5d3 "add obsol_d''"
-  2 new content-divergent changesets
-  $ hg --hidden log -q -r 'successors(3)'
+  $ hg --hidden log -q -r 'successors(min(desc("re:^add obsol_c$")))'
   4:725c380fe99b
-  10:2033b4e49474
   $ hg up tip
   ? files updated, 0 files merged, 0 files removed, 0 files unresolved (glob)
   $ hg log -G --template='{rev} - {node|short} {desc}\n'
-  @  11 - 9468a5f5d8b2 add obsol_d''
+  @  10 - 8396b69aa9f7 add obsol_d''
   |
-  *  10 - 2033b4e49474 add obsol_c
-  |
-  *  4 - 725c380fe99b add obsol_c'
+  o  4 - 725c380fe99b add obsol_c'
   |
   o  1 - 7c3bad9141dc add b
   |
@@ -554,14 +549,13 @@
   adding changesets
   adding manifests
   adding file changes
-  added 2 changesets with 1 changes to [12] files (re)
-  3 new obsolescence markers
+  added 1 changesets with 1 changes to 1 files
+  2 new obsolescence markers
   obsoleted 1 changesets
-  2 new content-divergent changesets
-  $ hg up -q 10
+  $ hg up -q 'desc("re:^add obsol_c'\''$")'
   $ mkcommit "obsol_d'''"
   created new head
-  $ hg debugobsolete `getid 11` `getid 12`
+  $ hg debugobsolete `getid 'max(desc("re:^add obsol_d'\'\''$"))'` `getid 'desc("re:^add obsol_d'\'\'\''$")'`
   obsoleted 1 changesets
   $ hg push ../other-new --traceback
   pushing to ../other-new
@@ -578,15 +572,13 @@
 (make an obsolete changeset public)
 
   $ cd local
-  $ hg phase --hidden --public 11
+  $ hg phase --hidden --public 'max(desc("re:^add obsol_d'\'\''$"))'
   1 new phase-divergent changesets
   $ hg log -G --template='{rev} - ({phase}) {node|short} {desc}\n'
-  @  12 - (draft) 6db5e282cb91 add obsol_d'''
+  @  11 - (draft) 5a9eef7b778b add obsol_d'''
   |
-  | o  11 - (public) 9468a5f5d8b2 add obsol_d''
+  | o  10 - (public) 8396b69aa9f7 add obsol_d''
   |/
-  o  10 - (public) 2033b4e49474 add obsol_c
-  |
   o  4 - (public) 725c380fe99b add obsol_c'
   |
   o  1 - (public) 7c3bad9141dc add b
@@ -594,9 +586,9 @@
   o  0 - (public) 1f0dee641bb7 add a
   
   $ hg log -r 'phasedivergent()'
-  changeset:   12:6db5e282cb91
+  changeset:   11:5a9eef7b778b
   tag:         tip
-  parent:      10:2033b4e49474
+  parent:      4:725c380fe99b
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   instability: phase-divergent
@@ -605,7 +597,7 @@
   $ hg push ../other-new/
   pushing to ../other-new/
   searching for changes
-  abort: push includes phase-divergent changeset: 6db5e282cb91!
+  abort: push includes phase-divergent changeset: 5a9eef7b778b!
   (use 'hg evolve' to get a stable history or --force to ignore warnings)
   [255]
 
@@ -618,31 +610,26 @@
   $ echo 42 >> f
   $ hg commit --amend --traceback --quiet
   $ hg log -G
-  @  changeset:   14:705ab2a6b72e
+  @  changeset:   13:6491691ca36f
   |  tag:         tip
-  |  parent:      10:2033b4e49474
+  |  parent:      4:725c380fe99b
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     add f
   |
-  | *  changeset:   12:6db5e282cb91
-  |/   parent:      10:2033b4e49474
+  | *  changeset:   11:5a9eef7b778b
+  |/   parent:      4:725c380fe99b
   |    user:        test
   |    date:        Thu Jan 01 00:00:00 1970 +0000
   |    instability: phase-divergent
   |    summary:     add obsol_d'''
   |
-  | o  changeset:   11:9468a5f5d8b2
-  |/   user:        test
+  | o  changeset:   10:8396b69aa9f7
+  |/   parent:      4:725c380fe99b
+  |    user:        test
   |    date:        Thu Jan 01 00:00:00 1970 +0000
   |    summary:     add obsol_d''
   |
-  o  changeset:   10:2033b4e49474
-  |  parent:      4:725c380fe99b
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add obsol_c
-  |
   o  changeset:   4:725c380fe99b
   |  parent:      1:7c3bad9141dc
   |  user:        test
@@ -667,76 +654,51 @@
   909a0fb57e5d909f353d89e394ffd7e0890fec88 159dfc9fa5d334d7e03a0aecfc7f7ab4c3431fea 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   1f0dee641bb7258c56bd60e93edfa2405381c41e 83b5778897adafb967ef2f75be3aaa4fce49a4cc 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   83b5778897adafb967ef2f75be3aaa4fce49a4cc 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  0d3f46688ccc6e756c7e96cf64c391c411309597 2033b4e494742365851fac84d276640cbf52833e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
-  159dfc9fa5d334d7e03a0aecfc7f7ab4c3431fea 9468a5f5d8b2c5d91e17474e95ae4791e9718fdf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
-  9468a5f5d8b2c5d91e17474e95ae4791e9718fdf 6db5e282cb91df5c43ff1f1287c119ff83230d42 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  0b1b6dd009c037985363e2290a0b579819f659db 705ab2a6b72e2cd86edb799ebe15f2695f86143e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
-#no produced by 2.3
-33d458d86621f3186c40bfccd77652f4a122743e 3734a65252e69ddcced85901647a4f335d40de1e 0 {'date': '* *', 'user': 'test'} (glob)
+  159dfc9fa5d334d7e03a0aecfc7f7ab4c3431fea 8396b69aa9f7b4ea03ed94c63641e3c021d71f0c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
+  8396b69aa9f7b4ea03ed94c63641e3c021d71f0c 5a9eef7b778bf2ba5e6cc1b741ec5a2110ba1480 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  bc294e7f0681bbed7b83047814e9eadabead2f8d 6491691ca36ffa1b1c48d9bf8605ac5a25d200f6 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
 
 Check divergence detection (note: multiple successors is sorted by changeset hash)
 
-  $ hg up 9468a5f5d8b2 #  add obsol_d''
+  $ hg up 'max(desc("re:^add obsol_d'\'\''$"))' #  add obsol_d''
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ mkcommit "obsolet_conflicting_d"
   $ hg summary
-  parent: 15:50f11e5e3a63 tip
+  parent: 14:553bdab4dee8 tip
    add obsolet_conflicting_d
   branch: default
   commit: (clean)
   update: (2|9|11) new changesets, (3|9|10) branch heads \(merge\) (re)
   phases: 3 draft
   phase-divergent: 1 changesets
-  $ hg debugobsolete `getid a7a6f2b5d8a5` `getid 50f11e5e3a63`
+  $ hg debugobsolete `getid 'desc("re:^add d$")'` `getid 'desc("re:^add obsolet_conflicting_d$")'`
   2 new content-divergent changesets
   $ hg log -r 'contentdivergent()'
-  changeset:   12:6db5e282cb91
-  parent:      10:2033b4e49474
+  changeset:   11:5a9eef7b778b
+  parent:      4:725c380fe99b
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   instability: phase-divergent, content-divergent
   summary:     add obsol_d'''
   
-  changeset:   15:50f11e5e3a63
+  changeset:   14:553bdab4dee8
   tag:         tip
-  parent:      11:9468a5f5d8b2
+  parent:      10:8396b69aa9f7
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   instability: content-divergent
   summary:     add obsolet_conflicting_d
   
 
-  $ hg up --hidden 3 -q
-  updated to hidden changeset 0d3f46688ccc
-  (hidden revision '0d3f46688ccc' has diverged)
-  working directory parent is obsolete! (0d3f46688ccc)
-  $ hg evolve
-  parent is obsolete with multiple successors:
-  [4] add obsol_c'
-  [10] add obsol_c
-  [2]
-  $ hg olog -a
-  o  2033b4e49474 (10) add obsol_c
-  |    rebased(parent) from 0d3f46688ccc using rebase by test (Thu Jan 01 00:00:00 1970 +0000)
-  |
-  | o  725c380fe99b (4) add obsol_c'
-  |/     rewritten from 0d3f46688ccc by test (Thu Jan 01 00:00:00 1970 +0000)
-  |
-  @  0d3f46688ccc (3) add obsol_c
-  |    rewritten from 4538525df7e2 by test (Thu Jan 01 00:00:00 1970 +0000)
-  |
-  x  4538525df7e2 (2) add c
-  
-
 Check import reports new unstable changeset:
 
-  $ hg up --hidden 2
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg up --hidden 'desc("re:^add c$")'
+  1 files updated, 0 files merged, 3 files removed, 0 files unresolved
   updated to hidden changeset 4538525df7e2
-  (hidden revision '4538525df7e2' has diverged)
+  (hidden revision '4538525df7e2' was rewritten as: 725c380fe99b)
   working directory parent is obsolete! (4538525df7e2)
-  (4538525df7e2 has diverged, use 'hg evolve --list --content-divergent' to resolve the issue)
-  $ hg export 9468a5f5d8b2 | hg import -
+  (use 'hg evolve' to update to its successor: 725c380fe99b)
+  $ hg export 'desc("re:^add obsol_d'\'\''$")' | hg import -
   applying patch from stdin
   1 new orphan changesets
 
@@ -745,7 +707,7 @@
 ==============================
 
   $ hg log -G --hidden
-  @  changeset:   16:a5f7a21fe7bc
+  @  changeset:   15:a5f7a21fe7bc
   |  tag:         tip
   |  parent:      2:4538525df7e2
   |  user:        test
@@ -753,44 +715,39 @@
   |  instability: orphan
   |  summary:     add obsol_d''
   |
-  | *  changeset:   15:50f11e5e3a63
-  | |  parent:      11:9468a5f5d8b2
+  | *  changeset:   14:553bdab4dee8
+  | |  parent:      10:8396b69aa9f7
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  instability: content-divergent
   | |  summary:     add obsolet_conflicting_d
   | |
-  | | o  changeset:   14:705ab2a6b72e
-  | | |  parent:      10:2033b4e49474
+  | | o  changeset:   13:6491691ca36f
+  | | |  parent:      4:725c380fe99b
   | | |  user:        test
   | | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | | |  summary:     add f
   | | |
-  | | | x  changeset:   13:0b1b6dd009c0
-  | | |/   parent:      10:2033b4e49474
+  | | | x  changeset:   12:bc294e7f0681
+  | | |/   parent:      4:725c380fe99b
   | | |    user:        test
   | | |    date:        Thu Jan 01 00:00:00 1970 +0000
-  | | |    obsolete:    amended using amend as 14:705ab2a6b72e
+  | | |    obsolete:    amended using amend as 13:6491691ca36f
   | | |    summary:     add f
   | | |
-  | | | *  changeset:   12:6db5e282cb91
-  | | |/   parent:      10:2033b4e49474
+  | | | *  changeset:   11:5a9eef7b778b
+  | | |/   parent:      4:725c380fe99b
   | | |    user:        test
   | | |    date:        Thu Jan 01 00:00:00 1970 +0000
   | | |    instability: phase-divergent, content-divergent
   | | |    summary:     add obsol_d'''
   | | |
-  | o |  changeset:   11:9468a5f5d8b2
-  | |/   user:        test
+  | o |  changeset:   10:8396b69aa9f7
+  | |/   parent:      4:725c380fe99b
+  | |    user:        test
   | |    date:        Thu Jan 01 00:00:00 1970 +0000
   | |    summary:     add obsol_d''
   | |
-  | o  changeset:   10:2033b4e49474
-  | |  parent:      4:725c380fe99b
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     add obsol_c
-  | |
   | | x  changeset:   9:83b5778897ad
   | |    parent:      -1:000000000000
   | |    user:        test
@@ -802,7 +759,7 @@
   | | |  parent:      3:0d3f46688ccc
   | | |  user:        test
   | | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | | |  obsolete:    rebased using rebase as 11:9468a5f5d8b2
+  | | |  obsolete:    rebased using rebase as 10:8396b69aa9f7
   | | |  summary:     add obsol_d''
   | | |
   | | | x  changeset:   7:909a0fb57e5d
@@ -824,7 +781,7 @@
   | | |    user:        test
   | | |    date:        Thu Jan 01 00:00:00 1970 +0000
   | | |    obsolete:    rewritten as 6:95de7fc6918d
-  | | |    obsolete:    rewritten as 15:50f11e5e3a63
+  | | |    obsolete:    rewritten as 14:553bdab4dee8
   | | |    summary:     add d
   | | |
   | o |  changeset:   4:725c380fe99b
@@ -838,7 +795,6 @@
   | |    user:        test
   | |    date:        Thu Jan 01 00:00:00 1970 +0000
   | |    obsolete:    rewritten as 4:725c380fe99b
-  | |    obsolete:    rebased using rebase as 10:2033b4e49474
   | |    summary:     add obsol_c
   | |
   x |  changeset:   2:4538525df7e2
@@ -860,17 +816,17 @@
 
 Simple rewrite
 
-  $ hg  --hidden debugobsolete --rev 3
+  $ hg  --hidden debugobsolete --rev 'min(desc("re:^add obsol_c$"))'
   4538525df7e2b9f09423636c61ef63a4cb872a2d 0d3f46688ccc6e756c7e96cf64c391c411309597 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
 
 simple rewrite with a prune attached to it
 
-  $ hg debugobsolete --rev 15
-  a7a6f2b5d8a54b81bc7aa2fba2934ad6d700a79e 50f11e5e3a63806e678c734e525502f522d37e38 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  $ hg debugobsolete --rev 'desc("re:^add obsolet_conflicting_d$")'
+  a7a6f2b5d8a54b81bc7aa2fba2934ad6d700a79e 553bdab4dee8f0032cd60de38be72d1d2e8225d5 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
 
 Transitive rewrite
 
-  $ hg --hidden debugobsolete --rev 8
+  $ hg --hidden debugobsolete --rev 'min(desc("re:^add obsol_d'\'\''$"))'
   909a0fb57e5d909f353d89e394ffd7e0890fec88 159dfc9fa5d334d7e03a0aecfc7f7ab4c3431fea 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   95de7fc6918dea4c9c8d5382f50649794b474c4a 909a0fb57e5d909f353d89e394ffd7e0890fec88 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   a7a6f2b5d8a54b81bc7aa2fba2934ad6d700a79e 95de7fc6918dea4c9c8d5382f50649794b474c4a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
--- a/tests/test-topic-push.t	Fri May 08 20:36:28 2020 +0800
+++ b/tests/test-topic-push.t	Fri Jul 31 18:40:28 2020 +0800
@@ -192,6 +192,22 @@
   |/
   o  0 default  public CA
   
+
+  $ hg outgoing draft --topic nonexistent
+  abort: topic 'nonexistent' does not exist!
+  [255]
+  $ hg push draft --topic nonexistent
+  abort: topic 'nonexistent' does not exist!
+  [255]
+
+  $ hg up babar
+  switching to topic babar
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg outgoing draft --topic .
+  comparing with $TESTTMP/draft
+  searching for changes
+  5 default babar draft C'A
+
   $ hg outgoing draft --topic babar
   comparing with $TESTTMP/draft
   searching for changes
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testlib/check-compat-strings.sh	Fri Jul 31 18:40:28 2020 +0800
@@ -0,0 +1,32 @@
+#!/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