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