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() |