changeset 50463:771294224bf6

branching: merge stable into default
author Raphaël Gomès <rgomes@octobus.net>
date Fri, 14 Apr 2023 10:41:40 +0200
parents 59fda8516774 (diff) ae9a15267315 (current diff)
children 5ae30ff79c76
files hgext/rebase.py mercurial/commands.py mercurial/configitems.py
diffstat 73 files changed, 2282 insertions(+), 720 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitattributes	Fri Apr 14 10:41:40 2023 +0200
@@ -0,0 +1,2 @@
+# So GitLab doesn't think we're using tons of Perl
+*.t -linguist-detectable
--- a/contrib/import-checker.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/contrib/import-checker.py	Fri Apr 14 10:41:40 2023 +0200
@@ -44,6 +44,7 @@
     # third-party imports should be directly imported
     'mercurial.thirdparty',
     'mercurial.thirdparty.attr',
+    'mercurial.thirdparty.jaraco.collections',
     'mercurial.thirdparty.zope',
     'mercurial.thirdparty.zope.interface',
     'typing',
--- a/hgext/fastexport.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/hgext/fastexport.py	Fri Apr 14 10:41:40 2023 +0200
@@ -69,10 +69,10 @@
     return b"refs/heads/" + branch
 
 
-def write_data(buf, data, skip_newline):
+def write_data(buf, data, add_newline=False):
     buf.append(b"data %d\n" % len(data))
     buf.append(data)
-    if not skip_newline or data[-1:] != b"\n":
+    if add_newline or data[-1:] != b"\n":
         buf.append(b"\n")
 
 
@@ -103,7 +103,7 @@
             marks[filerev] = mark
             data = filectx.data()
             buf = [b"blob\n", b"mark :%d\n" % mark]
-            write_data(buf, data, False)
+            write_data(buf, data, True)
             ui.write(*buf, keepprogressbar=True)
             del buf
 
@@ -122,7 +122,7 @@
             convert_to_git_date(ctx.date()),
         ),
     ]
-    write_data(buf, ctx.description(), True)
+    write_data(buf, ctx.description())
     if parents:
         buf.append(b"from :%d\n" % marks[parents[0].hex()])
     if len(parents) == 2:
--- a/hgext/infinitepush/__init__.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/hgext/infinitepush/__init__.py	Fri Apr 14 10:41:40 2023 +0200
@@ -330,6 +330,11 @@
         clientextsetup(ui)
 
 
+def uipopulate(ui):
+    if not ui.hasconfig(b"experimental", b"changegroup3"):
+        ui.setconfig(b"experimental", b"changegroup3", False, b"infinitepush")
+
+
 def commonsetup(ui):
     wireprotov1server.commands[b'listkeyspatterns'] = (
         wireprotolistkeyspatterns,
--- a/hgext/rebase.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/hgext/rebase.py	Fri Apr 14 10:41:40 2023 +0200
@@ -24,6 +24,7 @@
     wdirrev,
 )
 from mercurial.pycompat import open
+from mercurial.thirdparty.jaraco.collections import Projection
 from mercurial import (
     bookmarks,
     cmdutil,
@@ -52,6 +53,7 @@
     util,
 )
 
+
 # The following constants are used throughout the rebase module. The ordering of
 # their values must be maintained.
 
@@ -84,13 +86,17 @@
     return 1
 
 
-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 retained_extras():
+    """
+    Yield the names of the extras to be retained.
+    """
+    # graft
+    yield b'source'
+    yield b'intermediate-source'
+
+
+def _save_extras(ctx, extra):
+    extra.update(Projection(retained_extras(), ctx.extra()))
 
 
 def _savebranch(ctx, extra):
@@ -193,7 +199,7 @@
         self.date = opts.get('date', None)
 
         e = opts.get('extrafn')  # internal, used by e.g. hgsubversion
-        self.extrafns = [_savegraft]
+        self.extrafns = [_save_extras]
         if e:
             self.extrafns = [e]
 
--- a/mercurial/bundle2.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/bundle2.py	Fri Apr 14 10:41:40 2023 +0200
@@ -1703,6 +1703,7 @@
     vfs=None,
     compression=None,
     compopts=None,
+    allow_internal=False,
 ):
     if bundletype.startswith(b'HG10'):
         cg = changegroup.makechangegroup(repo, outgoing, b'01', source)
@@ -1718,6 +1719,14 @@
     elif not bundletype.startswith(b'HG20'):
         raise error.ProgrammingError(b'unknown bundle type: %s' % bundletype)
 
+    # enforce that no internal phase are to be bundled
+    bundled_internal = repo.revs(b"%ln and _internal()", outgoing.ancestorsof)
+    if bundled_internal and not allow_internal:
+        count = len(repo.revs(b'%ln and _internal()', outgoing.missing))
+        msg = "backup bundle would contains %d internal changesets"
+        msg %= count
+        raise error.ProgrammingError(msg)
+
     caps = {}
     if opts.get(b'obsolescence', False):
         caps[b'obsmarkers'] = (b'V1',)
@@ -1750,12 +1759,16 @@
             part.addparam(
                 b'nbchanges', b'%d' % cg.extras[b'clcount'], mandatory=False
             )
-        if opts.get(b'phases') and repo.revs(
-            b'%ln and secret()', outgoing.ancestorsof
-        ):
-            part.addparam(
-                b'targetphase', b'%d' % phases.secret, mandatory=False
-            )
+        if opts.get(b'phases'):
+            target_phase = phases.draft
+            for head in outgoing.ancestorsof:
+                target_phase = max(target_phase, repo[head].phase())
+            if target_phase > phases.draft:
+                part.addparam(
+                    b'targetphase',
+                    b'%d' % target_phase,
+                    mandatory=False,
+                )
     if repository.REPO_FEATURE_SIDE_DATA in repo.features:
         part.addparam(b'exp-sidedata', b'1')
 
--- a/mercurial/bundlecaches.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/bundlecaches.py	Fri Apr 14 10:41:40 2023 +0200
@@ -60,6 +60,7 @@
 _bundlespeccgversions = {
     b'v1': b'01',
     b'v2': b'02',
+    b'v3': b'03',
     b'packed1': b's1',
     b'bundle2': b'02',  # legacy
 }
@@ -82,6 +83,14 @@
         b'tagsfnodescache': True,
         b'revbranchcache': True,
     },
+    b'v3': {
+        b'changegroup': True,
+        b'cg.version': b'03',
+        b'obsolescence': False,
+        b'phases': True,
+        b'tagsfnodescache': True,
+        b'revbranchcache': True,
+    },
     b'streamv2': {
         b'changegroup': False,
         b'cg.version': b'02',
--- a/mercurial/cmdutil.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/cmdutil.py	Fri Apr 14 10:41:40 2023 +0200
@@ -2754,7 +2754,6 @@
 
 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
     err = 1
-    opts = pycompat.byteskwargs(opts)
 
     def write(path):
         filename = None
@@ -2768,7 +2767,7 @@
             except OSError:
                 pass
         with formatter.maybereopen(basefm, filename) as fm:
-            _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
+            _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
 
     # Automation often uses hg cat on single files, so special case it
     # for performance to avoid the cost of parsing the manifest.
@@ -2803,7 +2802,7 @@
                 basefm,
                 fntemplate,
                 subprefix,
-                **pycompat.strkwargs(opts),
+                **opts,
             ):
                 err = 0
         except error.RepoLookupError:
--- a/mercurial/commands.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/commands.py	Fri Apr 14 10:41:40 2023 +0200
@@ -1665,6 +1665,14 @@
         scmutil.nochangesfound(ui, repo, not base and excluded)
         return 1
 
+    # internal changeset are internal implementation details that should not
+    # leave the repository. Bundling with `hg bundle` create such risk.
+    bundled_internal = repo.revs(b"%ln and _internal()", missing)
+    if bundled_internal:
+        msg = _(b"cannot bundle internal changesets")
+        hint = _(b"%d internal changesets selected") % len(bundled_internal)
+        raise error.Abort(msg, hint=hint)
+
     if heads:
         outgoing = discovery.outgoing(
             repo, missingroots=missing, ancestorsof=heads
@@ -1714,8 +1722,9 @@
     bundlespec.set_param(
         b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
     )
-    phases_cfg = cfg(b'experimental', b'bundle-phases')
-    bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
+    if not bundlespec.params.get(b'phases', False):
+        phases_cfg = cfg(b'experimental', b'bundle-phases')
+        bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
 
     bundle2.writenewbundle(
         ui,
@@ -3529,22 +3538,20 @@
 
     """
     cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
-    opts = pycompat.byteskwargs(opts)
-    diff = opts.get(b'all') or opts.get(b'diff')
-    follow = opts.get(b'follow')
-    if opts.get(b'all_files') is None and not diff:
-        opts[b'all_files'] = True
+
+    diff = opts.get('all') or opts.get('diff')
+    follow = opts.get('follow')
+    if opts.get('all_files') is None and not diff:
+        opts['all_files'] = True
     plaingrep = (
-        opts.get(b'all_files')
-        and not opts.get(b'rev')
-        and not opts.get(b'follow')
+        opts.get('all_files') and not opts.get('rev') and not opts.get('follow')
     )
-    all_files = opts.get(b'all_files')
+    all_files = opts.get('all_files')
     if plaingrep:
-        opts[b'rev'] = [b'wdir()']
+        opts['rev'] = [b'wdir()']
 
     reflags = re.M
-    if opts.get(b'ignore_case'):
+    if opts.get('ignore_case'):
         reflags |= re.I
     try:
         regexp = util.re.compile(pattern, reflags)
@@ -3555,7 +3562,7 @@
         )
         return 1
     sep, eol = b':', b'\n'
-    if opts.get(b'print0'):
+    if opts.get('print0'):
         sep = eol = b'\0'
 
     searcher = grepmod.grepsearcher(
@@ -3603,7 +3610,7 @@
                     b'linenumber',
                     b'%d',
                     l.linenum,
-                    opts.get(b'line_number'),
+                    opts.get('line_number'),
                     b'',
                 ),
             ]
@@ -3625,14 +3632,14 @@
                         b'user',
                         b'%s',
                         formatuser(ctx.user()),
-                        opts.get(b'user'),
+                        opts.get('user'),
                         b'',
                     ),
                     (
                         b'date',
                         b'%s',
                         fm.formatdate(ctx.date(), datefmt),
-                        opts.get(b'date'),
+                        opts.get('date'),
                         b'',
                     ),
                 ]
@@ -3643,15 +3650,15 @@
                 field = fieldnamemap.get(name, name)
                 label = extra_label + (b'grep.%s' % name)
                 fm.condwrite(cond, field, fmt, data, label=label)
-            if not opts.get(b'files_with_matches'):
+            if not opts.get('files_with_matches'):
                 fm.plain(sep, label=b'grep.sep')
-                if not opts.get(b'text') and binary():
+                if not opts.get('text') and binary():
                     fm.plain(_(b" Binary file matches"))
                 else:
                     displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
             fm.plain(eol)
             found = True
-            if opts.get(b'files_with_matches'):
+            if opts.get('files_with_matches'):
                 break
         return found
 
@@ -3677,9 +3684,9 @@
     wopts = logcmdutil.walkopts(
         pats=pats,
         opts=opts,
-        revspec=opts[b'rev'],
-        include_pats=opts[b'include'],
-        exclude_pats=opts[b'exclude'],
+        revspec=opts['rev'],
+        include_pats=opts['include'],
+        exclude_pats=opts['exclude'],
         follow=follow,
         force_changelog_traversal=all_files,
         filter_revisions_by_pats=not all_files,
@@ -3687,7 +3694,7 @@
     revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
 
     ui.pager(b'grep')
-    fm = ui.formatter(b'grep', opts)
+    fm = ui.formatter(b'grep', pycompat.byteskwargs(opts))
     for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
         r = display(fm, fn, ctx, pstates, states)
         found = found or r
--- a/mercurial/configitems.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/configitems.py	Fri Apr 14 10:41:40 2023 +0200
@@ -971,7 +971,7 @@
 coreconfigitem(
     b'experimental',
     b'changegroup3',
-    default=False,
+    default=True,
 )
 coreconfigitem(
     b'experimental',
--- a/mercurial/crecord.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/crecord.py	Fri Apr 14 10:41:40 2023 +0200
@@ -1990,7 +1990,7 @@
         )
         # newwin([height, width,] begin_y, begin_x)
         self.statuswin = curses.newwin(self.numstatuslines, 0, 0, 0)
-        self.statuswin.keypad(1)  # interpret arrow-key, etc. esc sequences
+        self.statuswin.keypad(True)  # interpret arrow-key, etc. esc sequences
 
         # figure out how much space to allocate for the chunk-pad which is
         # used for displaying the patch
--- a/mercurial/debugcommands.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/debugcommands.py	Fri Apr 14 10:41:40 2023 +0200
@@ -93,6 +93,7 @@
     wireprotoserver,
 )
 from .interfaces import repository
+from .stabletailgraph import stabletailsort
 from .utils import (
     cborutil,
     compression,
@@ -3644,6 +3645,30 @@
 
 
 @command(
+    b'debug::stable-tail-sort',
+    [
+        (
+            b'T',
+            b'template',
+            b'{rev}\n',
+            _(b'display with template'),
+            _(b'TEMPLATE'),
+        ),
+    ],
+    b'REV',
+)
+def debug_stable_tail_sort(ui, repo, revspec, template, **opts):
+    """display the stable-tail sort of the ancestors of a given node"""
+    rev = logcmdutil.revsingle(repo, revspec).rev()
+    cl = repo.changelog
+
+    displayer = logcmdutil.maketemplater(ui, repo, template)
+    sorted_revs = stabletailsort._stable_tail_sort(cl, rev)
+    for ancestor_rev in sorted_revs:
+        displayer.show(repo[ancestor_rev])
+
+
+@command(
     b"debugbackupbundle",
     [
         (
@@ -4512,7 +4537,7 @@
             peer = None
         else:
             ui.write(_(b'creating ssh peer from handshake results\n'))
-            peer = sshpeer.makepeer(
+            peer = sshpeer._make_peer(
                 ui,
                 url,
                 proc,
@@ -4568,7 +4593,7 @@
             )
         else:
             peer_path = urlutil.try_path(ui, path)
-            peer = httppeer.makepeer(ui, peer_path, opener=opener)
+            peer = httppeer._make_peer(ui, peer_path, opener=opener)
 
         # We /could/ populate stdin/stdout with sock.makefile()...
     else:
--- a/mercurial/dirstate.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/dirstate.py	Fri Apr 14 10:41:40 2023 +0200
@@ -1760,12 +1760,6 @@
             return list(files)
         return [f for f in dmap if match(f)]
 
-    def _actualfilename(self, tr):
-        if tr:
-            return self._pendingfilename
-        else:
-            return self._filename
-
     def all_file_names(self):
         """list all filename currently used by this dirstate
 
--- a/mercurial/discovery.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/discovery.py	Fri Apr 14 10:41:40 2023 +0200
@@ -104,14 +104,14 @@
         if ancestorsof is None:
             ancestorsof = cl.heads()
         if missingroots:
-            discbases = []
-            for n in missingroots:
-                discbases.extend([p for p in cl.parents(n) if p != repo.nullid])
             # TODO remove call to nodesbetween.
             # TODO populate attributes on outgoing instance instead of setting
             # discbases.
             csets, roots, heads = cl.nodesbetween(missingroots, ancestorsof)
             included = set(csets)
+            discbases = []
+            for n in csets:
+                discbases.extend([p for p in cl.parents(n) if p != repo.nullid])
             ancestorsof = heads
             commonheads = [n for n in discbases if n not in included]
         elif not commonheads:
--- a/mercurial/encoding.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/encoding.py	Fri Apr 14 10:41:40 2023 +0200
@@ -657,7 +657,7 @@
             pass
 
     s = pycompat.bytestr(s)
-    r = b""
+    r = bytearray()
     pos = 0
     l = len(s)
     while pos < l:
@@ -673,7 +673,7 @@
             c = unichr(0xDC00 + ord(s[pos])).encode('utf-8', _utf8strict)
             pos += 1
         r += c
-    return r
+    return bytes(r)
 
 
 def fromutf8b(s):
@@ -712,7 +712,7 @@
     # helper again to walk the string without "decoding" it.
 
     s = pycompat.bytestr(s)
-    r = b""
+    r = bytearray()
     pos = 0
     l = len(s)
     while pos < l:
@@ -722,4 +722,4 @@
         if b"\xed\xb0\x80" <= c <= b"\xed\xb3\xbf":
             c = pycompat.bytechr(ord(c.decode("utf-8", _utf8strict)) & 0xFF)
         r += c
-    return r
+    return bytes(r)
--- a/mercurial/helptext/config.txt	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/helptext/config.txt	Fri Apr 14 10:41:40 2023 +0200
@@ -1622,7 +1622,7 @@
     in ``http_proxy.no``. (default: False)
 
 ``http``
-----------
+--------
 
 Used to configure access to Mercurial repositories via HTTP.
 
--- a/mercurial/helptext/rust.txt	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/helptext/rust.txt	Fri Apr 14 10:41:40 2023 +0200
@@ -76,8 +76,9 @@
 MSRV
 ====
 
-The minimum supported Rust version is currently 1.61.0. The project's policy is
-to follow the version from Debian testing, to make the distributions' job easier.
+The minimum supported Rust version is defined in `rust/clippy.toml`.
+The project's policy is to keep it at or below the version from Debian testing,
+to make the distributions' job easier.
 
 rhg
 ===
--- a/mercurial/httppeer.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/httppeer.py	Fri Apr 14 10:41:40 2023 +0200
@@ -592,7 +592,7 @@
     return respurl, info
 
 
-def makepeer(ui, path, opener=None, requestbuilder=urlreq.request):
+def _make_peer(ui, path, opener=None, requestbuilder=urlreq.request):
     """Construct an appropriate HTTP peer instance.
 
     ``opener`` is an ``url.opener`` that should be used to establish
@@ -628,7 +628,7 @@
                 _(b'Python support for SSL and HTTPS is not installed')
             )
 
-        inst = makepeer(ui, path)
+        inst = _make_peer(ui, path)
 
         return inst
     except error.RepoError as httpexception:
--- a/mercurial/mail.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/mail.py	Fri Apr 14 10:41:40 2023 +0200
@@ -54,9 +54,9 @@
         self._ui = ui
         self._host = host
 
-    def starttls(self, keyfile=None, certfile=None):
+    def starttls(self, keyfile=None, certfile=None, context=None):
         if not self.has_extn("starttls"):
-            msg = b"STARTTLS extension not supported by server"
+            msg = "STARTTLS extension not supported by server"
             raise smtplib.SMTPException(msg)
         (resp, reply) = self.docmd("STARTTLS")
         if resp == 220:
--- a/mercurial/phases.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/phases.py	Fri Apr 14 10:41:40 2023 +0200
@@ -154,6 +154,7 @@
 internal = 96  # non-continuous for compatibility
 allphases = (public, draft, secret, archived, internal)
 trackedphases = (draft, secret, archived, internal)
+not_public_phases = trackedphases
 # record phase names
 cmdphasenames = [b'public', b'draft', b'secret']  # known to `hg phase` command
 phasenames = dict(enumerate(cmdphasenames))
@@ -171,6 +172,10 @@
 remotehiddenphases = (secret, archived, internal)
 localhiddenphases = (internal, archived)
 
+all_internal_phases = tuple(p for p in allphases if p & internal)
+# We do not want any internal content to exit the repository, ever.
+no_bundle_phases = all_internal_phases
+
 
 def supportinternal(repo):
     # type: (localrepo.localrepository) -> bool
@@ -826,10 +831,8 @@
     cl = repo.changelog
 
     headsbyphase = {i: [] for i in allphases}
-    # No need to keep track of secret phase; any heads in the subset that
-    # are not mentioned are implicitly secret.
-    for phase in allphases[:secret]:
-        revset = b"heads(%%ln & %s())" % phasenames[phase]
+    for phase in allphases:
+        revset = b"heads(%%ln & _phase(%d))" % phase
         headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)]
     return headsbyphase
 
--- a/mercurial/repair.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/repair.py	Fri Apr 14 10:41:40 2023 +0200
@@ -34,7 +34,14 @@
 
 
 def backupbundle(
-    repo, bases, heads, node, suffix, compress=True, obsolescence=True
+    repo,
+    bases,
+    heads,
+    node,
+    suffix,
+    compress=True,
+    obsolescence=True,
+    tmp_backup=False,
 ):
     """create a bundle with the specified revisions as a backup"""
 
@@ -81,6 +88,7 @@
         contentopts,
         vfs,
         compression=comp,
+        allow_internal=tmp_backup,
     )
 
 
@@ -197,6 +205,7 @@
             b'temp',
             compress=False,
             obsolescence=False,
+            tmp_backup=True,
         )
 
     with ui.uninterruptible():
@@ -335,8 +344,26 @@
 def _createstripbackup(repo, stripbases, node, topic):
     # backup the changeset we are about to strip
     vfs = repo.vfs
-    cl = repo.changelog
-    backupfile = backupbundle(repo, stripbases, cl.heads(), node, topic)
+    unfi = repo.unfiltered()
+    to_node = unfi.changelog.node
+    # internal changeset are internal implementation details that should not
+    # leave the repository and not be exposed to the users. In addition feature
+    # using them requires to be resistant to strip. See test case for more
+    # details.
+    all_backup = unfi.revs(
+        b"(%ln)::(%ld) and not _internal()",
+        stripbases,
+        unfi.changelog.headrevs(),
+    )
+    if not all_backup:
+        return None
+
+    def to_nodes(revs):
+        return [to_node(r) for r in revs]
+
+    bases = to_nodes(unfi.revs("roots(%ld)", all_backup))
+    heads = to_nodes(unfi.revs("heads(%ld)", all_backup))
+    backupfile = backupbundle(repo, bases, heads, node, topic)
     repo.ui.status(_(b"saved backup bundle to %s\n") % vfs.join(backupfile))
     repo.ui.log(
         b"backupbundle", b"saved backup bundle to %s\n", vfs.join(backupfile)
--- a/mercurial/revlogutils/flagutil.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/revlogutils/flagutil.py	Fri Apr 14 10:41:40 2023 +0200
@@ -176,8 +176,12 @@
             vhash = True
 
             if flag not in revlog._flagprocessors:
+                hint = None
+                if flag == REVIDX_EXTSTORED:
+                    hint = _(b"the lfs extension must be enabled")
+
                 message = _(b"missing processor for flag '%#x'") % flag
-                raise revlog._flagserrorclass(message)
+                raise revlog._flagserrorclass(message, hint=hint)
 
             processor = revlog._flagprocessors[flag]
             if processor is not None:
--- a/mercurial/revset.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/revset.py	Fri Apr 14 10:41:40 2023 +0200
@@ -1967,6 +1967,12 @@
     return repo._phasecache.getrevset(repo, targets, subset)
 
 
+@predicate(b'_internal()', safe=True)
+def _internal(repo, subset, x):
+    getargs(x, 0, 0, _(b"_internal takes no arguments"))
+    return _phase(repo, subset, *phases.all_internal_phases)
+
+
 @predicate(b'_phase(idx)', safe=True)
 def phase(repo, subset, x):
     l = getargs(x, 1, 1, b"_phase requires one argument")
@@ -2061,7 +2067,7 @@
 @predicate(b'_notpublic', safe=True)
 def _notpublic(repo, subset, x):
     getargs(x, 0, 0, b"_notpublic takes no arguments")
-    return _phase(repo, subset, phases.draft, phases.secret)
+    return _phase(repo, subset, *phases.not_public_phases)
 
 
 # for internal use
--- a/mercurial/sshpeer.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/sshpeer.py	Fri Apr 14 10:41:40 2023 +0200
@@ -568,7 +568,7 @@
             self._readerr()
 
 
-def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
+def _make_peer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
     """Make a peer instance from existing pipes.
 
     ``path`` and ``proc`` are stored on the eventual peer instance and may
@@ -658,7 +658,7 @@
         ui, sshcmd, args, remotecmd, remotepath, sshenv
     )
 
-    peer = makepeer(ui, path, proc, stdin, stdout, stderr)
+    peer = _make_peer(ui, path, proc, stdin, stdout, stderr)
 
     # Finally, if supported by the server, notify it about our own
     # capabilities.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/stabletailgraph/stabletailsort.py	Fri Apr 14 10:41:40 2023 +0200
@@ -0,0 +1,110 @@
+# stabletailsort.py - stable ordering of revisions
+#
+# Copyright 2021-2023 Pacien TRAN-GIRARD <pacien.trangirard@pacien.net>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+"""
+Stable-tail sort computation.
+
+The "stable-tail sort", or STS, is a reverse topological ordering of the
+ancestors of a node, which tends to share large suffixes with the stable-tail
+sort of ancestors and other nodes, giving it its name.
+
+Its properties should make it suitable for making chunks of ancestors with high
+reuse and incrementality for example.
+
+This module and implementation are experimental. Most functions are not yet
+optimised to operate on large production graphs.
+"""
+
+import itertools
+from ..node import nullrev
+from .. import ancestor
+
+
+def _sorted_parents(cl, p1, p2):
+    """
+    Chooses and returns the pair (px, pt) from (p1, p2).
+
+    Where
+    "px" denotes the parent starting the "exclusive" part, and
+    "pt" denotes the parent starting the "Tail" part.
+
+    "px" is chosen as the parent with the lowest rank with the goal of
+    minimising the size of the exclusive part and maximise the size of the
+    tail part, hopefully reducing the overall complexity of the stable sort.
+
+    In case of equal ranks, the stable node ID is used as a tie-breaker.
+    """
+    r1, r2 = cl.fast_rank(p1), cl.fast_rank(p2)
+    if r1 < r2:
+        return (p1, p2)
+    elif r1 > r2:
+        return (p2, p1)
+    elif cl.node(p1) < cl.node(p2):
+        return (p1, p2)
+    else:
+        return (p2, p1)
+
+
+def _nonoedipal_parent_revs(cl, rev):
+    """
+    Returns the non-Å“dipal parent pair of the given revision.
+
+    An Å“dipal merge is a merge with parents p1, p2 with either
+    p1 in ancestors(p2) or p2 in ancestors(p1).
+    In the first case, p1 is the Å“dipal parent.
+    In the second case, p2 is the Å“dipal parent.
+
+    Å’dipal edges start empty exclusive parts. They do not bring new ancestors.
+    As such, they can be skipped when computing any topological sort or any
+    iteration over the ancestors of a node.
+
+    The Å“dipal edges are eliminated here using the rank information.
+    """
+    p1, p2 = cl.parentrevs(rev)
+    if p1 == nullrev or cl.fast_rank(p2) == cl.fast_rank(rev) - 1:
+        return p2, nullrev
+    elif p2 == nullrev or cl.fast_rank(p1) == cl.fast_rank(rev) - 1:
+        return p1, nullrev
+    else:
+        return p1, p2
+
+
+def _stable_tail_sort(cl, head_rev):
+    """
+    Naive topological iterator of the ancestors given by the stable-tail sort.
+
+    The stable-tail sort of a node "h" is defined as the sequence:
+    sts(h) := [h] + excl(h) + sts(pt(h))
+    where excl(h) := u for u in sts(px(h)) if u not in ancestors(pt(h))
+
+    This implementation uses a call-stack whose size is
+    O(number of open merges).
+
+    As such, this implementation exists mainly as a defining reference.
+    """
+    cursor_rev = head_rev
+    while cursor_rev != nullrev:
+        yield cursor_rev
+
+        p1, p2 = _nonoedipal_parent_revs(cl, cursor_rev)
+        if p1 == nullrev:
+            cursor_rev = p2
+        elif p2 == nullrev:
+            cursor_rev = p1
+        else:
+            px, pt = _sorted_parents(cl, p1, p2)
+
+            tail_ancestors = ancestor.lazyancestors(
+                cl.parentrevs, (pt,), inclusive=True
+            )
+            exclusive_ancestors = (
+                a for a in _stable_tail_sort(cl, px) if a not in tail_ancestors
+            )
+
+            excl_part_size = cl.fast_rank(cursor_rev) - cl.fast_rank(pt) - 1
+            yield from itertools.islice(exclusive_ancestors, excl_part_size)
+            cursor_rev = pt
--- a/mercurial/statichttprepo.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/statichttprepo.py	Fri Apr 14 10:41:40 2023 +0200
@@ -119,7 +119,7 @@
 
     def http_error_416(self, req, fp, code, msg, hdrs):
         # HTTP's Range Not Satisfiable error
-        raise _RangeError(b'Requested Range Not Satisfiable')
+        raise _RangeError('Requested Range Not Satisfiable')
 
 
 def build_opener(ui, authinfo):
@@ -134,13 +134,13 @@
 
         def __call__(self, path, mode=b'r', *args, **kw):
             if mode not in (b'r', b'rb'):
-                raise IOError(b'Permission denied')
+                raise IOError('Permission denied')
             f = b"/".join((self.base, urlreq.quote(path)))
             return httprangereader(f, urlopener)
 
-        def join(self, path):
+        def join(self, path, *insidef):
             if path:
-                return pathutil.join(self.base, path)
+                return pathutil.join(self.base, path, *insidef)
             else:
                 return self.base
 
--- a/mercurial/templates/json/map	Thu Apr 13 14:21:01 2023 +0200
+++ b/mercurial/templates/json/map	Fri Apr 14 10:41:40 2023 +0200
@@ -65,6 +65,7 @@
   "tags": [{join(changesettag, ", ")}],
   "user": {author|utf8|json},
   "parents": [{join(parent%changesetparent, ", ")}],
+  "children": [{join(child%changesetparent, ", ")}],
   "files": [{join(files, ", ")}],
   "diff": [{join(diff, ", ")}],
   "phase": {phase|json}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/thirdparty/jaraco/collections.py	Fri Apr 14 10:41:40 2023 +0200
@@ -0,0 +1,56 @@
+# adapted from jaraco.collections 3.9
+
+import collections
+
+
+class Projection(collections.abc.Mapping):
+    """
+    Project a set of keys over a mapping
+
+    >>> sample = {'a': 1, 'b': 2, 'c': 3}
+    >>> prj = Projection(['a', 'c', 'd'], sample)
+    >>> prj == {'a': 1, 'c': 3}
+    True
+
+    Keys should only appear if they were specified and exist in the space.
+
+    >>> sorted(list(prj.keys()))
+    ['a', 'c']
+
+    Attempting to access a key not in the projection
+    results in a KeyError.
+
+    >>> prj['b']
+    Traceback (most recent call last):
+    ...
+    KeyError: 'b'
+
+    Use the projection to update another dict.
+
+    >>> target = {'a': 2, 'b': 2}
+    >>> target.update(prj)
+    >>> target == {'a': 1, 'b': 2, 'c': 3}
+    True
+
+    Also note that Projection keeps a reference to the original dict, so
+    if you modify the original dict, that could modify the Projection.
+
+    >>> del sample['a']
+    >>> dict(prj)
+    {'c': 3}
+    """
+
+    def __init__(self, keys, space):
+        self._keys = tuple(keys)
+        self._space = space
+
+    def __getitem__(self, key):
+        if key not in self._keys:
+            raise KeyError(key)
+        return self._space[key]
+
+    def __iter__(self):
+        return iter(set(self._keys).intersection(self._space))
+
+    def __len__(self):
+        return len(tuple(iter(self)))
--- a/rust/README.rst	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/README.rst	Fri Apr 14 10:41:40 2023 +0200
@@ -7,17 +7,19 @@
 improves performance in some areas.
 
 There are currently four independent Rust projects:
+
 - chg. An implementation of chg, in Rust instead of C.
 - hgcli. A project that provides a (mostly) self-contained "hg" binary,
   for ease of deployment and a bit of speed, using PyOxidizer. See
-  hgcli/README.md.
+  ``hgcli/README.md``.
 - hg-core (and hg-cpython): implementation of some
   functionality of mercurial in Rust, e.g. ancestry computations in
   revision graphs, status or pull discovery. The top-level ``Cargo.toml`` file
   defines a workspace containing these crates.
 - rhg: a pure Rust implementation of Mercurial, with a fallback mechanism for
-  unsupported invocations. It reuses the logic `hg-core` but completely forgoes
-  interaction with Python. See `rust/rhg/README.md` for more details.
+  unsupported invocations. It reuses the logic ``hg-core`` but
+  completely forgoes interaction with Python. See
+  ``rust/rhg/README.md`` for more details.
 
 Using Rust code
 ===============
@@ -41,10 +43,10 @@
 ================
 
 In the future, compile-time opt-ins may be added
-to the `features` section in ``hg-cpython/Cargo.toml``.
+to the ``features`` section in ``hg-cpython/Cargo.toml``.
 
-To use features from the Makefile, use the `HG_RUST_FEATURES` environment
-variable: for instance `HG_RUST_FEATURES="some-feature other-feature"`
+To use features from the Makefile, use the ``HG_RUST_FEATURES`` environment
+variable: for instance ``HG_RUST_FEATURES="some-feature other-feature"``.
 
 Profiling
 =========
@@ -57,7 +59,7 @@
 Creating a ``.cargo/config`` file with the following content enables
 debug information in optimized builds. This make profiles more informative
 with source file name and line number for Rust stack frames and
-(in some cases) stack frames for Rust functions that have been inlined.
+(in some cases) stack frames for Rust functions that have been inlined::
 
   [profile.release]
   debug = true
@@ -69,7 +71,7 @@
 as opposed to tools for native code like ``perf``, which attribute
 time to the python interpreter instead of python functions).
 
-Example usage:
+Example usage::
 
   $ make PURE=--rust local # Don't forget to recompile after a code change
   $ py-spy record --native --output /tmp/profile.svg -- ./hg ...
@@ -77,9 +79,25 @@
 Developing Rust
 ===============
 
-The current version of Rust in use is ``1.61.0``, because it's what Debian
-testing has. You can use ``rustup override set 1.61.0`` at the root of the repo
-to make it easier on you.
+Minimum Supported Rust Version
+------------------------------
+
+The minimum supported rust version (MSRV) is specified in the `Clippy`_
+configuration file at ``rust/clippy.toml``. It is set to be ``1.61.0`` as of
+this writing, but keep in mind that the authoritative value is the one
+from the configuration file.
+
+We bump it from time to time, with the general rule being that our
+MSRV should not be greater that the version of the Rust toolchain
+shipping with Debian testing, so that the Rust enhanced Mercurial can
+be eventually packaged in Debian.
+
+To ensure that you are not depending on features introduced in later
+versions, you can issue ``rustup override set x.y.z`` at the root of
+the repository.
+
+Build and development
+---------------------
 
 Go to the ``hg-cpython`` folder::
 
@@ -117,8 +135,28 @@
 using the nightly version because it has been stable enough and provides
 comment folding.
 
-To format the entire Rust workspace::
+Our CI enforces that the code does not need reformatting. Before
+submitting your changes, please format the entire Rust workspace by running::
+
 
   $ cargo +nightly fmt
 
 This requires you to have the nightly toolchain installed.
+
+Linting: code sanity
+--------------------
+
+We're using `Clippy`_, the standard code diagnosis tool of the Rust
+community.
+
+Our CI enforces that the code is free of Clippy warnings, so you might
+want to run it on your side before submitting your changes. Simply do::
+
+  % cargo clippy
+
+from the top of the Rust workspace. Clippy is part of the default
+``rustup`` install, so it should work right away. In case it would
+not, you can install it with ``rustup component add``.
+
+
+.. _Clippy: https://doc.rust-lang.org/stable/clippy/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/clippy.toml	Fri Apr 14 10:41:40 2023 +0200
@@ -0,0 +1,1 @@
+msrv = "1.61.0"
--- a/rust/hg-core/src/checkexec.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/hg-core/src/checkexec.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -112,8 +112,10 @@
     Ok(false)
 }
 
-/// This function is a rust rewrite of [checkexec] function from [posix.py]
-/// Returns true if the filesystem supports execute permissions.
+/// This function is a Rust rewrite of the `checkexec` function from
+/// `posix.py`.
+///
+/// Returns `true` if the filesystem supports execute permissions.
 pub fn check_exec(path: impl AsRef<Path>) -> bool {
     check_exec_impl(path).unwrap_or(false)
 }
--- a/rust/hg-core/src/revlog/changelog.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/hg-core/src/revlog/changelog.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -1,6 +1,6 @@
 use crate::errors::HgError;
-use crate::revlog::Revision;
 use crate::revlog::{Node, NodePrefix};
+use crate::revlog::{Revision, NULL_REVISION};
 use crate::revlog::{Revlog, RevlogEntry, RevlogError};
 use crate::utils::hg_path::HgPath;
 use crate::vfs::Vfs;
@@ -9,7 +9,7 @@
 use std::borrow::Cow;
 use std::fmt::{Debug, Formatter};
 
-/// A specialized `Revlog` to work with `changelog` data format.
+/// A specialized `Revlog` to work with changelog data format.
 pub struct Changelog {
     /// The generic `revlog` format.
     pub(crate) revlog: Revlog,
@@ -23,7 +23,7 @@
         Ok(Self { revlog })
     }
 
-    /// Return the `ChangelogEntry` for the given node ID.
+    /// Return the `ChangelogRevisionData` for the given node ID.
     pub fn data_for_node(
         &self,
         node: NodePrefix,
@@ -32,30 +32,29 @@
         self.data_for_rev(rev)
     }
 
-    /// Return the `RevlogEntry` of the given revision number.
+    /// Return the [`ChangelogEntry`] for the given revision number.
     pub fn entry_for_rev(
         &self,
         rev: Revision,
-    ) -> Result<RevlogEntry, RevlogError> {
-        self.revlog.get_entry(rev)
+    ) -> Result<ChangelogEntry, RevlogError> {
+        let revlog_entry = self.revlog.get_entry(rev)?;
+        Ok(ChangelogEntry { revlog_entry })
     }
 
-    /// Return the `ChangelogEntry` of the given revision number.
+    /// Return the [`ChangelogRevisionData`] for the given revision number.
+    ///
+    /// This is a useful shortcut in case the caller does not need the
+    /// generic revlog information (parents, hashes etc). Otherwise
+    /// consider taking a [`ChangelogEntry`] with
+    /// [entry_for_rev](`Self::entry_for_rev`) and doing everything from there.
     pub fn data_for_rev(
         &self,
         rev: Revision,
     ) -> Result<ChangelogRevisionData, RevlogError> {
-        let bytes = self.revlog.get_rev_data(rev)?;
-        if bytes.is_empty() {
-            Ok(ChangelogRevisionData::null())
-        } else {
-            Ok(ChangelogRevisionData::new(bytes).map_err(|err| {
-                RevlogError::Other(HgError::CorruptedRepository(format!(
-                    "Invalid changelog data for revision {}: {:?}",
-                    rev, err
-                )))
-            })?)
+        if rev == NULL_REVISION {
+            return Ok(ChangelogRevisionData::null());
         }
+        self.entry_for_rev(rev)?.data()
     }
 
     pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
@@ -70,6 +69,59 @@
     }
 }
 
+/// A specialized `RevlogEntry` for `changelog` data format
+///
+/// This is a `RevlogEntry` with the added semantics that the associated
+/// data should meet the requirements for `changelog`, materialized by
+/// the fact that `data()` constructs a `ChangelogRevisionData`.
+/// In case that promise would be broken, the `data` method returns an error.
+#[derive(Clone)]
+pub struct ChangelogEntry<'changelog> {
+    /// Same data, as a generic `RevlogEntry`.
+    pub(crate) revlog_entry: RevlogEntry<'changelog>,
+}
+
+impl<'changelog> ChangelogEntry<'changelog> {
+    pub fn data<'a>(
+        &'a self,
+    ) -> Result<ChangelogRevisionData<'changelog>, RevlogError> {
+        let bytes = self.revlog_entry.data()?;
+        if bytes.is_empty() {
+            Ok(ChangelogRevisionData::null())
+        } else {
+            Ok(ChangelogRevisionData::new(bytes).map_err(|err| {
+                RevlogError::Other(HgError::CorruptedRepository(format!(
+                    "Invalid changelog data for revision {}: {:?}",
+                    self.revlog_entry.revision(),
+                    err
+                )))
+            })?)
+        }
+    }
+
+    /// Obtain a reference to the underlying `RevlogEntry`.
+    ///
+    /// This allows the caller to access the information that is common
+    /// to all revlog entries: revision number, node id, parent revisions etc.
+    pub fn as_revlog_entry(&self) -> &RevlogEntry {
+        &self.revlog_entry
+    }
+
+    pub fn p1_entry(&self) -> Result<Option<ChangelogEntry>, RevlogError> {
+        Ok(self
+            .revlog_entry
+            .p1_entry()?
+            .map(|revlog_entry| Self { revlog_entry }))
+    }
+
+    pub fn p2_entry(&self) -> Result<Option<ChangelogEntry>, RevlogError> {
+        Ok(self
+            .revlog_entry
+            .p2_entry()?
+            .map(|revlog_entry| Self { revlog_entry }))
+    }
+}
+
 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
 #[derive(PartialEq)]
 pub struct ChangelogRevisionData<'changelog> {
@@ -215,6 +267,8 @@
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::vfs::Vfs;
+    use crate::NULL_REVISION;
     use pretty_assertions::assert_eq;
 
     #[test]
@@ -268,4 +322,20 @@
         );
         assert_eq!(data.description(), b"some\ncommit\nmessage");
     }
+
+    #[test]
+    fn test_data_from_rev_null() -> Result<(), RevlogError> {
+        // an empty revlog will be enough for this case
+        let temp = tempfile::tempdir().unwrap();
+        let vfs = Vfs { base: temp.path() };
+        std::fs::write(temp.path().join("foo.i"), b"").unwrap();
+        let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
+
+        let changelog = Changelog { revlog };
+        assert_eq!(
+            changelog.data_for_rev(NULL_REVISION)?,
+            ChangelogRevisionData::null()
+        );
+        Ok(())
+    }
 }
--- a/rust/hg-core/src/revlog/mod.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/hg-core/src/revlog/mod.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -400,10 +400,10 @@
 /// The revlog entry's bytes and the necessary informations to extract
 /// the entry's data.
 #[derive(Clone)]
-pub struct RevlogEntry<'a> {
-    revlog: &'a Revlog,
+pub struct RevlogEntry<'revlog> {
+    revlog: &'revlog Revlog,
     rev: Revision,
-    bytes: &'a [u8],
+    bytes: &'revlog [u8],
     compressed_len: u32,
     uncompressed_len: i32,
     base_rev_or_base_of_delta_chain: Option<Revision>,
@@ -413,7 +413,7 @@
     hash: Node,
 }
 
-impl<'a> RevlogEntry<'a> {
+impl<'revlog> RevlogEntry<'revlog> {
     pub fn revision(&self) -> Revision {
         self.rev
     }
@@ -430,7 +430,9 @@
         self.p1 != NULL_REVISION
     }
 
-    pub fn p1_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
+    pub fn p1_entry(
+        &self,
+    ) -> Result<Option<RevlogEntry<'revlog>>, RevlogError> {
         if self.p1 == NULL_REVISION {
             Ok(None)
         } else {
@@ -438,7 +440,9 @@
         }
     }
 
-    pub fn p2_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
+    pub fn p2_entry(
+        &self,
+    ) -> Result<Option<RevlogEntry<'revlog>>, RevlogError> {
         if self.p2 == NULL_REVISION {
             Ok(None)
         } else {
@@ -473,7 +477,7 @@
     }
 
     /// The data for this entry, after resolving deltas if any.
-    pub fn rawdata(&self) -> Result<Cow<'a, [u8]>, HgError> {
+    pub fn rawdata(&self) -> Result<Cow<'revlog, [u8]>, HgError> {
         let mut entry = self.clone();
         let mut delta_chain = vec![];
 
@@ -503,8 +507,8 @@
 
     fn check_data(
         &self,
-        data: Cow<'a, [u8]>,
-    ) -> Result<Cow<'a, [u8]>, HgError> {
+        data: Cow<'revlog, [u8]>,
+    ) -> Result<Cow<'revlog, [u8]>, HgError> {
         if self.revlog.check_hash(
             self.p1,
             self.p2,
@@ -525,7 +529,7 @@
         }
     }
 
-    pub fn data(&self) -> Result<Cow<'a, [u8]>, HgError> {
+    pub fn data(&self) -> Result<Cow<'revlog, [u8]>, HgError> {
         let data = self.rawdata()?;
         if self.is_censored() {
             return Err(HgError::CensoredNodeError);
@@ -535,7 +539,7 @@
 
     /// Extract the data contained in the entry.
     /// This may be a delta. (See `is_delta`.)
-    fn data_chunk(&self) -> Result<Cow<'a, [u8]>, HgError> {
+    fn data_chunk(&self) -> Result<Cow<'revlog, [u8]>, HgError> {
         if self.bytes.is_empty() {
             return Ok(Cow::Borrowed(&[]));
         }
--- a/rust/hg-core/src/revlog/nodemap.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/hg-core/src/revlog/nodemap.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -25,6 +25,9 @@
 
 #[derive(Debug, PartialEq)]
 pub enum NodeMapError {
+    /// A `NodePrefix` matches several [`Revision`]s.
+    ///
+    /// This can be returned by methods meant for (at most) one match.
     MultipleResults,
     /// A `Revision` stored in the nodemap could not be found in the index
     RevisionNotInIndex(Revision),
@@ -35,8 +38,8 @@
 /// ## `RevlogIndex` and `NodeMap`
 ///
 /// One way to think about their relationship is that
-/// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
-/// carried by a [`RevlogIndex`].
+/// the `NodeMap` is a prefix-oriented reverse index of the [`Node`]
+/// information carried by a [`RevlogIndex`].
 ///
 /// Many of the methods in this trait take a `RevlogIndex` argument
 /// which is used for validation of their results. This index must naturally
@@ -45,14 +48,10 @@
 /// Notably, the `NodeMap` must not store
 /// information about more `Revision` values than there are in the index.
 /// In these methods, an encountered `Revision` is not in the index, a
-/// [`RevisionNotInIndex`] error is returned.
+/// [RevisionNotInIndex](NodeMapError) error is returned.
 ///
 /// In insert operations, the rule is thus that the `NodeMap` must always
-/// be updated after the `RevlogIndex`
-/// be updated first, and the `NodeMap` second.
-///
-/// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
-/// [`RevlogIndex`]: ../trait.RevlogIndex.html
+/// be updated after the `RevlogIndex` it is about.
 pub trait NodeMap {
     /// Find the unique `Revision` having the given `Node`
     ///
@@ -69,8 +68,8 @@
     ///
     /// If no Revision matches the given prefix, `Ok(None)` is returned.
     ///
-    /// If several Revisions match the given prefix, a [`MultipleResults`]
-    /// error is returned.
+    /// If several Revisions match the given prefix, a
+    /// [MultipleResults](NodeMapError)  error is returned.
     fn find_bin(
         &self,
         idx: &impl RevlogIndex,
@@ -84,17 +83,18 @@
     /// returns the number of hexadecimal digits that would had sufficed
     /// to find the revision uniquely.
     ///
-    /// Returns `None` if no `Revision` could be found for the prefix.
+    /// Returns `None` if no [`Revision`] could be found for the prefix.
     ///
-    /// If several Revisions match the given prefix, a [`MultipleResults`]
-    /// error is returned.
+    /// If several Revisions match the given prefix, a
+    /// [MultipleResults](NodeMapError)  error is returned.
     fn unique_prefix_len_bin(
         &self,
         idx: &impl RevlogIndex,
         node_prefix: NodePrefix,
     ) -> Result<Option<usize>, NodeMapError>;
 
-    /// Same as `unique_prefix_len_bin`, with a full `Node` as input
+    /// Same as [unique_prefix_len_bin](Self::unique_prefix_len_bin), with
+    /// a full [`Node`] as input
     fn unique_prefix_len_node(
         &self,
         idx: &impl RevlogIndex,
@@ -113,7 +113,7 @@
     ) -> Result<(), NodeMapError>;
 }
 
-/// Low level NodeTree [`Blocks`] elements
+/// Low level NodeTree [`Block`] elements
 ///
 /// These are exactly as for instance on persistent storage.
 type RawElement = unaligned::I32Be;
@@ -156,7 +156,9 @@
     }
 }
 
-/// A logical block of the `NodeTree`, packed with a fixed size.
+const ELEMENTS_PER_BLOCK: usize = 16; // number of different values in a nybble
+
+/// A logical block of the [`NodeTree`], packed with a fixed size.
 ///
 /// These are always used in container types implementing `Index<Block>`,
 /// such as `&Block`
@@ -167,21 +169,18 @@
 ///
 /// - absent (value -1)
 /// - another `Block` in the same indexable container (value ≥ 0)
-///  - a `Revision` leaf (value ≤ -2)
+///  - a [`Revision`] leaf (value ≤ -2)
 ///
 /// Endianness has to be fixed for consistency on shared storage across
 /// different architectures.
 ///
 /// A key difference with the C `nodetree` is that we need to be
 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
-/// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
+/// rather than 0 and the [`Revision`] range upper limit of -2 instead of -1.
 ///
 /// Another related difference is that `NULL_REVISION` (-1) is not
 /// represented at all, because we want an immutable empty nodetree
 /// to be valid.
-
-const ELEMENTS_PER_BLOCK: usize = 16; // number of different values in a nybble
-
 #[derive(Copy, Clone, BytesCast, PartialEq)]
 #[repr(transparent)]
 pub struct Block([RawElement; ELEMENTS_PER_BLOCK]);
@@ -218,7 +217,7 @@
 /// Because of the append only nature of our node trees, we need to
 /// keep the original untouched and store new blocks separately.
 ///
-/// The mutable root `Block` is kept apart so that we don't have to rebump
+/// The mutable root [`Block`] is kept apart so that we don't have to rebump
 /// it on each insertion.
 pub struct NodeTree {
     readonly: Box<dyn Deref<Target = [Block]> + Send>,
@@ -242,7 +241,7 @@
     }
 }
 
-/// Return `None` unless the `Node` for `rev` has given prefix in `index`.
+/// Return `None` unless the [`Node`] for `rev` has given prefix in `idx`.
 fn has_prefix_or_none(
     idx: &impl RevlogIndex,
     prefix: NodePrefix,
@@ -260,7 +259,7 @@
 }
 
 /// validate that the candidate's node starts indeed with given prefix,
-/// and treat ambiguities related to `NULL_REVISION`.
+/// and treat ambiguities related to [`NULL_REVISION`].
 ///
 /// From the data in the NodeTree, one can only conclude that some
 /// revision is the only one for a *subprefix* of the one being looked up.
@@ -304,12 +303,10 @@
 
     /// Create from an opaque bunch of bytes
     ///
-    /// The created `NodeTreeBytes` from `buffer`,
+    /// The created [`NodeTreeBytes`] from `bytes`,
     /// of which exactly `amount` bytes are used.
     ///
     /// - `buffer` could be derived from `PyBuffer` and `Mmap` objects.
-    /// - `offset` allows for the final file format to include fixed data
-    ///   (generation number, behavioural flags)
     /// - `amount` is expressed in bytes, and is not automatically derived from
     ///   `bytes`, so that a caller that manages them atomically can perform
     ///   temporary disk serializations and still rollback easily if needed.
@@ -323,7 +320,7 @@
         NodeTree::new(Box::new(NodeTreeBytes::new(bytes, amount)))
     }
 
-    /// Retrieve added `Block` and the original immutable data
+    /// Retrieve added [`Block`]s and the original immutable data
     pub fn into_readonly_and_added(
         self,
     ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<Block>) {
@@ -335,7 +332,7 @@
         (readonly, vec)
     }
 
-    /// Retrieve added `Blocks` as bytes, ready to be written to persistent
+    /// Retrieve added [`Block]s as bytes, ready to be written to persistent
     /// storage
     pub fn into_readonly_and_added_bytes(
         self,
@@ -381,16 +378,17 @@
     ///
     /// The first returned value is the result of analysing `NodeTree` data
     /// *alone*: whereas `None` guarantees that the given prefix is absent
-    /// from the `NodeTree` data (but still could match `NULL_NODE`), with
-    /// `Some(rev)`, it is to be understood that `rev` is the unique `Revision`
-    /// that could match the prefix. Actually, all that can be inferred from
+    /// from the [`NodeTree`] data (but still could match [`NULL_NODE`]), with
+    /// `Some(rev)`, it is to be understood that `rev` is the unique
+    /// [`Revision`] that could match the prefix. Actually, all that can
+    /// be inferred from
     /// the `NodeTree` data is that `rev` is the revision with the longest
     /// common node prefix with the given prefix.
     ///
     /// The second returned value is the size of the smallest subprefix
     /// of `prefix` that would give the same result, i.e. not the
-    /// `MultipleResults` error variant (again, using only the data of the
-    /// `NodeTree`).
+    /// [MultipleResults](NodeMapError) error variant (again, using only the
+    /// data of the [`NodeTree`]).
     fn lookup(
         &self,
         prefix: NodePrefix,
--- a/rust/hg-core/src/utils.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/hg-core/src/utils.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -301,7 +301,7 @@
 /// calling `merge(key, left_value, right_value)` to resolve keys that exist in
 /// both.
 ///
-/// CC https://github.com/bodil/im-rs/issues/166
+/// CC <https://github.com/bodil/im-rs/issues/166>
 pub(crate) fn ordmap_union_with_merge<K, V>(
     left: OrdMap<K, V>,
     right: OrdMap<K, V>,
--- a/rust/hg-core/src/utils/hg_path.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/hg-core/src/utils/hg_path.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -479,10 +479,11 @@
     }
 }
 
-/// TODO: Once https://www.mercurial-scm.org/wiki/WindowsUTF8Plan is
+/// Create a new [`OsString`] from types referenceable as [`HgPath`].
+///
+/// TODO: Once <https://www.mercurial-scm.org/wiki/WindowsUTF8Plan> is
 /// implemented, these conversion utils will have to work differently depending
 /// on the repository encoding: either `UTF-8` or `MBCS`.
-
 pub fn hg_path_to_os_string<P: AsRef<HgPath>>(
     hg_path: P,
 ) -> Result<OsString, HgPathError> {
@@ -498,12 +499,14 @@
     Ok(os_str.to_os_string())
 }
 
+/// Create a new [`PathBuf`] from types referenceable as [`HgPath`].
 pub fn hg_path_to_path_buf<P: AsRef<HgPath>>(
     hg_path: P,
 ) -> Result<PathBuf, HgPathError> {
     Ok(Path::new(&hg_path_to_os_string(hg_path)?).to_path_buf())
 }
 
+/// Create a new [`HgPathBuf`] from types referenceable as [`OsStr`].
 pub fn os_string_to_hg_path_buf<S: AsRef<OsStr>>(
     os_string: S,
 ) -> Result<HgPathBuf, HgPathError> {
@@ -520,6 +523,7 @@
     Ok(buf)
 }
 
+/// Create a new [`HgPathBuf`] from types referenceable as [`Path`].
 pub fn path_to_hg_path_buf<P: AsRef<Path>>(
     path: P,
 ) -> Result<HgPathBuf, HgPathError> {
--- a/rust/rhg/src/commands/status.rs	Thu Apr 13 14:21:01 2023 +0200
+++ b/rust/rhg/src/commands/status.rs	Fri Apr 14 10:41:40 2023 +0200
@@ -111,6 +111,13 @@
                 .long("copies"),
         )
         .arg(
+            Arg::new("print0")
+                .help("end filenames with NUL, for use with xargs")
+                .short('0')
+                .action(clap::ArgAction::SetTrue)
+                .long("print0"),
+        )
+        .arg(
             Arg::new("no-status")
                 .help("hide status prefix")
                 .short('n')
@@ -213,10 +220,11 @@
     let config = invocation.config;
     let args = invocation.subcommand_args;
 
-    // TODO add `!args.get_flag("print0") &&` when we support `print0`
+    let print0 = args.get_flag("print0");
     let verbose = args.get_flag("verbose")
         || config.get_bool(b"ui", b"verbose")?
         || config.get_bool(b"commands", b"status.verbose")?;
+    let verbose = verbose && !print0;
 
     let all = args.get_flag("all");
     let display_states = if all {
@@ -363,6 +371,7 @@
             } else {
                 None
             },
+            print0,
         };
         if display_states.modified {
             output.display(b"M ", "status.modified", ds_status.modified)?;
@@ -527,6 +536,7 @@
     ui: &'a Ui,
     no_status: bool,
     relativize: Option<RelativizePaths>,
+    print0: bool,
 }
 
 impl DisplayStatusPaths<'_> {
@@ -555,12 +565,15 @@
             if !self.no_status {
                 self.ui.write_stdout_labelled(status_prefix, label)?
             }
-            self.ui
-                .write_stdout_labelled(&format_bytes!(b"{}\n", path), label)?;
+            let linebreak = if self.print0 { b"\x00" } else { b"\n" };
+            self.ui.write_stdout_labelled(
+                &format_bytes!(b"{}{}", path, linebreak),
+                label,
+            )?;
             if let Some(source) = copy_source {
                 let label = "status.copied";
                 self.ui.write_stdout_labelled(
-                    &format_bytes!(b"  {}\n", source.as_bytes()),
+                    &format_bytes!(b"  {}{}", source.as_bytes(), linebreak),
                     label,
                 )?
             }
--- a/setup.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/setup.py	Fri Apr 14 10:41:40 2023 +0200
@@ -1299,9 +1299,11 @@
     'mercurial.hgweb',
     'mercurial.interfaces',
     'mercurial.pure',
+    'mercurial.stabletailgraph',
     'mercurial.templates',
     'mercurial.thirdparty',
     'mercurial.thirdparty.attr',
+    'mercurial.thirdparty.jaraco',
     'mercurial.thirdparty.zope',
     'mercurial.thirdparty.zope.interface',
     'mercurial.upgrade_utils',
--- a/tests/common-pattern.py	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/common-pattern.py	Fri Apr 14 10:41:40 2023 +0200
@@ -10,7 +10,7 @@
     (
         br'bundlecaps=HG20%2Cbundle2%3DHG20%250A'
         br'bookmarks%250A'
-        br'changegroup%253D01%252C02%250A'
+        br'changegroup%253D01%252C02%252C03%250A'
         br'checkheads%253Drelated%250A'
         br'digests%253Dmd5%252Csha1%252Csha512%250A'
         br'error%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250A'
@@ -26,7 +26,7 @@
     (
         br'bundlecaps=HG20%2Cbundle2%3DHG20%250A'
         br'bookmarks%250A'
-        br'changegroup%253D01%252C02%250A'
+        br'changegroup%253D01%252C02%252C03%250A'
         br'checkheads%3Drelated%0A'
         br'digests%253Dmd5%252Csha1%252Csha512%250A'
         br'error%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250A'
@@ -42,7 +42,7 @@
     (
         br'bundle2=HG20%0A'
         br'bookmarks%0A'
-        br'changegroup%3D01%2C02%0A'
+        br'changegroup%3D01%2C02%2C03%0A'
         br'checkheads%3Drelated%0A'
         br'digests%3Dmd5%2Csha1%2Csha512%0A'
         br'error%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A'
@@ -59,7 +59,7 @@
     (
         br'bundle2=HG20%0A'
         br'bookmarks%0A'
-        br'changegroup%3D01%2C02%0A'
+        br'changegroup%3D01%2C02%2C03%0A'
         br'checkheads%3Drelated%0A'
         br'digests%3Dmd5%2Csha1%2Csha512%0A'
         br'error%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A'
@@ -74,7 +74,7 @@
     (
         br'bundle2=HG20%0A'
         br'bookmarks%0A'
-        br'changegroup%3D01%2C02%0A'
+        br'changegroup%3D01%2C02%2C03%0A'
         br'digests%3Dmd5%2Csha1%2Csha512%0A'
         br'error%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A'
         br'hgtagsfnodes%0A'
@@ -122,6 +122,11 @@
             % (m.group(1), m.group(2))
         ),
     ),
+    # `discovery debug output
+    (
+        br'\b(\d+) total queries in \d.\d\d\d\ds\b',
+        lambda m: (br'%s total queries in *.????s (glob)' % m.group(1)),
+    ),
 ]
 
 # Various platform error strings, keyed on a common replacement string
--- a/tests/test-acl.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-acl.t	Fri Apr 14 10:41:40 2023 +0200
@@ -109,7 +109,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -175,7 +175,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -245,7 +245,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -325,7 +325,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -396,7 +396,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -471,7 +471,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -543,7 +543,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -620,7 +620,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -694,7 +694,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -767,7 +767,7 @@
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   bundle2-output-bundle: "HG20", 7 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:bookmarks" 37 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
@@ -856,7 +856,7 @@
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   bundle2-output-bundle: "HG20", 7 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:bookmarks" 37 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
@@ -947,7 +947,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1033,7 +1033,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1117,7 +1117,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1195,7 +1195,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1284,7 +1284,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1374,7 +1374,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1461,7 +1461,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1544,7 +1544,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1631,7 +1631,7 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1754,7 +1754,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1841,7 +1841,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1919,7 +1919,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -1993,7 +1993,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -2061,7 +2061,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -2153,7 +2153,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -2244,7 +2244,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -2317,7 +2317,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
@@ -2402,7 +2402,7 @@
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 48 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
--- a/tests/test-bad-extension.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-bad-extension.t	Fri Apr 14 10:41:40 2023 +0200
@@ -63,14 +63,11 @@
   Exception: bit bucket overflow
   *** failed to import extension "badext2": No module named 'badext2'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext.badext2'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext3rd.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext3rd.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext3rd.badext2'
   Traceback (most recent call last):
-  ImportError: No module named 'badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'badext2' (py36 !)
+  ModuleNotFoundError: No module named 'badext2'
 
 names of extensions failed to load can be accessed via extensions.notloaded()
 
@@ -111,25 +108,19 @@
   YYYY/MM/DD HH:MM:SS (PID)>   - loading extension: badext2
   YYYY/MM/DD HH:MM:SS (PID)>     - could not import hgext.badext2 (No module named *badext2*): trying hgext3rd.badext2 (glob)
   Traceback (most recent call last):
-  ImportError: No module named 'hgext.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext.badext2'
   YYYY/MM/DD HH:MM:SS (PID)>     - could not import hgext3rd.badext2 (No module named *badext2*): trying badext2 (glob)
   Traceback (most recent call last):
-  ImportError: No module named 'hgext.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext.badext2'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext3rd.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext3rd.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext3rd.badext2'
   *** failed to import extension "badext2": No module named 'badext2'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext.badext2'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext3rd.badext2' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext3rd.badext2' (py36 !)
+  ModuleNotFoundError: No module named 'hgext3rd.badext2'
   Traceback (most recent call last):
-  ModuleNotFoundError: No module named 'badext2' (py36 !)
-  ImportError: No module named 'badext2' (no-py36 !)
+  ModuleNotFoundError: No module named 'badext2'
   YYYY/MM/DD HH:MM:SS (PID)> > loaded 2 extensions, total time * (glob)
   YYYY/MM/DD HH:MM:SS (PID)> - loading configtable attributes
   YYYY/MM/DD HH:MM:SS (PID)> - executing uisetup hooks
--- a/tests/test-bookmarks-pushpull.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-bookmarks-pushpull.t	Fri Apr 14 10:41:40 2023 +0200
@@ -129,10 +129,10 @@
   bundle2-output: bundle parameter: 
   bundle2-output: start of parts
   bundle2-output: bundle part: "replycaps"
-  bundle2-output-part: "replycaps" 224 bytes payload
+  bundle2-output-part: "replycaps" 227 bytes payload
   bundle2-output: part 0: "REPLYCAPS"
   bundle2-output: header chunk size: 16
-  bundle2-output: payload chunk size: 224
+  bundle2-output: payload chunk size: 227
   bundle2-output: closing payload chunk
   bundle2-output: bundle part: "check:bookmarks"
   bundle2-output-part: "check:bookmarks" 23 bytes payload
@@ -162,9 +162,9 @@
   bundle2-input: part parameters: 0
   bundle2-input: found a handler for part replycaps
   bundle2-input-part: "replycaps" supported
-  bundle2-input: payload chunk size: 224
+  bundle2-input: payload chunk size: 227
   bundle2-input: payload chunk size: 0
-  bundle2-input-part: total payload size 224
+  bundle2-input-part: total payload size 227
   bundle2-input: part header size: 22
   bundle2-input: part type: "CHECK:BOOKMARKS"
   bundle2-input: part id: "1"
@@ -241,10 +241,10 @@
   bundle2-output: bundle parameter: 
   bundle2-output: start of parts
   bundle2-output: bundle part: "replycaps"
-  bundle2-output-part: "replycaps" 224 bytes payload
+  bundle2-output-part: "replycaps" 227 bytes payload
   bundle2-output: part 0: "REPLYCAPS"
   bundle2-output: header chunk size: 16
-  bundle2-output: payload chunk size: 224
+  bundle2-output: payload chunk size: 227
   bundle2-output: closing payload chunk
   bundle2-output: bundle part: "check:bookmarks"
   bundle2-output-part: "check:bookmarks" 23 bytes payload
@@ -275,9 +275,9 @@
   bundle2-input: part parameters: 0
   bundle2-input: found a handler for part replycaps
   bundle2-input-part: "replycaps" supported
-  bundle2-input: payload chunk size: 224
+  bundle2-input: payload chunk size: 227
   bundle2-input: payload chunk size: 0
-  bundle2-input-part: total payload size 224
+  bundle2-input-part: total payload size 227
   bundle2-input: part header size: 22
   bundle2-input: part type: "CHECK:BOOKMARKS"
   bundle2-input: part id: "1"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bundle-phase-internal.t	Fri Apr 14 10:41:40 2023 +0200
@@ -0,0 +1,286 @@
+=====================================================
+test behavior of the `internal` phase around bundling
+=====================================================
+
+Long story short, internal changeset are internal implementation details and
+they should never leave the repository. Hence, they should never be in a
+bundle.
+
+Setup
+=====
+
+  $ cat << EOF >> $HGRCPATH
+  > [ui]
+  > logtemplate="{node|short} [{phase}] {desc|firstline}"
+  > EOF
+
+
+  $ hg init reference-repo --config format.use-internal-phase=yes
+  $ cd reference-repo
+  $ echo a > a
+  $ hg add a
+  $ hg commit -m "a"
+  $ echo b > b
+  $ hg add b
+  $ hg commit -m "b"
+  $ echo b > c
+  $ hg add c
+  $ hg commit -m "c"
+  $ hg log -G
+  @  07f0cc02c068 [draft] c
+  |
+  o  d2ae7f538514 [draft] b
+  |
+  o  cb9a9f314b8b [draft] a
+  
+  $ hg up ".^"
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+do a shelve
+
+  $ touch a_file.txt
+  $ hg shelve -A
+  adding a_file.txt
+  shelved as default
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -G --hidden
+  o  2ec3cf310d86 [internal] changes to: b
+  |
+  | o  07f0cc02c068 [draft] c
+  |/
+  @  d2ae7f538514 [draft] b
+  |
+  o  cb9a9f314b8b [draft] a
+  
+  $ shelved_node=`hg log --rev tip --hidden -T '{node|short}'`
+
+add more changeset above it
+
+  $ hg up 'desc(a)'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo d > d
+  $ hg add d
+  $ hg commit -m "d"
+  created new head
+  $ echo d > e
+  $ hg add e
+  $ hg commit -m "e"
+  $ hg up null
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ hg log -G
+  o  636bc07920e3 [draft] e
+  |
+  o  980f7dc84c29 [draft] d
+  |
+  | o  07f0cc02c068 [draft] c
+  | |
+  | o  d2ae7f538514 [draft] b
+  |/
+  o  cb9a9f314b8b [draft] a
+  
+  $ hg log -G --hidden
+  o  636bc07920e3 [draft] e
+  |
+  o  980f7dc84c29 [draft] d
+  |
+  | o  2ec3cf310d86 [internal] changes to: b
+  | |
+  | | o  07f0cc02c068 [draft] c
+  | |/
+  | o  d2ae7f538514 [draft] b
+  |/
+  o  cb9a9f314b8b [draft] a
+  
+  $ cd ..
+
+backup bundle from strip
+========================
+
+strip an ancestors of the internal changeset
+--------------------------------------------
+
+  $ cp -ar reference-repo strip-ancestor
+  $ cd strip-ancestor
+
+The internal change is stripped, yet it should be skipped from the backup bundle.
+
+  $ hg log -G
+  o  636bc07920e3 [draft] e
+  |
+  o  980f7dc84c29 [draft] d
+  |
+  | o  07f0cc02c068 [draft] c
+  | |
+  | o  d2ae7f538514 [draft] b
+  |/
+  o  cb9a9f314b8b [draft] a
+  
+  $ hg debugstrip 'desc(b)'
+  saved backup bundle to $TESTTMP/strip-ancestor/.hg/strip-backup/d2ae7f538514-59bd8bc3-backup.hg
+
+The change should be either gone or hidden
+
+  $ hg log -G
+  o  636bc07920e3 [draft] e
+  |
+  o  980f7dc84c29 [draft] d
+  |
+  o  cb9a9f314b8b [draft] a
+  
+
+The backup should not include it (as people tend to manipulate these directly)
+
+  $ ls -1 .hg/strip-backup/
+  d2ae7f538514-59bd8bc3-backup.hg
+  $ hg debugbundle .hg/strip-backup/*.hg
+  Stream params: {Compression: BZ}
+  changegroup -- {nbchanges: 2, version: 03} (mandatory: True)
+      d2ae7f538514cd87c17547b0de4cea71fe1af9fb
+      07f0cc02c06869c81ebf33867edef30554020c0d
+  cache:rev-branch-cache -- {} (mandatory: False)
+  phase-heads -- {} (mandatory: True)
+      07f0cc02c06869c81ebf33867edef30554020c0d draft
+
+Shelve should still work
+
+  $ hg unshelve
+  unshelving change 'default'
+  rebasing shelved changes
+  $ hg status
+  A a_file.txt
+
+  $ cd ..
+
+strip an unrelated changeset with a lower revnum
+------------------------------------------------
+
+  $ cp -ar reference-repo strip-unrelated
+  $ cd strip-unrelated
+
+The internal change is not directly stripped, but it is affected by the strip
+and it is in the "temporary backup" zone. The zone that needs to be put in a
+temporary bundle while we affect data under it.
+
+  $ hg debugstrip 'desc(c)'
+  saved backup bundle to $TESTTMP/strip-unrelated/.hg/strip-backup/07f0cc02c068-8fd0515f-backup.hg
+
+The change should be either gone or hidden
+
+  $ hg log -G
+  o  636bc07920e3 [draft] e
+  |
+  o  980f7dc84c29 [draft] d
+  |
+  | o  d2ae7f538514 [draft] b
+  |/
+  o  cb9a9f314b8b [draft] a
+  
+The backup should not include it (as people tend to manipulate these directly)
+
+  $ ls -1 .hg/strip-backup/
+  07f0cc02c068-8fd0515f-backup.hg
+  $ hg debugbundle .hg/strip-backup/*.hg
+  Stream params: {Compression: BZ}
+  changegroup -- {nbchanges: 1, version: 03} (mandatory: True)
+      07f0cc02c06869c81ebf33867edef30554020c0d
+  cache:rev-branch-cache -- {} (mandatory: False)
+  phase-heads -- {} (mandatory: True)
+      07f0cc02c06869c81ebf33867edef30554020c0d draft
+
+Shelve should still work
+
+  $ hg unshelve
+  unshelving change 'default'
+  rebasing shelved changes
+  $ hg status
+  A a_file.txt
+
+  $ cd ..
+
+explicitly strip the internal changeset
+---------------------------------------
+
+  $ cp -ar reference-repo strip-explicit
+  $ cd strip-explicit
+
+The internal change is directly selected for stripping.
+
+  $ hg debugstrip --hidden $shelved_node
+
+The change should be gone
+
+  $ hg log -G --hidden
+  o  636bc07920e3 [draft] e
+  |
+  o  980f7dc84c29 [draft] d
+  |
+  | o  07f0cc02c068 [draft] c
+  | |
+  | o  d2ae7f538514 [draft] b
+  |/
+  o  cb9a9f314b8b [draft] a
+  
+
+We don't need to backup anything
+
+  $ ls -1 .hg/strip-backup/
+
+Shelve should still work
+
+  $ hg unshelve
+  unshelving change 'default'
+  rebasing shelved changes
+  $ hg status
+  A a_file.txt
+
+  $ cd ..
+
+Explicitly bundling the internal change
+=======================================
+
+  $ cd reference-repo
+
+try to bundle it alone explicitly
+---------------------------------
+
+We should not allow it
+
+  $ hg bundle --type v3 --exact --rev $shelved_node --hidden ../internal-01.hg
+  abort: cannot bundle internal changesets
+  (1 internal changesets selected)
+  [255]
+  $ hg debugbundle ../internal-01.hg
+  abort: $ENOENT$: '../internal-01.hg'
+  [255]
+
+try to bundle it with other, somewhat explicitly
+------------------------------------------------
+
+We should not allow it
+
+  $ hg bundle --type v3 --exact --rev 'desc(b)':: --hidden ../internal-02.hg
+  abort: cannot bundle internal changesets
+  (1 internal changesets selected)
+  [255]
+  $ hg debugbundle ../internal-02.hg
+  abort: $ENOENT$: '../internal-02.hg'
+  [255]
+
+bundle visible ancestors
+------------------------
+
+This should succeed as the standard filtering is skipping the internal change naturally
+
+  $ hg bundle --type v3 --exact --rev 'desc(b)':: ../internal-03.hg
+  2 changesets found
+  $ hg debugbundle ../internal-03.hg
+  Stream params: {Compression: BZ}
+  changegroup -- {nbchanges: 2, version: 03} (mandatory: True)
+      d2ae7f538514cd87c17547b0de4cea71fe1af9fb
+      07f0cc02c06869c81ebf33867edef30554020c0d
+  cache:rev-branch-cache -- {} (mandatory: False)
+  phase-heads -- {} (mandatory: True)
+      07f0cc02c06869c81ebf33867edef30554020c0d draft
+
+  $ cd ..
+
--- a/tests/test-bundle-phases.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-bundle-phases.t	Fri Apr 14 10:41:40 2023 +0200
@@ -44,6 +44,7 @@
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)
       26805aba1e600a82e93661149f2313866a221a7b draft
+      9bc730a19041f9ec7cb33c626e811aa233efb18c secret
   $ hg strip --no-backup C
 
 Phases show on incoming, and are also restored when pulling.  Secret commits
@@ -374,6 +375,7 @@
   phase-heads -- {} (mandatory: True)
       dc0947a82db884575bb76ea10ac97b08536bfa03 public
       03ca77807e919db8807c3749086dc36fb478cac0 draft
+      4e4f9194f9f181c57f62e823e8bdfa46ab9e4ff4 secret
   $ hg strip --no-backup A
   $ hg unbundle -q bundle
   $ rm bundle
@@ -398,6 +400,7 @@
       4e4f9194f9f181c57f62e823e8bdfa46ab9e4ff4
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)
+      4e4f9194f9f181c57f62e823e8bdfa46ab9e4ff4 secret
   $ rm bundle
 
   $ hg bundle --base A -r D bundle
@@ -411,6 +414,7 @@
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)
       dc0947a82db884575bb76ea10ac97b08536bfa03 public
+      4e4f9194f9f181c57f62e823e8bdfa46ab9e4ff4 secret
   $ rm bundle
 
   $ hg bundle --base 'B + C' -r 'D + E' bundle
@@ -423,4 +427,5 @@
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)
       03ca77807e919db8807c3749086dc36fb478cac0 draft
+      4e4f9194f9f181c57f62e823e8bdfa46ab9e4ff4 secret
   $ rm bundle
--- a/tests/test-bundle-type.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-bundle-type.t	Fri Apr 14 10:41:40 2023 +0200
@@ -4,127 +4,409 @@
   $ hg init t2
   $ cd t1
   $ echo blablablablabla > file.txt
-  $ hg ci -Ama
+  $ hg ci -A -m commit_root
   adding file.txt
-  $ hg log | grep summary
-  summary:     a
-  $ hg bundle ../b1 ../t2
+  $ echo kapoue > file.txt
+  $ hg ci -m commit_1
+  $ echo scrabageul > file.txt
+  $ hg ci -m commit_2
+  $ hg up 'desc("commit_root")'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo flagabalagla > file.txt
+  $ hg ci -m commit_3
+  created new head
+  $ echo aliofia > file.txt
+  $ hg ci -m commit_4
+  $ echo alklqo > file.txt
+  $ hg ci -m commit_5
+  $ echo peakfeo > file.txt
+  $ hg ci -m commit_6 --secret
+  $ hg phase --public --rev 'desc(commit_3)'
+  $ hg log -GT '[{phase}] {desc|firstline}\n'
+  @  [secret] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [public] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [public] commit_root
+  
+
+XXX the bundle generation is defined by a discovery round here. So the secret
+changeset should be excluded.
+
+  $ hg bundle ../b1.hg ../t2
   searching for changes
-  1 changesets found
+  7 changesets found (known-bad-output !)
+  6 changesets found (missing-correct-output !)
+  $ cd ..
 
-  $ cd ../t2
-  $ hg unbundle ../b1
+  $ hg -R t2 unbundle ./b1.hg
   adding changesets
   adding manifests
   adding file changes
-  added 1 changesets with 1 changes to 1 files
-  new changesets c35a0f9217e6 (1 drafts)
-  (run 'hg update' to get a working copy)
-  $ hg up
+  added 7 changesets with 7 changes to 1 files (+1 heads) (known-bad-output !)
+  added 6 changesets with 6 changes to 1 files (+1 heads) (missing-correct-output !)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R t2 up
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg log | grep summary
-  summary:     a
-  $ cd ..
+  updated to "b9f5f740a8cd: commit_6"
+  1 other heads for branch "default"
+  $ hg -R t2 log -GT '[{phase}] {desc|firstline}\n'
+  @  [draft] commit_6 (known-bad-output !)
+  | (known-bad-output !)
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
 
 Unknown compression type is rejected
 
   $ hg init t3
-  $ cd t3
-  $ hg -q unbundle ../b1
-  $ hg bundle -a -t unknown out.hg
+  $ hg -R t3 -q unbundle ./b1.hg
+  $ hg -R t3 bundle -a -t unknown out.hg
   abort: unknown is not a recognized bundle specification
   (see 'hg help bundlespec' for supported values for --type)
   [10]
 
-  $ hg bundle -a -t unknown-v2 out.hg
+  $ hg -R t3 bundle -a -t unknown-v2 out.hg
   abort: unknown compression is not supported
   (see 'hg help bundlespec' for supported values for --type)
   [10]
 
-  $ cd ..
+test bundle types
+=================
 
-test bundle types
+since we use --all, it is okay to include the secret changeset here. It is
+unfortunate that the phase information for the secret one is lost.
 
   $ testbundle() {
   >   echo % test bundle type $1
-  >   hg init t$1
-  >   cd t1
-  >   hg bundle -t $1 ../b$1 ../t$1
-  >   f -q -B6 -D ../b$1; echo
-  >   cd ../t$1
-  >   hg debugbundle ../b$1
-  >   hg debugbundle --spec ../b$1
+  >   echo '==================================='
+  >   hg -R t1 bundle --all --type $1 ./b-$1.hg
+  >   f -q -B6 -D ./b-$1.hg; echo
+  >   hg debugbundle ./b-$1.hg
+  >   hg debugbundle --spec ./b-$1.hg
   >   echo
-  >   cd ..
+  >   hg init repo-from-type-$1
+  >   hg unbundle -R repo-from-type-$1 ./b-$1.hg
+  >   hg -R repo-from-type-$1 log -GT '[{phase}] {desc|firstline}\n'
+  >   echo
   > }
 
-  $ for t in "None" "bzip2" "gzip" "none-v2" "v2" "v1" "gzip-v1"; do
+  $ for t in "None" "bzip2" "gzip" "none-v2" "v2" "v1" "gzip-v1" "v3"; do
   >   testbundle $t
   > done
   % test bundle type None
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   none-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type bzip2
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   bzip2-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type gzip
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {Compression: GZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   gzip-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type none-v2
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   none-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type v2
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   bzip2-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type v1
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG10BZ
-  c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+  901e97fadc587978ec52f2fa76af4aefc2d191e8
+  a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+  66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+  624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+  2ea90778052ba7558fab36e3fd5d149512ff986b
+  b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   bzip2-v1
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type gzip-v1
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG10GZ
-  c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+  901e97fadc587978ec52f2fa76af4aefc2d191e8
+  a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+  66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+  624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+  2ea90778052ba7558fab36e3fd5d149512ff986b
+  b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   gzip-v1
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
+  % test bundle type v3
+  ===================================
+  7 changesets found
+  HG20\x00\x00 (esc)
+  Stream params: {Compression: BZ}
+  changegroup -- {nbchanges: 7, targetphase: 2, version: 03} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
+  cache:rev-branch-cache -- {} (mandatory: False)
+  phase-heads -- {} (mandatory: True)
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d public
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55 draft
+      2ea90778052ba7558fab36e3fd5d149512ff986b draft
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5 secret
+  bzip2-v2;cg.version=03
+  
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (4 drafts, 1 secrets)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [secret] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [public] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [public] commit_root
+  
+  
 
 Compression level can be adjusted for bundle2 bundles
 
@@ -167,36 +449,90 @@
   >   testbundle $t
   > done
   % test bundle type zstd
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {Compression: ZS}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   zstd-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
   % test bundle type zstd-v2
-  searching for changes
-  1 changesets found
+  ===================================
+  7 changesets found
   HG20\x00\x00 (esc)
   Stream params: {Compression: ZS}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   cache:rev-branch-cache -- {} (mandatory: False)
   zstd-v2
   
+  adding changesets
+  adding manifests
+  adding file changes
+  added 7 changesets with 7 changes to 1 files (+1 heads)
+  new changesets ac39af4a9f7d:b9f5f740a8cd (7 drafts)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  o  [draft] commit_6
+  |
+  o  [draft] commit_5
+  |
+  o  [draft] commit_4
+  |
+  o  [draft] commit_3
+  |
+  | o  [draft] commit_2
+  | |
+  | o  [draft] commit_1
+  |/
+  o  [draft] commit_root
+  
+  
 
 Explicit request for zstd on non-generaldelta repos
 
   $ hg --config format.usegeneraldelta=false init nogd
   $ hg -q -R nogd pull t1
   $ hg -R nogd bundle -a -t zstd nogd-zstd
-  1 changesets found
+  6 changesets found
 
 zstd-v1 always fails
 
-  $ hg -R tzstd bundle -a -t zstd-v1 zstd-v1
+  $ hg -R t1 bundle -a -t zstd-v1 zstd-v1
   abort: compression engine zstd is not supported on v1 bundles
   (see 'hg help bundlespec' for supported values for --type)
   [10]
@@ -243,26 +579,44 @@
 Test controlling the changegroup version
 
   $ hg -R t1 bundle --config experimental.changegroup3=yes -a -t v2 ./v2-cg-default.hg
-  1 changesets found
+  7 changesets found
   $ hg debugbundle ./v2-cg-default.hg --part-type changegroup
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   $ hg debugbundle ./v2-cg-default.hg --spec
   bzip2-v2
   $ hg -R t1 bundle --config experimental.changegroup3=yes -a -t 'v2;cg.version=02' ./v2-cg-02.hg
-  1 changesets found
+  7 changesets found
   $ hg debugbundle ./v2-cg-02.hg --part-type changegroup
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 02} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   $ hg debugbundle ./v2-cg-02.hg --spec
   bzip2-v2
   $ hg -R t1 bundle --config experimental.changegroup3=yes -a -t 'v2;cg.version=03' ./v2-cg-03.hg
-  1 changesets found
+  7 changesets found
   $ hg debugbundle ./v2-cg-03.hg --part-type changegroup
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 03} (mandatory: True)
-      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  changegroup -- {nbchanges: 7, version: 03} (mandatory: True)
+      ac39af4a9f7d2aaa7d244720e57838be9bf63b03
+      901e97fadc587978ec52f2fa76af4aefc2d191e8
+      a8c3a1ed30eb71f03f476c5fa7ead831ef991a55
+      66e2c4b43e0cf8f0bdff0733a0b97ce57874e35d
+      624e609639853fe22c88d42a8fd1f53a0e9b7ebe
+      2ea90778052ba7558fab36e3fd5d149512ff986b
+      b9f5f740a8cd76700020e3903ee55ecff78bd3e5
   $ hg debugbundle ./v2-cg-03.hg --spec
   bzip2-v2;cg.version=03
--- a/tests/test-check-code.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-check-code.t	Fri Apr 14 10:41:40 2023 +0200
@@ -57,6 +57,7 @@
   .arcconfig
   .clang-format
   .editorconfig
+  .gitattributes
   .hgignore
   .hgsigs
   .hgtags
--- a/tests/test-clone-stream.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-clone-stream.t	Fri Apr 14 10:41:40 2023 +0200
@@ -128,6 +128,7 @@
     changegroup
       01
       02
+      03
     checkheads
       related
     digests
@@ -157,7 +158,7 @@
   added 3 changesets with 1088 changes to 1088 files
   new changesets 96ee1d7354c4:5223b5e3265f
 
-  $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
+  $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
   200 Script output follows
   content-type: application/mercurial-0.2
   
@@ -195,6 +196,7 @@
     changegroup
       01
       02
+      03
     checkheads
       related
     digests
@@ -224,7 +226,7 @@
   added 3 changesets with 1088 changes to 1088 files
   new changesets 96ee1d7354c4:5223b5e3265f
 
-  $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
+  $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
   200 Script output follows
   content-type: application/mercurial-0.2
   
@@ -284,7 +286,7 @@
 
 getbundle requests with stream=1 are uncompressed
 
-  $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto '0.1 0.2 comp=zlib,none' --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
+  $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto '0.1 0.2 comp=zlib,none' --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
   200 Script output follows
   content-type: application/mercurial-0.2
   
--- a/tests/test-clonebundles.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-clonebundles.t	Fri Apr 14 10:41:40 2023 +0200
@@ -587,7 +587,7 @@
   adding file changes
   adding bar revisions
   adding foo revisions
-  bundle2-input-part: total payload size 920
+  bundle2-input-part: total payload size 936
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-part: "phase-heads" supported
   bundle2-input-part: total payload size 24
--- a/tests/test-commit-amend.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-commit-amend.t	Fri Apr 14 10:41:40 2023 +0200
@@ -121,15 +121,15 @@
   committing changelog
   1 changesets found
   uncompressed size of bundle content:
-       254 (changelog)
-       163 (manifests)
-       133  a
+       256 (changelog)
+       165 (manifests)
+       135  a
   saved backup bundle to $TESTTMP/repo/.hg/strip-backup/47343646fa3d-c2758885-amend.hg
   1 changesets found
   uncompressed size of bundle content:
-       250 (changelog)
-       163 (manifests)
-       133  a
+       252 (changelog)
+       165 (manifests)
+       135  a
   adding branch
   adding changesets
   adding manifests
@@ -265,15 +265,15 @@
   committing changelog
   1 changesets found
   uncompressed size of bundle content:
-       249 (changelog)
-       163 (manifests)
-       135  a
+       251 (changelog)
+       165 (manifests)
+       137  a
   saved backup bundle to $TESTTMP/repo/.hg/strip-backup/a9a13940fc03-7c2e8674-amend.hg
   1 changesets found
   uncompressed size of bundle content:
-       257 (changelog)
-       163 (manifests)
-       135  a
+       259 (changelog)
+       165 (manifests)
+       137  a
   adding branch
   adding changesets
   adding manifests
@@ -301,15 +301,15 @@
   committing changelog
   1 changesets found
   uncompressed size of bundle content:
-       257 (changelog)
-       163 (manifests)
-       135  a
+       259 (changelog)
+       165 (manifests)
+       137  a
   saved backup bundle to $TESTTMP/repo/.hg/strip-backup/64a124ba1b44-10374b8f-amend.hg
   1 changesets found
   uncompressed size of bundle content:
-       257 (changelog)
-       163 (manifests)
-       137  a
+       259 (changelog)
+       165 (manifests)
+       139  a
   adding branch
   adding changesets
   adding manifests
--- a/tests/test-completion.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-completion.t	Fri Apr 14 10:41:40 2023 +0200
@@ -78,6 +78,7 @@
   debug-repair-issue6528
   debug-revlog-index
   debug-revlog-stats
+  debug::stable-tail-sort
   debugancestor
   debugantivirusrunning
   debugapplystreamclonebundle
@@ -273,6 +274,7 @@
   debug-repair-issue6528: to-report, from-report, paranoid, dry-run
   debug-revlog-index: changelog, manifest, dir, template
   debug-revlog-stats: changelog, manifest, filelogs, template
+  debug::stable-tail-sort: template
   debugancestor: 
   debugantivirusrunning: 
   debugapplystreamclonebundle: 
--- a/tests/test-debugcommands.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-debugcommands.t	Fri Apr 14 10:41:40 2023 +0200
@@ -636,6 +636,7 @@
     changegroup
       01
       02
+      03
     checkheads
       related
     digests
@@ -673,7 +674,7 @@
   devel-peer-request:   pairs: 81 bytes
   sending hello command
   sending between command
-  remote: 468
+  remote: 473
   remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlog-compression-zstd,revlogv1,sparserevlog unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
   remote: 1
   devel-peer-request: protocaps
@@ -693,7 +694,7 @@
   devel-peer-request:   pairs: 81 bytes
   sending hello command
   sending between command
-  remote: 468
+  remote: 473
   remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlog-compression-zstd,revlogv1,sparserevlog unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
   remote: 1
   devel-peer-request: protocaps
@@ -713,7 +714,7 @@
   devel-peer-request:   pairs: 81 bytes
   sending hello command
   sending between command
-  remote: 444
+  remote: 449
   remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1,sparserevlog unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
   remote: 1
   devel-peer-request: protocaps
--- a/tests/test-generaldelta.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-generaldelta.t	Fri Apr 14 10:41:40 2023 +0200
@@ -163,7 +163,7 @@
   saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg
   $ hg debugbundle .hg/strip-backup/*
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
+  changegroup -- {nbchanges: 1, version: 03} (mandatory: True)
       1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)
--- a/tests/test-help.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-help.t	Fri Apr 14 10:41:40 2023 +0200
@@ -987,6 +987,8 @@
                  dump index data for a revlog
    debug-revlog-stats
                  display statistics about revlogs in the store
+   debug::stable-tail-sort
+                 display the stable-tail sort of the ancestors of a given node
    debugancestor
                  find the ancestor revision of two revisions in a given index
    debugantivirusrunning
--- a/tests/test-hgweb-json.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-hgweb-json.t	Fri Apr 14 10:41:40 2023 +0200
@@ -777,6 +777,7 @@
   {
     "bookmarks": [],
     "branch": "default",
+    "children": [],
     "date": [
       0.0,
       0
@@ -809,6 +810,9 @@
   {
     "bookmarks": [],
     "branch": "default",
+    "children": [
+      "93a8ce14f89156426b7fa981af8042da53f03aa0"
+    ],
     "date": [
       0.0,
       0
@@ -897,6 +901,9 @@
       "bookmark1"
     ],
     "branch": "default",
+    "children": [
+      "78896eb0e102174ce9278438a95e12543e4367a7"
+    ],
     "date": [
       0.0,
       0
@@ -957,6 +964,9 @@
   {
     "bookmarks": [],
     "branch": "test-branch",
+    "children": [
+      "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
+    ],
     "date": [
       0.0,
       0
--- a/tests/test-hook.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-hook.t	Fri Apr 14 10:41:40 2023 +0200
@@ -985,13 +985,11 @@
   Traceback (most recent call last):
   SyntaxError: * (glob)
   Traceback (most recent call last):
-  ImportError: No module named 'hgext_syntaxerror' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
+  ModuleNotFoundError: No module named 'hgext_syntaxerror'
   Traceback (most recent call last):
   SyntaxError: * (glob)
   Traceback (most recent call last):
-  ImportError: No module named 'hgext_syntaxerror' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
+  ModuleNotFoundError: No module named 'hgext_syntaxerror'
   Traceback (most recent call last):
       raise error.HookLoadError( (py38 !)
   mercurial.error.HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
@@ -1147,21 +1145,16 @@
   $ hg --traceback commit -ma 2>&1 | egrep '^exception|ImportError|ModuleNotFoundError|Traceback|HookLoadError|abort'
   exception from first failed import attempt:
   Traceback (most recent call last):
-  ImportError: No module named 'somebogusmodule' (no-py36 !)
-  ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
+  ModuleNotFoundError: No module named 'somebogusmodule'
   exception from second failed import attempt:
   Traceback (most recent call last):
-  ImportError: No module named 'somebogusmodule' (no-py36 !)
-  ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
+  ModuleNotFoundError: No module named 'somebogusmodule'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext_importfail' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
+  ModuleNotFoundError: No module named 'hgext_importfail'
   Traceback (most recent call last):
-  ImportError: No module named 'somebogusmodule' (no-py36 !)
-  ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
+  ModuleNotFoundError: No module named 'somebogusmodule'
   Traceback (most recent call last):
-  ImportError: No module named 'hgext_importfail' (no-py36 !)
-  ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
+  ModuleNotFoundError: No module named 'hgext_importfail'
   Traceback (most recent call last):
       raise error.HookLoadError( (py38 !)
   mercurial.error.HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
--- a/tests/test-http-bad-server.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-http-bad-server.t	Fri Apr 14 10:41:40 2023 +0200
@@ -130,10 +130,8 @@
   readline(*) -> (*) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
   readline(*) -> (1?) Accept-Encoding* (glob)
   read limit reached; closing socket
@@ -153,7 +151,7 @@
 
   $ hg serve \
   > --config badserver.close-after-recv-patterns="GET /\?cmd=batch,user-agent: mercurial/proto-1.0,GET /\?cmd=getbundle" \
-  > --config badserver.close-after-recv-bytes=110,26,274 \
+  > --config badserver.close-after-recv-bytes=110,26,281 \
   > -p $HGPORT -d --pid-file=hg.pid -E error.log
   $ cat hg.pid > $DAEMON_PIDS
   $ hg clone http://localhost:$HGPORT/ clone
@@ -172,10 +170,8 @@
   readline(*) -> (*) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
@@ -191,16 +187,14 @@
   readline(*) -> (*) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (py36 !)
-  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (py36 !)
-  write(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (no-py36 !)
-  write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (no-py36 !)
+  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n
+  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
   readline(24 from ~) -> (*) GET /?cmd=getbundle HTTP* (glob)
   read limit reached; closing socket
   readline(~) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
-  readline(274 from *) -> (27) Accept-Encoding: identity\r\n (glob)
-  readline(247 from *) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
-  readline(218 from *) -> (218) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtag (glob)
+  readline(281 from *) -> (27) Accept-Encoding: identity\r\n (glob)
+  readline(254 from *) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
+  readline(225 from *) -> (225) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtag (glob)
   read limit reached; closing socket
 
   $ rm -f error.log
@@ -228,10 +222,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx httppostargs known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx httppostargs known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx httppostargs known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (27) POST /?cmd=batch HTTP/1.1\r\n (glob)
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (41) content-type: application/mercurial-0.1\r\n (glob)
@@ -256,7 +248,6 @@
   Traceback (most recent call last):
   Exception: connection closed after receiving N bytes
   
-  write(126) -> HTTP/1.1 500 Internal Server Error\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nTransfer-Encoding: chunked\r\n\r\n (no-py36 !)
 
   $ rm -f error.log
 
@@ -282,14 +273,12 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(1 from 160) -> (0) H (py36 !)
-  write(1 from 160) -> (0) H (no-py36 !)
+  sendall(1 from 160) -> (0) H
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=capabilities': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-  write(286) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\nHTTP/1.1 500 Internal Server Error\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nTransfer-Encoding: chunked\r\n\r\n (glob) (no-py36 !)
 
   $ rm -f error.log
 
@@ -315,10 +304,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(20 from *) -> (0) batch branchmap bund (glob) (py36 !)
-  write(160) -> (20) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(20 from *) -> (0) batch branchmap bund (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(20 from *) -> (0) batch branchmap bund (glob)
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=capabilities': (glob)
   Traceback (most recent call last):
@@ -354,10 +341,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> (568) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
@@ -367,14 +352,12 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(118 from 159) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: applicat (py36 !)
-  write(118 from 159) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: applicat (no-py36 !)
+  sendall(118 from 159) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: applicat
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=batch': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-  write(285) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\nHTTP/1.1 500 Internal Server Error\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nTransfer-Encoding: chunked\r\n\r\n (no-py36 !)
 
   $ rm -f error.log
 
@@ -400,10 +383,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
@@ -413,10 +394,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (py36 !)
-  sendall(24 from 42) -> (0) 96ee1d7354c4ad7372047672 (py36 !)
-  write(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (no-py36 !)
-  write(24 from 42) -> (0) 96ee1d7354c4ad7372047672 (no-py36 !)
+  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n
+  sendall(24 from 42) -> (0) 96ee1d7354c4ad7372047672
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=batch': (glob)
   Traceback (most recent call last):
@@ -453,10 +432,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
@@ -466,27 +443,23 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (py36 !)
-  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (py36 !)
-  write(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (no-py36 !)
-  write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (no-py36 !)
+  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n
+  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
   readline(~) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
-  readline(*) -> (440) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n (glob)
+  readline(*) -> (447) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n (glob)
   readline(*) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n (glob)
   readline(*) -> (35) accept: application/mercurial-0.1\r\n (glob)
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(129 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercuri (py36 !)
-  write(129 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercuri (no-py36 !)
+  sendall(129 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercuri
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-  write(293) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\nHTTP/1.1 500 Internal Server Error\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nTransfer-Encoding: chunked\r\n\r\n (no-py36 !)
 
   $ rm -f error.log
 
@@ -505,7 +478,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -6
   sendall(162 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunke
   write limit reached; closing socket
@@ -513,19 +485,6 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -7
-  write(41) -> Content-Type: application/mercurial-0.2\r\n
-  write(25 from 28) -> (0) Transfer-Encoding: chunke
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  write(293) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\nHTTP/1.1 500 Internal Server Error\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nTransfer-Encoding: chunked\r\n\r\n
-  
-#endif
-
   $ rm -f error.log
 
 Server sends empty HTTP body for getbundle
@@ -551,10 +510,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
@@ -564,27 +521,23 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (py36 !)
-  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (py36 !)
-  write(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (no-py36 !)
-  write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (no-py36 !)
+  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n
+  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
   readline(~) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
-  readline(*) -> (440) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n (glob)
+  readline(*) -> (447) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n (glob)
   readline(*) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n (glob)
   readline(*) -> (35) accept: application/mercurial-0.1\r\n (glob)
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(167 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n (py36 !)
-  write(167 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n (no-py36 !)
+  sendall(167 from 167) -> (0) HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-  write(293) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\nHTTP/1.1 500 Internal Server Error\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nTransfer-Encoding: chunked\r\n\r\n (no-py36 !)
 
   $ rm -f error.log
 
@@ -611,10 +564,8 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (py36 !)
-  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (py36 !)
-  write(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob) (no-py36 !)
-  write(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) (no-py36 !)
+  sendall(160) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: *\r\n\r\n (glob)
+  sendall(*) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=* unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob)
   readline(~) -> (26) GET /?cmd=batch HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
@@ -624,23 +575,21 @@
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (py36 !)
-  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n; (py36 !)
-  write(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n (no-py36 !)
+  sendall(159) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.1\r\nContent-Length: 42\r\n\r\n
+  sendall(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
   readline(~) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
   readline(*) -> (27) Accept-Encoding: identity\r\n (glob)
   readline(*) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
-  readline(*) -> (440) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n (glob)
+  readline(*) -> (447) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n (glob)
   readline(*) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n (glob)
   readline(*) -> (35) accept: application/mercurial-0.1\r\n (glob)
   readline(*) -> (2?) host: localhost:$HGPORT\r\n (glob)
   readline(*) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
   readline(*) -> (2) \r\n (glob)
-  sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n (py36 !)
-  sendall(6) -> 1\\r\\n\x04\\r\\n (esc) (py36 !)
-  sendall(9) -> 4\r\nnone\r\n (py36 !)
-  sendall(9 from 9) -> (0) 4\r\nHG20\r\n (py36 !)
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n (no-py36 !)
+  sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
+  sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
+  sendall(9) -> 4\r\nnone\r\n
+  sendall(9 from 9) -> (0) 4\r\nHG20\r\n
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
   Traceback (most recent call last):
@@ -665,7 +614,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -9
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -676,21 +624,6 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -11
-  readline(~) -> (2) \r\n
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(6 from 9) -> (0) 4\r\nHG2
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server sends incomplete bundle2 stream params length
@@ -709,7 +642,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -10
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -721,23 +653,6 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -12
-  readline(~) -> (2) \r\n
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(41) -> Content-Type: application/mercurial-0.2\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(6 from 9) -> (0) 4\\r\\n\x00\x00\x00 (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Servers stops after bundle2 stream params header
@@ -756,7 +671,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -10
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -768,23 +682,6 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -12
-  readline(~) -> (2) \r\n
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(41) -> Content-Type: application/mercurial-0.2\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server stops sending after bundle2 part header length
@@ -803,7 +700,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -11
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -816,32 +712,13 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -13
-  readline(~) -> (2) \r\n
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(41) -> Content-Type: application/mercurial-0.2\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server stops sending after bundle2 part header
 ----------------------------------------------
 
   $ hg serve \
-  > --config badserver.close-after-send-patterns="version02nbchanges1\\r\\n" \
+  > --config badserver.close-after-send-patterns="version03nbchanges1\\r\\n" \
   > -p $HGPORT -d --pid-file=hg.pid -E error.log
   $ cat hg.pid > $DAEMON_PIDS
 
@@ -856,7 +733,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -12
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -864,38 +740,19 @@
   sendall(9) -> 4\r\nHG20\r\n
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  sendall(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
+  sendall(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version03nbchanges1\\r\\n (esc)
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -14
-  readline(~) -> (2) \r\n
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(41) -> Content-Type: application/mercurial-0.2\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server stops after bundle2 part payload chunk size
 --------------------------------------------------
 
   $ hg serve \
-  > --config badserver.close-after-send-patterns='1d2\r\n.......' \
+  > --config badserver.close-after-send-patterns='1dc\r\n.......' \
   > -p $HGPORT -d --pid-file=hg.pid -E error.log
   $ cat hg.pid > $DAEMON_PIDS
 
@@ -910,7 +767,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -14
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -918,41 +774,21 @@
   sendall(9) -> 4\r\nHG20\r\n
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  sendall(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  sendall(12 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1d (esc)
+  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version03nbchanges1\\r\\n (esc)
+  sendall(9) -> 4\\r\\n\x00\x00\x01\xdc\\r\\n (esc)
+  sendall(12 from 483) -> (0) 1dc\\r\\n\x00\x00\x00\xb4\x96\xee\x1d (esc)
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -15
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(28) -> Transfer-Encoding: chunked\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  write(12 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1d (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server stops sending in middle of bundle2 payload chunk
 -------------------------------------------------------
 
   $ hg serve \
-  > --config badserver.close-after-send-patterns=':jL\0\0\x00\0\0\0\0\0\r\n' \
+  > --config badserver.close-after-send-patterns=':jL\0\0\x00\0\0\0\0\0\0\0\r\n' \
   > -p $HGPORT -d --pid-file=hg.pid -E error.log
   $ cat hg.pid > $DAEMON_PIDS
 
@@ -967,7 +803,6 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -14
   sendall(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
@@ -975,35 +810,14 @@
   sendall(9) -> 4\r\nHG20\r\n
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  sendall(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  sendall(473 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
+  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version03nbchanges1\\r\\n (esc)
+  sendall(9) -> 4\\r\\n\x00\x00\x01\xdc\\r\\n (esc)
+  sendall(483 from 483) -> (0) 1dc\\r\\n\x00\x00\x00\xb4\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa3j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00j\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
   write limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -16
-  readline(~) -> (2) \r\n
-  write(167) -> HTTP/1.1 200 Script output follows\r\nServer: badhttpserver\r\nDate: $HTTP_DATE$\r\nContent-Type: application/mercurial-0.2\r\nTransfer-Encoding: chunked\r\n\r\n
-  write(41) -> Content-Type: application/mercurial-0.2\r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  write(473 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server stops sending after 0 length payload chunk size
@@ -1027,16 +841,15 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -16
   sendall(6) -> 1\\r\\n\x04\\r\\n (esc)
   sendall(9) -> 4\r\nnone\r\n
   sendall(9) -> 4\r\nHG20\r\n
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  sendall(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  sendall(473) -> 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
+  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version03nbchanges1\\r\\n (esc)
+  sendall(9) -> 4\\r\\n\x00\x00\x01\xdc\\r\\n (esc)
+  sendall(483) -> 1dc\\r\\n\x00\x00\x00\xb4\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa3j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00j\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00 \\r\\n (esc)
   sendall(13 from 38) -> (0) 20\\r\\n\x08LISTKEYS (esc)
@@ -1045,28 +858,6 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -17
-  write(2) -> \r\n
-  write(6) -> 1\\r\\n\x04\\r\\n (esc)
-  write(9) -> 4\r\nnone\r\n
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  write(473) -> 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00 \\r\\n (esc)
-  write(13 from 38) -> (0) 20\\r\\n\x08LISTKEYS (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
 
 Server stops sending after 0 part bundle part header (indicating end of bundle2 payload)
@@ -1091,13 +882,12 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -20
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  sendall(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  sendall(473) -> 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
+  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version03nbchanges1\\r\\n (esc)
+  sendall(9) -> 4\\r\\n\x00\x00\x01\xdc\\r\\n (esc)
+  sendall(483) -> 1dc\\r\\n\x00\x00\x00\xb4\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa3j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00j\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00 \\r\\n (esc)
   sendall(38) -> 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00	\x06namespacephases\\r\\n (esc)
@@ -1113,32 +903,6 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -21
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  write(473) -> 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00 \\r\\n (esc)
-  write(38) -> 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00	\x06namespacephases\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00:\\r\\n (esc)
-  write(64) -> 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c	1\npublishing	True\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00#\\r\\n (esc)
-  write(41) -> 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00		namespacebookmarks\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
   $ rm -rf clone
 
@@ -1162,13 +926,12 @@
 
   $ killdaemons.py $DAEMON_PIDS
 
-#if py36
   $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -21
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  sendall(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  sendall(473) -> 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
+  sendall(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version03nbchanges1\\r\\n (esc)
+  sendall(9) -> 4\\r\\n\x00\x00\x01\xdc\\r\\n (esc)
+  sendall(483) -> 1dc\\r\\n\x00\x00\x00\xb4\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa3j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00j\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
   sendall(9) -> 4\\r\\n\x00\x00\x00 \\r\\n (esc)
   sendall(38) -> 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00	\x06namespacephases\\r\\n (esc)
@@ -1185,32 +948,5 @@
   Traceback (most recent call last):
   Exception: connection closed after sending N bytes
   
-
-#else
-  $ "$PYTHON" $TESTDIR/filtertraceback.py < error.log | tail -22
-  write(9) -> 4\r\nHG20\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00)\\r\\n (esc)
-  write(47) -> 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02	\x01version02nbchanges1\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
-  write(473) -> 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00 \\r\\n (esc)
-  write(38) -> 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00	\x06namespacephases\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00:\\r\\n (esc)
-  write(64) -> 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c	1\npublishing	True\r\n
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00#\\r\\n (esc)
-  write(41) -> 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00		namespacebookmarks\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(9) -> 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
-  write(3 from 5) -> (0) 0\r\n
-  write limit reached; closing socket
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=getbundle': (glob)
-  Traceback (most recent call last):
-  Exception: connection closed after sending N bytes
-  
-#endif
-
   $ rm -f error.log
   $ rm -rf clone
--- a/tests/test-http.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-http.t	Fri Apr 14 10:41:40 2023 +0200
@@ -341,20 +341,20 @@
   list of changesets:
   7f4e523d01f2cc3765ac8934da3d14db775ff872
   bundle2-output-bundle: "HG20", 5 parts total
-  bundle2-output-part: "replycaps" 207 bytes payload
+  bundle2-output-part: "replycaps" 210 bytes payload
   bundle2-output-part: "check:phases" 24 bytes payload
   bundle2-output-part: "check:updated-heads" streamed payload
   bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
   bundle2-output-part: "phase-heads" 24 bytes payload
   sending unbundle command
-  sending 1023 bytes
+  sending 1036 bytes
   devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
-  devel-peer-request:   Content-length 1023
+  devel-peer-request:   Content-length 1036
   devel-peer-request:   Content-type application/mercurial-0.1
   devel-peer-request:   Vary X-HgArg-1,X-HgProto-1
   devel-peer-request:   X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
   devel-peer-request:   16 bytes of commands arguments in headers
-  devel-peer-request:   1023 bytes of data
+  devel-peer-request:   1036 bytes of data
   devel-peer-request:   finished in *.???? seconds (200) (glob)
   bundle2-input-bundle: no-transaction
   bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
--- a/tests/test-largefiles.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-largefiles.t	Fri Apr 14 10:41:40 2023 +0200
@@ -1114,16 +1114,16 @@
   all local changesets known remotely
   6 changesets found
   uncompressed size of bundle content:
-      1389 (changelog)
-      1698 (manifests)
-       254  .hglf/large1
-       564  .hglf/large3
-       572  .hglf/sub/large4
-       182  .hglf/sub2/large6
-       182  .hglf/sub2/large7
-       212  normal1
-       457  normal3
-       465  sub/normal4
+      1401 (changelog)
+      1710 (manifests)
+       256  .hglf/large1
+       570  .hglf/large3
+       578  .hglf/sub/large4
+       184  .hglf/sub2/large6
+       184  .hglf/sub2/large7
+       214  normal1
+       463  normal3
+       471  sub/normal4
   adding changesets
   adding manifests
   adding file changes
--- a/tests/test-lfs-serve-access.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-lfs-serve-access.t	Fri Apr 14 10:41:40 2023 +0200
@@ -66,7 +66,7 @@
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 400 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
-  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Acheckheads%253Drelated%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 400 - (glob)
 
   $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
@@ -165,7 +165,7 @@
   $LOCALIP - - [$LOGDATE$] "POST /missing/objects/batch HTTP/1.1" 404 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=capabilities HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
-  $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Acheckheads%253Drelated%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
   $LOCALIP - - [$LOGDATE$] "POST /subdir/mount/point/.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point/.hg/lfs/objects/f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e HTTP/1.1" 200 - (glob)
 
@@ -311,7 +311,7 @@
   $ cat $TESTTMP/access.log
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
-  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Acheckheads%253Drelated%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
@@ -330,7 +330,7 @@
   $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c HTTP/1.1" 422 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
-  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Acheckheads%253Drelated%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=525251863cad618e55d483555f3d00a2ca99597e&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=525251863cad618e55d483555f3d00a2ca99597e&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob)
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
@@ -487,7 +487,7 @@
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 401 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
-  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Acheckheads%253Drelated%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 401 - (glob)
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 200 - (glob)
--- a/tests/test-lfs-serve.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-lfs-serve.t	Fri Apr 14 10:41:40 2023 +0200
@@ -308,9 +308,14 @@
   $ hg -R $TESTTMP/client4_pull pull http://localhost:$HGPORT
   pulling from http://localhost:$HGPORT/
   requesting all changes
-  remote: abort: no common changegroup version
-  abort: pull failed on remote
-  [100]
+  adding changesets
+  adding manifests
+  adding file changes
+  transaction abort!
+  rollback completed
+  abort: missing processor for flag '0x2000'
+  (the lfs extension must be enabled)
+  [50]
   $ hg debugrequires -R $TESTTMP/client4_pull/ | grep 'lfs'
   [1]
   $ hg debugrequires -R $SERVER_PATH --config extensions.lfs= | grep 'lfs'
--- a/tests/test-obsolete-changeset-exchange.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-obsolete-changeset-exchange.t	Fri Apr 14 10:41:40 2023 +0200
@@ -164,7 +164,7 @@
   adding manifests
   adding file changes
   adding foo revisions
-  bundle2-input-part: total payload size 476
+  bundle2-input-part: total payload size 486
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-part: "phase-heads" supported
   bundle2-input-part: total payload size 24
--- a/tests/test-obsolete-distributed.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-obsolete-distributed.t	Fri Apr 14 10:41:40 2023 +0200
@@ -163,7 +163,7 @@
   adding manifests
   adding file changes
   adding c_B1 revisions
-  bundle2-input-part: total payload size 485
+  bundle2-input-part: total payload size 495
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-part: "obsmarkers" supported
   bundle2-input-part: total payload size 143
--- a/tests/test-obsolete.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-obsolete.t	Fri Apr 14 10:41:40 2023 +0200
@@ -1600,7 +1600,7 @@
   
   $ hg debugbundle .hg/strip-backup/e008cf283490-*-backup.hg
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
+  changegroup -- {nbchanges: 1, version: 03} (mandatory: True)
       e008cf2834908e5d6b0f792a9d4b0e2272260fb8
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)
@@ -1643,7 +1643,7 @@
   
   $ hg debugbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 2, version: 02} (mandatory: True)
+  changegroup -- {nbchanges: 2, version: 03} (mandatory: True)
       e016b03fd86fcccc54817d120b90b751aaf367d6
       b0551702f918510f01ae838ab03a463054c67b46
   cache:rev-branch-cache -- {} (mandatory: False)
--- a/tests/test-phase-archived.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-phase-archived.t	Fri Apr 14 10:41:40 2023 +0200
@@ -141,3 +141,58 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     root
   
+
+Test that a strip will preserve unrelated changeset archived
+------------------------------------------------------------
+
+prepare a suitable tree
+
+  $ echo foo > bar
+  $ hg add bar
+  $ hg commit -m 'some more commit'
+  $ hg log -G --hidden -T '{rev} {node|short} [{phase}] {desc|firstline}\n'
+  @  3 f90bf4e57854 [draft] some more commit
+  |
+  o  2 d1e73e428f29 [draft] unbundletesting
+  |
+  | o  1 883aadbbf309 [draft] unbundletesting
+  |/
+  o  0 c1863a3840c6 [draft] root
+  
+  $ hg strip --soft --rev '.'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/f90bf4e57854-56b37ff2-backup.hg
+  $ hg log -G --hidden -T '{rev} {node|short} [{phase}] {desc|firstline}\n'
+  o  3 f90bf4e57854 [archived] some more commit
+  |
+  @  2 d1e73e428f29 [draft] unbundletesting
+  |
+  | o  1 883aadbbf309 [draft] unbundletesting
+  |/
+  o  0 c1863a3840c6 [draft] root
+  
+
+
+Strips the other (lower rev-num) head
+
+  $ hg strip --rev 'min(head() and not .)'
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/883aadbbf309-efc55adc-backup.hg
+
+The archived changeset should still be hidden
+
+  $ hg log -G -T '{rev} {node|short} [{phase}] {desc|firstline}\n'
+  @  1 d1e73e428f29 [draft] unbundletesting
+  |
+  o  0 c1863a3840c6 [draft] root
+  
+
+It may still be around:
+
+  $ hg log --hidden -G -T '{rev} {node|short} [{phase}] {desc|firstline}\n'
+  o  2 f90bf4e57854 [archived] some more commit
+  |
+  @  1 d1e73e428f29 [draft] unbundletesting
+  |
+  o  0 c1863a3840c6 [draft] root
+  
+
--- a/tests/test-phases-exchange.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-phases-exchange.t	Fri Apr 14 10:41:40 2023 +0200
@@ -852,9 +852,9 @@
   searching for changes
   1 changesets found
   uncompressed size of bundle content:
-       178 (changelog)
-       165 (manifests)
-       131  a-H
+       180 (changelog)
+       167 (manifests)
+       133  a-H
   adding changesets
   adding manifests
   adding file changes
--- a/tests/test-push-warn.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-push-warn.t	Fri Apr 14 10:41:40 2023 +0200
@@ -151,9 +151,9 @@
   searching for changes
   2 changesets found
   uncompressed size of bundle content:
-       352 (changelog)
-       326 (manifests)
-       25\d  foo (re)
+       356 (changelog)
+       330 (manifests)
+       261  foo
   adding changesets
   adding manifests
   adding file changes
--- a/tests/test-rebase-conflicts.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-rebase-conflicts.t	Fri Apr 14 10:41:40 2023 +0200
@@ -296,9 +296,8 @@
   bundle2-output-part: "cache:rev-branch-cache" (advisory) streamed payload
   bundle2-output-part: "phase-heads" 24 bytes payload
   saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-rebase.hg
-  3 changesets found
+  2 changesets found
   list of changesets:
-  4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
   19c888675e133ab5dff84516926a65672eaf04d9
   c1ffa3b5274e92a9388fe782854e295d2e8d0443
   bundle2-output-bundle: "HG20", 3 parts total
@@ -309,15 +308,14 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
   adding changesets
-  add changeset 4c9fbe56a16f
   add changeset 19c888675e13
   add changeset c1ffa3b5274e
   adding manifests
   adding file changes
   adding f1.txt revisions
-  bundle2-input-part: total payload size 1739
+  bundle2-input-part: total payload size 1255
   bundle2-input-part: "cache:rev-branch-cache" (advisory) supported
-  bundle2-input-part: total payload size 74
+  bundle2-input-part: total payload size 54
   bundle2-input-part: "phase-heads" supported
   bundle2-input-part: total payload size 24
   bundle2-input-bundle: 3 parts total
--- a/tests/test-rebase-mq-skip.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-rebase-mq-skip.t	Fri Apr 14 10:41:40 2023 +0200
@@ -75,17 +75,17 @@
   $TESTTMP/a/.hg/patches/p0.patch
   2 changesets found
   uncompressed size of bundle content:
-       348 (changelog)
-       324 (manifests)
-       129  p0
-       129  p1
+       352 (changelog)
+       328 (manifests)
+       131  p0
+       131  p1
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/13a46ce44f60-5da6ecfb-rebase.hg
   2 changesets found
   uncompressed size of bundle content:
-       403 (changelog)
-       324 (manifests)
-       129  p0
-       129  p1
+       407 (changelog)
+       328 (manifests)
+       131  p0
+       131  p1
   adding branch
   adding changesets
   adding manifests
--- a/tests/test-rebase-newancestor.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-rebase-newancestor.t	Fri Apr 14 10:41:40 2023 +0200
@@ -263,15 +263,15 @@
   rebase merging completed
   1 changesets found
   uncompressed size of bundle content:
-       199 (changelog)
-       216 (manifests)
-       182  other
+       201 (changelog)
+       218 (manifests)
+       184  other
   saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/4c5f12f25ebe-f46990e5-rebase.hg
   1 changesets found
   uncompressed size of bundle content:
-       254 (changelog)
-       167 (manifests)
-       182  other
+       256 (changelog)
+       169 (manifests)
+       184  other
   adding branch
   adding changesets
   adding manifests
--- a/tests/test-ssh.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-ssh.t	Fri Apr 14 10:41:40 2023 +0200
@@ -529,7 +529,7 @@
   no changes found
   devel-peer-request: getbundle
   devel-peer-request:   bookmarks: 1 bytes
-  devel-peer-request:   bundlecaps: 270 bytes
+  devel-peer-request:   bundlecaps: 275 bytes
   devel-peer-request:   cg: 1 bytes
   devel-peer-request:   common: 122 bytes
   devel-peer-request:   heads: 122 bytes
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-stabletailgraph.t	Fri Apr 14 10:41:40 2023 +0200
@@ -0,0 +1,710 @@
+====================================
+Test for the stabletailgraph package
+====================================
+
+This test file contains a bunch of small test graphs with some minimal yet
+non-trivial structure, on which the various stable-tail graph and stable-tail
+sort functions are tested.
+
+Each case consists of the creation of the interesting graph structure, followed
+by a check, for each noteworthy node, of:
+- the stable-tail sort output.
+
+In the ASCII art of the diagrams, the side of the exclusive part which is
+followed in priority is denoted with "<" or ">" if it is on the left or right
+respectively.
+
+The intermediary linear parts in the example graph are there to force the
+exclusive part choice (made on a min rank condition).
+
+
+Setup
+=====
+
+Enable the rank computation to test sorting based on the rank.
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > exp-use-changelog-v2=enable-unstable-format-and-corrupt-my-data
+  > 
+  > [alias]
+  > test-sts = debug::stable-tail-sort -T '{tags},'
+  > test-log = log --graph -T '{tags} rank={_fast_rank}'
+  > EOF
+
+
+Example 1: single merge node
+============================
+
+A base case with one branchpoint "b" and one merge node "e".
+
+The exclusive part, starting with the lowest-ranking parent "c" of "e",
+appears first in stable-tail sort of "e" and "f".
+
+#        f
+#        |
+#        e
+#        |
+#      --<--
+#      |   |
+#      c   d
+#      |   |
+#      --+--      <- at this point, the sort of "e" is done consuming its
+#        |           exclusive part [c] and jumps back to its other parent "d"
+#        b
+#        |
+#        a
+
+  $ hg init example-1
+  $ cd example-1
+  $ hg debugbuilddag '.:a*a:b*b:c<b+2:d*c/d:e*e:f.'
+  $ hg test-log
+  o  tip rank=8
+  |
+  o  f rank=7
+  |
+  o    e rank=6
+  |\
+  | o  d rank=4
+  | |
+  | o   rank=3
+  | |
+  o |  c rank=3
+  |/
+  o  b rank=2
+  |
+  o  a rank=1
+  
+
+Check the sort of the base linear case.
+
+  $ hg test-sts c
+  c,b,a, (no-eol)
+
+Check the stable-tail sort of "e": "c" should come before "d".
+
+  $ hg test-sts e
+  e,c,d,*,b,a, (no-eol) (glob)
+
+Check that the linear descendant of the merge inherits its sort properly.
+
+  $ hg test-sts f
+  f,e,c,d,*,b,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 2: nested exclusive parts, without specific leap
+========================================================
+
+"g" is a merge node whose exclusive part contains a merge node "e".
+We check that the stable-tail sort recurses properly by delegating.
+
+Notice that parts of the sort of "e" is an infix of the sort of "g".
+This is an expected property of the sort.
+
+#           g
+#           |
+#        ---<---
+#        |     |
+#        e     |    <- while processing the sort in the exclusive part of "g"
+#        |     |       we recursively process the exclusive part of "e"
+#      --<--   f
+#      |   |   |
+#      c   d   |
+#      |   |   |
+#      --+--   |
+#        |     |
+#        b     |
+#        |     |
+#        ---+---    <- done with excl(g), jump to "f"
+#           |
+#           a
+
+  $ hg init example-2
+  $ cd example-2
+  $ hg debugbuilddag '.:a*a:b*b:c<b+2:d*c/d:e<a+6:f*e/f:g.'
+  $ hg test-log
+  o  tip rank=14
+  |
+  o    g rank=13
+  |\
+  | o  f rank=7
+  | |
+  | o   rank=6
+  | |
+  | o   rank=5
+  | |
+  | o   rank=4
+  | |
+  | o   rank=3
+  | |
+  | o   rank=2
+  | |
+  o |    e rank=6
+  |\ \
+  | o |  d rank=4
+  | | |
+  | o |   rank=3
+  | | |
+  o | |  c rank=3
+  |/ /
+  o /  b rank=2
+  |/
+  o  a rank=1
+  
+Display the sort of "e" for reference
+
+  $ hg test-sts e
+  e,c,d,*,b,a, (no-eol) (glob)
+
+Check the correctness of the sort of "g",
+and that a part of the sort of "e" appears as an infix.
+
+  $ hg test-sts g
+  g,e,c,d,*,b,f,*,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 3: shadowing of a final leap
+====================================
+
+We have a merge "f" whose exclusive part contains a merge "d".
+
+The inherited parent of "d" is not in the exclusive part of "f".
+At the end of the exclusive part of "d",
+the leap to "c" is shadowed by the leap to "e", i.e. the inherited part to "f".
+
+Notice that emitting "c" before "e" would break the reverse topological
+ordering.
+
+#           f
+#           |
+#        ---<---
+#        |     |
+#        d     |
+#        |     e
+#      --<--   |
+#      |   |   |
+#      |   +----
+#      b   |
+#      |   c
+#      |   |
+#      --+--       <- at this point, jumping to "e", not the shadowed "c"
+#        |
+#        a
+
+  $ hg init example-3
+  $ cd example-3
+  $ hg debugbuilddag '.:a*a:b<a+2:c*b/c:d<c+3:e*d/e:f.'
+  $ hg test-log
+  o  tip rank=10
+  |
+  o    f rank=9
+  |\
+  | o  e rank=6
+  | |
+  | o   rank=5
+  | |
+  | o   rank=4
+  | |
+  o |  d rank=5
+  |\|
+  | o  c rank=3
+  | |
+  | o   rank=2
+  | |
+  o |  b rank=2
+  |/
+  o  a rank=1
+  
+
+Display the sort of "d" for reference:
+
+  $ hg test-sts d
+  d,b,c,*,a, (no-eol) (glob)
+
+Check that we leap from "b" directly to "e" (shadowing the leap to "c"),
+and that "c" is then emitted after "e" (its descendant).
+
+  $ hg test-sts f
+  f,d,b,e,*,c,*,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 4: skipping over nested exclusive part (entirely)
+=========================================================
+
+We have a merge "f" whose exclusive part contains a merge "d".
+
+The exclusive part of "d" is not in the exclusive part of "f".
+However, some of the inherited part of "d" is part of the exclusive part of "f"
+and needs to be iterated over before leaping to the inherited part of "f".
+
+The sort of "d" is partially reused for the ordering of the exclusive part of
+"f". However the reused part is not contiguous in the sort of "d".
+
+#           f
+#           |
+#        ---<---
+#        |     |
+#        d     |
+#        |     e
+#      -->--   |    <- in the sort of "f", we need to skip "c" and leap to the
+#      |   |   |       inherited part of "d"
+#      |   +----
+#      b   |
+#      |   c
+#      |   |
+#      --+--
+#        |
+#        a
+
+  $ hg init example-4
+  $ cd example-4
+  $ hg debugbuilddag '.:a*a+1:b<a+1:c*b/c:d<c+4:e*d/e:f.'
+  $ hg test-log
+  o  tip rank=11
+  |
+  o    f rank=10
+  |\
+  | o  e rank=6
+  | |
+  | o   rank=5
+  | |
+  | o   rank=4
+  | |
+  | o   rank=3
+  | |
+  o |  d rank=5
+  |\|
+  | o  c rank=2
+  | |
+  o |  b rank=3
+  | |
+  o |   rank=2
+  |/
+  o  a rank=1
+  
+
+Display the sort of "d" for reference:
+
+  $ hg test-sts d
+  d,c,b,*,a, (no-eol) (glob)
+
+Chack that sort "f" leaps from "d" to "b":
+
+  $ hg test-sts f
+  f,d,b,*,e,*,c,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 5: skipping over nested exclusive part (partially)
+==========================================================
+
+We have a merge "f" whose exclusive part contains a merge "d".
+
+Similar to example 4, but the exclusive part of "d" is only partially
+contained in the inherited part of "f".
+So, we need to leap in the middle of the exclusive part of "d".
+
+#           f
+#           |
+#        ---<---
+#        |     |
+#        d     |
+#        |     e
+#      -->--   |
+#      |   |   |
+#      |   g   |
+#      |   |   |
+#      |   +----    <- in the sort of "f", leaping from "g" to "b"
+#      b   |
+#      |   c
+#      |   |
+#      --+--
+#        |
+#        a
+
+  $ hg init example-5
+  $ cd example-5
+  $ hg debugbuilddag '.:a*a+2:b<a+1:c+1:g*b/g:d<c+6:e*d/e:f.'
+  $ hg test-log
+  o  tip rank=15
+  |
+  o    f rank=14
+  |\
+  | o  e rank=8
+  | |
+  | o   rank=7
+  | |
+  | o   rank=6
+  | |
+  | o   rank=5
+  | |
+  | o   rank=4
+  | |
+  | o   rank=3
+  | |
+  o |    d rank=7
+  |\ \
+  | o |  g rank=3
+  | |/
+  | o  c rank=2
+  | |
+  o |  b rank=4
+  | |
+  o |   rank=3
+  | |
+  o |   rank=2
+  |/
+  o  a rank=1
+  
+
+Display the sort of "d" for reference:
+
+  $ hg test-sts d
+  d,g,c,b,*,a, (no-eol) (glob)
+
+Check that sort "f" leaps from "g" to "b":
+
+  $ hg test-sts f
+  f,d,g,b,*,e,*,c,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 6: merge in the inherited part
+======================================
+
+Variant of example 2, but with a merge ("f") in the inherited part of "g".
+
+"g" is a merge node whose inherited part contains a merge node "f".
+We check that the stable-tail sort delegates properly after the exclusive part.
+
+#         g
+#         |
+#      ---<---
+#      |     |
+#      d     f
+#      |     |
+#      |  ---<---
+#      |  |     |
+#      |  e     c
+#      |  |     |
+#      ---+     |    <- at this point, we're done (for good) with the exclusive
+#         |     |       part of "g"
+#         b     |
+#         |     |
+#         ---+---
+#            |
+#            a
+
+  $ hg init example-6
+  $ cd example-6
+  $ hg debugbuilddag '.:a*a:b<a+3:c*b:d*b:e*e/c:f*d/f:g.'
+  $ hg test-log
+  o  tip rank=10
+  |
+  o    g rank=9
+  |\
+  | o    f rank=7
+  | |\
+  | | o  e rank=3
+  | | |
+  o---+  d rank=3
+   / /
+  o |  c rank=4
+  | |
+  o |   rank=3
+  | |
+  o |   rank=2
+  | |
+  | o  b rank=2
+  |/
+  o  a rank=1
+  
+
+Display the sort of "f" for reference:
+
+  $ hg test-sts f
+  f,e,b,c,*,a, (no-eol) (glob)
+
+Check that the sort of "g" delegates to the sort of "f" after processing its
+exclusive part of "g":
+
+  $ hg test-sts g
+  g,d,f,e,b,c,*,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 7: postponed iteration of common exclusive ancestors
+============================================================
+
+Sibling merges "j" and "k", with partially shared exclusive parts.
+
+When considering the sort of "l", the iteration over this shared part cannot
+happen when iterating over excl(j) and has to be postponed to excl(k).
+
+#            l
+#            |
+#        ----<----
+#        |       |
+#        j       k
+#        |       |
+#      -->--   --<--
+#      |   |   |   |
+#      g   e   h   i
+#      |   |   |   |
+#      |   --+--   |   <- at this point, for the sort of "l", the iteration on
+#      f     |     |      the end of excl(j) is postponed to the iteration of
+#      |     d     |      excl(k)
+#      |     |     |
+#      |     c     |
+#      |     |     |
+#      ---+---     |
+#         |        |
+#         b        |
+#         |        |
+#         ----+-----
+#             |
+#             a
+
+  $ hg init example-7
+  $ cd example-7
+  $ hg debugbuilddag \
+  > '.:a*a:b*b:c*c:d*d:e*b:f<f+3:g<d+2:h<a+6:i*e/g:j*h/i:k*j/k:l.'
+  $ hg test-log
+  o  tip rank=21
+  |
+  o    l rank=20
+  |\
+  | o    k rank=13
+  | |\
+  o \ \    j rank=10
+  |\ \ \
+  | | | o  i rank=7
+  | | | |
+  | | | o   rank=6
+  | | | |
+  | | | o   rank=5
+  | | | |
+  | | | o   rank=4
+  | | | |
+  | | | o   rank=3
+  | | | |
+  | | | o   rank=2
+  | | | |
+  | | o |  h rank=6
+  | | | |
+  | | o |   rank=5
+  | | | |
+  | o | |  g rank=6
+  | | | |
+  | o | |   rank=5
+  | | | |
+  | o | |   rank=4
+  | | | |
+  | o | |  f rank=3
+  | | | |
+  o---+ |  e rank=5
+   / / /
+  | o |  d rank=4
+  | | |
+  | o |  c rank=3
+  |/ /
+  o /  b rank=2
+  |/
+  o  a rank=1
+  
+
+Display the sort of "j" for reference:
+
+  $ hg test-sts j
+  j,e,d,c,g,*,f,b,a, (no-eol) (glob)
+
+Display the sort of "k" for reference:
+
+  $ hg test-sts k
+  k,h,*,d,c,b,i,*,a, (no-eol) (glob)
+
+Check that the common part of excl(j) and excl(k) is iterated over after "k":
+
+  $ hg test-sts l
+  l,j,e,g,*,f,k,h,*,d,c,b,i,*,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 8: postponed iteration of common ancestors between parts
+================================================================
+
+Sibling merges "g" and "i", with some part shared between the inherited part
+of "g" and the exclusive part of "i".
+
+When considering the sort of "j", the iteration over this shared part cannot
+happen when iterating over inherited(g) and has to be postponed to excl(i).
+
+#            j
+#            |
+#        ----<----
+#        |       |
+#        g       i
+#        |       |
+#      --<--   --<--
+#      |   |   |   |
+#      c   f   |   h
+#      |   |   |   |
+#      |   --+--   |   <- at this point, for the sort of "j", the iteration
+#      |     |     |      on the end of inherited(g) is postponed to the
+#      |     e     |      iteration of excl(k)
+#      |     |     |
+#      ---+---     |
+#         b        |
+#         |        |
+#         ----+-----
+#             |
+#             a
+
+  $ hg init example-8
+  $ cd example-8
+  $ hg debugbuilddag '.:a*a:b*b:c*b:d*d:e*e:f*c/f:g<a+5:h*e/h:i*g/i:j.'
+  $ hg test-log
+  o  tip rank=15
+  |
+  o    j rank=14
+  |\
+  | o    i rank=10
+  | |\
+  | | o  h rank=6
+  | | |
+  | | o   rank=5
+  | | |
+  | | o   rank=4
+  | | |
+  | | o   rank=3
+  | | |
+  | | o   rank=2
+  | | |
+  o | |    g rank=7
+  |\ \ \
+  | o | |  f rank=5
+  | |/ /
+  | o |  e rank=4
+  | | |
+  | o |  d rank=3
+  | | |
+  o | |  c rank=3
+  |/ /
+  o /  b rank=2
+  |/
+  o  a rank=1
+  
+
+Display the sort of "g" for reference:
+
+  $ hg test-sts g
+  g,c,f,e,d,b,a, (no-eol)
+
+Display the sort of "i" for reference:
+
+  $ hg test-sts i
+  i,e,d,b,h,*,a, (no-eol) (glob)
+
+Check that the common part of inherited(g) and excl(k) is iterated over after
+"i":
+
+  $ hg test-sts j
+  j,g,c,f,i,e,d,b,h,*,a, (no-eol) (glob)
+
+  $ cd ..
+
+
+Example 9: postponed iteration of common ancestors between both parts
+=====================================================================
+
+This is a combination of example 7 and 8 at the same time.
+Both excl(i) and excl(j) share a common part.
+Same with inherited(i) and inherited(j).
+
+We test that the walk on the common ancestors in both cases is properly
+postponed when considering sort(k).
+
+#            k
+#            |
+#        ----<----
+#        |       |
+#        i       j
+#        |       |
+#      --<--   --<--
+#      |   |   |   |
+#      c   f   g   h
+#      |   |   |   |
+#      |   e   |   |
+#      |   |   |   |
+#      +--]|[---   |   <- rest of excl(i) postponed to excl(j)
+#      |   |       |
+#      b   ----+----   <- rest of inherited(i) postponed to inherited(j)
+#      |       |
+#      |       d
+#      |       |
+#      ----+----
+#          |
+#          a
+
+  $ hg init example-9
+  $ cd example-9
+  $ hg debugbuilddag '.:a*a:b*b:c*a:d*d:e*e:f<b+2:g<d+3:h*c/f:i*g/h:j*i/j:k.'
+  $ hg test-log
+  o  tip rank=15
+  |
+  o    k rank=14
+  |\
+  | o    j rank=9
+  | |\
+  o \ \    i rank=7
+  |\ \ \
+  | | | o  h rank=5
+  | | | |
+  | | | o   rank=4
+  | | | |
+  | | | o   rank=3
+  | | | |
+  | | o |  g rank=4
+  | | | |
+  | | o |   rank=3
+  | | | |
+  | o | |  f rank=4
+  | | | |
+  | o---+  e rank=3
+  |  / /
+  | | o  d rank=2
+  | | |
+  o | |  c rank=3
+  |/ /
+  o /  b rank=2
+  |/
+  o  a rank=1
+  
+
+Display sort(i) for reference:
+
+  $ hg test-sts i
+  i,c,b,f,e,d,a, (no-eol)
+
+Display sort(j) for reference:
+
+  $ hg test-sts j
+  j,g,*,b,h,*,d,a, (no-eol) (glob)
+
+Check that the end of excl(i) is postponed to excl(j), the end of inherited(i)
+is postponed to inherited(j) in sort(k):
+
+  $ hg test-sts k
+  k,i,c,f,e,j,g,*,b,h,*,d,a, (no-eol) (glob)
+
+  $ cd ..
--- a/tests/test-status.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-status.t	Fri Apr 14 10:41:40 2023 +0200
@@ -246,6 +246,11 @@
   ! deleted
   ? unknown
 
+hg status -0:
+
+  $ hg status -0 --config rhg.on-unsupported=abort
+  A added\x00A copied\x00R removed\x00! deleted\x00? unknown\x00 (no-eol) (esc)
+
 hg status -A:
 
   $ hg status -A
--- a/tests/test-strip.t	Thu Apr 13 14:21:01 2023 +0200
+++ b/tests/test-strip.t	Fri Apr 14 10:41:40 2023 +0200
@@ -251,7 +251,7 @@
   
   $ hg debugbundle .hg/strip-backup/*
   Stream params: {Compression: BZ}
-  changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
+  changegroup -- {nbchanges: 1, version: 03} (mandatory: True)
       264128213d290d868c54642d13aeaa3675551a78
   cache:rev-branch-cache -- {} (mandatory: False)
   phase-heads -- {} (mandatory: True)