##// END OF EJS Templates
repoview: add a FilteredLookupError class...
Pierre-Yves David -
r23015:21c44c1a default
parent child Browse files
Show More
@@ -1,357 +1,358
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog 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 node import bin, hex, nullid
8 from node import bin, hex, nullid
9 from i18n import _
9 from i18n import _
10 import util, error, revlog, encoding
10 import util, error, revlog, encoding
11
11
12 _defaultextra = {'branch': 'default'}
12 _defaultextra = {'branch': 'default'}
13
13
14 def _string_escape(text):
14 def _string_escape(text):
15 """
15 """
16 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
16 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
17 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
17 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
18 >>> s
18 >>> s
19 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
19 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
20 >>> res = _string_escape(s)
20 >>> res = _string_escape(s)
21 >>> s == res.decode('string_escape')
21 >>> s == res.decode('string_escape')
22 True
22 True
23 """
23 """
24 # subset of the string_escape codec
24 # subset of the string_escape codec
25 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
25 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
26 return text.replace('\0', '\\0')
26 return text.replace('\0', '\\0')
27
27
28 def decodeextra(text):
28 def decodeextra(text):
29 """
29 """
30 >>> sorted(decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'})
30 >>> sorted(decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'})
31 ... ).iteritems())
31 ... ).iteritems())
32 [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')]
32 [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')]
33 >>> sorted(decodeextra(encodeextra({'foo': 'bar',
33 >>> sorted(decodeextra(encodeextra({'foo': 'bar',
34 ... 'baz': chr(92) + chr(0) + '2'})
34 ... 'baz': chr(92) + chr(0) + '2'})
35 ... ).iteritems())
35 ... ).iteritems())
36 [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')]
36 [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')]
37 """
37 """
38 extra = _defaultextra.copy()
38 extra = _defaultextra.copy()
39 for l in text.split('\0'):
39 for l in text.split('\0'):
40 if l:
40 if l:
41 if '\\0' in l:
41 if '\\0' in l:
42 # fix up \0 without getting into trouble with \\0
42 # fix up \0 without getting into trouble with \\0
43 l = l.replace('\\\\', '\\\\\n')
43 l = l.replace('\\\\', '\\\\\n')
44 l = l.replace('\\0', '\0')
44 l = l.replace('\\0', '\0')
45 l = l.replace('\n', '')
45 l = l.replace('\n', '')
46 k, v = l.decode('string_escape').split(':', 1)
46 k, v = l.decode('string_escape').split(':', 1)
47 extra[k] = v
47 extra[k] = v
48 return extra
48 return extra
49
49
50 def encodeextra(d):
50 def encodeextra(d):
51 # keys must be sorted to produce a deterministic changelog entry
51 # keys must be sorted to produce a deterministic changelog entry
52 items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)]
52 items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)]
53 return "\0".join(items)
53 return "\0".join(items)
54
54
55 def stripdesc(desc):
55 def stripdesc(desc):
56 """strip trailing whitespace and leading and trailing empty lines"""
56 """strip trailing whitespace and leading and trailing empty lines"""
57 return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
57 return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
58
58
59 class appender(object):
59 class appender(object):
60 '''the changelog index must be updated last on disk, so we use this class
60 '''the changelog index must be updated last on disk, so we use this class
61 to delay writes to it'''
61 to delay writes to it'''
62 def __init__(self, vfs, name, mode, buf):
62 def __init__(self, vfs, name, mode, buf):
63 self.data = buf
63 self.data = buf
64 fp = vfs(name, mode)
64 fp = vfs(name, mode)
65 self.fp = fp
65 self.fp = fp
66 self.offset = fp.tell()
66 self.offset = fp.tell()
67 self.size = vfs.fstat(fp).st_size
67 self.size = vfs.fstat(fp).st_size
68
68
69 def end(self):
69 def end(self):
70 return self.size + len("".join(self.data))
70 return self.size + len("".join(self.data))
71 def tell(self):
71 def tell(self):
72 return self.offset
72 return self.offset
73 def flush(self):
73 def flush(self):
74 pass
74 pass
75 def close(self):
75 def close(self):
76 self.fp.close()
76 self.fp.close()
77
77
78 def seek(self, offset, whence=0):
78 def seek(self, offset, whence=0):
79 '''virtual file offset spans real file and data'''
79 '''virtual file offset spans real file and data'''
80 if whence == 0:
80 if whence == 0:
81 self.offset = offset
81 self.offset = offset
82 elif whence == 1:
82 elif whence == 1:
83 self.offset += offset
83 self.offset += offset
84 elif whence == 2:
84 elif whence == 2:
85 self.offset = self.end() + offset
85 self.offset = self.end() + offset
86 if self.offset < self.size:
86 if self.offset < self.size:
87 self.fp.seek(self.offset)
87 self.fp.seek(self.offset)
88
88
89 def read(self, count=-1):
89 def read(self, count=-1):
90 '''only trick here is reads that span real file and data'''
90 '''only trick here is reads that span real file and data'''
91 ret = ""
91 ret = ""
92 if self.offset < self.size:
92 if self.offset < self.size:
93 s = self.fp.read(count)
93 s = self.fp.read(count)
94 ret = s
94 ret = s
95 self.offset += len(s)
95 self.offset += len(s)
96 if count > 0:
96 if count > 0:
97 count -= len(s)
97 count -= len(s)
98 if count != 0:
98 if count != 0:
99 doff = self.offset - self.size
99 doff = self.offset - self.size
100 self.data.insert(0, "".join(self.data))
100 self.data.insert(0, "".join(self.data))
101 del self.data[1:]
101 del self.data[1:]
102 s = self.data[0][doff:doff + count]
102 s = self.data[0][doff:doff + count]
103 self.offset += len(s)
103 self.offset += len(s)
104 ret += s
104 ret += s
105 return ret
105 return ret
106
106
107 def write(self, s):
107 def write(self, s):
108 self.data.append(str(s))
108 self.data.append(str(s))
109 self.offset += len(s)
109 self.offset += len(s)
110
110
111 def delayopener(opener, target, divert, buf):
111 def delayopener(opener, target, divert, buf):
112 def o(name, mode='r'):
112 def o(name, mode='r'):
113 if name != target:
113 if name != target:
114 return opener(name, mode)
114 return opener(name, mode)
115 if divert:
115 if divert:
116 return opener(name + ".a", mode.replace('a', 'w'))
116 return opener(name + ".a", mode.replace('a', 'w'))
117 # otherwise, divert to memory
117 # otherwise, divert to memory
118 return appender(opener, name, mode, buf)
118 return appender(opener, name, mode, buf)
119 return o
119 return o
120
120
121 class changelog(revlog.revlog):
121 class changelog(revlog.revlog):
122 def __init__(self, opener):
122 def __init__(self, opener):
123 revlog.revlog.__init__(self, opener, "00changelog.i")
123 revlog.revlog.__init__(self, opener, "00changelog.i")
124 if self._initempty:
124 if self._initempty:
125 # changelogs don't benefit from generaldelta
125 # changelogs don't benefit from generaldelta
126 self.version &= ~revlog.REVLOGGENERALDELTA
126 self.version &= ~revlog.REVLOGGENERALDELTA
127 self._generaldelta = False
127 self._generaldelta = False
128 self._realopener = opener
128 self._realopener = opener
129 self._delayed = False
129 self._delayed = False
130 self._delaybuf = []
130 self._delaybuf = []
131 self._divert = False
131 self._divert = False
132 self.filteredrevs = frozenset()
132 self.filteredrevs = frozenset()
133
133
134 def tip(self):
134 def tip(self):
135 """filtered version of revlog.tip"""
135 """filtered version of revlog.tip"""
136 for i in xrange(len(self) -1, -2, -1):
136 for i in xrange(len(self) -1, -2, -1):
137 if i not in self.filteredrevs:
137 if i not in self.filteredrevs:
138 return self.node(i)
138 return self.node(i)
139
139
140 def __iter__(self):
140 def __iter__(self):
141 """filtered version of revlog.__iter__"""
141 """filtered version of revlog.__iter__"""
142 if len(self.filteredrevs) == 0:
142 if len(self.filteredrevs) == 0:
143 return revlog.revlog.__iter__(self)
143 return revlog.revlog.__iter__(self)
144
144
145 def filterediter():
145 def filterediter():
146 for i in xrange(len(self)):
146 for i in xrange(len(self)):
147 if i not in self.filteredrevs:
147 if i not in self.filteredrevs:
148 yield i
148 yield i
149
149
150 return filterediter()
150 return filterediter()
151
151
152 def revs(self, start=0, stop=None):
152 def revs(self, start=0, stop=None):
153 """filtered version of revlog.revs"""
153 """filtered version of revlog.revs"""
154 for i in super(changelog, self).revs(start, stop):
154 for i in super(changelog, self).revs(start, stop):
155 if i not in self.filteredrevs:
155 if i not in self.filteredrevs:
156 yield i
156 yield i
157
157
158 @util.propertycache
158 @util.propertycache
159 def nodemap(self):
159 def nodemap(self):
160 # XXX need filtering too
160 # XXX need filtering too
161 self.rev(self.node(0))
161 self.rev(self.node(0))
162 return self._nodecache
162 return self._nodecache
163
163
164 def hasnode(self, node):
164 def hasnode(self, node):
165 """filtered version of revlog.hasnode"""
165 """filtered version of revlog.hasnode"""
166 try:
166 try:
167 i = self.rev(node)
167 i = self.rev(node)
168 return i not in self.filteredrevs
168 return i not in self.filteredrevs
169 except KeyError:
169 except KeyError:
170 return False
170 return False
171
171
172 def headrevs(self):
172 def headrevs(self):
173 if self.filteredrevs:
173 if self.filteredrevs:
174 try:
174 try:
175 return self.index.headrevs(self.filteredrevs)
175 return self.index.headrevs(self.filteredrevs)
176 # AttributeError covers non-c-extension environments.
176 # AttributeError covers non-c-extension environments.
177 # TypeError allows us work with old c extensions.
177 # TypeError allows us work with old c extensions.
178 except (AttributeError, TypeError):
178 except (AttributeError, TypeError):
179 return self._headrevs()
179 return self._headrevs()
180
180
181 return super(changelog, self).headrevs()
181 return super(changelog, self).headrevs()
182
182
183 def strip(self, *args, **kwargs):
183 def strip(self, *args, **kwargs):
184 # XXX make something better than assert
184 # XXX make something better than assert
185 # We can't expect proper strip behavior if we are filtered.
185 # We can't expect proper strip behavior if we are filtered.
186 assert not self.filteredrevs
186 assert not self.filteredrevs
187 super(changelog, self).strip(*args, **kwargs)
187 super(changelog, self).strip(*args, **kwargs)
188
188
189 def rev(self, node):
189 def rev(self, node):
190 """filtered version of revlog.rev"""
190 """filtered version of revlog.rev"""
191 r = super(changelog, self).rev(node)
191 r = super(changelog, self).rev(node)
192 if r in self.filteredrevs:
192 if r in self.filteredrevs:
193 raise error.LookupError(hex(node), self.indexfile, _('no node'))
193 raise error.FilteredLookupError(hex(node), self.indexfile,
194 _('filtered node'))
194 return r
195 return r
195
196
196 def node(self, rev):
197 def node(self, rev):
197 """filtered version of revlog.node"""
198 """filtered version of revlog.node"""
198 if rev in self.filteredrevs:
199 if rev in self.filteredrevs:
199 raise error.FilteredIndexError(rev)
200 raise error.FilteredIndexError(rev)
200 return super(changelog, self).node(rev)
201 return super(changelog, self).node(rev)
201
202
202 def linkrev(self, rev):
203 def linkrev(self, rev):
203 """filtered version of revlog.linkrev"""
204 """filtered version of revlog.linkrev"""
204 if rev in self.filteredrevs:
205 if rev in self.filteredrevs:
205 raise error.FilteredIndexError(rev)
206 raise error.FilteredIndexError(rev)
206 return super(changelog, self).linkrev(rev)
207 return super(changelog, self).linkrev(rev)
207
208
208 def parentrevs(self, rev):
209 def parentrevs(self, rev):
209 """filtered version of revlog.parentrevs"""
210 """filtered version of revlog.parentrevs"""
210 if rev in self.filteredrevs:
211 if rev in self.filteredrevs:
211 raise error.FilteredIndexError(rev)
212 raise error.FilteredIndexError(rev)
212 return super(changelog, self).parentrevs(rev)
213 return super(changelog, self).parentrevs(rev)
213
214
214 def flags(self, rev):
215 def flags(self, rev):
215 """filtered version of revlog.flags"""
216 """filtered version of revlog.flags"""
216 if rev in self.filteredrevs:
217 if rev in self.filteredrevs:
217 raise error.FilteredIndexError(rev)
218 raise error.FilteredIndexError(rev)
218 return super(changelog, self).flags(rev)
219 return super(changelog, self).flags(rev)
219
220
220 def delayupdate(self):
221 def delayupdate(self):
221 "delay visibility of index updates to other readers"
222 "delay visibility of index updates to other readers"
222 self._delayed = True
223 self._delayed = True
223 self._divert = (len(self) == 0)
224 self._divert = (len(self) == 0)
224 self._delaybuf = []
225 self._delaybuf = []
225 self.opener = delayopener(self._realopener, self.indexfile,
226 self.opener = delayopener(self._realopener, self.indexfile,
226 self._divert, self._delaybuf)
227 self._divert, self._delaybuf)
227
228
228 def finalize(self, tr):
229 def finalize(self, tr):
229 "finalize index updates"
230 "finalize index updates"
230 self._delayed = False
231 self._delayed = False
231 self.opener = self._realopener
232 self.opener = self._realopener
232 # move redirected index data back into place
233 # move redirected index data back into place
233 if self._divert:
234 if self._divert:
234 tmpname = self.indexfile + ".a"
235 tmpname = self.indexfile + ".a"
235 nfile = self.opener.open(tmpname)
236 nfile = self.opener.open(tmpname)
236 nfile.close()
237 nfile.close()
237 self.opener.rename(tmpname, self.indexfile)
238 self.opener.rename(tmpname, self.indexfile)
238 elif self._delaybuf:
239 elif self._delaybuf:
239 fp = self.opener(self.indexfile, 'a')
240 fp = self.opener(self.indexfile, 'a')
240 fp.write("".join(self._delaybuf))
241 fp.write("".join(self._delaybuf))
241 fp.close()
242 fp.close()
242 self._delaybuf = []
243 self._delaybuf = []
243 # split when we're done
244 # split when we're done
244 self.checkinlinesize(tr)
245 self.checkinlinesize(tr)
245
246
246 def readpending(self, file):
247 def readpending(self, file):
247 r = revlog.revlog(self.opener, file)
248 r = revlog.revlog(self.opener, file)
248 self.index = r.index
249 self.index = r.index
249 self.nodemap = r.nodemap
250 self.nodemap = r.nodemap
250 self._nodecache = r._nodecache
251 self._nodecache = r._nodecache
251 self._chunkcache = r._chunkcache
252 self._chunkcache = r._chunkcache
252
253
253 def writepending(self):
254 def writepending(self):
254 "create a file containing the unfinalized state for pretxnchangegroup"
255 "create a file containing the unfinalized state for pretxnchangegroup"
255 if self._delaybuf:
256 if self._delaybuf:
256 # make a temporary copy of the index
257 # make a temporary copy of the index
257 fp1 = self._realopener(self.indexfile)
258 fp1 = self._realopener(self.indexfile)
258 fp2 = self._realopener(self.indexfile + ".a", "w")
259 fp2 = self._realopener(self.indexfile + ".a", "w")
259 fp2.write(fp1.read())
260 fp2.write(fp1.read())
260 # add pending data
261 # add pending data
261 fp2.write("".join(self._delaybuf))
262 fp2.write("".join(self._delaybuf))
262 fp2.close()
263 fp2.close()
263 # switch modes so finalize can simply rename
264 # switch modes so finalize can simply rename
264 self._delaybuf = []
265 self._delaybuf = []
265 self._divert = True
266 self._divert = True
266
267
267 if self._divert:
268 if self._divert:
268 return True
269 return True
269
270
270 return False
271 return False
271
272
272 def checkinlinesize(self, tr, fp=None):
273 def checkinlinesize(self, tr, fp=None):
273 if not self._delayed:
274 if not self._delayed:
274 revlog.revlog.checkinlinesize(self, tr, fp)
275 revlog.revlog.checkinlinesize(self, tr, fp)
275
276
276 def read(self, node):
277 def read(self, node):
277 """
278 """
278 format used:
279 format used:
279 nodeid\n : manifest node in ascii
280 nodeid\n : manifest node in ascii
280 user\n : user, no \n or \r allowed
281 user\n : user, no \n or \r allowed
281 time tz extra\n : date (time is int or float, timezone is int)
282 time tz extra\n : date (time is int or float, timezone is int)
282 : extra is metadata, encoded and separated by '\0'
283 : extra is metadata, encoded and separated by '\0'
283 : older versions ignore it
284 : older versions ignore it
284 files\n\n : files modified by the cset, no \n or \r allowed
285 files\n\n : files modified by the cset, no \n or \r allowed
285 (.*) : comment (free text, ideally utf-8)
286 (.*) : comment (free text, ideally utf-8)
286
287
287 changelog v0 doesn't use extra
288 changelog v0 doesn't use extra
288 """
289 """
289 text = self.revision(node)
290 text = self.revision(node)
290 if not text:
291 if not text:
291 return (nullid, "", (0, 0), [], "", _defaultextra)
292 return (nullid, "", (0, 0), [], "", _defaultextra)
292 last = text.index("\n\n")
293 last = text.index("\n\n")
293 desc = encoding.tolocal(text[last + 2:])
294 desc = encoding.tolocal(text[last + 2:])
294 l = text[:last].split('\n')
295 l = text[:last].split('\n')
295 manifest = bin(l[0])
296 manifest = bin(l[0])
296 user = encoding.tolocal(l[1])
297 user = encoding.tolocal(l[1])
297
298
298 tdata = l[2].split(' ', 2)
299 tdata = l[2].split(' ', 2)
299 if len(tdata) != 3:
300 if len(tdata) != 3:
300 time = float(tdata[0])
301 time = float(tdata[0])
301 try:
302 try:
302 # various tools did silly things with the time zone field.
303 # various tools did silly things with the time zone field.
303 timezone = int(tdata[1])
304 timezone = int(tdata[1])
304 except ValueError:
305 except ValueError:
305 timezone = 0
306 timezone = 0
306 extra = _defaultextra
307 extra = _defaultextra
307 else:
308 else:
308 time, timezone = float(tdata[0]), int(tdata[1])
309 time, timezone = float(tdata[0]), int(tdata[1])
309 extra = decodeextra(tdata[2])
310 extra = decodeextra(tdata[2])
310
311
311 files = l[3:]
312 files = l[3:]
312 return (manifest, user, (time, timezone), files, desc, extra)
313 return (manifest, user, (time, timezone), files, desc, extra)
313
314
314 def add(self, manifest, files, desc, transaction, p1, p2,
315 def add(self, manifest, files, desc, transaction, p1, p2,
315 user, date=None, extra=None):
316 user, date=None, extra=None):
316 # Convert to UTF-8 encoded bytestrings as the very first
317 # Convert to UTF-8 encoded bytestrings as the very first
317 # thing: calling any method on a localstr object will turn it
318 # thing: calling any method on a localstr object will turn it
318 # into a str object and the cached UTF-8 string is thus lost.
319 # into a str object and the cached UTF-8 string is thus lost.
319 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
320 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
320
321
321 user = user.strip()
322 user = user.strip()
322 # An empty username or a username with a "\n" will make the
323 # An empty username or a username with a "\n" will make the
323 # revision text contain two "\n\n" sequences -> corrupt
324 # revision text contain two "\n\n" sequences -> corrupt
324 # repository since read cannot unpack the revision.
325 # repository since read cannot unpack the revision.
325 if not user:
326 if not user:
326 raise error.RevlogError(_("empty username"))
327 raise error.RevlogError(_("empty username"))
327 if "\n" in user:
328 if "\n" in user:
328 raise error.RevlogError(_("username %s contains a newline")
329 raise error.RevlogError(_("username %s contains a newline")
329 % repr(user))
330 % repr(user))
330
331
331 desc = stripdesc(desc)
332 desc = stripdesc(desc)
332
333
333 if date:
334 if date:
334 parseddate = "%d %d" % util.parsedate(date)
335 parseddate = "%d %d" % util.parsedate(date)
335 else:
336 else:
336 parseddate = "%d %d" % util.makedate()
337 parseddate = "%d %d" % util.makedate()
337 if extra:
338 if extra:
338 branch = extra.get("branch")
339 branch = extra.get("branch")
339 if branch in ("default", ""):
340 if branch in ("default", ""):
340 del extra["branch"]
341 del extra["branch"]
341 elif branch in (".", "null", "tip"):
342 elif branch in (".", "null", "tip"):
342 raise error.RevlogError(_('the name \'%s\' is reserved')
343 raise error.RevlogError(_('the name \'%s\' is reserved')
343 % branch)
344 % branch)
344 if extra:
345 if extra:
345 extra = encodeextra(extra)
346 extra = encodeextra(extra)
346 parseddate = "%s %s" % (parseddate, extra)
347 parseddate = "%s %s" % (parseddate, extra)
347 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
348 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
348 text = "\n".join(l)
349 text = "\n".join(l)
349 return self.addrevision(text, transaction, len(self), p1, p2)
350 return self.addrevision(text, transaction, len(self), p1, p2)
350
351
351 def branchinfo(self, rev):
352 def branchinfo(self, rev):
352 """return the branch name and open/close state of a revision
353 """return the branch name and open/close state of a revision
353
354
354 This function exists because creating a changectx object
355 This function exists because creating a changectx object
355 just to access this is costly."""
356 just to access this is costly."""
356 extra = self.read(rev)[5]
357 extra = self.read(rev)[5]
357 return encoding.tolocal(extra.get("branch")), 'close' in extra
358 return encoding.tolocal(extra.get("branch")), 'close' in extra
@@ -1,129 +1,132
1 # error.py - Mercurial exceptions
1 # error.py - Mercurial exceptions
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 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 """Mercurial exceptions.
8 """Mercurial exceptions.
9
9
10 This allows us to catch exceptions at higher levels without forcing
10 This allows us to catch exceptions at higher levels without forcing
11 imports.
11 imports.
12 """
12 """
13
13
14 # Do not import anything here, please
14 # Do not import anything here, please
15
15
16 class RevlogError(Exception):
16 class RevlogError(Exception):
17 pass
17 pass
18
18
19 class FilteredIndexError(IndexError):
19 class FilteredIndexError(IndexError):
20 pass
20 pass
21
21
22 class LookupError(RevlogError, KeyError):
22 class LookupError(RevlogError, KeyError):
23 def __init__(self, name, index, message):
23 def __init__(self, name, index, message):
24 self.name = name
24 self.name = name
25 if isinstance(name, str) and len(name) == 20:
25 if isinstance(name, str) and len(name) == 20:
26 from node import short
26 from node import short
27 name = short(name)
27 name = short(name)
28 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
28 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
29
29
30 def __str__(self):
30 def __str__(self):
31 return RevlogError.__str__(self)
31 return RevlogError.__str__(self)
32
32
33 class FilteredLookupError(LookupError):
34 pass
35
33 class ManifestLookupError(LookupError):
36 class ManifestLookupError(LookupError):
34 pass
37 pass
35
38
36 class CommandError(Exception):
39 class CommandError(Exception):
37 """Exception raised on errors in parsing the command line."""
40 """Exception raised on errors in parsing the command line."""
38
41
39 class InterventionRequired(Exception):
42 class InterventionRequired(Exception):
40 """Exception raised when a command requires human intervention."""
43 """Exception raised when a command requires human intervention."""
41
44
42 class Abort(Exception):
45 class Abort(Exception):
43 """Raised if a command needs to print an error and exit."""
46 """Raised if a command needs to print an error and exit."""
44 def __init__(self, *args, **kw):
47 def __init__(self, *args, **kw):
45 Exception.__init__(self, *args)
48 Exception.__init__(self, *args)
46 self.hint = kw.get('hint')
49 self.hint = kw.get('hint')
47
50
48 class ConfigError(Abort):
51 class ConfigError(Abort):
49 """Exception raised when parsing config files"""
52 """Exception raised when parsing config files"""
50
53
51 class OutOfBandError(Exception):
54 class OutOfBandError(Exception):
52 """Exception raised when a remote repo reports failure"""
55 """Exception raised when a remote repo reports failure"""
53
56
54 class ParseError(Exception):
57 class ParseError(Exception):
55 """Exception raised when parsing config files (msg[, pos])"""
58 """Exception raised when parsing config files (msg[, pos])"""
56
59
57 class RepoError(Exception):
60 class RepoError(Exception):
58 def __init__(self, *args, **kw):
61 def __init__(self, *args, **kw):
59 Exception.__init__(self, *args)
62 Exception.__init__(self, *args)
60 self.hint = kw.get('hint')
63 self.hint = kw.get('hint')
61
64
62 class RepoLookupError(RepoError):
65 class RepoLookupError(RepoError):
63 pass
66 pass
64
67
65 class CapabilityError(RepoError):
68 class CapabilityError(RepoError):
66 pass
69 pass
67
70
68 class RequirementError(RepoError):
71 class RequirementError(RepoError):
69 """Exception raised if .hg/requires has an unknown entry."""
72 """Exception raised if .hg/requires has an unknown entry."""
70 pass
73 pass
71
74
72 class LockError(IOError):
75 class LockError(IOError):
73 def __init__(self, errno, strerror, filename, desc):
76 def __init__(self, errno, strerror, filename, desc):
74 IOError.__init__(self, errno, strerror, filename)
77 IOError.__init__(self, errno, strerror, filename)
75 self.desc = desc
78 self.desc = desc
76
79
77 class LockHeld(LockError):
80 class LockHeld(LockError):
78 def __init__(self, errno, filename, desc, locker):
81 def __init__(self, errno, filename, desc, locker):
79 LockError.__init__(self, errno, 'Lock held', filename, desc)
82 LockError.__init__(self, errno, 'Lock held', filename, desc)
80 self.locker = locker
83 self.locker = locker
81
84
82 class LockUnavailable(LockError):
85 class LockUnavailable(LockError):
83 pass
86 pass
84
87
85 class ResponseError(Exception):
88 class ResponseError(Exception):
86 """Raised to print an error with part of output and exit."""
89 """Raised to print an error with part of output and exit."""
87
90
88 class UnknownCommand(Exception):
91 class UnknownCommand(Exception):
89 """Exception raised if command is not in the command table."""
92 """Exception raised if command is not in the command table."""
90
93
91 class AmbiguousCommand(Exception):
94 class AmbiguousCommand(Exception):
92 """Exception raised if command shortcut matches more than one command."""
95 """Exception raised if command shortcut matches more than one command."""
93
96
94 # derived from KeyboardInterrupt to simplify some breakout code
97 # derived from KeyboardInterrupt to simplify some breakout code
95 class SignalInterrupt(KeyboardInterrupt):
98 class SignalInterrupt(KeyboardInterrupt):
96 """Exception raised on SIGTERM and SIGHUP."""
99 """Exception raised on SIGTERM and SIGHUP."""
97
100
98 class SignatureError(Exception):
101 class SignatureError(Exception):
99 pass
102 pass
100
103
101 class PushRaced(RuntimeError):
104 class PushRaced(RuntimeError):
102 """An exception raised during unbundling that indicate a push race"""
105 """An exception raised during unbundling that indicate a push race"""
103
106
104 # bundle2 related errors
107 # bundle2 related errors
105 class BundleValueError(ValueError):
108 class BundleValueError(ValueError):
106 """error raised when bundle2 cannot be processed"""
109 """error raised when bundle2 cannot be processed"""
107
110
108 class UnsupportedPartError(BundleValueError):
111 class UnsupportedPartError(BundleValueError):
109 def __init__(self, parttype=None, params=()):
112 def __init__(self, parttype=None, params=()):
110 self.parttype = parttype
113 self.parttype = parttype
111 self.params = params
114 self.params = params
112 if self.parttype is None:
115 if self.parttype is None:
113 msg = 'Stream Parameter'
116 msg = 'Stream Parameter'
114 else:
117 else:
115 msg = parttype
118 msg = parttype
116 if self.params:
119 if self.params:
117 msg = '%s - %s' % (msg, ', '.join(self.params))
120 msg = '%s - %s' % (msg, ', '.join(self.params))
118 ValueError.__init__(self, msg)
121 ValueError.__init__(self, msg)
119
122
120 class ReadOnlyPartError(RuntimeError):
123 class ReadOnlyPartError(RuntimeError):
121 """error raised when code tries to alter a part being generated"""
124 """error raised when code tries to alter a part being generated"""
122 pass
125 pass
123
126
124 class CensoredNodeError(RevlogError):
127 class CensoredNodeError(RevlogError):
125 """error raised when content verification fails on a censored node"""
128 """error raised when content verification fails on a censored node"""
126
129
127 def __init__(self, filename, node):
130 def __init__(self, filename, node):
128 from node import short
131 from node import short
129 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
132 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
General Comments 0
You need to be logged in to leave comments. Login now