##// END OF EJS Templates
treemanifest: allow setting flag to 't'...
Martin von Zweigbergk -
r28215:f7c5c784 default
parent child Browse files
Show More
@@ -1,1079 +1,1078 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 __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):
523 def iterentries(self):
524 self._load()
524 self._load()
525 for p, n in sorted(self._dirs.items() + self._files.items()):
525 for p, n in sorted(self._dirs.items() + self._files.items()):
526 if p in self._files:
526 if p in self._files:
527 yield self._subpath(p), n, self._flags.get(p, '')
527 yield self._subpath(p), n, self._flags.get(p, '')
528 else:
528 else:
529 for x in n.iterentries():
529 for x in n.iterentries():
530 yield x
530 yield x
531
531
532 def iteritems(self):
532 def iteritems(self):
533 self._load()
533 self._load()
534 for p, n in sorted(self._dirs.items() + self._files.items()):
534 for p, n in sorted(self._dirs.items() + self._files.items()):
535 if p in self._files:
535 if p in self._files:
536 yield self._subpath(p), n
536 yield self._subpath(p), n
537 else:
537 else:
538 for f, sn in n.iteritems():
538 for f, sn in n.iteritems():
539 yield f, sn
539 yield f, sn
540
540
541 def iterkeys(self):
541 def iterkeys(self):
542 self._load()
542 self._load()
543 for p in sorted(self._dirs.keys() + self._files.keys()):
543 for p in sorted(self._dirs.keys() + self._files.keys()):
544 if p in self._files:
544 if p in self._files:
545 yield self._subpath(p)
545 yield self._subpath(p)
546 else:
546 else:
547 for f in self._dirs[p].iterkeys():
547 for f in self._dirs[p].iterkeys():
548 yield f
548 yield f
549
549
550 def keys(self):
550 def keys(self):
551 return list(self.iterkeys())
551 return list(self.iterkeys())
552
552
553 def __iter__(self):
553 def __iter__(self):
554 return self.iterkeys()
554 return self.iterkeys()
555
555
556 def __contains__(self, f):
556 def __contains__(self, f):
557 if f is None:
557 if f is None:
558 return False
558 return False
559 self._load()
559 self._load()
560 dir, subpath = _splittopdir(f)
560 dir, subpath = _splittopdir(f)
561 if dir:
561 if dir:
562 if dir not in self._dirs:
562 if dir not in self._dirs:
563 return False
563 return False
564 return self._dirs[dir].__contains__(subpath)
564 return self._dirs[dir].__contains__(subpath)
565 else:
565 else:
566 return f in self._files
566 return f in self._files
567
567
568 def get(self, f, default=None):
568 def get(self, f, default=None):
569 self._load()
569 self._load()
570 dir, subpath = _splittopdir(f)
570 dir, subpath = _splittopdir(f)
571 if dir:
571 if dir:
572 if dir not in self._dirs:
572 if dir not in self._dirs:
573 return default
573 return default
574 return self._dirs[dir].get(subpath, default)
574 return self._dirs[dir].get(subpath, default)
575 else:
575 else:
576 return self._files.get(f, default)
576 return self._files.get(f, default)
577
577
578 def __getitem__(self, f):
578 def __getitem__(self, f):
579 self._load()
579 self._load()
580 dir, subpath = _splittopdir(f)
580 dir, subpath = _splittopdir(f)
581 if dir:
581 if dir:
582 return self._dirs[dir].__getitem__(subpath)
582 return self._dirs[dir].__getitem__(subpath)
583 else:
583 else:
584 return self._files[f]
584 return self._files[f]
585
585
586 def flags(self, f):
586 def flags(self, f):
587 self._load()
587 self._load()
588 dir, subpath = _splittopdir(f)
588 dir, subpath = _splittopdir(f)
589 if dir:
589 if dir:
590 if dir not in self._dirs:
590 if dir not in self._dirs:
591 return ''
591 return ''
592 return self._dirs[dir].flags(subpath)
592 return self._dirs[dir].flags(subpath)
593 else:
593 else:
594 if f in self._dirs:
594 if f in self._dirs:
595 return ''
595 return ''
596 return self._flags.get(f, '')
596 return self._flags.get(f, '')
597
597
598 def find(self, f):
598 def find(self, f):
599 self._load()
599 self._load()
600 dir, subpath = _splittopdir(f)
600 dir, subpath = _splittopdir(f)
601 if dir:
601 if dir:
602 return self._dirs[dir].find(subpath)
602 return self._dirs[dir].find(subpath)
603 else:
603 else:
604 return self._files[f], self._flags.get(f, '')
604 return self._files[f], self._flags.get(f, '')
605
605
606 def __delitem__(self, f):
606 def __delitem__(self, f):
607 self._load()
607 self._load()
608 dir, subpath = _splittopdir(f)
608 dir, subpath = _splittopdir(f)
609 if dir:
609 if dir:
610 self._dirs[dir].__delitem__(subpath)
610 self._dirs[dir].__delitem__(subpath)
611 # If the directory is now empty, remove it
611 # If the directory is now empty, remove it
612 if self._dirs[dir]._isempty():
612 if self._dirs[dir]._isempty():
613 del self._dirs[dir]
613 del self._dirs[dir]
614 else:
614 else:
615 del self._files[f]
615 del self._files[f]
616 if f in self._flags:
616 if f in self._flags:
617 del self._flags[f]
617 del self._flags[f]
618 self._dirty = True
618 self._dirty = True
619
619
620 def __setitem__(self, f, n):
620 def __setitem__(self, f, n):
621 assert n is not None
621 assert n is not None
622 self._load()
622 self._load()
623 dir, subpath = _splittopdir(f)
623 dir, subpath = _splittopdir(f)
624 if dir:
624 if dir:
625 if dir not in self._dirs:
625 if dir not in self._dirs:
626 self._dirs[dir] = treemanifest(self._subpath(dir))
626 self._dirs[dir] = treemanifest(self._subpath(dir))
627 self._dirs[dir].__setitem__(subpath, n)
627 self._dirs[dir].__setitem__(subpath, n)
628 else:
628 else:
629 self._files[f] = n[:21] # to match manifestdict's behavior
629 self._files[f] = n[:21] # to match manifestdict's behavior
630 self._dirty = True
630 self._dirty = True
631
631
632 def _load(self):
632 def _load(self):
633 if self._loadfunc is not _noop:
633 if self._loadfunc is not _noop:
634 lf, self._loadfunc = self._loadfunc, _noop
634 lf, self._loadfunc = self._loadfunc, _noop
635 lf(self)
635 lf(self)
636 elif self._copyfunc is not _noop:
636 elif self._copyfunc is not _noop:
637 cf, self._copyfunc = self._copyfunc, _noop
637 cf, self._copyfunc = self._copyfunc, _noop
638 cf(self)
638 cf(self)
639
639
640 def setflag(self, f, flags):
640 def setflag(self, f, flags):
641 """Set the flags (symlink, executable) for path f."""
641 """Set the flags (symlink, executable) for path f."""
642 assert 't' not in flags
643 self._load()
642 self._load()
644 dir, subpath = _splittopdir(f)
643 dir, subpath = _splittopdir(f)
645 if dir:
644 if dir:
646 if dir not in self._dirs:
645 if dir not in self._dirs:
647 self._dirs[dir] = treemanifest(self._subpath(dir))
646 self._dirs[dir] = treemanifest(self._subpath(dir))
648 self._dirs[dir].setflag(subpath, flags)
647 self._dirs[dir].setflag(subpath, flags)
649 else:
648 else:
650 self._flags[f] = flags
649 self._flags[f] = flags
651 self._dirty = True
650 self._dirty = True
652
651
653 def copy(self):
652 def copy(self):
654 copy = treemanifest(self._dir)
653 copy = treemanifest(self._dir)
655 copy._node = self._node
654 copy._node = self._node
656 copy._dirty = self._dirty
655 copy._dirty = self._dirty
657 if self._copyfunc is _noop:
656 if self._copyfunc is _noop:
658 def _copyfunc(s):
657 def _copyfunc(s):
659 self._load()
658 self._load()
660 for d in self._dirs:
659 for d in self._dirs:
661 s._dirs[d] = self._dirs[d].copy()
660 s._dirs[d] = self._dirs[d].copy()
662 s._files = dict.copy(self._files)
661 s._files = dict.copy(self._files)
663 s._flags = dict.copy(self._flags)
662 s._flags = dict.copy(self._flags)
664 if self._loadfunc is _noop:
663 if self._loadfunc is _noop:
665 _copyfunc(copy)
664 _copyfunc(copy)
666 else:
665 else:
667 copy._copyfunc = _copyfunc
666 copy._copyfunc = _copyfunc
668 else:
667 else:
669 copy._copyfunc = self._copyfunc
668 copy._copyfunc = self._copyfunc
670 return copy
669 return copy
671
670
672 def filesnotin(self, m2):
671 def filesnotin(self, m2):
673 '''Set of files in this manifest that are not in the other'''
672 '''Set of files in this manifest that are not in the other'''
674 files = set()
673 files = set()
675 def _filesnotin(t1, t2):
674 def _filesnotin(t1, t2):
676 if t1._node == t2._node and not t1._dirty and not t2._dirty:
675 if t1._node == t2._node and not t1._dirty and not t2._dirty:
677 return
676 return
678 t1._load()
677 t1._load()
679 t2._load()
678 t2._load()
680 for d, m1 in t1._dirs.iteritems():
679 for d, m1 in t1._dirs.iteritems():
681 if d in t2._dirs:
680 if d in t2._dirs:
682 m2 = t2._dirs[d]
681 m2 = t2._dirs[d]
683 _filesnotin(m1, m2)
682 _filesnotin(m1, m2)
684 else:
683 else:
685 files.update(m1.iterkeys())
684 files.update(m1.iterkeys())
686
685
687 for fn in t1._files.iterkeys():
686 for fn in t1._files.iterkeys():
688 if fn not in t2._files:
687 if fn not in t2._files:
689 files.add(t1._subpath(fn))
688 files.add(t1._subpath(fn))
690
689
691 _filesnotin(self, m2)
690 _filesnotin(self, m2)
692 return files
691 return files
693
692
694 @propertycache
693 @propertycache
695 def _alldirs(self):
694 def _alldirs(self):
696 return util.dirs(self)
695 return util.dirs(self)
697
696
698 def dirs(self):
697 def dirs(self):
699 return self._alldirs
698 return self._alldirs
700
699
701 def hasdir(self, dir):
700 def hasdir(self, dir):
702 self._load()
701 self._load()
703 topdir, subdir = _splittopdir(dir)
702 topdir, subdir = _splittopdir(dir)
704 if topdir:
703 if topdir:
705 if topdir in self._dirs:
704 if topdir in self._dirs:
706 return self._dirs[topdir].hasdir(subdir)
705 return self._dirs[topdir].hasdir(subdir)
707 return False
706 return False
708 return (dir + '/') in self._dirs
707 return (dir + '/') in self._dirs
709
708
710 def walk(self, match):
709 def walk(self, match):
711 '''Generates matching file names.
710 '''Generates matching file names.
712
711
713 Equivalent to manifest.matches(match).iterkeys(), but without creating
712 Equivalent to manifest.matches(match).iterkeys(), but without creating
714 an entirely new manifest.
713 an entirely new manifest.
715
714
716 It also reports nonexistent files by marking them bad with match.bad().
715 It also reports nonexistent files by marking them bad with match.bad().
717 '''
716 '''
718 if match.always():
717 if match.always():
719 for f in iter(self):
718 for f in iter(self):
720 yield f
719 yield f
721 return
720 return
722
721
723 fset = set(match.files())
722 fset = set(match.files())
724
723
725 for fn in self._walk(match):
724 for fn in self._walk(match):
726 if fn in fset:
725 if fn in fset:
727 # specified pattern is the exact name
726 # specified pattern is the exact name
728 fset.remove(fn)
727 fset.remove(fn)
729 yield fn
728 yield fn
730
729
731 # for dirstate.walk, files=['.'] means "walk the whole tree".
730 # for dirstate.walk, files=['.'] means "walk the whole tree".
732 # follow that here, too
731 # follow that here, too
733 fset.discard('.')
732 fset.discard('.')
734
733
735 for fn in sorted(fset):
734 for fn in sorted(fset):
736 if not self.hasdir(fn):
735 if not self.hasdir(fn):
737 match.bad(fn, None)
736 match.bad(fn, None)
738
737
739 def _walk(self, match):
738 def _walk(self, match):
740 '''Recursively generates matching file names for walk().'''
739 '''Recursively generates matching file names for walk().'''
741 if not match.visitdir(self._dir[:-1] or '.'):
740 if not match.visitdir(self._dir[:-1] or '.'):
742 return
741 return
743
742
744 # yield this dir's files and walk its submanifests
743 # yield this dir's files and walk its submanifests
745 self._load()
744 self._load()
746 for p in sorted(self._dirs.keys() + self._files.keys()):
745 for p in sorted(self._dirs.keys() + self._files.keys()):
747 if p in self._files:
746 if p in self._files:
748 fullp = self._subpath(p)
747 fullp = self._subpath(p)
749 if match(fullp):
748 if match(fullp):
750 yield fullp
749 yield fullp
751 else:
750 else:
752 for f in self._dirs[p]._walk(match):
751 for f in self._dirs[p]._walk(match):
753 yield f
752 yield f
754
753
755 def matches(self, match):
754 def matches(self, match):
756 '''generate a new manifest filtered by the match argument'''
755 '''generate a new manifest filtered by the match argument'''
757 if match.always():
756 if match.always():
758 return self.copy()
757 return self.copy()
759
758
760 return self._matches(match)
759 return self._matches(match)
761
760
762 def _matches(self, match):
761 def _matches(self, match):
763 '''recursively generate a new manifest filtered by the match argument.
762 '''recursively generate a new manifest filtered by the match argument.
764 '''
763 '''
765
764
766 visit = match.visitdir(self._dir[:-1] or '.')
765 visit = match.visitdir(self._dir[:-1] or '.')
767 if visit == 'all':
766 if visit == 'all':
768 return self.copy()
767 return self.copy()
769 ret = treemanifest(self._dir)
768 ret = treemanifest(self._dir)
770 if not visit:
769 if not visit:
771 return ret
770 return ret
772
771
773 self._load()
772 self._load()
774 for fn in self._files:
773 for fn in self._files:
775 fullp = self._subpath(fn)
774 fullp = self._subpath(fn)
776 if not match(fullp):
775 if not match(fullp):
777 continue
776 continue
778 ret._files[fn] = self._files[fn]
777 ret._files[fn] = self._files[fn]
779 if fn in self._flags:
778 if fn in self._flags:
780 ret._flags[fn] = self._flags[fn]
779 ret._flags[fn] = self._flags[fn]
781
780
782 for dir, subm in self._dirs.iteritems():
781 for dir, subm in self._dirs.iteritems():
783 m = subm._matches(match)
782 m = subm._matches(match)
784 if not m._isempty():
783 if not m._isempty():
785 ret._dirs[dir] = m
784 ret._dirs[dir] = m
786
785
787 if not ret._isempty():
786 if not ret._isempty():
788 ret._dirty = True
787 ret._dirty = True
789 return ret
788 return ret
790
789
791 def diff(self, m2, clean=False):
790 def diff(self, m2, clean=False):
792 '''Finds changes between the current manifest and m2.
791 '''Finds changes between the current manifest and m2.
793
792
794 Args:
793 Args:
795 m2: the manifest to which this manifest should be compared.
794 m2: the manifest to which this manifest should be compared.
796 clean: if true, include files unchanged between these manifests
795 clean: if true, include files unchanged between these manifests
797 with a None value in the returned dictionary.
796 with a None value in the returned dictionary.
798
797
799 The result is returned as a dict with filename as key and
798 The result is returned as a dict with filename as key and
800 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
799 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
801 nodeid in the current/other manifest and fl1/fl2 is the flag
800 nodeid in the current/other manifest and fl1/fl2 is the flag
802 in the current/other manifest. Where the file does not exist,
801 in the current/other manifest. Where the file does not exist,
803 the nodeid will be None and the flags will be the empty
802 the nodeid will be None and the flags will be the empty
804 string.
803 string.
805 '''
804 '''
806 result = {}
805 result = {}
807 emptytree = treemanifest()
806 emptytree = treemanifest()
808 def _diff(t1, t2):
807 def _diff(t1, t2):
809 if t1._node == t2._node and not t1._dirty and not t2._dirty:
808 if t1._node == t2._node and not t1._dirty and not t2._dirty:
810 return
809 return
811 t1._load()
810 t1._load()
812 t2._load()
811 t2._load()
813 for d, m1 in t1._dirs.iteritems():
812 for d, m1 in t1._dirs.iteritems():
814 m2 = t2._dirs.get(d, emptytree)
813 m2 = t2._dirs.get(d, emptytree)
815 _diff(m1, m2)
814 _diff(m1, m2)
816
815
817 for d, m2 in t2._dirs.iteritems():
816 for d, m2 in t2._dirs.iteritems():
818 if d not in t1._dirs:
817 if d not in t1._dirs:
819 _diff(emptytree, m2)
818 _diff(emptytree, m2)
820
819
821 for fn, n1 in t1._files.iteritems():
820 for fn, n1 in t1._files.iteritems():
822 fl1 = t1._flags.get(fn, '')
821 fl1 = t1._flags.get(fn, '')
823 n2 = t2._files.get(fn, None)
822 n2 = t2._files.get(fn, None)
824 fl2 = t2._flags.get(fn, '')
823 fl2 = t2._flags.get(fn, '')
825 if n1 != n2 or fl1 != fl2:
824 if n1 != n2 or fl1 != fl2:
826 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
825 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
827 elif clean:
826 elif clean:
828 result[t1._subpath(fn)] = None
827 result[t1._subpath(fn)] = None
829
828
830 for fn, n2 in t2._files.iteritems():
829 for fn, n2 in t2._files.iteritems():
831 if fn not in t1._files:
830 if fn not in t1._files:
832 fl2 = t2._flags.get(fn, '')
831 fl2 = t2._flags.get(fn, '')
833 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
832 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
834
833
835 _diff(self, m2)
834 _diff(self, m2)
836 return result
835 return result
837
836
838 def unmodifiedsince(self, m2):
837 def unmodifiedsince(self, m2):
839 return not self._dirty and not m2._dirty and self._node == m2._node
838 return not self._dirty and not m2._dirty and self._node == m2._node
840
839
841 def parse(self, text, readsubtree):
840 def parse(self, text, readsubtree):
842 for f, n, fl in _parse(text):
841 for f, n, fl in _parse(text):
843 if fl == 't':
842 if fl == 't':
844 f = f + '/'
843 f = f + '/'
845 self._dirs[f] = readsubtree(self._subpath(f), n)
844 self._dirs[f] = readsubtree(self._subpath(f), n)
846 elif '/' in f:
845 elif '/' in f:
847 # This is a flat manifest, so use __setitem__ and setflag rather
846 # This is a flat manifest, so use __setitem__ and setflag rather
848 # than assigning directly to _files and _flags, so we can
847 # than assigning directly to _files and _flags, so we can
849 # assign a path in a subdirectory, and to mark dirty (compared
848 # assign a path in a subdirectory, and to mark dirty (compared
850 # to nullid).
849 # to nullid).
851 self[f] = n
850 self[f] = n
852 if fl:
851 if fl:
853 self.setflag(f, fl)
852 self.setflag(f, fl)
854 else:
853 else:
855 # Assigning to _files and _flags avoids marking as dirty,
854 # Assigning to _files and _flags avoids marking as dirty,
856 # and should be a little faster.
855 # and should be a little faster.
857 self._files[f] = n
856 self._files[f] = n
858 if fl:
857 if fl:
859 self._flags[f] = fl
858 self._flags[f] = fl
860
859
861 def text(self, usemanifestv2=False):
860 def text(self, usemanifestv2=False):
862 """Get the full data of this manifest as a bytestring."""
861 """Get the full data of this manifest as a bytestring."""
863 self._load()
862 self._load()
864 return _text(self.iterentries(), usemanifestv2)
863 return _text(self.iterentries(), usemanifestv2)
865
864
866 def dirtext(self, usemanifestv2=False):
865 def dirtext(self, usemanifestv2=False):
867 """Get the full data of this directory as a bytestring. Make sure that
866 """Get the full data of this directory as a bytestring. Make sure that
868 any submanifests have been written first, so their nodeids are correct.
867 any submanifests have been written first, so their nodeids are correct.
869 """
868 """
870 self._load()
869 self._load()
871 flags = self.flags
870 flags = self.flags
872 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
871 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
873 files = [(f, self._files[f], flags(f)) for f in self._files]
872 files = [(f, self._files[f], flags(f)) for f in self._files]
874 return _text(sorted(dirs + files), usemanifestv2)
873 return _text(sorted(dirs + files), usemanifestv2)
875
874
876 def read(self, gettext, readsubtree):
875 def read(self, gettext, readsubtree):
877 def _load_for_read(s):
876 def _load_for_read(s):
878 s.parse(gettext(), readsubtree)
877 s.parse(gettext(), readsubtree)
879 s._dirty = False
878 s._dirty = False
880 self._loadfunc = _load_for_read
879 self._loadfunc = _load_for_read
881
880
882 def writesubtrees(self, m1, m2, writesubtree):
881 def writesubtrees(self, m1, m2, writesubtree):
883 self._load() # for consistency; should never have any effect here
882 self._load() # for consistency; should never have any effect here
884 emptytree = treemanifest()
883 emptytree = treemanifest()
885 for d, subm in self._dirs.iteritems():
884 for d, subm in self._dirs.iteritems():
886 subp1 = m1._dirs.get(d, emptytree)._node
885 subp1 = m1._dirs.get(d, emptytree)._node
887 subp2 = m2._dirs.get(d, emptytree)._node
886 subp2 = m2._dirs.get(d, emptytree)._node
888 if subp1 == revlog.nullid:
887 if subp1 == revlog.nullid:
889 subp1, subp2 = subp2, subp1
888 subp1, subp2 = subp2, subp1
890 writesubtree(subm, subp1, subp2)
889 writesubtree(subm, subp1, subp2)
891
890
892 class manifest(revlog.revlog):
891 class manifest(revlog.revlog):
893 def __init__(self, opener, dir='', dirlogcache=None):
892 def __init__(self, opener, dir='', dirlogcache=None):
894 '''The 'dir' and 'dirlogcache' arguments are for internal use by
893 '''The 'dir' and 'dirlogcache' arguments are for internal use by
895 manifest.manifest only. External users should create a root manifest
894 manifest.manifest only. External users should create a root manifest
896 log with manifest.manifest(opener) and call dirlog() on it.
895 log with manifest.manifest(opener) and call dirlog() on it.
897 '''
896 '''
898 # During normal operations, we expect to deal with not more than four
897 # During normal operations, we expect to deal with not more than four
899 # revs at a time (such as during commit --amend). When rebasing large
898 # revs at a time (such as during commit --amend). When rebasing large
900 # stacks of commits, the number can go up, hence the config knob below.
899 # stacks of commits, the number can go up, hence the config knob below.
901 cachesize = 4
900 cachesize = 4
902 usetreemanifest = False
901 usetreemanifest = False
903 usemanifestv2 = False
902 usemanifestv2 = False
904 opts = getattr(opener, 'options', None)
903 opts = getattr(opener, 'options', None)
905 if opts is not None:
904 if opts is not None:
906 cachesize = opts.get('manifestcachesize', cachesize)
905 cachesize = opts.get('manifestcachesize', cachesize)
907 usetreemanifest = opts.get('treemanifest', usetreemanifest)
906 usetreemanifest = opts.get('treemanifest', usetreemanifest)
908 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
907 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
909 self._mancache = util.lrucachedict(cachesize)
908 self._mancache = util.lrucachedict(cachesize)
910 self._treeinmem = usetreemanifest
909 self._treeinmem = usetreemanifest
911 self._treeondisk = usetreemanifest
910 self._treeondisk = usetreemanifest
912 self._usemanifestv2 = usemanifestv2
911 self._usemanifestv2 = usemanifestv2
913 indexfile = "00manifest.i"
912 indexfile = "00manifest.i"
914 if dir:
913 if dir:
915 assert self._treeondisk
914 assert self._treeondisk
916 if not dir.endswith('/'):
915 if not dir.endswith('/'):
917 dir = dir + '/'
916 dir = dir + '/'
918 indexfile = "meta/" + dir + "00manifest.i"
917 indexfile = "meta/" + dir + "00manifest.i"
919 revlog.revlog.__init__(self, opener, indexfile)
918 revlog.revlog.__init__(self, opener, indexfile)
920 self._dir = dir
919 self._dir = dir
921 # The dirlogcache is kept on the root manifest log
920 # The dirlogcache is kept on the root manifest log
922 if dir:
921 if dir:
923 self._dirlogcache = dirlogcache
922 self._dirlogcache = dirlogcache
924 else:
923 else:
925 self._dirlogcache = {'': self}
924 self._dirlogcache = {'': self}
926
925
927 def _newmanifest(self, data=''):
926 def _newmanifest(self, data=''):
928 if self._treeinmem:
927 if self._treeinmem:
929 return treemanifest(self._dir, data)
928 return treemanifest(self._dir, data)
930 return manifestdict(data)
929 return manifestdict(data)
931
930
932 def dirlog(self, dir):
931 def dirlog(self, dir):
933 if dir:
932 if dir:
934 assert self._treeondisk
933 assert self._treeondisk
935 if dir not in self._dirlogcache:
934 if dir not in self._dirlogcache:
936 self._dirlogcache[dir] = manifest(self.opener, dir,
935 self._dirlogcache[dir] = manifest(self.opener, dir,
937 self._dirlogcache)
936 self._dirlogcache)
938 return self._dirlogcache[dir]
937 return self._dirlogcache[dir]
939
938
940 def _slowreaddelta(self, node):
939 def _slowreaddelta(self, node):
941 r0 = self.deltaparent(self.rev(node))
940 r0 = self.deltaparent(self.rev(node))
942 m0 = self.read(self.node(r0))
941 m0 = self.read(self.node(r0))
943 m1 = self.read(node)
942 m1 = self.read(node)
944 md = self._newmanifest()
943 md = self._newmanifest()
945 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
944 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
946 if n1:
945 if n1:
947 md[f] = n1
946 md[f] = n1
948 if fl1:
947 if fl1:
949 md.setflag(f, fl1)
948 md.setflag(f, fl1)
950 return md
949 return md
951
950
952 def readdelta(self, node):
951 def readdelta(self, node):
953 if self._usemanifestv2 or self._treeondisk:
952 if self._usemanifestv2 or self._treeondisk:
954 return self._slowreaddelta(node)
953 return self._slowreaddelta(node)
955 r = self.rev(node)
954 r = self.rev(node)
956 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
955 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
957 return self._newmanifest(d)
956 return self._newmanifest(d)
958
957
959 def readshallowdelta(self, node):
958 def readshallowdelta(self, node):
960 '''For flat manifests, this is the same as readdelta(). For
959 '''For flat manifests, this is the same as readdelta(). For
961 treemanifests, this will read the delta for this revlog's directory,
960 treemanifests, this will read the delta for this revlog's directory,
962 without recursively reading subdirectory manifests. Instead, any
961 without recursively reading subdirectory manifests. Instead, any
963 subdirectory entry will be reported as it appears in the manifests, i.e.
962 subdirectory entry will be reported as it appears in the manifests, i.e.
964 the subdirectory will be reported among files and distinguished only by
963 the subdirectory will be reported among files and distinguished only by
965 its 't' flag.'''
964 its 't' flag.'''
966 if not self._treeondisk:
965 if not self._treeondisk:
967 return self.readdelta(node)
966 return self.readdelta(node)
968 if self._usemanifestv2:
967 if self._usemanifestv2:
969 raise error.Abort(
968 raise error.Abort(
970 "readshallowdelta() not implemented for manifestv2")
969 "readshallowdelta() not implemented for manifestv2")
971 r = self.rev(node)
970 r = self.rev(node)
972 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
971 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
973 return manifestdict(d)
972 return manifestdict(d)
974
973
975 def readfast(self, node):
974 def readfast(self, node):
976 '''use the faster of readdelta or read
975 '''use the faster of readdelta or read
977
976
978 This will return a manifest which is either only the files
977 This will return a manifest which is either only the files
979 added/modified relative to p1, or all files in the
978 added/modified relative to p1, or all files in the
980 manifest. Which one is returned depends on the codepath used
979 manifest. Which one is returned depends on the codepath used
981 to retrieve the data.
980 to retrieve the data.
982 '''
981 '''
983 r = self.rev(node)
982 r = self.rev(node)
984 deltaparent = self.deltaparent(r)
983 deltaparent = self.deltaparent(r)
985 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
984 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
986 return self.readdelta(node)
985 return self.readdelta(node)
987 return self.read(node)
986 return self.read(node)
988
987
989 def read(self, node):
988 def read(self, node):
990 if node == revlog.nullid:
989 if node == revlog.nullid:
991 return self._newmanifest() # don't upset local cache
990 return self._newmanifest() # don't upset local cache
992 if node in self._mancache:
991 if node in self._mancache:
993 return self._mancache[node][0]
992 return self._mancache[node][0]
994 if self._treeondisk:
993 if self._treeondisk:
995 def gettext():
994 def gettext():
996 return self.revision(node)
995 return self.revision(node)
997 def readsubtree(dir, subm):
996 def readsubtree(dir, subm):
998 return self.dirlog(dir).read(subm)
997 return self.dirlog(dir).read(subm)
999 m = self._newmanifest()
998 m = self._newmanifest()
1000 m.read(gettext, readsubtree)
999 m.read(gettext, readsubtree)
1001 m.setnode(node)
1000 m.setnode(node)
1002 arraytext = None
1001 arraytext = None
1003 else:
1002 else:
1004 text = self.revision(node)
1003 text = self.revision(node)
1005 m = self._newmanifest(text)
1004 m = self._newmanifest(text)
1006 arraytext = array.array('c', text)
1005 arraytext = array.array('c', text)
1007 self._mancache[node] = (m, arraytext)
1006 self._mancache[node] = (m, arraytext)
1008 return m
1007 return m
1009
1008
1010 def find(self, node, f):
1009 def find(self, node, f):
1011 '''look up entry for a single file efficiently.
1010 '''look up entry for a single file efficiently.
1012 return (node, flags) pair if found, (None, None) if not.'''
1011 return (node, flags) pair if found, (None, None) if not.'''
1013 m = self.read(node)
1012 m = self.read(node)
1014 try:
1013 try:
1015 return m.find(f)
1014 return m.find(f)
1016 except KeyError:
1015 except KeyError:
1017 return None, None
1016 return None, None
1018
1017
1019 def add(self, m, transaction, link, p1, p2, added, removed):
1018 def add(self, m, transaction, link, p1, p2, added, removed):
1020 if (p1 in self._mancache and not self._treeinmem
1019 if (p1 in self._mancache and not self._treeinmem
1021 and not self._usemanifestv2):
1020 and not self._usemanifestv2):
1022 # If our first parent is in the manifest cache, we can
1021 # If our first parent is in the manifest cache, we can
1023 # compute a delta here using properties we know about the
1022 # compute a delta here using properties we know about the
1024 # manifest up-front, which may save time later for the
1023 # manifest up-front, which may save time later for the
1025 # revlog layer.
1024 # revlog layer.
1026
1025
1027 _checkforbidden(added)
1026 _checkforbidden(added)
1028 # combine the changed lists into one sorted iterator
1027 # combine the changed lists into one sorted iterator
1029 work = heapq.merge([(x, False) for x in added],
1028 work = heapq.merge([(x, False) for x in added],
1030 [(x, True) for x in removed])
1029 [(x, True) for x in removed])
1031
1030
1032 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
1031 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
1033 cachedelta = self.rev(p1), deltatext
1032 cachedelta = self.rev(p1), deltatext
1034 text = util.buffer(arraytext)
1033 text = util.buffer(arraytext)
1035 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1034 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1036 else:
1035 else:
1037 # The first parent manifest isn't already loaded, so we'll
1036 # The first parent manifest isn't already loaded, so we'll
1038 # just encode a fulltext of the manifest and pass that
1037 # just encode a fulltext of the manifest and pass that
1039 # through to the revlog layer, and let it handle the delta
1038 # through to the revlog layer, and let it handle the delta
1040 # process.
1039 # process.
1041 if self._treeondisk:
1040 if self._treeondisk:
1042 m1 = self.read(p1)
1041 m1 = self.read(p1)
1043 m2 = self.read(p2)
1042 m2 = self.read(p2)
1044 n = self._addtree(m, transaction, link, m1, m2)
1043 n = self._addtree(m, transaction, link, m1, m2)
1045 arraytext = None
1044 arraytext = None
1046 else:
1045 else:
1047 text = m.text(self._usemanifestv2)
1046 text = m.text(self._usemanifestv2)
1048 n = self.addrevision(text, transaction, link, p1, p2)
1047 n = self.addrevision(text, transaction, link, p1, p2)
1049 arraytext = array.array('c', text)
1048 arraytext = array.array('c', text)
1050
1049
1051 self._mancache[n] = (m, arraytext)
1050 self._mancache[n] = (m, arraytext)
1052
1051
1053 return n
1052 return n
1054
1053
1055 def _addtree(self, m, transaction, link, m1, m2):
1054 def _addtree(self, m, transaction, link, m1, m2):
1056 # If the manifest is unchanged compared to one parent,
1055 # If the manifest is unchanged compared to one parent,
1057 # don't write a new revision
1056 # don't write a new revision
1058 if m.unmodifiedsince(m1) or m.unmodifiedsince(m2):
1057 if m.unmodifiedsince(m1) or m.unmodifiedsince(m2):
1059 return m.node()
1058 return m.node()
1060 def writesubtree(subm, subp1, subp2):
1059 def writesubtree(subm, subp1, subp2):
1061 sublog = self.dirlog(subm.dir())
1060 sublog = self.dirlog(subm.dir())
1062 sublog.add(subm, transaction, link, subp1, subp2, None, None)
1061 sublog.add(subm, transaction, link, subp1, subp2, None, None)
1063 m.writesubtrees(m1, m2, writesubtree)
1062 m.writesubtrees(m1, m2, writesubtree)
1064 text = m.dirtext(self._usemanifestv2)
1063 text = m.dirtext(self._usemanifestv2)
1065 # Double-check whether contents are unchanged to one parent
1064 # Double-check whether contents are unchanged to one parent
1066 if text == m1.dirtext(self._usemanifestv2):
1065 if text == m1.dirtext(self._usemanifestv2):
1067 n = m1.node()
1066 n = m1.node()
1068 elif text == m2.dirtext(self._usemanifestv2):
1067 elif text == m2.dirtext(self._usemanifestv2):
1069 n = m2.node()
1068 n = m2.node()
1070 else:
1069 else:
1071 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1070 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1072 # Save nodeid so parent manifest can calculate its nodeid
1071 # Save nodeid so parent manifest can calculate its nodeid
1073 m.setnode(n)
1072 m.setnode(n)
1074 return n
1073 return n
1075
1074
1076 def clearcaches(self):
1075 def clearcaches(self):
1077 super(manifest, self).clearcaches()
1076 super(manifest, self).clearcaches()
1078 self._mancache.clear()
1077 self._mancache.clear()
1079 self._dirlogcache = {'': self}
1078 self._dirlogcache = {'': self}
General Comments 0
You need to be logged in to leave comments. Login now