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