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