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