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