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