/mercurial/dispatch.py

https://bitbucket.org/mirror/mercurial/ · Python · 911 lines · 773 code · 80 blank · 58 comment · 249 complexity · 6deb2cd9395380c1859cd7845d1852ad MD5 · raw file

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