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