changeset 6530:afc41038dd3d

branching: merge with stable
author Anton Shestakov <av6@dwimlabs.net>
date Wed, 05 Jul 2023 15:57:19 -0300
parents 206fb02765bf (diff) ce67a138c5d6 (current diff)
children ce450e90a0a5
files CHANGELOG hgext3rd/evolve/metadata.py hgext3rd/topic/__init__.py
diffstat 27 files changed, 558 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Wed Jul 05 15:51:56 2023 -0300
+++ b/CHANGELOG	Wed Jul 05 15:57:19 2023 -0300
@@ -1,6 +1,15 @@
 Changelog
 =========
 
+11.1.0 - in progress
+--------------------
+
+  * evolve, pullbundle: drop compatibility with Mercurial 4.8
+
+topic (1.1.0)
+
+  * drop compatibility with Mercurial 4.8
+
 11.0.2 -- 2023-07-05
 --------------------
 
--- a/debian/control	Wed Jul 05 15:51:56 2023 -0300
+++ b/debian/control	Wed Jul 05 15:57:19 2023 -0300
@@ -7,7 +7,7 @@
  Pierre-Yves David <pierre-yves.david@logilab.fr>,
 Standards-Version: 4.3.0
 Build-Depends:
- mercurial (>= 4.8),
+ mercurial (>= 4.9),
  python3,
  debhelper (>= 10),
  dh-python,
@@ -26,7 +26,7 @@
  ${python3:Depends},
  ${misc:Depends},
  ${sphinxdoc:Depends},
- mercurial (>= 4.8),
+ mercurial (>= 4.9),
 Built-Using: ${sphinxdoc:Built-Using}
 Description: evolve extension for Mercurial
  This package provides the experimental "evolve" extension for the Mercurial
--- a/hgext3rd/evolve/__init__.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/__init__.py	Wed Jul 05 15:57:19 2023 -0300
@@ -33,7 +33,7 @@
 backported to older version of Mercurial by this extension. Some older
 experimental protocols are also supported for a longer time in the extension to
 help people transitioning. (The extension is currently compatible down to
-Mercurial version 4.8).
+Mercurial version 4.9).
 
 New Config::
 
@@ -490,9 +490,14 @@
 
 @eh.uisetup
 def _installalias(ui):
+    odiffalias = b"diff --hidden --rev 'limit(predecessors(.),1)' --rev ."
     if ui.config(b'alias', b'odiff', None) is None:
         ui.setconfig(b'alias', b'odiff',
-                     b"diff --hidden --rev 'limit(predecessors(.),1)' --rev .",
+                     odiffalias,
+                     b'evolve')
+    if ui.config(b'alias', b'obsdiff', None) is None:
+        ui.setconfig(b'alias', b'obsdiff',
+                     odiffalias,
                      b'evolve')
 
 #####################################################################
--- a/hgext3rd/evolve/compat.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/compat.py	Wed Jul 05 15:57:19 2023 -0300
@@ -21,6 +21,7 @@
     node,
     obsolete,
     pycompat,
+    rewriteutil,
     scmutil,
     util,
 )
@@ -559,3 +560,26 @@
 else:
     # hg <= 5.6 (527ce85c2e60)
     StateError = error.Abort
+
+try:
+    retained_extras_on_rebase = rewriteutil.retained_extras_on_rebase
+    preserve_extras_on_rebase = rewriteutil.preserve_extras_on_rebase
+except AttributeError:
+    # hg <= 6.4 (cbcbf63b6dbf)
+    retained_extras_on_rebase = {
+        b'source',
+        b'intermediate-source',
+    }
+
+    def preserve_extras_on_rebase(old_ctx, new_extra):
+        """preserve the relevant `extra` entries from old_ctx on rebase-like operations
+        """
+        old_extra = old_ctx.extra()
+        for key in retained_extras_on_rebase:
+            value = old_extra.get(key)
+            if value is not None:
+                new_extra[key] = value
+
+    # give other extensions an opportunity to collaborate
+    rewriteutil.retained_extras_on_rebase = retained_extras_on_rebase
+    rewriteutil.preserve_extras_on_rebase = preserve_extras_on_rebase
--- a/hgext3rd/evolve/evolvecmd.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/evolvecmd.py	Wed Jul 05 15:57:19 2023 -0300
@@ -959,18 +959,9 @@
     _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate)
     return nodenew
 
-# This is copied from hgext.rebase._savegraft()
-def _savegraft(ctx, extra):
-    s = ctx.extra().get(b'source', None)
-    if s is not None:
-        extra[b'source'] = s
-    s = ctx.extra().get(b'intermediate-source', None)
-    if s is not None:
-        extra[b'intermediate-source'] = s
-
 def _relocatecommit(repo, orig, dest, pctx, keepbranch, commitmsg, update):
     extra = {}
-    _savegraft(orig, extra)
+    compat.preserve_extras_on_rebase(orig, extra)
     extra[b'rebase_source'] = orig.hex()
     targetphase = max(orig.phase(), phases.draft)
     configoverrides = {
@@ -2201,7 +2192,7 @@
     ctx = orig
 
     extra = {}
-    _savegraft(ctx, extra)
+    compat.preserve_extras_on_rebase(orig, extra)
     extra[b'rebase_source'] = orig.hex()
 
     user = ctx.user()
--- a/hgext3rd/evolve/metadata.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/metadata.py	Wed Jul 05 15:57:19 2023 -0300
@@ -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'11.0.3.dev0'
-testedwith = b'4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3 6.4 6.5'
-minimumhgversion = b'4.8'
+__version__ = b'11.1.0.dev0'
+testedwith = b'4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3 6.4 6.5'
+minimumhgversion = b'4.9'
 buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/stablerange.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/stablerange.py	Wed Jul 05 15:57:19 2023 -0300
@@ -417,7 +417,7 @@
 }
 
 @eh.command(
-    b'debugstablerange',
+    b'debug::evo-ext-stable-range',
     [
         (b'r', b'rev', [], b'operate on (rev, 0) ranges for rev in REVS'),
         (b'', b'subranges', False, b'recursively display data for subranges too'),
--- a/hgext3rd/evolve/stablerangecache.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/stablerangecache.py	Wed Jul 05 15:57:19 2023 -0300
@@ -441,7 +441,7 @@
             super(sqlstablerange, self).warmup(repo, upto)
 
 @eh.command(
-    b'debugstablerangecache',
+    b'debug::evo-ext-stable-range-cache',
     [] + commands.formatteropts,
     _(b''))
 def debugstablerangecache(ui, repo, **opts):
--- a/hgext3rd/evolve/stablesort.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/evolve/stablesort.py	Wed Jul 05 15:57:19 2023 -0300
@@ -293,7 +293,7 @@
     return key
 
 @eh.command(
-    b'debugstablesort',
+    b'debug::evo-ext-stable-sort',
     [
         (b'r', b'rev', [], b'heads to start from'),
         (b'', b'method', b'branchpoint', b"method used for sorting, one of: "
@@ -326,7 +326,7 @@
     displayer.close()
 
 @eh.command(
-    b'debugstablesortcache',
+    b'debug::evo-ext-stable-sort-cache',
     [] + commands.formatteropts,
     _(b''))
 def debugstablesortcache(ui, repo, **opts):
--- a/hgext3rd/pullbundle.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/pullbundle.py	Wed Jul 05 15:57:19 2023 -0300
@@ -93,8 +93,8 @@
 from mercurial.i18n import _
 
 __version__ = b'0.2.0.dev'
-testedwith = b'4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1'
-minimumhgversion = b'4.8'
+testedwith = b'4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3'
+minimumhgversion = b'4.9'
 buglink = b'https://bz.mercurial-scm.org/'
 
 cmdtable = {}
--- a/hgext3rd/topic/__init__.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/topic/__init__.py	Wed Jul 05 15:57:19 2023 -0300
@@ -187,6 +187,7 @@
     pycompat,
     registrar,
     scmutil,
+    smartset,
     templatefilters,
     util,
 )
@@ -233,10 +234,10 @@
               b'log.topic': b'green_background',
               }
 
-__version__ = b'1.0.3.dev0'
+__version__ = b'1.1.0.dev0'
 
-testedwith = b'4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3 6.4 6.5'
-minimumhgversion = b'4.8'
+testedwith = b'4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3 6.4 6.5'
+minimumhgversion = b'4.9'
 buglink = b'https://bz.mercurial-scm.org/'
 
 configtable = {}
@@ -269,6 +270,9 @@
 configitem(b'_internal', b'tns-publish',
            default=False,
 )
+configitem(b'devel', b'tns-report-transactions',
+           default=lambda: [],
+)
 configitem(b'experimental', b'topic-mode.server',
            default=configitems.dynamicdefault,
 )
@@ -298,17 +302,17 @@
 
 def _contexttns(self, force=False):
     if not force and not self.mutable():
-        return b'default'
+        return b'none'
     cache = getattr(self._repo, '_tnscache', None)
     # topic loaded, but not enabled (eg: multiple repo in the same process)
     if cache is None:
-        return b'default'
+        return b'none'
     if self.rev() is None:
         # don't cache volatile ctx instances that aren't stored on-disk yet
-        return self.extra().get(b'topic-namespace', b'default')
+        return self.extra().get(b'topic-namespace', b'none')
     tns = cache.get(self.rev())
     if tns is None:
-        tns = self.extra().get(b'topic-namespace', b'default')
+        tns = self.extra().get(b'topic-namespace', b'none')
         self._repo._tnscache[self.rev()] = tns
     return tns
 
@@ -430,6 +434,64 @@
         or (missing and self.deleted())
     )
 
+def find_affected_tns(repo, tr):
+    origrepolen = tr.changes[b'origrepolen']
+    unfi = repo.unfiltered()
+
+    affected = set()
+    # These are the new changesets that weren't in the repo before this
+    # transaction
+    for rev in smartset.spanset(repo, start=origrepolen):
+        ctx = unfi[rev]
+        tns = ctx.topic_namespace()
+        affected.add(tns)
+
+    # These are the changesets obsoleted by this transaction
+    for rev in obsutil.getobsoleted(repo, tr):
+        ctx = unfi[rev]
+        tns = ctx.topic_namespace()
+        affected.add(tns)
+
+    # Phase movements, we only care about:
+    # - publishing changesets (since they lose topic namespace)
+    # - forcefully making changesets draft again
+    # - turning secret changesets draft and making them visible to peers
+    tnsphases = (phases.secret, phases.draft)
+    for revs, (old, new) in tr.changes[b'phases']:
+        if old not in tnsphases and new not in tnsphases:
+            # Skip phase movement if there is no phase (old or new) that has
+            # visible topic namespace (i.e. draft and secret)
+            continue
+        revs = [rev for rev in revs if rev < origrepolen]
+        for rev in revs:
+            ctx = unfi[rev]
+            tns = ctx.topic_namespace(force=True)
+            affected.add(tns)
+
+    # We want to detect any bookmark movement, even within one topic namespace
+    for name, nodes in tr.changes[b'bookmarks'].items():
+        for n in nodes:
+            if n is not None and n in unfi:
+                ctx = unfi[n]
+                tns = ctx.topic_namespace()
+                affected.add(tns)
+
+    # We don't care about changesets without topic namespace
+    affected.discard(b'none')
+
+    tr.changes[b'tns'] = affected
+    report_affected_tns(repo, tr)
+
+def report_affected_tns(repo, tr):
+    report = set(repo.ui.configlist(b'devel', b'tns-report-transactions'))
+    if b'*' not in report:
+        # * matches any transaction
+        if not any(trname in report for trname in tr._names):
+            return
+
+    if tr.changes[b'tns']:
+        repo.ui.status(b'topic namespaces affected: %s\n' % b' '.join(sorted(tr.changes[b'tns'])))
+
 def uisetup(ui):
     destination.modsetup(ui)
     discovery.modsetup(ui)
@@ -568,15 +630,16 @@
         def topic_namespaces(self):
             if self._topic_namespaces is not None:
                 return self._topic_namespaces
-            namespaces = set([self.currenttns])
+            namespaces = set([b'none', self.currenttns])
             for c in self.set(b'not public()'):
                 namespaces.add(c.topic_namespace())
+            namespaces.remove(b'none')
             self._topic_namespaces = namespaces
             return namespaces
 
         @property
         def currenttns(self):
-            return self.vfs.tryread(b'topic-namespace') or b'default'
+            return self.vfs.tryread(b'topic-namespace') or b'none'
 
         @util.propertycache
         def _topiccache(self):
@@ -741,6 +804,25 @@
                 else:
                     tr.addvalidator(b'000-reject-publish', _validate)
 
+            if util.safehasattr(tr, '_validator'):
+                # hg <= 5.3 (36f08ae87ef6)
+                origvalidator = tr._validator
+
+            def _validate(tr2):
+                repo = reporef()
+                find_affected_tns(repo, tr2)
+
+            def validator(tr2):
+                result = origvalidator(tr2)
+                _validate(tr2)
+                return result
+
+            if util.safehasattr(tr, '_validator'):
+                # hg <= 5.3 (36f08ae87ef6)
+                tr._validator = validator
+            else:
+                tr.addvalidator(b'000-find-affected-tns', _validate)
+
             # real transaction start
             ct = self.currenttopic
             if not ct:
@@ -817,7 +899,7 @@
             self._extra[b'topic-namespace'] = repo.currenttns
         else:
             # Default value will be dropped from extra by another hack at the changegroup level
-            self._extra[b'topic-namespace'] = b'default'
+            self._extra[b'topic-namespace'] = b'none'
     if constants.extrakey not in self._extra:
         if getattr(repo, 'currenttopic', b''):
             self._extra[constants.extrakey] = repo.currenttopic
@@ -828,7 +910,7 @@
 def wrapadd(orig, cl, manifest, files, desc, transaction, p1, p2, user,
             date=None, extra=None, p1copies=None, p2copies=None,
             filesadded=None, filesremoved=None):
-    if b'topic-namespace' in extra and extra[b'topic-namespace'] == b'default':
+    if b'topic-namespace' in extra and extra[b'topic-namespace'] == b'none':
         extra = extra.copy()
         del extra[b'topic-namespace']
     if constants.extrakey in extra and not extra[constants.extrakey]:
@@ -1518,14 +1600,14 @@
         if repo.ui.configbool(b'_internal', b'keep-topic'):
             ist0 = True
         if ((not partial and not branchmerge) or isrebase) and not ist0:
-            tns = b'default'
+            tns = b'none'
             t = b''
             pctx = repo[node]
             if pctx.phase() > phases.public:
                 tns = pctx.topic_namespace()
                 t = pctx.topic()
             repo.vfs.write(b'topic-namespace', tns)
-            if tns != b'default' and tns != otns:
+            if tns != b'none' and tns != otns:
                 repo.ui.status(_(b"switching to topic-namespace %s\n") % tns)
             repo.vfs.write(b'topic', t)
             if t and t != ot:
@@ -1651,7 +1733,7 @@
         scmutil.checknewlabel(repo, tns, b'topic namespace')
     ctns = repo.currenttns
     _changecurrenttns(repo, tns)
-    if ctns == b'default' and tns:
+    if ctns == b'none' and tns:
         repo.ui.status(_(b'marked working directory as topic namespace: %s\n')
                        % tns)
 
--- a/hgext3rd/topic/common.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/topic/common.py	Wed Jul 05 15:57:19 2023 -0300
@@ -11,21 +11,21 @@
     """parse branch//namespace/topic string into branch, namespace and topic
 
     >>> parsefqbn(b'branch//topic')
-    ('branch', 'default', 'topic')
+    ('branch', 'none', 'topic')
     >>> parsefqbn(b'//namespace/topic')
     ('default', 'namespace', 'topic')
     >>> parsefqbn(b'branch//')
-    ('branch', 'default', '')
+    ('branch', 'none', '')
     >>> parsefqbn(b'//namespace/')
     ('default', 'namespace', '')
     >>> parsefqbn(b'/topic')
-    ('/topic', 'default', '')
+    ('/topic', 'none', '')
     >>> parsefqbn(b'//topic')
-    ('default', 'default', 'topic')
+    ('default', 'none', 'topic')
     >>> parsefqbn(b'branch//namespace/topic')
     ('branch', 'namespace', 'topic')
     >>> parsefqbn(b'file:///tmp/branch//')
-    ('file:///tmp/branch', 'default', '')
+    ('file:///tmp/branch', 'none', '')
     >>> parsefqbn(b'http://example.com/branch//namespace/topic')
     ('http://example.com/branch', 'namespace', 'topic')
     """
@@ -39,7 +39,7 @@
     tns, sep, topic = other.partition(b'/')
     if not sep:
         # when there's no / in the rest of the string, there can only be topic
-        tns, topic = b'default', tns
+        tns, topic = b'none', tns
     return branch, tns, topic
 
 def formatfqbn(branch=b'', namespace=b'', topic=b'', short=True):
@@ -70,7 +70,7 @@
     """
     result = b''
     showbranch = True # branch and not (short and branch == b'default')
-    shownamespace = namespace and not (short and namespace == b'default')
+    shownamespace = namespace and not (short and namespace == b'none')
     if short and not showbranch and not shownamespace and not topic:
         # if there's nothing to show, show at least branch
         showbranch = True
--- a/hgext3rd/topic/discovery.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/topic/discovery.py	Wed Jul 05 15:57:19 2023 -0300
@@ -248,7 +248,7 @@
     data = {}
     for b in repo.branchmap().iterbranches():
         namedbranch, tns, topic = common.parsefqbn(b[0])
-        if tns != b'default' or topic:
+        if tns != b'none' or topic:
             continue
         oldheads = [repo[n].rev() for n in b[1]]
         newheads = filterfn(repo, oldheads)
--- a/hgext3rd/topic/revset.py	Wed Jul 05 15:51:56 2023 -0300
+++ b/hgext3rd/topic/revset.py	Wed Jul 05 15:57:19 2023 -0300
@@ -25,7 +25,7 @@
 def topicnamespaceset(repo, subset, x):
     """All changesets with the specified topic namespace or the topic
     namespaces of the given changesets. Without the argument, all changesets
-    with any non-default topic namespace.
+    with any non-empty topic namespace.
 
     Pattern matching is supported for `string`. See
     :hg:`help revisions.patterns`.
--- a/tests/test-cache-corruption.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-cache-corruption.t	Wed Jul 05 15:57:19 2023 -0300
@@ -152,7 +152,7 @@
   0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
   0030: 00 00 00 00                                     |....|
 
-  $ hg debugstablesortcache --debug
+  $ hg debug::evo-ext-stable-sort-cache --debug
   number of revisions:            3
   number of merge:                0
   number of jumps:                0
@@ -164,7 +164,7 @@
   0010: 49 c6 55 ea 65 b2 95 e3 00 00 00 0c 00 00 00 00 |I.U.e...........|
   0020: 00 00 00 00 00 00 00 00                         |........|
 
-  $ hg debugstablesortcache --debug
+  $ hg debug::evo-ext-stable-sort-cache --debug
   number of revisions:            3
   stablesortcache file seems to be corrupted, it will be rebuilt from scratch
   number of merge:                0
@@ -177,7 +177,7 @@
   0010: 49 c6 55 ea 65 b2 95 e3 00 00 00 18 00 00 00 00 |I.U.e...........|
   0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
 
-  $ hg debugstablesortcache --debug
+  $ hg debug::evo-ext-stable-sort-cache --debug
   number of revisions:            3
   stablesortcache file seems to be corrupted, it will be rebuilt from scratch
   number of merge:                0
@@ -305,7 +305,7 @@
   0040: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
   0050: 00 00 00 00                                     |....|
 
-  $ hg debugstablesortcache --debug
+  $ hg debug::evo-ext-stable-sort-cache --debug
   number of revisions:            4
   number of merge:                1
   number of jumps:                1
@@ -324,7 +324,7 @@
   0020: 00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 |................|
   0030: 01 00 00 00 02 00 00 00                         |........|
 
-  $ hg debugstablesortcache --debug
+  $ hg debug::evo-ext-stable-sort-cache --debug
   number of revisions:            4
   stablesortcache file seems to be corrupted, it will be rebuilt from scratch
   number of merge:                1
@@ -345,7 +345,7 @@
   0030: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
   0040: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
 
-  $ hg debugstablesortcache --debug
+  $ hg debug::evo-ext-stable-sort-cache --debug
   number of revisions:            4
   stablesortcache file seems to be corrupted, it will be rebuilt from scratch
   number of merge:                1
--- a/tests/test-check-sdist.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-check-sdist.t	Wed Jul 05 15:57:19 2023 -0300
@@ -34,8 +34,12 @@
   hg-evolve-*.tar.gz (glob)
 
   $ tar -tzf hg-evolve-*.tar.gz | sed 's|^hg-evolve-[^/]*/||' | sort > files
-  $ wc -l files
-  361 files
+  $ egrep '^tests/test-.*\.(t|py)$' files > test-files
+  $ egrep -v '^tests/test-.*\.(t|py)$' files > other-files
+  $ wc -l other-files
+  150 other-files
+  $ wc -l test-files
+  ??? test-files (glob)
   $ fgrep debian files
   tests/test-check-debian.t
   $ fgrep __init__.py files
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-extras.t	Wed Jul 05 15:57:19 2023 -0300
@@ -0,0 +1,42 @@
+Testing retained_extras_on_rebase usage in evolve and modifying it in an extension
+
+  $ . $TESTDIR/testlib/common.sh
+
+  $ hg init repo
+  $ cd repo
+  $ cat > .hg/hgrc << EOF
+  > [extensions]
+  > evolve =
+  > EOF
+
+  $ echo apple > a
+  $ hg ci -qAm 'apple'
+  $ echo banana > b
+  $ hg ci -qAm 'banana' --config extensions.commitextras= \
+  > --extra useful=b-for-banana \
+  > --extra useless=banana-peel
+
+amending apple
+
+  $ hg prev
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  [0] apple
+  $ echo apricot > a
+  $ hg amend -m 'apricot'
+  1 new orphan changesets
+
+the commit still has all extras that we added previously
+
+  $ hg log -r 'desc("banana")' -T '{join(extras, " ")}\n'
+  *useful=b-for-banana*useless=banana-peel* (glob)
+
+let's run evolve with our extension
+
+  $ hg --config extensions.retained_extras=${TESTDIR}/testlib/retain-extras-ext.py evolve
+  move:[1] banana
+  atop:[2] apricot
+
+evolving banana retained "useful" and discarded "useless"
+
+  $ hg log -r 'desc("banana")' -T '{join(extras, " ")}\n'
+  *useful=b-for-banana* (glob)
--- a/tests/test-evolve-issue6246.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-evolve-issue6246.t	Wed Jul 05 15:57:19 2023 -0300
@@ -17,7 +17,7 @@
   $ touch .hg/cache/evoext_stablerange_v2.sqlite
   $ chmod 0000 .hg/cache/evoext_stablerange_v2.sqlite
 
-  $ hg debugstablerange --method default --verify --subranges --rev 1 --debug
+  $ hg debug::evo-ext-stable-range --method default --verify --subranges --rev 1 --debug
   stable-range cache: unable to load, regenerating
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-namespaces-report.t	Wed Jul 05 15:57:19 2023 -0300
@@ -0,0 +1,275 @@
+============================================================
+Test detection of topic name space affected by a transaction
+============================================================
+
+Reporting affected topic namespaces in transactions
+
+  $ . "$TESTDIR/testlib/common.sh"
+
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > evolve =
+  > topic =
+  > [phases]
+  > publish = no
+  > [devel]
+  > tns-report-transactions = push
+  > EOF
+
+  $ hg init orig
+
+case 1: new changeset (draft with topic namespace)
+==================================================
+
+topic namespace of that changeset is reported
+
+  $ hg clone orig case-1 -q
+  $ cd orig
+
+  $ echo apple > a
+  $ hg ci -qAm apple
+
+  $ hg push ../case-1
+  pushing to ../case-1
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+  $ echo banana > b
+  $ hg debug-topic-namespace bob
+  marked working directory as topic namespace: bob
+  $ hg ci -qAm 'banana'
+
+XXX: should not require --new-branch
+
+  $ hg push ../case-1 --new-branch
+  pushing to ../case-1
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: bob
+  added 1 changesets with 1 changes to 1 files
+
+  $ cd ..
+
+case 2: obsmarker affecting known changeset
+===========================================
+
+topic namespaces of both the precursor and the successor are affected
+
+  $ hg clone orig case-2 -q
+  $ cd orig
+
+  $ echo broccoli > b
+  $ hg debug-topic-namespace bruce
+  $ hg ci --amend -m 'broccoli'
+
+XXX: should not require --new-branch
+
+  $ hg push ../case-2 --new-branch
+  pushing to ../case-2
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: bob bruce
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  1 new obsolescence markers
+  obsoleted 1 changesets
+
+  $ cd ..
+
+case 3: phase divergence
+========================
+
+3 phase divergence resolution can point to a thing but not affect it (probably not affected)
+
+In this case, the pushed changeset comes with an obsmarker whose predecessors
+has the `charlie` topic-namespace and the successors has the `carol`
+topic-namespace. However, that obsolescence is part of a phase-divergence
+fixup, so we should now mark `coconut` as affected since it is already public.
+
+  $ hg clone orig case-3 -q
+  $ cd orig
+
+  $ hg debug-topic-namespace charlie
+  $ echo coconut > c
+  $ hg ci -qAm 'coconut'
+
+  $ hg debug-topic-namespace carol
+  $ echo cloudberry > c
+  $ hg ci --amend -m 'cloudberry'
+
+  $ hg phase --hidden -r 'desc("coconut")' --public
+  1 new phase-divergent changesets
+
+  $ hg evolve --phase-divergent
+  recreate:[4] cloudberry
+  atop:[3] coconut
+  committed as c398b3caf447
+  working directory is now at c398b3caf447
+
+XXX: should not require --new-branch
+
+  $ hg push ../case-3 --new-branch
+  pushing to ../case-3
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: bruce carol
+  added 2 changesets with 2 changes to 1 files
+  2 new obsolescence markers
+
+  $ cd ..
+
+case 4: phase movement: publishing drafts
+=========================================
+
+topic namespaces of published changesets are affected
+
+  $ hg clone orig case-4 -q
+  $ cd orig
+
+  $ hg push ../case-4 --publish
+  pushing to ../case-4
+  searching for changes
+  no changes found
+  topic namespaces affected: carol
+  [1]
+
+  $ cd ..
+
+case 5: bookmark movement
+=========================
+
+Bookmark movement that affect tns (like putting a bookmark on obsolete
+changesets) their topic namespaces reappear and are therefore reported
+
+  $ hg clone orig case-5 -q
+  $ cd orig
+
+  $ hg debug-topic-namespace dana
+  $ echo durian > d
+  $ hg ci -qAm 'durian'
+
+XXX: should not require --new-branch
+
+  $ hg push ../case-5 --new-branch
+  pushing to ../case-5
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: dana
+  added 1 changesets with 1 changes to 1 files
+
+  $ hg debug-topic-namespace dave
+  $ echo dragonfruit > d
+  $ hg ci --amend -m 'dragonfruit'
+
+XXX: should not require --new-branch
+
+  $ hg push ../case-5 --new-branch
+  pushing to ../case-5
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: dana dave
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  1 new obsolescence markers
+  obsoleted 1 changesets
+
+  $ hg bookmark --hidden -r 'desc("durian")' @
+  bookmarking hidden changeset ac97707e6b4a
+  (hidden revision 'ac97707e6b4a' was rewritten as: 822ac0064503)
+
+  $ hg push ../case-5 -B @
+  pushing to ../case-5
+  searching for changes
+  no changes found
+  topic namespaces affected: dana
+  exporting bookmark @
+  [1]
+
+  $ cd ..
+
+case 6: phase movement: publishing secret changesets
+====================================================
+
+(that are known on the server)
+
+topic namespaces of published changesets are affected
+
+  $ hg clone orig case-6 -q
+  $ cd orig
+
+  $ hg push ../case-6 -r . --publish
+  pushing to ../case-6
+  searching for changes
+  no changes found
+  topic namespaces affected: dave
+  [1]
+
+previous topic namespace is resurrected...
+
+  $ hg phase --secret --force -r . --config 'devel.tns-report-transactions=phase'
+  topic namespaces affected: dave
+
+...just to disappear again
+
+  $ hg push ../case-6 -r . --config 'devel.tns-report-transactions=*'
+  pushing to ../case-6
+  searching for changes
+  no changes found
+  topic namespaces affected: dave
+  [1]
+
+  $ cd ..
+
+case 7: phase movement: secret->draft on the server
+===================================================
+
+changeset becomes visible to peers, so its topic namespace is affected
+
+  $ hg clone orig case-7 -q
+  $ cd orig
+
+  $ hg phase --draft --force -r tip
+  $ hg phase --secret --force -r tip -R ../case-7
+
+XXX: should not require --new-branch
+
+  $ hg push ../case-7 -r . --config 'devel.tns-report-transactions=*' --new-branch
+  pushing to ../case-7
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: dave
+  added 0 changesets with 0 changes to 1 files
+
+  $ cd ..
+
+case: 99 pushing obsmarker for an unknown changeset
+===================================================
+doesn't affect any topic namespace, we report nothing
+
+  $ hg clone orig case-99 -q
+  $ cd orig
+
+  $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa `getid "desc('dragonfruit')"`
+  1 new obsolescence markers
+
+  $ hg push ../case-99
+  pushing to ../case-99
+  searching for changes
+  no changes found
+  1 new obsolescence markers
+  [1]
+
+  $ cd ..
--- a/tests/test-namespaces.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-namespaces.t	Wed Jul 05 15:57:19 2023 -0300
@@ -20,7 +20,8 @@
 
   $ hg debug-topic-namespace --clear
   $ hg debug-topic-namespaces
-  default
+
+  $ hg topics
 
   $ hg debugtopicnamespace --clear nonsense
   abort: cannot use --clear when setting a topic namespace
@@ -53,7 +54,7 @@
   $ hg up null
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg debug-topic-namespace
-  default
+  none
   $ hg topics
      feature (1 changesets)
   $ hg up 0
@@ -104,7 +105,7 @@
 no double slashes means it's a named branch
   $ hg debug-parse-fqbn foo/bar
   branch:    foo/bar
-  namespace: default
+  namespace: none
   topic:     
 
 Formatting
@@ -120,9 +121,9 @@
 
 default values
 
-  $ hg debug-format-fqbn -b default -n default -t '' --no-short
-  default//default/
-  $ hg debug-format-fqbn -b default -n default -t '' --short
+  $ hg debug-format-fqbn -b default -n none -t '' --no-short
+  default//none/
+  $ hg debug-format-fqbn -b default -n none -t '' --short
   default
 
   $ hg debug-format-fqbn -b default -n namespace -t '' --no-short
@@ -130,9 +131,9 @@
   $ hg debug-format-fqbn -b default -n namespace -t '' --short
   default//namespace/
 
-  $ hg debug-format-fqbn -b default -n default -t topic --no-short
-  default//default/topic
-  $ hg debug-format-fqbn -b default -n default -t topic --short
+  $ hg debug-format-fqbn -b default -n none -t topic --no-short
+  default//none/topic
+  $ hg debug-format-fqbn -b default -n none -t topic --short
   default//topic
 
   $ cd ..
--- a/tests/test-stablerange-branchpoint.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-stablerange-branchpoint.t	Wed Jul 05 15:57:19 2023 -0300
@@ -15,14 +15,14 @@
 #if basic-branchpoint
   $ cat << EOF >> $HGRCPATH
   > [defaults]
-  > debugstablerange = --method basic-branchpoint
+  > debug::evo-ext-stable-range = --method basic-branchpoint
   > EOF
 #endif
 
 #if branchpoint
   $ cat << EOF >> $HGRCPATH
   > [defaults]
-  > debugstablerange = --method branchpoint
+  > debug::evo-ext-stable-range = --method branchpoint
   > EOF
 #endif
 
@@ -40,14 +40,14 @@
   bebd167eb94d 5
   c8d03c1b5e94 6
   f69452c5b1af 7
-  $ hg debugstablerange --verify --verbose --subranges --rev 1 | tee 1.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 1 | tee 1.range
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   66f7d451a68b-1 (1, 2, 1) [leaf] - 
 
 bigger subset reuse most of the previous one
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 4 | tee 4.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 4 | tee 4.range
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
@@ -75,7 +75,7 @@
 Using a range not ending on 2**N boundary
 we fall back on 2**N as much as possible
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 5 | tee 5.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 5 | tee 5.range
   c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
@@ -107,7 +107,7 @@
 
 Even two unperfect range overlap a lot
 
-  $ hg debugstablerange --verify --verbose --subranges --rev tip | tee tip.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev tip | tee tip.range
   f69452c5b1af-0 (6, 7, 7) [complete] - 2dc09a01254d-0 (3, 4, 4), f69452c5b1af-4 (6, 7, 3)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   f69452c5b1af-4 (6, 7, 3) [complete] - c8d03c1b5e94-4 (5, 6, 2), f69452c5b1af-6 (6, 7, 1)
@@ -192,11 +192,11 @@
 
 (left branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' | tee left-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'left~2' | tee left-2.range
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   66f7d451a68b-1 (1, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev left | tee left.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev left | tee left.range
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
@@ -219,11 +219,11 @@
 
 (right branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev right~2 | tee right-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right~2 | tee right-2.range
   e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   e7bd5218ca15-1 (4, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev right | tee right.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right | tee right.range
   a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2)
   a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1)
   e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
@@ -246,7 +246,7 @@
 
 The merge reuse as much of the slicing created for one of the branch
 
-  $ hg debugstablerange --verify --verbose --subranges --rev merge | tee merge.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev merge | tee merge.range
   5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2)
@@ -372,13 +372,13 @@
 
 (left branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' | tee left-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'left~2' | tee left-2.range
   01241442b3c2-0 (2, 3, 3) [complete] - 66f7d451a68b-0 (1, 2, 2), 01241442b3c2-2 (2, 3, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   01241442b3c2-2 (2, 3, 1) [leaf] - 
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   66f7d451a68b-1 (1, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev left | tee left.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev left | tee left.range
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
@@ -406,7 +406,7 @@
 
 (right branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev right~2 | tee right-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right~2 | tee right-2.range
   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
   de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
@@ -414,7 +414,7 @@
   42b07e8da27d-3 (7, 4, 1) [leaf] - 
   b9bc20507e0b-2 (6, 3, 1) [leaf] - 
   de561312eff4-1 (5, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev right | tee right.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right | tee right.range
   f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
@@ -449,7 +449,7 @@
 
 We are still able to reuse one of the branch however
 
-  $ hg debugstablerange --verify --verbose --subranges --rev merge | tee merge.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev merge | tee merge.range
   8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), 8aca7f8c9bd2-8 (10, 11, 3)
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
@@ -526,7 +526,7 @@
 
 Range above the merge, reuse subrange from the merge
 
-  $ hg debugstablerange --verify --verbose --subranges --rev tip | tee tip.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev tip | tee tip.range
   e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-1 (7, 4, 3), e6b8d5b46647-8 (12, 13, 5)
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1)
@@ -653,7 +653,7 @@
   b4594d867745 6
   43227190fef8 5
   1d8d22637c2d 8
-  $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'head()'
   1d8d22637c2d-0 (15, 8, 8) [complete] - 2b6d669947cd-0 (3, 4, 4), 1d8d22637c2d-4 (15, 8, 4)
   dcbb326fdec2-0 (9, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), dcbb326fdec2-4 (9, 7, 3)
   ff43616e5d0f-0 (10, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), ff43616e5d0f-4 (10, 7, 3)
@@ -718,7 +718,7 @@
   fa942426a6fd 2
   36315563e2fa 3
   f37e476fba9a 5
-  $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'head()'
   f37e476fba9a-0 (4, 5, 5) [complete] - 66f7d451a68b-0 (1, 2, 2), 36315563e2fa-1 (3, 3, 2), f37e476fba9a-4 (4, 5, 1)
   36315563e2fa-1 (3, 3, 2) [complete] - fa942426a6fd-1 (2, 2, 1), 36315563e2fa-2 (3, 3, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
--- a/tests/test-stablerange.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-stablerange.t	Wed Jul 05 15:57:19 2023 -0300
@@ -15,21 +15,21 @@
 #if default
   $ cat << EOF >> $HGRCPATH
   > [defaults]
-  > debugstablerange = --method default
+  > debug::evo-ext-stable-range = --method default
   > EOF
 #endif
 
 #if basic-mergepoint
   $ cat << EOF >> $HGRCPATH
   > [defaults]
-  > debugstablerange = --method basic-mergepoint
+  > debug::evo-ext-stable-range = --method basic-mergepoint
   > EOF
 #endif
 
 #if mergepoint
   $ cat << EOF >> $HGRCPATH
   > [defaults]
-  > debugstablerange = --method mergepoint
+  > debug::evo-ext-stable-range = --method mergepoint
   > EOF
 #endif
 
@@ -47,14 +47,14 @@
   bebd167eb94d 5
   c8d03c1b5e94 6
   f69452c5b1af 7
-  $ hg debugstablerange --verify --verbose --subranges --rev 1 | tee 1.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 1 | tee 1.range
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   66f7d451a68b-1 (1, 2, 1) [leaf] - 
 
 bigger subset reuse most of the previous one
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 4 | tee 4.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 4 | tee 4.range
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
@@ -82,7 +82,7 @@
 Using a range not ending on 2**N boundary
 we fall back on 2**N as much as possible
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 5 | tee 5.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 5 | tee 5.range
   c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
@@ -114,7 +114,7 @@
 
 Even two unperfect range overlap a lot
 
-  $ hg debugstablerange --verify --verbose --subranges --rev tip | tee tip.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev tip | tee tip.range
   f69452c5b1af-0 (6, 7, 7) [complete] - 2dc09a01254d-0 (3, 4, 4), f69452c5b1af-4 (6, 7, 3)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   f69452c5b1af-4 (6, 7, 3) [complete] - c8d03c1b5e94-4 (5, 6, 2), f69452c5b1af-6 (6, 7, 1)
@@ -199,11 +199,11 @@
 
 (left branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' | tee left-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'left~2' | tee left-2.range
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   66f7d451a68b-1 (1, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev left | tee left.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev left | tee left.range
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
@@ -226,11 +226,11 @@
 
 (right branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev right~2 | tee right-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right~2 | tee right-2.range
   e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   e7bd5218ca15-1 (4, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev right | tee right.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right | tee right.range
   a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2)
   a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1)
   e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1)
@@ -253,7 +253,7 @@
 
 The merge reuse as much of the slicing created for one of the branch
 
-  $ hg debugstablerange --verify --verbose --subranges --rev merge | tee merge.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev merge | tee merge.range
   5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2)
@@ -379,13 +379,13 @@
 
 (left branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' | tee left-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'left~2' | tee left-2.range
   01241442b3c2-0 (2, 3, 3) [complete] - 66f7d451a68b-0 (1, 2, 2), 01241442b3c2-2 (2, 3, 1)
   66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1)
   01241442b3c2-2 (2, 3, 1) [leaf] - 
   1ea73414a91b-0 (0, 1, 1) [leaf] - 
   66f7d451a68b-1 (1, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev left | tee left.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev left | tee left.range
   bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1)
   2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2)
   2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1)
@@ -413,7 +413,7 @@
 
 (right branch)
 
-  $ hg debugstablerange --verify --verbose --subranges --rev right~2 | tee right-2.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right~2 | tee right-2.range
   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
   de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1)
@@ -421,7 +421,7 @@
   42b07e8da27d-3 (7, 4, 1) [leaf] - 
   b9bc20507e0b-2 (6, 3, 1) [leaf] - 
   de561312eff4-1 (5, 2, 1) [leaf] - 
-  $ hg debugstablerange --verify --verbose --subranges --rev right | tee right.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev right | tee right.range
   f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
   42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1)
@@ -456,7 +456,7 @@
 
 We are still able to reuse one of the branch however
 
-  $ hg debugstablerange --verify --verbose --subranges --rev merge | tee merge.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev merge | tee merge.range
   8aca7f8c9bd2-0 (10, 11, 11) [complete] - f4b7da68b467-0 (9, 6, 6), 01241442b3c2-1 (2, 3, 2), 8aca7f8c9bd2-8 (10, 11, 3)
   f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
   42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2)
@@ -534,7 +534,7 @@
 
 Range above the merge, reuse subrange from the merge
 
-  $ hg debugstablerange --verify --verbose --subranges --rev tip | tee tip.range
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev tip | tee tip.range
   e6b8d5b46647-0 (12, 13, 13) [complete] - f4b7da68b467-0 (9, 6, 6), 01241442b3c2-1 (2, 3, 2), e6b8d5b46647-8 (12, 13, 5)
   f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2)
   e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1)
@@ -661,7 +661,7 @@
   b4594d867745 6
   43227190fef8 5
   1d8d22637c2d 8
-  $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'head()'
   1d8d22637c2d-0 (15, 8, 8) [complete] - 2b6d669947cd-0 (3, 4, 4), 1d8d22637c2d-4 (15, 8, 4)
   dcbb326fdec2-0 (9, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), dcbb326fdec2-4 (9, 7, 3)
   ff43616e5d0f-0 (10, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), ff43616e5d0f-4 (10, 7, 3)
@@ -726,7 +726,7 @@
   fa942426a6fd 2
   36315563e2fa 3
   f37e476fba9a 5
-  $ hg debugstablerange --verify --verbose --subranges --rev 'head()'
+  $ hg debug::evo-ext-stable-range --verify --verbose --subranges --rev 'head()'
   f37e476fba9a-0 (4, 5, 5) [complete] - 36315563e2fa-0 (3, 3, 3), 66f7d451a68b-1 (1, 2, 1), f37e476fba9a-4 (4, 5, 1)
   36315563e2fa-0 (3, 3, 3) [complete] - fa942426a6fd-0 (2, 2, 2), 36315563e2fa-2 (3, 3, 1)
   fa942426a6fd-0 (2, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), fa942426a6fd-1 (2, 2, 1)
--- a/tests/test-stablesort-branchpoint-criss-cross.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-stablesort-branchpoint-criss-cross.t	Wed Jul 05 15:57:19 2023 -0300
@@ -9,7 +9,7 @@
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
   > [alias]
-  > showsort = debugstablesort --template="{node|short}\n" --method branchpoint
+  > showsort = debug::evo-ext-stable-sort --template="{node|short}\n" --method branchpoint
   > EOF
 
   $ checktopo () {
--- a/tests/test-stablesort-branchpoint.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-stablesort-branchpoint.t	Wed Jul 05 15:57:19 2023 -0300
@@ -9,7 +9,7 @@
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
   > [alias]
-  > showsort = debugstablesort --template="{node|short}\n" --method branchpoint
+  > showsort = debug::evo-ext-stable-sort --template="{node|short}\n" --method branchpoint
   > EOF
 
   $ checktopo () {
--- a/tests/test-stablesort-criss-cross.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-stablesort-criss-cross.t	Wed Jul 05 15:57:19 2023 -0300
@@ -9,7 +9,7 @@
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
   > [alias]
-  > showsort = debugstablesort --template="{node|short}\n" --method headondisk
+  > showsort = debug::evo-ext-stable-sort --template="{node|short}\n" --method headondisk
   > EOF
 
   $ checktopo () {
--- a/tests/test-stablesort.t	Wed Jul 05 15:51:56 2023 -0300
+++ b/tests/test-stablesort.t	Wed Jul 05 15:57:19 2023 -0300
@@ -9,8 +9,8 @@
   > [ui]
   > logtemplate = "{rev} {node|short} {desc} {tags}\n"
   > [alias]
-  > showsort = debugstablesort --template="{node|short}\n" --method basic-mergepoint
-  > showsorthead = debugstablesort --template="{node|short}\n" --method headondisk
+  > showsort = debug::evo-ext-stable-sort --template="{node|short}\n" --method basic-mergepoint
+  > showsorthead = debug::evo-ext-stable-sort --template="{node|short}\n" --method headondisk
   > EOF
 
   $ checktopo () {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testlib/retain-extras-ext.py	Wed Jul 05 15:57:19 2023 -0300
@@ -0,0 +1,16 @@
+"""
+Wrap 'retained_extras_on_rebase' (from either mercurial or evolve) to retain
+the "useful" extra.
+"""
+
+from mercurial import rewriteutil
+
+try:
+    rewriteutil.retained_extras_on_rebase
+except AttributeError:
+    # install the compatibility layer on older version
+    from hgext3rd.evolve import compat
+    compat.retained_extras_on_rebase # silence linter
+
+def extsetup(ui):
+    rewriteutil.retained_extras_on_rebase.add(b'useful')