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