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