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