Show More
@@ -538,9 +538,6 b" coreconfigitem('experimental', 'hook-tra" | |||
|
538 | 538 | coreconfigitem('experimental', 'httppostargs', |
|
539 | 539 | default=False, |
|
540 | 540 | ) |
|
541 | coreconfigitem('experimental', 'manifestv2', | |
|
542 | default=False, | |
|
543 | ) | |
|
544 | 541 | coreconfigitem('experimental', 'mergedriver', |
|
545 | 542 | default=None, |
|
546 | 543 | ) |
@@ -1,4 +1,3 b'' | |||
|
1 | ||
|
2 | 1 | Repositories contain a file (``.hg/requires``) containing a list of |
|
3 | 2 | features/capabilities that are *required* for clients to interface |
|
4 | 3 | with the repository. This file has been present in Mercurial since |
@@ -105,8 +104,10 b' manifestv2' | |||
|
105 | 104 | Denotes that version 2 of manifests are being used. |
|
106 | 105 | |
|
107 | 106 | Support for this requirement was added in Mercurial 3.4 (released |
|
108 | May 2015). The requirement is currently experimental and is disabled | |
|
109 | by default. | |
|
107 | May 2015). The new format failed to meet expectations and support | |
|
108 | for the format and requirement were removed in Mercurial 4.6 | |
|
109 | (released May 2018) since the feature never graduated frome experiment | |
|
110 | status. | |
|
110 | 111 | |
|
111 | 112 | treemanifest |
|
112 | 113 | ============ |
@@ -303,11 +303,15 b" REVLOGV2_REQUIREMENT = 'exp-revlogv2.0'" | |||
|
303 | 303 | |
|
304 | 304 | class localrepository(object): |
|
305 | 305 | |
|
306 | # obsolete experimental requirements: | |
|
307 | # - manifestv2: An experimental new manifest format that allowed | |
|
308 | # for stem compression of long paths. Experiment ended up not | |
|
309 | # being successful (repository sizes went up due to worse delta | |
|
310 | # chains), and the code was deleted in 4.6. | |
|
306 | 311 | supportedformats = { |
|
307 | 312 | 'revlogv1', |
|
308 | 313 | 'generaldelta', |
|
309 | 314 | 'treemanifest', |
|
310 | 'manifestv2', | |
|
311 | 315 | REVLOGV2_REQUIREMENT, |
|
312 | 316 | } |
|
313 | 317 | _basesupported = supportedformats | { |
@@ -322,7 +326,6 b' class localrepository(object):' | |||
|
322 | 326 | 'revlogv1', |
|
323 | 327 | 'generaldelta', |
|
324 | 328 | 'treemanifest', |
|
325 | 'manifestv2', | |
|
326 | 329 | } |
|
327 | 330 | |
|
328 | 331 | # a list of (ui, featureset) functions. |
@@ -2261,8 +2264,6 b' def newreporequirements(repo):' | |||
|
2261 | 2264 | requirements.add('generaldelta') |
|
2262 | 2265 | if ui.configbool('experimental', 'treemanifest'): |
|
2263 | 2266 | requirements.add('treemanifest') |
|
2264 | if ui.configbool('experimental', 'manifestv2'): | |
|
2265 | requirements.add('manifestv2') | |
|
2266 | 2267 | |
|
2267 | 2268 | revlogv2 = ui.config('experimental', 'revlogv2') |
|
2268 | 2269 | if revlogv2 == 'enable-unstable-format-and-corrupt-my-data': |
@@ -9,7 +9,6 b' from __future__ import absolute_import' | |||
|
9 | 9 | |
|
10 | 10 | import heapq |
|
11 | 11 | import itertools |
|
12 | import os | |
|
13 | 12 | import struct |
|
14 | 13 | |
|
15 | 14 | from .i18n import _ |
@@ -28,7 +27,7 b' from . import (' | |||
|
28 | 27 | parsers = policy.importmod(r'parsers') |
|
29 | 28 | propertycache = util.propertycache |
|
30 | 29 | |
|
31 |
def _parse |
|
|
30 | def _parse(data): | |
|
32 | 31 | # This method does a little bit of excessive-looking |
|
33 | 32 | # precondition checking. This is so that the behavior of this |
|
34 | 33 | # class exactly matches its C counterpart to try and help |
@@ -47,43 +46,7 b' def _parsev1(data):' | |||
|
47 | 46 | else: |
|
48 | 47 | yield f, bin(n), '' |
|
49 | 48 | |
|
50 |
def _ |
|
|
51 | metadataend = data.find('\n') | |
|
52 | # Just ignore metadata for now | |
|
53 | pos = metadataend + 1 | |
|
54 | prevf = '' | |
|
55 | while pos < len(data): | |
|
56 | end = data.find('\n', pos + 1) # +1 to skip stem length byte | |
|
57 | if end == -1: | |
|
58 | raise ValueError('Manifest ended with incomplete file entry.') | |
|
59 | stemlen = ord(data[pos:pos + 1]) | |
|
60 | items = data[pos + 1:end].split('\0') | |
|
61 | f = prevf[:stemlen] + items[0] | |
|
62 | if prevf > f: | |
|
63 | raise ValueError('Manifest entries not in sorted order.') | |
|
64 | fl = items[1] | |
|
65 | # Just ignore metadata (items[2:] for now) | |
|
66 | n = data[end + 1:end + 21] | |
|
67 | yield f, n, fl | |
|
68 | pos = end + 22 | |
|
69 | prevf = f | |
|
70 | ||
|
71 | def _parse(data): | |
|
72 | """Generates (path, node, flags) tuples from a manifest text""" | |
|
73 | if data.startswith('\0'): | |
|
74 | return iter(_parsev2(data)) | |
|
75 | else: | |
|
76 | return iter(_parsev1(data)) | |
|
77 | ||
|
78 | def _text(it, usemanifestv2): | |
|
79 | """Given an iterator over (path, node, flags) tuples, returns a manifest | |
|
80 | text""" | |
|
81 | if usemanifestv2: | |
|
82 | return _textv2(it) | |
|
83 | else: | |
|
84 | return _textv1(it) | |
|
85 | ||
|
86 | def _textv1(it): | |
|
49 | def _text(it): | |
|
87 | 50 | files = [] |
|
88 | 51 | lines = [] |
|
89 | 52 | _hex = revlog.hex |
@@ -96,19 +59,6 b' def _textv1(it):' | |||
|
96 | 59 | _checkforbidden(files) |
|
97 | 60 | return ''.join(lines) |
|
98 | 61 | |
|
99 | def _textv2(it): | |
|
100 | files = [] | |
|
101 | lines = ['\0\n'] | |
|
102 | prevf = '' | |
|
103 | for f, n, fl in it: | |
|
104 | files.append(f) | |
|
105 | stem = os.path.commonprefix([prevf, f]) | |
|
106 | stemlen = min(len(stem), 255) | |
|
107 | lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n)) | |
|
108 | prevf = f | |
|
109 | _checkforbidden(files) | |
|
110 | return ''.join(lines) | |
|
111 | ||
|
112 | 62 | class lazymanifestiter(object): |
|
113 | 63 | def __init__(self, lm): |
|
114 | 64 | self.pos = 0 |
@@ -414,13 +364,7 b' except AttributeError:' | |||
|
414 | 364 | |
|
415 | 365 | class manifestdict(object): |
|
416 | 366 | def __init__(self, data=''): |
|
417 | if data.startswith('\0'): | |
|
418 | #_lazymanifest can not parse v2 | |
|
419 | self._lm = _lazymanifest('') | |
|
420 | for f, n, fl in _parsev2(data): | |
|
421 | self._lm[f] = n, fl | |
|
422 | else: | |
|
423 | self._lm = _lazymanifest(data) | |
|
367 | self._lm = _lazymanifest(data) | |
|
424 | 368 | |
|
425 | 369 | def __getitem__(self, key): |
|
426 | 370 | return self._lm[key][0] |
@@ -589,12 +533,9 b' class manifestdict(object):' | |||
|
589 | 533 | def iterentries(self): |
|
590 | 534 | return self._lm.iterentries() |
|
591 | 535 | |
|
592 |
def text(self |
|
|
593 | if usemanifestv2: | |
|
594 |
|
|
|
595 | else: | |
|
596 | # use (probably) native version for v1 | |
|
597 | return self._lm.text() | |
|
536 | def text(self): | |
|
537 | # most likely uses native version | |
|
538 | return self._lm.text() | |
|
598 | 539 | |
|
599 | 540 | def fastdelta(self, base, changes): |
|
600 | 541 | """Given a base manifest text as a bytearray and a list of changes |
@@ -1138,12 +1079,12 b' class treemanifest(object):' | |||
|
1138 | 1079 | if fl: |
|
1139 | 1080 | self._flags[f] = fl |
|
1140 | 1081 | |
|
1141 |
def text(self |
|
|
1082 | def text(self): | |
|
1142 | 1083 | """Get the full data of this manifest as a bytestring.""" |
|
1143 | 1084 | self._load() |
|
1144 |
return _text(self.iterentries() |
|
|
1085 | return _text(self.iterentries()) | |
|
1145 | 1086 | |
|
1146 |
def dirtext(self |
|
|
1087 | def dirtext(self): | |
|
1147 | 1088 | """Get the full data of this directory as a bytestring. Make sure that |
|
1148 | 1089 | any submanifests have been written first, so their nodeids are correct. |
|
1149 | 1090 | """ |
@@ -1151,7 +1092,7 b' class treemanifest(object):' | |||
|
1151 | 1092 | flags = self.flags |
|
1152 | 1093 | dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs] |
|
1153 | 1094 | files = [(f, self._files[f], flags(f)) for f in self._files] |
|
1154 |
return _text(sorted(dirs + files) |
|
|
1095 | return _text(sorted(dirs + files)) | |
|
1155 | 1096 | |
|
1156 | 1097 | def read(self, gettext, readsubtree): |
|
1157 | 1098 | def _load_for_read(s): |
@@ -1208,15 +1149,12 b' class manifestrevlog(revlog.revlog):' | |||
|
1208 | 1149 | # stacks of commits, the number can go up, hence the config knob below. |
|
1209 | 1150 | cachesize = 4 |
|
1210 | 1151 | optiontreemanifest = False |
|
1211 | usemanifestv2 = False | |
|
1212 | 1152 | opts = getattr(opener, 'options', None) |
|
1213 | 1153 | if opts is not None: |
|
1214 | 1154 | cachesize = opts.get('manifestcachesize', cachesize) |
|
1215 | 1155 | optiontreemanifest = opts.get('treemanifest', False) |
|
1216 | usemanifestv2 = opts.get('manifestv2', usemanifestv2) | |
|
1217 | 1156 | |
|
1218 | 1157 | self._treeondisk = optiontreemanifest or treemanifest |
|
1219 | self._usemanifestv2 = usemanifestv2 | |
|
1220 | 1158 | |
|
1221 | 1159 | self._fulltextcache = util.lrucachedict(cachesize) |
|
1222 | 1160 | |
@@ -1262,8 +1200,7 b' class manifestrevlog(revlog.revlog):' | |||
|
1262 | 1200 | return self._dirlogcache[d] |
|
1263 | 1201 | |
|
1264 | 1202 | def add(self, m, transaction, link, p1, p2, added, removed, readtree=None): |
|
1265 |
if |
|
|
1266 | and not self._usemanifestv2): | |
|
1203 | if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'): | |
|
1267 | 1204 | # If our first parent is in the manifest cache, we can |
|
1268 | 1205 | # compute a delta here using properties we know about the |
|
1269 | 1206 | # manifest up-front, which may save time later for the |
@@ -1290,7 +1227,7 b' class manifestrevlog(revlog.revlog):' | |||
|
1290 | 1227 | n = self._addtree(m, transaction, link, m1, m2, readtree) |
|
1291 | 1228 | arraytext = None |
|
1292 | 1229 | else: |
|
1293 |
text = m.text( |
|
|
1230 | text = m.text() | |
|
1294 | 1231 | n = self.addrevision(text, transaction, link, p1, p2) |
|
1295 | 1232 | arraytext = bytearray(text) |
|
1296 | 1233 | |
@@ -1309,13 +1246,13 b' class manifestrevlog(revlog.revlog):' | |||
|
1309 | 1246 | sublog.add(subm, transaction, link, subp1, subp2, None, None, |
|
1310 | 1247 | readtree=readtree) |
|
1311 | 1248 | m.writesubtrees(m1, m2, writesubtree) |
|
1312 |
text = m.dirtext( |
|
|
1249 | text = m.dirtext() | |
|
1313 | 1250 | n = None |
|
1314 | 1251 | if self._dir != '': |
|
1315 | 1252 | # Double-check whether contents are unchanged to one parent |
|
1316 |
if text == m1.dirtext( |
|
|
1253 | if text == m1.dirtext(): | |
|
1317 | 1254 | n = m1.node() |
|
1318 |
elif text == m2.dirtext( |
|
|
1255 | elif text == m2.dirtext(): | |
|
1319 | 1256 | n = m2.node() |
|
1320 | 1257 | |
|
1321 | 1258 | if not n: |
@@ -1493,19 +1430,6 b' class manifestctx(object):' | |||
|
1493 | 1430 | Changing the value of `shallow` has no effect on flat manifests. |
|
1494 | 1431 | ''' |
|
1495 | 1432 | revlog = self._revlog() |
|
1496 | if revlog._usemanifestv2: | |
|
1497 | # Need to perform a slow delta | |
|
1498 | r0 = revlog.deltaparent(revlog.rev(self._node)) | |
|
1499 | m0 = self._manifestlog[revlog.node(r0)].read() | |
|
1500 | m1 = self.read() | |
|
1501 | md = manifestdict() | |
|
1502 | for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems(): | |
|
1503 | if n1: | |
|
1504 | md[f] = n1 | |
|
1505 | if fl1: | |
|
1506 | md.setflag(f, fl1) | |
|
1507 | return md | |
|
1508 | ||
|
1509 | 1433 | r = revlog.rev(self._node) |
|
1510 | 1434 | d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r)) |
|
1511 | 1435 | return manifestdict(d) |
@@ -1608,7 +1532,7 b' class treemanifestctx(object):' | |||
|
1608 | 1532 | its 't' flag. |
|
1609 | 1533 | ''' |
|
1610 | 1534 | revlog = self._revlog() |
|
1611 | if shallow and not revlog._usemanifestv2: | |
|
1535 | if shallow: | |
|
1612 | 1536 | r = revlog.rev(self._node) |
|
1613 | 1537 | d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r)) |
|
1614 | 1538 | return manifestdict(d) |
@@ -46,7 +46,6 b' def blocksourcerequirements(repo):' | |||
|
46 | 46 | return { |
|
47 | 47 | # The upgrade code does not yet support these experimental features. |
|
48 | 48 | # This is an artificial limitation. |
|
49 | 'manifestv2', | |
|
50 | 49 | 'treemanifest', |
|
51 | 50 | # This was a precursor to generaldelta and was never enabled by default. |
|
52 | 51 | # It should (hopefully) not exist in the wild. |
@@ -11,7 +11,6 b' from mercurial import (' | |||
|
11 | 11 | ) |
|
12 | 12 | |
|
13 | 13 | EMTPY_MANIFEST = b'' |
|
14 | EMTPY_MANIFEST_V2 = b'\0\n' | |
|
15 | 14 | |
|
16 | 15 | HASH_1 = b'1' * 40 |
|
17 | 16 | BIN_HASH_1 = binascii.unhexlify(HASH_1) |
@@ -28,42 +27,6 b' A_SHORT_MANIFEST = (' | |||
|
28 | 27 | b'flag2': b'l', |
|
29 | 28 | } |
|
30 | 29 | |
|
31 | # Same data as A_SHORT_MANIFEST | |
|
32 | A_SHORT_MANIFEST_V2 = ( | |
|
33 | b'\0\n' | |
|
34 | b'\x00bar/baz/qux.py\0%(flag2)s\n%(hash2)s\n' | |
|
35 | b'\x00foo\0%(flag1)s\n%(hash1)s\n' | |
|
36 | ) % {b'hash1': BIN_HASH_1, | |
|
37 | b'flag1': b'', | |
|
38 | b'hash2': BIN_HASH_2, | |
|
39 | b'flag2': b'l', | |
|
40 | } | |
|
41 | ||
|
42 | # Same data as A_SHORT_MANIFEST | |
|
43 | A_METADATA_MANIFEST = ( | |
|
44 | b'\0foo\0bar\n' | |
|
45 | b'\x00bar/baz/qux.py\0%(flag2)s\0foo\0bar\n%(hash2)s\n' # flag and metadata | |
|
46 | b'\x00foo\0%(flag1)s\0foo\n%(hash1)s\n' # no flag, but metadata | |
|
47 | ) % {b'hash1': BIN_HASH_1, | |
|
48 | b'flag1': b'', | |
|
49 | b'hash2': BIN_HASH_2, | |
|
50 | b'flag2': b'l', | |
|
51 | } | |
|
52 | ||
|
53 | A_STEM_COMPRESSED_MANIFEST = ( | |
|
54 | b'\0\n' | |
|
55 | b'\x00bar/baz/qux.py\0%(flag2)s\n%(hash2)s\n' | |
|
56 | b'\x04qux/foo.py\0%(flag1)s\n%(hash1)s\n' # simple case of 4 stem chars | |
|
57 | b'\x0az.py\0%(flag1)s\n%(hash1)s\n' # tricky newline = 10 stem characters | |
|
58 | b'\x00%(verylongdir)sx/x\0\n%(hash1)s\n' | |
|
59 | b'\xffx/y\0\n%(hash2)s\n' # more than 255 stem chars | |
|
60 | ) % {b'hash1': BIN_HASH_1, | |
|
61 | b'flag1': b'', | |
|
62 | b'hash2': BIN_HASH_2, | |
|
63 | b'flag2': b'l', | |
|
64 | b'verylongdir': 255 * b'x', | |
|
65 | } | |
|
66 | ||
|
67 | 30 | A_DEEPER_MANIFEST = ( |
|
68 | 31 | b'a/b/c/bar.py\0%(hash3)s%(flag1)s\n' |
|
69 | 32 | b'a/b/c/bar.txt\0%(hash1)s%(flag1)s\n' |
@@ -111,11 +74,6 b' class basemanifesttests(object):' | |||
|
111 | 74 | self.assertEqual(0, len(m)) |
|
112 | 75 | self.assertEqual([], list(m)) |
|
113 | 76 | |
|
114 | def testEmptyManifestv2(self): | |
|
115 | m = self.parsemanifest(EMTPY_MANIFEST_V2) | |
|
116 | self.assertEqual(0, len(m)) | |
|
117 | self.assertEqual([], list(m)) | |
|
118 | ||
|
119 | 77 | def testManifest(self): |
|
120 | 78 | m = self.parsemanifest(A_SHORT_MANIFEST) |
|
121 | 79 | self.assertEqual([b'bar/baz/qux.py', b'foo'], list(m)) |
@@ -126,31 +84,6 b' class basemanifesttests(object):' | |||
|
126 | 84 | with self.assertRaises(KeyError): |
|
127 | 85 | m[b'wat'] |
|
128 | 86 | |
|
129 | def testParseManifestV2(self): | |
|
130 | m1 = self.parsemanifest(A_SHORT_MANIFEST) | |
|
131 | m2 = self.parsemanifest(A_SHORT_MANIFEST_V2) | |
|
132 | # Should have same content as A_SHORT_MANIFEST | |
|
133 | self.assertEqual(m1.text(), m2.text()) | |
|
134 | ||
|
135 | def testParseManifestMetadata(self): | |
|
136 | # Metadata is for future-proofing and should be accepted but ignored | |
|
137 | m = self.parsemanifest(A_METADATA_MANIFEST) | |
|
138 | self.assertEqual(A_SHORT_MANIFEST, m.text()) | |
|
139 | ||
|
140 | def testParseManifestStemCompression(self): | |
|
141 | m = self.parsemanifest(A_STEM_COMPRESSED_MANIFEST) | |
|
142 | self.assertIn(b'bar/baz/qux.py', m) | |
|
143 | self.assertIn(b'bar/qux/foo.py', m) | |
|
144 | self.assertIn(b'bar/qux/foz.py', m) | |
|
145 | self.assertIn(256 * b'x' + b'/x', m) | |
|
146 | self.assertIn(256 * b'x' + b'/y', m) | |
|
147 | self.assertEqual(A_STEM_COMPRESSED_MANIFEST, m.text(usemanifestv2=True)) | |
|
148 | ||
|
149 | def testTextV2(self): | |
|
150 | m1 = self.parsemanifest(A_SHORT_MANIFEST) | |
|
151 | v2text = m1.text(usemanifestv2=True) | |
|
152 | self.assertEqual(A_SHORT_MANIFEST_V2, v2text) | |
|
153 | ||
|
154 | 87 | def testSetItem(self): |
|
155 | 88 | want = BIN_HASH_1 |
|
156 | 89 |
@@ -31,23 +31,18 b' Cannot upgrade shared repositories' | |||
|
31 | 31 | abort: cannot upgrade repository; unsupported source requirement: shared |
|
32 | 32 | [255] |
|
33 | 33 | |
|
34 |
Do not yet support upgrading |
|
|
35 | ||
|
36 | $ hg --config experimental.manifestv2=true init manifestv2 | |
|
37 | $ hg -R manifestv2 debugupgraderepo | |
|
38 | abort: cannot upgrade repository; unsupported source requirement: manifestv2 | |
|
39 | [255] | |
|
34 | Do not yet support upgrading treemanifest repos | |
|
40 | 35 | |
|
41 | 36 |
$ |
|
42 | 37 | $ hg -R treemanifest debugupgraderepo |
|
43 | 38 | abort: cannot upgrade repository; unsupported source requirement: treemanifest |
|
44 | 39 | [255] |
|
45 | 40 | |
|
46 |
Cannot add |
|
|
41 | Cannot add treemanifest requirement during upgrade | |
|
47 | 42 | |
|
48 | 43 | $ hg init disallowaddedreq |
|
49 |
$ hg -R disallowaddedreq --config experimental. |
|
|
50 |
abort: cannot upgrade repository; do not support adding requirement: |
|
|
44 | $ hg -R disallowaddedreq --config experimental.treemanifest=true debugupgraderepo | |
|
45 | abort: cannot upgrade repository; do not support adding requirement: treemanifest | |
|
51 | 46 | [255] |
|
52 | 47 | |
|
53 | 48 | An upgrade of a repository created with recommended settings only suggests optimizations |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now