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