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