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