comparison mercurial/cmdutil.py @ 4549:0c61124ad877

dispatch: move dispatching code to cmdutil
author Matt Mackall <mpm@selenic.com>
date Mon, 11 Jun 2007 21:09:24 -0500
parents c9fcebbfc422
children 6ed91894261e
comparison
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: