##// END OF EJS Templates
manifest: document return type of readfast()...
Augie Fackler -
r24925:d9832a12 default
parent child Browse files
Show More
@@ -1,854 +1,860 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 i18n import _
8 from i18n import _
9 import mdiff, parsers, error, revlog, util
9 import mdiff, parsers, error, revlog, util
10 import array, struct
10 import array, struct
11 import os
11 import os
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 def _parsev1(data):
15 def _parsev1(data):
16 # This method does a little bit of excessive-looking
16 # This method does a little bit of excessive-looking
17 # precondition checking. This is so that the behavior of this
17 # precondition checking. This is so that the behavior of this
18 # class exactly matches its C counterpart to try and help
18 # class exactly matches its C counterpart to try and help
19 # prevent surprise breakage for anyone that develops against
19 # prevent surprise breakage for anyone that develops against
20 # the pure version.
20 # the pure version.
21 if data and data[-1] != '\n':
21 if data and data[-1] != '\n':
22 raise ValueError('Manifest did not end in a newline.')
22 raise ValueError('Manifest did not end in a newline.')
23 prev = None
23 prev = None
24 for l in data.splitlines():
24 for l in data.splitlines():
25 if prev is not None and prev > l:
25 if prev is not None and prev > l:
26 raise ValueError('Manifest lines not in sorted order.')
26 raise ValueError('Manifest lines not in sorted order.')
27 prev = l
27 prev = l
28 f, n = l.split('\0')
28 f, n = l.split('\0')
29 if len(n) > 40:
29 if len(n) > 40:
30 yield f, revlog.bin(n[:40]), n[40:]
30 yield f, revlog.bin(n[:40]), n[40:]
31 else:
31 else:
32 yield f, revlog.bin(n), ''
32 yield f, revlog.bin(n), ''
33
33
34 def _parsev2(data):
34 def _parsev2(data):
35 metadataend = data.find('\n')
35 metadataend = data.find('\n')
36 # Just ignore metadata for now
36 # Just ignore metadata for now
37 pos = metadataend + 1
37 pos = metadataend + 1
38 prevf = ''
38 prevf = ''
39 while pos < len(data):
39 while pos < len(data):
40 end = data.find('\n', pos + 1) # +1 to skip stem length byte
40 end = data.find('\n', pos + 1) # +1 to skip stem length byte
41 if end == -1:
41 if end == -1:
42 raise ValueError('Manifest ended with incomplete file entry.')
42 raise ValueError('Manifest ended with incomplete file entry.')
43 stemlen = ord(data[pos])
43 stemlen = ord(data[pos])
44 items = data[pos + 1:end].split('\0')
44 items = data[pos + 1:end].split('\0')
45 f = prevf[:stemlen] + items[0]
45 f = prevf[:stemlen] + items[0]
46 if prevf > f:
46 if prevf > f:
47 raise ValueError('Manifest entries not in sorted order.')
47 raise ValueError('Manifest entries not in sorted order.')
48 fl = items[1]
48 fl = items[1]
49 # Just ignore metadata (items[2:] for now)
49 # Just ignore metadata (items[2:] for now)
50 n = data[end + 1:end + 21]
50 n = data[end + 1:end + 21]
51 yield f, n, fl
51 yield f, n, fl
52 pos = end + 22
52 pos = end + 22
53 prevf = f
53 prevf = f
54
54
55 def _parse(data):
55 def _parse(data):
56 """Generates (path, node, flags) tuples from a manifest text"""
56 """Generates (path, node, flags) tuples from a manifest text"""
57 if data.startswith('\0'):
57 if data.startswith('\0'):
58 return iter(_parsev2(data))
58 return iter(_parsev2(data))
59 else:
59 else:
60 return iter(_parsev1(data))
60 return iter(_parsev1(data))
61
61
62 def _text(it, usemanifestv2):
62 def _text(it, usemanifestv2):
63 """Given an iterator over (path, node, flags) tuples, returns a manifest
63 """Given an iterator over (path, node, flags) tuples, returns a manifest
64 text"""
64 text"""
65 if usemanifestv2:
65 if usemanifestv2:
66 return _textv2(it)
66 return _textv2(it)
67 else:
67 else:
68 return _textv1(it)
68 return _textv1(it)
69
69
70 def _textv1(it):
70 def _textv1(it):
71 files = []
71 files = []
72 lines = []
72 lines = []
73 _hex = revlog.hex
73 _hex = revlog.hex
74 for f, n, fl in it:
74 for f, n, fl in it:
75 files.append(f)
75 files.append(f)
76 # if this is changed to support newlines in filenames,
76 # if this is changed to support newlines in filenames,
77 # be sure to check the templates/ dir again (especially *-raw.tmpl)
77 # be sure to check the templates/ dir again (especially *-raw.tmpl)
78 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
78 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
79
79
80 _checkforbidden(files)
80 _checkforbidden(files)
81 return ''.join(lines)
81 return ''.join(lines)
82
82
83 def _textv2(it):
83 def _textv2(it):
84 files = []
84 files = []
85 lines = ['\0\n']
85 lines = ['\0\n']
86 prevf = ''
86 prevf = ''
87 for f, n, fl in it:
87 for f, n, fl in it:
88 files.append(f)
88 files.append(f)
89 stem = os.path.commonprefix([prevf, f])
89 stem = os.path.commonprefix([prevf, f])
90 stemlen = min(len(stem), 255)
90 stemlen = min(len(stem), 255)
91 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
91 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
92 prevf = f
92 prevf = f
93 _checkforbidden(files)
93 _checkforbidden(files)
94 return ''.join(lines)
94 return ''.join(lines)
95
95
96 class _lazymanifest(dict):
96 class _lazymanifest(dict):
97 """This is the pure implementation of lazymanifest.
97 """This is the pure implementation of lazymanifest.
98
98
99 It has not been optimized *at all* and is not lazy.
99 It has not been optimized *at all* and is not lazy.
100 """
100 """
101
101
102 def __init__(self, data):
102 def __init__(self, data):
103 dict.__init__(self)
103 dict.__init__(self)
104 for f, n, fl in _parse(data):
104 for f, n, fl in _parse(data):
105 self[f] = n, fl
105 self[f] = n, fl
106
106
107 def __setitem__(self, k, v):
107 def __setitem__(self, k, v):
108 node, flag = v
108 node, flag = v
109 assert node is not None
109 assert node is not None
110 if len(node) > 21:
110 if len(node) > 21:
111 node = node[:21] # match c implementation behavior
111 node = node[:21] # match c implementation behavior
112 dict.__setitem__(self, k, (node, flag))
112 dict.__setitem__(self, k, (node, flag))
113
113
114 def __iter__(self):
114 def __iter__(self):
115 return iter(sorted(dict.keys(self)))
115 return iter(sorted(dict.keys(self)))
116
116
117 def iterkeys(self):
117 def iterkeys(self):
118 return iter(sorted(dict.keys(self)))
118 return iter(sorted(dict.keys(self)))
119
119
120 def iterentries(self):
120 def iterentries(self):
121 return ((f, e[0], e[1]) for f, e in sorted(self.iteritems()))
121 return ((f, e[0], e[1]) for f, e in sorted(self.iteritems()))
122
122
123 def copy(self):
123 def copy(self):
124 c = _lazymanifest('')
124 c = _lazymanifest('')
125 c.update(self)
125 c.update(self)
126 return c
126 return c
127
127
128 def diff(self, m2, clean=False):
128 def diff(self, m2, clean=False):
129 '''Finds changes between the current manifest and m2.'''
129 '''Finds changes between the current manifest and m2.'''
130 diff = {}
130 diff = {}
131
131
132 for fn, e1 in self.iteritems():
132 for fn, e1 in self.iteritems():
133 if fn not in m2:
133 if fn not in m2:
134 diff[fn] = e1, (None, '')
134 diff[fn] = e1, (None, '')
135 else:
135 else:
136 e2 = m2[fn]
136 e2 = m2[fn]
137 if e1 != e2:
137 if e1 != e2:
138 diff[fn] = e1, e2
138 diff[fn] = e1, e2
139 elif clean:
139 elif clean:
140 diff[fn] = None
140 diff[fn] = None
141
141
142 for fn, e2 in m2.iteritems():
142 for fn, e2 in m2.iteritems():
143 if fn not in self:
143 if fn not in self:
144 diff[fn] = (None, ''), e2
144 diff[fn] = (None, ''), e2
145
145
146 return diff
146 return diff
147
147
148 def filtercopy(self, filterfn):
148 def filtercopy(self, filterfn):
149 c = _lazymanifest('')
149 c = _lazymanifest('')
150 for f, n, fl in self.iterentries():
150 for f, n, fl in self.iterentries():
151 if filterfn(f):
151 if filterfn(f):
152 c[f] = n, fl
152 c[f] = n, fl
153 return c
153 return c
154
154
155 def text(self):
155 def text(self):
156 """Get the full data of this manifest as a bytestring."""
156 """Get the full data of this manifest as a bytestring."""
157 return _textv1(self.iterentries())
157 return _textv1(self.iterentries())
158
158
159 try:
159 try:
160 _lazymanifest = parsers.lazymanifest
160 _lazymanifest = parsers.lazymanifest
161 except AttributeError:
161 except AttributeError:
162 pass
162 pass
163
163
164 class manifestdict(object):
164 class manifestdict(object):
165 def __init__(self, data=''):
165 def __init__(self, data=''):
166 if data.startswith('\0'):
166 if data.startswith('\0'):
167 #_lazymanifest can not parse v2
167 #_lazymanifest can not parse v2
168 self._lm = _lazymanifest('')
168 self._lm = _lazymanifest('')
169 for f, n, fl in _parsev2(data):
169 for f, n, fl in _parsev2(data):
170 self._lm[f] = n, fl
170 self._lm[f] = n, fl
171 else:
171 else:
172 self._lm = _lazymanifest(data)
172 self._lm = _lazymanifest(data)
173
173
174 def __getitem__(self, key):
174 def __getitem__(self, key):
175 return self._lm[key][0]
175 return self._lm[key][0]
176
176
177 def find(self, key):
177 def find(self, key):
178 return self._lm[key]
178 return self._lm[key]
179
179
180 def __len__(self):
180 def __len__(self):
181 return len(self._lm)
181 return len(self._lm)
182
182
183 def __setitem__(self, key, node):
183 def __setitem__(self, key, node):
184 self._lm[key] = node, self.flags(key, '')
184 self._lm[key] = node, self.flags(key, '')
185
185
186 def __contains__(self, key):
186 def __contains__(self, key):
187 return key in self._lm
187 return key in self._lm
188
188
189 def __delitem__(self, key):
189 def __delitem__(self, key):
190 del self._lm[key]
190 del self._lm[key]
191
191
192 def __iter__(self):
192 def __iter__(self):
193 return self._lm.__iter__()
193 return self._lm.__iter__()
194
194
195 def iterkeys(self):
195 def iterkeys(self):
196 return self._lm.iterkeys()
196 return self._lm.iterkeys()
197
197
198 def keys(self):
198 def keys(self):
199 return list(self.iterkeys())
199 return list(self.iterkeys())
200
200
201 def filesnotin(self, m2):
201 def filesnotin(self, m2):
202 '''Set of files in this manifest that are not in the other'''
202 '''Set of files in this manifest that are not in the other'''
203 files = set(self)
203 files = set(self)
204 files.difference_update(m2)
204 files.difference_update(m2)
205 return files
205 return files
206
206
207 @propertycache
207 @propertycache
208 def _dirs(self):
208 def _dirs(self):
209 return util.dirs(self)
209 return util.dirs(self)
210
210
211 def dirs(self):
211 def dirs(self):
212 return self._dirs
212 return self._dirs
213
213
214 def hasdir(self, dir):
214 def hasdir(self, dir):
215 return dir in self._dirs
215 return dir in self._dirs
216
216
217 def _filesfastpath(self, match):
217 def _filesfastpath(self, match):
218 '''Checks whether we can correctly and quickly iterate over matcher
218 '''Checks whether we can correctly and quickly iterate over matcher
219 files instead of over manifest files.'''
219 files instead of over manifest files.'''
220 files = match.files()
220 files = match.files()
221 return (len(files) < 100 and (match.isexact() or
221 return (len(files) < 100 and (match.isexact() or
222 (not match.anypats() and util.all(fn in self for fn in files))))
222 (not match.anypats() and util.all(fn in self for fn in files))))
223
223
224 def walk(self, match):
224 def walk(self, match):
225 '''Generates matching file names.
225 '''Generates matching file names.
226
226
227 Equivalent to manifest.matches(match).iterkeys(), but without creating
227 Equivalent to manifest.matches(match).iterkeys(), but without creating
228 an entirely new manifest.
228 an entirely new manifest.
229
229
230 It also reports nonexistent files by marking them bad with match.bad().
230 It also reports nonexistent files by marking them bad with match.bad().
231 '''
231 '''
232 if match.always():
232 if match.always():
233 for f in iter(self):
233 for f in iter(self):
234 yield f
234 yield f
235 return
235 return
236
236
237 fset = set(match.files())
237 fset = set(match.files())
238
238
239 # avoid the entire walk if we're only looking for specific files
239 # avoid the entire walk if we're only looking for specific files
240 if self._filesfastpath(match):
240 if self._filesfastpath(match):
241 for fn in sorted(fset):
241 for fn in sorted(fset):
242 yield fn
242 yield fn
243 return
243 return
244
244
245 for fn in self:
245 for fn in self:
246 if fn in fset:
246 if fn in fset:
247 # specified pattern is the exact name
247 # specified pattern is the exact name
248 fset.remove(fn)
248 fset.remove(fn)
249 if match(fn):
249 if match(fn):
250 yield fn
250 yield fn
251
251
252 # for dirstate.walk, files=['.'] means "walk the whole tree".
252 # for dirstate.walk, files=['.'] means "walk the whole tree".
253 # follow that here, too
253 # follow that here, too
254 fset.discard('.')
254 fset.discard('.')
255
255
256 for fn in sorted(fset):
256 for fn in sorted(fset):
257 if not self.hasdir(fn):
257 if not self.hasdir(fn):
258 match.bad(fn, None)
258 match.bad(fn, None)
259
259
260 def matches(self, match):
260 def matches(self, match):
261 '''generate a new manifest filtered by the match argument'''
261 '''generate a new manifest filtered by the match argument'''
262 if match.always():
262 if match.always():
263 return self.copy()
263 return self.copy()
264
264
265 if self._filesfastpath(match):
265 if self._filesfastpath(match):
266 m = manifestdict()
266 m = manifestdict()
267 lm = self._lm
267 lm = self._lm
268 for fn in match.files():
268 for fn in match.files():
269 if fn in lm:
269 if fn in lm:
270 m._lm[fn] = lm[fn]
270 m._lm[fn] = lm[fn]
271 return m
271 return m
272
272
273 m = manifestdict()
273 m = manifestdict()
274 m._lm = self._lm.filtercopy(match)
274 m._lm = self._lm.filtercopy(match)
275 return m
275 return m
276
276
277 def diff(self, m2, clean=False):
277 def diff(self, m2, clean=False):
278 '''Finds changes between the current manifest and m2.
278 '''Finds changes between the current manifest and m2.
279
279
280 Args:
280 Args:
281 m2: the manifest to which this manifest should be compared.
281 m2: the manifest to which this manifest should be compared.
282 clean: if true, include files unchanged between these manifests
282 clean: if true, include files unchanged between these manifests
283 with a None value in the returned dictionary.
283 with a None value in the returned dictionary.
284
284
285 The result is returned as a dict with filename as key and
285 The result is returned as a dict with filename as key and
286 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
286 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
287 nodeid in the current/other manifest and fl1/fl2 is the flag
287 nodeid in the current/other manifest and fl1/fl2 is the flag
288 in the current/other manifest. Where the file does not exist,
288 in the current/other manifest. Where the file does not exist,
289 the nodeid will be None and the flags will be the empty
289 the nodeid will be None and the flags will be the empty
290 string.
290 string.
291 '''
291 '''
292 return self._lm.diff(m2._lm, clean)
292 return self._lm.diff(m2._lm, clean)
293
293
294 def setflag(self, key, flag):
294 def setflag(self, key, flag):
295 self._lm[key] = self[key], flag
295 self._lm[key] = self[key], flag
296
296
297 def get(self, key, default=None):
297 def get(self, key, default=None):
298 try:
298 try:
299 return self._lm[key][0]
299 return self._lm[key][0]
300 except KeyError:
300 except KeyError:
301 return default
301 return default
302
302
303 def flags(self, key, default=''):
303 def flags(self, key, default=''):
304 try:
304 try:
305 return self._lm[key][1]
305 return self._lm[key][1]
306 except KeyError:
306 except KeyError:
307 return default
307 return default
308
308
309 def copy(self):
309 def copy(self):
310 c = manifestdict()
310 c = manifestdict()
311 c._lm = self._lm.copy()
311 c._lm = self._lm.copy()
312 return c
312 return c
313
313
314 def iteritems(self):
314 def iteritems(self):
315 return (x[:2] for x in self._lm.iterentries())
315 return (x[:2] for x in self._lm.iterentries())
316
316
317 def text(self, usemanifestv2=False):
317 def text(self, usemanifestv2=False):
318 if usemanifestv2:
318 if usemanifestv2:
319 return _textv2(self._lm.iterentries())
319 return _textv2(self._lm.iterentries())
320 else:
320 else:
321 # use (probably) native version for v1
321 # use (probably) native version for v1
322 return self._lm.text()
322 return self._lm.text()
323
323
324 def fastdelta(self, base, changes):
324 def fastdelta(self, base, changes):
325 """Given a base manifest text as an array.array and a list of changes
325 """Given a base manifest text as an array.array and a list of changes
326 relative to that text, compute a delta that can be used by revlog.
326 relative to that text, compute a delta that can be used by revlog.
327 """
327 """
328 delta = []
328 delta = []
329 dstart = None
329 dstart = None
330 dend = None
330 dend = None
331 dline = [""]
331 dline = [""]
332 start = 0
332 start = 0
333 # zero copy representation of base as a buffer
333 # zero copy representation of base as a buffer
334 addbuf = util.buffer(base)
334 addbuf = util.buffer(base)
335
335
336 # start with a readonly loop that finds the offset of
336 # start with a readonly loop that finds the offset of
337 # each line and creates the deltas
337 # each line and creates the deltas
338 for f, todelete in changes:
338 for f, todelete in changes:
339 # bs will either be the index of the item or the insert point
339 # bs will either be the index of the item or the insert point
340 start, end = _msearch(addbuf, f, start)
340 start, end = _msearch(addbuf, f, start)
341 if not todelete:
341 if not todelete:
342 h, fl = self._lm[f]
342 h, fl = self._lm[f]
343 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
343 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
344 else:
344 else:
345 if start == end:
345 if start == end:
346 # item we want to delete was not found, error out
346 # item we want to delete was not found, error out
347 raise AssertionError(
347 raise AssertionError(
348 _("failed to remove %s from manifest") % f)
348 _("failed to remove %s from manifest") % f)
349 l = ""
349 l = ""
350 if dstart is not None and dstart <= start and dend >= start:
350 if dstart is not None and dstart <= start and dend >= start:
351 if dend < end:
351 if dend < end:
352 dend = end
352 dend = end
353 if l:
353 if l:
354 dline.append(l)
354 dline.append(l)
355 else:
355 else:
356 if dstart is not None:
356 if dstart is not None:
357 delta.append([dstart, dend, "".join(dline)])
357 delta.append([dstart, dend, "".join(dline)])
358 dstart = start
358 dstart = start
359 dend = end
359 dend = end
360 dline = [l]
360 dline = [l]
361
361
362 if dstart is not None:
362 if dstart is not None:
363 delta.append([dstart, dend, "".join(dline)])
363 delta.append([dstart, dend, "".join(dline)])
364 # apply the delta to the base, and get a delta for addrevision
364 # apply the delta to the base, and get a delta for addrevision
365 deltatext, arraytext = _addlistdelta(base, delta)
365 deltatext, arraytext = _addlistdelta(base, delta)
366 return arraytext, deltatext
366 return arraytext, deltatext
367
367
368 def _msearch(m, s, lo=0, hi=None):
368 def _msearch(m, s, lo=0, hi=None):
369 '''return a tuple (start, end) that says where to find s within m.
369 '''return a tuple (start, end) that says where to find s within m.
370
370
371 If the string is found m[start:end] are the line containing
371 If the string is found m[start:end] are the line containing
372 that string. If start == end the string was not found and
372 that string. If start == end the string was not found and
373 they indicate the proper sorted insertion point.
373 they indicate the proper sorted insertion point.
374
374
375 m should be a buffer or a string
375 m should be a buffer or a string
376 s is a string'''
376 s is a string'''
377 def advance(i, c):
377 def advance(i, c):
378 while i < lenm and m[i] != c:
378 while i < lenm and m[i] != c:
379 i += 1
379 i += 1
380 return i
380 return i
381 if not s:
381 if not s:
382 return (lo, lo)
382 return (lo, lo)
383 lenm = len(m)
383 lenm = len(m)
384 if not hi:
384 if not hi:
385 hi = lenm
385 hi = lenm
386 while lo < hi:
386 while lo < hi:
387 mid = (lo + hi) // 2
387 mid = (lo + hi) // 2
388 start = mid
388 start = mid
389 while start > 0 and m[start - 1] != '\n':
389 while start > 0 and m[start - 1] != '\n':
390 start -= 1
390 start -= 1
391 end = advance(start, '\0')
391 end = advance(start, '\0')
392 if m[start:end] < s:
392 if m[start:end] < s:
393 # we know that after the null there are 40 bytes of sha1
393 # we know that after the null there are 40 bytes of sha1
394 # this translates to the bisect lo = mid + 1
394 # this translates to the bisect lo = mid + 1
395 lo = advance(end + 40, '\n') + 1
395 lo = advance(end + 40, '\n') + 1
396 else:
396 else:
397 # this translates to the bisect hi = mid
397 # this translates to the bisect hi = mid
398 hi = start
398 hi = start
399 end = advance(lo, '\0')
399 end = advance(lo, '\0')
400 found = m[lo:end]
400 found = m[lo:end]
401 if s == found:
401 if s == found:
402 # we know that after the null there are 40 bytes of sha1
402 # we know that after the null there are 40 bytes of sha1
403 end = advance(end + 40, '\n')
403 end = advance(end + 40, '\n')
404 return (lo, end + 1)
404 return (lo, end + 1)
405 else:
405 else:
406 return (lo, lo)
406 return (lo, lo)
407
407
408 def _checkforbidden(l):
408 def _checkforbidden(l):
409 """Check filenames for illegal characters."""
409 """Check filenames for illegal characters."""
410 for f in l:
410 for f in l:
411 if '\n' in f or '\r' in f:
411 if '\n' in f or '\r' in f:
412 raise error.RevlogError(
412 raise error.RevlogError(
413 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
413 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
414
414
415
415
416 # apply the changes collected during the bisect loop to our addlist
416 # apply the changes collected during the bisect loop to our addlist
417 # return a delta suitable for addrevision
417 # return a delta suitable for addrevision
418 def _addlistdelta(addlist, x):
418 def _addlistdelta(addlist, x):
419 # for large addlist arrays, building a new array is cheaper
419 # for large addlist arrays, building a new array is cheaper
420 # than repeatedly modifying the existing one
420 # than repeatedly modifying the existing one
421 currentposition = 0
421 currentposition = 0
422 newaddlist = array.array('c')
422 newaddlist = array.array('c')
423
423
424 for start, end, content in x:
424 for start, end, content in x:
425 newaddlist += addlist[currentposition:start]
425 newaddlist += addlist[currentposition:start]
426 if content:
426 if content:
427 newaddlist += array.array('c', content)
427 newaddlist += array.array('c', content)
428
428
429 currentposition = end
429 currentposition = end
430
430
431 newaddlist += addlist[currentposition:]
431 newaddlist += addlist[currentposition:]
432
432
433 deltatext = "".join(struct.pack(">lll", start, end, len(content))
433 deltatext = "".join(struct.pack(">lll", start, end, len(content))
434 + content for start, end, content in x)
434 + content for start, end, content in x)
435 return deltatext, newaddlist
435 return deltatext, newaddlist
436
436
437 def _splittopdir(f):
437 def _splittopdir(f):
438 if '/' in f:
438 if '/' in f:
439 dir, subpath = f.split('/', 1)
439 dir, subpath = f.split('/', 1)
440 return dir + '/', subpath
440 return dir + '/', subpath
441 else:
441 else:
442 return '', f
442 return '', f
443
443
444 class treemanifest(object):
444 class treemanifest(object):
445 def __init__(self, dir='', text=''):
445 def __init__(self, dir='', text=''):
446 self._dir = dir
446 self._dir = dir
447 self._dirs = {}
447 self._dirs = {}
448 # Using _lazymanifest here is a little slower than plain old dicts
448 # Using _lazymanifest here is a little slower than plain old dicts
449 self._files = {}
449 self._files = {}
450 self._flags = {}
450 self._flags = {}
451 self.parse(text)
451 self.parse(text)
452
452
453 def _subpath(self, path):
453 def _subpath(self, path):
454 return self._dir + path
454 return self._dir + path
455
455
456 def __len__(self):
456 def __len__(self):
457 size = len(self._files)
457 size = len(self._files)
458 for m in self._dirs.values():
458 for m in self._dirs.values():
459 size += m.__len__()
459 size += m.__len__()
460 return size
460 return size
461
461
462 def _isempty(self):
462 def _isempty(self):
463 return (not self._files and (not self._dirs or
463 return (not self._files and (not self._dirs or
464 util.all(m._isempty() for m in self._dirs.values())))
464 util.all(m._isempty() for m in self._dirs.values())))
465
465
466 def __str__(self):
466 def __str__(self):
467 return '<treemanifest dir=%s>' % self._dir
467 return '<treemanifest dir=%s>' % self._dir
468
468
469 def iteritems(self):
469 def iteritems(self):
470 for p, n in sorted(self._dirs.items() + self._files.items()):
470 for p, n in sorted(self._dirs.items() + self._files.items()):
471 if p in self._files:
471 if p in self._files:
472 yield self._subpath(p), n
472 yield self._subpath(p), n
473 else:
473 else:
474 for f, sn in n.iteritems():
474 for f, sn in n.iteritems():
475 yield f, sn
475 yield f, sn
476
476
477 def iterkeys(self):
477 def iterkeys(self):
478 for p in sorted(self._dirs.keys() + self._files.keys()):
478 for p in sorted(self._dirs.keys() + self._files.keys()):
479 if p in self._files:
479 if p in self._files:
480 yield self._subpath(p)
480 yield self._subpath(p)
481 else:
481 else:
482 for f in self._dirs[p].iterkeys():
482 for f in self._dirs[p].iterkeys():
483 yield f
483 yield f
484
484
485 def keys(self):
485 def keys(self):
486 return list(self.iterkeys())
486 return list(self.iterkeys())
487
487
488 def __iter__(self):
488 def __iter__(self):
489 return self.iterkeys()
489 return self.iterkeys()
490
490
491 def __contains__(self, f):
491 def __contains__(self, f):
492 if f is None:
492 if f is None:
493 return False
493 return False
494 dir, subpath = _splittopdir(f)
494 dir, subpath = _splittopdir(f)
495 if dir:
495 if dir:
496 if dir not in self._dirs:
496 if dir not in self._dirs:
497 return False
497 return False
498 return self._dirs[dir].__contains__(subpath)
498 return self._dirs[dir].__contains__(subpath)
499 else:
499 else:
500 return f in self._files
500 return f in self._files
501
501
502 def get(self, f, default=None):
502 def get(self, f, default=None):
503 dir, subpath = _splittopdir(f)
503 dir, subpath = _splittopdir(f)
504 if dir:
504 if dir:
505 if dir not in self._dirs:
505 if dir not in self._dirs:
506 return default
506 return default
507 return self._dirs[dir].get(subpath, default)
507 return self._dirs[dir].get(subpath, default)
508 else:
508 else:
509 return self._files.get(f, default)
509 return self._files.get(f, default)
510
510
511 def __getitem__(self, f):
511 def __getitem__(self, f):
512 dir, subpath = _splittopdir(f)
512 dir, subpath = _splittopdir(f)
513 if dir:
513 if dir:
514 return self._dirs[dir].__getitem__(subpath)
514 return self._dirs[dir].__getitem__(subpath)
515 else:
515 else:
516 return self._files[f]
516 return self._files[f]
517
517
518 def flags(self, f):
518 def flags(self, f):
519 dir, subpath = _splittopdir(f)
519 dir, subpath = _splittopdir(f)
520 if dir:
520 if dir:
521 if dir not in self._dirs:
521 if dir not in self._dirs:
522 return ''
522 return ''
523 return self._dirs[dir].flags(subpath)
523 return self._dirs[dir].flags(subpath)
524 else:
524 else:
525 if f in self._dirs:
525 if f in self._dirs:
526 return ''
526 return ''
527 return self._flags.get(f, '')
527 return self._flags.get(f, '')
528
528
529 def find(self, f):
529 def find(self, f):
530 dir, subpath = _splittopdir(f)
530 dir, subpath = _splittopdir(f)
531 if dir:
531 if dir:
532 return self._dirs[dir].find(subpath)
532 return self._dirs[dir].find(subpath)
533 else:
533 else:
534 return self._files[f], self._flags.get(f, '')
534 return self._files[f], self._flags.get(f, '')
535
535
536 def __delitem__(self, f):
536 def __delitem__(self, f):
537 dir, subpath = _splittopdir(f)
537 dir, subpath = _splittopdir(f)
538 if dir:
538 if dir:
539 self._dirs[dir].__delitem__(subpath)
539 self._dirs[dir].__delitem__(subpath)
540 # If the directory is now empty, remove it
540 # If the directory is now empty, remove it
541 if self._dirs[dir]._isempty():
541 if self._dirs[dir]._isempty():
542 del self._dirs[dir]
542 del self._dirs[dir]
543 else:
543 else:
544 del self._files[f]
544 del self._files[f]
545 if f in self._flags:
545 if f in self._flags:
546 del self._flags[f]
546 del self._flags[f]
547
547
548 def __setitem__(self, f, n):
548 def __setitem__(self, f, n):
549 assert n is not None
549 assert n is not None
550 dir, subpath = _splittopdir(f)
550 dir, subpath = _splittopdir(f)
551 if dir:
551 if dir:
552 if dir not in self._dirs:
552 if dir not in self._dirs:
553 self._dirs[dir] = treemanifest(self._subpath(dir))
553 self._dirs[dir] = treemanifest(self._subpath(dir))
554 self._dirs[dir].__setitem__(subpath, n)
554 self._dirs[dir].__setitem__(subpath, n)
555 else:
555 else:
556 self._files[f] = n[:21] # to match manifestdict's behavior
556 self._files[f] = n[:21] # to match manifestdict's behavior
557
557
558 def setflag(self, f, flags):
558 def setflag(self, f, flags):
559 """Set the flags (symlink, executable) for path f."""
559 """Set the flags (symlink, executable) for path f."""
560 dir, subpath = _splittopdir(f)
560 dir, subpath = _splittopdir(f)
561 if dir:
561 if dir:
562 if dir not in self._dirs:
562 if dir not in self._dirs:
563 self._dirs[dir] = treemanifest(self._subpath(dir))
563 self._dirs[dir] = treemanifest(self._subpath(dir))
564 self._dirs[dir].setflag(subpath, flags)
564 self._dirs[dir].setflag(subpath, flags)
565 else:
565 else:
566 self._flags[f] = flags
566 self._flags[f] = flags
567
567
568 def copy(self):
568 def copy(self):
569 copy = treemanifest(self._dir)
569 copy = treemanifest(self._dir)
570 for d in self._dirs:
570 for d in self._dirs:
571 copy._dirs[d] = self._dirs[d].copy()
571 copy._dirs[d] = self._dirs[d].copy()
572 copy._files = dict.copy(self._files)
572 copy._files = dict.copy(self._files)
573 copy._flags = dict.copy(self._flags)
573 copy._flags = dict.copy(self._flags)
574 return copy
574 return copy
575
575
576 def filesnotin(self, m2):
576 def filesnotin(self, m2):
577 '''Set of files in this manifest that are not in the other'''
577 '''Set of files in this manifest that are not in the other'''
578 files = set()
578 files = set()
579 def _filesnotin(t1, t2):
579 def _filesnotin(t1, t2):
580 for d, m1 in t1._dirs.iteritems():
580 for d, m1 in t1._dirs.iteritems():
581 if d in t2._dirs:
581 if d in t2._dirs:
582 m2 = t2._dirs[d]
582 m2 = t2._dirs[d]
583 _filesnotin(m1, m2)
583 _filesnotin(m1, m2)
584 else:
584 else:
585 files.update(m1.iterkeys())
585 files.update(m1.iterkeys())
586
586
587 for fn in t1._files.iterkeys():
587 for fn in t1._files.iterkeys():
588 if fn not in t2._files:
588 if fn not in t2._files:
589 files.add(t1._subpath(fn))
589 files.add(t1._subpath(fn))
590
590
591 _filesnotin(self, m2)
591 _filesnotin(self, m2)
592 return files
592 return files
593
593
594 @propertycache
594 @propertycache
595 def _alldirs(self):
595 def _alldirs(self):
596 return util.dirs(self)
596 return util.dirs(self)
597
597
598 def dirs(self):
598 def dirs(self):
599 return self._alldirs
599 return self._alldirs
600
600
601 def hasdir(self, dir):
601 def hasdir(self, dir):
602 topdir, subdir = _splittopdir(dir)
602 topdir, subdir = _splittopdir(dir)
603 if topdir:
603 if topdir:
604 if topdir in self._dirs:
604 if topdir in self._dirs:
605 return self._dirs[topdir].hasdir(subdir)
605 return self._dirs[topdir].hasdir(subdir)
606 return False
606 return False
607 return (dir + '/') in self._dirs
607 return (dir + '/') in self._dirs
608
608
609 def walk(self, match):
609 def walk(self, match):
610 '''Generates matching file names.
610 '''Generates matching file names.
611
611
612 Equivalent to manifest.matches(match).iterkeys(), but without creating
612 Equivalent to manifest.matches(match).iterkeys(), but without creating
613 an entirely new manifest.
613 an entirely new manifest.
614
614
615 It also reports nonexistent files by marking them bad with match.bad().
615 It also reports nonexistent files by marking them bad with match.bad().
616 '''
616 '''
617 if match.always():
617 if match.always():
618 for f in iter(self):
618 for f in iter(self):
619 yield f
619 yield f
620 return
620 return
621
621
622 fset = set(match.files())
622 fset = set(match.files())
623
623
624 for fn in self._walk(match):
624 for fn in self._walk(match):
625 if fn in fset:
625 if fn in fset:
626 # specified pattern is the exact name
626 # specified pattern is the exact name
627 fset.remove(fn)
627 fset.remove(fn)
628 yield fn
628 yield fn
629
629
630 # for dirstate.walk, files=['.'] means "walk the whole tree".
630 # for dirstate.walk, files=['.'] means "walk the whole tree".
631 # follow that here, too
631 # follow that here, too
632 fset.discard('.')
632 fset.discard('.')
633
633
634 for fn in sorted(fset):
634 for fn in sorted(fset):
635 if not self.hasdir(fn):
635 if not self.hasdir(fn):
636 match.bad(fn, None)
636 match.bad(fn, None)
637
637
638 def _walk(self, match, alldirs=False):
638 def _walk(self, match, alldirs=False):
639 '''Recursively generates matching file names for walk().
639 '''Recursively generates matching file names for walk().
640
640
641 Will visit all subdirectories if alldirs is True, otherwise it will
641 Will visit all subdirectories if alldirs is True, otherwise it will
642 only visit subdirectories for which match.visitdir is True.'''
642 only visit subdirectories for which match.visitdir is True.'''
643
643
644 if not alldirs:
644 if not alldirs:
645 # substring to strip trailing slash
645 # substring to strip trailing slash
646 visit = match.visitdir(self._dir[:-1] or '.')
646 visit = match.visitdir(self._dir[:-1] or '.')
647 if not visit:
647 if not visit:
648 return
648 return
649 alldirs = (visit == 'all')
649 alldirs = (visit == 'all')
650
650
651 # yield this dir's files and walk its submanifests
651 # yield this dir's files and walk its submanifests
652 for p in sorted(self._dirs.keys() + self._files.keys()):
652 for p in sorted(self._dirs.keys() + self._files.keys()):
653 if p in self._files:
653 if p in self._files:
654 fullp = self._subpath(p)
654 fullp = self._subpath(p)
655 if match(fullp):
655 if match(fullp):
656 yield fullp
656 yield fullp
657 else:
657 else:
658 for f in self._dirs[p]._walk(match, alldirs):
658 for f in self._dirs[p]._walk(match, alldirs):
659 yield f
659 yield f
660
660
661 def matches(self, match):
661 def matches(self, match):
662 '''generate a new manifest filtered by the match argument'''
662 '''generate a new manifest filtered by the match argument'''
663 if match.always():
663 if match.always():
664 return self.copy()
664 return self.copy()
665
665
666 return self._matches(match)
666 return self._matches(match)
667
667
668 def _matches(self, match, alldirs=False):
668 def _matches(self, match, alldirs=False):
669 '''recursively generate a new manifest filtered by the match argument.
669 '''recursively generate a new manifest filtered by the match argument.
670
670
671 Will visit all subdirectories if alldirs is True, otherwise it will
671 Will visit all subdirectories if alldirs is True, otherwise it will
672 only visit subdirectories for which match.visitdir is True.'''
672 only visit subdirectories for which match.visitdir is True.'''
673
673
674 ret = treemanifest(self._dir)
674 ret = treemanifest(self._dir)
675 if not alldirs:
675 if not alldirs:
676 # substring to strip trailing slash
676 # substring to strip trailing slash
677 visit = match.visitdir(self._dir[:-1] or '.')
677 visit = match.visitdir(self._dir[:-1] or '.')
678 if not visit:
678 if not visit:
679 return ret
679 return ret
680 alldirs = (visit == 'all')
680 alldirs = (visit == 'all')
681
681
682 for fn in self._files:
682 for fn in self._files:
683 fullp = self._subpath(fn)
683 fullp = self._subpath(fn)
684 if not match(fullp):
684 if not match(fullp):
685 continue
685 continue
686 ret._files[fn] = self._files[fn]
686 ret._files[fn] = self._files[fn]
687 if fn in self._flags:
687 if fn in self._flags:
688 ret._flags[fn] = self._flags[fn]
688 ret._flags[fn] = self._flags[fn]
689
689
690 for dir, subm in self._dirs.iteritems():
690 for dir, subm in self._dirs.iteritems():
691 m = subm._matches(match, alldirs)
691 m = subm._matches(match, alldirs)
692 if not m._isempty():
692 if not m._isempty():
693 ret._dirs[dir] = m
693 ret._dirs[dir] = m
694
694
695 return ret
695 return ret
696
696
697 def diff(self, m2, clean=False):
697 def diff(self, m2, clean=False):
698 '''Finds changes between the current manifest and m2.
698 '''Finds changes between the current manifest and m2.
699
699
700 Args:
700 Args:
701 m2: the manifest to which this manifest should be compared.
701 m2: the manifest to which this manifest should be compared.
702 clean: if true, include files unchanged between these manifests
702 clean: if true, include files unchanged between these manifests
703 with a None value in the returned dictionary.
703 with a None value in the returned dictionary.
704
704
705 The result is returned as a dict with filename as key and
705 The result is returned as a dict with filename as key and
706 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
706 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
707 nodeid in the current/other manifest and fl1/fl2 is the flag
707 nodeid in the current/other manifest and fl1/fl2 is the flag
708 in the current/other manifest. Where the file does not exist,
708 in the current/other manifest. Where the file does not exist,
709 the nodeid will be None and the flags will be the empty
709 the nodeid will be None and the flags will be the empty
710 string.
710 string.
711 '''
711 '''
712 result = {}
712 result = {}
713 emptytree = treemanifest()
713 emptytree = treemanifest()
714 def _diff(t1, t2):
714 def _diff(t1, t2):
715 for d, m1 in t1._dirs.iteritems():
715 for d, m1 in t1._dirs.iteritems():
716 m2 = t2._dirs.get(d, emptytree)
716 m2 = t2._dirs.get(d, emptytree)
717 _diff(m1, m2)
717 _diff(m1, m2)
718
718
719 for d, m2 in t2._dirs.iteritems():
719 for d, m2 in t2._dirs.iteritems():
720 if d not in t1._dirs:
720 if d not in t1._dirs:
721 _diff(emptytree, m2)
721 _diff(emptytree, m2)
722
722
723 for fn, n1 in t1._files.iteritems():
723 for fn, n1 in t1._files.iteritems():
724 fl1 = t1._flags.get(fn, '')
724 fl1 = t1._flags.get(fn, '')
725 n2 = t2._files.get(fn, None)
725 n2 = t2._files.get(fn, None)
726 fl2 = t2._flags.get(fn, '')
726 fl2 = t2._flags.get(fn, '')
727 if n1 != n2 or fl1 != fl2:
727 if n1 != n2 or fl1 != fl2:
728 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
728 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
729 elif clean:
729 elif clean:
730 result[t1._subpath(fn)] = None
730 result[t1._subpath(fn)] = None
731
731
732 for fn, n2 in t2._files.iteritems():
732 for fn, n2 in t2._files.iteritems():
733 if fn not in t1._files:
733 if fn not in t1._files:
734 fl2 = t2._flags.get(fn, '')
734 fl2 = t2._flags.get(fn, '')
735 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
735 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
736
736
737 _diff(self, m2)
737 _diff(self, m2)
738 return result
738 return result
739
739
740 def parse(self, text):
740 def parse(self, text):
741 for f, n, fl in _parse(text):
741 for f, n, fl in _parse(text):
742 self[f] = n
742 self[f] = n
743 if fl:
743 if fl:
744 self.setflag(f, fl)
744 self.setflag(f, fl)
745
745
746 def text(self, usemanifestv2=False):
746 def text(self, usemanifestv2=False):
747 """Get the full data of this manifest as a bytestring."""
747 """Get the full data of this manifest as a bytestring."""
748 flags = self.flags
748 flags = self.flags
749 return _text(((f, self[f], flags(f)) for f in self.keys()),
749 return _text(((f, self[f], flags(f)) for f in self.keys()),
750 usemanifestv2)
750 usemanifestv2)
751
751
752 class manifest(revlog.revlog):
752 class manifest(revlog.revlog):
753 def __init__(self, opener):
753 def __init__(self, opener):
754 # During normal operations, we expect to deal with not more than four
754 # During normal operations, we expect to deal with not more than four
755 # revs at a time (such as during commit --amend). When rebasing large
755 # revs at a time (such as during commit --amend). When rebasing large
756 # stacks of commits, the number can go up, hence the config knob below.
756 # stacks of commits, the number can go up, hence the config knob below.
757 cachesize = 4
757 cachesize = 4
758 usetreemanifest = False
758 usetreemanifest = False
759 usemanifestv2 = False
759 usemanifestv2 = False
760 opts = getattr(opener, 'options', None)
760 opts = getattr(opener, 'options', None)
761 if opts is not None:
761 if opts is not None:
762 cachesize = opts.get('manifestcachesize', cachesize)
762 cachesize = opts.get('manifestcachesize', cachesize)
763 usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
763 usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
764 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
764 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
765 self._mancache = util.lrucachedict(cachesize)
765 self._mancache = util.lrucachedict(cachesize)
766 revlog.revlog.__init__(self, opener, "00manifest.i")
766 revlog.revlog.__init__(self, opener, "00manifest.i")
767 self._treeinmem = usetreemanifest
767 self._treeinmem = usetreemanifest
768 self._treeondisk = usetreemanifest
768 self._treeondisk = usetreemanifest
769 self._usemanifestv2 = usemanifestv2
769 self._usemanifestv2 = usemanifestv2
770
770
771 def _newmanifest(self, data=''):
771 def _newmanifest(self, data=''):
772 if self._treeinmem:
772 if self._treeinmem:
773 return treemanifest('', data)
773 return treemanifest('', data)
774 return manifestdict(data)
774 return manifestdict(data)
775
775
776 def _slowreaddelta(self, node):
776 def _slowreaddelta(self, node):
777 r0 = self.deltaparent(self.rev(node))
777 r0 = self.deltaparent(self.rev(node))
778 m0 = self.read(self.node(r0))
778 m0 = self.read(self.node(r0))
779 m1 = self.read(node)
779 m1 = self.read(node)
780 md = self._newmanifest()
780 md = self._newmanifest()
781 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
781 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
782 if n1:
782 if n1:
783 md[f] = n1
783 md[f] = n1
784 if fl1:
784 if fl1:
785 md.setflag(f, fl1)
785 md.setflag(f, fl1)
786 return md
786 return md
787
787
788 def readdelta(self, node):
788 def readdelta(self, node):
789 if self._usemanifestv2 or self._treeondisk:
789 if self._usemanifestv2 or self._treeondisk:
790 return self._slowreaddelta(node)
790 return self._slowreaddelta(node)
791 r = self.rev(node)
791 r = self.rev(node)
792 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
792 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
793 return self._newmanifest(d)
793 return self._newmanifest(d)
794
794
795 def readfast(self, node):
795 def readfast(self, node):
796 '''use the faster of readdelta or read'''
796 '''use the faster of readdelta or read
797
798 This will return a manifest which is either only the files
799 added/modified relative to p1, or all files in the
800 manifest. Which one is returned depends on the codepath used
801 to retrieve the data.
802 '''
797 r = self.rev(node)
803 r = self.rev(node)
798 deltaparent = self.deltaparent(r)
804 deltaparent = self.deltaparent(r)
799 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
805 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
800 return self.readdelta(node)
806 return self.readdelta(node)
801 return self.read(node)
807 return self.read(node)
802
808
803 def read(self, node):
809 def read(self, node):
804 if node == revlog.nullid:
810 if node == revlog.nullid:
805 return self._newmanifest() # don't upset local cache
811 return self._newmanifest() # don't upset local cache
806 if node in self._mancache:
812 if node in self._mancache:
807 return self._mancache[node][0]
813 return self._mancache[node][0]
808 text = self.revision(node)
814 text = self.revision(node)
809 arraytext = array.array('c', text)
815 arraytext = array.array('c', text)
810 m = self._newmanifest(text)
816 m = self._newmanifest(text)
811 self._mancache[node] = (m, arraytext)
817 self._mancache[node] = (m, arraytext)
812 return m
818 return m
813
819
814 def find(self, node, f):
820 def find(self, node, f):
815 '''look up entry for a single file efficiently.
821 '''look up entry for a single file efficiently.
816 return (node, flags) pair if found, (None, None) if not.'''
822 return (node, flags) pair if found, (None, None) if not.'''
817 m = self.read(node)
823 m = self.read(node)
818 try:
824 try:
819 return m.find(f)
825 return m.find(f)
820 except KeyError:
826 except KeyError:
821 return None, None
827 return None, None
822
828
823 def add(self, m, transaction, link, p1, p2, added, removed):
829 def add(self, m, transaction, link, p1, p2, added, removed):
824 if (p1 in self._mancache and not self._treeinmem
830 if (p1 in self._mancache and not self._treeinmem
825 and not self._usemanifestv2):
831 and not self._usemanifestv2):
826 # If our first parent is in the manifest cache, we can
832 # If our first parent is in the manifest cache, we can
827 # compute a delta here using properties we know about the
833 # compute a delta here using properties we know about the
828 # manifest up-front, which may save time later for the
834 # manifest up-front, which may save time later for the
829 # revlog layer.
835 # revlog layer.
830
836
831 _checkforbidden(added)
837 _checkforbidden(added)
832 # combine the changed lists into one list for sorting
838 # combine the changed lists into one list for sorting
833 work = [(x, False) for x in added]
839 work = [(x, False) for x in added]
834 work.extend((x, True) for x in removed)
840 work.extend((x, True) for x in removed)
835 # this could use heapq.merge() (from Python 2.6+) or equivalent
841 # this could use heapq.merge() (from Python 2.6+) or equivalent
836 # since the lists are already sorted
842 # since the lists are already sorted
837 work.sort()
843 work.sort()
838
844
839 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
845 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
840 cachedelta = self.rev(p1), deltatext
846 cachedelta = self.rev(p1), deltatext
841 text = util.buffer(arraytext)
847 text = util.buffer(arraytext)
842 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
848 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
843 else:
849 else:
844 # The first parent manifest isn't already loaded, so we'll
850 # The first parent manifest isn't already loaded, so we'll
845 # just encode a fulltext of the manifest and pass that
851 # just encode a fulltext of the manifest and pass that
846 # through to the revlog layer, and let it handle the delta
852 # through to the revlog layer, and let it handle the delta
847 # process.
853 # process.
848 text = m.text(self._usemanifestv2)
854 text = m.text(self._usemanifestv2)
849 arraytext = array.array('c', text)
855 arraytext = array.array('c', text)
850 n = self.addrevision(text, transaction, link, p1, p2)
856 n = self.addrevision(text, transaction, link, p1, p2)
851
857
852 self._mancache[n] = (m, arraytext)
858 self._mancache[n] = (m, arraytext)
853
859
854 return n
860 return n
General Comments 0
You need to be logged in to leave comments. Login now