##// END OF EJS Templates
manifest: move addlistdelta to module-level...
Augie Fackler -
r22409:8f09b785 default
parent child Browse files
Show More
@@ -1,233 +1,234
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, dicthelpers
9 import mdiff, parsers, error, revlog, util, dicthelpers
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 flags(self, f):
20 def flags(self, f):
21 return self._flags.get(f, "")
21 return self._flags.get(f, "")
22 def withflags(self):
22 def withflags(self):
23 return set(self._flags.keys())
23 return set(self._flags.keys())
24 def set(self, f, flags):
24 def set(self, f, flags):
25 self._flags[f] = flags
25 self._flags[f] = flags
26 def copy(self):
26 def copy(self):
27 return manifestdict(self, dict.copy(self._flags))
27 return manifestdict(self, dict.copy(self._flags))
28 def intersectfiles(self, files):
28 def intersectfiles(self, files):
29 '''make a new manifestdict with the intersection of self with files
29 '''make a new manifestdict with the intersection of self with files
30
30
31 The algorithm assumes that files is much smaller than self.'''
31 The algorithm assumes that files is much smaller than self.'''
32 ret = manifestdict()
32 ret = manifestdict()
33 for fn in files:
33 for fn in files:
34 if fn in self:
34 if fn in self:
35 ret[fn] = self[fn]
35 ret[fn] = self[fn]
36 flags = self._flags.get(fn, None)
36 flags = self._flags.get(fn, None)
37 if flags:
37 if flags:
38 ret._flags[fn] = flags
38 ret._flags[fn] = flags
39 return ret
39 return ret
40 def flagsdiff(self, d2):
40 def flagsdiff(self, d2):
41 return dicthelpers.diff(self._flags, d2._flags, "")
41 return dicthelpers.diff(self._flags, d2._flags, "")
42
42
43
43
44 def checkforbidden(l):
44 def checkforbidden(l):
45 """Check filenames for illegal characters."""
45 """Check filenames for illegal characters."""
46 for f in l:
46 for f in l:
47 if '\n' in f or '\r' in f:
47 if '\n' in f or '\r' in f:
48 raise error.RevlogError(
48 raise error.RevlogError(
49 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
49 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
50
50
51
51
52 # apply the changes collected during the bisect loop to our addlist
53 # return a delta suitable for addrevision
54 def addlistdelta(addlist, x):
55 # for large addlist arrays, building a new array is cheaper
56 # than repeatedly modifying the existing one
57 currentposition = 0
58 newaddlist = array.array('c')
59
60 for start, end, content in x:
61 newaddlist += addlist[currentposition:start]
62 if content:
63 newaddlist += array.array('c', content)
64
65 currentposition = end
66
67 newaddlist += addlist[currentposition:]
68
69 deltatext = "".join(struct.pack(">lll", start, end, len(content))
70 + content for start, end, content in x)
71 return deltatext, newaddlist
72
73
52 class manifest(revlog.revlog):
74 class manifest(revlog.revlog):
53 def __init__(self, opener):
75 def __init__(self, opener):
54 # we expect to deal with not more than four revs at a time,
76 # we expect to deal with not more than four revs at a time,
55 # during a commit --amend
77 # during a commit --amend
56 self._mancache = util.lrucachedict(4)
78 self._mancache = util.lrucachedict(4)
57 revlog.revlog.__init__(self, opener, "00manifest.i")
79 revlog.revlog.__init__(self, opener, "00manifest.i")
58
80
59 def parse(self, lines):
81 def parse(self, lines):
60 mfdict = manifestdict()
82 mfdict = manifestdict()
61 parsers.parse_manifest(mfdict, mfdict._flags, lines)
83 parsers.parse_manifest(mfdict, mfdict._flags, lines)
62 return mfdict
84 return mfdict
63
85
64 def readdelta(self, node):
86 def readdelta(self, node):
65 r = self.rev(node)
87 r = self.rev(node)
66 return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
88 return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
67
89
68 def readfast(self, node):
90 def readfast(self, node):
69 '''use the faster of readdelta or read'''
91 '''use the faster of readdelta or read'''
70 r = self.rev(node)
92 r = self.rev(node)
71 deltaparent = self.deltaparent(r)
93 deltaparent = self.deltaparent(r)
72 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
94 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
73 return self.readdelta(node)
95 return self.readdelta(node)
74 return self.read(node)
96 return self.read(node)
75
97
76 def read(self, node):
98 def read(self, node):
77 if node == revlog.nullid:
99 if node == revlog.nullid:
78 return manifestdict() # don't upset local cache
100 return manifestdict() # don't upset local cache
79 if node in self._mancache:
101 if node in self._mancache:
80 return self._mancache[node][0]
102 return self._mancache[node][0]
81 text = self.revision(node)
103 text = self.revision(node)
82 arraytext = array.array('c', text)
104 arraytext = array.array('c', text)
83 mapping = self.parse(text)
105 mapping = self.parse(text)
84 self._mancache[node] = (mapping, arraytext)
106 self._mancache[node] = (mapping, arraytext)
85 return mapping
107 return mapping
86
108
87 def _search(self, m, s, lo=0, hi=None):
109 def _search(self, m, s, lo=0, hi=None):
88 '''return a tuple (start, end) that says where to find s within m.
110 '''return a tuple (start, end) that says where to find s within m.
89
111
90 If the string is found m[start:end] are the line containing
112 If the string is found m[start:end] are the line containing
91 that string. If start == end the string was not found and
113 that string. If start == end the string was not found and
92 they indicate the proper sorted insertion point.
114 they indicate the proper sorted insertion point.
93
115
94 m should be a buffer or a string
116 m should be a buffer or a string
95 s is a string'''
117 s is a string'''
96 def advance(i, c):
118 def advance(i, c):
97 while i < lenm and m[i] != c:
119 while i < lenm and m[i] != c:
98 i += 1
120 i += 1
99 return i
121 return i
100 if not s:
122 if not s:
101 return (lo, lo)
123 return (lo, lo)
102 lenm = len(m)
124 lenm = len(m)
103 if not hi:
125 if not hi:
104 hi = lenm
126 hi = lenm
105 while lo < hi:
127 while lo < hi:
106 mid = (lo + hi) // 2
128 mid = (lo + hi) // 2
107 start = mid
129 start = mid
108 while start > 0 and m[start - 1] != '\n':
130 while start > 0 and m[start - 1] != '\n':
109 start -= 1
131 start -= 1
110 end = advance(start, '\0')
132 end = advance(start, '\0')
111 if m[start:end] < s:
133 if m[start:end] < s:
112 # we know that after the null there are 40 bytes of sha1
134 # we know that after the null there are 40 bytes of sha1
113 # this translates to the bisect lo = mid + 1
135 # this translates to the bisect lo = mid + 1
114 lo = advance(end + 40, '\n') + 1
136 lo = advance(end + 40, '\n') + 1
115 else:
137 else:
116 # this translates to the bisect hi = mid
138 # this translates to the bisect hi = mid
117 hi = start
139 hi = start
118 end = advance(lo, '\0')
140 end = advance(lo, '\0')
119 found = m[lo:end]
141 found = m[lo:end]
120 if s == found:
142 if s == found:
121 # we know that after the null there are 40 bytes of sha1
143 # we know that after the null there are 40 bytes of sha1
122 end = advance(end + 40, '\n')
144 end = advance(end + 40, '\n')
123 return (lo, end + 1)
145 return (lo, end + 1)
124 else:
146 else:
125 return (lo, lo)
147 return (lo, lo)
126
148
127 def find(self, node, f):
149 def find(self, node, f):
128 '''look up entry for a single file efficiently.
150 '''look up entry for a single file efficiently.
129 return (node, flags) pair if found, (None, None) if not.'''
151 return (node, flags) pair if found, (None, None) if not.'''
130 if node in self._mancache:
152 if node in self._mancache:
131 mapping = self._mancache[node][0]
153 mapping = self._mancache[node][0]
132 return mapping.get(f), mapping.flags(f)
154 return mapping.get(f), mapping.flags(f)
133 text = self.revision(node)
155 text = self.revision(node)
134 start, end = self._search(text, f)
156 start, end = self._search(text, f)
135 if start == end:
157 if start == end:
136 return None, None
158 return None, None
137 l = text[start:end]
159 l = text[start:end]
138 f, n = l.split('\0')
160 f, n = l.split('\0')
139 return revlog.bin(n[:40]), n[40:-1]
161 return revlog.bin(n[:40]), n[40:-1]
140
162
141 def add(self, map, transaction, link, p1=None, p2=None,
163 def add(self, map, transaction, link, p1=None, p2=None,
142 changed=None):
164 changed=None):
143 # apply the changes collected during the bisect loop to our addlist
144 # return a delta suitable for addrevision
145 def addlistdelta(addlist, x):
146 # for large addlist arrays, building a new array is cheaper
147 # than repeatedly modifying the existing one
148 currentposition = 0
149 newaddlist = array.array('c')
150
151 for start, end, content in x:
152 newaddlist += addlist[currentposition:start]
153 if content:
154 newaddlist += array.array('c', content)
155
156 currentposition = end
157
158 newaddlist += addlist[currentposition:]
159
160 deltatext = "".join(struct.pack(">lll", start, end, len(content))
161 + content for start, end, content in x)
162 return deltatext, newaddlist
163
164 # if we're using the cache, make sure it is valid and
165 # if we're using the cache, make sure it is valid and
165 # parented by the same node we're diffing against
166 # parented by the same node we're diffing against
166 if not (changed and p1 and (p1 in self._mancache)):
167 if not (changed and p1 and (p1 in self._mancache)):
167 files = sorted(map)
168 files = sorted(map)
168 checkforbidden(files)
169 checkforbidden(files)
169
170
170 # if this is changed to support newlines in filenames,
171 # if this is changed to support newlines in filenames,
171 # be sure to check the templates/ dir again (especially *-raw.tmpl)
172 # be sure to check the templates/ dir again (especially *-raw.tmpl)
172 hex, flags = revlog.hex, map.flags
173 hex, flags = revlog.hex, map.flags
173 text = ''.join("%s\0%s%s\n" % (f, hex(map[f]), flags(f))
174 text = ''.join("%s\0%s%s\n" % (f, hex(map[f]), flags(f))
174 for f in files)
175 for f in files)
175 arraytext = array.array('c', text)
176 arraytext = array.array('c', text)
176 cachedelta = None
177 cachedelta = None
177 else:
178 else:
178 added, removed = changed
179 added, removed = changed
179 addlist = self._mancache[p1][1]
180 addlist = self._mancache[p1][1]
180
181
181 checkforbidden(added)
182 checkforbidden(added)
182 # combine the changed lists into one list for sorting
183 # combine the changed lists into one list for sorting
183 work = [(x, False) for x in added]
184 work = [(x, False) for x in added]
184 work.extend((x, True) for x in removed)
185 work.extend((x, True) for x in removed)
185 # this could use heapq.merge() (from Python 2.6+) or equivalent
186 # this could use heapq.merge() (from Python 2.6+) or equivalent
186 # since the lists are already sorted
187 # since the lists are already sorted
187 work.sort()
188 work.sort()
188
189
189 delta = []
190 delta = []
190 dstart = None
191 dstart = None
191 dend = None
192 dend = None
192 dline = [""]
193 dline = [""]
193 start = 0
194 start = 0
194 # zero copy representation of addlist as a buffer
195 # zero copy representation of addlist as a buffer
195 addbuf = util.buffer(addlist)
196 addbuf = util.buffer(addlist)
196
197
197 # start with a readonly loop that finds the offset of
198 # start with a readonly loop that finds the offset of
198 # each line and creates the deltas
199 # each line and creates the deltas
199 for f, todelete in work:
200 for f, todelete in work:
200 # bs will either be the index of the item or the insert point
201 # bs will either be the index of the item or the insert point
201 start, end = self._search(addbuf, f, start)
202 start, end = self._search(addbuf, f, start)
202 if not todelete:
203 if not todelete:
203 l = "%s\0%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
204 l = "%s\0%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
204 else:
205 else:
205 if start == end:
206 if start == end:
206 # item we want to delete was not found, error out
207 # item we want to delete was not found, error out
207 raise AssertionError(
208 raise AssertionError(
208 _("failed to remove %s from manifest") % f)
209 _("failed to remove %s from manifest") % f)
209 l = ""
210 l = ""
210 if dstart is not None and dstart <= start and dend >= start:
211 if dstart is not None and dstart <= start and dend >= start:
211 if dend < end:
212 if dend < end:
212 dend = end
213 dend = end
213 if l:
214 if l:
214 dline.append(l)
215 dline.append(l)
215 else:
216 else:
216 if dstart is not None:
217 if dstart is not None:
217 delta.append([dstart, dend, "".join(dline)])
218 delta.append([dstart, dend, "".join(dline)])
218 dstart = start
219 dstart = start
219 dend = end
220 dend = end
220 dline = [l]
221 dline = [l]
221
222
222 if dstart is not None:
223 if dstart is not None:
223 delta.append([dstart, dend, "".join(dline)])
224 delta.append([dstart, dend, "".join(dline)])
224 # apply the delta to the addlist, and get a delta for addrevision
225 # apply the delta to the addlist, and get a delta for addrevision
225 deltatext, addlist = addlistdelta(addlist, delta)
226 deltatext, addlist = addlistdelta(addlist, delta)
226 cachedelta = (self.rev(p1), deltatext)
227 cachedelta = (self.rev(p1), deltatext)
227 arraytext = addlist
228 arraytext = addlist
228 text = util.buffer(arraytext)
229 text = util.buffer(arraytext)
229
230
230 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
231 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
231 self._mancache[n] = (map, arraytext)
232 self._mancache[n] = (map, arraytext)
232
233
233 return n
234 return n
General Comments 0
You need to be logged in to leave comments. Login now