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