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