##// END OF EJS Templates
treemanifest: use visitchildrenset when doing a walk...
Kyle Lippincott -
r39558:3ba9ef0f default
parent child Browse files
Show More
@@ -1,2002 +1,2004 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import 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.RevlogError(
646 raise error.RevlogError(
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 path, node, readsubtree = self._lazydirs[d]
710 self._dirs[d] = readsubtree(path, node)
710 self._dirs[d] = readsubtree(path, node)
711 del self._lazydirs[d]
711 del self._lazydirs[d]
712
712
713 def _loadchildrensetlazy(self, visit):
713 def _loadchildrensetlazy(self, visit):
714 if not visit:
714 if not visit:
715 return None
715 return None
716 if visit == 'all' or visit == 'this':
716 if visit == 'all' or visit == 'this':
717 self._loadalllazy()
717 self._loadalllazy()
718 return None
718 return None
719
719
720 todel = []
720 todel = []
721 for k in visit:
721 for k in visit:
722 kslash = k + '/'
722 kslash = k + '/'
723 ld = self._lazydirs.get(kslash)
723 ld = self._lazydirs.get(kslash)
724 if ld:
724 if ld:
725 path, node, readsubtree = ld
725 path, node, readsubtree = ld
726 self._dirs[kslash] = readsubtree(path, node)
726 self._dirs[kslash] = readsubtree(path, node)
727 todel.append(kslash)
727 todel.append(kslash)
728 for kslash in todel:
728 for kslash in todel:
729 del self._lazydirs[kslash]
729 del self._lazydirs[kslash]
730 return visit
730 return visit
731
731
732 def __len__(self):
732 def __len__(self):
733 self._load()
733 self._load()
734 size = len(self._files)
734 size = len(self._files)
735 self._loadalllazy()
735 self._loadalllazy()
736 for m in self._dirs.values():
736 for m in self._dirs.values():
737 size += m.__len__()
737 size += m.__len__()
738 return size
738 return size
739
739
740 def __nonzero__(self):
740 def __nonzero__(self):
741 # Faster than "__len() != 0" since it avoids loading sub-manifests
741 # Faster than "__len() != 0" since it avoids loading sub-manifests
742 return not self._isempty()
742 return not self._isempty()
743
743
744 __bool__ = __nonzero__
744 __bool__ = __nonzero__
745
745
746 def _isempty(self):
746 def _isempty(self):
747 self._load() # for consistency; already loaded by all callers
747 self._load() # for consistency; already loaded by all callers
748 # See if we can skip loading everything.
748 # See if we can skip loading everything.
749 if self._files or (self._dirs and
749 if self._files or (self._dirs and
750 any(not m._isempty() for m in self._dirs.values())):
750 any(not m._isempty() for m in self._dirs.values())):
751 return False
751 return False
752 self._loadalllazy()
752 self._loadalllazy()
753 return (not self._dirs or
753 return (not self._dirs or
754 all(m._isempty() for m in self._dirs.values()))
754 all(m._isempty() for m in self._dirs.values()))
755
755
756 def __repr__(self):
756 def __repr__(self):
757 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
757 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
758 (self._dir, hex(self._node),
758 (self._dir, hex(self._node),
759 bool(self._loadfunc is _noop),
759 bool(self._loadfunc is _noop),
760 self._dirty, id(self)))
760 self._dirty, id(self)))
761
761
762 def dir(self):
762 def dir(self):
763 '''The directory that this tree manifest represents, including a
763 '''The directory that this tree manifest represents, including a
764 trailing '/'. Empty string for the repo root directory.'''
764 trailing '/'. Empty string for the repo root directory.'''
765 return self._dir
765 return self._dir
766
766
767 def node(self):
767 def node(self):
768 '''This node of this instance. nullid for unsaved instances. Should
768 '''This node of this instance. nullid for unsaved instances. Should
769 be updated when the instance is read or written from a revlog.
769 be updated when the instance is read or written from a revlog.
770 '''
770 '''
771 assert not self._dirty
771 assert not self._dirty
772 return self._node
772 return self._node
773
773
774 def setnode(self, node):
774 def setnode(self, node):
775 self._node = node
775 self._node = node
776 self._dirty = False
776 self._dirty = False
777
777
778 def iterentries(self):
778 def iterentries(self):
779 self._load()
779 self._load()
780 self._loadalllazy()
780 self._loadalllazy()
781 for p, n in sorted(itertools.chain(self._dirs.items(),
781 for p, n in sorted(itertools.chain(self._dirs.items(),
782 self._files.items())):
782 self._files.items())):
783 if p in self._files:
783 if p in self._files:
784 yield self._subpath(p), n, self._flags.get(p, '')
784 yield self._subpath(p), n, self._flags.get(p, '')
785 else:
785 else:
786 for x in n.iterentries():
786 for x in n.iterentries():
787 yield x
787 yield x
788
788
789 def items(self):
789 def items(self):
790 self._load()
790 self._load()
791 self._loadalllazy()
791 self._loadalllazy()
792 for p, n in sorted(itertools.chain(self._dirs.items(),
792 for p, n in sorted(itertools.chain(self._dirs.items(),
793 self._files.items())):
793 self._files.items())):
794 if p in self._files:
794 if p in self._files:
795 yield self._subpath(p), n
795 yield self._subpath(p), n
796 else:
796 else:
797 for f, sn in n.iteritems():
797 for f, sn in n.iteritems():
798 yield f, sn
798 yield f, sn
799
799
800 iteritems = items
800 iteritems = items
801
801
802 def iterkeys(self):
802 def iterkeys(self):
803 self._load()
803 self._load()
804 self._loadalllazy()
804 self._loadalllazy()
805 for p in sorted(itertools.chain(self._dirs, self._files)):
805 for p in sorted(itertools.chain(self._dirs, self._files)):
806 if p in self._files:
806 if p in self._files:
807 yield self._subpath(p)
807 yield self._subpath(p)
808 else:
808 else:
809 for f in self._dirs[p]:
809 for f in self._dirs[p]:
810 yield f
810 yield f
811
811
812 def keys(self):
812 def keys(self):
813 return list(self.iterkeys())
813 return list(self.iterkeys())
814
814
815 def __iter__(self):
815 def __iter__(self):
816 return self.iterkeys()
816 return self.iterkeys()
817
817
818 def __contains__(self, f):
818 def __contains__(self, f):
819 if f is None:
819 if f is None:
820 return False
820 return False
821 self._load()
821 self._load()
822 dir, subpath = _splittopdir(f)
822 dir, subpath = _splittopdir(f)
823 if dir:
823 if dir:
824 if dir in self._lazydirs:
824 if dir in self._lazydirs:
825 self._loadlazy(dir)
825 self._loadlazy(dir)
826
826
827 if dir not in self._dirs:
827 if dir not in self._dirs:
828 return False
828 return False
829
829
830 return self._dirs[dir].__contains__(subpath)
830 return self._dirs[dir].__contains__(subpath)
831 else:
831 else:
832 return f in self._files
832 return f in self._files
833
833
834 def get(self, f, default=None):
834 def get(self, f, default=None):
835 self._load()
835 self._load()
836 dir, subpath = _splittopdir(f)
836 dir, subpath = _splittopdir(f)
837 if dir:
837 if dir:
838 if dir in self._lazydirs:
838 if dir in self._lazydirs:
839 self._loadlazy(dir)
839 self._loadlazy(dir)
840
840
841 if dir not in self._dirs:
841 if dir not in self._dirs:
842 return default
842 return default
843 return self._dirs[dir].get(subpath, default)
843 return self._dirs[dir].get(subpath, default)
844 else:
844 else:
845 return self._files.get(f, default)
845 return self._files.get(f, default)
846
846
847 def __getitem__(self, f):
847 def __getitem__(self, f):
848 self._load()
848 self._load()
849 dir, subpath = _splittopdir(f)
849 dir, subpath = _splittopdir(f)
850 if dir:
850 if dir:
851 if dir in self._lazydirs:
851 if dir in self._lazydirs:
852 self._loadlazy(dir)
852 self._loadlazy(dir)
853
853
854 return self._dirs[dir].__getitem__(subpath)
854 return self._dirs[dir].__getitem__(subpath)
855 else:
855 else:
856 return self._files[f]
856 return self._files[f]
857
857
858 def flags(self, f):
858 def flags(self, f):
859 self._load()
859 self._load()
860 dir, subpath = _splittopdir(f)
860 dir, subpath = _splittopdir(f)
861 if dir:
861 if dir:
862 if dir in self._lazydirs:
862 if dir in self._lazydirs:
863 self._loadlazy(dir)
863 self._loadlazy(dir)
864
864
865 if dir not in self._dirs:
865 if dir not in self._dirs:
866 return ''
866 return ''
867 return self._dirs[dir].flags(subpath)
867 return self._dirs[dir].flags(subpath)
868 else:
868 else:
869 if f in self._lazydirs or f in self._dirs:
869 if f in self._lazydirs or f in self._dirs:
870 return ''
870 return ''
871 return self._flags.get(f, '')
871 return self._flags.get(f, '')
872
872
873 def find(self, f):
873 def find(self, f):
874 self._load()
874 self._load()
875 dir, subpath = _splittopdir(f)
875 dir, subpath = _splittopdir(f)
876 if dir:
876 if dir:
877 if dir in self._lazydirs:
877 if dir in self._lazydirs:
878 self._loadlazy(dir)
878 self._loadlazy(dir)
879
879
880 return self._dirs[dir].find(subpath)
880 return self._dirs[dir].find(subpath)
881 else:
881 else:
882 return self._files[f], self._flags.get(f, '')
882 return self._files[f], self._flags.get(f, '')
883
883
884 def __delitem__(self, f):
884 def __delitem__(self, f):
885 self._load()
885 self._load()
886 dir, subpath = _splittopdir(f)
886 dir, subpath = _splittopdir(f)
887 if dir:
887 if dir:
888 if dir in self._lazydirs:
888 if dir in self._lazydirs:
889 self._loadlazy(dir)
889 self._loadlazy(dir)
890
890
891 self._dirs[dir].__delitem__(subpath)
891 self._dirs[dir].__delitem__(subpath)
892 # If the directory is now empty, remove it
892 # If the directory is now empty, remove it
893 if self._dirs[dir]._isempty():
893 if self._dirs[dir]._isempty():
894 del self._dirs[dir]
894 del self._dirs[dir]
895 else:
895 else:
896 del self._files[f]
896 del self._files[f]
897 if f in self._flags:
897 if f in self._flags:
898 del self._flags[f]
898 del self._flags[f]
899 self._dirty = True
899 self._dirty = True
900
900
901 def __setitem__(self, f, n):
901 def __setitem__(self, f, n):
902 assert n is not None
902 assert n is not None
903 self._load()
903 self._load()
904 dir, subpath = _splittopdir(f)
904 dir, subpath = _splittopdir(f)
905 if dir:
905 if dir:
906 if dir in self._lazydirs:
906 if dir in self._lazydirs:
907 self._loadlazy(dir)
907 self._loadlazy(dir)
908 if dir not in self._dirs:
908 if dir not in self._dirs:
909 self._dirs[dir] = treemanifest(self._subpath(dir))
909 self._dirs[dir] = treemanifest(self._subpath(dir))
910 self._dirs[dir].__setitem__(subpath, n)
910 self._dirs[dir].__setitem__(subpath, n)
911 else:
911 else:
912 self._files[f] = n[:21] # to match manifestdict's behavior
912 self._files[f] = n[:21] # to match manifestdict's behavior
913 self._dirty = True
913 self._dirty = True
914
914
915 def _load(self):
915 def _load(self):
916 if self._loadfunc is not _noop:
916 if self._loadfunc is not _noop:
917 lf, self._loadfunc = self._loadfunc, _noop
917 lf, self._loadfunc = self._loadfunc, _noop
918 lf(self)
918 lf(self)
919 elif self._copyfunc is not _noop:
919 elif self._copyfunc is not _noop:
920 cf, self._copyfunc = self._copyfunc, _noop
920 cf, self._copyfunc = self._copyfunc, _noop
921 cf(self)
921 cf(self)
922
922
923 def setflag(self, f, flags):
923 def setflag(self, f, flags):
924 """Set the flags (symlink, executable) for path f."""
924 """Set the flags (symlink, executable) for path f."""
925 self._load()
925 self._load()
926 dir, subpath = _splittopdir(f)
926 dir, subpath = _splittopdir(f)
927 if dir:
927 if dir:
928 if dir in self._lazydirs:
928 if dir in self._lazydirs:
929 self._loadlazy(dir)
929 self._loadlazy(dir)
930 if dir not in self._dirs:
930 if dir not in self._dirs:
931 self._dirs[dir] = treemanifest(self._subpath(dir))
931 self._dirs[dir] = treemanifest(self._subpath(dir))
932 self._dirs[dir].setflag(subpath, flags)
932 self._dirs[dir].setflag(subpath, flags)
933 else:
933 else:
934 self._flags[f] = flags
934 self._flags[f] = flags
935 self._dirty = True
935 self._dirty = True
936
936
937 def copy(self):
937 def copy(self):
938 copy = treemanifest(self._dir)
938 copy = treemanifest(self._dir)
939 copy._node = self._node
939 copy._node = self._node
940 copy._dirty = self._dirty
940 copy._dirty = self._dirty
941 if self._copyfunc is _noop:
941 if self._copyfunc is _noop:
942 def _copyfunc(s):
942 def _copyfunc(s):
943 self._load()
943 self._load()
944 # OPT: it'd be nice to not load everything here. Unfortunately
944 # 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.
945 # this makes a mess of the "dirty" state tracking if we don't.
946 self._loadalllazy()
946 self._loadalllazy()
947 sdirs = s._dirs
947 sdirs = s._dirs
948 for d, v in self._dirs.iteritems():
948 for d, v in self._dirs.iteritems():
949 sdirs[d] = v.copy()
949 sdirs[d] = v.copy()
950 s._files = dict.copy(self._files)
950 s._files = dict.copy(self._files)
951 s._flags = dict.copy(self._flags)
951 s._flags = dict.copy(self._flags)
952 if self._loadfunc is _noop:
952 if self._loadfunc is _noop:
953 _copyfunc(copy)
953 _copyfunc(copy)
954 else:
954 else:
955 copy._copyfunc = _copyfunc
955 copy._copyfunc = _copyfunc
956 else:
956 else:
957 copy._copyfunc = self._copyfunc
957 copy._copyfunc = self._copyfunc
958 return copy
958 return copy
959
959
960 def filesnotin(self, m2, match=None):
960 def filesnotin(self, m2, match=None):
961 '''Set of files in this manifest that are not in the other'''
961 '''Set of files in this manifest that are not in the other'''
962 if match and not match.always():
962 if match and not match.always():
963 m1 = self.matches(match)
963 m1 = self.matches(match)
964 m2 = m2.matches(match)
964 m2 = m2.matches(match)
965 return m1.filesnotin(m2)
965 return m1.filesnotin(m2)
966
966
967 files = set()
967 files = set()
968 def _filesnotin(t1, t2):
968 def _filesnotin(t1, t2):
969 if t1._node == t2._node and not t1._dirty and not t2._dirty:
969 if t1._node == t2._node and not t1._dirty and not t2._dirty:
970 return
970 return
971 t1._load()
971 t1._load()
972 t2._load()
972 t2._load()
973 t1._loadalllazy()
973 t1._loadalllazy()
974 t2._loadalllazy()
974 t2._loadalllazy()
975 for d, m1 in t1._dirs.iteritems():
975 for d, m1 in t1._dirs.iteritems():
976 if d in t2._dirs:
976 if d in t2._dirs:
977 m2 = t2._dirs[d]
977 m2 = t2._dirs[d]
978 _filesnotin(m1, m2)
978 _filesnotin(m1, m2)
979 else:
979 else:
980 files.update(m1.iterkeys())
980 files.update(m1.iterkeys())
981
981
982 for fn in t1._files:
982 for fn in t1._files:
983 if fn not in t2._files:
983 if fn not in t2._files:
984 files.add(t1._subpath(fn))
984 files.add(t1._subpath(fn))
985
985
986 _filesnotin(self, m2)
986 _filesnotin(self, m2)
987 return files
987 return files
988
988
989 @propertycache
989 @propertycache
990 def _alldirs(self):
990 def _alldirs(self):
991 return util.dirs(self)
991 return util.dirs(self)
992
992
993 def dirs(self):
993 def dirs(self):
994 return self._alldirs
994 return self._alldirs
995
995
996 def hasdir(self, dir):
996 def hasdir(self, dir):
997 self._load()
997 self._load()
998 topdir, subdir = _splittopdir(dir)
998 topdir, subdir = _splittopdir(dir)
999 if topdir:
999 if topdir:
1000 if topdir in self._lazydirs:
1000 if topdir in self._lazydirs:
1001 self._loadlazy(topdir)
1001 self._loadlazy(topdir)
1002 if topdir in self._dirs:
1002 if topdir in self._dirs:
1003 return self._dirs[topdir].hasdir(subdir)
1003 return self._dirs[topdir].hasdir(subdir)
1004 return False
1004 return False
1005 dirslash = dir + '/'
1005 dirslash = dir + '/'
1006 return dirslash in self._dirs or dirslash in self._lazydirs
1006 return dirslash in self._dirs or dirslash in self._lazydirs
1007
1007
1008 def walk(self, match):
1008 def walk(self, match):
1009 '''Generates matching file names.
1009 '''Generates matching file names.
1010
1010
1011 Equivalent to manifest.matches(match).iterkeys(), but without creating
1011 Equivalent to manifest.matches(match).iterkeys(), but without creating
1012 an entirely new manifest.
1012 an entirely new manifest.
1013
1013
1014 It also reports nonexistent files by marking them bad with match.bad().
1014 It also reports nonexistent files by marking them bad with match.bad().
1015 '''
1015 '''
1016 if match.always():
1016 if match.always():
1017 for f in iter(self):
1017 for f in iter(self):
1018 yield f
1018 yield f
1019 return
1019 return
1020
1020
1021 fset = set(match.files())
1021 fset = set(match.files())
1022
1022
1023 for fn in self._walk(match):
1023 for fn in self._walk(match):
1024 if fn in fset:
1024 if fn in fset:
1025 # specified pattern is the exact name
1025 # specified pattern is the exact name
1026 fset.remove(fn)
1026 fset.remove(fn)
1027 yield fn
1027 yield fn
1028
1028
1029 # for dirstate.walk, files=['.'] means "walk the whole tree".
1029 # for dirstate.walk, files=['.'] means "walk the whole tree".
1030 # follow that here, too
1030 # follow that here, too
1031 fset.discard('.')
1031 fset.discard('.')
1032
1032
1033 for fn in sorted(fset):
1033 for fn in sorted(fset):
1034 if not self.hasdir(fn):
1034 if not self.hasdir(fn):
1035 match.bad(fn, None)
1035 match.bad(fn, None)
1036
1036
1037 def _walk(self, match):
1037 def _walk(self, match):
1038 '''Recursively generates matching file names for walk().'''
1038 '''Recursively generates matching file names for walk().'''
1039 if not match.visitdir(self._dir[:-1] or '.'):
1039 visit = match.visitchildrenset(self._dir[:-1] or '.')
1040 if not visit:
1040 return
1041 return
1041
1042
1042 # yield this dir's files and walk its submanifests
1043 # yield this dir's files and walk its submanifests
1043 self._load()
1044 self._load()
1044 self._loadalllazy()
1045 visit = self._loadchildrensetlazy(visit)
1045 for p in sorted(list(self._dirs) + list(self._files)):
1046 for p in sorted(list(self._dirs) + list(self._files)):
1046 if p in self._files:
1047 if p in self._files:
1047 fullp = self._subpath(p)
1048 fullp = self._subpath(p)
1048 if match(fullp):
1049 if match(fullp):
1049 yield fullp
1050 yield fullp
1050 else:
1051 else:
1052 if not visit or p[:-1] in visit:
1051 for f in self._dirs[p]._walk(match):
1053 for f in self._dirs[p]._walk(match):
1052 yield f
1054 yield f
1053
1055
1054 def matches(self, match):
1056 def matches(self, match):
1055 '''generate a new manifest filtered by the match argument'''
1057 '''generate a new manifest filtered by the match argument'''
1056 if match.always():
1058 if match.always():
1057 return self.copy()
1059 return self.copy()
1058
1060
1059 return self._matches(match)
1061 return self._matches(match)
1060
1062
1061 def _matches(self, match):
1063 def _matches(self, match):
1062 '''recursively generate a new manifest filtered by the match argument.
1064 '''recursively generate a new manifest filtered by the match argument.
1063 '''
1065 '''
1064
1066
1065 visit = match.visitchildrenset(self._dir[:-1] or '.')
1067 visit = match.visitchildrenset(self._dir[:-1] or '.')
1066 if visit == 'all':
1068 if visit == 'all':
1067 return self.copy()
1069 return self.copy()
1068 ret = treemanifest(self._dir)
1070 ret = treemanifest(self._dir)
1069 if not visit:
1071 if not visit:
1070 return ret
1072 return ret
1071
1073
1072 self._load()
1074 self._load()
1073 for fn in self._files:
1075 for fn in self._files:
1074 # While visitchildrenset *usually* lists only subdirs, this is
1076 # While visitchildrenset *usually* lists only subdirs, this is
1075 # actually up to the matcher and may have some files in the set().
1077 # actually up to the matcher and may have some files in the set().
1076 # If visit == 'this', we should obviously look at the files in this
1078 # If visit == 'this', we should obviously look at the files in this
1077 # directory; if visit is a set, and fn is in it, we should inspect
1079 # directory; if visit is a set, and fn is in it, we should inspect
1078 # fn (but no need to inspect things not in the set).
1080 # fn (but no need to inspect things not in the set).
1079 if visit != 'this' and fn not in visit:
1081 if visit != 'this' and fn not in visit:
1080 continue
1082 continue
1081 fullp = self._subpath(fn)
1083 fullp = self._subpath(fn)
1082 # visitchildrenset isn't perfect, we still need to call the regular
1084 # visitchildrenset isn't perfect, we still need to call the regular
1083 # matcher code to further filter results.
1085 # matcher code to further filter results.
1084 if not match(fullp):
1086 if not match(fullp):
1085 continue
1087 continue
1086 ret._files[fn] = self._files[fn]
1088 ret._files[fn] = self._files[fn]
1087 if fn in self._flags:
1089 if fn in self._flags:
1088 ret._flags[fn] = self._flags[fn]
1090 ret._flags[fn] = self._flags[fn]
1089
1091
1090 visit = self._loadchildrensetlazy(visit)
1092 visit = self._loadchildrensetlazy(visit)
1091 for dir, subm in self._dirs.iteritems():
1093 for dir, subm in self._dirs.iteritems():
1092 if visit and dir[:-1] not in visit:
1094 if visit and dir[:-1] not in visit:
1093 continue
1095 continue
1094 m = subm._matches(match)
1096 m = subm._matches(match)
1095 if not m._isempty():
1097 if not m._isempty():
1096 ret._dirs[dir] = m
1098 ret._dirs[dir] = m
1097
1099
1098 if not ret._isempty():
1100 if not ret._isempty():
1099 ret._dirty = True
1101 ret._dirty = True
1100 return ret
1102 return ret
1101
1103
1102 def diff(self, m2, match=None, clean=False):
1104 def diff(self, m2, match=None, clean=False):
1103 '''Finds changes between the current manifest and m2.
1105 '''Finds changes between the current manifest and m2.
1104
1106
1105 Args:
1107 Args:
1106 m2: the manifest to which this manifest should be compared.
1108 m2: the manifest to which this manifest should be compared.
1107 clean: if true, include files unchanged between these manifests
1109 clean: if true, include files unchanged between these manifests
1108 with a None value in the returned dictionary.
1110 with a None value in the returned dictionary.
1109
1111
1110 The result is returned as a dict with filename as key and
1112 The result is returned as a dict with filename as key and
1111 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1113 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1112 nodeid in the current/other manifest and fl1/fl2 is the flag
1114 nodeid in the current/other manifest and fl1/fl2 is the flag
1113 in the current/other manifest. Where the file does not exist,
1115 in the current/other manifest. Where the file does not exist,
1114 the nodeid will be None and the flags will be the empty
1116 the nodeid will be None and the flags will be the empty
1115 string.
1117 string.
1116 '''
1118 '''
1117 if match and not match.always():
1119 if match and not match.always():
1118 m1 = self.matches(match)
1120 m1 = self.matches(match)
1119 m2 = m2.matches(match)
1121 m2 = m2.matches(match)
1120 return m1.diff(m2, clean=clean)
1122 return m1.diff(m2, clean=clean)
1121 result = {}
1123 result = {}
1122 emptytree = treemanifest()
1124 emptytree = treemanifest()
1123 def _diff(t1, t2):
1125 def _diff(t1, t2):
1124 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1126 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1125 return
1127 return
1126 t1._load()
1128 t1._load()
1127 t2._load()
1129 t2._load()
1128 # OPT: do we need to load everything?
1130 # OPT: do we need to load everything?
1129 t1._loadalllazy()
1131 t1._loadalllazy()
1130 t2._loadalllazy()
1132 t2._loadalllazy()
1131 for d, m1 in t1._dirs.iteritems():
1133 for d, m1 in t1._dirs.iteritems():
1132 m2 = t2._dirs.get(d, emptytree)
1134 m2 = t2._dirs.get(d, emptytree)
1133 _diff(m1, m2)
1135 _diff(m1, m2)
1134
1136
1135 for d, m2 in t2._dirs.iteritems():
1137 for d, m2 in t2._dirs.iteritems():
1136 if d not in t1._dirs:
1138 if d not in t1._dirs:
1137 _diff(emptytree, m2)
1139 _diff(emptytree, m2)
1138
1140
1139 for fn, n1 in t1._files.iteritems():
1141 for fn, n1 in t1._files.iteritems():
1140 fl1 = t1._flags.get(fn, '')
1142 fl1 = t1._flags.get(fn, '')
1141 n2 = t2._files.get(fn, None)
1143 n2 = t2._files.get(fn, None)
1142 fl2 = t2._flags.get(fn, '')
1144 fl2 = t2._flags.get(fn, '')
1143 if n1 != n2 or fl1 != fl2:
1145 if n1 != n2 or fl1 != fl2:
1144 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1146 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1145 elif clean:
1147 elif clean:
1146 result[t1._subpath(fn)] = None
1148 result[t1._subpath(fn)] = None
1147
1149
1148 for fn, n2 in t2._files.iteritems():
1150 for fn, n2 in t2._files.iteritems():
1149 if fn not in t1._files:
1151 if fn not in t1._files:
1150 fl2 = t2._flags.get(fn, '')
1152 fl2 = t2._flags.get(fn, '')
1151 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1153 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1152
1154
1153 _diff(self, m2)
1155 _diff(self, m2)
1154 return result
1156 return result
1155
1157
1156 def unmodifiedsince(self, m2):
1158 def unmodifiedsince(self, m2):
1157 return not self._dirty and not m2._dirty and self._node == m2._node
1159 return not self._dirty and not m2._dirty and self._node == m2._node
1158
1160
1159 def parse(self, text, readsubtree):
1161 def parse(self, text, readsubtree):
1160 selflazy = self._lazydirs
1162 selflazy = self._lazydirs
1161 subpath = self._subpath
1163 subpath = self._subpath
1162 for f, n, fl in _parse(text):
1164 for f, n, fl in _parse(text):
1163 if fl == 't':
1165 if fl == 't':
1164 f = f + '/'
1166 f = f + '/'
1165 selflazy[f] = (subpath(f), n, readsubtree)
1167 selflazy[f] = (subpath(f), n, readsubtree)
1166 elif '/' in f:
1168 elif '/' in f:
1167 # This is a flat manifest, so use __setitem__ and setflag rather
1169 # This is a flat manifest, so use __setitem__ and setflag rather
1168 # than assigning directly to _files and _flags, so we can
1170 # than assigning directly to _files and _flags, so we can
1169 # assign a path in a subdirectory, and to mark dirty (compared
1171 # assign a path in a subdirectory, and to mark dirty (compared
1170 # to nullid).
1172 # to nullid).
1171 self[f] = n
1173 self[f] = n
1172 if fl:
1174 if fl:
1173 self.setflag(f, fl)
1175 self.setflag(f, fl)
1174 else:
1176 else:
1175 # Assigning to _files and _flags avoids marking as dirty,
1177 # Assigning to _files and _flags avoids marking as dirty,
1176 # and should be a little faster.
1178 # and should be a little faster.
1177 self._files[f] = n
1179 self._files[f] = n
1178 if fl:
1180 if fl:
1179 self._flags[f] = fl
1181 self._flags[f] = fl
1180
1182
1181 def text(self):
1183 def text(self):
1182 """Get the full data of this manifest as a bytestring."""
1184 """Get the full data of this manifest as a bytestring."""
1183 self._load()
1185 self._load()
1184 return _text(self.iterentries())
1186 return _text(self.iterentries())
1185
1187
1186 def dirtext(self):
1188 def dirtext(self):
1187 """Get the full data of this directory as a bytestring. Make sure that
1189 """Get the full data of this directory as a bytestring. Make sure that
1188 any submanifests have been written first, so their nodeids are correct.
1190 any submanifests have been written first, so their nodeids are correct.
1189 """
1191 """
1190 self._load()
1192 self._load()
1191 flags = self.flags
1193 flags = self.flags
1192 lazydirs = [(d[:-1], node, 't') for
1194 lazydirs = [(d[:-1], node, 't') for
1193 d, (path, node, readsubtree) in self._lazydirs.iteritems()]
1195 d, (path, node, readsubtree) in self._lazydirs.iteritems()]
1194 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1196 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1195 files = [(f, self._files[f], flags(f)) for f in self._files]
1197 files = [(f, self._files[f], flags(f)) for f in self._files]
1196 return _text(sorted(dirs + files + lazydirs))
1198 return _text(sorted(dirs + files + lazydirs))
1197
1199
1198 def read(self, gettext, readsubtree):
1200 def read(self, gettext, readsubtree):
1199 def _load_for_read(s):
1201 def _load_for_read(s):
1200 s.parse(gettext(), readsubtree)
1202 s.parse(gettext(), readsubtree)
1201 s._dirty = False
1203 s._dirty = False
1202 self._loadfunc = _load_for_read
1204 self._loadfunc = _load_for_read
1203
1205
1204 def writesubtrees(self, m1, m2, writesubtree):
1206 def writesubtrees(self, m1, m2, writesubtree):
1205 self._load() # for consistency; should never have any effect here
1207 self._load() # for consistency; should never have any effect here
1206 m1._load()
1208 m1._load()
1207 m2._load()
1209 m2._load()
1208 emptytree = treemanifest()
1210 emptytree = treemanifest()
1209 def getnode(m, d):
1211 def getnode(m, d):
1210 ld = m._lazydirs.get(d)
1212 ld = m._lazydirs.get(d)
1211 if ld:
1213 if ld:
1212 return ld[1]
1214 return ld[1]
1213 return m._dirs.get(d, emptytree)._node
1215 return m._dirs.get(d, emptytree)._node
1214
1216
1215 for d, subm in self._dirs.iteritems():
1217 for d, subm in self._dirs.iteritems():
1216 subp1 = getnode(m1, d)
1218 subp1 = getnode(m1, d)
1217 subp2 = getnode(m2, d)
1219 subp2 = getnode(m2, d)
1218 if subp1 == nullid:
1220 if subp1 == nullid:
1219 subp1, subp2 = subp2, subp1
1221 subp1, subp2 = subp2, subp1
1220 writesubtree(subm, subp1, subp2)
1222 writesubtree(subm, subp1, subp2)
1221
1223
1222 def walksubtrees(self, matcher=None):
1224 def walksubtrees(self, matcher=None):
1223 """Returns an iterator of the subtrees of this manifest, including this
1225 """Returns an iterator of the subtrees of this manifest, including this
1224 manifest itself.
1226 manifest itself.
1225
1227
1226 If `matcher` is provided, it only returns subtrees that match.
1228 If `matcher` is provided, it only returns subtrees that match.
1227 """
1229 """
1228 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1230 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1229 return
1231 return
1230 if not matcher or matcher(self._dir[:-1]):
1232 if not matcher or matcher(self._dir[:-1]):
1231 yield self
1233 yield self
1232
1234
1233 self._load()
1235 self._load()
1234 # OPT: use visitchildrenset to avoid loading everything.
1236 # OPT: use visitchildrenset to avoid loading everything.
1235 self._loadalllazy()
1237 self._loadalllazy()
1236 for d, subm in self._dirs.iteritems():
1238 for d, subm in self._dirs.iteritems():
1237 for subtree in subm.walksubtrees(matcher=matcher):
1239 for subtree in subm.walksubtrees(matcher=matcher):
1238 yield subtree
1240 yield subtree
1239
1241
1240 class manifestfulltextcache(util.lrucachedict):
1242 class manifestfulltextcache(util.lrucachedict):
1241 """File-backed LRU cache for the manifest cache
1243 """File-backed LRU cache for the manifest cache
1242
1244
1243 File consists of entries, up to EOF:
1245 File consists of entries, up to EOF:
1244
1246
1245 - 20 bytes node, 4 bytes length, <length> manifest data
1247 - 20 bytes node, 4 bytes length, <length> manifest data
1246
1248
1247 These are written in reverse cache order (oldest to newest).
1249 These are written in reverse cache order (oldest to newest).
1248
1250
1249 """
1251 """
1250 def __init__(self, max):
1252 def __init__(self, max):
1251 super(manifestfulltextcache, self).__init__(max)
1253 super(manifestfulltextcache, self).__init__(max)
1252 self._dirty = False
1254 self._dirty = False
1253 self._read = False
1255 self._read = False
1254 self._opener = None
1256 self._opener = None
1255
1257
1256 def read(self):
1258 def read(self):
1257 if self._read or self._opener is None:
1259 if self._read or self._opener is None:
1258 return
1260 return
1259
1261
1260 try:
1262 try:
1261 with self._opener('manifestfulltextcache') as fp:
1263 with self._opener('manifestfulltextcache') as fp:
1262 set = super(manifestfulltextcache, self).__setitem__
1264 set = super(manifestfulltextcache, self).__setitem__
1263 # ignore trailing data, this is a cache, corruption is skipped
1265 # ignore trailing data, this is a cache, corruption is skipped
1264 while True:
1266 while True:
1265 node = fp.read(20)
1267 node = fp.read(20)
1266 if len(node) < 20:
1268 if len(node) < 20:
1267 break
1269 break
1268 try:
1270 try:
1269 size = struct.unpack('>L', fp.read(4))[0]
1271 size = struct.unpack('>L', fp.read(4))[0]
1270 except struct.error:
1272 except struct.error:
1271 break
1273 break
1272 value = bytearray(fp.read(size))
1274 value = bytearray(fp.read(size))
1273 if len(value) != size:
1275 if len(value) != size:
1274 break
1276 break
1275 set(node, value)
1277 set(node, value)
1276 except IOError:
1278 except IOError:
1277 # the file is allowed to be missing
1279 # the file is allowed to be missing
1278 pass
1280 pass
1279
1281
1280 self._read = True
1282 self._read = True
1281 self._dirty = False
1283 self._dirty = False
1282
1284
1283 def write(self):
1285 def write(self):
1284 if not self._dirty or self._opener is None:
1286 if not self._dirty or self._opener is None:
1285 return
1287 return
1286 # rotate backwards to the first used node
1288 # rotate backwards to the first used node
1287 with self._opener(
1289 with self._opener(
1288 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1290 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1289 ) as fp:
1291 ) as fp:
1290 node = self._head.prev
1292 node = self._head.prev
1291 while True:
1293 while True:
1292 if node.key in self._cache:
1294 if node.key in self._cache:
1293 fp.write(node.key)
1295 fp.write(node.key)
1294 fp.write(struct.pack('>L', len(node.value)))
1296 fp.write(struct.pack('>L', len(node.value)))
1295 fp.write(node.value)
1297 fp.write(node.value)
1296 if node is self._head:
1298 if node is self._head:
1297 break
1299 break
1298 node = node.prev
1300 node = node.prev
1299
1301
1300 def __len__(self):
1302 def __len__(self):
1301 if not self._read:
1303 if not self._read:
1302 self.read()
1304 self.read()
1303 return super(manifestfulltextcache, self).__len__()
1305 return super(manifestfulltextcache, self).__len__()
1304
1306
1305 def __contains__(self, k):
1307 def __contains__(self, k):
1306 if not self._read:
1308 if not self._read:
1307 self.read()
1309 self.read()
1308 return super(manifestfulltextcache, self).__contains__(k)
1310 return super(manifestfulltextcache, self).__contains__(k)
1309
1311
1310 def __iter__(self):
1312 def __iter__(self):
1311 if not self._read:
1313 if not self._read:
1312 self.read()
1314 self.read()
1313 return super(manifestfulltextcache, self).__iter__()
1315 return super(manifestfulltextcache, self).__iter__()
1314
1316
1315 def __getitem__(self, k):
1317 def __getitem__(self, k):
1316 if not self._read:
1318 if not self._read:
1317 self.read()
1319 self.read()
1318 # the cache lru order can change on read
1320 # the cache lru order can change on read
1319 setdirty = self._cache.get(k) is not self._head
1321 setdirty = self._cache.get(k) is not self._head
1320 value = super(manifestfulltextcache, self).__getitem__(k)
1322 value = super(manifestfulltextcache, self).__getitem__(k)
1321 if setdirty:
1323 if setdirty:
1322 self._dirty = True
1324 self._dirty = True
1323 return value
1325 return value
1324
1326
1325 def __setitem__(self, k, v):
1327 def __setitem__(self, k, v):
1326 if not self._read:
1328 if not self._read:
1327 self.read()
1329 self.read()
1328 super(manifestfulltextcache, self).__setitem__(k, v)
1330 super(manifestfulltextcache, self).__setitem__(k, v)
1329 self._dirty = True
1331 self._dirty = True
1330
1332
1331 def __delitem__(self, k):
1333 def __delitem__(self, k):
1332 if not self._read:
1334 if not self._read:
1333 self.read()
1335 self.read()
1334 super(manifestfulltextcache, self).__delitem__(k)
1336 super(manifestfulltextcache, self).__delitem__(k)
1335 self._dirty = True
1337 self._dirty = True
1336
1338
1337 def get(self, k, default=None):
1339 def get(self, k, default=None):
1338 if not self._read:
1340 if not self._read:
1339 self.read()
1341 self.read()
1340 return super(manifestfulltextcache, self).get(k, default=default)
1342 return super(manifestfulltextcache, self).get(k, default=default)
1341
1343
1342 def clear(self, clear_persisted_data=False):
1344 def clear(self, clear_persisted_data=False):
1343 super(manifestfulltextcache, self).clear()
1345 super(manifestfulltextcache, self).clear()
1344 if clear_persisted_data:
1346 if clear_persisted_data:
1345 self._dirty = True
1347 self._dirty = True
1346 self.write()
1348 self.write()
1347 self._read = False
1349 self._read = False
1348
1350
1349 @interfaceutil.implementer(repository.imanifeststorage)
1351 @interfaceutil.implementer(repository.imanifeststorage)
1350 class manifestrevlog(object):
1352 class manifestrevlog(object):
1351 '''A revlog that stores manifest texts. This is responsible for caching the
1353 '''A revlog that stores manifest texts. This is responsible for caching the
1352 full-text manifest contents.
1354 full-text manifest contents.
1353 '''
1355 '''
1354 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1356 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1355 treemanifest=False):
1357 treemanifest=False):
1356 """Constructs a new manifest revlog
1358 """Constructs a new manifest revlog
1357
1359
1358 `indexfile` - used by extensions to have two manifests at once, like
1360 `indexfile` - used by extensions to have two manifests at once, like
1359 when transitioning between flatmanifeset and treemanifests.
1361 when transitioning between flatmanifeset and treemanifests.
1360
1362
1361 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1363 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1362 options can also be used to make this a tree manifest revlog. The opener
1364 options can also be used to make this a tree manifest revlog. The opener
1363 option takes precedence, so if it is set to True, we ignore whatever
1365 option takes precedence, so if it is set to True, we ignore whatever
1364 value is passed in to the constructor.
1366 value is passed in to the constructor.
1365 """
1367 """
1366 # During normal operations, we expect to deal with not more than four
1368 # During normal operations, we expect to deal with not more than four
1367 # revs at a time (such as during commit --amend). When rebasing large
1369 # revs at a time (such as during commit --amend). When rebasing large
1368 # stacks of commits, the number can go up, hence the config knob below.
1370 # stacks of commits, the number can go up, hence the config knob below.
1369 cachesize = 4
1371 cachesize = 4
1370 optiontreemanifest = False
1372 optiontreemanifest = False
1371 opts = getattr(opener, 'options', None)
1373 opts = getattr(opener, 'options', None)
1372 if opts is not None:
1374 if opts is not None:
1373 cachesize = opts.get('manifestcachesize', cachesize)
1375 cachesize = opts.get('manifestcachesize', cachesize)
1374 optiontreemanifest = opts.get('treemanifest', False)
1376 optiontreemanifest = opts.get('treemanifest', False)
1375
1377
1376 self._treeondisk = optiontreemanifest or treemanifest
1378 self._treeondisk = optiontreemanifest or treemanifest
1377
1379
1378 self._fulltextcache = manifestfulltextcache(cachesize)
1380 self._fulltextcache = manifestfulltextcache(cachesize)
1379
1381
1380 if tree:
1382 if tree:
1381 assert self._treeondisk, 'opts is %r' % opts
1383 assert self._treeondisk, 'opts is %r' % opts
1382
1384
1383 if indexfile is None:
1385 if indexfile is None:
1384 indexfile = '00manifest.i'
1386 indexfile = '00manifest.i'
1385 if tree:
1387 if tree:
1386 indexfile = "meta/" + tree + indexfile
1388 indexfile = "meta/" + tree + indexfile
1387
1389
1388 self.tree = tree
1390 self.tree = tree
1389
1391
1390 # The dirlogcache is kept on the root manifest log
1392 # The dirlogcache is kept on the root manifest log
1391 if tree:
1393 if tree:
1392 self._dirlogcache = dirlogcache
1394 self._dirlogcache = dirlogcache
1393 else:
1395 else:
1394 self._dirlogcache = {'': self}
1396 self._dirlogcache = {'': self}
1395
1397
1396 self._revlog = revlog.revlog(opener, indexfile,
1398 self._revlog = revlog.revlog(opener, indexfile,
1397 # only root indexfile is cached
1399 # only root indexfile is cached
1398 checkambig=not bool(tree),
1400 checkambig=not bool(tree),
1399 mmaplargeindex=True)
1401 mmaplargeindex=True)
1400
1402
1401 self.index = self._revlog.index
1403 self.index = self._revlog.index
1402 self.version = self._revlog.version
1404 self.version = self._revlog.version
1403 self._generaldelta = self._revlog._generaldelta
1405 self._generaldelta = self._revlog._generaldelta
1404
1406
1405 def _setupmanifestcachehooks(self, repo):
1407 def _setupmanifestcachehooks(self, repo):
1406 """Persist the manifestfulltextcache on lock release"""
1408 """Persist the manifestfulltextcache on lock release"""
1407 if not util.safehasattr(repo, '_lockref'):
1409 if not util.safehasattr(repo, '_lockref'):
1408 return
1410 return
1409
1411
1410 self._fulltextcache._opener = repo.cachevfs
1412 self._fulltextcache._opener = repo.cachevfs
1411 reporef = weakref.ref(repo)
1413 reporef = weakref.ref(repo)
1412 manifestrevlogref = weakref.ref(self)
1414 manifestrevlogref = weakref.ref(self)
1413
1415
1414 def persistmanifestcache():
1416 def persistmanifestcache():
1415 repo = reporef()
1417 repo = reporef()
1416 self = manifestrevlogref()
1418 self = manifestrevlogref()
1417 if repo is None or self is None:
1419 if repo is None or self is None:
1418 return
1420 return
1419 if repo.manifestlog.getstorage(b'') is not self:
1421 if repo.manifestlog.getstorage(b'') is not self:
1420 # there's a different manifest in play now, abort
1422 # there's a different manifest in play now, abort
1421 return
1423 return
1422 self._fulltextcache.write()
1424 self._fulltextcache.write()
1423
1425
1424 if repo._currentlock(repo._lockref) is not None:
1426 if repo._currentlock(repo._lockref) is not None:
1425 repo._afterlock(persistmanifestcache)
1427 repo._afterlock(persistmanifestcache)
1426
1428
1427 @property
1429 @property
1428 def fulltextcache(self):
1430 def fulltextcache(self):
1429 return self._fulltextcache
1431 return self._fulltextcache
1430
1432
1431 def clearcaches(self, clear_persisted_data=False):
1433 def clearcaches(self, clear_persisted_data=False):
1432 self._revlog.clearcaches()
1434 self._revlog.clearcaches()
1433 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1435 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1434 self._dirlogcache = {self.tree: self}
1436 self._dirlogcache = {self.tree: self}
1435
1437
1436 def dirlog(self, d):
1438 def dirlog(self, d):
1437 if d:
1439 if d:
1438 assert self._treeondisk
1440 assert self._treeondisk
1439 if d not in self._dirlogcache:
1441 if d not in self._dirlogcache:
1440 mfrevlog = manifestrevlog(self.opener, d,
1442 mfrevlog = manifestrevlog(self.opener, d,
1441 self._dirlogcache,
1443 self._dirlogcache,
1442 treemanifest=self._treeondisk)
1444 treemanifest=self._treeondisk)
1443 self._dirlogcache[d] = mfrevlog
1445 self._dirlogcache[d] = mfrevlog
1444 return self._dirlogcache[d]
1446 return self._dirlogcache[d]
1445
1447
1446 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1448 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1447 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1449 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1448 # If our first parent is in the manifest cache, we can
1450 # If our first parent is in the manifest cache, we can
1449 # compute a delta here using properties we know about the
1451 # compute a delta here using properties we know about the
1450 # manifest up-front, which may save time later for the
1452 # manifest up-front, which may save time later for the
1451 # revlog layer.
1453 # revlog layer.
1452
1454
1453 _checkforbidden(added)
1455 _checkforbidden(added)
1454 # combine the changed lists into one sorted iterator
1456 # combine the changed lists into one sorted iterator
1455 work = heapq.merge([(x, False) for x in added],
1457 work = heapq.merge([(x, False) for x in added],
1456 [(x, True) for x in removed])
1458 [(x, True) for x in removed])
1457
1459
1458 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1460 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1459 cachedelta = self._revlog.rev(p1), deltatext
1461 cachedelta = self._revlog.rev(p1), deltatext
1460 text = util.buffer(arraytext)
1462 text = util.buffer(arraytext)
1461 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1463 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1462 cachedelta)
1464 cachedelta)
1463 else:
1465 else:
1464 # The first parent manifest isn't already loaded, so we'll
1466 # The first parent manifest isn't already loaded, so we'll
1465 # just encode a fulltext of the manifest and pass that
1467 # just encode a fulltext of the manifest and pass that
1466 # through to the revlog layer, and let it handle the delta
1468 # through to the revlog layer, and let it handle the delta
1467 # process.
1469 # process.
1468 if self._treeondisk:
1470 if self._treeondisk:
1469 assert readtree, "readtree must be set for treemanifest writes"
1471 assert readtree, "readtree must be set for treemanifest writes"
1470 m1 = readtree(self.tree, p1)
1472 m1 = readtree(self.tree, p1)
1471 m2 = readtree(self.tree, p2)
1473 m2 = readtree(self.tree, p2)
1472 n = self._addtree(m, transaction, link, m1, m2, readtree)
1474 n = self._addtree(m, transaction, link, m1, m2, readtree)
1473 arraytext = None
1475 arraytext = None
1474 else:
1476 else:
1475 text = m.text()
1477 text = m.text()
1476 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1478 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1477 arraytext = bytearray(text)
1479 arraytext = bytearray(text)
1478
1480
1479 if arraytext is not None:
1481 if arraytext is not None:
1480 self.fulltextcache[n] = arraytext
1482 self.fulltextcache[n] = arraytext
1481
1483
1482 return n
1484 return n
1483
1485
1484 def _addtree(self, m, transaction, link, m1, m2, readtree):
1486 def _addtree(self, m, transaction, link, m1, m2, readtree):
1485 # If the manifest is unchanged compared to one parent,
1487 # If the manifest is unchanged compared to one parent,
1486 # don't write a new revision
1488 # don't write a new revision
1487 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1489 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1488 m2)):
1490 m2)):
1489 return m.node()
1491 return m.node()
1490 def writesubtree(subm, subp1, subp2):
1492 def writesubtree(subm, subp1, subp2):
1491 sublog = self.dirlog(subm.dir())
1493 sublog = self.dirlog(subm.dir())
1492 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1494 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1493 readtree=readtree)
1495 readtree=readtree)
1494 m.writesubtrees(m1, m2, writesubtree)
1496 m.writesubtrees(m1, m2, writesubtree)
1495 text = m.dirtext()
1497 text = m.dirtext()
1496 n = None
1498 n = None
1497 if self.tree != '':
1499 if self.tree != '':
1498 # Double-check whether contents are unchanged to one parent
1500 # Double-check whether contents are unchanged to one parent
1499 if text == m1.dirtext():
1501 if text == m1.dirtext():
1500 n = m1.node()
1502 n = m1.node()
1501 elif text == m2.dirtext():
1503 elif text == m2.dirtext():
1502 n = m2.node()
1504 n = m2.node()
1503
1505
1504 if not n:
1506 if not n:
1505 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1507 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1506 m2.node())
1508 m2.node())
1507
1509
1508 # Save nodeid so parent manifest can calculate its nodeid
1510 # Save nodeid so parent manifest can calculate its nodeid
1509 m.setnode(n)
1511 m.setnode(n)
1510 return n
1512 return n
1511
1513
1512 def __len__(self):
1514 def __len__(self):
1513 return len(self._revlog)
1515 return len(self._revlog)
1514
1516
1515 def __iter__(self):
1517 def __iter__(self):
1516 return self._revlog.__iter__()
1518 return self._revlog.__iter__()
1517
1519
1518 def rev(self, node):
1520 def rev(self, node):
1519 return self._revlog.rev(node)
1521 return self._revlog.rev(node)
1520
1522
1521 def node(self, rev):
1523 def node(self, rev):
1522 return self._revlog.node(rev)
1524 return self._revlog.node(rev)
1523
1525
1524 def lookup(self, value):
1526 def lookup(self, value):
1525 return self._revlog.lookup(value)
1527 return self._revlog.lookup(value)
1526
1528
1527 def parentrevs(self, rev):
1529 def parentrevs(self, rev):
1528 return self._revlog.parentrevs(rev)
1530 return self._revlog.parentrevs(rev)
1529
1531
1530 def parents(self, node):
1532 def parents(self, node):
1531 return self._revlog.parents(node)
1533 return self._revlog.parents(node)
1532
1534
1533 def linkrev(self, rev):
1535 def linkrev(self, rev):
1534 return self._revlog.linkrev(rev)
1536 return self._revlog.linkrev(rev)
1535
1537
1536 def checksize(self):
1538 def checksize(self):
1537 return self._revlog.checksize()
1539 return self._revlog.checksize()
1538
1540
1539 def revision(self, node, _df=None, raw=False):
1541 def revision(self, node, _df=None, raw=False):
1540 return self._revlog.revision(node, _df=_df, raw=raw)
1542 return self._revlog.revision(node, _df=_df, raw=raw)
1541
1543
1542 def revdiff(self, rev1, rev2):
1544 def revdiff(self, rev1, rev2):
1543 return self._revlog.revdiff(rev1, rev2)
1545 return self._revlog.revdiff(rev1, rev2)
1544
1546
1545 def cmp(self, node, text):
1547 def cmp(self, node, text):
1546 return self._revlog.cmp(node, text)
1548 return self._revlog.cmp(node, text)
1547
1549
1548 def deltaparent(self, rev):
1550 def deltaparent(self, rev):
1549 return self._revlog.deltaparent(rev)
1551 return self._revlog.deltaparent(rev)
1550
1552
1551 def emitrevisiondeltas(self, requests):
1553 def emitrevisiondeltas(self, requests):
1552 return self._revlog.emitrevisiondeltas(requests)
1554 return self._revlog.emitrevisiondeltas(requests)
1553
1555
1554 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1556 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1555 return self._revlog.addgroup(deltas, linkmapper, transaction,
1557 return self._revlog.addgroup(deltas, linkmapper, transaction,
1556 addrevisioncb=addrevisioncb)
1558 addrevisioncb=addrevisioncb)
1557
1559
1558 def getstrippoint(self, minlink):
1560 def getstrippoint(self, minlink):
1559 return self._revlog.getstrippoint(minlink)
1561 return self._revlog.getstrippoint(minlink)
1560
1562
1561 def strip(self, minlink, transaction):
1563 def strip(self, minlink, transaction):
1562 return self._revlog.strip(minlink, transaction)
1564 return self._revlog.strip(minlink, transaction)
1563
1565
1564 def files(self):
1566 def files(self):
1565 return self._revlog.files()
1567 return self._revlog.files()
1566
1568
1567 def clone(self, tr, destrevlog, **kwargs):
1569 def clone(self, tr, destrevlog, **kwargs):
1568 if not isinstance(destrevlog, manifestrevlog):
1570 if not isinstance(destrevlog, manifestrevlog):
1569 raise error.ProgrammingError('expected manifestrevlog to clone()')
1571 raise error.ProgrammingError('expected manifestrevlog to clone()')
1570
1572
1571 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1573 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1572
1574
1573 @property
1575 @property
1574 def indexfile(self):
1576 def indexfile(self):
1575 return self._revlog.indexfile
1577 return self._revlog.indexfile
1576
1578
1577 @indexfile.setter
1579 @indexfile.setter
1578 def indexfile(self, value):
1580 def indexfile(self, value):
1579 self._revlog.indexfile = value
1581 self._revlog.indexfile = value
1580
1582
1581 @property
1583 @property
1582 def opener(self):
1584 def opener(self):
1583 return self._revlog.opener
1585 return self._revlog.opener
1584
1586
1585 @opener.setter
1587 @opener.setter
1586 def opener(self, value):
1588 def opener(self, value):
1587 self._revlog.opener = value
1589 self._revlog.opener = value
1588
1590
1589 @interfaceutil.implementer(repository.imanifestlog)
1591 @interfaceutil.implementer(repository.imanifestlog)
1590 class manifestlog(object):
1592 class manifestlog(object):
1591 """A collection class representing the collection of manifest snapshots
1593 """A collection class representing the collection of manifest snapshots
1592 referenced by commits in the repository.
1594 referenced by commits in the repository.
1593
1595
1594 In this situation, 'manifest' refers to the abstract concept of a snapshot
1596 In this situation, 'manifest' refers to the abstract concept of a snapshot
1595 of the list of files in the given commit. Consumers of the output of this
1597 of the list of files in the given commit. Consumers of the output of this
1596 class do not care about the implementation details of the actual manifests
1598 class do not care about the implementation details of the actual manifests
1597 they receive (i.e. tree or flat or lazily loaded, etc)."""
1599 they receive (i.e. tree or flat or lazily loaded, etc)."""
1598 def __init__(self, opener, repo):
1600 def __init__(self, opener, repo):
1599 usetreemanifest = False
1601 usetreemanifest = False
1600 cachesize = 4
1602 cachesize = 4
1601
1603
1602 opts = getattr(opener, 'options', None)
1604 opts = getattr(opener, 'options', None)
1603 if opts is not None:
1605 if opts is not None:
1604 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1606 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1605 cachesize = opts.get('manifestcachesize', cachesize)
1607 cachesize = opts.get('manifestcachesize', cachesize)
1606
1608
1607 self._treemanifests = usetreemanifest
1609 self._treemanifests = usetreemanifest
1608
1610
1609 self._rootstore = repo._constructmanifest()
1611 self._rootstore = repo._constructmanifest()
1610 self._rootstore._setupmanifestcachehooks(repo)
1612 self._rootstore._setupmanifestcachehooks(repo)
1611 self._narrowmatch = repo.narrowmatch()
1613 self._narrowmatch = repo.narrowmatch()
1612
1614
1613 # A cache of the manifestctx or treemanifestctx for each directory
1615 # A cache of the manifestctx or treemanifestctx for each directory
1614 self._dirmancache = {}
1616 self._dirmancache = {}
1615 self._dirmancache[''] = util.lrucachedict(cachesize)
1617 self._dirmancache[''] = util.lrucachedict(cachesize)
1616
1618
1617 self._cachesize = cachesize
1619 self._cachesize = cachesize
1618
1620
1619 def __getitem__(self, node):
1621 def __getitem__(self, node):
1620 """Retrieves the manifest instance for the given node. Throws a
1622 """Retrieves the manifest instance for the given node. Throws a
1621 LookupError if not found.
1623 LookupError if not found.
1622 """
1624 """
1623 return self.get('', node)
1625 return self.get('', node)
1624
1626
1625 def get(self, tree, node, verify=True):
1627 def get(self, tree, node, verify=True):
1626 """Retrieves the manifest instance for the given node. Throws a
1628 """Retrieves the manifest instance for the given node. Throws a
1627 LookupError if not found.
1629 LookupError if not found.
1628
1630
1629 `verify` - if True an exception will be thrown if the node is not in
1631 `verify` - if True an exception will be thrown if the node is not in
1630 the revlog
1632 the revlog
1631 """
1633 """
1632 if node in self._dirmancache.get(tree, ()):
1634 if node in self._dirmancache.get(tree, ()):
1633 return self._dirmancache[tree][node]
1635 return self._dirmancache[tree][node]
1634
1636
1635 if not self._narrowmatch.always():
1637 if not self._narrowmatch.always():
1636 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1638 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1637 return excludeddirmanifestctx(tree, node)
1639 return excludeddirmanifestctx(tree, node)
1638 if tree:
1640 if tree:
1639 if self._rootstore._treeondisk:
1641 if self._rootstore._treeondisk:
1640 if verify:
1642 if verify:
1641 # Side-effect is LookupError is raised if node doesn't
1643 # Side-effect is LookupError is raised if node doesn't
1642 # exist.
1644 # exist.
1643 self.getstorage(tree).rev(node)
1645 self.getstorage(tree).rev(node)
1644
1646
1645 m = treemanifestctx(self, tree, node)
1647 m = treemanifestctx(self, tree, node)
1646 else:
1648 else:
1647 raise error.Abort(
1649 raise error.Abort(
1648 _("cannot ask for manifest directory '%s' in a flat "
1650 _("cannot ask for manifest directory '%s' in a flat "
1649 "manifest") % tree)
1651 "manifest") % tree)
1650 else:
1652 else:
1651 if verify:
1653 if verify:
1652 # Side-effect is LookupError is raised if node doesn't exist.
1654 # Side-effect is LookupError is raised if node doesn't exist.
1653 self._rootstore.rev(node)
1655 self._rootstore.rev(node)
1654
1656
1655 if self._treemanifests:
1657 if self._treemanifests:
1656 m = treemanifestctx(self, '', node)
1658 m = treemanifestctx(self, '', node)
1657 else:
1659 else:
1658 m = manifestctx(self, node)
1660 m = manifestctx(self, node)
1659
1661
1660 if node != nullid:
1662 if node != nullid:
1661 mancache = self._dirmancache.get(tree)
1663 mancache = self._dirmancache.get(tree)
1662 if not mancache:
1664 if not mancache:
1663 mancache = util.lrucachedict(self._cachesize)
1665 mancache = util.lrucachedict(self._cachesize)
1664 self._dirmancache[tree] = mancache
1666 self._dirmancache[tree] = mancache
1665 mancache[node] = m
1667 mancache[node] = m
1666 return m
1668 return m
1667
1669
1668 def getstorage(self, tree):
1670 def getstorage(self, tree):
1669 return self._rootstore.dirlog(tree)
1671 return self._rootstore.dirlog(tree)
1670
1672
1671 def clearcaches(self, clear_persisted_data=False):
1673 def clearcaches(self, clear_persisted_data=False):
1672 self._dirmancache.clear()
1674 self._dirmancache.clear()
1673 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1675 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1674
1676
1675 def rev(self, node):
1677 def rev(self, node):
1676 return self._rootstore.rev(node)
1678 return self._rootstore.rev(node)
1677
1679
1678 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1680 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1679 class memmanifestctx(object):
1681 class memmanifestctx(object):
1680 def __init__(self, manifestlog):
1682 def __init__(self, manifestlog):
1681 self._manifestlog = manifestlog
1683 self._manifestlog = manifestlog
1682 self._manifestdict = manifestdict()
1684 self._manifestdict = manifestdict()
1683
1685
1684 def _storage(self):
1686 def _storage(self):
1685 return self._manifestlog.getstorage(b'')
1687 return self._manifestlog.getstorage(b'')
1686
1688
1687 def new(self):
1689 def new(self):
1688 return memmanifestctx(self._manifestlog)
1690 return memmanifestctx(self._manifestlog)
1689
1691
1690 def copy(self):
1692 def copy(self):
1691 memmf = memmanifestctx(self._manifestlog)
1693 memmf = memmanifestctx(self._manifestlog)
1692 memmf._manifestdict = self.read().copy()
1694 memmf._manifestdict = self.read().copy()
1693 return memmf
1695 return memmf
1694
1696
1695 def read(self):
1697 def read(self):
1696 return self._manifestdict
1698 return self._manifestdict
1697
1699
1698 def write(self, transaction, link, p1, p2, added, removed):
1700 def write(self, transaction, link, p1, p2, added, removed):
1699 return self._storage().add(self._manifestdict, transaction, link,
1701 return self._storage().add(self._manifestdict, transaction, link,
1700 p1, p2, added, removed)
1702 p1, p2, added, removed)
1701
1703
1702 @interfaceutil.implementer(repository.imanifestrevisionstored)
1704 @interfaceutil.implementer(repository.imanifestrevisionstored)
1703 class manifestctx(object):
1705 class manifestctx(object):
1704 """A class representing a single revision of a manifest, including its
1706 """A class representing a single revision of a manifest, including its
1705 contents, its parent revs, and its linkrev.
1707 contents, its parent revs, and its linkrev.
1706 """
1708 """
1707 def __init__(self, manifestlog, node):
1709 def __init__(self, manifestlog, node):
1708 self._manifestlog = manifestlog
1710 self._manifestlog = manifestlog
1709 self._data = None
1711 self._data = None
1710
1712
1711 self._node = node
1713 self._node = node
1712
1714
1713 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1715 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1714 # but let's add it later when something needs it and we can load it
1716 # but let's add it later when something needs it and we can load it
1715 # lazily.
1717 # lazily.
1716 #self.p1, self.p2 = store.parents(node)
1718 #self.p1, self.p2 = store.parents(node)
1717 #rev = store.rev(node)
1719 #rev = store.rev(node)
1718 #self.linkrev = store.linkrev(rev)
1720 #self.linkrev = store.linkrev(rev)
1719
1721
1720 def _storage(self):
1722 def _storage(self):
1721 return self._manifestlog.getstorage(b'')
1723 return self._manifestlog.getstorage(b'')
1722
1724
1723 def node(self):
1725 def node(self):
1724 return self._node
1726 return self._node
1725
1727
1726 def new(self):
1728 def new(self):
1727 return memmanifestctx(self._manifestlog)
1729 return memmanifestctx(self._manifestlog)
1728
1730
1729 def copy(self):
1731 def copy(self):
1730 memmf = memmanifestctx(self._manifestlog)
1732 memmf = memmanifestctx(self._manifestlog)
1731 memmf._manifestdict = self.read().copy()
1733 memmf._manifestdict = self.read().copy()
1732 return memmf
1734 return memmf
1733
1735
1734 @propertycache
1736 @propertycache
1735 def parents(self):
1737 def parents(self):
1736 return self._storage().parents(self._node)
1738 return self._storage().parents(self._node)
1737
1739
1738 def read(self):
1740 def read(self):
1739 if self._data is None:
1741 if self._data is None:
1740 if self._node == nullid:
1742 if self._node == nullid:
1741 self._data = manifestdict()
1743 self._data = manifestdict()
1742 else:
1744 else:
1743 store = self._storage()
1745 store = self._storage()
1744 if self._node in store.fulltextcache:
1746 if self._node in store.fulltextcache:
1745 text = pycompat.bytestr(store.fulltextcache[self._node])
1747 text = pycompat.bytestr(store.fulltextcache[self._node])
1746 else:
1748 else:
1747 text = store.revision(self._node)
1749 text = store.revision(self._node)
1748 arraytext = bytearray(text)
1750 arraytext = bytearray(text)
1749 store.fulltextcache[self._node] = arraytext
1751 store.fulltextcache[self._node] = arraytext
1750 self._data = manifestdict(text)
1752 self._data = manifestdict(text)
1751 return self._data
1753 return self._data
1752
1754
1753 def readfast(self, shallow=False):
1755 def readfast(self, shallow=False):
1754 '''Calls either readdelta or read, based on which would be less work.
1756 '''Calls either readdelta or read, based on which would be less work.
1755 readdelta is called if the delta is against the p1, and therefore can be
1757 readdelta is called if the delta is against the p1, and therefore can be
1756 read quickly.
1758 read quickly.
1757
1759
1758 If `shallow` is True, nothing changes since this is a flat manifest.
1760 If `shallow` is True, nothing changes since this is a flat manifest.
1759 '''
1761 '''
1760 store = self._storage()
1762 store = self._storage()
1761 r = store.rev(self._node)
1763 r = store.rev(self._node)
1762 deltaparent = store.deltaparent(r)
1764 deltaparent = store.deltaparent(r)
1763 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1765 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1764 return self.readdelta()
1766 return self.readdelta()
1765 return self.read()
1767 return self.read()
1766
1768
1767 def readdelta(self, shallow=False):
1769 def readdelta(self, shallow=False):
1768 '''Returns a manifest containing just the entries that are present
1770 '''Returns a manifest containing just the entries that are present
1769 in this manifest, but not in its p1 manifest. This is efficient to read
1771 in this manifest, but not in its p1 manifest. This is efficient to read
1770 if the revlog delta is already p1.
1772 if the revlog delta is already p1.
1771
1773
1772 Changing the value of `shallow` has no effect on flat manifests.
1774 Changing the value of `shallow` has no effect on flat manifests.
1773 '''
1775 '''
1774 store = self._storage()
1776 store = self._storage()
1775 r = store.rev(self._node)
1777 r = store.rev(self._node)
1776 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1778 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1777 return manifestdict(d)
1779 return manifestdict(d)
1778
1780
1779 def find(self, key):
1781 def find(self, key):
1780 return self.read().find(key)
1782 return self.read().find(key)
1781
1783
1782 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1784 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1783 class memtreemanifestctx(object):
1785 class memtreemanifestctx(object):
1784 def __init__(self, manifestlog, dir=''):
1786 def __init__(self, manifestlog, dir=''):
1785 self._manifestlog = manifestlog
1787 self._manifestlog = manifestlog
1786 self._dir = dir
1788 self._dir = dir
1787 self._treemanifest = treemanifest()
1789 self._treemanifest = treemanifest()
1788
1790
1789 def _storage(self):
1791 def _storage(self):
1790 return self._manifestlog.getstorage(b'')
1792 return self._manifestlog.getstorage(b'')
1791
1793
1792 def new(self, dir=''):
1794 def new(self, dir=''):
1793 return memtreemanifestctx(self._manifestlog, dir=dir)
1795 return memtreemanifestctx(self._manifestlog, dir=dir)
1794
1796
1795 def copy(self):
1797 def copy(self):
1796 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1798 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1797 memmf._treemanifest = self._treemanifest.copy()
1799 memmf._treemanifest = self._treemanifest.copy()
1798 return memmf
1800 return memmf
1799
1801
1800 def read(self):
1802 def read(self):
1801 return self._treemanifest
1803 return self._treemanifest
1802
1804
1803 def write(self, transaction, link, p1, p2, added, removed):
1805 def write(self, transaction, link, p1, p2, added, removed):
1804 def readtree(dir, node):
1806 def readtree(dir, node):
1805 return self._manifestlog.get(dir, node).read()
1807 return self._manifestlog.get(dir, node).read()
1806 return self._storage().add(self._treemanifest, transaction, link,
1808 return self._storage().add(self._treemanifest, transaction, link,
1807 p1, p2, added, removed, readtree=readtree)
1809 p1, p2, added, removed, readtree=readtree)
1808
1810
1809 @interfaceutil.implementer(repository.imanifestrevisionstored)
1811 @interfaceutil.implementer(repository.imanifestrevisionstored)
1810 class treemanifestctx(object):
1812 class treemanifestctx(object):
1811 def __init__(self, manifestlog, dir, node):
1813 def __init__(self, manifestlog, dir, node):
1812 self._manifestlog = manifestlog
1814 self._manifestlog = manifestlog
1813 self._dir = dir
1815 self._dir = dir
1814 self._data = None
1816 self._data = None
1815
1817
1816 self._node = node
1818 self._node = node
1817
1819
1818 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1820 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1819 # we can instantiate treemanifestctx objects for directories we don't
1821 # we can instantiate treemanifestctx objects for directories we don't
1820 # have on disk.
1822 # have on disk.
1821 #self.p1, self.p2 = store.parents(node)
1823 #self.p1, self.p2 = store.parents(node)
1822 #rev = store.rev(node)
1824 #rev = store.rev(node)
1823 #self.linkrev = store.linkrev(rev)
1825 #self.linkrev = store.linkrev(rev)
1824
1826
1825 def _storage(self):
1827 def _storage(self):
1826 narrowmatch = self._manifestlog._narrowmatch
1828 narrowmatch = self._manifestlog._narrowmatch
1827 if not narrowmatch.always():
1829 if not narrowmatch.always():
1828 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1830 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1829 return excludedmanifestrevlog(self._dir)
1831 return excludedmanifestrevlog(self._dir)
1830 return self._manifestlog.getstorage(self._dir)
1832 return self._manifestlog.getstorage(self._dir)
1831
1833
1832 def read(self):
1834 def read(self):
1833 if self._data is None:
1835 if self._data is None:
1834 store = self._storage()
1836 store = self._storage()
1835 if self._node == nullid:
1837 if self._node == nullid:
1836 self._data = treemanifest()
1838 self._data = treemanifest()
1837 # TODO accessing non-public API
1839 # TODO accessing non-public API
1838 elif store._treeondisk:
1840 elif store._treeondisk:
1839 m = treemanifest(dir=self._dir)
1841 m = treemanifest(dir=self._dir)
1840 def gettext():
1842 def gettext():
1841 return store.revision(self._node)
1843 return store.revision(self._node)
1842 def readsubtree(dir, subm):
1844 def readsubtree(dir, subm):
1843 # Set verify to False since we need to be able to create
1845 # Set verify to False since we need to be able to create
1844 # subtrees for trees that don't exist on disk.
1846 # subtrees for trees that don't exist on disk.
1845 return self._manifestlog.get(dir, subm, verify=False).read()
1847 return self._manifestlog.get(dir, subm, verify=False).read()
1846 m.read(gettext, readsubtree)
1848 m.read(gettext, readsubtree)
1847 m.setnode(self._node)
1849 m.setnode(self._node)
1848 self._data = m
1850 self._data = m
1849 else:
1851 else:
1850 if self._node in store.fulltextcache:
1852 if self._node in store.fulltextcache:
1851 text = pycompat.bytestr(store.fulltextcache[self._node])
1853 text = pycompat.bytestr(store.fulltextcache[self._node])
1852 else:
1854 else:
1853 text = store.revision(self._node)
1855 text = store.revision(self._node)
1854 arraytext = bytearray(text)
1856 arraytext = bytearray(text)
1855 store.fulltextcache[self._node] = arraytext
1857 store.fulltextcache[self._node] = arraytext
1856 self._data = treemanifest(dir=self._dir, text=text)
1858 self._data = treemanifest(dir=self._dir, text=text)
1857
1859
1858 return self._data
1860 return self._data
1859
1861
1860 def node(self):
1862 def node(self):
1861 return self._node
1863 return self._node
1862
1864
1863 def new(self, dir=''):
1865 def new(self, dir=''):
1864 return memtreemanifestctx(self._manifestlog, dir=dir)
1866 return memtreemanifestctx(self._manifestlog, dir=dir)
1865
1867
1866 def copy(self):
1868 def copy(self):
1867 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1869 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1868 memmf._treemanifest = self.read().copy()
1870 memmf._treemanifest = self.read().copy()
1869 return memmf
1871 return memmf
1870
1872
1871 @propertycache
1873 @propertycache
1872 def parents(self):
1874 def parents(self):
1873 return self._storage().parents(self._node)
1875 return self._storage().parents(self._node)
1874
1876
1875 def readdelta(self, shallow=False):
1877 def readdelta(self, shallow=False):
1876 '''Returns a manifest containing just the entries that are present
1878 '''Returns a manifest containing just the entries that are present
1877 in this manifest, but not in its p1 manifest. This is efficient to read
1879 in this manifest, but not in its p1 manifest. This is efficient to read
1878 if the revlog delta is already p1.
1880 if the revlog delta is already p1.
1879
1881
1880 If `shallow` is True, this will read the delta for this directory,
1882 If `shallow` is True, this will read the delta for this directory,
1881 without recursively reading subdirectory manifests. Instead, any
1883 without recursively reading subdirectory manifests. Instead, any
1882 subdirectory entry will be reported as it appears in the manifest, i.e.
1884 subdirectory entry will be reported as it appears in the manifest, i.e.
1883 the subdirectory will be reported among files and distinguished only by
1885 the subdirectory will be reported among files and distinguished only by
1884 its 't' flag.
1886 its 't' flag.
1885 '''
1887 '''
1886 store = self._storage()
1888 store = self._storage()
1887 if shallow:
1889 if shallow:
1888 r = store.rev(self._node)
1890 r = store.rev(self._node)
1889 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1891 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1890 return manifestdict(d)
1892 return manifestdict(d)
1891 else:
1893 else:
1892 # Need to perform a slow delta
1894 # Need to perform a slow delta
1893 r0 = store.deltaparent(store.rev(self._node))
1895 r0 = store.deltaparent(store.rev(self._node))
1894 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1896 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1895 m1 = self.read()
1897 m1 = self.read()
1896 md = treemanifest(dir=self._dir)
1898 md = treemanifest(dir=self._dir)
1897 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1899 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1898 if n1:
1900 if n1:
1899 md[f] = n1
1901 md[f] = n1
1900 if fl1:
1902 if fl1:
1901 md.setflag(f, fl1)
1903 md.setflag(f, fl1)
1902 return md
1904 return md
1903
1905
1904 def readfast(self, shallow=False):
1906 def readfast(self, shallow=False):
1905 '''Calls either readdelta or read, based on which would be less work.
1907 '''Calls either readdelta or read, based on which would be less work.
1906 readdelta is called if the delta is against the p1, and therefore can be
1908 readdelta is called if the delta is against the p1, and therefore can be
1907 read quickly.
1909 read quickly.
1908
1910
1909 If `shallow` is True, it only returns the entries from this manifest,
1911 If `shallow` is True, it only returns the entries from this manifest,
1910 and not any submanifests.
1912 and not any submanifests.
1911 '''
1913 '''
1912 store = self._storage()
1914 store = self._storage()
1913 r = store.rev(self._node)
1915 r = store.rev(self._node)
1914 deltaparent = store.deltaparent(r)
1916 deltaparent = store.deltaparent(r)
1915 if (deltaparent != nullrev and
1917 if (deltaparent != nullrev and
1916 deltaparent in store.parentrevs(r)):
1918 deltaparent in store.parentrevs(r)):
1917 return self.readdelta(shallow=shallow)
1919 return self.readdelta(shallow=shallow)
1918
1920
1919 if shallow:
1921 if shallow:
1920 return manifestdict(store.revision(self._node))
1922 return manifestdict(store.revision(self._node))
1921 else:
1923 else:
1922 return self.read()
1924 return self.read()
1923
1925
1924 def find(self, key):
1926 def find(self, key):
1925 return self.read().find(key)
1927 return self.read().find(key)
1926
1928
1927 class excludeddir(treemanifest):
1929 class excludeddir(treemanifest):
1928 """Stand-in for a directory that is excluded from the repository.
1930 """Stand-in for a directory that is excluded from the repository.
1929
1931
1930 With narrowing active on a repository that uses treemanifests,
1932 With narrowing active on a repository that uses treemanifests,
1931 some of the directory revlogs will be excluded from the resulting
1933 some of the directory revlogs will be excluded from the resulting
1932 clone. This is a huge storage win for clients, but means we need
1934 clone. This is a huge storage win for clients, but means we need
1933 some sort of pseudo-manifest to surface to internals so we can
1935 some sort of pseudo-manifest to surface to internals so we can
1934 detect a merge conflict outside the narrowspec. That's what this
1936 detect a merge conflict outside the narrowspec. That's what this
1935 class is: it stands in for a directory whose node is known, but
1937 class is: it stands in for a directory whose node is known, but
1936 whose contents are unknown.
1938 whose contents are unknown.
1937 """
1939 """
1938 def __init__(self, dir, node):
1940 def __init__(self, dir, node):
1939 super(excludeddir, self).__init__(dir)
1941 super(excludeddir, self).__init__(dir)
1940 self._node = node
1942 self._node = node
1941 # Add an empty file, which will be included by iterators and such,
1943 # Add an empty file, which will be included by iterators and such,
1942 # appearing as the directory itself (i.e. something like "dir/")
1944 # appearing as the directory itself (i.e. something like "dir/")
1943 self._files[''] = node
1945 self._files[''] = node
1944 self._flags[''] = 't'
1946 self._flags[''] = 't'
1945
1947
1946 # Manifests outside the narrowspec should never be modified, so avoid
1948 # Manifests outside the narrowspec should never be modified, so avoid
1947 # copying. This makes a noticeable difference when there are very many
1949 # copying. This makes a noticeable difference when there are very many
1948 # directories outside the narrowspec. Also, it makes sense for the copy to
1950 # directories outside the narrowspec. Also, it makes sense for the copy to
1949 # be of the same type as the original, which would not happen with the
1951 # be of the same type as the original, which would not happen with the
1950 # super type's copy().
1952 # super type's copy().
1951 def copy(self):
1953 def copy(self):
1952 return self
1954 return self
1953
1955
1954 class excludeddirmanifestctx(treemanifestctx):
1956 class excludeddirmanifestctx(treemanifestctx):
1955 """context wrapper for excludeddir - see that docstring for rationale"""
1957 """context wrapper for excludeddir - see that docstring for rationale"""
1956 def __init__(self, dir, node):
1958 def __init__(self, dir, node):
1957 self._dir = dir
1959 self._dir = dir
1958 self._node = node
1960 self._node = node
1959
1961
1960 def read(self):
1962 def read(self):
1961 return excludeddir(self._dir, self._node)
1963 return excludeddir(self._dir, self._node)
1962
1964
1963 def write(self, *args):
1965 def write(self, *args):
1964 raise error.ProgrammingError(
1966 raise error.ProgrammingError(
1965 'attempt to write manifest from excluded dir %s' % self._dir)
1967 'attempt to write manifest from excluded dir %s' % self._dir)
1966
1968
1967 class excludedmanifestrevlog(manifestrevlog):
1969 class excludedmanifestrevlog(manifestrevlog):
1968 """Stand-in for excluded treemanifest revlogs.
1970 """Stand-in for excluded treemanifest revlogs.
1969
1971
1970 When narrowing is active on a treemanifest repository, we'll have
1972 When narrowing is active on a treemanifest repository, we'll have
1971 references to directories we can't see due to the revlog being
1973 references to directories we can't see due to the revlog being
1972 skipped. This class exists to conform to the manifestrevlog
1974 skipped. This class exists to conform to the manifestrevlog
1973 interface for those directories and proactively prevent writes to
1975 interface for those directories and proactively prevent writes to
1974 outside the narrowspec.
1976 outside the narrowspec.
1975 """
1977 """
1976
1978
1977 def __init__(self, dir):
1979 def __init__(self, dir):
1978 self._dir = dir
1980 self._dir = dir
1979
1981
1980 def __len__(self):
1982 def __len__(self):
1981 raise error.ProgrammingError(
1983 raise error.ProgrammingError(
1982 'attempt to get length of excluded dir %s' % self._dir)
1984 'attempt to get length of excluded dir %s' % self._dir)
1983
1985
1984 def rev(self, node):
1986 def rev(self, node):
1985 raise error.ProgrammingError(
1987 raise error.ProgrammingError(
1986 'attempt to get rev from excluded dir %s' % self._dir)
1988 'attempt to get rev from excluded dir %s' % self._dir)
1987
1989
1988 def linkrev(self, node):
1990 def linkrev(self, node):
1989 raise error.ProgrammingError(
1991 raise error.ProgrammingError(
1990 'attempt to get linkrev from excluded dir %s' % self._dir)
1992 'attempt to get linkrev from excluded dir %s' % self._dir)
1991
1993
1992 def node(self, rev):
1994 def node(self, rev):
1993 raise error.ProgrammingError(
1995 raise error.ProgrammingError(
1994 'attempt to get node from excluded dir %s' % self._dir)
1996 'attempt to get node from excluded dir %s' % self._dir)
1995
1997
1996 def add(self, *args, **kwargs):
1998 def add(self, *args, **kwargs):
1997 # We should never write entries in dirlogs outside the narrow clone.
1999 # We should never write entries in dirlogs outside the narrow clone.
1998 # However, the method still gets called from writesubtree() in
2000 # However, the method still gets called from writesubtree() in
1999 # _addtree(), so we need to handle it. We should possibly make that
2001 # _addtree(), so we need to handle it. We should possibly make that
2000 # avoid calling add() with a clean manifest (_dirty is always False
2002 # avoid calling add() with a clean manifest (_dirty is always False
2001 # in excludeddir instances).
2003 # in excludeddir instances).
2002 pass
2004 pass
General Comments 0
You need to be logged in to leave comments. Login now