##// END OF EJS Templates
manifestdict: inline _intersectfiles()...
Martin von Zweigbergk -
r24666:3092885b default
parent child Browse files
Show More
@@ -1,839 +1,833
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 _intersectfiles(self, files):
202 '''make a new lazymanifest with the intersection of self with files
203
204 The algorithm assumes that files is much smaller than self.'''
205 ret = manifestdict()
206 lm = self._lm
207 for fn in files:
208 if fn in lm:
209 ret._lm[fn] = lm[fn]
210 return ret
211
212 def filesnotin(self, m2):
201 def filesnotin(self, m2):
213 '''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'''
214 files = set(self)
203 files = set(self)
215 files.difference_update(m2)
204 files.difference_update(m2)
216 return files
205 return files
217
206
218 @propertycache
207 @propertycache
219 def _dirs(self):
208 def _dirs(self):
220 return util.dirs(self)
209 return util.dirs(self)
221
210
222 def dirs(self):
211 def dirs(self):
223 return self._dirs
212 return self._dirs
224
213
225 def hasdir(self, dir):
214 def hasdir(self, dir):
226 return dir in self._dirs
215 return dir in self._dirs
227
216
228 def walk(self, match):
217 def walk(self, match):
229 '''Generates matching file names.
218 '''Generates matching file names.
230
219
231 Equivalent to manifest.matches(match).iterkeys(), but without creating
220 Equivalent to manifest.matches(match).iterkeys(), but without creating
232 an entirely new manifest.
221 an entirely new manifest.
233
222
234 It also reports nonexistent files by marking them bad with match.bad().
223 It also reports nonexistent files by marking them bad with match.bad().
235 '''
224 '''
236 fset = set(match.files())
225 fset = set(match.files())
237
226
238 # avoid the entire walk if we're only looking for specific files
227 # avoid the entire walk if we're only looking for specific files
239 if fset and not match.anypats():
228 if fset and not match.anypats():
240 if util.all(fn in self for fn in fset):
229 if util.all(fn in self for fn in fset):
241 for fn in sorted(fset):
230 for fn in sorted(fset):
242 yield fn
231 yield fn
243 raise StopIteration
232 raise StopIteration
244
233
245 for fn in self:
234 for fn in self:
246 if fn in fset:
235 if fn in fset:
247 # specified pattern is the exact name
236 # specified pattern is the exact name
248 fset.remove(fn)
237 fset.remove(fn)
249 if match(fn):
238 if match(fn):
250 yield fn
239 yield fn
251
240
252 # for dirstate.walk, files=['.'] means "walk the whole tree".
241 # for dirstate.walk, files=['.'] means "walk the whole tree".
253 # follow that here, too
242 # follow that here, too
254 fset.discard('.')
243 fset.discard('.')
255
244
256 for fn in sorted(fset):
245 for fn in sorted(fset):
257 if not self.hasdir(fn):
246 if not self.hasdir(fn):
258 match.bad(fn, None)
247 match.bad(fn, None)
259
248
260 def matches(self, match):
249 def matches(self, match):
261 '''generate a new manifest filtered by the match argument'''
250 '''generate a new manifest filtered by the match argument'''
262 if match.always():
251 if match.always():
263 return self.copy()
252 return self.copy()
264
253
265 files = match.files()
254 files = match.files()
266 if (len(files) < 100 and (match.isexact() or
255 if (len(files) < 100 and (match.isexact() or
267 (not match.anypats() and util.all(fn in self for fn in files)))):
256 (not match.anypats() and util.all(fn in self for fn in files)))):
268 return self._intersectfiles(files)
257 m = manifestdict()
258 lm = self._lm
259 for fn in match.files():
260 if fn in lm:
261 m._lm[fn] = lm[fn]
262 return m
269
263
270 m = manifestdict('')
264 m = manifestdict('')
271 m._lm = self._lm.filtercopy(match)
265 m._lm = self._lm.filtercopy(match)
272 return m
266 return m
273
267
274 def diff(self, m2, clean=False):
268 def diff(self, m2, clean=False):
275 '''Finds changes between the current manifest and m2.
269 '''Finds changes between the current manifest and m2.
276
270
277 Args:
271 Args:
278 m2: the manifest to which this manifest should be compared.
272 m2: the manifest to which this manifest should be compared.
279 clean: if true, include files unchanged between these manifests
273 clean: if true, include files unchanged between these manifests
280 with a None value in the returned dictionary.
274 with a None value in the returned dictionary.
281
275
282 The result is returned as a dict with filename as key and
276 The result is returned as a dict with filename as key and
283 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
277 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
284 nodeid in the current/other manifest and fl1/fl2 is the flag
278 nodeid in the current/other manifest and fl1/fl2 is the flag
285 in the current/other manifest. Where the file does not exist,
279 in the current/other manifest. Where the file does not exist,
286 the nodeid will be None and the flags will be the empty
280 the nodeid will be None and the flags will be the empty
287 string.
281 string.
288 '''
282 '''
289 return self._lm.diff(m2._lm, clean)
283 return self._lm.diff(m2._lm, clean)
290
284
291 def setflag(self, key, flag):
285 def setflag(self, key, flag):
292 self._lm[key] = self[key], flag
286 self._lm[key] = self[key], flag
293
287
294 def get(self, key, default=None):
288 def get(self, key, default=None):
295 try:
289 try:
296 return self._lm[key][0]
290 return self._lm[key][0]
297 except KeyError:
291 except KeyError:
298 return default
292 return default
299
293
300 def flags(self, key, default=''):
294 def flags(self, key, default=''):
301 try:
295 try:
302 return self._lm[key][1]
296 return self._lm[key][1]
303 except KeyError:
297 except KeyError:
304 return default
298 return default
305
299
306 def copy(self):
300 def copy(self):
307 c = manifestdict('')
301 c = manifestdict('')
308 c._lm = self._lm.copy()
302 c._lm = self._lm.copy()
309 return c
303 return c
310
304
311 def iteritems(self):
305 def iteritems(self):
312 return (x[:2] for x in self._lm.iterentries())
306 return (x[:2] for x in self._lm.iterentries())
313
307
314 def text(self, usemanifestv2=False):
308 def text(self, usemanifestv2=False):
315 if usemanifestv2:
309 if usemanifestv2:
316 return _textv2(self._lm.iterentries())
310 return _textv2(self._lm.iterentries())
317 else:
311 else:
318 # use (probably) native version for v1
312 # use (probably) native version for v1
319 return self._lm.text()
313 return self._lm.text()
320
314
321 def fastdelta(self, base, changes):
315 def fastdelta(self, base, changes):
322 """Given a base manifest text as an array.array and a list of changes
316 """Given a base manifest text as an array.array and a list of changes
323 relative to that text, compute a delta that can be used by revlog.
317 relative to that text, compute a delta that can be used by revlog.
324 """
318 """
325 delta = []
319 delta = []
326 dstart = None
320 dstart = None
327 dend = None
321 dend = None
328 dline = [""]
322 dline = [""]
329 start = 0
323 start = 0
330 # zero copy representation of base as a buffer
324 # zero copy representation of base as a buffer
331 addbuf = util.buffer(base)
325 addbuf = util.buffer(base)
332
326
333 # start with a readonly loop that finds the offset of
327 # start with a readonly loop that finds the offset of
334 # each line and creates the deltas
328 # each line and creates the deltas
335 for f, todelete in changes:
329 for f, todelete in changes:
336 # bs will either be the index of the item or the insert point
330 # bs will either be the index of the item or the insert point
337 start, end = _msearch(addbuf, f, start)
331 start, end = _msearch(addbuf, f, start)
338 if not todelete:
332 if not todelete:
339 h, fl = self._lm[f]
333 h, fl = self._lm[f]
340 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
334 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
341 else:
335 else:
342 if start == end:
336 if start == end:
343 # item we want to delete was not found, error out
337 # item we want to delete was not found, error out
344 raise AssertionError(
338 raise AssertionError(
345 _("failed to remove %s from manifest") % f)
339 _("failed to remove %s from manifest") % f)
346 l = ""
340 l = ""
347 if dstart is not None and dstart <= start and dend >= start:
341 if dstart is not None and dstart <= start and dend >= start:
348 if dend < end:
342 if dend < end:
349 dend = end
343 dend = end
350 if l:
344 if l:
351 dline.append(l)
345 dline.append(l)
352 else:
346 else:
353 if dstart is not None:
347 if dstart is not None:
354 delta.append([dstart, dend, "".join(dline)])
348 delta.append([dstart, dend, "".join(dline)])
355 dstart = start
349 dstart = start
356 dend = end
350 dend = end
357 dline = [l]
351 dline = [l]
358
352
359 if dstart is not None:
353 if dstart is not None:
360 delta.append([dstart, dend, "".join(dline)])
354 delta.append([dstart, dend, "".join(dline)])
361 # apply the delta to the base, and get a delta for addrevision
355 # apply the delta to the base, and get a delta for addrevision
362 deltatext, arraytext = _addlistdelta(base, delta)
356 deltatext, arraytext = _addlistdelta(base, delta)
363 return arraytext, deltatext
357 return arraytext, deltatext
364
358
365 def _msearch(m, s, lo=0, hi=None):
359 def _msearch(m, s, lo=0, hi=None):
366 '''return a tuple (start, end) that says where to find s within m.
360 '''return a tuple (start, end) that says where to find s within m.
367
361
368 If the string is found m[start:end] are the line containing
362 If the string is found m[start:end] are the line containing
369 that string. If start == end the string was not found and
363 that string. If start == end the string was not found and
370 they indicate the proper sorted insertion point.
364 they indicate the proper sorted insertion point.
371
365
372 m should be a buffer or a string
366 m should be a buffer or a string
373 s is a string'''
367 s is a string'''
374 def advance(i, c):
368 def advance(i, c):
375 while i < lenm and m[i] != c:
369 while i < lenm and m[i] != c:
376 i += 1
370 i += 1
377 return i
371 return i
378 if not s:
372 if not s:
379 return (lo, lo)
373 return (lo, lo)
380 lenm = len(m)
374 lenm = len(m)
381 if not hi:
375 if not hi:
382 hi = lenm
376 hi = lenm
383 while lo < hi:
377 while lo < hi:
384 mid = (lo + hi) // 2
378 mid = (lo + hi) // 2
385 start = mid
379 start = mid
386 while start > 0 and m[start - 1] != '\n':
380 while start > 0 and m[start - 1] != '\n':
387 start -= 1
381 start -= 1
388 end = advance(start, '\0')
382 end = advance(start, '\0')
389 if m[start:end] < s:
383 if m[start:end] < s:
390 # we know that after the null there are 40 bytes of sha1
384 # we know that after the null there are 40 bytes of sha1
391 # this translates to the bisect lo = mid + 1
385 # this translates to the bisect lo = mid + 1
392 lo = advance(end + 40, '\n') + 1
386 lo = advance(end + 40, '\n') + 1
393 else:
387 else:
394 # this translates to the bisect hi = mid
388 # this translates to the bisect hi = mid
395 hi = start
389 hi = start
396 end = advance(lo, '\0')
390 end = advance(lo, '\0')
397 found = m[lo:end]
391 found = m[lo:end]
398 if s == found:
392 if s == found:
399 # 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
400 end = advance(end + 40, '\n')
394 end = advance(end + 40, '\n')
401 return (lo, end + 1)
395 return (lo, end + 1)
402 else:
396 else:
403 return (lo, lo)
397 return (lo, lo)
404
398
405 def _checkforbidden(l):
399 def _checkforbidden(l):
406 """Check filenames for illegal characters."""
400 """Check filenames for illegal characters."""
407 for f in l:
401 for f in l:
408 if '\n' in f or '\r' in f:
402 if '\n' in f or '\r' in f:
409 raise error.RevlogError(
403 raise error.RevlogError(
410 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
404 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
411
405
412
406
413 # apply the changes collected during the bisect loop to our addlist
407 # apply the changes collected during the bisect loop to our addlist
414 # return a delta suitable for addrevision
408 # return a delta suitable for addrevision
415 def _addlistdelta(addlist, x):
409 def _addlistdelta(addlist, x):
416 # for large addlist arrays, building a new array is cheaper
410 # for large addlist arrays, building a new array is cheaper
417 # than repeatedly modifying the existing one
411 # than repeatedly modifying the existing one
418 currentposition = 0
412 currentposition = 0
419 newaddlist = array.array('c')
413 newaddlist = array.array('c')
420
414
421 for start, end, content in x:
415 for start, end, content in x:
422 newaddlist += addlist[currentposition:start]
416 newaddlist += addlist[currentposition:start]
423 if content:
417 if content:
424 newaddlist += array.array('c', content)
418 newaddlist += array.array('c', content)
425
419
426 currentposition = end
420 currentposition = end
427
421
428 newaddlist += addlist[currentposition:]
422 newaddlist += addlist[currentposition:]
429
423
430 deltatext = "".join(struct.pack(">lll", start, end, len(content))
424 deltatext = "".join(struct.pack(">lll", start, end, len(content))
431 + content for start, end, content in x)
425 + content for start, end, content in x)
432 return deltatext, newaddlist
426 return deltatext, newaddlist
433
427
434 def _splittopdir(f):
428 def _splittopdir(f):
435 if '/' in f:
429 if '/' in f:
436 dir, subpath = f.split('/', 1)
430 dir, subpath = f.split('/', 1)
437 return dir + '/', subpath
431 return dir + '/', subpath
438 else:
432 else:
439 return '', f
433 return '', f
440
434
441 class treemanifest(object):
435 class treemanifest(object):
442 def __init__(self, dir='', text=''):
436 def __init__(self, dir='', text=''):
443 self._dir = dir
437 self._dir = dir
444 self._dirs = {}
438 self._dirs = {}
445 # Using _lazymanifest here is a little slower than plain old dicts
439 # Using _lazymanifest here is a little slower than plain old dicts
446 self._files = {}
440 self._files = {}
447 self._flags = {}
441 self._flags = {}
448 for f, n, fl in _parse(text):
442 for f, n, fl in _parse(text):
449 self[f] = n
443 self[f] = n
450 if fl:
444 if fl:
451 self.setflag(f, fl)
445 self.setflag(f, fl)
452
446
453 def _subpath(self, path):
447 def _subpath(self, path):
454 return self._dir + path
448 return self._dir + path
455
449
456 def __len__(self):
450 def __len__(self):
457 size = len(self._files)
451 size = len(self._files)
458 for m in self._dirs.values():
452 for m in self._dirs.values():
459 size += m.__len__()
453 size += m.__len__()
460 return size
454 return size
461
455
462 def _isempty(self):
456 def _isempty(self):
463 return (not self._files and (not self._dirs or
457 return (not self._files and (not self._dirs or
464 util.all(m._isempty() for m in self._dirs.values())))
458 util.all(m._isempty() for m in self._dirs.values())))
465
459
466 def __str__(self):
460 def __str__(self):
467 return '<treemanifest dir=%s>' % self._dir
461 return '<treemanifest dir=%s>' % self._dir
468
462
469 def iteritems(self):
463 def iteritems(self):
470 for p, n in sorted(self._dirs.items() + self._files.items()):
464 for p, n in sorted(self._dirs.items() + self._files.items()):
471 if p in self._files:
465 if p in self._files:
472 yield self._subpath(p), n
466 yield self._subpath(p), n
473 else:
467 else:
474 for f, sn in n.iteritems():
468 for f, sn in n.iteritems():
475 yield f, sn
469 yield f, sn
476
470
477 def iterkeys(self):
471 def iterkeys(self):
478 for p in sorted(self._dirs.keys() + self._files.keys()):
472 for p in sorted(self._dirs.keys() + self._files.keys()):
479 if p in self._files:
473 if p in self._files:
480 yield self._subpath(p)
474 yield self._subpath(p)
481 else:
475 else:
482 for f in self._dirs[p].iterkeys():
476 for f in self._dirs[p].iterkeys():
483 yield f
477 yield f
484
478
485 def keys(self):
479 def keys(self):
486 return list(self.iterkeys())
480 return list(self.iterkeys())
487
481
488 def __iter__(self):
482 def __iter__(self):
489 return self.iterkeys()
483 return self.iterkeys()
490
484
491 def __contains__(self, f):
485 def __contains__(self, f):
492 if f is None:
486 if f is None:
493 return False
487 return False
494 dir, subpath = _splittopdir(f)
488 dir, subpath = _splittopdir(f)
495 if dir:
489 if dir:
496 if dir not in self._dirs:
490 if dir not in self._dirs:
497 return False
491 return False
498 return self._dirs[dir].__contains__(subpath)
492 return self._dirs[dir].__contains__(subpath)
499 else:
493 else:
500 return f in self._files
494 return f in self._files
501
495
502 def get(self, f, default=None):
496 def get(self, f, default=None):
503 dir, subpath = _splittopdir(f)
497 dir, subpath = _splittopdir(f)
504 if dir:
498 if dir:
505 if dir not in self._dirs:
499 if dir not in self._dirs:
506 return default
500 return default
507 return self._dirs[dir].get(subpath, default)
501 return self._dirs[dir].get(subpath, default)
508 else:
502 else:
509 return self._files.get(f, default)
503 return self._files.get(f, default)
510
504
511 def __getitem__(self, f):
505 def __getitem__(self, f):
512 dir, subpath = _splittopdir(f)
506 dir, subpath = _splittopdir(f)
513 if dir:
507 if dir:
514 return self._dirs[dir].__getitem__(subpath)
508 return self._dirs[dir].__getitem__(subpath)
515 else:
509 else:
516 return self._files[f]
510 return self._files[f]
517
511
518 def flags(self, f):
512 def flags(self, f):
519 dir, subpath = _splittopdir(f)
513 dir, subpath = _splittopdir(f)
520 if dir:
514 if dir:
521 if dir not in self._dirs:
515 if dir not in self._dirs:
522 return ''
516 return ''
523 return self._dirs[dir].flags(subpath)
517 return self._dirs[dir].flags(subpath)
524 else:
518 else:
525 if f in self._dirs:
519 if f in self._dirs:
526 return ''
520 return ''
527 return self._flags.get(f, '')
521 return self._flags.get(f, '')
528
522
529 def find(self, f):
523 def find(self, f):
530 dir, subpath = _splittopdir(f)
524 dir, subpath = _splittopdir(f)
531 if dir:
525 if dir:
532 return self._dirs[dir].find(subpath)
526 return self._dirs[dir].find(subpath)
533 else:
527 else:
534 return self._files[f], self._flags.get(f, '')
528 return self._files[f], self._flags.get(f, '')
535
529
536 def __delitem__(self, f):
530 def __delitem__(self, f):
537 dir, subpath = _splittopdir(f)
531 dir, subpath = _splittopdir(f)
538 if dir:
532 if dir:
539 self._dirs[dir].__delitem__(subpath)
533 self._dirs[dir].__delitem__(subpath)
540 # If the directory is now empty, remove it
534 # If the directory is now empty, remove it
541 if self._dirs[dir]._isempty():
535 if self._dirs[dir]._isempty():
542 del self._dirs[dir]
536 del self._dirs[dir]
543 else:
537 else:
544 del self._files[f]
538 del self._files[f]
545 if f in self._flags:
539 if f in self._flags:
546 del self._flags[f]
540 del self._flags[f]
547
541
548 def __setitem__(self, f, n):
542 def __setitem__(self, f, n):
549 assert n is not None
543 assert n is not None
550 dir, subpath = _splittopdir(f)
544 dir, subpath = _splittopdir(f)
551 if dir:
545 if dir:
552 if dir not in self._dirs:
546 if dir not in self._dirs:
553 self._dirs[dir] = treemanifest(self._subpath(dir))
547 self._dirs[dir] = treemanifest(self._subpath(dir))
554 self._dirs[dir].__setitem__(subpath, n)
548 self._dirs[dir].__setitem__(subpath, n)
555 else:
549 else:
556 self._files[f] = n[:21] # to match manifestdict's behavior
550 self._files[f] = n[:21] # to match manifestdict's behavior
557
551
558 def setflag(self, f, flags):
552 def setflag(self, f, flags):
559 """Set the flags (symlink, executable) for path f."""
553 """Set the flags (symlink, executable) for path f."""
560 dir, subpath = _splittopdir(f)
554 dir, subpath = _splittopdir(f)
561 if dir:
555 if dir:
562 if dir not in self._dirs:
556 if dir not in self._dirs:
563 self._dirs[dir] = treemanifest(self._subpath(dir))
557 self._dirs[dir] = treemanifest(self._subpath(dir))
564 self._dirs[dir].setflag(subpath, flags)
558 self._dirs[dir].setflag(subpath, flags)
565 else:
559 else:
566 self._flags[f] = flags
560 self._flags[f] = flags
567
561
568 def copy(self):
562 def copy(self):
569 copy = treemanifest(self._dir)
563 copy = treemanifest(self._dir)
570 for d in self._dirs:
564 for d in self._dirs:
571 copy._dirs[d] = self._dirs[d].copy()
565 copy._dirs[d] = self._dirs[d].copy()
572 copy._files = dict.copy(self._files)
566 copy._files = dict.copy(self._files)
573 copy._flags = dict.copy(self._flags)
567 copy._flags = dict.copy(self._flags)
574 return copy
568 return copy
575
569
576 def filesnotin(self, m2):
570 def filesnotin(self, m2):
577 '''Set of files in this manifest that are not in the other'''
571 '''Set of files in this manifest that are not in the other'''
578 files = set()
572 files = set()
579 def _filesnotin(t1, t2):
573 def _filesnotin(t1, t2):
580 for d, m1 in t1._dirs.iteritems():
574 for d, m1 in t1._dirs.iteritems():
581 if d in t2._dirs:
575 if d in t2._dirs:
582 m2 = t2._dirs[d]
576 m2 = t2._dirs[d]
583 _filesnotin(m1, m2)
577 _filesnotin(m1, m2)
584 else:
578 else:
585 files.update(m1.iterkeys())
579 files.update(m1.iterkeys())
586
580
587 for fn in t1._files.iterkeys():
581 for fn in t1._files.iterkeys():
588 if fn not in t2._files:
582 if fn not in t2._files:
589 files.add(t1._subpath(fn))
583 files.add(t1._subpath(fn))
590
584
591 _filesnotin(self, m2)
585 _filesnotin(self, m2)
592 return files
586 return files
593
587
594 @propertycache
588 @propertycache
595 def _alldirs(self):
589 def _alldirs(self):
596 return util.dirs(self)
590 return util.dirs(self)
597
591
598 def dirs(self):
592 def dirs(self):
599 return self._alldirs
593 return self._alldirs
600
594
601 def hasdir(self, dir):
595 def hasdir(self, dir):
602 topdir, subdir = _splittopdir(dir)
596 topdir, subdir = _splittopdir(dir)
603 if topdir:
597 if topdir:
604 if topdir in self._dirs:
598 if topdir in self._dirs:
605 return self._dirs[topdir].hasdir(subdir)
599 return self._dirs[topdir].hasdir(subdir)
606 return False
600 return False
607 return (dir + '/') in self._dirs
601 return (dir + '/') in self._dirs
608
602
609 def walk(self, match):
603 def walk(self, match):
610 '''Generates matching file names.
604 '''Generates matching file names.
611
605
612 Equivalent to manifest.matches(match).iterkeys(), but without creating
606 Equivalent to manifest.matches(match).iterkeys(), but without creating
613 an entirely new manifest.
607 an entirely new manifest.
614
608
615 It also reports nonexistent files by marking them bad with match.bad().
609 It also reports nonexistent files by marking them bad with match.bad().
616 '''
610 '''
617 fset = set(match.files())
611 fset = set(match.files())
618
612
619 # avoid the entire walk if we're only looking for specific files
613 # avoid the entire walk if we're only looking for specific files
620 if fset and not match.anypats():
614 if fset and not match.anypats():
621 if util.all(fn in self for fn in fset):
615 if util.all(fn in self for fn in fset):
622 for fn in sorted(fset):
616 for fn in sorted(fset):
623 yield fn
617 yield fn
624 raise StopIteration
618 raise StopIteration
625
619
626 for fn in self._walk(match):
620 for fn in self._walk(match):
627 if fn in fset:
621 if fn in fset:
628 # specified pattern is the exact name
622 # specified pattern is the exact name
629 fset.remove(fn)
623 fset.remove(fn)
630 yield fn
624 yield fn
631
625
632 # for dirstate.walk, files=['.'] means "walk the whole tree".
626 # for dirstate.walk, files=['.'] means "walk the whole tree".
633 # follow that here, too
627 # follow that here, too
634 fset.discard('.')
628 fset.discard('.')
635
629
636 for fn in sorted(fset):
630 for fn in sorted(fset):
637 if not self.hasdir(fn):
631 if not self.hasdir(fn):
638 match.bad(fn, None)
632 match.bad(fn, None)
639
633
640 def _walk(self, match):
634 def _walk(self, match):
641 '''Recursively generates matching file names for walk().'''
635 '''Recursively generates matching file names for walk().'''
642
636
643 # yield this dir's files and walk its submanifests
637 # yield this dir's files and walk its submanifests
644 for p in sorted(self._dirs.keys() + self._files.keys()):
638 for p in sorted(self._dirs.keys() + self._files.keys()):
645 if p in self._files:
639 if p in self._files:
646 fullp = self._subpath(p)
640 fullp = self._subpath(p)
647 if match(fullp):
641 if match(fullp):
648 yield fullp
642 yield fullp
649 else:
643 else:
650 for f in self._dirs[p]._walk(match):
644 for f in self._dirs[p]._walk(match):
651 yield f
645 yield f
652
646
653 def matches(self, match):
647 def matches(self, match):
654 '''generate a new manifest filtered by the match argument'''
648 '''generate a new manifest filtered by the match argument'''
655 if match.always():
649 if match.always():
656 return self.copy()
650 return self.copy()
657
651
658 return self._matches(match)
652 return self._matches(match)
659
653
660 def _matches(self, match, alldirs=False):
654 def _matches(self, match, alldirs=False):
661 '''recursively generate a new manifest filtered by the match argument.
655 '''recursively generate a new manifest filtered by the match argument.
662
656
663 Will visit all subdirectories if alldirs is True, otherwise it will
657 Will visit all subdirectories if alldirs is True, otherwise it will
664 only visit subdirectories for which match.visitdir is True.'''
658 only visit subdirectories for which match.visitdir is True.'''
665
659
666 ret = treemanifest(self._dir)
660 ret = treemanifest(self._dir)
667 if not alldirs:
661 if not alldirs:
668 # substring to strip trailing slash
662 # substring to strip trailing slash
669 visit = match.visitdir(self._dir[:-1] or '.')
663 visit = match.visitdir(self._dir[:-1] or '.')
670 if not visit:
664 if not visit:
671 return ret
665 return ret
672 alldirs = (visit == 'all')
666 alldirs = (visit == 'all')
673
667
674 for fn in self._files:
668 for fn in self._files:
675 fullp = self._subpath(fn)
669 fullp = self._subpath(fn)
676 if not match(fullp):
670 if not match(fullp):
677 continue
671 continue
678 ret._files[fn] = self._files[fn]
672 ret._files[fn] = self._files[fn]
679 if fn in self._flags:
673 if fn in self._flags:
680 ret._flags[fn] = self._flags[fn]
674 ret._flags[fn] = self._flags[fn]
681
675
682 for dir, subm in self._dirs.iteritems():
676 for dir, subm in self._dirs.iteritems():
683 m = subm._matches(match, alldirs)
677 m = subm._matches(match, alldirs)
684 if not m._isempty():
678 if not m._isempty():
685 ret._dirs[dir] = m
679 ret._dirs[dir] = m
686
680
687 return ret
681 return ret
688
682
689 def diff(self, m2, clean=False):
683 def diff(self, m2, clean=False):
690 '''Finds changes between the current manifest and m2.
684 '''Finds changes between the current manifest and m2.
691
685
692 Args:
686 Args:
693 m2: the manifest to which this manifest should be compared.
687 m2: the manifest to which this manifest should be compared.
694 clean: if true, include files unchanged between these manifests
688 clean: if true, include files unchanged between these manifests
695 with a None value in the returned dictionary.
689 with a None value in the returned dictionary.
696
690
697 The result is returned as a dict with filename as key and
691 The result is returned as a dict with filename as key and
698 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
692 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
699 nodeid in the current/other manifest and fl1/fl2 is the flag
693 nodeid in the current/other manifest and fl1/fl2 is the flag
700 in the current/other manifest. Where the file does not exist,
694 in the current/other manifest. Where the file does not exist,
701 the nodeid will be None and the flags will be the empty
695 the nodeid will be None and the flags will be the empty
702 string.
696 string.
703 '''
697 '''
704 result = {}
698 result = {}
705 emptytree = treemanifest()
699 emptytree = treemanifest()
706 def _diff(t1, t2):
700 def _diff(t1, t2):
707 for d, m1 in t1._dirs.iteritems():
701 for d, m1 in t1._dirs.iteritems():
708 m2 = t2._dirs.get(d, emptytree)
702 m2 = t2._dirs.get(d, emptytree)
709 _diff(m1, m2)
703 _diff(m1, m2)
710
704
711 for d, m2 in t2._dirs.iteritems():
705 for d, m2 in t2._dirs.iteritems():
712 if d not in t1._dirs:
706 if d not in t1._dirs:
713 _diff(emptytree, m2)
707 _diff(emptytree, m2)
714
708
715 for fn, n1 in t1._files.iteritems():
709 for fn, n1 in t1._files.iteritems():
716 fl1 = t1._flags.get(fn, '')
710 fl1 = t1._flags.get(fn, '')
717 n2 = t2._files.get(fn, None)
711 n2 = t2._files.get(fn, None)
718 fl2 = t2._flags.get(fn, '')
712 fl2 = t2._flags.get(fn, '')
719 if n1 != n2 or fl1 != fl2:
713 if n1 != n2 or fl1 != fl2:
720 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
714 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
721 elif clean:
715 elif clean:
722 result[t1._subpath(fn)] = None
716 result[t1._subpath(fn)] = None
723
717
724 for fn, n2 in t2._files.iteritems():
718 for fn, n2 in t2._files.iteritems():
725 if fn not in t1._files:
719 if fn not in t1._files:
726 fl2 = t2._flags.get(fn, '')
720 fl2 = t2._flags.get(fn, '')
727 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
721 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
728
722
729 _diff(self, m2)
723 _diff(self, m2)
730 return result
724 return result
731
725
732 def text(self, usemanifestv2=False):
726 def text(self, usemanifestv2=False):
733 """Get the full data of this manifest as a bytestring."""
727 """Get the full data of this manifest as a bytestring."""
734 flags = self.flags
728 flags = self.flags
735 return _text(((f, self[f], flags(f)) for f in self.keys()),
729 return _text(((f, self[f], flags(f)) for f in self.keys()),
736 usemanifestv2)
730 usemanifestv2)
737
731
738 class manifest(revlog.revlog):
732 class manifest(revlog.revlog):
739 def __init__(self, opener):
733 def __init__(self, opener):
740 # During normal operations, we expect to deal with not more than four
734 # During normal operations, we expect to deal with not more than four
741 # revs at a time (such as during commit --amend). When rebasing large
735 # revs at a time (such as during commit --amend). When rebasing large
742 # stacks of commits, the number can go up, hence the config knob below.
736 # stacks of commits, the number can go up, hence the config knob below.
743 cachesize = 4
737 cachesize = 4
744 usetreemanifest = False
738 usetreemanifest = False
745 usemanifestv2 = False
739 usemanifestv2 = False
746 opts = getattr(opener, 'options', None)
740 opts = getattr(opener, 'options', None)
747 if opts is not None:
741 if opts is not None:
748 cachesize = opts.get('manifestcachesize', cachesize)
742 cachesize = opts.get('manifestcachesize', cachesize)
749 usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
743 usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
750 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
744 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
751 self._mancache = util.lrucachedict(cachesize)
745 self._mancache = util.lrucachedict(cachesize)
752 revlog.revlog.__init__(self, opener, "00manifest.i")
746 revlog.revlog.__init__(self, opener, "00manifest.i")
753 self._usetreemanifest = usetreemanifest
747 self._usetreemanifest = usetreemanifest
754 self._usemanifestv2 = usemanifestv2
748 self._usemanifestv2 = usemanifestv2
755
749
756 def _newmanifest(self, data=''):
750 def _newmanifest(self, data=''):
757 if self._usetreemanifest:
751 if self._usetreemanifest:
758 return treemanifest('', data)
752 return treemanifest('', data)
759 return manifestdict(data)
753 return manifestdict(data)
760
754
761 def _slowreaddelta(self, node):
755 def _slowreaddelta(self, node):
762 r0 = self.deltaparent(self.rev(node))
756 r0 = self.deltaparent(self.rev(node))
763 m0 = self.read(self.node(r0))
757 m0 = self.read(self.node(r0))
764 m1 = self.read(node)
758 m1 = self.read(node)
765 md = self._newmanifest()
759 md = self._newmanifest()
766 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
760 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
767 if n1:
761 if n1:
768 md[f] = n1
762 md[f] = n1
769 if fl1:
763 if fl1:
770 md.setflag(f, fl1)
764 md.setflag(f, fl1)
771 return md
765 return md
772
766
773 def readdelta(self, node):
767 def readdelta(self, node):
774 if self._usemanifestv2 or self._usetreemanifest:
768 if self._usemanifestv2 or self._usetreemanifest:
775 return self._slowreaddelta(node)
769 return self._slowreaddelta(node)
776 r = self.rev(node)
770 r = self.rev(node)
777 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
771 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
778 return self._newmanifest(d)
772 return self._newmanifest(d)
779
773
780 def readfast(self, node):
774 def readfast(self, node):
781 '''use the faster of readdelta or read'''
775 '''use the faster of readdelta or read'''
782 r = self.rev(node)
776 r = self.rev(node)
783 deltaparent = self.deltaparent(r)
777 deltaparent = self.deltaparent(r)
784 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
778 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
785 return self.readdelta(node)
779 return self.readdelta(node)
786 return self.read(node)
780 return self.read(node)
787
781
788 def read(self, node):
782 def read(self, node):
789 if node == revlog.nullid:
783 if node == revlog.nullid:
790 return self._newmanifest() # don't upset local cache
784 return self._newmanifest() # don't upset local cache
791 if node in self._mancache:
785 if node in self._mancache:
792 return self._mancache[node][0]
786 return self._mancache[node][0]
793 text = self.revision(node)
787 text = self.revision(node)
794 arraytext = array.array('c', text)
788 arraytext = array.array('c', text)
795 m = self._newmanifest(text)
789 m = self._newmanifest(text)
796 self._mancache[node] = (m, arraytext)
790 self._mancache[node] = (m, arraytext)
797 return m
791 return m
798
792
799 def find(self, node, f):
793 def find(self, node, f):
800 '''look up entry for a single file efficiently.
794 '''look up entry for a single file efficiently.
801 return (node, flags) pair if found, (None, None) if not.'''
795 return (node, flags) pair if found, (None, None) if not.'''
802 m = self.read(node)
796 m = self.read(node)
803 try:
797 try:
804 return m.find(f)
798 return m.find(f)
805 except KeyError:
799 except KeyError:
806 return None, None
800 return None, None
807
801
808 def add(self, m, transaction, link, p1, p2, added, removed):
802 def add(self, m, transaction, link, p1, p2, added, removed):
809 if (p1 in self._mancache and not self._usetreemanifest
803 if (p1 in self._mancache and not self._usetreemanifest
810 and not self._usemanifestv2):
804 and not self._usemanifestv2):
811 # If our first parent is in the manifest cache, we can
805 # If our first parent is in the manifest cache, we can
812 # compute a delta here using properties we know about the
806 # compute a delta here using properties we know about the
813 # manifest up-front, which may save time later for the
807 # manifest up-front, which may save time later for the
814 # revlog layer.
808 # revlog layer.
815
809
816 _checkforbidden(added)
810 _checkforbidden(added)
817 # combine the changed lists into one list for sorting
811 # combine the changed lists into one list for sorting
818 work = [(x, False) for x in added]
812 work = [(x, False) for x in added]
819 work.extend((x, True) for x in removed)
813 work.extend((x, True) for x in removed)
820 # this could use heapq.merge() (from Python 2.6+) or equivalent
814 # this could use heapq.merge() (from Python 2.6+) or equivalent
821 # since the lists are already sorted
815 # since the lists are already sorted
822 work.sort()
816 work.sort()
823
817
824 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
818 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
825 cachedelta = self.rev(p1), deltatext
819 cachedelta = self.rev(p1), deltatext
826 text = util.buffer(arraytext)
820 text = util.buffer(arraytext)
827 else:
821 else:
828 # The first parent manifest isn't already loaded, so we'll
822 # The first parent manifest isn't already loaded, so we'll
829 # just encode a fulltext of the manifest and pass that
823 # just encode a fulltext of the manifest and pass that
830 # through to the revlog layer, and let it handle the delta
824 # through to the revlog layer, and let it handle the delta
831 # process.
825 # process.
832 text = m.text(self._usemanifestv2)
826 text = m.text(self._usemanifestv2)
833 arraytext = array.array('c', text)
827 arraytext = array.array('c', text)
834 cachedelta = None
828 cachedelta = None
835
829
836 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
830 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
837 self._mancache[n] = (m, arraytext)
831 self._mancache[n] = (m, arraytext)
838
832
839 return n
833 return n
General Comments 0
You need to be logged in to leave comments. Login now