##// END OF EJS Templates
treemanifests: make _loadlazy tolerate item not on _lazydirs...
spectral -
r40017:da0319e0 default
parent child Browse files
Show More
@@ -1,2033 +1,2035
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import itertools
11 import itertools
12 import struct
12 import struct
13 import weakref
13 import weakref
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from . import (
22 from . import (
23 error,
23 error,
24 mdiff,
24 mdiff,
25 policy,
25 policy,
26 pycompat,
26 pycompat,
27 repository,
27 repository,
28 revlog,
28 revlog,
29 util,
29 util,
30 )
30 )
31 from .utils import (
31 from .utils import (
32 interfaceutil,
32 interfaceutil,
33 )
33 )
34
34
35 parsers = policy.importmod(r'parsers')
35 parsers = policy.importmod(r'parsers')
36 propertycache = util.propertycache
36 propertycache = util.propertycache
37
37
38 def _parse(data):
38 def _parse(data):
39 # This method does a little bit of excessive-looking
39 # This method does a little bit of excessive-looking
40 # precondition checking. This is so that the behavior of this
40 # precondition checking. This is so that the behavior of this
41 # class exactly matches its C counterpart to try and help
41 # class exactly matches its C counterpart to try and help
42 # prevent surprise breakage for anyone that develops against
42 # prevent surprise breakage for anyone that develops against
43 # the pure version.
43 # the pure version.
44 if data and data[-1:] != '\n':
44 if data and data[-1:] != '\n':
45 raise ValueError('Manifest did not end in a newline.')
45 raise ValueError('Manifest did not end in a newline.')
46 prev = None
46 prev = None
47 for l in data.splitlines():
47 for l in data.splitlines():
48 if prev is not None and prev > l:
48 if prev is not None and prev > l:
49 raise ValueError('Manifest lines not in sorted order.')
49 raise ValueError('Manifest lines not in sorted order.')
50 prev = l
50 prev = l
51 f, n = l.split('\0')
51 f, n = l.split('\0')
52 if len(n) > 40:
52 if len(n) > 40:
53 yield f, bin(n[:40]), n[40:]
53 yield f, bin(n[:40]), n[40:]
54 else:
54 else:
55 yield f, bin(n), ''
55 yield f, bin(n), ''
56
56
57 def _text(it):
57 def _text(it):
58 files = []
58 files = []
59 lines = []
59 lines = []
60 for f, n, fl in it:
60 for f, n, fl in it:
61 files.append(f)
61 files.append(f)
62 # if this is changed to support newlines in filenames,
62 # if this is changed to support newlines in filenames,
63 # be sure to check the templates/ dir again (especially *-raw.tmpl)
63 # be sure to check the templates/ dir again (especially *-raw.tmpl)
64 lines.append("%s\0%s%s\n" % (f, hex(n), fl))
64 lines.append("%s\0%s%s\n" % (f, hex(n), fl))
65
65
66 _checkforbidden(files)
66 _checkforbidden(files)
67 return ''.join(lines)
67 return ''.join(lines)
68
68
69 class lazymanifestiter(object):
69 class lazymanifestiter(object):
70 def __init__(self, lm):
70 def __init__(self, lm):
71 self.pos = 0
71 self.pos = 0
72 self.lm = lm
72 self.lm = lm
73
73
74 def __iter__(self):
74 def __iter__(self):
75 return self
75 return self
76
76
77 def next(self):
77 def next(self):
78 try:
78 try:
79 data, pos = self.lm._get(self.pos)
79 data, pos = self.lm._get(self.pos)
80 except IndexError:
80 except IndexError:
81 raise StopIteration
81 raise StopIteration
82 if pos == -1:
82 if pos == -1:
83 self.pos += 1
83 self.pos += 1
84 return data[0]
84 return data[0]
85 self.pos += 1
85 self.pos += 1
86 zeropos = data.find('\x00', pos)
86 zeropos = data.find('\x00', pos)
87 return data[pos:zeropos]
87 return data[pos:zeropos]
88
88
89 __next__ = next
89 __next__ = next
90
90
91 class lazymanifestiterentries(object):
91 class lazymanifestiterentries(object):
92 def __init__(self, lm):
92 def __init__(self, lm):
93 self.lm = lm
93 self.lm = lm
94 self.pos = 0
94 self.pos = 0
95
95
96 def __iter__(self):
96 def __iter__(self):
97 return self
97 return self
98
98
99 def next(self):
99 def next(self):
100 try:
100 try:
101 data, pos = self.lm._get(self.pos)
101 data, pos = self.lm._get(self.pos)
102 except IndexError:
102 except IndexError:
103 raise StopIteration
103 raise StopIteration
104 if pos == -1:
104 if pos == -1:
105 self.pos += 1
105 self.pos += 1
106 return data
106 return data
107 zeropos = data.find('\x00', pos)
107 zeropos = data.find('\x00', pos)
108 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
108 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
109 zeropos + 1, 40)
109 zeropos + 1, 40)
110 flags = self.lm._getflags(data, self.pos, zeropos)
110 flags = self.lm._getflags(data, self.pos, zeropos)
111 self.pos += 1
111 self.pos += 1
112 return (data[pos:zeropos], hashval, flags)
112 return (data[pos:zeropos], hashval, flags)
113
113
114 __next__ = next
114 __next__ = next
115
115
116 def unhexlify(data, extra, pos, length):
116 def unhexlify(data, extra, pos, length):
117 s = bin(data[pos:pos + length])
117 s = bin(data[pos:pos + length])
118 if extra:
118 if extra:
119 s += chr(extra & 0xff)
119 s += chr(extra & 0xff)
120 return s
120 return s
121
121
122 def _cmp(a, b):
122 def _cmp(a, b):
123 return (a > b) - (a < b)
123 return (a > b) - (a < b)
124
124
125 class _lazymanifest(object):
125 class _lazymanifest(object):
126 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
126 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
127 if positions is None:
127 if positions is None:
128 self.positions = self.findlines(data)
128 self.positions = self.findlines(data)
129 self.extrainfo = [0] * len(self.positions)
129 self.extrainfo = [0] * len(self.positions)
130 self.data = data
130 self.data = data
131 self.extradata = []
131 self.extradata = []
132 else:
132 else:
133 self.positions = positions[:]
133 self.positions = positions[:]
134 self.extrainfo = extrainfo[:]
134 self.extrainfo = extrainfo[:]
135 self.extradata = extradata[:]
135 self.extradata = extradata[:]
136 self.data = data
136 self.data = data
137
137
138 def findlines(self, data):
138 def findlines(self, data):
139 if not data:
139 if not data:
140 return []
140 return []
141 pos = data.find("\n")
141 pos = data.find("\n")
142 if pos == -1 or data[-1:] != '\n':
142 if pos == -1 or data[-1:] != '\n':
143 raise ValueError("Manifest did not end in a newline.")
143 raise ValueError("Manifest did not end in a newline.")
144 positions = [0]
144 positions = [0]
145 prev = data[:data.find('\x00')]
145 prev = data[:data.find('\x00')]
146 while pos < len(data) - 1 and pos != -1:
146 while pos < len(data) - 1 and pos != -1:
147 positions.append(pos + 1)
147 positions.append(pos + 1)
148 nexts = data[pos + 1:data.find('\x00', pos + 1)]
148 nexts = data[pos + 1:data.find('\x00', pos + 1)]
149 if nexts < prev:
149 if nexts < prev:
150 raise ValueError("Manifest lines not in sorted order.")
150 raise ValueError("Manifest lines not in sorted order.")
151 prev = nexts
151 prev = nexts
152 pos = data.find("\n", pos + 1)
152 pos = data.find("\n", pos + 1)
153 return positions
153 return positions
154
154
155 def _get(self, index):
155 def _get(self, index):
156 # get the position encoded in pos:
156 # get the position encoded in pos:
157 # positive number is an index in 'data'
157 # positive number is an index in 'data'
158 # negative number is in extrapieces
158 # negative number is in extrapieces
159 pos = self.positions[index]
159 pos = self.positions[index]
160 if pos >= 0:
160 if pos >= 0:
161 return self.data, pos
161 return self.data, pos
162 return self.extradata[-pos - 1], -1
162 return self.extradata[-pos - 1], -1
163
163
164 def _getkey(self, pos):
164 def _getkey(self, pos):
165 if pos >= 0:
165 if pos >= 0:
166 return self.data[pos:self.data.find('\x00', pos + 1)]
166 return self.data[pos:self.data.find('\x00', pos + 1)]
167 return self.extradata[-pos - 1][0]
167 return self.extradata[-pos - 1][0]
168
168
169 def bsearch(self, key):
169 def bsearch(self, key):
170 first = 0
170 first = 0
171 last = len(self.positions) - 1
171 last = len(self.positions) - 1
172
172
173 while first <= last:
173 while first <= last:
174 midpoint = (first + last)//2
174 midpoint = (first + last)//2
175 nextpos = self.positions[midpoint]
175 nextpos = self.positions[midpoint]
176 candidate = self._getkey(nextpos)
176 candidate = self._getkey(nextpos)
177 r = _cmp(key, candidate)
177 r = _cmp(key, candidate)
178 if r == 0:
178 if r == 0:
179 return midpoint
179 return midpoint
180 else:
180 else:
181 if r < 0:
181 if r < 0:
182 last = midpoint - 1
182 last = midpoint - 1
183 else:
183 else:
184 first = midpoint + 1
184 first = midpoint + 1
185 return -1
185 return -1
186
186
187 def bsearch2(self, key):
187 def bsearch2(self, key):
188 # same as the above, but will always return the position
188 # same as the above, but will always return the position
189 # done for performance reasons
189 # done for performance reasons
190 first = 0
190 first = 0
191 last = len(self.positions) - 1
191 last = len(self.positions) - 1
192
192
193 while first <= last:
193 while first <= last:
194 midpoint = (first + last)//2
194 midpoint = (first + last)//2
195 nextpos = self.positions[midpoint]
195 nextpos = self.positions[midpoint]
196 candidate = self._getkey(nextpos)
196 candidate = self._getkey(nextpos)
197 r = _cmp(key, candidate)
197 r = _cmp(key, candidate)
198 if r == 0:
198 if r == 0:
199 return (midpoint, True)
199 return (midpoint, True)
200 else:
200 else:
201 if r < 0:
201 if r < 0:
202 last = midpoint - 1
202 last = midpoint - 1
203 else:
203 else:
204 first = midpoint + 1
204 first = midpoint + 1
205 return (first, False)
205 return (first, False)
206
206
207 def __contains__(self, key):
207 def __contains__(self, key):
208 return self.bsearch(key) != -1
208 return self.bsearch(key) != -1
209
209
210 def _getflags(self, data, needle, pos):
210 def _getflags(self, data, needle, pos):
211 start = pos + 41
211 start = pos + 41
212 end = data.find("\n", start)
212 end = data.find("\n", start)
213 if end == -1:
213 if end == -1:
214 end = len(data) - 1
214 end = len(data) - 1
215 if start == end:
215 if start == end:
216 return ''
216 return ''
217 return self.data[start:end]
217 return self.data[start:end]
218
218
219 def __getitem__(self, key):
219 def __getitem__(self, key):
220 if not isinstance(key, bytes):
220 if not isinstance(key, bytes):
221 raise TypeError("getitem: manifest keys must be a bytes.")
221 raise TypeError("getitem: manifest keys must be a bytes.")
222 needle = self.bsearch(key)
222 needle = self.bsearch(key)
223 if needle == -1:
223 if needle == -1:
224 raise KeyError
224 raise KeyError
225 data, pos = self._get(needle)
225 data, pos = self._get(needle)
226 if pos == -1:
226 if pos == -1:
227 return (data[1], data[2])
227 return (data[1], data[2])
228 zeropos = data.find('\x00', pos)
228 zeropos = data.find('\x00', pos)
229 assert 0 <= needle <= len(self.positions)
229 assert 0 <= needle <= len(self.positions)
230 assert len(self.extrainfo) == len(self.positions)
230 assert len(self.extrainfo) == len(self.positions)
231 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
231 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
232 flags = self._getflags(data, needle, zeropos)
232 flags = self._getflags(data, needle, zeropos)
233 return (hashval, flags)
233 return (hashval, flags)
234
234
235 def __delitem__(self, key):
235 def __delitem__(self, key):
236 needle, found = self.bsearch2(key)
236 needle, found = self.bsearch2(key)
237 if not found:
237 if not found:
238 raise KeyError
238 raise KeyError
239 cur = self.positions[needle]
239 cur = self.positions[needle]
240 self.positions = self.positions[:needle] + self.positions[needle + 1:]
240 self.positions = self.positions[:needle] + self.positions[needle + 1:]
241 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
241 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
242 if cur >= 0:
242 if cur >= 0:
243 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
243 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
244
244
245 def __setitem__(self, key, value):
245 def __setitem__(self, key, value):
246 if not isinstance(key, bytes):
246 if not isinstance(key, bytes):
247 raise TypeError("setitem: manifest keys must be a byte string.")
247 raise TypeError("setitem: manifest keys must be a byte string.")
248 if not isinstance(value, tuple) or len(value) != 2:
248 if not isinstance(value, tuple) or len(value) != 2:
249 raise TypeError("Manifest values must be a tuple of (node, flags).")
249 raise TypeError("Manifest values must be a tuple of (node, flags).")
250 hashval = value[0]
250 hashval = value[0]
251 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
251 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
252 raise TypeError("node must be a 20-byte byte string")
252 raise TypeError("node must be a 20-byte byte string")
253 flags = value[1]
253 flags = value[1]
254 if len(hashval) == 22:
254 if len(hashval) == 22:
255 hashval = hashval[:-1]
255 hashval = hashval[:-1]
256 if not isinstance(flags, bytes) or len(flags) > 1:
256 if not isinstance(flags, bytes) or len(flags) > 1:
257 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
257 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
258 needle, found = self.bsearch2(key)
258 needle, found = self.bsearch2(key)
259 if found:
259 if found:
260 # put the item
260 # put the item
261 pos = self.positions[needle]
261 pos = self.positions[needle]
262 if pos < 0:
262 if pos < 0:
263 self.extradata[-pos - 1] = (key, hashval, value[1])
263 self.extradata[-pos - 1] = (key, hashval, value[1])
264 else:
264 else:
265 # just don't bother
265 # just don't bother
266 self.extradata.append((key, hashval, value[1]))
266 self.extradata.append((key, hashval, value[1]))
267 self.positions[needle] = -len(self.extradata)
267 self.positions[needle] = -len(self.extradata)
268 else:
268 else:
269 # not found, put it in with extra positions
269 # not found, put it in with extra positions
270 self.extradata.append((key, hashval, value[1]))
270 self.extradata.append((key, hashval, value[1]))
271 self.positions = (self.positions[:needle] + [-len(self.extradata)]
271 self.positions = (self.positions[:needle] + [-len(self.extradata)]
272 + self.positions[needle:])
272 + self.positions[needle:])
273 self.extrainfo = (self.extrainfo[:needle] + [0] +
273 self.extrainfo = (self.extrainfo[:needle] + [0] +
274 self.extrainfo[needle:])
274 self.extrainfo[needle:])
275
275
276 def copy(self):
276 def copy(self):
277 # XXX call _compact like in C?
277 # XXX call _compact like in C?
278 return _lazymanifest(self.data, self.positions, self.extrainfo,
278 return _lazymanifest(self.data, self.positions, self.extrainfo,
279 self.extradata)
279 self.extradata)
280
280
281 def _compact(self):
281 def _compact(self):
282 # hopefully not called TOO often
282 # hopefully not called TOO often
283 if len(self.extradata) == 0:
283 if len(self.extradata) == 0:
284 return
284 return
285 l = []
285 l = []
286 last_cut = 0
286 last_cut = 0
287 i = 0
287 i = 0
288 offset = 0
288 offset = 0
289 self.extrainfo = [0] * len(self.positions)
289 self.extrainfo = [0] * len(self.positions)
290 while i < len(self.positions):
290 while i < len(self.positions):
291 if self.positions[i] >= 0:
291 if self.positions[i] >= 0:
292 cur = self.positions[i]
292 cur = self.positions[i]
293 last_cut = cur
293 last_cut = cur
294 while True:
294 while True:
295 self.positions[i] = offset
295 self.positions[i] = offset
296 i += 1
296 i += 1
297 if i == len(self.positions) or self.positions[i] < 0:
297 if i == len(self.positions) or self.positions[i] < 0:
298 break
298 break
299 offset += self.positions[i] - cur
299 offset += self.positions[i] - cur
300 cur = self.positions[i]
300 cur = self.positions[i]
301 end_cut = self.data.find('\n', cur)
301 end_cut = self.data.find('\n', cur)
302 if end_cut != -1:
302 if end_cut != -1:
303 end_cut += 1
303 end_cut += 1
304 offset += end_cut - cur
304 offset += end_cut - cur
305 l.append(self.data[last_cut:end_cut])
305 l.append(self.data[last_cut:end_cut])
306 else:
306 else:
307 while i < len(self.positions) and self.positions[i] < 0:
307 while i < len(self.positions) and self.positions[i] < 0:
308 cur = self.positions[i]
308 cur = self.positions[i]
309 t = self.extradata[-cur - 1]
309 t = self.extradata[-cur - 1]
310 l.append(self._pack(t))
310 l.append(self._pack(t))
311 self.positions[i] = offset
311 self.positions[i] = offset
312 if len(t[1]) > 20:
312 if len(t[1]) > 20:
313 self.extrainfo[i] = ord(t[1][21])
313 self.extrainfo[i] = ord(t[1][21])
314 offset += len(l[-1])
314 offset += len(l[-1])
315 i += 1
315 i += 1
316 self.data = ''.join(l)
316 self.data = ''.join(l)
317 self.extradata = []
317 self.extradata = []
318
318
319 def _pack(self, d):
319 def _pack(self, d):
320 return d[0] + '\x00' + hex(d[1][:20]) + d[2] + '\n'
320 return d[0] + '\x00' + hex(d[1][:20]) + d[2] + '\n'
321
321
322 def text(self):
322 def text(self):
323 self._compact()
323 self._compact()
324 return self.data
324 return self.data
325
325
326 def diff(self, m2, clean=False):
326 def diff(self, m2, clean=False):
327 '''Finds changes between the current manifest and m2.'''
327 '''Finds changes between the current manifest and m2.'''
328 # XXX think whether efficiency matters here
328 # XXX think whether efficiency matters here
329 diff = {}
329 diff = {}
330
330
331 for fn, e1, flags in self.iterentries():
331 for fn, e1, flags in self.iterentries():
332 if fn not in m2:
332 if fn not in m2:
333 diff[fn] = (e1, flags), (None, '')
333 diff[fn] = (e1, flags), (None, '')
334 else:
334 else:
335 e2 = m2[fn]
335 e2 = m2[fn]
336 if (e1, flags) != e2:
336 if (e1, flags) != e2:
337 diff[fn] = (e1, flags), e2
337 diff[fn] = (e1, flags), e2
338 elif clean:
338 elif clean:
339 diff[fn] = None
339 diff[fn] = None
340
340
341 for fn, e2, flags in m2.iterentries():
341 for fn, e2, flags in m2.iterentries():
342 if fn not in self:
342 if fn not in self:
343 diff[fn] = (None, ''), (e2, flags)
343 diff[fn] = (None, ''), (e2, flags)
344
344
345 return diff
345 return diff
346
346
347 def iterentries(self):
347 def iterentries(self):
348 return lazymanifestiterentries(self)
348 return lazymanifestiterentries(self)
349
349
350 def iterkeys(self):
350 def iterkeys(self):
351 return lazymanifestiter(self)
351 return lazymanifestiter(self)
352
352
353 def __iter__(self):
353 def __iter__(self):
354 return lazymanifestiter(self)
354 return lazymanifestiter(self)
355
355
356 def __len__(self):
356 def __len__(self):
357 return len(self.positions)
357 return len(self.positions)
358
358
359 def filtercopy(self, filterfn):
359 def filtercopy(self, filterfn):
360 # XXX should be optimized
360 # XXX should be optimized
361 c = _lazymanifest('')
361 c = _lazymanifest('')
362 for f, n, fl in self.iterentries():
362 for f, n, fl in self.iterentries():
363 if filterfn(f):
363 if filterfn(f):
364 c[f] = n, fl
364 c[f] = n, fl
365 return c
365 return c
366
366
367 try:
367 try:
368 _lazymanifest = parsers.lazymanifest
368 _lazymanifest = parsers.lazymanifest
369 except AttributeError:
369 except AttributeError:
370 pass
370 pass
371
371
372 @interfaceutil.implementer(repository.imanifestdict)
372 @interfaceutil.implementer(repository.imanifestdict)
373 class manifestdict(object):
373 class manifestdict(object):
374 def __init__(self, data=''):
374 def __init__(self, data=''):
375 self._lm = _lazymanifest(data)
375 self._lm = _lazymanifest(data)
376
376
377 def __getitem__(self, key):
377 def __getitem__(self, key):
378 return self._lm[key][0]
378 return self._lm[key][0]
379
379
380 def find(self, key):
380 def find(self, key):
381 return self._lm[key]
381 return self._lm[key]
382
382
383 def __len__(self):
383 def __len__(self):
384 return len(self._lm)
384 return len(self._lm)
385
385
386 def __nonzero__(self):
386 def __nonzero__(self):
387 # nonzero is covered by the __len__ function, but implementing it here
387 # nonzero is covered by the __len__ function, but implementing it here
388 # makes it easier for extensions to override.
388 # makes it easier for extensions to override.
389 return len(self._lm) != 0
389 return len(self._lm) != 0
390
390
391 __bool__ = __nonzero__
391 __bool__ = __nonzero__
392
392
393 def __setitem__(self, key, node):
393 def __setitem__(self, key, node):
394 self._lm[key] = node, self.flags(key, '')
394 self._lm[key] = node, self.flags(key, '')
395
395
396 def __contains__(self, key):
396 def __contains__(self, key):
397 if key is None:
397 if key is None:
398 return False
398 return False
399 return key in self._lm
399 return key in self._lm
400
400
401 def __delitem__(self, key):
401 def __delitem__(self, key):
402 del self._lm[key]
402 del self._lm[key]
403
403
404 def __iter__(self):
404 def __iter__(self):
405 return self._lm.__iter__()
405 return self._lm.__iter__()
406
406
407 def iterkeys(self):
407 def iterkeys(self):
408 return self._lm.iterkeys()
408 return self._lm.iterkeys()
409
409
410 def keys(self):
410 def keys(self):
411 return list(self.iterkeys())
411 return list(self.iterkeys())
412
412
413 def filesnotin(self, m2, match=None):
413 def filesnotin(self, m2, match=None):
414 '''Set of files in this manifest that are not in the other'''
414 '''Set of files in this manifest that are not in the other'''
415 if match:
415 if match:
416 m1 = self.matches(match)
416 m1 = self.matches(match)
417 m2 = m2.matches(match)
417 m2 = m2.matches(match)
418 return m1.filesnotin(m2)
418 return m1.filesnotin(m2)
419 diff = self.diff(m2)
419 diff = self.diff(m2)
420 files = set(filepath
420 files = set(filepath
421 for filepath, hashflags in diff.iteritems()
421 for filepath, hashflags in diff.iteritems()
422 if hashflags[1][0] is None)
422 if hashflags[1][0] is None)
423 return files
423 return files
424
424
425 @propertycache
425 @propertycache
426 def _dirs(self):
426 def _dirs(self):
427 return util.dirs(self)
427 return util.dirs(self)
428
428
429 def dirs(self):
429 def dirs(self):
430 return self._dirs
430 return self._dirs
431
431
432 def hasdir(self, dir):
432 def hasdir(self, dir):
433 return dir in self._dirs
433 return dir in self._dirs
434
434
435 def _filesfastpath(self, match):
435 def _filesfastpath(self, match):
436 '''Checks whether we can correctly and quickly iterate over matcher
436 '''Checks whether we can correctly and quickly iterate over matcher
437 files instead of over manifest files.'''
437 files instead of over manifest files.'''
438 files = match.files()
438 files = match.files()
439 return (len(files) < 100 and (match.isexact() or
439 return (len(files) < 100 and (match.isexact() or
440 (match.prefix() and all(fn in self for fn in files))))
440 (match.prefix() and all(fn in self for fn in files))))
441
441
442 def walk(self, match):
442 def walk(self, match):
443 '''Generates matching file names.
443 '''Generates matching file names.
444
444
445 Equivalent to manifest.matches(match).iterkeys(), but without creating
445 Equivalent to manifest.matches(match).iterkeys(), but without creating
446 an entirely new manifest.
446 an entirely new manifest.
447
447
448 It also reports nonexistent files by marking them bad with match.bad().
448 It also reports nonexistent files by marking them bad with match.bad().
449 '''
449 '''
450 if match.always():
450 if match.always():
451 for f in iter(self):
451 for f in iter(self):
452 yield f
452 yield f
453 return
453 return
454
454
455 fset = set(match.files())
455 fset = set(match.files())
456
456
457 # avoid the entire walk if we're only looking for specific files
457 # avoid the entire walk if we're only looking for specific files
458 if self._filesfastpath(match):
458 if self._filesfastpath(match):
459 for fn in sorted(fset):
459 for fn in sorted(fset):
460 yield fn
460 yield fn
461 return
461 return
462
462
463 for fn in self:
463 for fn in self:
464 if fn in fset:
464 if fn in fset:
465 # specified pattern is the exact name
465 # specified pattern is the exact name
466 fset.remove(fn)
466 fset.remove(fn)
467 if match(fn):
467 if match(fn):
468 yield fn
468 yield fn
469
469
470 # for dirstate.walk, files=['.'] means "walk the whole tree".
470 # for dirstate.walk, files=['.'] means "walk the whole tree".
471 # follow that here, too
471 # follow that here, too
472 fset.discard('.')
472 fset.discard('.')
473
473
474 for fn in sorted(fset):
474 for fn in sorted(fset):
475 if not self.hasdir(fn):
475 if not self.hasdir(fn):
476 match.bad(fn, None)
476 match.bad(fn, None)
477
477
478 def matches(self, match):
478 def matches(self, match):
479 '''generate a new manifest filtered by the match argument'''
479 '''generate a new manifest filtered by the match argument'''
480 if match.always():
480 if match.always():
481 return self.copy()
481 return self.copy()
482
482
483 if self._filesfastpath(match):
483 if self._filesfastpath(match):
484 m = manifestdict()
484 m = manifestdict()
485 lm = self._lm
485 lm = self._lm
486 for fn in match.files():
486 for fn in match.files():
487 if fn in lm:
487 if fn in lm:
488 m._lm[fn] = lm[fn]
488 m._lm[fn] = lm[fn]
489 return m
489 return m
490
490
491 m = manifestdict()
491 m = manifestdict()
492 m._lm = self._lm.filtercopy(match)
492 m._lm = self._lm.filtercopy(match)
493 return m
493 return m
494
494
495 def diff(self, m2, match=None, clean=False):
495 def diff(self, m2, match=None, clean=False):
496 '''Finds changes between the current manifest and m2.
496 '''Finds changes between the current manifest and m2.
497
497
498 Args:
498 Args:
499 m2: the manifest to which this manifest should be compared.
499 m2: the manifest to which this manifest should be compared.
500 clean: if true, include files unchanged between these manifests
500 clean: if true, include files unchanged between these manifests
501 with a None value in the returned dictionary.
501 with a None value in the returned dictionary.
502
502
503 The result is returned as a dict with filename as key and
503 The result is returned as a dict with filename as key and
504 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
504 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
505 nodeid in the current/other manifest and fl1/fl2 is the flag
505 nodeid in the current/other manifest and fl1/fl2 is the flag
506 in the current/other manifest. Where the file does not exist,
506 in the current/other manifest. Where the file does not exist,
507 the nodeid will be None and the flags will be the empty
507 the nodeid will be None and the flags will be the empty
508 string.
508 string.
509 '''
509 '''
510 if match:
510 if match:
511 m1 = self.matches(match)
511 m1 = self.matches(match)
512 m2 = m2.matches(match)
512 m2 = m2.matches(match)
513 return m1.diff(m2, clean=clean)
513 return m1.diff(m2, clean=clean)
514 return self._lm.diff(m2._lm, clean)
514 return self._lm.diff(m2._lm, clean)
515
515
516 def setflag(self, key, flag):
516 def setflag(self, key, flag):
517 self._lm[key] = self[key], flag
517 self._lm[key] = self[key], flag
518
518
519 def get(self, key, default=None):
519 def get(self, key, default=None):
520 try:
520 try:
521 return self._lm[key][0]
521 return self._lm[key][0]
522 except KeyError:
522 except KeyError:
523 return default
523 return default
524
524
525 def flags(self, key, default=''):
525 def flags(self, key, default=''):
526 try:
526 try:
527 return self._lm[key][1]
527 return self._lm[key][1]
528 except KeyError:
528 except KeyError:
529 return default
529 return default
530
530
531 def copy(self):
531 def copy(self):
532 c = manifestdict()
532 c = manifestdict()
533 c._lm = self._lm.copy()
533 c._lm = self._lm.copy()
534 return c
534 return c
535
535
536 def items(self):
536 def items(self):
537 return (x[:2] for x in self._lm.iterentries())
537 return (x[:2] for x in self._lm.iterentries())
538
538
539 def iteritems(self):
539 def iteritems(self):
540 return (x[:2] for x in self._lm.iterentries())
540 return (x[:2] for x in self._lm.iterentries())
541
541
542 def iterentries(self):
542 def iterentries(self):
543 return self._lm.iterentries()
543 return self._lm.iterentries()
544
544
545 def text(self):
545 def text(self):
546 # most likely uses native version
546 # most likely uses native version
547 return self._lm.text()
547 return self._lm.text()
548
548
549 def fastdelta(self, base, changes):
549 def fastdelta(self, base, changes):
550 """Given a base manifest text as a bytearray and a list of changes
550 """Given a base manifest text as a bytearray and a list of changes
551 relative to that text, compute a delta that can be used by revlog.
551 relative to that text, compute a delta that can be used by revlog.
552 """
552 """
553 delta = []
553 delta = []
554 dstart = None
554 dstart = None
555 dend = None
555 dend = None
556 dline = [""]
556 dline = [""]
557 start = 0
557 start = 0
558 # zero copy representation of base as a buffer
558 # zero copy representation of base as a buffer
559 addbuf = util.buffer(base)
559 addbuf = util.buffer(base)
560
560
561 changes = list(changes)
561 changes = list(changes)
562 if len(changes) < 1000:
562 if len(changes) < 1000:
563 # start with a readonly loop that finds the offset of
563 # start with a readonly loop that finds the offset of
564 # each line and creates the deltas
564 # each line and creates the deltas
565 for f, todelete in changes:
565 for f, todelete in changes:
566 # bs will either be the index of the item or the insert point
566 # bs will either be the index of the item or the insert point
567 start, end = _msearch(addbuf, f, start)
567 start, end = _msearch(addbuf, f, start)
568 if not todelete:
568 if not todelete:
569 h, fl = self._lm[f]
569 h, fl = self._lm[f]
570 l = "%s\0%s%s\n" % (f, hex(h), fl)
570 l = "%s\0%s%s\n" % (f, hex(h), fl)
571 else:
571 else:
572 if start == end:
572 if start == end:
573 # item we want to delete was not found, error out
573 # item we want to delete was not found, error out
574 raise AssertionError(
574 raise AssertionError(
575 _("failed to remove %s from manifest") % f)
575 _("failed to remove %s from manifest") % f)
576 l = ""
576 l = ""
577 if dstart is not None and dstart <= start and dend >= start:
577 if dstart is not None and dstart <= start and dend >= start:
578 if dend < end:
578 if dend < end:
579 dend = end
579 dend = end
580 if l:
580 if l:
581 dline.append(l)
581 dline.append(l)
582 else:
582 else:
583 if dstart is not None:
583 if dstart is not None:
584 delta.append([dstart, dend, "".join(dline)])
584 delta.append([dstart, dend, "".join(dline)])
585 dstart = start
585 dstart = start
586 dend = end
586 dend = end
587 dline = [l]
587 dline = [l]
588
588
589 if dstart is not None:
589 if dstart is not None:
590 delta.append([dstart, dend, "".join(dline)])
590 delta.append([dstart, dend, "".join(dline)])
591 # apply the delta to the base, and get a delta for addrevision
591 # apply the delta to the base, and get a delta for addrevision
592 deltatext, arraytext = _addlistdelta(base, delta)
592 deltatext, arraytext = _addlistdelta(base, delta)
593 else:
593 else:
594 # For large changes, it's much cheaper to just build the text and
594 # For large changes, it's much cheaper to just build the text and
595 # diff it.
595 # diff it.
596 arraytext = bytearray(self.text())
596 arraytext = bytearray(self.text())
597 deltatext = mdiff.textdiff(
597 deltatext = mdiff.textdiff(
598 util.buffer(base), util.buffer(arraytext))
598 util.buffer(base), util.buffer(arraytext))
599
599
600 return arraytext, deltatext
600 return arraytext, deltatext
601
601
602 def _msearch(m, s, lo=0, hi=None):
602 def _msearch(m, s, lo=0, hi=None):
603 '''return a tuple (start, end) that says where to find s within m.
603 '''return a tuple (start, end) that says where to find s within m.
604
604
605 If the string is found m[start:end] are the line containing
605 If the string is found m[start:end] are the line containing
606 that string. If start == end the string was not found and
606 that string. If start == end the string was not found and
607 they indicate the proper sorted insertion point.
607 they indicate the proper sorted insertion point.
608
608
609 m should be a buffer, a memoryview or a byte string.
609 m should be a buffer, a memoryview or a byte string.
610 s is a byte string'''
610 s is a byte string'''
611 def advance(i, c):
611 def advance(i, c):
612 while i < lenm and m[i:i + 1] != c:
612 while i < lenm and m[i:i + 1] != c:
613 i += 1
613 i += 1
614 return i
614 return i
615 if not s:
615 if not s:
616 return (lo, lo)
616 return (lo, lo)
617 lenm = len(m)
617 lenm = len(m)
618 if not hi:
618 if not hi:
619 hi = lenm
619 hi = lenm
620 while lo < hi:
620 while lo < hi:
621 mid = (lo + hi) // 2
621 mid = (lo + hi) // 2
622 start = mid
622 start = mid
623 while start > 0 and m[start - 1:start] != '\n':
623 while start > 0 and m[start - 1:start] != '\n':
624 start -= 1
624 start -= 1
625 end = advance(start, '\0')
625 end = advance(start, '\0')
626 if bytes(m[start:end]) < s:
626 if bytes(m[start:end]) < s:
627 # we know that after the null there are 40 bytes of sha1
627 # we know that after the null there are 40 bytes of sha1
628 # this translates to the bisect lo = mid + 1
628 # this translates to the bisect lo = mid + 1
629 lo = advance(end + 40, '\n') + 1
629 lo = advance(end + 40, '\n') + 1
630 else:
630 else:
631 # this translates to the bisect hi = mid
631 # this translates to the bisect hi = mid
632 hi = start
632 hi = start
633 end = advance(lo, '\0')
633 end = advance(lo, '\0')
634 found = m[lo:end]
634 found = m[lo:end]
635 if s == found:
635 if s == found:
636 # we know that after the null there are 40 bytes of sha1
636 # we know that after the null there are 40 bytes of sha1
637 end = advance(end + 40, '\n')
637 end = advance(end + 40, '\n')
638 return (lo, end + 1)
638 return (lo, end + 1)
639 else:
639 else:
640 return (lo, lo)
640 return (lo, lo)
641
641
642 def _checkforbidden(l):
642 def _checkforbidden(l):
643 """Check filenames for illegal characters."""
643 """Check filenames for illegal characters."""
644 for f in l:
644 for f in l:
645 if '\n' in f or '\r' in f:
645 if '\n' in f or '\r' in f:
646 raise error.StorageError(
646 raise error.StorageError(
647 _("'\\n' and '\\r' disallowed in filenames: %r")
647 _("'\\n' and '\\r' disallowed in filenames: %r")
648 % pycompat.bytestr(f))
648 % pycompat.bytestr(f))
649
649
650
650
651 # apply the changes collected during the bisect loop to our addlist
651 # apply the changes collected during the bisect loop to our addlist
652 # return a delta suitable for addrevision
652 # return a delta suitable for addrevision
653 def _addlistdelta(addlist, x):
653 def _addlistdelta(addlist, x):
654 # for large addlist arrays, building a new array is cheaper
654 # for large addlist arrays, building a new array is cheaper
655 # than repeatedly modifying the existing one
655 # than repeatedly modifying the existing one
656 currentposition = 0
656 currentposition = 0
657 newaddlist = bytearray()
657 newaddlist = bytearray()
658
658
659 for start, end, content in x:
659 for start, end, content in x:
660 newaddlist += addlist[currentposition:start]
660 newaddlist += addlist[currentposition:start]
661 if content:
661 if content:
662 newaddlist += bytearray(content)
662 newaddlist += bytearray(content)
663
663
664 currentposition = end
664 currentposition = end
665
665
666 newaddlist += addlist[currentposition:]
666 newaddlist += addlist[currentposition:]
667
667
668 deltatext = "".join(struct.pack(">lll", start, end, len(content))
668 deltatext = "".join(struct.pack(">lll", start, end, len(content))
669 + content for start, end, content in x)
669 + content for start, end, content in x)
670 return deltatext, newaddlist
670 return deltatext, newaddlist
671
671
672 def _splittopdir(f):
672 def _splittopdir(f):
673 if '/' in f:
673 if '/' in f:
674 dir, subpath = f.split('/', 1)
674 dir, subpath = f.split('/', 1)
675 return dir + '/', subpath
675 return dir + '/', subpath
676 else:
676 else:
677 return '', f
677 return '', f
678
678
679 _noop = lambda s: None
679 _noop = lambda s: None
680
680
681 class treemanifest(object):
681 class treemanifest(object):
682 def __init__(self, dir='', text=''):
682 def __init__(self, dir='', text=''):
683 self._dir = dir
683 self._dir = dir
684 self._node = nullid
684 self._node = nullid
685 self._loadfunc = _noop
685 self._loadfunc = _noop
686 self._copyfunc = _noop
686 self._copyfunc = _noop
687 self._dirty = False
687 self._dirty = False
688 self._dirs = {}
688 self._dirs = {}
689 self._lazydirs = {}
689 self._lazydirs = {}
690 # Using _lazymanifest here is a little slower than plain old dicts
690 # Using _lazymanifest here is a little slower than plain old dicts
691 self._files = {}
691 self._files = {}
692 self._flags = {}
692 self._flags = {}
693 if text:
693 if text:
694 def readsubtree(subdir, subm):
694 def readsubtree(subdir, subm):
695 raise AssertionError('treemanifest constructor only accepts '
695 raise AssertionError('treemanifest constructor only accepts '
696 'flat manifests')
696 'flat manifests')
697 self.parse(text, readsubtree)
697 self.parse(text, readsubtree)
698 self._dirty = True # Mark flat manifest dirty after parsing
698 self._dirty = True # Mark flat manifest dirty after parsing
699
699
700 def _subpath(self, path):
700 def _subpath(self, path):
701 return self._dir + path
701 return self._dir + path
702
702
703 def _loadalllazy(self):
703 def _loadalllazy(self):
704 for k, (path, node, readsubtree) in self._lazydirs.iteritems():
704 for k, (path, node, readsubtree) in self._lazydirs.iteritems():
705 self._dirs[k] = readsubtree(path, node)
705 self._dirs[k] = readsubtree(path, node)
706 self._lazydirs = {}
706 self._lazydirs = {}
707
707
708 def _loadlazy(self, d):
708 def _loadlazy(self, d):
709 path, node, readsubtree = self._lazydirs[d]
709 v = self._lazydirs.get(d)
710 self._dirs[d] = readsubtree(path, node)
710 if v:
711 del self._lazydirs[d]
711 path, node, readsubtree = v
712 self._dirs[d] = readsubtree(path, node)
713 del self._lazydirs[d]
712
714
713 def _loadchildrensetlazy(self, visit):
715 def _loadchildrensetlazy(self, visit):
714 if not visit:
716 if not visit:
715 return None
717 return None
716 if visit == 'all' or visit == 'this':
718 if visit == 'all' or visit == 'this':
717 self._loadalllazy()
719 self._loadalllazy()
718 return None
720 return None
719
721
720 todel = []
722 todel = []
721 for k in visit:
723 for k in visit:
722 kslash = k + '/'
724 kslash = k + '/'
723 ld = self._lazydirs.get(kslash)
725 ld = self._lazydirs.get(kslash)
724 if ld:
726 if ld:
725 path, node, readsubtree = ld
727 path, node, readsubtree = ld
726 self._dirs[kslash] = readsubtree(path, node)
728 self._dirs[kslash] = readsubtree(path, node)
727 todel.append(kslash)
729 todel.append(kslash)
728 for kslash in todel:
730 for kslash in todel:
729 del self._lazydirs[kslash]
731 del self._lazydirs[kslash]
730 return visit
732 return visit
731
733
732 def __len__(self):
734 def __len__(self):
733 self._load()
735 self._load()
734 size = len(self._files)
736 size = len(self._files)
735 self._loadalllazy()
737 self._loadalllazy()
736 for m in self._dirs.values():
738 for m in self._dirs.values():
737 size += m.__len__()
739 size += m.__len__()
738 return size
740 return size
739
741
740 def __nonzero__(self):
742 def __nonzero__(self):
741 # Faster than "__len() != 0" since it avoids loading sub-manifests
743 # Faster than "__len() != 0" since it avoids loading sub-manifests
742 return not self._isempty()
744 return not self._isempty()
743
745
744 __bool__ = __nonzero__
746 __bool__ = __nonzero__
745
747
746 def _isempty(self):
748 def _isempty(self):
747 self._load() # for consistency; already loaded by all callers
749 self._load() # for consistency; already loaded by all callers
748 # See if we can skip loading everything.
750 # See if we can skip loading everything.
749 if self._files or (self._dirs and
751 if self._files or (self._dirs and
750 any(not m._isempty() for m in self._dirs.values())):
752 any(not m._isempty() for m in self._dirs.values())):
751 return False
753 return False
752 self._loadalllazy()
754 self._loadalllazy()
753 return (not self._dirs or
755 return (not self._dirs or
754 all(m._isempty() for m in self._dirs.values()))
756 all(m._isempty() for m in self._dirs.values()))
755
757
756 def __repr__(self):
758 def __repr__(self):
757 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
759 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
758 (self._dir, hex(self._node),
760 (self._dir, hex(self._node),
759 bool(self._loadfunc is _noop),
761 bool(self._loadfunc is _noop),
760 self._dirty, id(self)))
762 self._dirty, id(self)))
761
763
762 def dir(self):
764 def dir(self):
763 '''The directory that this tree manifest represents, including a
765 '''The directory that this tree manifest represents, including a
764 trailing '/'. Empty string for the repo root directory.'''
766 trailing '/'. Empty string for the repo root directory.'''
765 return self._dir
767 return self._dir
766
768
767 def node(self):
769 def node(self):
768 '''This node of this instance. nullid for unsaved instances. Should
770 '''This node of this instance. nullid for unsaved instances. Should
769 be updated when the instance is read or written from a revlog.
771 be updated when the instance is read or written from a revlog.
770 '''
772 '''
771 assert not self._dirty
773 assert not self._dirty
772 return self._node
774 return self._node
773
775
774 def setnode(self, node):
776 def setnode(self, node):
775 self._node = node
777 self._node = node
776 self._dirty = False
778 self._dirty = False
777
779
778 def iterentries(self):
780 def iterentries(self):
779 self._load()
781 self._load()
780 self._loadalllazy()
782 self._loadalllazy()
781 for p, n in sorted(itertools.chain(self._dirs.items(),
783 for p, n in sorted(itertools.chain(self._dirs.items(),
782 self._files.items())):
784 self._files.items())):
783 if p in self._files:
785 if p in self._files:
784 yield self._subpath(p), n, self._flags.get(p, '')
786 yield self._subpath(p), n, self._flags.get(p, '')
785 else:
787 else:
786 for x in n.iterentries():
788 for x in n.iterentries():
787 yield x
789 yield x
788
790
789 def items(self):
791 def items(self):
790 self._load()
792 self._load()
791 self._loadalllazy()
793 self._loadalllazy()
792 for p, n in sorted(itertools.chain(self._dirs.items(),
794 for p, n in sorted(itertools.chain(self._dirs.items(),
793 self._files.items())):
795 self._files.items())):
794 if p in self._files:
796 if p in self._files:
795 yield self._subpath(p), n
797 yield self._subpath(p), n
796 else:
798 else:
797 for f, sn in n.iteritems():
799 for f, sn in n.iteritems():
798 yield f, sn
800 yield f, sn
799
801
800 iteritems = items
802 iteritems = items
801
803
802 def iterkeys(self):
804 def iterkeys(self):
803 self._load()
805 self._load()
804 self._loadalllazy()
806 self._loadalllazy()
805 for p in sorted(itertools.chain(self._dirs, self._files)):
807 for p in sorted(itertools.chain(self._dirs, self._files)):
806 if p in self._files:
808 if p in self._files:
807 yield self._subpath(p)
809 yield self._subpath(p)
808 else:
810 else:
809 for f in self._dirs[p]:
811 for f in self._dirs[p]:
810 yield f
812 yield f
811
813
812 def keys(self):
814 def keys(self):
813 return list(self.iterkeys())
815 return list(self.iterkeys())
814
816
815 def __iter__(self):
817 def __iter__(self):
816 return self.iterkeys()
818 return self.iterkeys()
817
819
818 def __contains__(self, f):
820 def __contains__(self, f):
819 if f is None:
821 if f is None:
820 return False
822 return False
821 self._load()
823 self._load()
822 dir, subpath = _splittopdir(f)
824 dir, subpath = _splittopdir(f)
823 if dir:
825 if dir:
824 if dir in self._lazydirs:
826 if dir in self._lazydirs:
825 self._loadlazy(dir)
827 self._loadlazy(dir)
826
828
827 if dir not in self._dirs:
829 if dir not in self._dirs:
828 return False
830 return False
829
831
830 return self._dirs[dir].__contains__(subpath)
832 return self._dirs[dir].__contains__(subpath)
831 else:
833 else:
832 return f in self._files
834 return f in self._files
833
835
834 def get(self, f, default=None):
836 def get(self, f, default=None):
835 self._load()
837 self._load()
836 dir, subpath = _splittopdir(f)
838 dir, subpath = _splittopdir(f)
837 if dir:
839 if dir:
838 if dir in self._lazydirs:
840 if dir in self._lazydirs:
839 self._loadlazy(dir)
841 self._loadlazy(dir)
840
842
841 if dir not in self._dirs:
843 if dir not in self._dirs:
842 return default
844 return default
843 return self._dirs[dir].get(subpath, default)
845 return self._dirs[dir].get(subpath, default)
844 else:
846 else:
845 return self._files.get(f, default)
847 return self._files.get(f, default)
846
848
847 def __getitem__(self, f):
849 def __getitem__(self, f):
848 self._load()
850 self._load()
849 dir, subpath = _splittopdir(f)
851 dir, subpath = _splittopdir(f)
850 if dir:
852 if dir:
851 if dir in self._lazydirs:
853 if dir in self._lazydirs:
852 self._loadlazy(dir)
854 self._loadlazy(dir)
853
855
854 return self._dirs[dir].__getitem__(subpath)
856 return self._dirs[dir].__getitem__(subpath)
855 else:
857 else:
856 return self._files[f]
858 return self._files[f]
857
859
858 def flags(self, f):
860 def flags(self, f):
859 self._load()
861 self._load()
860 dir, subpath = _splittopdir(f)
862 dir, subpath = _splittopdir(f)
861 if dir:
863 if dir:
862 if dir in self._lazydirs:
864 if dir in self._lazydirs:
863 self._loadlazy(dir)
865 self._loadlazy(dir)
864
866
865 if dir not in self._dirs:
867 if dir not in self._dirs:
866 return ''
868 return ''
867 return self._dirs[dir].flags(subpath)
869 return self._dirs[dir].flags(subpath)
868 else:
870 else:
869 if f in self._lazydirs or f in self._dirs:
871 if f in self._lazydirs or f in self._dirs:
870 return ''
872 return ''
871 return self._flags.get(f, '')
873 return self._flags.get(f, '')
872
874
873 def find(self, f):
875 def find(self, f):
874 self._load()
876 self._load()
875 dir, subpath = _splittopdir(f)
877 dir, subpath = _splittopdir(f)
876 if dir:
878 if dir:
877 if dir in self._lazydirs:
879 if dir in self._lazydirs:
878 self._loadlazy(dir)
880 self._loadlazy(dir)
879
881
880 return self._dirs[dir].find(subpath)
882 return self._dirs[dir].find(subpath)
881 else:
883 else:
882 return self._files[f], self._flags.get(f, '')
884 return self._files[f], self._flags.get(f, '')
883
885
884 def __delitem__(self, f):
886 def __delitem__(self, f):
885 self._load()
887 self._load()
886 dir, subpath = _splittopdir(f)
888 dir, subpath = _splittopdir(f)
887 if dir:
889 if dir:
888 if dir in self._lazydirs:
890 if dir in self._lazydirs:
889 self._loadlazy(dir)
891 self._loadlazy(dir)
890
892
891 self._dirs[dir].__delitem__(subpath)
893 self._dirs[dir].__delitem__(subpath)
892 # If the directory is now empty, remove it
894 # If the directory is now empty, remove it
893 if self._dirs[dir]._isempty():
895 if self._dirs[dir]._isempty():
894 del self._dirs[dir]
896 del self._dirs[dir]
895 else:
897 else:
896 del self._files[f]
898 del self._files[f]
897 if f in self._flags:
899 if f in self._flags:
898 del self._flags[f]
900 del self._flags[f]
899 self._dirty = True
901 self._dirty = True
900
902
901 def __setitem__(self, f, n):
903 def __setitem__(self, f, n):
902 assert n is not None
904 assert n is not None
903 self._load()
905 self._load()
904 dir, subpath = _splittopdir(f)
906 dir, subpath = _splittopdir(f)
905 if dir:
907 if dir:
906 if dir in self._lazydirs:
908 if dir in self._lazydirs:
907 self._loadlazy(dir)
909 self._loadlazy(dir)
908 if dir not in self._dirs:
910 if dir not in self._dirs:
909 self._dirs[dir] = treemanifest(self._subpath(dir))
911 self._dirs[dir] = treemanifest(self._subpath(dir))
910 self._dirs[dir].__setitem__(subpath, n)
912 self._dirs[dir].__setitem__(subpath, n)
911 else:
913 else:
912 self._files[f] = n[:21] # to match manifestdict's behavior
914 self._files[f] = n[:21] # to match manifestdict's behavior
913 self._dirty = True
915 self._dirty = True
914
916
915 def _load(self):
917 def _load(self):
916 if self._loadfunc is not _noop:
918 if self._loadfunc is not _noop:
917 lf, self._loadfunc = self._loadfunc, _noop
919 lf, self._loadfunc = self._loadfunc, _noop
918 lf(self)
920 lf(self)
919 elif self._copyfunc is not _noop:
921 elif self._copyfunc is not _noop:
920 cf, self._copyfunc = self._copyfunc, _noop
922 cf, self._copyfunc = self._copyfunc, _noop
921 cf(self)
923 cf(self)
922
924
923 def setflag(self, f, flags):
925 def setflag(self, f, flags):
924 """Set the flags (symlink, executable) for path f."""
926 """Set the flags (symlink, executable) for path f."""
925 self._load()
927 self._load()
926 dir, subpath = _splittopdir(f)
928 dir, subpath = _splittopdir(f)
927 if dir:
929 if dir:
928 if dir in self._lazydirs:
930 if dir in self._lazydirs:
929 self._loadlazy(dir)
931 self._loadlazy(dir)
930 if dir not in self._dirs:
932 if dir not in self._dirs:
931 self._dirs[dir] = treemanifest(self._subpath(dir))
933 self._dirs[dir] = treemanifest(self._subpath(dir))
932 self._dirs[dir].setflag(subpath, flags)
934 self._dirs[dir].setflag(subpath, flags)
933 else:
935 else:
934 self._flags[f] = flags
936 self._flags[f] = flags
935 self._dirty = True
937 self._dirty = True
936
938
937 def copy(self):
939 def copy(self):
938 copy = treemanifest(self._dir)
940 copy = treemanifest(self._dir)
939 copy._node = self._node
941 copy._node = self._node
940 copy._dirty = self._dirty
942 copy._dirty = self._dirty
941 if self._copyfunc is _noop:
943 if self._copyfunc is _noop:
942 def _copyfunc(s):
944 def _copyfunc(s):
943 self._load()
945 self._load()
944 # OPT: it'd be nice to not load everything here. Unfortunately
946 # OPT: it'd be nice to not load everything here. Unfortunately
945 # this makes a mess of the "dirty" state tracking if we don't.
947 # this makes a mess of the "dirty" state tracking if we don't.
946 self._loadalllazy()
948 self._loadalllazy()
947 sdirs = s._dirs
949 sdirs = s._dirs
948 for d, v in self._dirs.iteritems():
950 for d, v in self._dirs.iteritems():
949 sdirs[d] = v.copy()
951 sdirs[d] = v.copy()
950 s._files = dict.copy(self._files)
952 s._files = dict.copy(self._files)
951 s._flags = dict.copy(self._flags)
953 s._flags = dict.copy(self._flags)
952 if self._loadfunc is _noop:
954 if self._loadfunc is _noop:
953 _copyfunc(copy)
955 _copyfunc(copy)
954 else:
956 else:
955 copy._copyfunc = _copyfunc
957 copy._copyfunc = _copyfunc
956 else:
958 else:
957 copy._copyfunc = self._copyfunc
959 copy._copyfunc = self._copyfunc
958 return copy
960 return copy
959
961
960 def filesnotin(self, m2, match=None):
962 def filesnotin(self, m2, match=None):
961 '''Set of files in this manifest that are not in the other'''
963 '''Set of files in this manifest that are not in the other'''
962 if match and not match.always():
964 if match and not match.always():
963 m1 = self.matches(match)
965 m1 = self.matches(match)
964 m2 = m2.matches(match)
966 m2 = m2.matches(match)
965 return m1.filesnotin(m2)
967 return m1.filesnotin(m2)
966
968
967 files = set()
969 files = set()
968 def _filesnotin(t1, t2):
970 def _filesnotin(t1, t2):
969 if t1._node == t2._node and not t1._dirty and not t2._dirty:
971 if t1._node == t2._node and not t1._dirty and not t2._dirty:
970 return
972 return
971 t1._load()
973 t1._load()
972 t2._load()
974 t2._load()
973 t1._loadalllazy()
975 t1._loadalllazy()
974 t2._loadalllazy()
976 t2._loadalllazy()
975 for d, m1 in t1._dirs.iteritems():
977 for d, m1 in t1._dirs.iteritems():
976 if d in t2._dirs:
978 if d in t2._dirs:
977 m2 = t2._dirs[d]
979 m2 = t2._dirs[d]
978 _filesnotin(m1, m2)
980 _filesnotin(m1, m2)
979 else:
981 else:
980 files.update(m1.iterkeys())
982 files.update(m1.iterkeys())
981
983
982 for fn in t1._files:
984 for fn in t1._files:
983 if fn not in t2._files:
985 if fn not in t2._files:
984 files.add(t1._subpath(fn))
986 files.add(t1._subpath(fn))
985
987
986 _filesnotin(self, m2)
988 _filesnotin(self, m2)
987 return files
989 return files
988
990
989 @propertycache
991 @propertycache
990 def _alldirs(self):
992 def _alldirs(self):
991 return util.dirs(self)
993 return util.dirs(self)
992
994
993 def dirs(self):
995 def dirs(self):
994 return self._alldirs
996 return self._alldirs
995
997
996 def hasdir(self, dir):
998 def hasdir(self, dir):
997 self._load()
999 self._load()
998 topdir, subdir = _splittopdir(dir)
1000 topdir, subdir = _splittopdir(dir)
999 if topdir:
1001 if topdir:
1000 if topdir in self._lazydirs:
1002 if topdir in self._lazydirs:
1001 self._loadlazy(topdir)
1003 self._loadlazy(topdir)
1002 if topdir in self._dirs:
1004 if topdir in self._dirs:
1003 return self._dirs[topdir].hasdir(subdir)
1005 return self._dirs[topdir].hasdir(subdir)
1004 return False
1006 return False
1005 dirslash = dir + '/'
1007 dirslash = dir + '/'
1006 return dirslash in self._dirs or dirslash in self._lazydirs
1008 return dirslash in self._dirs or dirslash in self._lazydirs
1007
1009
1008 def walk(self, match):
1010 def walk(self, match):
1009 '''Generates matching file names.
1011 '''Generates matching file names.
1010
1012
1011 Equivalent to manifest.matches(match).iterkeys(), but without creating
1013 Equivalent to manifest.matches(match).iterkeys(), but without creating
1012 an entirely new manifest.
1014 an entirely new manifest.
1013
1015
1014 It also reports nonexistent files by marking them bad with match.bad().
1016 It also reports nonexistent files by marking them bad with match.bad().
1015 '''
1017 '''
1016 if match.always():
1018 if match.always():
1017 for f in iter(self):
1019 for f in iter(self):
1018 yield f
1020 yield f
1019 return
1021 return
1020
1022
1021 fset = set(match.files())
1023 fset = set(match.files())
1022
1024
1023 for fn in self._walk(match):
1025 for fn in self._walk(match):
1024 if fn in fset:
1026 if fn in fset:
1025 # specified pattern is the exact name
1027 # specified pattern is the exact name
1026 fset.remove(fn)
1028 fset.remove(fn)
1027 yield fn
1029 yield fn
1028
1030
1029 # for dirstate.walk, files=['.'] means "walk the whole tree".
1031 # for dirstate.walk, files=['.'] means "walk the whole tree".
1030 # follow that here, too
1032 # follow that here, too
1031 fset.discard('.')
1033 fset.discard('.')
1032
1034
1033 for fn in sorted(fset):
1035 for fn in sorted(fset):
1034 if not self.hasdir(fn):
1036 if not self.hasdir(fn):
1035 match.bad(fn, None)
1037 match.bad(fn, None)
1036
1038
1037 def _walk(self, match):
1039 def _walk(self, match):
1038 '''Recursively generates matching file names for walk().'''
1040 '''Recursively generates matching file names for walk().'''
1039 visit = match.visitchildrenset(self._dir[:-1] or '.')
1041 visit = match.visitchildrenset(self._dir[:-1] or '.')
1040 if not visit:
1042 if not visit:
1041 return
1043 return
1042
1044
1043 # yield this dir's files and walk its submanifests
1045 # yield this dir's files and walk its submanifests
1044 self._load()
1046 self._load()
1045 visit = self._loadchildrensetlazy(visit)
1047 visit = self._loadchildrensetlazy(visit)
1046 for p in sorted(list(self._dirs) + list(self._files)):
1048 for p in sorted(list(self._dirs) + list(self._files)):
1047 if p in self._files:
1049 if p in self._files:
1048 fullp = self._subpath(p)
1050 fullp = self._subpath(p)
1049 if match(fullp):
1051 if match(fullp):
1050 yield fullp
1052 yield fullp
1051 else:
1053 else:
1052 if not visit or p[:-1] in visit:
1054 if not visit or p[:-1] in visit:
1053 for f in self._dirs[p]._walk(match):
1055 for f in self._dirs[p]._walk(match):
1054 yield f
1056 yield f
1055
1057
1056 def matches(self, match):
1058 def matches(self, match):
1057 '''generate a new manifest filtered by the match argument'''
1059 '''generate a new manifest filtered by the match argument'''
1058 if match.always():
1060 if match.always():
1059 return self.copy()
1061 return self.copy()
1060
1062
1061 return self._matches(match)
1063 return self._matches(match)
1062
1064
1063 def _matches(self, match):
1065 def _matches(self, match):
1064 '''recursively generate a new manifest filtered by the match argument.
1066 '''recursively generate a new manifest filtered by the match argument.
1065 '''
1067 '''
1066
1068
1067 visit = match.visitchildrenset(self._dir[:-1] or '.')
1069 visit = match.visitchildrenset(self._dir[:-1] or '.')
1068 if visit == 'all':
1070 if visit == 'all':
1069 return self.copy()
1071 return self.copy()
1070 ret = treemanifest(self._dir)
1072 ret = treemanifest(self._dir)
1071 if not visit:
1073 if not visit:
1072 return ret
1074 return ret
1073
1075
1074 self._load()
1076 self._load()
1075 for fn in self._files:
1077 for fn in self._files:
1076 # While visitchildrenset *usually* lists only subdirs, this is
1078 # While visitchildrenset *usually* lists only subdirs, this is
1077 # actually up to the matcher and may have some files in the set().
1079 # actually up to the matcher and may have some files in the set().
1078 # If visit == 'this', we should obviously look at the files in this
1080 # If visit == 'this', we should obviously look at the files in this
1079 # directory; if visit is a set, and fn is in it, we should inspect
1081 # directory; if visit is a set, and fn is in it, we should inspect
1080 # fn (but no need to inspect things not in the set).
1082 # fn (but no need to inspect things not in the set).
1081 if visit != 'this' and fn not in visit:
1083 if visit != 'this' and fn not in visit:
1082 continue
1084 continue
1083 fullp = self._subpath(fn)
1085 fullp = self._subpath(fn)
1084 # visitchildrenset isn't perfect, we still need to call the regular
1086 # visitchildrenset isn't perfect, we still need to call the regular
1085 # matcher code to further filter results.
1087 # matcher code to further filter results.
1086 if not match(fullp):
1088 if not match(fullp):
1087 continue
1089 continue
1088 ret._files[fn] = self._files[fn]
1090 ret._files[fn] = self._files[fn]
1089 if fn in self._flags:
1091 if fn in self._flags:
1090 ret._flags[fn] = self._flags[fn]
1092 ret._flags[fn] = self._flags[fn]
1091
1093
1092 visit = self._loadchildrensetlazy(visit)
1094 visit = self._loadchildrensetlazy(visit)
1093 for dir, subm in self._dirs.iteritems():
1095 for dir, subm in self._dirs.iteritems():
1094 if visit and dir[:-1] not in visit:
1096 if visit and dir[:-1] not in visit:
1095 continue
1097 continue
1096 m = subm._matches(match)
1098 m = subm._matches(match)
1097 if not m._isempty():
1099 if not m._isempty():
1098 ret._dirs[dir] = m
1100 ret._dirs[dir] = m
1099
1101
1100 if not ret._isempty():
1102 if not ret._isempty():
1101 ret._dirty = True
1103 ret._dirty = True
1102 return ret
1104 return ret
1103
1105
1104 def diff(self, m2, match=None, clean=False):
1106 def diff(self, m2, match=None, clean=False):
1105 '''Finds changes between the current manifest and m2.
1107 '''Finds changes between the current manifest and m2.
1106
1108
1107 Args:
1109 Args:
1108 m2: the manifest to which this manifest should be compared.
1110 m2: the manifest to which this manifest should be compared.
1109 clean: if true, include files unchanged between these manifests
1111 clean: if true, include files unchanged between these manifests
1110 with a None value in the returned dictionary.
1112 with a None value in the returned dictionary.
1111
1113
1112 The result is returned as a dict with filename as key and
1114 The result is returned as a dict with filename as key and
1113 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1115 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1114 nodeid in the current/other manifest and fl1/fl2 is the flag
1116 nodeid in the current/other manifest and fl1/fl2 is the flag
1115 in the current/other manifest. Where the file does not exist,
1117 in the current/other manifest. Where the file does not exist,
1116 the nodeid will be None and the flags will be the empty
1118 the nodeid will be None and the flags will be the empty
1117 string.
1119 string.
1118 '''
1120 '''
1119 if match and not match.always():
1121 if match and not match.always():
1120 m1 = self.matches(match)
1122 m1 = self.matches(match)
1121 m2 = m2.matches(match)
1123 m2 = m2.matches(match)
1122 return m1.diff(m2, clean=clean)
1124 return m1.diff(m2, clean=clean)
1123 result = {}
1125 result = {}
1124 emptytree = treemanifest()
1126 emptytree = treemanifest()
1125 def _diff(t1, t2):
1127 def _diff(t1, t2):
1126 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1128 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1127 return
1129 return
1128 t1._load()
1130 t1._load()
1129 t2._load()
1131 t2._load()
1130 # OPT: do we need to load everything?
1132 # OPT: do we need to load everything?
1131 t1._loadalllazy()
1133 t1._loadalllazy()
1132 t2._loadalllazy()
1134 t2._loadalllazy()
1133 for d, m1 in t1._dirs.iteritems():
1135 for d, m1 in t1._dirs.iteritems():
1134 m2 = t2._dirs.get(d, emptytree)
1136 m2 = t2._dirs.get(d, emptytree)
1135 _diff(m1, m2)
1137 _diff(m1, m2)
1136
1138
1137 for d, m2 in t2._dirs.iteritems():
1139 for d, m2 in t2._dirs.iteritems():
1138 if d not in t1._dirs:
1140 if d not in t1._dirs:
1139 _diff(emptytree, m2)
1141 _diff(emptytree, m2)
1140
1142
1141 for fn, n1 in t1._files.iteritems():
1143 for fn, n1 in t1._files.iteritems():
1142 fl1 = t1._flags.get(fn, '')
1144 fl1 = t1._flags.get(fn, '')
1143 n2 = t2._files.get(fn, None)
1145 n2 = t2._files.get(fn, None)
1144 fl2 = t2._flags.get(fn, '')
1146 fl2 = t2._flags.get(fn, '')
1145 if n1 != n2 or fl1 != fl2:
1147 if n1 != n2 or fl1 != fl2:
1146 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1148 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1147 elif clean:
1149 elif clean:
1148 result[t1._subpath(fn)] = None
1150 result[t1._subpath(fn)] = None
1149
1151
1150 for fn, n2 in t2._files.iteritems():
1152 for fn, n2 in t2._files.iteritems():
1151 if fn not in t1._files:
1153 if fn not in t1._files:
1152 fl2 = t2._flags.get(fn, '')
1154 fl2 = t2._flags.get(fn, '')
1153 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1155 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1154
1156
1155 _diff(self, m2)
1157 _diff(self, m2)
1156 return result
1158 return result
1157
1159
1158 def unmodifiedsince(self, m2):
1160 def unmodifiedsince(self, m2):
1159 return not self._dirty and not m2._dirty and self._node == m2._node
1161 return not self._dirty and not m2._dirty and self._node == m2._node
1160
1162
1161 def parse(self, text, readsubtree):
1163 def parse(self, text, readsubtree):
1162 selflazy = self._lazydirs
1164 selflazy = self._lazydirs
1163 subpath = self._subpath
1165 subpath = self._subpath
1164 for f, n, fl in _parse(text):
1166 for f, n, fl in _parse(text):
1165 if fl == 't':
1167 if fl == 't':
1166 f = f + '/'
1168 f = f + '/'
1167 selflazy[f] = (subpath(f), n, readsubtree)
1169 selflazy[f] = (subpath(f), n, readsubtree)
1168 elif '/' in f:
1170 elif '/' in f:
1169 # This is a flat manifest, so use __setitem__ and setflag rather
1171 # This is a flat manifest, so use __setitem__ and setflag rather
1170 # than assigning directly to _files and _flags, so we can
1172 # than assigning directly to _files and _flags, so we can
1171 # assign a path in a subdirectory, and to mark dirty (compared
1173 # assign a path in a subdirectory, and to mark dirty (compared
1172 # to nullid).
1174 # to nullid).
1173 self[f] = n
1175 self[f] = n
1174 if fl:
1176 if fl:
1175 self.setflag(f, fl)
1177 self.setflag(f, fl)
1176 else:
1178 else:
1177 # Assigning to _files and _flags avoids marking as dirty,
1179 # Assigning to _files and _flags avoids marking as dirty,
1178 # and should be a little faster.
1180 # and should be a little faster.
1179 self._files[f] = n
1181 self._files[f] = n
1180 if fl:
1182 if fl:
1181 self._flags[f] = fl
1183 self._flags[f] = fl
1182
1184
1183 def text(self):
1185 def text(self):
1184 """Get the full data of this manifest as a bytestring."""
1186 """Get the full data of this manifest as a bytestring."""
1185 self._load()
1187 self._load()
1186 return _text(self.iterentries())
1188 return _text(self.iterentries())
1187
1189
1188 def dirtext(self):
1190 def dirtext(self):
1189 """Get the full data of this directory as a bytestring. Make sure that
1191 """Get the full data of this directory as a bytestring. Make sure that
1190 any submanifests have been written first, so their nodeids are correct.
1192 any submanifests have been written first, so their nodeids are correct.
1191 """
1193 """
1192 self._load()
1194 self._load()
1193 flags = self.flags
1195 flags = self.flags
1194 lazydirs = [(d[:-1], node, 't') for
1196 lazydirs = [(d[:-1], node, 't') for
1195 d, (path, node, readsubtree) in self._lazydirs.iteritems()]
1197 d, (path, node, readsubtree) in self._lazydirs.iteritems()]
1196 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1198 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1197 files = [(f, self._files[f], flags(f)) for f in self._files]
1199 files = [(f, self._files[f], flags(f)) for f in self._files]
1198 return _text(sorted(dirs + files + lazydirs))
1200 return _text(sorted(dirs + files + lazydirs))
1199
1201
1200 def read(self, gettext, readsubtree):
1202 def read(self, gettext, readsubtree):
1201 def _load_for_read(s):
1203 def _load_for_read(s):
1202 s.parse(gettext(), readsubtree)
1204 s.parse(gettext(), readsubtree)
1203 s._dirty = False
1205 s._dirty = False
1204 self._loadfunc = _load_for_read
1206 self._loadfunc = _load_for_read
1205
1207
1206 def writesubtrees(self, m1, m2, writesubtree, match):
1208 def writesubtrees(self, m1, m2, writesubtree, match):
1207 self._load() # for consistency; should never have any effect here
1209 self._load() # for consistency; should never have any effect here
1208 m1._load()
1210 m1._load()
1209 m2._load()
1211 m2._load()
1210 emptytree = treemanifest()
1212 emptytree = treemanifest()
1211 def getnode(m, d):
1213 def getnode(m, d):
1212 ld = m._lazydirs.get(d)
1214 ld = m._lazydirs.get(d)
1213 if ld:
1215 if ld:
1214 return ld[1]
1216 return ld[1]
1215 return m._dirs.get(d, emptytree)._node
1217 return m._dirs.get(d, emptytree)._node
1216
1218
1217 # we should have always loaded everything by the time we get here for
1219 # we should have always loaded everything by the time we get here for
1218 # `self`, but possibly not in `m1` or `m2`.
1220 # `self`, but possibly not in `m1` or `m2`.
1219 assert not self._lazydirs
1221 assert not self._lazydirs
1220 # let's skip investigating things that `match` says we do not need.
1222 # let's skip investigating things that `match` says we do not need.
1221 visit = match.visitchildrenset(self._dir[:-1] or '.')
1223 visit = match.visitchildrenset(self._dir[:-1] or '.')
1222 if visit == 'this' or visit == 'all':
1224 if visit == 'this' or visit == 'all':
1223 visit = None
1225 visit = None
1224 for d, subm in self._dirs.iteritems():
1226 for d, subm in self._dirs.iteritems():
1225 if visit and d[:-1] not in visit:
1227 if visit and d[:-1] not in visit:
1226 continue
1228 continue
1227 subp1 = getnode(m1, d)
1229 subp1 = getnode(m1, d)
1228 subp2 = getnode(m2, d)
1230 subp2 = getnode(m2, d)
1229 if subp1 == nullid:
1231 if subp1 == nullid:
1230 subp1, subp2 = subp2, subp1
1232 subp1, subp2 = subp2, subp1
1231 writesubtree(subm, subp1, subp2, match)
1233 writesubtree(subm, subp1, subp2, match)
1232
1234
1233 def walksubtrees(self, matcher=None):
1235 def walksubtrees(self, matcher=None):
1234 """Returns an iterator of the subtrees of this manifest, including this
1236 """Returns an iterator of the subtrees of this manifest, including this
1235 manifest itself.
1237 manifest itself.
1236
1238
1237 If `matcher` is provided, it only returns subtrees that match.
1239 If `matcher` is provided, it only returns subtrees that match.
1238 """
1240 """
1239 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1241 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1240 return
1242 return
1241 if not matcher or matcher(self._dir[:-1]):
1243 if not matcher or matcher(self._dir[:-1]):
1242 yield self
1244 yield self
1243
1245
1244 self._load()
1246 self._load()
1245 # OPT: use visitchildrenset to avoid loading everything.
1247 # OPT: use visitchildrenset to avoid loading everything.
1246 self._loadalllazy()
1248 self._loadalllazy()
1247 for d, subm in self._dirs.iteritems():
1249 for d, subm in self._dirs.iteritems():
1248 for subtree in subm.walksubtrees(matcher=matcher):
1250 for subtree in subm.walksubtrees(matcher=matcher):
1249 yield subtree
1251 yield subtree
1250
1252
1251 class manifestfulltextcache(util.lrucachedict):
1253 class manifestfulltextcache(util.lrucachedict):
1252 """File-backed LRU cache for the manifest cache
1254 """File-backed LRU cache for the manifest cache
1253
1255
1254 File consists of entries, up to EOF:
1256 File consists of entries, up to EOF:
1255
1257
1256 - 20 bytes node, 4 bytes length, <length> manifest data
1258 - 20 bytes node, 4 bytes length, <length> manifest data
1257
1259
1258 These are written in reverse cache order (oldest to newest).
1260 These are written in reverse cache order (oldest to newest).
1259
1261
1260 """
1262 """
1261 def __init__(self, max):
1263 def __init__(self, max):
1262 super(manifestfulltextcache, self).__init__(max)
1264 super(manifestfulltextcache, self).__init__(max)
1263 self._dirty = False
1265 self._dirty = False
1264 self._read = False
1266 self._read = False
1265 self._opener = None
1267 self._opener = None
1266
1268
1267 def read(self):
1269 def read(self):
1268 if self._read or self._opener is None:
1270 if self._read or self._opener is None:
1269 return
1271 return
1270
1272
1271 try:
1273 try:
1272 with self._opener('manifestfulltextcache') as fp:
1274 with self._opener('manifestfulltextcache') as fp:
1273 set = super(manifestfulltextcache, self).__setitem__
1275 set = super(manifestfulltextcache, self).__setitem__
1274 # ignore trailing data, this is a cache, corruption is skipped
1276 # ignore trailing data, this is a cache, corruption is skipped
1275 while True:
1277 while True:
1276 node = fp.read(20)
1278 node = fp.read(20)
1277 if len(node) < 20:
1279 if len(node) < 20:
1278 break
1280 break
1279 try:
1281 try:
1280 size = struct.unpack('>L', fp.read(4))[0]
1282 size = struct.unpack('>L', fp.read(4))[0]
1281 except struct.error:
1283 except struct.error:
1282 break
1284 break
1283 value = bytearray(fp.read(size))
1285 value = bytearray(fp.read(size))
1284 if len(value) != size:
1286 if len(value) != size:
1285 break
1287 break
1286 set(node, value)
1288 set(node, value)
1287 except IOError:
1289 except IOError:
1288 # the file is allowed to be missing
1290 # the file is allowed to be missing
1289 pass
1291 pass
1290
1292
1291 self._read = True
1293 self._read = True
1292 self._dirty = False
1294 self._dirty = False
1293
1295
1294 def write(self):
1296 def write(self):
1295 if not self._dirty or self._opener is None:
1297 if not self._dirty or self._opener is None:
1296 return
1298 return
1297 # rotate backwards to the first used node
1299 # rotate backwards to the first used node
1298 with self._opener(
1300 with self._opener(
1299 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1301 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1300 ) as fp:
1302 ) as fp:
1301 node = self._head.prev
1303 node = self._head.prev
1302 while True:
1304 while True:
1303 if node.key in self._cache:
1305 if node.key in self._cache:
1304 fp.write(node.key)
1306 fp.write(node.key)
1305 fp.write(struct.pack('>L', len(node.value)))
1307 fp.write(struct.pack('>L', len(node.value)))
1306 fp.write(node.value)
1308 fp.write(node.value)
1307 if node is self._head:
1309 if node is self._head:
1308 break
1310 break
1309 node = node.prev
1311 node = node.prev
1310
1312
1311 def __len__(self):
1313 def __len__(self):
1312 if not self._read:
1314 if not self._read:
1313 self.read()
1315 self.read()
1314 return super(manifestfulltextcache, self).__len__()
1316 return super(manifestfulltextcache, self).__len__()
1315
1317
1316 def __contains__(self, k):
1318 def __contains__(self, k):
1317 if not self._read:
1319 if not self._read:
1318 self.read()
1320 self.read()
1319 return super(manifestfulltextcache, self).__contains__(k)
1321 return super(manifestfulltextcache, self).__contains__(k)
1320
1322
1321 def __iter__(self):
1323 def __iter__(self):
1322 if not self._read:
1324 if not self._read:
1323 self.read()
1325 self.read()
1324 return super(manifestfulltextcache, self).__iter__()
1326 return super(manifestfulltextcache, self).__iter__()
1325
1327
1326 def __getitem__(self, k):
1328 def __getitem__(self, k):
1327 if not self._read:
1329 if not self._read:
1328 self.read()
1330 self.read()
1329 # the cache lru order can change on read
1331 # the cache lru order can change on read
1330 setdirty = self._cache.get(k) is not self._head
1332 setdirty = self._cache.get(k) is not self._head
1331 value = super(manifestfulltextcache, self).__getitem__(k)
1333 value = super(manifestfulltextcache, self).__getitem__(k)
1332 if setdirty:
1334 if setdirty:
1333 self._dirty = True
1335 self._dirty = True
1334 return value
1336 return value
1335
1337
1336 def __setitem__(self, k, v):
1338 def __setitem__(self, k, v):
1337 if not self._read:
1339 if not self._read:
1338 self.read()
1340 self.read()
1339 super(manifestfulltextcache, self).__setitem__(k, v)
1341 super(manifestfulltextcache, self).__setitem__(k, v)
1340 self._dirty = True
1342 self._dirty = True
1341
1343
1342 def __delitem__(self, k):
1344 def __delitem__(self, k):
1343 if not self._read:
1345 if not self._read:
1344 self.read()
1346 self.read()
1345 super(manifestfulltextcache, self).__delitem__(k)
1347 super(manifestfulltextcache, self).__delitem__(k)
1346 self._dirty = True
1348 self._dirty = True
1347
1349
1348 def get(self, k, default=None):
1350 def get(self, k, default=None):
1349 if not self._read:
1351 if not self._read:
1350 self.read()
1352 self.read()
1351 return super(manifestfulltextcache, self).get(k, default=default)
1353 return super(manifestfulltextcache, self).get(k, default=default)
1352
1354
1353 def clear(self, clear_persisted_data=False):
1355 def clear(self, clear_persisted_data=False):
1354 super(manifestfulltextcache, self).clear()
1356 super(manifestfulltextcache, self).clear()
1355 if clear_persisted_data:
1357 if clear_persisted_data:
1356 self._dirty = True
1358 self._dirty = True
1357 self.write()
1359 self.write()
1358 self._read = False
1360 self._read = False
1359
1361
1360 @interfaceutil.implementer(repository.imanifeststorage)
1362 @interfaceutil.implementer(repository.imanifeststorage)
1361 class manifestrevlog(object):
1363 class manifestrevlog(object):
1362 '''A revlog that stores manifest texts. This is responsible for caching the
1364 '''A revlog that stores manifest texts. This is responsible for caching the
1363 full-text manifest contents.
1365 full-text manifest contents.
1364 '''
1366 '''
1365 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1367 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1366 treemanifest=False):
1368 treemanifest=False):
1367 """Constructs a new manifest revlog
1369 """Constructs a new manifest revlog
1368
1370
1369 `indexfile` - used by extensions to have two manifests at once, like
1371 `indexfile` - used by extensions to have two manifests at once, like
1370 when transitioning between flatmanifeset and treemanifests.
1372 when transitioning between flatmanifeset and treemanifests.
1371
1373
1372 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1374 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1373 options can also be used to make this a tree manifest revlog. The opener
1375 options can also be used to make this a tree manifest revlog. The opener
1374 option takes precedence, so if it is set to True, we ignore whatever
1376 option takes precedence, so if it is set to True, we ignore whatever
1375 value is passed in to the constructor.
1377 value is passed in to the constructor.
1376 """
1378 """
1377 # During normal operations, we expect to deal with not more than four
1379 # During normal operations, we expect to deal with not more than four
1378 # revs at a time (such as during commit --amend). When rebasing large
1380 # revs at a time (such as during commit --amend). When rebasing large
1379 # stacks of commits, the number can go up, hence the config knob below.
1381 # stacks of commits, the number can go up, hence the config knob below.
1380 cachesize = 4
1382 cachesize = 4
1381 optiontreemanifest = False
1383 optiontreemanifest = False
1382 opts = getattr(opener, 'options', None)
1384 opts = getattr(opener, 'options', None)
1383 if opts is not None:
1385 if opts is not None:
1384 cachesize = opts.get('manifestcachesize', cachesize)
1386 cachesize = opts.get('manifestcachesize', cachesize)
1385 optiontreemanifest = opts.get('treemanifest', False)
1387 optiontreemanifest = opts.get('treemanifest', False)
1386
1388
1387 self._treeondisk = optiontreemanifest or treemanifest
1389 self._treeondisk = optiontreemanifest or treemanifest
1388
1390
1389 self._fulltextcache = manifestfulltextcache(cachesize)
1391 self._fulltextcache = manifestfulltextcache(cachesize)
1390
1392
1391 if tree:
1393 if tree:
1392 assert self._treeondisk, 'opts is %r' % opts
1394 assert self._treeondisk, 'opts is %r' % opts
1393
1395
1394 if indexfile is None:
1396 if indexfile is None:
1395 indexfile = '00manifest.i'
1397 indexfile = '00manifest.i'
1396 if tree:
1398 if tree:
1397 indexfile = "meta/" + tree + indexfile
1399 indexfile = "meta/" + tree + indexfile
1398
1400
1399 self.tree = tree
1401 self.tree = tree
1400
1402
1401 # The dirlogcache is kept on the root manifest log
1403 # The dirlogcache is kept on the root manifest log
1402 if tree:
1404 if tree:
1403 self._dirlogcache = dirlogcache
1405 self._dirlogcache = dirlogcache
1404 else:
1406 else:
1405 self._dirlogcache = {'': self}
1407 self._dirlogcache = {'': self}
1406
1408
1407 self._revlog = revlog.revlog(opener, indexfile,
1409 self._revlog = revlog.revlog(opener, indexfile,
1408 # only root indexfile is cached
1410 # only root indexfile is cached
1409 checkambig=not bool(tree),
1411 checkambig=not bool(tree),
1410 mmaplargeindex=True)
1412 mmaplargeindex=True)
1411
1413
1412 self.index = self._revlog.index
1414 self.index = self._revlog.index
1413 self.version = self._revlog.version
1415 self.version = self._revlog.version
1414 self._generaldelta = self._revlog._generaldelta
1416 self._generaldelta = self._revlog._generaldelta
1415
1417
1416 def _setupmanifestcachehooks(self, repo):
1418 def _setupmanifestcachehooks(self, repo):
1417 """Persist the manifestfulltextcache on lock release"""
1419 """Persist the manifestfulltextcache on lock release"""
1418 if not util.safehasattr(repo, '_lockref'):
1420 if not util.safehasattr(repo, '_lockref'):
1419 return
1421 return
1420
1422
1421 self._fulltextcache._opener = repo.cachevfs
1423 self._fulltextcache._opener = repo.cachevfs
1422 reporef = weakref.ref(repo)
1424 reporef = weakref.ref(repo)
1423 manifestrevlogref = weakref.ref(self)
1425 manifestrevlogref = weakref.ref(self)
1424
1426
1425 def persistmanifestcache():
1427 def persistmanifestcache():
1426 repo = reporef()
1428 repo = reporef()
1427 self = manifestrevlogref()
1429 self = manifestrevlogref()
1428 if repo is None or self is None:
1430 if repo is None or self is None:
1429 return
1431 return
1430 if repo.manifestlog.getstorage(b'') is not self:
1432 if repo.manifestlog.getstorage(b'') is not self:
1431 # there's a different manifest in play now, abort
1433 # there's a different manifest in play now, abort
1432 return
1434 return
1433 self._fulltextcache.write()
1435 self._fulltextcache.write()
1434
1436
1435 if repo._currentlock(repo._lockref) is not None:
1437 if repo._currentlock(repo._lockref) is not None:
1436 repo._afterlock(persistmanifestcache)
1438 repo._afterlock(persistmanifestcache)
1437
1439
1438 @property
1440 @property
1439 def fulltextcache(self):
1441 def fulltextcache(self):
1440 return self._fulltextcache
1442 return self._fulltextcache
1441
1443
1442 def clearcaches(self, clear_persisted_data=False):
1444 def clearcaches(self, clear_persisted_data=False):
1443 self._revlog.clearcaches()
1445 self._revlog.clearcaches()
1444 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1446 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1445 self._dirlogcache = {self.tree: self}
1447 self._dirlogcache = {self.tree: self}
1446
1448
1447 def dirlog(self, d):
1449 def dirlog(self, d):
1448 if d:
1450 if d:
1449 assert self._treeondisk
1451 assert self._treeondisk
1450 if d not in self._dirlogcache:
1452 if d not in self._dirlogcache:
1451 mfrevlog = manifestrevlog(self.opener, d,
1453 mfrevlog = manifestrevlog(self.opener, d,
1452 self._dirlogcache,
1454 self._dirlogcache,
1453 treemanifest=self._treeondisk)
1455 treemanifest=self._treeondisk)
1454 self._dirlogcache[d] = mfrevlog
1456 self._dirlogcache[d] = mfrevlog
1455 return self._dirlogcache[d]
1457 return self._dirlogcache[d]
1456
1458
1457 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None,
1459 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None,
1458 match=None):
1460 match=None):
1459 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1461 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1460 # If our first parent is in the manifest cache, we can
1462 # If our first parent is in the manifest cache, we can
1461 # compute a delta here using properties we know about the
1463 # compute a delta here using properties we know about the
1462 # manifest up-front, which may save time later for the
1464 # manifest up-front, which may save time later for the
1463 # revlog layer.
1465 # revlog layer.
1464
1466
1465 _checkforbidden(added)
1467 _checkforbidden(added)
1466 # combine the changed lists into one sorted iterator
1468 # combine the changed lists into one sorted iterator
1467 work = heapq.merge([(x, False) for x in added],
1469 work = heapq.merge([(x, False) for x in added],
1468 [(x, True) for x in removed])
1470 [(x, True) for x in removed])
1469
1471
1470 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1472 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1471 cachedelta = self._revlog.rev(p1), deltatext
1473 cachedelta = self._revlog.rev(p1), deltatext
1472 text = util.buffer(arraytext)
1474 text = util.buffer(arraytext)
1473 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1475 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1474 cachedelta)
1476 cachedelta)
1475 else:
1477 else:
1476 # The first parent manifest isn't already loaded, so we'll
1478 # The first parent manifest isn't already loaded, so we'll
1477 # just encode a fulltext of the manifest and pass that
1479 # just encode a fulltext of the manifest and pass that
1478 # through to the revlog layer, and let it handle the delta
1480 # through to the revlog layer, and let it handle the delta
1479 # process.
1481 # process.
1480 if self._treeondisk:
1482 if self._treeondisk:
1481 assert readtree, "readtree must be set for treemanifest writes"
1483 assert readtree, "readtree must be set for treemanifest writes"
1482 assert match, "match must be specified for treemanifest writes"
1484 assert match, "match must be specified for treemanifest writes"
1483 m1 = readtree(self.tree, p1)
1485 m1 = readtree(self.tree, p1)
1484 m2 = readtree(self.tree, p2)
1486 m2 = readtree(self.tree, p2)
1485 n = self._addtree(m, transaction, link, m1, m2, readtree,
1487 n = self._addtree(m, transaction, link, m1, m2, readtree,
1486 match=match)
1488 match=match)
1487 arraytext = None
1489 arraytext = None
1488 else:
1490 else:
1489 text = m.text()
1491 text = m.text()
1490 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1492 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1491 arraytext = bytearray(text)
1493 arraytext = bytearray(text)
1492
1494
1493 if arraytext is not None:
1495 if arraytext is not None:
1494 self.fulltextcache[n] = arraytext
1496 self.fulltextcache[n] = arraytext
1495
1497
1496 return n
1498 return n
1497
1499
1498 def _addtree(self, m, transaction, link, m1, m2, readtree, match):
1500 def _addtree(self, m, transaction, link, m1, m2, readtree, match):
1499 # If the manifest is unchanged compared to one parent,
1501 # If the manifest is unchanged compared to one parent,
1500 # don't write a new revision
1502 # don't write a new revision
1501 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1503 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1502 m2)):
1504 m2)):
1503 return m.node()
1505 return m.node()
1504 def writesubtree(subm, subp1, subp2, match):
1506 def writesubtree(subm, subp1, subp2, match):
1505 sublog = self.dirlog(subm.dir())
1507 sublog = self.dirlog(subm.dir())
1506 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1508 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1507 readtree=readtree, match=match)
1509 readtree=readtree, match=match)
1508 m.writesubtrees(m1, m2, writesubtree, match)
1510 m.writesubtrees(m1, m2, writesubtree, match)
1509 text = m.dirtext()
1511 text = m.dirtext()
1510 n = None
1512 n = None
1511 if self.tree != '':
1513 if self.tree != '':
1512 # Double-check whether contents are unchanged to one parent
1514 # Double-check whether contents are unchanged to one parent
1513 if text == m1.dirtext():
1515 if text == m1.dirtext():
1514 n = m1.node()
1516 n = m1.node()
1515 elif text == m2.dirtext():
1517 elif text == m2.dirtext():
1516 n = m2.node()
1518 n = m2.node()
1517
1519
1518 if not n:
1520 if not n:
1519 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1521 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1520 m2.node())
1522 m2.node())
1521
1523
1522 # Save nodeid so parent manifest can calculate its nodeid
1524 # Save nodeid so parent manifest can calculate its nodeid
1523 m.setnode(n)
1525 m.setnode(n)
1524 return n
1526 return n
1525
1527
1526 def __len__(self):
1528 def __len__(self):
1527 return len(self._revlog)
1529 return len(self._revlog)
1528
1530
1529 def __iter__(self):
1531 def __iter__(self):
1530 return self._revlog.__iter__()
1532 return self._revlog.__iter__()
1531
1533
1532 def rev(self, node):
1534 def rev(self, node):
1533 return self._revlog.rev(node)
1535 return self._revlog.rev(node)
1534
1536
1535 def node(self, rev):
1537 def node(self, rev):
1536 return self._revlog.node(rev)
1538 return self._revlog.node(rev)
1537
1539
1538 def lookup(self, value):
1540 def lookup(self, value):
1539 return self._revlog.lookup(value)
1541 return self._revlog.lookup(value)
1540
1542
1541 def parentrevs(self, rev):
1543 def parentrevs(self, rev):
1542 return self._revlog.parentrevs(rev)
1544 return self._revlog.parentrevs(rev)
1543
1545
1544 def parents(self, node):
1546 def parents(self, node):
1545 return self._revlog.parents(node)
1547 return self._revlog.parents(node)
1546
1548
1547 def linkrev(self, rev):
1549 def linkrev(self, rev):
1548 return self._revlog.linkrev(rev)
1550 return self._revlog.linkrev(rev)
1549
1551
1550 def checksize(self):
1552 def checksize(self):
1551 return self._revlog.checksize()
1553 return self._revlog.checksize()
1552
1554
1553 def revision(self, node, _df=None, raw=False):
1555 def revision(self, node, _df=None, raw=False):
1554 return self._revlog.revision(node, _df=_df, raw=raw)
1556 return self._revlog.revision(node, _df=_df, raw=raw)
1555
1557
1556 def revdiff(self, rev1, rev2):
1558 def revdiff(self, rev1, rev2):
1557 return self._revlog.revdiff(rev1, rev2)
1559 return self._revlog.revdiff(rev1, rev2)
1558
1560
1559 def cmp(self, node, text):
1561 def cmp(self, node, text):
1560 return self._revlog.cmp(node, text)
1562 return self._revlog.cmp(node, text)
1561
1563
1562 def deltaparent(self, rev):
1564 def deltaparent(self, rev):
1563 return self._revlog.deltaparent(rev)
1565 return self._revlog.deltaparent(rev)
1564
1566
1565 def emitrevisions(self, nodes, nodesorder=None,
1567 def emitrevisions(self, nodes, nodesorder=None,
1566 revisiondata=False, assumehaveparentrevisions=False,
1568 revisiondata=False, assumehaveparentrevisions=False,
1567 deltaprevious=False):
1569 deltaprevious=False):
1568 return self._revlog.emitrevisions(
1570 return self._revlog.emitrevisions(
1569 nodes, nodesorder=nodesorder, revisiondata=revisiondata,
1571 nodes, nodesorder=nodesorder, revisiondata=revisiondata,
1570 assumehaveparentrevisions=assumehaveparentrevisions,
1572 assumehaveparentrevisions=assumehaveparentrevisions,
1571 deltaprevious=deltaprevious)
1573 deltaprevious=deltaprevious)
1572
1574
1573 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1575 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1574 return self._revlog.addgroup(deltas, linkmapper, transaction,
1576 return self._revlog.addgroup(deltas, linkmapper, transaction,
1575 addrevisioncb=addrevisioncb)
1577 addrevisioncb=addrevisioncb)
1576
1578
1577 def rawsize(self, rev):
1579 def rawsize(self, rev):
1578 return self._revlog.rawsize(rev)
1580 return self._revlog.rawsize(rev)
1579
1581
1580 def getstrippoint(self, minlink):
1582 def getstrippoint(self, minlink):
1581 return self._revlog.getstrippoint(minlink)
1583 return self._revlog.getstrippoint(minlink)
1582
1584
1583 def strip(self, minlink, transaction):
1585 def strip(self, minlink, transaction):
1584 return self._revlog.strip(minlink, transaction)
1586 return self._revlog.strip(minlink, transaction)
1585
1587
1586 def files(self):
1588 def files(self):
1587 return self._revlog.files()
1589 return self._revlog.files()
1588
1590
1589 def clone(self, tr, destrevlog, **kwargs):
1591 def clone(self, tr, destrevlog, **kwargs):
1590 if not isinstance(destrevlog, manifestrevlog):
1592 if not isinstance(destrevlog, manifestrevlog):
1591 raise error.ProgrammingError('expected manifestrevlog to clone()')
1593 raise error.ProgrammingError('expected manifestrevlog to clone()')
1592
1594
1593 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1595 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1594
1596
1595 def storageinfo(self, exclusivefiles=False, sharedfiles=False,
1597 def storageinfo(self, exclusivefiles=False, sharedfiles=False,
1596 revisionscount=False, trackedsize=False,
1598 revisionscount=False, trackedsize=False,
1597 storedsize=False):
1599 storedsize=False):
1598 return self._revlog.storageinfo(
1600 return self._revlog.storageinfo(
1599 exclusivefiles=exclusivefiles, sharedfiles=sharedfiles,
1601 exclusivefiles=exclusivefiles, sharedfiles=sharedfiles,
1600 revisionscount=revisionscount, trackedsize=trackedsize,
1602 revisionscount=revisionscount, trackedsize=trackedsize,
1601 storedsize=storedsize)
1603 storedsize=storedsize)
1602
1604
1603 @property
1605 @property
1604 def indexfile(self):
1606 def indexfile(self):
1605 return self._revlog.indexfile
1607 return self._revlog.indexfile
1606
1608
1607 @indexfile.setter
1609 @indexfile.setter
1608 def indexfile(self, value):
1610 def indexfile(self, value):
1609 self._revlog.indexfile = value
1611 self._revlog.indexfile = value
1610
1612
1611 @property
1613 @property
1612 def opener(self):
1614 def opener(self):
1613 return self._revlog.opener
1615 return self._revlog.opener
1614
1616
1615 @opener.setter
1617 @opener.setter
1616 def opener(self, value):
1618 def opener(self, value):
1617 self._revlog.opener = value
1619 self._revlog.opener = value
1618
1620
1619 @interfaceutil.implementer(repository.imanifestlog)
1621 @interfaceutil.implementer(repository.imanifestlog)
1620 class manifestlog(object):
1622 class manifestlog(object):
1621 """A collection class representing the collection of manifest snapshots
1623 """A collection class representing the collection of manifest snapshots
1622 referenced by commits in the repository.
1624 referenced by commits in the repository.
1623
1625
1624 In this situation, 'manifest' refers to the abstract concept of a snapshot
1626 In this situation, 'manifest' refers to the abstract concept of a snapshot
1625 of the list of files in the given commit. Consumers of the output of this
1627 of the list of files in the given commit. Consumers of the output of this
1626 class do not care about the implementation details of the actual manifests
1628 class do not care about the implementation details of the actual manifests
1627 they receive (i.e. tree or flat or lazily loaded, etc)."""
1629 they receive (i.e. tree or flat or lazily loaded, etc)."""
1628 def __init__(self, opener, repo, rootstore):
1630 def __init__(self, opener, repo, rootstore):
1629 usetreemanifest = False
1631 usetreemanifest = False
1630 cachesize = 4
1632 cachesize = 4
1631
1633
1632 opts = getattr(opener, 'options', None)
1634 opts = getattr(opener, 'options', None)
1633 if opts is not None:
1635 if opts is not None:
1634 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1636 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1635 cachesize = opts.get('manifestcachesize', cachesize)
1637 cachesize = opts.get('manifestcachesize', cachesize)
1636
1638
1637 self._treemanifests = usetreemanifest
1639 self._treemanifests = usetreemanifest
1638
1640
1639 self._rootstore = rootstore
1641 self._rootstore = rootstore
1640 self._rootstore._setupmanifestcachehooks(repo)
1642 self._rootstore._setupmanifestcachehooks(repo)
1641 self._narrowmatch = repo.narrowmatch()
1643 self._narrowmatch = repo.narrowmatch()
1642
1644
1643 # A cache of the manifestctx or treemanifestctx for each directory
1645 # A cache of the manifestctx or treemanifestctx for each directory
1644 self._dirmancache = {}
1646 self._dirmancache = {}
1645 self._dirmancache[''] = util.lrucachedict(cachesize)
1647 self._dirmancache[''] = util.lrucachedict(cachesize)
1646
1648
1647 self._cachesize = cachesize
1649 self._cachesize = cachesize
1648
1650
1649 def __getitem__(self, node):
1651 def __getitem__(self, node):
1650 """Retrieves the manifest instance for the given node. Throws a
1652 """Retrieves the manifest instance for the given node. Throws a
1651 LookupError if not found.
1653 LookupError if not found.
1652 """
1654 """
1653 return self.get('', node)
1655 return self.get('', node)
1654
1656
1655 def get(self, tree, node, verify=True):
1657 def get(self, tree, node, verify=True):
1656 """Retrieves the manifest instance for the given node. Throws a
1658 """Retrieves the manifest instance for the given node. Throws a
1657 LookupError if not found.
1659 LookupError if not found.
1658
1660
1659 `verify` - if True an exception will be thrown if the node is not in
1661 `verify` - if True an exception will be thrown if the node is not in
1660 the revlog
1662 the revlog
1661 """
1663 """
1662 if node in self._dirmancache.get(tree, ()):
1664 if node in self._dirmancache.get(tree, ()):
1663 return self._dirmancache[tree][node]
1665 return self._dirmancache[tree][node]
1664
1666
1665 if not self._narrowmatch.always():
1667 if not self._narrowmatch.always():
1666 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1668 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1667 return excludeddirmanifestctx(tree, node)
1669 return excludeddirmanifestctx(tree, node)
1668 if tree:
1670 if tree:
1669 if self._rootstore._treeondisk:
1671 if self._rootstore._treeondisk:
1670 if verify:
1672 if verify:
1671 # Side-effect is LookupError is raised if node doesn't
1673 # Side-effect is LookupError is raised if node doesn't
1672 # exist.
1674 # exist.
1673 self.getstorage(tree).rev(node)
1675 self.getstorage(tree).rev(node)
1674
1676
1675 m = treemanifestctx(self, tree, node)
1677 m = treemanifestctx(self, tree, node)
1676 else:
1678 else:
1677 raise error.Abort(
1679 raise error.Abort(
1678 _("cannot ask for manifest directory '%s' in a flat "
1680 _("cannot ask for manifest directory '%s' in a flat "
1679 "manifest") % tree)
1681 "manifest") % tree)
1680 else:
1682 else:
1681 if verify:
1683 if verify:
1682 # Side-effect is LookupError is raised if node doesn't exist.
1684 # Side-effect is LookupError is raised if node doesn't exist.
1683 self._rootstore.rev(node)
1685 self._rootstore.rev(node)
1684
1686
1685 if self._treemanifests:
1687 if self._treemanifests:
1686 m = treemanifestctx(self, '', node)
1688 m = treemanifestctx(self, '', node)
1687 else:
1689 else:
1688 m = manifestctx(self, node)
1690 m = manifestctx(self, node)
1689
1691
1690 if node != nullid:
1692 if node != nullid:
1691 mancache = self._dirmancache.get(tree)
1693 mancache = self._dirmancache.get(tree)
1692 if not mancache:
1694 if not mancache:
1693 mancache = util.lrucachedict(self._cachesize)
1695 mancache = util.lrucachedict(self._cachesize)
1694 self._dirmancache[tree] = mancache
1696 self._dirmancache[tree] = mancache
1695 mancache[node] = m
1697 mancache[node] = m
1696 return m
1698 return m
1697
1699
1698 def getstorage(self, tree):
1700 def getstorage(self, tree):
1699 return self._rootstore.dirlog(tree)
1701 return self._rootstore.dirlog(tree)
1700
1702
1701 def clearcaches(self, clear_persisted_data=False):
1703 def clearcaches(self, clear_persisted_data=False):
1702 self._dirmancache.clear()
1704 self._dirmancache.clear()
1703 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1705 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1704
1706
1705 def rev(self, node):
1707 def rev(self, node):
1706 return self._rootstore.rev(node)
1708 return self._rootstore.rev(node)
1707
1709
1708 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1710 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1709 class memmanifestctx(object):
1711 class memmanifestctx(object):
1710 def __init__(self, manifestlog):
1712 def __init__(self, manifestlog):
1711 self._manifestlog = manifestlog
1713 self._manifestlog = manifestlog
1712 self._manifestdict = manifestdict()
1714 self._manifestdict = manifestdict()
1713
1715
1714 def _storage(self):
1716 def _storage(self):
1715 return self._manifestlog.getstorage(b'')
1717 return self._manifestlog.getstorage(b'')
1716
1718
1717 def new(self):
1719 def new(self):
1718 return memmanifestctx(self._manifestlog)
1720 return memmanifestctx(self._manifestlog)
1719
1721
1720 def copy(self):
1722 def copy(self):
1721 memmf = memmanifestctx(self._manifestlog)
1723 memmf = memmanifestctx(self._manifestlog)
1722 memmf._manifestdict = self.read().copy()
1724 memmf._manifestdict = self.read().copy()
1723 return memmf
1725 return memmf
1724
1726
1725 def read(self):
1727 def read(self):
1726 return self._manifestdict
1728 return self._manifestdict
1727
1729
1728 def write(self, transaction, link, p1, p2, added, removed, match=None):
1730 def write(self, transaction, link, p1, p2, added, removed, match=None):
1729 return self._storage().add(self._manifestdict, transaction, link,
1731 return self._storage().add(self._manifestdict, transaction, link,
1730 p1, p2, added, removed, match=match)
1732 p1, p2, added, removed, match=match)
1731
1733
1732 @interfaceutil.implementer(repository.imanifestrevisionstored)
1734 @interfaceutil.implementer(repository.imanifestrevisionstored)
1733 class manifestctx(object):
1735 class manifestctx(object):
1734 """A class representing a single revision of a manifest, including its
1736 """A class representing a single revision of a manifest, including its
1735 contents, its parent revs, and its linkrev.
1737 contents, its parent revs, and its linkrev.
1736 """
1738 """
1737 def __init__(self, manifestlog, node):
1739 def __init__(self, manifestlog, node):
1738 self._manifestlog = manifestlog
1740 self._manifestlog = manifestlog
1739 self._data = None
1741 self._data = None
1740
1742
1741 self._node = node
1743 self._node = node
1742
1744
1743 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1745 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1744 # but let's add it later when something needs it and we can load it
1746 # but let's add it later when something needs it and we can load it
1745 # lazily.
1747 # lazily.
1746 #self.p1, self.p2 = store.parents(node)
1748 #self.p1, self.p2 = store.parents(node)
1747 #rev = store.rev(node)
1749 #rev = store.rev(node)
1748 #self.linkrev = store.linkrev(rev)
1750 #self.linkrev = store.linkrev(rev)
1749
1751
1750 def _storage(self):
1752 def _storage(self):
1751 return self._manifestlog.getstorage(b'')
1753 return self._manifestlog.getstorage(b'')
1752
1754
1753 def node(self):
1755 def node(self):
1754 return self._node
1756 return self._node
1755
1757
1756 def new(self):
1758 def new(self):
1757 return memmanifestctx(self._manifestlog)
1759 return memmanifestctx(self._manifestlog)
1758
1760
1759 def copy(self):
1761 def copy(self):
1760 memmf = memmanifestctx(self._manifestlog)
1762 memmf = memmanifestctx(self._manifestlog)
1761 memmf._manifestdict = self.read().copy()
1763 memmf._manifestdict = self.read().copy()
1762 return memmf
1764 return memmf
1763
1765
1764 @propertycache
1766 @propertycache
1765 def parents(self):
1767 def parents(self):
1766 return self._storage().parents(self._node)
1768 return self._storage().parents(self._node)
1767
1769
1768 def read(self):
1770 def read(self):
1769 if self._data is None:
1771 if self._data is None:
1770 if self._node == nullid:
1772 if self._node == nullid:
1771 self._data = manifestdict()
1773 self._data = manifestdict()
1772 else:
1774 else:
1773 store = self._storage()
1775 store = self._storage()
1774 if self._node in store.fulltextcache:
1776 if self._node in store.fulltextcache:
1775 text = pycompat.bytestr(store.fulltextcache[self._node])
1777 text = pycompat.bytestr(store.fulltextcache[self._node])
1776 else:
1778 else:
1777 text = store.revision(self._node)
1779 text = store.revision(self._node)
1778 arraytext = bytearray(text)
1780 arraytext = bytearray(text)
1779 store.fulltextcache[self._node] = arraytext
1781 store.fulltextcache[self._node] = arraytext
1780 self._data = manifestdict(text)
1782 self._data = manifestdict(text)
1781 return self._data
1783 return self._data
1782
1784
1783 def readfast(self, shallow=False):
1785 def readfast(self, shallow=False):
1784 '''Calls either readdelta or read, based on which would be less work.
1786 '''Calls either readdelta or read, based on which would be less work.
1785 readdelta is called if the delta is against the p1, and therefore can be
1787 readdelta is called if the delta is against the p1, and therefore can be
1786 read quickly.
1788 read quickly.
1787
1789
1788 If `shallow` is True, nothing changes since this is a flat manifest.
1790 If `shallow` is True, nothing changes since this is a flat manifest.
1789 '''
1791 '''
1790 store = self._storage()
1792 store = self._storage()
1791 r = store.rev(self._node)
1793 r = store.rev(self._node)
1792 deltaparent = store.deltaparent(r)
1794 deltaparent = store.deltaparent(r)
1793 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1795 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1794 return self.readdelta()
1796 return self.readdelta()
1795 return self.read()
1797 return self.read()
1796
1798
1797 def readdelta(self, shallow=False):
1799 def readdelta(self, shallow=False):
1798 '''Returns a manifest containing just the entries that are present
1800 '''Returns a manifest containing just the entries that are present
1799 in this manifest, but not in its p1 manifest. This is efficient to read
1801 in this manifest, but not in its p1 manifest. This is efficient to read
1800 if the revlog delta is already p1.
1802 if the revlog delta is already p1.
1801
1803
1802 Changing the value of `shallow` has no effect on flat manifests.
1804 Changing the value of `shallow` has no effect on flat manifests.
1803 '''
1805 '''
1804 store = self._storage()
1806 store = self._storage()
1805 r = store.rev(self._node)
1807 r = store.rev(self._node)
1806 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1808 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1807 return manifestdict(d)
1809 return manifestdict(d)
1808
1810
1809 def find(self, key):
1811 def find(self, key):
1810 return self.read().find(key)
1812 return self.read().find(key)
1811
1813
1812 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1814 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1813 class memtreemanifestctx(object):
1815 class memtreemanifestctx(object):
1814 def __init__(self, manifestlog, dir=''):
1816 def __init__(self, manifestlog, dir=''):
1815 self._manifestlog = manifestlog
1817 self._manifestlog = manifestlog
1816 self._dir = dir
1818 self._dir = dir
1817 self._treemanifest = treemanifest()
1819 self._treemanifest = treemanifest()
1818
1820
1819 def _storage(self):
1821 def _storage(self):
1820 return self._manifestlog.getstorage(b'')
1822 return self._manifestlog.getstorage(b'')
1821
1823
1822 def new(self, dir=''):
1824 def new(self, dir=''):
1823 return memtreemanifestctx(self._manifestlog, dir=dir)
1825 return memtreemanifestctx(self._manifestlog, dir=dir)
1824
1826
1825 def copy(self):
1827 def copy(self):
1826 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1828 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1827 memmf._treemanifest = self._treemanifest.copy()
1829 memmf._treemanifest = self._treemanifest.copy()
1828 return memmf
1830 return memmf
1829
1831
1830 def read(self):
1832 def read(self):
1831 return self._treemanifest
1833 return self._treemanifest
1832
1834
1833 def write(self, transaction, link, p1, p2, added, removed, match=None):
1835 def write(self, transaction, link, p1, p2, added, removed, match=None):
1834 def readtree(dir, node):
1836 def readtree(dir, node):
1835 return self._manifestlog.get(dir, node).read()
1837 return self._manifestlog.get(dir, node).read()
1836 return self._storage().add(self._treemanifest, transaction, link,
1838 return self._storage().add(self._treemanifest, transaction, link,
1837 p1, p2, added, removed, readtree=readtree,
1839 p1, p2, added, removed, readtree=readtree,
1838 match=match)
1840 match=match)
1839
1841
1840 @interfaceutil.implementer(repository.imanifestrevisionstored)
1842 @interfaceutil.implementer(repository.imanifestrevisionstored)
1841 class treemanifestctx(object):
1843 class treemanifestctx(object):
1842 def __init__(self, manifestlog, dir, node):
1844 def __init__(self, manifestlog, dir, node):
1843 self._manifestlog = manifestlog
1845 self._manifestlog = manifestlog
1844 self._dir = dir
1846 self._dir = dir
1845 self._data = None
1847 self._data = None
1846
1848
1847 self._node = node
1849 self._node = node
1848
1850
1849 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1851 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1850 # we can instantiate treemanifestctx objects for directories we don't
1852 # we can instantiate treemanifestctx objects for directories we don't
1851 # have on disk.
1853 # have on disk.
1852 #self.p1, self.p2 = store.parents(node)
1854 #self.p1, self.p2 = store.parents(node)
1853 #rev = store.rev(node)
1855 #rev = store.rev(node)
1854 #self.linkrev = store.linkrev(rev)
1856 #self.linkrev = store.linkrev(rev)
1855
1857
1856 def _storage(self):
1858 def _storage(self):
1857 narrowmatch = self._manifestlog._narrowmatch
1859 narrowmatch = self._manifestlog._narrowmatch
1858 if not narrowmatch.always():
1860 if not narrowmatch.always():
1859 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1861 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1860 return excludedmanifestrevlog(self._dir)
1862 return excludedmanifestrevlog(self._dir)
1861 return self._manifestlog.getstorage(self._dir)
1863 return self._manifestlog.getstorage(self._dir)
1862
1864
1863 def read(self):
1865 def read(self):
1864 if self._data is None:
1866 if self._data is None:
1865 store = self._storage()
1867 store = self._storage()
1866 if self._node == nullid:
1868 if self._node == nullid:
1867 self._data = treemanifest()
1869 self._data = treemanifest()
1868 # TODO accessing non-public API
1870 # TODO accessing non-public API
1869 elif store._treeondisk:
1871 elif store._treeondisk:
1870 m = treemanifest(dir=self._dir)
1872 m = treemanifest(dir=self._dir)
1871 def gettext():
1873 def gettext():
1872 return store.revision(self._node)
1874 return store.revision(self._node)
1873 def readsubtree(dir, subm):
1875 def readsubtree(dir, subm):
1874 # Set verify to False since we need to be able to create
1876 # Set verify to False since we need to be able to create
1875 # subtrees for trees that don't exist on disk.
1877 # subtrees for trees that don't exist on disk.
1876 return self._manifestlog.get(dir, subm, verify=False).read()
1878 return self._manifestlog.get(dir, subm, verify=False).read()
1877 m.read(gettext, readsubtree)
1879 m.read(gettext, readsubtree)
1878 m.setnode(self._node)
1880 m.setnode(self._node)
1879 self._data = m
1881 self._data = m
1880 else:
1882 else:
1881 if self._node in store.fulltextcache:
1883 if self._node in store.fulltextcache:
1882 text = pycompat.bytestr(store.fulltextcache[self._node])
1884 text = pycompat.bytestr(store.fulltextcache[self._node])
1883 else:
1885 else:
1884 text = store.revision(self._node)
1886 text = store.revision(self._node)
1885 arraytext = bytearray(text)
1887 arraytext = bytearray(text)
1886 store.fulltextcache[self._node] = arraytext
1888 store.fulltextcache[self._node] = arraytext
1887 self._data = treemanifest(dir=self._dir, text=text)
1889 self._data = treemanifest(dir=self._dir, text=text)
1888
1890
1889 return self._data
1891 return self._data
1890
1892
1891 def node(self):
1893 def node(self):
1892 return self._node
1894 return self._node
1893
1895
1894 def new(self, dir=''):
1896 def new(self, dir=''):
1895 return memtreemanifestctx(self._manifestlog, dir=dir)
1897 return memtreemanifestctx(self._manifestlog, dir=dir)
1896
1898
1897 def copy(self):
1899 def copy(self):
1898 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1900 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1899 memmf._treemanifest = self.read().copy()
1901 memmf._treemanifest = self.read().copy()
1900 return memmf
1902 return memmf
1901
1903
1902 @propertycache
1904 @propertycache
1903 def parents(self):
1905 def parents(self):
1904 return self._storage().parents(self._node)
1906 return self._storage().parents(self._node)
1905
1907
1906 def readdelta(self, shallow=False):
1908 def readdelta(self, shallow=False):
1907 '''Returns a manifest containing just the entries that are present
1909 '''Returns a manifest containing just the entries that are present
1908 in this manifest, but not in its p1 manifest. This is efficient to read
1910 in this manifest, but not in its p1 manifest. This is efficient to read
1909 if the revlog delta is already p1.
1911 if the revlog delta is already p1.
1910
1912
1911 If `shallow` is True, this will read the delta for this directory,
1913 If `shallow` is True, this will read the delta for this directory,
1912 without recursively reading subdirectory manifests. Instead, any
1914 without recursively reading subdirectory manifests. Instead, any
1913 subdirectory entry will be reported as it appears in the manifest, i.e.
1915 subdirectory entry will be reported as it appears in the manifest, i.e.
1914 the subdirectory will be reported among files and distinguished only by
1916 the subdirectory will be reported among files and distinguished only by
1915 its 't' flag.
1917 its 't' flag.
1916 '''
1918 '''
1917 store = self._storage()
1919 store = self._storage()
1918 if shallow:
1920 if shallow:
1919 r = store.rev(self._node)
1921 r = store.rev(self._node)
1920 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1922 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1921 return manifestdict(d)
1923 return manifestdict(d)
1922 else:
1924 else:
1923 # Need to perform a slow delta
1925 # Need to perform a slow delta
1924 r0 = store.deltaparent(store.rev(self._node))
1926 r0 = store.deltaparent(store.rev(self._node))
1925 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1927 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1926 m1 = self.read()
1928 m1 = self.read()
1927 md = treemanifest(dir=self._dir)
1929 md = treemanifest(dir=self._dir)
1928 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1930 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1929 if n1:
1931 if n1:
1930 md[f] = n1
1932 md[f] = n1
1931 if fl1:
1933 if fl1:
1932 md.setflag(f, fl1)
1934 md.setflag(f, fl1)
1933 return md
1935 return md
1934
1936
1935 def readfast(self, shallow=False):
1937 def readfast(self, shallow=False):
1936 '''Calls either readdelta or read, based on which would be less work.
1938 '''Calls either readdelta or read, based on which would be less work.
1937 readdelta is called if the delta is against the p1, and therefore can be
1939 readdelta is called if the delta is against the p1, and therefore can be
1938 read quickly.
1940 read quickly.
1939
1941
1940 If `shallow` is True, it only returns the entries from this manifest,
1942 If `shallow` is True, it only returns the entries from this manifest,
1941 and not any submanifests.
1943 and not any submanifests.
1942 '''
1944 '''
1943 store = self._storage()
1945 store = self._storage()
1944 r = store.rev(self._node)
1946 r = store.rev(self._node)
1945 deltaparent = store.deltaparent(r)
1947 deltaparent = store.deltaparent(r)
1946 if (deltaparent != nullrev and
1948 if (deltaparent != nullrev and
1947 deltaparent in store.parentrevs(r)):
1949 deltaparent in store.parentrevs(r)):
1948 return self.readdelta(shallow=shallow)
1950 return self.readdelta(shallow=shallow)
1949
1951
1950 if shallow:
1952 if shallow:
1951 return manifestdict(store.revision(self._node))
1953 return manifestdict(store.revision(self._node))
1952 else:
1954 else:
1953 return self.read()
1955 return self.read()
1954
1956
1955 def find(self, key):
1957 def find(self, key):
1956 return self.read().find(key)
1958 return self.read().find(key)
1957
1959
1958 class excludeddir(treemanifest):
1960 class excludeddir(treemanifest):
1959 """Stand-in for a directory that is excluded from the repository.
1961 """Stand-in for a directory that is excluded from the repository.
1960
1962
1961 With narrowing active on a repository that uses treemanifests,
1963 With narrowing active on a repository that uses treemanifests,
1962 some of the directory revlogs will be excluded from the resulting
1964 some of the directory revlogs will be excluded from the resulting
1963 clone. This is a huge storage win for clients, but means we need
1965 clone. This is a huge storage win for clients, but means we need
1964 some sort of pseudo-manifest to surface to internals so we can
1966 some sort of pseudo-manifest to surface to internals so we can
1965 detect a merge conflict outside the narrowspec. That's what this
1967 detect a merge conflict outside the narrowspec. That's what this
1966 class is: it stands in for a directory whose node is known, but
1968 class is: it stands in for a directory whose node is known, but
1967 whose contents are unknown.
1969 whose contents are unknown.
1968 """
1970 """
1969 def __init__(self, dir, node):
1971 def __init__(self, dir, node):
1970 super(excludeddir, self).__init__(dir)
1972 super(excludeddir, self).__init__(dir)
1971 self._node = node
1973 self._node = node
1972 # Add an empty file, which will be included by iterators and such,
1974 # Add an empty file, which will be included by iterators and such,
1973 # appearing as the directory itself (i.e. something like "dir/")
1975 # appearing as the directory itself (i.e. something like "dir/")
1974 self._files[''] = node
1976 self._files[''] = node
1975 self._flags[''] = 't'
1977 self._flags[''] = 't'
1976
1978
1977 # Manifests outside the narrowspec should never be modified, so avoid
1979 # Manifests outside the narrowspec should never be modified, so avoid
1978 # copying. This makes a noticeable difference when there are very many
1980 # copying. This makes a noticeable difference when there are very many
1979 # directories outside the narrowspec. Also, it makes sense for the copy to
1981 # directories outside the narrowspec. Also, it makes sense for the copy to
1980 # be of the same type as the original, which would not happen with the
1982 # be of the same type as the original, which would not happen with the
1981 # super type's copy().
1983 # super type's copy().
1982 def copy(self):
1984 def copy(self):
1983 return self
1985 return self
1984
1986
1985 class excludeddirmanifestctx(treemanifestctx):
1987 class excludeddirmanifestctx(treemanifestctx):
1986 """context wrapper for excludeddir - see that docstring for rationale"""
1988 """context wrapper for excludeddir - see that docstring for rationale"""
1987 def __init__(self, dir, node):
1989 def __init__(self, dir, node):
1988 self._dir = dir
1990 self._dir = dir
1989 self._node = node
1991 self._node = node
1990
1992
1991 def read(self):
1993 def read(self):
1992 return excludeddir(self._dir, self._node)
1994 return excludeddir(self._dir, self._node)
1993
1995
1994 def write(self, *args):
1996 def write(self, *args):
1995 raise error.ProgrammingError(
1997 raise error.ProgrammingError(
1996 'attempt to write manifest from excluded dir %s' % self._dir)
1998 'attempt to write manifest from excluded dir %s' % self._dir)
1997
1999
1998 class excludedmanifestrevlog(manifestrevlog):
2000 class excludedmanifestrevlog(manifestrevlog):
1999 """Stand-in for excluded treemanifest revlogs.
2001 """Stand-in for excluded treemanifest revlogs.
2000
2002
2001 When narrowing is active on a treemanifest repository, we'll have
2003 When narrowing is active on a treemanifest repository, we'll have
2002 references to directories we can't see due to the revlog being
2004 references to directories we can't see due to the revlog being
2003 skipped. This class exists to conform to the manifestrevlog
2005 skipped. This class exists to conform to the manifestrevlog
2004 interface for those directories and proactively prevent writes to
2006 interface for those directories and proactively prevent writes to
2005 outside the narrowspec.
2007 outside the narrowspec.
2006 """
2008 """
2007
2009
2008 def __init__(self, dir):
2010 def __init__(self, dir):
2009 self._dir = dir
2011 self._dir = dir
2010
2012
2011 def __len__(self):
2013 def __len__(self):
2012 raise error.ProgrammingError(
2014 raise error.ProgrammingError(
2013 'attempt to get length of excluded dir %s' % self._dir)
2015 'attempt to get length of excluded dir %s' % self._dir)
2014
2016
2015 def rev(self, node):
2017 def rev(self, node):
2016 raise error.ProgrammingError(
2018 raise error.ProgrammingError(
2017 'attempt to get rev from excluded dir %s' % self._dir)
2019 'attempt to get rev from excluded dir %s' % self._dir)
2018
2020
2019 def linkrev(self, node):
2021 def linkrev(self, node):
2020 raise error.ProgrammingError(
2022 raise error.ProgrammingError(
2021 'attempt to get linkrev from excluded dir %s' % self._dir)
2023 'attempt to get linkrev from excluded dir %s' % self._dir)
2022
2024
2023 def node(self, rev):
2025 def node(self, rev):
2024 raise error.ProgrammingError(
2026 raise error.ProgrammingError(
2025 'attempt to get node from excluded dir %s' % self._dir)
2027 'attempt to get node from excluded dir %s' % self._dir)
2026
2028
2027 def add(self, *args, **kwargs):
2029 def add(self, *args, **kwargs):
2028 # We should never write entries in dirlogs outside the narrow clone.
2030 # We should never write entries in dirlogs outside the narrow clone.
2029 # However, the method still gets called from writesubtree() in
2031 # However, the method still gets called from writesubtree() in
2030 # _addtree(), so we need to handle it. We should possibly make that
2032 # _addtree(), so we need to handle it. We should possibly make that
2031 # avoid calling add() with a clean manifest (_dirty is always False
2033 # avoid calling add() with a clean manifest (_dirty is always False
2032 # in excludeddir instances).
2034 # in excludeddir instances).
2033 pass
2035 pass
General Comments 0
You need to be logged in to leave comments. Login now