##// END OF EJS Templates
error: allow a 'hint' to OutOfBandError...
Pierre-Yves David -
r25242:8de7d1d9 default
parent child Browse files
Show More
@@ -1,1002 +1,1009 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching 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 i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import difflib
10 import difflib
11 import util, commands, hg, fancyopts, extensions, hook, error
11 import util, commands, hg, fancyopts, extensions, hook, error
12 import cmdutil, encoding
12 import cmdutil, encoding
13 import ui as uimod
13 import ui as uimod
14
14
15 class request(object):
15 class request(object):
16 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
17 ferr=None):
17 ferr=None):
18 self.args = args
18 self.args = args
19 self.ui = ui
19 self.ui = ui
20 self.repo = repo
20 self.repo = repo
21
21
22 # input/output/error streams
22 # input/output/error streams
23 self.fin = fin
23 self.fin = fin
24 self.fout = fout
24 self.fout = fout
25 self.ferr = ferr
25 self.ferr = ferr
26
26
27 def run():
27 def run():
28 "run the command in sys.argv"
28 "run the command in sys.argv"
29 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
30
30
31 def _getsimilar(symbols, value):
31 def _getsimilar(symbols, value):
32 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
32 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
33 # The cutoff for similarity here is pretty arbitrary. It should
33 # The cutoff for similarity here is pretty arbitrary. It should
34 # probably be investigated and tweaked.
34 # probably be investigated and tweaked.
35 return [s for s in symbols if sim(s) > 0.6]
35 return [s for s in symbols if sim(s) > 0.6]
36
36
37 def _formatparse(write, inst):
37 def _formatparse(write, inst):
38 similar = []
38 similar = []
39 if isinstance(inst, error.UnknownIdentifier):
39 if isinstance(inst, error.UnknownIdentifier):
40 # make sure to check fileset first, as revset can invoke fileset
40 # make sure to check fileset first, as revset can invoke fileset
41 similar = _getsimilar(inst.symbols, inst.function)
41 similar = _getsimilar(inst.symbols, inst.function)
42 if len(inst.args) > 1:
42 if len(inst.args) > 1:
43 write(_("hg: parse error at %s: %s\n") %
43 write(_("hg: parse error at %s: %s\n") %
44 (inst.args[1], inst.args[0]))
44 (inst.args[1], inst.args[0]))
45 if (inst.args[0][0] == ' '):
45 if (inst.args[0][0] == ' '):
46 write(_("unexpected leading whitespace\n"))
46 write(_("unexpected leading whitespace\n"))
47 else:
47 else:
48 write(_("hg: parse error: %s\n") % inst.args[0])
48 write(_("hg: parse error: %s\n") % inst.args[0])
49 if similar:
49 if similar:
50 if len(similar) == 1:
50 if len(similar) == 1:
51 write(_("(did you mean %r?)\n") % similar[0])
51 write(_("(did you mean %r?)\n") % similar[0])
52 else:
52 else:
53 ss = ", ".join(sorted(similar))
53 ss = ", ".join(sorted(similar))
54 write(_("(did you mean one of %s?)\n") % ss)
54 write(_("(did you mean one of %s?)\n") % ss)
55
55
56 def dispatch(req):
56 def dispatch(req):
57 "run the command specified in req.args"
57 "run the command specified in req.args"
58 if req.ferr:
58 if req.ferr:
59 ferr = req.ferr
59 ferr = req.ferr
60 elif req.ui:
60 elif req.ui:
61 ferr = req.ui.ferr
61 ferr = req.ui.ferr
62 else:
62 else:
63 ferr = sys.stderr
63 ferr = sys.stderr
64
64
65 try:
65 try:
66 if not req.ui:
66 if not req.ui:
67 req.ui = uimod.ui()
67 req.ui = uimod.ui()
68 if '--traceback' in req.args:
68 if '--traceback' in req.args:
69 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
69 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
70
70
71 # set ui streams from the request
71 # set ui streams from the request
72 if req.fin:
72 if req.fin:
73 req.ui.fin = req.fin
73 req.ui.fin = req.fin
74 if req.fout:
74 if req.fout:
75 req.ui.fout = req.fout
75 req.ui.fout = req.fout
76 if req.ferr:
76 if req.ferr:
77 req.ui.ferr = req.ferr
77 req.ui.ferr = req.ferr
78 except util.Abort, inst:
78 except util.Abort, inst:
79 ferr.write(_("abort: %s\n") % inst)
79 ferr.write(_("abort: %s\n") % inst)
80 if inst.hint:
80 if inst.hint:
81 ferr.write(_("(%s)\n") % inst.hint)
81 ferr.write(_("(%s)\n") % inst.hint)
82 return -1
82 return -1
83 except error.ParseError, inst:
83 except error.ParseError, inst:
84 _formatparse(ferr.write, inst)
84 _formatparse(ferr.write, inst)
85 return -1
85 return -1
86
86
87 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
87 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
88 starttime = time.time()
88 starttime = time.time()
89 ret = None
89 ret = None
90 try:
90 try:
91 ret = _runcatch(req)
91 ret = _runcatch(req)
92 return ret
92 return ret
93 finally:
93 finally:
94 duration = time.time() - starttime
94 duration = time.time() - starttime
95 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
95 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
96 msg, ret or 0, duration)
96 msg, ret or 0, duration)
97
97
98 def _runcatch(req):
98 def _runcatch(req):
99 def catchterm(*args):
99 def catchterm(*args):
100 raise error.SignalInterrupt
100 raise error.SignalInterrupt
101
101
102 ui = req.ui
102 ui = req.ui
103 try:
103 try:
104 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
104 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
105 num = getattr(signal, name, None)
105 num = getattr(signal, name, None)
106 if num:
106 if num:
107 signal.signal(num, catchterm)
107 signal.signal(num, catchterm)
108 except ValueError:
108 except ValueError:
109 pass # happens if called in a thread
109 pass # happens if called in a thread
110
110
111 try:
111 try:
112 try:
112 try:
113 debugger = 'pdb'
113 debugger = 'pdb'
114 debugtrace = {
114 debugtrace = {
115 'pdb' : pdb.set_trace
115 'pdb' : pdb.set_trace
116 }
116 }
117 debugmortem = {
117 debugmortem = {
118 'pdb' : pdb.post_mortem
118 'pdb' : pdb.post_mortem
119 }
119 }
120
120
121 # read --config before doing anything else
121 # read --config before doing anything else
122 # (e.g. to change trust settings for reading .hg/hgrc)
122 # (e.g. to change trust settings for reading .hg/hgrc)
123 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
123 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
124
124
125 if req.repo:
125 if req.repo:
126 # copy configs that were passed on the cmdline (--config) to
126 # copy configs that were passed on the cmdline (--config) to
127 # the repo ui
127 # the repo ui
128 for sec, name, val in cfgs:
128 for sec, name, val in cfgs:
129 req.repo.ui.setconfig(sec, name, val, source='--config')
129 req.repo.ui.setconfig(sec, name, val, source='--config')
130
130
131 # if we are in HGPLAIN mode, then disable custom debugging
131 # if we are in HGPLAIN mode, then disable custom debugging
132 debugger = ui.config("ui", "debugger")
132 debugger = ui.config("ui", "debugger")
133 debugmod = pdb
133 debugmod = pdb
134 if not debugger or ui.plain():
134 if not debugger or ui.plain():
135 debugger = 'pdb'
135 debugger = 'pdb'
136 elif '--debugger' in req.args:
136 elif '--debugger' in req.args:
137 # This import can be slow for fancy debuggers, so only
137 # This import can be slow for fancy debuggers, so only
138 # do it when absolutely necessary, i.e. when actual
138 # do it when absolutely necessary, i.e. when actual
139 # debugging has been requested
139 # debugging has been requested
140 try:
140 try:
141 debugmod = __import__(debugger)
141 debugmod = __import__(debugger)
142 except ImportError:
142 except ImportError:
143 pass # Leave debugmod = pdb
143 pass # Leave debugmod = pdb
144
144
145 debugtrace[debugger] = debugmod.set_trace
145 debugtrace[debugger] = debugmod.set_trace
146 debugmortem[debugger] = debugmod.post_mortem
146 debugmortem[debugger] = debugmod.post_mortem
147
147
148 # enter the debugger before command execution
148 # enter the debugger before command execution
149 if '--debugger' in req.args:
149 if '--debugger' in req.args:
150 ui.warn(_("entering debugger - "
150 ui.warn(_("entering debugger - "
151 "type c to continue starting hg or h for help\n"))
151 "type c to continue starting hg or h for help\n"))
152
152
153 if (debugger != 'pdb' and
153 if (debugger != 'pdb' and
154 debugtrace[debugger] == debugtrace['pdb']):
154 debugtrace[debugger] == debugtrace['pdb']):
155 ui.warn(_("%s debugger specified "
155 ui.warn(_("%s debugger specified "
156 "but its module was not found\n") % debugger)
156 "but its module was not found\n") % debugger)
157
157
158 debugtrace[debugger]()
158 debugtrace[debugger]()
159 try:
159 try:
160 return _dispatch(req)
160 return _dispatch(req)
161 finally:
161 finally:
162 ui.flush()
162 ui.flush()
163 except: # re-raises
163 except: # re-raises
164 # enter the debugger when we hit an exception
164 # enter the debugger when we hit an exception
165 if '--debugger' in req.args:
165 if '--debugger' in req.args:
166 traceback.print_exc()
166 traceback.print_exc()
167 debugmortem[debugger](sys.exc_info()[2])
167 debugmortem[debugger](sys.exc_info()[2])
168 ui.traceback()
168 ui.traceback()
169 raise
169 raise
170
170
171 # Global exception handling, alphabetically
171 # Global exception handling, alphabetically
172 # Mercurial-specific first, followed by built-in and library exceptions
172 # Mercurial-specific first, followed by built-in and library exceptions
173 except error.AmbiguousCommand, inst:
173 except error.AmbiguousCommand, inst:
174 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
174 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
175 (inst.args[0], " ".join(inst.args[1])))
175 (inst.args[0], " ".join(inst.args[1])))
176 except error.ParseError, inst:
176 except error.ParseError, inst:
177 _formatparse(ui.warn, inst)
177 _formatparse(ui.warn, inst)
178 return -1
178 return -1
179 except error.LockHeld, inst:
179 except error.LockHeld, inst:
180 if inst.errno == errno.ETIMEDOUT:
180 if inst.errno == errno.ETIMEDOUT:
181 reason = _('timed out waiting for lock held by %s') % inst.locker
181 reason = _('timed out waiting for lock held by %s') % inst.locker
182 else:
182 else:
183 reason = _('lock held by %s') % inst.locker
183 reason = _('lock held by %s') % inst.locker
184 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
184 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
185 except error.LockUnavailable, inst:
185 except error.LockUnavailable, inst:
186 ui.warn(_("abort: could not lock %s: %s\n") %
186 ui.warn(_("abort: could not lock %s: %s\n") %
187 (inst.desc or inst.filename, inst.strerror))
187 (inst.desc or inst.filename, inst.strerror))
188 except error.CommandError, inst:
188 except error.CommandError, inst:
189 if inst.args[0]:
189 if inst.args[0]:
190 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
190 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
191 commands.help_(ui, inst.args[0], full=False, command=True)
191 commands.help_(ui, inst.args[0], full=False, command=True)
192 else:
192 else:
193 ui.warn(_("hg: %s\n") % inst.args[1])
193 ui.warn(_("hg: %s\n") % inst.args[1])
194 commands.help_(ui, 'shortlist')
194 commands.help_(ui, 'shortlist')
195 except error.OutOfBandError, inst:
195 except error.OutOfBandError, inst:
196 ui.warn(_("abort: remote error:\n"))
196 if inst.args:
197 ui.warn(''.join(inst.args))
197 msg = _("abort: remote error:\n")
198 else:
199 msg = _("abort: remote error\n")
200 ui.warn(msg)
201 if inst.args:
202 ui.warn(''.join(inst.args))
203 if inst.hint:
204 ui.warn('(%s)\n' % inst.hint)
198 except error.RepoError, inst:
205 except error.RepoError, inst:
199 ui.warn(_("abort: %s!\n") % inst)
206 ui.warn(_("abort: %s!\n") % inst)
200 if inst.hint:
207 if inst.hint:
201 ui.warn(_("(%s)\n") % inst.hint)
208 ui.warn(_("(%s)\n") % inst.hint)
202 except error.ResponseError, inst:
209 except error.ResponseError, inst:
203 ui.warn(_("abort: %s") % inst.args[0])
210 ui.warn(_("abort: %s") % inst.args[0])
204 if not isinstance(inst.args[1], basestring):
211 if not isinstance(inst.args[1], basestring):
205 ui.warn(" %r\n" % (inst.args[1],))
212 ui.warn(" %r\n" % (inst.args[1],))
206 elif not inst.args[1]:
213 elif not inst.args[1]:
207 ui.warn(_(" empty string\n"))
214 ui.warn(_(" empty string\n"))
208 else:
215 else:
209 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
216 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
210 except error.CensoredNodeError, inst:
217 except error.CensoredNodeError, inst:
211 ui.warn(_("abort: file censored %s!\n") % inst)
218 ui.warn(_("abort: file censored %s!\n") % inst)
212 except error.RevlogError, inst:
219 except error.RevlogError, inst:
213 ui.warn(_("abort: %s!\n") % inst)
220 ui.warn(_("abort: %s!\n") % inst)
214 except error.SignalInterrupt:
221 except error.SignalInterrupt:
215 ui.warn(_("killed!\n"))
222 ui.warn(_("killed!\n"))
216 except error.UnknownCommand, inst:
223 except error.UnknownCommand, inst:
217 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
224 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
218 try:
225 try:
219 # check if the command is in a disabled extension
226 # check if the command is in a disabled extension
220 # (but don't check for extensions themselves)
227 # (but don't check for extensions themselves)
221 commands.help_(ui, inst.args[0], unknowncmd=True)
228 commands.help_(ui, inst.args[0], unknowncmd=True)
222 except error.UnknownCommand:
229 except error.UnknownCommand:
223 suggested = False
230 suggested = False
224 if len(inst.args) == 2:
231 if len(inst.args) == 2:
225 sim = _getsimilar(inst.args[1], inst.args[0])
232 sim = _getsimilar(inst.args[1], inst.args[0])
226 if sim:
233 if sim:
227 ui.warn(_('(did you mean one of %s?)\n') %
234 ui.warn(_('(did you mean one of %s?)\n') %
228 ', '.join(sorted(sim)))
235 ', '.join(sorted(sim)))
229 suggested = True
236 suggested = True
230 if not suggested:
237 if not suggested:
231 commands.help_(ui, 'shortlist')
238 commands.help_(ui, 'shortlist')
232 except error.InterventionRequired, inst:
239 except error.InterventionRequired, inst:
233 ui.warn("%s\n" % inst)
240 ui.warn("%s\n" % inst)
234 return 1
241 return 1
235 except util.Abort, inst:
242 except util.Abort, inst:
236 ui.warn(_("abort: %s\n") % inst)
243 ui.warn(_("abort: %s\n") % inst)
237 if inst.hint:
244 if inst.hint:
238 ui.warn(_("(%s)\n") % inst.hint)
245 ui.warn(_("(%s)\n") % inst.hint)
239 except ImportError, inst:
246 except ImportError, inst:
240 ui.warn(_("abort: %s!\n") % inst)
247 ui.warn(_("abort: %s!\n") % inst)
241 m = str(inst).split()[-1]
248 m = str(inst).split()[-1]
242 if m in "mpatch bdiff".split():
249 if m in "mpatch bdiff".split():
243 ui.warn(_("(did you forget to compile extensions?)\n"))
250 ui.warn(_("(did you forget to compile extensions?)\n"))
244 elif m in "zlib".split():
251 elif m in "zlib".split():
245 ui.warn(_("(is your Python install correct?)\n"))
252 ui.warn(_("(is your Python install correct?)\n"))
246 except IOError, inst:
253 except IOError, inst:
247 if util.safehasattr(inst, "code"):
254 if util.safehasattr(inst, "code"):
248 ui.warn(_("abort: %s\n") % inst)
255 ui.warn(_("abort: %s\n") % inst)
249 elif util.safehasattr(inst, "reason"):
256 elif util.safehasattr(inst, "reason"):
250 try: # usually it is in the form (errno, strerror)
257 try: # usually it is in the form (errno, strerror)
251 reason = inst.reason.args[1]
258 reason = inst.reason.args[1]
252 except (AttributeError, IndexError):
259 except (AttributeError, IndexError):
253 # it might be anything, for example a string
260 # it might be anything, for example a string
254 reason = inst.reason
261 reason = inst.reason
255 if isinstance(reason, unicode):
262 if isinstance(reason, unicode):
256 # SSLError of Python 2.7.9 contains a unicode
263 # SSLError of Python 2.7.9 contains a unicode
257 reason = reason.encode(encoding.encoding, 'replace')
264 reason = reason.encode(encoding.encoding, 'replace')
258 ui.warn(_("abort: error: %s\n") % reason)
265 ui.warn(_("abort: error: %s\n") % reason)
259 elif (util.safehasattr(inst, "args")
266 elif (util.safehasattr(inst, "args")
260 and inst.args and inst.args[0] == errno.EPIPE):
267 and inst.args and inst.args[0] == errno.EPIPE):
261 if ui.debugflag:
268 if ui.debugflag:
262 ui.warn(_("broken pipe\n"))
269 ui.warn(_("broken pipe\n"))
263 elif getattr(inst, "strerror", None):
270 elif getattr(inst, "strerror", None):
264 if getattr(inst, "filename", None):
271 if getattr(inst, "filename", None):
265 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
272 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
266 else:
273 else:
267 ui.warn(_("abort: %s\n") % inst.strerror)
274 ui.warn(_("abort: %s\n") % inst.strerror)
268 else:
275 else:
269 raise
276 raise
270 except OSError, inst:
277 except OSError, inst:
271 if getattr(inst, "filename", None) is not None:
278 if getattr(inst, "filename", None) is not None:
272 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
279 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
273 else:
280 else:
274 ui.warn(_("abort: %s\n") % inst.strerror)
281 ui.warn(_("abort: %s\n") % inst.strerror)
275 except KeyboardInterrupt:
282 except KeyboardInterrupt:
276 try:
283 try:
277 ui.warn(_("interrupted!\n"))
284 ui.warn(_("interrupted!\n"))
278 except IOError, inst:
285 except IOError, inst:
279 if inst.errno == errno.EPIPE:
286 if inst.errno == errno.EPIPE:
280 if ui.debugflag:
287 if ui.debugflag:
281 ui.warn(_("\nbroken pipe\n"))
288 ui.warn(_("\nbroken pipe\n"))
282 else:
289 else:
283 raise
290 raise
284 except MemoryError:
291 except MemoryError:
285 ui.warn(_("abort: out of memory\n"))
292 ui.warn(_("abort: out of memory\n"))
286 except SystemExit, inst:
293 except SystemExit, inst:
287 # Commands shouldn't sys.exit directly, but give a return code.
294 # Commands shouldn't sys.exit directly, but give a return code.
288 # Just in case catch this and and pass exit code to caller.
295 # Just in case catch this and and pass exit code to caller.
289 return inst.code
296 return inst.code
290 except socket.error, inst:
297 except socket.error, inst:
291 ui.warn(_("abort: %s\n") % inst.args[-1])
298 ui.warn(_("abort: %s\n") % inst.args[-1])
292 except: # re-raises
299 except: # re-raises
293 myver = util.version()
300 myver = util.version()
294 # For compatibility checking, we discard the portion of the hg
301 # For compatibility checking, we discard the portion of the hg
295 # version after the + on the assumption that if a "normal
302 # version after the + on the assumption that if a "normal
296 # user" is running a build with a + in it the packager
303 # user" is running a build with a + in it the packager
297 # probably built from fairly close to a tag and anyone with a
304 # probably built from fairly close to a tag and anyone with a
298 # 'make local' copy of hg (where the version number can be out
305 # 'make local' copy of hg (where the version number can be out
299 # of date) will be clueful enough to notice the implausible
306 # of date) will be clueful enough to notice the implausible
300 # version number and try updating.
307 # version number and try updating.
301 compare = myver.split('+')[0]
308 compare = myver.split('+')[0]
302 ct = tuplever(compare)
309 ct = tuplever(compare)
303 worst = None, ct, ''
310 worst = None, ct, ''
304 for name, mod in extensions.extensions():
311 for name, mod in extensions.extensions():
305 testedwith = getattr(mod, 'testedwith', '')
312 testedwith = getattr(mod, 'testedwith', '')
306 report = getattr(mod, 'buglink', _('the extension author.'))
313 report = getattr(mod, 'buglink', _('the extension author.'))
307 if not testedwith.strip():
314 if not testedwith.strip():
308 # We found an untested extension. It's likely the culprit.
315 # We found an untested extension. It's likely the culprit.
309 worst = name, 'unknown', report
316 worst = name, 'unknown', report
310 break
317 break
311
318
312 # Never blame on extensions bundled with Mercurial.
319 # Never blame on extensions bundled with Mercurial.
313 if testedwith == 'internal':
320 if testedwith == 'internal':
314 continue
321 continue
315
322
316 tested = [tuplever(t) for t in testedwith.split()]
323 tested = [tuplever(t) for t in testedwith.split()]
317 if ct in tested:
324 if ct in tested:
318 continue
325 continue
319
326
320 lower = [t for t in tested if t < ct]
327 lower = [t for t in tested if t < ct]
321 nearest = max(lower or tested)
328 nearest = max(lower or tested)
322 if worst[0] is None or nearest < worst[1]:
329 if worst[0] is None or nearest < worst[1]:
323 worst = name, nearest, report
330 worst = name, nearest, report
324 if worst[0] is not None:
331 if worst[0] is not None:
325 name, testedwith, report = worst
332 name, testedwith, report = worst
326 if not isinstance(testedwith, str):
333 if not isinstance(testedwith, str):
327 testedwith = '.'.join([str(c) for c in testedwith])
334 testedwith = '.'.join([str(c) for c in testedwith])
328 warning = (_('** Unknown exception encountered with '
335 warning = (_('** Unknown exception encountered with '
329 'possibly-broken third-party extension %s\n'
336 'possibly-broken third-party extension %s\n'
330 '** which supports versions %s of Mercurial.\n'
337 '** which supports versions %s of Mercurial.\n'
331 '** Please disable %s and try your action again.\n'
338 '** Please disable %s and try your action again.\n'
332 '** If that fixes the bug please report it to %s\n')
339 '** If that fixes the bug please report it to %s\n')
333 % (name, testedwith, name, report))
340 % (name, testedwith, name, report))
334 else:
341 else:
335 warning = (_("** unknown exception encountered, "
342 warning = (_("** unknown exception encountered, "
336 "please report by visiting\n") +
343 "please report by visiting\n") +
337 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
344 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
338 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
345 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
339 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
346 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
340 (_("** Extensions loaded: %s\n") %
347 (_("** Extensions loaded: %s\n") %
341 ", ".join([x[0] for x in extensions.extensions()])))
348 ", ".join([x[0] for x in extensions.extensions()])))
342 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
349 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
343 ui.warn(warning)
350 ui.warn(warning)
344 raise
351 raise
345
352
346 return -1
353 return -1
347
354
348 def tuplever(v):
355 def tuplever(v):
349 try:
356 try:
350 # Assertion: tuplever is only used for extension compatibility
357 # Assertion: tuplever is only used for extension compatibility
351 # checking. Otherwise, the discarding of extra version fields is
358 # checking. Otherwise, the discarding of extra version fields is
352 # incorrect.
359 # incorrect.
353 return tuple([int(i) for i in v.split('.')[0:2]])
360 return tuple([int(i) for i in v.split('.')[0:2]])
354 except ValueError:
361 except ValueError:
355 return tuple()
362 return tuple()
356
363
357 def aliasargs(fn, givenargs):
364 def aliasargs(fn, givenargs):
358 args = getattr(fn, 'args', [])
365 args = getattr(fn, 'args', [])
359 if args:
366 if args:
360 cmd = ' '.join(map(util.shellquote, args))
367 cmd = ' '.join(map(util.shellquote, args))
361
368
362 nums = []
369 nums = []
363 def replacer(m):
370 def replacer(m):
364 num = int(m.group(1)) - 1
371 num = int(m.group(1)) - 1
365 nums.append(num)
372 nums.append(num)
366 if num < len(givenargs):
373 if num < len(givenargs):
367 return givenargs[num]
374 return givenargs[num]
368 raise util.Abort(_('too few arguments for command alias'))
375 raise util.Abort(_('too few arguments for command alias'))
369 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
376 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
370 givenargs = [x for i, x in enumerate(givenargs)
377 givenargs = [x for i, x in enumerate(givenargs)
371 if i not in nums]
378 if i not in nums]
372 args = shlex.split(cmd)
379 args = shlex.split(cmd)
373 return args + givenargs
380 return args + givenargs
374
381
375 def aliasinterpolate(name, args, cmd):
382 def aliasinterpolate(name, args, cmd):
376 '''interpolate args into cmd for shell aliases
383 '''interpolate args into cmd for shell aliases
377
384
378 This also handles $0, $@ and "$@".
385 This also handles $0, $@ and "$@".
379 '''
386 '''
380 # util.interpolate can't deal with "$@" (with quotes) because it's only
387 # util.interpolate can't deal with "$@" (with quotes) because it's only
381 # built to match prefix + patterns.
388 # built to match prefix + patterns.
382 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
389 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
383 replacemap['$0'] = name
390 replacemap['$0'] = name
384 replacemap['$$'] = '$'
391 replacemap['$$'] = '$'
385 replacemap['$@'] = ' '.join(args)
392 replacemap['$@'] = ' '.join(args)
386 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
393 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
387 # parameters, separated out into words. Emulate the same behavior here by
394 # parameters, separated out into words. Emulate the same behavior here by
388 # quoting the arguments individually. POSIX shells will then typically
395 # quoting the arguments individually. POSIX shells will then typically
389 # tokenize each argument into exactly one word.
396 # tokenize each argument into exactly one word.
390 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
397 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
391 # escape '\$' for regex
398 # escape '\$' for regex
392 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
399 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
393 r = re.compile(regex)
400 r = re.compile(regex)
394 return r.sub(lambda x: replacemap[x.group()], cmd)
401 return r.sub(lambda x: replacemap[x.group()], cmd)
395
402
396 class cmdalias(object):
403 class cmdalias(object):
397 def __init__(self, name, definition, cmdtable):
404 def __init__(self, name, definition, cmdtable):
398 self.name = self.cmd = name
405 self.name = self.cmd = name
399 self.cmdname = ''
406 self.cmdname = ''
400 self.definition = definition
407 self.definition = definition
401 self.fn = None
408 self.fn = None
402 self.args = []
409 self.args = []
403 self.opts = []
410 self.opts = []
404 self.help = ''
411 self.help = ''
405 self.norepo = True
412 self.norepo = True
406 self.optionalrepo = False
413 self.optionalrepo = False
407 self.badalias = None
414 self.badalias = None
408 self.unknowncmd = False
415 self.unknowncmd = False
409
416
410 try:
417 try:
411 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
418 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
412 for alias, e in cmdtable.iteritems():
419 for alias, e in cmdtable.iteritems():
413 if e is entry:
420 if e is entry:
414 self.cmd = alias
421 self.cmd = alias
415 break
422 break
416 self.shadows = True
423 self.shadows = True
417 except error.UnknownCommand:
424 except error.UnknownCommand:
418 self.shadows = False
425 self.shadows = False
419
426
420 if not self.definition:
427 if not self.definition:
421 self.badalias = _("no definition for alias '%s'") % self.name
428 self.badalias = _("no definition for alias '%s'") % self.name
422 return
429 return
423
430
424 if self.definition.startswith('!'):
431 if self.definition.startswith('!'):
425 self.shell = True
432 self.shell = True
426 def fn(ui, *args):
433 def fn(ui, *args):
427 env = {'HG_ARGS': ' '.join((self.name,) + args)}
434 env = {'HG_ARGS': ' '.join((self.name,) + args)}
428 def _checkvar(m):
435 def _checkvar(m):
429 if m.groups()[0] == '$':
436 if m.groups()[0] == '$':
430 return m.group()
437 return m.group()
431 elif int(m.groups()[0]) <= len(args):
438 elif int(m.groups()[0]) <= len(args):
432 return m.group()
439 return m.group()
433 else:
440 else:
434 ui.debug("No argument found for substitution "
441 ui.debug("No argument found for substitution "
435 "of %i variable in alias '%s' definition."
442 "of %i variable in alias '%s' definition."
436 % (int(m.groups()[0]), self.name))
443 % (int(m.groups()[0]), self.name))
437 return ''
444 return ''
438 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
445 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
439 cmd = aliasinterpolate(self.name, args, cmd)
446 cmd = aliasinterpolate(self.name, args, cmd)
440 return ui.system(cmd, environ=env)
447 return ui.system(cmd, environ=env)
441 self.fn = fn
448 self.fn = fn
442 return
449 return
443
450
444 try:
451 try:
445 args = shlex.split(self.definition)
452 args = shlex.split(self.definition)
446 except ValueError, inst:
453 except ValueError, inst:
447 self.badalias = (_("error in definition for alias '%s': %s")
454 self.badalias = (_("error in definition for alias '%s': %s")
448 % (self.name, inst))
455 % (self.name, inst))
449 return
456 return
450 self.cmdname = cmd = args.pop(0)
457 self.cmdname = cmd = args.pop(0)
451 args = map(util.expandpath, args)
458 args = map(util.expandpath, args)
452
459
453 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
460 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
454 if _earlygetopt([invalidarg], args):
461 if _earlygetopt([invalidarg], args):
455 self.badalias = (_("error in definition for alias '%s': %s may "
462 self.badalias = (_("error in definition for alias '%s': %s may "
456 "only be given on the command line")
463 "only be given on the command line")
457 % (self.name, invalidarg))
464 % (self.name, invalidarg))
458 return
465 return
459
466
460 try:
467 try:
461 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
468 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
462 if len(tableentry) > 2:
469 if len(tableentry) > 2:
463 self.fn, self.opts, self.help = tableentry
470 self.fn, self.opts, self.help = tableentry
464 else:
471 else:
465 self.fn, self.opts = tableentry
472 self.fn, self.opts = tableentry
466
473
467 self.args = aliasargs(self.fn, args)
474 self.args = aliasargs(self.fn, args)
468 if cmd not in commands.norepo.split(' '):
475 if cmd not in commands.norepo.split(' '):
469 self.norepo = False
476 self.norepo = False
470 if cmd in commands.optionalrepo.split(' '):
477 if cmd in commands.optionalrepo.split(' '):
471 self.optionalrepo = True
478 self.optionalrepo = True
472 if self.help.startswith("hg " + cmd):
479 if self.help.startswith("hg " + cmd):
473 # drop prefix in old-style help lines so hg shows the alias
480 # drop prefix in old-style help lines so hg shows the alias
474 self.help = self.help[4 + len(cmd):]
481 self.help = self.help[4 + len(cmd):]
475 self.__doc__ = self.fn.__doc__
482 self.__doc__ = self.fn.__doc__
476
483
477 except error.UnknownCommand:
484 except error.UnknownCommand:
478 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
485 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
479 % (self.name, cmd))
486 % (self.name, cmd))
480 self.unknowncmd = True
487 self.unknowncmd = True
481 except error.AmbiguousCommand:
488 except error.AmbiguousCommand:
482 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
489 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
483 % (self.name, cmd))
490 % (self.name, cmd))
484
491
485 def __call__(self, ui, *args, **opts):
492 def __call__(self, ui, *args, **opts):
486 if self.badalias:
493 if self.badalias:
487 hint = None
494 hint = None
488 if self.unknowncmd:
495 if self.unknowncmd:
489 try:
496 try:
490 # check if the command is in a disabled extension
497 # check if the command is in a disabled extension
491 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
498 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
492 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
499 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
493 except error.UnknownCommand:
500 except error.UnknownCommand:
494 pass
501 pass
495 raise util.Abort(self.badalias, hint=hint)
502 raise util.Abort(self.badalias, hint=hint)
496 if self.shadows:
503 if self.shadows:
497 ui.debug("alias '%s' shadows command '%s'\n" %
504 ui.debug("alias '%s' shadows command '%s'\n" %
498 (self.name, self.cmdname))
505 (self.name, self.cmdname))
499
506
500 if util.safehasattr(self, 'shell'):
507 if util.safehasattr(self, 'shell'):
501 return self.fn(ui, *args, **opts)
508 return self.fn(ui, *args, **opts)
502 else:
509 else:
503 try:
510 try:
504 return util.checksignature(self.fn)(ui, *args, **opts)
511 return util.checksignature(self.fn)(ui, *args, **opts)
505 except error.SignatureError:
512 except error.SignatureError:
506 args = ' '.join([self.cmdname] + self.args)
513 args = ' '.join([self.cmdname] + self.args)
507 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
514 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
508 raise
515 raise
509
516
510 def addaliases(ui, cmdtable):
517 def addaliases(ui, cmdtable):
511 # aliases are processed after extensions have been loaded, so they
518 # aliases are processed after extensions have been loaded, so they
512 # may use extension commands. Aliases can also use other alias definitions,
519 # may use extension commands. Aliases can also use other alias definitions,
513 # but only if they have been defined prior to the current definition.
520 # but only if they have been defined prior to the current definition.
514 for alias, definition in ui.configitems('alias'):
521 for alias, definition in ui.configitems('alias'):
515 aliasdef = cmdalias(alias, definition, cmdtable)
522 aliasdef = cmdalias(alias, definition, cmdtable)
516
523
517 try:
524 try:
518 olddef = cmdtable[aliasdef.cmd][0]
525 olddef = cmdtable[aliasdef.cmd][0]
519 if olddef.definition == aliasdef.definition:
526 if olddef.definition == aliasdef.definition:
520 continue
527 continue
521 except (KeyError, AttributeError):
528 except (KeyError, AttributeError):
522 # definition might not exist or it might not be a cmdalias
529 # definition might not exist or it might not be a cmdalias
523 pass
530 pass
524
531
525 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
532 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
526 if aliasdef.norepo:
533 if aliasdef.norepo:
527 commands.norepo += ' %s' % alias
534 commands.norepo += ' %s' % alias
528 if aliasdef.optionalrepo:
535 if aliasdef.optionalrepo:
529 commands.optionalrepo += ' %s' % alias
536 commands.optionalrepo += ' %s' % alias
530
537
531 def _parse(ui, args):
538 def _parse(ui, args):
532 options = {}
539 options = {}
533 cmdoptions = {}
540 cmdoptions = {}
534
541
535 try:
542 try:
536 args = fancyopts.fancyopts(args, commands.globalopts, options)
543 args = fancyopts.fancyopts(args, commands.globalopts, options)
537 except fancyopts.getopt.GetoptError, inst:
544 except fancyopts.getopt.GetoptError, inst:
538 raise error.CommandError(None, inst)
545 raise error.CommandError(None, inst)
539
546
540 if args:
547 if args:
541 cmd, args = args[0], args[1:]
548 cmd, args = args[0], args[1:]
542 aliases, entry = cmdutil.findcmd(cmd, commands.table,
549 aliases, entry = cmdutil.findcmd(cmd, commands.table,
543 ui.configbool("ui", "strict"))
550 ui.configbool("ui", "strict"))
544 cmd = aliases[0]
551 cmd = aliases[0]
545 args = aliasargs(entry[0], args)
552 args = aliasargs(entry[0], args)
546 defaults = ui.config("defaults", cmd)
553 defaults = ui.config("defaults", cmd)
547 if defaults:
554 if defaults:
548 args = map(util.expandpath, shlex.split(defaults)) + args
555 args = map(util.expandpath, shlex.split(defaults)) + args
549 c = list(entry[1])
556 c = list(entry[1])
550 else:
557 else:
551 cmd = None
558 cmd = None
552 c = []
559 c = []
553
560
554 # combine global options into local
561 # combine global options into local
555 for o in commands.globalopts:
562 for o in commands.globalopts:
556 c.append((o[0], o[1], options[o[1]], o[3]))
563 c.append((o[0], o[1], options[o[1]], o[3]))
557
564
558 try:
565 try:
559 args = fancyopts.fancyopts(args, c, cmdoptions, True)
566 args = fancyopts.fancyopts(args, c, cmdoptions, True)
560 except fancyopts.getopt.GetoptError, inst:
567 except fancyopts.getopt.GetoptError, inst:
561 raise error.CommandError(cmd, inst)
568 raise error.CommandError(cmd, inst)
562
569
563 # separate global options back out
570 # separate global options back out
564 for o in commands.globalopts:
571 for o in commands.globalopts:
565 n = o[1]
572 n = o[1]
566 options[n] = cmdoptions[n]
573 options[n] = cmdoptions[n]
567 del cmdoptions[n]
574 del cmdoptions[n]
568
575
569 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
576 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
570
577
571 def _parseconfig(ui, config):
578 def _parseconfig(ui, config):
572 """parse the --config options from the command line"""
579 """parse the --config options from the command line"""
573 configs = []
580 configs = []
574
581
575 for cfg in config:
582 for cfg in config:
576 try:
583 try:
577 name, value = cfg.split('=', 1)
584 name, value = cfg.split('=', 1)
578 section, name = name.split('.', 1)
585 section, name = name.split('.', 1)
579 if not section or not name:
586 if not section or not name:
580 raise IndexError
587 raise IndexError
581 ui.setconfig(section, name, value, '--config')
588 ui.setconfig(section, name, value, '--config')
582 configs.append((section, name, value))
589 configs.append((section, name, value))
583 except (IndexError, ValueError):
590 except (IndexError, ValueError):
584 raise util.Abort(_('malformed --config option: %r '
591 raise util.Abort(_('malformed --config option: %r '
585 '(use --config section.name=value)') % cfg)
592 '(use --config section.name=value)') % cfg)
586
593
587 return configs
594 return configs
588
595
589 def _earlygetopt(aliases, args):
596 def _earlygetopt(aliases, args):
590 """Return list of values for an option (or aliases).
597 """Return list of values for an option (or aliases).
591
598
592 The values are listed in the order they appear in args.
599 The values are listed in the order they appear in args.
593 The options and values are removed from args.
600 The options and values are removed from args.
594
601
595 >>> args = ['x', '--cwd', 'foo', 'y']
602 >>> args = ['x', '--cwd', 'foo', 'y']
596 >>> _earlygetopt(['--cwd'], args), args
603 >>> _earlygetopt(['--cwd'], args), args
597 (['foo'], ['x', 'y'])
604 (['foo'], ['x', 'y'])
598
605
599 >>> args = ['x', '--cwd=bar', 'y']
606 >>> args = ['x', '--cwd=bar', 'y']
600 >>> _earlygetopt(['--cwd'], args), args
607 >>> _earlygetopt(['--cwd'], args), args
601 (['bar'], ['x', 'y'])
608 (['bar'], ['x', 'y'])
602
609
603 >>> args = ['x', '-R', 'foo', 'y']
610 >>> args = ['x', '-R', 'foo', 'y']
604 >>> _earlygetopt(['-R'], args), args
611 >>> _earlygetopt(['-R'], args), args
605 (['foo'], ['x', 'y'])
612 (['foo'], ['x', 'y'])
606
613
607 >>> args = ['x', '-Rbar', 'y']
614 >>> args = ['x', '-Rbar', 'y']
608 >>> _earlygetopt(['-R'], args), args
615 >>> _earlygetopt(['-R'], args), args
609 (['bar'], ['x', 'y'])
616 (['bar'], ['x', 'y'])
610 """
617 """
611 try:
618 try:
612 argcount = args.index("--")
619 argcount = args.index("--")
613 except ValueError:
620 except ValueError:
614 argcount = len(args)
621 argcount = len(args)
615 shortopts = [opt for opt in aliases if len(opt) == 2]
622 shortopts = [opt for opt in aliases if len(opt) == 2]
616 values = []
623 values = []
617 pos = 0
624 pos = 0
618 while pos < argcount:
625 while pos < argcount:
619 fullarg = arg = args[pos]
626 fullarg = arg = args[pos]
620 equals = arg.find('=')
627 equals = arg.find('=')
621 if equals > -1:
628 if equals > -1:
622 arg = arg[:equals]
629 arg = arg[:equals]
623 if arg in aliases:
630 if arg in aliases:
624 del args[pos]
631 del args[pos]
625 if equals > -1:
632 if equals > -1:
626 values.append(fullarg[equals + 1:])
633 values.append(fullarg[equals + 1:])
627 argcount -= 1
634 argcount -= 1
628 else:
635 else:
629 if pos + 1 >= argcount:
636 if pos + 1 >= argcount:
630 # ignore and let getopt report an error if there is no value
637 # ignore and let getopt report an error if there is no value
631 break
638 break
632 values.append(args.pop(pos))
639 values.append(args.pop(pos))
633 argcount -= 2
640 argcount -= 2
634 elif arg[:2] in shortopts:
641 elif arg[:2] in shortopts:
635 # short option can have no following space, e.g. hg log -Rfoo
642 # short option can have no following space, e.g. hg log -Rfoo
636 values.append(args.pop(pos)[2:])
643 values.append(args.pop(pos)[2:])
637 argcount -= 1
644 argcount -= 1
638 else:
645 else:
639 pos += 1
646 pos += 1
640 return values
647 return values
641
648
642 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
649 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
643 # run pre-hook, and abort if it fails
650 # run pre-hook, and abort if it fails
644 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
651 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
645 pats=cmdpats, opts=cmdoptions)
652 pats=cmdpats, opts=cmdoptions)
646 ret = _runcommand(ui, options, cmd, d)
653 ret = _runcommand(ui, options, cmd, d)
647 # run post-hook, passing command result
654 # run post-hook, passing command result
648 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
655 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
649 result=ret, pats=cmdpats, opts=cmdoptions)
656 result=ret, pats=cmdpats, opts=cmdoptions)
650 return ret
657 return ret
651
658
652 def _getlocal(ui, rpath):
659 def _getlocal(ui, rpath):
653 """Return (path, local ui object) for the given target path.
660 """Return (path, local ui object) for the given target path.
654
661
655 Takes paths in [cwd]/.hg/hgrc into account."
662 Takes paths in [cwd]/.hg/hgrc into account."
656 """
663 """
657 try:
664 try:
658 wd = os.getcwd()
665 wd = os.getcwd()
659 except OSError, e:
666 except OSError, e:
660 raise util.Abort(_("error getting current working directory: %s") %
667 raise util.Abort(_("error getting current working directory: %s") %
661 e.strerror)
668 e.strerror)
662 path = cmdutil.findrepo(wd) or ""
669 path = cmdutil.findrepo(wd) or ""
663 if not path:
670 if not path:
664 lui = ui
671 lui = ui
665 else:
672 else:
666 lui = ui.copy()
673 lui = ui.copy()
667 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
674 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
668
675
669 if rpath and rpath[-1]:
676 if rpath and rpath[-1]:
670 path = lui.expandpath(rpath[-1])
677 path = lui.expandpath(rpath[-1])
671 lui = ui.copy()
678 lui = ui.copy()
672 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
679 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
673
680
674 return path, lui
681 return path, lui
675
682
676 def _checkshellalias(lui, ui, args, precheck=True):
683 def _checkshellalias(lui, ui, args, precheck=True):
677 """Return the function to run the shell alias, if it is required
684 """Return the function to run the shell alias, if it is required
678
685
679 'precheck' is whether this function is invoked before adding
686 'precheck' is whether this function is invoked before adding
680 aliases or not.
687 aliases or not.
681 """
688 """
682 options = {}
689 options = {}
683
690
684 try:
691 try:
685 args = fancyopts.fancyopts(args, commands.globalopts, options)
692 args = fancyopts.fancyopts(args, commands.globalopts, options)
686 except fancyopts.getopt.GetoptError:
693 except fancyopts.getopt.GetoptError:
687 return
694 return
688
695
689 if not args:
696 if not args:
690 return
697 return
691
698
692 if precheck:
699 if precheck:
693 strict = True
700 strict = True
694 norepo = commands.norepo
701 norepo = commands.norepo
695 optionalrepo = commands.optionalrepo
702 optionalrepo = commands.optionalrepo
696 def restorecommands():
703 def restorecommands():
697 commands.norepo = norepo
704 commands.norepo = norepo
698 commands.optionalrepo = optionalrepo
705 commands.optionalrepo = optionalrepo
699 cmdtable = commands.table.copy()
706 cmdtable = commands.table.copy()
700 addaliases(lui, cmdtable)
707 addaliases(lui, cmdtable)
701 else:
708 else:
702 strict = False
709 strict = False
703 def restorecommands():
710 def restorecommands():
704 pass
711 pass
705 cmdtable = commands.table
712 cmdtable = commands.table
706
713
707 cmd = args[0]
714 cmd = args[0]
708 try:
715 try:
709 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
716 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
710 except (error.AmbiguousCommand, error.UnknownCommand):
717 except (error.AmbiguousCommand, error.UnknownCommand):
711 restorecommands()
718 restorecommands()
712 return
719 return
713
720
714 cmd = aliases[0]
721 cmd = aliases[0]
715 fn = entry[0]
722 fn = entry[0]
716
723
717 if cmd and util.safehasattr(fn, 'shell'):
724 if cmd and util.safehasattr(fn, 'shell'):
718 d = lambda: fn(ui, *args[1:])
725 d = lambda: fn(ui, *args[1:])
719 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
726 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
720 [], {})
727 [], {})
721
728
722 restorecommands()
729 restorecommands()
723
730
724 _loaded = set()
731 _loaded = set()
725 def _dispatch(req):
732 def _dispatch(req):
726 args = req.args
733 args = req.args
727 ui = req.ui
734 ui = req.ui
728
735
729 # check for cwd
736 # check for cwd
730 cwd = _earlygetopt(['--cwd'], args)
737 cwd = _earlygetopt(['--cwd'], args)
731 if cwd:
738 if cwd:
732 os.chdir(cwd[-1])
739 os.chdir(cwd[-1])
733
740
734 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
741 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
735 path, lui = _getlocal(ui, rpath)
742 path, lui = _getlocal(ui, rpath)
736
743
737 # Now that we're operating in the right directory/repository with
744 # Now that we're operating in the right directory/repository with
738 # the right config settings, check for shell aliases
745 # the right config settings, check for shell aliases
739 shellaliasfn = _checkshellalias(lui, ui, args)
746 shellaliasfn = _checkshellalias(lui, ui, args)
740 if shellaliasfn:
747 if shellaliasfn:
741 return shellaliasfn()
748 return shellaliasfn()
742
749
743 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
750 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
744 # reposetup. Programs like TortoiseHg will call _dispatch several
751 # reposetup. Programs like TortoiseHg will call _dispatch several
745 # times so we keep track of configured extensions in _loaded.
752 # times so we keep track of configured extensions in _loaded.
746 extensions.loadall(lui)
753 extensions.loadall(lui)
747 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
754 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
748 # Propagate any changes to lui.__class__ by extensions
755 # Propagate any changes to lui.__class__ by extensions
749 ui.__class__ = lui.__class__
756 ui.__class__ = lui.__class__
750
757
751 # (uisetup and extsetup are handled in extensions.loadall)
758 # (uisetup and extsetup are handled in extensions.loadall)
752
759
753 for name, module in exts:
760 for name, module in exts:
754 cmdtable = getattr(module, 'cmdtable', {})
761 cmdtable = getattr(module, 'cmdtable', {})
755 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
762 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
756 if overrides:
763 if overrides:
757 ui.warn(_("extension '%s' overrides commands: %s\n")
764 ui.warn(_("extension '%s' overrides commands: %s\n")
758 % (name, " ".join(overrides)))
765 % (name, " ".join(overrides)))
759 commands.table.update(cmdtable)
766 commands.table.update(cmdtable)
760 _loaded.add(name)
767 _loaded.add(name)
761
768
762 # (reposetup is handled in hg.repository)
769 # (reposetup is handled in hg.repository)
763
770
764 addaliases(lui, commands.table)
771 addaliases(lui, commands.table)
765
772
766 if not lui.configbool("ui", "strict"):
773 if not lui.configbool("ui", "strict"):
767 # All aliases and commands are completely defined, now.
774 # All aliases and commands are completely defined, now.
768 # Check abbreviation/ambiguity of shell alias again, because shell
775 # Check abbreviation/ambiguity of shell alias again, because shell
769 # alias may cause failure of "_parse" (see issue4355)
776 # alias may cause failure of "_parse" (see issue4355)
770 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
777 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
771 if shellaliasfn:
778 if shellaliasfn:
772 return shellaliasfn()
779 return shellaliasfn()
773
780
774 # check for fallback encoding
781 # check for fallback encoding
775 fallback = lui.config('ui', 'fallbackencoding')
782 fallback = lui.config('ui', 'fallbackencoding')
776 if fallback:
783 if fallback:
777 encoding.fallbackencoding = fallback
784 encoding.fallbackencoding = fallback
778
785
779 fullargs = args
786 fullargs = args
780 cmd, func, args, options, cmdoptions = _parse(lui, args)
787 cmd, func, args, options, cmdoptions = _parse(lui, args)
781
788
782 if options["config"]:
789 if options["config"]:
783 raise util.Abort(_("option --config may not be abbreviated!"))
790 raise util.Abort(_("option --config may not be abbreviated!"))
784 if options["cwd"]:
791 if options["cwd"]:
785 raise util.Abort(_("option --cwd may not be abbreviated!"))
792 raise util.Abort(_("option --cwd may not be abbreviated!"))
786 if options["repository"]:
793 if options["repository"]:
787 raise util.Abort(_(
794 raise util.Abort(_(
788 "option -R has to be separated from other options (e.g. not -qR) "
795 "option -R has to be separated from other options (e.g. not -qR) "
789 "and --repository may only be abbreviated as --repo!"))
796 "and --repository may only be abbreviated as --repo!"))
790
797
791 if options["encoding"]:
798 if options["encoding"]:
792 encoding.encoding = options["encoding"]
799 encoding.encoding = options["encoding"]
793 if options["encodingmode"]:
800 if options["encodingmode"]:
794 encoding.encodingmode = options["encodingmode"]
801 encoding.encodingmode = options["encodingmode"]
795 if options["time"]:
802 if options["time"]:
796 def get_times():
803 def get_times():
797 t = os.times()
804 t = os.times()
798 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
805 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
799 t = (t[0], t[1], t[2], t[3], time.clock())
806 t = (t[0], t[1], t[2], t[3], time.clock())
800 return t
807 return t
801 s = get_times()
808 s = get_times()
802 def print_time():
809 def print_time():
803 t = get_times()
810 t = get_times()
804 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
811 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
805 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
812 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
806 atexit.register(print_time)
813 atexit.register(print_time)
807
814
808 uis = set([ui, lui])
815 uis = set([ui, lui])
809
816
810 if req.repo:
817 if req.repo:
811 uis.add(req.repo.ui)
818 uis.add(req.repo.ui)
812
819
813 if options['verbose'] or options['debug'] or options['quiet']:
820 if options['verbose'] or options['debug'] or options['quiet']:
814 for opt in ('verbose', 'debug', 'quiet'):
821 for opt in ('verbose', 'debug', 'quiet'):
815 val = str(bool(options[opt]))
822 val = str(bool(options[opt]))
816 for ui_ in uis:
823 for ui_ in uis:
817 ui_.setconfig('ui', opt, val, '--' + opt)
824 ui_.setconfig('ui', opt, val, '--' + opt)
818
825
819 if options['traceback']:
826 if options['traceback']:
820 for ui_ in uis:
827 for ui_ in uis:
821 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
828 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
822
829
823 if options['noninteractive']:
830 if options['noninteractive']:
824 for ui_ in uis:
831 for ui_ in uis:
825 ui_.setconfig('ui', 'interactive', 'off', '-y')
832 ui_.setconfig('ui', 'interactive', 'off', '-y')
826
833
827 if cmdoptions.get('insecure', False):
834 if cmdoptions.get('insecure', False):
828 for ui_ in uis:
835 for ui_ in uis:
829 ui_.setconfig('web', 'cacerts', '!', '--insecure')
836 ui_.setconfig('web', 'cacerts', '!', '--insecure')
830
837
831 if options['version']:
838 if options['version']:
832 return commands.version_(ui)
839 return commands.version_(ui)
833 if options['help']:
840 if options['help']:
834 return commands.help_(ui, cmd, command=True)
841 return commands.help_(ui, cmd, command=True)
835 elif not cmd:
842 elif not cmd:
836 return commands.help_(ui, 'shortlist')
843 return commands.help_(ui, 'shortlist')
837
844
838 repo = None
845 repo = None
839 cmdpats = args[:]
846 cmdpats = args[:]
840 if cmd not in commands.norepo.split():
847 if cmd not in commands.norepo.split():
841 # use the repo from the request only if we don't have -R
848 # use the repo from the request only if we don't have -R
842 if not rpath and not cwd:
849 if not rpath and not cwd:
843 repo = req.repo
850 repo = req.repo
844
851
845 if repo:
852 if repo:
846 # set the descriptors of the repo ui to those of ui
853 # set the descriptors of the repo ui to those of ui
847 repo.ui.fin = ui.fin
854 repo.ui.fin = ui.fin
848 repo.ui.fout = ui.fout
855 repo.ui.fout = ui.fout
849 repo.ui.ferr = ui.ferr
856 repo.ui.ferr = ui.ferr
850 else:
857 else:
851 try:
858 try:
852 repo = hg.repository(ui, path=path)
859 repo = hg.repository(ui, path=path)
853 if not repo.local():
860 if not repo.local():
854 raise util.Abort(_("repository '%s' is not local") % path)
861 raise util.Abort(_("repository '%s' is not local") % path)
855 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
862 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
856 except error.RequirementError:
863 except error.RequirementError:
857 raise
864 raise
858 except error.RepoError:
865 except error.RepoError:
859 if cmd not in commands.optionalrepo.split():
866 if cmd not in commands.optionalrepo.split():
860 if (cmd in commands.inferrepo.split() and
867 if (cmd in commands.inferrepo.split() and
861 args and not path): # try to infer -R from command args
868 args and not path): # try to infer -R from command args
862 repos = map(cmdutil.findrepo, args)
869 repos = map(cmdutil.findrepo, args)
863 guess = repos[0]
870 guess = repos[0]
864 if guess and repos.count(guess) == len(repos):
871 if guess and repos.count(guess) == len(repos):
865 req.args = ['--repository', guess] + fullargs
872 req.args = ['--repository', guess] + fullargs
866 return _dispatch(req)
873 return _dispatch(req)
867 if not path:
874 if not path:
868 raise error.RepoError(_("no repository found in '%s'"
875 raise error.RepoError(_("no repository found in '%s'"
869 " (.hg not found)")
876 " (.hg not found)")
870 % os.getcwd())
877 % os.getcwd())
871 raise
878 raise
872 if repo:
879 if repo:
873 ui = repo.ui
880 ui = repo.ui
874 if options['hidden']:
881 if options['hidden']:
875 repo = repo.unfiltered()
882 repo = repo.unfiltered()
876 args.insert(0, repo)
883 args.insert(0, repo)
877 elif rpath:
884 elif rpath:
878 ui.warn(_("warning: --repository ignored\n"))
885 ui.warn(_("warning: --repository ignored\n"))
879
886
880 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
887 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
881 ui.log("command", '%s\n', msg)
888 ui.log("command", '%s\n', msg)
882 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
889 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
883 try:
890 try:
884 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
891 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
885 cmdpats, cmdoptions)
892 cmdpats, cmdoptions)
886 finally:
893 finally:
887 if repo and repo != req.repo:
894 if repo and repo != req.repo:
888 repo.close()
895 repo.close()
889
896
890 def lsprofile(ui, func, fp):
897 def lsprofile(ui, func, fp):
891 format = ui.config('profiling', 'format', default='text')
898 format = ui.config('profiling', 'format', default='text')
892 field = ui.config('profiling', 'sort', default='inlinetime')
899 field = ui.config('profiling', 'sort', default='inlinetime')
893 limit = ui.configint('profiling', 'limit', default=30)
900 limit = ui.configint('profiling', 'limit', default=30)
894 climit = ui.configint('profiling', 'nested', default=5)
901 climit = ui.configint('profiling', 'nested', default=5)
895
902
896 if format not in ['text', 'kcachegrind']:
903 if format not in ['text', 'kcachegrind']:
897 ui.warn(_("unrecognized profiling format '%s'"
904 ui.warn(_("unrecognized profiling format '%s'"
898 " - Ignored\n") % format)
905 " - Ignored\n") % format)
899 format = 'text'
906 format = 'text'
900
907
901 try:
908 try:
902 from mercurial import lsprof
909 from mercurial import lsprof
903 except ImportError:
910 except ImportError:
904 raise util.Abort(_(
911 raise util.Abort(_(
905 'lsprof not available - install from '
912 'lsprof not available - install from '
906 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
913 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
907 p = lsprof.Profiler()
914 p = lsprof.Profiler()
908 p.enable(subcalls=True)
915 p.enable(subcalls=True)
909 try:
916 try:
910 return func()
917 return func()
911 finally:
918 finally:
912 p.disable()
919 p.disable()
913
920
914 if format == 'kcachegrind':
921 if format == 'kcachegrind':
915 import lsprofcalltree
922 import lsprofcalltree
916 calltree = lsprofcalltree.KCacheGrind(p)
923 calltree = lsprofcalltree.KCacheGrind(p)
917 calltree.output(fp)
924 calltree.output(fp)
918 else:
925 else:
919 # format == 'text'
926 # format == 'text'
920 stats = lsprof.Stats(p.getstats())
927 stats = lsprof.Stats(p.getstats())
921 stats.sort(field)
928 stats.sort(field)
922 stats.pprint(limit=limit, file=fp, climit=climit)
929 stats.pprint(limit=limit, file=fp, climit=climit)
923
930
924 def flameprofile(ui, func, fp):
931 def flameprofile(ui, func, fp):
925 try:
932 try:
926 from flamegraph import flamegraph
933 from flamegraph import flamegraph
927 except ImportError:
934 except ImportError:
928 raise util.Abort(_(
935 raise util.Abort(_(
929 'flamegraph not available - install from '
936 'flamegraph not available - install from '
930 'https://github.com/evanhempel/python-flamegraph'))
937 'https://github.com/evanhempel/python-flamegraph'))
931 freq = ui.configint('profiling', 'freq', default=1000)
938 freq = ui.configint('profiling', 'freq', default=1000)
932 filter_ = None
939 filter_ = None
933 collapse_recursion = True
940 collapse_recursion = True
934 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
941 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
935 filter_, collapse_recursion)
942 filter_, collapse_recursion)
936 start_time = time.clock()
943 start_time = time.clock()
937 try:
944 try:
938 thread.start()
945 thread.start()
939 func()
946 func()
940 finally:
947 finally:
941 thread.stop()
948 thread.stop()
942 thread.join()
949 thread.join()
943 print 'Collected %d stack frames (%d unique) in %2.2f seconds.' % (
950 print 'Collected %d stack frames (%d unique) in %2.2f seconds.' % (
944 time.clock() - start_time, thread.num_frames(),
951 time.clock() - start_time, thread.num_frames(),
945 thread.num_frames(unique=True))
952 thread.num_frames(unique=True))
946
953
947
954
948 def statprofile(ui, func, fp):
955 def statprofile(ui, func, fp):
949 try:
956 try:
950 import statprof
957 import statprof
951 except ImportError:
958 except ImportError:
952 raise util.Abort(_(
959 raise util.Abort(_(
953 'statprof not available - install using "easy_install statprof"'))
960 'statprof not available - install using "easy_install statprof"'))
954
961
955 freq = ui.configint('profiling', 'freq', default=1000)
962 freq = ui.configint('profiling', 'freq', default=1000)
956 if freq > 0:
963 if freq > 0:
957 statprof.reset(freq)
964 statprof.reset(freq)
958 else:
965 else:
959 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
966 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
960
967
961 statprof.start()
968 statprof.start()
962 try:
969 try:
963 return func()
970 return func()
964 finally:
971 finally:
965 statprof.stop()
972 statprof.stop()
966 statprof.display(fp)
973 statprof.display(fp)
967
974
968 def _runcommand(ui, options, cmd, cmdfunc):
975 def _runcommand(ui, options, cmd, cmdfunc):
969 def checkargs():
976 def checkargs():
970 try:
977 try:
971 return cmdfunc()
978 return cmdfunc()
972 except error.SignatureError:
979 except error.SignatureError:
973 raise error.CommandError(cmd, _("invalid arguments"))
980 raise error.CommandError(cmd, _("invalid arguments"))
974
981
975 if options['profile']:
982 if options['profile']:
976 profiler = os.getenv('HGPROF')
983 profiler = os.getenv('HGPROF')
977 if profiler is None:
984 if profiler is None:
978 profiler = ui.config('profiling', 'type', default='ls')
985 profiler = ui.config('profiling', 'type', default='ls')
979 if profiler not in ('ls', 'stat', 'flame'):
986 if profiler not in ('ls', 'stat', 'flame'):
980 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
987 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
981 profiler = 'ls'
988 profiler = 'ls'
982
989
983 output = ui.config('profiling', 'output')
990 output = ui.config('profiling', 'output')
984
991
985 if output:
992 if output:
986 path = ui.expandpath(output)
993 path = ui.expandpath(output)
987 fp = open(path, 'wb')
994 fp = open(path, 'wb')
988 else:
995 else:
989 fp = sys.stderr
996 fp = sys.stderr
990
997
991 try:
998 try:
992 if profiler == 'ls':
999 if profiler == 'ls':
993 return lsprofile(ui, checkargs, fp)
1000 return lsprofile(ui, checkargs, fp)
994 elif profiler == 'flame':
1001 elif profiler == 'flame':
995 return flameprofile(ui, checkargs, fp)
1002 return flameprofile(ui, checkargs, fp)
996 else:
1003 else:
997 return statprofile(ui, checkargs, fp)
1004 return statprofile(ui, checkargs, fp)
998 finally:
1005 finally:
999 if output:
1006 if output:
1000 fp.close()
1007 fp.close()
1001 else:
1008 else:
1002 return checkargs()
1009 return checkargs()
@@ -1,166 +1,170 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):
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 self.index = index
25 self.index = index
26 # this can't be called 'message' because at least some installs of
26 # this can't be called 'message' because at least some installs of
27 # Python 2.6+ complain about the 'message' property being deprecated
27 # Python 2.6+ complain about the 'message' property being deprecated
28 self.lookupmessage = message
28 self.lookupmessage = message
29 if isinstance(name, str) and len(name) == 20:
29 if isinstance(name, str) and len(name) == 20:
30 from node import short
30 from node import short
31 name = short(name)
31 name = short(name)
32 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
32 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
33
33
34 def __str__(self):
34 def __str__(self):
35 return RevlogError.__str__(self)
35 return RevlogError.__str__(self)
36
36
37 class FilteredLookupError(LookupError):
37 class FilteredLookupError(LookupError):
38 pass
38 pass
39
39
40 class ManifestLookupError(LookupError):
40 class ManifestLookupError(LookupError):
41 pass
41 pass
42
42
43 class CommandError(Exception):
43 class CommandError(Exception):
44 """Exception raised on errors in parsing the command line."""
44 """Exception raised on errors in parsing the command line."""
45
45
46 class InterventionRequired(Exception):
46 class InterventionRequired(Exception):
47 """Exception raised when a command requires human intervention."""
47 """Exception raised when a command requires human intervention."""
48
48
49 class Abort(Exception):
49 class Abort(Exception):
50 """Raised if a command needs to print an error and exit."""
50 """Raised if a command needs to print an error and exit."""
51 def __init__(self, *args, **kw):
51 def __init__(self, *args, **kw):
52 Exception.__init__(self, *args)
52 Exception.__init__(self, *args)
53 self.hint = kw.get('hint')
53 self.hint = kw.get('hint')
54
54
55 class HookAbort(Abort):
55 class HookAbort(Abort):
56 """raised when a validation hook fails, aborting an operation
56 """raised when a validation hook fails, aborting an operation
57
57
58 Exists to allow more specialized catching."""
58 Exists to allow more specialized catching."""
59 pass
59 pass
60
60
61 class ConfigError(Abort):
61 class ConfigError(Abort):
62 """Exception raised when parsing config files"""
62 """Exception raised when parsing config files"""
63
63
64 class OutOfBandError(Exception):
64 class OutOfBandError(Exception):
65 """Exception raised when a remote repo reports failure"""
65 """Exception raised when a remote repo reports failure"""
66
66
67 def __init__(self, *args, **kw):
68 Exception.__init__(self, *args)
69 self.hint = kw.get('hint')
70
67 class ParseError(Exception):
71 class ParseError(Exception):
68 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
72 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
69
73
70 class UnknownIdentifier(ParseError):
74 class UnknownIdentifier(ParseError):
71 """Exception raised when a {rev,file}set references an unknown identifier"""
75 """Exception raised when a {rev,file}set references an unknown identifier"""
72
76
73 def __init__(self, function, symbols):
77 def __init__(self, function, symbols):
74 from i18n import _
78 from i18n import _
75 ParseError.__init__(self, _("unknown identifier: %s") % function)
79 ParseError.__init__(self, _("unknown identifier: %s") % function)
76 self.function = function
80 self.function = function
77 self.symbols = symbols
81 self.symbols = symbols
78
82
79 class RepoError(Exception):
83 class RepoError(Exception):
80 def __init__(self, *args, **kw):
84 def __init__(self, *args, **kw):
81 Exception.__init__(self, *args)
85 Exception.__init__(self, *args)
82 self.hint = kw.get('hint')
86 self.hint = kw.get('hint')
83
87
84 class RepoLookupError(RepoError):
88 class RepoLookupError(RepoError):
85 pass
89 pass
86
90
87 class FilteredRepoLookupError(RepoLookupError):
91 class FilteredRepoLookupError(RepoLookupError):
88 pass
92 pass
89
93
90 class CapabilityError(RepoError):
94 class CapabilityError(RepoError):
91 pass
95 pass
92
96
93 class RequirementError(RepoError):
97 class RequirementError(RepoError):
94 """Exception raised if .hg/requires has an unknown entry."""
98 """Exception raised if .hg/requires has an unknown entry."""
95 pass
99 pass
96
100
97 class LockError(IOError):
101 class LockError(IOError):
98 def __init__(self, errno, strerror, filename, desc):
102 def __init__(self, errno, strerror, filename, desc):
99 IOError.__init__(self, errno, strerror, filename)
103 IOError.__init__(self, errno, strerror, filename)
100 self.desc = desc
104 self.desc = desc
101
105
102 class LockHeld(LockError):
106 class LockHeld(LockError):
103 def __init__(self, errno, filename, desc, locker):
107 def __init__(self, errno, filename, desc, locker):
104 LockError.__init__(self, errno, 'Lock held', filename, desc)
108 LockError.__init__(self, errno, 'Lock held', filename, desc)
105 self.locker = locker
109 self.locker = locker
106
110
107 class LockUnavailable(LockError):
111 class LockUnavailable(LockError):
108 pass
112 pass
109
113
110 class ResponseError(Exception):
114 class ResponseError(Exception):
111 """Raised to print an error with part of output and exit."""
115 """Raised to print an error with part of output and exit."""
112
116
113 class UnknownCommand(Exception):
117 class UnknownCommand(Exception):
114 """Exception raised if command is not in the command table."""
118 """Exception raised if command is not in the command table."""
115
119
116 class AmbiguousCommand(Exception):
120 class AmbiguousCommand(Exception):
117 """Exception raised if command shortcut matches more than one command."""
121 """Exception raised if command shortcut matches more than one command."""
118
122
119 # derived from KeyboardInterrupt to simplify some breakout code
123 # derived from KeyboardInterrupt to simplify some breakout code
120 class SignalInterrupt(KeyboardInterrupt):
124 class SignalInterrupt(KeyboardInterrupt):
121 """Exception raised on SIGTERM and SIGHUP."""
125 """Exception raised on SIGTERM and SIGHUP."""
122
126
123 class SignatureError(Exception):
127 class SignatureError(Exception):
124 pass
128 pass
125
129
126 class PushRaced(RuntimeError):
130 class PushRaced(RuntimeError):
127 """An exception raised during unbundling that indicate a push race"""
131 """An exception raised during unbundling that indicate a push race"""
128
132
129 # bundle2 related errors
133 # bundle2 related errors
130 class BundleValueError(ValueError):
134 class BundleValueError(ValueError):
131 """error raised when bundle2 cannot be processed"""
135 """error raised when bundle2 cannot be processed"""
132
136
133 class UnsupportedPartError(BundleValueError):
137 class UnsupportedPartError(BundleValueError):
134 def __init__(self, parttype=None, params=()):
138 def __init__(self, parttype=None, params=()):
135 self.parttype = parttype
139 self.parttype = parttype
136 self.params = params
140 self.params = params
137 if self.parttype is None:
141 if self.parttype is None:
138 msg = 'Stream Parameter'
142 msg = 'Stream Parameter'
139 else:
143 else:
140 msg = parttype
144 msg = parttype
141 if self.params:
145 if self.params:
142 msg = '%s - %s' % (msg, ', '.join(self.params))
146 msg = '%s - %s' % (msg, ', '.join(self.params))
143 ValueError.__init__(self, msg)
147 ValueError.__init__(self, msg)
144
148
145 class ReadOnlyPartError(RuntimeError):
149 class ReadOnlyPartError(RuntimeError):
146 """error raised when code tries to alter a part being generated"""
150 """error raised when code tries to alter a part being generated"""
147 pass
151 pass
148
152
149 class CensoredNodeError(RevlogError):
153 class CensoredNodeError(RevlogError):
150 """error raised when content verification fails on a censored node
154 """error raised when content verification fails on a censored node
151
155
152 Also contains the tombstone data substituted for the uncensored data.
156 Also contains the tombstone data substituted for the uncensored data.
153 """
157 """
154
158
155 def __init__(self, filename, node, tombstone):
159 def __init__(self, filename, node, tombstone):
156 from node import short
160 from node import short
157 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
161 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
158 self.tombstone = tombstone
162 self.tombstone = tombstone
159
163
160 class CensoredBaseError(RevlogError):
164 class CensoredBaseError(RevlogError):
161 """error raised when a delta is rejected because its base is censored
165 """error raised when a delta is rejected because its base is censored
162
166
163 A delta based on a censored revision must be formed as single patch
167 A delta based on a censored revision must be formed as single patch
164 operation which replaces the entire base with new content. This ensures
168 operation which replaces the entire base with new content. This ensures
165 the delta may be applied by clones which have not censored the base.
169 the delta may be applied by clones which have not censored the base.
166 """
170 """
General Comments 0
You need to be logged in to leave comments. Login now