##// END OF EJS Templates
treemanifest: avoid loading everything just to get their nodeid...
Kyle Lippincott -
r39554:c29548ba default
parent child Browse files
Show More
@@ -1,1972 +1,1973
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 and not match.always():
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 and not match.always():
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 def getnode(m, d):
1181 # aren't dirty and don't need to be written.
1181 ld = m._lazydirs.get(d)
1182 self._loadalllazy()
1182 if ld:
1183 m1._loadalllazy()
1183 return ld[1]
1184 m2._loadalllazy()
1184 return m._dirs.get(d, emptytree)._node
1185
1185 for d, subm in self._dirs.iteritems():
1186 for d, subm in self._dirs.iteritems():
1186 subp1 = m1._dirs.get(d, emptytree)._node
1187 subp1 = getnode(m1, d)
1187 subp2 = m2._dirs.get(d, emptytree)._node
1188 subp2 = getnode(m2, d)
1188 if subp1 == nullid:
1189 if subp1 == nullid:
1189 subp1, subp2 = subp2, subp1
1190 subp1, subp2 = subp2, subp1
1190 writesubtree(subm, subp1, subp2)
1191 writesubtree(subm, subp1, subp2)
1191
1192
1192 def walksubtrees(self, matcher=None):
1193 def walksubtrees(self, matcher=None):
1193 """Returns an iterator of the subtrees of this manifest, including this
1194 """Returns an iterator of the subtrees of this manifest, including this
1194 manifest itself.
1195 manifest itself.
1195
1196
1196 If `matcher` is provided, it only returns subtrees that match.
1197 If `matcher` is provided, it only returns subtrees that match.
1197 """
1198 """
1198 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1199 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1199 return
1200 return
1200 if not matcher or matcher(self._dir[:-1]):
1201 if not matcher or matcher(self._dir[:-1]):
1201 yield self
1202 yield self
1202
1203
1203 self._load()
1204 self._load()
1204 # OPT: use visitchildrenset to avoid loading everything.
1205 # OPT: use visitchildrenset to avoid loading everything.
1205 self._loadalllazy()
1206 self._loadalllazy()
1206 for d, subm in self._dirs.iteritems():
1207 for d, subm in self._dirs.iteritems():
1207 for subtree in subm.walksubtrees(matcher=matcher):
1208 for subtree in subm.walksubtrees(matcher=matcher):
1208 yield subtree
1209 yield subtree
1209
1210
1210 class manifestfulltextcache(util.lrucachedict):
1211 class manifestfulltextcache(util.lrucachedict):
1211 """File-backed LRU cache for the manifest cache
1212 """File-backed LRU cache for the manifest cache
1212
1213
1213 File consists of entries, up to EOF:
1214 File consists of entries, up to EOF:
1214
1215
1215 - 20 bytes node, 4 bytes length, <length> manifest data
1216 - 20 bytes node, 4 bytes length, <length> manifest data
1216
1217
1217 These are written in reverse cache order (oldest to newest).
1218 These are written in reverse cache order (oldest to newest).
1218
1219
1219 """
1220 """
1220 def __init__(self, max):
1221 def __init__(self, max):
1221 super(manifestfulltextcache, self).__init__(max)
1222 super(manifestfulltextcache, self).__init__(max)
1222 self._dirty = False
1223 self._dirty = False
1223 self._read = False
1224 self._read = False
1224 self._opener = None
1225 self._opener = None
1225
1226
1226 def read(self):
1227 def read(self):
1227 if self._read or self._opener is None:
1228 if self._read or self._opener is None:
1228 return
1229 return
1229
1230
1230 try:
1231 try:
1231 with self._opener('manifestfulltextcache') as fp:
1232 with self._opener('manifestfulltextcache') as fp:
1232 set = super(manifestfulltextcache, self).__setitem__
1233 set = super(manifestfulltextcache, self).__setitem__
1233 # ignore trailing data, this is a cache, corruption is skipped
1234 # ignore trailing data, this is a cache, corruption is skipped
1234 while True:
1235 while True:
1235 node = fp.read(20)
1236 node = fp.read(20)
1236 if len(node) < 20:
1237 if len(node) < 20:
1237 break
1238 break
1238 try:
1239 try:
1239 size = struct.unpack('>L', fp.read(4))[0]
1240 size = struct.unpack('>L', fp.read(4))[0]
1240 except struct.error:
1241 except struct.error:
1241 break
1242 break
1242 value = bytearray(fp.read(size))
1243 value = bytearray(fp.read(size))
1243 if len(value) != size:
1244 if len(value) != size:
1244 break
1245 break
1245 set(node, value)
1246 set(node, value)
1246 except IOError:
1247 except IOError:
1247 # the file is allowed to be missing
1248 # the file is allowed to be missing
1248 pass
1249 pass
1249
1250
1250 self._read = True
1251 self._read = True
1251 self._dirty = False
1252 self._dirty = False
1252
1253
1253 def write(self):
1254 def write(self):
1254 if not self._dirty or self._opener is None:
1255 if not self._dirty or self._opener is None:
1255 return
1256 return
1256 # rotate backwards to the first used node
1257 # rotate backwards to the first used node
1257 with self._opener(
1258 with self._opener(
1258 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1259 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1259 ) as fp:
1260 ) as fp:
1260 node = self._head.prev
1261 node = self._head.prev
1261 while True:
1262 while True:
1262 if node.key in self._cache:
1263 if node.key in self._cache:
1263 fp.write(node.key)
1264 fp.write(node.key)
1264 fp.write(struct.pack('>L', len(node.value)))
1265 fp.write(struct.pack('>L', len(node.value)))
1265 fp.write(node.value)
1266 fp.write(node.value)
1266 if node is self._head:
1267 if node is self._head:
1267 break
1268 break
1268 node = node.prev
1269 node = node.prev
1269
1270
1270 def __len__(self):
1271 def __len__(self):
1271 if not self._read:
1272 if not self._read:
1272 self.read()
1273 self.read()
1273 return super(manifestfulltextcache, self).__len__()
1274 return super(manifestfulltextcache, self).__len__()
1274
1275
1275 def __contains__(self, k):
1276 def __contains__(self, k):
1276 if not self._read:
1277 if not self._read:
1277 self.read()
1278 self.read()
1278 return super(manifestfulltextcache, self).__contains__(k)
1279 return super(manifestfulltextcache, self).__contains__(k)
1279
1280
1280 def __iter__(self):
1281 def __iter__(self):
1281 if not self._read:
1282 if not self._read:
1282 self.read()
1283 self.read()
1283 return super(manifestfulltextcache, self).__iter__()
1284 return super(manifestfulltextcache, self).__iter__()
1284
1285
1285 def __getitem__(self, k):
1286 def __getitem__(self, k):
1286 if not self._read:
1287 if not self._read:
1287 self.read()
1288 self.read()
1288 # the cache lru order can change on read
1289 # the cache lru order can change on read
1289 setdirty = self._cache.get(k) is not self._head
1290 setdirty = self._cache.get(k) is not self._head
1290 value = super(manifestfulltextcache, self).__getitem__(k)
1291 value = super(manifestfulltextcache, self).__getitem__(k)
1291 if setdirty:
1292 if setdirty:
1292 self._dirty = True
1293 self._dirty = True
1293 return value
1294 return value
1294
1295
1295 def __setitem__(self, k, v):
1296 def __setitem__(self, k, v):
1296 if not self._read:
1297 if not self._read:
1297 self.read()
1298 self.read()
1298 super(manifestfulltextcache, self).__setitem__(k, v)
1299 super(manifestfulltextcache, self).__setitem__(k, v)
1299 self._dirty = True
1300 self._dirty = True
1300
1301
1301 def __delitem__(self, k):
1302 def __delitem__(self, k):
1302 if not self._read:
1303 if not self._read:
1303 self.read()
1304 self.read()
1304 super(manifestfulltextcache, self).__delitem__(k)
1305 super(manifestfulltextcache, self).__delitem__(k)
1305 self._dirty = True
1306 self._dirty = True
1306
1307
1307 def get(self, k, default=None):
1308 def get(self, k, default=None):
1308 if not self._read:
1309 if not self._read:
1309 self.read()
1310 self.read()
1310 return super(manifestfulltextcache, self).get(k, default=default)
1311 return super(manifestfulltextcache, self).get(k, default=default)
1311
1312
1312 def clear(self, clear_persisted_data=False):
1313 def clear(self, clear_persisted_data=False):
1313 super(manifestfulltextcache, self).clear()
1314 super(manifestfulltextcache, self).clear()
1314 if clear_persisted_data:
1315 if clear_persisted_data:
1315 self._dirty = True
1316 self._dirty = True
1316 self.write()
1317 self.write()
1317 self._read = False
1318 self._read = False
1318
1319
1319 @interfaceutil.implementer(repository.imanifeststorage)
1320 @interfaceutil.implementer(repository.imanifeststorage)
1320 class manifestrevlog(object):
1321 class manifestrevlog(object):
1321 '''A revlog that stores manifest texts. This is responsible for caching the
1322 '''A revlog that stores manifest texts. This is responsible for caching the
1322 full-text manifest contents.
1323 full-text manifest contents.
1323 '''
1324 '''
1324 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1325 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1325 treemanifest=False):
1326 treemanifest=False):
1326 """Constructs a new manifest revlog
1327 """Constructs a new manifest revlog
1327
1328
1328 `indexfile` - used by extensions to have two manifests at once, like
1329 `indexfile` - used by extensions to have two manifests at once, like
1329 when transitioning between flatmanifeset and treemanifests.
1330 when transitioning between flatmanifeset and treemanifests.
1330
1331
1331 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1332 `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
1333 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
1334 option takes precedence, so if it is set to True, we ignore whatever
1334 value is passed in to the constructor.
1335 value is passed in to the constructor.
1335 """
1336 """
1336 # During normal operations, we expect to deal with not more than four
1337 # 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
1338 # 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.
1339 # stacks of commits, the number can go up, hence the config knob below.
1339 cachesize = 4
1340 cachesize = 4
1340 optiontreemanifest = False
1341 optiontreemanifest = False
1341 opts = getattr(opener, 'options', None)
1342 opts = getattr(opener, 'options', None)
1342 if opts is not None:
1343 if opts is not None:
1343 cachesize = opts.get('manifestcachesize', cachesize)
1344 cachesize = opts.get('manifestcachesize', cachesize)
1344 optiontreemanifest = opts.get('treemanifest', False)
1345 optiontreemanifest = opts.get('treemanifest', False)
1345
1346
1346 self._treeondisk = optiontreemanifest or treemanifest
1347 self._treeondisk = optiontreemanifest or treemanifest
1347
1348
1348 self._fulltextcache = manifestfulltextcache(cachesize)
1349 self._fulltextcache = manifestfulltextcache(cachesize)
1349
1350
1350 if tree:
1351 if tree:
1351 assert self._treeondisk, 'opts is %r' % opts
1352 assert self._treeondisk, 'opts is %r' % opts
1352
1353
1353 if indexfile is None:
1354 if indexfile is None:
1354 indexfile = '00manifest.i'
1355 indexfile = '00manifest.i'
1355 if tree:
1356 if tree:
1356 indexfile = "meta/" + tree + indexfile
1357 indexfile = "meta/" + tree + indexfile
1357
1358
1358 self.tree = tree
1359 self.tree = tree
1359
1360
1360 # The dirlogcache is kept on the root manifest log
1361 # The dirlogcache is kept on the root manifest log
1361 if tree:
1362 if tree:
1362 self._dirlogcache = dirlogcache
1363 self._dirlogcache = dirlogcache
1363 else:
1364 else:
1364 self._dirlogcache = {'': self}
1365 self._dirlogcache = {'': self}
1365
1366
1366 self._revlog = revlog.revlog(opener, indexfile,
1367 self._revlog = revlog.revlog(opener, indexfile,
1367 # only root indexfile is cached
1368 # only root indexfile is cached
1368 checkambig=not bool(tree),
1369 checkambig=not bool(tree),
1369 mmaplargeindex=True)
1370 mmaplargeindex=True)
1370
1371
1371 self.index = self._revlog.index
1372 self.index = self._revlog.index
1372 self.version = self._revlog.version
1373 self.version = self._revlog.version
1373 self._generaldelta = self._revlog._generaldelta
1374 self._generaldelta = self._revlog._generaldelta
1374
1375
1375 def _setupmanifestcachehooks(self, repo):
1376 def _setupmanifestcachehooks(self, repo):
1376 """Persist the manifestfulltextcache on lock release"""
1377 """Persist the manifestfulltextcache on lock release"""
1377 if not util.safehasattr(repo, '_lockref'):
1378 if not util.safehasattr(repo, '_lockref'):
1378 return
1379 return
1379
1380
1380 self._fulltextcache._opener = repo.cachevfs
1381 self._fulltextcache._opener = repo.cachevfs
1381 reporef = weakref.ref(repo)
1382 reporef = weakref.ref(repo)
1382 manifestrevlogref = weakref.ref(self)
1383 manifestrevlogref = weakref.ref(self)
1383
1384
1384 def persistmanifestcache():
1385 def persistmanifestcache():
1385 repo = reporef()
1386 repo = reporef()
1386 self = manifestrevlogref()
1387 self = manifestrevlogref()
1387 if repo is None or self is None:
1388 if repo is None or self is None:
1388 return
1389 return
1389 if repo.manifestlog.getstorage(b'') is not self:
1390 if repo.manifestlog.getstorage(b'') is not self:
1390 # there's a different manifest in play now, abort
1391 # there's a different manifest in play now, abort
1391 return
1392 return
1392 self._fulltextcache.write()
1393 self._fulltextcache.write()
1393
1394
1394 if repo._currentlock(repo._lockref) is not None:
1395 if repo._currentlock(repo._lockref) is not None:
1395 repo._afterlock(persistmanifestcache)
1396 repo._afterlock(persistmanifestcache)
1396
1397
1397 @property
1398 @property
1398 def fulltextcache(self):
1399 def fulltextcache(self):
1399 return self._fulltextcache
1400 return self._fulltextcache
1400
1401
1401 def clearcaches(self, clear_persisted_data=False):
1402 def clearcaches(self, clear_persisted_data=False):
1402 self._revlog.clearcaches()
1403 self._revlog.clearcaches()
1403 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1404 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1404 self._dirlogcache = {self.tree: self}
1405 self._dirlogcache = {self.tree: self}
1405
1406
1406 def dirlog(self, d):
1407 def dirlog(self, d):
1407 if d:
1408 if d:
1408 assert self._treeondisk
1409 assert self._treeondisk
1409 if d not in self._dirlogcache:
1410 if d not in self._dirlogcache:
1410 mfrevlog = manifestrevlog(self.opener, d,
1411 mfrevlog = manifestrevlog(self.opener, d,
1411 self._dirlogcache,
1412 self._dirlogcache,
1412 treemanifest=self._treeondisk)
1413 treemanifest=self._treeondisk)
1413 self._dirlogcache[d] = mfrevlog
1414 self._dirlogcache[d] = mfrevlog
1414 return self._dirlogcache[d]
1415 return self._dirlogcache[d]
1415
1416
1416 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1417 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1417 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1418 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1418 # If our first parent is in the manifest cache, we can
1419 # If our first parent is in the manifest cache, we can
1419 # compute a delta here using properties we know about the
1420 # compute a delta here using properties we know about the
1420 # manifest up-front, which may save time later for the
1421 # manifest up-front, which may save time later for the
1421 # revlog layer.
1422 # revlog layer.
1422
1423
1423 _checkforbidden(added)
1424 _checkforbidden(added)
1424 # combine the changed lists into one sorted iterator
1425 # combine the changed lists into one sorted iterator
1425 work = heapq.merge([(x, False) for x in added],
1426 work = heapq.merge([(x, False) for x in added],
1426 [(x, True) for x in removed])
1427 [(x, True) for x in removed])
1427
1428
1428 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1429 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1429 cachedelta = self._revlog.rev(p1), deltatext
1430 cachedelta = self._revlog.rev(p1), deltatext
1430 text = util.buffer(arraytext)
1431 text = util.buffer(arraytext)
1431 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1432 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1432 cachedelta)
1433 cachedelta)
1433 else:
1434 else:
1434 # The first parent manifest isn't already loaded, so we'll
1435 # The first parent manifest isn't already loaded, so we'll
1435 # just encode a fulltext of the manifest and pass that
1436 # just encode a fulltext of the manifest and pass that
1436 # through to the revlog layer, and let it handle the delta
1437 # through to the revlog layer, and let it handle the delta
1437 # process.
1438 # process.
1438 if self._treeondisk:
1439 if self._treeondisk:
1439 assert readtree, "readtree must be set for treemanifest writes"
1440 assert readtree, "readtree must be set for treemanifest writes"
1440 m1 = readtree(self.tree, p1)
1441 m1 = readtree(self.tree, p1)
1441 m2 = readtree(self.tree, p2)
1442 m2 = readtree(self.tree, p2)
1442 n = self._addtree(m, transaction, link, m1, m2, readtree)
1443 n = self._addtree(m, transaction, link, m1, m2, readtree)
1443 arraytext = None
1444 arraytext = None
1444 else:
1445 else:
1445 text = m.text()
1446 text = m.text()
1446 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1447 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1447 arraytext = bytearray(text)
1448 arraytext = bytearray(text)
1448
1449
1449 if arraytext is not None:
1450 if arraytext is not None:
1450 self.fulltextcache[n] = arraytext
1451 self.fulltextcache[n] = arraytext
1451
1452
1452 return n
1453 return n
1453
1454
1454 def _addtree(self, m, transaction, link, m1, m2, readtree):
1455 def _addtree(self, m, transaction, link, m1, m2, readtree):
1455 # If the manifest is unchanged compared to one parent,
1456 # If the manifest is unchanged compared to one parent,
1456 # don't write a new revision
1457 # don't write a new revision
1457 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1458 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1458 m2)):
1459 m2)):
1459 return m.node()
1460 return m.node()
1460 def writesubtree(subm, subp1, subp2):
1461 def writesubtree(subm, subp1, subp2):
1461 sublog = self.dirlog(subm.dir())
1462 sublog = self.dirlog(subm.dir())
1462 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1463 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1463 readtree=readtree)
1464 readtree=readtree)
1464 m.writesubtrees(m1, m2, writesubtree)
1465 m.writesubtrees(m1, m2, writesubtree)
1465 text = m.dirtext()
1466 text = m.dirtext()
1466 n = None
1467 n = None
1467 if self.tree != '':
1468 if self.tree != '':
1468 # Double-check whether contents are unchanged to one parent
1469 # Double-check whether contents are unchanged to one parent
1469 if text == m1.dirtext():
1470 if text == m1.dirtext():
1470 n = m1.node()
1471 n = m1.node()
1471 elif text == m2.dirtext():
1472 elif text == m2.dirtext():
1472 n = m2.node()
1473 n = m2.node()
1473
1474
1474 if not n:
1475 if not n:
1475 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1476 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1476 m2.node())
1477 m2.node())
1477
1478
1478 # Save nodeid so parent manifest can calculate its nodeid
1479 # Save nodeid so parent manifest can calculate its nodeid
1479 m.setnode(n)
1480 m.setnode(n)
1480 return n
1481 return n
1481
1482
1482 def __len__(self):
1483 def __len__(self):
1483 return len(self._revlog)
1484 return len(self._revlog)
1484
1485
1485 def __iter__(self):
1486 def __iter__(self):
1486 return self._revlog.__iter__()
1487 return self._revlog.__iter__()
1487
1488
1488 def rev(self, node):
1489 def rev(self, node):
1489 return self._revlog.rev(node)
1490 return self._revlog.rev(node)
1490
1491
1491 def node(self, rev):
1492 def node(self, rev):
1492 return self._revlog.node(rev)
1493 return self._revlog.node(rev)
1493
1494
1494 def lookup(self, value):
1495 def lookup(self, value):
1495 return self._revlog.lookup(value)
1496 return self._revlog.lookup(value)
1496
1497
1497 def parentrevs(self, rev):
1498 def parentrevs(self, rev):
1498 return self._revlog.parentrevs(rev)
1499 return self._revlog.parentrevs(rev)
1499
1500
1500 def parents(self, node):
1501 def parents(self, node):
1501 return self._revlog.parents(node)
1502 return self._revlog.parents(node)
1502
1503
1503 def linkrev(self, rev):
1504 def linkrev(self, rev):
1504 return self._revlog.linkrev(rev)
1505 return self._revlog.linkrev(rev)
1505
1506
1506 def checksize(self):
1507 def checksize(self):
1507 return self._revlog.checksize()
1508 return self._revlog.checksize()
1508
1509
1509 def revision(self, node, _df=None, raw=False):
1510 def revision(self, node, _df=None, raw=False):
1510 return self._revlog.revision(node, _df=_df, raw=raw)
1511 return self._revlog.revision(node, _df=_df, raw=raw)
1511
1512
1512 def revdiff(self, rev1, rev2):
1513 def revdiff(self, rev1, rev2):
1513 return self._revlog.revdiff(rev1, rev2)
1514 return self._revlog.revdiff(rev1, rev2)
1514
1515
1515 def cmp(self, node, text):
1516 def cmp(self, node, text):
1516 return self._revlog.cmp(node, text)
1517 return self._revlog.cmp(node, text)
1517
1518
1518 def deltaparent(self, rev):
1519 def deltaparent(self, rev):
1519 return self._revlog.deltaparent(rev)
1520 return self._revlog.deltaparent(rev)
1520
1521
1521 def emitrevisiondeltas(self, requests):
1522 def emitrevisiondeltas(self, requests):
1522 return self._revlog.emitrevisiondeltas(requests)
1523 return self._revlog.emitrevisiondeltas(requests)
1523
1524
1524 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1525 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1525 return self._revlog.addgroup(deltas, linkmapper, transaction,
1526 return self._revlog.addgroup(deltas, linkmapper, transaction,
1526 addrevisioncb=addrevisioncb)
1527 addrevisioncb=addrevisioncb)
1527
1528
1528 def getstrippoint(self, minlink):
1529 def getstrippoint(self, minlink):
1529 return self._revlog.getstrippoint(minlink)
1530 return self._revlog.getstrippoint(minlink)
1530
1531
1531 def strip(self, minlink, transaction):
1532 def strip(self, minlink, transaction):
1532 return self._revlog.strip(minlink, transaction)
1533 return self._revlog.strip(minlink, transaction)
1533
1534
1534 def files(self):
1535 def files(self):
1535 return self._revlog.files()
1536 return self._revlog.files()
1536
1537
1537 def clone(self, tr, destrevlog, **kwargs):
1538 def clone(self, tr, destrevlog, **kwargs):
1538 if not isinstance(destrevlog, manifestrevlog):
1539 if not isinstance(destrevlog, manifestrevlog):
1539 raise error.ProgrammingError('expected manifestrevlog to clone()')
1540 raise error.ProgrammingError('expected manifestrevlog to clone()')
1540
1541
1541 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1542 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1542
1543
1543 @property
1544 @property
1544 def indexfile(self):
1545 def indexfile(self):
1545 return self._revlog.indexfile
1546 return self._revlog.indexfile
1546
1547
1547 @indexfile.setter
1548 @indexfile.setter
1548 def indexfile(self, value):
1549 def indexfile(self, value):
1549 self._revlog.indexfile = value
1550 self._revlog.indexfile = value
1550
1551
1551 @property
1552 @property
1552 def opener(self):
1553 def opener(self):
1553 return self._revlog.opener
1554 return self._revlog.opener
1554
1555
1555 @opener.setter
1556 @opener.setter
1556 def opener(self, value):
1557 def opener(self, value):
1557 self._revlog.opener = value
1558 self._revlog.opener = value
1558
1559
1559 @interfaceutil.implementer(repository.imanifestlog)
1560 @interfaceutil.implementer(repository.imanifestlog)
1560 class manifestlog(object):
1561 class manifestlog(object):
1561 """A collection class representing the collection of manifest snapshots
1562 """A collection class representing the collection of manifest snapshots
1562 referenced by commits in the repository.
1563 referenced by commits in the repository.
1563
1564
1564 In this situation, 'manifest' refers to the abstract concept of a snapshot
1565 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
1566 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
1567 class do not care about the implementation details of the actual manifests
1567 they receive (i.e. tree or flat or lazily loaded, etc)."""
1568 they receive (i.e. tree or flat or lazily loaded, etc)."""
1568 def __init__(self, opener, repo):
1569 def __init__(self, opener, repo):
1569 usetreemanifest = False
1570 usetreemanifest = False
1570 cachesize = 4
1571 cachesize = 4
1571
1572
1572 opts = getattr(opener, 'options', None)
1573 opts = getattr(opener, 'options', None)
1573 if opts is not None:
1574 if opts is not None:
1574 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1575 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1575 cachesize = opts.get('manifestcachesize', cachesize)
1576 cachesize = opts.get('manifestcachesize', cachesize)
1576
1577
1577 self._treemanifests = usetreemanifest
1578 self._treemanifests = usetreemanifest
1578
1579
1579 self._rootstore = repo._constructmanifest()
1580 self._rootstore = repo._constructmanifest()
1580 self._rootstore._setupmanifestcachehooks(repo)
1581 self._rootstore._setupmanifestcachehooks(repo)
1581 self._narrowmatch = repo.narrowmatch()
1582 self._narrowmatch = repo.narrowmatch()
1582
1583
1583 # A cache of the manifestctx or treemanifestctx for each directory
1584 # A cache of the manifestctx or treemanifestctx for each directory
1584 self._dirmancache = {}
1585 self._dirmancache = {}
1585 self._dirmancache[''] = util.lrucachedict(cachesize)
1586 self._dirmancache[''] = util.lrucachedict(cachesize)
1586
1587
1587 self._cachesize = cachesize
1588 self._cachesize = cachesize
1588
1589
1589 def __getitem__(self, node):
1590 def __getitem__(self, node):
1590 """Retrieves the manifest instance for the given node. Throws a
1591 """Retrieves the manifest instance for the given node. Throws a
1591 LookupError if not found.
1592 LookupError if not found.
1592 """
1593 """
1593 return self.get('', node)
1594 return self.get('', node)
1594
1595
1595 def get(self, tree, node, verify=True):
1596 def get(self, tree, node, verify=True):
1596 """Retrieves the manifest instance for the given node. Throws a
1597 """Retrieves the manifest instance for the given node. Throws a
1597 LookupError if not found.
1598 LookupError if not found.
1598
1599
1599 `verify` - if True an exception will be thrown if the node is not in
1600 `verify` - if True an exception will be thrown if the node is not in
1600 the revlog
1601 the revlog
1601 """
1602 """
1602 if node in self._dirmancache.get(tree, ()):
1603 if node in self._dirmancache.get(tree, ()):
1603 return self._dirmancache[tree][node]
1604 return self._dirmancache[tree][node]
1604
1605
1605 if not self._narrowmatch.always():
1606 if not self._narrowmatch.always():
1606 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1607 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1607 return excludeddirmanifestctx(tree, node)
1608 return excludeddirmanifestctx(tree, node)
1608 if tree:
1609 if tree:
1609 if self._rootstore._treeondisk:
1610 if self._rootstore._treeondisk:
1610 if verify:
1611 if verify:
1611 # Side-effect is LookupError is raised if node doesn't
1612 # Side-effect is LookupError is raised if node doesn't
1612 # exist.
1613 # exist.
1613 self.getstorage(tree).rev(node)
1614 self.getstorage(tree).rev(node)
1614
1615
1615 m = treemanifestctx(self, tree, node)
1616 m = treemanifestctx(self, tree, node)
1616 else:
1617 else:
1617 raise error.Abort(
1618 raise error.Abort(
1618 _("cannot ask for manifest directory '%s' in a flat "
1619 _("cannot ask for manifest directory '%s' in a flat "
1619 "manifest") % tree)
1620 "manifest") % tree)
1620 else:
1621 else:
1621 if verify:
1622 if verify:
1622 # Side-effect is LookupError is raised if node doesn't exist.
1623 # Side-effect is LookupError is raised if node doesn't exist.
1623 self._rootstore.rev(node)
1624 self._rootstore.rev(node)
1624
1625
1625 if self._treemanifests:
1626 if self._treemanifests:
1626 m = treemanifestctx(self, '', node)
1627 m = treemanifestctx(self, '', node)
1627 else:
1628 else:
1628 m = manifestctx(self, node)
1629 m = manifestctx(self, node)
1629
1630
1630 if node != nullid:
1631 if node != nullid:
1631 mancache = self._dirmancache.get(tree)
1632 mancache = self._dirmancache.get(tree)
1632 if not mancache:
1633 if not mancache:
1633 mancache = util.lrucachedict(self._cachesize)
1634 mancache = util.lrucachedict(self._cachesize)
1634 self._dirmancache[tree] = mancache
1635 self._dirmancache[tree] = mancache
1635 mancache[node] = m
1636 mancache[node] = m
1636 return m
1637 return m
1637
1638
1638 def getstorage(self, tree):
1639 def getstorage(self, tree):
1639 return self._rootstore.dirlog(tree)
1640 return self._rootstore.dirlog(tree)
1640
1641
1641 def clearcaches(self, clear_persisted_data=False):
1642 def clearcaches(self, clear_persisted_data=False):
1642 self._dirmancache.clear()
1643 self._dirmancache.clear()
1643 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1644 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1644
1645
1645 def rev(self, node):
1646 def rev(self, node):
1646 return self._rootstore.rev(node)
1647 return self._rootstore.rev(node)
1647
1648
1648 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1649 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1649 class memmanifestctx(object):
1650 class memmanifestctx(object):
1650 def __init__(self, manifestlog):
1651 def __init__(self, manifestlog):
1651 self._manifestlog = manifestlog
1652 self._manifestlog = manifestlog
1652 self._manifestdict = manifestdict()
1653 self._manifestdict = manifestdict()
1653
1654
1654 def _storage(self):
1655 def _storage(self):
1655 return self._manifestlog.getstorage(b'')
1656 return self._manifestlog.getstorage(b'')
1656
1657
1657 def new(self):
1658 def new(self):
1658 return memmanifestctx(self._manifestlog)
1659 return memmanifestctx(self._manifestlog)
1659
1660
1660 def copy(self):
1661 def copy(self):
1661 memmf = memmanifestctx(self._manifestlog)
1662 memmf = memmanifestctx(self._manifestlog)
1662 memmf._manifestdict = self.read().copy()
1663 memmf._manifestdict = self.read().copy()
1663 return memmf
1664 return memmf
1664
1665
1665 def read(self):
1666 def read(self):
1666 return self._manifestdict
1667 return self._manifestdict
1667
1668
1668 def write(self, transaction, link, p1, p2, added, removed):
1669 def write(self, transaction, link, p1, p2, added, removed):
1669 return self._storage().add(self._manifestdict, transaction, link,
1670 return self._storage().add(self._manifestdict, transaction, link,
1670 p1, p2, added, removed)
1671 p1, p2, added, removed)
1671
1672
1672 @interfaceutil.implementer(repository.imanifestrevisionstored)
1673 @interfaceutil.implementer(repository.imanifestrevisionstored)
1673 class manifestctx(object):
1674 class manifestctx(object):
1674 """A class representing a single revision of a manifest, including its
1675 """A class representing a single revision of a manifest, including its
1675 contents, its parent revs, and its linkrev.
1676 contents, its parent revs, and its linkrev.
1676 """
1677 """
1677 def __init__(self, manifestlog, node):
1678 def __init__(self, manifestlog, node):
1678 self._manifestlog = manifestlog
1679 self._manifestlog = manifestlog
1679 self._data = None
1680 self._data = None
1680
1681
1681 self._node = node
1682 self._node = node
1682
1683
1683 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1684 # 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
1685 # but let's add it later when something needs it and we can load it
1685 # lazily.
1686 # lazily.
1686 #self.p1, self.p2 = store.parents(node)
1687 #self.p1, self.p2 = store.parents(node)
1687 #rev = store.rev(node)
1688 #rev = store.rev(node)
1688 #self.linkrev = store.linkrev(rev)
1689 #self.linkrev = store.linkrev(rev)
1689
1690
1690 def _storage(self):
1691 def _storage(self):
1691 return self._manifestlog.getstorage(b'')
1692 return self._manifestlog.getstorage(b'')
1692
1693
1693 def node(self):
1694 def node(self):
1694 return self._node
1695 return self._node
1695
1696
1696 def new(self):
1697 def new(self):
1697 return memmanifestctx(self._manifestlog)
1698 return memmanifestctx(self._manifestlog)
1698
1699
1699 def copy(self):
1700 def copy(self):
1700 memmf = memmanifestctx(self._manifestlog)
1701 memmf = memmanifestctx(self._manifestlog)
1701 memmf._manifestdict = self.read().copy()
1702 memmf._manifestdict = self.read().copy()
1702 return memmf
1703 return memmf
1703
1704
1704 @propertycache
1705 @propertycache
1705 def parents(self):
1706 def parents(self):
1706 return self._storage().parents(self._node)
1707 return self._storage().parents(self._node)
1707
1708
1708 def read(self):
1709 def read(self):
1709 if self._data is None:
1710 if self._data is None:
1710 if self._node == nullid:
1711 if self._node == nullid:
1711 self._data = manifestdict()
1712 self._data = manifestdict()
1712 else:
1713 else:
1713 store = self._storage()
1714 store = self._storage()
1714 if self._node in store.fulltextcache:
1715 if self._node in store.fulltextcache:
1715 text = pycompat.bytestr(store.fulltextcache[self._node])
1716 text = pycompat.bytestr(store.fulltextcache[self._node])
1716 else:
1717 else:
1717 text = store.revision(self._node)
1718 text = store.revision(self._node)
1718 arraytext = bytearray(text)
1719 arraytext = bytearray(text)
1719 store.fulltextcache[self._node] = arraytext
1720 store.fulltextcache[self._node] = arraytext
1720 self._data = manifestdict(text)
1721 self._data = manifestdict(text)
1721 return self._data
1722 return self._data
1722
1723
1723 def readfast(self, shallow=False):
1724 def readfast(self, shallow=False):
1724 '''Calls either readdelta or read, based on which would be less work.
1725 '''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
1726 readdelta is called if the delta is against the p1, and therefore can be
1726 read quickly.
1727 read quickly.
1727
1728
1728 If `shallow` is True, nothing changes since this is a flat manifest.
1729 If `shallow` is True, nothing changes since this is a flat manifest.
1729 '''
1730 '''
1730 store = self._storage()
1731 store = self._storage()
1731 r = store.rev(self._node)
1732 r = store.rev(self._node)
1732 deltaparent = store.deltaparent(r)
1733 deltaparent = store.deltaparent(r)
1733 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1734 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1734 return self.readdelta()
1735 return self.readdelta()
1735 return self.read()
1736 return self.read()
1736
1737
1737 def readdelta(self, shallow=False):
1738 def readdelta(self, shallow=False):
1738 '''Returns a manifest containing just the entries that are present
1739 '''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
1740 in this manifest, but not in its p1 manifest. This is efficient to read
1740 if the revlog delta is already p1.
1741 if the revlog delta is already p1.
1741
1742
1742 Changing the value of `shallow` has no effect on flat manifests.
1743 Changing the value of `shallow` has no effect on flat manifests.
1743 '''
1744 '''
1744 store = self._storage()
1745 store = self._storage()
1745 r = store.rev(self._node)
1746 r = store.rev(self._node)
1746 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1747 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1747 return manifestdict(d)
1748 return manifestdict(d)
1748
1749
1749 def find(self, key):
1750 def find(self, key):
1750 return self.read().find(key)
1751 return self.read().find(key)
1751
1752
1752 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1753 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1753 class memtreemanifestctx(object):
1754 class memtreemanifestctx(object):
1754 def __init__(self, manifestlog, dir=''):
1755 def __init__(self, manifestlog, dir=''):
1755 self._manifestlog = manifestlog
1756 self._manifestlog = manifestlog
1756 self._dir = dir
1757 self._dir = dir
1757 self._treemanifest = treemanifest()
1758 self._treemanifest = treemanifest()
1758
1759
1759 def _storage(self):
1760 def _storage(self):
1760 return self._manifestlog.getstorage(b'')
1761 return self._manifestlog.getstorage(b'')
1761
1762
1762 def new(self, dir=''):
1763 def new(self, dir=''):
1763 return memtreemanifestctx(self._manifestlog, dir=dir)
1764 return memtreemanifestctx(self._manifestlog, dir=dir)
1764
1765
1765 def copy(self):
1766 def copy(self):
1766 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1767 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1767 memmf._treemanifest = self._treemanifest.copy()
1768 memmf._treemanifest = self._treemanifest.copy()
1768 return memmf
1769 return memmf
1769
1770
1770 def read(self):
1771 def read(self):
1771 return self._treemanifest
1772 return self._treemanifest
1772
1773
1773 def write(self, transaction, link, p1, p2, added, removed):
1774 def write(self, transaction, link, p1, p2, added, removed):
1774 def readtree(dir, node):
1775 def readtree(dir, node):
1775 return self._manifestlog.get(dir, node).read()
1776 return self._manifestlog.get(dir, node).read()
1776 return self._storage().add(self._treemanifest, transaction, link,
1777 return self._storage().add(self._treemanifest, transaction, link,
1777 p1, p2, added, removed, readtree=readtree)
1778 p1, p2, added, removed, readtree=readtree)
1778
1779
1779 @interfaceutil.implementer(repository.imanifestrevisionstored)
1780 @interfaceutil.implementer(repository.imanifestrevisionstored)
1780 class treemanifestctx(object):
1781 class treemanifestctx(object):
1781 def __init__(self, manifestlog, dir, node):
1782 def __init__(self, manifestlog, dir, node):
1782 self._manifestlog = manifestlog
1783 self._manifestlog = manifestlog
1783 self._dir = dir
1784 self._dir = dir
1784 self._data = None
1785 self._data = None
1785
1786
1786 self._node = node
1787 self._node = node
1787
1788
1788 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1789 # 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
1790 # we can instantiate treemanifestctx objects for directories we don't
1790 # have on disk.
1791 # have on disk.
1791 #self.p1, self.p2 = store.parents(node)
1792 #self.p1, self.p2 = store.parents(node)
1792 #rev = store.rev(node)
1793 #rev = store.rev(node)
1793 #self.linkrev = store.linkrev(rev)
1794 #self.linkrev = store.linkrev(rev)
1794
1795
1795 def _storage(self):
1796 def _storage(self):
1796 narrowmatch = self._manifestlog._narrowmatch
1797 narrowmatch = self._manifestlog._narrowmatch
1797 if not narrowmatch.always():
1798 if not narrowmatch.always():
1798 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1799 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1799 return excludedmanifestrevlog(self._dir)
1800 return excludedmanifestrevlog(self._dir)
1800 return self._manifestlog.getstorage(self._dir)
1801 return self._manifestlog.getstorage(self._dir)
1801
1802
1802 def read(self):
1803 def read(self):
1803 if self._data is None:
1804 if self._data is None:
1804 store = self._storage()
1805 store = self._storage()
1805 if self._node == nullid:
1806 if self._node == nullid:
1806 self._data = treemanifest()
1807 self._data = treemanifest()
1807 # TODO accessing non-public API
1808 # TODO accessing non-public API
1808 elif store._treeondisk:
1809 elif store._treeondisk:
1809 m = treemanifest(dir=self._dir)
1810 m = treemanifest(dir=self._dir)
1810 def gettext():
1811 def gettext():
1811 return store.revision(self._node)
1812 return store.revision(self._node)
1812 def readsubtree(dir, subm):
1813 def readsubtree(dir, subm):
1813 # Set verify to False since we need to be able to create
1814 # Set verify to False since we need to be able to create
1814 # subtrees for trees that don't exist on disk.
1815 # subtrees for trees that don't exist on disk.
1815 return self._manifestlog.get(dir, subm, verify=False).read()
1816 return self._manifestlog.get(dir, subm, verify=False).read()
1816 m.read(gettext, readsubtree)
1817 m.read(gettext, readsubtree)
1817 m.setnode(self._node)
1818 m.setnode(self._node)
1818 self._data = m
1819 self._data = m
1819 else:
1820 else:
1820 if self._node in store.fulltextcache:
1821 if self._node in store.fulltextcache:
1821 text = pycompat.bytestr(store.fulltextcache[self._node])
1822 text = pycompat.bytestr(store.fulltextcache[self._node])
1822 else:
1823 else:
1823 text = store.revision(self._node)
1824 text = store.revision(self._node)
1824 arraytext = bytearray(text)
1825 arraytext = bytearray(text)
1825 store.fulltextcache[self._node] = arraytext
1826 store.fulltextcache[self._node] = arraytext
1826 self._data = treemanifest(dir=self._dir, text=text)
1827 self._data = treemanifest(dir=self._dir, text=text)
1827
1828
1828 return self._data
1829 return self._data
1829
1830
1830 def node(self):
1831 def node(self):
1831 return self._node
1832 return self._node
1832
1833
1833 def new(self, dir=''):
1834 def new(self, dir=''):
1834 return memtreemanifestctx(self._manifestlog, dir=dir)
1835 return memtreemanifestctx(self._manifestlog, dir=dir)
1835
1836
1836 def copy(self):
1837 def copy(self):
1837 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1838 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1838 memmf._treemanifest = self.read().copy()
1839 memmf._treemanifest = self.read().copy()
1839 return memmf
1840 return memmf
1840
1841
1841 @propertycache
1842 @propertycache
1842 def parents(self):
1843 def parents(self):
1843 return self._storage().parents(self._node)
1844 return self._storage().parents(self._node)
1844
1845
1845 def readdelta(self, shallow=False):
1846 def readdelta(self, shallow=False):
1846 '''Returns a manifest containing just the entries that are present
1847 '''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
1848 in this manifest, but not in its p1 manifest. This is efficient to read
1848 if the revlog delta is already p1.
1849 if the revlog delta is already p1.
1849
1850
1850 If `shallow` is True, this will read the delta for this directory,
1851 If `shallow` is True, this will read the delta for this directory,
1851 without recursively reading subdirectory manifests. Instead, any
1852 without recursively reading subdirectory manifests. Instead, any
1852 subdirectory entry will be reported as it appears in the manifest, i.e.
1853 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
1854 the subdirectory will be reported among files and distinguished only by
1854 its 't' flag.
1855 its 't' flag.
1855 '''
1856 '''
1856 store = self._storage()
1857 store = self._storage()
1857 if shallow:
1858 if shallow:
1858 r = store.rev(self._node)
1859 r = store.rev(self._node)
1859 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1860 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1860 return manifestdict(d)
1861 return manifestdict(d)
1861 else:
1862 else:
1862 # Need to perform a slow delta
1863 # Need to perform a slow delta
1863 r0 = store.deltaparent(store.rev(self._node))
1864 r0 = store.deltaparent(store.rev(self._node))
1864 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1865 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1865 m1 = self.read()
1866 m1 = self.read()
1866 md = treemanifest(dir=self._dir)
1867 md = treemanifest(dir=self._dir)
1867 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1868 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1868 if n1:
1869 if n1:
1869 md[f] = n1
1870 md[f] = n1
1870 if fl1:
1871 if fl1:
1871 md.setflag(f, fl1)
1872 md.setflag(f, fl1)
1872 return md
1873 return md
1873
1874
1874 def readfast(self, shallow=False):
1875 def readfast(self, shallow=False):
1875 '''Calls either readdelta or read, based on which would be less work.
1876 '''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
1877 readdelta is called if the delta is against the p1, and therefore can be
1877 read quickly.
1878 read quickly.
1878
1879
1879 If `shallow` is True, it only returns the entries from this manifest,
1880 If `shallow` is True, it only returns the entries from this manifest,
1880 and not any submanifests.
1881 and not any submanifests.
1881 '''
1882 '''
1882 store = self._storage()
1883 store = self._storage()
1883 r = store.rev(self._node)
1884 r = store.rev(self._node)
1884 deltaparent = store.deltaparent(r)
1885 deltaparent = store.deltaparent(r)
1885 if (deltaparent != nullrev and
1886 if (deltaparent != nullrev and
1886 deltaparent in store.parentrevs(r)):
1887 deltaparent in store.parentrevs(r)):
1887 return self.readdelta(shallow=shallow)
1888 return self.readdelta(shallow=shallow)
1888
1889
1889 if shallow:
1890 if shallow:
1890 return manifestdict(store.revision(self._node))
1891 return manifestdict(store.revision(self._node))
1891 else:
1892 else:
1892 return self.read()
1893 return self.read()
1893
1894
1894 def find(self, key):
1895 def find(self, key):
1895 return self.read().find(key)
1896 return self.read().find(key)
1896
1897
1897 class excludeddir(treemanifest):
1898 class excludeddir(treemanifest):
1898 """Stand-in for a directory that is excluded from the repository.
1899 """Stand-in for a directory that is excluded from the repository.
1899
1900
1900 With narrowing active on a repository that uses treemanifests,
1901 With narrowing active on a repository that uses treemanifests,
1901 some of the directory revlogs will be excluded from the resulting
1902 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
1903 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
1904 some sort of pseudo-manifest to surface to internals so we can
1904 detect a merge conflict outside the narrowspec. That's what this
1905 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
1906 class is: it stands in for a directory whose node is known, but
1906 whose contents are unknown.
1907 whose contents are unknown.
1907 """
1908 """
1908 def __init__(self, dir, node):
1909 def __init__(self, dir, node):
1909 super(excludeddir, self).__init__(dir)
1910 super(excludeddir, self).__init__(dir)
1910 self._node = node
1911 self._node = node
1911 # Add an empty file, which will be included by iterators and such,
1912 # Add an empty file, which will be included by iterators and such,
1912 # appearing as the directory itself (i.e. something like "dir/")
1913 # appearing as the directory itself (i.e. something like "dir/")
1913 self._files[''] = node
1914 self._files[''] = node
1914 self._flags[''] = 't'
1915 self._flags[''] = 't'
1915
1916
1916 # Manifests outside the narrowspec should never be modified, so avoid
1917 # Manifests outside the narrowspec should never be modified, so avoid
1917 # copying. This makes a noticeable difference when there are very many
1918 # copying. This makes a noticeable difference when there are very many
1918 # directories outside the narrowspec. Also, it makes sense for the copy to
1919 # 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
1920 # be of the same type as the original, which would not happen with the
1920 # super type's copy().
1921 # super type's copy().
1921 def copy(self):
1922 def copy(self):
1922 return self
1923 return self
1923
1924
1924 class excludeddirmanifestctx(treemanifestctx):
1925 class excludeddirmanifestctx(treemanifestctx):
1925 """context wrapper for excludeddir - see that docstring for rationale"""
1926 """context wrapper for excludeddir - see that docstring for rationale"""
1926 def __init__(self, dir, node):
1927 def __init__(self, dir, node):
1927 self._dir = dir
1928 self._dir = dir
1928 self._node = node
1929 self._node = node
1929
1930
1930 def read(self):
1931 def read(self):
1931 return excludeddir(self._dir, self._node)
1932 return excludeddir(self._dir, self._node)
1932
1933
1933 def write(self, *args):
1934 def write(self, *args):
1934 raise error.ProgrammingError(
1935 raise error.ProgrammingError(
1935 'attempt to write manifest from excluded dir %s' % self._dir)
1936 'attempt to write manifest from excluded dir %s' % self._dir)
1936
1937
1937 class excludedmanifestrevlog(manifestrevlog):
1938 class excludedmanifestrevlog(manifestrevlog):
1938 """Stand-in for excluded treemanifest revlogs.
1939 """Stand-in for excluded treemanifest revlogs.
1939
1940
1940 When narrowing is active on a treemanifest repository, we'll have
1941 When narrowing is active on a treemanifest repository, we'll have
1941 references to directories we can't see due to the revlog being
1942 references to directories we can't see due to the revlog being
1942 skipped. This class exists to conform to the manifestrevlog
1943 skipped. This class exists to conform to the manifestrevlog
1943 interface for those directories and proactively prevent writes to
1944 interface for those directories and proactively prevent writes to
1944 outside the narrowspec.
1945 outside the narrowspec.
1945 """
1946 """
1946
1947
1947 def __init__(self, dir):
1948 def __init__(self, dir):
1948 self._dir = dir
1949 self._dir = dir
1949
1950
1950 def __len__(self):
1951 def __len__(self):
1951 raise error.ProgrammingError(
1952 raise error.ProgrammingError(
1952 'attempt to get length of excluded dir %s' % self._dir)
1953 'attempt to get length of excluded dir %s' % self._dir)
1953
1954
1954 def rev(self, node):
1955 def rev(self, node):
1955 raise error.ProgrammingError(
1956 raise error.ProgrammingError(
1956 'attempt to get rev from excluded dir %s' % self._dir)
1957 'attempt to get rev from excluded dir %s' % self._dir)
1957
1958
1958 def linkrev(self, node):
1959 def linkrev(self, node):
1959 raise error.ProgrammingError(
1960 raise error.ProgrammingError(
1960 'attempt to get linkrev from excluded dir %s' % self._dir)
1961 'attempt to get linkrev from excluded dir %s' % self._dir)
1961
1962
1962 def node(self, rev):
1963 def node(self, rev):
1963 raise error.ProgrammingError(
1964 raise error.ProgrammingError(
1964 'attempt to get node from excluded dir %s' % self._dir)
1965 'attempt to get node from excluded dir %s' % self._dir)
1965
1966
1966 def add(self, *args, **kwargs):
1967 def add(self, *args, **kwargs):
1967 # We should never write entries in dirlogs outside the narrow clone.
1968 # We should never write entries in dirlogs outside the narrow clone.
1968 # However, the method still gets called from writesubtree() in
1969 # However, the method still gets called from writesubtree() in
1969 # _addtree(), so we need to handle it. We should possibly make that
1970 # _addtree(), so we need to handle it. We should possibly make that
1970 # avoid calling add() with a clean manifest (_dirty is always False
1971 # avoid calling add() with a clean manifest (_dirty is always False
1971 # in excludeddir instances).
1972 # in excludeddir instances).
1972 pass
1973 pass
General Comments 0
You need to be logged in to leave comments. Login now