##// END OF EJS Templates
dispatch: mark callcatch() as a private function
Yuya Nishihara -
r32040:0fb78cb9 default
parent child Browse files
Show More
@@ -1,961 +1,961 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 difflib
10 import difflib
11 import errno
11 import errno
12 import getopt
12 import getopt
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import signal
16 import signal
17 import sys
17 import sys
18 import time
18 import time
19 import traceback
19 import traceback
20
20
21
21
22 from .i18n import _
22 from .i18n import _
23
23
24 from . import (
24 from . import (
25 cmdutil,
25 cmdutil,
26 color,
26 color,
27 commands,
27 commands,
28 debugcommands,
28 debugcommands,
29 demandimport,
29 demandimport,
30 encoding,
30 encoding,
31 error,
31 error,
32 extensions,
32 extensions,
33 fancyopts,
33 fancyopts,
34 fileset,
34 fileset,
35 help,
35 help,
36 hg,
36 hg,
37 hook,
37 hook,
38 profiling,
38 profiling,
39 pycompat,
39 pycompat,
40 revset,
40 revset,
41 scmutil,
41 scmutil,
42 templatefilters,
42 templatefilters,
43 templatekw,
43 templatekw,
44 templater,
44 templater,
45 ui as uimod,
45 ui as uimod,
46 util,
46 util,
47 )
47 )
48
48
49 class request(object):
49 class request(object):
50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
51 ferr=None):
51 ferr=None):
52 self.args = args
52 self.args = args
53 self.ui = ui
53 self.ui = ui
54 self.repo = repo
54 self.repo = repo
55
55
56 # input/output/error streams
56 # input/output/error streams
57 self.fin = fin
57 self.fin = fin
58 self.fout = fout
58 self.fout = fout
59 self.ferr = ferr
59 self.ferr = ferr
60
60
61 def _runexithandlers(self):
61 def _runexithandlers(self):
62 exc = None
62 exc = None
63 handlers = self.ui._exithandlers
63 handlers = self.ui._exithandlers
64 try:
64 try:
65 while handlers:
65 while handlers:
66 func, args, kwargs = handlers.pop()
66 func, args, kwargs = handlers.pop()
67 try:
67 try:
68 func(*args, **kwargs)
68 func(*args, **kwargs)
69 except: # re-raises below
69 except: # re-raises below
70 if exc is None:
70 if exc is None:
71 exc = sys.exc_info()[1]
71 exc = sys.exc_info()[1]
72 self.ui.warn(('error in exit handlers:\n'))
72 self.ui.warn(('error in exit handlers:\n'))
73 self.ui.traceback(force=True)
73 self.ui.traceback(force=True)
74 finally:
74 finally:
75 if exc is not None:
75 if exc is not None:
76 raise exc
76 raise exc
77
77
78 def run():
78 def run():
79 "run the command in sys.argv"
79 "run the command in sys.argv"
80 req = request(pycompat.sysargv[1:])
80 req = request(pycompat.sysargv[1:])
81 err = None
81 err = None
82 try:
82 try:
83 status = (dispatch(req) or 0) & 255
83 status = (dispatch(req) or 0) & 255
84 except error.StdioError as err:
84 except error.StdioError as err:
85 status = -1
85 status = -1
86 if util.safehasattr(req.ui, 'fout'):
86 if util.safehasattr(req.ui, 'fout'):
87 try:
87 try:
88 req.ui.fout.close()
88 req.ui.fout.close()
89 except IOError as err:
89 except IOError as err:
90 status = -1
90 status = -1
91 if util.safehasattr(req.ui, 'ferr'):
91 if util.safehasattr(req.ui, 'ferr'):
92 if err is not None and err.errno != errno.EPIPE:
92 if err is not None and err.errno != errno.EPIPE:
93 req.ui.ferr.write('abort: %s\n' % err.strerror)
93 req.ui.ferr.write('abort: %s\n' % err.strerror)
94 req.ui.ferr.close()
94 req.ui.ferr.close()
95 sys.exit(status & 255)
95 sys.exit(status & 255)
96
96
97 def _getsimilar(symbols, value):
97 def _getsimilar(symbols, value):
98 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
98 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
99 # The cutoff for similarity here is pretty arbitrary. It should
99 # The cutoff for similarity here is pretty arbitrary. It should
100 # probably be investigated and tweaked.
100 # probably be investigated and tweaked.
101 return [s for s in symbols if sim(s) > 0.6]
101 return [s for s in symbols if sim(s) > 0.6]
102
102
103 def _reportsimilar(write, similar):
103 def _reportsimilar(write, similar):
104 if len(similar) == 1:
104 if len(similar) == 1:
105 write(_("(did you mean %s?)\n") % similar[0])
105 write(_("(did you mean %s?)\n") % similar[0])
106 elif similar:
106 elif similar:
107 ss = ", ".join(sorted(similar))
107 ss = ", ".join(sorted(similar))
108 write(_("(did you mean one of %s?)\n") % ss)
108 write(_("(did you mean one of %s?)\n") % ss)
109
109
110 def _formatparse(write, inst):
110 def _formatparse(write, inst):
111 similar = []
111 similar = []
112 if isinstance(inst, error.UnknownIdentifier):
112 if isinstance(inst, error.UnknownIdentifier):
113 # make sure to check fileset first, as revset can invoke fileset
113 # make sure to check fileset first, as revset can invoke fileset
114 similar = _getsimilar(inst.symbols, inst.function)
114 similar = _getsimilar(inst.symbols, inst.function)
115 if len(inst.args) > 1:
115 if len(inst.args) > 1:
116 write(_("hg: parse error at %s: %s\n") %
116 write(_("hg: parse error at %s: %s\n") %
117 (inst.args[1], inst.args[0]))
117 (inst.args[1], inst.args[0]))
118 if (inst.args[0][0] == ' '):
118 if (inst.args[0][0] == ' '):
119 write(_("unexpected leading whitespace\n"))
119 write(_("unexpected leading whitespace\n"))
120 else:
120 else:
121 write(_("hg: parse error: %s\n") % inst.args[0])
121 write(_("hg: parse error: %s\n") % inst.args[0])
122 _reportsimilar(write, similar)
122 _reportsimilar(write, similar)
123 if inst.hint:
123 if inst.hint:
124 write(_("(%s)\n") % inst.hint)
124 write(_("(%s)\n") % inst.hint)
125
125
126 def _formatargs(args):
126 def _formatargs(args):
127 return ' '.join(util.shellquote(a) for a in args)
127 return ' '.join(util.shellquote(a) for a in args)
128
128
129 def dispatch(req):
129 def dispatch(req):
130 "run the command specified in req.args"
130 "run the command specified in req.args"
131 if req.ferr:
131 if req.ferr:
132 ferr = req.ferr
132 ferr = req.ferr
133 elif req.ui:
133 elif req.ui:
134 ferr = req.ui.ferr
134 ferr = req.ui.ferr
135 else:
135 else:
136 ferr = util.stderr
136 ferr = util.stderr
137
137
138 try:
138 try:
139 if not req.ui:
139 if not req.ui:
140 req.ui = uimod.ui.load()
140 req.ui = uimod.ui.load()
141 if '--traceback' in req.args:
141 if '--traceback' in req.args:
142 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
142 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
143
143
144 # set ui streams from the request
144 # set ui streams from the request
145 if req.fin:
145 if req.fin:
146 req.ui.fin = req.fin
146 req.ui.fin = req.fin
147 if req.fout:
147 if req.fout:
148 req.ui.fout = req.fout
148 req.ui.fout = req.fout
149 if req.ferr:
149 if req.ferr:
150 req.ui.ferr = req.ferr
150 req.ui.ferr = req.ferr
151 except error.Abort as inst:
151 except error.Abort as inst:
152 ferr.write(_("abort: %s\n") % inst)
152 ferr.write(_("abort: %s\n") % inst)
153 if inst.hint:
153 if inst.hint:
154 ferr.write(_("(%s)\n") % inst.hint)
154 ferr.write(_("(%s)\n") % inst.hint)
155 return -1
155 return -1
156 except error.ParseError as inst:
156 except error.ParseError as inst:
157 _formatparse(ferr.write, inst)
157 _formatparse(ferr.write, inst)
158 return -1
158 return -1
159
159
160 msg = _formatargs(req.args)
160 msg = _formatargs(req.args)
161 starttime = util.timer()
161 starttime = util.timer()
162 ret = None
162 ret = None
163 try:
163 try:
164 ret = _runcatch(req)
164 ret = _runcatch(req)
165 except KeyboardInterrupt:
165 except KeyboardInterrupt:
166 try:
166 try:
167 req.ui.warn(_("interrupted!\n"))
167 req.ui.warn(_("interrupted!\n"))
168 except IOError as inst:
168 except IOError as inst:
169 if inst.errno != errno.EPIPE:
169 if inst.errno != errno.EPIPE:
170 raise
170 raise
171 ret = -1
171 ret = -1
172 finally:
172 finally:
173 duration = util.timer() - starttime
173 duration = util.timer() - starttime
174 req.ui.flush()
174 req.ui.flush()
175 if req.ui.logblockedtimes:
175 if req.ui.logblockedtimes:
176 req.ui._blockedtimes['command_duration'] = duration * 1000
176 req.ui._blockedtimes['command_duration'] = duration * 1000
177 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
177 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
178 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
178 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
179 msg, ret or 0, duration)
179 msg, ret or 0, duration)
180 try:
180 try:
181 req._runexithandlers()
181 req._runexithandlers()
182 except: # exiting, so no re-raises
182 except: # exiting, so no re-raises
183 ret = ret or -1
183 ret = ret or -1
184 return ret
184 return ret
185
185
186 def _runcatch(req):
186 def _runcatch(req):
187 def catchterm(*args):
187 def catchterm(*args):
188 raise error.SignalInterrupt
188 raise error.SignalInterrupt
189
189
190 ui = req.ui
190 ui = req.ui
191 try:
191 try:
192 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
192 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
193 num = getattr(signal, name, None)
193 num = getattr(signal, name, None)
194 if num:
194 if num:
195 signal.signal(num, catchterm)
195 signal.signal(num, catchterm)
196 except ValueError:
196 except ValueError:
197 pass # happens if called in a thread
197 pass # happens if called in a thread
198
198
199 def _runcatchfunc():
199 def _runcatchfunc():
200 try:
200 try:
201 debugger = 'pdb'
201 debugger = 'pdb'
202 debugtrace = {
202 debugtrace = {
203 'pdb' : pdb.set_trace
203 'pdb' : pdb.set_trace
204 }
204 }
205 debugmortem = {
205 debugmortem = {
206 'pdb' : pdb.post_mortem
206 'pdb' : pdb.post_mortem
207 }
207 }
208
208
209 # read --config before doing anything else
209 # read --config before doing anything else
210 # (e.g. to change trust settings for reading .hg/hgrc)
210 # (e.g. to change trust settings for reading .hg/hgrc)
211 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
211 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
212
212
213 if req.repo:
213 if req.repo:
214 # copy configs that were passed on the cmdline (--config) to
214 # copy configs that were passed on the cmdline (--config) to
215 # the repo ui
215 # the repo ui
216 for sec, name, val in cfgs:
216 for sec, name, val in cfgs:
217 req.repo.ui.setconfig(sec, name, val, source='--config')
217 req.repo.ui.setconfig(sec, name, val, source='--config')
218
218
219 # developer config: ui.debugger
219 # developer config: ui.debugger
220 debugger = ui.config("ui", "debugger")
220 debugger = ui.config("ui", "debugger")
221 debugmod = pdb
221 debugmod = pdb
222 if not debugger or ui.plain():
222 if not debugger or ui.plain():
223 # if we are in HGPLAIN mode, then disable custom debugging
223 # if we are in HGPLAIN mode, then disable custom debugging
224 debugger = 'pdb'
224 debugger = 'pdb'
225 elif '--debugger' in req.args:
225 elif '--debugger' in req.args:
226 # This import can be slow for fancy debuggers, so only
226 # This import can be slow for fancy debuggers, so only
227 # do it when absolutely necessary, i.e. when actual
227 # do it when absolutely necessary, i.e. when actual
228 # debugging has been requested
228 # debugging has been requested
229 with demandimport.deactivated():
229 with demandimport.deactivated():
230 try:
230 try:
231 debugmod = __import__(debugger)
231 debugmod = __import__(debugger)
232 except ImportError:
232 except ImportError:
233 pass # Leave debugmod = pdb
233 pass # Leave debugmod = pdb
234
234
235 debugtrace[debugger] = debugmod.set_trace
235 debugtrace[debugger] = debugmod.set_trace
236 debugmortem[debugger] = debugmod.post_mortem
236 debugmortem[debugger] = debugmod.post_mortem
237
237
238 # enter the debugger before command execution
238 # enter the debugger before command execution
239 if '--debugger' in req.args:
239 if '--debugger' in req.args:
240 ui.warn(_("entering debugger - "
240 ui.warn(_("entering debugger - "
241 "type c to continue starting hg or h for help\n"))
241 "type c to continue starting hg or h for help\n"))
242
242
243 if (debugger != 'pdb' and
243 if (debugger != 'pdb' and
244 debugtrace[debugger] == debugtrace['pdb']):
244 debugtrace[debugger] == debugtrace['pdb']):
245 ui.warn(_("%s debugger specified "
245 ui.warn(_("%s debugger specified "
246 "but its module was not found\n") % debugger)
246 "but its module was not found\n") % debugger)
247 with demandimport.deactivated():
247 with demandimport.deactivated():
248 debugtrace[debugger]()
248 debugtrace[debugger]()
249 try:
249 try:
250 return _dispatch(req)
250 return _dispatch(req)
251 finally:
251 finally:
252 ui.flush()
252 ui.flush()
253 except: # re-raises
253 except: # re-raises
254 # enter the debugger when we hit an exception
254 # enter the debugger when we hit an exception
255 if '--debugger' in req.args:
255 if '--debugger' in req.args:
256 traceback.print_exc()
256 traceback.print_exc()
257 debugmortem[debugger](sys.exc_info()[2])
257 debugmortem[debugger](sys.exc_info()[2])
258 ui.traceback()
258 ui.traceback()
259 raise
259 raise
260
260
261 return callcatch(ui, _runcatchfunc)
261 return _callcatch(ui, _runcatchfunc)
262
262
263 def callcatch(ui, func):
263 def _callcatch(ui, func):
264 """like scmutil.callcatch but handles more high-level exceptions about
264 """like scmutil.callcatch but handles more high-level exceptions about
265 config parsing and commands. besides, use handlecommandexception to handle
265 config parsing and commands. besides, use handlecommandexception to handle
266 uncaught exceptions.
266 uncaught exceptions.
267 """
267 """
268 try:
268 try:
269 return scmutil.callcatch(ui, func)
269 return scmutil.callcatch(ui, func)
270 except error.AmbiguousCommand as inst:
270 except error.AmbiguousCommand as inst:
271 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
271 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
272 (inst.args[0], " ".join(inst.args[1])))
272 (inst.args[0], " ".join(inst.args[1])))
273 except error.CommandError as inst:
273 except error.CommandError as inst:
274 if inst.args[0]:
274 if inst.args[0]:
275 ui.pager('help')
275 ui.pager('help')
276 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
276 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
277 commands.help_(ui, inst.args[0], full=False, command=True)
277 commands.help_(ui, inst.args[0], full=False, command=True)
278 else:
278 else:
279 ui.pager('help')
279 ui.pager('help')
280 ui.warn(_("hg: %s\n") % inst.args[1])
280 ui.warn(_("hg: %s\n") % inst.args[1])
281 commands.help_(ui, 'shortlist')
281 commands.help_(ui, 'shortlist')
282 except error.ParseError as inst:
282 except error.ParseError as inst:
283 _formatparse(ui.warn, inst)
283 _formatparse(ui.warn, inst)
284 return -1
284 return -1
285 except error.UnknownCommand as inst:
285 except error.UnknownCommand as inst:
286 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
286 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
287 try:
287 try:
288 # check if the command is in a disabled extension
288 # check if the command is in a disabled extension
289 # (but don't check for extensions themselves)
289 # (but don't check for extensions themselves)
290 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
290 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
291 ui.warn(nocmdmsg)
291 ui.warn(nocmdmsg)
292 ui.write(formatted)
292 ui.write(formatted)
293 except (error.UnknownCommand, error.Abort):
293 except (error.UnknownCommand, error.Abort):
294 suggested = False
294 suggested = False
295 if len(inst.args) == 2:
295 if len(inst.args) == 2:
296 sim = _getsimilar(inst.args[1], inst.args[0])
296 sim = _getsimilar(inst.args[1], inst.args[0])
297 if sim:
297 if sim:
298 ui.warn(nocmdmsg)
298 ui.warn(nocmdmsg)
299 _reportsimilar(ui.warn, sim)
299 _reportsimilar(ui.warn, sim)
300 suggested = True
300 suggested = True
301 if not suggested:
301 if not suggested:
302 ui.pager('help')
302 ui.pager('help')
303 ui.warn(nocmdmsg)
303 ui.warn(nocmdmsg)
304 commands.help_(ui, 'shortlist')
304 commands.help_(ui, 'shortlist')
305 except IOError:
305 except IOError:
306 raise
306 raise
307 except KeyboardInterrupt:
307 except KeyboardInterrupt:
308 raise
308 raise
309 except: # probably re-raises
309 except: # probably re-raises
310 if not handlecommandexception(ui):
310 if not handlecommandexception(ui):
311 raise
311 raise
312
312
313 return -1
313 return -1
314
314
315 def aliasargs(fn, givenargs):
315 def aliasargs(fn, givenargs):
316 args = getattr(fn, 'args', [])
316 args = getattr(fn, 'args', [])
317 if args:
317 if args:
318 cmd = ' '.join(map(util.shellquote, args))
318 cmd = ' '.join(map(util.shellquote, args))
319
319
320 nums = []
320 nums = []
321 def replacer(m):
321 def replacer(m):
322 num = int(m.group(1)) - 1
322 num = int(m.group(1)) - 1
323 nums.append(num)
323 nums.append(num)
324 if num < len(givenargs):
324 if num < len(givenargs):
325 return givenargs[num]
325 return givenargs[num]
326 raise error.Abort(_('too few arguments for command alias'))
326 raise error.Abort(_('too few arguments for command alias'))
327 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
327 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
328 givenargs = [x for i, x in enumerate(givenargs)
328 givenargs = [x for i, x in enumerate(givenargs)
329 if i not in nums]
329 if i not in nums]
330 args = pycompat.shlexsplit(cmd)
330 args = pycompat.shlexsplit(cmd)
331 return args + givenargs
331 return args + givenargs
332
332
333 def aliasinterpolate(name, args, cmd):
333 def aliasinterpolate(name, args, cmd):
334 '''interpolate args into cmd for shell aliases
334 '''interpolate args into cmd for shell aliases
335
335
336 This also handles $0, $@ and "$@".
336 This also handles $0, $@ and "$@".
337 '''
337 '''
338 # util.interpolate can't deal with "$@" (with quotes) because it's only
338 # util.interpolate can't deal with "$@" (with quotes) because it's only
339 # built to match prefix + patterns.
339 # built to match prefix + patterns.
340 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
340 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
341 replacemap['$0'] = name
341 replacemap['$0'] = name
342 replacemap['$$'] = '$'
342 replacemap['$$'] = '$'
343 replacemap['$@'] = ' '.join(args)
343 replacemap['$@'] = ' '.join(args)
344 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
344 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
345 # parameters, separated out into words. Emulate the same behavior here by
345 # parameters, separated out into words. Emulate the same behavior here by
346 # quoting the arguments individually. POSIX shells will then typically
346 # quoting the arguments individually. POSIX shells will then typically
347 # tokenize each argument into exactly one word.
347 # tokenize each argument into exactly one word.
348 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
348 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
349 # escape '\$' for regex
349 # escape '\$' for regex
350 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
350 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
351 r = re.compile(regex)
351 r = re.compile(regex)
352 return r.sub(lambda x: replacemap[x.group()], cmd)
352 return r.sub(lambda x: replacemap[x.group()], cmd)
353
353
354 class cmdalias(object):
354 class cmdalias(object):
355 def __init__(self, name, definition, cmdtable, source):
355 def __init__(self, name, definition, cmdtable, source):
356 self.name = self.cmd = name
356 self.name = self.cmd = name
357 self.cmdname = ''
357 self.cmdname = ''
358 self.definition = definition
358 self.definition = definition
359 self.fn = None
359 self.fn = None
360 self.givenargs = []
360 self.givenargs = []
361 self.opts = []
361 self.opts = []
362 self.help = ''
362 self.help = ''
363 self.badalias = None
363 self.badalias = None
364 self.unknowncmd = False
364 self.unknowncmd = False
365 self.source = source
365 self.source = source
366
366
367 try:
367 try:
368 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
368 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
369 for alias, e in cmdtable.iteritems():
369 for alias, e in cmdtable.iteritems():
370 if e is entry:
370 if e is entry:
371 self.cmd = alias
371 self.cmd = alias
372 break
372 break
373 self.shadows = True
373 self.shadows = True
374 except error.UnknownCommand:
374 except error.UnknownCommand:
375 self.shadows = False
375 self.shadows = False
376
376
377 if not self.definition:
377 if not self.definition:
378 self.badalias = _("no definition for alias '%s'") % self.name
378 self.badalias = _("no definition for alias '%s'") % self.name
379 return
379 return
380
380
381 if self.definition.startswith('!'):
381 if self.definition.startswith('!'):
382 self.shell = True
382 self.shell = True
383 def fn(ui, *args):
383 def fn(ui, *args):
384 env = {'HG_ARGS': ' '.join((self.name,) + args)}
384 env = {'HG_ARGS': ' '.join((self.name,) + args)}
385 def _checkvar(m):
385 def _checkvar(m):
386 if m.groups()[0] == '$':
386 if m.groups()[0] == '$':
387 return m.group()
387 return m.group()
388 elif int(m.groups()[0]) <= len(args):
388 elif int(m.groups()[0]) <= len(args):
389 return m.group()
389 return m.group()
390 else:
390 else:
391 ui.debug("No argument found for substitution "
391 ui.debug("No argument found for substitution "
392 "of %i variable in alias '%s' definition."
392 "of %i variable in alias '%s' definition."
393 % (int(m.groups()[0]), self.name))
393 % (int(m.groups()[0]), self.name))
394 return ''
394 return ''
395 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
395 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
396 cmd = aliasinterpolate(self.name, args, cmd)
396 cmd = aliasinterpolate(self.name, args, cmd)
397 return ui.system(cmd, environ=env,
397 return ui.system(cmd, environ=env,
398 blockedtag='alias_%s' % self.name)
398 blockedtag='alias_%s' % self.name)
399 self.fn = fn
399 self.fn = fn
400 return
400 return
401
401
402 try:
402 try:
403 args = pycompat.shlexsplit(self.definition)
403 args = pycompat.shlexsplit(self.definition)
404 except ValueError as inst:
404 except ValueError as inst:
405 self.badalias = (_("error in definition for alias '%s': %s")
405 self.badalias = (_("error in definition for alias '%s': %s")
406 % (self.name, inst))
406 % (self.name, inst))
407 return
407 return
408 self.cmdname = cmd = args.pop(0)
408 self.cmdname = cmd = args.pop(0)
409 self.givenargs = args
409 self.givenargs = args
410
410
411 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
411 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
412 if _earlygetopt([invalidarg], args):
412 if _earlygetopt([invalidarg], args):
413 self.badalias = (_("error in definition for alias '%s': %s may "
413 self.badalias = (_("error in definition for alias '%s': %s may "
414 "only be given on the command line")
414 "only be given on the command line")
415 % (self.name, invalidarg))
415 % (self.name, invalidarg))
416 return
416 return
417
417
418 try:
418 try:
419 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
419 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
420 if len(tableentry) > 2:
420 if len(tableentry) > 2:
421 self.fn, self.opts, self.help = tableentry
421 self.fn, self.opts, self.help = tableentry
422 else:
422 else:
423 self.fn, self.opts = tableentry
423 self.fn, self.opts = tableentry
424
424
425 if self.help.startswith("hg " + cmd):
425 if self.help.startswith("hg " + cmd):
426 # drop prefix in old-style help lines so hg shows the alias
426 # drop prefix in old-style help lines so hg shows the alias
427 self.help = self.help[4 + len(cmd):]
427 self.help = self.help[4 + len(cmd):]
428 self.__doc__ = self.fn.__doc__
428 self.__doc__ = self.fn.__doc__
429
429
430 except error.UnknownCommand:
430 except error.UnknownCommand:
431 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
431 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
432 % (self.name, cmd))
432 % (self.name, cmd))
433 self.unknowncmd = True
433 self.unknowncmd = True
434 except error.AmbiguousCommand:
434 except error.AmbiguousCommand:
435 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
435 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
436 % (self.name, cmd))
436 % (self.name, cmd))
437
437
438 @property
438 @property
439 def args(self):
439 def args(self):
440 args = pycompat.maplist(util.expandpath, self.givenargs)
440 args = pycompat.maplist(util.expandpath, self.givenargs)
441 return aliasargs(self.fn, args)
441 return aliasargs(self.fn, args)
442
442
443 def __getattr__(self, name):
443 def __getattr__(self, name):
444 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
444 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
445 if name not in adefaults:
445 if name not in adefaults:
446 raise AttributeError(name)
446 raise AttributeError(name)
447 if self.badalias or util.safehasattr(self, 'shell'):
447 if self.badalias or util.safehasattr(self, 'shell'):
448 return adefaults[name]
448 return adefaults[name]
449 return getattr(self.fn, name)
449 return getattr(self.fn, name)
450
450
451 def __call__(self, ui, *args, **opts):
451 def __call__(self, ui, *args, **opts):
452 if self.badalias:
452 if self.badalias:
453 hint = None
453 hint = None
454 if self.unknowncmd:
454 if self.unknowncmd:
455 try:
455 try:
456 # check if the command is in a disabled extension
456 # check if the command is in a disabled extension
457 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
457 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
458 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
458 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
459 except error.UnknownCommand:
459 except error.UnknownCommand:
460 pass
460 pass
461 raise error.Abort(self.badalias, hint=hint)
461 raise error.Abort(self.badalias, hint=hint)
462 if self.shadows:
462 if self.shadows:
463 ui.debug("alias '%s' shadows command '%s'\n" %
463 ui.debug("alias '%s' shadows command '%s'\n" %
464 (self.name, self.cmdname))
464 (self.name, self.cmdname))
465
465
466 ui.log('commandalias', "alias '%s' expands to '%s'\n",
466 ui.log('commandalias', "alias '%s' expands to '%s'\n",
467 self.name, self.definition)
467 self.name, self.definition)
468 if util.safehasattr(self, 'shell'):
468 if util.safehasattr(self, 'shell'):
469 return self.fn(ui, *args, **opts)
469 return self.fn(ui, *args, **opts)
470 else:
470 else:
471 try:
471 try:
472 return util.checksignature(self.fn)(ui, *args, **opts)
472 return util.checksignature(self.fn)(ui, *args, **opts)
473 except error.SignatureError:
473 except error.SignatureError:
474 args = ' '.join([self.cmdname] + self.args)
474 args = ' '.join([self.cmdname] + self.args)
475 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
475 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
476 raise
476 raise
477
477
478 def addaliases(ui, cmdtable):
478 def addaliases(ui, cmdtable):
479 # aliases are processed after extensions have been loaded, so they
479 # aliases are processed after extensions have been loaded, so they
480 # may use extension commands. Aliases can also use other alias definitions,
480 # may use extension commands. Aliases can also use other alias definitions,
481 # but only if they have been defined prior to the current definition.
481 # but only if they have been defined prior to the current definition.
482 for alias, definition in ui.configitems('alias'):
482 for alias, definition in ui.configitems('alias'):
483 source = ui.configsource('alias', alias)
483 source = ui.configsource('alias', alias)
484 aliasdef = cmdalias(alias, definition, cmdtable, source)
484 aliasdef = cmdalias(alias, definition, cmdtable, source)
485
485
486 try:
486 try:
487 olddef = cmdtable[aliasdef.cmd][0]
487 olddef = cmdtable[aliasdef.cmd][0]
488 if olddef.definition == aliasdef.definition:
488 if olddef.definition == aliasdef.definition:
489 continue
489 continue
490 except (KeyError, AttributeError):
490 except (KeyError, AttributeError):
491 # definition might not exist or it might not be a cmdalias
491 # definition might not exist or it might not be a cmdalias
492 pass
492 pass
493
493
494 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
494 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
495
495
496 def _parse(ui, args):
496 def _parse(ui, args):
497 options = {}
497 options = {}
498 cmdoptions = {}
498 cmdoptions = {}
499
499
500 try:
500 try:
501 args = fancyopts.fancyopts(args, commands.globalopts, options)
501 args = fancyopts.fancyopts(args, commands.globalopts, options)
502 except getopt.GetoptError as inst:
502 except getopt.GetoptError as inst:
503 raise error.CommandError(None, inst)
503 raise error.CommandError(None, inst)
504
504
505 if args:
505 if args:
506 cmd, args = args[0], args[1:]
506 cmd, args = args[0], args[1:]
507 aliases, entry = cmdutil.findcmd(cmd, commands.table,
507 aliases, entry = cmdutil.findcmd(cmd, commands.table,
508 ui.configbool("ui", "strict"))
508 ui.configbool("ui", "strict"))
509 cmd = aliases[0]
509 cmd = aliases[0]
510 args = aliasargs(entry[0], args)
510 args = aliasargs(entry[0], args)
511 defaults = ui.config("defaults", cmd)
511 defaults = ui.config("defaults", cmd)
512 if defaults:
512 if defaults:
513 args = pycompat.maplist(
513 args = pycompat.maplist(
514 util.expandpath, pycompat.shlexsplit(defaults)) + args
514 util.expandpath, pycompat.shlexsplit(defaults)) + args
515 c = list(entry[1])
515 c = list(entry[1])
516 else:
516 else:
517 cmd = None
517 cmd = None
518 c = []
518 c = []
519
519
520 # combine global options into local
520 # combine global options into local
521 for o in commands.globalopts:
521 for o in commands.globalopts:
522 c.append((o[0], o[1], options[o[1]], o[3]))
522 c.append((o[0], o[1], options[o[1]], o[3]))
523
523
524 try:
524 try:
525 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
525 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
526 except getopt.GetoptError as inst:
526 except getopt.GetoptError as inst:
527 raise error.CommandError(cmd, inst)
527 raise error.CommandError(cmd, inst)
528
528
529 # separate global options back out
529 # separate global options back out
530 for o in commands.globalopts:
530 for o in commands.globalopts:
531 n = o[1]
531 n = o[1]
532 options[n] = cmdoptions[n]
532 options[n] = cmdoptions[n]
533 del cmdoptions[n]
533 del cmdoptions[n]
534
534
535 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
535 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
536
536
537 def _parseconfig(ui, config):
537 def _parseconfig(ui, config):
538 """parse the --config options from the command line"""
538 """parse the --config options from the command line"""
539 configs = []
539 configs = []
540
540
541 for cfg in config:
541 for cfg in config:
542 try:
542 try:
543 name, value = [cfgelem.strip()
543 name, value = [cfgelem.strip()
544 for cfgelem in cfg.split('=', 1)]
544 for cfgelem in cfg.split('=', 1)]
545 section, name = name.split('.', 1)
545 section, name = name.split('.', 1)
546 if not section or not name:
546 if not section or not name:
547 raise IndexError
547 raise IndexError
548 ui.setconfig(section, name, value, '--config')
548 ui.setconfig(section, name, value, '--config')
549 configs.append((section, name, value))
549 configs.append((section, name, value))
550 except (IndexError, ValueError):
550 except (IndexError, ValueError):
551 raise error.Abort(_('malformed --config option: %r '
551 raise error.Abort(_('malformed --config option: %r '
552 '(use --config section.name=value)') % cfg)
552 '(use --config section.name=value)') % cfg)
553
553
554 return configs
554 return configs
555
555
556 def _earlygetopt(aliases, args):
556 def _earlygetopt(aliases, args):
557 """Return list of values for an option (or aliases).
557 """Return list of values for an option (or aliases).
558
558
559 The values are listed in the order they appear in args.
559 The values are listed in the order they appear in args.
560 The options and values are removed from args.
560 The options and values are removed from args.
561
561
562 >>> args = ['x', '--cwd', 'foo', 'y']
562 >>> args = ['x', '--cwd', 'foo', 'y']
563 >>> _earlygetopt(['--cwd'], args), args
563 >>> _earlygetopt(['--cwd'], args), args
564 (['foo'], ['x', 'y'])
564 (['foo'], ['x', 'y'])
565
565
566 >>> args = ['x', '--cwd=bar', 'y']
566 >>> args = ['x', '--cwd=bar', 'y']
567 >>> _earlygetopt(['--cwd'], args), args
567 >>> _earlygetopt(['--cwd'], args), args
568 (['bar'], ['x', 'y'])
568 (['bar'], ['x', 'y'])
569
569
570 >>> args = ['x', '-R', 'foo', 'y']
570 >>> args = ['x', '-R', 'foo', 'y']
571 >>> _earlygetopt(['-R'], args), args
571 >>> _earlygetopt(['-R'], args), args
572 (['foo'], ['x', 'y'])
572 (['foo'], ['x', 'y'])
573
573
574 >>> args = ['x', '-Rbar', 'y']
574 >>> args = ['x', '-Rbar', 'y']
575 >>> _earlygetopt(['-R'], args), args
575 >>> _earlygetopt(['-R'], args), args
576 (['bar'], ['x', 'y'])
576 (['bar'], ['x', 'y'])
577 """
577 """
578 try:
578 try:
579 argcount = args.index("--")
579 argcount = args.index("--")
580 except ValueError:
580 except ValueError:
581 argcount = len(args)
581 argcount = len(args)
582 shortopts = [opt for opt in aliases if len(opt) == 2]
582 shortopts = [opt for opt in aliases if len(opt) == 2]
583 values = []
583 values = []
584 pos = 0
584 pos = 0
585 while pos < argcount:
585 while pos < argcount:
586 fullarg = arg = args[pos]
586 fullarg = arg = args[pos]
587 equals = arg.find('=')
587 equals = arg.find('=')
588 if equals > -1:
588 if equals > -1:
589 arg = arg[:equals]
589 arg = arg[:equals]
590 if arg in aliases:
590 if arg in aliases:
591 del args[pos]
591 del args[pos]
592 if equals > -1:
592 if equals > -1:
593 values.append(fullarg[equals + 1:])
593 values.append(fullarg[equals + 1:])
594 argcount -= 1
594 argcount -= 1
595 else:
595 else:
596 if pos + 1 >= argcount:
596 if pos + 1 >= argcount:
597 # ignore and let getopt report an error if there is no value
597 # ignore and let getopt report an error if there is no value
598 break
598 break
599 values.append(args.pop(pos))
599 values.append(args.pop(pos))
600 argcount -= 2
600 argcount -= 2
601 elif arg[:2] in shortopts:
601 elif arg[:2] in shortopts:
602 # short option can have no following space, e.g. hg log -Rfoo
602 # short option can have no following space, e.g. hg log -Rfoo
603 values.append(args.pop(pos)[2:])
603 values.append(args.pop(pos)[2:])
604 argcount -= 1
604 argcount -= 1
605 else:
605 else:
606 pos += 1
606 pos += 1
607 return values
607 return values
608
608
609 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
609 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
610 # run pre-hook, and abort if it fails
610 # run pre-hook, and abort if it fails
611 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
611 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
612 pats=cmdpats, opts=cmdoptions)
612 pats=cmdpats, opts=cmdoptions)
613 try:
613 try:
614 ret = _runcommand(ui, options, cmd, d)
614 ret = _runcommand(ui, options, cmd, d)
615 # run post-hook, passing command result
615 # run post-hook, passing command result
616 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
616 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
617 result=ret, pats=cmdpats, opts=cmdoptions)
617 result=ret, pats=cmdpats, opts=cmdoptions)
618 except Exception:
618 except Exception:
619 # run failure hook and re-raise
619 # run failure hook and re-raise
620 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
620 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
621 pats=cmdpats, opts=cmdoptions)
621 pats=cmdpats, opts=cmdoptions)
622 raise
622 raise
623 return ret
623 return ret
624
624
625 def _getlocal(ui, rpath, wd=None):
625 def _getlocal(ui, rpath, wd=None):
626 """Return (path, local ui object) for the given target path.
626 """Return (path, local ui object) for the given target path.
627
627
628 Takes paths in [cwd]/.hg/hgrc into account."
628 Takes paths in [cwd]/.hg/hgrc into account."
629 """
629 """
630 if wd is None:
630 if wd is None:
631 try:
631 try:
632 wd = pycompat.getcwd()
632 wd = pycompat.getcwd()
633 except OSError as e:
633 except OSError as e:
634 raise error.Abort(_("error getting current working directory: %s") %
634 raise error.Abort(_("error getting current working directory: %s") %
635 e.strerror)
635 e.strerror)
636 path = cmdutil.findrepo(wd) or ""
636 path = cmdutil.findrepo(wd) or ""
637 if not path:
637 if not path:
638 lui = ui
638 lui = ui
639 else:
639 else:
640 lui = ui.copy()
640 lui = ui.copy()
641 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
641 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
642
642
643 if rpath and rpath[-1]:
643 if rpath and rpath[-1]:
644 path = lui.expandpath(rpath[-1])
644 path = lui.expandpath(rpath[-1])
645 lui = ui.copy()
645 lui = ui.copy()
646 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
646 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
647
647
648 return path, lui
648 return path, lui
649
649
650 def _checkshellalias(lui, ui, args):
650 def _checkshellalias(lui, ui, args):
651 """Return the function to run the shell alias, if it is required"""
651 """Return the function to run the shell alias, if it is required"""
652 options = {}
652 options = {}
653
653
654 try:
654 try:
655 args = fancyopts.fancyopts(args, commands.globalopts, options)
655 args = fancyopts.fancyopts(args, commands.globalopts, options)
656 except getopt.GetoptError:
656 except getopt.GetoptError:
657 return
657 return
658
658
659 if not args:
659 if not args:
660 return
660 return
661
661
662 cmdtable = commands.table
662 cmdtable = commands.table
663
663
664 cmd = args[0]
664 cmd = args[0]
665 try:
665 try:
666 strict = ui.configbool("ui", "strict")
666 strict = ui.configbool("ui", "strict")
667 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
667 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
668 except (error.AmbiguousCommand, error.UnknownCommand):
668 except (error.AmbiguousCommand, error.UnknownCommand):
669 return
669 return
670
670
671 cmd = aliases[0]
671 cmd = aliases[0]
672 fn = entry[0]
672 fn = entry[0]
673
673
674 if cmd and util.safehasattr(fn, 'shell'):
674 if cmd and util.safehasattr(fn, 'shell'):
675 d = lambda: fn(ui, *args[1:])
675 d = lambda: fn(ui, *args[1:])
676 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
676 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
677 [], {})
677 [], {})
678
678
679 _loaded = set()
679 _loaded = set()
680
680
681 # list of (objname, loadermod, loadername) tuple:
681 # list of (objname, loadermod, loadername) tuple:
682 # - objname is the name of an object in extension module, from which
682 # - objname is the name of an object in extension module, from which
683 # extra information is loaded
683 # extra information is loaded
684 # - loadermod is the module where loader is placed
684 # - loadermod is the module where loader is placed
685 # - loadername is the name of the function, which takes (ui, extensionname,
685 # - loadername is the name of the function, which takes (ui, extensionname,
686 # extraobj) arguments
686 # extraobj) arguments
687 extraloaders = [
687 extraloaders = [
688 ('cmdtable', commands, 'loadcmdtable'),
688 ('cmdtable', commands, 'loadcmdtable'),
689 ('colortable', color, 'loadcolortable'),
689 ('colortable', color, 'loadcolortable'),
690 ('filesetpredicate', fileset, 'loadpredicate'),
690 ('filesetpredicate', fileset, 'loadpredicate'),
691 ('revsetpredicate', revset, 'loadpredicate'),
691 ('revsetpredicate', revset, 'loadpredicate'),
692 ('templatefilter', templatefilters, 'loadfilter'),
692 ('templatefilter', templatefilters, 'loadfilter'),
693 ('templatefunc', templater, 'loadfunction'),
693 ('templatefunc', templater, 'loadfunction'),
694 ('templatekeyword', templatekw, 'loadkeyword'),
694 ('templatekeyword', templatekw, 'loadkeyword'),
695 ]
695 ]
696
696
697 def _dispatch(req):
697 def _dispatch(req):
698 args = req.args
698 args = req.args
699 ui = req.ui
699 ui = req.ui
700
700
701 # check for cwd
701 # check for cwd
702 cwd = _earlygetopt(['--cwd'], args)
702 cwd = _earlygetopt(['--cwd'], args)
703 if cwd:
703 if cwd:
704 os.chdir(cwd[-1])
704 os.chdir(cwd[-1])
705
705
706 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
706 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
707 path, lui = _getlocal(ui, rpath)
707 path, lui = _getlocal(ui, rpath)
708
708
709 # Side-effect of accessing is debugcommands module is guaranteed to be
709 # Side-effect of accessing is debugcommands module is guaranteed to be
710 # imported and commands.table is populated.
710 # imported and commands.table is populated.
711 debugcommands.command
711 debugcommands.command
712
712
713 uis = set([ui, lui])
713 uis = set([ui, lui])
714
714
715 if req.repo:
715 if req.repo:
716 uis.add(req.repo.ui)
716 uis.add(req.repo.ui)
717
717
718 if '--profile' in args:
718 if '--profile' in args:
719 for ui_ in uis:
719 for ui_ in uis:
720 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
720 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
721
721
722 with profiling.maybeprofile(lui):
722 with profiling.maybeprofile(lui):
723 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
723 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
724 # reposetup. Programs like TortoiseHg will call _dispatch several
724 # reposetup. Programs like TortoiseHg will call _dispatch several
725 # times so we keep track of configured extensions in _loaded.
725 # times so we keep track of configured extensions in _loaded.
726 extensions.loadall(lui)
726 extensions.loadall(lui)
727 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
727 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
728 # Propagate any changes to lui.__class__ by extensions
728 # Propagate any changes to lui.__class__ by extensions
729 ui.__class__ = lui.__class__
729 ui.__class__ = lui.__class__
730
730
731 # (uisetup and extsetup are handled in extensions.loadall)
731 # (uisetup and extsetup are handled in extensions.loadall)
732
732
733 for name, module in exts:
733 for name, module in exts:
734 for objname, loadermod, loadername in extraloaders:
734 for objname, loadermod, loadername in extraloaders:
735 extraobj = getattr(module, objname, None)
735 extraobj = getattr(module, objname, None)
736 if extraobj is not None:
736 if extraobj is not None:
737 getattr(loadermod, loadername)(ui, name, extraobj)
737 getattr(loadermod, loadername)(ui, name, extraobj)
738 _loaded.add(name)
738 _loaded.add(name)
739
739
740 # (reposetup is handled in hg.repository)
740 # (reposetup is handled in hg.repository)
741
741
742 addaliases(lui, commands.table)
742 addaliases(lui, commands.table)
743
743
744 # All aliases and commands are completely defined, now.
744 # All aliases and commands are completely defined, now.
745 # Check abbreviation/ambiguity of shell alias.
745 # Check abbreviation/ambiguity of shell alias.
746 shellaliasfn = _checkshellalias(lui, ui, args)
746 shellaliasfn = _checkshellalias(lui, ui, args)
747 if shellaliasfn:
747 if shellaliasfn:
748 return shellaliasfn()
748 return shellaliasfn()
749
749
750 # check for fallback encoding
750 # check for fallback encoding
751 fallback = lui.config('ui', 'fallbackencoding')
751 fallback = lui.config('ui', 'fallbackencoding')
752 if fallback:
752 if fallback:
753 encoding.fallbackencoding = fallback
753 encoding.fallbackencoding = fallback
754
754
755 fullargs = args
755 fullargs = args
756 cmd, func, args, options, cmdoptions = _parse(lui, args)
756 cmd, func, args, options, cmdoptions = _parse(lui, args)
757
757
758 if options["config"]:
758 if options["config"]:
759 raise error.Abort(_("option --config may not be abbreviated!"))
759 raise error.Abort(_("option --config may not be abbreviated!"))
760 if options["cwd"]:
760 if options["cwd"]:
761 raise error.Abort(_("option --cwd may not be abbreviated!"))
761 raise error.Abort(_("option --cwd may not be abbreviated!"))
762 if options["repository"]:
762 if options["repository"]:
763 raise error.Abort(_(
763 raise error.Abort(_(
764 "option -R has to be separated from other options (e.g. not "
764 "option -R has to be separated from other options (e.g. not "
765 "-qR) and --repository may only be abbreviated as --repo!"))
765 "-qR) and --repository may only be abbreviated as --repo!"))
766
766
767 if options["encoding"]:
767 if options["encoding"]:
768 encoding.encoding = options["encoding"]
768 encoding.encoding = options["encoding"]
769 if options["encodingmode"]:
769 if options["encodingmode"]:
770 encoding.encodingmode = options["encodingmode"]
770 encoding.encodingmode = options["encodingmode"]
771 if options["time"]:
771 if options["time"]:
772 def get_times():
772 def get_times():
773 t = os.times()
773 t = os.times()
774 if t[4] == 0.0:
774 if t[4] == 0.0:
775 # Windows leaves this as zero, so use time.clock()
775 # Windows leaves this as zero, so use time.clock()
776 t = (t[0], t[1], t[2], t[3], time.clock())
776 t = (t[0], t[1], t[2], t[3], time.clock())
777 return t
777 return t
778 s = get_times()
778 s = get_times()
779 def print_time():
779 def print_time():
780 t = get_times()
780 t = get_times()
781 ui.warn(
781 ui.warn(
782 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
782 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
783 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
783 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
784 ui.atexit(print_time)
784 ui.atexit(print_time)
785
785
786 if options['verbose'] or options['debug'] or options['quiet']:
786 if options['verbose'] or options['debug'] or options['quiet']:
787 for opt in ('verbose', 'debug', 'quiet'):
787 for opt in ('verbose', 'debug', 'quiet'):
788 val = str(bool(options[opt]))
788 val = str(bool(options[opt]))
789 if pycompat.ispy3:
789 if pycompat.ispy3:
790 val = val.encode('ascii')
790 val = val.encode('ascii')
791 for ui_ in uis:
791 for ui_ in uis:
792 ui_.setconfig('ui', opt, val, '--' + opt)
792 ui_.setconfig('ui', opt, val, '--' + opt)
793
793
794 if options['traceback']:
794 if options['traceback']:
795 for ui_ in uis:
795 for ui_ in uis:
796 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
796 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
797
797
798 if options['noninteractive']:
798 if options['noninteractive']:
799 for ui_ in uis:
799 for ui_ in uis:
800 ui_.setconfig('ui', 'interactive', 'off', '-y')
800 ui_.setconfig('ui', 'interactive', 'off', '-y')
801
801
802 if util.parsebool(options['pager']):
802 if util.parsebool(options['pager']):
803 ui.pager('internal-always-' + cmd)
803 ui.pager('internal-always-' + cmd)
804 elif options['pager'] != 'auto':
804 elif options['pager'] != 'auto':
805 ui.disablepager()
805 ui.disablepager()
806
806
807 if cmdoptions.get('insecure', False):
807 if cmdoptions.get('insecure', False):
808 for ui_ in uis:
808 for ui_ in uis:
809 ui_.insecureconnections = True
809 ui_.insecureconnections = True
810
810
811 # setup color handling
811 # setup color handling
812 coloropt = options['color']
812 coloropt = options['color']
813 for ui_ in uis:
813 for ui_ in uis:
814 if coloropt:
814 if coloropt:
815 ui_.setconfig('ui', 'color', coloropt, '--color')
815 ui_.setconfig('ui', 'color', coloropt, '--color')
816 color.setup(ui_)
816 color.setup(ui_)
817
817
818 if options['version']:
818 if options['version']:
819 return commands.version_(ui)
819 return commands.version_(ui)
820 if options['help']:
820 if options['help']:
821 return commands.help_(ui, cmd, command=cmd is not None)
821 return commands.help_(ui, cmd, command=cmd is not None)
822 elif not cmd:
822 elif not cmd:
823 return commands.help_(ui, 'shortlist')
823 return commands.help_(ui, 'shortlist')
824
824
825 repo = None
825 repo = None
826 cmdpats = args[:]
826 cmdpats = args[:]
827 if not func.norepo:
827 if not func.norepo:
828 # use the repo from the request only if we don't have -R
828 # use the repo from the request only if we don't have -R
829 if not rpath and not cwd:
829 if not rpath and not cwd:
830 repo = req.repo
830 repo = req.repo
831
831
832 if repo:
832 if repo:
833 # set the descriptors of the repo ui to those of ui
833 # set the descriptors of the repo ui to those of ui
834 repo.ui.fin = ui.fin
834 repo.ui.fin = ui.fin
835 repo.ui.fout = ui.fout
835 repo.ui.fout = ui.fout
836 repo.ui.ferr = ui.ferr
836 repo.ui.ferr = ui.ferr
837 else:
837 else:
838 try:
838 try:
839 repo = hg.repository(ui, path=path)
839 repo = hg.repository(ui, path=path)
840 if not repo.local():
840 if not repo.local():
841 raise error.Abort(_("repository '%s' is not local")
841 raise error.Abort(_("repository '%s' is not local")
842 % path)
842 % path)
843 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
843 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
844 'repo')
844 'repo')
845 except error.RequirementError:
845 except error.RequirementError:
846 raise
846 raise
847 except error.RepoError:
847 except error.RepoError:
848 if rpath and rpath[-1]: # invalid -R path
848 if rpath and rpath[-1]: # invalid -R path
849 raise
849 raise
850 if not func.optionalrepo:
850 if not func.optionalrepo:
851 if func.inferrepo and args and not path:
851 if func.inferrepo and args and not path:
852 # try to infer -R from command args
852 # try to infer -R from command args
853 repos = map(cmdutil.findrepo, args)
853 repos = map(cmdutil.findrepo, args)
854 guess = repos[0]
854 guess = repos[0]
855 if guess and repos.count(guess) == len(repos):
855 if guess and repos.count(guess) == len(repos):
856 req.args = ['--repository', guess] + fullargs
856 req.args = ['--repository', guess] + fullargs
857 return _dispatch(req)
857 return _dispatch(req)
858 if not path:
858 if not path:
859 raise error.RepoError(_("no repository found in"
859 raise error.RepoError(_("no repository found in"
860 " '%s' (.hg not found)")
860 " '%s' (.hg not found)")
861 % pycompat.getcwd())
861 % pycompat.getcwd())
862 raise
862 raise
863 if repo:
863 if repo:
864 ui = repo.ui
864 ui = repo.ui
865 if options['hidden']:
865 if options['hidden']:
866 repo = repo.unfiltered()
866 repo = repo.unfiltered()
867 args.insert(0, repo)
867 args.insert(0, repo)
868 elif rpath:
868 elif rpath:
869 ui.warn(_("warning: --repository ignored\n"))
869 ui.warn(_("warning: --repository ignored\n"))
870
870
871 msg = _formatargs(fullargs)
871 msg = _formatargs(fullargs)
872 ui.log("command", '%s\n', msg)
872 ui.log("command", '%s\n', msg)
873 strcmdopt = pycompat.strkwargs(cmdoptions)
873 strcmdopt = pycompat.strkwargs(cmdoptions)
874 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
874 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
875 try:
875 try:
876 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
876 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
877 cmdpats, cmdoptions)
877 cmdpats, cmdoptions)
878 finally:
878 finally:
879 if repo and repo != req.repo:
879 if repo and repo != req.repo:
880 repo.close()
880 repo.close()
881
881
882 def _runcommand(ui, options, cmd, cmdfunc):
882 def _runcommand(ui, options, cmd, cmdfunc):
883 """Run a command function, possibly with profiling enabled."""
883 """Run a command function, possibly with profiling enabled."""
884 try:
884 try:
885 return cmdfunc()
885 return cmdfunc()
886 except error.SignatureError:
886 except error.SignatureError:
887 raise error.CommandError(cmd, _('invalid arguments'))
887 raise error.CommandError(cmd, _('invalid arguments'))
888
888
889 def _exceptionwarning(ui):
889 def _exceptionwarning(ui):
890 """Produce a warning message for the current active exception"""
890 """Produce a warning message for the current active exception"""
891
891
892 # For compatibility checking, we discard the portion of the hg
892 # For compatibility checking, we discard the portion of the hg
893 # version after the + on the assumption that if a "normal
893 # version after the + on the assumption that if a "normal
894 # user" is running a build with a + in it the packager
894 # user" is running a build with a + in it the packager
895 # probably built from fairly close to a tag and anyone with a
895 # probably built from fairly close to a tag and anyone with a
896 # 'make local' copy of hg (where the version number can be out
896 # 'make local' copy of hg (where the version number can be out
897 # of date) will be clueful enough to notice the implausible
897 # of date) will be clueful enough to notice the implausible
898 # version number and try updating.
898 # version number and try updating.
899 ct = util.versiontuple(n=2)
899 ct = util.versiontuple(n=2)
900 worst = None, ct, ''
900 worst = None, ct, ''
901 if ui.config('ui', 'supportcontact', None) is None:
901 if ui.config('ui', 'supportcontact', None) is None:
902 for name, mod in extensions.extensions():
902 for name, mod in extensions.extensions():
903 testedwith = getattr(mod, 'testedwith', '')
903 testedwith = getattr(mod, 'testedwith', '')
904 if pycompat.ispy3 and isinstance(testedwith, str):
904 if pycompat.ispy3 and isinstance(testedwith, str):
905 testedwith = testedwith.encode(u'utf-8')
905 testedwith = testedwith.encode(u'utf-8')
906 report = getattr(mod, 'buglink', _('the extension author.'))
906 report = getattr(mod, 'buglink', _('the extension author.'))
907 if not testedwith.strip():
907 if not testedwith.strip():
908 # We found an untested extension. It's likely the culprit.
908 # We found an untested extension. It's likely the culprit.
909 worst = name, 'unknown', report
909 worst = name, 'unknown', report
910 break
910 break
911
911
912 # Never blame on extensions bundled with Mercurial.
912 # Never blame on extensions bundled with Mercurial.
913 if extensions.ismoduleinternal(mod):
913 if extensions.ismoduleinternal(mod):
914 continue
914 continue
915
915
916 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
916 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
917 if ct in tested:
917 if ct in tested:
918 continue
918 continue
919
919
920 lower = [t for t in tested if t < ct]
920 lower = [t for t in tested if t < ct]
921 nearest = max(lower or tested)
921 nearest = max(lower or tested)
922 if worst[0] is None or nearest < worst[1]:
922 if worst[0] is None or nearest < worst[1]:
923 worst = name, nearest, report
923 worst = name, nearest, report
924 if worst[0] is not None:
924 if worst[0] is not None:
925 name, testedwith, report = worst
925 name, testedwith, report = worst
926 if not isinstance(testedwith, (bytes, str)):
926 if not isinstance(testedwith, (bytes, str)):
927 testedwith = '.'.join([str(c) for c in testedwith])
927 testedwith = '.'.join([str(c) for c in testedwith])
928 warning = (_('** Unknown exception encountered with '
928 warning = (_('** Unknown exception encountered with '
929 'possibly-broken third-party extension %s\n'
929 'possibly-broken third-party extension %s\n'
930 '** which supports versions %s of Mercurial.\n'
930 '** which supports versions %s of Mercurial.\n'
931 '** Please disable %s and try your action again.\n'
931 '** Please disable %s and try your action again.\n'
932 '** If that fixes the bug please report it to %s\n')
932 '** If that fixes the bug please report it to %s\n')
933 % (name, testedwith, name, report))
933 % (name, testedwith, name, report))
934 else:
934 else:
935 bugtracker = ui.config('ui', 'supportcontact', None)
935 bugtracker = ui.config('ui', 'supportcontact', None)
936 if bugtracker is None:
936 if bugtracker is None:
937 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
937 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
938 warning = (_("** unknown exception encountered, "
938 warning = (_("** unknown exception encountered, "
939 "please report by visiting\n** ") + bugtracker + '\n')
939 "please report by visiting\n** ") + bugtracker + '\n')
940 if pycompat.ispy3:
940 if pycompat.ispy3:
941 sysversion = sys.version.encode(u'utf-8')
941 sysversion = sys.version.encode(u'utf-8')
942 else:
942 else:
943 sysversion = sys.version
943 sysversion = sys.version
944 sysversion = sysversion.replace('\n', '')
944 sysversion = sysversion.replace('\n', '')
945 warning += ((_("** Python %s\n") % sysversion) +
945 warning += ((_("** Python %s\n") % sysversion) +
946 (_("** Mercurial Distributed SCM (version %s)\n") %
946 (_("** Mercurial Distributed SCM (version %s)\n") %
947 util.version()) +
947 util.version()) +
948 (_("** Extensions loaded: %s\n") %
948 (_("** Extensions loaded: %s\n") %
949 ", ".join([x[0] for x in extensions.extensions()])))
949 ", ".join([x[0] for x in extensions.extensions()])))
950 return warning
950 return warning
951
951
952 def handlecommandexception(ui):
952 def handlecommandexception(ui):
953 """Produce a warning message for broken commands
953 """Produce a warning message for broken commands
954
954
955 Called when handling an exception; the exception is reraised if
955 Called when handling an exception; the exception is reraised if
956 this function returns False, ignored otherwise.
956 this function returns False, ignored otherwise.
957 """
957 """
958 warning = _exceptionwarning(ui)
958 warning = _exceptionwarning(ui)
959 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
959 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
960 ui.warn(warning)
960 ui.warn(warning)
961 return False # re-raise the exception
961 return False # re-raise the exception
@@ -1,180 +1,180 b''
1
1
2 $ cat << EOF > buggylocking.py
2 $ cat << EOF > buggylocking.py
3 > """A small extension that tests our developer warnings
3 > """A small extension that tests our developer warnings
4 > """
4 > """
5 >
5 >
6 > from mercurial import cmdutil, repair, util
6 > from mercurial import cmdutil, repair, util
7 >
7 >
8 > cmdtable = {}
8 > cmdtable = {}
9 > command = cmdutil.command(cmdtable)
9 > command = cmdutil.command(cmdtable)
10 >
10 >
11 > @command('buggylocking', [], '')
11 > @command('buggylocking', [], '')
12 > def buggylocking(ui, repo):
12 > def buggylocking(ui, repo):
13 > lo = repo.lock()
13 > lo = repo.lock()
14 > wl = repo.wlock()
14 > wl = repo.wlock()
15 > wl.release()
15 > wl.release()
16 > lo.release()
16 > lo.release()
17 >
17 >
18 > @command('buggytransaction', [], '')
18 > @command('buggytransaction', [], '')
19 > def buggylocking(ui, repo):
19 > def buggylocking(ui, repo):
20 > tr = repo.transaction('buggy')
20 > tr = repo.transaction('buggy')
21 > # make sure we rollback the transaction as we don't want to rely on the__del__
21 > # make sure we rollback the transaction as we don't want to rely on the__del__
22 > tr.release()
22 > tr.release()
23 >
23 >
24 > @command('properlocking', [], '')
24 > @command('properlocking', [], '')
25 > def properlocking(ui, repo):
25 > def properlocking(ui, repo):
26 > """check that reentrance is fine"""
26 > """check that reentrance is fine"""
27 > wl = repo.wlock()
27 > wl = repo.wlock()
28 > lo = repo.lock()
28 > lo = repo.lock()
29 > tr = repo.transaction('proper')
29 > tr = repo.transaction('proper')
30 > tr2 = repo.transaction('proper')
30 > tr2 = repo.transaction('proper')
31 > lo2 = repo.lock()
31 > lo2 = repo.lock()
32 > wl2 = repo.wlock()
32 > wl2 = repo.wlock()
33 > wl2.release()
33 > wl2.release()
34 > lo2.release()
34 > lo2.release()
35 > tr2.close()
35 > tr2.close()
36 > tr.close()
36 > tr.close()
37 > lo.release()
37 > lo.release()
38 > wl.release()
38 > wl.release()
39 >
39 >
40 > @command('nowaitlocking', [], '')
40 > @command('nowaitlocking', [], '')
41 > def nowaitlocking(ui, repo):
41 > def nowaitlocking(ui, repo):
42 > lo = repo.lock()
42 > lo = repo.lock()
43 > wl = repo.wlock(wait=False)
43 > wl = repo.wlock(wait=False)
44 > wl.release()
44 > wl.release()
45 > lo.release()
45 > lo.release()
46 >
46 >
47 > @command('stripintr', [], '')
47 > @command('stripintr', [], '')
48 > def stripintr(ui, repo):
48 > def stripintr(ui, repo):
49 > lo = repo.lock()
49 > lo = repo.lock()
50 > tr = repo.transaction('foobar')
50 > tr = repo.transaction('foobar')
51 > try:
51 > try:
52 > repair.strip(repo.ui, repo, [repo['.'].node()])
52 > repair.strip(repo.ui, repo, [repo['.'].node()])
53 > finally:
53 > finally:
54 > lo.release()
54 > lo.release()
55 > @command('oldanddeprecated', [], '')
55 > @command('oldanddeprecated', [], '')
56 > def oldanddeprecated(ui, repo):
56 > def oldanddeprecated(ui, repo):
57 > """test deprecation warning API"""
57 > """test deprecation warning API"""
58 > def foobar(ui):
58 > def foobar(ui):
59 > ui.deprecwarn('foorbar is deprecated, go shopping', '42.1337')
59 > ui.deprecwarn('foorbar is deprecated, go shopping', '42.1337')
60 > foobar(ui)
60 > foobar(ui)
61 > @command('nouiwarning', [], '')
61 > @command('nouiwarning', [], '')
62 > def nouiwarning(ui, repo):
62 > def nouiwarning(ui, repo):
63 > util.nouideprecwarn('this is a test', '13.37')
63 > util.nouideprecwarn('this is a test', '13.37')
64 > EOF
64 > EOF
65
65
66 $ cat << EOF >> $HGRCPATH
66 $ cat << EOF >> $HGRCPATH
67 > [extensions]
67 > [extensions]
68 > buggylocking=$TESTTMP/buggylocking.py
68 > buggylocking=$TESTTMP/buggylocking.py
69 > mock=$TESTDIR/mockblackbox.py
69 > mock=$TESTDIR/mockblackbox.py
70 > blackbox=
70 > blackbox=
71 > [devel]
71 > [devel]
72 > all-warnings=1
72 > all-warnings=1
73 > EOF
73 > EOF
74
74
75 $ hg init lock-checker
75 $ hg init lock-checker
76 $ cd lock-checker
76 $ cd lock-checker
77 $ hg buggylocking
77 $ hg buggylocking
78 devel-warn: "wlock" acquired after "lock" at: $TESTTMP/buggylocking.py:* (buggylocking) (glob)
78 devel-warn: "wlock" acquired after "lock" at: $TESTTMP/buggylocking.py:* (buggylocking) (glob)
79 $ cat << EOF >> $HGRCPATH
79 $ cat << EOF >> $HGRCPATH
80 > [devel]
80 > [devel]
81 > all=0
81 > all=0
82 > check-locks=1
82 > check-locks=1
83 > EOF
83 > EOF
84 $ hg buggylocking
84 $ hg buggylocking
85 devel-warn: "wlock" acquired after "lock" at: $TESTTMP/buggylocking.py:* (buggylocking) (glob)
85 devel-warn: "wlock" acquired after "lock" at: $TESTTMP/buggylocking.py:* (buggylocking) (glob)
86 $ hg buggylocking --traceback
86 $ hg buggylocking --traceback
87 devel-warn: "wlock" acquired after "lock" at:
87 devel-warn: "wlock" acquired after "lock" at:
88 */hg:* in * (glob)
88 */hg:* in * (glob)
89 */mercurial/dispatch.py:* in run (glob)
89 */mercurial/dispatch.py:* in run (glob)
90 */mercurial/dispatch.py:* in dispatch (glob)
90 */mercurial/dispatch.py:* in dispatch (glob)
91 */mercurial/dispatch.py:* in _runcatch (glob)
91 */mercurial/dispatch.py:* in _runcatch (glob)
92 */mercurial/dispatch.py:* in callcatch (glob)
92 */mercurial/dispatch.py:* in _callcatch (glob)
93 */mercurial/scmutil.py* in callcatch (glob)
93 */mercurial/scmutil.py* in callcatch (glob)
94 */mercurial/dispatch.py:* in _runcatchfunc (glob)
94 */mercurial/dispatch.py:* in _runcatchfunc (glob)
95 */mercurial/dispatch.py:* in _dispatch (glob)
95 */mercurial/dispatch.py:* in _dispatch (glob)
96 */mercurial/dispatch.py:* in runcommand (glob)
96 */mercurial/dispatch.py:* in runcommand (glob)
97 */mercurial/dispatch.py:* in _runcommand (glob)
97 */mercurial/dispatch.py:* in _runcommand (glob)
98 */mercurial/dispatch.py:* in <lambda> (glob)
98 */mercurial/dispatch.py:* in <lambda> (glob)
99 */mercurial/util.py:* in check (glob)
99 */mercurial/util.py:* in check (glob)
100 $TESTTMP/buggylocking.py:* in buggylocking (glob)
100 $TESTTMP/buggylocking.py:* in buggylocking (glob)
101 $ hg properlocking
101 $ hg properlocking
102 $ hg nowaitlocking
102 $ hg nowaitlocking
103
103
104 $ echo a > a
104 $ echo a > a
105 $ hg add a
105 $ hg add a
106 $ hg commit -m a
106 $ hg commit -m a
107 $ hg stripintr 2>&1 | egrep -v '^(\*\*| )'
107 $ hg stripintr 2>&1 | egrep -v '^(\*\*| )'
108 saved backup bundle to $TESTTMP/lock-checker/.hg/strip-backup/*-backup.hg (glob)
108 saved backup bundle to $TESTTMP/lock-checker/.hg/strip-backup/*-backup.hg (glob)
109 Traceback (most recent call last):
109 Traceback (most recent call last):
110 mercurial.error.ProgrammingError: cannot strip from inside a transaction
110 mercurial.error.ProgrammingError: cannot strip from inside a transaction
111
111
112 $ hg oldanddeprecated
112 $ hg oldanddeprecated
113 devel-warn: foorbar is deprecated, go shopping
113 devel-warn: foorbar is deprecated, go shopping
114 (compatibility will be dropped after Mercurial-42.1337, update your code.) at: $TESTTMP/buggylocking.py:* (oldanddeprecated) (glob)
114 (compatibility will be dropped after Mercurial-42.1337, update your code.) at: $TESTTMP/buggylocking.py:* (oldanddeprecated) (glob)
115
115
116 $ hg oldanddeprecated --traceback
116 $ hg oldanddeprecated --traceback
117 devel-warn: foorbar is deprecated, go shopping
117 devel-warn: foorbar is deprecated, go shopping
118 (compatibility will be dropped after Mercurial-42.1337, update your code.) at:
118 (compatibility will be dropped after Mercurial-42.1337, update your code.) at:
119 */hg:* in <module> (glob)
119 */hg:* in <module> (glob)
120 */mercurial/dispatch.py:* in run (glob)
120 */mercurial/dispatch.py:* in run (glob)
121 */mercurial/dispatch.py:* in dispatch (glob)
121 */mercurial/dispatch.py:* in dispatch (glob)
122 */mercurial/dispatch.py:* in _runcatch (glob)
122 */mercurial/dispatch.py:* in _runcatch (glob)
123 */mercurial/dispatch.py:* in callcatch (glob)
123 */mercurial/dispatch.py:* in _callcatch (glob)
124 */mercurial/scmutil.py* in callcatch (glob)
124 */mercurial/scmutil.py* in callcatch (glob)
125 */mercurial/dispatch.py:* in _runcatchfunc (glob)
125 */mercurial/dispatch.py:* in _runcatchfunc (glob)
126 */mercurial/dispatch.py:* in _dispatch (glob)
126 */mercurial/dispatch.py:* in _dispatch (glob)
127 */mercurial/dispatch.py:* in runcommand (glob)
127 */mercurial/dispatch.py:* in runcommand (glob)
128 */mercurial/dispatch.py:* in _runcommand (glob)
128 */mercurial/dispatch.py:* in _runcommand (glob)
129 */mercurial/dispatch.py:* in <lambda> (glob)
129 */mercurial/dispatch.py:* in <lambda> (glob)
130 */mercurial/util.py:* in check (glob)
130 */mercurial/util.py:* in check (glob)
131 $TESTTMP/buggylocking.py:* in oldanddeprecated (glob)
131 $TESTTMP/buggylocking.py:* in oldanddeprecated (glob)
132 $ hg blackbox -l 7
132 $ hg blackbox -l 7
133 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated
133 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated
134 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: foorbar is deprecated, go shopping
134 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: foorbar is deprecated, go shopping
135 (compatibility will be dropped after Mercurial-42.1337, update your code.) at: $TESTTMP/buggylocking.py:* (oldanddeprecated) (glob)
135 (compatibility will be dropped after Mercurial-42.1337, update your code.) at: $TESTTMP/buggylocking.py:* (oldanddeprecated) (glob)
136 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated exited 0 after * seconds (glob)
136 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated exited 0 after * seconds (glob)
137 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated --traceback
137 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated --traceback
138 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: foorbar is deprecated, go shopping
138 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: foorbar is deprecated, go shopping
139 (compatibility will be dropped after Mercurial-42.1337, update your code.) at:
139 (compatibility will be dropped after Mercurial-42.1337, update your code.) at:
140 */hg:* in <module> (glob)
140 */hg:* in <module> (glob)
141 */mercurial/dispatch.py:* in run (glob)
141 */mercurial/dispatch.py:* in run (glob)
142 */mercurial/dispatch.py:* in dispatch (glob)
142 */mercurial/dispatch.py:* in dispatch (glob)
143 */mercurial/dispatch.py:* in _runcatch (glob)
143 */mercurial/dispatch.py:* in _runcatch (glob)
144 */mercurial/dispatch.py:* in callcatch (glob)
144 */mercurial/dispatch.py:* in _callcatch (glob)
145 */mercurial/scmutil.py* in callcatch (glob)
145 */mercurial/scmutil.py* in callcatch (glob)
146 */mercurial/dispatch.py:* in _runcatchfunc (glob)
146 */mercurial/dispatch.py:* in _runcatchfunc (glob)
147 */mercurial/dispatch.py:* in _dispatch (glob)
147 */mercurial/dispatch.py:* in _dispatch (glob)
148 */mercurial/dispatch.py:* in runcommand (glob)
148 */mercurial/dispatch.py:* in runcommand (glob)
149 */mercurial/dispatch.py:* in _runcommand (glob)
149 */mercurial/dispatch.py:* in _runcommand (glob)
150 */mercurial/dispatch.py:* in <lambda> (glob)
150 */mercurial/dispatch.py:* in <lambda> (glob)
151 */mercurial/util.py:* in check (glob)
151 */mercurial/util.py:* in check (glob)
152 $TESTTMP/buggylocking.py:* in oldanddeprecated (glob)
152 $TESTTMP/buggylocking.py:* in oldanddeprecated (glob)
153 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated --traceback exited 0 after * seconds (glob)
153 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated --traceback exited 0 after * seconds (glob)
154 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> blackbox -l 7
154 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> blackbox -l 7
155
155
156 Test programming error failure:
156 Test programming error failure:
157
157
158 $ hg buggytransaction 2>&1 | egrep -v '^ '
158 $ hg buggytransaction 2>&1 | egrep -v '^ '
159 ** Unknown exception encountered with possibly-broken third-party extension buggylocking
159 ** Unknown exception encountered with possibly-broken third-party extension buggylocking
160 ** which supports versions unknown of Mercurial.
160 ** which supports versions unknown of Mercurial.
161 ** Please disable buggylocking and try your action again.
161 ** Please disable buggylocking and try your action again.
162 ** If that fixes the bug please report it to the extension author.
162 ** If that fixes the bug please report it to the extension author.
163 ** Python * (glob)
163 ** Python * (glob)
164 ** Mercurial Distributed SCM (*) (glob)
164 ** Mercurial Distributed SCM (*) (glob)
165 ** Extensions loaded: * (glob)
165 ** Extensions loaded: * (glob)
166 Traceback (most recent call last):
166 Traceback (most recent call last):
167 mercurial.error.ProgrammingError: transaction requires locking
167 mercurial.error.ProgrammingError: transaction requires locking
168
168
169 Old style deprecation warning
169 Old style deprecation warning
170
170
171 $ hg nouiwarning
171 $ hg nouiwarning
172 $TESTTMP/buggylocking.py:61: DeprecationWarning: this is a test
172 $TESTTMP/buggylocking.py:61: DeprecationWarning: this is a test
173 (compatibility will be dropped after Mercurial-13.37, update your code.)
173 (compatibility will be dropped after Mercurial-13.37, update your code.)
174 util.nouideprecwarn('this is a test', '13.37')
174 util.nouideprecwarn('this is a test', '13.37')
175
175
176 (disabled outside of test run)
176 (disabled outside of test run)
177
177
178 $ HGEMITWARNINGS= hg nouiwarning
178 $ HGEMITWARNINGS= hg nouiwarning
179
179
180 $ cd ..
180 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now