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