##// END OF EJS Templates
dispatch: move detection of profiling earlier during startup
Bryan O'Sullivan -
r30933:69a3c6c8 default
parent child Browse files
Show More
@@ -1,888 +1,888 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, 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 getopt
13 import getopt
14 import os
14 import os
15 import pdb
15 import pdb
16 import re
16 import re
17 import signal
17 import signal
18 import sys
18 import sys
19 import time
19 import time
20 import traceback
20 import traceback
21
21
22
22
23 from .i18n import _
23 from .i18n import _
24
24
25 from . import (
25 from . import (
26 cmdutil,
26 cmdutil,
27 color,
27 color,
28 commands,
28 commands,
29 debugcommands,
29 debugcommands,
30 demandimport,
30 demandimport,
31 encoding,
31 encoding,
32 error,
32 error,
33 extensions,
33 extensions,
34 fancyopts,
34 fancyopts,
35 fileset,
35 fileset,
36 hg,
36 hg,
37 hook,
37 hook,
38 profiling,
38 profiling,
39 pycompat,
39 pycompat,
40 revset,
40 revset,
41 scmutil,
41 scmutil,
42 templatefilters,
42 templatefilters,
43 templatekw,
43 templatekw,
44 templater,
44 templater,
45 ui as uimod,
45 ui as uimod,
46 util,
46 util,
47 )
47 )
48
48
49 class request(object):
49 class request(object):
50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
51 ferr=None):
51 ferr=None):
52 self.args = args
52 self.args = args
53 self.ui = ui
53 self.ui = ui
54 self.repo = repo
54 self.repo = repo
55
55
56 # input/output/error streams
56 # input/output/error streams
57 self.fin = fin
57 self.fin = fin
58 self.fout = fout
58 self.fout = fout
59 self.ferr = ferr
59 self.ferr = ferr
60
60
61 def run():
61 def run():
62 "run the command in sys.argv"
62 "run the command in sys.argv"
63 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
63 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
64
64
65 def _getsimilar(symbols, value):
65 def _getsimilar(symbols, value):
66 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
66 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
67 # The cutoff for similarity here is pretty arbitrary. It should
67 # The cutoff for similarity here is pretty arbitrary. It should
68 # probably be investigated and tweaked.
68 # probably be investigated and tweaked.
69 return [s for s in symbols if sim(s) > 0.6]
69 return [s for s in symbols if sim(s) > 0.6]
70
70
71 def _reportsimilar(write, similar):
71 def _reportsimilar(write, similar):
72 if len(similar) == 1:
72 if len(similar) == 1:
73 write(_("(did you mean %s?)\n") % similar[0])
73 write(_("(did you mean %s?)\n") % similar[0])
74 elif similar:
74 elif similar:
75 ss = ", ".join(sorted(similar))
75 ss = ", ".join(sorted(similar))
76 write(_("(did you mean one of %s?)\n") % ss)
76 write(_("(did you mean one of %s?)\n") % ss)
77
77
78 def _formatparse(write, inst):
78 def _formatparse(write, inst):
79 similar = []
79 similar = []
80 if isinstance(inst, error.UnknownIdentifier):
80 if isinstance(inst, error.UnknownIdentifier):
81 # make sure to check fileset first, as revset can invoke fileset
81 # make sure to check fileset first, as revset can invoke fileset
82 similar = _getsimilar(inst.symbols, inst.function)
82 similar = _getsimilar(inst.symbols, inst.function)
83 if len(inst.args) > 1:
83 if len(inst.args) > 1:
84 write(_("hg: parse error at %s: %s\n") %
84 write(_("hg: parse error at %s: %s\n") %
85 (inst.args[1], inst.args[0]))
85 (inst.args[1], inst.args[0]))
86 if (inst.args[0][0] == ' '):
86 if (inst.args[0][0] == ' '):
87 write(_("unexpected leading whitespace\n"))
87 write(_("unexpected leading whitespace\n"))
88 else:
88 else:
89 write(_("hg: parse error: %s\n") % inst.args[0])
89 write(_("hg: parse error: %s\n") % inst.args[0])
90 _reportsimilar(write, similar)
90 _reportsimilar(write, similar)
91 if inst.hint:
91 if inst.hint:
92 write(_("(%s)\n") % inst.hint)
92 write(_("(%s)\n") % inst.hint)
93
93
94 def dispatch(req):
94 def dispatch(req):
95 "run the command specified in req.args"
95 "run the command specified in req.args"
96 if req.ferr:
96 if req.ferr:
97 ferr = req.ferr
97 ferr = req.ferr
98 elif req.ui:
98 elif req.ui:
99 ferr = req.ui.ferr
99 ferr = req.ui.ferr
100 else:
100 else:
101 ferr = util.stderr
101 ferr = util.stderr
102
102
103 try:
103 try:
104 if not req.ui:
104 if not req.ui:
105 req.ui = uimod.ui.load()
105 req.ui = uimod.ui.load()
106 if '--traceback' in req.args:
106 if '--traceback' in req.args:
107 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
107 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
108
108
109 # set ui streams from the request
109 # set ui streams from the request
110 if req.fin:
110 if req.fin:
111 req.ui.fin = req.fin
111 req.ui.fin = req.fin
112 if req.fout:
112 if req.fout:
113 req.ui.fout = req.fout
113 req.ui.fout = req.fout
114 if req.ferr:
114 if req.ferr:
115 req.ui.ferr = req.ferr
115 req.ui.ferr = req.ferr
116 except error.Abort as inst:
116 except error.Abort as inst:
117 ferr.write(_("abort: %s\n") % inst)
117 ferr.write(_("abort: %s\n") % inst)
118 if inst.hint:
118 if inst.hint:
119 ferr.write(_("(%s)\n") % inst.hint)
119 ferr.write(_("(%s)\n") % inst.hint)
120 return -1
120 return -1
121 except error.ParseError as inst:
121 except error.ParseError as inst:
122 _formatparse(ferr.write, inst)
122 _formatparse(ferr.write, inst)
123 return -1
123 return -1
124
124
125 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
125 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
126 starttime = time.time()
126 starttime = time.time()
127 ret = None
127 ret = None
128 try:
128 try:
129 ret = _runcatch(req)
129 ret = _runcatch(req)
130 except KeyboardInterrupt:
130 except KeyboardInterrupt:
131 try:
131 try:
132 req.ui.warn(_("interrupted!\n"))
132 req.ui.warn(_("interrupted!\n"))
133 except IOError as inst:
133 except IOError as inst:
134 if inst.errno != errno.EPIPE:
134 if inst.errno != errno.EPIPE:
135 raise
135 raise
136 ret = -1
136 ret = -1
137 finally:
137 finally:
138 duration = time.time() - starttime
138 duration = time.time() - starttime
139 req.ui.flush()
139 req.ui.flush()
140 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
140 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
141 msg, ret or 0, duration)
141 msg, ret or 0, duration)
142 return ret
142 return ret
143
143
144 def _runcatch(req):
144 def _runcatch(req):
145 def catchterm(*args):
145 def catchterm(*args):
146 raise error.SignalInterrupt
146 raise error.SignalInterrupt
147
147
148 ui = req.ui
148 ui = req.ui
149 try:
149 try:
150 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
150 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
151 num = getattr(signal, name, None)
151 num = getattr(signal, name, None)
152 if num:
152 if num:
153 signal.signal(num, catchterm)
153 signal.signal(num, catchterm)
154 except ValueError:
154 except ValueError:
155 pass # happens if called in a thread
155 pass # happens if called in a thread
156
156
157 def _runcatchfunc():
157 def _runcatchfunc():
158 try:
158 try:
159 debugger = 'pdb'
159 debugger = 'pdb'
160 debugtrace = {
160 debugtrace = {
161 'pdb' : pdb.set_trace
161 'pdb' : pdb.set_trace
162 }
162 }
163 debugmortem = {
163 debugmortem = {
164 'pdb' : pdb.post_mortem
164 'pdb' : pdb.post_mortem
165 }
165 }
166
166
167 # read --config before doing anything else
167 # read --config before doing anything else
168 # (e.g. to change trust settings for reading .hg/hgrc)
168 # (e.g. to change trust settings for reading .hg/hgrc)
169 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
169 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
170
170
171 if req.repo:
171 if req.repo:
172 # copy configs that were passed on the cmdline (--config) to
172 # copy configs that were passed on the cmdline (--config) to
173 # the repo ui
173 # the repo ui
174 for sec, name, val in cfgs:
174 for sec, name, val in cfgs:
175 req.repo.ui.setconfig(sec, name, val, source='--config')
175 req.repo.ui.setconfig(sec, name, val, source='--config')
176
176
177 # developer config: ui.debugger
177 # developer config: ui.debugger
178 debugger = ui.config("ui", "debugger")
178 debugger = ui.config("ui", "debugger")
179 debugmod = pdb
179 debugmod = pdb
180 if not debugger or ui.plain():
180 if not debugger or ui.plain():
181 # if we are in HGPLAIN mode, then disable custom debugging
181 # if we are in HGPLAIN mode, then disable custom debugging
182 debugger = 'pdb'
182 debugger = 'pdb'
183 elif '--debugger' in req.args:
183 elif '--debugger' in req.args:
184 # This import can be slow for fancy debuggers, so only
184 # This import can be slow for fancy debuggers, so only
185 # do it when absolutely necessary, i.e. when actual
185 # do it when absolutely necessary, i.e. when actual
186 # debugging has been requested
186 # debugging has been requested
187 with demandimport.deactivated():
187 with demandimport.deactivated():
188 try:
188 try:
189 debugmod = __import__(debugger)
189 debugmod = __import__(debugger)
190 except ImportError:
190 except ImportError:
191 pass # Leave debugmod = pdb
191 pass # Leave debugmod = pdb
192
192
193 debugtrace[debugger] = debugmod.set_trace
193 debugtrace[debugger] = debugmod.set_trace
194 debugmortem[debugger] = debugmod.post_mortem
194 debugmortem[debugger] = debugmod.post_mortem
195
195
196 # enter the debugger before command execution
196 # enter the debugger before command execution
197 if '--debugger' in req.args:
197 if '--debugger' in req.args:
198 ui.warn(_("entering debugger - "
198 ui.warn(_("entering debugger - "
199 "type c to continue starting hg or h for help\n"))
199 "type c to continue starting hg or h for help\n"))
200
200
201 if (debugger != 'pdb' and
201 if (debugger != 'pdb' and
202 debugtrace[debugger] == debugtrace['pdb']):
202 debugtrace[debugger] == debugtrace['pdb']):
203 ui.warn(_("%s debugger specified "
203 ui.warn(_("%s debugger specified "
204 "but its module was not found\n") % debugger)
204 "but its module was not found\n") % debugger)
205 with demandimport.deactivated():
205 with demandimport.deactivated():
206 debugtrace[debugger]()
206 debugtrace[debugger]()
207 try:
207 try:
208 return _dispatch(req)
208 return _dispatch(req)
209 finally:
209 finally:
210 ui.flush()
210 ui.flush()
211 except: # re-raises
211 except: # re-raises
212 # enter the debugger when we hit an exception
212 # enter the debugger when we hit an exception
213 if '--debugger' in req.args:
213 if '--debugger' in req.args:
214 traceback.print_exc()
214 traceback.print_exc()
215 debugmortem[debugger](sys.exc_info()[2])
215 debugmortem[debugger](sys.exc_info()[2])
216 ui.traceback()
216 ui.traceback()
217 raise
217 raise
218
218
219 return callcatch(ui, _runcatchfunc)
219 return callcatch(ui, _runcatchfunc)
220
220
221 def callcatch(ui, func):
221 def callcatch(ui, func):
222 """like scmutil.callcatch but handles more high-level exceptions about
222 """like scmutil.callcatch but handles more high-level exceptions about
223 config parsing and commands. besides, use handlecommandexception to handle
223 config parsing and commands. besides, use handlecommandexception to handle
224 uncaught exceptions.
224 uncaught exceptions.
225 """
225 """
226 try:
226 try:
227 return scmutil.callcatch(ui, func)
227 return scmutil.callcatch(ui, func)
228 except error.AmbiguousCommand as inst:
228 except error.AmbiguousCommand as inst:
229 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
229 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
230 (inst.args[0], " ".join(inst.args[1])))
230 (inst.args[0], " ".join(inst.args[1])))
231 except error.CommandError as inst:
231 except error.CommandError as inst:
232 if inst.args[0]:
232 if inst.args[0]:
233 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
233 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
234 commands.help_(ui, inst.args[0], full=False, command=True)
234 commands.help_(ui, inst.args[0], full=False, command=True)
235 else:
235 else:
236 ui.warn(_("hg: %s\n") % inst.args[1])
236 ui.warn(_("hg: %s\n") % inst.args[1])
237 commands.help_(ui, 'shortlist')
237 commands.help_(ui, 'shortlist')
238 except error.ParseError as inst:
238 except error.ParseError as inst:
239 _formatparse(ui.warn, inst)
239 _formatparse(ui.warn, inst)
240 return -1
240 return -1
241 except error.UnknownCommand as inst:
241 except error.UnknownCommand as inst:
242 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
242 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
243 try:
243 try:
244 # check if the command is in a disabled extension
244 # check if the command is in a disabled extension
245 # (but don't check for extensions themselves)
245 # (but don't check for extensions themselves)
246 commands.help_(ui, inst.args[0], unknowncmd=True)
246 commands.help_(ui, inst.args[0], unknowncmd=True)
247 except (error.UnknownCommand, error.Abort):
247 except (error.UnknownCommand, error.Abort):
248 suggested = False
248 suggested = False
249 if len(inst.args) == 2:
249 if len(inst.args) == 2:
250 sim = _getsimilar(inst.args[1], inst.args[0])
250 sim = _getsimilar(inst.args[1], inst.args[0])
251 if sim:
251 if sim:
252 _reportsimilar(ui.warn, sim)
252 _reportsimilar(ui.warn, sim)
253 suggested = True
253 suggested = True
254 if not suggested:
254 if not suggested:
255 commands.help_(ui, 'shortlist')
255 commands.help_(ui, 'shortlist')
256 except IOError:
256 except IOError:
257 raise
257 raise
258 except KeyboardInterrupt:
258 except KeyboardInterrupt:
259 raise
259 raise
260 except: # probably re-raises
260 except: # probably re-raises
261 if not handlecommandexception(ui):
261 if not handlecommandexception(ui):
262 raise
262 raise
263
263
264 return -1
264 return -1
265
265
266 def aliasargs(fn, givenargs):
266 def aliasargs(fn, givenargs):
267 args = getattr(fn, 'args', [])
267 args = getattr(fn, 'args', [])
268 if args:
268 if args:
269 cmd = ' '.join(map(util.shellquote, args))
269 cmd = ' '.join(map(util.shellquote, args))
270
270
271 nums = []
271 nums = []
272 def replacer(m):
272 def replacer(m):
273 num = int(m.group(1)) - 1
273 num = int(m.group(1)) - 1
274 nums.append(num)
274 nums.append(num)
275 if num < len(givenargs):
275 if num < len(givenargs):
276 return givenargs[num]
276 return givenargs[num]
277 raise error.Abort(_('too few arguments for command alias'))
277 raise error.Abort(_('too few arguments for command alias'))
278 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
278 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
279 givenargs = [x for i, x in enumerate(givenargs)
279 givenargs = [x for i, x in enumerate(givenargs)
280 if i not in nums]
280 if i not in nums]
281 args = pycompat.shlexsplit(cmd)
281 args = pycompat.shlexsplit(cmd)
282 return args + givenargs
282 return args + givenargs
283
283
284 def aliasinterpolate(name, args, cmd):
284 def aliasinterpolate(name, args, cmd):
285 '''interpolate args into cmd for shell aliases
285 '''interpolate args into cmd for shell aliases
286
286
287 This also handles $0, $@ and "$@".
287 This also handles $0, $@ and "$@".
288 '''
288 '''
289 # util.interpolate can't deal with "$@" (with quotes) because it's only
289 # util.interpolate can't deal with "$@" (with quotes) because it's only
290 # built to match prefix + patterns.
290 # built to match prefix + patterns.
291 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
291 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
292 replacemap['$0'] = name
292 replacemap['$0'] = name
293 replacemap['$$'] = '$'
293 replacemap['$$'] = '$'
294 replacemap['$@'] = ' '.join(args)
294 replacemap['$@'] = ' '.join(args)
295 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
295 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
296 # parameters, separated out into words. Emulate the same behavior here by
296 # parameters, separated out into words. Emulate the same behavior here by
297 # quoting the arguments individually. POSIX shells will then typically
297 # quoting the arguments individually. POSIX shells will then typically
298 # tokenize each argument into exactly one word.
298 # tokenize each argument into exactly one word.
299 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
299 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
300 # escape '\$' for regex
300 # escape '\$' for regex
301 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
301 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
302 r = re.compile(regex)
302 r = re.compile(regex)
303 return r.sub(lambda x: replacemap[x.group()], cmd)
303 return r.sub(lambda x: replacemap[x.group()], cmd)
304
304
305 class cmdalias(object):
305 class cmdalias(object):
306 def __init__(self, name, definition, cmdtable, source):
306 def __init__(self, name, definition, cmdtable, source):
307 self.name = self.cmd = name
307 self.name = self.cmd = name
308 self.cmdname = ''
308 self.cmdname = ''
309 self.definition = definition
309 self.definition = definition
310 self.fn = None
310 self.fn = None
311 self.givenargs = []
311 self.givenargs = []
312 self.opts = []
312 self.opts = []
313 self.help = ''
313 self.help = ''
314 self.badalias = None
314 self.badalias = None
315 self.unknowncmd = False
315 self.unknowncmd = False
316 self.source = source
316 self.source = source
317
317
318 try:
318 try:
319 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
319 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
320 for alias, e in cmdtable.iteritems():
320 for alias, e in cmdtable.iteritems():
321 if e is entry:
321 if e is entry:
322 self.cmd = alias
322 self.cmd = alias
323 break
323 break
324 self.shadows = True
324 self.shadows = True
325 except error.UnknownCommand:
325 except error.UnknownCommand:
326 self.shadows = False
326 self.shadows = False
327
327
328 if not self.definition:
328 if not self.definition:
329 self.badalias = _("no definition for alias '%s'") % self.name
329 self.badalias = _("no definition for alias '%s'") % self.name
330 return
330 return
331
331
332 if self.definition.startswith('!'):
332 if self.definition.startswith('!'):
333 self.shell = True
333 self.shell = True
334 def fn(ui, *args):
334 def fn(ui, *args):
335 env = {'HG_ARGS': ' '.join((self.name,) + args)}
335 env = {'HG_ARGS': ' '.join((self.name,) + args)}
336 def _checkvar(m):
336 def _checkvar(m):
337 if m.groups()[0] == '$':
337 if m.groups()[0] == '$':
338 return m.group()
338 return m.group()
339 elif int(m.groups()[0]) <= len(args):
339 elif int(m.groups()[0]) <= len(args):
340 return m.group()
340 return m.group()
341 else:
341 else:
342 ui.debug("No argument found for substitution "
342 ui.debug("No argument found for substitution "
343 "of %i variable in alias '%s' definition."
343 "of %i variable in alias '%s' definition."
344 % (int(m.groups()[0]), self.name))
344 % (int(m.groups()[0]), self.name))
345 return ''
345 return ''
346 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
346 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
347 cmd = aliasinterpolate(self.name, args, cmd)
347 cmd = aliasinterpolate(self.name, args, cmd)
348 return ui.system(cmd, environ=env)
348 return ui.system(cmd, environ=env)
349 self.fn = fn
349 self.fn = fn
350 return
350 return
351
351
352 try:
352 try:
353 args = pycompat.shlexsplit(self.definition)
353 args = pycompat.shlexsplit(self.definition)
354 except ValueError as inst:
354 except ValueError as inst:
355 self.badalias = (_("error in definition for alias '%s': %s")
355 self.badalias = (_("error in definition for alias '%s': %s")
356 % (self.name, inst))
356 % (self.name, inst))
357 return
357 return
358 self.cmdname = cmd = args.pop(0)
358 self.cmdname = cmd = args.pop(0)
359 self.givenargs = args
359 self.givenargs = args
360
360
361 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
361 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
362 if _earlygetopt([invalidarg], args):
362 if _earlygetopt([invalidarg], args):
363 self.badalias = (_("error in definition for alias '%s': %s may "
363 self.badalias = (_("error in definition for alias '%s': %s may "
364 "only be given on the command line")
364 "only be given on the command line")
365 % (self.name, invalidarg))
365 % (self.name, invalidarg))
366 return
366 return
367
367
368 try:
368 try:
369 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
369 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
370 if len(tableentry) > 2:
370 if len(tableentry) > 2:
371 self.fn, self.opts, self.help = tableentry
371 self.fn, self.opts, self.help = tableentry
372 else:
372 else:
373 self.fn, self.opts = tableentry
373 self.fn, self.opts = tableentry
374
374
375 if self.help.startswith("hg " + cmd):
375 if self.help.startswith("hg " + cmd):
376 # drop prefix in old-style help lines so hg shows the alias
376 # drop prefix in old-style help lines so hg shows the alias
377 self.help = self.help[4 + len(cmd):]
377 self.help = self.help[4 + len(cmd):]
378 self.__doc__ = self.fn.__doc__
378 self.__doc__ = self.fn.__doc__
379
379
380 except error.UnknownCommand:
380 except error.UnknownCommand:
381 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
381 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
382 % (self.name, cmd))
382 % (self.name, cmd))
383 self.unknowncmd = True
383 self.unknowncmd = True
384 except error.AmbiguousCommand:
384 except error.AmbiguousCommand:
385 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
385 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
386 % (self.name, cmd))
386 % (self.name, cmd))
387
387
388 @property
388 @property
389 def args(self):
389 def args(self):
390 args = map(util.expandpath, self.givenargs)
390 args = map(util.expandpath, self.givenargs)
391 return aliasargs(self.fn, args)
391 return aliasargs(self.fn, args)
392
392
393 def __getattr__(self, name):
393 def __getattr__(self, name):
394 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
394 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
395 if name not in adefaults:
395 if name not in adefaults:
396 raise AttributeError(name)
396 raise AttributeError(name)
397 if self.badalias or util.safehasattr(self, 'shell'):
397 if self.badalias or util.safehasattr(self, 'shell'):
398 return adefaults[name]
398 return adefaults[name]
399 return getattr(self.fn, name)
399 return getattr(self.fn, name)
400
400
401 def __call__(self, ui, *args, **opts):
401 def __call__(self, ui, *args, **opts):
402 if self.badalias:
402 if self.badalias:
403 hint = None
403 hint = None
404 if self.unknowncmd:
404 if self.unknowncmd:
405 try:
405 try:
406 # check if the command is in a disabled extension
406 # check if the command is in a disabled extension
407 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
407 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
408 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
408 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
409 except error.UnknownCommand:
409 except error.UnknownCommand:
410 pass
410 pass
411 raise error.Abort(self.badalias, hint=hint)
411 raise error.Abort(self.badalias, hint=hint)
412 if self.shadows:
412 if self.shadows:
413 ui.debug("alias '%s' shadows command '%s'\n" %
413 ui.debug("alias '%s' shadows command '%s'\n" %
414 (self.name, self.cmdname))
414 (self.name, self.cmdname))
415
415
416 ui.log('commandalias', "alias '%s' expands to '%s'\n",
416 ui.log('commandalias', "alias '%s' expands to '%s'\n",
417 self.name, self.definition)
417 self.name, self.definition)
418 if util.safehasattr(self, 'shell'):
418 if util.safehasattr(self, 'shell'):
419 return self.fn(ui, *args, **opts)
419 return self.fn(ui, *args, **opts)
420 else:
420 else:
421 try:
421 try:
422 return util.checksignature(self.fn)(ui, *args, **opts)
422 return util.checksignature(self.fn)(ui, *args, **opts)
423 except error.SignatureError:
423 except error.SignatureError:
424 args = ' '.join([self.cmdname] + self.args)
424 args = ' '.join([self.cmdname] + self.args)
425 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
425 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
426 raise
426 raise
427
427
428 def addaliases(ui, cmdtable):
428 def addaliases(ui, cmdtable):
429 # aliases are processed after extensions have been loaded, so they
429 # aliases are processed after extensions have been loaded, so they
430 # may use extension commands. Aliases can also use other alias definitions,
430 # may use extension commands. Aliases can also use other alias definitions,
431 # but only if they have been defined prior to the current definition.
431 # but only if they have been defined prior to the current definition.
432 for alias, definition in ui.configitems('alias'):
432 for alias, definition in ui.configitems('alias'):
433 source = ui.configsource('alias', alias)
433 source = ui.configsource('alias', alias)
434 aliasdef = cmdalias(alias, definition, cmdtable, source)
434 aliasdef = cmdalias(alias, definition, cmdtable, source)
435
435
436 try:
436 try:
437 olddef = cmdtable[aliasdef.cmd][0]
437 olddef = cmdtable[aliasdef.cmd][0]
438 if olddef.definition == aliasdef.definition:
438 if olddef.definition == aliasdef.definition:
439 continue
439 continue
440 except (KeyError, AttributeError):
440 except (KeyError, AttributeError):
441 # definition might not exist or it might not be a cmdalias
441 # definition might not exist or it might not be a cmdalias
442 pass
442 pass
443
443
444 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
444 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
445
445
446 def _parse(ui, args):
446 def _parse(ui, args):
447 options = {}
447 options = {}
448 cmdoptions = {}
448 cmdoptions = {}
449
449
450 try:
450 try:
451 args = fancyopts.fancyopts(args, commands.globalopts, options)
451 args = fancyopts.fancyopts(args, commands.globalopts, options)
452 except getopt.GetoptError as inst:
452 except getopt.GetoptError as inst:
453 raise error.CommandError(None, inst)
453 raise error.CommandError(None, inst)
454
454
455 if args:
455 if args:
456 cmd, args = args[0], args[1:]
456 cmd, args = args[0], args[1:]
457 aliases, entry = cmdutil.findcmd(cmd, commands.table,
457 aliases, entry = cmdutil.findcmd(cmd, commands.table,
458 ui.configbool("ui", "strict"))
458 ui.configbool("ui", "strict"))
459 cmd = aliases[0]
459 cmd = aliases[0]
460 args = aliasargs(entry[0], args)
460 args = aliasargs(entry[0], args)
461 defaults = ui.config("defaults", cmd)
461 defaults = ui.config("defaults", cmd)
462 if defaults:
462 if defaults:
463 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args
463 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args
464 c = list(entry[1])
464 c = list(entry[1])
465 else:
465 else:
466 cmd = None
466 cmd = None
467 c = []
467 c = []
468
468
469 # combine global options into local
469 # combine global options into local
470 for o in commands.globalopts:
470 for o in commands.globalopts:
471 c.append((o[0], o[1], options[o[1]], o[3]))
471 c.append((o[0], o[1], options[o[1]], o[3]))
472
472
473 try:
473 try:
474 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
474 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
475 except getopt.GetoptError as inst:
475 except getopt.GetoptError as inst:
476 raise error.CommandError(cmd, inst)
476 raise error.CommandError(cmd, inst)
477
477
478 # separate global options back out
478 # separate global options back out
479 for o in commands.globalopts:
479 for o in commands.globalopts:
480 n = o[1]
480 n = o[1]
481 options[n] = cmdoptions[n]
481 options[n] = cmdoptions[n]
482 del cmdoptions[n]
482 del cmdoptions[n]
483
483
484 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
484 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
485
485
486 def _parseconfig(ui, config):
486 def _parseconfig(ui, config):
487 """parse the --config options from the command line"""
487 """parse the --config options from the command line"""
488 configs = []
488 configs = []
489
489
490 for cfg in config:
490 for cfg in config:
491 try:
491 try:
492 name, value = [cfgelem.strip()
492 name, value = [cfgelem.strip()
493 for cfgelem in cfg.split('=', 1)]
493 for cfgelem in cfg.split('=', 1)]
494 section, name = name.split('.', 1)
494 section, name = name.split('.', 1)
495 if not section or not name:
495 if not section or not name:
496 raise IndexError
496 raise IndexError
497 ui.setconfig(section, name, value, '--config')
497 ui.setconfig(section, name, value, '--config')
498 configs.append((section, name, value))
498 configs.append((section, name, value))
499 except (IndexError, ValueError):
499 except (IndexError, ValueError):
500 raise error.Abort(_('malformed --config option: %r '
500 raise error.Abort(_('malformed --config option: %r '
501 '(use --config section.name=value)') % cfg)
501 '(use --config section.name=value)') % cfg)
502
502
503 return configs
503 return configs
504
504
505 def _earlygetopt(aliases, args):
505 def _earlygetopt(aliases, args):
506 """Return list of values for an option (or aliases).
506 """Return list of values for an option (or aliases).
507
507
508 The values are listed in the order they appear in args.
508 The values are listed in the order they appear in args.
509 The options and values are removed from args.
509 The options and values are removed from args.
510
510
511 >>> args = ['x', '--cwd', 'foo', 'y']
511 >>> args = ['x', '--cwd', 'foo', 'y']
512 >>> _earlygetopt(['--cwd'], args), args
512 >>> _earlygetopt(['--cwd'], args), args
513 (['foo'], ['x', 'y'])
513 (['foo'], ['x', 'y'])
514
514
515 >>> args = ['x', '--cwd=bar', 'y']
515 >>> args = ['x', '--cwd=bar', 'y']
516 >>> _earlygetopt(['--cwd'], args), args
516 >>> _earlygetopt(['--cwd'], args), args
517 (['bar'], ['x', 'y'])
517 (['bar'], ['x', 'y'])
518
518
519 >>> args = ['x', '-R', 'foo', 'y']
519 >>> args = ['x', '-R', 'foo', 'y']
520 >>> _earlygetopt(['-R'], args), args
520 >>> _earlygetopt(['-R'], args), args
521 (['foo'], ['x', 'y'])
521 (['foo'], ['x', 'y'])
522
522
523 >>> args = ['x', '-Rbar', 'y']
523 >>> args = ['x', '-Rbar', 'y']
524 >>> _earlygetopt(['-R'], args), args
524 >>> _earlygetopt(['-R'], args), args
525 (['bar'], ['x', 'y'])
525 (['bar'], ['x', 'y'])
526 """
526 """
527 try:
527 try:
528 argcount = args.index("--")
528 argcount = args.index("--")
529 except ValueError:
529 except ValueError:
530 argcount = len(args)
530 argcount = len(args)
531 shortopts = [opt for opt in aliases if len(opt) == 2]
531 shortopts = [opt for opt in aliases if len(opt) == 2]
532 values = []
532 values = []
533 pos = 0
533 pos = 0
534 while pos < argcount:
534 while pos < argcount:
535 fullarg = arg = args[pos]
535 fullarg = arg = args[pos]
536 equals = arg.find('=')
536 equals = arg.find('=')
537 if equals > -1:
537 if equals > -1:
538 arg = arg[:equals]
538 arg = arg[:equals]
539 if arg in aliases:
539 if arg in aliases:
540 del args[pos]
540 del args[pos]
541 if equals > -1:
541 if equals > -1:
542 values.append(fullarg[equals + 1:])
542 values.append(fullarg[equals + 1:])
543 argcount -= 1
543 argcount -= 1
544 else:
544 else:
545 if pos + 1 >= argcount:
545 if pos + 1 >= argcount:
546 # ignore and let getopt report an error if there is no value
546 # ignore and let getopt report an error if there is no value
547 break
547 break
548 values.append(args.pop(pos))
548 values.append(args.pop(pos))
549 argcount -= 2
549 argcount -= 2
550 elif arg[:2] in shortopts:
550 elif arg[:2] in shortopts:
551 # short option can have no following space, e.g. hg log -Rfoo
551 # short option can have no following space, e.g. hg log -Rfoo
552 values.append(args.pop(pos)[2:])
552 values.append(args.pop(pos)[2:])
553 argcount -= 1
553 argcount -= 1
554 else:
554 else:
555 pos += 1
555 pos += 1
556 return values
556 return values
557
557
558 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
558 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
559 # run pre-hook, and abort if it fails
559 # run pre-hook, and abort if it fails
560 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
560 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
561 pats=cmdpats, opts=cmdoptions)
561 pats=cmdpats, opts=cmdoptions)
562 try:
562 try:
563 ret = _runcommand(ui, options, cmd, d)
563 ret = _runcommand(ui, options, cmd, d)
564 # run post-hook, passing command result
564 # run post-hook, passing command result
565 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
565 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
566 result=ret, pats=cmdpats, opts=cmdoptions)
566 result=ret, pats=cmdpats, opts=cmdoptions)
567 except Exception:
567 except Exception:
568 # run failure hook and re-raise
568 # run failure hook and re-raise
569 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
569 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
570 pats=cmdpats, opts=cmdoptions)
570 pats=cmdpats, opts=cmdoptions)
571 raise
571 raise
572 return ret
572 return ret
573
573
574 def _getlocal(ui, rpath, wd=None):
574 def _getlocal(ui, rpath, wd=None):
575 """Return (path, local ui object) for the given target path.
575 """Return (path, local ui object) for the given target path.
576
576
577 Takes paths in [cwd]/.hg/hgrc into account."
577 Takes paths in [cwd]/.hg/hgrc into account."
578 """
578 """
579 if wd is None:
579 if wd is None:
580 try:
580 try:
581 wd = pycompat.getcwd()
581 wd = pycompat.getcwd()
582 except OSError as e:
582 except OSError as e:
583 raise error.Abort(_("error getting current working directory: %s") %
583 raise error.Abort(_("error getting current working directory: %s") %
584 e.strerror)
584 e.strerror)
585 path = cmdutil.findrepo(wd) or ""
585 path = cmdutil.findrepo(wd) or ""
586 if not path:
586 if not path:
587 lui = ui
587 lui = ui
588 else:
588 else:
589 lui = ui.copy()
589 lui = ui.copy()
590 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
590 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
591
591
592 if rpath and rpath[-1]:
592 if rpath and rpath[-1]:
593 path = lui.expandpath(rpath[-1])
593 path = lui.expandpath(rpath[-1])
594 lui = ui.copy()
594 lui = ui.copy()
595 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
595 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
596
596
597 return path, lui
597 return path, lui
598
598
599 def _checkshellalias(lui, ui, args):
599 def _checkshellalias(lui, ui, args):
600 """Return the function to run the shell alias, if it is required"""
600 """Return the function to run the shell alias, if it is required"""
601 options = {}
601 options = {}
602
602
603 try:
603 try:
604 args = fancyopts.fancyopts(args, commands.globalopts, options)
604 args = fancyopts.fancyopts(args, commands.globalopts, options)
605 except getopt.GetoptError:
605 except getopt.GetoptError:
606 return
606 return
607
607
608 if not args:
608 if not args:
609 return
609 return
610
610
611 cmdtable = commands.table
611 cmdtable = commands.table
612
612
613 cmd = args[0]
613 cmd = args[0]
614 try:
614 try:
615 strict = ui.configbool("ui", "strict")
615 strict = ui.configbool("ui", "strict")
616 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
616 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
617 except (error.AmbiguousCommand, error.UnknownCommand):
617 except (error.AmbiguousCommand, error.UnknownCommand):
618 return
618 return
619
619
620 cmd = aliases[0]
620 cmd = aliases[0]
621 fn = entry[0]
621 fn = entry[0]
622
622
623 if cmd and util.safehasattr(fn, 'shell'):
623 if cmd and util.safehasattr(fn, 'shell'):
624 d = lambda: fn(ui, *args[1:])
624 d = lambda: fn(ui, *args[1:])
625 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
625 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
626 [], {})
626 [], {})
627
627
628 _loaded = set()
628 _loaded = set()
629
629
630 # list of (objname, loadermod, loadername) tuple:
630 # list of (objname, loadermod, loadername) tuple:
631 # - objname is the name of an object in extension module, from which
631 # - objname is the name of an object in extension module, from which
632 # extra information is loaded
632 # extra information is loaded
633 # - loadermod is the module where loader is placed
633 # - loadermod is the module where loader is placed
634 # - loadername is the name of the function, which takes (ui, extensionname,
634 # - loadername is the name of the function, which takes (ui, extensionname,
635 # extraobj) arguments
635 # extraobj) arguments
636 extraloaders = [
636 extraloaders = [
637 ('cmdtable', commands, 'loadcmdtable'),
637 ('cmdtable', commands, 'loadcmdtable'),
638 ('colortable', color, 'loadcolortable'),
638 ('colortable', color, 'loadcolortable'),
639 ('filesetpredicate', fileset, 'loadpredicate'),
639 ('filesetpredicate', fileset, 'loadpredicate'),
640 ('revsetpredicate', revset, 'loadpredicate'),
640 ('revsetpredicate', revset, 'loadpredicate'),
641 ('templatefilter', templatefilters, 'loadfilter'),
641 ('templatefilter', templatefilters, 'loadfilter'),
642 ('templatefunc', templater, 'loadfunction'),
642 ('templatefunc', templater, 'loadfunction'),
643 ('templatekeyword', templatekw, 'loadkeyword'),
643 ('templatekeyword', templatekw, 'loadkeyword'),
644 ]
644 ]
645
645
646 def _dispatch(req):
646 def _dispatch(req):
647 args = req.args
647 args = req.args
648 ui = req.ui
648 ui = req.ui
649
649
650 # check for cwd
650 # check for cwd
651 cwd = _earlygetopt(['--cwd'], args)
651 cwd = _earlygetopt(['--cwd'], args)
652 if cwd:
652 if cwd:
653 os.chdir(cwd[-1])
653 os.chdir(cwd[-1])
654
654
655 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
655 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
656 path, lui = _getlocal(ui, rpath)
656 path, lui = _getlocal(ui, rpath)
657
657
658 # Side-effect of accessing is debugcommands module is guaranteed to be
658 # Side-effect of accessing is debugcommands module is guaranteed to be
659 # imported and commands.table is populated.
659 # imported and commands.table is populated.
660 debugcommands.command
660 debugcommands.command
661
661
662 uis = set([ui, lui])
663
664 if req.repo:
665 uis.add(req.repo.ui)
666
667 if '--profile' in args:
668 for ui_ in uis:
669 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
670
662 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
671 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
663 # reposetup. Programs like TortoiseHg will call _dispatch several
672 # reposetup. Programs like TortoiseHg will call _dispatch several
664 # times so we keep track of configured extensions in _loaded.
673 # times so we keep track of configured extensions in _loaded.
665 extensions.loadall(lui)
674 extensions.loadall(lui)
666 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
675 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
667 # Propagate any changes to lui.__class__ by extensions
676 # Propagate any changes to lui.__class__ by extensions
668 ui.__class__ = lui.__class__
677 ui.__class__ = lui.__class__
669
678
670 # (uisetup and extsetup are handled in extensions.loadall)
679 # (uisetup and extsetup are handled in extensions.loadall)
671
680
672 for name, module in exts:
681 for name, module in exts:
673 for objname, loadermod, loadername in extraloaders:
682 for objname, loadermod, loadername in extraloaders:
674 extraobj = getattr(module, objname, None)
683 extraobj = getattr(module, objname, None)
675 if extraobj is not None:
684 if extraobj is not None:
676 getattr(loadermod, loadername)(ui, name, extraobj)
685 getattr(loadermod, loadername)(ui, name, extraobj)
677 _loaded.add(name)
686 _loaded.add(name)
678
687
679 # (reposetup is handled in hg.repository)
688 # (reposetup is handled in hg.repository)
680
689
681 addaliases(lui, commands.table)
690 addaliases(lui, commands.table)
682
691
683 # All aliases and commands are completely defined, now.
692 # All aliases and commands are completely defined, now.
684 # Check abbreviation/ambiguity of shell alias.
693 # Check abbreviation/ambiguity of shell alias.
685 shellaliasfn = _checkshellalias(lui, ui, args)
694 shellaliasfn = _checkshellalias(lui, ui, args)
686 if shellaliasfn:
695 if shellaliasfn:
687 with profiling.maybeprofile(lui):
696 with profiling.maybeprofile(lui):
688 return shellaliasfn()
697 return shellaliasfn()
689
698
690 # check for fallback encoding
699 # check for fallback encoding
691 fallback = lui.config('ui', 'fallbackencoding')
700 fallback = lui.config('ui', 'fallbackencoding')
692 if fallback:
701 if fallback:
693 encoding.fallbackencoding = fallback
702 encoding.fallbackencoding = fallback
694
703
695 fullargs = args
704 fullargs = args
696 cmd, func, args, options, cmdoptions = _parse(lui, args)
705 cmd, func, args, options, cmdoptions = _parse(lui, args)
697
706
698 if options["config"]:
707 if options["config"]:
699 raise error.Abort(_("option --config may not be abbreviated!"))
708 raise error.Abort(_("option --config may not be abbreviated!"))
700 if options["cwd"]:
709 if options["cwd"]:
701 raise error.Abort(_("option --cwd may not be abbreviated!"))
710 raise error.Abort(_("option --cwd may not be abbreviated!"))
702 if options["repository"]:
711 if options["repository"]:
703 raise error.Abort(_(
712 raise error.Abort(_(
704 "option -R has to be separated from other options (e.g. not -qR) "
713 "option -R has to be separated from other options (e.g. not -qR) "
705 "and --repository may only be abbreviated as --repo!"))
714 "and --repository may only be abbreviated as --repo!"))
706
715
707 if options["encoding"]:
716 if options["encoding"]:
708 encoding.encoding = options["encoding"]
717 encoding.encoding = options["encoding"]
709 if options["encodingmode"]:
718 if options["encodingmode"]:
710 encoding.encodingmode = options["encodingmode"]
719 encoding.encodingmode = options["encodingmode"]
711 if options["time"]:
720 if options["time"]:
712 def get_times():
721 def get_times():
713 t = os.times()
722 t = os.times()
714 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
723 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
715 t = (t[0], t[1], t[2], t[3], time.clock())
724 t = (t[0], t[1], t[2], t[3], time.clock())
716 return t
725 return t
717 s = get_times()
726 s = get_times()
718 def print_time():
727 def print_time():
719 t = get_times()
728 t = get_times()
720 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
729 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
721 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
730 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
722 atexit.register(print_time)
731 atexit.register(print_time)
723
732
724 uis = set([ui, lui])
725
726 if req.repo:
727 uis.add(req.repo.ui)
728
729 if options['verbose'] or options['debug'] or options['quiet']:
733 if options['verbose'] or options['debug'] or options['quiet']:
730 for opt in ('verbose', 'debug', 'quiet'):
734 for opt in ('verbose', 'debug', 'quiet'):
731 val = str(bool(options[opt]))
735 val = str(bool(options[opt]))
732 for ui_ in uis:
736 for ui_ in uis:
733 ui_.setconfig('ui', opt, val, '--' + opt)
737 ui_.setconfig('ui', opt, val, '--' + opt)
734
738
735 if options['profile']:
736 for ui_ in uis:
737 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
738
739 if options['traceback']:
739 if options['traceback']:
740 for ui_ in uis:
740 for ui_ in uis:
741 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
741 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
742
742
743 if options['noninteractive']:
743 if options['noninteractive']:
744 for ui_ in uis:
744 for ui_ in uis:
745 ui_.setconfig('ui', 'interactive', 'off', '-y')
745 ui_.setconfig('ui', 'interactive', 'off', '-y')
746
746
747 if cmdoptions.get('insecure', False):
747 if cmdoptions.get('insecure', False):
748 for ui_ in uis:
748 for ui_ in uis:
749 ui_.insecureconnections = True
749 ui_.insecureconnections = True
750
750
751 if options['version']:
751 if options['version']:
752 return commands.version_(ui)
752 return commands.version_(ui)
753 if options['help']:
753 if options['help']:
754 return commands.help_(ui, cmd, command=cmd is not None)
754 return commands.help_(ui, cmd, command=cmd is not None)
755 elif not cmd:
755 elif not cmd:
756 return commands.help_(ui, 'shortlist')
756 return commands.help_(ui, 'shortlist')
757
757
758 with profiling.maybeprofile(lui):
758 with profiling.maybeprofile(lui):
759 repo = None
759 repo = None
760 cmdpats = args[:]
760 cmdpats = args[:]
761 if not func.norepo:
761 if not func.norepo:
762 # use the repo from the request only if we don't have -R
762 # use the repo from the request only if we don't have -R
763 if not rpath and not cwd:
763 if not rpath and not cwd:
764 repo = req.repo
764 repo = req.repo
765
765
766 if repo:
766 if repo:
767 # set the descriptors of the repo ui to those of ui
767 # set the descriptors of the repo ui to those of ui
768 repo.ui.fin = ui.fin
768 repo.ui.fin = ui.fin
769 repo.ui.fout = ui.fout
769 repo.ui.fout = ui.fout
770 repo.ui.ferr = ui.ferr
770 repo.ui.ferr = ui.ferr
771 else:
771 else:
772 try:
772 try:
773 repo = hg.repository(ui, path=path)
773 repo = hg.repository(ui, path=path)
774 if not repo.local():
774 if not repo.local():
775 raise error.Abort(_("repository '%s' is not local")
775 raise error.Abort(_("repository '%s' is not local")
776 % path)
776 % path)
777 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
777 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
778 'repo')
778 'repo')
779 except error.RequirementError:
779 except error.RequirementError:
780 raise
780 raise
781 except error.RepoError:
781 except error.RepoError:
782 if rpath and rpath[-1]: # invalid -R path
782 if rpath and rpath[-1]: # invalid -R path
783 raise
783 raise
784 if not func.optionalrepo:
784 if not func.optionalrepo:
785 if func.inferrepo and args and not path:
785 if func.inferrepo and args and not path:
786 # try to infer -R from command args
786 # try to infer -R from command args
787 repos = map(cmdutil.findrepo, args)
787 repos = map(cmdutil.findrepo, args)
788 guess = repos[0]
788 guess = repos[0]
789 if guess and repos.count(guess) == len(repos):
789 if guess and repos.count(guess) == len(repos):
790 req.args = ['--repository', guess] + fullargs
790 req.args = ['--repository', guess] + fullargs
791 return _dispatch(req)
791 return _dispatch(req)
792 if not path:
792 if not path:
793 raise error.RepoError(_("no repository found in"
793 raise error.RepoError(_("no repository found in"
794 " '%s' (.hg not found)")
794 " '%s' (.hg not found)")
795 % pycompat.getcwd())
795 % pycompat.getcwd())
796 raise
796 raise
797 if repo:
797 if repo:
798 ui = repo.ui
798 ui = repo.ui
799 if options['hidden']:
799 if options['hidden']:
800 repo = repo.unfiltered()
800 repo = repo.unfiltered()
801 args.insert(0, repo)
801 args.insert(0, repo)
802 elif rpath:
802 elif rpath:
803 ui.warn(_("warning: --repository ignored\n"))
803 ui.warn(_("warning: --repository ignored\n"))
804
804
805 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
805 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
806 ui.log("command", '%s\n', msg)
806 ui.log("command", '%s\n', msg)
807 strcmdopt = pycompat.strkwargs(cmdoptions)
807 strcmdopt = pycompat.strkwargs(cmdoptions)
808 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
808 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
809 try:
809 try:
810 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
810 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
811 cmdpats, cmdoptions)
811 cmdpats, cmdoptions)
812 finally:
812 finally:
813 if repo and repo != req.repo:
813 if repo and repo != req.repo:
814 repo.close()
814 repo.close()
815
815
816 def _runcommand(ui, options, cmd, cmdfunc):
816 def _runcommand(ui, options, cmd, cmdfunc):
817 """Run a command function, possibly with profiling enabled."""
817 """Run a command function, possibly with profiling enabled."""
818 try:
818 try:
819 return cmdfunc()
819 return cmdfunc()
820 except error.SignatureError:
820 except error.SignatureError:
821 raise error.CommandError(cmd, _('invalid arguments'))
821 raise error.CommandError(cmd, _('invalid arguments'))
822
822
823 def _exceptionwarning(ui):
823 def _exceptionwarning(ui):
824 """Produce a warning message for the current active exception"""
824 """Produce a warning message for the current active exception"""
825
825
826 # For compatibility checking, we discard the portion of the hg
826 # For compatibility checking, we discard the portion of the hg
827 # version after the + on the assumption that if a "normal
827 # version after the + on the assumption that if a "normal
828 # user" is running a build with a + in it the packager
828 # user" is running a build with a + in it the packager
829 # probably built from fairly close to a tag and anyone with a
829 # probably built from fairly close to a tag and anyone with a
830 # 'make local' copy of hg (where the version number can be out
830 # 'make local' copy of hg (where the version number can be out
831 # of date) will be clueful enough to notice the implausible
831 # of date) will be clueful enough to notice the implausible
832 # version number and try updating.
832 # version number and try updating.
833 ct = util.versiontuple(n=2)
833 ct = util.versiontuple(n=2)
834 worst = None, ct, ''
834 worst = None, ct, ''
835 if ui.config('ui', 'supportcontact', None) is None:
835 if ui.config('ui', 'supportcontact', None) is None:
836 for name, mod in extensions.extensions():
836 for name, mod in extensions.extensions():
837 testedwith = getattr(mod, 'testedwith', '')
837 testedwith = getattr(mod, 'testedwith', '')
838 report = getattr(mod, 'buglink', _('the extension author.'))
838 report = getattr(mod, 'buglink', _('the extension author.'))
839 if not testedwith.strip():
839 if not testedwith.strip():
840 # We found an untested extension. It's likely the culprit.
840 # We found an untested extension. It's likely the culprit.
841 worst = name, 'unknown', report
841 worst = name, 'unknown', report
842 break
842 break
843
843
844 # Never blame on extensions bundled with Mercurial.
844 # Never blame on extensions bundled with Mercurial.
845 if extensions.ismoduleinternal(mod):
845 if extensions.ismoduleinternal(mod):
846 continue
846 continue
847
847
848 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
848 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
849 if ct in tested:
849 if ct in tested:
850 continue
850 continue
851
851
852 lower = [t for t in tested if t < ct]
852 lower = [t for t in tested if t < ct]
853 nearest = max(lower or tested)
853 nearest = max(lower or tested)
854 if worst[0] is None or nearest < worst[1]:
854 if worst[0] is None or nearest < worst[1]:
855 worst = name, nearest, report
855 worst = name, nearest, report
856 if worst[0] is not None:
856 if worst[0] is not None:
857 name, testedwith, report = worst
857 name, testedwith, report = worst
858 if not isinstance(testedwith, str):
858 if not isinstance(testedwith, str):
859 testedwith = '.'.join([str(c) for c in testedwith])
859 testedwith = '.'.join([str(c) for c in testedwith])
860 warning = (_('** Unknown exception encountered with '
860 warning = (_('** Unknown exception encountered with '
861 'possibly-broken third-party extension %s\n'
861 'possibly-broken third-party extension %s\n'
862 '** which supports versions %s of Mercurial.\n'
862 '** which supports versions %s of Mercurial.\n'
863 '** Please disable %s and try your action again.\n'
863 '** Please disable %s and try your action again.\n'
864 '** If that fixes the bug please report it to %s\n')
864 '** If that fixes the bug please report it to %s\n')
865 % (name, testedwith, name, report))
865 % (name, testedwith, name, report))
866 else:
866 else:
867 bugtracker = ui.config('ui', 'supportcontact', None)
867 bugtracker = ui.config('ui', 'supportcontact', None)
868 if bugtracker is None:
868 if bugtracker is None:
869 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
869 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
870 warning = (_("** unknown exception encountered, "
870 warning = (_("** unknown exception encountered, "
871 "please report by visiting\n** ") + bugtracker + '\n')
871 "please report by visiting\n** ") + bugtracker + '\n')
872 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
872 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
873 (_("** Mercurial Distributed SCM (version %s)\n") %
873 (_("** Mercurial Distributed SCM (version %s)\n") %
874 util.version()) +
874 util.version()) +
875 (_("** Extensions loaded: %s\n") %
875 (_("** Extensions loaded: %s\n") %
876 ", ".join([x[0] for x in extensions.extensions()])))
876 ", ".join([x[0] for x in extensions.extensions()])))
877 return warning
877 return warning
878
878
879 def handlecommandexception(ui):
879 def handlecommandexception(ui):
880 """Produce a warning message for broken commands
880 """Produce a warning message for broken commands
881
881
882 Called when handling an exception; the exception is reraised if
882 Called when handling an exception; the exception is reraised if
883 this function returns False, ignored otherwise.
883 this function returns False, ignored otherwise.
884 """
884 """
885 warning = _exceptionwarning(ui)
885 warning = _exceptionwarning(ui)
886 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
886 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
887 ui.warn(warning)
887 ui.warn(warning)
888 return False # re-raise the exception
888 return False # re-raise the exception
General Comments 0
You need to be logged in to leave comments. Login now