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