##// END OF EJS Templates
dispatch: show deprecation warning if command has no attributes (issue5137)...
Yuya Nishihara -
r28623:38dc3f28 default
parent child Browse files
Show More
@@ -1,1059 +1,1064 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 templatekw,
38 templatekw,
39 ui as uimod,
39 ui as uimod,
40 util,
40 util,
41 )
41 )
42
42
43 class request(object):
43 class request(object):
44 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
44 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
45 ferr=None):
45 ferr=None):
46 self.args = args
46 self.args = args
47 self.ui = ui
47 self.ui = ui
48 self.repo = repo
48 self.repo = repo
49
49
50 # input/output/error streams
50 # input/output/error streams
51 self.fin = fin
51 self.fin = fin
52 self.fout = fout
52 self.fout = fout
53 self.ferr = ferr
53 self.ferr = ferr
54
54
55 def run():
55 def run():
56 "run the command in sys.argv"
56 "run the command in sys.argv"
57 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
57 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
58
58
59 def _getsimilar(symbols, value):
59 def _getsimilar(symbols, value):
60 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
60 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
61 # The cutoff for similarity here is pretty arbitrary. It should
61 # The cutoff for similarity here is pretty arbitrary. It should
62 # probably be investigated and tweaked.
62 # probably be investigated and tweaked.
63 return [s for s in symbols if sim(s) > 0.6]
63 return [s for s in symbols if sim(s) > 0.6]
64
64
65 def _reportsimilar(write, similar):
65 def _reportsimilar(write, similar):
66 if len(similar) == 1:
66 if len(similar) == 1:
67 write(_("(did you mean %s?)\n") % similar[0])
67 write(_("(did you mean %s?)\n") % similar[0])
68 elif similar:
68 elif similar:
69 ss = ", ".join(sorted(similar))
69 ss = ", ".join(sorted(similar))
70 write(_("(did you mean one of %s?)\n") % ss)
70 write(_("(did you mean one of %s?)\n") % ss)
71
71
72 def _formatparse(write, inst):
72 def _formatparse(write, inst):
73 similar = []
73 similar = []
74 if isinstance(inst, error.UnknownIdentifier):
74 if isinstance(inst, error.UnknownIdentifier):
75 # make sure to check fileset first, as revset can invoke fileset
75 # make sure to check fileset first, as revset can invoke fileset
76 similar = _getsimilar(inst.symbols, inst.function)
76 similar = _getsimilar(inst.symbols, inst.function)
77 if len(inst.args) > 1:
77 if len(inst.args) > 1:
78 write(_("hg: parse error at %s: %s\n") %
78 write(_("hg: parse error at %s: %s\n") %
79 (inst.args[1], inst.args[0]))
79 (inst.args[1], inst.args[0]))
80 if (inst.args[0][0] == ' '):
80 if (inst.args[0][0] == ' '):
81 write(_("unexpected leading whitespace\n"))
81 write(_("unexpected leading whitespace\n"))
82 else:
82 else:
83 write(_("hg: parse error: %s\n") % inst.args[0])
83 write(_("hg: parse error: %s\n") % inst.args[0])
84 _reportsimilar(write, similar)
84 _reportsimilar(write, similar)
85 if inst.hint:
85 if inst.hint:
86 write(_("(%s)\n") % inst.hint)
86 write(_("(%s)\n") % inst.hint)
87
87
88 def dispatch(req):
88 def dispatch(req):
89 "run the command specified in req.args"
89 "run the command specified in req.args"
90 if req.ferr:
90 if req.ferr:
91 ferr = req.ferr
91 ferr = req.ferr
92 elif req.ui:
92 elif req.ui:
93 ferr = req.ui.ferr
93 ferr = req.ui.ferr
94 else:
94 else:
95 ferr = sys.stderr
95 ferr = sys.stderr
96
96
97 try:
97 try:
98 if not req.ui:
98 if not req.ui:
99 req.ui = uimod.ui()
99 req.ui = uimod.ui()
100 if '--traceback' in req.args:
100 if '--traceback' in req.args:
101 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
101 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
102
102
103 # set ui streams from the request
103 # set ui streams from the request
104 if req.fin:
104 if req.fin:
105 req.ui.fin = req.fin
105 req.ui.fin = req.fin
106 if req.fout:
106 if req.fout:
107 req.ui.fout = req.fout
107 req.ui.fout = req.fout
108 if req.ferr:
108 if req.ferr:
109 req.ui.ferr = req.ferr
109 req.ui.ferr = req.ferr
110 except error.Abort as inst:
110 except error.Abort as inst:
111 ferr.write(_("abort: %s\n") % inst)
111 ferr.write(_("abort: %s\n") % inst)
112 if inst.hint:
112 if inst.hint:
113 ferr.write(_("(%s)\n") % inst.hint)
113 ferr.write(_("(%s)\n") % inst.hint)
114 return -1
114 return -1
115 except error.ParseError as inst:
115 except error.ParseError as inst:
116 _formatparse(ferr.write, inst)
116 _formatparse(ferr.write, inst)
117 return -1
117 return -1
118
118
119 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
119 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
120 starttime = time.time()
120 starttime = time.time()
121 ret = None
121 ret = None
122 try:
122 try:
123 ret = _runcatch(req)
123 ret = _runcatch(req)
124 except KeyboardInterrupt:
124 except KeyboardInterrupt:
125 try:
125 try:
126 req.ui.warn(_("interrupted!\n"))
126 req.ui.warn(_("interrupted!\n"))
127 except IOError as inst:
127 except IOError as inst:
128 if inst.errno != errno.EPIPE:
128 if inst.errno != errno.EPIPE:
129 raise
129 raise
130 ret = -1
130 ret = -1
131 finally:
131 finally:
132 duration = time.time() - starttime
132 duration = time.time() - starttime
133 req.ui.flush()
133 req.ui.flush()
134 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
134 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
135 msg, ret or 0, duration)
135 msg, ret or 0, duration)
136 return ret
136 return ret
137
137
138 def _runcatch(req):
138 def _runcatch(req):
139 def catchterm(*args):
139 def catchterm(*args):
140 raise error.SignalInterrupt
140 raise error.SignalInterrupt
141
141
142 ui = req.ui
142 ui = req.ui
143 try:
143 try:
144 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
144 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
145 num = getattr(signal, name, None)
145 num = getattr(signal, name, None)
146 if num:
146 if num:
147 signal.signal(num, catchterm)
147 signal.signal(num, catchterm)
148 except ValueError:
148 except ValueError:
149 pass # happens if called in a thread
149 pass # happens if called in a thread
150
150
151 try:
151 try:
152 try:
152 try:
153 debugger = 'pdb'
153 debugger = 'pdb'
154 debugtrace = {
154 debugtrace = {
155 'pdb' : pdb.set_trace
155 'pdb' : pdb.set_trace
156 }
156 }
157 debugmortem = {
157 debugmortem = {
158 'pdb' : pdb.post_mortem
158 'pdb' : pdb.post_mortem
159 }
159 }
160
160
161 # read --config before doing anything else
161 # read --config before doing anything else
162 # (e.g. to change trust settings for reading .hg/hgrc)
162 # (e.g. to change trust settings for reading .hg/hgrc)
163 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
163 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
164
164
165 if req.repo:
165 if req.repo:
166 # copy configs that were passed on the cmdline (--config) to
166 # copy configs that were passed on the cmdline (--config) to
167 # the repo ui
167 # the repo ui
168 for sec, name, val in cfgs:
168 for sec, name, val in cfgs:
169 req.repo.ui.setconfig(sec, name, val, source='--config')
169 req.repo.ui.setconfig(sec, name, val, source='--config')
170
170
171 # developer config: ui.debugger
171 # developer config: ui.debugger
172 debugger = ui.config("ui", "debugger")
172 debugger = ui.config("ui", "debugger")
173 debugmod = pdb
173 debugmod = pdb
174 if not debugger or ui.plain():
174 if not debugger or ui.plain():
175 # if we are in HGPLAIN mode, then disable custom debugging
175 # if we are in HGPLAIN mode, then disable custom debugging
176 debugger = 'pdb'
176 debugger = 'pdb'
177 elif '--debugger' in req.args:
177 elif '--debugger' in req.args:
178 # This import can be slow for fancy debuggers, so only
178 # This import can be slow for fancy debuggers, so only
179 # do it when absolutely necessary, i.e. when actual
179 # do it when absolutely necessary, i.e. when actual
180 # debugging has been requested
180 # debugging has been requested
181 with demandimport.deactivated():
181 with demandimport.deactivated():
182 try:
182 try:
183 debugmod = __import__(debugger)
183 debugmod = __import__(debugger)
184 except ImportError:
184 except ImportError:
185 pass # Leave debugmod = pdb
185 pass # Leave debugmod = pdb
186
186
187 debugtrace[debugger] = debugmod.set_trace
187 debugtrace[debugger] = debugmod.set_trace
188 debugmortem[debugger] = debugmod.post_mortem
188 debugmortem[debugger] = debugmod.post_mortem
189
189
190 # enter the debugger before command execution
190 # enter the debugger before command execution
191 if '--debugger' in req.args:
191 if '--debugger' in req.args:
192 ui.warn(_("entering debugger - "
192 ui.warn(_("entering debugger - "
193 "type c to continue starting hg or h for help\n"))
193 "type c to continue starting hg or h for help\n"))
194
194
195 if (debugger != 'pdb' and
195 if (debugger != 'pdb' and
196 debugtrace[debugger] == debugtrace['pdb']):
196 debugtrace[debugger] == debugtrace['pdb']):
197 ui.warn(_("%s debugger specified "
197 ui.warn(_("%s debugger specified "
198 "but its module was not found\n") % debugger)
198 "but its module was not found\n") % debugger)
199 with demandimport.deactivated():
199 with demandimport.deactivated():
200 debugtrace[debugger]()
200 debugtrace[debugger]()
201 try:
201 try:
202 return _dispatch(req)
202 return _dispatch(req)
203 finally:
203 finally:
204 ui.flush()
204 ui.flush()
205 except: # re-raises
205 except: # re-raises
206 # enter the debugger when we hit an exception
206 # enter the debugger when we hit an exception
207 if '--debugger' in req.args:
207 if '--debugger' in req.args:
208 traceback.print_exc()
208 traceback.print_exc()
209 debugmortem[debugger](sys.exc_info()[2])
209 debugmortem[debugger](sys.exc_info()[2])
210 ui.traceback()
210 ui.traceback()
211 raise
211 raise
212
212
213 # Global exception handling, alphabetically
213 # Global exception handling, alphabetically
214 # Mercurial-specific first, followed by built-in and library exceptions
214 # Mercurial-specific first, followed by built-in and library exceptions
215 except error.AmbiguousCommand as inst:
215 except error.AmbiguousCommand as inst:
216 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
216 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
217 (inst.args[0], " ".join(inst.args[1])))
217 (inst.args[0], " ".join(inst.args[1])))
218 except error.ParseError as inst:
218 except error.ParseError as inst:
219 _formatparse(ui.warn, inst)
219 _formatparse(ui.warn, inst)
220 return -1
220 return -1
221 except error.LockHeld as inst:
221 except error.LockHeld as inst:
222 if inst.errno == errno.ETIMEDOUT:
222 if inst.errno == errno.ETIMEDOUT:
223 reason = _('timed out waiting for lock held by %s') % inst.locker
223 reason = _('timed out waiting for lock held by %s') % inst.locker
224 else:
224 else:
225 reason = _('lock held by %s') % inst.locker
225 reason = _('lock held by %s') % inst.locker
226 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
226 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
227 except error.LockUnavailable as inst:
227 except error.LockUnavailable as inst:
228 ui.warn(_("abort: could not lock %s: %s\n") %
228 ui.warn(_("abort: could not lock %s: %s\n") %
229 (inst.desc or inst.filename, inst.strerror))
229 (inst.desc or inst.filename, inst.strerror))
230 except error.CommandError as inst:
230 except error.CommandError as inst:
231 if inst.args[0]:
231 if inst.args[0]:
232 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
232 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
233 commands.help_(ui, inst.args[0], full=False, command=True)
233 commands.help_(ui, inst.args[0], full=False, command=True)
234 else:
234 else:
235 ui.warn(_("hg: %s\n") % inst.args[1])
235 ui.warn(_("hg: %s\n") % inst.args[1])
236 commands.help_(ui, 'shortlist')
236 commands.help_(ui, 'shortlist')
237 except error.OutOfBandError as inst:
237 except error.OutOfBandError as inst:
238 if inst.args:
238 if inst.args:
239 msg = _("abort: remote error:\n")
239 msg = _("abort: remote error:\n")
240 else:
240 else:
241 msg = _("abort: remote error\n")
241 msg = _("abort: remote error\n")
242 ui.warn(msg)
242 ui.warn(msg)
243 if inst.args:
243 if inst.args:
244 ui.warn(''.join(inst.args))
244 ui.warn(''.join(inst.args))
245 if inst.hint:
245 if inst.hint:
246 ui.warn('(%s)\n' % inst.hint)
246 ui.warn('(%s)\n' % inst.hint)
247 except error.RepoError as inst:
247 except error.RepoError as inst:
248 ui.warn(_("abort: %s!\n") % inst)
248 ui.warn(_("abort: %s!\n") % inst)
249 if inst.hint:
249 if inst.hint:
250 ui.warn(_("(%s)\n") % inst.hint)
250 ui.warn(_("(%s)\n") % inst.hint)
251 except error.ResponseError as inst:
251 except error.ResponseError as inst:
252 ui.warn(_("abort: %s") % inst.args[0])
252 ui.warn(_("abort: %s") % inst.args[0])
253 if not isinstance(inst.args[1], basestring):
253 if not isinstance(inst.args[1], basestring):
254 ui.warn(" %r\n" % (inst.args[1],))
254 ui.warn(" %r\n" % (inst.args[1],))
255 elif not inst.args[1]:
255 elif not inst.args[1]:
256 ui.warn(_(" empty string\n"))
256 ui.warn(_(" empty string\n"))
257 else:
257 else:
258 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
258 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
259 except error.CensoredNodeError as inst:
259 except error.CensoredNodeError as inst:
260 ui.warn(_("abort: file censored %s!\n") % inst)
260 ui.warn(_("abort: file censored %s!\n") % inst)
261 except error.RevlogError as inst:
261 except error.RevlogError as inst:
262 ui.warn(_("abort: %s!\n") % inst)
262 ui.warn(_("abort: %s!\n") % inst)
263 except error.SignalInterrupt:
263 except error.SignalInterrupt:
264 ui.warn(_("killed!\n"))
264 ui.warn(_("killed!\n"))
265 except error.UnknownCommand as inst:
265 except error.UnknownCommand as inst:
266 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
266 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
267 try:
267 try:
268 # check if the command is in a disabled extension
268 # check if the command is in a disabled extension
269 # (but don't check for extensions themselves)
269 # (but don't check for extensions themselves)
270 commands.help_(ui, inst.args[0], unknowncmd=True)
270 commands.help_(ui, inst.args[0], unknowncmd=True)
271 except (error.UnknownCommand, error.Abort):
271 except (error.UnknownCommand, error.Abort):
272 suggested = False
272 suggested = False
273 if len(inst.args) == 2:
273 if len(inst.args) == 2:
274 sim = _getsimilar(inst.args[1], inst.args[0])
274 sim = _getsimilar(inst.args[1], inst.args[0])
275 if sim:
275 if sim:
276 _reportsimilar(ui.warn, sim)
276 _reportsimilar(ui.warn, sim)
277 suggested = True
277 suggested = True
278 if not suggested:
278 if not suggested:
279 commands.help_(ui, 'shortlist')
279 commands.help_(ui, 'shortlist')
280 except error.InterventionRequired as inst:
280 except error.InterventionRequired as inst:
281 ui.warn("%s\n" % inst)
281 ui.warn("%s\n" % inst)
282 if inst.hint:
282 if inst.hint:
283 ui.warn(_("(%s)\n") % inst.hint)
283 ui.warn(_("(%s)\n") % inst.hint)
284 return 1
284 return 1
285 except error.Abort as inst:
285 except error.Abort as inst:
286 ui.warn(_("abort: %s\n") % inst)
286 ui.warn(_("abort: %s\n") % inst)
287 if inst.hint:
287 if inst.hint:
288 ui.warn(_("(%s)\n") % inst.hint)
288 ui.warn(_("(%s)\n") % inst.hint)
289 except ImportError as inst:
289 except ImportError as inst:
290 ui.warn(_("abort: %s!\n") % inst)
290 ui.warn(_("abort: %s!\n") % inst)
291 m = str(inst).split()[-1]
291 m = str(inst).split()[-1]
292 if m in "mpatch bdiff".split():
292 if m in "mpatch bdiff".split():
293 ui.warn(_("(did you forget to compile extensions?)\n"))
293 ui.warn(_("(did you forget to compile extensions?)\n"))
294 elif m in "zlib".split():
294 elif m in "zlib".split():
295 ui.warn(_("(is your Python install correct?)\n"))
295 ui.warn(_("(is your Python install correct?)\n"))
296 except IOError as inst:
296 except IOError as inst:
297 if util.safehasattr(inst, "code"):
297 if util.safehasattr(inst, "code"):
298 ui.warn(_("abort: %s\n") % inst)
298 ui.warn(_("abort: %s\n") % inst)
299 elif util.safehasattr(inst, "reason"):
299 elif util.safehasattr(inst, "reason"):
300 try: # usually it is in the form (errno, strerror)
300 try: # usually it is in the form (errno, strerror)
301 reason = inst.reason.args[1]
301 reason = inst.reason.args[1]
302 except (AttributeError, IndexError):
302 except (AttributeError, IndexError):
303 # it might be anything, for example a string
303 # it might be anything, for example a string
304 reason = inst.reason
304 reason = inst.reason
305 if isinstance(reason, unicode):
305 if isinstance(reason, unicode):
306 # SSLError of Python 2.7.9 contains a unicode
306 # SSLError of Python 2.7.9 contains a unicode
307 reason = reason.encode(encoding.encoding, 'replace')
307 reason = reason.encode(encoding.encoding, 'replace')
308 ui.warn(_("abort: error: %s\n") % reason)
308 ui.warn(_("abort: error: %s\n") % reason)
309 elif (util.safehasattr(inst, "args")
309 elif (util.safehasattr(inst, "args")
310 and inst.args and inst.args[0] == errno.EPIPE):
310 and inst.args and inst.args[0] == errno.EPIPE):
311 pass
311 pass
312 elif getattr(inst, "strerror", None):
312 elif getattr(inst, "strerror", None):
313 if getattr(inst, "filename", None):
313 if getattr(inst, "filename", None):
314 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
314 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
315 else:
315 else:
316 ui.warn(_("abort: %s\n") % inst.strerror)
316 ui.warn(_("abort: %s\n") % inst.strerror)
317 else:
317 else:
318 raise
318 raise
319 except OSError as inst:
319 except OSError as inst:
320 if getattr(inst, "filename", None) is not None:
320 if getattr(inst, "filename", None) is not None:
321 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
321 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
322 else:
322 else:
323 ui.warn(_("abort: %s\n") % inst.strerror)
323 ui.warn(_("abort: %s\n") % inst.strerror)
324 except KeyboardInterrupt:
324 except KeyboardInterrupt:
325 raise
325 raise
326 except MemoryError:
326 except MemoryError:
327 ui.warn(_("abort: out of memory\n"))
327 ui.warn(_("abort: out of memory\n"))
328 except SystemExit as inst:
328 except SystemExit as inst:
329 # Commands shouldn't sys.exit directly, but give a return code.
329 # Commands shouldn't sys.exit directly, but give a return code.
330 # Just in case catch this and and pass exit code to caller.
330 # Just in case catch this and and pass exit code to caller.
331 return inst.code
331 return inst.code
332 except socket.error as inst:
332 except socket.error as inst:
333 ui.warn(_("abort: %s\n") % inst.args[-1])
333 ui.warn(_("abort: %s\n") % inst.args[-1])
334 except: # re-raises
334 except: # re-raises
335 # For compatibility checking, we discard the portion of the hg
335 # For compatibility checking, we discard the portion of the hg
336 # version after the + on the assumption that if a "normal
336 # version after the + on the assumption that if a "normal
337 # user" is running a build with a + in it the packager
337 # user" is running a build with a + in it the packager
338 # probably built from fairly close to a tag and anyone with a
338 # probably built from fairly close to a tag and anyone with a
339 # 'make local' copy of hg (where the version number can be out
339 # 'make local' copy of hg (where the version number can be out
340 # of date) will be clueful enough to notice the implausible
340 # of date) will be clueful enough to notice the implausible
341 # version number and try updating.
341 # version number and try updating.
342 ct = util.versiontuple(n=2)
342 ct = util.versiontuple(n=2)
343 worst = None, ct, ''
343 worst = None, ct, ''
344 if ui.config('ui', 'supportcontact', None) is None:
344 if ui.config('ui', 'supportcontact', None) is None:
345 for name, mod in extensions.extensions():
345 for name, mod in extensions.extensions():
346 testedwith = getattr(mod, 'testedwith', '')
346 testedwith = getattr(mod, 'testedwith', '')
347 report = getattr(mod, 'buglink', _('the extension author.'))
347 report = getattr(mod, 'buglink', _('the extension author.'))
348 if not testedwith.strip():
348 if not testedwith.strip():
349 # We found an untested extension. It's likely the culprit.
349 # We found an untested extension. It's likely the culprit.
350 worst = name, 'unknown', report
350 worst = name, 'unknown', report
351 break
351 break
352
352
353 # Never blame on extensions bundled with Mercurial.
353 # Never blame on extensions bundled with Mercurial.
354 if testedwith == 'internal':
354 if testedwith == 'internal':
355 continue
355 continue
356
356
357 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
357 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
358 if ct in tested:
358 if ct in tested:
359 continue
359 continue
360
360
361 lower = [t for t in tested if t < ct]
361 lower = [t for t in tested if t < ct]
362 nearest = max(lower or tested)
362 nearest = max(lower or tested)
363 if worst[0] is None or nearest < worst[1]:
363 if worst[0] is None or nearest < worst[1]:
364 worst = name, nearest, report
364 worst = name, nearest, report
365 if worst[0] is not None:
365 if worst[0] is not None:
366 name, testedwith, report = worst
366 name, testedwith, report = worst
367 if not isinstance(testedwith, str):
367 if not isinstance(testedwith, str):
368 testedwith = '.'.join([str(c) for c in testedwith])
368 testedwith = '.'.join([str(c) for c in testedwith])
369 warning = (_('** Unknown exception encountered with '
369 warning = (_('** Unknown exception encountered with '
370 'possibly-broken third-party extension %s\n'
370 'possibly-broken third-party extension %s\n'
371 '** which supports versions %s of Mercurial.\n'
371 '** which supports versions %s of Mercurial.\n'
372 '** Please disable %s and try your action again.\n'
372 '** Please disable %s and try your action again.\n'
373 '** If that fixes the bug please report it to %s\n')
373 '** If that fixes the bug please report it to %s\n')
374 % (name, testedwith, name, report))
374 % (name, testedwith, name, report))
375 else:
375 else:
376 bugtracker = ui.config('ui', 'supportcontact', None)
376 bugtracker = ui.config('ui', 'supportcontact', None)
377 if bugtracker is None:
377 if bugtracker is None:
378 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
378 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
379 warning = (_("** unknown exception encountered, "
379 warning = (_("** unknown exception encountered, "
380 "please report by visiting\n** ") + bugtracker + '\n')
380 "please report by visiting\n** ") + bugtracker + '\n')
381 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
381 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
382 (_("** Mercurial Distributed SCM (version %s)\n") %
382 (_("** Mercurial Distributed SCM (version %s)\n") %
383 util.version()) +
383 util.version()) +
384 (_("** Extensions loaded: %s\n") %
384 (_("** Extensions loaded: %s\n") %
385 ", ".join([x[0] for x in extensions.extensions()])))
385 ", ".join([x[0] for x in extensions.extensions()])))
386 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
386 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
387 ui.warn(warning)
387 ui.warn(warning)
388 raise
388 raise
389
389
390 return -1
390 return -1
391
391
392 def aliasargs(fn, givenargs):
392 def aliasargs(fn, givenargs):
393 args = getattr(fn, 'args', [])
393 args = getattr(fn, 'args', [])
394 if args:
394 if args:
395 cmd = ' '.join(map(util.shellquote, args))
395 cmd = ' '.join(map(util.shellquote, args))
396
396
397 nums = []
397 nums = []
398 def replacer(m):
398 def replacer(m):
399 num = int(m.group(1)) - 1
399 num = int(m.group(1)) - 1
400 nums.append(num)
400 nums.append(num)
401 if num < len(givenargs):
401 if num < len(givenargs):
402 return givenargs[num]
402 return givenargs[num]
403 raise error.Abort(_('too few arguments for command alias'))
403 raise error.Abort(_('too few arguments for command alias'))
404 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
404 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
405 givenargs = [x for i, x in enumerate(givenargs)
405 givenargs = [x for i, x in enumerate(givenargs)
406 if i not in nums]
406 if i not in nums]
407 args = shlex.split(cmd)
407 args = shlex.split(cmd)
408 return args + givenargs
408 return args + givenargs
409
409
410 def aliasinterpolate(name, args, cmd):
410 def aliasinterpolate(name, args, cmd):
411 '''interpolate args into cmd for shell aliases
411 '''interpolate args into cmd for shell aliases
412
412
413 This also handles $0, $@ and "$@".
413 This also handles $0, $@ and "$@".
414 '''
414 '''
415 # util.interpolate can't deal with "$@" (with quotes) because it's only
415 # util.interpolate can't deal with "$@" (with quotes) because it's only
416 # built to match prefix + patterns.
416 # built to match prefix + patterns.
417 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
417 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
418 replacemap['$0'] = name
418 replacemap['$0'] = name
419 replacemap['$$'] = '$'
419 replacemap['$$'] = '$'
420 replacemap['$@'] = ' '.join(args)
420 replacemap['$@'] = ' '.join(args)
421 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
421 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
422 # parameters, separated out into words. Emulate the same behavior here by
422 # parameters, separated out into words. Emulate the same behavior here by
423 # quoting the arguments individually. POSIX shells will then typically
423 # quoting the arguments individually. POSIX shells will then typically
424 # tokenize each argument into exactly one word.
424 # tokenize each argument into exactly one word.
425 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
425 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
426 # escape '\$' for regex
426 # escape '\$' for regex
427 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
427 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
428 r = re.compile(regex)
428 r = re.compile(regex)
429 return r.sub(lambda x: replacemap[x.group()], cmd)
429 return r.sub(lambda x: replacemap[x.group()], cmd)
430
430
431 class cmdalias(object):
431 class cmdalias(object):
432 def __init__(self, name, definition, cmdtable):
432 def __init__(self, name, definition, cmdtable):
433 self.name = self.cmd = name
433 self.name = self.cmd = name
434 self.cmdname = ''
434 self.cmdname = ''
435 self.definition = definition
435 self.definition = definition
436 self.fn = None
436 self.fn = None
437 self.args = []
437 self.args = []
438 self.opts = []
438 self.opts = []
439 self.help = ''
439 self.help = ''
440 self.badalias = None
440 self.badalias = None
441 self.unknowncmd = False
441 self.unknowncmd = False
442
442
443 try:
443 try:
444 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
444 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
445 for alias, e in cmdtable.iteritems():
445 for alias, e in cmdtable.iteritems():
446 if e is entry:
446 if e is entry:
447 self.cmd = alias
447 self.cmd = alias
448 break
448 break
449 self.shadows = True
449 self.shadows = True
450 except error.UnknownCommand:
450 except error.UnknownCommand:
451 self.shadows = False
451 self.shadows = False
452
452
453 if not self.definition:
453 if not self.definition:
454 self.badalias = _("no definition for alias '%s'") % self.name
454 self.badalias = _("no definition for alias '%s'") % self.name
455 return
455 return
456
456
457 if self.definition.startswith('!'):
457 if self.definition.startswith('!'):
458 self.shell = True
458 self.shell = True
459 def fn(ui, *args):
459 def fn(ui, *args):
460 env = {'HG_ARGS': ' '.join((self.name,) + args)}
460 env = {'HG_ARGS': ' '.join((self.name,) + args)}
461 def _checkvar(m):
461 def _checkvar(m):
462 if m.groups()[0] == '$':
462 if m.groups()[0] == '$':
463 return m.group()
463 return m.group()
464 elif int(m.groups()[0]) <= len(args):
464 elif int(m.groups()[0]) <= len(args):
465 return m.group()
465 return m.group()
466 else:
466 else:
467 ui.debug("No argument found for substitution "
467 ui.debug("No argument found for substitution "
468 "of %i variable in alias '%s' definition."
468 "of %i variable in alias '%s' definition."
469 % (int(m.groups()[0]), self.name))
469 % (int(m.groups()[0]), self.name))
470 return ''
470 return ''
471 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
471 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
472 cmd = aliasinterpolate(self.name, args, cmd)
472 cmd = aliasinterpolate(self.name, args, cmd)
473 return ui.system(cmd, environ=env)
473 return ui.system(cmd, environ=env)
474 self.fn = fn
474 self.fn = fn
475 return
475 return
476
476
477 try:
477 try:
478 args = shlex.split(self.definition)
478 args = shlex.split(self.definition)
479 except ValueError as inst:
479 except ValueError as inst:
480 self.badalias = (_("error in definition for alias '%s': %s")
480 self.badalias = (_("error in definition for alias '%s': %s")
481 % (self.name, inst))
481 % (self.name, inst))
482 return
482 return
483 self.cmdname = cmd = args.pop(0)
483 self.cmdname = cmd = args.pop(0)
484 args = map(util.expandpath, args)
484 args = map(util.expandpath, args)
485
485
486 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
486 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
487 if _earlygetopt([invalidarg], args):
487 if _earlygetopt([invalidarg], args):
488 self.badalias = (_("error in definition for alias '%s': %s may "
488 self.badalias = (_("error in definition for alias '%s': %s may "
489 "only be given on the command line")
489 "only be given on the command line")
490 % (self.name, invalidarg))
490 % (self.name, invalidarg))
491 return
491 return
492
492
493 try:
493 try:
494 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
494 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
495 if len(tableentry) > 2:
495 if len(tableentry) > 2:
496 self.fn, self.opts, self.help = tableentry
496 self.fn, self.opts, self.help = tableentry
497 else:
497 else:
498 self.fn, self.opts = tableentry
498 self.fn, self.opts = tableentry
499
499
500 self.args = aliasargs(self.fn, args)
500 self.args = aliasargs(self.fn, args)
501 if self.help.startswith("hg " + cmd):
501 if self.help.startswith("hg " + cmd):
502 # drop prefix in old-style help lines so hg shows the alias
502 # drop prefix in old-style help lines so hg shows the alias
503 self.help = self.help[4 + len(cmd):]
503 self.help = self.help[4 + len(cmd):]
504 self.__doc__ = self.fn.__doc__
504 self.__doc__ = self.fn.__doc__
505
505
506 except error.UnknownCommand:
506 except error.UnknownCommand:
507 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
507 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
508 % (self.name, cmd))
508 % (self.name, cmd))
509 self.unknowncmd = True
509 self.unknowncmd = True
510 except error.AmbiguousCommand:
510 except error.AmbiguousCommand:
511 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
511 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
512 % (self.name, cmd))
512 % (self.name, cmd))
513
513
514 def __getattr__(self, name):
514 def __getattr__(self, name):
515 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
515 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
516 if name not in adefaults:
516 if name not in adefaults:
517 raise AttributeError(name)
517 raise AttributeError(name)
518 if self.badalias or util.safehasattr(self, 'shell'):
518 if self.badalias or util.safehasattr(self, 'shell'):
519 return adefaults[name]
519 return adefaults[name]
520 return getattr(self.fn, name)
520 return getattr(self.fn, name)
521
521
522 def __call__(self, ui, *args, **opts):
522 def __call__(self, ui, *args, **opts):
523 if self.badalias:
523 if self.badalias:
524 hint = None
524 hint = None
525 if self.unknowncmd:
525 if self.unknowncmd:
526 try:
526 try:
527 # check if the command is in a disabled extension
527 # check if the command is in a disabled extension
528 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
528 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
529 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
529 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
530 except error.UnknownCommand:
530 except error.UnknownCommand:
531 pass
531 pass
532 raise error.Abort(self.badalias, hint=hint)
532 raise error.Abort(self.badalias, hint=hint)
533 if self.shadows:
533 if self.shadows:
534 ui.debug("alias '%s' shadows command '%s'\n" %
534 ui.debug("alias '%s' shadows command '%s'\n" %
535 (self.name, self.cmdname))
535 (self.name, self.cmdname))
536
536
537 if util.safehasattr(self, 'shell'):
537 if util.safehasattr(self, 'shell'):
538 return self.fn(ui, *args, **opts)
538 return self.fn(ui, *args, **opts)
539 else:
539 else:
540 try:
540 try:
541 return util.checksignature(self.fn)(ui, *args, **opts)
541 return util.checksignature(self.fn)(ui, *args, **opts)
542 except error.SignatureError:
542 except error.SignatureError:
543 args = ' '.join([self.cmdname] + self.args)
543 args = ' '.join([self.cmdname] + self.args)
544 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
544 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
545 raise
545 raise
546
546
547 def addaliases(ui, cmdtable):
547 def addaliases(ui, cmdtable):
548 # aliases are processed after extensions have been loaded, so they
548 # aliases are processed after extensions have been loaded, so they
549 # may use extension commands. Aliases can also use other alias definitions,
549 # may use extension commands. Aliases can also use other alias definitions,
550 # but only if they have been defined prior to the current definition.
550 # but only if they have been defined prior to the current definition.
551 for alias, definition in ui.configitems('alias'):
551 for alias, definition in ui.configitems('alias'):
552 aliasdef = cmdalias(alias, definition, cmdtable)
552 aliasdef = cmdalias(alias, definition, cmdtable)
553
553
554 try:
554 try:
555 olddef = cmdtable[aliasdef.cmd][0]
555 olddef = cmdtable[aliasdef.cmd][0]
556 if olddef.definition == aliasdef.definition:
556 if olddef.definition == aliasdef.definition:
557 continue
557 continue
558 except (KeyError, AttributeError):
558 except (KeyError, AttributeError):
559 # definition might not exist or it might not be a cmdalias
559 # definition might not exist or it might not be a cmdalias
560 pass
560 pass
561
561
562 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
562 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
563
563
564 def _parse(ui, args):
564 def _parse(ui, args):
565 options = {}
565 options = {}
566 cmdoptions = {}
566 cmdoptions = {}
567
567
568 try:
568 try:
569 args = fancyopts.fancyopts(args, commands.globalopts, options)
569 args = fancyopts.fancyopts(args, commands.globalopts, options)
570 except fancyopts.getopt.GetoptError as inst:
570 except fancyopts.getopt.GetoptError as inst:
571 raise error.CommandError(None, inst)
571 raise error.CommandError(None, inst)
572
572
573 if args:
573 if args:
574 cmd, args = args[0], args[1:]
574 cmd, args = args[0], args[1:]
575 aliases, entry = cmdutil.findcmd(cmd, commands.table,
575 aliases, entry = cmdutil.findcmd(cmd, commands.table,
576 ui.configbool("ui", "strict"))
576 ui.configbool("ui", "strict"))
577 cmd = aliases[0]
577 cmd = aliases[0]
578 args = aliasargs(entry[0], args)
578 args = aliasargs(entry[0], args)
579 defaults = ui.config("defaults", cmd)
579 defaults = ui.config("defaults", cmd)
580 if defaults:
580 if defaults:
581 args = map(util.expandpath, shlex.split(defaults)) + args
581 args = map(util.expandpath, shlex.split(defaults)) + args
582 c = list(entry[1])
582 c = list(entry[1])
583 else:
583 else:
584 cmd = None
584 cmd = None
585 c = []
585 c = []
586
586
587 # combine global options into local
587 # combine global options into local
588 for o in commands.globalopts:
588 for o in commands.globalopts:
589 c.append((o[0], o[1], options[o[1]], o[3]))
589 c.append((o[0], o[1], options[o[1]], o[3]))
590
590
591 try:
591 try:
592 args = fancyopts.fancyopts(args, c, cmdoptions, True)
592 args = fancyopts.fancyopts(args, c, cmdoptions, True)
593 except fancyopts.getopt.GetoptError as inst:
593 except fancyopts.getopt.GetoptError as inst:
594 raise error.CommandError(cmd, inst)
594 raise error.CommandError(cmd, inst)
595
595
596 # separate global options back out
596 # separate global options back out
597 for o in commands.globalopts:
597 for o in commands.globalopts:
598 n = o[1]
598 n = o[1]
599 options[n] = cmdoptions[n]
599 options[n] = cmdoptions[n]
600 del cmdoptions[n]
600 del cmdoptions[n]
601
601
602 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
602 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
603
603
604 def _parseconfig(ui, config):
604 def _parseconfig(ui, config):
605 """parse the --config options from the command line"""
605 """parse the --config options from the command line"""
606 configs = []
606 configs = []
607
607
608 for cfg in config:
608 for cfg in config:
609 try:
609 try:
610 name, value = [cfgelem.strip()
610 name, value = [cfgelem.strip()
611 for cfgelem in cfg.split('=', 1)]
611 for cfgelem in cfg.split('=', 1)]
612 section, name = name.split('.', 1)
612 section, name = name.split('.', 1)
613 if not section or not name:
613 if not section or not name:
614 raise IndexError
614 raise IndexError
615 ui.setconfig(section, name, value, '--config')
615 ui.setconfig(section, name, value, '--config')
616 configs.append((section, name, value))
616 configs.append((section, name, value))
617 except (IndexError, ValueError):
617 except (IndexError, ValueError):
618 raise error.Abort(_('malformed --config option: %r '
618 raise error.Abort(_('malformed --config option: %r '
619 '(use --config section.name=value)') % cfg)
619 '(use --config section.name=value)') % cfg)
620
620
621 return configs
621 return configs
622
622
623 def _earlygetopt(aliases, args):
623 def _earlygetopt(aliases, args):
624 """Return list of values for an option (or aliases).
624 """Return list of values for an option (or aliases).
625
625
626 The values are listed in the order they appear in args.
626 The values are listed in the order they appear in args.
627 The options and values are removed from args.
627 The options and values are removed from args.
628
628
629 >>> args = ['x', '--cwd', 'foo', 'y']
629 >>> args = ['x', '--cwd', 'foo', 'y']
630 >>> _earlygetopt(['--cwd'], args), args
630 >>> _earlygetopt(['--cwd'], args), args
631 (['foo'], ['x', 'y'])
631 (['foo'], ['x', 'y'])
632
632
633 >>> args = ['x', '--cwd=bar', 'y']
633 >>> args = ['x', '--cwd=bar', 'y']
634 >>> _earlygetopt(['--cwd'], args), args
634 >>> _earlygetopt(['--cwd'], args), args
635 (['bar'], ['x', 'y'])
635 (['bar'], ['x', 'y'])
636
636
637 >>> args = ['x', '-R', 'foo', 'y']
637 >>> args = ['x', '-R', 'foo', 'y']
638 >>> _earlygetopt(['-R'], args), args
638 >>> _earlygetopt(['-R'], args), args
639 (['foo'], ['x', 'y'])
639 (['foo'], ['x', 'y'])
640
640
641 >>> args = ['x', '-Rbar', 'y']
641 >>> args = ['x', '-Rbar', 'y']
642 >>> _earlygetopt(['-R'], args), args
642 >>> _earlygetopt(['-R'], args), args
643 (['bar'], ['x', 'y'])
643 (['bar'], ['x', 'y'])
644 """
644 """
645 try:
645 try:
646 argcount = args.index("--")
646 argcount = args.index("--")
647 except ValueError:
647 except ValueError:
648 argcount = len(args)
648 argcount = len(args)
649 shortopts = [opt for opt in aliases if len(opt) == 2]
649 shortopts = [opt for opt in aliases if len(opt) == 2]
650 values = []
650 values = []
651 pos = 0
651 pos = 0
652 while pos < argcount:
652 while pos < argcount:
653 fullarg = arg = args[pos]
653 fullarg = arg = args[pos]
654 equals = arg.find('=')
654 equals = arg.find('=')
655 if equals > -1:
655 if equals > -1:
656 arg = arg[:equals]
656 arg = arg[:equals]
657 if arg in aliases:
657 if arg in aliases:
658 del args[pos]
658 del args[pos]
659 if equals > -1:
659 if equals > -1:
660 values.append(fullarg[equals + 1:])
660 values.append(fullarg[equals + 1:])
661 argcount -= 1
661 argcount -= 1
662 else:
662 else:
663 if pos + 1 >= argcount:
663 if pos + 1 >= argcount:
664 # ignore and let getopt report an error if there is no value
664 # ignore and let getopt report an error if there is no value
665 break
665 break
666 values.append(args.pop(pos))
666 values.append(args.pop(pos))
667 argcount -= 2
667 argcount -= 2
668 elif arg[:2] in shortopts:
668 elif arg[:2] in shortopts:
669 # short option can have no following space, e.g. hg log -Rfoo
669 # short option can have no following space, e.g. hg log -Rfoo
670 values.append(args.pop(pos)[2:])
670 values.append(args.pop(pos)[2:])
671 argcount -= 1
671 argcount -= 1
672 else:
672 else:
673 pos += 1
673 pos += 1
674 return values
674 return values
675
675
676 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
676 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
677 # run pre-hook, and abort if it fails
677 # run pre-hook, and abort if it fails
678 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
678 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
679 pats=cmdpats, opts=cmdoptions)
679 pats=cmdpats, opts=cmdoptions)
680 ret = _runcommand(ui, options, cmd, d)
680 ret = _runcommand(ui, options, cmd, d)
681 # run post-hook, passing command result
681 # run post-hook, passing command result
682 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
682 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
683 result=ret, pats=cmdpats, opts=cmdoptions)
683 result=ret, pats=cmdpats, opts=cmdoptions)
684 return ret
684 return ret
685
685
686 def _getlocal(ui, rpath, wd=None):
686 def _getlocal(ui, rpath, wd=None):
687 """Return (path, local ui object) for the given target path.
687 """Return (path, local ui object) for the given target path.
688
688
689 Takes paths in [cwd]/.hg/hgrc into account."
689 Takes paths in [cwd]/.hg/hgrc into account."
690 """
690 """
691 if wd is None:
691 if wd is None:
692 try:
692 try:
693 wd = os.getcwd()
693 wd = os.getcwd()
694 except OSError as e:
694 except OSError as e:
695 raise error.Abort(_("error getting current working directory: %s") %
695 raise error.Abort(_("error getting current working directory: %s") %
696 e.strerror)
696 e.strerror)
697 path = cmdutil.findrepo(wd) or ""
697 path = cmdutil.findrepo(wd) or ""
698 if not path:
698 if not path:
699 lui = ui
699 lui = ui
700 else:
700 else:
701 lui = ui.copy()
701 lui = ui.copy()
702 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
702 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
703
703
704 if rpath and rpath[-1]:
704 if rpath and rpath[-1]:
705 path = lui.expandpath(rpath[-1])
705 path = lui.expandpath(rpath[-1])
706 lui = ui.copy()
706 lui = ui.copy()
707 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
707 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
708
708
709 return path, lui
709 return path, lui
710
710
711 def _checkshellalias(lui, ui, args, precheck=True):
711 def _checkshellalias(lui, ui, args, precheck=True):
712 """Return the function to run the shell alias, if it is required
712 """Return the function to run the shell alias, if it is required
713
713
714 'precheck' is whether this function is invoked before adding
714 'precheck' is whether this function is invoked before adding
715 aliases or not.
715 aliases or not.
716 """
716 """
717 options = {}
717 options = {}
718
718
719 try:
719 try:
720 args = fancyopts.fancyopts(args, commands.globalopts, options)
720 args = fancyopts.fancyopts(args, commands.globalopts, options)
721 except fancyopts.getopt.GetoptError:
721 except fancyopts.getopt.GetoptError:
722 return
722 return
723
723
724 if not args:
724 if not args:
725 return
725 return
726
726
727 if precheck:
727 if precheck:
728 strict = True
728 strict = True
729 cmdtable = commands.table.copy()
729 cmdtable = commands.table.copy()
730 addaliases(lui, cmdtable)
730 addaliases(lui, cmdtable)
731 else:
731 else:
732 strict = False
732 strict = False
733 cmdtable = commands.table
733 cmdtable = commands.table
734
734
735 cmd = args[0]
735 cmd = args[0]
736 try:
736 try:
737 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
737 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
738 except (error.AmbiguousCommand, error.UnknownCommand):
738 except (error.AmbiguousCommand, error.UnknownCommand):
739 return
739 return
740
740
741 cmd = aliases[0]
741 cmd = aliases[0]
742 fn = entry[0]
742 fn = entry[0]
743
743
744 if cmd and util.safehasattr(fn, 'shell'):
744 if cmd and util.safehasattr(fn, 'shell'):
745 d = lambda: fn(ui, *args[1:])
745 d = lambda: fn(ui, *args[1:])
746 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
746 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
747 [], {})
747 [], {})
748
748
749 def _cmdattr(ui, cmd, func, attr):
749 def _cmdattr(ui, cmd, func, attr):
750 return getattr(func, attr)
750 try:
751 return getattr(func, attr)
752 except AttributeError:
753 ui.deprecwarn("missing attribute '%s', use @command decorator "
754 "to register '%s'" % (attr, cmd), '3.8')
755 return False
751
756
752 _loaded = set()
757 _loaded = set()
753
758
754 # list of (objname, loadermod, loadername) tuple:
759 # list of (objname, loadermod, loadername) tuple:
755 # - objname is the name of an object in extension module, from which
760 # - objname is the name of an object in extension module, from which
756 # extra information is loaded
761 # extra information is loaded
757 # - loadermod is the module where loader is placed
762 # - loadermod is the module where loader is placed
758 # - loadername is the name of the function, which takes (ui, extensionname,
763 # - loadername is the name of the function, which takes (ui, extensionname,
759 # extraobj) arguments
764 # extraobj) arguments
760 extraloaders = [
765 extraloaders = [
761 ('cmdtable', commands, 'loadcmdtable'),
766 ('cmdtable', commands, 'loadcmdtable'),
762 ('filesetpredicate', fileset, 'loadpredicate'),
767 ('filesetpredicate', fileset, 'loadpredicate'),
763 ('revsetpredicate', revset, 'loadpredicate'),
768 ('revsetpredicate', revset, 'loadpredicate'),
764 ('templatekeyword', templatekw, 'loadkeyword'),
769 ('templatekeyword', templatekw, 'loadkeyword'),
765 ]
770 ]
766
771
767 def _dispatch(req):
772 def _dispatch(req):
768 args = req.args
773 args = req.args
769 ui = req.ui
774 ui = req.ui
770
775
771 # check for cwd
776 # check for cwd
772 cwd = _earlygetopt(['--cwd'], args)
777 cwd = _earlygetopt(['--cwd'], args)
773 if cwd:
778 if cwd:
774 os.chdir(cwd[-1])
779 os.chdir(cwd[-1])
775
780
776 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
781 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
777 path, lui = _getlocal(ui, rpath)
782 path, lui = _getlocal(ui, rpath)
778
783
779 # Now that we're operating in the right directory/repository with
784 # Now that we're operating in the right directory/repository with
780 # the right config settings, check for shell aliases
785 # the right config settings, check for shell aliases
781 shellaliasfn = _checkshellalias(lui, ui, args)
786 shellaliasfn = _checkshellalias(lui, ui, args)
782 if shellaliasfn:
787 if shellaliasfn:
783 return shellaliasfn()
788 return shellaliasfn()
784
789
785 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
790 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
786 # reposetup. Programs like TortoiseHg will call _dispatch several
791 # reposetup. Programs like TortoiseHg will call _dispatch several
787 # times so we keep track of configured extensions in _loaded.
792 # times so we keep track of configured extensions in _loaded.
788 extensions.loadall(lui)
793 extensions.loadall(lui)
789 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
794 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
790 # Propagate any changes to lui.__class__ by extensions
795 # Propagate any changes to lui.__class__ by extensions
791 ui.__class__ = lui.__class__
796 ui.__class__ = lui.__class__
792
797
793 # (uisetup and extsetup are handled in extensions.loadall)
798 # (uisetup and extsetup are handled in extensions.loadall)
794
799
795 for name, module in exts:
800 for name, module in exts:
796 for objname, loadermod, loadername in extraloaders:
801 for objname, loadermod, loadername in extraloaders:
797 extraobj = getattr(module, objname, None)
802 extraobj = getattr(module, objname, None)
798 if extraobj is not None:
803 if extraobj is not None:
799 getattr(loadermod, loadername)(ui, name, extraobj)
804 getattr(loadermod, loadername)(ui, name, extraobj)
800 _loaded.add(name)
805 _loaded.add(name)
801
806
802 # (reposetup is handled in hg.repository)
807 # (reposetup is handled in hg.repository)
803
808
804 addaliases(lui, commands.table)
809 addaliases(lui, commands.table)
805
810
806 if not lui.configbool("ui", "strict"):
811 if not lui.configbool("ui", "strict"):
807 # All aliases and commands are completely defined, now.
812 # All aliases and commands are completely defined, now.
808 # Check abbreviation/ambiguity of shell alias again, because shell
813 # Check abbreviation/ambiguity of shell alias again, because shell
809 # alias may cause failure of "_parse" (see issue4355)
814 # alias may cause failure of "_parse" (see issue4355)
810 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
815 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
811 if shellaliasfn:
816 if shellaliasfn:
812 return shellaliasfn()
817 return shellaliasfn()
813
818
814 # check for fallback encoding
819 # check for fallback encoding
815 fallback = lui.config('ui', 'fallbackencoding')
820 fallback = lui.config('ui', 'fallbackencoding')
816 if fallback:
821 if fallback:
817 encoding.fallbackencoding = fallback
822 encoding.fallbackencoding = fallback
818
823
819 fullargs = args
824 fullargs = args
820 cmd, func, args, options, cmdoptions = _parse(lui, args)
825 cmd, func, args, options, cmdoptions = _parse(lui, args)
821
826
822 if options["config"]:
827 if options["config"]:
823 raise error.Abort(_("option --config may not be abbreviated!"))
828 raise error.Abort(_("option --config may not be abbreviated!"))
824 if options["cwd"]:
829 if options["cwd"]:
825 raise error.Abort(_("option --cwd may not be abbreviated!"))
830 raise error.Abort(_("option --cwd may not be abbreviated!"))
826 if options["repository"]:
831 if options["repository"]:
827 raise error.Abort(_(
832 raise error.Abort(_(
828 "option -R has to be separated from other options (e.g. not -qR) "
833 "option -R has to be separated from other options (e.g. not -qR) "
829 "and --repository may only be abbreviated as --repo!"))
834 "and --repository may only be abbreviated as --repo!"))
830
835
831 if options["encoding"]:
836 if options["encoding"]:
832 encoding.encoding = options["encoding"]
837 encoding.encoding = options["encoding"]
833 if options["encodingmode"]:
838 if options["encodingmode"]:
834 encoding.encodingmode = options["encodingmode"]
839 encoding.encodingmode = options["encodingmode"]
835 if options["time"]:
840 if options["time"]:
836 def get_times():
841 def get_times():
837 t = os.times()
842 t = os.times()
838 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
843 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
839 t = (t[0], t[1], t[2], t[3], time.clock())
844 t = (t[0], t[1], t[2], t[3], time.clock())
840 return t
845 return t
841 s = get_times()
846 s = get_times()
842 def print_time():
847 def print_time():
843 t = get_times()
848 t = get_times()
844 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
849 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
845 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
850 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
846 atexit.register(print_time)
851 atexit.register(print_time)
847
852
848 uis = set([ui, lui])
853 uis = set([ui, lui])
849
854
850 if req.repo:
855 if req.repo:
851 uis.add(req.repo.ui)
856 uis.add(req.repo.ui)
852
857
853 if options['verbose'] or options['debug'] or options['quiet']:
858 if options['verbose'] or options['debug'] or options['quiet']:
854 for opt in ('verbose', 'debug', 'quiet'):
859 for opt in ('verbose', 'debug', 'quiet'):
855 val = str(bool(options[opt]))
860 val = str(bool(options[opt]))
856 for ui_ in uis:
861 for ui_ in uis:
857 ui_.setconfig('ui', opt, val, '--' + opt)
862 ui_.setconfig('ui', opt, val, '--' + opt)
858
863
859 if options['traceback']:
864 if options['traceback']:
860 for ui_ in uis:
865 for ui_ in uis:
861 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
866 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
862
867
863 if options['noninteractive']:
868 if options['noninteractive']:
864 for ui_ in uis:
869 for ui_ in uis:
865 ui_.setconfig('ui', 'interactive', 'off', '-y')
870 ui_.setconfig('ui', 'interactive', 'off', '-y')
866
871
867 if cmdoptions.get('insecure', False):
872 if cmdoptions.get('insecure', False):
868 for ui_ in uis:
873 for ui_ in uis:
869 ui_.setconfig('web', 'cacerts', '!', '--insecure')
874 ui_.setconfig('web', 'cacerts', '!', '--insecure')
870
875
871 if options['version']:
876 if options['version']:
872 return commands.version_(ui)
877 return commands.version_(ui)
873 if options['help']:
878 if options['help']:
874 return commands.help_(ui, cmd, command=cmd is not None)
879 return commands.help_(ui, cmd, command=cmd is not None)
875 elif not cmd:
880 elif not cmd:
876 return commands.help_(ui, 'shortlist')
881 return commands.help_(ui, 'shortlist')
877
882
878 repo = None
883 repo = None
879 cmdpats = args[:]
884 cmdpats = args[:]
880 if not _cmdattr(ui, cmd, func, 'norepo'):
885 if not _cmdattr(ui, cmd, func, 'norepo'):
881 # use the repo from the request only if we don't have -R
886 # use the repo from the request only if we don't have -R
882 if not rpath and not cwd:
887 if not rpath and not cwd:
883 repo = req.repo
888 repo = req.repo
884
889
885 if repo:
890 if repo:
886 # set the descriptors of the repo ui to those of ui
891 # set the descriptors of the repo ui to those of ui
887 repo.ui.fin = ui.fin
892 repo.ui.fin = ui.fin
888 repo.ui.fout = ui.fout
893 repo.ui.fout = ui.fout
889 repo.ui.ferr = ui.ferr
894 repo.ui.ferr = ui.ferr
890 else:
895 else:
891 try:
896 try:
892 repo = hg.repository(ui, path=path)
897 repo = hg.repository(ui, path=path)
893 if not repo.local():
898 if not repo.local():
894 raise error.Abort(_("repository '%s' is not local") % path)
899 raise error.Abort(_("repository '%s' is not local") % path)
895 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
900 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
896 except error.RequirementError:
901 except error.RequirementError:
897 raise
902 raise
898 except error.RepoError:
903 except error.RepoError:
899 if rpath and rpath[-1]: # invalid -R path
904 if rpath and rpath[-1]: # invalid -R path
900 raise
905 raise
901 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
906 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
902 if (_cmdattr(ui, cmd, func, 'inferrepo') and
907 if (_cmdattr(ui, cmd, func, 'inferrepo') and
903 args and not path):
908 args and not path):
904 # try to infer -R from command args
909 # try to infer -R from command args
905 repos = map(cmdutil.findrepo, args)
910 repos = map(cmdutil.findrepo, args)
906 guess = repos[0]
911 guess = repos[0]
907 if guess and repos.count(guess) == len(repos):
912 if guess and repos.count(guess) == len(repos):
908 req.args = ['--repository', guess] + fullargs
913 req.args = ['--repository', guess] + fullargs
909 return _dispatch(req)
914 return _dispatch(req)
910 if not path:
915 if not path:
911 raise error.RepoError(_("no repository found in '%s'"
916 raise error.RepoError(_("no repository found in '%s'"
912 " (.hg not found)")
917 " (.hg not found)")
913 % os.getcwd())
918 % os.getcwd())
914 raise
919 raise
915 if repo:
920 if repo:
916 ui = repo.ui
921 ui = repo.ui
917 if options['hidden']:
922 if options['hidden']:
918 repo = repo.unfiltered()
923 repo = repo.unfiltered()
919 args.insert(0, repo)
924 args.insert(0, repo)
920 elif rpath:
925 elif rpath:
921 ui.warn(_("warning: --repository ignored\n"))
926 ui.warn(_("warning: --repository ignored\n"))
922
927
923 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
928 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
924 ui.log("command", '%s\n', msg)
929 ui.log("command", '%s\n', msg)
925 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
930 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
926 try:
931 try:
927 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
932 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
928 cmdpats, cmdoptions)
933 cmdpats, cmdoptions)
929 finally:
934 finally:
930 if repo and repo != req.repo:
935 if repo and repo != req.repo:
931 repo.close()
936 repo.close()
932
937
933 def lsprofile(ui, func, fp):
938 def lsprofile(ui, func, fp):
934 format = ui.config('profiling', 'format', default='text')
939 format = ui.config('profiling', 'format', default='text')
935 field = ui.config('profiling', 'sort', default='inlinetime')
940 field = ui.config('profiling', 'sort', default='inlinetime')
936 limit = ui.configint('profiling', 'limit', default=30)
941 limit = ui.configint('profiling', 'limit', default=30)
937 climit = ui.configint('profiling', 'nested', default=0)
942 climit = ui.configint('profiling', 'nested', default=0)
938
943
939 if format not in ['text', 'kcachegrind']:
944 if format not in ['text', 'kcachegrind']:
940 ui.warn(_("unrecognized profiling format '%s'"
945 ui.warn(_("unrecognized profiling format '%s'"
941 " - Ignored\n") % format)
946 " - Ignored\n") % format)
942 format = 'text'
947 format = 'text'
943
948
944 try:
949 try:
945 from . import lsprof
950 from . import lsprof
946 except ImportError:
951 except ImportError:
947 raise error.Abort(_(
952 raise error.Abort(_(
948 'lsprof not available - install from '
953 'lsprof not available - install from '
949 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
954 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
950 p = lsprof.Profiler()
955 p = lsprof.Profiler()
951 p.enable(subcalls=True)
956 p.enable(subcalls=True)
952 try:
957 try:
953 return func()
958 return func()
954 finally:
959 finally:
955 p.disable()
960 p.disable()
956
961
957 if format == 'kcachegrind':
962 if format == 'kcachegrind':
958 from . import lsprofcalltree
963 from . import lsprofcalltree
959 calltree = lsprofcalltree.KCacheGrind(p)
964 calltree = lsprofcalltree.KCacheGrind(p)
960 calltree.output(fp)
965 calltree.output(fp)
961 else:
966 else:
962 # format == 'text'
967 # format == 'text'
963 stats = lsprof.Stats(p.getstats())
968 stats = lsprof.Stats(p.getstats())
964 stats.sort(field)
969 stats.sort(field)
965 stats.pprint(limit=limit, file=fp, climit=climit)
970 stats.pprint(limit=limit, file=fp, climit=climit)
966
971
967 def flameprofile(ui, func, fp):
972 def flameprofile(ui, func, fp):
968 try:
973 try:
969 from flamegraph import flamegraph
974 from flamegraph import flamegraph
970 except ImportError:
975 except ImportError:
971 raise error.Abort(_(
976 raise error.Abort(_(
972 'flamegraph not available - install from '
977 'flamegraph not available - install from '
973 'https://github.com/evanhempel/python-flamegraph'))
978 'https://github.com/evanhempel/python-flamegraph'))
974 # developer config: profiling.freq
979 # developer config: profiling.freq
975 freq = ui.configint('profiling', 'freq', default=1000)
980 freq = ui.configint('profiling', 'freq', default=1000)
976 filter_ = None
981 filter_ = None
977 collapse_recursion = True
982 collapse_recursion = True
978 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
983 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
979 filter_, collapse_recursion)
984 filter_, collapse_recursion)
980 start_time = time.clock()
985 start_time = time.clock()
981 try:
986 try:
982 thread.start()
987 thread.start()
983 func()
988 func()
984 finally:
989 finally:
985 thread.stop()
990 thread.stop()
986 thread.join()
991 thread.join()
987 print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
992 print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
988 time.clock() - start_time, thread.num_frames(),
993 time.clock() - start_time, thread.num_frames(),
989 thread.num_frames(unique=True)))
994 thread.num_frames(unique=True)))
990
995
991
996
992 def statprofile(ui, func, fp):
997 def statprofile(ui, func, fp):
993 try:
998 try:
994 import statprof
999 import statprof
995 except ImportError:
1000 except ImportError:
996 raise error.Abort(_(
1001 raise error.Abort(_(
997 'statprof not available - install using "easy_install statprof"'))
1002 'statprof not available - install using "easy_install statprof"'))
998
1003
999 freq = ui.configint('profiling', 'freq', default=1000)
1004 freq = ui.configint('profiling', 'freq', default=1000)
1000 if freq > 0:
1005 if freq > 0:
1001 statprof.reset(freq)
1006 statprof.reset(freq)
1002 else:
1007 else:
1003 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
1008 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
1004
1009
1005 statprof.start()
1010 statprof.start()
1006 try:
1011 try:
1007 return func()
1012 return func()
1008 finally:
1013 finally:
1009 statprof.stop()
1014 statprof.stop()
1010 statprof.display(fp)
1015 statprof.display(fp)
1011
1016
1012 def _runcommand(ui, options, cmd, cmdfunc):
1017 def _runcommand(ui, options, cmd, cmdfunc):
1013 """Enables the profiler if applicable.
1018 """Enables the profiler if applicable.
1014
1019
1015 ``profiling.enabled`` - boolean config that enables or disables profiling
1020 ``profiling.enabled`` - boolean config that enables or disables profiling
1016 """
1021 """
1017 def checkargs():
1022 def checkargs():
1018 try:
1023 try:
1019 return cmdfunc()
1024 return cmdfunc()
1020 except error.SignatureError:
1025 except error.SignatureError:
1021 raise error.CommandError(cmd, _("invalid arguments"))
1026 raise error.CommandError(cmd, _("invalid arguments"))
1022
1027
1023 if options['profile'] or ui.configbool('profiling', 'enabled'):
1028 if options['profile'] or ui.configbool('profiling', 'enabled'):
1024 profiler = os.getenv('HGPROF')
1029 profiler = os.getenv('HGPROF')
1025 if profiler is None:
1030 if profiler is None:
1026 profiler = ui.config('profiling', 'type', default='ls')
1031 profiler = ui.config('profiling', 'type', default='ls')
1027 if profiler not in ('ls', 'stat', 'flame'):
1032 if profiler not in ('ls', 'stat', 'flame'):
1028 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
1033 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
1029 profiler = 'ls'
1034 profiler = 'ls'
1030
1035
1031 output = ui.config('profiling', 'output')
1036 output = ui.config('profiling', 'output')
1032
1037
1033 if output == 'blackbox':
1038 if output == 'blackbox':
1034 import StringIO
1039 import StringIO
1035 fp = StringIO.StringIO()
1040 fp = StringIO.StringIO()
1036 elif output:
1041 elif output:
1037 path = ui.expandpath(output)
1042 path = ui.expandpath(output)
1038 fp = open(path, 'wb')
1043 fp = open(path, 'wb')
1039 else:
1044 else:
1040 fp = sys.stderr
1045 fp = sys.stderr
1041
1046
1042 try:
1047 try:
1043 if profiler == 'ls':
1048 if profiler == 'ls':
1044 return lsprofile(ui, checkargs, fp)
1049 return lsprofile(ui, checkargs, fp)
1045 elif profiler == 'flame':
1050 elif profiler == 'flame':
1046 return flameprofile(ui, checkargs, fp)
1051 return flameprofile(ui, checkargs, fp)
1047 else:
1052 else:
1048 return statprofile(ui, checkargs, fp)
1053 return statprofile(ui, checkargs, fp)
1049 finally:
1054 finally:
1050 if output:
1055 if output:
1051 if output == 'blackbox':
1056 if output == 'blackbox':
1052 val = "Profile:\n%s" % fp.getvalue()
1057 val = "Profile:\n%s" % fp.getvalue()
1053 # ui.log treats the input as a format string,
1058 # ui.log treats the input as a format string,
1054 # so we need to escape any % signs.
1059 # so we need to escape any % signs.
1055 val = val.replace('%', '%%')
1060 val = val.replace('%', '%%')
1056 ui.log('profile', val)
1061 ui.log('profile', val)
1057 fp.close()
1062 fp.close()
1058 else:
1063 else:
1059 return checkargs()
1064 return checkargs()
@@ -1,1228 +1,1270 b''
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import cmdutil, commands
5 > from mercurial import cmdutil, commands
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = cmdutil.command(cmdtable)
7 > command = cmdutil.command(cmdtable)
8 > def uisetup(ui):
8 > def uisetup(ui):
9 > ui.write("uisetup called\\n")
9 > ui.write("uisetup called\\n")
10 > ui.flush()
10 > ui.flush()
11 > def reposetup(ui, repo):
11 > def reposetup(ui, repo):
12 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
13 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
14 > ui.flush()
14 > ui.flush()
15 > @command('foo', [], 'hg foo')
15 > @command('foo', [], 'hg foo')
16 > def foo(ui, *args, **kwargs):
16 > def foo(ui, *args, **kwargs):
17 > ui.write("Foo\\n")
17 > ui.write("Foo\\n")
18 > @command('bar', [], 'hg bar', norepo=True)
18 > @command('bar', [], 'hg bar', norepo=True)
19 > def bar(ui, *args, **kwargs):
19 > def bar(ui, *args, **kwargs):
20 > ui.write("Bar\\n")
20 > ui.write("Bar\\n")
21 > EOF
21 > EOF
22 $ abspath=`pwd`/foobar.py
22 $ abspath=`pwd`/foobar.py
23
23
24 $ mkdir barfoo
24 $ mkdir barfoo
25 $ cp foobar.py barfoo/__init__.py
25 $ cp foobar.py barfoo/__init__.py
26 $ barfoopath=`pwd`/barfoo
26 $ barfoopath=`pwd`/barfoo
27
27
28 $ hg init a
28 $ hg init a
29 $ cd a
29 $ cd a
30 $ echo foo > file
30 $ echo foo > file
31 $ hg add file
31 $ hg add file
32 $ hg commit -m 'add file'
32 $ hg commit -m 'add file'
33
33
34 $ echo '[extensions]' >> $HGRCPATH
34 $ echo '[extensions]' >> $HGRCPATH
35 $ echo "foobar = $abspath" >> $HGRCPATH
35 $ echo "foobar = $abspath" >> $HGRCPATH
36 $ hg foo
36 $ hg foo
37 uisetup called
37 uisetup called
38 reposetup called for a
38 reposetup called for a
39 ui == repo.ui
39 ui == repo.ui
40 Foo
40 Foo
41
41
42 $ cd ..
42 $ cd ..
43 $ hg clone a b
43 $ hg clone a b
44 uisetup called
44 uisetup called
45 reposetup called for a
45 reposetup called for a
46 ui == repo.ui
46 ui == repo.ui
47 reposetup called for b
47 reposetup called for b
48 ui == repo.ui
48 ui == repo.ui
49 updating to branch default
49 updating to branch default
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51
51
52 $ hg bar
52 $ hg bar
53 uisetup called
53 uisetup called
54 Bar
54 Bar
55 $ echo 'foobar = !' >> $HGRCPATH
55 $ echo 'foobar = !' >> $HGRCPATH
56
56
57 module/__init__.py-style
57 module/__init__.py-style
58
58
59 $ echo "barfoo = $barfoopath" >> $HGRCPATH
59 $ echo "barfoo = $barfoopath" >> $HGRCPATH
60 $ cd a
60 $ cd a
61 $ hg foo
61 $ hg foo
62 uisetup called
62 uisetup called
63 reposetup called for a
63 reposetup called for a
64 ui == repo.ui
64 ui == repo.ui
65 Foo
65 Foo
66 $ echo 'barfoo = !' >> $HGRCPATH
66 $ echo 'barfoo = !' >> $HGRCPATH
67
67
68 Check that extensions are loaded in phases:
68 Check that extensions are loaded in phases:
69
69
70 $ cat > foo.py <<EOF
70 $ cat > foo.py <<EOF
71 > import os
71 > import os
72 > name = os.path.basename(__file__).rsplit('.', 1)[0]
72 > name = os.path.basename(__file__).rsplit('.', 1)[0]
73 > print "1) %s imported" % name
73 > print "1) %s imported" % name
74 > def uisetup(ui):
74 > def uisetup(ui):
75 > print "2) %s uisetup" % name
75 > print "2) %s uisetup" % name
76 > def extsetup():
76 > def extsetup():
77 > print "3) %s extsetup" % name
77 > print "3) %s extsetup" % name
78 > def reposetup(ui, repo):
78 > def reposetup(ui, repo):
79 > print "4) %s reposetup" % name
79 > print "4) %s reposetup" % name
80 > EOF
80 > EOF
81
81
82 $ cp foo.py bar.py
82 $ cp foo.py bar.py
83 $ echo 'foo = foo.py' >> $HGRCPATH
83 $ echo 'foo = foo.py' >> $HGRCPATH
84 $ echo 'bar = bar.py' >> $HGRCPATH
84 $ echo 'bar = bar.py' >> $HGRCPATH
85
85
86 Command with no output, we just want to see the extensions loaded:
86 Command with no output, we just want to see the extensions loaded:
87
87
88 $ hg paths
88 $ hg paths
89 1) foo imported
89 1) foo imported
90 1) bar imported
90 1) bar imported
91 2) foo uisetup
91 2) foo uisetup
92 2) bar uisetup
92 2) bar uisetup
93 3) foo extsetup
93 3) foo extsetup
94 3) bar extsetup
94 3) bar extsetup
95 4) foo reposetup
95 4) foo reposetup
96 4) bar reposetup
96 4) bar reposetup
97
97
98 Check hgweb's load order:
98 Check hgweb's load order:
99
99
100 $ cat > hgweb.cgi <<EOF
100 $ cat > hgweb.cgi <<EOF
101 > #!/usr/bin/env python
101 > #!/usr/bin/env python
102 > from mercurial import demandimport; demandimport.enable()
102 > from mercurial import demandimport; demandimport.enable()
103 > from mercurial.hgweb import hgweb
103 > from mercurial.hgweb import hgweb
104 > from mercurial.hgweb import wsgicgi
104 > from mercurial.hgweb import wsgicgi
105 > application = hgweb('.', 'test repo')
105 > application = hgweb('.', 'test repo')
106 > wsgicgi.launch(application)
106 > wsgicgi.launch(application)
107 > EOF
107 > EOF
108
108
109 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
109 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
110 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
110 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
111 > | grep '^[0-9]) ' # ignores HTML output
111 > | grep '^[0-9]) ' # ignores HTML output
112 1) foo imported
112 1) foo imported
113 1) bar imported
113 1) bar imported
114 2) foo uisetup
114 2) foo uisetup
115 2) bar uisetup
115 2) bar uisetup
116 3) foo extsetup
116 3) foo extsetup
117 3) bar extsetup
117 3) bar extsetup
118 4) foo reposetup
118 4) foo reposetup
119 4) bar reposetup
119 4) bar reposetup
120
120
121 $ echo 'foo = !' >> $HGRCPATH
121 $ echo 'foo = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
123
123
124 Check "from __future__ import absolute_import" support for external libraries
124 Check "from __future__ import absolute_import" support for external libraries
125
125
126 #if windows
126 #if windows
127 $ PATHSEP=";"
127 $ PATHSEP=";"
128 #else
128 #else
129 $ PATHSEP=":"
129 $ PATHSEP=":"
130 #endif
130 #endif
131 $ export PATHSEP
131 $ export PATHSEP
132
132
133 $ mkdir $TESTTMP/libroot
133 $ mkdir $TESTTMP/libroot
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
135 $ mkdir $TESTTMP/libroot/mod
135 $ mkdir $TESTTMP/libroot/mod
136 $ touch $TESTTMP/libroot/mod/__init__.py
136 $ touch $TESTTMP/libroot/mod/__init__.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
138
138
139 #if absimport
139 #if absimport
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
141 > from __future__ import absolute_import
141 > from __future__ import absolute_import
142 > import ambig # should load "libroot/ambig.py"
142 > import ambig # should load "libroot/ambig.py"
143 > s = ambig.s
143 > s = ambig.s
144 > EOF
144 > EOF
145 $ cat > loadabs.py <<EOF
145 $ cat > loadabs.py <<EOF
146 > import mod.ambigabs as ambigabs
146 > import mod.ambigabs as ambigabs
147 > def extsetup():
147 > def extsetup():
148 > print 'ambigabs.s=%s' % ambigabs.s
148 > print 'ambigabs.s=%s' % ambigabs.s
149 > EOF
149 > EOF
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
151 ambigabs.s=libroot/ambig.py
151 ambigabs.s=libroot/ambig.py
152 $TESTTMP/a (glob)
152 $TESTTMP/a (glob)
153 #endif
153 #endif
154
154
155 #if no-py3k
155 #if no-py3k
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
157 > import ambig # should load "libroot/mod/ambig.py"
157 > import ambig # should load "libroot/mod/ambig.py"
158 > s = ambig.s
158 > s = ambig.s
159 > EOF
159 > EOF
160 $ cat > loadrel.py <<EOF
160 $ cat > loadrel.py <<EOF
161 > import mod.ambigrel as ambigrel
161 > import mod.ambigrel as ambigrel
162 > def extsetup():
162 > def extsetup():
163 > print 'ambigrel.s=%s' % ambigrel.s
163 > print 'ambigrel.s=%s' % ambigrel.s
164 > EOF
164 > EOF
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
166 ambigrel.s=libroot/mod/ambig.py
166 ambigrel.s=libroot/mod/ambig.py
167 $TESTTMP/a (glob)
167 $TESTTMP/a (glob)
168 #endif
168 #endif
169
169
170 Check absolute/relative import of extension specific modules
170 Check absolute/relative import of extension specific modules
171
171
172 $ mkdir $TESTTMP/extroot
172 $ mkdir $TESTTMP/extroot
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
174 > s = 'this is extroot.bar'
174 > s = 'this is extroot.bar'
175 > EOF
175 > EOF
176 $ mkdir $TESTTMP/extroot/sub1
176 $ mkdir $TESTTMP/extroot/sub1
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
178 > s = 'this is extroot.sub1.__init__'
178 > s = 'this is extroot.sub1.__init__'
179 > EOF
179 > EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
181 > s = 'this is extroot.sub1.baz'
181 > s = 'this is extroot.sub1.baz'
182 > EOF
182 > EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
184 > s = 'this is extroot.__init__'
184 > s = 'this is extroot.__init__'
185 > import foo
185 > import foo
186 > def extsetup(ui):
186 > def extsetup(ui):
187 > ui.write('(extroot) ', foo.func(), '\n')
187 > ui.write('(extroot) ', foo.func(), '\n')
188 > ui.flush()
188 > ui.flush()
189 > EOF
189 > EOF
190
190
191 $ cat > $TESTTMP/extroot/foo.py <<EOF
191 $ cat > $TESTTMP/extroot/foo.py <<EOF
192 > # test absolute import
192 > # test absolute import
193 > buf = []
193 > buf = []
194 > def func():
194 > def func():
195 > # "not locals" case
195 > # "not locals" case
196 > import extroot.bar
196 > import extroot.bar
197 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
197 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
198 > return '\n(extroot) '.join(buf)
198 > return '\n(extroot) '.join(buf)
199 > # "fromlist == ('*',)" case
199 > # "fromlist == ('*',)" case
200 > from extroot.bar import *
200 > from extroot.bar import *
201 > buf.append('from extroot.bar import *: %s' % s)
201 > buf.append('from extroot.bar import *: %s' % s)
202 > # "not fromlist" and "if '.' in name" case
202 > # "not fromlist" and "if '.' in name" case
203 > import extroot.sub1.baz
203 > import extroot.sub1.baz
204 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
204 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
205 > # "not fromlist" and NOT "if '.' in name" case
205 > # "not fromlist" and NOT "if '.' in name" case
206 > import extroot
206 > import extroot
207 > buf.append('import extroot: %s' % extroot.s)
207 > buf.append('import extroot: %s' % extroot.s)
208 > # NOT "not fromlist" and NOT "level != -1" case
208 > # NOT "not fromlist" and NOT "level != -1" case
209 > from extroot.bar import s
209 > from extroot.bar import s
210 > buf.append('from extroot.bar import s: %s' % s)
210 > buf.append('from extroot.bar import s: %s' % s)
211 > EOF
211 > EOF
212 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
212 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
213 (extroot) from extroot.bar import *: this is extroot.bar
213 (extroot) from extroot.bar import *: this is extroot.bar
214 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
214 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
215 (extroot) import extroot: this is extroot.__init__
215 (extroot) import extroot: this is extroot.__init__
216 (extroot) from extroot.bar import s: this is extroot.bar
216 (extroot) from extroot.bar import s: this is extroot.bar
217 (extroot) import extroot.bar in func(): this is extroot.bar
217 (extroot) import extroot.bar in func(): this is extroot.bar
218 $TESTTMP/a (glob)
218 $TESTTMP/a (glob)
219
219
220 #if no-py3k
220 #if no-py3k
221 $ rm "$TESTTMP"/extroot/foo.*
221 $ rm "$TESTTMP"/extroot/foo.*
222 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 $ cat > $TESTTMP/extroot/foo.py <<EOF
223 > # test relative import
223 > # test relative import
224 > buf = []
224 > buf = []
225 > def func():
225 > def func():
226 > # "not locals" case
226 > # "not locals" case
227 > import bar
227 > import bar
228 > buf.append('import bar in func(): %s' % bar.s)
228 > buf.append('import bar in func(): %s' % bar.s)
229 > return '\n(extroot) '.join(buf)
229 > return '\n(extroot) '.join(buf)
230 > # "fromlist == ('*',)" case
230 > # "fromlist == ('*',)" case
231 > from bar import *
231 > from bar import *
232 > buf.append('from bar import *: %s' % s)
232 > buf.append('from bar import *: %s' % s)
233 > # "not fromlist" and "if '.' in name" case
233 > # "not fromlist" and "if '.' in name" case
234 > import sub1.baz
234 > import sub1.baz
235 > buf.append('import sub1.baz: %s' % sub1.baz.s)
235 > buf.append('import sub1.baz: %s' % sub1.baz.s)
236 > # "not fromlist" and NOT "if '.' in name" case
236 > # "not fromlist" and NOT "if '.' in name" case
237 > import sub1
237 > import sub1
238 > buf.append('import sub1: %s' % sub1.s)
238 > buf.append('import sub1: %s' % sub1.s)
239 > # NOT "not fromlist" and NOT "level != -1" case
239 > # NOT "not fromlist" and NOT "level != -1" case
240 > from bar import s
240 > from bar import s
241 > buf.append('from bar import s: %s' % s)
241 > buf.append('from bar import s: %s' % s)
242 > EOF
242 > EOF
243 $ hg --config extensions.extroot=$TESTTMP/extroot root
243 $ hg --config extensions.extroot=$TESTTMP/extroot root
244 (extroot) from bar import *: this is extroot.bar
244 (extroot) from bar import *: this is extroot.bar
245 (extroot) import sub1.baz: this is extroot.sub1.baz
245 (extroot) import sub1.baz: this is extroot.sub1.baz
246 (extroot) import sub1: this is extroot.sub1.__init__
246 (extroot) import sub1: this is extroot.sub1.__init__
247 (extroot) from bar import s: this is extroot.bar
247 (extroot) from bar import s: this is extroot.bar
248 (extroot) import bar in func(): this is extroot.bar
248 (extroot) import bar in func(): this is extroot.bar
249 $TESTTMP/a (glob)
249 $TESTTMP/a (glob)
250 #endif
250 #endif
251
251
252 $ cd ..
252 $ cd ..
253
253
254 hide outer repo
254 hide outer repo
255 $ hg init
255 $ hg init
256
256
257 $ cat > empty.py <<EOF
257 $ cat > empty.py <<EOF
258 > '''empty cmdtable
258 > '''empty cmdtable
259 > '''
259 > '''
260 > cmdtable = {}
260 > cmdtable = {}
261 > EOF
261 > EOF
262 $ emptypath=`pwd`/empty.py
262 $ emptypath=`pwd`/empty.py
263 $ echo "empty = $emptypath" >> $HGRCPATH
263 $ echo "empty = $emptypath" >> $HGRCPATH
264 $ hg help empty
264 $ hg help empty
265 empty extension - empty cmdtable
265 empty extension - empty cmdtable
266
266
267 no commands defined
267 no commands defined
268
268
269
269
270 $ echo 'empty = !' >> $HGRCPATH
270 $ echo 'empty = !' >> $HGRCPATH
271
271
272 $ cat > debugextension.py <<EOF
272 $ cat > debugextension.py <<EOF
273 > '''only debugcommands
273 > '''only debugcommands
274 > '''
274 > '''
275 > from mercurial import cmdutil
275 > from mercurial import cmdutil
276 > cmdtable = {}
276 > cmdtable = {}
277 > command = cmdutil.command(cmdtable)
277 > command = cmdutil.command(cmdtable)
278 > @command('debugfoobar', [], 'hg debugfoobar')
278 > @command('debugfoobar', [], 'hg debugfoobar')
279 > def debugfoobar(ui, repo, *args, **opts):
279 > def debugfoobar(ui, repo, *args, **opts):
280 > "yet another debug command"
280 > "yet another debug command"
281 > pass
281 > pass
282 > @command('foo', [], 'hg foo')
282 > @command('foo', [], 'hg foo')
283 > def foo(ui, repo, *args, **opts):
283 > def foo(ui, repo, *args, **opts):
284 > """yet another foo command
284 > """yet another foo command
285 > This command has been DEPRECATED since forever.
285 > This command has been DEPRECATED since forever.
286 > """
286 > """
287 > pass
287 > pass
288 > EOF
288 > EOF
289 $ debugpath=`pwd`/debugextension.py
289 $ debugpath=`pwd`/debugextension.py
290 $ echo "debugextension = $debugpath" >> $HGRCPATH
290 $ echo "debugextension = $debugpath" >> $HGRCPATH
291
291
292 $ hg help debugextension
292 $ hg help debugextension
293 hg debugextensions
293 hg debugextensions
294
294
295 show information about active extensions
295 show information about active extensions
296
296
297 options:
297 options:
298
298
299 (some details hidden, use --verbose to show complete help)
299 (some details hidden, use --verbose to show complete help)
300
300
301
301
302 $ hg --verbose help debugextension
302 $ hg --verbose help debugextension
303 hg debugextensions
303 hg debugextensions
304
304
305 show information about active extensions
305 show information about active extensions
306
306
307 options:
307 options:
308
308
309 -T --template TEMPLATE display with template (EXPERIMENTAL)
309 -T --template TEMPLATE display with template (EXPERIMENTAL)
310
310
311 global options ([+] can be repeated):
311 global options ([+] can be repeated):
312
312
313 -R --repository REPO repository root directory or name of overlay bundle
313 -R --repository REPO repository root directory or name of overlay bundle
314 file
314 file
315 --cwd DIR change working directory
315 --cwd DIR change working directory
316 -y --noninteractive do not prompt, automatically pick the first choice for
316 -y --noninteractive do not prompt, automatically pick the first choice for
317 all prompts
317 all prompts
318 -q --quiet suppress output
318 -q --quiet suppress output
319 -v --verbose enable additional output
319 -v --verbose enable additional output
320 --config CONFIG [+] set/override config option (use 'section.name=value')
320 --config CONFIG [+] set/override config option (use 'section.name=value')
321 --debug enable debugging output
321 --debug enable debugging output
322 --debugger start debugger
322 --debugger start debugger
323 --encoding ENCODE set the charset encoding (default: ascii)
323 --encoding ENCODE set the charset encoding (default: ascii)
324 --encodingmode MODE set the charset encoding mode (default: strict)
324 --encodingmode MODE set the charset encoding mode (default: strict)
325 --traceback always print a traceback on exception
325 --traceback always print a traceback on exception
326 --time time how long the command takes
326 --time time how long the command takes
327 --profile print command execution profile
327 --profile print command execution profile
328 --version output version information and exit
328 --version output version information and exit
329 -h --help display help and exit
329 -h --help display help and exit
330 --hidden consider hidden changesets
330 --hidden consider hidden changesets
331
331
332
332
333
333
334
334
335
335
336
336
337 $ hg --debug help debugextension
337 $ hg --debug help debugextension
338 hg debugextensions
338 hg debugextensions
339
339
340 show information about active extensions
340 show information about active extensions
341
341
342 options:
342 options:
343
343
344 -T --template TEMPLATE display with template (EXPERIMENTAL)
344 -T --template TEMPLATE display with template (EXPERIMENTAL)
345
345
346 global options ([+] can be repeated):
346 global options ([+] can be repeated):
347
347
348 -R --repository REPO repository root directory or name of overlay bundle
348 -R --repository REPO repository root directory or name of overlay bundle
349 file
349 file
350 --cwd DIR change working directory
350 --cwd DIR change working directory
351 -y --noninteractive do not prompt, automatically pick the first choice for
351 -y --noninteractive do not prompt, automatically pick the first choice for
352 all prompts
352 all prompts
353 -q --quiet suppress output
353 -q --quiet suppress output
354 -v --verbose enable additional output
354 -v --verbose enable additional output
355 --config CONFIG [+] set/override config option (use 'section.name=value')
355 --config CONFIG [+] set/override config option (use 'section.name=value')
356 --debug enable debugging output
356 --debug enable debugging output
357 --debugger start debugger
357 --debugger start debugger
358 --encoding ENCODE set the charset encoding (default: ascii)
358 --encoding ENCODE set the charset encoding (default: ascii)
359 --encodingmode MODE set the charset encoding mode (default: strict)
359 --encodingmode MODE set the charset encoding mode (default: strict)
360 --traceback always print a traceback on exception
360 --traceback always print a traceback on exception
361 --time time how long the command takes
361 --time time how long the command takes
362 --profile print command execution profile
362 --profile print command execution profile
363 --version output version information and exit
363 --version output version information and exit
364 -h --help display help and exit
364 -h --help display help and exit
365 --hidden consider hidden changesets
365 --hidden consider hidden changesets
366
366
367
367
368
368
369
369
370
370
371 $ echo 'debugextension = !' >> $HGRCPATH
371 $ echo 'debugextension = !' >> $HGRCPATH
372
372
373 Asking for help about a deprecated extension should do something useful:
373 Asking for help about a deprecated extension should do something useful:
374
374
375 $ hg help glog
375 $ hg help glog
376 'glog' is provided by the following extension:
376 'glog' is provided by the following extension:
377
377
378 graphlog command to view revision graphs from a shell (DEPRECATED)
378 graphlog command to view revision graphs from a shell (DEPRECATED)
379
379
380 (use "hg help extensions" for information on enabling extensions)
380 (use "hg help extensions" for information on enabling extensions)
381
381
382 Extension module help vs command help:
382 Extension module help vs command help:
383
383
384 $ echo 'extdiff =' >> $HGRCPATH
384 $ echo 'extdiff =' >> $HGRCPATH
385 $ hg help extdiff
385 $ hg help extdiff
386 hg extdiff [OPT]... [FILE]...
386 hg extdiff [OPT]... [FILE]...
387
387
388 use external program to diff repository (or selected files)
388 use external program to diff repository (or selected files)
389
389
390 Show differences between revisions for the specified files, using an
390 Show differences between revisions for the specified files, using an
391 external program. The default program used is diff, with default options
391 external program. The default program used is diff, with default options
392 "-Npru".
392 "-Npru".
393
393
394 To select a different program, use the -p/--program option. The program
394 To select a different program, use the -p/--program option. The program
395 will be passed the names of two directories to compare. To pass additional
395 will be passed the names of two directories to compare. To pass additional
396 options to the program, use -o/--option. These will be passed before the
396 options to the program, use -o/--option. These will be passed before the
397 names of the directories to compare.
397 names of the directories to compare.
398
398
399 When two revision arguments are given, then changes are shown between
399 When two revision arguments are given, then changes are shown between
400 those revisions. If only one revision is specified then that revision is
400 those revisions. If only one revision is specified then that revision is
401 compared to the working directory, and, when no revisions are specified,
401 compared to the working directory, and, when no revisions are specified,
402 the working directory files are compared to its parent.
402 the working directory files are compared to its parent.
403
403
404 (use "hg help -e extdiff" to show help for the extdiff extension)
404 (use "hg help -e extdiff" to show help for the extdiff extension)
405
405
406 options ([+] can be repeated):
406 options ([+] can be repeated):
407
407
408 -p --program CMD comparison program to run
408 -p --program CMD comparison program to run
409 -o --option OPT [+] pass option to comparison program
409 -o --option OPT [+] pass option to comparison program
410 -r --rev REV [+] revision
410 -r --rev REV [+] revision
411 -c --change REV change made by revision
411 -c --change REV change made by revision
412 --patch compare patches for two revisions
412 --patch compare patches for two revisions
413 -I --include PATTERN [+] include names matching the given patterns
413 -I --include PATTERN [+] include names matching the given patterns
414 -X --exclude PATTERN [+] exclude names matching the given patterns
414 -X --exclude PATTERN [+] exclude names matching the given patterns
415 -S --subrepos recurse into subrepositories
415 -S --subrepos recurse into subrepositories
416
416
417 (some details hidden, use --verbose to show complete help)
417 (some details hidden, use --verbose to show complete help)
418
418
419
419
420
420
421
421
422
422
423
423
424
424
425
425
426
426
427
427
428 $ hg help --extension extdiff
428 $ hg help --extension extdiff
429 extdiff extension - command to allow external programs to compare revisions
429 extdiff extension - command to allow external programs to compare revisions
430
430
431 The extdiff Mercurial extension allows you to use external programs to compare
431 The extdiff Mercurial extension allows you to use external programs to compare
432 revisions, or revision with working directory. The external diff programs are
432 revisions, or revision with working directory. The external diff programs are
433 called with a configurable set of options and two non-option arguments: paths
433 called with a configurable set of options and two non-option arguments: paths
434 to directories containing snapshots of files to compare.
434 to directories containing snapshots of files to compare.
435
435
436 The extdiff extension also allows you to configure new diff commands, so you
436 The extdiff extension also allows you to configure new diff commands, so you
437 do not need to type 'hg extdiff -p kdiff3' always.
437 do not need to type 'hg extdiff -p kdiff3' always.
438
438
439 [extdiff]
439 [extdiff]
440 # add new command that runs GNU diff(1) in 'context diff' mode
440 # add new command that runs GNU diff(1) in 'context diff' mode
441 cdiff = gdiff -Nprc5
441 cdiff = gdiff -Nprc5
442 ## or the old way:
442 ## or the old way:
443 #cmd.cdiff = gdiff
443 #cmd.cdiff = gdiff
444 #opts.cdiff = -Nprc5
444 #opts.cdiff = -Nprc5
445
445
446 # add new command called meld, runs meld (no need to name twice). If
446 # add new command called meld, runs meld (no need to name twice). If
447 # the meld executable is not available, the meld tool in [merge-tools]
447 # the meld executable is not available, the meld tool in [merge-tools]
448 # will be used, if available
448 # will be used, if available
449 meld =
449 meld =
450
450
451 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
451 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
452 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
452 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
453 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
453 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
454 # your .vimrc
454 # your .vimrc
455 vimdiff = gvim -f "+next" \
455 vimdiff = gvim -f "+next" \
456 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
456 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
457
457
458 Tool arguments can include variables that are expanded at runtime:
458 Tool arguments can include variables that are expanded at runtime:
459
459
460 $parent1, $plabel1 - filename, descriptive label of first parent
460 $parent1, $plabel1 - filename, descriptive label of first parent
461 $child, $clabel - filename, descriptive label of child revision
461 $child, $clabel - filename, descriptive label of child revision
462 $parent2, $plabel2 - filename, descriptive label of second parent
462 $parent2, $plabel2 - filename, descriptive label of second parent
463 $root - repository root
463 $root - repository root
464 $parent is an alias for $parent1.
464 $parent is an alias for $parent1.
465
465
466 The extdiff extension will look in your [diff-tools] and [merge-tools]
466 The extdiff extension will look in your [diff-tools] and [merge-tools]
467 sections for diff tool arguments, when none are specified in [extdiff].
467 sections for diff tool arguments, when none are specified in [extdiff].
468
468
469 [extdiff]
469 [extdiff]
470 kdiff3 =
470 kdiff3 =
471
471
472 [diff-tools]
472 [diff-tools]
473 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
473 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
474
474
475 You can use -I/-X and list of file or directory names like normal 'hg diff'
475 You can use -I/-X and list of file or directory names like normal 'hg diff'
476 command. The extdiff extension makes snapshots of only needed files, so
476 command. The extdiff extension makes snapshots of only needed files, so
477 running the external diff program will actually be pretty fast (at least
477 running the external diff program will actually be pretty fast (at least
478 faster than having to compare the entire tree).
478 faster than having to compare the entire tree).
479
479
480 list of commands:
480 list of commands:
481
481
482 extdiff use external program to diff repository (or selected files)
482 extdiff use external program to diff repository (or selected files)
483
483
484 (use "hg help -v -e extdiff" to show built-in aliases and global options)
484 (use "hg help -v -e extdiff" to show built-in aliases and global options)
485
485
486
486
487
487
488
488
489
489
490
490
491
491
492
492
493
493
494
494
495
495
496
496
497
497
498
498
499
499
500
500
501 $ echo 'extdiff = !' >> $HGRCPATH
501 $ echo 'extdiff = !' >> $HGRCPATH
502
502
503 Test help topic with same name as extension
503 Test help topic with same name as extension
504
504
505 $ cat > multirevs.py <<EOF
505 $ cat > multirevs.py <<EOF
506 > from mercurial import cmdutil, commands
506 > from mercurial import cmdutil, commands
507 > cmdtable = {}
507 > cmdtable = {}
508 > command = cmdutil.command(cmdtable)
508 > command = cmdutil.command(cmdtable)
509 > """multirevs extension
509 > """multirevs extension
510 > Big multi-line module docstring."""
510 > Big multi-line module docstring."""
511 > @command('multirevs', [], 'ARG', norepo=True)
511 > @command('multirevs', [], 'ARG', norepo=True)
512 > def multirevs(ui, repo, arg, *args, **opts):
512 > def multirevs(ui, repo, arg, *args, **opts):
513 > """multirevs command"""
513 > """multirevs command"""
514 > pass
514 > pass
515 > EOF
515 > EOF
516 $ echo "multirevs = multirevs.py" >> $HGRCPATH
516 $ echo "multirevs = multirevs.py" >> $HGRCPATH
517
517
518 $ hg help multirevs
518 $ hg help multirevs
519 Specifying Multiple Revisions
519 Specifying Multiple Revisions
520 """""""""""""""""""""""""""""
520 """""""""""""""""""""""""""""
521
521
522 When Mercurial accepts more than one revision, they may be specified
522 When Mercurial accepts more than one revision, they may be specified
523 individually, or provided as a topologically continuous range, separated
523 individually, or provided as a topologically continuous range, separated
524 by the ":" character.
524 by the ":" character.
525
525
526 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
526 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
527 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
527 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
528 specified, it defaults to revision number 0. If END is not specified, it
528 specified, it defaults to revision number 0. If END is not specified, it
529 defaults to the tip. The range ":" thus means "all revisions".
529 defaults to the tip. The range ":" thus means "all revisions".
530
530
531 If BEGIN is greater than END, revisions are treated in reverse order.
531 If BEGIN is greater than END, revisions are treated in reverse order.
532
532
533 A range acts as a closed interval. This means that a range of 3:5 gives 3,
533 A range acts as a closed interval. This means that a range of 3:5 gives 3,
534 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
534 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
535
535
536 use "hg help -c multirevs" to see help for the multirevs command
536 use "hg help -c multirevs" to see help for the multirevs command
537
537
538
538
539
539
540
540
541
541
542
542
543 $ hg help -c multirevs
543 $ hg help -c multirevs
544 hg multirevs ARG
544 hg multirevs ARG
545
545
546 multirevs command
546 multirevs command
547
547
548 (some details hidden, use --verbose to show complete help)
548 (some details hidden, use --verbose to show complete help)
549
549
550
550
551
551
552 $ hg multirevs
552 $ hg multirevs
553 hg multirevs: invalid arguments
553 hg multirevs: invalid arguments
554 hg multirevs ARG
554 hg multirevs ARG
555
555
556 multirevs command
556 multirevs command
557
557
558 (use "hg multirevs -h" to show more help)
558 (use "hg multirevs -h" to show more help)
559 [255]
559 [255]
560
560
561
561
562
562
563 $ echo "multirevs = !" >> $HGRCPATH
563 $ echo "multirevs = !" >> $HGRCPATH
564
564
565 Issue811: Problem loading extensions twice (by site and by user)
565 Issue811: Problem loading extensions twice (by site and by user)
566
566
567 $ cat <<EOF >> $HGRCPATH
567 $ cat <<EOF >> $HGRCPATH
568 > mq =
568 > mq =
569 > strip =
569 > strip =
570 > hgext.mq =
570 > hgext.mq =
571 > hgext/mq =
571 > hgext/mq =
572 > EOF
572 > EOF
573
573
574 Show extensions:
574 Show extensions:
575 (note that mq force load strip, also checking it's not loaded twice)
575 (note that mq force load strip, also checking it's not loaded twice)
576
576
577 $ hg debugextensions
577 $ hg debugextensions
578 mq
578 mq
579 strip
579 strip
580
580
581 For extensions, which name matches one of its commands, help
581 For extensions, which name matches one of its commands, help
582 message should ask '-v -e' to get list of built-in aliases
582 message should ask '-v -e' to get list of built-in aliases
583 along with extension help itself
583 along with extension help itself
584
584
585 $ mkdir $TESTTMP/d
585 $ mkdir $TESTTMP/d
586 $ cat > $TESTTMP/d/dodo.py <<EOF
586 $ cat > $TESTTMP/d/dodo.py <<EOF
587 > """
587 > """
588 > This is an awesome 'dodo' extension. It does nothing and
588 > This is an awesome 'dodo' extension. It does nothing and
589 > writes 'Foo foo'
589 > writes 'Foo foo'
590 > """
590 > """
591 > from mercurial import cmdutil, commands
591 > from mercurial import cmdutil, commands
592 > cmdtable = {}
592 > cmdtable = {}
593 > command = cmdutil.command(cmdtable)
593 > command = cmdutil.command(cmdtable)
594 > @command('dodo', [], 'hg dodo')
594 > @command('dodo', [], 'hg dodo')
595 > def dodo(ui, *args, **kwargs):
595 > def dodo(ui, *args, **kwargs):
596 > """Does nothing"""
596 > """Does nothing"""
597 > ui.write("I do nothing. Yay\\n")
597 > ui.write("I do nothing. Yay\\n")
598 > @command('foofoo', [], 'hg foofoo')
598 > @command('foofoo', [], 'hg foofoo')
599 > def foofoo(ui, *args, **kwargs):
599 > def foofoo(ui, *args, **kwargs):
600 > """Writes 'Foo foo'"""
600 > """Writes 'Foo foo'"""
601 > ui.write("Foo foo\\n")
601 > ui.write("Foo foo\\n")
602 > EOF
602 > EOF
603 $ dodopath=$TESTTMP/d/dodo.py
603 $ dodopath=$TESTTMP/d/dodo.py
604
604
605 $ echo "dodo = $dodopath" >> $HGRCPATH
605 $ echo "dodo = $dodopath" >> $HGRCPATH
606
606
607 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
607 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
608 $ hg help -e dodo
608 $ hg help -e dodo
609 dodo extension -
609 dodo extension -
610
610
611 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
611 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
612
612
613 list of commands:
613 list of commands:
614
614
615 dodo Does nothing
615 dodo Does nothing
616 foofoo Writes 'Foo foo'
616 foofoo Writes 'Foo foo'
617
617
618 (use "hg help -v -e dodo" to show built-in aliases and global options)
618 (use "hg help -v -e dodo" to show built-in aliases and global options)
619
619
620 Make sure that '-v -e' prints list of built-in aliases along with
620 Make sure that '-v -e' prints list of built-in aliases along with
621 extension help itself
621 extension help itself
622 $ hg help -v -e dodo
622 $ hg help -v -e dodo
623 dodo extension -
623 dodo extension -
624
624
625 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
625 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
626
626
627 list of commands:
627 list of commands:
628
628
629 dodo Does nothing
629 dodo Does nothing
630 foofoo Writes 'Foo foo'
630 foofoo Writes 'Foo foo'
631
631
632 global options ([+] can be repeated):
632 global options ([+] can be repeated):
633
633
634 -R --repository REPO repository root directory or name of overlay bundle
634 -R --repository REPO repository root directory or name of overlay bundle
635 file
635 file
636 --cwd DIR change working directory
636 --cwd DIR change working directory
637 -y --noninteractive do not prompt, automatically pick the first choice for
637 -y --noninteractive do not prompt, automatically pick the first choice for
638 all prompts
638 all prompts
639 -q --quiet suppress output
639 -q --quiet suppress output
640 -v --verbose enable additional output
640 -v --verbose enable additional output
641 --config CONFIG [+] set/override config option (use 'section.name=value')
641 --config CONFIG [+] set/override config option (use 'section.name=value')
642 --debug enable debugging output
642 --debug enable debugging output
643 --debugger start debugger
643 --debugger start debugger
644 --encoding ENCODE set the charset encoding (default: ascii)
644 --encoding ENCODE set the charset encoding (default: ascii)
645 --encodingmode MODE set the charset encoding mode (default: strict)
645 --encodingmode MODE set the charset encoding mode (default: strict)
646 --traceback always print a traceback on exception
646 --traceback always print a traceback on exception
647 --time time how long the command takes
647 --time time how long the command takes
648 --profile print command execution profile
648 --profile print command execution profile
649 --version output version information and exit
649 --version output version information and exit
650 -h --help display help and exit
650 -h --help display help and exit
651 --hidden consider hidden changesets
651 --hidden consider hidden changesets
652
652
653 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
653 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
654 $ hg help -v dodo
654 $ hg help -v dodo
655 hg dodo
655 hg dodo
656
656
657 Does nothing
657 Does nothing
658
658
659 (use "hg help -e dodo" to show help for the dodo extension)
659 (use "hg help -e dodo" to show help for the dodo extension)
660
660
661 options:
661 options:
662
662
663 --mq operate on patch repository
663 --mq operate on patch repository
664
664
665 global options ([+] can be repeated):
665 global options ([+] can be repeated):
666
666
667 -R --repository REPO repository root directory or name of overlay bundle
667 -R --repository REPO repository root directory or name of overlay bundle
668 file
668 file
669 --cwd DIR change working directory
669 --cwd DIR change working directory
670 -y --noninteractive do not prompt, automatically pick the first choice for
670 -y --noninteractive do not prompt, automatically pick the first choice for
671 all prompts
671 all prompts
672 -q --quiet suppress output
672 -q --quiet suppress output
673 -v --verbose enable additional output
673 -v --verbose enable additional output
674 --config CONFIG [+] set/override config option (use 'section.name=value')
674 --config CONFIG [+] set/override config option (use 'section.name=value')
675 --debug enable debugging output
675 --debug enable debugging output
676 --debugger start debugger
676 --debugger start debugger
677 --encoding ENCODE set the charset encoding (default: ascii)
677 --encoding ENCODE set the charset encoding (default: ascii)
678 --encodingmode MODE set the charset encoding mode (default: strict)
678 --encodingmode MODE set the charset encoding mode (default: strict)
679 --traceback always print a traceback on exception
679 --traceback always print a traceback on exception
680 --time time how long the command takes
680 --time time how long the command takes
681 --profile print command execution profile
681 --profile print command execution profile
682 --version output version information and exit
682 --version output version information and exit
683 -h --help display help and exit
683 -h --help display help and exit
684 --hidden consider hidden changesets
684 --hidden consider hidden changesets
685
685
686 In case when extension name doesn't match any of its commands,
686 In case when extension name doesn't match any of its commands,
687 help message should ask for '-v' to get list of built-in aliases
687 help message should ask for '-v' to get list of built-in aliases
688 along with extension help
688 along with extension help
689 $ cat > $TESTTMP/d/dudu.py <<EOF
689 $ cat > $TESTTMP/d/dudu.py <<EOF
690 > """
690 > """
691 > This is an awesome 'dudu' extension. It does something and
691 > This is an awesome 'dudu' extension. It does something and
692 > also writes 'Beep beep'
692 > also writes 'Beep beep'
693 > """
693 > """
694 > from mercurial import cmdutil, commands
694 > from mercurial import cmdutil, commands
695 > cmdtable = {}
695 > cmdtable = {}
696 > command = cmdutil.command(cmdtable)
696 > command = cmdutil.command(cmdtable)
697 > @command('something', [], 'hg something')
697 > @command('something', [], 'hg something')
698 > def something(ui, *args, **kwargs):
698 > def something(ui, *args, **kwargs):
699 > """Does something"""
699 > """Does something"""
700 > ui.write("I do something. Yaaay\\n")
700 > ui.write("I do something. Yaaay\\n")
701 > @command('beep', [], 'hg beep')
701 > @command('beep', [], 'hg beep')
702 > def beep(ui, *args, **kwargs):
702 > def beep(ui, *args, **kwargs):
703 > """Writes 'Beep beep'"""
703 > """Writes 'Beep beep'"""
704 > ui.write("Beep beep\\n")
704 > ui.write("Beep beep\\n")
705 > EOF
705 > EOF
706 $ dudupath=$TESTTMP/d/dudu.py
706 $ dudupath=$TESTTMP/d/dudu.py
707
707
708 $ echo "dudu = $dudupath" >> $HGRCPATH
708 $ echo "dudu = $dudupath" >> $HGRCPATH
709
709
710 $ hg help -e dudu
710 $ hg help -e dudu
711 dudu extension -
711 dudu extension -
712
712
713 This is an awesome 'dudu' extension. It does something and also writes 'Beep
713 This is an awesome 'dudu' extension. It does something and also writes 'Beep
714 beep'
714 beep'
715
715
716 list of commands:
716 list of commands:
717
717
718 beep Writes 'Beep beep'
718 beep Writes 'Beep beep'
719 something Does something
719 something Does something
720
720
721 (use "hg help -v dudu" to show built-in aliases and global options)
721 (use "hg help -v dudu" to show built-in aliases and global options)
722
722
723 In case when extension name doesn't match any of its commands,
723 In case when extension name doesn't match any of its commands,
724 help options '-v' and '-v -e' should be equivalent
724 help options '-v' and '-v -e' should be equivalent
725 $ hg help -v dudu
725 $ hg help -v dudu
726 dudu extension -
726 dudu extension -
727
727
728 This is an awesome 'dudu' extension. It does something and also writes 'Beep
728 This is an awesome 'dudu' extension. It does something and also writes 'Beep
729 beep'
729 beep'
730
730
731 list of commands:
731 list of commands:
732
732
733 beep Writes 'Beep beep'
733 beep Writes 'Beep beep'
734 something Does something
734 something Does something
735
735
736 global options ([+] can be repeated):
736 global options ([+] can be repeated):
737
737
738 -R --repository REPO repository root directory or name of overlay bundle
738 -R --repository REPO repository root directory or name of overlay bundle
739 file
739 file
740 --cwd DIR change working directory
740 --cwd DIR change working directory
741 -y --noninteractive do not prompt, automatically pick the first choice for
741 -y --noninteractive do not prompt, automatically pick the first choice for
742 all prompts
742 all prompts
743 -q --quiet suppress output
743 -q --quiet suppress output
744 -v --verbose enable additional output
744 -v --verbose enable additional output
745 --config CONFIG [+] set/override config option (use 'section.name=value')
745 --config CONFIG [+] set/override config option (use 'section.name=value')
746 --debug enable debugging output
746 --debug enable debugging output
747 --debugger start debugger
747 --debugger start debugger
748 --encoding ENCODE set the charset encoding (default: ascii)
748 --encoding ENCODE set the charset encoding (default: ascii)
749 --encodingmode MODE set the charset encoding mode (default: strict)
749 --encodingmode MODE set the charset encoding mode (default: strict)
750 --traceback always print a traceback on exception
750 --traceback always print a traceback on exception
751 --time time how long the command takes
751 --time time how long the command takes
752 --profile print command execution profile
752 --profile print command execution profile
753 --version output version information and exit
753 --version output version information and exit
754 -h --help display help and exit
754 -h --help display help and exit
755 --hidden consider hidden changesets
755 --hidden consider hidden changesets
756
756
757 $ hg help -v -e dudu
757 $ hg help -v -e dudu
758 dudu extension -
758 dudu extension -
759
759
760 This is an awesome 'dudu' extension. It does something and also writes 'Beep
760 This is an awesome 'dudu' extension. It does something and also writes 'Beep
761 beep'
761 beep'
762
762
763 list of commands:
763 list of commands:
764
764
765 beep Writes 'Beep beep'
765 beep Writes 'Beep beep'
766 something Does something
766 something Does something
767
767
768 global options ([+] can be repeated):
768 global options ([+] can be repeated):
769
769
770 -R --repository REPO repository root directory or name of overlay bundle
770 -R --repository REPO repository root directory or name of overlay bundle
771 file
771 file
772 --cwd DIR change working directory
772 --cwd DIR change working directory
773 -y --noninteractive do not prompt, automatically pick the first choice for
773 -y --noninteractive do not prompt, automatically pick the first choice for
774 all prompts
774 all prompts
775 -q --quiet suppress output
775 -q --quiet suppress output
776 -v --verbose enable additional output
776 -v --verbose enable additional output
777 --config CONFIG [+] set/override config option (use 'section.name=value')
777 --config CONFIG [+] set/override config option (use 'section.name=value')
778 --debug enable debugging output
778 --debug enable debugging output
779 --debugger start debugger
779 --debugger start debugger
780 --encoding ENCODE set the charset encoding (default: ascii)
780 --encoding ENCODE set the charset encoding (default: ascii)
781 --encodingmode MODE set the charset encoding mode (default: strict)
781 --encodingmode MODE set the charset encoding mode (default: strict)
782 --traceback always print a traceback on exception
782 --traceback always print a traceback on exception
783 --time time how long the command takes
783 --time time how long the command takes
784 --profile print command execution profile
784 --profile print command execution profile
785 --version output version information and exit
785 --version output version information and exit
786 -h --help display help and exit
786 -h --help display help and exit
787 --hidden consider hidden changesets
787 --hidden consider hidden changesets
788
788
789 Disabled extension commands:
789 Disabled extension commands:
790
790
791 $ ORGHGRCPATH=$HGRCPATH
791 $ ORGHGRCPATH=$HGRCPATH
792 $ HGRCPATH=
792 $ HGRCPATH=
793 $ export HGRCPATH
793 $ export HGRCPATH
794 $ hg help email
794 $ hg help email
795 'email' is provided by the following extension:
795 'email' is provided by the following extension:
796
796
797 patchbomb command to send changesets as (a series of) patch emails
797 patchbomb command to send changesets as (a series of) patch emails
798
798
799 (use "hg help extensions" for information on enabling extensions)
799 (use "hg help extensions" for information on enabling extensions)
800
800
801
801
802 $ hg qdel
802 $ hg qdel
803 hg: unknown command 'qdel'
803 hg: unknown command 'qdel'
804 'qdelete' is provided by the following extension:
804 'qdelete' is provided by the following extension:
805
805
806 mq manage a stack of patches
806 mq manage a stack of patches
807
807
808 (use "hg help extensions" for information on enabling extensions)
808 (use "hg help extensions" for information on enabling extensions)
809 [255]
809 [255]
810
810
811
811
812 $ hg churn
812 $ hg churn
813 hg: unknown command 'churn'
813 hg: unknown command 'churn'
814 'churn' is provided by the following extension:
814 'churn' is provided by the following extension:
815
815
816 churn command to display statistics about repository history
816 churn command to display statistics about repository history
817
817
818 (use "hg help extensions" for information on enabling extensions)
818 (use "hg help extensions" for information on enabling extensions)
819 [255]
819 [255]
820
820
821
821
822
822
823 Disabled extensions:
823 Disabled extensions:
824
824
825 $ hg help churn
825 $ hg help churn
826 churn extension - command to display statistics about repository history
826 churn extension - command to display statistics about repository history
827
827
828 (use "hg help extensions" for information on enabling extensions)
828 (use "hg help extensions" for information on enabling extensions)
829
829
830 $ hg help patchbomb
830 $ hg help patchbomb
831 patchbomb extension - command to send changesets as (a series of) patch emails
831 patchbomb extension - command to send changesets as (a series of) patch emails
832
832
833 (use "hg help extensions" for information on enabling extensions)
833 (use "hg help extensions" for information on enabling extensions)
834
834
835
835
836 Broken disabled extension and command:
836 Broken disabled extension and command:
837
837
838 $ mkdir hgext
838 $ mkdir hgext
839 $ echo > hgext/__init__.py
839 $ echo > hgext/__init__.py
840 $ cat > hgext/broken.py <<EOF
840 $ cat > hgext/broken.py <<EOF
841 > "broken extension'
841 > "broken extension'
842 > EOF
842 > EOF
843 $ cat > path.py <<EOF
843 $ cat > path.py <<EOF
844 > import os, sys
844 > import os, sys
845 > sys.path.insert(0, os.environ['HGEXTPATH'])
845 > sys.path.insert(0, os.environ['HGEXTPATH'])
846 > EOF
846 > EOF
847 $ HGEXTPATH=`pwd`
847 $ HGEXTPATH=`pwd`
848 $ export HGEXTPATH
848 $ export HGEXTPATH
849
849
850 $ hg --config extensions.path=./path.py help broken
850 $ hg --config extensions.path=./path.py help broken
851 broken extension - (no help text available)
851 broken extension - (no help text available)
852
852
853 (use "hg help extensions" for information on enabling extensions)
853 (use "hg help extensions" for information on enabling extensions)
854
854
855
855
856 $ cat > hgext/forest.py <<EOF
856 $ cat > hgext/forest.py <<EOF
857 > cmdtable = None
857 > cmdtable = None
858 > EOF
858 > EOF
859 $ hg --config extensions.path=./path.py help foo > /dev/null
859 $ hg --config extensions.path=./path.py help foo > /dev/null
860 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
860 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
861 abort: no such help topic: foo
861 abort: no such help topic: foo
862 (try "hg help --keyword foo")
862 (try "hg help --keyword foo")
863 [255]
863 [255]
864
864
865 $ cat > throw.py <<EOF
865 $ cat > throw.py <<EOF
866 > from mercurial import cmdutil, commands, util
866 > from mercurial import cmdutil, commands, util
867 > cmdtable = {}
867 > cmdtable = {}
868 > command = cmdutil.command(cmdtable)
868 > command = cmdutil.command(cmdtable)
869 > class Bogon(Exception): pass
869 > class Bogon(Exception): pass
870 > @command('throw', [], 'hg throw', norepo=True)
870 > @command('throw', [], 'hg throw', norepo=True)
871 > def throw(ui, **opts):
871 > def throw(ui, **opts):
872 > """throws an exception"""
872 > """throws an exception"""
873 > raise Bogon()
873 > raise Bogon()
874 > EOF
874 > EOF
875
875
876 No declared supported version, extension complains:
876 No declared supported version, extension complains:
877 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
877 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
878 ** Unknown exception encountered with possibly-broken third-party extension throw
878 ** Unknown exception encountered with possibly-broken third-party extension throw
879 ** which supports versions unknown of Mercurial.
879 ** which supports versions unknown of Mercurial.
880 ** Please disable throw and try your action again.
880 ** Please disable throw and try your action again.
881 ** If that fixes the bug please report it to the extension author.
881 ** If that fixes the bug please report it to the extension author.
882 ** Python * (glob)
882 ** Python * (glob)
883 ** Mercurial Distributed SCM * (glob)
883 ** Mercurial Distributed SCM * (glob)
884 ** Extensions loaded: throw
884 ** Extensions loaded: throw
885
885
886 empty declaration of supported version, extension complains:
886 empty declaration of supported version, extension complains:
887 $ echo "testedwith = ''" >> throw.py
887 $ echo "testedwith = ''" >> throw.py
888 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
888 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
889 ** Unknown exception encountered with possibly-broken third-party extension throw
889 ** Unknown exception encountered with possibly-broken third-party extension throw
890 ** which supports versions unknown of Mercurial.
890 ** which supports versions unknown of Mercurial.
891 ** Please disable throw and try your action again.
891 ** Please disable throw and try your action again.
892 ** If that fixes the bug please report it to the extension author.
892 ** If that fixes the bug please report it to the extension author.
893 ** Python * (glob)
893 ** Python * (glob)
894 ** Mercurial Distributed SCM (*) (glob)
894 ** Mercurial Distributed SCM (*) (glob)
895 ** Extensions loaded: throw
895 ** Extensions loaded: throw
896
896
897 If the extension specifies a buglink, show that:
897 If the extension specifies a buglink, show that:
898 $ echo 'buglink = "http://example.com/bts"' >> throw.py
898 $ echo 'buglink = "http://example.com/bts"' >> throw.py
899 $ rm -f throw.pyc throw.pyo
899 $ rm -f throw.pyc throw.pyo
900 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
900 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
901 ** Unknown exception encountered with possibly-broken third-party extension throw
901 ** Unknown exception encountered with possibly-broken third-party extension throw
902 ** which supports versions unknown of Mercurial.
902 ** which supports versions unknown of Mercurial.
903 ** Please disable throw and try your action again.
903 ** Please disable throw and try your action again.
904 ** If that fixes the bug please report it to http://example.com/bts
904 ** If that fixes the bug please report it to http://example.com/bts
905 ** Python * (glob)
905 ** Python * (glob)
906 ** Mercurial Distributed SCM (*) (glob)
906 ** Mercurial Distributed SCM (*) (glob)
907 ** Extensions loaded: throw
907 ** Extensions loaded: throw
908
908
909 If the extensions declare outdated versions, accuse the older extension first:
909 If the extensions declare outdated versions, accuse the older extension first:
910 $ echo "from mercurial import util" >> older.py
910 $ echo "from mercurial import util" >> older.py
911 $ echo "util.version = lambda:'2.2'" >> older.py
911 $ echo "util.version = lambda:'2.2'" >> older.py
912 $ echo "testedwith = '1.9.3'" >> older.py
912 $ echo "testedwith = '1.9.3'" >> older.py
913 $ echo "testedwith = '2.1.1'" >> throw.py
913 $ echo "testedwith = '2.1.1'" >> throw.py
914 $ rm -f throw.pyc throw.pyo
914 $ rm -f throw.pyc throw.pyo
915 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
915 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
916 > throw 2>&1 | egrep '^\*\*'
916 > throw 2>&1 | egrep '^\*\*'
917 ** Unknown exception encountered with possibly-broken third-party extension older
917 ** Unknown exception encountered with possibly-broken third-party extension older
918 ** which supports versions 1.9 of Mercurial.
918 ** which supports versions 1.9 of Mercurial.
919 ** Please disable older and try your action again.
919 ** Please disable older and try your action again.
920 ** If that fixes the bug please report it to the extension author.
920 ** If that fixes the bug please report it to the extension author.
921 ** Python * (glob)
921 ** Python * (glob)
922 ** Mercurial Distributed SCM (version 2.2)
922 ** Mercurial Distributed SCM (version 2.2)
923 ** Extensions loaded: throw, older
923 ** Extensions loaded: throw, older
924
924
925 One extension only tested with older, one only with newer versions:
925 One extension only tested with older, one only with newer versions:
926 $ echo "util.version = lambda:'2.1'" >> older.py
926 $ echo "util.version = lambda:'2.1'" >> older.py
927 $ rm -f older.pyc older.pyo
927 $ rm -f older.pyc older.pyo
928 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
928 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
929 > throw 2>&1 | egrep '^\*\*'
929 > throw 2>&1 | egrep '^\*\*'
930 ** Unknown exception encountered with possibly-broken third-party extension older
930 ** Unknown exception encountered with possibly-broken third-party extension older
931 ** which supports versions 1.9 of Mercurial.
931 ** which supports versions 1.9 of Mercurial.
932 ** Please disable older and try your action again.
932 ** Please disable older and try your action again.
933 ** If that fixes the bug please report it to the extension author.
933 ** If that fixes the bug please report it to the extension author.
934 ** Python * (glob)
934 ** Python * (glob)
935 ** Mercurial Distributed SCM (version 2.1)
935 ** Mercurial Distributed SCM (version 2.1)
936 ** Extensions loaded: throw, older
936 ** Extensions loaded: throw, older
937
937
938 Older extension is tested with current version, the other only with newer:
938 Older extension is tested with current version, the other only with newer:
939 $ echo "util.version = lambda:'1.9.3'" >> older.py
939 $ echo "util.version = lambda:'1.9.3'" >> older.py
940 $ rm -f older.pyc older.pyo
940 $ rm -f older.pyc older.pyo
941 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
941 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
942 > throw 2>&1 | egrep '^\*\*'
942 > throw 2>&1 | egrep '^\*\*'
943 ** Unknown exception encountered with possibly-broken third-party extension throw
943 ** Unknown exception encountered with possibly-broken third-party extension throw
944 ** which supports versions 2.1 of Mercurial.
944 ** which supports versions 2.1 of Mercurial.
945 ** Please disable throw and try your action again.
945 ** Please disable throw and try your action again.
946 ** If that fixes the bug please report it to http://example.com/bts
946 ** If that fixes the bug please report it to http://example.com/bts
947 ** Python * (glob)
947 ** Python * (glob)
948 ** Mercurial Distributed SCM (version 1.9.3)
948 ** Mercurial Distributed SCM (version 1.9.3)
949 ** Extensions loaded: throw, older
949 ** Extensions loaded: throw, older
950
950
951 Ability to point to a different point
951 Ability to point to a different point
952 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
952 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
953 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
953 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
954 ** unknown exception encountered, please report by visiting
954 ** unknown exception encountered, please report by visiting
955 ** Your Local Goat Lenders
955 ** Your Local Goat Lenders
956 ** Python * (glob)
956 ** Python * (glob)
957 ** Mercurial Distributed SCM (*) (glob)
957 ** Mercurial Distributed SCM (*) (glob)
958 ** Extensions loaded: throw, older
958 ** Extensions loaded: throw, older
959
959
960 Declare the version as supporting this hg version, show regular bts link:
960 Declare the version as supporting this hg version, show regular bts link:
961 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
961 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
962 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
962 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
963 $ if [ -z "$hgver" ]; then
963 $ if [ -z "$hgver" ]; then
964 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
964 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
965 > fi
965 > fi
966 $ rm -f throw.pyc throw.pyo
966 $ rm -f throw.pyc throw.pyo
967 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
967 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
968 ** unknown exception encountered, please report by visiting
968 ** unknown exception encountered, please report by visiting
969 ** https://mercurial-scm.org/wiki/BugTracker
969 ** https://mercurial-scm.org/wiki/BugTracker
970 ** Python * (glob)
970 ** Python * (glob)
971 ** Mercurial Distributed SCM (*) (glob)
971 ** Mercurial Distributed SCM (*) (glob)
972 ** Extensions loaded: throw
972 ** Extensions loaded: throw
973
973
974 Patch version is ignored during compatibility check
974 Patch version is ignored during compatibility check
975 $ echo "testedwith = '3.2'" >> throw.py
975 $ echo "testedwith = '3.2'" >> throw.py
976 $ echo "util.version = lambda:'3.2.2'" >> throw.py
976 $ echo "util.version = lambda:'3.2.2'" >> throw.py
977 $ rm -f throw.pyc throw.pyo
977 $ rm -f throw.pyc throw.pyo
978 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
978 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
979 ** unknown exception encountered, please report by visiting
979 ** unknown exception encountered, please report by visiting
980 ** https://mercurial-scm.org/wiki/BugTracker
980 ** https://mercurial-scm.org/wiki/BugTracker
981 ** Python * (glob)
981 ** Python * (glob)
982 ** Mercurial Distributed SCM (*) (glob)
982 ** Mercurial Distributed SCM (*) (glob)
983 ** Extensions loaded: throw
983 ** Extensions loaded: throw
984
984
985 Test version number support in 'hg version':
985 Test version number support in 'hg version':
986 $ echo '__version__ = (1, 2, 3)' >> throw.py
986 $ echo '__version__ = (1, 2, 3)' >> throw.py
987 $ rm -f throw.pyc throw.pyo
987 $ rm -f throw.pyc throw.pyo
988 $ hg version -v
988 $ hg version -v
989 Mercurial Distributed SCM (version *) (glob)
989 Mercurial Distributed SCM (version *) (glob)
990 (see https://mercurial-scm.org for more information)
990 (see https://mercurial-scm.org for more information)
991
991
992 Copyright (C) 2005-* Matt Mackall and others (glob)
992 Copyright (C) 2005-* Matt Mackall and others (glob)
993 This is free software; see the source for copying conditions. There is NO
993 This is free software; see the source for copying conditions. There is NO
994 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
994 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
995
995
996 Enabled extensions:
996 Enabled extensions:
997
997
998
998
999 $ hg version -v --config extensions.throw=throw.py
999 $ hg version -v --config extensions.throw=throw.py
1000 Mercurial Distributed SCM (version *) (glob)
1000 Mercurial Distributed SCM (version *) (glob)
1001 (see https://mercurial-scm.org for more information)
1001 (see https://mercurial-scm.org for more information)
1002
1002
1003 Copyright (C) 2005-* Matt Mackall and others (glob)
1003 Copyright (C) 2005-* Matt Mackall and others (glob)
1004 This is free software; see the source for copying conditions. There is NO
1004 This is free software; see the source for copying conditions. There is NO
1005 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1005 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1006
1006
1007 Enabled extensions:
1007 Enabled extensions:
1008
1008
1009 throw external 1.2.3
1009 throw external 1.2.3
1010 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1010 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1011 $ rm -f throw.pyc throw.pyo
1011 $ rm -f throw.pyc throw.pyo
1012 $ hg version -v --config extensions.throw=throw.py
1012 $ hg version -v --config extensions.throw=throw.py
1013 Mercurial Distributed SCM (version *) (glob)
1013 Mercurial Distributed SCM (version *) (glob)
1014 (see https://mercurial-scm.org for more information)
1014 (see https://mercurial-scm.org for more information)
1015
1015
1016 Copyright (C) 2005-* Matt Mackall and others (glob)
1016 Copyright (C) 2005-* Matt Mackall and others (glob)
1017 This is free software; see the source for copying conditions. There is NO
1017 This is free software; see the source for copying conditions. There is NO
1018 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1018 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1019
1019
1020 Enabled extensions:
1020 Enabled extensions:
1021
1021
1022 throw external 1.twentythree
1022 throw external 1.twentythree
1023
1023
1024 Refuse to load extensions with minimum version requirements
1024 Refuse to load extensions with minimum version requirements
1025
1025
1026 $ cat > minversion1.py << EOF
1026 $ cat > minversion1.py << EOF
1027 > from mercurial import util
1027 > from mercurial import util
1028 > util.version = lambda: '3.5.2'
1028 > util.version = lambda: '3.5.2'
1029 > minimumhgversion = '3.6'
1029 > minimumhgversion = '3.6'
1030 > EOF
1030 > EOF
1031 $ hg --config extensions.minversion=minversion1.py version
1031 $ hg --config extensions.minversion=minversion1.py version
1032 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1032 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1033 Mercurial Distributed SCM (version 3.5.2)
1033 Mercurial Distributed SCM (version 3.5.2)
1034 (see https://mercurial-scm.org for more information)
1034 (see https://mercurial-scm.org for more information)
1035
1035
1036 Copyright (C) 2005-* Matt Mackall and others (glob)
1036 Copyright (C) 2005-* Matt Mackall and others (glob)
1037 This is free software; see the source for copying conditions. There is NO
1037 This is free software; see the source for copying conditions. There is NO
1038 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1038 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1039
1039
1040 $ cat > minversion2.py << EOF
1040 $ cat > minversion2.py << EOF
1041 > from mercurial import util
1041 > from mercurial import util
1042 > util.version = lambda: '3.6'
1042 > util.version = lambda: '3.6'
1043 > minimumhgversion = '3.7'
1043 > minimumhgversion = '3.7'
1044 > EOF
1044 > EOF
1045 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1045 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1046 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1046 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1047
1047
1048 Can load version that is only off by point release
1048 Can load version that is only off by point release
1049
1049
1050 $ cat > minversion2.py << EOF
1050 $ cat > minversion2.py << EOF
1051 > from mercurial import util
1051 > from mercurial import util
1052 > util.version = lambda: '3.6.1'
1052 > util.version = lambda: '3.6.1'
1053 > minimumhgversion = '3.6'
1053 > minimumhgversion = '3.6'
1054 > EOF
1054 > EOF
1055 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1055 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1056 [1]
1056 [1]
1057
1057
1058 Can load minimum version identical to current
1058 Can load minimum version identical to current
1059
1059
1060 $ cat > minversion3.py << EOF
1060 $ cat > minversion3.py << EOF
1061 > from mercurial import util
1061 > from mercurial import util
1062 > util.version = lambda: '3.5'
1062 > util.version = lambda: '3.5'
1063 > minimumhgversion = '3.5'
1063 > minimumhgversion = '3.5'
1064 > EOF
1064 > EOF
1065 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1065 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1066 [1]
1066 [1]
1067
1067
1068 Restore HGRCPATH
1068 Restore HGRCPATH
1069
1069
1070 $ HGRCPATH=$ORGHGRCPATH
1070 $ HGRCPATH=$ORGHGRCPATH
1071 $ export HGRCPATH
1071 $ export HGRCPATH
1072
1072
1073 Commands handling multiple repositories at a time should invoke only
1073 Commands handling multiple repositories at a time should invoke only
1074 "reposetup()" of extensions enabling in the target repository.
1074 "reposetup()" of extensions enabling in the target repository.
1075
1075
1076 $ mkdir reposetup-test
1076 $ mkdir reposetup-test
1077 $ cd reposetup-test
1077 $ cd reposetup-test
1078
1078
1079 $ cat > $TESTTMP/reposetuptest.py <<EOF
1079 $ cat > $TESTTMP/reposetuptest.py <<EOF
1080 > from mercurial import extensions
1080 > from mercurial import extensions
1081 > def reposetup(ui, repo):
1081 > def reposetup(ui, repo):
1082 > ui.write('reposetup() for %s\n' % (repo.root))
1082 > ui.write('reposetup() for %s\n' % (repo.root))
1083 > ui.flush()
1083 > ui.flush()
1084 > EOF
1084 > EOF
1085 $ hg init src
1085 $ hg init src
1086 $ echo a > src/a
1086 $ echo a > src/a
1087 $ hg -R src commit -Am '#0 at src/a'
1087 $ hg -R src commit -Am '#0 at src/a'
1088 adding a
1088 adding a
1089 $ echo '[extensions]' >> src/.hg/hgrc
1089 $ echo '[extensions]' >> src/.hg/hgrc
1090 $ echo '# enable extension locally' >> src/.hg/hgrc
1090 $ echo '# enable extension locally' >> src/.hg/hgrc
1091 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1091 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1092 $ hg -R src status
1092 $ hg -R src status
1093 reposetup() for $TESTTMP/reposetup-test/src (glob)
1093 reposetup() for $TESTTMP/reposetup-test/src (glob)
1094
1094
1095 $ hg clone -U src clone-dst1
1095 $ hg clone -U src clone-dst1
1096 reposetup() for $TESTTMP/reposetup-test/src (glob)
1096 reposetup() for $TESTTMP/reposetup-test/src (glob)
1097 $ hg init push-dst1
1097 $ hg init push-dst1
1098 $ hg -q -R src push push-dst1
1098 $ hg -q -R src push push-dst1
1099 reposetup() for $TESTTMP/reposetup-test/src (glob)
1099 reposetup() for $TESTTMP/reposetup-test/src (glob)
1100 $ hg init pull-src1
1100 $ hg init pull-src1
1101 $ hg -q -R pull-src1 pull src
1101 $ hg -q -R pull-src1 pull src
1102 reposetup() for $TESTTMP/reposetup-test/src (glob)
1102 reposetup() for $TESTTMP/reposetup-test/src (glob)
1103
1103
1104 $ cat <<EOF >> $HGRCPATH
1104 $ cat <<EOF >> $HGRCPATH
1105 > [extensions]
1105 > [extensions]
1106 > # disable extension globally and explicitly
1106 > # disable extension globally and explicitly
1107 > reposetuptest = !
1107 > reposetuptest = !
1108 > EOF
1108 > EOF
1109 $ hg clone -U src clone-dst2
1109 $ hg clone -U src clone-dst2
1110 reposetup() for $TESTTMP/reposetup-test/src (glob)
1110 reposetup() for $TESTTMP/reposetup-test/src (glob)
1111 $ hg init push-dst2
1111 $ hg init push-dst2
1112 $ hg -q -R src push push-dst2
1112 $ hg -q -R src push push-dst2
1113 reposetup() for $TESTTMP/reposetup-test/src (glob)
1113 reposetup() for $TESTTMP/reposetup-test/src (glob)
1114 $ hg init pull-src2
1114 $ hg init pull-src2
1115 $ hg -q -R pull-src2 pull src
1115 $ hg -q -R pull-src2 pull src
1116 reposetup() for $TESTTMP/reposetup-test/src (glob)
1116 reposetup() for $TESTTMP/reposetup-test/src (glob)
1117
1117
1118 $ cat <<EOF >> $HGRCPATH
1118 $ cat <<EOF >> $HGRCPATH
1119 > [extensions]
1119 > [extensions]
1120 > # enable extension globally
1120 > # enable extension globally
1121 > reposetuptest = $TESTTMP/reposetuptest.py
1121 > reposetuptest = $TESTTMP/reposetuptest.py
1122 > EOF
1122 > EOF
1123 $ hg clone -U src clone-dst3
1123 $ hg clone -U src clone-dst3
1124 reposetup() for $TESTTMP/reposetup-test/src (glob)
1124 reposetup() for $TESTTMP/reposetup-test/src (glob)
1125 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1125 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1126 $ hg init push-dst3
1126 $ hg init push-dst3
1127 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1127 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1128 $ hg -q -R src push push-dst3
1128 $ hg -q -R src push push-dst3
1129 reposetup() for $TESTTMP/reposetup-test/src (glob)
1129 reposetup() for $TESTTMP/reposetup-test/src (glob)
1130 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1130 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1131 $ hg init pull-src3
1131 $ hg init pull-src3
1132 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1132 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1133 $ hg -q -R pull-src3 pull src
1133 $ hg -q -R pull-src3 pull src
1134 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1134 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1135 reposetup() for $TESTTMP/reposetup-test/src (glob)
1135 reposetup() for $TESTTMP/reposetup-test/src (glob)
1136
1136
1137 $ echo '[extensions]' >> src/.hg/hgrc
1137 $ echo '[extensions]' >> src/.hg/hgrc
1138 $ echo '# disable extension locally' >> src/.hg/hgrc
1138 $ echo '# disable extension locally' >> src/.hg/hgrc
1139 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1139 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1140 $ hg clone -U src clone-dst4
1140 $ hg clone -U src clone-dst4
1141 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1141 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1142 $ hg init push-dst4
1142 $ hg init push-dst4
1143 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1143 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1144 $ hg -q -R src push push-dst4
1144 $ hg -q -R src push push-dst4
1145 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1145 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1146 $ hg init pull-src4
1146 $ hg init pull-src4
1147 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1147 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1148 $ hg -q -R pull-src4 pull src
1148 $ hg -q -R pull-src4 pull src
1149 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1149 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1150
1150
1151 disabling in command line overlays with all configuration
1151 disabling in command line overlays with all configuration
1152 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1152 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1153 $ hg --config extensions.reposetuptest=! init push-dst5
1153 $ hg --config extensions.reposetuptest=! init push-dst5
1154 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1154 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1155 $ hg --config extensions.reposetuptest=! init pull-src5
1155 $ hg --config extensions.reposetuptest=! init pull-src5
1156 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1156 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1157
1157
1158 $ cat <<EOF >> $HGRCPATH
1158 $ cat <<EOF >> $HGRCPATH
1159 > [extensions]
1159 > [extensions]
1160 > # disable extension globally and explicitly
1160 > # disable extension globally and explicitly
1161 > reposetuptest = !
1161 > reposetuptest = !
1162 > EOF
1162 > EOF
1163 $ hg init parent
1163 $ hg init parent
1164 $ hg init parent/sub1
1164 $ hg init parent/sub1
1165 $ echo 1 > parent/sub1/1
1165 $ echo 1 > parent/sub1/1
1166 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1166 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1167 adding 1
1167 adding 1
1168 $ hg init parent/sub2
1168 $ hg init parent/sub2
1169 $ hg init parent/sub2/sub21
1169 $ hg init parent/sub2/sub21
1170 $ echo 21 > parent/sub2/sub21/21
1170 $ echo 21 > parent/sub2/sub21/21
1171 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1171 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1172 adding 21
1172 adding 21
1173 $ cat > parent/sub2/.hgsub <<EOF
1173 $ cat > parent/sub2/.hgsub <<EOF
1174 > sub21 = sub21
1174 > sub21 = sub21
1175 > EOF
1175 > EOF
1176 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1176 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1177 adding .hgsub
1177 adding .hgsub
1178 $ hg init parent/sub3
1178 $ hg init parent/sub3
1179 $ echo 3 > parent/sub3/3
1179 $ echo 3 > parent/sub3/3
1180 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1180 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1181 adding 3
1181 adding 3
1182 $ cat > parent/.hgsub <<EOF
1182 $ cat > parent/.hgsub <<EOF
1183 > sub1 = sub1
1183 > sub1 = sub1
1184 > sub2 = sub2
1184 > sub2 = sub2
1185 > sub3 = sub3
1185 > sub3 = sub3
1186 > EOF
1186 > EOF
1187 $ hg -R parent commit -Am '#0 at parent'
1187 $ hg -R parent commit -Am '#0 at parent'
1188 adding .hgsub
1188 adding .hgsub
1189 $ echo '[extensions]' >> parent/.hg/hgrc
1189 $ echo '[extensions]' >> parent/.hg/hgrc
1190 $ echo '# enable extension locally' >> parent/.hg/hgrc
1190 $ echo '# enable extension locally' >> parent/.hg/hgrc
1191 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1191 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1192 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1192 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1193 $ hg -R parent status -S -A
1193 $ hg -R parent status -S -A
1194 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1194 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1195 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1195 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1196 C .hgsub
1196 C .hgsub
1197 C .hgsubstate
1197 C .hgsubstate
1198 C sub1/1
1198 C sub1/1
1199 C sub2/.hgsub
1199 C sub2/.hgsub
1200 C sub2/.hgsubstate
1200 C sub2/.hgsubstate
1201 C sub2/sub21/21
1201 C sub2/sub21/21
1202 C sub3/3
1202 C sub3/3
1203
1203
1204 $ cd ..
1204 $ cd ..
1205
1205
1206 Test compatibility with extension commands that don't use @command (issue5137)
1207
1208 $ hg init deprecated
1209 $ cd deprecated
1210
1211 $ cat <<EOF > deprecatedcmd.py
1212 > def deprecatedcmd(repo, ui):
1213 > pass
1214 > cmdtable = {
1215 > 'deprecatedcmd': (deprecatedcmd, [], ''),
1216 > }
1217 > EOF
1218 $ cat <<EOF > .hg/hgrc
1219 > [extensions]
1220 > deprecatedcmd = `pwd`/deprecatedcmd.py
1221 > mq = !
1222 > hgext.mq = !
1223 > hgext/mq = !
1224 > [alias]
1225 > deprecatedalias = deprecatedcmd
1226 > EOF
1227
1228 $ hg deprecatedcmd
1229 devel-warn: missing attribute 'norepo', use @command decorator to register 'deprecatedcmd'
1230 (compatibility will be dropped after Mercurial-3.8, update your code.) at: * (glob)
1231
1232 $ hg deprecatedalias
1233 devel-warn: missing attribute 'norepo', use @command decorator to register 'deprecatedalias'
1234 (compatibility will be dropped after Mercurial-3.8, update your code.) at: * (glob)
1235
1236 no warning unless command is executed:
1237
1238 $ hg paths
1239
1240 but mq iterates over command table:
1241
1242 $ hg --config extensions.mq= paths
1243 devel-warn: missing attribute 'norepo', use @command decorator to register 'deprecatedcmd'
1244 (compatibility will be dropped after Mercurial-3.8, update your code.) at: * (glob)
1245
1246 $ cd ..
1247
1206 Test synopsis and docstring extending
1248 Test synopsis and docstring extending
1207
1249
1208 $ hg init exthelp
1250 $ hg init exthelp
1209 $ cat > exthelp.py <<EOF
1251 $ cat > exthelp.py <<EOF
1210 > from mercurial import commands, extensions
1252 > from mercurial import commands, extensions
1211 > def exbookmarks(orig, *args, **opts):
1253 > def exbookmarks(orig, *args, **opts):
1212 > return orig(*args, **opts)
1254 > return orig(*args, **opts)
1213 > def uisetup(ui):
1255 > def uisetup(ui):
1214 > synopsis = ' GREPME [--foo] [-x]'
1256 > synopsis = ' GREPME [--foo] [-x]'
1215 > docstring = '''
1257 > docstring = '''
1216 > GREPME make sure that this is in the help!
1258 > GREPME make sure that this is in the help!
1217 > '''
1259 > '''
1218 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1260 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1219 > synopsis, docstring)
1261 > synopsis, docstring)
1220 > EOF
1262 > EOF
1221 $ abspath=`pwd`/exthelp.py
1263 $ abspath=`pwd`/exthelp.py
1222 $ echo '[extensions]' >> $HGRCPATH
1264 $ echo '[extensions]' >> $HGRCPATH
1223 $ echo "exthelp = $abspath" >> $HGRCPATH
1265 $ echo "exthelp = $abspath" >> $HGRCPATH
1224 $ cd exthelp
1266 $ cd exthelp
1225 $ hg help bookmarks | grep GREPME
1267 $ hg help bookmarks | grep GREPME
1226 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1268 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1227 GREPME make sure that this is in the help!
1269 GREPME make sure that this is in the help!
1228
1270
General Comments 0
You need to be logged in to leave comments. Login now