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