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