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