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