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