##// END OF EJS Templates
manifest: drop withflags() method, which is now unused
Augie Fackler -
r23758:399a8403 default
parent child Browse files
Show More
@@ -1,297 +1,295
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import mdiff, parsers, error, revlog, util
9 import mdiff, parsers, error, revlog, util
10 import array, struct
10 import array, struct
11
11
12 class manifestdict(dict):
12 class manifestdict(dict):
13 def __init__(self, mapping=None, flags=None):
13 def __init__(self, mapping=None, flags=None):
14 if mapping is None:
14 if mapping is None:
15 mapping = {}
15 mapping = {}
16 if flags is None:
16 if flags is None:
17 flags = {}
17 flags = {}
18 dict.__init__(self, mapping)
18 dict.__init__(self, mapping)
19 self._flags = flags
19 self._flags = flags
20 def __setitem__(self, k, v):
20 def __setitem__(self, k, v):
21 assert v is not None
21 assert v is not None
22 dict.__setitem__(self, k, v)
22 dict.__setitem__(self, k, v)
23 def flags(self, f):
23 def flags(self, f):
24 return self._flags.get(f, "")
24 return self._flags.get(f, "")
25 def withflags(self):
26 return set(self._flags.keys())
27 def setflag(self, f, flags):
25 def setflag(self, f, flags):
28 """Set the flags (symlink, executable) for path f."""
26 """Set the flags (symlink, executable) for path f."""
29 self._flags[f] = flags
27 self._flags[f] = flags
30 def copy(self):
28 def copy(self):
31 return manifestdict(self, dict.copy(self._flags))
29 return manifestdict(self, dict.copy(self._flags))
32 def intersectfiles(self, files):
30 def intersectfiles(self, files):
33 '''make a new manifestdict with the intersection of self with files
31 '''make a new manifestdict with the intersection of self with files
34
32
35 The algorithm assumes that files is much smaller than self.'''
33 The algorithm assumes that files is much smaller than self.'''
36 ret = manifestdict()
34 ret = manifestdict()
37 for fn in files:
35 for fn in files:
38 if fn in self:
36 if fn in self:
39 ret[fn] = self[fn]
37 ret[fn] = self[fn]
40 flags = self._flags.get(fn, None)
38 flags = self._flags.get(fn, None)
41 if flags:
39 if flags:
42 ret._flags[fn] = flags
40 ret._flags[fn] = flags
43 return ret
41 return ret
44
42
45 def matches(self, match):
43 def matches(self, match):
46 '''generate a new manifest filtered by the match argument'''
44 '''generate a new manifest filtered by the match argument'''
47 if match.always():
45 if match.always():
48 return self.copy()
46 return self.copy()
49
47
50 files = match.files()
48 files = match.files()
51 if (match.matchfn == match.exact or
49 if (match.matchfn == match.exact or
52 (not match.anypats() and util.all(fn in self for fn in files))):
50 (not match.anypats() and util.all(fn in self for fn in files))):
53 return self.intersectfiles(files)
51 return self.intersectfiles(files)
54
52
55 mf = self.copy()
53 mf = self.copy()
56 for fn in mf.keys():
54 for fn in mf.keys():
57 if not match(fn):
55 if not match(fn):
58 del mf[fn]
56 del mf[fn]
59 return mf
57 return mf
60
58
61 def diff(self, m2, clean=False):
59 def diff(self, m2, clean=False):
62 '''Finds changes between the current manifest and m2.
60 '''Finds changes between the current manifest and m2.
63
61
64 Args:
62 Args:
65 m2: the manifest to which this manifest should be compared.
63 m2: the manifest to which this manifest should be compared.
66 clean: if true, include files unchanged between these manifests
64 clean: if true, include files unchanged between these manifests
67 with a None value in the returned dictionary.
65 with a None value in the returned dictionary.
68
66
69 The result is returned as a dict with filename as key and
67 The result is returned as a dict with filename as key and
70 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
68 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
71 nodeid in the current/other manifest and fl1/fl2 is the flag
69 nodeid in the current/other manifest and fl1/fl2 is the flag
72 in the current/other manifest. Where the file does not exist,
70 in the current/other manifest. Where the file does not exist,
73 the nodeid will be None and the flags will be the empty
71 the nodeid will be None and the flags will be the empty
74 string.
72 string.
75 '''
73 '''
76 diff = {}
74 diff = {}
77
75
78 for fn, n1 in self.iteritems():
76 for fn, n1 in self.iteritems():
79 fl1 = self._flags.get(fn, '')
77 fl1 = self._flags.get(fn, '')
80 n2 = m2.get(fn, None)
78 n2 = m2.get(fn, None)
81 fl2 = m2._flags.get(fn, '')
79 fl2 = m2._flags.get(fn, '')
82 if n2 is None:
80 if n2 is None:
83 fl2 = ''
81 fl2 = ''
84 if n1 != n2 or fl1 != fl2:
82 if n1 != n2 or fl1 != fl2:
85 diff[fn] = ((n1, fl1), (n2, fl2))
83 diff[fn] = ((n1, fl1), (n2, fl2))
86 elif clean:
84 elif clean:
87 diff[fn] = None
85 diff[fn] = None
88
86
89 for fn, n2 in m2.iteritems():
87 for fn, n2 in m2.iteritems():
90 if fn not in self:
88 if fn not in self:
91 fl2 = m2._flags.get(fn, '')
89 fl2 = m2._flags.get(fn, '')
92 diff[fn] = ((None, ''), (n2, fl2))
90 diff[fn] = ((None, ''), (n2, fl2))
93
91
94 return diff
92 return diff
95
93
96 def text(self):
94 def text(self):
97 """Get the full data of this manifest as a bytestring."""
95 """Get the full data of this manifest as a bytestring."""
98 fl = sorted(self)
96 fl = sorted(self)
99 _checkforbidden(fl)
97 _checkforbidden(fl)
100
98
101 hex, flags = revlog.hex, self.flags
99 hex, flags = revlog.hex, self.flags
102 # if this is changed to support newlines in filenames,
100 # if this is changed to support newlines in filenames,
103 # be sure to check the templates/ dir again (especially *-raw.tmpl)
101 # be sure to check the templates/ dir again (especially *-raw.tmpl)
104 return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl)
102 return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl)
105
103
106 def fastdelta(self, base, changes):
104 def fastdelta(self, base, changes):
107 """Given a base manifest text as an array.array and a list of changes
105 """Given a base manifest text as an array.array and a list of changes
108 relative to that text, compute a delta that can be used by revlog.
106 relative to that text, compute a delta that can be used by revlog.
109 """
107 """
110 delta = []
108 delta = []
111 dstart = None
109 dstart = None
112 dend = None
110 dend = None
113 dline = [""]
111 dline = [""]
114 start = 0
112 start = 0
115 # zero copy representation of base as a buffer
113 # zero copy representation of base as a buffer
116 addbuf = util.buffer(base)
114 addbuf = util.buffer(base)
117
115
118 # start with a readonly loop that finds the offset of
116 # start with a readonly loop that finds the offset of
119 # each line and creates the deltas
117 # each line and creates the deltas
120 for f, todelete in changes:
118 for f, todelete in changes:
121 # bs will either be the index of the item or the insert point
119 # bs will either be the index of the item or the insert point
122 start, end = _msearch(addbuf, f, start)
120 start, end = _msearch(addbuf, f, start)
123 if not todelete:
121 if not todelete:
124 l = "%s\0%s%s\n" % (f, revlog.hex(self[f]), self.flags(f))
122 l = "%s\0%s%s\n" % (f, revlog.hex(self[f]), self.flags(f))
125 else:
123 else:
126 if start == end:
124 if start == end:
127 # item we want to delete was not found, error out
125 # item we want to delete was not found, error out
128 raise AssertionError(
126 raise AssertionError(
129 _("failed to remove %s from manifest") % f)
127 _("failed to remove %s from manifest") % f)
130 l = ""
128 l = ""
131 if dstart is not None and dstart <= start and dend >= start:
129 if dstart is not None and dstart <= start and dend >= start:
132 if dend < end:
130 if dend < end:
133 dend = end
131 dend = end
134 if l:
132 if l:
135 dline.append(l)
133 dline.append(l)
136 else:
134 else:
137 if dstart is not None:
135 if dstart is not None:
138 delta.append([dstart, dend, "".join(dline)])
136 delta.append([dstart, dend, "".join(dline)])
139 dstart = start
137 dstart = start
140 dend = end
138 dend = end
141 dline = [l]
139 dline = [l]
142
140
143 if dstart is not None:
141 if dstart is not None:
144 delta.append([dstart, dend, "".join(dline)])
142 delta.append([dstart, dend, "".join(dline)])
145 # apply the delta to the base, and get a delta for addrevision
143 # apply the delta to the base, and get a delta for addrevision
146 deltatext, arraytext = _addlistdelta(base, delta)
144 deltatext, arraytext = _addlistdelta(base, delta)
147 return arraytext, deltatext
145 return arraytext, deltatext
148
146
149 def _msearch(m, s, lo=0, hi=None):
147 def _msearch(m, s, lo=0, hi=None):
150 '''return a tuple (start, end) that says where to find s within m.
148 '''return a tuple (start, end) that says where to find s within m.
151
149
152 If the string is found m[start:end] are the line containing
150 If the string is found m[start:end] are the line containing
153 that string. If start == end the string was not found and
151 that string. If start == end the string was not found and
154 they indicate the proper sorted insertion point.
152 they indicate the proper sorted insertion point.
155
153
156 m should be a buffer or a string
154 m should be a buffer or a string
157 s is a string'''
155 s is a string'''
158 def advance(i, c):
156 def advance(i, c):
159 while i < lenm and m[i] != c:
157 while i < lenm and m[i] != c:
160 i += 1
158 i += 1
161 return i
159 return i
162 if not s:
160 if not s:
163 return (lo, lo)
161 return (lo, lo)
164 lenm = len(m)
162 lenm = len(m)
165 if not hi:
163 if not hi:
166 hi = lenm
164 hi = lenm
167 while lo < hi:
165 while lo < hi:
168 mid = (lo + hi) // 2
166 mid = (lo + hi) // 2
169 start = mid
167 start = mid
170 while start > 0 and m[start - 1] != '\n':
168 while start > 0 and m[start - 1] != '\n':
171 start -= 1
169 start -= 1
172 end = advance(start, '\0')
170 end = advance(start, '\0')
173 if m[start:end] < s:
171 if m[start:end] < s:
174 # we know that after the null there are 40 bytes of sha1
172 # we know that after the null there are 40 bytes of sha1
175 # this translates to the bisect lo = mid + 1
173 # this translates to the bisect lo = mid + 1
176 lo = advance(end + 40, '\n') + 1
174 lo = advance(end + 40, '\n') + 1
177 else:
175 else:
178 # this translates to the bisect hi = mid
176 # this translates to the bisect hi = mid
179 hi = start
177 hi = start
180 end = advance(lo, '\0')
178 end = advance(lo, '\0')
181 found = m[lo:end]
179 found = m[lo:end]
182 if s == found:
180 if s == found:
183 # we know that after the null there are 40 bytes of sha1
181 # we know that after the null there are 40 bytes of sha1
184 end = advance(end + 40, '\n')
182 end = advance(end + 40, '\n')
185 return (lo, end + 1)
183 return (lo, end + 1)
186 else:
184 else:
187 return (lo, lo)
185 return (lo, lo)
188
186
189 def _checkforbidden(l):
187 def _checkforbidden(l):
190 """Check filenames for illegal characters."""
188 """Check filenames for illegal characters."""
191 for f in l:
189 for f in l:
192 if '\n' in f or '\r' in f:
190 if '\n' in f or '\r' in f:
193 raise error.RevlogError(
191 raise error.RevlogError(
194 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
192 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
195
193
196
194
197 # apply the changes collected during the bisect loop to our addlist
195 # apply the changes collected during the bisect loop to our addlist
198 # return a delta suitable for addrevision
196 # return a delta suitable for addrevision
199 def _addlistdelta(addlist, x):
197 def _addlistdelta(addlist, x):
200 # for large addlist arrays, building a new array is cheaper
198 # for large addlist arrays, building a new array is cheaper
201 # than repeatedly modifying the existing one
199 # than repeatedly modifying the existing one
202 currentposition = 0
200 currentposition = 0
203 newaddlist = array.array('c')
201 newaddlist = array.array('c')
204
202
205 for start, end, content in x:
203 for start, end, content in x:
206 newaddlist += addlist[currentposition:start]
204 newaddlist += addlist[currentposition:start]
207 if content:
205 if content:
208 newaddlist += array.array('c', content)
206 newaddlist += array.array('c', content)
209
207
210 currentposition = end
208 currentposition = end
211
209
212 newaddlist += addlist[currentposition:]
210 newaddlist += addlist[currentposition:]
213
211
214 deltatext = "".join(struct.pack(">lll", start, end, len(content))
212 deltatext = "".join(struct.pack(">lll", start, end, len(content))
215 + content for start, end, content in x)
213 + content for start, end, content in x)
216 return deltatext, newaddlist
214 return deltatext, newaddlist
217
215
218 def _parse(lines):
216 def _parse(lines):
219 mfdict = manifestdict()
217 mfdict = manifestdict()
220 parsers.parse_manifest(mfdict, mfdict._flags, lines)
218 parsers.parse_manifest(mfdict, mfdict._flags, lines)
221 return mfdict
219 return mfdict
222
220
223 class manifest(revlog.revlog):
221 class manifest(revlog.revlog):
224 def __init__(self, opener):
222 def __init__(self, opener):
225 # we expect to deal with not more than four revs at a time,
223 # we expect to deal with not more than four revs at a time,
226 # during a commit --amend
224 # during a commit --amend
227 self._mancache = util.lrucachedict(4)
225 self._mancache = util.lrucachedict(4)
228 revlog.revlog.__init__(self, opener, "00manifest.i")
226 revlog.revlog.__init__(self, opener, "00manifest.i")
229
227
230 def readdelta(self, node):
228 def readdelta(self, node):
231 r = self.rev(node)
229 r = self.rev(node)
232 return _parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
230 return _parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
233
231
234 def readfast(self, node):
232 def readfast(self, node):
235 '''use the faster of readdelta or read'''
233 '''use the faster of readdelta or read'''
236 r = self.rev(node)
234 r = self.rev(node)
237 deltaparent = self.deltaparent(r)
235 deltaparent = self.deltaparent(r)
238 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
236 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
239 return self.readdelta(node)
237 return self.readdelta(node)
240 return self.read(node)
238 return self.read(node)
241
239
242 def read(self, node):
240 def read(self, node):
243 if node == revlog.nullid:
241 if node == revlog.nullid:
244 return manifestdict() # don't upset local cache
242 return manifestdict() # don't upset local cache
245 if node in self._mancache:
243 if node in self._mancache:
246 return self._mancache[node][0]
244 return self._mancache[node][0]
247 text = self.revision(node)
245 text = self.revision(node)
248 arraytext = array.array('c', text)
246 arraytext = array.array('c', text)
249 mapping = _parse(text)
247 mapping = _parse(text)
250 self._mancache[node] = (mapping, arraytext)
248 self._mancache[node] = (mapping, arraytext)
251 return mapping
249 return mapping
252
250
253 def find(self, node, f):
251 def find(self, node, f):
254 '''look up entry for a single file efficiently.
252 '''look up entry for a single file efficiently.
255 return (node, flags) pair if found, (None, None) if not.'''
253 return (node, flags) pair if found, (None, None) if not.'''
256 if node in self._mancache:
254 if node in self._mancache:
257 mapping = self._mancache[node][0]
255 mapping = self._mancache[node][0]
258 return mapping.get(f), mapping.flags(f)
256 return mapping.get(f), mapping.flags(f)
259 text = self.revision(node)
257 text = self.revision(node)
260 start, end = _msearch(text, f)
258 start, end = _msearch(text, f)
261 if start == end:
259 if start == end:
262 return None, None
260 return None, None
263 l = text[start:end]
261 l = text[start:end]
264 f, n = l.split('\0')
262 f, n = l.split('\0')
265 return revlog.bin(n[:40]), n[40:-1]
263 return revlog.bin(n[:40]), n[40:-1]
266
264
267 def add(self, map, transaction, link, p1, p2, added, removed):
265 def add(self, map, transaction, link, p1, p2, added, removed):
268 if p1 in self._mancache:
266 if p1 in self._mancache:
269 # If our first parent is in the manifest cache, we can
267 # If our first parent is in the manifest cache, we can
270 # compute a delta here using properties we know about the
268 # compute a delta here using properties we know about the
271 # manifest up-front, which may save time later for the
269 # manifest up-front, which may save time later for the
272 # revlog layer.
270 # revlog layer.
273
271
274 _checkforbidden(added)
272 _checkforbidden(added)
275 # combine the changed lists into one list for sorting
273 # combine the changed lists into one list for sorting
276 work = [(x, False) for x in added]
274 work = [(x, False) for x in added]
277 work.extend((x, True) for x in removed)
275 work.extend((x, True) for x in removed)
278 # this could use heapq.merge() (from Python 2.6+) or equivalent
276 # this could use heapq.merge() (from Python 2.6+) or equivalent
279 # since the lists are already sorted
277 # since the lists are already sorted
280 work.sort()
278 work.sort()
281
279
282 arraytext, deltatext = map.fastdelta(self._mancache[p1][1], work)
280 arraytext, deltatext = map.fastdelta(self._mancache[p1][1], work)
283 cachedelta = self.rev(p1), deltatext
281 cachedelta = self.rev(p1), deltatext
284 text = util.buffer(arraytext)
282 text = util.buffer(arraytext)
285 else:
283 else:
286 # The first parent manifest isn't already loaded, so we'll
284 # The first parent manifest isn't already loaded, so we'll
287 # just encode a fulltext of the manifest and pass that
285 # just encode a fulltext of the manifest and pass that
288 # through to the revlog layer, and let it handle the delta
286 # through to the revlog layer, and let it handle the delta
289 # process.
287 # process.
290 text = map.text()
288 text = map.text()
291 arraytext = array.array('c', text)
289 arraytext = array.array('c', text)
292 cachedelta = None
290 cachedelta = None
293
291
294 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
292 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
295 self._mancache[n] = (map, arraytext)
293 self._mancache[n] = (map, arraytext)
296
294
297 return n
295 return n
General Comments 0
You need to be logged in to leave comments. Login now