##// END OF EJS Templates
repoview: add a FilteredIndexError class...
Pierre-Yves David -
r23014:f0081332 default
parent child Browse files
Show More
@@ -1,357 +1,357 b''
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.LookupError(hex(node), self.indexfile, _('no node'))
194 return r
194 return r
195
195
196 def node(self, rev):
196 def node(self, rev):
197 """filtered version of revlog.node"""
197 """filtered version of revlog.node"""
198 if rev in self.filteredrevs:
198 if rev in self.filteredrevs:
199 raise IndexError(rev)
199 raise error.FilteredIndexError(rev)
200 return super(changelog, self).node(rev)
200 return super(changelog, self).node(rev)
201
201
202 def linkrev(self, rev):
202 def linkrev(self, rev):
203 """filtered version of revlog.linkrev"""
203 """filtered version of revlog.linkrev"""
204 if rev in self.filteredrevs:
204 if rev in self.filteredrevs:
205 raise IndexError(rev)
205 raise error.FilteredIndexError(rev)
206 return super(changelog, self).linkrev(rev)
206 return super(changelog, self).linkrev(rev)
207
207
208 def parentrevs(self, rev):
208 def parentrevs(self, rev):
209 """filtered version of revlog.parentrevs"""
209 """filtered version of revlog.parentrevs"""
210 if rev in self.filteredrevs:
210 if rev in self.filteredrevs:
211 raise IndexError(rev)
211 raise error.FilteredIndexError(rev)
212 return super(changelog, self).parentrevs(rev)
212 return super(changelog, self).parentrevs(rev)
213
213
214 def flags(self, rev):
214 def flags(self, rev):
215 """filtered version of revlog.flags"""
215 """filtered version of revlog.flags"""
216 if rev in self.filteredrevs:
216 if rev in self.filteredrevs:
217 raise IndexError(rev)
217 raise error.FilteredIndexError(rev)
218 return super(changelog, self).flags(rev)
218 return super(changelog, self).flags(rev)
219
219
220 def delayupdate(self):
220 def delayupdate(self):
221 "delay visibility of index updates to other readers"
221 "delay visibility of index updates to other readers"
222 self._delayed = True
222 self._delayed = True
223 self._divert = (len(self) == 0)
223 self._divert = (len(self) == 0)
224 self._delaybuf = []
224 self._delaybuf = []
225 self.opener = delayopener(self._realopener, self.indexfile,
225 self.opener = delayopener(self._realopener, self.indexfile,
226 self._divert, self._delaybuf)
226 self._divert, self._delaybuf)
227
227
228 def finalize(self, tr):
228 def finalize(self, tr):
229 "finalize index updates"
229 "finalize index updates"
230 self._delayed = False
230 self._delayed = False
231 self.opener = self._realopener
231 self.opener = self._realopener
232 # move redirected index data back into place
232 # move redirected index data back into place
233 if self._divert:
233 if self._divert:
234 tmpname = self.indexfile + ".a"
234 tmpname = self.indexfile + ".a"
235 nfile = self.opener.open(tmpname)
235 nfile = self.opener.open(tmpname)
236 nfile.close()
236 nfile.close()
237 self.opener.rename(tmpname, self.indexfile)
237 self.opener.rename(tmpname, self.indexfile)
238 elif self._delaybuf:
238 elif self._delaybuf:
239 fp = self.opener(self.indexfile, 'a')
239 fp = self.opener(self.indexfile, 'a')
240 fp.write("".join(self._delaybuf))
240 fp.write("".join(self._delaybuf))
241 fp.close()
241 fp.close()
242 self._delaybuf = []
242 self._delaybuf = []
243 # split when we're done
243 # split when we're done
244 self.checkinlinesize(tr)
244 self.checkinlinesize(tr)
245
245
246 def readpending(self, file):
246 def readpending(self, file):
247 r = revlog.revlog(self.opener, file)
247 r = revlog.revlog(self.opener, file)
248 self.index = r.index
248 self.index = r.index
249 self.nodemap = r.nodemap
249 self.nodemap = r.nodemap
250 self._nodecache = r._nodecache
250 self._nodecache = r._nodecache
251 self._chunkcache = r._chunkcache
251 self._chunkcache = r._chunkcache
252
252
253 def writepending(self):
253 def writepending(self):
254 "create a file containing the unfinalized state for pretxnchangegroup"
254 "create a file containing the unfinalized state for pretxnchangegroup"
255 if self._delaybuf:
255 if self._delaybuf:
256 # make a temporary copy of the index
256 # make a temporary copy of the index
257 fp1 = self._realopener(self.indexfile)
257 fp1 = self._realopener(self.indexfile)
258 fp2 = self._realopener(self.indexfile + ".a", "w")
258 fp2 = self._realopener(self.indexfile + ".a", "w")
259 fp2.write(fp1.read())
259 fp2.write(fp1.read())
260 # add pending data
260 # add pending data
261 fp2.write("".join(self._delaybuf))
261 fp2.write("".join(self._delaybuf))
262 fp2.close()
262 fp2.close()
263 # switch modes so finalize can simply rename
263 # switch modes so finalize can simply rename
264 self._delaybuf = []
264 self._delaybuf = []
265 self._divert = True
265 self._divert = True
266
266
267 if self._divert:
267 if self._divert:
268 return True
268 return True
269
269
270 return False
270 return False
271
271
272 def checkinlinesize(self, tr, fp=None):
272 def checkinlinesize(self, tr, fp=None):
273 if not self._delayed:
273 if not self._delayed:
274 revlog.revlog.checkinlinesize(self, tr, fp)
274 revlog.revlog.checkinlinesize(self, tr, fp)
275
275
276 def read(self, node):
276 def read(self, node):
277 """
277 """
278 format used:
278 format used:
279 nodeid\n : manifest node in ascii
279 nodeid\n : manifest node in ascii
280 user\n : user, no \n or \r allowed
280 user\n : user, no \n or \r allowed
281 time tz extra\n : date (time is int or float, timezone is int)
281 time tz extra\n : date (time is int or float, timezone is int)
282 : extra is metadata, encoded and separated by '\0'
282 : extra is metadata, encoded and separated by '\0'
283 : older versions ignore it
283 : older versions ignore it
284 files\n\n : files modified by the cset, no \n or \r allowed
284 files\n\n : files modified by the cset, no \n or \r allowed
285 (.*) : comment (free text, ideally utf-8)
285 (.*) : comment (free text, ideally utf-8)
286
286
287 changelog v0 doesn't use extra
287 changelog v0 doesn't use extra
288 """
288 """
289 text = self.revision(node)
289 text = self.revision(node)
290 if not text:
290 if not text:
291 return (nullid, "", (0, 0), [], "", _defaultextra)
291 return (nullid, "", (0, 0), [], "", _defaultextra)
292 last = text.index("\n\n")
292 last = text.index("\n\n")
293 desc = encoding.tolocal(text[last + 2:])
293 desc = encoding.tolocal(text[last + 2:])
294 l = text[:last].split('\n')
294 l = text[:last].split('\n')
295 manifest = bin(l[0])
295 manifest = bin(l[0])
296 user = encoding.tolocal(l[1])
296 user = encoding.tolocal(l[1])
297
297
298 tdata = l[2].split(' ', 2)
298 tdata = l[2].split(' ', 2)
299 if len(tdata) != 3:
299 if len(tdata) != 3:
300 time = float(tdata[0])
300 time = float(tdata[0])
301 try:
301 try:
302 # various tools did silly things with the time zone field.
302 # various tools did silly things with the time zone field.
303 timezone = int(tdata[1])
303 timezone = int(tdata[1])
304 except ValueError:
304 except ValueError:
305 timezone = 0
305 timezone = 0
306 extra = _defaultextra
306 extra = _defaultextra
307 else:
307 else:
308 time, timezone = float(tdata[0]), int(tdata[1])
308 time, timezone = float(tdata[0]), int(tdata[1])
309 extra = decodeextra(tdata[2])
309 extra = decodeextra(tdata[2])
310
310
311 files = l[3:]
311 files = l[3:]
312 return (manifest, user, (time, timezone), files, desc, extra)
312 return (manifest, user, (time, timezone), files, desc, extra)
313
313
314 def add(self, manifest, files, desc, transaction, p1, p2,
314 def add(self, manifest, files, desc, transaction, p1, p2,
315 user, date=None, extra=None):
315 user, date=None, extra=None):
316 # Convert to UTF-8 encoded bytestrings as the very first
316 # Convert to UTF-8 encoded bytestrings as the very first
317 # thing: calling any method on a localstr object will turn it
317 # 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.
318 # into a str object and the cached UTF-8 string is thus lost.
319 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
319 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
320
320
321 user = user.strip()
321 user = user.strip()
322 # An empty username or a username with a "\n" will make the
322 # An empty username or a username with a "\n" will make the
323 # revision text contain two "\n\n" sequences -> corrupt
323 # revision text contain two "\n\n" sequences -> corrupt
324 # repository since read cannot unpack the revision.
324 # repository since read cannot unpack the revision.
325 if not user:
325 if not user:
326 raise error.RevlogError(_("empty username"))
326 raise error.RevlogError(_("empty username"))
327 if "\n" in user:
327 if "\n" in user:
328 raise error.RevlogError(_("username %s contains a newline")
328 raise error.RevlogError(_("username %s contains a newline")
329 % repr(user))
329 % repr(user))
330
330
331 desc = stripdesc(desc)
331 desc = stripdesc(desc)
332
332
333 if date:
333 if date:
334 parseddate = "%d %d" % util.parsedate(date)
334 parseddate = "%d %d" % util.parsedate(date)
335 else:
335 else:
336 parseddate = "%d %d" % util.makedate()
336 parseddate = "%d %d" % util.makedate()
337 if extra:
337 if extra:
338 branch = extra.get("branch")
338 branch = extra.get("branch")
339 if branch in ("default", ""):
339 if branch in ("default", ""):
340 del extra["branch"]
340 del extra["branch"]
341 elif branch in (".", "null", "tip"):
341 elif branch in (".", "null", "tip"):
342 raise error.RevlogError(_('the name \'%s\' is reserved')
342 raise error.RevlogError(_('the name \'%s\' is reserved')
343 % branch)
343 % branch)
344 if extra:
344 if extra:
345 extra = encodeextra(extra)
345 extra = encodeextra(extra)
346 parseddate = "%s %s" % (parseddate, extra)
346 parseddate = "%s %s" % (parseddate, extra)
347 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
347 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
348 text = "\n".join(l)
348 text = "\n".join(l)
349 return self.addrevision(text, transaction, len(self), p1, p2)
349 return self.addrevision(text, transaction, len(self), p1, p2)
350
350
351 def branchinfo(self, rev):
351 def branchinfo(self, rev):
352 """return the branch name and open/close state of a revision
352 """return the branch name and open/close state of a revision
353
353
354 This function exists because creating a changectx object
354 This function exists because creating a changectx object
355 just to access this is costly."""
355 just to access this is costly."""
356 extra = self.read(rev)[5]
356 extra = self.read(rev)[5]
357 return encoding.tolocal(extra.get("branch")), 'close' in extra
357 return encoding.tolocal(extra.get("branch")), 'close' in extra
@@ -1,126 +1,129 b''
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):
20 pass
21
19 class LookupError(RevlogError, KeyError):
22 class LookupError(RevlogError, KeyError):
20 def __init__(self, name, index, message):
23 def __init__(self, name, index, message):
21 self.name = name
24 self.name = name
22 if isinstance(name, str) and len(name) == 20:
25 if isinstance(name, str) and len(name) == 20:
23 from node import short
26 from node import short
24 name = short(name)
27 name = short(name)
25 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
28 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
26
29
27 def __str__(self):
30 def __str__(self):
28 return RevlogError.__str__(self)
31 return RevlogError.__str__(self)
29
32
30 class ManifestLookupError(LookupError):
33 class ManifestLookupError(LookupError):
31 pass
34 pass
32
35
33 class CommandError(Exception):
36 class CommandError(Exception):
34 """Exception raised on errors in parsing the command line."""
37 """Exception raised on errors in parsing the command line."""
35
38
36 class InterventionRequired(Exception):
39 class InterventionRequired(Exception):
37 """Exception raised when a command requires human intervention."""
40 """Exception raised when a command requires human intervention."""
38
41
39 class Abort(Exception):
42 class Abort(Exception):
40 """Raised if a command needs to print an error and exit."""
43 """Raised if a command needs to print an error and exit."""
41 def __init__(self, *args, **kw):
44 def __init__(self, *args, **kw):
42 Exception.__init__(self, *args)
45 Exception.__init__(self, *args)
43 self.hint = kw.get('hint')
46 self.hint = kw.get('hint')
44
47
45 class ConfigError(Abort):
48 class ConfigError(Abort):
46 """Exception raised when parsing config files"""
49 """Exception raised when parsing config files"""
47
50
48 class OutOfBandError(Exception):
51 class OutOfBandError(Exception):
49 """Exception raised when a remote repo reports failure"""
52 """Exception raised when a remote repo reports failure"""
50
53
51 class ParseError(Exception):
54 class ParseError(Exception):
52 """Exception raised when parsing config files (msg[, pos])"""
55 """Exception raised when parsing config files (msg[, pos])"""
53
56
54 class RepoError(Exception):
57 class RepoError(Exception):
55 def __init__(self, *args, **kw):
58 def __init__(self, *args, **kw):
56 Exception.__init__(self, *args)
59 Exception.__init__(self, *args)
57 self.hint = kw.get('hint')
60 self.hint = kw.get('hint')
58
61
59 class RepoLookupError(RepoError):
62 class RepoLookupError(RepoError):
60 pass
63 pass
61
64
62 class CapabilityError(RepoError):
65 class CapabilityError(RepoError):
63 pass
66 pass
64
67
65 class RequirementError(RepoError):
68 class RequirementError(RepoError):
66 """Exception raised if .hg/requires has an unknown entry."""
69 """Exception raised if .hg/requires has an unknown entry."""
67 pass
70 pass
68
71
69 class LockError(IOError):
72 class LockError(IOError):
70 def __init__(self, errno, strerror, filename, desc):
73 def __init__(self, errno, strerror, filename, desc):
71 IOError.__init__(self, errno, strerror, filename)
74 IOError.__init__(self, errno, strerror, filename)
72 self.desc = desc
75 self.desc = desc
73
76
74 class LockHeld(LockError):
77 class LockHeld(LockError):
75 def __init__(self, errno, filename, desc, locker):
78 def __init__(self, errno, filename, desc, locker):
76 LockError.__init__(self, errno, 'Lock held', filename, desc)
79 LockError.__init__(self, errno, 'Lock held', filename, desc)
77 self.locker = locker
80 self.locker = locker
78
81
79 class LockUnavailable(LockError):
82 class LockUnavailable(LockError):
80 pass
83 pass
81
84
82 class ResponseError(Exception):
85 class ResponseError(Exception):
83 """Raised to print an error with part of output and exit."""
86 """Raised to print an error with part of output and exit."""
84
87
85 class UnknownCommand(Exception):
88 class UnknownCommand(Exception):
86 """Exception raised if command is not in the command table."""
89 """Exception raised if command is not in the command table."""
87
90
88 class AmbiguousCommand(Exception):
91 class AmbiguousCommand(Exception):
89 """Exception raised if command shortcut matches more than one command."""
92 """Exception raised if command shortcut matches more than one command."""
90
93
91 # derived from KeyboardInterrupt to simplify some breakout code
94 # derived from KeyboardInterrupt to simplify some breakout code
92 class SignalInterrupt(KeyboardInterrupt):
95 class SignalInterrupt(KeyboardInterrupt):
93 """Exception raised on SIGTERM and SIGHUP."""
96 """Exception raised on SIGTERM and SIGHUP."""
94
97
95 class SignatureError(Exception):
98 class SignatureError(Exception):
96 pass
99 pass
97
100
98 class PushRaced(RuntimeError):
101 class PushRaced(RuntimeError):
99 """An exception raised during unbundling that indicate a push race"""
102 """An exception raised during unbundling that indicate a push race"""
100
103
101 # bundle2 related errors
104 # bundle2 related errors
102 class BundleValueError(ValueError):
105 class BundleValueError(ValueError):
103 """error raised when bundle2 cannot be processed"""
106 """error raised when bundle2 cannot be processed"""
104
107
105 class UnsupportedPartError(BundleValueError):
108 class UnsupportedPartError(BundleValueError):
106 def __init__(self, parttype=None, params=()):
109 def __init__(self, parttype=None, params=()):
107 self.parttype = parttype
110 self.parttype = parttype
108 self.params = params
111 self.params = params
109 if self.parttype is None:
112 if self.parttype is None:
110 msg = 'Stream Parameter'
113 msg = 'Stream Parameter'
111 else:
114 else:
112 msg = parttype
115 msg = parttype
113 if self.params:
116 if self.params:
114 msg = '%s - %s' % (msg, ', '.join(self.params))
117 msg = '%s - %s' % (msg, ', '.join(self.params))
115 ValueError.__init__(self, msg)
118 ValueError.__init__(self, msg)
116
119
117 class ReadOnlyPartError(RuntimeError):
120 class ReadOnlyPartError(RuntimeError):
118 """error raised when code tries to alter a part being generated"""
121 """error raised when code tries to alter a part being generated"""
119 pass
122 pass
120
123
121 class CensoredNodeError(RevlogError):
124 class CensoredNodeError(RevlogError):
122 """error raised when content verification fails on a censored node"""
125 """error raised when content verification fails on a censored node"""
123
126
124 def __init__(self, filename, node):
127 def __init__(self, filename, node):
125 from node import short
128 from node import short
126 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
129 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
General Comments 0
You need to be logged in to leave comments. Login now