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