diff -r c9fcebbfc422 -r 0c61124ad877 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Mon Jun 11 21:09:24 2007 -0500 +++ b/mercurial/cmdutil.py Mon Jun 11 21:09:24 2007 -0500 @@ -7,10 +7,279 @@ from node import * from i18n import _ -import os, sys, mdiff, bdiff, util, templater, patch +import os, sys, mdiff, bdiff, util, templater, patch, commands +import atexit, signal, pdb, hg, lock, fancyopts, traceback +import socket, revlog, version, extensions, errno revrangesep = ':' +class UnknownCommand(Exception): + """Exception raised if command is not in the command table.""" +class AmbiguousCommand(Exception): + """Exception raised if command shortcut matches more than one command.""" +class ParseError(Exception): + """Exception raised on errors in parsing the command line.""" + +def runcatch(u, args): + def catchterm(*args): + raise util.SignalInterrupt + + for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': + num = getattr(signal, name, None) + if num: signal.signal(num, catchterm) + + try: + return dispatch(u, args) + except ParseError, inst: + if inst.args[0]: + u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1])) + commands.help_(u, inst.args[0]) + else: + u.warn(_("hg: %s\n") % inst.args[1]) + commands.help_(u, 'shortlist') + except AmbiguousCommand, inst: + u.warn(_("hg: command '%s' is ambiguous:\n %s\n") % + (inst.args[0], " ".join(inst.args[1]))) + except UnknownCommand, inst: + u.warn(_("hg: unknown command '%s'\n") % inst.args[0]) + commands.help_(u, 'shortlist') + except hg.RepoError, inst: + u.warn(_("abort: %s!\n") % inst) + except lock.LockHeld, inst: + if inst.errno == errno.ETIMEDOUT: + reason = _('timed out waiting for lock held by %s') % inst.locker + else: + reason = _('lock held by %s') % inst.locker + u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason)) + except lock.LockUnavailable, inst: + u.warn(_("abort: could not lock %s: %s\n") % + (inst.desc or inst.filename, inst.strerror)) + except revlog.RevlogError, inst: + u.warn(_("abort: %s!\n") % inst) + except util.SignalInterrupt: + u.warn(_("killed!\n")) + except KeyboardInterrupt: + try: + u.warn(_("interrupted!\n")) + except IOError, inst: + if inst.errno == errno.EPIPE: + if u.debugflag: + u.warn(_("\nbroken pipe\n")) + else: + raise + except socket.error, inst: + u.warn(_("abort: %s\n") % inst[1]) + except IOError, inst: + if hasattr(inst, "code"): + u.warn(_("abort: %s\n") % inst) + elif hasattr(inst, "reason"): + try: # usually it is in the form (errno, strerror) + reason = inst.reason.args[1] + except: # it might be anything, for example a string + reason = inst.reason + u.warn(_("abort: error: %s\n") % reason) + elif hasattr(inst, "args") and inst[0] == errno.EPIPE: + if u.debugflag: + u.warn(_("broken pipe\n")) + elif getattr(inst, "strerror", None): + if getattr(inst, "filename", None): + u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename)) + else: + u.warn(_("abort: %s\n") % inst.strerror) + else: + raise + except OSError, inst: + if getattr(inst, "filename", None): + u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename)) + else: + u.warn(_("abort: %s\n") % inst.strerror) + except util.UnexpectedOutput, inst: + u.warn(_("abort: %s") % inst[0]) + if not isinstance(inst[1], basestring): + u.warn(" %r\n" % (inst[1],)) + elif not inst[1]: + u.warn(_(" empty string\n")) + else: + u.warn("\n%r\n" % util.ellipsis(inst[1])) + except util.Abort, inst: + u.warn(_("abort: %s\n") % inst) + except TypeError, inst: + # was this an argument error? + tb = traceback.extract_tb(sys.exc_info()[2]) + if len(tb) > 2: # no + raise + u.debug(inst, "\n") + u.warn(_("%s: invalid arguments\n") % cmd) + commands.help_(u, cmd) + except SystemExit, inst: + # Commands shouldn't sys.exit directly, but give a return code. + # Just in case catch this and and pass exit code to caller. + return inst.code + except: + u.warn(_("** unknown exception encountered, details follow\n")) + u.warn(_("** report bug details to " + "http://www.selenic.com/mercurial/bts\n")) + u.warn(_("** or mercurial@selenic.com\n")) + u.warn(_("** Mercurial Distributed SCM (version %s)\n") + % version.get_version()) + raise + + return -1 + +def findpossible(ui, cmd): + """ + Return cmd -> (aliases, command table entry) + for each matching command. + Return debug commands (or their aliases) only if no normal command matches. + """ + choice = {} + debugchoice = {} + for e in commands.table.keys(): + aliases = e.lstrip("^").split("|") + found = None + if cmd in aliases: + found = cmd + elif not ui.config("ui", "strict"): + for a in aliases: + if a.startswith(cmd): + found = a + break + if found is not None: + if aliases[0].startswith("debug") or found.startswith("debug"): + debugchoice[found] = (aliases, commands.table[e]) + else: + choice[found] = (aliases, commands.table[e]) + + if not choice and debugchoice: + choice = debugchoice + + return choice + +def findcmd(ui, cmd): + """Return (aliases, command table entry) for command string.""" + choice = findpossible(ui, cmd) + + if choice.has_key(cmd): + return choice[cmd] + + if len(choice) > 1: + clist = choice.keys() + clist.sort() + raise AmbiguousCommand(cmd, clist) + + if choice: + return choice.values()[0] + + raise UnknownCommand(cmd) + +def parse(ui, args): + options = {} + cmdoptions = {} + + try: + args = fancyopts.fancyopts(args, commands.globalopts, options) + except fancyopts.getopt.GetoptError, inst: + raise ParseError(None, inst) + + if args: + cmd, args = args[0], args[1:] + aliases, i = findcmd(ui, cmd) + cmd = aliases[0] + defaults = ui.config("defaults", cmd) + if defaults: + args = shlex.split(defaults) + args + c = list(i[1]) + else: + cmd = None + c = [] + + # combine global options into local + for o in commands.globalopts: + c.append((o[0], o[1], options[o[1]], o[3])) + + try: + args = fancyopts.fancyopts(args, c, cmdoptions) + except fancyopts.getopt.GetoptError, inst: + raise ParseError(cmd, inst) + + # separate global options back out + for o in commands.globalopts: + n = o[1] + options[n] = cmdoptions[n] + del cmdoptions[n] + + return (cmd, cmd and i[0] or None, args, options, cmdoptions) + +def parseconfig(config): + """parse the --config options from the command line""" + parsed = [] + for cfg in config: + try: + name, value = cfg.split('=', 1) + section, name = name.split('.', 1) + if not section or not name: + raise IndexError + parsed.append((section, name, value)) + except (IndexError, ValueError): + raise util.Abort(_('malformed --config option: %s') % cfg) + return parsed + +def dispatch(u, args): + extensions.loadall(u) + u.addreadhook(extensions.loadall) + + cmd, func, args, options, cmdoptions = parse(u, args) + + if options["encoding"]: + util._encoding = options["encoding"] + if options["encodingmode"]: + util._encodingmode = options["encodingmode"] + if options["time"]: + def get_times(): + t = os.times() + if t[4] == 0.0: # Windows leaves this as zero, so use time.clock() + t = (t[0], t[1], t[2], t[3], time.clock()) + return t + s = get_times() + def print_time(): + t = get_times() + u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % + (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3])) + atexit.register(print_time) + + if options['cwd']: + os.chdir(options['cwd']) + + u.updateopts(options["verbose"], options["debug"], options["quiet"], + not options["noninteractive"], options["traceback"], + parseconfig(options["config"])) + + path = u.expandpath(options["repository"]) or "" + repo = path and hg.repository(u, path=path) or None + if repo and not repo.local(): + raise util.Abort(_("repository '%s' is not local") % path) + + if options['help']: + return commands.help_(u, cmd, options['version']) + elif options['version']: + return commands.version_(u) + elif not cmd: + return commands.help_(u, 'shortlist') + + if cmd not in commands.norepo.split(): + try: + if not repo: + repo = hg.repository(u, path=path) + u = repo.ui + except hg.RepoError: + if cmd not in commands.optionalrepo.split(): + raise + d = lambda: func(u, repo, *args, **cmdoptions) + else: + d = lambda: func(u, *args, **cmdoptions) + + return runcommand(u, options, d) + def runcommand(u, options, d): # enter the debugger before command execution if options['debugger']: @@ -64,6 +333,37 @@ u.print_exc() raise +def bail_if_changed(repo): + modified, added, removed, deleted = repo.status()[:4] + if modified or added or removed or deleted: + raise util.Abort(_("outstanding uncommitted changes")) + +def logmessage(opts): + """ get the log message according to -m and -l option """ + message = opts['message'] + logfile = opts['logfile'] + + if message and logfile: + raise util.Abort(_('options --message and --logfile are mutually ' + 'exclusive')) + if not message and logfile: + try: + if logfile == '-': + message = sys.stdin.read() + else: + message = open(logfile).read() + except IOError, inst: + raise util.Abort(_("can't read commit message '%s': %s") % + (logfile, inst.strerror)) + return message + +def setremoteconfig(ui, opts): + "copy remote options to ui tree" + if opts.get('ssh'): + ui.setconfig("ui", "ssh", opts['ssh']) + if opts.get('remotecmd'): + ui.setconfig("ui", "remotecmd", opts['remotecmd']) + def parseurl(url, revs): '''parse url#branch, returning url, branch + revs'''