mercurial/cmdutil.py
changeset 4549 0c61124ad877
parent 4548 c9fcebbfc422
child 4550 6ed91894261e
equal deleted inserted replaced
4548:c9fcebbfc422 4549:0c61124ad877
     5 # This software may be used and distributed according to the terms
     5 # This software may be used and distributed according to the terms
     6 # of the GNU General Public License, incorporated herein by reference.
     6 # of the GNU General Public License, incorporated herein by reference.
     7 
     7 
     8 from node import *
     8 from node import *
     9 from i18n import _
     9 from i18n import _
    10 import os, sys, mdiff, bdiff, util, templater, patch
    10 import os, sys, mdiff, bdiff, util, templater, patch, commands
       
    11 import atexit, signal, pdb, hg, lock, fancyopts, traceback
       
    12 import socket, revlog, version, extensions, errno
    11 
    13 
    12 revrangesep = ':'
    14 revrangesep = ':'
       
    15 
       
    16 class UnknownCommand(Exception):
       
    17     """Exception raised if command is not in the command table."""
       
    18 class AmbiguousCommand(Exception):
       
    19     """Exception raised if command shortcut matches more than one command."""
       
    20 class ParseError(Exception):
       
    21     """Exception raised on errors in parsing the command line."""
       
    22 
       
    23 def runcatch(u, args):
       
    24     def catchterm(*args):
       
    25         raise util.SignalInterrupt
       
    26 
       
    27     for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
       
    28         num = getattr(signal, name, None)
       
    29         if num: signal.signal(num, catchterm)
       
    30 
       
    31     try:
       
    32         return dispatch(u, args)
       
    33     except ParseError, inst:
       
    34         if inst.args[0]:
       
    35             u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
       
    36             commands.help_(u, inst.args[0])
       
    37         else:
       
    38             u.warn(_("hg: %s\n") % inst.args[1])
       
    39             commands.help_(u, 'shortlist')
       
    40     except AmbiguousCommand, inst:
       
    41         u.warn(_("hg: command '%s' is ambiguous:\n    %s\n") %
       
    42                 (inst.args[0], " ".join(inst.args[1])))
       
    43     except UnknownCommand, inst:
       
    44         u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
       
    45         commands.help_(u, 'shortlist')
       
    46     except hg.RepoError, inst:
       
    47         u.warn(_("abort: %s!\n") % inst)
       
    48     except lock.LockHeld, inst:
       
    49         if inst.errno == errno.ETIMEDOUT:
       
    50             reason = _('timed out waiting for lock held by %s') % inst.locker
       
    51         else:
       
    52             reason = _('lock held by %s') % inst.locker
       
    53         u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
       
    54     except lock.LockUnavailable, inst:
       
    55         u.warn(_("abort: could not lock %s: %s\n") %
       
    56                (inst.desc or inst.filename, inst.strerror))
       
    57     except revlog.RevlogError, inst:
       
    58         u.warn(_("abort: %s!\n") % inst)
       
    59     except util.SignalInterrupt:
       
    60         u.warn(_("killed!\n"))
       
    61     except KeyboardInterrupt:
       
    62         try:
       
    63             u.warn(_("interrupted!\n"))
       
    64         except IOError, inst:
       
    65             if inst.errno == errno.EPIPE:
       
    66                 if u.debugflag:
       
    67                     u.warn(_("\nbroken pipe\n"))
       
    68             else:
       
    69                 raise
       
    70     except socket.error, inst:
       
    71         u.warn(_("abort: %s\n") % inst[1])
       
    72     except IOError, inst:
       
    73         if hasattr(inst, "code"):
       
    74             u.warn(_("abort: %s\n") % inst)
       
    75         elif hasattr(inst, "reason"):
       
    76             try: # usually it is in the form (errno, strerror)
       
    77                 reason = inst.reason.args[1]
       
    78             except: # it might be anything, for example a string
       
    79                 reason = inst.reason
       
    80             u.warn(_("abort: error: %s\n") % reason)
       
    81         elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
       
    82             if u.debugflag:
       
    83                 u.warn(_("broken pipe\n"))
       
    84         elif getattr(inst, "strerror", None):
       
    85             if getattr(inst, "filename", None):
       
    86                 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
       
    87             else:
       
    88                 u.warn(_("abort: %s\n") % inst.strerror)
       
    89         else:
       
    90             raise
       
    91     except OSError, inst:
       
    92         if getattr(inst, "filename", None):
       
    93             u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
       
    94         else:
       
    95             u.warn(_("abort: %s\n") % inst.strerror)
       
    96     except util.UnexpectedOutput, inst:
       
    97         u.warn(_("abort: %s") % inst[0])
       
    98         if not isinstance(inst[1], basestring):
       
    99             u.warn(" %r\n" % (inst[1],))
       
   100         elif not inst[1]:
       
   101             u.warn(_(" empty string\n"))
       
   102         else:
       
   103             u.warn("\n%r\n" % util.ellipsis(inst[1]))
       
   104     except util.Abort, inst:
       
   105         u.warn(_("abort: %s\n") % inst)
       
   106     except TypeError, inst:
       
   107         # was this an argument error?
       
   108         tb = traceback.extract_tb(sys.exc_info()[2])
       
   109         if len(tb) > 2: # no
       
   110             raise
       
   111         u.debug(inst, "\n")
       
   112         u.warn(_("%s: invalid arguments\n") % cmd)
       
   113         commands.help_(u, cmd)
       
   114     except SystemExit, inst:
       
   115         # Commands shouldn't sys.exit directly, but give a return code.
       
   116         # Just in case catch this and and pass exit code to caller.
       
   117         return inst.code
       
   118     except:
       
   119         u.warn(_("** unknown exception encountered, details follow\n"))
       
   120         u.warn(_("** report bug details to "
       
   121                  "http://www.selenic.com/mercurial/bts\n"))
       
   122         u.warn(_("** or mercurial@selenic.com\n"))
       
   123         u.warn(_("** Mercurial Distributed SCM (version %s)\n")
       
   124                % version.get_version())
       
   125         raise
       
   126 
       
   127     return -1
       
   128 
       
   129 def findpossible(ui, cmd):
       
   130     """
       
   131     Return cmd -> (aliases, command table entry)
       
   132     for each matching command.
       
   133     Return debug commands (or their aliases) only if no normal command matches.
       
   134     """
       
   135     choice = {}
       
   136     debugchoice = {}
       
   137     for e in commands.table.keys():
       
   138         aliases = e.lstrip("^").split("|")
       
   139         found = None
       
   140         if cmd in aliases:
       
   141             found = cmd
       
   142         elif not ui.config("ui", "strict"):
       
   143             for a in aliases:
       
   144                 if a.startswith(cmd):
       
   145                     found = a
       
   146                     break
       
   147         if found is not None:
       
   148             if aliases[0].startswith("debug") or found.startswith("debug"):
       
   149                 debugchoice[found] = (aliases, commands.table[e])
       
   150             else:
       
   151                 choice[found] = (aliases, commands.table[e])
       
   152 
       
   153     if not choice and debugchoice:
       
   154         choice = debugchoice
       
   155 
       
   156     return choice
       
   157 
       
   158 def findcmd(ui, cmd):
       
   159     """Return (aliases, command table entry) for command string."""
       
   160     choice = findpossible(ui, cmd)
       
   161 
       
   162     if choice.has_key(cmd):
       
   163         return choice[cmd]
       
   164 
       
   165     if len(choice) > 1:
       
   166         clist = choice.keys()
       
   167         clist.sort()
       
   168         raise AmbiguousCommand(cmd, clist)
       
   169 
       
   170     if choice:
       
   171         return choice.values()[0]
       
   172 
       
   173     raise UnknownCommand(cmd)
       
   174 
       
   175 def parse(ui, args):
       
   176     options = {}
       
   177     cmdoptions = {}
       
   178 
       
   179     try:
       
   180         args = fancyopts.fancyopts(args, commands.globalopts, options)
       
   181     except fancyopts.getopt.GetoptError, inst:
       
   182         raise ParseError(None, inst)
       
   183 
       
   184     if args:
       
   185         cmd, args = args[0], args[1:]
       
   186         aliases, i = findcmd(ui, cmd)
       
   187         cmd = aliases[0]
       
   188         defaults = ui.config("defaults", cmd)
       
   189         if defaults:
       
   190             args = shlex.split(defaults) + args
       
   191         c = list(i[1])
       
   192     else:
       
   193         cmd = None
       
   194         c = []
       
   195 
       
   196     # combine global options into local
       
   197     for o in commands.globalopts:
       
   198         c.append((o[0], o[1], options[o[1]], o[3]))
       
   199 
       
   200     try:
       
   201         args = fancyopts.fancyopts(args, c, cmdoptions)
       
   202     except fancyopts.getopt.GetoptError, inst:
       
   203         raise ParseError(cmd, inst)
       
   204 
       
   205     # separate global options back out
       
   206     for o in commands.globalopts:
       
   207         n = o[1]
       
   208         options[n] = cmdoptions[n]
       
   209         del cmdoptions[n]
       
   210 
       
   211     return (cmd, cmd and i[0] or None, args, options, cmdoptions)
       
   212 
       
   213 def parseconfig(config):
       
   214     """parse the --config options from the command line"""
       
   215     parsed = []
       
   216     for cfg in config:
       
   217         try:
       
   218             name, value = cfg.split('=', 1)
       
   219             section, name = name.split('.', 1)
       
   220             if not section or not name:
       
   221                 raise IndexError
       
   222             parsed.append((section, name, value))
       
   223         except (IndexError, ValueError):
       
   224             raise util.Abort(_('malformed --config option: %s') % cfg)
       
   225     return parsed
       
   226 
       
   227 def dispatch(u, args):
       
   228     extensions.loadall(u)
       
   229     u.addreadhook(extensions.loadall)
       
   230 
       
   231     cmd, func, args, options, cmdoptions = parse(u, args)
       
   232 
       
   233     if options["encoding"]:
       
   234         util._encoding = options["encoding"]
       
   235     if options["encodingmode"]:
       
   236         util._encodingmode = options["encodingmode"]
       
   237     if options["time"]:
       
   238         def get_times():
       
   239             t = os.times()
       
   240             if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
       
   241                 t = (t[0], t[1], t[2], t[3], time.clock())
       
   242             return t
       
   243         s = get_times()
       
   244         def print_time():
       
   245             t = get_times()
       
   246             u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
       
   247                 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
       
   248         atexit.register(print_time)
       
   249 
       
   250     if options['cwd']:
       
   251         os.chdir(options['cwd'])
       
   252 
       
   253     u.updateopts(options["verbose"], options["debug"], options["quiet"],
       
   254                  not options["noninteractive"], options["traceback"],
       
   255                  parseconfig(options["config"]))
       
   256 
       
   257     path = u.expandpath(options["repository"]) or ""
       
   258     repo = path and hg.repository(u, path=path) or None
       
   259     if repo and not repo.local():
       
   260         raise util.Abort(_("repository '%s' is not local") % path)
       
   261 
       
   262     if options['help']:
       
   263         return commands.help_(u, cmd, options['version'])
       
   264     elif options['version']:
       
   265         return commands.version_(u)
       
   266     elif not cmd:
       
   267         return commands.help_(u, 'shortlist')
       
   268 
       
   269     if cmd not in commands.norepo.split():
       
   270         try:
       
   271             if not repo:
       
   272                 repo = hg.repository(u, path=path)
       
   273             u = repo.ui
       
   274         except hg.RepoError:
       
   275             if cmd not in commands.optionalrepo.split():
       
   276                 raise
       
   277         d = lambda: func(u, repo, *args, **cmdoptions)
       
   278     else:
       
   279         d = lambda: func(u, *args, **cmdoptions)
       
   280 
       
   281     return runcommand(u, options, d)
    13 
   282 
    14 def runcommand(u, options, d):
   283 def runcommand(u, options, d):
    15     # enter the debugger before command execution
   284     # enter the debugger before command execution
    16     if options['debugger']:
   285     if options['debugger']:
    17         pdb.set_trace()
   286         pdb.set_trace()
    61         # enter the debugger when we hit an exception
   330         # enter the debugger when we hit an exception
    62         if options['debugger']:
   331         if options['debugger']:
    63             pdb.post_mortem(sys.exc_info()[2])
   332             pdb.post_mortem(sys.exc_info()[2])
    64         u.print_exc()
   333         u.print_exc()
    65         raise
   334         raise
       
   335 
       
   336 def bail_if_changed(repo):
       
   337     modified, added, removed, deleted = repo.status()[:4]
       
   338     if modified or added or removed or deleted:
       
   339         raise util.Abort(_("outstanding uncommitted changes"))
       
   340 
       
   341 def logmessage(opts):
       
   342     """ get the log message according to -m and -l option """
       
   343     message = opts['message']
       
   344     logfile = opts['logfile']
       
   345 
       
   346     if message and logfile:
       
   347         raise util.Abort(_('options --message and --logfile are mutually '
       
   348                            'exclusive'))
       
   349     if not message and logfile:
       
   350         try:
       
   351             if logfile == '-':
       
   352                 message = sys.stdin.read()
       
   353             else:
       
   354                 message = open(logfile).read()
       
   355         except IOError, inst:
       
   356             raise util.Abort(_("can't read commit message '%s': %s") %
       
   357                              (logfile, inst.strerror))
       
   358     return message
       
   359 
       
   360 def setremoteconfig(ui, opts):
       
   361     "copy remote options to ui tree"
       
   362     if opts.get('ssh'):
       
   363         ui.setconfig("ui", "ssh", opts['ssh'])
       
   364     if opts.get('remotecmd'):
       
   365         ui.setconfig("ui", "remotecmd", opts['remotecmd'])
    66 
   366 
    67 def parseurl(url, revs):
   367 def parseurl(url, revs):
    68     '''parse url#branch, returning url, branch + revs'''
   368     '''parse url#branch, returning url, branch + revs'''
    69 
   369 
    70     if '#' not in url:
   370     if '#' not in url: