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