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