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