##// END OF EJS Templates
manifest: fix some pure-Python parser bits to work on Python 3
Augie Fackler -
r32535:260a6f71 default
parent child Browse files
Show More
@@ -1,1632 +1,1632 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import os
11 import os
12 import struct
12 import struct
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 bin,
16 bin,
17 hex,
17 hex,
18 )
18 )
19 from . import (
19 from . import (
20 error,
20 error,
21 mdiff,
21 mdiff,
22 policy,
22 policy,
23 revlog,
23 revlog,
24 util,
24 util,
25 )
25 )
26
26
27 parsers = policy.importmod(r'parsers')
27 parsers = policy.importmod(r'parsers')
28 propertycache = util.propertycache
28 propertycache = util.propertycache
29
29
30 def _parsev1(data):
30 def _parsev1(data):
31 # This method does a little bit of excessive-looking
31 # This method does a little bit of excessive-looking
32 # precondition checking. This is so that the behavior of this
32 # precondition checking. This is so that the behavior of this
33 # class exactly matches its C counterpart to try and help
33 # class exactly matches its C counterpart to try and help
34 # prevent surprise breakage for anyone that develops against
34 # prevent surprise breakage for anyone that develops against
35 # the pure version.
35 # the pure version.
36 if data and data[-1] != '\n':
36 if data and data[-1:] != '\n':
37 raise ValueError('Manifest did not end in a newline.')
37 raise ValueError('Manifest did not end in a newline.')
38 prev = None
38 prev = None
39 for l in data.splitlines():
39 for l in data.splitlines():
40 if prev is not None and prev > l:
40 if prev is not None and prev > l:
41 raise ValueError('Manifest lines not in sorted order.')
41 raise ValueError('Manifest lines not in sorted order.')
42 prev = l
42 prev = l
43 f, n = l.split('\0')
43 f, n = l.split('\0')
44 if len(n) > 40:
44 if len(n) > 40:
45 yield f, bin(n[:40]), n[40:]
45 yield f, bin(n[:40]), n[40:]
46 else:
46 else:
47 yield f, bin(n), ''
47 yield f, bin(n), ''
48
48
49 def _parsev2(data):
49 def _parsev2(data):
50 metadataend = data.find('\n')
50 metadataend = data.find('\n')
51 # Just ignore metadata for now
51 # Just ignore metadata for now
52 pos = metadataend + 1
52 pos = metadataend + 1
53 prevf = ''
53 prevf = ''
54 while pos < len(data):
54 while pos < len(data):
55 end = data.find('\n', pos + 1) # +1 to skip stem length byte
55 end = data.find('\n', pos + 1) # +1 to skip stem length byte
56 if end == -1:
56 if end == -1:
57 raise ValueError('Manifest ended with incomplete file entry.')
57 raise ValueError('Manifest ended with incomplete file entry.')
58 stemlen = ord(data[pos])
58 stemlen = ord(data[pos:pos + 1])
59 items = data[pos + 1:end].split('\0')
59 items = data[pos + 1:end].split('\0')
60 f = prevf[:stemlen] + items[0]
60 f = prevf[:stemlen] + items[0]
61 if prevf > f:
61 if prevf > f:
62 raise ValueError('Manifest entries not in sorted order.')
62 raise ValueError('Manifest entries not in sorted order.')
63 fl = items[1]
63 fl = items[1]
64 # Just ignore metadata (items[2:] for now)
64 # Just ignore metadata (items[2:] for now)
65 n = data[end + 1:end + 21]
65 n = data[end + 1:end + 21]
66 yield f, n, fl
66 yield f, n, fl
67 pos = end + 22
67 pos = end + 22
68 prevf = f
68 prevf = f
69
69
70 def _parse(data):
70 def _parse(data):
71 """Generates (path, node, flags) tuples from a manifest text"""
71 """Generates (path, node, flags) tuples from a manifest text"""
72 if data.startswith('\0'):
72 if data.startswith('\0'):
73 return iter(_parsev2(data))
73 return iter(_parsev2(data))
74 else:
74 else:
75 return iter(_parsev1(data))
75 return iter(_parsev1(data))
76
76
77 def _text(it, usemanifestv2):
77 def _text(it, usemanifestv2):
78 """Given an iterator over (path, node, flags) tuples, returns a manifest
78 """Given an iterator over (path, node, flags) tuples, returns a manifest
79 text"""
79 text"""
80 if usemanifestv2:
80 if usemanifestv2:
81 return _textv2(it)
81 return _textv2(it)
82 else:
82 else:
83 return _textv1(it)
83 return _textv1(it)
84
84
85 def _textv1(it):
85 def _textv1(it):
86 files = []
86 files = []
87 lines = []
87 lines = []
88 _hex = revlog.hex
88 _hex = revlog.hex
89 for f, n, fl in it:
89 for f, n, fl in it:
90 files.append(f)
90 files.append(f)
91 # if this is changed to support newlines in filenames,
91 # if this is changed to support newlines in filenames,
92 # be sure to check the templates/ dir again (especially *-raw.tmpl)
92 # be sure to check the templates/ dir again (especially *-raw.tmpl)
93 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
93 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
94
94
95 _checkforbidden(files)
95 _checkforbidden(files)
96 return ''.join(lines)
96 return ''.join(lines)
97
97
98 def _textv2(it):
98 def _textv2(it):
99 files = []
99 files = []
100 lines = ['\0\n']
100 lines = ['\0\n']
101 prevf = ''
101 prevf = ''
102 for f, n, fl in it:
102 for f, n, fl in it:
103 files.append(f)
103 files.append(f)
104 stem = os.path.commonprefix([prevf, f])
104 stem = os.path.commonprefix([prevf, f])
105 stemlen = min(len(stem), 255)
105 stemlen = min(len(stem), 255)
106 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
106 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
107 prevf = f
107 prevf = f
108 _checkforbidden(files)
108 _checkforbidden(files)
109 return ''.join(lines)
109 return ''.join(lines)
110
110
111 class lazymanifestiter(object):
111 class lazymanifestiter(object):
112 def __init__(self, lm):
112 def __init__(self, lm):
113 self.pos = 0
113 self.pos = 0
114 self.lm = lm
114 self.lm = lm
115
115
116 def __iter__(self):
116 def __iter__(self):
117 return self
117 return self
118
118
119 def next(self):
119 def next(self):
120 try:
120 try:
121 data, pos = self.lm._get(self.pos)
121 data, pos = self.lm._get(self.pos)
122 except IndexError:
122 except IndexError:
123 raise StopIteration
123 raise StopIteration
124 if pos == -1:
124 if pos == -1:
125 self.pos += 1
125 self.pos += 1
126 return data[0]
126 return data[0]
127 self.pos += 1
127 self.pos += 1
128 zeropos = data.find('\x00', pos)
128 zeropos = data.find('\x00', pos)
129 return data[pos:zeropos]
129 return data[pos:zeropos]
130
130
131 __next__ = next
131 __next__ = next
132
132
133 class lazymanifestiterentries(object):
133 class lazymanifestiterentries(object):
134 def __init__(self, lm):
134 def __init__(self, lm):
135 self.lm = lm
135 self.lm = lm
136 self.pos = 0
136 self.pos = 0
137
137
138 def __iter__(self):
138 def __iter__(self):
139 return self
139 return self
140
140
141 def next(self):
141 def next(self):
142 try:
142 try:
143 data, pos = self.lm._get(self.pos)
143 data, pos = self.lm._get(self.pos)
144 except IndexError:
144 except IndexError:
145 raise StopIteration
145 raise StopIteration
146 if pos == -1:
146 if pos == -1:
147 self.pos += 1
147 self.pos += 1
148 return data
148 return data
149 zeropos = data.find('\x00', pos)
149 zeropos = data.find('\x00', pos)
150 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
150 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
151 zeropos + 1, 40)
151 zeropos + 1, 40)
152 flags = self.lm._getflags(data, self.pos, zeropos)
152 flags = self.lm._getflags(data, self.pos, zeropos)
153 self.pos += 1
153 self.pos += 1
154 return (data[pos:zeropos], hashval, flags)
154 return (data[pos:zeropos], hashval, flags)
155
155
156 __next__ = next
156 __next__ = next
157
157
158 def unhexlify(data, extra, pos, length):
158 def unhexlify(data, extra, pos, length):
159 s = bin(data[pos:pos + length])
159 s = bin(data[pos:pos + length])
160 if extra:
160 if extra:
161 s += chr(extra & 0xff)
161 s += chr(extra & 0xff)
162 return s
162 return s
163
163
164 def _cmp(a, b):
164 def _cmp(a, b):
165 return (a > b) - (a < b)
165 return (a > b) - (a < b)
166
166
167 class _lazymanifest(object):
167 class _lazymanifest(object):
168 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
168 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
169 if positions is None:
169 if positions is None:
170 self.positions = self.findlines(data)
170 self.positions = self.findlines(data)
171 self.extrainfo = [0] * len(self.positions)
171 self.extrainfo = [0] * len(self.positions)
172 self.data = data
172 self.data = data
173 self.extradata = []
173 self.extradata = []
174 else:
174 else:
175 self.positions = positions[:]
175 self.positions = positions[:]
176 self.extrainfo = extrainfo[:]
176 self.extrainfo = extrainfo[:]
177 self.extradata = extradata[:]
177 self.extradata = extradata[:]
178 self.data = data
178 self.data = data
179
179
180 def findlines(self, data):
180 def findlines(self, data):
181 if not data:
181 if not data:
182 return []
182 return []
183 pos = data.find("\n")
183 pos = data.find("\n")
184 if pos == -1 or data[-1:] != '\n':
184 if pos == -1 or data[-1:] != '\n':
185 raise ValueError("Manifest did not end in a newline.")
185 raise ValueError("Manifest did not end in a newline.")
186 positions = [0]
186 positions = [0]
187 prev = data[:data.find('\x00')]
187 prev = data[:data.find('\x00')]
188 while pos < len(data) - 1 and pos != -1:
188 while pos < len(data) - 1 and pos != -1:
189 positions.append(pos + 1)
189 positions.append(pos + 1)
190 nexts = data[pos + 1:data.find('\x00', pos + 1)]
190 nexts = data[pos + 1:data.find('\x00', pos + 1)]
191 if nexts < prev:
191 if nexts < prev:
192 raise ValueError("Manifest lines not in sorted order.")
192 raise ValueError("Manifest lines not in sorted order.")
193 prev = nexts
193 prev = nexts
194 pos = data.find("\n", pos + 1)
194 pos = data.find("\n", pos + 1)
195 return positions
195 return positions
196
196
197 def _get(self, index):
197 def _get(self, index):
198 # get the position encoded in pos:
198 # get the position encoded in pos:
199 # positive number is an index in 'data'
199 # positive number is an index in 'data'
200 # negative number is in extrapieces
200 # negative number is in extrapieces
201 pos = self.positions[index]
201 pos = self.positions[index]
202 if pos >= 0:
202 if pos >= 0:
203 return self.data, pos
203 return self.data, pos
204 return self.extradata[-pos - 1], -1
204 return self.extradata[-pos - 1], -1
205
205
206 def _getkey(self, pos):
206 def _getkey(self, pos):
207 if pos >= 0:
207 if pos >= 0:
208 return self.data[pos:self.data.find('\x00', pos + 1)]
208 return self.data[pos:self.data.find('\x00', pos + 1)]
209 return self.extradata[-pos - 1][0]
209 return self.extradata[-pos - 1][0]
210
210
211 def bsearch(self, key):
211 def bsearch(self, key):
212 first = 0
212 first = 0
213 last = len(self.positions) - 1
213 last = len(self.positions) - 1
214
214
215 while first <= last:
215 while first <= last:
216 midpoint = (first + last)//2
216 midpoint = (first + last)//2
217 nextpos = self.positions[midpoint]
217 nextpos = self.positions[midpoint]
218 candidate = self._getkey(nextpos)
218 candidate = self._getkey(nextpos)
219 r = _cmp(key, candidate)
219 r = _cmp(key, candidate)
220 if r == 0:
220 if r == 0:
221 return midpoint
221 return midpoint
222 else:
222 else:
223 if r < 0:
223 if r < 0:
224 last = midpoint - 1
224 last = midpoint - 1
225 else:
225 else:
226 first = midpoint + 1
226 first = midpoint + 1
227 return -1
227 return -1
228
228
229 def bsearch2(self, key):
229 def bsearch2(self, key):
230 # same as the above, but will always return the position
230 # same as the above, but will always return the position
231 # done for performance reasons
231 # done for performance reasons
232 first = 0
232 first = 0
233 last = len(self.positions) - 1
233 last = len(self.positions) - 1
234
234
235 while first <= last:
235 while first <= last:
236 midpoint = (first + last)//2
236 midpoint = (first + last)//2
237 nextpos = self.positions[midpoint]
237 nextpos = self.positions[midpoint]
238 candidate = self._getkey(nextpos)
238 candidate = self._getkey(nextpos)
239 r = _cmp(key, candidate)
239 r = _cmp(key, candidate)
240 if r == 0:
240 if r == 0:
241 return (midpoint, True)
241 return (midpoint, True)
242 else:
242 else:
243 if r < 0:
243 if r < 0:
244 last = midpoint - 1
244 last = midpoint - 1
245 else:
245 else:
246 first = midpoint + 1
246 first = midpoint + 1
247 return (first, False)
247 return (first, False)
248
248
249 def __contains__(self, key):
249 def __contains__(self, key):
250 return self.bsearch(key) != -1
250 return self.bsearch(key) != -1
251
251
252 def _getflags(self, data, needle, pos):
252 def _getflags(self, data, needle, pos):
253 start = pos + 41
253 start = pos + 41
254 end = data.find("\n", start)
254 end = data.find("\n", start)
255 if end == -1:
255 if end == -1:
256 end = len(data) - 1
256 end = len(data) - 1
257 if start == end:
257 if start == end:
258 return ''
258 return ''
259 return self.data[start:end]
259 return self.data[start:end]
260
260
261 def __getitem__(self, key):
261 def __getitem__(self, key):
262 if not isinstance(key, bytes):
262 if not isinstance(key, bytes):
263 raise TypeError("getitem: manifest keys must be a bytes.")
263 raise TypeError("getitem: manifest keys must be a bytes.")
264 needle = self.bsearch(key)
264 needle = self.bsearch(key)
265 if needle == -1:
265 if needle == -1:
266 raise KeyError
266 raise KeyError
267 data, pos = self._get(needle)
267 data, pos = self._get(needle)
268 if pos == -1:
268 if pos == -1:
269 return (data[1], data[2])
269 return (data[1], data[2])
270 zeropos = data.find('\x00', pos)
270 zeropos = data.find('\x00', pos)
271 assert 0 <= needle <= len(self.positions)
271 assert 0 <= needle <= len(self.positions)
272 assert len(self.extrainfo) == len(self.positions)
272 assert len(self.extrainfo) == len(self.positions)
273 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
273 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
274 flags = self._getflags(data, needle, zeropos)
274 flags = self._getflags(data, needle, zeropos)
275 return (hashval, flags)
275 return (hashval, flags)
276
276
277 def __delitem__(self, key):
277 def __delitem__(self, key):
278 needle, found = self.bsearch2(key)
278 needle, found = self.bsearch2(key)
279 if not found:
279 if not found:
280 raise KeyError
280 raise KeyError
281 cur = self.positions[needle]
281 cur = self.positions[needle]
282 self.positions = self.positions[:needle] + self.positions[needle + 1:]
282 self.positions = self.positions[:needle] + self.positions[needle + 1:]
283 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
283 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
284 if cur >= 0:
284 if cur >= 0:
285 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
285 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
286
286
287 def __setitem__(self, key, value):
287 def __setitem__(self, key, value):
288 if not isinstance(key, bytes):
288 if not isinstance(key, bytes):
289 raise TypeError("setitem: manifest keys must be a byte string.")
289 raise TypeError("setitem: manifest keys must be a byte string.")
290 if not isinstance(value, tuple) or len(value) != 2:
290 if not isinstance(value, tuple) or len(value) != 2:
291 raise TypeError("Manifest values must be a tuple of (node, flags).")
291 raise TypeError("Manifest values must be a tuple of (node, flags).")
292 hashval = value[0]
292 hashval = value[0]
293 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
293 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
294 raise TypeError("node must be a 20-byte byte string")
294 raise TypeError("node must be a 20-byte byte string")
295 flags = value[1]
295 flags = value[1]
296 if len(hashval) == 22:
296 if len(hashval) == 22:
297 hashval = hashval[:-1]
297 hashval = hashval[:-1]
298 if not isinstance(flags, bytes) or len(flags) > 1:
298 if not isinstance(flags, bytes) or len(flags) > 1:
299 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
299 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
300 needle, found = self.bsearch2(key)
300 needle, found = self.bsearch2(key)
301 if found:
301 if found:
302 # put the item
302 # put the item
303 pos = self.positions[needle]
303 pos = self.positions[needle]
304 if pos < 0:
304 if pos < 0:
305 self.extradata[-pos - 1] = (key, hashval, value[1])
305 self.extradata[-pos - 1] = (key, hashval, value[1])
306 else:
306 else:
307 # just don't bother
307 # just don't bother
308 self.extradata.append((key, hashval, value[1]))
308 self.extradata.append((key, hashval, value[1]))
309 self.positions[needle] = -len(self.extradata)
309 self.positions[needle] = -len(self.extradata)
310 else:
310 else:
311 # not found, put it in with extra positions
311 # not found, put it in with extra positions
312 self.extradata.append((key, hashval, value[1]))
312 self.extradata.append((key, hashval, value[1]))
313 self.positions = (self.positions[:needle] + [-len(self.extradata)]
313 self.positions = (self.positions[:needle] + [-len(self.extradata)]
314 + self.positions[needle:])
314 + self.positions[needle:])
315 self.extrainfo = (self.extrainfo[:needle] + [0] +
315 self.extrainfo = (self.extrainfo[:needle] + [0] +
316 self.extrainfo[needle:])
316 self.extrainfo[needle:])
317
317
318 def copy(self):
318 def copy(self):
319 # XXX call _compact like in C?
319 # XXX call _compact like in C?
320 return _lazymanifest(self.data, self.positions, self.extrainfo,
320 return _lazymanifest(self.data, self.positions, self.extrainfo,
321 self.extradata)
321 self.extradata)
322
322
323 def _compact(self):
323 def _compact(self):
324 # hopefully not called TOO often
324 # hopefully not called TOO often
325 if len(self.extradata) == 0:
325 if len(self.extradata) == 0:
326 return
326 return
327 l = []
327 l = []
328 last_cut = 0
328 last_cut = 0
329 i = 0
329 i = 0
330 offset = 0
330 offset = 0
331 self.extrainfo = [0] * len(self.positions)
331 self.extrainfo = [0] * len(self.positions)
332 while i < len(self.positions):
332 while i < len(self.positions):
333 if self.positions[i] >= 0:
333 if self.positions[i] >= 0:
334 cur = self.positions[i]
334 cur = self.positions[i]
335 last_cut = cur
335 last_cut = cur
336 while True:
336 while True:
337 self.positions[i] = offset
337 self.positions[i] = offset
338 i += 1
338 i += 1
339 if i == len(self.positions) or self.positions[i] < 0:
339 if i == len(self.positions) or self.positions[i] < 0:
340 break
340 break
341 offset += self.positions[i] - cur
341 offset += self.positions[i] - cur
342 cur = self.positions[i]
342 cur = self.positions[i]
343 end_cut = self.data.find('\n', cur)
343 end_cut = self.data.find('\n', cur)
344 if end_cut != -1:
344 if end_cut != -1:
345 end_cut += 1
345 end_cut += 1
346 offset += end_cut - cur
346 offset += end_cut - cur
347 l.append(self.data[last_cut:end_cut])
347 l.append(self.data[last_cut:end_cut])
348 else:
348 else:
349 while i < len(self.positions) and self.positions[i] < 0:
349 while i < len(self.positions) and self.positions[i] < 0:
350 cur = self.positions[i]
350 cur = self.positions[i]
351 t = self.extradata[-cur - 1]
351 t = self.extradata[-cur - 1]
352 l.append(self._pack(t))
352 l.append(self._pack(t))
353 self.positions[i] = offset
353 self.positions[i] = offset
354 if len(t[1]) > 20:
354 if len(t[1]) > 20:
355 self.extrainfo[i] = ord(t[1][21])
355 self.extrainfo[i] = ord(t[1][21])
356 offset += len(l[-1])
356 offset += len(l[-1])
357 i += 1
357 i += 1
358 self.data = ''.join(l)
358 self.data = ''.join(l)
359 self.extradata = []
359 self.extradata = []
360
360
361 def _pack(self, d):
361 def _pack(self, d):
362 return d[0] + '\x00' + hex(d[1][:20]) + d[2] + '\n'
362 return d[0] + '\x00' + hex(d[1][:20]) + d[2] + '\n'
363
363
364 def text(self):
364 def text(self):
365 self._compact()
365 self._compact()
366 return self.data
366 return self.data
367
367
368 def diff(self, m2, clean=False):
368 def diff(self, m2, clean=False):
369 '''Finds changes between the current manifest and m2.'''
369 '''Finds changes between the current manifest and m2.'''
370 # XXX think whether efficiency matters here
370 # XXX think whether efficiency matters here
371 diff = {}
371 diff = {}
372
372
373 for fn, e1, flags in self.iterentries():
373 for fn, e1, flags in self.iterentries():
374 if fn not in m2:
374 if fn not in m2:
375 diff[fn] = (e1, flags), (None, '')
375 diff[fn] = (e1, flags), (None, '')
376 else:
376 else:
377 e2 = m2[fn]
377 e2 = m2[fn]
378 if (e1, flags) != e2:
378 if (e1, flags) != e2:
379 diff[fn] = (e1, flags), e2
379 diff[fn] = (e1, flags), e2
380 elif clean:
380 elif clean:
381 diff[fn] = None
381 diff[fn] = None
382
382
383 for fn, e2, flags in m2.iterentries():
383 for fn, e2, flags in m2.iterentries():
384 if fn not in self:
384 if fn not in self:
385 diff[fn] = (None, ''), (e2, flags)
385 diff[fn] = (None, ''), (e2, flags)
386
386
387 return diff
387 return diff
388
388
389 def iterentries(self):
389 def iterentries(self):
390 return lazymanifestiterentries(self)
390 return lazymanifestiterentries(self)
391
391
392 def iterkeys(self):
392 def iterkeys(self):
393 return lazymanifestiter(self)
393 return lazymanifestiter(self)
394
394
395 def __iter__(self):
395 def __iter__(self):
396 return lazymanifestiter(self)
396 return lazymanifestiter(self)
397
397
398 def __len__(self):
398 def __len__(self):
399 return len(self.positions)
399 return len(self.positions)
400
400
401 def filtercopy(self, filterfn):
401 def filtercopy(self, filterfn):
402 # XXX should be optimized
402 # XXX should be optimized
403 c = _lazymanifest('')
403 c = _lazymanifest('')
404 for f, n, fl in self.iterentries():
404 for f, n, fl in self.iterentries():
405 if filterfn(f):
405 if filterfn(f):
406 c[f] = n, fl
406 c[f] = n, fl
407 return c
407 return c
408
408
409 try:
409 try:
410 _lazymanifest = parsers.lazymanifest
410 _lazymanifest = parsers.lazymanifest
411 except AttributeError:
411 except AttributeError:
412 pass
412 pass
413
413
414 class manifestdict(object):
414 class manifestdict(object):
415 def __init__(self, data=''):
415 def __init__(self, data=''):
416 if data.startswith('\0'):
416 if data.startswith('\0'):
417 #_lazymanifest can not parse v2
417 #_lazymanifest can not parse v2
418 self._lm = _lazymanifest('')
418 self._lm = _lazymanifest('')
419 for f, n, fl in _parsev2(data):
419 for f, n, fl in _parsev2(data):
420 self._lm[f] = n, fl
420 self._lm[f] = n, fl
421 else:
421 else:
422 self._lm = _lazymanifest(data)
422 self._lm = _lazymanifest(data)
423
423
424 def __getitem__(self, key):
424 def __getitem__(self, key):
425 return self._lm[key][0]
425 return self._lm[key][0]
426
426
427 def find(self, key):
427 def find(self, key):
428 return self._lm[key]
428 return self._lm[key]
429
429
430 def __len__(self):
430 def __len__(self):
431 return len(self._lm)
431 return len(self._lm)
432
432
433 def __nonzero__(self):
433 def __nonzero__(self):
434 # nonzero is covered by the __len__ function, but implementing it here
434 # nonzero is covered by the __len__ function, but implementing it here
435 # makes it easier for extensions to override.
435 # makes it easier for extensions to override.
436 return len(self._lm) != 0
436 return len(self._lm) != 0
437
437
438 __bool__ = __nonzero__
438 __bool__ = __nonzero__
439
439
440 def __setitem__(self, key, node):
440 def __setitem__(self, key, node):
441 self._lm[key] = node, self.flags(key, '')
441 self._lm[key] = node, self.flags(key, '')
442
442
443 def __contains__(self, key):
443 def __contains__(self, key):
444 return key in self._lm
444 return key in self._lm
445
445
446 def __delitem__(self, key):
446 def __delitem__(self, key):
447 del self._lm[key]
447 del self._lm[key]
448
448
449 def __iter__(self):
449 def __iter__(self):
450 return self._lm.__iter__()
450 return self._lm.__iter__()
451
451
452 def iterkeys(self):
452 def iterkeys(self):
453 return self._lm.iterkeys()
453 return self._lm.iterkeys()
454
454
455 def keys(self):
455 def keys(self):
456 return list(self.iterkeys())
456 return list(self.iterkeys())
457
457
458 def filesnotin(self, m2, match=None):
458 def filesnotin(self, m2, match=None):
459 '''Set of files in this manifest that are not in the other'''
459 '''Set of files in this manifest that are not in the other'''
460 if match:
460 if match:
461 m1 = self.matches(match)
461 m1 = self.matches(match)
462 m2 = m2.matches(match)
462 m2 = m2.matches(match)
463 return m1.filesnotin(m2)
463 return m1.filesnotin(m2)
464 diff = self.diff(m2)
464 diff = self.diff(m2)
465 files = set(filepath
465 files = set(filepath
466 for filepath, hashflags in diff.iteritems()
466 for filepath, hashflags in diff.iteritems()
467 if hashflags[1][0] is None)
467 if hashflags[1][0] is None)
468 return files
468 return files
469
469
470 @propertycache
470 @propertycache
471 def _dirs(self):
471 def _dirs(self):
472 return util.dirs(self)
472 return util.dirs(self)
473
473
474 def dirs(self):
474 def dirs(self):
475 return self._dirs
475 return self._dirs
476
476
477 def hasdir(self, dir):
477 def hasdir(self, dir):
478 return dir in self._dirs
478 return dir in self._dirs
479
479
480 def _filesfastpath(self, match):
480 def _filesfastpath(self, match):
481 '''Checks whether we can correctly and quickly iterate over matcher
481 '''Checks whether we can correctly and quickly iterate over matcher
482 files instead of over manifest files.'''
482 files instead of over manifest files.'''
483 files = match.files()
483 files = match.files()
484 return (len(files) < 100 and (match.isexact() or
484 return (len(files) < 100 and (match.isexact() or
485 (match.prefix() and all(fn in self for fn in files))))
485 (match.prefix() and all(fn in self for fn in files))))
486
486
487 def walk(self, match):
487 def walk(self, match):
488 '''Generates matching file names.
488 '''Generates matching file names.
489
489
490 Equivalent to manifest.matches(match).iterkeys(), but without creating
490 Equivalent to manifest.matches(match).iterkeys(), but without creating
491 an entirely new manifest.
491 an entirely new manifest.
492
492
493 It also reports nonexistent files by marking them bad with match.bad().
493 It also reports nonexistent files by marking them bad with match.bad().
494 '''
494 '''
495 if match.always():
495 if match.always():
496 for f in iter(self):
496 for f in iter(self):
497 yield f
497 yield f
498 return
498 return
499
499
500 fset = set(match.files())
500 fset = set(match.files())
501
501
502 # avoid the entire walk if we're only looking for specific files
502 # avoid the entire walk if we're only looking for specific files
503 if self._filesfastpath(match):
503 if self._filesfastpath(match):
504 for fn in sorted(fset):
504 for fn in sorted(fset):
505 yield fn
505 yield fn
506 return
506 return
507
507
508 for fn in self:
508 for fn in self:
509 if fn in fset:
509 if fn in fset:
510 # specified pattern is the exact name
510 # specified pattern is the exact name
511 fset.remove(fn)
511 fset.remove(fn)
512 if match(fn):
512 if match(fn):
513 yield fn
513 yield fn
514
514
515 # for dirstate.walk, files=['.'] means "walk the whole tree".
515 # for dirstate.walk, files=['.'] means "walk the whole tree".
516 # follow that here, too
516 # follow that here, too
517 fset.discard('.')
517 fset.discard('.')
518
518
519 for fn in sorted(fset):
519 for fn in sorted(fset):
520 if not self.hasdir(fn):
520 if not self.hasdir(fn):
521 match.bad(fn, None)
521 match.bad(fn, None)
522
522
523 def matches(self, match):
523 def matches(self, match):
524 '''generate a new manifest filtered by the match argument'''
524 '''generate a new manifest filtered by the match argument'''
525 if match.always():
525 if match.always():
526 return self.copy()
526 return self.copy()
527
527
528 if self._filesfastpath(match):
528 if self._filesfastpath(match):
529 m = manifestdict()
529 m = manifestdict()
530 lm = self._lm
530 lm = self._lm
531 for fn in match.files():
531 for fn in match.files():
532 if fn in lm:
532 if fn in lm:
533 m._lm[fn] = lm[fn]
533 m._lm[fn] = lm[fn]
534 return m
534 return m
535
535
536 m = manifestdict()
536 m = manifestdict()
537 m._lm = self._lm.filtercopy(match)
537 m._lm = self._lm.filtercopy(match)
538 return m
538 return m
539
539
540 def diff(self, m2, match=None, clean=False):
540 def diff(self, m2, match=None, clean=False):
541 '''Finds changes between the current manifest and m2.
541 '''Finds changes between the current manifest and m2.
542
542
543 Args:
543 Args:
544 m2: the manifest to which this manifest should be compared.
544 m2: the manifest to which this manifest should be compared.
545 clean: if true, include files unchanged between these manifests
545 clean: if true, include files unchanged between these manifests
546 with a None value in the returned dictionary.
546 with a None value in the returned dictionary.
547
547
548 The result is returned as a dict with filename as key and
548 The result is returned as a dict with filename as key and
549 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
549 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
550 nodeid in the current/other manifest and fl1/fl2 is the flag
550 nodeid in the current/other manifest and fl1/fl2 is the flag
551 in the current/other manifest. Where the file does not exist,
551 in the current/other manifest. Where the file does not exist,
552 the nodeid will be None and the flags will be the empty
552 the nodeid will be None and the flags will be the empty
553 string.
553 string.
554 '''
554 '''
555 if match:
555 if match:
556 m1 = self.matches(match)
556 m1 = self.matches(match)
557 m2 = m2.matches(match)
557 m2 = m2.matches(match)
558 return m1.diff(m2, clean=clean)
558 return m1.diff(m2, clean=clean)
559 return self._lm.diff(m2._lm, clean)
559 return self._lm.diff(m2._lm, clean)
560
560
561 def setflag(self, key, flag):
561 def setflag(self, key, flag):
562 self._lm[key] = self[key], flag
562 self._lm[key] = self[key], flag
563
563
564 def get(self, key, default=None):
564 def get(self, key, default=None):
565 try:
565 try:
566 return self._lm[key][0]
566 return self._lm[key][0]
567 except KeyError:
567 except KeyError:
568 return default
568 return default
569
569
570 def flags(self, key, default=''):
570 def flags(self, key, default=''):
571 try:
571 try:
572 return self._lm[key][1]
572 return self._lm[key][1]
573 except KeyError:
573 except KeyError:
574 return default
574 return default
575
575
576 def copy(self):
576 def copy(self):
577 c = manifestdict()
577 c = manifestdict()
578 c._lm = self._lm.copy()
578 c._lm = self._lm.copy()
579 return c
579 return c
580
580
581 def iteritems(self):
581 def iteritems(self):
582 return (x[:2] for x in self._lm.iterentries())
582 return (x[:2] for x in self._lm.iterentries())
583
583
584 def iterentries(self):
584 def iterentries(self):
585 return self._lm.iterentries()
585 return self._lm.iterentries()
586
586
587 def text(self, usemanifestv2=False):
587 def text(self, usemanifestv2=False):
588 if usemanifestv2:
588 if usemanifestv2:
589 return _textv2(self._lm.iterentries())
589 return _textv2(self._lm.iterentries())
590 else:
590 else:
591 # use (probably) native version for v1
591 # use (probably) native version for v1
592 return self._lm.text()
592 return self._lm.text()
593
593
594 def fastdelta(self, base, changes):
594 def fastdelta(self, base, changes):
595 """Given a base manifest text as a bytearray and a list of changes
595 """Given a base manifest text as a bytearray and a list of changes
596 relative to that text, compute a delta that can be used by revlog.
596 relative to that text, compute a delta that can be used by revlog.
597 """
597 """
598 delta = []
598 delta = []
599 dstart = None
599 dstart = None
600 dend = None
600 dend = None
601 dline = [""]
601 dline = [""]
602 start = 0
602 start = 0
603 # zero copy representation of base as a buffer
603 # zero copy representation of base as a buffer
604 addbuf = util.buffer(base)
604 addbuf = util.buffer(base)
605
605
606 changes = list(changes)
606 changes = list(changes)
607 if len(changes) < 1000:
607 if len(changes) < 1000:
608 # start with a readonly loop that finds the offset of
608 # start with a readonly loop that finds the offset of
609 # each line and creates the deltas
609 # each line and creates the deltas
610 for f, todelete in changes:
610 for f, todelete in changes:
611 # bs will either be the index of the item or the insert point
611 # bs will either be the index of the item or the insert point
612 start, end = _msearch(addbuf, f, start)
612 start, end = _msearch(addbuf, f, start)
613 if not todelete:
613 if not todelete:
614 h, fl = self._lm[f]
614 h, fl = self._lm[f]
615 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
615 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
616 else:
616 else:
617 if start == end:
617 if start == end:
618 # item we want to delete was not found, error out
618 # item we want to delete was not found, error out
619 raise AssertionError(
619 raise AssertionError(
620 _("failed to remove %s from manifest") % f)
620 _("failed to remove %s from manifest") % f)
621 l = ""
621 l = ""
622 if dstart is not None and dstart <= start and dend >= start:
622 if dstart is not None and dstart <= start and dend >= start:
623 if dend < end:
623 if dend < end:
624 dend = end
624 dend = end
625 if l:
625 if l:
626 dline.append(l)
626 dline.append(l)
627 else:
627 else:
628 if dstart is not None:
628 if dstart is not None:
629 delta.append([dstart, dend, "".join(dline)])
629 delta.append([dstart, dend, "".join(dline)])
630 dstart = start
630 dstart = start
631 dend = end
631 dend = end
632 dline = [l]
632 dline = [l]
633
633
634 if dstart is not None:
634 if dstart is not None:
635 delta.append([dstart, dend, "".join(dline)])
635 delta.append([dstart, dend, "".join(dline)])
636 # apply the delta to the base, and get a delta for addrevision
636 # apply the delta to the base, and get a delta for addrevision
637 deltatext, arraytext = _addlistdelta(base, delta)
637 deltatext, arraytext = _addlistdelta(base, delta)
638 else:
638 else:
639 # For large changes, it's much cheaper to just build the text and
639 # For large changes, it's much cheaper to just build the text and
640 # diff it.
640 # diff it.
641 arraytext = bytearray(self.text())
641 arraytext = bytearray(self.text())
642 deltatext = mdiff.textdiff(
642 deltatext = mdiff.textdiff(
643 util.buffer(base), util.buffer(arraytext))
643 util.buffer(base), util.buffer(arraytext))
644
644
645 return arraytext, deltatext
645 return arraytext, deltatext
646
646
647 def _msearch(m, s, lo=0, hi=None):
647 def _msearch(m, s, lo=0, hi=None):
648 '''return a tuple (start, end) that says where to find s within m.
648 '''return a tuple (start, end) that says where to find s within m.
649
649
650 If the string is found m[start:end] are the line containing
650 If the string is found m[start:end] are the line containing
651 that string. If start == end the string was not found and
651 that string. If start == end the string was not found and
652 they indicate the proper sorted insertion point.
652 they indicate the proper sorted insertion point.
653
653
654 m should be a buffer, a memoryview or a byte string.
654 m should be a buffer, a memoryview or a byte string.
655 s is a byte string'''
655 s is a byte string'''
656 def advance(i, c):
656 def advance(i, c):
657 while i < lenm and m[i:i + 1] != c:
657 while i < lenm and m[i:i + 1] != c:
658 i += 1
658 i += 1
659 return i
659 return i
660 if not s:
660 if not s:
661 return (lo, lo)
661 return (lo, lo)
662 lenm = len(m)
662 lenm = len(m)
663 if not hi:
663 if not hi:
664 hi = lenm
664 hi = lenm
665 while lo < hi:
665 while lo < hi:
666 mid = (lo + hi) // 2
666 mid = (lo + hi) // 2
667 start = mid
667 start = mid
668 while start > 0 and m[start - 1:start] != '\n':
668 while start > 0 and m[start - 1:start] != '\n':
669 start -= 1
669 start -= 1
670 end = advance(start, '\0')
670 end = advance(start, '\0')
671 if bytes(m[start:end]) < s:
671 if bytes(m[start:end]) < s:
672 # we know that after the null there are 40 bytes of sha1
672 # we know that after the null there are 40 bytes of sha1
673 # this translates to the bisect lo = mid + 1
673 # this translates to the bisect lo = mid + 1
674 lo = advance(end + 40, '\n') + 1
674 lo = advance(end + 40, '\n') + 1
675 else:
675 else:
676 # this translates to the bisect hi = mid
676 # this translates to the bisect hi = mid
677 hi = start
677 hi = start
678 end = advance(lo, '\0')
678 end = advance(lo, '\0')
679 found = m[lo:end]
679 found = m[lo:end]
680 if s == found:
680 if s == found:
681 # we know that after the null there are 40 bytes of sha1
681 # we know that after the null there are 40 bytes of sha1
682 end = advance(end + 40, '\n')
682 end = advance(end + 40, '\n')
683 return (lo, end + 1)
683 return (lo, end + 1)
684 else:
684 else:
685 return (lo, lo)
685 return (lo, lo)
686
686
687 def _checkforbidden(l):
687 def _checkforbidden(l):
688 """Check filenames for illegal characters."""
688 """Check filenames for illegal characters."""
689 for f in l:
689 for f in l:
690 if '\n' in f or '\r' in f:
690 if '\n' in f or '\r' in f:
691 raise error.RevlogError(
691 raise error.RevlogError(
692 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
692 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
693
693
694
694
695 # apply the changes collected during the bisect loop to our addlist
695 # apply the changes collected during the bisect loop to our addlist
696 # return a delta suitable for addrevision
696 # return a delta suitable for addrevision
697 def _addlistdelta(addlist, x):
697 def _addlistdelta(addlist, x):
698 # for large addlist arrays, building a new array is cheaper
698 # for large addlist arrays, building a new array is cheaper
699 # than repeatedly modifying the existing one
699 # than repeatedly modifying the existing one
700 currentposition = 0
700 currentposition = 0
701 newaddlist = bytearray()
701 newaddlist = bytearray()
702
702
703 for start, end, content in x:
703 for start, end, content in x:
704 newaddlist += addlist[currentposition:start]
704 newaddlist += addlist[currentposition:start]
705 if content:
705 if content:
706 newaddlist += bytearray(content)
706 newaddlist += bytearray(content)
707
707
708 currentposition = end
708 currentposition = end
709
709
710 newaddlist += addlist[currentposition:]
710 newaddlist += addlist[currentposition:]
711
711
712 deltatext = "".join(struct.pack(">lll", start, end, len(content))
712 deltatext = "".join(struct.pack(">lll", start, end, len(content))
713 + content for start, end, content in x)
713 + content for start, end, content in x)
714 return deltatext, newaddlist
714 return deltatext, newaddlist
715
715
716 def _splittopdir(f):
716 def _splittopdir(f):
717 if '/' in f:
717 if '/' in f:
718 dir, subpath = f.split('/', 1)
718 dir, subpath = f.split('/', 1)
719 return dir + '/', subpath
719 return dir + '/', subpath
720 else:
720 else:
721 return '', f
721 return '', f
722
722
723 _noop = lambda s: None
723 _noop = lambda s: None
724
724
725 class treemanifest(object):
725 class treemanifest(object):
726 def __init__(self, dir='', text=''):
726 def __init__(self, dir='', text=''):
727 self._dir = dir
727 self._dir = dir
728 self._node = revlog.nullid
728 self._node = revlog.nullid
729 self._loadfunc = _noop
729 self._loadfunc = _noop
730 self._copyfunc = _noop
730 self._copyfunc = _noop
731 self._dirty = False
731 self._dirty = False
732 self._dirs = {}
732 self._dirs = {}
733 # Using _lazymanifest here is a little slower than plain old dicts
733 # Using _lazymanifest here is a little slower than plain old dicts
734 self._files = {}
734 self._files = {}
735 self._flags = {}
735 self._flags = {}
736 if text:
736 if text:
737 def readsubtree(subdir, subm):
737 def readsubtree(subdir, subm):
738 raise AssertionError('treemanifest constructor only accepts '
738 raise AssertionError('treemanifest constructor only accepts '
739 'flat manifests')
739 'flat manifests')
740 self.parse(text, readsubtree)
740 self.parse(text, readsubtree)
741 self._dirty = True # Mark flat manifest dirty after parsing
741 self._dirty = True # Mark flat manifest dirty after parsing
742
742
743 def _subpath(self, path):
743 def _subpath(self, path):
744 return self._dir + path
744 return self._dir + path
745
745
746 def __len__(self):
746 def __len__(self):
747 self._load()
747 self._load()
748 size = len(self._files)
748 size = len(self._files)
749 for m in self._dirs.values():
749 for m in self._dirs.values():
750 size += m.__len__()
750 size += m.__len__()
751 return size
751 return size
752
752
753 def _isempty(self):
753 def _isempty(self):
754 self._load() # for consistency; already loaded by all callers
754 self._load() # for consistency; already loaded by all callers
755 return (not self._files and (not self._dirs or
755 return (not self._files and (not self._dirs or
756 all(m._isempty() for m in self._dirs.values())))
756 all(m._isempty() for m in self._dirs.values())))
757
757
758 def __repr__(self):
758 def __repr__(self):
759 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
759 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
760 (self._dir, revlog.hex(self._node),
760 (self._dir, revlog.hex(self._node),
761 bool(self._loadfunc is _noop),
761 bool(self._loadfunc is _noop),
762 self._dirty, id(self)))
762 self._dirty, id(self)))
763
763
764 def dir(self):
764 def dir(self):
765 '''The directory that this tree manifest represents, including a
765 '''The directory that this tree manifest represents, including a
766 trailing '/'. Empty string for the repo root directory.'''
766 trailing '/'. Empty string for the repo root directory.'''
767 return self._dir
767 return self._dir
768
768
769 def node(self):
769 def node(self):
770 '''This node of this instance. nullid for unsaved instances. Should
770 '''This node of this instance. nullid for unsaved instances. Should
771 be updated when the instance is read or written from a revlog.
771 be updated when the instance is read or written from a revlog.
772 '''
772 '''
773 assert not self._dirty
773 assert not self._dirty
774 return self._node
774 return self._node
775
775
776 def setnode(self, node):
776 def setnode(self, node):
777 self._node = node
777 self._node = node
778 self._dirty = False
778 self._dirty = False
779
779
780 def iterentries(self):
780 def iterentries(self):
781 self._load()
781 self._load()
782 for p, n in sorted(self._dirs.items() + self._files.items()):
782 for p, n in sorted(self._dirs.items() + self._files.items()):
783 if p in self._files:
783 if p in self._files:
784 yield self._subpath(p), n, self._flags.get(p, '')
784 yield self._subpath(p), n, self._flags.get(p, '')
785 else:
785 else:
786 for x in n.iterentries():
786 for x in n.iterentries():
787 yield x
787 yield x
788
788
789 def iteritems(self):
789 def iteritems(self):
790 self._load()
790 self._load()
791 for p, n in sorted(self._dirs.items() + self._files.items()):
791 for p, n in sorted(self._dirs.items() + self._files.items()):
792 if p in self._files:
792 if p in self._files:
793 yield self._subpath(p), n
793 yield self._subpath(p), n
794 else:
794 else:
795 for f, sn in n.iteritems():
795 for f, sn in n.iteritems():
796 yield f, sn
796 yield f, sn
797
797
798 def iterkeys(self):
798 def iterkeys(self):
799 self._load()
799 self._load()
800 for p in sorted(self._dirs.keys() + self._files.keys()):
800 for p in sorted(self._dirs.keys() + self._files.keys()):
801 if p in self._files:
801 if p in self._files:
802 yield self._subpath(p)
802 yield self._subpath(p)
803 else:
803 else:
804 for f in self._dirs[p].iterkeys():
804 for f in self._dirs[p].iterkeys():
805 yield f
805 yield f
806
806
807 def keys(self):
807 def keys(self):
808 return list(self.iterkeys())
808 return list(self.iterkeys())
809
809
810 def __iter__(self):
810 def __iter__(self):
811 return self.iterkeys()
811 return self.iterkeys()
812
812
813 def __contains__(self, f):
813 def __contains__(self, f):
814 if f is None:
814 if f is None:
815 return False
815 return False
816 self._load()
816 self._load()
817 dir, subpath = _splittopdir(f)
817 dir, subpath = _splittopdir(f)
818 if dir:
818 if dir:
819 if dir not in self._dirs:
819 if dir not in self._dirs:
820 return False
820 return False
821 return self._dirs[dir].__contains__(subpath)
821 return self._dirs[dir].__contains__(subpath)
822 else:
822 else:
823 return f in self._files
823 return f in self._files
824
824
825 def get(self, f, default=None):
825 def get(self, f, default=None):
826 self._load()
826 self._load()
827 dir, subpath = _splittopdir(f)
827 dir, subpath = _splittopdir(f)
828 if dir:
828 if dir:
829 if dir not in self._dirs:
829 if dir not in self._dirs:
830 return default
830 return default
831 return self._dirs[dir].get(subpath, default)
831 return self._dirs[dir].get(subpath, default)
832 else:
832 else:
833 return self._files.get(f, default)
833 return self._files.get(f, default)
834
834
835 def __getitem__(self, f):
835 def __getitem__(self, f):
836 self._load()
836 self._load()
837 dir, subpath = _splittopdir(f)
837 dir, subpath = _splittopdir(f)
838 if dir:
838 if dir:
839 return self._dirs[dir].__getitem__(subpath)
839 return self._dirs[dir].__getitem__(subpath)
840 else:
840 else:
841 return self._files[f]
841 return self._files[f]
842
842
843 def flags(self, f):
843 def flags(self, f):
844 self._load()
844 self._load()
845 dir, subpath = _splittopdir(f)
845 dir, subpath = _splittopdir(f)
846 if dir:
846 if dir:
847 if dir not in self._dirs:
847 if dir not in self._dirs:
848 return ''
848 return ''
849 return self._dirs[dir].flags(subpath)
849 return self._dirs[dir].flags(subpath)
850 else:
850 else:
851 if f in self._dirs:
851 if f in self._dirs:
852 return ''
852 return ''
853 return self._flags.get(f, '')
853 return self._flags.get(f, '')
854
854
855 def find(self, f):
855 def find(self, f):
856 self._load()
856 self._load()
857 dir, subpath = _splittopdir(f)
857 dir, subpath = _splittopdir(f)
858 if dir:
858 if dir:
859 return self._dirs[dir].find(subpath)
859 return self._dirs[dir].find(subpath)
860 else:
860 else:
861 return self._files[f], self._flags.get(f, '')
861 return self._files[f], self._flags.get(f, '')
862
862
863 def __delitem__(self, f):
863 def __delitem__(self, f):
864 self._load()
864 self._load()
865 dir, subpath = _splittopdir(f)
865 dir, subpath = _splittopdir(f)
866 if dir:
866 if dir:
867 self._dirs[dir].__delitem__(subpath)
867 self._dirs[dir].__delitem__(subpath)
868 # If the directory is now empty, remove it
868 # If the directory is now empty, remove it
869 if self._dirs[dir]._isempty():
869 if self._dirs[dir]._isempty():
870 del self._dirs[dir]
870 del self._dirs[dir]
871 else:
871 else:
872 del self._files[f]
872 del self._files[f]
873 if f in self._flags:
873 if f in self._flags:
874 del self._flags[f]
874 del self._flags[f]
875 self._dirty = True
875 self._dirty = True
876
876
877 def __setitem__(self, f, n):
877 def __setitem__(self, f, n):
878 assert n is not None
878 assert n is not None
879 self._load()
879 self._load()
880 dir, subpath = _splittopdir(f)
880 dir, subpath = _splittopdir(f)
881 if dir:
881 if dir:
882 if dir not in self._dirs:
882 if dir not in self._dirs:
883 self._dirs[dir] = treemanifest(self._subpath(dir))
883 self._dirs[dir] = treemanifest(self._subpath(dir))
884 self._dirs[dir].__setitem__(subpath, n)
884 self._dirs[dir].__setitem__(subpath, n)
885 else:
885 else:
886 self._files[f] = n[:21] # to match manifestdict's behavior
886 self._files[f] = n[:21] # to match manifestdict's behavior
887 self._dirty = True
887 self._dirty = True
888
888
889 def _load(self):
889 def _load(self):
890 if self._loadfunc is not _noop:
890 if self._loadfunc is not _noop:
891 lf, self._loadfunc = self._loadfunc, _noop
891 lf, self._loadfunc = self._loadfunc, _noop
892 lf(self)
892 lf(self)
893 elif self._copyfunc is not _noop:
893 elif self._copyfunc is not _noop:
894 cf, self._copyfunc = self._copyfunc, _noop
894 cf, self._copyfunc = self._copyfunc, _noop
895 cf(self)
895 cf(self)
896
896
897 def setflag(self, f, flags):
897 def setflag(self, f, flags):
898 """Set the flags (symlink, executable) for path f."""
898 """Set the flags (symlink, executable) for path f."""
899 self._load()
899 self._load()
900 dir, subpath = _splittopdir(f)
900 dir, subpath = _splittopdir(f)
901 if dir:
901 if dir:
902 if dir not in self._dirs:
902 if dir not in self._dirs:
903 self._dirs[dir] = treemanifest(self._subpath(dir))
903 self._dirs[dir] = treemanifest(self._subpath(dir))
904 self._dirs[dir].setflag(subpath, flags)
904 self._dirs[dir].setflag(subpath, flags)
905 else:
905 else:
906 self._flags[f] = flags
906 self._flags[f] = flags
907 self._dirty = True
907 self._dirty = True
908
908
909 def copy(self):
909 def copy(self):
910 copy = treemanifest(self._dir)
910 copy = treemanifest(self._dir)
911 copy._node = self._node
911 copy._node = self._node
912 copy._dirty = self._dirty
912 copy._dirty = self._dirty
913 if self._copyfunc is _noop:
913 if self._copyfunc is _noop:
914 def _copyfunc(s):
914 def _copyfunc(s):
915 self._load()
915 self._load()
916 for d in self._dirs:
916 for d in self._dirs:
917 s._dirs[d] = self._dirs[d].copy()
917 s._dirs[d] = self._dirs[d].copy()
918 s._files = dict.copy(self._files)
918 s._files = dict.copy(self._files)
919 s._flags = dict.copy(self._flags)
919 s._flags = dict.copy(self._flags)
920 if self._loadfunc is _noop:
920 if self._loadfunc is _noop:
921 _copyfunc(copy)
921 _copyfunc(copy)
922 else:
922 else:
923 copy._copyfunc = _copyfunc
923 copy._copyfunc = _copyfunc
924 else:
924 else:
925 copy._copyfunc = self._copyfunc
925 copy._copyfunc = self._copyfunc
926 return copy
926 return copy
927
927
928 def filesnotin(self, m2, match=None):
928 def filesnotin(self, m2, match=None):
929 '''Set of files in this manifest that are not in the other'''
929 '''Set of files in this manifest that are not in the other'''
930 if match:
930 if match:
931 m1 = self.matches(match)
931 m1 = self.matches(match)
932 m2 = m2.matches(match)
932 m2 = m2.matches(match)
933 return m1.filesnotin(m2)
933 return m1.filesnotin(m2)
934
934
935 files = set()
935 files = set()
936 def _filesnotin(t1, t2):
936 def _filesnotin(t1, t2):
937 if t1._node == t2._node and not t1._dirty and not t2._dirty:
937 if t1._node == t2._node and not t1._dirty and not t2._dirty:
938 return
938 return
939 t1._load()
939 t1._load()
940 t2._load()
940 t2._load()
941 for d, m1 in t1._dirs.iteritems():
941 for d, m1 in t1._dirs.iteritems():
942 if d in t2._dirs:
942 if d in t2._dirs:
943 m2 = t2._dirs[d]
943 m2 = t2._dirs[d]
944 _filesnotin(m1, m2)
944 _filesnotin(m1, m2)
945 else:
945 else:
946 files.update(m1.iterkeys())
946 files.update(m1.iterkeys())
947
947
948 for fn in t1._files.iterkeys():
948 for fn in t1._files.iterkeys():
949 if fn not in t2._files:
949 if fn not in t2._files:
950 files.add(t1._subpath(fn))
950 files.add(t1._subpath(fn))
951
951
952 _filesnotin(self, m2)
952 _filesnotin(self, m2)
953 return files
953 return files
954
954
955 @propertycache
955 @propertycache
956 def _alldirs(self):
956 def _alldirs(self):
957 return util.dirs(self)
957 return util.dirs(self)
958
958
959 def dirs(self):
959 def dirs(self):
960 return self._alldirs
960 return self._alldirs
961
961
962 def hasdir(self, dir):
962 def hasdir(self, dir):
963 self._load()
963 self._load()
964 topdir, subdir = _splittopdir(dir)
964 topdir, subdir = _splittopdir(dir)
965 if topdir:
965 if topdir:
966 if topdir in self._dirs:
966 if topdir in self._dirs:
967 return self._dirs[topdir].hasdir(subdir)
967 return self._dirs[topdir].hasdir(subdir)
968 return False
968 return False
969 return (dir + '/') in self._dirs
969 return (dir + '/') in self._dirs
970
970
971 def walk(self, match):
971 def walk(self, match):
972 '''Generates matching file names.
972 '''Generates matching file names.
973
973
974 Equivalent to manifest.matches(match).iterkeys(), but without creating
974 Equivalent to manifest.matches(match).iterkeys(), but without creating
975 an entirely new manifest.
975 an entirely new manifest.
976
976
977 It also reports nonexistent files by marking them bad with match.bad().
977 It also reports nonexistent files by marking them bad with match.bad().
978 '''
978 '''
979 if match.always():
979 if match.always():
980 for f in iter(self):
980 for f in iter(self):
981 yield f
981 yield f
982 return
982 return
983
983
984 fset = set(match.files())
984 fset = set(match.files())
985
985
986 for fn in self._walk(match):
986 for fn in self._walk(match):
987 if fn in fset:
987 if fn in fset:
988 # specified pattern is the exact name
988 # specified pattern is the exact name
989 fset.remove(fn)
989 fset.remove(fn)
990 yield fn
990 yield fn
991
991
992 # for dirstate.walk, files=['.'] means "walk the whole tree".
992 # for dirstate.walk, files=['.'] means "walk the whole tree".
993 # follow that here, too
993 # follow that here, too
994 fset.discard('.')
994 fset.discard('.')
995
995
996 for fn in sorted(fset):
996 for fn in sorted(fset):
997 if not self.hasdir(fn):
997 if not self.hasdir(fn):
998 match.bad(fn, None)
998 match.bad(fn, None)
999
999
1000 def _walk(self, match):
1000 def _walk(self, match):
1001 '''Recursively generates matching file names for walk().'''
1001 '''Recursively generates matching file names for walk().'''
1002 if not match.visitdir(self._dir[:-1] or '.'):
1002 if not match.visitdir(self._dir[:-1] or '.'):
1003 return
1003 return
1004
1004
1005 # yield this dir's files and walk its submanifests
1005 # yield this dir's files and walk its submanifests
1006 self._load()
1006 self._load()
1007 for p in sorted(self._dirs.keys() + self._files.keys()):
1007 for p in sorted(self._dirs.keys() + self._files.keys()):
1008 if p in self._files:
1008 if p in self._files:
1009 fullp = self._subpath(p)
1009 fullp = self._subpath(p)
1010 if match(fullp):
1010 if match(fullp):
1011 yield fullp
1011 yield fullp
1012 else:
1012 else:
1013 for f in self._dirs[p]._walk(match):
1013 for f in self._dirs[p]._walk(match):
1014 yield f
1014 yield f
1015
1015
1016 def matches(self, match):
1016 def matches(self, match):
1017 '''generate a new manifest filtered by the match argument'''
1017 '''generate a new manifest filtered by the match argument'''
1018 if match.always():
1018 if match.always():
1019 return self.copy()
1019 return self.copy()
1020
1020
1021 return self._matches(match)
1021 return self._matches(match)
1022
1022
1023 def _matches(self, match):
1023 def _matches(self, match):
1024 '''recursively generate a new manifest filtered by the match argument.
1024 '''recursively generate a new manifest filtered by the match argument.
1025 '''
1025 '''
1026
1026
1027 visit = match.visitdir(self._dir[:-1] or '.')
1027 visit = match.visitdir(self._dir[:-1] or '.')
1028 if visit == 'all':
1028 if visit == 'all':
1029 return self.copy()
1029 return self.copy()
1030 ret = treemanifest(self._dir)
1030 ret = treemanifest(self._dir)
1031 if not visit:
1031 if not visit:
1032 return ret
1032 return ret
1033
1033
1034 self._load()
1034 self._load()
1035 for fn in self._files:
1035 for fn in self._files:
1036 fullp = self._subpath(fn)
1036 fullp = self._subpath(fn)
1037 if not match(fullp):
1037 if not match(fullp):
1038 continue
1038 continue
1039 ret._files[fn] = self._files[fn]
1039 ret._files[fn] = self._files[fn]
1040 if fn in self._flags:
1040 if fn in self._flags:
1041 ret._flags[fn] = self._flags[fn]
1041 ret._flags[fn] = self._flags[fn]
1042
1042
1043 for dir, subm in self._dirs.iteritems():
1043 for dir, subm in self._dirs.iteritems():
1044 m = subm._matches(match)
1044 m = subm._matches(match)
1045 if not m._isempty():
1045 if not m._isempty():
1046 ret._dirs[dir] = m
1046 ret._dirs[dir] = m
1047
1047
1048 if not ret._isempty():
1048 if not ret._isempty():
1049 ret._dirty = True
1049 ret._dirty = True
1050 return ret
1050 return ret
1051
1051
1052 def diff(self, m2, match=None, clean=False):
1052 def diff(self, m2, match=None, clean=False):
1053 '''Finds changes between the current manifest and m2.
1053 '''Finds changes between the current manifest and m2.
1054
1054
1055 Args:
1055 Args:
1056 m2: the manifest to which this manifest should be compared.
1056 m2: the manifest to which this manifest should be compared.
1057 clean: if true, include files unchanged between these manifests
1057 clean: if true, include files unchanged between these manifests
1058 with a None value in the returned dictionary.
1058 with a None value in the returned dictionary.
1059
1059
1060 The result is returned as a dict with filename as key and
1060 The result is returned as a dict with filename as key and
1061 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1061 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1062 nodeid in the current/other manifest and fl1/fl2 is the flag
1062 nodeid in the current/other manifest and fl1/fl2 is the flag
1063 in the current/other manifest. Where the file does not exist,
1063 in the current/other manifest. Where the file does not exist,
1064 the nodeid will be None and the flags will be the empty
1064 the nodeid will be None and the flags will be the empty
1065 string.
1065 string.
1066 '''
1066 '''
1067 if match:
1067 if match:
1068 m1 = self.matches(match)
1068 m1 = self.matches(match)
1069 m2 = m2.matches(match)
1069 m2 = m2.matches(match)
1070 return m1.diff(m2, clean=clean)
1070 return m1.diff(m2, clean=clean)
1071 result = {}
1071 result = {}
1072 emptytree = treemanifest()
1072 emptytree = treemanifest()
1073 def _diff(t1, t2):
1073 def _diff(t1, t2):
1074 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1074 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1075 return
1075 return
1076 t1._load()
1076 t1._load()
1077 t2._load()
1077 t2._load()
1078 for d, m1 in t1._dirs.iteritems():
1078 for d, m1 in t1._dirs.iteritems():
1079 m2 = t2._dirs.get(d, emptytree)
1079 m2 = t2._dirs.get(d, emptytree)
1080 _diff(m1, m2)
1080 _diff(m1, m2)
1081
1081
1082 for d, m2 in t2._dirs.iteritems():
1082 for d, m2 in t2._dirs.iteritems():
1083 if d not in t1._dirs:
1083 if d not in t1._dirs:
1084 _diff(emptytree, m2)
1084 _diff(emptytree, m2)
1085
1085
1086 for fn, n1 in t1._files.iteritems():
1086 for fn, n1 in t1._files.iteritems():
1087 fl1 = t1._flags.get(fn, '')
1087 fl1 = t1._flags.get(fn, '')
1088 n2 = t2._files.get(fn, None)
1088 n2 = t2._files.get(fn, None)
1089 fl2 = t2._flags.get(fn, '')
1089 fl2 = t2._flags.get(fn, '')
1090 if n1 != n2 or fl1 != fl2:
1090 if n1 != n2 or fl1 != fl2:
1091 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1091 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1092 elif clean:
1092 elif clean:
1093 result[t1._subpath(fn)] = None
1093 result[t1._subpath(fn)] = None
1094
1094
1095 for fn, n2 in t2._files.iteritems():
1095 for fn, n2 in t2._files.iteritems():
1096 if fn not in t1._files:
1096 if fn not in t1._files:
1097 fl2 = t2._flags.get(fn, '')
1097 fl2 = t2._flags.get(fn, '')
1098 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1098 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1099
1099
1100 _diff(self, m2)
1100 _diff(self, m2)
1101 return result
1101 return result
1102
1102
1103 def unmodifiedsince(self, m2):
1103 def unmodifiedsince(self, m2):
1104 return not self._dirty and not m2._dirty and self._node == m2._node
1104 return not self._dirty and not m2._dirty and self._node == m2._node
1105
1105
1106 def parse(self, text, readsubtree):
1106 def parse(self, text, readsubtree):
1107 for f, n, fl in _parse(text):
1107 for f, n, fl in _parse(text):
1108 if fl == 't':
1108 if fl == 't':
1109 f = f + '/'
1109 f = f + '/'
1110 self._dirs[f] = readsubtree(self._subpath(f), n)
1110 self._dirs[f] = readsubtree(self._subpath(f), n)
1111 elif '/' in f:
1111 elif '/' in f:
1112 # This is a flat manifest, so use __setitem__ and setflag rather
1112 # This is a flat manifest, so use __setitem__ and setflag rather
1113 # than assigning directly to _files and _flags, so we can
1113 # than assigning directly to _files and _flags, so we can
1114 # assign a path in a subdirectory, and to mark dirty (compared
1114 # assign a path in a subdirectory, and to mark dirty (compared
1115 # to nullid).
1115 # to nullid).
1116 self[f] = n
1116 self[f] = n
1117 if fl:
1117 if fl:
1118 self.setflag(f, fl)
1118 self.setflag(f, fl)
1119 else:
1119 else:
1120 # Assigning to _files and _flags avoids marking as dirty,
1120 # Assigning to _files and _flags avoids marking as dirty,
1121 # and should be a little faster.
1121 # and should be a little faster.
1122 self._files[f] = n
1122 self._files[f] = n
1123 if fl:
1123 if fl:
1124 self._flags[f] = fl
1124 self._flags[f] = fl
1125
1125
1126 def text(self, usemanifestv2=False):
1126 def text(self, usemanifestv2=False):
1127 """Get the full data of this manifest as a bytestring."""
1127 """Get the full data of this manifest as a bytestring."""
1128 self._load()
1128 self._load()
1129 return _text(self.iterentries(), usemanifestv2)
1129 return _text(self.iterentries(), usemanifestv2)
1130
1130
1131 def dirtext(self, usemanifestv2=False):
1131 def dirtext(self, usemanifestv2=False):
1132 """Get the full data of this directory as a bytestring. Make sure that
1132 """Get the full data of this directory as a bytestring. Make sure that
1133 any submanifests have been written first, so their nodeids are correct.
1133 any submanifests have been written first, so their nodeids are correct.
1134 """
1134 """
1135 self._load()
1135 self._load()
1136 flags = self.flags
1136 flags = self.flags
1137 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1137 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1138 files = [(f, self._files[f], flags(f)) for f in self._files]
1138 files = [(f, self._files[f], flags(f)) for f in self._files]
1139 return _text(sorted(dirs + files), usemanifestv2)
1139 return _text(sorted(dirs + files), usemanifestv2)
1140
1140
1141 def read(self, gettext, readsubtree):
1141 def read(self, gettext, readsubtree):
1142 def _load_for_read(s):
1142 def _load_for_read(s):
1143 s.parse(gettext(), readsubtree)
1143 s.parse(gettext(), readsubtree)
1144 s._dirty = False
1144 s._dirty = False
1145 self._loadfunc = _load_for_read
1145 self._loadfunc = _load_for_read
1146
1146
1147 def writesubtrees(self, m1, m2, writesubtree):
1147 def writesubtrees(self, m1, m2, writesubtree):
1148 self._load() # for consistency; should never have any effect here
1148 self._load() # for consistency; should never have any effect here
1149 m1._load()
1149 m1._load()
1150 m2._load()
1150 m2._load()
1151 emptytree = treemanifest()
1151 emptytree = treemanifest()
1152 for d, subm in self._dirs.iteritems():
1152 for d, subm in self._dirs.iteritems():
1153 subp1 = m1._dirs.get(d, emptytree)._node
1153 subp1 = m1._dirs.get(d, emptytree)._node
1154 subp2 = m2._dirs.get(d, emptytree)._node
1154 subp2 = m2._dirs.get(d, emptytree)._node
1155 if subp1 == revlog.nullid:
1155 if subp1 == revlog.nullid:
1156 subp1, subp2 = subp2, subp1
1156 subp1, subp2 = subp2, subp1
1157 writesubtree(subm, subp1, subp2)
1157 writesubtree(subm, subp1, subp2)
1158
1158
1159 def walksubtrees(self, matcher=None):
1159 def walksubtrees(self, matcher=None):
1160 """Returns an iterator of the subtrees of this manifest, including this
1160 """Returns an iterator of the subtrees of this manifest, including this
1161 manifest itself.
1161 manifest itself.
1162
1162
1163 If `matcher` is provided, it only returns subtrees that match.
1163 If `matcher` is provided, it only returns subtrees that match.
1164 """
1164 """
1165 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1165 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1166 return
1166 return
1167 if not matcher or matcher(self._dir[:-1]):
1167 if not matcher or matcher(self._dir[:-1]):
1168 yield self
1168 yield self
1169
1169
1170 self._load()
1170 self._load()
1171 for d, subm in self._dirs.iteritems():
1171 for d, subm in self._dirs.iteritems():
1172 for subtree in subm.walksubtrees(matcher=matcher):
1172 for subtree in subm.walksubtrees(matcher=matcher):
1173 yield subtree
1173 yield subtree
1174
1174
1175 class manifestrevlog(revlog.revlog):
1175 class manifestrevlog(revlog.revlog):
1176 '''A revlog that stores manifest texts. This is responsible for caching the
1176 '''A revlog that stores manifest texts. This is responsible for caching the
1177 full-text manifest contents.
1177 full-text manifest contents.
1178 '''
1178 '''
1179 def __init__(self, opener, dir='', dirlogcache=None, indexfile=None,
1179 def __init__(self, opener, dir='', dirlogcache=None, indexfile=None,
1180 treemanifest=False):
1180 treemanifest=False):
1181 """Constructs a new manifest revlog
1181 """Constructs a new manifest revlog
1182
1182
1183 `indexfile` - used by extensions to have two manifests at once, like
1183 `indexfile` - used by extensions to have two manifests at once, like
1184 when transitioning between flatmanifeset and treemanifests.
1184 when transitioning between flatmanifeset and treemanifests.
1185
1185
1186 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1186 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1187 options can also be used to make this a tree manifest revlog. The opener
1187 options can also be used to make this a tree manifest revlog. The opener
1188 option takes precedence, so if it is set to True, we ignore whatever
1188 option takes precedence, so if it is set to True, we ignore whatever
1189 value is passed in to the constructor.
1189 value is passed in to the constructor.
1190 """
1190 """
1191 # During normal operations, we expect to deal with not more than four
1191 # During normal operations, we expect to deal with not more than four
1192 # revs at a time (such as during commit --amend). When rebasing large
1192 # revs at a time (such as during commit --amend). When rebasing large
1193 # stacks of commits, the number can go up, hence the config knob below.
1193 # stacks of commits, the number can go up, hence the config knob below.
1194 cachesize = 4
1194 cachesize = 4
1195 optiontreemanifest = False
1195 optiontreemanifest = False
1196 usemanifestv2 = False
1196 usemanifestv2 = False
1197 opts = getattr(opener, 'options', None)
1197 opts = getattr(opener, 'options', None)
1198 if opts is not None:
1198 if opts is not None:
1199 cachesize = opts.get('manifestcachesize', cachesize)
1199 cachesize = opts.get('manifestcachesize', cachesize)
1200 optiontreemanifest = opts.get('treemanifest', False)
1200 optiontreemanifest = opts.get('treemanifest', False)
1201 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
1201 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
1202
1202
1203 self._treeondisk = optiontreemanifest or treemanifest
1203 self._treeondisk = optiontreemanifest or treemanifest
1204 self._usemanifestv2 = usemanifestv2
1204 self._usemanifestv2 = usemanifestv2
1205
1205
1206 self._fulltextcache = util.lrucachedict(cachesize)
1206 self._fulltextcache = util.lrucachedict(cachesize)
1207
1207
1208 if dir:
1208 if dir:
1209 assert self._treeondisk, 'opts is %r' % opts
1209 assert self._treeondisk, 'opts is %r' % opts
1210 if not dir.endswith('/'):
1210 if not dir.endswith('/'):
1211 dir = dir + '/'
1211 dir = dir + '/'
1212
1212
1213 if indexfile is None:
1213 if indexfile is None:
1214 indexfile = '00manifest.i'
1214 indexfile = '00manifest.i'
1215 if dir:
1215 if dir:
1216 indexfile = "meta/" + dir + indexfile
1216 indexfile = "meta/" + dir + indexfile
1217
1217
1218 self._dir = dir
1218 self._dir = dir
1219 # The dirlogcache is kept on the root manifest log
1219 # The dirlogcache is kept on the root manifest log
1220 if dir:
1220 if dir:
1221 self._dirlogcache = dirlogcache
1221 self._dirlogcache = dirlogcache
1222 else:
1222 else:
1223 self._dirlogcache = {'': self}
1223 self._dirlogcache = {'': self}
1224
1224
1225 super(manifestrevlog, self).__init__(opener, indexfile,
1225 super(manifestrevlog, self).__init__(opener, indexfile,
1226 checkambig=bool(dir))
1226 checkambig=bool(dir))
1227
1227
1228 @property
1228 @property
1229 def fulltextcache(self):
1229 def fulltextcache(self):
1230 return self._fulltextcache
1230 return self._fulltextcache
1231
1231
1232 def clearcaches(self):
1232 def clearcaches(self):
1233 super(manifestrevlog, self).clearcaches()
1233 super(manifestrevlog, self).clearcaches()
1234 self._fulltextcache.clear()
1234 self._fulltextcache.clear()
1235 self._dirlogcache = {'': self}
1235 self._dirlogcache = {'': self}
1236
1236
1237 def dirlog(self, dir):
1237 def dirlog(self, dir):
1238 if dir:
1238 if dir:
1239 assert self._treeondisk
1239 assert self._treeondisk
1240 if dir not in self._dirlogcache:
1240 if dir not in self._dirlogcache:
1241 mfrevlog = manifestrevlog(self.opener, dir,
1241 mfrevlog = manifestrevlog(self.opener, dir,
1242 self._dirlogcache,
1242 self._dirlogcache,
1243 treemanifest=self._treeondisk)
1243 treemanifest=self._treeondisk)
1244 self._dirlogcache[dir] = mfrevlog
1244 self._dirlogcache[dir] = mfrevlog
1245 return self._dirlogcache[dir]
1245 return self._dirlogcache[dir]
1246
1246
1247 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1247 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1248 if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta')
1248 if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta')
1249 and not self._usemanifestv2):
1249 and not self._usemanifestv2):
1250 # If our first parent is in the manifest cache, we can
1250 # If our first parent is in the manifest cache, we can
1251 # compute a delta here using properties we know about the
1251 # compute a delta here using properties we know about the
1252 # manifest up-front, which may save time later for the
1252 # manifest up-front, which may save time later for the
1253 # revlog layer.
1253 # revlog layer.
1254
1254
1255 _checkforbidden(added)
1255 _checkforbidden(added)
1256 # combine the changed lists into one sorted iterator
1256 # combine the changed lists into one sorted iterator
1257 work = heapq.merge([(x, False) for x in added],
1257 work = heapq.merge([(x, False) for x in added],
1258 [(x, True) for x in removed])
1258 [(x, True) for x in removed])
1259
1259
1260 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1260 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1261 cachedelta = self.rev(p1), deltatext
1261 cachedelta = self.rev(p1), deltatext
1262 text = util.buffer(arraytext)
1262 text = util.buffer(arraytext)
1263 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1263 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1264 else:
1264 else:
1265 # The first parent manifest isn't already loaded, so we'll
1265 # The first parent manifest isn't already loaded, so we'll
1266 # just encode a fulltext of the manifest and pass that
1266 # just encode a fulltext of the manifest and pass that
1267 # through to the revlog layer, and let it handle the delta
1267 # through to the revlog layer, and let it handle the delta
1268 # process.
1268 # process.
1269 if self._treeondisk:
1269 if self._treeondisk:
1270 assert readtree, "readtree must be set for treemanifest writes"
1270 assert readtree, "readtree must be set for treemanifest writes"
1271 m1 = readtree(self._dir, p1)
1271 m1 = readtree(self._dir, p1)
1272 m2 = readtree(self._dir, p2)
1272 m2 = readtree(self._dir, p2)
1273 n = self._addtree(m, transaction, link, m1, m2, readtree)
1273 n = self._addtree(m, transaction, link, m1, m2, readtree)
1274 arraytext = None
1274 arraytext = None
1275 else:
1275 else:
1276 text = m.text(self._usemanifestv2)
1276 text = m.text(self._usemanifestv2)
1277 n = self.addrevision(text, transaction, link, p1, p2)
1277 n = self.addrevision(text, transaction, link, p1, p2)
1278 arraytext = bytearray(text)
1278 arraytext = bytearray(text)
1279
1279
1280 if arraytext is not None:
1280 if arraytext is not None:
1281 self.fulltextcache[n] = arraytext
1281 self.fulltextcache[n] = arraytext
1282
1282
1283 return n
1283 return n
1284
1284
1285 def _addtree(self, m, transaction, link, m1, m2, readtree):
1285 def _addtree(self, m, transaction, link, m1, m2, readtree):
1286 # If the manifest is unchanged compared to one parent,
1286 # If the manifest is unchanged compared to one parent,
1287 # don't write a new revision
1287 # don't write a new revision
1288 if self._dir != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(m2)):
1288 if self._dir != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(m2)):
1289 return m.node()
1289 return m.node()
1290 def writesubtree(subm, subp1, subp2):
1290 def writesubtree(subm, subp1, subp2):
1291 sublog = self.dirlog(subm.dir())
1291 sublog = self.dirlog(subm.dir())
1292 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1292 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1293 readtree=readtree)
1293 readtree=readtree)
1294 m.writesubtrees(m1, m2, writesubtree)
1294 m.writesubtrees(m1, m2, writesubtree)
1295 text = m.dirtext(self._usemanifestv2)
1295 text = m.dirtext(self._usemanifestv2)
1296 n = None
1296 n = None
1297 if self._dir != '':
1297 if self._dir != '':
1298 # Double-check whether contents are unchanged to one parent
1298 # Double-check whether contents are unchanged to one parent
1299 if text == m1.dirtext(self._usemanifestv2):
1299 if text == m1.dirtext(self._usemanifestv2):
1300 n = m1.node()
1300 n = m1.node()
1301 elif text == m2.dirtext(self._usemanifestv2):
1301 elif text == m2.dirtext(self._usemanifestv2):
1302 n = m2.node()
1302 n = m2.node()
1303
1303
1304 if not n:
1304 if not n:
1305 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1305 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1306
1306
1307 # Save nodeid so parent manifest can calculate its nodeid
1307 # Save nodeid so parent manifest can calculate its nodeid
1308 m.setnode(n)
1308 m.setnode(n)
1309 return n
1309 return n
1310
1310
1311 class manifestlog(object):
1311 class manifestlog(object):
1312 """A collection class representing the collection of manifest snapshots
1312 """A collection class representing the collection of manifest snapshots
1313 referenced by commits in the repository.
1313 referenced by commits in the repository.
1314
1314
1315 In this situation, 'manifest' refers to the abstract concept of a snapshot
1315 In this situation, 'manifest' refers to the abstract concept of a snapshot
1316 of the list of files in the given commit. Consumers of the output of this
1316 of the list of files in the given commit. Consumers of the output of this
1317 class do not care about the implementation details of the actual manifests
1317 class do not care about the implementation details of the actual manifests
1318 they receive (i.e. tree or flat or lazily loaded, etc)."""
1318 they receive (i.e. tree or flat or lazily loaded, etc)."""
1319 def __init__(self, opener, repo):
1319 def __init__(self, opener, repo):
1320 usetreemanifest = False
1320 usetreemanifest = False
1321 cachesize = 4
1321 cachesize = 4
1322
1322
1323 opts = getattr(opener, 'options', None)
1323 opts = getattr(opener, 'options', None)
1324 if opts is not None:
1324 if opts is not None:
1325 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1325 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1326 cachesize = opts.get('manifestcachesize', cachesize)
1326 cachesize = opts.get('manifestcachesize', cachesize)
1327 self._treeinmem = usetreemanifest
1327 self._treeinmem = usetreemanifest
1328
1328
1329 self._revlog = repo._constructmanifest()
1329 self._revlog = repo._constructmanifest()
1330
1330
1331 # A cache of the manifestctx or treemanifestctx for each directory
1331 # A cache of the manifestctx or treemanifestctx for each directory
1332 self._dirmancache = {}
1332 self._dirmancache = {}
1333 self._dirmancache[''] = util.lrucachedict(cachesize)
1333 self._dirmancache[''] = util.lrucachedict(cachesize)
1334
1334
1335 self.cachesize = cachesize
1335 self.cachesize = cachesize
1336
1336
1337 def __getitem__(self, node):
1337 def __getitem__(self, node):
1338 """Retrieves the manifest instance for the given node. Throws a
1338 """Retrieves the manifest instance for the given node. Throws a
1339 LookupError if not found.
1339 LookupError if not found.
1340 """
1340 """
1341 return self.get('', node)
1341 return self.get('', node)
1342
1342
1343 def get(self, dir, node, verify=True):
1343 def get(self, dir, node, verify=True):
1344 """Retrieves the manifest instance for the given node. Throws a
1344 """Retrieves the manifest instance for the given node. Throws a
1345 LookupError if not found.
1345 LookupError if not found.
1346
1346
1347 `verify` - if True an exception will be thrown if the node is not in
1347 `verify` - if True an exception will be thrown if the node is not in
1348 the revlog
1348 the revlog
1349 """
1349 """
1350 if node in self._dirmancache.get(dir, ()):
1350 if node in self._dirmancache.get(dir, ()):
1351 return self._dirmancache[dir][node]
1351 return self._dirmancache[dir][node]
1352
1352
1353 if dir:
1353 if dir:
1354 if self._revlog._treeondisk:
1354 if self._revlog._treeondisk:
1355 if verify:
1355 if verify:
1356 dirlog = self._revlog.dirlog(dir)
1356 dirlog = self._revlog.dirlog(dir)
1357 if node not in dirlog.nodemap:
1357 if node not in dirlog.nodemap:
1358 raise LookupError(node, dirlog.indexfile,
1358 raise LookupError(node, dirlog.indexfile,
1359 _('no node'))
1359 _('no node'))
1360 m = treemanifestctx(self, dir, node)
1360 m = treemanifestctx(self, dir, node)
1361 else:
1361 else:
1362 raise error.Abort(
1362 raise error.Abort(
1363 _("cannot ask for manifest directory '%s' in a flat "
1363 _("cannot ask for manifest directory '%s' in a flat "
1364 "manifest") % dir)
1364 "manifest") % dir)
1365 else:
1365 else:
1366 if verify:
1366 if verify:
1367 if node not in self._revlog.nodemap:
1367 if node not in self._revlog.nodemap:
1368 raise LookupError(node, self._revlog.indexfile,
1368 raise LookupError(node, self._revlog.indexfile,
1369 _('no node'))
1369 _('no node'))
1370 if self._treeinmem:
1370 if self._treeinmem:
1371 m = treemanifestctx(self, '', node)
1371 m = treemanifestctx(self, '', node)
1372 else:
1372 else:
1373 m = manifestctx(self, node)
1373 m = manifestctx(self, node)
1374
1374
1375 if node != revlog.nullid:
1375 if node != revlog.nullid:
1376 mancache = self._dirmancache.get(dir)
1376 mancache = self._dirmancache.get(dir)
1377 if not mancache:
1377 if not mancache:
1378 mancache = util.lrucachedict(self.cachesize)
1378 mancache = util.lrucachedict(self.cachesize)
1379 self._dirmancache[dir] = mancache
1379 self._dirmancache[dir] = mancache
1380 mancache[node] = m
1380 mancache[node] = m
1381 return m
1381 return m
1382
1382
1383 def clearcaches(self):
1383 def clearcaches(self):
1384 self._dirmancache.clear()
1384 self._dirmancache.clear()
1385 self._revlog.clearcaches()
1385 self._revlog.clearcaches()
1386
1386
1387 class memmanifestctx(object):
1387 class memmanifestctx(object):
1388 def __init__(self, manifestlog):
1388 def __init__(self, manifestlog):
1389 self._manifestlog = manifestlog
1389 self._manifestlog = manifestlog
1390 self._manifestdict = manifestdict()
1390 self._manifestdict = manifestdict()
1391
1391
1392 def _revlog(self):
1392 def _revlog(self):
1393 return self._manifestlog._revlog
1393 return self._manifestlog._revlog
1394
1394
1395 def new(self):
1395 def new(self):
1396 return memmanifestctx(self._manifestlog)
1396 return memmanifestctx(self._manifestlog)
1397
1397
1398 def copy(self):
1398 def copy(self):
1399 memmf = memmanifestctx(self._manifestlog)
1399 memmf = memmanifestctx(self._manifestlog)
1400 memmf._manifestdict = self.read().copy()
1400 memmf._manifestdict = self.read().copy()
1401 return memmf
1401 return memmf
1402
1402
1403 def read(self):
1403 def read(self):
1404 return self._manifestdict
1404 return self._manifestdict
1405
1405
1406 def write(self, transaction, link, p1, p2, added, removed):
1406 def write(self, transaction, link, p1, p2, added, removed):
1407 return self._revlog().add(self._manifestdict, transaction, link, p1, p2,
1407 return self._revlog().add(self._manifestdict, transaction, link, p1, p2,
1408 added, removed)
1408 added, removed)
1409
1409
1410 class manifestctx(object):
1410 class manifestctx(object):
1411 """A class representing a single revision of a manifest, including its
1411 """A class representing a single revision of a manifest, including its
1412 contents, its parent revs, and its linkrev.
1412 contents, its parent revs, and its linkrev.
1413 """
1413 """
1414 def __init__(self, manifestlog, node):
1414 def __init__(self, manifestlog, node):
1415 self._manifestlog = manifestlog
1415 self._manifestlog = manifestlog
1416 self._data = None
1416 self._data = None
1417
1417
1418 self._node = node
1418 self._node = node
1419
1419
1420 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1420 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1421 # but let's add it later when something needs it and we can load it
1421 # but let's add it later when something needs it and we can load it
1422 # lazily.
1422 # lazily.
1423 #self.p1, self.p2 = revlog.parents(node)
1423 #self.p1, self.p2 = revlog.parents(node)
1424 #rev = revlog.rev(node)
1424 #rev = revlog.rev(node)
1425 #self.linkrev = revlog.linkrev(rev)
1425 #self.linkrev = revlog.linkrev(rev)
1426
1426
1427 def _revlog(self):
1427 def _revlog(self):
1428 return self._manifestlog._revlog
1428 return self._manifestlog._revlog
1429
1429
1430 def node(self):
1430 def node(self):
1431 return self._node
1431 return self._node
1432
1432
1433 def new(self):
1433 def new(self):
1434 return memmanifestctx(self._manifestlog)
1434 return memmanifestctx(self._manifestlog)
1435
1435
1436 def copy(self):
1436 def copy(self):
1437 memmf = memmanifestctx(self._manifestlog)
1437 memmf = memmanifestctx(self._manifestlog)
1438 memmf._manifestdict = self.read().copy()
1438 memmf._manifestdict = self.read().copy()
1439 return memmf
1439 return memmf
1440
1440
1441 @propertycache
1441 @propertycache
1442 def parents(self):
1442 def parents(self):
1443 return self._revlog().parents(self._node)
1443 return self._revlog().parents(self._node)
1444
1444
1445 def read(self):
1445 def read(self):
1446 if self._data is None:
1446 if self._data is None:
1447 if self._node == revlog.nullid:
1447 if self._node == revlog.nullid:
1448 self._data = manifestdict()
1448 self._data = manifestdict()
1449 else:
1449 else:
1450 rl = self._revlog()
1450 rl = self._revlog()
1451 text = rl.revision(self._node)
1451 text = rl.revision(self._node)
1452 arraytext = bytearray(text)
1452 arraytext = bytearray(text)
1453 rl._fulltextcache[self._node] = arraytext
1453 rl._fulltextcache[self._node] = arraytext
1454 self._data = manifestdict(text)
1454 self._data = manifestdict(text)
1455 return self._data
1455 return self._data
1456
1456
1457 def readfast(self, shallow=False):
1457 def readfast(self, shallow=False):
1458 '''Calls either readdelta or read, based on which would be less work.
1458 '''Calls either readdelta or read, based on which would be less work.
1459 readdelta is called if the delta is against the p1, and therefore can be
1459 readdelta is called if the delta is against the p1, and therefore can be
1460 read quickly.
1460 read quickly.
1461
1461
1462 If `shallow` is True, nothing changes since this is a flat manifest.
1462 If `shallow` is True, nothing changes since this is a flat manifest.
1463 '''
1463 '''
1464 rl = self._revlog()
1464 rl = self._revlog()
1465 r = rl.rev(self._node)
1465 r = rl.rev(self._node)
1466 deltaparent = rl.deltaparent(r)
1466 deltaparent = rl.deltaparent(r)
1467 if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r):
1467 if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r):
1468 return self.readdelta()
1468 return self.readdelta()
1469 return self.read()
1469 return self.read()
1470
1470
1471 def readdelta(self, shallow=False):
1471 def readdelta(self, shallow=False):
1472 '''Returns a manifest containing just the entries that are present
1472 '''Returns a manifest containing just the entries that are present
1473 in this manifest, but not in its p1 manifest. This is efficient to read
1473 in this manifest, but not in its p1 manifest. This is efficient to read
1474 if the revlog delta is already p1.
1474 if the revlog delta is already p1.
1475
1475
1476 Changing the value of `shallow` has no effect on flat manifests.
1476 Changing the value of `shallow` has no effect on flat manifests.
1477 '''
1477 '''
1478 revlog = self._revlog()
1478 revlog = self._revlog()
1479 if revlog._usemanifestv2:
1479 if revlog._usemanifestv2:
1480 # Need to perform a slow delta
1480 # Need to perform a slow delta
1481 r0 = revlog.deltaparent(revlog.rev(self._node))
1481 r0 = revlog.deltaparent(revlog.rev(self._node))
1482 m0 = self._manifestlog[revlog.node(r0)].read()
1482 m0 = self._manifestlog[revlog.node(r0)].read()
1483 m1 = self.read()
1483 m1 = self.read()
1484 md = manifestdict()
1484 md = manifestdict()
1485 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1485 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1486 if n1:
1486 if n1:
1487 md[f] = n1
1487 md[f] = n1
1488 if fl1:
1488 if fl1:
1489 md.setflag(f, fl1)
1489 md.setflag(f, fl1)
1490 return md
1490 return md
1491
1491
1492 r = revlog.rev(self._node)
1492 r = revlog.rev(self._node)
1493 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1493 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1494 return manifestdict(d)
1494 return manifestdict(d)
1495
1495
1496 def find(self, key):
1496 def find(self, key):
1497 return self.read().find(key)
1497 return self.read().find(key)
1498
1498
1499 class memtreemanifestctx(object):
1499 class memtreemanifestctx(object):
1500 def __init__(self, manifestlog, dir=''):
1500 def __init__(self, manifestlog, dir=''):
1501 self._manifestlog = manifestlog
1501 self._manifestlog = manifestlog
1502 self._dir = dir
1502 self._dir = dir
1503 self._treemanifest = treemanifest()
1503 self._treemanifest = treemanifest()
1504
1504
1505 def _revlog(self):
1505 def _revlog(self):
1506 return self._manifestlog._revlog
1506 return self._manifestlog._revlog
1507
1507
1508 def new(self, dir=''):
1508 def new(self, dir=''):
1509 return memtreemanifestctx(self._manifestlog, dir=dir)
1509 return memtreemanifestctx(self._manifestlog, dir=dir)
1510
1510
1511 def copy(self):
1511 def copy(self):
1512 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1512 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1513 memmf._treemanifest = self._treemanifest.copy()
1513 memmf._treemanifest = self._treemanifest.copy()
1514 return memmf
1514 return memmf
1515
1515
1516 def read(self):
1516 def read(self):
1517 return self._treemanifest
1517 return self._treemanifest
1518
1518
1519 def write(self, transaction, link, p1, p2, added, removed):
1519 def write(self, transaction, link, p1, p2, added, removed):
1520 def readtree(dir, node):
1520 def readtree(dir, node):
1521 return self._manifestlog.get(dir, node).read()
1521 return self._manifestlog.get(dir, node).read()
1522 return self._revlog().add(self._treemanifest, transaction, link, p1, p2,
1522 return self._revlog().add(self._treemanifest, transaction, link, p1, p2,
1523 added, removed, readtree=readtree)
1523 added, removed, readtree=readtree)
1524
1524
1525 class treemanifestctx(object):
1525 class treemanifestctx(object):
1526 def __init__(self, manifestlog, dir, node):
1526 def __init__(self, manifestlog, dir, node):
1527 self._manifestlog = manifestlog
1527 self._manifestlog = manifestlog
1528 self._dir = dir
1528 self._dir = dir
1529 self._data = None
1529 self._data = None
1530
1530
1531 self._node = node
1531 self._node = node
1532
1532
1533 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1533 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1534 # we can instantiate treemanifestctx objects for directories we don't
1534 # we can instantiate treemanifestctx objects for directories we don't
1535 # have on disk.
1535 # have on disk.
1536 #self.p1, self.p2 = revlog.parents(node)
1536 #self.p1, self.p2 = revlog.parents(node)
1537 #rev = revlog.rev(node)
1537 #rev = revlog.rev(node)
1538 #self.linkrev = revlog.linkrev(rev)
1538 #self.linkrev = revlog.linkrev(rev)
1539
1539
1540 def _revlog(self):
1540 def _revlog(self):
1541 return self._manifestlog._revlog.dirlog(self._dir)
1541 return self._manifestlog._revlog.dirlog(self._dir)
1542
1542
1543 def read(self):
1543 def read(self):
1544 if self._data is None:
1544 if self._data is None:
1545 rl = self._revlog()
1545 rl = self._revlog()
1546 if self._node == revlog.nullid:
1546 if self._node == revlog.nullid:
1547 self._data = treemanifest()
1547 self._data = treemanifest()
1548 elif rl._treeondisk:
1548 elif rl._treeondisk:
1549 m = treemanifest(dir=self._dir)
1549 m = treemanifest(dir=self._dir)
1550 def gettext():
1550 def gettext():
1551 return rl.revision(self._node)
1551 return rl.revision(self._node)
1552 def readsubtree(dir, subm):
1552 def readsubtree(dir, subm):
1553 # Set verify to False since we need to be able to create
1553 # Set verify to False since we need to be able to create
1554 # subtrees for trees that don't exist on disk.
1554 # subtrees for trees that don't exist on disk.
1555 return self._manifestlog.get(dir, subm, verify=False).read()
1555 return self._manifestlog.get(dir, subm, verify=False).read()
1556 m.read(gettext, readsubtree)
1556 m.read(gettext, readsubtree)
1557 m.setnode(self._node)
1557 m.setnode(self._node)
1558 self._data = m
1558 self._data = m
1559 else:
1559 else:
1560 text = rl.revision(self._node)
1560 text = rl.revision(self._node)
1561 arraytext = bytearray(text)
1561 arraytext = bytearray(text)
1562 rl.fulltextcache[self._node] = arraytext
1562 rl.fulltextcache[self._node] = arraytext
1563 self._data = treemanifest(dir=self._dir, text=text)
1563 self._data = treemanifest(dir=self._dir, text=text)
1564
1564
1565 return self._data
1565 return self._data
1566
1566
1567 def node(self):
1567 def node(self):
1568 return self._node
1568 return self._node
1569
1569
1570 def new(self, dir=''):
1570 def new(self, dir=''):
1571 return memtreemanifestctx(self._manifestlog, dir=dir)
1571 return memtreemanifestctx(self._manifestlog, dir=dir)
1572
1572
1573 def copy(self):
1573 def copy(self):
1574 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1574 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1575 memmf._treemanifest = self.read().copy()
1575 memmf._treemanifest = self.read().copy()
1576 return memmf
1576 return memmf
1577
1577
1578 @propertycache
1578 @propertycache
1579 def parents(self):
1579 def parents(self):
1580 return self._revlog().parents(self._node)
1580 return self._revlog().parents(self._node)
1581
1581
1582 def readdelta(self, shallow=False):
1582 def readdelta(self, shallow=False):
1583 '''Returns a manifest containing just the entries that are present
1583 '''Returns a manifest containing just the entries that are present
1584 in this manifest, but not in its p1 manifest. This is efficient to read
1584 in this manifest, but not in its p1 manifest. This is efficient to read
1585 if the revlog delta is already p1.
1585 if the revlog delta is already p1.
1586
1586
1587 If `shallow` is True, this will read the delta for this directory,
1587 If `shallow` is True, this will read the delta for this directory,
1588 without recursively reading subdirectory manifests. Instead, any
1588 without recursively reading subdirectory manifests. Instead, any
1589 subdirectory entry will be reported as it appears in the manifest, i.e.
1589 subdirectory entry will be reported as it appears in the manifest, i.e.
1590 the subdirectory will be reported among files and distinguished only by
1590 the subdirectory will be reported among files and distinguished only by
1591 its 't' flag.
1591 its 't' flag.
1592 '''
1592 '''
1593 revlog = self._revlog()
1593 revlog = self._revlog()
1594 if shallow and not revlog._usemanifestv2:
1594 if shallow and not revlog._usemanifestv2:
1595 r = revlog.rev(self._node)
1595 r = revlog.rev(self._node)
1596 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1596 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1597 return manifestdict(d)
1597 return manifestdict(d)
1598 else:
1598 else:
1599 # Need to perform a slow delta
1599 # Need to perform a slow delta
1600 r0 = revlog.deltaparent(revlog.rev(self._node))
1600 r0 = revlog.deltaparent(revlog.rev(self._node))
1601 m0 = self._manifestlog.get(self._dir, revlog.node(r0)).read()
1601 m0 = self._manifestlog.get(self._dir, revlog.node(r0)).read()
1602 m1 = self.read()
1602 m1 = self.read()
1603 md = treemanifest(dir=self._dir)
1603 md = treemanifest(dir=self._dir)
1604 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1604 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1605 if n1:
1605 if n1:
1606 md[f] = n1
1606 md[f] = n1
1607 if fl1:
1607 if fl1:
1608 md.setflag(f, fl1)
1608 md.setflag(f, fl1)
1609 return md
1609 return md
1610
1610
1611 def readfast(self, shallow=False):
1611 def readfast(self, shallow=False):
1612 '''Calls either readdelta or read, based on which would be less work.
1612 '''Calls either readdelta or read, based on which would be less work.
1613 readdelta is called if the delta is against the p1, and therefore can be
1613 readdelta is called if the delta is against the p1, and therefore can be
1614 read quickly.
1614 read quickly.
1615
1615
1616 If `shallow` is True, it only returns the entries from this manifest,
1616 If `shallow` is True, it only returns the entries from this manifest,
1617 and not any submanifests.
1617 and not any submanifests.
1618 '''
1618 '''
1619 rl = self._revlog()
1619 rl = self._revlog()
1620 r = rl.rev(self._node)
1620 r = rl.rev(self._node)
1621 deltaparent = rl.deltaparent(r)
1621 deltaparent = rl.deltaparent(r)
1622 if (deltaparent != revlog.nullrev and
1622 if (deltaparent != revlog.nullrev and
1623 deltaparent in rl.parentrevs(r)):
1623 deltaparent in rl.parentrevs(r)):
1624 return self.readdelta(shallow=shallow)
1624 return self.readdelta(shallow=shallow)
1625
1625
1626 if shallow:
1626 if shallow:
1627 return manifestdict(rl.revision(self._node))
1627 return manifestdict(rl.revision(self._node))
1628 else:
1628 else:
1629 return self.read()
1629 return self.read()
1630
1630
1631 def find(self, key):
1631 def find(self, key):
1632 return self.read().find(key)
1632 return self.read().find(key)
General Comments 0
You need to be logged in to leave comments. Login now