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