##// END OF EJS Templates
manifest.add(): cleanup worklist construction and iteration
Benoit Boissinot -
r9415:e0cc9fa2 default
parent child Browse files
Show More
@@ -1,196 +1,198 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import mdiff, parsers, error, revlog
9 import mdiff, parsers, error, revlog
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: mapping = {}
14 if mapping is None: mapping = {}
15 if flags is None: flags = {}
15 if flags is None: flags = {}
16 dict.__init__(self, mapping)
16 dict.__init__(self, mapping)
17 self._flags = flags
17 self._flags = flags
18 def flags(self, f):
18 def flags(self, f):
19 return self._flags.get(f, "")
19 return self._flags.get(f, "")
20 def set(self, f, flags):
20 def set(self, f, flags):
21 self._flags[f] = flags
21 self._flags[f] = flags
22 def copy(self):
22 def copy(self):
23 return manifestdict(dict.copy(self), dict.copy(self._flags))
23 return manifestdict(dict.copy(self), dict.copy(self._flags))
24
24
25 class manifest(revlog.revlog):
25 class manifest(revlog.revlog):
26 def __init__(self, opener):
26 def __init__(self, opener):
27 self._mancache = None
27 self._mancache = None
28 revlog.revlog.__init__(self, opener, "00manifest.i")
28 revlog.revlog.__init__(self, opener, "00manifest.i")
29
29
30 def parse(self, lines):
30 def parse(self, lines):
31 mfdict = manifestdict()
31 mfdict = manifestdict()
32 parsers.parse_manifest(mfdict, mfdict._flags, lines)
32 parsers.parse_manifest(mfdict, mfdict._flags, lines)
33 return mfdict
33 return mfdict
34
34
35 def readdelta(self, node):
35 def readdelta(self, node):
36 r = self.rev(node)
36 r = self.rev(node)
37 return self.parse(mdiff.patchtext(self.revdiff(r - 1, r)))
37 return self.parse(mdiff.patchtext(self.revdiff(r - 1, r)))
38
38
39 def read(self, node):
39 def read(self, node):
40 if node == revlog.nullid:
40 if node == revlog.nullid:
41 return manifestdict() # don't upset local cache
41 return manifestdict() # don't upset local cache
42 if self._mancache and self._mancache[0] == node:
42 if self._mancache and self._mancache[0] == node:
43 return self._mancache[1]
43 return self._mancache[1]
44 text = self.revision(node)
44 text = self.revision(node)
45 arraytext = array.array('c', text)
45 arraytext = array.array('c', text)
46 mapping = self.parse(text)
46 mapping = self.parse(text)
47 self._mancache = (node, mapping, arraytext)
47 self._mancache = (node, mapping, arraytext)
48 return mapping
48 return mapping
49
49
50 def _search(self, m, s, lo=0, hi=None):
50 def _search(self, m, s, lo=0, hi=None):
51 '''return a tuple (start, end) that says where to find s within m.
51 '''return a tuple (start, end) that says where to find s within m.
52
52
53 If the string is found m[start:end] are the line containing
53 If the string is found m[start:end] are the line containing
54 that string. If start == end the string was not found and
54 that string. If start == end the string was not found and
55 they indicate the proper sorted insertion point. This was
55 they indicate the proper sorted insertion point. This was
56 taken from bisect_left, and modified to find line start/end as
56 taken from bisect_left, and modified to find line start/end as
57 it goes along.
57 it goes along.
58
58
59 m should be a buffer or a string
59 m should be a buffer or a string
60 s is a string'''
60 s is a string'''
61 def advance(i, c):
61 def advance(i, c):
62 while i < lenm and m[i] != c:
62 while i < lenm and m[i] != c:
63 i += 1
63 i += 1
64 return i
64 return i
65 if not s:
65 if not s:
66 return (lo, lo)
66 return (lo, lo)
67 lenm = len(m)
67 lenm = len(m)
68 if not hi:
68 if not hi:
69 hi = lenm
69 hi = lenm
70 while lo < hi:
70 while lo < hi:
71 mid = (lo + hi) // 2
71 mid = (lo + hi) // 2
72 start = mid
72 start = mid
73 while start > 0 and m[start-1] != '\n':
73 while start > 0 and m[start-1] != '\n':
74 start -= 1
74 start -= 1
75 end = advance(start, '\0')
75 end = advance(start, '\0')
76 if m[start:end] < s:
76 if m[start:end] < s:
77 # we know that after the null there are 40 bytes of sha1
77 # we know that after the null there are 40 bytes of sha1
78 # this translates to the bisect lo = mid + 1
78 # this translates to the bisect lo = mid + 1
79 lo = advance(end + 40, '\n') + 1
79 lo = advance(end + 40, '\n') + 1
80 else:
80 else:
81 # this translates to the bisect hi = mid
81 # this translates to the bisect hi = mid
82 hi = start
82 hi = start
83 end = advance(lo, '\0')
83 end = advance(lo, '\0')
84 found = m[lo:end]
84 found = m[lo:end]
85 if cmp(s, found) == 0:
85 if cmp(s, found) == 0:
86 # we know that after the null there are 40 bytes of sha1
86 # we know that after the null there are 40 bytes of sha1
87 end = advance(end + 40, '\n')
87 end = advance(end + 40, '\n')
88 return (lo, end+1)
88 return (lo, end+1)
89 else:
89 else:
90 return (lo, lo)
90 return (lo, lo)
91
91
92 def find(self, node, f):
92 def find(self, node, f):
93 '''look up entry for a single file efficiently.
93 '''look up entry for a single file efficiently.
94 return (node, flags) pair if found, (None, None) if not.'''
94 return (node, flags) pair if found, (None, None) if not.'''
95 if self._mancache and self._mancache[0] == node:
95 if self._mancache and self._mancache[0] == node:
96 return self._mancache[1].get(f), self._mancache[1].flags(f)
96 return self._mancache[1].get(f), self._mancache[1].flags(f)
97 text = self.revision(node)
97 text = self.revision(node)
98 start, end = self._search(text, f)
98 start, end = self._search(text, f)
99 if start == end:
99 if start == end:
100 return None, None
100 return None, None
101 l = text[start:end]
101 l = text[start:end]
102 f, n = l.split('\0')
102 f, n = l.split('\0')
103 return revlog.bin(n[:40]), n[40:-1]
103 return revlog.bin(n[:40]), n[40:-1]
104
104
105 def add(self, map, transaction, link, p1=None, p2=None,
105 def add(self, map, transaction, link, p1=None, p2=None,
106 changed=None):
106 changed=None):
107 # apply the changes collected during the bisect loop to our addlist
107 # apply the changes collected during the bisect loop to our addlist
108 # return a delta suitable for addrevision
108 # return a delta suitable for addrevision
109 def addlistdelta(addlist, x):
109 def addlistdelta(addlist, x):
110 # start from the bottom up
110 # start from the bottom up
111 # so changes to the offsets don't mess things up.
111 # so changes to the offsets don't mess things up.
112 for start, end, content in reversed(x):
112 for start, end, content in reversed(x):
113 if content:
113 if content:
114 addlist[start:end] = array.array('c', content)
114 addlist[start:end] = array.array('c', content)
115 else:
115 else:
116 del addlist[start:end]
116 del addlist[start:end]
117 return "".join(struct.pack(">lll", start, end, len(content)) + content
117 return "".join(struct.pack(">lll", start, end, len(content)) + content
118 for start, end, content in x)
118 for start, end, content in x)
119
119
120 def checkforbidden(l):
120 def checkforbidden(l):
121 for f in l:
121 for f in l:
122 if '\n' in f or '\r' in f:
122 if '\n' in f or '\r' in f:
123 raise error.RevlogError(
123 raise error.RevlogError(
124 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
124 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
125
125
126 # if we're using the cache, make sure it is valid and
126 # if we're using the cache, make sure it is valid and
127 # parented by the same node we're diffing against
127 # parented by the same node we're diffing against
128 if not (changed and self._mancache and p1 and self._mancache[0] == p1):
128 if not (changed and self._mancache and p1 and self._mancache[0] == p1):
129 files = sorted(map)
129 files = sorted(map)
130 checkforbidden(files)
130 checkforbidden(files)
131
131
132 # if this is changed to support newlines in filenames,
132 # if this is changed to support newlines in filenames,
133 # be sure to check the templates/ dir again (especially *-raw.tmpl)
133 # be sure to check the templates/ dir again (especially *-raw.tmpl)
134 hex, flags = revlog.hex, map.flags
134 hex, flags = revlog.hex, map.flags
135 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags(f))
135 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags(f))
136 for f in files]
136 for f in files]
137 arraytext = array.array('c', "".join(text))
137 arraytext = array.array('c', "".join(text))
138 cachedelta = None
138 cachedelta = None
139 else:
139 else:
140 added, removed = changed
140 addlist = self._mancache[2]
141 addlist = self._mancache[2]
141
142
142 checkforbidden(changed[0])
143 checkforbidden(added)
143 # combine the changed lists into one list for sorting
144 # combine the changed lists into one list for sorting
144 work = [[x, 0] for x in changed[0]]
145 work = [(x, False) for x in added]
145 work[len(work):] = [[x, 1] for x in changed[1]]
146 work.extend((x, True) for x in removed)
147 # this could use heapq.merge() (from python2.6+) or equivalent
148 # since the lists are already sorted
146 work.sort()
149 work.sort()
147
150
148 delta = []
151 delta = []
149 dstart = None
152 dstart = None
150 dend = None
153 dend = None
151 dline = [""]
154 dline = [""]
152 start = 0
155 start = 0
153 # zero copy representation of addlist as a buffer
156 # zero copy representation of addlist as a buffer
154 addbuf = buffer(addlist)
157 addbuf = buffer(addlist)
155
158
156 # start with a readonly loop that finds the offset of
159 # start with a readonly loop that finds the offset of
157 # each line and creates the deltas
160 # each line and creates the deltas
158 for w in work:
161 for f, todelete in work:
159 f = w[0]
160 # bs will either be the index of the item or the insert point
162 # bs will either be the index of the item or the insert point
161 start, end = self._search(addbuf, f, start)
163 start, end = self._search(addbuf, f, start)
162 if w[1] == 0:
164 if not todelete:
163 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
165 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
164 else:
166 else:
165 l = ""
167 if start == end:
166 if start == end and w[1] == 1:
167 # item we want to delete was not found, error out
168 # item we want to delete was not found, error out
168 raise AssertionError(
169 raise AssertionError(
169 _("failed to remove %s from manifest") % f)
170 _("failed to remove %s from manifest") % f)
171 l = ""
170 if dstart != None and dstart <= start and dend >= start:
172 if dstart != None and dstart <= start and dend >= start:
171 if dend < end:
173 if dend < end:
172 dend = end
174 dend = end
173 if l:
175 if l:
174 dline.append(l)
176 dline.append(l)
175 else:
177 else:
176 if dstart != None:
178 if dstart != None:
177 delta.append([dstart, dend, "".join(dline)])
179 delta.append([dstart, dend, "".join(dline)])
178 dstart = start
180 dstart = start
179 dend = end
181 dend = end
180 dline = [l]
182 dline = [l]
181
183
182 if dstart != None:
184 if dstart != None:
183 delta.append([dstart, dend, "".join(dline)])
185 delta.append([dstart, dend, "".join(dline)])
184 # apply the delta to the addlist, and get a delta for addrevision
186 # apply the delta to the addlist, and get a delta for addrevision
185 cachedelta = addlistdelta(addlist, delta)
187 cachedelta = addlistdelta(addlist, delta)
186
188
187 # the delta is only valid if we've been processing the tip revision
189 # the delta is only valid if we've been processing the tip revision
188 if p1 != self.tip():
190 if p1 != self.tip():
189 cachedelta = None
191 cachedelta = None
190 arraytext = addlist
192 arraytext = addlist
191
193
192 n = self.addrevision(buffer(arraytext), transaction, link,
194 n = self.addrevision(buffer(arraytext), transaction, link,
193 p1, p2, cachedelta)
195 p1, p2, cachedelta)
194 self._mancache = (n, map, arraytext)
196 self._mancache = (n, map, arraytext)
195
197
196 return n
198 return n
General Comments 0
You need to be logged in to leave comments. Login now