comparison mercurial/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 919f8ce040be
children 701d3554de0e
comparison
equal deleted inserted replaced
24571:919f8ce040be 24572:b83679eb5f86
9 import mdiff, parsers, error, revlog, util, scmutil 9 import mdiff, parsers, error, revlog, util, scmutil
10 import array, struct 10 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
19 # prevent surprise breakage for anyone that develops against 18 # prevent surprise breakage for anyone that develops against
20 # the pure version. 19 # the pure version.
29 if len(n) > 40: 28 if len(n) > 40:
30 yield f, revlog.bin(n[:40]), n[40:] 29 yield f, revlog.bin(n[:40]), n[40:]
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"""
37 files = [] 64 files = []
38 lines = [] 65 lines = []
114 except AttributeError: 141 except AttributeError:
115 pass 142 pass
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]
123 156
124 def find(self, key): 157 def find(self, key):