##// END OF EJS Templates
py3: return False early while checking whether None is a key in lazymanifest...
Pulkit Goyal -
r34347:05167447 default
parent child Browse files
Show More
@@ -1,1641 +1,1643
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:
446 return False
445 return key in self._lm
447 return key in self._lm
446
448
447 def __delitem__(self, key):
449 def __delitem__(self, key):
448 del self._lm[key]
450 del self._lm[key]
449
451
450 def __iter__(self):
452 def __iter__(self):
451 return self._lm.__iter__()
453 return self._lm.__iter__()
452
454
453 def iterkeys(self):
455 def iterkeys(self):
454 return self._lm.iterkeys()
456 return self._lm.iterkeys()
455
457
456 def keys(self):
458 def keys(self):
457 return list(self.iterkeys())
459 return list(self.iterkeys())
458
460
459 def filesnotin(self, m2, match=None):
461 def filesnotin(self, m2, match=None):
460 '''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'''
461 if match:
463 if match:
462 m1 = self.matches(match)
464 m1 = self.matches(match)
463 m2 = m2.matches(match)
465 m2 = m2.matches(match)
464 return m1.filesnotin(m2)
466 return m1.filesnotin(m2)
465 diff = self.diff(m2)
467 diff = self.diff(m2)
466 files = set(filepath
468 files = set(filepath
467 for filepath, hashflags in diff.iteritems()
469 for filepath, hashflags in diff.iteritems()
468 if hashflags[1][0] is None)
470 if hashflags[1][0] is None)
469 return files
471 return files
470
472
471 @propertycache
473 @propertycache
472 def _dirs(self):
474 def _dirs(self):
473 return util.dirs(self)
475 return util.dirs(self)
474
476
475 def dirs(self):
477 def dirs(self):
476 return self._dirs
478 return self._dirs
477
479
478 def hasdir(self, dir):
480 def hasdir(self, dir):
479 return dir in self._dirs
481 return dir in self._dirs
480
482
481 def _filesfastpath(self, match):
483 def _filesfastpath(self, match):
482 '''Checks whether we can correctly and quickly iterate over matcher
484 '''Checks whether we can correctly and quickly iterate over matcher
483 files instead of over manifest files.'''
485 files instead of over manifest files.'''
484 files = match.files()
486 files = match.files()
485 return (len(files) < 100 and (match.isexact() or
487 return (len(files) < 100 and (match.isexact() or
486 (match.prefix() and all(fn in self for fn in files))))
488 (match.prefix() and all(fn in self for fn in files))))
487
489
488 def walk(self, match):
490 def walk(self, match):
489 '''Generates matching file names.
491 '''Generates matching file names.
490
492
491 Equivalent to manifest.matches(match).iterkeys(), but without creating
493 Equivalent to manifest.matches(match).iterkeys(), but without creating
492 an entirely new manifest.
494 an entirely new manifest.
493
495
494 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().
495 '''
497 '''
496 if match.always():
498 if match.always():
497 for f in iter(self):
499 for f in iter(self):
498 yield f
500 yield f
499 return
501 return
500
502
501 fset = set(match.files())
503 fset = set(match.files())
502
504
503 # 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
504 if self._filesfastpath(match):
506 if self._filesfastpath(match):
505 for fn in sorted(fset):
507 for fn in sorted(fset):
506 yield fn
508 yield fn
507 return
509 return
508
510
509 for fn in self:
511 for fn in self:
510 if fn in fset:
512 if fn in fset:
511 # specified pattern is the exact name
513 # specified pattern is the exact name
512 fset.remove(fn)
514 fset.remove(fn)
513 if match(fn):
515 if match(fn):
514 yield fn
516 yield fn
515
517
516 # for dirstate.walk, files=['.'] means "walk the whole tree".
518 # for dirstate.walk, files=['.'] means "walk the whole tree".
517 # follow that here, too
519 # follow that here, too
518 fset.discard('.')
520 fset.discard('.')
519
521
520 for fn in sorted(fset):
522 for fn in sorted(fset):
521 if not self.hasdir(fn):
523 if not self.hasdir(fn):
522 match.bad(fn, None)
524 match.bad(fn, None)
523
525
524 def matches(self, match):
526 def matches(self, match):
525 '''generate a new manifest filtered by the match argument'''
527 '''generate a new manifest filtered by the match argument'''
526 if match.always():
528 if match.always():
527 return self.copy()
529 return self.copy()
528
530
529 if self._filesfastpath(match):
531 if self._filesfastpath(match):
530 m = manifestdict()
532 m = manifestdict()
531 lm = self._lm
533 lm = self._lm
532 for fn in match.files():
534 for fn in match.files():
533 if fn in lm:
535 if fn in lm:
534 m._lm[fn] = lm[fn]
536 m._lm[fn] = lm[fn]
535 return m
537 return m
536
538
537 m = manifestdict()
539 m = manifestdict()
538 m._lm = self._lm.filtercopy(match)
540 m._lm = self._lm.filtercopy(match)
539 return m
541 return m
540
542
541 def diff(self, m2, match=None, clean=False):
543 def diff(self, m2, match=None, clean=False):
542 '''Finds changes between the current manifest and m2.
544 '''Finds changes between the current manifest and m2.
543
545
544 Args:
546 Args:
545 m2: the manifest to which this manifest should be compared.
547 m2: the manifest to which this manifest should be compared.
546 clean: if true, include files unchanged between these manifests
548 clean: if true, include files unchanged between these manifests
547 with a None value in the returned dictionary.
549 with a None value in the returned dictionary.
548
550
549 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
550 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
551 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
552 in the current/other manifest. Where the file does not exist,
554 in the current/other manifest. Where the file does not exist,
553 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
554 string.
556 string.
555 '''
557 '''
556 if match:
558 if match:
557 m1 = self.matches(match)
559 m1 = self.matches(match)
558 m2 = m2.matches(match)
560 m2 = m2.matches(match)
559 return m1.diff(m2, clean=clean)
561 return m1.diff(m2, clean=clean)
560 return self._lm.diff(m2._lm, clean)
562 return self._lm.diff(m2._lm, clean)
561
563
562 def setflag(self, key, flag):
564 def setflag(self, key, flag):
563 self._lm[key] = self[key], flag
565 self._lm[key] = self[key], flag
564
566
565 def get(self, key, default=None):
567 def get(self, key, default=None):
566 try:
568 try:
567 return self._lm[key][0]
569 return self._lm[key][0]
568 except KeyError:
570 except KeyError:
569 return default
571 return default
570
572
571 def flags(self, key, default=''):
573 def flags(self, key, default=''):
572 try:
574 try:
573 return self._lm[key][1]
575 return self._lm[key][1]
574 except KeyError:
576 except KeyError:
575 return default
577 return default
576
578
577 def copy(self):
579 def copy(self):
578 c = manifestdict()
580 c = manifestdict()
579 c._lm = self._lm.copy()
581 c._lm = self._lm.copy()
580 return c
582 return c
581
583
582 def items(self):
584 def items(self):
583 return (x[:2] for x in self._lm.iterentries())
585 return (x[:2] for x in self._lm.iterentries())
584
586
585 iteritems = items
587 iteritems = items
586
588
587 def iterentries(self):
589 def iterentries(self):
588 return self._lm.iterentries()
590 return self._lm.iterentries()
589
591
590 def text(self, usemanifestv2=False):
592 def text(self, usemanifestv2=False):
591 if usemanifestv2:
593 if usemanifestv2:
592 return _textv2(self._lm.iterentries())
594 return _textv2(self._lm.iterentries())
593 else:
595 else:
594 # use (probably) native version for v1
596 # use (probably) native version for v1
595 return self._lm.text()
597 return self._lm.text()
596
598
597 def fastdelta(self, base, changes):
599 def fastdelta(self, base, changes):
598 """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
599 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.
600 """
602 """
601 delta = []
603 delta = []
602 dstart = None
604 dstart = None
603 dend = None
605 dend = None
604 dline = [""]
606 dline = [""]
605 start = 0
607 start = 0
606 # zero copy representation of base as a buffer
608 # zero copy representation of base as a buffer
607 addbuf = util.buffer(base)
609 addbuf = util.buffer(base)
608
610
609 changes = list(changes)
611 changes = list(changes)
610 if len(changes) < 1000:
612 if len(changes) < 1000:
611 # start with a readonly loop that finds the offset of
613 # start with a readonly loop that finds the offset of
612 # each line and creates the deltas
614 # each line and creates the deltas
613 for f, todelete in changes:
615 for f, todelete in changes:
614 # 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
615 start, end = _msearch(addbuf, f, start)
617 start, end = _msearch(addbuf, f, start)
616 if not todelete:
618 if not todelete:
617 h, fl = self._lm[f]
619 h, fl = self._lm[f]
618 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
620 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
619 else:
621 else:
620 if start == end:
622 if start == end:
621 # item we want to delete was not found, error out
623 # item we want to delete was not found, error out
622 raise AssertionError(
624 raise AssertionError(
623 _("failed to remove %s from manifest") % f)
625 _("failed to remove %s from manifest") % f)
624 l = ""
626 l = ""
625 if dstart is not None and dstart <= start and dend >= start:
627 if dstart is not None and dstart <= start and dend >= start:
626 if dend < end:
628 if dend < end:
627 dend = end
629 dend = end
628 if l:
630 if l:
629 dline.append(l)
631 dline.append(l)
630 else:
632 else:
631 if dstart is not None:
633 if dstart is not None:
632 delta.append([dstart, dend, "".join(dline)])
634 delta.append([dstart, dend, "".join(dline)])
633 dstart = start
635 dstart = start
634 dend = end
636 dend = end
635 dline = [l]
637 dline = [l]
636
638
637 if dstart is not None:
639 if dstart is not None:
638 delta.append([dstart, dend, "".join(dline)])
640 delta.append([dstart, dend, "".join(dline)])
639 # 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
640 deltatext, arraytext = _addlistdelta(base, delta)
642 deltatext, arraytext = _addlistdelta(base, delta)
641 else:
643 else:
642 # 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
643 # diff it.
645 # diff it.
644 arraytext = bytearray(self.text())
646 arraytext = bytearray(self.text())
645 deltatext = mdiff.textdiff(
647 deltatext = mdiff.textdiff(
646 util.buffer(base), util.buffer(arraytext))
648 util.buffer(base), util.buffer(arraytext))
647
649
648 return arraytext, deltatext
650 return arraytext, deltatext
649
651
650 def _msearch(m, s, lo=0, hi=None):
652 def _msearch(m, s, lo=0, hi=None):
651 '''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.
652
654
653 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
654 that string. If start == end the string was not found and
656 that string. If start == end the string was not found and
655 they indicate the proper sorted insertion point.
657 they indicate the proper sorted insertion point.
656
658
657 m should be a buffer, a memoryview or a byte string.
659 m should be a buffer, a memoryview or a byte string.
658 s is a byte string'''
660 s is a byte string'''
659 def advance(i, c):
661 def advance(i, c):
660 while i < lenm and m[i:i + 1] != c:
662 while i < lenm and m[i:i + 1] != c:
661 i += 1
663 i += 1
662 return i
664 return i
663 if not s:
665 if not s:
664 return (lo, lo)
666 return (lo, lo)
665 lenm = len(m)
667 lenm = len(m)
666 if not hi:
668 if not hi:
667 hi = lenm
669 hi = lenm
668 while lo < hi:
670 while lo < hi:
669 mid = (lo + hi) // 2
671 mid = (lo + hi) // 2
670 start = mid
672 start = mid
671 while start > 0 and m[start - 1:start] != '\n':
673 while start > 0 and m[start - 1:start] != '\n':
672 start -= 1
674 start -= 1
673 end = advance(start, '\0')
675 end = advance(start, '\0')
674 if bytes(m[start:end]) < s:
676 if bytes(m[start:end]) < s:
675 # 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
676 # this translates to the bisect lo = mid + 1
678 # this translates to the bisect lo = mid + 1
677 lo = advance(end + 40, '\n') + 1
679 lo = advance(end + 40, '\n') + 1
678 else:
680 else:
679 # this translates to the bisect hi = mid
681 # this translates to the bisect hi = mid
680 hi = start
682 hi = start
681 end = advance(lo, '\0')
683 end = advance(lo, '\0')
682 found = m[lo:end]
684 found = m[lo:end]
683 if s == found:
685 if s == found:
684 # 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
685 end = advance(end + 40, '\n')
687 end = advance(end + 40, '\n')
686 return (lo, end + 1)
688 return (lo, end + 1)
687 else:
689 else:
688 return (lo, lo)
690 return (lo, lo)
689
691
690 def _checkforbidden(l):
692 def _checkforbidden(l):
691 """Check filenames for illegal characters."""
693 """Check filenames for illegal characters."""
692 for f in l:
694 for f in l:
693 if '\n' in f or '\r' in f:
695 if '\n' in f or '\r' in f:
694 raise error.RevlogError(
696 raise error.RevlogError(
695 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
697 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
696
698
697
699
698 # apply the changes collected during the bisect loop to our addlist
700 # apply the changes collected during the bisect loop to our addlist
699 # return a delta suitable for addrevision
701 # return a delta suitable for addrevision
700 def _addlistdelta(addlist, x):
702 def _addlistdelta(addlist, x):
701 # for large addlist arrays, building a new array is cheaper
703 # for large addlist arrays, building a new array is cheaper
702 # than repeatedly modifying the existing one
704 # than repeatedly modifying the existing one
703 currentposition = 0
705 currentposition = 0
704 newaddlist = bytearray()
706 newaddlist = bytearray()
705
707
706 for start, end, content in x:
708 for start, end, content in x:
707 newaddlist += addlist[currentposition:start]
709 newaddlist += addlist[currentposition:start]
708 if content:
710 if content:
709 newaddlist += bytearray(content)
711 newaddlist += bytearray(content)
710
712
711 currentposition = end
713 currentposition = end
712
714
713 newaddlist += addlist[currentposition:]
715 newaddlist += addlist[currentposition:]
714
716
715 deltatext = "".join(struct.pack(">lll", start, end, len(content))
717 deltatext = "".join(struct.pack(">lll", start, end, len(content))
716 + content for start, end, content in x)
718 + content for start, end, content in x)
717 return deltatext, newaddlist
719 return deltatext, newaddlist
718
720
719 def _splittopdir(f):
721 def _splittopdir(f):
720 if '/' in f:
722 if '/' in f:
721 dir, subpath = f.split('/', 1)
723 dir, subpath = f.split('/', 1)
722 return dir + '/', subpath
724 return dir + '/', subpath
723 else:
725 else:
724 return '', f
726 return '', f
725
727
726 _noop = lambda s: None
728 _noop = lambda s: None
727
729
728 class treemanifest(object):
730 class treemanifest(object):
729 def __init__(self, dir='', text=''):
731 def __init__(self, dir='', text=''):
730 self._dir = dir
732 self._dir = dir
731 self._node = revlog.nullid
733 self._node = revlog.nullid
732 self._loadfunc = _noop
734 self._loadfunc = _noop
733 self._copyfunc = _noop
735 self._copyfunc = _noop
734 self._dirty = False
736 self._dirty = False
735 self._dirs = {}
737 self._dirs = {}
736 # Using _lazymanifest here is a little slower than plain old dicts
738 # Using _lazymanifest here is a little slower than plain old dicts
737 self._files = {}
739 self._files = {}
738 self._flags = {}
740 self._flags = {}
739 if text:
741 if text:
740 def readsubtree(subdir, subm):
742 def readsubtree(subdir, subm):
741 raise AssertionError('treemanifest constructor only accepts '
743 raise AssertionError('treemanifest constructor only accepts '
742 'flat manifests')
744 'flat manifests')
743 self.parse(text, readsubtree)
745 self.parse(text, readsubtree)
744 self._dirty = True # Mark flat manifest dirty after parsing
746 self._dirty = True # Mark flat manifest dirty after parsing
745
747
746 def _subpath(self, path):
748 def _subpath(self, path):
747 return self._dir + path
749 return self._dir + path
748
750
749 def __len__(self):
751 def __len__(self):
750 self._load()
752 self._load()
751 size = len(self._files)
753 size = len(self._files)
752 for m in self._dirs.values():
754 for m in self._dirs.values():
753 size += m.__len__()
755 size += m.__len__()
754 return size
756 return size
755
757
756 def _isempty(self):
758 def _isempty(self):
757 self._load() # for consistency; already loaded by all callers
759 self._load() # for consistency; already loaded by all callers
758 return (not self._files and (not self._dirs or
760 return (not self._files and (not self._dirs or
759 all(m._isempty() for m in self._dirs.values())))
761 all(m._isempty() for m in self._dirs.values())))
760
762
761 def __repr__(self):
763 def __repr__(self):
762 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
764 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
763 (self._dir, revlog.hex(self._node),
765 (self._dir, revlog.hex(self._node),
764 bool(self._loadfunc is _noop),
766 bool(self._loadfunc is _noop),
765 self._dirty, id(self)))
767 self._dirty, id(self)))
766
768
767 def dir(self):
769 def dir(self):
768 '''The directory that this tree manifest represents, including a
770 '''The directory that this tree manifest represents, including a
769 trailing '/'. Empty string for the repo root directory.'''
771 trailing '/'. Empty string for the repo root directory.'''
770 return self._dir
772 return self._dir
771
773
772 def node(self):
774 def node(self):
773 '''This node of this instance. nullid for unsaved instances. Should
775 '''This node of this instance. nullid for unsaved instances. Should
774 be updated when the instance is read or written from a revlog.
776 be updated when the instance is read or written from a revlog.
775 '''
777 '''
776 assert not self._dirty
778 assert not self._dirty
777 return self._node
779 return self._node
778
780
779 def setnode(self, node):
781 def setnode(self, node):
780 self._node = node
782 self._node = node
781 self._dirty = False
783 self._dirty = False
782
784
783 def iterentries(self):
785 def iterentries(self):
784 self._load()
786 self._load()
785 for p, n in sorted(itertools.chain(self._dirs.items(),
787 for p, n in sorted(itertools.chain(self._dirs.items(),
786 self._files.items())):
788 self._files.items())):
787 if p in self._files:
789 if p in self._files:
788 yield self._subpath(p), n, self._flags.get(p, '')
790 yield self._subpath(p), n, self._flags.get(p, '')
789 else:
791 else:
790 for x in n.iterentries():
792 for x in n.iterentries():
791 yield x
793 yield x
792
794
793 def items(self):
795 def items(self):
794 self._load()
796 self._load()
795 for p, n in sorted(itertools.chain(self._dirs.items(),
797 for p, n in sorted(itertools.chain(self._dirs.items(),
796 self._files.items())):
798 self._files.items())):
797 if p in self._files:
799 if p in self._files:
798 yield self._subpath(p), n
800 yield self._subpath(p), n
799 else:
801 else:
800 for f, sn in n.iteritems():
802 for f, sn in n.iteritems():
801 yield f, sn
803 yield f, sn
802
804
803 iteritems = items
805 iteritems = items
804
806
805 def iterkeys(self):
807 def iterkeys(self):
806 self._load()
808 self._load()
807 for p in sorted(itertools.chain(self._dirs, self._files)):
809 for p in sorted(itertools.chain(self._dirs, self._files)):
808 if p in self._files:
810 if p in self._files:
809 yield self._subpath(p)
811 yield self._subpath(p)
810 else:
812 else:
811 for f in self._dirs[p].iterkeys():
813 for f in self._dirs[p].iterkeys():
812 yield f
814 yield f
813
815
814 def keys(self):
816 def keys(self):
815 return list(self.iterkeys())
817 return list(self.iterkeys())
816
818
817 def __iter__(self):
819 def __iter__(self):
818 return self.iterkeys()
820 return self.iterkeys()
819
821
820 def __contains__(self, f):
822 def __contains__(self, f):
821 if f is None:
823 if f is None:
822 return False
824 return False
823 self._load()
825 self._load()
824 dir, subpath = _splittopdir(f)
826 dir, subpath = _splittopdir(f)
825 if dir:
827 if dir:
826 if dir not in self._dirs:
828 if dir not in self._dirs:
827 return False
829 return False
828 return self._dirs[dir].__contains__(subpath)
830 return self._dirs[dir].__contains__(subpath)
829 else:
831 else:
830 return f in self._files
832 return f in self._files
831
833
832 def get(self, f, default=None):
834 def get(self, f, default=None):
833 self._load()
835 self._load()
834 dir, subpath = _splittopdir(f)
836 dir, subpath = _splittopdir(f)
835 if dir:
837 if dir:
836 if dir not in self._dirs:
838 if dir not in self._dirs:
837 return default
839 return default
838 return self._dirs[dir].get(subpath, default)
840 return self._dirs[dir].get(subpath, default)
839 else:
841 else:
840 return self._files.get(f, default)
842 return self._files.get(f, default)
841
843
842 def __getitem__(self, f):
844 def __getitem__(self, f):
843 self._load()
845 self._load()
844 dir, subpath = _splittopdir(f)
846 dir, subpath = _splittopdir(f)
845 if dir:
847 if dir:
846 return self._dirs[dir].__getitem__(subpath)
848 return self._dirs[dir].__getitem__(subpath)
847 else:
849 else:
848 return self._files[f]
850 return self._files[f]
849
851
850 def flags(self, f):
852 def flags(self, f):
851 self._load()
853 self._load()
852 dir, subpath = _splittopdir(f)
854 dir, subpath = _splittopdir(f)
853 if dir:
855 if dir:
854 if dir not in self._dirs:
856 if dir not in self._dirs:
855 return ''
857 return ''
856 return self._dirs[dir].flags(subpath)
858 return self._dirs[dir].flags(subpath)
857 else:
859 else:
858 if f in self._dirs:
860 if f in self._dirs:
859 return ''
861 return ''
860 return self._flags.get(f, '')
862 return self._flags.get(f, '')
861
863
862 def find(self, f):
864 def find(self, f):
863 self._load()
865 self._load()
864 dir, subpath = _splittopdir(f)
866 dir, subpath = _splittopdir(f)
865 if dir:
867 if dir:
866 return self._dirs[dir].find(subpath)
868 return self._dirs[dir].find(subpath)
867 else:
869 else:
868 return self._files[f], self._flags.get(f, '')
870 return self._files[f], self._flags.get(f, '')
869
871
870 def __delitem__(self, f):
872 def __delitem__(self, f):
871 self._load()
873 self._load()
872 dir, subpath = _splittopdir(f)
874 dir, subpath = _splittopdir(f)
873 if dir:
875 if dir:
874 self._dirs[dir].__delitem__(subpath)
876 self._dirs[dir].__delitem__(subpath)
875 # If the directory is now empty, remove it
877 # If the directory is now empty, remove it
876 if self._dirs[dir]._isempty():
878 if self._dirs[dir]._isempty():
877 del self._dirs[dir]
879 del self._dirs[dir]
878 else:
880 else:
879 del self._files[f]
881 del self._files[f]
880 if f in self._flags:
882 if f in self._flags:
881 del self._flags[f]
883 del self._flags[f]
882 self._dirty = True
884 self._dirty = True
883
885
884 def __setitem__(self, f, n):
886 def __setitem__(self, f, n):
885 assert n is not None
887 assert n is not None
886 self._load()
888 self._load()
887 dir, subpath = _splittopdir(f)
889 dir, subpath = _splittopdir(f)
888 if dir:
890 if dir:
889 if dir not in self._dirs:
891 if dir not in self._dirs:
890 self._dirs[dir] = treemanifest(self._subpath(dir))
892 self._dirs[dir] = treemanifest(self._subpath(dir))
891 self._dirs[dir].__setitem__(subpath, n)
893 self._dirs[dir].__setitem__(subpath, n)
892 else:
894 else:
893 self._files[f] = n[:21] # to match manifestdict's behavior
895 self._files[f] = n[:21] # to match manifestdict's behavior
894 self._dirty = True
896 self._dirty = True
895
897
896 def _load(self):
898 def _load(self):
897 if self._loadfunc is not _noop:
899 if self._loadfunc is not _noop:
898 lf, self._loadfunc = self._loadfunc, _noop
900 lf, self._loadfunc = self._loadfunc, _noop
899 lf(self)
901 lf(self)
900 elif self._copyfunc is not _noop:
902 elif self._copyfunc is not _noop:
901 cf, self._copyfunc = self._copyfunc, _noop
903 cf, self._copyfunc = self._copyfunc, _noop
902 cf(self)
904 cf(self)
903
905
904 def setflag(self, f, flags):
906 def setflag(self, f, flags):
905 """Set the flags (symlink, executable) for path f."""
907 """Set the flags (symlink, executable) for path f."""
906 self._load()
908 self._load()
907 dir, subpath = _splittopdir(f)
909 dir, subpath = _splittopdir(f)
908 if dir:
910 if dir:
909 if dir not in self._dirs:
911 if dir not in self._dirs:
910 self._dirs[dir] = treemanifest(self._subpath(dir))
912 self._dirs[dir] = treemanifest(self._subpath(dir))
911 self._dirs[dir].setflag(subpath, flags)
913 self._dirs[dir].setflag(subpath, flags)
912 else:
914 else:
913 self._flags[f] = flags
915 self._flags[f] = flags
914 self._dirty = True
916 self._dirty = True
915
917
916 def copy(self):
918 def copy(self):
917 copy = treemanifest(self._dir)
919 copy = treemanifest(self._dir)
918 copy._node = self._node
920 copy._node = self._node
919 copy._dirty = self._dirty
921 copy._dirty = self._dirty
920 if self._copyfunc is _noop:
922 if self._copyfunc is _noop:
921 def _copyfunc(s):
923 def _copyfunc(s):
922 self._load()
924 self._load()
923 for d in self._dirs:
925 for d in self._dirs:
924 s._dirs[d] = self._dirs[d].copy()
926 s._dirs[d] = self._dirs[d].copy()
925 s._files = dict.copy(self._files)
927 s._files = dict.copy(self._files)
926 s._flags = dict.copy(self._flags)
928 s._flags = dict.copy(self._flags)
927 if self._loadfunc is _noop:
929 if self._loadfunc is _noop:
928 _copyfunc(copy)
930 _copyfunc(copy)
929 else:
931 else:
930 copy._copyfunc = _copyfunc
932 copy._copyfunc = _copyfunc
931 else:
933 else:
932 copy._copyfunc = self._copyfunc
934 copy._copyfunc = self._copyfunc
933 return copy
935 return copy
934
936
935 def filesnotin(self, m2, match=None):
937 def filesnotin(self, m2, match=None):
936 '''Set of files in this manifest that are not in the other'''
938 '''Set of files in this manifest that are not in the other'''
937 if match:
939 if match:
938 m1 = self.matches(match)
940 m1 = self.matches(match)
939 m2 = m2.matches(match)
941 m2 = m2.matches(match)
940 return m1.filesnotin(m2)
942 return m1.filesnotin(m2)
941
943
942 files = set()
944 files = set()
943 def _filesnotin(t1, t2):
945 def _filesnotin(t1, t2):
944 if t1._node == t2._node and not t1._dirty and not t2._dirty:
946 if t1._node == t2._node and not t1._dirty and not t2._dirty:
945 return
947 return
946 t1._load()
948 t1._load()
947 t2._load()
949 t2._load()
948 for d, m1 in t1._dirs.iteritems():
950 for d, m1 in t1._dirs.iteritems():
949 if d in t2._dirs:
951 if d in t2._dirs:
950 m2 = t2._dirs[d]
952 m2 = t2._dirs[d]
951 _filesnotin(m1, m2)
953 _filesnotin(m1, m2)
952 else:
954 else:
953 files.update(m1.iterkeys())
955 files.update(m1.iterkeys())
954
956
955 for fn in t1._files.iterkeys():
957 for fn in t1._files.iterkeys():
956 if fn not in t2._files:
958 if fn not in t2._files:
957 files.add(t1._subpath(fn))
959 files.add(t1._subpath(fn))
958
960
959 _filesnotin(self, m2)
961 _filesnotin(self, m2)
960 return files
962 return files
961
963
962 @propertycache
964 @propertycache
963 def _alldirs(self):
965 def _alldirs(self):
964 return util.dirs(self)
966 return util.dirs(self)
965
967
966 def dirs(self):
968 def dirs(self):
967 return self._alldirs
969 return self._alldirs
968
970
969 def hasdir(self, dir):
971 def hasdir(self, dir):
970 self._load()
972 self._load()
971 topdir, subdir = _splittopdir(dir)
973 topdir, subdir = _splittopdir(dir)
972 if topdir:
974 if topdir:
973 if topdir in self._dirs:
975 if topdir in self._dirs:
974 return self._dirs[topdir].hasdir(subdir)
976 return self._dirs[topdir].hasdir(subdir)
975 return False
977 return False
976 return (dir + '/') in self._dirs
978 return (dir + '/') in self._dirs
977
979
978 def walk(self, match):
980 def walk(self, match):
979 '''Generates matching file names.
981 '''Generates matching file names.
980
982
981 Equivalent to manifest.matches(match).iterkeys(), but without creating
983 Equivalent to manifest.matches(match).iterkeys(), but without creating
982 an entirely new manifest.
984 an entirely new manifest.
983
985
984 It also reports nonexistent files by marking them bad with match.bad().
986 It also reports nonexistent files by marking them bad with match.bad().
985 '''
987 '''
986 if match.always():
988 if match.always():
987 for f in iter(self):
989 for f in iter(self):
988 yield f
990 yield f
989 return
991 return
990
992
991 fset = set(match.files())
993 fset = set(match.files())
992
994
993 for fn in self._walk(match):
995 for fn in self._walk(match):
994 if fn in fset:
996 if fn in fset:
995 # specified pattern is the exact name
997 # specified pattern is the exact name
996 fset.remove(fn)
998 fset.remove(fn)
997 yield fn
999 yield fn
998
1000
999 # for dirstate.walk, files=['.'] means "walk the whole tree".
1001 # for dirstate.walk, files=['.'] means "walk the whole tree".
1000 # follow that here, too
1002 # follow that here, too
1001 fset.discard('.')
1003 fset.discard('.')
1002
1004
1003 for fn in sorted(fset):
1005 for fn in sorted(fset):
1004 if not self.hasdir(fn):
1006 if not self.hasdir(fn):
1005 match.bad(fn, None)
1007 match.bad(fn, None)
1006
1008
1007 def _walk(self, match):
1009 def _walk(self, match):
1008 '''Recursively generates matching file names for walk().'''
1010 '''Recursively generates matching file names for walk().'''
1009 if not match.visitdir(self._dir[:-1] or '.'):
1011 if not match.visitdir(self._dir[:-1] or '.'):
1010 return
1012 return
1011
1013
1012 # yield this dir's files and walk its submanifests
1014 # yield this dir's files and walk its submanifests
1013 self._load()
1015 self._load()
1014 for p in sorted(self._dirs.keys() + self._files.keys()):
1016 for p in sorted(self._dirs.keys() + self._files.keys()):
1015 if p in self._files:
1017 if p in self._files:
1016 fullp = self._subpath(p)
1018 fullp = self._subpath(p)
1017 if match(fullp):
1019 if match(fullp):
1018 yield fullp
1020 yield fullp
1019 else:
1021 else:
1020 for f in self._dirs[p]._walk(match):
1022 for f in self._dirs[p]._walk(match):
1021 yield f
1023 yield f
1022
1024
1023 def matches(self, match):
1025 def matches(self, match):
1024 '''generate a new manifest filtered by the match argument'''
1026 '''generate a new manifest filtered by the match argument'''
1025 if match.always():
1027 if match.always():
1026 return self.copy()
1028 return self.copy()
1027
1029
1028 return self._matches(match)
1030 return self._matches(match)
1029
1031
1030 def _matches(self, match):
1032 def _matches(self, match):
1031 '''recursively generate a new manifest filtered by the match argument.
1033 '''recursively generate a new manifest filtered by the match argument.
1032 '''
1034 '''
1033
1035
1034 visit = match.visitdir(self._dir[:-1] or '.')
1036 visit = match.visitdir(self._dir[:-1] or '.')
1035 if visit == 'all':
1037 if visit == 'all':
1036 return self.copy()
1038 return self.copy()
1037 ret = treemanifest(self._dir)
1039 ret = treemanifest(self._dir)
1038 if not visit:
1040 if not visit:
1039 return ret
1041 return ret
1040
1042
1041 self._load()
1043 self._load()
1042 for fn in self._files:
1044 for fn in self._files:
1043 fullp = self._subpath(fn)
1045 fullp = self._subpath(fn)
1044 if not match(fullp):
1046 if not match(fullp):
1045 continue
1047 continue
1046 ret._files[fn] = self._files[fn]
1048 ret._files[fn] = self._files[fn]
1047 if fn in self._flags:
1049 if fn in self._flags:
1048 ret._flags[fn] = self._flags[fn]
1050 ret._flags[fn] = self._flags[fn]
1049
1051
1050 for dir, subm in self._dirs.iteritems():
1052 for dir, subm in self._dirs.iteritems():
1051 m = subm._matches(match)
1053 m = subm._matches(match)
1052 if not m._isempty():
1054 if not m._isempty():
1053 ret._dirs[dir] = m
1055 ret._dirs[dir] = m
1054
1056
1055 if not ret._isempty():
1057 if not ret._isempty():
1056 ret._dirty = True
1058 ret._dirty = True
1057 return ret
1059 return ret
1058
1060
1059 def diff(self, m2, match=None, clean=False):
1061 def diff(self, m2, match=None, clean=False):
1060 '''Finds changes between the current manifest and m2.
1062 '''Finds changes between the current manifest and m2.
1061
1063
1062 Args:
1064 Args:
1063 m2: the manifest to which this manifest should be compared.
1065 m2: the manifest to which this manifest should be compared.
1064 clean: if true, include files unchanged between these manifests
1066 clean: if true, include files unchanged between these manifests
1065 with a None value in the returned dictionary.
1067 with a None value in the returned dictionary.
1066
1068
1067 The result is returned as a dict with filename as key and
1069 The result is returned as a dict with filename as key and
1068 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1070 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1069 nodeid in the current/other manifest and fl1/fl2 is the flag
1071 nodeid in the current/other manifest and fl1/fl2 is the flag
1070 in the current/other manifest. Where the file does not exist,
1072 in the current/other manifest. Where the file does not exist,
1071 the nodeid will be None and the flags will be the empty
1073 the nodeid will be None and the flags will be the empty
1072 string.
1074 string.
1073 '''
1075 '''
1074 if match:
1076 if match:
1075 m1 = self.matches(match)
1077 m1 = self.matches(match)
1076 m2 = m2.matches(match)
1078 m2 = m2.matches(match)
1077 return m1.diff(m2, clean=clean)
1079 return m1.diff(m2, clean=clean)
1078 result = {}
1080 result = {}
1079 emptytree = treemanifest()
1081 emptytree = treemanifest()
1080 def _diff(t1, t2):
1082 def _diff(t1, t2):
1081 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1083 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1082 return
1084 return
1083 t1._load()
1085 t1._load()
1084 t2._load()
1086 t2._load()
1085 for d, m1 in t1._dirs.iteritems():
1087 for d, m1 in t1._dirs.iteritems():
1086 m2 = t2._dirs.get(d, emptytree)
1088 m2 = t2._dirs.get(d, emptytree)
1087 _diff(m1, m2)
1089 _diff(m1, m2)
1088
1090
1089 for d, m2 in t2._dirs.iteritems():
1091 for d, m2 in t2._dirs.iteritems():
1090 if d not in t1._dirs:
1092 if d not in t1._dirs:
1091 _diff(emptytree, m2)
1093 _diff(emptytree, m2)
1092
1094
1093 for fn, n1 in t1._files.iteritems():
1095 for fn, n1 in t1._files.iteritems():
1094 fl1 = t1._flags.get(fn, '')
1096 fl1 = t1._flags.get(fn, '')
1095 n2 = t2._files.get(fn, None)
1097 n2 = t2._files.get(fn, None)
1096 fl2 = t2._flags.get(fn, '')
1098 fl2 = t2._flags.get(fn, '')
1097 if n1 != n2 or fl1 != fl2:
1099 if n1 != n2 or fl1 != fl2:
1098 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1100 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1099 elif clean:
1101 elif clean:
1100 result[t1._subpath(fn)] = None
1102 result[t1._subpath(fn)] = None
1101
1103
1102 for fn, n2 in t2._files.iteritems():
1104 for fn, n2 in t2._files.iteritems():
1103 if fn not in t1._files:
1105 if fn not in t1._files:
1104 fl2 = t2._flags.get(fn, '')
1106 fl2 = t2._flags.get(fn, '')
1105 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1107 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1106
1108
1107 _diff(self, m2)
1109 _diff(self, m2)
1108 return result
1110 return result
1109
1111
1110 def unmodifiedsince(self, m2):
1112 def unmodifiedsince(self, m2):
1111 return not self._dirty and not m2._dirty and self._node == m2._node
1113 return not self._dirty and not m2._dirty and self._node == m2._node
1112
1114
1113 def parse(self, text, readsubtree):
1115 def parse(self, text, readsubtree):
1114 for f, n, fl in _parse(text):
1116 for f, n, fl in _parse(text):
1115 if fl == 't':
1117 if fl == 't':
1116 f = f + '/'
1118 f = f + '/'
1117 self._dirs[f] = readsubtree(self._subpath(f), n)
1119 self._dirs[f] = readsubtree(self._subpath(f), n)
1118 elif '/' in f:
1120 elif '/' in f:
1119 # This is a flat manifest, so use __setitem__ and setflag rather
1121 # This is a flat manifest, so use __setitem__ and setflag rather
1120 # than assigning directly to _files and _flags, so we can
1122 # than assigning directly to _files and _flags, so we can
1121 # assign a path in a subdirectory, and to mark dirty (compared
1123 # assign a path in a subdirectory, and to mark dirty (compared
1122 # to nullid).
1124 # to nullid).
1123 self[f] = n
1125 self[f] = n
1124 if fl:
1126 if fl:
1125 self.setflag(f, fl)
1127 self.setflag(f, fl)
1126 else:
1128 else:
1127 # Assigning to _files and _flags avoids marking as dirty,
1129 # Assigning to _files and _flags avoids marking as dirty,
1128 # and should be a little faster.
1130 # and should be a little faster.
1129 self._files[f] = n
1131 self._files[f] = n
1130 if fl:
1132 if fl:
1131 self._flags[f] = fl
1133 self._flags[f] = fl
1132
1134
1133 def text(self, usemanifestv2=False):
1135 def text(self, usemanifestv2=False):
1134 """Get the full data of this manifest as a bytestring."""
1136 """Get the full data of this manifest as a bytestring."""
1135 self._load()
1137 self._load()
1136 return _text(self.iterentries(), usemanifestv2)
1138 return _text(self.iterentries(), usemanifestv2)
1137
1139
1138 def dirtext(self, usemanifestv2=False):
1140 def dirtext(self, usemanifestv2=False):
1139 """Get the full data of this directory as a bytestring. Make sure that
1141 """Get the full data of this directory as a bytestring. Make sure that
1140 any submanifests have been written first, so their nodeids are correct.
1142 any submanifests have been written first, so their nodeids are correct.
1141 """
1143 """
1142 self._load()
1144 self._load()
1143 flags = self.flags
1145 flags = self.flags
1144 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1146 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1145 files = [(f, self._files[f], flags(f)) for f in self._files]
1147 files = [(f, self._files[f], flags(f)) for f in self._files]
1146 return _text(sorted(dirs + files), usemanifestv2)
1148 return _text(sorted(dirs + files), usemanifestv2)
1147
1149
1148 def read(self, gettext, readsubtree):
1150 def read(self, gettext, readsubtree):
1149 def _load_for_read(s):
1151 def _load_for_read(s):
1150 s.parse(gettext(), readsubtree)
1152 s.parse(gettext(), readsubtree)
1151 s._dirty = False
1153 s._dirty = False
1152 self._loadfunc = _load_for_read
1154 self._loadfunc = _load_for_read
1153
1155
1154 def writesubtrees(self, m1, m2, writesubtree):
1156 def writesubtrees(self, m1, m2, writesubtree):
1155 self._load() # for consistency; should never have any effect here
1157 self._load() # for consistency; should never have any effect here
1156 m1._load()
1158 m1._load()
1157 m2._load()
1159 m2._load()
1158 emptytree = treemanifest()
1160 emptytree = treemanifest()
1159 for d, subm in self._dirs.iteritems():
1161 for d, subm in self._dirs.iteritems():
1160 subp1 = m1._dirs.get(d, emptytree)._node
1162 subp1 = m1._dirs.get(d, emptytree)._node
1161 subp2 = m2._dirs.get(d, emptytree)._node
1163 subp2 = m2._dirs.get(d, emptytree)._node
1162 if subp1 == revlog.nullid:
1164 if subp1 == revlog.nullid:
1163 subp1, subp2 = subp2, subp1
1165 subp1, subp2 = subp2, subp1
1164 writesubtree(subm, subp1, subp2)
1166 writesubtree(subm, subp1, subp2)
1165
1167
1166 def walksubtrees(self, matcher=None):
1168 def walksubtrees(self, matcher=None):
1167 """Returns an iterator of the subtrees of this manifest, including this
1169 """Returns an iterator of the subtrees of this manifest, including this
1168 manifest itself.
1170 manifest itself.
1169
1171
1170 If `matcher` is provided, it only returns subtrees that match.
1172 If `matcher` is provided, it only returns subtrees that match.
1171 """
1173 """
1172 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1174 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1173 return
1175 return
1174 if not matcher or matcher(self._dir[:-1]):
1176 if not matcher or matcher(self._dir[:-1]):
1175 yield self
1177 yield self
1176
1178
1177 self._load()
1179 self._load()
1178 for d, subm in self._dirs.iteritems():
1180 for d, subm in self._dirs.iteritems():
1179 for subtree in subm.walksubtrees(matcher=matcher):
1181 for subtree in subm.walksubtrees(matcher=matcher):
1180 yield subtree
1182 yield subtree
1181
1183
1182 class manifestrevlog(revlog.revlog):
1184 class manifestrevlog(revlog.revlog):
1183 '''A revlog that stores manifest texts. This is responsible for caching the
1185 '''A revlog that stores manifest texts. This is responsible for caching the
1184 full-text manifest contents.
1186 full-text manifest contents.
1185 '''
1187 '''
1186 def __init__(self, opener, dir='', dirlogcache=None, indexfile=None,
1188 def __init__(self, opener, dir='', dirlogcache=None, indexfile=None,
1187 treemanifest=False):
1189 treemanifest=False):
1188 """Constructs a new manifest revlog
1190 """Constructs a new manifest revlog
1189
1191
1190 `indexfile` - used by extensions to have two manifests at once, like
1192 `indexfile` - used by extensions to have two manifests at once, like
1191 when transitioning between flatmanifeset and treemanifests.
1193 when transitioning between flatmanifeset and treemanifests.
1192
1194
1193 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1195 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1194 options can also be used to make this a tree manifest revlog. The opener
1196 options can also be used to make this a tree manifest revlog. The opener
1195 option takes precedence, so if it is set to True, we ignore whatever
1197 option takes precedence, so if it is set to True, we ignore whatever
1196 value is passed in to the constructor.
1198 value is passed in to the constructor.
1197 """
1199 """
1198 # During normal operations, we expect to deal with not more than four
1200 # During normal operations, we expect to deal with not more than four
1199 # revs at a time (such as during commit --amend). When rebasing large
1201 # revs at a time (such as during commit --amend). When rebasing large
1200 # stacks of commits, the number can go up, hence the config knob below.
1202 # stacks of commits, the number can go up, hence the config knob below.
1201 cachesize = 4
1203 cachesize = 4
1202 optiontreemanifest = False
1204 optiontreemanifest = False
1203 usemanifestv2 = False
1205 usemanifestv2 = False
1204 opts = getattr(opener, 'options', None)
1206 opts = getattr(opener, 'options', None)
1205 if opts is not None:
1207 if opts is not None:
1206 cachesize = opts.get('manifestcachesize', cachesize)
1208 cachesize = opts.get('manifestcachesize', cachesize)
1207 optiontreemanifest = opts.get('treemanifest', False)
1209 optiontreemanifest = opts.get('treemanifest', False)
1208 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
1210 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
1209
1211
1210 self._treeondisk = optiontreemanifest or treemanifest
1212 self._treeondisk = optiontreemanifest or treemanifest
1211 self._usemanifestv2 = usemanifestv2
1213 self._usemanifestv2 = usemanifestv2
1212
1214
1213 self._fulltextcache = util.lrucachedict(cachesize)
1215 self._fulltextcache = util.lrucachedict(cachesize)
1214
1216
1215 if dir:
1217 if dir:
1216 assert self._treeondisk, 'opts is %r' % opts
1218 assert self._treeondisk, 'opts is %r' % opts
1217 if not dir.endswith('/'):
1219 if not dir.endswith('/'):
1218 dir = dir + '/'
1220 dir = dir + '/'
1219
1221
1220 if indexfile is None:
1222 if indexfile is None:
1221 indexfile = '00manifest.i'
1223 indexfile = '00manifest.i'
1222 if dir:
1224 if dir:
1223 indexfile = "meta/" + dir + indexfile
1225 indexfile = "meta/" + dir + indexfile
1224
1226
1225 self._dir = dir
1227 self._dir = dir
1226 # The dirlogcache is kept on the root manifest log
1228 # The dirlogcache is kept on the root manifest log
1227 if dir:
1229 if dir:
1228 self._dirlogcache = dirlogcache
1230 self._dirlogcache = dirlogcache
1229 else:
1231 else:
1230 self._dirlogcache = {'': self}
1232 self._dirlogcache = {'': self}
1231
1233
1232 super(manifestrevlog, self).__init__(opener, indexfile,
1234 super(manifestrevlog, self).__init__(opener, indexfile,
1233 # only root indexfile is cached
1235 # only root indexfile is cached
1234 checkambig=not bool(dir),
1236 checkambig=not bool(dir),
1235 mmaplargeindex=True)
1237 mmaplargeindex=True)
1236
1238
1237 @property
1239 @property
1238 def fulltextcache(self):
1240 def fulltextcache(self):
1239 return self._fulltextcache
1241 return self._fulltextcache
1240
1242
1241 def clearcaches(self):
1243 def clearcaches(self):
1242 super(manifestrevlog, self).clearcaches()
1244 super(manifestrevlog, self).clearcaches()
1243 self._fulltextcache.clear()
1245 self._fulltextcache.clear()
1244 self._dirlogcache = {'': self}
1246 self._dirlogcache = {'': self}
1245
1247
1246 def dirlog(self, dir):
1248 def dirlog(self, dir):
1247 if dir:
1249 if dir:
1248 assert self._treeondisk
1250 assert self._treeondisk
1249 if dir not in self._dirlogcache:
1251 if dir not in self._dirlogcache:
1250 mfrevlog = manifestrevlog(self.opener, dir,
1252 mfrevlog = manifestrevlog(self.opener, dir,
1251 self._dirlogcache,
1253 self._dirlogcache,
1252 treemanifest=self._treeondisk)
1254 treemanifest=self._treeondisk)
1253 self._dirlogcache[dir] = mfrevlog
1255 self._dirlogcache[dir] = mfrevlog
1254 return self._dirlogcache[dir]
1256 return self._dirlogcache[dir]
1255
1257
1256 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1258 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1257 if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta')
1259 if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta')
1258 and not self._usemanifestv2):
1260 and not self._usemanifestv2):
1259 # If our first parent is in the manifest cache, we can
1261 # If our first parent is in the manifest cache, we can
1260 # compute a delta here using properties we know about the
1262 # compute a delta here using properties we know about the
1261 # manifest up-front, which may save time later for the
1263 # manifest up-front, which may save time later for the
1262 # revlog layer.
1264 # revlog layer.
1263
1265
1264 _checkforbidden(added)
1266 _checkforbidden(added)
1265 # combine the changed lists into one sorted iterator
1267 # combine the changed lists into one sorted iterator
1266 work = heapq.merge([(x, False) for x in added],
1268 work = heapq.merge([(x, False) for x in added],
1267 [(x, True) for x in removed])
1269 [(x, True) for x in removed])
1268
1270
1269 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1271 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1270 cachedelta = self.rev(p1), deltatext
1272 cachedelta = self.rev(p1), deltatext
1271 text = util.buffer(arraytext)
1273 text = util.buffer(arraytext)
1272 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1274 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1273 else:
1275 else:
1274 # The first parent manifest isn't already loaded, so we'll
1276 # The first parent manifest isn't already loaded, so we'll
1275 # just encode a fulltext of the manifest and pass that
1277 # just encode a fulltext of the manifest and pass that
1276 # through to the revlog layer, and let it handle the delta
1278 # through to the revlog layer, and let it handle the delta
1277 # process.
1279 # process.
1278 if self._treeondisk:
1280 if self._treeondisk:
1279 assert readtree, "readtree must be set for treemanifest writes"
1281 assert readtree, "readtree must be set for treemanifest writes"
1280 m1 = readtree(self._dir, p1)
1282 m1 = readtree(self._dir, p1)
1281 m2 = readtree(self._dir, p2)
1283 m2 = readtree(self._dir, p2)
1282 n = self._addtree(m, transaction, link, m1, m2, readtree)
1284 n = self._addtree(m, transaction, link, m1, m2, readtree)
1283 arraytext = None
1285 arraytext = None
1284 else:
1286 else:
1285 text = m.text(self._usemanifestv2)
1287 text = m.text(self._usemanifestv2)
1286 n = self.addrevision(text, transaction, link, p1, p2)
1288 n = self.addrevision(text, transaction, link, p1, p2)
1287 arraytext = bytearray(text)
1289 arraytext = bytearray(text)
1288
1290
1289 if arraytext is not None:
1291 if arraytext is not None:
1290 self.fulltextcache[n] = arraytext
1292 self.fulltextcache[n] = arraytext
1291
1293
1292 return n
1294 return n
1293
1295
1294 def _addtree(self, m, transaction, link, m1, m2, readtree):
1296 def _addtree(self, m, transaction, link, m1, m2, readtree):
1295 # If the manifest is unchanged compared to one parent,
1297 # If the manifest is unchanged compared to one parent,
1296 # don't write a new revision
1298 # don't write a new revision
1297 if self._dir != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(m2)):
1299 if self._dir != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(m2)):
1298 return m.node()
1300 return m.node()
1299 def writesubtree(subm, subp1, subp2):
1301 def writesubtree(subm, subp1, subp2):
1300 sublog = self.dirlog(subm.dir())
1302 sublog = self.dirlog(subm.dir())
1301 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1303 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1302 readtree=readtree)
1304 readtree=readtree)
1303 m.writesubtrees(m1, m2, writesubtree)
1305 m.writesubtrees(m1, m2, writesubtree)
1304 text = m.dirtext(self._usemanifestv2)
1306 text = m.dirtext(self._usemanifestv2)
1305 n = None
1307 n = None
1306 if self._dir != '':
1308 if self._dir != '':
1307 # Double-check whether contents are unchanged to one parent
1309 # Double-check whether contents are unchanged to one parent
1308 if text == m1.dirtext(self._usemanifestv2):
1310 if text == m1.dirtext(self._usemanifestv2):
1309 n = m1.node()
1311 n = m1.node()
1310 elif text == m2.dirtext(self._usemanifestv2):
1312 elif text == m2.dirtext(self._usemanifestv2):
1311 n = m2.node()
1313 n = m2.node()
1312
1314
1313 if not n:
1315 if not n:
1314 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1316 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1315
1317
1316 # Save nodeid so parent manifest can calculate its nodeid
1318 # Save nodeid so parent manifest can calculate its nodeid
1317 m.setnode(n)
1319 m.setnode(n)
1318 return n
1320 return n
1319
1321
1320 class manifestlog(object):
1322 class manifestlog(object):
1321 """A collection class representing the collection of manifest snapshots
1323 """A collection class representing the collection of manifest snapshots
1322 referenced by commits in the repository.
1324 referenced by commits in the repository.
1323
1325
1324 In this situation, 'manifest' refers to the abstract concept of a snapshot
1326 In this situation, 'manifest' refers to the abstract concept of a snapshot
1325 of the list of files in the given commit. Consumers of the output of this
1327 of the list of files in the given commit. Consumers of the output of this
1326 class do not care about the implementation details of the actual manifests
1328 class do not care about the implementation details of the actual manifests
1327 they receive (i.e. tree or flat or lazily loaded, etc)."""
1329 they receive (i.e. tree or flat or lazily loaded, etc)."""
1328 def __init__(self, opener, repo):
1330 def __init__(self, opener, repo):
1329 usetreemanifest = False
1331 usetreemanifest = False
1330 cachesize = 4
1332 cachesize = 4
1331
1333
1332 opts = getattr(opener, 'options', None)
1334 opts = getattr(opener, 'options', None)
1333 if opts is not None:
1335 if opts is not None:
1334 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1336 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1335 cachesize = opts.get('manifestcachesize', cachesize)
1337 cachesize = opts.get('manifestcachesize', cachesize)
1336 self._treeinmem = usetreemanifest
1338 self._treeinmem = usetreemanifest
1337
1339
1338 self._revlog = repo._constructmanifest()
1340 self._revlog = repo._constructmanifest()
1339
1341
1340 # A cache of the manifestctx or treemanifestctx for each directory
1342 # A cache of the manifestctx or treemanifestctx for each directory
1341 self._dirmancache = {}
1343 self._dirmancache = {}
1342 self._dirmancache[''] = util.lrucachedict(cachesize)
1344 self._dirmancache[''] = util.lrucachedict(cachesize)
1343
1345
1344 self.cachesize = cachesize
1346 self.cachesize = cachesize
1345
1347
1346 def __getitem__(self, node):
1348 def __getitem__(self, node):
1347 """Retrieves the manifest instance for the given node. Throws a
1349 """Retrieves the manifest instance for the given node. Throws a
1348 LookupError if not found.
1350 LookupError if not found.
1349 """
1351 """
1350 return self.get('', node)
1352 return self.get('', node)
1351
1353
1352 def get(self, dir, node, verify=True):
1354 def get(self, dir, node, verify=True):
1353 """Retrieves the manifest instance for the given node. Throws a
1355 """Retrieves the manifest instance for the given node. Throws a
1354 LookupError if not found.
1356 LookupError if not found.
1355
1357
1356 `verify` - if True an exception will be thrown if the node is not in
1358 `verify` - if True an exception will be thrown if the node is not in
1357 the revlog
1359 the revlog
1358 """
1360 """
1359 if node in self._dirmancache.get(dir, ()):
1361 if node in self._dirmancache.get(dir, ()):
1360 return self._dirmancache[dir][node]
1362 return self._dirmancache[dir][node]
1361
1363
1362 if dir:
1364 if dir:
1363 if self._revlog._treeondisk:
1365 if self._revlog._treeondisk:
1364 if verify:
1366 if verify:
1365 dirlog = self._revlog.dirlog(dir)
1367 dirlog = self._revlog.dirlog(dir)
1366 if node not in dirlog.nodemap:
1368 if node not in dirlog.nodemap:
1367 raise LookupError(node, dirlog.indexfile,
1369 raise LookupError(node, dirlog.indexfile,
1368 _('no node'))
1370 _('no node'))
1369 m = treemanifestctx(self, dir, node)
1371 m = treemanifestctx(self, dir, node)
1370 else:
1372 else:
1371 raise error.Abort(
1373 raise error.Abort(
1372 _("cannot ask for manifest directory '%s' in a flat "
1374 _("cannot ask for manifest directory '%s' in a flat "
1373 "manifest") % dir)
1375 "manifest") % dir)
1374 else:
1376 else:
1375 if verify:
1377 if verify:
1376 if node not in self._revlog.nodemap:
1378 if node not in self._revlog.nodemap:
1377 raise LookupError(node, self._revlog.indexfile,
1379 raise LookupError(node, self._revlog.indexfile,
1378 _('no node'))
1380 _('no node'))
1379 if self._treeinmem:
1381 if self._treeinmem:
1380 m = treemanifestctx(self, '', node)
1382 m = treemanifestctx(self, '', node)
1381 else:
1383 else:
1382 m = manifestctx(self, node)
1384 m = manifestctx(self, node)
1383
1385
1384 if node != revlog.nullid:
1386 if node != revlog.nullid:
1385 mancache = self._dirmancache.get(dir)
1387 mancache = self._dirmancache.get(dir)
1386 if not mancache:
1388 if not mancache:
1387 mancache = util.lrucachedict(self.cachesize)
1389 mancache = util.lrucachedict(self.cachesize)
1388 self._dirmancache[dir] = mancache
1390 self._dirmancache[dir] = mancache
1389 mancache[node] = m
1391 mancache[node] = m
1390 return m
1392 return m
1391
1393
1392 def clearcaches(self):
1394 def clearcaches(self):
1393 self._dirmancache.clear()
1395 self._dirmancache.clear()
1394 self._revlog.clearcaches()
1396 self._revlog.clearcaches()
1395
1397
1396 class memmanifestctx(object):
1398 class memmanifestctx(object):
1397 def __init__(self, manifestlog):
1399 def __init__(self, manifestlog):
1398 self._manifestlog = manifestlog
1400 self._manifestlog = manifestlog
1399 self._manifestdict = manifestdict()
1401 self._manifestdict = manifestdict()
1400
1402
1401 def _revlog(self):
1403 def _revlog(self):
1402 return self._manifestlog._revlog
1404 return self._manifestlog._revlog
1403
1405
1404 def new(self):
1406 def new(self):
1405 return memmanifestctx(self._manifestlog)
1407 return memmanifestctx(self._manifestlog)
1406
1408
1407 def copy(self):
1409 def copy(self):
1408 memmf = memmanifestctx(self._manifestlog)
1410 memmf = memmanifestctx(self._manifestlog)
1409 memmf._manifestdict = self.read().copy()
1411 memmf._manifestdict = self.read().copy()
1410 return memmf
1412 return memmf
1411
1413
1412 def read(self):
1414 def read(self):
1413 return self._manifestdict
1415 return self._manifestdict
1414
1416
1415 def write(self, transaction, link, p1, p2, added, removed):
1417 def write(self, transaction, link, p1, p2, added, removed):
1416 return self._revlog().add(self._manifestdict, transaction, link, p1, p2,
1418 return self._revlog().add(self._manifestdict, transaction, link, p1, p2,
1417 added, removed)
1419 added, removed)
1418
1420
1419 class manifestctx(object):
1421 class manifestctx(object):
1420 """A class representing a single revision of a manifest, including its
1422 """A class representing a single revision of a manifest, including its
1421 contents, its parent revs, and its linkrev.
1423 contents, its parent revs, and its linkrev.
1422 """
1424 """
1423 def __init__(self, manifestlog, node):
1425 def __init__(self, manifestlog, node):
1424 self._manifestlog = manifestlog
1426 self._manifestlog = manifestlog
1425 self._data = None
1427 self._data = None
1426
1428
1427 self._node = node
1429 self._node = node
1428
1430
1429 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1431 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1430 # but let's add it later when something needs it and we can load it
1432 # but let's add it later when something needs it and we can load it
1431 # lazily.
1433 # lazily.
1432 #self.p1, self.p2 = revlog.parents(node)
1434 #self.p1, self.p2 = revlog.parents(node)
1433 #rev = revlog.rev(node)
1435 #rev = revlog.rev(node)
1434 #self.linkrev = revlog.linkrev(rev)
1436 #self.linkrev = revlog.linkrev(rev)
1435
1437
1436 def _revlog(self):
1438 def _revlog(self):
1437 return self._manifestlog._revlog
1439 return self._manifestlog._revlog
1438
1440
1439 def node(self):
1441 def node(self):
1440 return self._node
1442 return self._node
1441
1443
1442 def new(self):
1444 def new(self):
1443 return memmanifestctx(self._manifestlog)
1445 return memmanifestctx(self._manifestlog)
1444
1446
1445 def copy(self):
1447 def copy(self):
1446 memmf = memmanifestctx(self._manifestlog)
1448 memmf = memmanifestctx(self._manifestlog)
1447 memmf._manifestdict = self.read().copy()
1449 memmf._manifestdict = self.read().copy()
1448 return memmf
1450 return memmf
1449
1451
1450 @propertycache
1452 @propertycache
1451 def parents(self):
1453 def parents(self):
1452 return self._revlog().parents(self._node)
1454 return self._revlog().parents(self._node)
1453
1455
1454 def read(self):
1456 def read(self):
1455 if self._data is None:
1457 if self._data is None:
1456 if self._node == revlog.nullid:
1458 if self._node == revlog.nullid:
1457 self._data = manifestdict()
1459 self._data = manifestdict()
1458 else:
1460 else:
1459 rl = self._revlog()
1461 rl = self._revlog()
1460 text = rl.revision(self._node)
1462 text = rl.revision(self._node)
1461 arraytext = bytearray(text)
1463 arraytext = bytearray(text)
1462 rl._fulltextcache[self._node] = arraytext
1464 rl._fulltextcache[self._node] = arraytext
1463 self._data = manifestdict(text)
1465 self._data = manifestdict(text)
1464 return self._data
1466 return self._data
1465
1467
1466 def readfast(self, shallow=False):
1468 def readfast(self, shallow=False):
1467 '''Calls either readdelta or read, based on which would be less work.
1469 '''Calls either readdelta or read, based on which would be less work.
1468 readdelta is called if the delta is against the p1, and therefore can be
1470 readdelta is called if the delta is against the p1, and therefore can be
1469 read quickly.
1471 read quickly.
1470
1472
1471 If `shallow` is True, nothing changes since this is a flat manifest.
1473 If `shallow` is True, nothing changes since this is a flat manifest.
1472 '''
1474 '''
1473 rl = self._revlog()
1475 rl = self._revlog()
1474 r = rl.rev(self._node)
1476 r = rl.rev(self._node)
1475 deltaparent = rl.deltaparent(r)
1477 deltaparent = rl.deltaparent(r)
1476 if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r):
1478 if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r):
1477 return self.readdelta()
1479 return self.readdelta()
1478 return self.read()
1480 return self.read()
1479
1481
1480 def readdelta(self, shallow=False):
1482 def readdelta(self, shallow=False):
1481 '''Returns a manifest containing just the entries that are present
1483 '''Returns a manifest containing just the entries that are present
1482 in this manifest, but not in its p1 manifest. This is efficient to read
1484 in this manifest, but not in its p1 manifest. This is efficient to read
1483 if the revlog delta is already p1.
1485 if the revlog delta is already p1.
1484
1486
1485 Changing the value of `shallow` has no effect on flat manifests.
1487 Changing the value of `shallow` has no effect on flat manifests.
1486 '''
1488 '''
1487 revlog = self._revlog()
1489 revlog = self._revlog()
1488 if revlog._usemanifestv2:
1490 if revlog._usemanifestv2:
1489 # Need to perform a slow delta
1491 # Need to perform a slow delta
1490 r0 = revlog.deltaparent(revlog.rev(self._node))
1492 r0 = revlog.deltaparent(revlog.rev(self._node))
1491 m0 = self._manifestlog[revlog.node(r0)].read()
1493 m0 = self._manifestlog[revlog.node(r0)].read()
1492 m1 = self.read()
1494 m1 = self.read()
1493 md = manifestdict()
1495 md = manifestdict()
1494 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1496 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1495 if n1:
1497 if n1:
1496 md[f] = n1
1498 md[f] = n1
1497 if fl1:
1499 if fl1:
1498 md.setflag(f, fl1)
1500 md.setflag(f, fl1)
1499 return md
1501 return md
1500
1502
1501 r = revlog.rev(self._node)
1503 r = revlog.rev(self._node)
1502 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1504 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1503 return manifestdict(d)
1505 return manifestdict(d)
1504
1506
1505 def find(self, key):
1507 def find(self, key):
1506 return self.read().find(key)
1508 return self.read().find(key)
1507
1509
1508 class memtreemanifestctx(object):
1510 class memtreemanifestctx(object):
1509 def __init__(self, manifestlog, dir=''):
1511 def __init__(self, manifestlog, dir=''):
1510 self._manifestlog = manifestlog
1512 self._manifestlog = manifestlog
1511 self._dir = dir
1513 self._dir = dir
1512 self._treemanifest = treemanifest()
1514 self._treemanifest = treemanifest()
1513
1515
1514 def _revlog(self):
1516 def _revlog(self):
1515 return self._manifestlog._revlog
1517 return self._manifestlog._revlog
1516
1518
1517 def new(self, dir=''):
1519 def new(self, dir=''):
1518 return memtreemanifestctx(self._manifestlog, dir=dir)
1520 return memtreemanifestctx(self._manifestlog, dir=dir)
1519
1521
1520 def copy(self):
1522 def copy(self):
1521 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1523 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1522 memmf._treemanifest = self._treemanifest.copy()
1524 memmf._treemanifest = self._treemanifest.copy()
1523 return memmf
1525 return memmf
1524
1526
1525 def read(self):
1527 def read(self):
1526 return self._treemanifest
1528 return self._treemanifest
1527
1529
1528 def write(self, transaction, link, p1, p2, added, removed):
1530 def write(self, transaction, link, p1, p2, added, removed):
1529 def readtree(dir, node):
1531 def readtree(dir, node):
1530 return self._manifestlog.get(dir, node).read()
1532 return self._manifestlog.get(dir, node).read()
1531 return self._revlog().add(self._treemanifest, transaction, link, p1, p2,
1533 return self._revlog().add(self._treemanifest, transaction, link, p1, p2,
1532 added, removed, readtree=readtree)
1534 added, removed, readtree=readtree)
1533
1535
1534 class treemanifestctx(object):
1536 class treemanifestctx(object):
1535 def __init__(self, manifestlog, dir, node):
1537 def __init__(self, manifestlog, dir, node):
1536 self._manifestlog = manifestlog
1538 self._manifestlog = manifestlog
1537 self._dir = dir
1539 self._dir = dir
1538 self._data = None
1540 self._data = None
1539
1541
1540 self._node = node
1542 self._node = node
1541
1543
1542 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1544 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1543 # we can instantiate treemanifestctx objects for directories we don't
1545 # we can instantiate treemanifestctx objects for directories we don't
1544 # have on disk.
1546 # have on disk.
1545 #self.p1, self.p2 = revlog.parents(node)
1547 #self.p1, self.p2 = revlog.parents(node)
1546 #rev = revlog.rev(node)
1548 #rev = revlog.rev(node)
1547 #self.linkrev = revlog.linkrev(rev)
1549 #self.linkrev = revlog.linkrev(rev)
1548
1550
1549 def _revlog(self):
1551 def _revlog(self):
1550 return self._manifestlog._revlog.dirlog(self._dir)
1552 return self._manifestlog._revlog.dirlog(self._dir)
1551
1553
1552 def read(self):
1554 def read(self):
1553 if self._data is None:
1555 if self._data is None:
1554 rl = self._revlog()
1556 rl = self._revlog()
1555 if self._node == revlog.nullid:
1557 if self._node == revlog.nullid:
1556 self._data = treemanifest()
1558 self._data = treemanifest()
1557 elif rl._treeondisk:
1559 elif rl._treeondisk:
1558 m = treemanifest(dir=self._dir)
1560 m = treemanifest(dir=self._dir)
1559 def gettext():
1561 def gettext():
1560 return rl.revision(self._node)
1562 return rl.revision(self._node)
1561 def readsubtree(dir, subm):
1563 def readsubtree(dir, subm):
1562 # Set verify to False since we need to be able to create
1564 # Set verify to False since we need to be able to create
1563 # subtrees for trees that don't exist on disk.
1565 # subtrees for trees that don't exist on disk.
1564 return self._manifestlog.get(dir, subm, verify=False).read()
1566 return self._manifestlog.get(dir, subm, verify=False).read()
1565 m.read(gettext, readsubtree)
1567 m.read(gettext, readsubtree)
1566 m.setnode(self._node)
1568 m.setnode(self._node)
1567 self._data = m
1569 self._data = m
1568 else:
1570 else:
1569 text = rl.revision(self._node)
1571 text = rl.revision(self._node)
1570 arraytext = bytearray(text)
1572 arraytext = bytearray(text)
1571 rl.fulltextcache[self._node] = arraytext
1573 rl.fulltextcache[self._node] = arraytext
1572 self._data = treemanifest(dir=self._dir, text=text)
1574 self._data = treemanifest(dir=self._dir, text=text)
1573
1575
1574 return self._data
1576 return self._data
1575
1577
1576 def node(self):
1578 def node(self):
1577 return self._node
1579 return self._node
1578
1580
1579 def new(self, dir=''):
1581 def new(self, dir=''):
1580 return memtreemanifestctx(self._manifestlog, dir=dir)
1582 return memtreemanifestctx(self._manifestlog, dir=dir)
1581
1583
1582 def copy(self):
1584 def copy(self):
1583 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1585 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1584 memmf._treemanifest = self.read().copy()
1586 memmf._treemanifest = self.read().copy()
1585 return memmf
1587 return memmf
1586
1588
1587 @propertycache
1589 @propertycache
1588 def parents(self):
1590 def parents(self):
1589 return self._revlog().parents(self._node)
1591 return self._revlog().parents(self._node)
1590
1592
1591 def readdelta(self, shallow=False):
1593 def readdelta(self, shallow=False):
1592 '''Returns a manifest containing just the entries that are present
1594 '''Returns a manifest containing just the entries that are present
1593 in this manifest, but not in its p1 manifest. This is efficient to read
1595 in this manifest, but not in its p1 manifest. This is efficient to read
1594 if the revlog delta is already p1.
1596 if the revlog delta is already p1.
1595
1597
1596 If `shallow` is True, this will read the delta for this directory,
1598 If `shallow` is True, this will read the delta for this directory,
1597 without recursively reading subdirectory manifests. Instead, any
1599 without recursively reading subdirectory manifests. Instead, any
1598 subdirectory entry will be reported as it appears in the manifest, i.e.
1600 subdirectory entry will be reported as it appears in the manifest, i.e.
1599 the subdirectory will be reported among files and distinguished only by
1601 the subdirectory will be reported among files and distinguished only by
1600 its 't' flag.
1602 its 't' flag.
1601 '''
1603 '''
1602 revlog = self._revlog()
1604 revlog = self._revlog()
1603 if shallow and not revlog._usemanifestv2:
1605 if shallow and not revlog._usemanifestv2:
1604 r = revlog.rev(self._node)
1606 r = revlog.rev(self._node)
1605 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1607 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1606 return manifestdict(d)
1608 return manifestdict(d)
1607 else:
1609 else:
1608 # Need to perform a slow delta
1610 # Need to perform a slow delta
1609 r0 = revlog.deltaparent(revlog.rev(self._node))
1611 r0 = revlog.deltaparent(revlog.rev(self._node))
1610 m0 = self._manifestlog.get(self._dir, revlog.node(r0)).read()
1612 m0 = self._manifestlog.get(self._dir, revlog.node(r0)).read()
1611 m1 = self.read()
1613 m1 = self.read()
1612 md = treemanifest(dir=self._dir)
1614 md = treemanifest(dir=self._dir)
1613 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1615 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1614 if n1:
1616 if n1:
1615 md[f] = n1
1617 md[f] = n1
1616 if fl1:
1618 if fl1:
1617 md.setflag(f, fl1)
1619 md.setflag(f, fl1)
1618 return md
1620 return md
1619
1621
1620 def readfast(self, shallow=False):
1622 def readfast(self, shallow=False):
1621 '''Calls either readdelta or read, based on which would be less work.
1623 '''Calls either readdelta or read, based on which would be less work.
1622 readdelta is called if the delta is against the p1, and therefore can be
1624 readdelta is called if the delta is against the p1, and therefore can be
1623 read quickly.
1625 read quickly.
1624
1626
1625 If `shallow` is True, it only returns the entries from this manifest,
1627 If `shallow` is True, it only returns the entries from this manifest,
1626 and not any submanifests.
1628 and not any submanifests.
1627 '''
1629 '''
1628 rl = self._revlog()
1630 rl = self._revlog()
1629 r = rl.rev(self._node)
1631 r = rl.rev(self._node)
1630 deltaparent = rl.deltaparent(r)
1632 deltaparent = rl.deltaparent(r)
1631 if (deltaparent != revlog.nullrev and
1633 if (deltaparent != revlog.nullrev and
1632 deltaparent in rl.parentrevs(r)):
1634 deltaparent in rl.parentrevs(r)):
1633 return self.readdelta(shallow=shallow)
1635 return self.readdelta(shallow=shallow)
1634
1636
1635 if shallow:
1637 if shallow:
1636 return manifestdict(rl.revision(self._node))
1638 return manifestdict(rl.revision(self._node))
1637 else:
1639 else:
1638 return self.read()
1640 return self.read()
1639
1641
1640 def find(self, key):
1642 def find(self, key):
1641 return self.read().find(key)
1643 return self.read().find(key)
General Comments 0
You need to be logged in to leave comments. Login now