Mercurial > hg
comparison tests/test-manifest.py @ 24572:b83679eb5f86
manifestv2: add support for reading new manifest format
The new manifest format is designed to be smaller, in particular to
produce smaller deltas. It stores hashes in binary and puts the hash
on a new line (for smaller deltas). It also uses stem compression to
save space for long paths. The format has room for metadata, but
that's there only for future-proofing. The parser thus accepts any
metadata and throws it away. For more information, see
http://mercurial.selenic.com/wiki/ManifestV2Plan.
The current manifest format doesn't allow an empty filename, so we use
an empty filename on the first line to tell a manifest of the new
format from the old. Since we still never write manifests in the new
format, the added code is unused, but it is tested by
test-manifest.py.
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Fri, 27 Mar 2015 22:26:41 -0700 |
parents | 487245cbf1ab |
children | 701d3554de0e |
comparison
equal
deleted
inserted
replaced
24571:919f8ce040be | 24572:b83679eb5f86 |
---|---|
6 | 6 |
7 from mercurial import manifest as manifestmod | 7 from mercurial import manifest as manifestmod |
8 from mercurial import match as matchmod | 8 from mercurial import match as matchmod |
9 | 9 |
10 EMTPY_MANIFEST = '' | 10 EMTPY_MANIFEST = '' |
11 EMTPY_MANIFEST_V2 = '\0\n' | |
11 | 12 |
12 HASH_1 = '1' * 40 | 13 HASH_1 = '1' * 40 |
13 BIN_HASH_1 = binascii.unhexlify(HASH_1) | 14 BIN_HASH_1 = binascii.unhexlify(HASH_1) |
14 HASH_2 = 'f' * 40 | 15 HASH_2 = 'f' * 40 |
15 BIN_HASH_2 = binascii.unhexlify(HASH_2) | 16 BIN_HASH_2 = binascii.unhexlify(HASH_2) |
20 'foo\0%(hash1)s%(flag1)s\n' | 21 'foo\0%(hash1)s%(flag1)s\n' |
21 ) % {'hash1': HASH_1, | 22 ) % {'hash1': HASH_1, |
22 'flag1': '', | 23 'flag1': '', |
23 'hash2': HASH_2, | 24 'hash2': HASH_2, |
24 'flag2': 'l', | 25 'flag2': 'l', |
26 } | |
27 | |
28 # Same data as A_SHORT_MANIFEST | |
29 A_SHORT_MANIFEST_V2 = ( | |
30 '\0\n' | |
31 '\x00bar/baz/qux.py\0%(flag2)s\n%(hash2)s\n' | |
32 '\x00foo\0%(flag1)s\n%(hash1)s\n' | |
33 ) % {'hash1': BIN_HASH_1, | |
34 'flag1': '', | |
35 'hash2': BIN_HASH_2, | |
36 'flag2': 'l', | |
37 } | |
38 | |
39 # Same data as A_SHORT_MANIFEST | |
40 A_METADATA_MANIFEST = ( | |
41 '\0foo\0bar\n' | |
42 '\x00bar/baz/qux.py\0%(flag2)s\0foo\0bar\n%(hash2)s\n' # flag and metadata | |
43 '\x00foo\0%(flag1)s\0foo\n%(hash1)s\n' # no flag, but metadata | |
44 ) % {'hash1': BIN_HASH_1, | |
45 'flag1': '', | |
46 'hash2': BIN_HASH_2, | |
47 'flag2': 'l', | |
48 } | |
49 | |
50 A_STEM_COMPRESSED_MANIFEST = ( | |
51 '\0\n' | |
52 '\x00bar/baz/qux.py\0%(flag2)s\n%(hash2)s\n' | |
53 '\x04qux/foo.py\0%(flag1)s\n%(hash1)s\n' # simple case of 4 stem chars | |
54 '\x0az.py\0%(flag1)s\n%(hash1)s\n' # tricky newline = 10 stem characters | |
55 '\x00%(verylongdir)sx/x\0\n%(hash1)s\n' | |
56 '\xffx/y\0\n%(hash2)s\n' # more than 255 stem chars | |
57 ) % {'hash1': BIN_HASH_1, | |
58 'flag1': '', | |
59 'hash2': BIN_HASH_2, | |
60 'flag2': 'l', | |
61 'verylongdir': 255 * 'x', | |
25 } | 62 } |
26 | 63 |
27 A_DEEPER_MANIFEST = ( | 64 A_DEEPER_MANIFEST = ( |
28 'a/b/c/bar.py\0%(hash3)s%(flag1)s\n' | 65 'a/b/c/bar.py\0%(hash3)s%(flag1)s\n' |
29 'a/b/c/bar.txt\0%(hash1)s%(flag1)s\n' | 66 'a/b/c/bar.txt\0%(hash1)s%(flag1)s\n' |
75 def testEmptyManifest(self): | 112 def testEmptyManifest(self): |
76 m = parsemanifest(EMTPY_MANIFEST) | 113 m = parsemanifest(EMTPY_MANIFEST) |
77 self.assertEqual(0, len(m)) | 114 self.assertEqual(0, len(m)) |
78 self.assertEqual([], list(m)) | 115 self.assertEqual([], list(m)) |
79 | 116 |
117 def testEmptyManifestv2(self): | |
118 m = parsemanifest(EMTPY_MANIFEST_V2) | |
119 self.assertEqual(0, len(m)) | |
120 self.assertEqual([], list(m)) | |
121 | |
80 def testManifest(self): | 122 def testManifest(self): |
81 m = parsemanifest(A_SHORT_MANIFEST) | 123 m = parsemanifest(A_SHORT_MANIFEST) |
82 self.assertEqual(['bar/baz/qux.py', 'foo'], list(m)) | 124 self.assertEqual(['bar/baz/qux.py', 'foo'], list(m)) |
83 self.assertEqual(BIN_HASH_2, m['bar/baz/qux.py']) | 125 self.assertEqual(BIN_HASH_2, m['bar/baz/qux.py']) |
84 self.assertEqual('l', m.flags('bar/baz/qux.py')) | 126 self.assertEqual('l', m.flags('bar/baz/qux.py')) |
85 self.assertEqual(BIN_HASH_1, m['foo']) | 127 self.assertEqual(BIN_HASH_1, m['foo']) |
86 self.assertEqual('', m.flags('foo')) | 128 self.assertEqual('', m.flags('foo')) |
87 self.assertRaises(KeyError, lambda : m['wat']) | 129 self.assertRaises(KeyError, lambda : m['wat']) |
130 | |
131 def testParseManifestV2(self): | |
132 m1 = parsemanifest(A_SHORT_MANIFEST) | |
133 m2 = parsemanifest(A_SHORT_MANIFEST_V2) | |
134 # Should have same content as A_SHORT_MANIFEST | |
135 self.assertEqual(m1.text(), m2.text()) | |
136 | |
137 def testParseManifestMetadata(self): | |
138 # Metadata is for future-proofing and should be accepted but ignored | |
139 m = parsemanifest(A_METADATA_MANIFEST) | |
140 self.assertEqual(A_SHORT_MANIFEST, m.text()) | |
141 | |
142 def testParseManifestStemCompression(self): | |
143 m = parsemanifest(A_STEM_COMPRESSED_MANIFEST) | |
144 self.assertIn('bar/baz/qux.py', m) | |
145 self.assertIn('bar/qux/foo.py', m) | |
146 self.assertIn('bar/qux/foz.py', m) | |
147 self.assertIn(256 * 'x' + '/x', m) | |
148 self.assertIn(256 * 'x' + '/y', m) | |
88 | 149 |
89 def testSetItem(self): | 150 def testSetItem(self): |
90 want = BIN_HASH_1 | 151 want = BIN_HASH_1 |
91 | 152 |
92 m = parsemanifest(EMTPY_MANIFEST) | 153 m = parsemanifest(EMTPY_MANIFEST) |