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