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