##// END OF EJS Templates
manifest: improve error message about newlines in filenames...
Greg Ward -
r8077:d051342f default
parent child Browse files
Show More
@@ -1,200 +1,200 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
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 i18n import _
8 from i18n import _
9 import array, struct, mdiff, parsers, util, error, revlog
9 import array, struct, mdiff, parsers, util, error, revlog
10
10
11 class manifestdict(dict):
11 class manifestdict(dict):
12 def __init__(self, mapping=None, flags=None):
12 def __init__(self, mapping=None, flags=None):
13 if mapping is None: mapping = {}
13 if mapping is None: mapping = {}
14 if flags is None: flags = {}
14 if flags is None: flags = {}
15 dict.__init__(self, mapping)
15 dict.__init__(self, mapping)
16 self._flags = flags
16 self._flags = flags
17 def flags(self, f):
17 def flags(self, f):
18 return self._flags.get(f, "")
18 return self._flags.get(f, "")
19 def set(self, f, flags):
19 def set(self, f, flags):
20 self._flags[f] = flags
20 self._flags[f] = flags
21 def copy(self):
21 def copy(self):
22 return manifestdict(dict.copy(self), dict.copy(self._flags))
22 return manifestdict(dict.copy(self), dict.copy(self._flags))
23
23
24 class manifest(revlog.revlog):
24 class manifest(revlog.revlog):
25 def __init__(self, opener):
25 def __init__(self, opener):
26 self.mapcache = None
26 self.mapcache = None
27 self.listcache = None
27 self.listcache = 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.mapcache and self.mapcache[0] == node:
42 if self.mapcache and self.mapcache[0] == node:
43 return self.mapcache[1]
43 return self.mapcache[1]
44 text = self.revision(node)
44 text = self.revision(node)
45 self.listcache = array.array('c', text)
45 self.listcache = array.array('c', text)
46 mapping = self.parse(text)
46 mapping = self.parse(text)
47 self.mapcache = (node, mapping)
47 self.mapcache = (node, mapping)
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.mapcache and node == self.mapcache[0]:
95 if self.mapcache and node == self.mapcache[0]:
96 return self.mapcache[1].get(f), self.mapcache[1].flags(f)
96 return self.mapcache[1].get(f), self.mapcache[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 i = len(x)
112 i = len(x)
113 while i > 0:
113 while i > 0:
114 i -= 1
114 i -= 1
115 start = x[i][0]
115 start = x[i][0]
116 end = x[i][1]
116 end = x[i][1]
117 if x[i][2]:
117 if x[i][2]:
118 addlist[start:end] = array.array('c', x[i][2])
118 addlist[start:end] = array.array('c', x[i][2])
119 else:
119 else:
120 del addlist[start:end]
120 del addlist[start:end]
121 return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2]
121 return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2]
122 for d in x ])
122 for d in x ])
123
123
124 def checkforbidden(l):
124 def checkforbidden(l):
125 for f in l:
125 for f in l:
126 if '\n' in f or '\r' in f:
126 if '\n' in f or '\r' in f:
127 raise error.RevlogError(
127 raise error.RevlogError(
128 _("'\\n' and '\\r' disallowed in filenames"))
128 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
129
129
130 # if we're using the listcache, make sure it is valid and
130 # if we're using the listcache, make sure it is valid and
131 # parented by the same node we're diffing against
131 # parented by the same node we're diffing against
132 if not (changed and self.listcache and p1 and self.mapcache[0] == p1):
132 if not (changed and self.listcache and p1 and self.mapcache[0] == p1):
133 files = util.sort(map)
133 files = util.sort(map)
134 checkforbidden(files)
134 checkforbidden(files)
135
135
136 # if this is changed to support newlines in filenames,
136 # if this is changed to support newlines in filenames,
137 # be sure to check the templates/ dir again (especially *-raw.tmpl)
137 # be sure to check the templates/ dir again (especially *-raw.tmpl)
138 hex, flags = revlog.hex, map.flags
138 hex, flags = revlog.hex, map.flags
139 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags(f))
139 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags(f))
140 for f in files]
140 for f in files]
141 self.listcache = array.array('c', "".join(text))
141 self.listcache = array.array('c', "".join(text))
142 cachedelta = None
142 cachedelta = None
143 else:
143 else:
144 addlist = self.listcache
144 addlist = self.listcache
145
145
146 checkforbidden(changed[0])
146 checkforbidden(changed[0])
147 # combine the changed lists into one list for sorting
147 # combine the changed lists into one list for sorting
148 work = [[x, 0] for x in changed[0]]
148 work = [[x, 0] for x in changed[0]]
149 work[len(work):] = [[x, 1] for x in changed[1]]
149 work[len(work):] = [[x, 1] for x in changed[1]]
150 work.sort()
150 work.sort()
151
151
152 delta = []
152 delta = []
153 dstart = None
153 dstart = None
154 dend = None
154 dend = None
155 dline = [""]
155 dline = [""]
156 start = 0
156 start = 0
157 # zero copy representation of addlist as a buffer
157 # zero copy representation of addlist as a buffer
158 addbuf = buffer(addlist)
158 addbuf = buffer(addlist)
159
159
160 # start with a readonly loop that finds the offset of
160 # start with a readonly loop that finds the offset of
161 # each line and creates the deltas
161 # each line and creates the deltas
162 for w in work:
162 for w in work:
163 f = w[0]
163 f = w[0]
164 # bs will either be the index of the item or the insert point
164 # bs will either be the index of the item or the insert point
165 start, end = self._search(addbuf, f, start)
165 start, end = self._search(addbuf, f, start)
166 if w[1] == 0:
166 if w[1] == 0:
167 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
167 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
168 else:
168 else:
169 l = ""
169 l = ""
170 if start == end and w[1] == 1:
170 if start == end and w[1] == 1:
171 # item we want to delete was not found, error out
171 # item we want to delete was not found, error out
172 raise AssertionError(
172 raise AssertionError(
173 _("failed to remove %s from manifest") % f)
173 _("failed to remove %s from manifest") % f)
174 if dstart != None and dstart <= start and dend >= start:
174 if dstart != None and dstart <= start and dend >= start:
175 if dend < end:
175 if dend < end:
176 dend = end
176 dend = end
177 if l:
177 if l:
178 dline.append(l)
178 dline.append(l)
179 else:
179 else:
180 if dstart != None:
180 if dstart != None:
181 delta.append([dstart, dend, "".join(dline)])
181 delta.append([dstart, dend, "".join(dline)])
182 dstart = start
182 dstart = start
183 dend = end
183 dend = end
184 dline = [l]
184 dline = [l]
185
185
186 if dstart != None:
186 if dstart != None:
187 delta.append([dstart, dend, "".join(dline)])
187 delta.append([dstart, dend, "".join(dline)])
188 # apply the delta to the addlist, and get a delta for addrevision
188 # apply the delta to the addlist, and get a delta for addrevision
189 cachedelta = addlistdelta(addlist, delta)
189 cachedelta = addlistdelta(addlist, delta)
190
190
191 # the delta is only valid if we've been processing the tip revision
191 # the delta is only valid if we've been processing the tip revision
192 if self.mapcache[0] != self.tip():
192 if self.mapcache[0] != self.tip():
193 cachedelta = None
193 cachedelta = None
194 self.listcache = addlist
194 self.listcache = addlist
195
195
196 n = self.addrevision(buffer(self.listcache), transaction, link,
196 n = self.addrevision(buffer(self.listcache), transaction, link,
197 p1, p2, cachedelta)
197 p1, p2, cachedelta)
198 self.mapcache = (n, map)
198 self.mapcache = (n, map)
199
199
200 return n
200 return n
General Comments 0
You need to be logged in to leave comments. Login now