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