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