##// END OF EJS Templates
treemanifest: separate flags for trees in memory and trees on disk...
Martin von Zweigbergk -
r24701:03ee5767 default
parent child Browse files
Show More
@@ -1,850 +1,851 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 for f, n, fl in _parse(text):
451 for f, n, fl in _parse(text):
452 self[f] = n
452 self[f] = n
453 if fl:
453 if fl:
454 self.setflag(f, fl)
454 self.setflag(f, fl)
455
455
456 def _subpath(self, path):
456 def _subpath(self, path):
457 return self._dir + path
457 return self._dir + path
458
458
459 def __len__(self):
459 def __len__(self):
460 size = len(self._files)
460 size = len(self._files)
461 for m in self._dirs.values():
461 for m in self._dirs.values():
462 size += m.__len__()
462 size += m.__len__()
463 return size
463 return size
464
464
465 def _isempty(self):
465 def _isempty(self):
466 return (not self._files and (not self._dirs or
466 return (not self._files and (not self._dirs or
467 util.all(m._isempty() for m in self._dirs.values())))
467 util.all(m._isempty() for m in self._dirs.values())))
468
468
469 def __str__(self):
469 def __str__(self):
470 return '<treemanifest dir=%s>' % self._dir
470 return '<treemanifest dir=%s>' % self._dir
471
471
472 def iteritems(self):
472 def iteritems(self):
473 for p, n in sorted(self._dirs.items() + self._files.items()):
473 for p, n in sorted(self._dirs.items() + self._files.items()):
474 if p in self._files:
474 if p in self._files:
475 yield self._subpath(p), n
475 yield self._subpath(p), n
476 else:
476 else:
477 for f, sn in n.iteritems():
477 for f, sn in n.iteritems():
478 yield f, sn
478 yield f, sn
479
479
480 def iterkeys(self):
480 def iterkeys(self):
481 for p in sorted(self._dirs.keys() + self._files.keys()):
481 for p in sorted(self._dirs.keys() + self._files.keys()):
482 if p in self._files:
482 if p in self._files:
483 yield self._subpath(p)
483 yield self._subpath(p)
484 else:
484 else:
485 for f in self._dirs[p].iterkeys():
485 for f in self._dirs[p].iterkeys():
486 yield f
486 yield f
487
487
488 def keys(self):
488 def keys(self):
489 return list(self.iterkeys())
489 return list(self.iterkeys())
490
490
491 def __iter__(self):
491 def __iter__(self):
492 return self.iterkeys()
492 return self.iterkeys()
493
493
494 def __contains__(self, f):
494 def __contains__(self, f):
495 if f is None:
495 if f is None:
496 return False
496 return False
497 dir, subpath = _splittopdir(f)
497 dir, subpath = _splittopdir(f)
498 if dir:
498 if dir:
499 if dir not in self._dirs:
499 if dir not in self._dirs:
500 return False
500 return False
501 return self._dirs[dir].__contains__(subpath)
501 return self._dirs[dir].__contains__(subpath)
502 else:
502 else:
503 return f in self._files
503 return f in self._files
504
504
505 def get(self, f, default=None):
505 def get(self, f, default=None):
506 dir, subpath = _splittopdir(f)
506 dir, subpath = _splittopdir(f)
507 if dir:
507 if dir:
508 if dir not in self._dirs:
508 if dir not in self._dirs:
509 return default
509 return default
510 return self._dirs[dir].get(subpath, default)
510 return self._dirs[dir].get(subpath, default)
511 else:
511 else:
512 return self._files.get(f, default)
512 return self._files.get(f, default)
513
513
514 def __getitem__(self, f):
514 def __getitem__(self, f):
515 dir, subpath = _splittopdir(f)
515 dir, subpath = _splittopdir(f)
516 if dir:
516 if dir:
517 return self._dirs[dir].__getitem__(subpath)
517 return self._dirs[dir].__getitem__(subpath)
518 else:
518 else:
519 return self._files[f]
519 return self._files[f]
520
520
521 def flags(self, f):
521 def flags(self, f):
522 dir, subpath = _splittopdir(f)
522 dir, subpath = _splittopdir(f)
523 if dir:
523 if dir:
524 if dir not in self._dirs:
524 if dir not in self._dirs:
525 return ''
525 return ''
526 return self._dirs[dir].flags(subpath)
526 return self._dirs[dir].flags(subpath)
527 else:
527 else:
528 if f in self._dirs:
528 if f in self._dirs:
529 return ''
529 return ''
530 return self._flags.get(f, '')
530 return self._flags.get(f, '')
531
531
532 def find(self, f):
532 def find(self, f):
533 dir, subpath = _splittopdir(f)
533 dir, subpath = _splittopdir(f)
534 if dir:
534 if dir:
535 return self._dirs[dir].find(subpath)
535 return self._dirs[dir].find(subpath)
536 else:
536 else:
537 return self._files[f], self._flags.get(f, '')
537 return self._files[f], self._flags.get(f, '')
538
538
539 def __delitem__(self, f):
539 def __delitem__(self, f):
540 dir, subpath = _splittopdir(f)
540 dir, subpath = _splittopdir(f)
541 if dir:
541 if dir:
542 self._dirs[dir].__delitem__(subpath)
542 self._dirs[dir].__delitem__(subpath)
543 # If the directory is now empty, remove it
543 # If the directory is now empty, remove it
544 if self._dirs[dir]._isempty():
544 if self._dirs[dir]._isempty():
545 del self._dirs[dir]
545 del self._dirs[dir]
546 else:
546 else:
547 del self._files[f]
547 del self._files[f]
548 if f in self._flags:
548 if f in self._flags:
549 del self._flags[f]
549 del self._flags[f]
550
550
551 def __setitem__(self, f, n):
551 def __setitem__(self, f, n):
552 assert n is not None
552 assert n is not None
553 dir, subpath = _splittopdir(f)
553 dir, subpath = _splittopdir(f)
554 if dir:
554 if dir:
555 if dir not in self._dirs:
555 if dir not in self._dirs:
556 self._dirs[dir] = treemanifest(self._subpath(dir))
556 self._dirs[dir] = treemanifest(self._subpath(dir))
557 self._dirs[dir].__setitem__(subpath, n)
557 self._dirs[dir].__setitem__(subpath, n)
558 else:
558 else:
559 self._files[f] = n[:21] # to match manifestdict's behavior
559 self._files[f] = n[:21] # to match manifestdict's behavior
560
560
561 def setflag(self, f, flags):
561 def setflag(self, f, flags):
562 """Set the flags (symlink, executable) for path f."""
562 """Set the flags (symlink, executable) for path f."""
563 dir, subpath = _splittopdir(f)
563 dir, subpath = _splittopdir(f)
564 if dir:
564 if dir:
565 if dir not in self._dirs:
565 if dir not in self._dirs:
566 self._dirs[dir] = treemanifest(self._subpath(dir))
566 self._dirs[dir] = treemanifest(self._subpath(dir))
567 self._dirs[dir].setflag(subpath, flags)
567 self._dirs[dir].setflag(subpath, flags)
568 else:
568 else:
569 self._flags[f] = flags
569 self._flags[f] = flags
570
570
571 def copy(self):
571 def copy(self):
572 copy = treemanifest(self._dir)
572 copy = treemanifest(self._dir)
573 for d in self._dirs:
573 for d in self._dirs:
574 copy._dirs[d] = self._dirs[d].copy()
574 copy._dirs[d] = self._dirs[d].copy()
575 copy._files = dict.copy(self._files)
575 copy._files = dict.copy(self._files)
576 copy._flags = dict.copy(self._flags)
576 copy._flags = dict.copy(self._flags)
577 return copy
577 return copy
578
578
579 def filesnotin(self, m2):
579 def filesnotin(self, m2):
580 '''Set of files in this manifest that are not in the other'''
580 '''Set of files in this manifest that are not in the other'''
581 files = set()
581 files = set()
582 def _filesnotin(t1, t2):
582 def _filesnotin(t1, t2):
583 for d, m1 in t1._dirs.iteritems():
583 for d, m1 in t1._dirs.iteritems():
584 if d in t2._dirs:
584 if d in t2._dirs:
585 m2 = t2._dirs[d]
585 m2 = t2._dirs[d]
586 _filesnotin(m1, m2)
586 _filesnotin(m1, m2)
587 else:
587 else:
588 files.update(m1.iterkeys())
588 files.update(m1.iterkeys())
589
589
590 for fn in t1._files.iterkeys():
590 for fn in t1._files.iterkeys():
591 if fn not in t2._files:
591 if fn not in t2._files:
592 files.add(t1._subpath(fn))
592 files.add(t1._subpath(fn))
593
593
594 _filesnotin(self, m2)
594 _filesnotin(self, m2)
595 return files
595 return files
596
596
597 @propertycache
597 @propertycache
598 def _alldirs(self):
598 def _alldirs(self):
599 return util.dirs(self)
599 return util.dirs(self)
600
600
601 def dirs(self):
601 def dirs(self):
602 return self._alldirs
602 return self._alldirs
603
603
604 def hasdir(self, dir):
604 def hasdir(self, dir):
605 topdir, subdir = _splittopdir(dir)
605 topdir, subdir = _splittopdir(dir)
606 if topdir:
606 if topdir:
607 if topdir in self._dirs:
607 if topdir in self._dirs:
608 return self._dirs[topdir].hasdir(subdir)
608 return self._dirs[topdir].hasdir(subdir)
609 return False
609 return False
610 return (dir + '/') in self._dirs
610 return (dir + '/') in self._dirs
611
611
612 def walk(self, match):
612 def walk(self, match):
613 '''Generates matching file names.
613 '''Generates matching file names.
614
614
615 Equivalent to manifest.matches(match).iterkeys(), but without creating
615 Equivalent to manifest.matches(match).iterkeys(), but without creating
616 an entirely new manifest.
616 an entirely new manifest.
617
617
618 It also reports nonexistent files by marking them bad with match.bad().
618 It also reports nonexistent files by marking them bad with match.bad().
619 '''
619 '''
620 if match.always():
620 if match.always():
621 for f in iter(self):
621 for f in iter(self):
622 yield f
622 yield f
623 return
623 return
624
624
625 fset = set(match.files())
625 fset = set(match.files())
626
626
627 for fn in self._walk(match):
627 for fn in self._walk(match):
628 if fn in fset:
628 if fn in fset:
629 # specified pattern is the exact name
629 # specified pattern is the exact name
630 fset.remove(fn)
630 fset.remove(fn)
631 yield fn
631 yield fn
632
632
633 # for dirstate.walk, files=['.'] means "walk the whole tree".
633 # for dirstate.walk, files=['.'] means "walk the whole tree".
634 # follow that here, too
634 # follow that here, too
635 fset.discard('.')
635 fset.discard('.')
636
636
637 for fn in sorted(fset):
637 for fn in sorted(fset):
638 if not self.hasdir(fn):
638 if not self.hasdir(fn):
639 match.bad(fn, None)
639 match.bad(fn, None)
640
640
641 def _walk(self, match, alldirs=False):
641 def _walk(self, match, alldirs=False):
642 '''Recursively generates matching file names for walk().
642 '''Recursively generates matching file names for walk().
643
643
644 Will visit all subdirectories if alldirs is True, otherwise it will
644 Will visit all subdirectories if alldirs is True, otherwise it will
645 only visit subdirectories for which match.visitdir is True.'''
645 only visit subdirectories for which match.visitdir is True.'''
646
646
647 if not alldirs:
647 if not alldirs:
648 # substring to strip trailing slash
648 # substring to strip trailing slash
649 visit = match.visitdir(self._dir[:-1] or '.')
649 visit = match.visitdir(self._dir[:-1] or '.')
650 if not visit:
650 if not visit:
651 return
651 return
652 alldirs = (visit == 'all')
652 alldirs = (visit == 'all')
653
653
654 # yield this dir's files and walk its submanifests
654 # yield this dir's files and walk its submanifests
655 for p in sorted(self._dirs.keys() + self._files.keys()):
655 for p in sorted(self._dirs.keys() + self._files.keys()):
656 if p in self._files:
656 if p in self._files:
657 fullp = self._subpath(p)
657 fullp = self._subpath(p)
658 if match(fullp):
658 if match(fullp):
659 yield fullp
659 yield fullp
660 else:
660 else:
661 for f in self._dirs[p]._walk(match, alldirs):
661 for f in self._dirs[p]._walk(match, alldirs):
662 yield f
662 yield f
663
663
664 def matches(self, match):
664 def matches(self, match):
665 '''generate a new manifest filtered by the match argument'''
665 '''generate a new manifest filtered by the match argument'''
666 if match.always():
666 if match.always():
667 return self.copy()
667 return self.copy()
668
668
669 return self._matches(match)
669 return self._matches(match)
670
670
671 def _matches(self, match, alldirs=False):
671 def _matches(self, match, alldirs=False):
672 '''recursively generate a new manifest filtered by the match argument.
672 '''recursively generate a new manifest filtered by the match argument.
673
673
674 Will visit all subdirectories if alldirs is True, otherwise it will
674 Will visit all subdirectories if alldirs is True, otherwise it will
675 only visit subdirectories for which match.visitdir is True.'''
675 only visit subdirectories for which match.visitdir is True.'''
676
676
677 ret = treemanifest(self._dir)
677 ret = treemanifest(self._dir)
678 if not alldirs:
678 if not alldirs:
679 # substring to strip trailing slash
679 # substring to strip trailing slash
680 visit = match.visitdir(self._dir[:-1] or '.')
680 visit = match.visitdir(self._dir[:-1] or '.')
681 if not visit:
681 if not visit:
682 return ret
682 return ret
683 alldirs = (visit == 'all')
683 alldirs = (visit == 'all')
684
684
685 for fn in self._files:
685 for fn in self._files:
686 fullp = self._subpath(fn)
686 fullp = self._subpath(fn)
687 if not match(fullp):
687 if not match(fullp):
688 continue
688 continue
689 ret._files[fn] = self._files[fn]
689 ret._files[fn] = self._files[fn]
690 if fn in self._flags:
690 if fn in self._flags:
691 ret._flags[fn] = self._flags[fn]
691 ret._flags[fn] = self._flags[fn]
692
692
693 for dir, subm in self._dirs.iteritems():
693 for dir, subm in self._dirs.iteritems():
694 m = subm._matches(match, alldirs)
694 m = subm._matches(match, alldirs)
695 if not m._isempty():
695 if not m._isempty():
696 ret._dirs[dir] = m
696 ret._dirs[dir] = m
697
697
698 return ret
698 return ret
699
699
700 def diff(self, m2, clean=False):
700 def diff(self, m2, clean=False):
701 '''Finds changes between the current manifest and m2.
701 '''Finds changes between the current manifest and m2.
702
702
703 Args:
703 Args:
704 m2: the manifest to which this manifest should be compared.
704 m2: the manifest to which this manifest should be compared.
705 clean: if true, include files unchanged between these manifests
705 clean: if true, include files unchanged between these manifests
706 with a None value in the returned dictionary.
706 with a None value in the returned dictionary.
707
707
708 The result is returned as a dict with filename as key and
708 The result is returned as a dict with filename as key and
709 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
709 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
710 nodeid in the current/other manifest and fl1/fl2 is the flag
710 nodeid in the current/other manifest and fl1/fl2 is the flag
711 in the current/other manifest. Where the file does not exist,
711 in the current/other manifest. Where the file does not exist,
712 the nodeid will be None and the flags will be the empty
712 the nodeid will be None and the flags will be the empty
713 string.
713 string.
714 '''
714 '''
715 result = {}
715 result = {}
716 emptytree = treemanifest()
716 emptytree = treemanifest()
717 def _diff(t1, t2):
717 def _diff(t1, t2):
718 for d, m1 in t1._dirs.iteritems():
718 for d, m1 in t1._dirs.iteritems():
719 m2 = t2._dirs.get(d, emptytree)
719 m2 = t2._dirs.get(d, emptytree)
720 _diff(m1, m2)
720 _diff(m1, m2)
721
721
722 for d, m2 in t2._dirs.iteritems():
722 for d, m2 in t2._dirs.iteritems():
723 if d not in t1._dirs:
723 if d not in t1._dirs:
724 _diff(emptytree, m2)
724 _diff(emptytree, m2)
725
725
726 for fn, n1 in t1._files.iteritems():
726 for fn, n1 in t1._files.iteritems():
727 fl1 = t1._flags.get(fn, '')
727 fl1 = t1._flags.get(fn, '')
728 n2 = t2._files.get(fn, None)
728 n2 = t2._files.get(fn, None)
729 fl2 = t2._flags.get(fn, '')
729 fl2 = t2._flags.get(fn, '')
730 if n1 != n2 or fl1 != fl2:
730 if n1 != n2 or fl1 != fl2:
731 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
731 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
732 elif clean:
732 elif clean:
733 result[t1._subpath(fn)] = None
733 result[t1._subpath(fn)] = None
734
734
735 for fn, n2 in t2._files.iteritems():
735 for fn, n2 in t2._files.iteritems():
736 if fn not in t1._files:
736 if fn not in t1._files:
737 fl2 = t2._flags.get(fn, '')
737 fl2 = t2._flags.get(fn, '')
738 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
738 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
739
739
740 _diff(self, m2)
740 _diff(self, m2)
741 return result
741 return result
742
742
743 def text(self, usemanifestv2=False):
743 def text(self, usemanifestv2=False):
744 """Get the full data of this manifest as a bytestring."""
744 """Get the full data of this manifest as a bytestring."""
745 flags = self.flags
745 flags = self.flags
746 return _text(((f, self[f], flags(f)) for f in self.keys()),
746 return _text(((f, self[f], flags(f)) for f in self.keys()),
747 usemanifestv2)
747 usemanifestv2)
748
748
749 class manifest(revlog.revlog):
749 class manifest(revlog.revlog):
750 def __init__(self, opener):
750 def __init__(self, opener):
751 # During normal operations, we expect to deal with not more than four
751 # During normal operations, we expect to deal with not more than four
752 # revs at a time (such as during commit --amend). When rebasing large
752 # revs at a time (such as during commit --amend). When rebasing large
753 # stacks of commits, the number can go up, hence the config knob below.
753 # stacks of commits, the number can go up, hence the config knob below.
754 cachesize = 4
754 cachesize = 4
755 usetreemanifest = False
755 usetreemanifest = False
756 usemanifestv2 = False
756 usemanifestv2 = False
757 opts = getattr(opener, 'options', None)
757 opts = getattr(opener, 'options', None)
758 if opts is not None:
758 if opts is not None:
759 cachesize = opts.get('manifestcachesize', cachesize)
759 cachesize = opts.get('manifestcachesize', cachesize)
760 usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
760 usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
761 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
761 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
762 self._mancache = util.lrucachedict(cachesize)
762 self._mancache = util.lrucachedict(cachesize)
763 revlog.revlog.__init__(self, opener, "00manifest.i")
763 revlog.revlog.__init__(self, opener, "00manifest.i")
764 self._usetreemanifest = usetreemanifest
764 self._treeinmem = usetreemanifest
765 self._treeondisk = usetreemanifest
765 self._usemanifestv2 = usemanifestv2
766 self._usemanifestv2 = usemanifestv2
766
767
767 def _newmanifest(self, data=''):
768 def _newmanifest(self, data=''):
768 if self._usetreemanifest:
769 if self._treeinmem:
769 return treemanifest('', data)
770 return treemanifest('', data)
770 return manifestdict(data)
771 return manifestdict(data)
771
772
772 def _slowreaddelta(self, node):
773 def _slowreaddelta(self, node):
773 r0 = self.deltaparent(self.rev(node))
774 r0 = self.deltaparent(self.rev(node))
774 m0 = self.read(self.node(r0))
775 m0 = self.read(self.node(r0))
775 m1 = self.read(node)
776 m1 = self.read(node)
776 md = self._newmanifest()
777 md = self._newmanifest()
777 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
778 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
778 if n1:
779 if n1:
779 md[f] = n1
780 md[f] = n1
780 if fl1:
781 if fl1:
781 md.setflag(f, fl1)
782 md.setflag(f, fl1)
782 return md
783 return md
783
784
784 def readdelta(self, node):
785 def readdelta(self, node):
785 if self._usemanifestv2 or self._usetreemanifest:
786 if self._usemanifestv2 or self._treeondisk:
786 return self._slowreaddelta(node)
787 return self._slowreaddelta(node)
787 r = self.rev(node)
788 r = self.rev(node)
788 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
789 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
789 return self._newmanifest(d)
790 return self._newmanifest(d)
790
791
791 def readfast(self, node):
792 def readfast(self, node):
792 '''use the faster of readdelta or read'''
793 '''use the faster of readdelta or read'''
793 r = self.rev(node)
794 r = self.rev(node)
794 deltaparent = self.deltaparent(r)
795 deltaparent = self.deltaparent(r)
795 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
796 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
796 return self.readdelta(node)
797 return self.readdelta(node)
797 return self.read(node)
798 return self.read(node)
798
799
799 def read(self, node):
800 def read(self, node):
800 if node == revlog.nullid:
801 if node == revlog.nullid:
801 return self._newmanifest() # don't upset local cache
802 return self._newmanifest() # don't upset local cache
802 if node in self._mancache:
803 if node in self._mancache:
803 return self._mancache[node][0]
804 return self._mancache[node][0]
804 text = self.revision(node)
805 text = self.revision(node)
805 arraytext = array.array('c', text)
806 arraytext = array.array('c', text)
806 m = self._newmanifest(text)
807 m = self._newmanifest(text)
807 self._mancache[node] = (m, arraytext)
808 self._mancache[node] = (m, arraytext)
808 return m
809 return m
809
810
810 def find(self, node, f):
811 def find(self, node, f):
811 '''look up entry for a single file efficiently.
812 '''look up entry for a single file efficiently.
812 return (node, flags) pair if found, (None, None) if not.'''
813 return (node, flags) pair if found, (None, None) if not.'''
813 m = self.read(node)
814 m = self.read(node)
814 try:
815 try:
815 return m.find(f)
816 return m.find(f)
816 except KeyError:
817 except KeyError:
817 return None, None
818 return None, None
818
819
819 def add(self, m, transaction, link, p1, p2, added, removed):
820 def add(self, m, transaction, link, p1, p2, added, removed):
820 if (p1 in self._mancache and not self._usetreemanifest
821 if (p1 in self._mancache and not self._treeinmem
821 and not self._usemanifestv2):
822 and not self._usemanifestv2):
822 # If our first parent is in the manifest cache, we can
823 # If our first parent is in the manifest cache, we can
823 # compute a delta here using properties we know about the
824 # compute a delta here using properties we know about the
824 # manifest up-front, which may save time later for the
825 # manifest up-front, which may save time later for the
825 # revlog layer.
826 # revlog layer.
826
827
827 _checkforbidden(added)
828 _checkforbidden(added)
828 # combine the changed lists into one list for sorting
829 # combine the changed lists into one list for sorting
829 work = [(x, False) for x in added]
830 work = [(x, False) for x in added]
830 work.extend((x, True) for x in removed)
831 work.extend((x, True) for x in removed)
831 # this could use heapq.merge() (from Python 2.6+) or equivalent
832 # this could use heapq.merge() (from Python 2.6+) or equivalent
832 # since the lists are already sorted
833 # since the lists are already sorted
833 work.sort()
834 work.sort()
834
835
835 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
836 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
836 cachedelta = self.rev(p1), deltatext
837 cachedelta = self.rev(p1), deltatext
837 text = util.buffer(arraytext)
838 text = util.buffer(arraytext)
838 else:
839 else:
839 # The first parent manifest isn't already loaded, so we'll
840 # The first parent manifest isn't already loaded, so we'll
840 # just encode a fulltext of the manifest and pass that
841 # just encode a fulltext of the manifest and pass that
841 # through to the revlog layer, and let it handle the delta
842 # through to the revlog layer, and let it handle the delta
842 # process.
843 # process.
843 text = m.text(self._usemanifestv2)
844 text = m.text(self._usemanifestv2)
844 arraytext = array.array('c', text)
845 arraytext = array.array('c', text)
845 cachedelta = None
846 cachedelta = None
846
847
847 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
848 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
848 self._mancache[n] = (m, arraytext)
849 self._mancache[n] = (m, arraytext)
849
850
850 return n
851 return n
General Comments 0
You need to be logged in to leave comments. Login now