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