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