# HG changeset patch # User Martin von Zweigbergk # Date 1427835693 25200 # Node ID 701d3554de0e47f224239eac7023f9d7ee13c9c4 # Parent b83679eb5f86a727fbef267711300709164cd2a3 manifestv2: add support for writing new manifest format If .hg/requires has 'manifestv2', the manifest will be written using the new format. diff -r b83679eb5f86 -r 701d3554de0e mercurial/manifest.py --- a/mercurial/manifest.py Fri Mar 27 22:26:41 2015 -0700 +++ b/mercurial/manifest.py Tue Mar 31 14:01:33 2015 -0700 @@ -8,6 +8,7 @@ from i18n import _ import mdiff, parsers, error, revlog, util, scmutil import array, struct +import os propertycache = util.propertycache @@ -58,9 +59,15 @@ else: return iter(_parsev1(data)) -def _text(it): +def _text(it, usemanifestv2): """Given an iterator over (path, node, flags) tuples, returns a manifest text""" + if usemanifestv2: + return _textv2(it) + else: + return _textv1(it) + +def _textv1(it): files = [] lines = [] _hex = revlog.hex @@ -73,6 +80,19 @@ _checkforbidden(files) return ''.join(lines) +def _textv2(it): + files = [] + lines = ['\0\n'] + prevf = '' + for f, n, fl in it: + files.append(f) + stem = os.path.commonprefix([prevf, f]) + stemlen = min(len(stem), 255) + lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n)) + prevf = f + _checkforbidden(files) + return ''.join(lines) + class _lazymanifest(dict): """This is the pure implementation of lazymanifest. @@ -134,7 +154,7 @@ def text(self): """Get the full data of this manifest as a bytestring.""" - return _text(self.iterentries()) + return _textv1(self.iterentries()) try: _lazymanifest = parsers.lazymanifest @@ -259,8 +279,12 @@ def iteritems(self): return (x[:2] for x in self._lm.iterentries()) - def text(self): - return self._lm.text() + def text(self, usemanifestv2=False): + if usemanifestv2: + return _textv2(self._lm.iterentries()) + else: + # use (probably) native version for v1 + return self._lm.text() def fastdelta(self, base, changes): """Given a base manifest text as an array.array and a list of changes @@ -621,10 +645,11 @@ _diff(self, m2) return result - def text(self): + def text(self, usemanifestv2=False): """Get the full data of this manifest as a bytestring.""" flags = self.flags - return _text((f, self[f], flags(f)) for f in self.keys()) + return _text(((f, self[f], flags(f)) for f in self.keys()), + usemanifestv2) class manifest(revlog.revlog): def __init__(self, opener): @@ -720,7 +745,7 @@ # just encode a fulltext of the manifest and pass that # through to the revlog layer, and let it handle the delta # process. - text = m.text() + text = m.text(self._usemanifestv2) arraytext = array.array('c', text) cachedelta = None diff -r b83679eb5f86 -r 701d3554de0e tests/test-manifest.py --- a/tests/test-manifest.py Fri Mar 27 22:26:41 2015 -0700 +++ b/tests/test-manifest.py Tue Mar 31 14:01:33 2015 -0700 @@ -146,6 +146,12 @@ self.assertIn('bar/qux/foz.py', m) self.assertIn(256 * 'x' + '/x', m) self.assertIn(256 * 'x' + '/y', m) + self.assertEqual(A_STEM_COMPRESSED_MANIFEST, m.text(usemanifestv2=True)) + + def testTextV2(self): + m1 = parsemanifest(A_SHORT_MANIFEST) + v2text = m1.text(usemanifestv2=True) + self.assertEqual(A_SHORT_MANIFEST_V2, v2text) def testSetItem(self): want = BIN_HASH_1 diff -r b83679eb5f86 -r 701d3554de0e tests/test-manifestv2.t --- a/tests/test-manifestv2.t Fri Mar 27 22:26:41 2015 -0700 +++ b/tests/test-manifestv2.t Tue Mar 31 14:01:33 2015 -0700 @@ -23,9 +23,9 @@ checking files 3 files, 2 changesets, 4 total revisions -TODO: Check that manifest revlog is smaller than for v1 +Check that manifest revlog is smaller than for v1 $ hg debugindex -m rev offset length base linkrev nodeid p1 p2 - 0 0 106 0 0 f6279f9f8b31 000000000000 000000000000 - 1 106 59 0 1 cd20459b75e6 f6279f9f8b31 000000000000 + 0 0 81 0 0 57361477c778 000000000000 000000000000 + 1 81 33 0 1 aeaab5a2ef74 57361477c778 000000000000