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