Show More
@@ -11,8 +11,7 b' import array, struct' | |||||
11 |
|
11 | |||
12 | propertycache = util.propertycache |
|
12 | propertycache = util.propertycache | |
13 |
|
13 | |||
14 | def _parse(data): |
|
14 | def _parsev1(data): | |
15 | """Generates (path, node, flags) tuples from a manifest text""" |
|
|||
16 | # This method does a little bit of excessive-looking |
|
15 | # This method does a little bit of excessive-looking | |
17 | # precondition checking. This is so that the behavior of this |
|
16 | # precondition checking. This is so that the behavior of this | |
18 | # class exactly matches its C counterpart to try and help |
|
17 | # class exactly matches its C counterpart to try and help | |
@@ -31,6 +30,34 b' def _parse(data):' | |||||
31 | else: |
|
30 | else: | |
32 | yield f, revlog.bin(n), '' |
|
31 | yield f, revlog.bin(n), '' | |
33 |
|
32 | |||
|
33 | def _parsev2(data): | |||
|
34 | metadataend = data.find('\n') | |||
|
35 | # Just ignore metadata for now | |||
|
36 | pos = metadataend + 1 | |||
|
37 | prevf = '' | |||
|
38 | while pos < len(data): | |||
|
39 | end = data.find('\n', pos + 1) # +1 to skip stem length byte | |||
|
40 | if end == -1: | |||
|
41 | raise ValueError('Manifest ended with incomplete file entry.') | |||
|
42 | stemlen = ord(data[pos]) | |||
|
43 | items = data[pos + 1:end].split('\0') | |||
|
44 | f = prevf[:stemlen] + items[0] | |||
|
45 | if prevf > f: | |||
|
46 | raise ValueError('Manifest entries not in sorted order.') | |||
|
47 | fl = items[1] | |||
|
48 | # Just ignore metadata (items[2:] for now) | |||
|
49 | n = data[end + 1:end + 21] | |||
|
50 | yield f, n, fl | |||
|
51 | pos = end + 22 | |||
|
52 | prevf = f | |||
|
53 | ||||
|
54 | def _parse(data): | |||
|
55 | """Generates (path, node, flags) tuples from a manifest text""" | |||
|
56 | if data.startswith('\0'): | |||
|
57 | return iter(_parsev2(data)) | |||
|
58 | else: | |||
|
59 | return iter(_parsev1(data)) | |||
|
60 | ||||
34 | def _text(it): |
|
61 | def _text(it): | |
35 | """Given an iterator over (path, node, flags) tuples, returns a manifest |
|
62 | """Given an iterator over (path, node, flags) tuples, returns a manifest | |
36 | text""" |
|
63 | text""" | |
@@ -116,7 +143,13 b' except AttributeError:' | |||||
116 |
|
143 | |||
117 | class manifestdict(object): |
|
144 | class manifestdict(object): | |
118 | def __init__(self, data=''): |
|
145 | def __init__(self, data=''): | |
119 | self._lm = _lazymanifest(data) |
|
146 | if data.startswith('\0'): | |
|
147 | #_lazymanifest can not parse v2 | |||
|
148 | self._lm = _lazymanifest('') | |||
|
149 | for f, n, fl in _parsev2(data): | |||
|
150 | self._lm[f] = n, fl | |||
|
151 | else: | |||
|
152 | self._lm = _lazymanifest(data) | |||
120 |
|
153 | |||
121 | def __getitem__(self, key): |
|
154 | def __getitem__(self, key): | |
122 | return self._lm[key][0] |
|
155 | return self._lm[key][0] |
@@ -8,6 +8,7 b' from mercurial import manifest as manife' | |||||
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) | |
@@ -24,6 +25,42 b' A_SHORT_MANIFEST = (' | |||||
24 | 'flag2': 'l', |
|
25 | 'flag2': 'l', | |
25 | } |
|
26 | } | |
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', | |||
|
62 | } | |||
|
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' | |
@@ -77,6 +114,11 b' class testmanifest(unittest.TestCase):' | |||||
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)) | |
@@ -86,6 +128,25 b' class testmanifest(unittest.TestCase):' | |||||
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']) | |
88 |
|
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) | |||
|
149 | ||||
89 | def testSetItem(self): |
|
150 | def testSetItem(self): | |
90 | want = BIN_HASH_1 |
|
151 | want = BIN_HASH_1 | |
91 |
|
152 |
General Comments 0
You need to be logged in to leave comments.
Login now