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