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