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