# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741689492 -3600
#      Tue Mar 11 11:38:12 2025 +0100
# Branch stable
# Node ID fcd7cb10623df23ff4d03ec5ccfb4ee77bcb9b15
# Parent  6e1eb7212633b3af6feda2c06e8bc26253010aec
drawdag: add  documentation about special comment around files

The feature was not documented, so we start with documenting it before
extending it.

diff --git a/tests/drawdag.py b/tests/drawdag.py
--- a/tests/drawdag.py
+++ b/tests/drawdag.py
@@ -79,6 +79,19 @@
       # replace: A -> B -> C -> D  # chained 1 to 1 replacements
       # split: A -> B, C           # 1 to many
       # prune: A, B, C             # many to nothing
+
+Special comment can also be used to control file content for some revision.
+
+The example below create two extra files in A, update on in B.
+
+In all case the file matching the node name is created for all non-merge
+commit.
+
+    C # A/file/path=content in A
+    | # A/other/file/path=content for another file
+    B # B/file/path=updated content in B
+    | 
+    A
 """
 
 import collections
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741689510 -3600
#      Tue Mar 11 11:38:30 2025 +0100
# Branch stable
# Node ID d686448e8eb6adb6a966860f3985fab5208bae60
# Parent  fcd7cb10623df23ff4d03ec5ccfb4ee77bcb9b15
drawdag: add more test output regarding file content

This will be useful to test deletion in the next changesets.

diff --git a/tests/test-drawdag.t b/tests/test-drawdag.t
--- a/tests/test-drawdag.t
+++ b/tests/test-drawdag.t
@@ -253,6 +253,32 @@
   |
   o  A A dir1/a
   
+  $ for r in `hg log -T '{rev} ' --rev "sort(all())"`; do
+  >     hg log --rev $r -T "### {tags} ###\n";
+  >     hg diff --change $r --stat;
+  > done
+  ### A ###
+   A      |  1 +
+   dir1/a |  2 ++
+   2 files changed, 3 insertions(+), 0 deletions(-)
+  ### B ###
+   B      |  1 +
+   dir2/b |  1 +
+   2 files changed, 2 insertions(+), 0 deletions(-)
+  ### C tip ###
+   A      |  1 +
+   B      |  2 +-
+   dir1/a |  2 ++
+   dir1/c |  1 +
+   dir2/c |  1 +
+   5 files changed, 6 insertions(+), 1 deletions(-)
+  $ hg files -r C
+  A
+  B
+  dir1/a
+  dir1/c
+  dir2/b
+  dir2/c
   $ for f in `hg files -r C`; do
   >   echo FILE "$f"
   >   hg cat -r C "$f"
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741685595 -3600
#      Tue Mar 11 10:33:15 2025 +0100
# Branch stable
# Node ID 25079cc2016fc114895fca304481a0b8ff7a9e6a
# Parent  d686448e8eb6adb6a966860f3985fab5208bae60
drawdag: document and augment file creation capability

In order to test some stream clone related feature, we need to be able to create
file with more complex name and delete them.

We document the file related feature in the process.

diff --git a/tests/drawdag.py b/tests/drawdag.py
--- a/tests/drawdag.py
+++ b/tests/drawdag.py
@@ -82,7 +82,8 @@
 
 Special comment can also be used to control file content for some revision.
 
-The example below create two extra files in A, update on in B.
+The example below create two extra files in A, update on in B and delete the
+other on in C (using a special content to mark the deletion).
 
 In all case the file matching the node name is created for all non-merge
 commit.
@@ -90,7 +91,7 @@
     C # A/file/path=content in A
     | # A/other/file/path=content for another file
     B # B/file/path=updated content in B
-    | 
+    | # C/other/file/path=<deleted>
     A
 """
 
@@ -313,7 +314,10 @@
             self._parents.append(repo[repo.nullid])
 
     def filectx(self, key):
-        return simplefilectx(key, self._added[key])
+        content = self._added.get(key)
+        if content is not None:
+            return simplefilectx(key, content)
+        return None
 
     def commit(self):
         return self._repo.commitctx(self)
@@ -394,10 +398,13 @@
     # parse comments to get extra file content instructions
     files = collections.defaultdict(dict)  # {(name, path): content}
     comments = list(_getcomments(text))
-    filere = re.compile(br'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M)
+    filere = re.compile(br'^(\w+)/([-_\w/"]+)\s*=\s*(.*)$', re.M)
     for name, path, content in filere.findall(b'\n'.join(comments)):
         content = content.replace(br'\n', b'\n').replace(br'\1', b'\1')
-        files[name][path] = content
+        if content == b"<deleted>":
+            files[name][path] = None
+        else:
+            files[name][path] = content
 
     committed = {None: repo.nullid}  # {name: node}
 
diff --git a/tests/test-drawdag.t b/tests/test-drawdag.t
--- a/tests/test-drawdag.t
+++ b/tests/test-drawdag.t
@@ -242,36 +242,43 @@
   > |\      # B/dir2/b = 34
   > A B     # C/dir1/c = 5
   >         # C/dir2/c = 6
+  >         # A/to-be"deleted" = I am doomed.
+  >         # B/to-be-deleted_too = The end is near.
+  >         # C/to-be"deleted"=<deleted>
+  >         # C/to-be-deleted_too = <deleted>
   >         # C/A = a
   >         # C/B = b
   > EOS
 
   $ hg log -G -T '{desc} {files}'
-  o    C A B dir1/c dir2/c
+  o    C A B dir1/c dir2/c to-be"deleted" to-be-deleted_too
   |\
-  | o  B B dir2/b
+  | o  B B dir2/b to-be-deleted_too
   |
-  o  A A dir1/a
+  o  A A dir1/a to-be"deleted"
   
   $ for r in `hg log -T '{rev} ' --rev "sort(all())"`; do
   >     hg log --rev $r -T "### {tags} ###\n";
   >     hg diff --change $r --stat;
   > done
   ### A ###
-   A      |  1 +
-   dir1/a |  2 ++
-   2 files changed, 3 insertions(+), 0 deletions(-)
+   A              |  1 +
+   dir1/a         |  2 ++
+   to-be"deleted" |  1 +
+   3 files changed, 4 insertions(+), 0 deletions(-)
   ### B ###
-   B      |  1 +
-   dir2/b |  1 +
-   2 files changed, 2 insertions(+), 0 deletions(-)
+   B                 |  1 +
+   dir2/b            |  1 +
+   to-be-deleted_too |  1 +
+   3 files changed, 3 insertions(+), 0 deletions(-)
   ### C tip ###
-   A      |  1 +
-   B      |  2 +-
-   dir1/a |  2 ++
-   dir1/c |  1 +
-   dir2/c |  1 +
-   5 files changed, 6 insertions(+), 1 deletions(-)
+   A              |  2 +-
+   B              |  1 +
+   dir1/c         |  1 +
+   dir2/b         |  1 +
+   dir2/c         |  1 +
+   to-be"deleted" |  1 -
+   6 files changed, 5 insertions(+), 2 deletions(-)
   $ hg files -r C
   A
   B
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741685859 -3600
#      Tue Mar 11 10:37:39 2025 +0100
# Branch stable
# Node ID 46603c00a9f28cc6d5583bf63d676ddf576561e7
# Parent  25079cc2016fc114895fca304481a0b8ff7a9e6a
stream: audit path on encoded path

In 5b8f6e198a6e, we wrongly called `vfs._auditpath` on the pre-encoding path, so
we would get abort on problem that the encoding is here to handle.

So we fix the problem (easily) and update the test to cover this (with
significantly more work).

Thanks to Matt Harbison for spotting this during the freeze.

diff --git a/mercurial/vfs.py b/mercurial/vfs.py
--- a/mercurial/vfs.py
+++ b/mercurial/vfs.py
@@ -476,9 +476,9 @@
             <path> is the real file system path content should be written to,
             <mode> is the file mode that need to be set if any.
         """
-        self._auditpath(path, b'wb')
+        real_path = self.join(path)
+        self._auditpath(real_path, b'wb')
         self.register_file(path)
-        real_path = self.join(path)
         dirname, basename = util.split(real_path)
         if dirname not in known_directories:
             util.makedirs(dirname, self.createmode, True)
diff --git a/tests/test-stream-bundle-v2.t b/tests/test-stream-bundle-v2.t
--- a/tests/test-stream-bundle-v2.t
+++ b/tests/test-stream-bundle-v2.t
@@ -36,6 +36,8 @@
   $ cp $HGRCPATH $TESTTMP/hgrc.orig
 
   $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > portablefilenames=abort
   > [experimental]
   > evolution.createmarkers=True
   > evolution.exchange=True
@@ -60,11 +62,11 @@
   $ hg debugdrawdag <<'EOF'
   > E
   > |
-  > D
+  > D  # D/ba"r=<deleted>
   > |
-  > C
+  > C  # C/ba"r=faz
   > |
-  > B
+  > B  # B/blu=fuz
   > |
   > A
   > EOF
@@ -72,9 +74,9 @@
   $ hg bundle -a --type="none-v2;stream=$stream_version" bundle.hg
   $ hg debugbundle bundle.hg
   Stream params: {}
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
-  stream2 -- {bytecount: 1819, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
+  stream2 -- {bytecount: 1908, filecount: 14, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
+  stream2 -- {bytecount: 1911, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
+  stream2 -- {bytecount: 2103, filecount: 16, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 no-zstd !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 zstd no-rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 rust !)
@@ -89,9 +91,9 @@
   $ hg bundle -a --type="none-$bundle_format" bundle.hg
   $ hg debugbundle bundle.hg
   Stream params: {}
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
-  stream2 -- {bytecount: 1819, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
+  stream2 -- {bytecount: 1908, filecount: 14, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
+  stream2 -- {bytecount: 1911, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
+  stream2 -- {bytecount: 2103, filecount: 16, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 no-zstd !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 zstd no-rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 rust !)
@@ -133,8 +135,8 @@
   adding changesets
   adding manifests
   adding file changes
-  added 5 changesets with 5 changes to 5 files
-  new changesets 426bada5c675:9bc730a19041 (5 drafts)
+  added 5 changesets with 7 changes to 7 files
+  new changesets 426bada5c675:92165ab525bf (5 drafts)
 
 known requirements
 ------------------
@@ -193,8 +195,9 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream2" (params: 3 mandatory) supported
   applying stream bundle
-  12 files to transfer, 1.65 KB of data (no-rust !)
-  14 files to transfer, 1.78 KB of data (rust !)
+  14 files to transfer, 1.86 KB of data (no-rust no-zstd !)
+  14 files to transfer, 1.87 KB of data (no-rust zstd !)
+  16 files to transfer, 2.05 KB of data (rust !)
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -202,19 +205,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  bundle2-input-part: total payload size 1857 (no-rust !)
-  bundle2-input-part: total payload size 2025 (rust !)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2099 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2102 (no-rust zstd !)
+  bundle2-input-part: total payload size 2337 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -233,7 +242,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -244,7 +253,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
@@ -261,8 +272,9 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream2" (params: 3 mandatory) supported
   applying stream bundle
-  12 files to transfer, 1.65 KB of data (no-rust !)
-  14 files to transfer, 1.78 KB of data (rust !)
+  14 files to transfer, 1.86 KB of data (no-rust no-zstd !)
+  14 files to transfer, 1.87 KB of data (no-rust zstd !)
+  16 files to transfer, 2.05 KB of data (rust !)
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -270,19 +282,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  bundle2-input-part: total payload size 1857 (no-rust !)
-  bundle2-input-part: total payload size 2025 (rust !)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2099 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2102 (no-rust zstd !)
+  bundle2-input-part: total payload size 2337 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -301,7 +319,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -312,7 +330,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
@@ -327,7 +347,7 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream3-exp" (params: 1 mandatory) supported
   applying stream bundle
-  11 entries to transfer
+  13 entries to transfer
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -335,19 +355,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  bundle2-input-part: total payload size 1869 (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
-  bundle2-input-part: total payload size 2037 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  bundle2-input-part: total payload size 2113 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2116 (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2351 (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -366,7 +392,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -377,7 +403,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
@@ -389,7 +417,7 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream3-exp" (params: 1 mandatory) supported
   applying stream bundle
-  11 entries to transfer
+  13 entries to transfer
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -397,19 +425,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  bundle2-input-part: total payload size 1869 (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
-  bundle2-input-part: total payload size 2037 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  bundle2-input-part: total payload size 2113 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2116 (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2351 (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -428,7 +462,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -439,7 +473,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741656582 -3600
#      Tue Mar 11 02:29:42 2025 +0100
# Branch stable
# Node ID cdd7bf612c7be0b545843b092b49625f62158b3a
# Parent  46603c00a9f28cc6d5583bf63d676ddf576561e7
bundle-spec: properly format boolean parameter (issue6960)

This was breaking automatic clone bundle generation. This changeset fixes it and
add a test to catch it in the future.

diff --git a/mercurial/bundlecaches.py b/mercurial/bundlecaches.py
--- a/mercurial/bundlecaches.py
+++ b/mercurial/bundlecaches.py
@@ -102,8 +102,12 @@
 
     def as_spec(self):
         parts = [b"%s-%s" % (self.compression, self.version)]
-        for param in sorted(self._explicit_params.items()):
-            parts.append(b'%s=%s' % param)
+        for param, raw_value in sorted(self._explicit_params.items()):
+            if isinstance(raw_value, bool):
+                value = b"yes" if raw_value else b"no"
+            else:
+                value = raw_value
+            parts.append(b'%s=%s' % (param, value))
         return b';'.join(parts)
 
 
diff --git a/tests/test-clonebundles-autogen.t b/tests/test-clonebundles-autogen.t
--- a/tests/test-clonebundles-autogen.t
+++ b/tests/test-clonebundles-autogen.t
@@ -575,6 +575,7 @@
   full-bzip2-v2-13_revs-8a81f9be54ea_tip-*_acbr.hg (glob)
   full-bzip2-v2-15_revs-17615b3984c2_tip-*_acbr.hg (glob)
   $ ls -1 ../final-upload
+  $ cd ..
 
 Check the url is correct
 ------------------------
@@ -589,3 +590,44 @@
   searching for changes
   no changes found
   15 local changesets published
+
+Test spec with boolean component (issue6960)
+============================================
+
+  $ cat >> ./server/.hg/hgrc << EOF
+  > [clone-bundles]
+  > auto-generate.formats = gzip-v2;obsolescence=yes,gzip-v3;obsolescence=no,gzip-v3;obsolescence=yes;obsolescence-mandatory=no
+  > EOF
+
+  $ rm ./server/.hg/clonebundles.manifest
+  $ hg -R ./server/ admin::clone-bundles-refresh
+  clone-bundles: deleting inline bundle full-bzip2-v1-13_revs-8a81f9be54ea_tip-*_acbr.hg (glob)
+  clone-bundles: deleting inline bundle full-bzip2-v2-13_revs-8a81f9be54ea_tip-*_acbr.hg (glob)
+  clone-bundles: starting bundle generation: gzip-v2;obsolescence=yes
+  15 changesets found
+  clone-bundles: starting bundle generation: gzip-v3;obsolescence=no
+  15 changesets found
+  clone-bundles: starting bundle generation: gzip-v3;obsolescence=yes;obsolescence-mandatory=no
+  15 changesets found
+  $ cat ./server/.hg/clonebundles.manifest
+  peer-bundle-cache://full-bzip2-v1-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=bzip2-v1 (glob)
+  peer-bundle-cache://full-bzip2-v2-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=bzip2-v2 (glob)
+  peer-bundle-cache://full-gzip-v2;obsolescence=yes-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=gzip-v2;obsolescence=yes (glob)
+  peer-bundle-cache://full-gzip-v3;obsolescence=no-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=gzip-v3;obsolescence=no (glob)
+  peer-bundle-cache://full-gzip-v3;obsolescence=yes;obsolescence-mandatory=no-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=gzip-v3;obsolescence=yes;obsolescence-mandatory=no (glob)
+
+
+Check the manifest is correct
+-----------------------------
+
+  $ hg clone -U ssh://user@dummy/server ssh-inline-more-param-clone
+  applying clone bundle from peer-bundle-cache://full-bzip2-v1-15_revs-17615b3984c2_tip-*_acbr.hg (glob)
+  adding changesets
+  adding manifests
+  adding file changes
+  added 15 changesets with 15 changes to 15 files
+  finished applying clone bundle
+  searching for changes
+  no changes found
+  15 local changesets published
+
# HG changeset patch
# User Arseniy Alekseyev <aalekseyev@janestreet.com>
# Date 1741295861 18000
#      Thu Mar 06 16:17:41 2025 -0500
# Branch stable
# Node ID c18776eefe6e1e6fbe2bc2979a280b02258c0506
# Parent  cdd7bf612c7be0b545843b092b49625f62158b3a
vfs: fix a bug where the revlog gets copied every single write

diff --git a/rust/hg-core/src/vfs.rs b/rust/hg-core/src/vfs.rs
--- a/rust/hg-core/src/vfs.rs
+++ b/rust/hg-core/src/vfs.rs
@@ -929,7 +929,7 @@
 /// a local clone, and we don't want to modify the original repo.
 fn copy_in_place_if_hardlink(path: &Path) -> Result<(), HgError> {
     let metadata = path.metadata().when_writing_file(path)?;
-    if metadata.nlink() > 0 {
+    if metadata.nlink() > 1 {
         // If it's hardlinked, copy it and rename it back before changing it.
         let tmpdir = path.parent().expect("file at root");
         let name = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741779942 -3600
#      Wed Mar 12 12:45:42 2025 +0100
# Branch stable
# Node ID c90a0f428809e8972bbe1f98c1c30c952dc3b288
# Parent  c18776eefe6e1e6fbe2bc2979a280b02258c0506
test-clonebundles-autoge: make sure all background process are completed

Some operation and state are still around after the destination bundle are
created. So we have to wait on another conditional too.

diff --git a/tests/test-clonebundles-autogen.t b/tests/test-clonebundles-autogen.t
--- a/tests/test-clonebundles-autogen.t
+++ b/tests/test-clonebundles-autogen.t
@@ -446,6 +446,21 @@
   $ $RUNTESTDIR/testlib/wait-on-file 30 $v1_file
   $ $RUNTESTDIR/testlib/wait-on-file 30 $v2_file
 
+all file created, but some cleanup might still be in progress, we wait 30 second for them
+
+# note: we should adjust that timing according to the test timeout, but that is more a
+#       change for the default branch than the stable branch
+
+  $ for x in `$RUNTESTDIR/seq.py 30`; do
+  >      if grep --invert-match DONE ../server/.hg/clonebundles.auto-gen > /dev/null; then
+  >          # some task still running
+  >          sleep 1
+  >          continue
+  >      fi
+  >      # all task are done running
+  >      break
+  > done
+
 We should have bundle now
 
   $ cat ../server/.hg/clonebundles.manifest
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1741768799 -3600
#      Wed Mar 12 09:39:59 2025 +0100
# Branch stable
# Node ID c29c7e083c342ccc2aed77e513c77a5d67e8cb86
# Parent  c90a0f428809e8972bbe1f98c1c30c952dc3b288
doc: update document for the `"parallel-stream-bundle-processing" options

Updating the doc was overlooked in e75ed9ae5fb9.

diff --git a/mercurial/configitems.toml b/mercurial/configitems.toml
--- a/mercurial/configitems.toml
+++ b/mercurial/configitems.toml
@@ -2988,8 +2988,8 @@
 When set to 0, the default, the value used depends on the value of the
 `usage.resources.cpu` configuration:
 - low:    1 writer
-- medium: 2 writers (default)
-- high:   4 writers
+- medium: 4 writers (default)
+- high:   8 writers
 """
 
 [[items]]
@@ -3003,8 +3003,8 @@
 
 When set to 0, the default value depends on the `usage.resources.memory` value.
 - low:    100 MB,
-- medium:   1 GB,
-- high:   unrestricted memory usage.
+- medium: 500 MB,
+- high:     2 GB
 """
 
 
# HG changeset patch
# User Arseniy Alekseyev <aalekseyev@janestreet.com>
# Date 1741979600 14400
#      Fri Mar 14 15:13:20 2025 -0400
# Branch stable
# Node ID da40e6aec8071886592baeec6700ca2932deb3ca
# Parent  c29c7e083c342ccc2aed77e513c77a5d67e8cb86
bundle2: fix a bug where _forwardchunks doesn't work with empty bundles

It waits for two NULL terminators: a bundle part terminator followed by a
bundle terminator. Since the empty bundle has no parts, bundle part
terminator never comes.

The fix is to only start waiting for a bundle part terminator once
we've seen a part.

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -939,7 +939,16 @@
             yield _pack(_fstreamparamsize, paramssize)
         # From there, payload might need to be decompressed
         self._fp = self._compengine.decompressorreader(self._fp)
-        emptycount = 0
+
+        # We usually wait for empty terminators: a bundle part terminator
+        # followed by a bundle terminator.
+        #
+        # Since the empty bundle has no parts,
+        # bundle part terminator never comes.
+        #
+        # So we boostrap this detection with am empty part, which the first
+        # piece of part will reset.
+        emptycount = 1
         while emptycount < 2:
             # so we can brainlessly loop
             assert _fpartheadersize == _fpayloadsize
diff --git a/tests/test-bundle2.py b/tests/test-bundle2.py
new file mode 100644
--- /dev/null
+++ b/tests/test-bundle2.py
@@ -0,0 +1,33 @@
+import unittest
+from mercurial import bundle2, ui as uimod
+
+bundle20 = bundle2.bundle20
+unbundle20 = bundle2.unbundle20
+
+ui = uimod.ui.load()
+
+
+class Bundle2tests(unittest.TestCase):
+    def test_nonempty_bundle_forwardchunks(self):
+        bundler = bundle20(ui)
+        bundler.newpart(
+            b'cache:rev-branch-cache', data=b'some-data', mandatory=False
+        )
+        data = b''.join(list(bundler.getchunks()))
+        unbundle = unbundle20(ui, __import__("io").BytesIO(data[4:]))
+        forwarded_data = b''.join(list(unbundle._forwardchunks()))
+        self.assertEqual(data, forwarded_data)
+
+    def test_empty_bundle_forwardchunks(self):
+        bundler = bundle20(ui)
+        data = b''.join(list(bundler.getchunks()))
+        self.assertEqual(data, b'HG20\0\0\0\0\0\0\0\0')
+        unbundle = unbundle20(ui, __import__("io").BytesIO(data[4:]))
+        forwarded_data = b''.join(list(unbundle._forwardchunks()))
+        self.assertEqual(data, forwarded_data)
+
+
+if __name__ == '__main__':
+    import silenttestrunner
+
+    silenttestrunner.main(__name__)
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1742242849 -3600
#      Mon Mar 17 21:20:49 2025 +0100
# Branch stable
# Node ID cb2b2242428df5be87693b8acaac71deafcff2c5
# Parent  da40e6aec8071886592baeec6700ca2932deb3ca
release-note: update the 7.0 changelog

A few notable bugfix landed.

diff --git a/relnotes/7.0 b/relnotes/7.0
--- a/relnotes/7.0
+++ b/relnotes/7.0
@@ -53,6 +53,9 @@
 - rhg: set the expected dirstate permissions (0o666 minus umask) (a48c688d3e80)
 - rhg: fix matcher issue (136e74c2bf8f)
 - rhg files correctly implements `--rev` (it instead provided `--revision`)
+- clone-bundles: fix background spawning of automatic generation
+- bundle-spec: properly format boolean parameter (issue6960)
+- bundle2: fix a bug where _forwardchunks doesn't work with empty bundles
 
 == Rust ==
 
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1742247635 -3600
#      Mon Mar 17 22:40:35 2025 +0100
# Branch stable
# Node ID fea85ff980ef8122c684d70b5f8db7d6f6137c20
# Parent  cb2b2242428df5be87693b8acaac71deafcff2c5
Added tag 7.0rc1 for changeset cb2b2242428d

diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -289,3 +289,4 @@
 b964f92261d4fbb64f19aa6af2b072f7730b913a 6.9.2
 89ab2459f62ac8da3eb4f3ee2120d1814ce805d5 6.9.3
 ed68f9a47a090cc6450a7d5617baf123ba5fc42a 7.0rc0
+cb2b2242428df5be87693b8acaac71deafcff2c5 7.0rc1
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1742247644 -3600
#      Mon Mar 17 22:40:44 2025 +0100
# Branch stable
# Node ID 84c5a98964ac077b7296244a4aa8e1117ffa8485
# Parent  fea85ff980ef8122c684d70b5f8db7d6f6137c20
Added signature for changeset cb2b2242428d

diff --git a/.hgsigs b/.hgsigs
--- a/.hgsigs
+++ b/.hgsigs
@@ -273,3 +273,4 @@
 b964f92261d4fbb64f19aa6af2b072f7730b913a 0 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
+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
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1742250808 -3600
#      Mon Mar 17 23:33:28 2025 +0100
# Node ID 1cc4a52d9256dd09816d4e9d6b58e1b49e06772c
# Parent  a232522d377076537ae60536fe868b1623e9515d
# Parent  84c5a98964ac077b7296244a4aa8e1117ffa8485
branching: merge 7.0rc1 into default

diff --git a/.hgsigs b/.hgsigs
--- a/.hgsigs
+++ b/.hgsigs
@@ -273,3 +273,4 @@
 b964f92261d4fbb64f19aa6af2b072f7730b913a 0 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
+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
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -289,3 +289,4 @@
 b964f92261d4fbb64f19aa6af2b072f7730b913a 6.9.2
 89ab2459f62ac8da3eb4f3ee2120d1814ce805d5 6.9.3
 ed68f9a47a090cc6450a7d5617baf123ba5fc42a 7.0rc0
+cb2b2242428df5be87693b8acaac71deafcff2c5 7.0rc1
diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -939,7 +939,16 @@
             yield _pack(_fstreamparamsize, paramssize)
         # From there, payload might need to be decompressed
         self._fp = self._compengine.decompressorreader(self._fp)
-        emptycount = 0
+
+        # We usually wait for empty terminators: a bundle part terminator
+        # followed by a bundle terminator.
+        #
+        # Since the empty bundle has no parts,
+        # bundle part terminator never comes.
+        #
+        # So we boostrap this detection with am empty part, which the first
+        # piece of part will reset.
+        emptycount = 1
         while emptycount < 2:
             # so we can brainlessly loop
             assert _fpartheadersize == _fpayloadsize
diff --git a/mercurial/bundlecaches.py b/mercurial/bundlecaches.py
--- a/mercurial/bundlecaches.py
+++ b/mercurial/bundlecaches.py
@@ -102,8 +102,12 @@
 
     def as_spec(self):
         parts = [b"%s-%s" % (self.compression, self.version)]
-        for param in sorted(self._explicit_params.items()):
-            parts.append(b'%s=%s' % param)
+        for param, raw_value in sorted(self._explicit_params.items()):
+            if isinstance(raw_value, bool):
+                value = b"yes" if raw_value else b"no"
+            else:
+                value = raw_value
+            parts.append(b'%s=%s' % (param, value))
         return b';'.join(parts)
 
 
diff --git a/mercurial/configitems.toml b/mercurial/configitems.toml
--- a/mercurial/configitems.toml
+++ b/mercurial/configitems.toml
@@ -2988,8 +2988,8 @@
 When set to 0, the default, the value used depends on the value of the
 `usage.resources.cpu` configuration:
 - low:    1 writer
-- medium: 2 writers (default)
-- high:   4 writers
+- medium: 4 writers (default)
+- high:   8 writers
 """
 
 [[items]]
@@ -3003,8 +3003,8 @@
 
 When set to 0, the default value depends on the `usage.resources.memory` value.
 - low:    100 MB,
-- medium:   1 GB,
-- high:   unrestricted memory usage.
+- medium: 500 MB,
+- high:     2 GB
 """
 
 
diff --git a/mercurial/vfs.py b/mercurial/vfs.py
--- a/mercurial/vfs.py
+++ b/mercurial/vfs.py
@@ -476,9 +476,9 @@
             <path> is the real file system path content should be written to,
             <mode> is the file mode that need to be set if any.
         """
-        self._auditpath(path, b'wb')
+        real_path = self.join(path)
+        self._auditpath(real_path, b'wb')
         self.register_file(path)
-        real_path = self.join(path)
         dirname, basename = util.split(real_path)
         if dirname not in known_directories:
             util.makedirs(dirname, self.createmode, True)
diff --git a/relnotes/7.0 b/relnotes/7.0
--- a/relnotes/7.0
+++ b/relnotes/7.0
@@ -53,6 +53,9 @@
 - rhg: set the expected dirstate permissions (0o666 minus umask) (a48c688d3e80)
 - rhg: fix matcher issue (136e74c2bf8f)
 - rhg files correctly implements `--rev` (it instead provided `--revision`)
+- clone-bundles: fix background spawning of automatic generation
+- bundle-spec: properly format boolean parameter (issue6960)
+- bundle2: fix a bug where _forwardchunks doesn't work with empty bundles
 
 == Rust ==
 
diff --git a/rust/hg-core/src/vfs.rs b/rust/hg-core/src/vfs.rs
--- a/rust/hg-core/src/vfs.rs
+++ b/rust/hg-core/src/vfs.rs
@@ -929,7 +929,7 @@
 /// a local clone, and we don't want to modify the original repo.
 fn copy_in_place_if_hardlink(path: &Path) -> Result<(), HgError> {
     let metadata = path.metadata().when_writing_file(path)?;
-    if metadata.nlink() > 0 {
+    if metadata.nlink() > 1 {
         // If it's hardlinked, copy it and rename it back before changing it.
         let tmpdir = path.parent().expect("file at root");
         let name = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
diff --git a/tests/drawdag.py b/tests/drawdag.py
--- a/tests/drawdag.py
+++ b/tests/drawdag.py
@@ -79,6 +79,20 @@
       # replace: A -> B -> C -> D  # chained 1 to 1 replacements
       # split: A -> B, C           # 1 to many
       # prune: A, B, C             # many to nothing
+
+Special comment can also be used to control file content for some revision.
+
+The example below create two extra files in A, update on in B and delete the
+other on in C (using a special content to mark the deletion).
+
+In all case the file matching the node name is created for all non-merge
+commit.
+
+    C # A/file/path=content in A
+    | # A/other/file/path=content for another file
+    B # B/file/path=updated content in B
+    | # C/other/file/path=<deleted>
+    A
 """
 
 import collections
@@ -300,7 +314,10 @@
             self._parents.append(repo[repo.nullid])
 
     def filectx(self, key):
-        return simplefilectx(key, self._added[key])
+        content = self._added.get(key)
+        if content is not None:
+            return simplefilectx(key, content)
+        return None
 
     def commit(self):
         return self._repo.commitctx(self)
@@ -381,10 +398,13 @@
     # parse comments to get extra file content instructions
     files = collections.defaultdict(dict)  # {(name, path): content}
     comments = list(_getcomments(text))
-    filere = re.compile(br'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M)
+    filere = re.compile(br'^(\w+)/([-_\w/"]+)\s*=\s*(.*)$', re.M)
     for name, path, content in filere.findall(b'\n'.join(comments)):
         content = content.replace(br'\n', b'\n').replace(br'\1', b'\1')
-        files[name][path] = content
+        if content == b"<deleted>":
+            files[name][path] = None
+        else:
+            files[name][path] = content
 
     committed = {None: repo.nullid}  # {name: node}
 
diff --git a/tests/test-bundle2.py b/tests/test-bundle2.py
new file mode 100644
--- /dev/null
+++ b/tests/test-bundle2.py
@@ -0,0 +1,33 @@
+import unittest
+from mercurial import bundle2, ui as uimod
+
+bundle20 = bundle2.bundle20
+unbundle20 = bundle2.unbundle20
+
+ui = uimod.ui.load()
+
+
+class Bundle2tests(unittest.TestCase):
+    def test_nonempty_bundle_forwardchunks(self):
+        bundler = bundle20(ui)
+        bundler.newpart(
+            b'cache:rev-branch-cache', data=b'some-data', mandatory=False
+        )
+        data = b''.join(list(bundler.getchunks()))
+        unbundle = unbundle20(ui, __import__("io").BytesIO(data[4:]))
+        forwarded_data = b''.join(list(unbundle._forwardchunks()))
+        self.assertEqual(data, forwarded_data)
+
+    def test_empty_bundle_forwardchunks(self):
+        bundler = bundle20(ui)
+        data = b''.join(list(bundler.getchunks()))
+        self.assertEqual(data, b'HG20\0\0\0\0\0\0\0\0')
+        unbundle = unbundle20(ui, __import__("io").BytesIO(data[4:]))
+        forwarded_data = b''.join(list(unbundle._forwardchunks()))
+        self.assertEqual(data, forwarded_data)
+
+
+if __name__ == '__main__':
+    import silenttestrunner
+
+    silenttestrunner.main(__name__)
diff --git a/tests/test-clonebundles-autogen.t b/tests/test-clonebundles-autogen.t
--- a/tests/test-clonebundles-autogen.t
+++ b/tests/test-clonebundles-autogen.t
@@ -446,6 +446,21 @@
   $ $RUNTESTDIR/testlib/wait-on-file 30 $v1_file
   $ $RUNTESTDIR/testlib/wait-on-file 30 $v2_file
 
+all file created, but some cleanup might still be in progress, we wait 30 second for them
+
+# note: we should adjust that timing according to the test timeout, but that is more a
+#       change for the default branch than the stable branch
+
+  $ for x in `$RUNTESTDIR/seq.py 30`; do
+  >      if grep --invert-match DONE ../server/.hg/clonebundles.auto-gen > /dev/null; then
+  >          # some task still running
+  >          sleep 1
+  >          continue
+  >      fi
+  >      # all task are done running
+  >      break
+  > done
+
 We should have bundle now
 
   $ cat ../server/.hg/clonebundles.manifest
@@ -575,6 +590,7 @@
   full-bzip2-v2-13_revs-8a81f9be54ea_tip-*_acbr.hg (glob)
   full-bzip2-v2-15_revs-17615b3984c2_tip-*_acbr.hg (glob)
   $ ls -1 ../final-upload
+  $ cd ..
 
 Check the url is correct
 ------------------------
@@ -589,3 +605,44 @@
   searching for changes
   no changes found
   15 local changesets published
+
+Test spec with boolean component (issue6960)
+============================================
+
+  $ cat >> ./server/.hg/hgrc << EOF
+  > [clone-bundles]
+  > auto-generate.formats = gzip-v2;obsolescence=yes,gzip-v3;obsolescence=no,gzip-v3;obsolescence=yes;obsolescence-mandatory=no
+  > EOF
+
+  $ rm ./server/.hg/clonebundles.manifest
+  $ hg -R ./server/ admin::clone-bundles-refresh
+  clone-bundles: deleting inline bundle full-bzip2-v1-13_revs-8a81f9be54ea_tip-*_acbr.hg (glob)
+  clone-bundles: deleting inline bundle full-bzip2-v2-13_revs-8a81f9be54ea_tip-*_acbr.hg (glob)
+  clone-bundles: starting bundle generation: gzip-v2;obsolescence=yes
+  15 changesets found
+  clone-bundles: starting bundle generation: gzip-v3;obsolescence=no
+  15 changesets found
+  clone-bundles: starting bundle generation: gzip-v3;obsolescence=yes;obsolescence-mandatory=no
+  15 changesets found
+  $ cat ./server/.hg/clonebundles.manifest
+  peer-bundle-cache://full-bzip2-v1-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=bzip2-v1 (glob)
+  peer-bundle-cache://full-bzip2-v2-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=bzip2-v2 (glob)
+  peer-bundle-cache://full-gzip-v2;obsolescence=yes-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=gzip-v2;obsolescence=yes (glob)
+  peer-bundle-cache://full-gzip-v3;obsolescence=no-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=gzip-v3;obsolescence=no (glob)
+  peer-bundle-cache://full-gzip-v3;obsolescence=yes;obsolescence-mandatory=no-15_revs-17615b3984c2_tip-*_acbr.hg BUNDLESPEC=gzip-v3;obsolescence=yes;obsolescence-mandatory=no (glob)
+
+
+Check the manifest is correct
+-----------------------------
+
+  $ hg clone -U ssh://user@dummy/server ssh-inline-more-param-clone
+  applying clone bundle from peer-bundle-cache://full-bzip2-v1-15_revs-17615b3984c2_tip-*_acbr.hg (glob)
+  adding changesets
+  adding manifests
+  adding file changes
+  added 15 changesets with 15 changes to 15 files
+  finished applying clone bundle
+  searching for changes
+  no changes found
+  15 local changesets published
+
diff --git a/tests/test-drawdag.t b/tests/test-drawdag.t
--- a/tests/test-drawdag.t
+++ b/tests/test-drawdag.t
@@ -242,17 +242,50 @@
   > |\      # B/dir2/b = 34
   > A B     # C/dir1/c = 5
   >         # C/dir2/c = 6
+  >         # A/to-be"deleted" = I am doomed.
+  >         # B/to-be-deleted_too = The end is near.
+  >         # C/to-be"deleted"=<deleted>
+  >         # C/to-be-deleted_too = <deleted>
   >         # C/A = a
   >         # C/B = b
   > EOS
 
   $ hg log -G -T '{desc} {files}'
-  o    C A B dir1/c dir2/c
+  o    C A B dir1/c dir2/c to-be"deleted" to-be-deleted_too
   |\
-  | o  B B dir2/b
+  | o  B B dir2/b to-be-deleted_too
   |
-  o  A A dir1/a
+  o  A A dir1/a to-be"deleted"
   
+  $ for r in `hg log -T '{rev} ' --rev "sort(all())"`; do
+  >     hg log --rev $r -T "### {tags} ###\n";
+  >     hg diff --change $r --stat;
+  > done
+  ### A ###
+   A              |  1 +
+   dir1/a         |  2 ++
+   to-be"deleted" |  1 +
+   3 files changed, 4 insertions(+), 0 deletions(-)
+  ### B ###
+   B                 |  1 +
+   dir2/b            |  1 +
+   to-be-deleted_too |  1 +
+   3 files changed, 3 insertions(+), 0 deletions(-)
+  ### C tip ###
+   A              |  2 +-
+   B              |  1 +
+   dir1/c         |  1 +
+   dir2/b         |  1 +
+   dir2/c         |  1 +
+   to-be"deleted" |  1 -
+   6 files changed, 5 insertions(+), 2 deletions(-)
+  $ hg files -r C
+  A
+  B
+  dir1/a
+  dir1/c
+  dir2/b
+  dir2/c
   $ for f in `hg files -r C`; do
   >   echo FILE "$f"
   >   hg cat -r C "$f"
diff --git a/tests/test-stream-bundle-v2.t b/tests/test-stream-bundle-v2.t
--- a/tests/test-stream-bundle-v2.t
+++ b/tests/test-stream-bundle-v2.t
@@ -36,6 +36,8 @@
   $ cp $HGRCPATH $TESTTMP/hgrc.orig
 
   $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > portablefilenames=abort
   > [experimental]
   > evolution.createmarkers=True
   > evolution.exchange=True
@@ -60,11 +62,11 @@
   $ hg debugdrawdag <<'EOF'
   > E
   > |
-  > D
+  > D  # D/ba"r=<deleted>
   > |
-  > C
+  > C  # C/ba"r=faz
   > |
-  > B
+  > B  # B/blu=fuz
   > |
   > A
   > EOF
@@ -72,9 +74,9 @@
   $ hg bundle -a --type="none-v2;stream=$stream_version" bundle.hg
   $ hg debugbundle bundle.hg
   Stream params: {}
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
-  stream2 -- {bytecount: 1819, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
+  stream2 -- {bytecount: 1908, filecount: 14, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
+  stream2 -- {bytecount: 1911, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
+  stream2 -- {bytecount: 2103, filecount: 16, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 no-zstd !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 zstd no-rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 rust !)
@@ -89,9 +91,9 @@
   $ hg bundle -a --type="none-$bundle_format" bundle.hg
   $ hg debugbundle bundle.hg
   Stream params: {}
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
-  stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
-  stream2 -- {bytecount: 1819, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
+  stream2 -- {bytecount: 1908, filecount: 14, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
+  stream2 -- {bytecount: 1911, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
+  stream2 -- {bytecount: 2103, filecount: 16, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 no-zstd !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 zstd no-rust !)
   stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 rust !)
@@ -133,8 +135,8 @@
   adding changesets
   adding manifests
   adding file changes
-  added 5 changesets with 5 changes to 5 files
-  new changesets 426bada5c675:9bc730a19041 (5 drafts)
+  added 5 changesets with 7 changes to 7 files
+  new changesets 426bada5c675:92165ab525bf (5 drafts)
 
 known requirements
 ------------------
@@ -193,8 +195,9 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream2" (params: 3 mandatory) supported
   applying stream bundle
-  12 files to transfer, 1.65 KB of data (no-rust !)
-  14 files to transfer, 1.78 KB of data (rust !)
+  14 files to transfer, 1.86 KB of data (no-rust no-zstd !)
+  14 files to transfer, 1.87 KB of data (no-rust zstd !)
+  16 files to transfer, 2.05 KB of data (rust !)
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -202,19 +205,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  bundle2-input-part: total payload size 1857 (no-rust !)
-  bundle2-input-part: total payload size 2025 (rust !)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2099 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2102 (no-rust zstd !)
+  bundle2-input-part: total payload size 2337 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -233,7 +242,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -244,7 +253,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
@@ -261,8 +272,9 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream2" (params: 3 mandatory) supported
   applying stream bundle
-  12 files to transfer, 1.65 KB of data (no-rust !)
-  14 files to transfer, 1.78 KB of data (rust !)
+  14 files to transfer, 1.86 KB of data (no-rust no-zstd !)
+  14 files to transfer, 1.87 KB of data (no-rust zstd !)
+  16 files to transfer, 2.05 KB of data (rust !)
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -270,19 +282,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  bundle2-input-part: total payload size 1857 (no-rust !)
-  bundle2-input-part: total payload size 2025 (rust !)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2099 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2102 (no-rust zstd !)
+  bundle2-input-part: total payload size 2337 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -301,7 +319,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -312,7 +330,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
@@ -327,7 +347,7 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream3-exp" (params: 1 mandatory) supported
   applying stream bundle
-  11 entries to transfer
+  13 entries to transfer
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -335,19 +355,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  bundle2-input-part: total payload size 1869 (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
-  bundle2-input-part: total payload size 2037 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  bundle2-input-part: total payload size 2113 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2116 (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2351 (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -366,7 +392,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -377,7 +403,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
 
@@ -389,7 +417,7 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "stream3-exp" (params: 1 mandatory) supported
   applying stream bundle
-  11 entries to transfer
+  13 entries to transfer
   starting 4 threads for background file closing (?)
   starting 4 threads for background file closing (?)
   adding [s] data/A.i (66 bytes)
@@ -397,19 +425,25 @@
   adding [s] data/C.i (66 bytes)
   adding [s] data/D.i (66 bytes)
   adding [s] data/E.i (66 bytes)
+  adding [s] data/ba"r.i (68 bytes)
+  adding [s] data/blu.i (68 bytes)
   adding [s] phaseroots (43 bytes)
-  adding [s] 00manifest.i (584 bytes)
+  adding [s] 00manifest.i (649 bytes) (no-zstd !)
+  adding [s] 00manifest.i (652 bytes) (zstd no-rust !)
+  adding [s] 00manifest.i (654 bytes) (zstd rust !)
   adding [s] 00changelog.n (62 bytes) (rust !)
-  adding [s] 00changelog-b875dfc5.nd (64 bytes) (rust !)
-  adding [s] 00changelog.d (275 bytes)
+  adding [s] 00changelog-b875dfc5.nd (128 bytes) (rust !)
+  adding [s] 00changelog.d (289 bytes)
   adding [s] 00changelog.i (320 bytes)
   adding [c] branch2-served (94 bytes)
   adding [c] rbc-names-v2 (7 bytes)
   adding [c] rbc-revs-v2 (40 bytes)
-  stream-cloned 12 files / 1.65 KB in * seconds (* */sec) (glob) (no-rust !)
-  bundle2-input-part: total payload size 1869 (no-rust !)
-  stream-cloned 14 files / 1.78 KB in * seconds (* */sec) (glob) (rust !)
-  bundle2-input-part: total payload size 2037 (rust !)
+  stream-cloned 14 files / 1.86 KB in * seconds (* */sec) (glob) (no-rust no-zstd !)
+  stream-cloned 14 files / 1.87 KB in * seconds (* */sec) (glob) (no-rust zstd !)
+  bundle2-input-part: total payload size 2113 (no-rust no-zstd !)
+  bundle2-input-part: total payload size 2116 (no-rust zstd !)
+  stream-cloned 16 files / 2.05 KB in * seconds (* */sec) (glob) (rust !)
+  bundle2-input-part: total payload size 2351 (rust !)
   bundle2-input-bundle: 1 parts total
   updating the branch cache
   finished applying clone bundle
@@ -428,7 +462,7 @@
   updating to branch default
   resolving manifests (no-rust !)
    branchmerge: False, force: False, partial: False (no-rust !)
-   ancestor: 000000000000, local: 000000000000+, remote: 9bc730a19041 (no-rust !)
+   ancestor: 000000000000, local: 000000000000+, remote: 92165ab525bf (no-rust !)
    A: remote created -> g (no-rust !)
   getting A (no-rust !)
    B: remote created -> g (no-rust !)
@@ -439,7 +473,9 @@
   getting D (no-rust !)
    E: remote created -> g (no-rust !)
   getting E (no-rust !)
-  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+   blu: remote created -> g (no-rust !)
+  getting blu (no-rust !)
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating the branch cache
   (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)