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