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