changeset 5878:04a1713c9ef5

branching: merge with stable
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 23 Apr 2021 11:55:42 +0200
parents 1bd2c4f147e1 (current diff) 44c20093fc0e (diff)
children e24bdf74cf83
files tests/test-discovery-obshashrange.t
diffstat 11 files changed, 468 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/hgext3rd/evolve/compat.py	Sun Mar 28 10:20:34 2021 +0200
+++ b/hgext3rd/evolve/compat.py	Fri Apr 23 11:55:42 2021 +0200
@@ -6,7 +6,6 @@
 Compatibility module
 """
 
-import array
 import contextlib
 
 from mercurial import (
@@ -17,7 +16,6 @@
     logcmdutil,
     merge as mergemod,
     obsolete,
-    pycompat,
     registrar,
     repair,
     scmutil,
@@ -25,13 +23,6 @@
     ui as uimod,
 )
 
-if pycompat.ispy3:
-    arraytobytes = array.array.tobytes
-    arrayfrombytes = array.array.frombytes
-else:
-    arraytobytes = array.array.tostring
-    arrayfrombytes = array.array.fromstring
-
 # hg <= 5.2 (c21aca51b392)
 try:
     from mercurial import pathutil
--- a/hgext3rd/evolve/depthcache.py	Sun Mar 28 10:20:34 2021 +0200
+++ b/hgext3rd/evolve/depthcache.py	Fri Apr 23 11:55:42 2021 +0200
@@ -181,20 +181,23 @@
         assert repo.filtername is None
 
         data = repo.cachevfs.tryread(self._filepath)
+        self._cachekey = self.emptykey
         self._data = array.array(r'l')
-        if not data:
-            self._cachekey = self.emptykey
-        else:
+        if data:
             headerdata = data[:self._cachekeysize]
-            self._cachekey = self._deserializecachekey(headerdata)
-            compat.arrayfrombytes(self._data, data[self._cachekeysize:])
+            cachekey = self._deserializecachekey(headerdata)
+            expected = self._datastruct.size * (cachekey[0] + 1)
+            data = data[self._cachekeysize:]
+            if len(data) == expected:
+                self._data.extend(self._deserializedata(data))
+                self._cachekey = cachekey
+            else:
+                repo.ui.debug(b'depthcache file seems to be corrupted, '
+                              b'it will be rebuilt from scratch\n')
         self._ondiskkey = self._cachekey
 
     def save(self, repo):
         """save the data to disk
-
-        Format is pretty simple, we serialise the cache key and then drop the
-        bytearray.
         """
         if self._cachekey is None or self._cachekey == self._ondiskkey:
             return
@@ -203,7 +206,7 @@
             cachefile = repo.cachevfs(self._filepath, b'w', atomictemp=True)
             headerdata = self._serializecachekey()
             cachefile.write(headerdata)
-            cachefile.write(compat.arraytobytes(self._data))
+            cachefile.write(self._serializedata(self._data))
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
--- a/hgext3rd/evolve/firstmergecache.py	Sun Mar 28 10:20:34 2021 +0200
+++ b/hgext3rd/evolve/firstmergecache.py	Fri Apr 23 11:55:42 2021 +0200
@@ -31,6 +31,15 @@
 
 eh = exthelper.exthelper()
 
+@eh.command(b'debugfirstmergecache', [])
+def debugfirstmergecache(ui, repo, **opts):
+    """display the contents of firstmergecache"""
+    cache = repo.firstmergecache
+    cache.save(repo)
+    for r in repo:
+        ctx = repo[r]
+        ui.write(b'%s %d\n' % (ctx, cache.get(r)))
+
 @eh.reposetup
 def setupcache(ui, repo):
 
@@ -120,20 +129,23 @@
         assert repo.filtername is None
 
         data = repo.cachevfs.tryread(self._filepath)
+        self._cachekey = self.emptykey
         self._data = array.array(r'l')
-        if not data:
-            self._cachekey = self.emptykey
-        else:
+        if data:
             headerdata = data[:self._cachekeysize]
-            self._cachekey = self._deserializecachekey(headerdata)
-            compat.arrayfrombytes(self._data, data[self._cachekeysize:])
+            cachekey = self._deserializecachekey(headerdata)
+            expected = self._datastruct.size * (cachekey[0] + 1)
+            data = data[self._cachekeysize:]
+            if len(data) == expected:
+                self._data.extend(self._deserializedata(data))
+                self._cachekey = cachekey
+            else:
+                repo.ui.debug(b'firstmergecache file seems to be corrupted, '
+                              b'it will be rebuilt from scratch\n')
         self._ondiskkey = self._cachekey
 
     def save(self, repo):
         """save the data to disk
-
-        Format is pretty simple, we serialise the cache key and then drop the
-        bytearray.
         """
         if self._cachekey is None or self._cachekey == self._ondiskkey:
             return
@@ -142,7 +154,7 @@
             cachefile = repo.cachevfs(self._filepath, b'w', atomictemp=True)
             headerdata = self._serializecachekey()
             cachefile.write(headerdata)
-            cachefile.write(compat.arraytobytes(self._data))
+            cachefile.write(self._serializedata(self._data))
             cachefile.close()
             self._ondiskkey = self._cachekey
         except (IOError, OSError) as exc:
--- a/hgext3rd/evolve/genericcaches.py	Sun Mar 28 10:20:34 2021 +0200
+++ b/hgext3rd/evolve/genericcaches.py	Fri Apr 23 11:55:42 2021 +0200
@@ -34,6 +34,8 @@
     _cachekeyspec = b'' # used for serialization
     _cachename = None # used for debug message
 
+    _datastruct = struct.Struct('<q')
+
     @abc.abstractmethod
     def __init__(self):
         super(incrementalcachebase, self).__init__()
@@ -133,6 +135,17 @@
         """read the cachekey from bytes"""
         return self._cachekeystruct.unpack(data)
 
+    def _serializedata(self, data):
+        """turn data into binary form"""
+        return b''.join(self._datastruct.pack(item) for item in data)
+
+    def _deserializedata(self, data):
+        """turn binary into data"""
+        return (
+            self._datastruct.unpack_from(data, i)[0]
+            for i in range(0, len(data), self._datastruct.size)
+        )
+
 class changelogsourcebase(incrementalcachebase):  # pytype: disable=ignored-metaclass
     """an abstract class for cache sourcing data from the changelog
 
@@ -143,7 +156,7 @@
     __metaclass__ = abc.ABCMeta
 
     # default key used for an empty cache
-    emptykey = (0, node.nullid)
+    emptykey = (node.nullrev, node.nullid)
     _cachekeyspec = b'i20s'
     _cachename = None # used for debug message
 
--- a/hgext3rd/evolve/stablesort.py	Sun Mar 28 10:20:34 2021 +0200
+++ b/hgext3rd/evolve/stablesort.py	Fri Apr 23 11:55:42 2021 +0200
@@ -340,6 +340,7 @@
     merge = unfi.revs('merge()')
     nbmerge = len(merge)
     cache = unfi.stablesort
+    cache.save(repo)
     ui.write(b'number of merge:     %12d\n' % nbmerge)
     alljumps = []
     alljumpssize = []
@@ -894,20 +895,31 @@
         assert repo.filtername is None
 
         data = repo.cachevfs.tryread(self._filepath)
+        self._cachekey = self.emptykey
         self._index = array.array(r'l')
         self._data = array.array(r'l')
-        if not data:
-            self._cachekey = self.emptykey
-        else:
+        if data:
             headerdata = data[:self._cachekeysize]
-            self._cachekey = self._deserializecachekey(headerdata)
+            cachekey = self._deserializecachekey(headerdata)
             offset = self._cachekeysize
             indexsizedata = data[offset:offset + S_INDEXSIZE.size]
             indexsize = S_INDEXSIZE.unpack(indexsizedata)[0]
             offset += S_INDEXSIZE.size
-            compat.arrayfrombytes(self._index, data[offset:offset + indexsize])
-            offset += indexsize
-            compat.arrayfrombytes(self._data, data[offset:])
+            if indexsize % self._datastruct.size == 0 and len(data) - offset >= indexsize:
+                index = list(self._deserializedata(data[offset:offset + indexsize]))
+                offset += indexsize
+                expected = index[-1] * self._datastruct.size * 3
+                data = data[offset:]
+            else:
+                # index cannot be read, so we need to abort somehow
+                expected = None
+            if len(data) == expected:
+                self._index.extend(index)
+                self._data.extend(self._deserializedata(data))
+                self._cachekey = cachekey
+            else:
+                repo.ui.debug(b'stablesortcache file seems to be corrupted, '
+                              b'it will be rebuilt from scratch\n')
         self._ondiskkey = self._cachekey
         pass
 
@@ -923,8 +935,8 @@
 
             # data to write
             headerdata = self._serializecachekey()
-            indexdata = compat.arraytobytes(self._index)
-            data = compat.arraytobytes(self._data)
+            indexdata = self._serializedata(self._index)
+            data = self._serializedata(self._data)
             indexsize = S_INDEXSIZE.pack(len(indexdata))
 
             # writing
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-cache-corruption.t	Fri Apr 23 11:55:42 2021 +0200
@@ -0,0 +1,360 @@
+Testing cache corruption and recovery
+https://bz.mercurial-scm.org/show_bug.cgi?id=6354
+
+  $ . $TESTDIR/testlib/pythonpath.sh
+
+  $ cat << EOF >> $HGRCPATH
+  > [extensions]
+  > evolve =
+  > [experimental]
+  > obshashrange = 1
+  > obshashrange.warm-cache = yes
+  > [ui]
+  > logtemplate = "{rev} {node|short} {desc} {tags}\n"
+  > EOF
+
+  $ cat >> repack.py << EOF
+  > import struct
+  > import sys
+  > # imitating array.array().tobytes() with a platform-dependent item size
+  > sixtyfour = struct.Struct('<q') # as seen on 64-bit platforms
+  > thirtytwo = struct.Struct('<l') # as seen on 32-bit platforms
+  > iss = struct.Struct('<I') # for rewriting indexsize of stablesortcache
+  > data = []
+  > with open(sys.argv[1], 'rb') as f:
+  >     header = f.read(24)
+  >     if '--index' in sys.argv:
+  >         indexsize = iss.unpack(f.read(iss.size))[0]
+  >     while True:
+  >         buf = f.read(sixtyfour.size)
+  >         if not buf: break
+  >         data.append(sixtyfour.unpack(buf)[0])
+  > with open(sys.argv[1], 'wb') as f:
+  >     f.write(header)
+  >     if '--index' in sys.argv:
+  >         indexsize = int(indexsize * thirtytwo.size / sixtyfour.size)
+  >         f.write(iss.pack(indexsize))
+  >     for item in data:
+  >         f.write(thirtytwo.pack(item))
+  > EOF
+
+  $ cat >> truncate.py << EOF
+  > import os
+  > import sys
+  > with open(sys.argv[1], 'ab') as fp:
+  >     fp.seek(int(sys.argv[2]), os.SEEK_END)
+  >     fp.truncate()
+  > EOF
+
+Simple linear setup
+
+  $ hg init linear
+  $ cd linear
+
+  $ hg debugbuilddag '+3'
+  $ hg log -G
+  o  2 01241442b3c2 r2 tip
+  |
+  o  1 66f7d451a68b r1
+  |
+  o  0 1ea73414a91b r0
+  
+  $ f -s .hg/cache/evoext-*
+  .hg/cache/evoext-depthcache-00: size=48
+  .hg/cache/evoext-firstmerge-00: size=48
+  .hg/cache/evoext-obscache-00: size=67
+  .hg/cache/evoext-stablesortcache-00: size=52
+
+testing depthcache
+
+  $ f -H .hg/cache/evoext-depthcache-00
+  .hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 01 00 00 00 00 00 00 00 |I.U.e...........|
+  0020: 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 |................|
+
+  $ hg debugdepth --rev 'all()' --method compare --debug
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+
+  $ "$PYTHON" ../repack.py .hg/cache/evoext-depthcache-00
+  $ f -H .hg/cache/evoext-depthcache-00
+  .hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 01 00 00 00 02 00 00 00 |I.U.e...........|
+  0020: 03 00 00 00                                     |....|
+
+  $ hg debugdepth --rev 'all()' --method compare --debug
+  depthcache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+
+  $ "$PYTHON" ../truncate.py .hg/cache/evoext-depthcache-00 -4
+  $ f -H .hg/cache/evoext-depthcache-00
+  .hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 01 00 00 00 00 00 00 00 |I.U.e...........|
+  0020: 02 00 00 00 00 00 00 00 03 00 00 00             |............|
+
+  $ hg debugdepth --rev 'all()' --method compare --debug
+  depthcache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b 1
+  66f7d451a68b 2
+  01241442b3c2 3
+
+testing firstmergecache
+
+  $ f -H .hg/cache/evoext-firstmerge-00
+  .hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 ff ff ff ff ff ff ff ff |I.U.e...........|
+  0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
+
+  $ hg debugfirstmergecache --debug
+  1ea73414a91b -1
+  66f7d451a68b -1
+  01241442b3c2 -1
+
+  $ "$PYTHON" ../repack.py .hg/cache/evoext-firstmerge-00
+  $ f -H .hg/cache/evoext-firstmerge-00
+  .hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 ff ff ff ff ff ff ff ff |I.U.e...........|
+  0020: ff ff ff ff                                     |....|
+
+  $ hg debugfirstmergecache --debug
+  firstmergecache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b -1
+  66f7d451a68b -1
+  01241442b3c2 -1
+
+  $ "$PYTHON" ../truncate.py .hg/cache/evoext-firstmerge-00 -4
+  $ f -H .hg/cache/evoext-firstmerge-00
+  .hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 ff ff ff ff ff ff ff ff |I.U.e...........|
+  0020: ff ff ff ff ff ff ff ff ff ff ff ff             |............|
+
+  $ hg debugfirstmergecache --debug
+  firstmergecache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b -1
+  66f7d451a68b -1
+  01241442b3c2 -1
+
+testing stablesortcache
+
+  $ f -H .hg/cache/evoext-stablesortcache-00
+  .hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 00 00 00 18 00 00 00 00 |I.U.e...........|
+  0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+  0030: 00 00 00 00                                     |....|
+
+  $ hg debugstablesortcache --debug
+  number of revisions:            3
+  number of merge:                0
+  number of jumps:                0
+
+  $ "$PYTHON" ../repack.py .hg/cache/evoext-stablesortcache-00 --index
+  $ f -H .hg/cache/evoext-stablesortcache-00
+  .hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 00 00 00 0c 00 00 00 00 |I.U.e...........|
+  0020: 00 00 00 00 00 00 00 00                         |........|
+
+  $ hg debugstablesortcache --debug
+  number of revisions:            3
+  stablesortcache file seems to be corrupted, it will be rebuilt from scratch
+  number of merge:                0
+  number of jumps:                0
+
+  $ "$PYTHON" ../truncate.py .hg/cache/evoext-stablesortcache-00 -4
+  $ f -H .hg/cache/evoext-stablesortcache-00
+  .hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 02 01 24 14 42 b3 c2 bf 32 11 e5 93 b5 |.....$.B...2....|
+  0010: 49 c6 55 ea 65 b2 95 e3 00 00 00 18 00 00 00 00 |I.U.e...........|
+  0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+
+  $ hg debugstablesortcache --debug
+  number of revisions:            3
+  stablesortcache file seems to be corrupted, it will be rebuilt from scratch
+  number of merge:                0
+  number of jumps:                0
+
+  $ cd ..
+
+A "diamond" setup with a merge
+
+  $ hg init with-a-merge
+  $ cd with-a-merge
+
+  $ hg debugbuilddag '+2 *2 /2'
+  $ hg log -G
+  o    3 2b6d669947cd r3 tip
+  |\
+  | o  2 fa942426a6fd r2
+  | |
+  o |  1 66f7d451a68b r1
+  |/
+  o  0 1ea73414a91b r0
+  
+  $ f -s .hg/cache/evoext-*
+  .hg/cache/evoext-depthcache-00: size=56
+  .hg/cache/evoext-firstmerge-00: size=56
+  .hg/cache/evoext-obscache-00: size=68
+  .hg/cache/evoext-stablesortcache-00: size=84
+
+testing depthcache
+
+  $ f -H .hg/cache/evoext-depthcache-00
+  .hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef 01 00 00 00 00 00 00 00 |.....p~.........|
+  0020: 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
+  0030: 04 00 00 00 00 00 00 00                         |........|
+
+  $ hg debugdepth --rev 'all()' --method compare --debug
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+
+  $ "$PYTHON" ../repack.py .hg/cache/evoext-depthcache-00
+  $ f -H .hg/cache/evoext-depthcache-00
+  .hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef 01 00 00 00 02 00 00 00 |.....p~.........|
+  0020: 02 00 00 00 04 00 00 00                         |........|
+
+  $ hg debugdepth --rev 'all()' --method compare --debug
+  depthcache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+
+  $ "$PYTHON" ../truncate.py .hg/cache/evoext-depthcache-00 -4
+  $ f -H .hg/cache/evoext-depthcache-00
+  .hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef 01 00 00 00 00 00 00 00 |.....p~.........|
+  0020: 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
+  0030: 04 00 00 00                                     |....|
+
+  $ hg debugdepth --rev 'all()' --method compare --debug
+  depthcache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b 1
+  66f7d451a68b 2
+  fa942426a6fd 2
+  2b6d669947cd 4
+
+testing firstmergecache
+
+  $ f -H .hg/cache/evoext-firstmerge-00
+  .hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef ff ff ff ff ff ff ff ff |.....p~.........|
+  0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
+  0030: 03 00 00 00 00 00 00 00                         |........|
+
+  $ hg debugfirstmergecache --debug
+  1ea73414a91b -1
+  66f7d451a68b -1
+  fa942426a6fd -1
+  2b6d669947cd 3
+
+  $ "$PYTHON" ../repack.py .hg/cache/evoext-firstmerge-00
+  $ f -H .hg/cache/evoext-firstmerge-00
+  .hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef ff ff ff ff ff ff ff ff |.....p~.........|
+  0020: ff ff ff ff 03 00 00 00                         |........|
+
+  $ hg debugfirstmergecache --debug
+  firstmergecache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b -1
+  66f7d451a68b -1
+  fa942426a6fd -1
+  2b6d669947cd 3
+
+  $ "$PYTHON" ../truncate.py .hg/cache/evoext-firstmerge-00 -4
+  $ f -H .hg/cache/evoext-firstmerge-00
+  .hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef ff ff ff ff ff ff ff ff |.....p~.........|
+  0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
+  0030: 03 00 00 00                                     |....|
+
+  $ hg debugfirstmergecache --debug
+  firstmergecache file seems to be corrupted, it will be rebuilt from scratch
+  1ea73414a91b -1
+  66f7d451a68b -1
+  fa942426a6fd -1
+  2b6d669947cd 3
+
+testing stablesortcache
+
+  $ f -H .hg/cache/evoext-stablesortcache-00
+  .hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef 00 00 00 20 00 00 00 00 |.....p~.... ....|
+  0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+  0030: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
+  0040: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
+  0050: 00 00 00 00                                     |....|
+
+  $ hg debugstablesortcache --debug
+  number of revisions:            4
+  number of merge:                1
+  number of jumps:                1
+  average jumps:                  1.000
+  median jumps:                   1
+  90% jumps:                      1
+  99% jumps:                      1
+  max jumps:                      1
+  jump cache size:               12 bytes
+
+  $ "$PYTHON" ../repack.py .hg/cache/evoext-stablesortcache-00 --index
+  $ f -H .hg/cache/evoext-stablesortcache-00
+  .hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef 00 00 00 10 00 00 00 00 |.....p~.........|
+  0020: 00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 |................|
+  0030: 01 00 00 00 02 00 00 00                         |........|
+
+  $ hg debugstablesortcache --debug
+  number of revisions:            4
+  stablesortcache file seems to be corrupted, it will be rebuilt from scratch
+  number of merge:                1
+  number of jumps:                1
+  average jumps:                  1.000
+  median jumps:                   1
+  90% jumps:                      1
+  99% jumps:                      1
+  max jumps:                      1
+  jump cache size:               12 bytes
+
+  $ "$PYTHON" ../truncate.py .hg/cache/evoext-stablesortcache-00 -4
+  $ f -H .hg/cache/evoext-stablesortcache-00
+  .hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 03 2b 6d 66 99 47 cd 52 4d 74 f4 3c 1b |....+mf.G.RMt.<.|
+  0010: 11 c7 84 85 89 70 7e ef 00 00 00 20 00 00 00 00 |.....p~.... ....|
+  0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+  0030: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
+  0040: 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 |................|
+
+  $ hg debugstablesortcache --debug
+  number of revisions:            4
+  stablesortcache file seems to be corrupted, it will be rebuilt from scratch
+  number of merge:                1
+  number of jumps:                1
+  average jumps:                  1.000
+  median jumps:                   1
+  90% jumps:                      1
+  99% jumps:                      1
+  max jumps:                      1
+  jump cache size:               12 bytes
+
+  $ cd ..
--- a/tests/test-check-sdist.t	Sun Mar 28 10:20:34 2021 +0200
+++ b/tests/test-check-sdist.t	Fri Apr 23 11:55:42 2021 +0200
@@ -35,7 +35,7 @@
 
   $ tar -tzf hg-evolve-*.tar.gz | sed 's|^hg-evolve-[^/]*/||' | sort > files
   $ wc -l files
-  347 files
+  348 files
   $ fgrep debian files
   tests/test-check-debian.t
   $ fgrep __init__.py files
--- a/tests/test-discovery-obshashrange-cache.t	Sun Mar 28 10:20:34 2021 +0200
+++ b/tests/test-discovery-obshashrange-cache.t	Fri Apr 23 11:55:42 2021 +0200
@@ -169,3 +169,39 @@
   main/.hg/cache/evoext-stablesortcache-00: size=92
   main/.hg/cache/evoext_obshashrange_v2.sqlite: size=?* (glob)
   main/.hg/cache/evoext_stablerange_v2.sqlite: size=?* (glob)
+
+let's look at the contents of the caches
+
+the reason we're doing this is to make sure our serialization works the same
+way on all platforms, see https://bz.mercurial-scm.org/show_bug.cgi?id=6354
+
+we don't need to check sqlite caches
+
+  $ f -H main/.hg/cache/evoext-*
+  main/.hg/cache/evoext-depthcache-00:
+  0000: 00 00 00 07 4d e3 2a 90 b6 6c d0 83 eb f3 c0 0b |....M.*..l......|
+  0010: 41 27 7a a7 ab ca 51 dd 01 00 00 00 00 00 00 00 |A'z...Q.........|
+  0020: 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 |................|
+  0030: 04 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 |................|
+  0040: 06 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 |................|
+  0050: 08 00 00 00 00 00 00 00                         |........|
+  main/.hg/cache/evoext-firstmerge-00:
+  0000: 00 00 00 07 4d e3 2a 90 b6 6c d0 83 eb f3 c0 0b |....M.*..l......|
+  0010: 41 27 7a a7 ab ca 51 dd ff ff ff ff ff ff ff ff |A'z...Q.........|
+  0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
+  0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
+  0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
+  0050: ff ff ff ff ff ff ff ff                         |........|
+  main/.hg/cache/evoext-obscache-00:
+  0000: 00 00 00 00 00 00 00 07 4d e3 2a 90 b6 6c d0 83 |........M.*..l..|
+  0010: eb f3 c0 0b 41 27 7a a7 ab ca 51 dd 00 00 00 00 |....A'z...Q.....|
+  0020: 00 00 00 08 00 00 00 00 00 00 02 29 4c 16 b4 10 |...........)L...|
+  0030: 03 f6 c1 57 8d 58 25 ef c7 73 cd 79 03 4b fb 46 |...W.X%..s.y.K.F|
+  0040: 00 00 00 00 00 00 00 00                         |........|
+  main/.hg/cache/evoext-stablesortcache-00:
+  0000: 00 00 00 07 4d e3 2a 90 b6 6c d0 83 eb f3 c0 0b |....M.*..l......|
+  0010: 41 27 7a a7 ab ca 51 dd 00 00 00 40 00 00 00 00 |A'z...Q....@....|
+  0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+  0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+  0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+  0050: 00 00 00 00 00 00 00 00 00 00 00 00             |............|
--- a/tests/test-discovery-obshashrange.t	Sun Mar 28 10:20:34 2021 +0200
+++ b/tests/test-discovery-obshashrange.t	Fri Apr 23 11:55:42 2021 +0200
@@ -134,14 +134,10 @@
   (run 'hg update' to get a working copy)
   $ hg -R ../server blackbox
   * @0000000000000000000000000000000000000000 (*)> debugobshashrange --subranges --rev tip (glob)
-  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
-  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
   1970/01/01 00:00:00 * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (8r, 5o) (glob)
-  1970/01/01 00:00:00 * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-stablesort cache reset (glob)
   1970/01/01 00:00:00 * @0000000000000000000000000000000000000000 (*)> updated evo-ext-stablesort in *.???? seconds (8r) (glob)
-  1970/01/01 00:00:00 * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-firstmerge cache reset (glob)
   1970/01/01 00:00:00 * @0000000000000000000000000000000000000000 (*)> updated evo-ext-firstmerge in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> debugobshashrange --subranges --rev tip exited 0 after *.?? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> -R server serve --stdio (glob)
@@ -154,14 +150,6 @@
   cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   $ rm ../server/.hg/blackbox.log
   $ hg blackbox
-  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-firstmerge cache reset (glob)
-  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-firstmerge in *.???? seconds (0r) (glob)
-  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
-  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (0r) (glob)
-  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-stablesort cache reset (glob)
-  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-stablesort in *.???? seconds (0r) (glob)
-  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
-  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (0r) (glob)
   * @0000000000000000000000000000000000000000 (*)> 0/0 cache hits/lookups in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> clone *ssh://user@dummy/server* client exited 0 after *.?? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> pull --rev 4 (glob)
@@ -334,7 +322,6 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> preparing listkeys for "namespaces" (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending listkeys command (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> received listkey for "namespaces": 40 bytes (glob)
-  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (6r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (6r) (glob)
   1970/01/01 00:00:00 * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (6r, 4o) (glob)
@@ -1068,7 +1055,6 @@
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G exited 0 after *.?? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> pull (glob)
-  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-depthcache cache reset (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
--- a/tests/test-topic-server.t	Sun Mar 28 10:20:34 2021 +0200
+++ b/tests/test-topic-server.t	Fri Apr 23 11:55:42 2021 +0200
@@ -48,7 +48,7 @@
   Mercurial Distributed SCM (*) (glob)
   (see https://mercurial-scm.org for more information)
   
-  Copyright (C) 2005-* Matt Mackall and others (glob)
+  Copyright (C) 2005-* (glob)
   This is free software; see the source for copying conditions. There is NO
   warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   
@@ -75,7 +75,7 @@
   Mercurial Distributed SCM (*) (glob)
   (see https://mercurial-scm.org for more information)
   
-  Copyright (C) 2005-* Matt Mackall and others (glob)
+  Copyright (C) 2005-* (glob)
   This is free software; see the source for copying conditions. There is NO
   warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   
--- a/tests/test-version-install.t	Sun Mar 28 10:20:34 2021 +0200
+++ b/tests/test-version-install.t	Fri Apr 23 11:55:42 2021 +0200
@@ -9,7 +9,7 @@
   Mercurial Distributed SCM (version *) (glob)
   (see https://mercurial-scm.org for more information)
   
-  Copyright (C) 2005-* Matt Mackall and others (glob)
+  Copyright (C) 2005-* (glob)
   This is free software; see the source for copying conditions. There is NO
   warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.