##// END OF EJS Templates
extensions: peek command table of disabled extensions without importing...
Yuya Nishihara -
r38180:bdf344ae default
parent child Browse files
Show More
@@ -1,735 +1,770 b''
1 # extensions.py - extension handling for mercurial
1 # extensions.py - extension handling 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
8 from __future__ import absolute_import
9
9
10 import ast
11 import collections
10 import functools
12 import functools
11 import imp
13 import imp
12 import inspect
14 import inspect
13 import os
15 import os
14
16
15 from .i18n import (
17 from .i18n import (
16 _,
18 _,
17 gettext,
19 gettext,
18 )
20 )
19
21
20 from . import (
22 from . import (
21 cmdutil,
23 cmdutil,
22 configitems,
24 configitems,
23 error,
25 error,
24 pycompat,
26 pycompat,
25 util,
27 util,
26 )
28 )
27
29
28 from .utils import (
30 from .utils import (
29 stringutil,
31 stringutil,
30 )
32 )
31
33
32 _extensions = {}
34 _extensions = {}
33 _disabledextensions = {}
35 _disabledextensions = {}
34 _aftercallbacks = {}
36 _aftercallbacks = {}
35 _order = []
37 _order = []
36 _builtin = {
38 _builtin = {
37 'hbisect',
39 'hbisect',
38 'bookmarks',
40 'bookmarks',
39 'color',
41 'color',
40 'parentrevspec',
42 'parentrevspec',
41 'progress',
43 'progress',
42 'interhg',
44 'interhg',
43 'inotify',
45 'inotify',
44 'hgcia'
46 'hgcia'
45 }
47 }
46
48
47 def extensions(ui=None):
49 def extensions(ui=None):
48 if ui:
50 if ui:
49 def enabled(name):
51 def enabled(name):
50 for format in ['%s', 'hgext.%s']:
52 for format in ['%s', 'hgext.%s']:
51 conf = ui.config('extensions', format % name)
53 conf = ui.config('extensions', format % name)
52 if conf is not None and not conf.startswith('!'):
54 if conf is not None and not conf.startswith('!'):
53 return True
55 return True
54 else:
56 else:
55 enabled = lambda name: True
57 enabled = lambda name: True
56 for name in _order:
58 for name in _order:
57 module = _extensions[name]
59 module = _extensions[name]
58 if module and enabled(name):
60 if module and enabled(name):
59 yield name, module
61 yield name, module
60
62
61 def find(name):
63 def find(name):
62 '''return module with given extension name'''
64 '''return module with given extension name'''
63 mod = None
65 mod = None
64 try:
66 try:
65 mod = _extensions[name]
67 mod = _extensions[name]
66 except KeyError:
68 except KeyError:
67 for k, v in _extensions.iteritems():
69 for k, v in _extensions.iteritems():
68 if k.endswith('.' + name) or k.endswith('/' + name):
70 if k.endswith('.' + name) or k.endswith('/' + name):
69 mod = v
71 mod = v
70 break
72 break
71 if not mod:
73 if not mod:
72 raise KeyError(name)
74 raise KeyError(name)
73 return mod
75 return mod
74
76
75 def loadpath(path, module_name):
77 def loadpath(path, module_name):
76 module_name = module_name.replace('.', '_')
78 module_name = module_name.replace('.', '_')
77 path = util.normpath(util.expandpath(path))
79 path = util.normpath(util.expandpath(path))
78 module_name = pycompat.fsdecode(module_name)
80 module_name = pycompat.fsdecode(module_name)
79 path = pycompat.fsdecode(path)
81 path = pycompat.fsdecode(path)
80 if os.path.isdir(path):
82 if os.path.isdir(path):
81 # module/__init__.py style
83 # module/__init__.py style
82 d, f = os.path.split(path)
84 d, f = os.path.split(path)
83 fd, fpath, desc = imp.find_module(f, [d])
85 fd, fpath, desc = imp.find_module(f, [d])
84 return imp.load_module(module_name, fd, fpath, desc)
86 return imp.load_module(module_name, fd, fpath, desc)
85 else:
87 else:
86 try:
88 try:
87 return imp.load_source(module_name, path)
89 return imp.load_source(module_name, path)
88 except IOError as exc:
90 except IOError as exc:
89 if not exc.filename:
91 if not exc.filename:
90 exc.filename = path # python does not fill this
92 exc.filename = path # python does not fill this
91 raise
93 raise
92
94
93 def _importh(name):
95 def _importh(name):
94 """import and return the <name> module"""
96 """import and return the <name> module"""
95 mod = __import__(pycompat.sysstr(name))
97 mod = __import__(pycompat.sysstr(name))
96 components = name.split('.')
98 components = name.split('.')
97 for comp in components[1:]:
99 for comp in components[1:]:
98 mod = getattr(mod, comp)
100 mod = getattr(mod, comp)
99 return mod
101 return mod
100
102
101 def _importext(name, path=None, reportfunc=None):
103 def _importext(name, path=None, reportfunc=None):
102 if path:
104 if path:
103 # the module will be loaded in sys.modules
105 # the module will be loaded in sys.modules
104 # choose an unique name so that it doesn't
106 # choose an unique name so that it doesn't
105 # conflicts with other modules
107 # conflicts with other modules
106 mod = loadpath(path, 'hgext.%s' % name)
108 mod = loadpath(path, 'hgext.%s' % name)
107 else:
109 else:
108 try:
110 try:
109 mod = _importh("hgext.%s" % name)
111 mod = _importh("hgext.%s" % name)
110 except ImportError as err:
112 except ImportError as err:
111 if reportfunc:
113 if reportfunc:
112 reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name)
114 reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name)
113 try:
115 try:
114 mod = _importh("hgext3rd.%s" % name)
116 mod = _importh("hgext3rd.%s" % name)
115 except ImportError as err:
117 except ImportError as err:
116 if reportfunc:
118 if reportfunc:
117 reportfunc(err, "hgext3rd.%s" % name, name)
119 reportfunc(err, "hgext3rd.%s" % name, name)
118 mod = _importh(name)
120 mod = _importh(name)
119 return mod
121 return mod
120
122
121 def _reportimporterror(ui, err, failed, next):
123 def _reportimporterror(ui, err, failed, next):
122 # note: this ui.debug happens before --debug is processed,
124 # note: this ui.debug happens before --debug is processed,
123 # Use --config ui.debug=1 to see them.
125 # Use --config ui.debug=1 to see them.
124 ui.debug('could not import %s (%s): trying %s\n'
126 ui.debug('could not import %s (%s): trying %s\n'
125 % (failed, stringutil.forcebytestr(err), next))
127 % (failed, stringutil.forcebytestr(err), next))
126 if ui.debugflag:
128 if ui.debugflag:
127 ui.traceback()
129 ui.traceback()
128
130
129 def _rejectunicode(name, xs):
131 def _rejectunicode(name, xs):
130 if isinstance(xs, (list, set, tuple)):
132 if isinstance(xs, (list, set, tuple)):
131 for x in xs:
133 for x in xs:
132 _rejectunicode(name, x)
134 _rejectunicode(name, x)
133 elif isinstance(xs, dict):
135 elif isinstance(xs, dict):
134 for k, v in xs.items():
136 for k, v in xs.items():
135 _rejectunicode(name, k)
137 _rejectunicode(name, k)
136 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
138 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
137 elif isinstance(xs, type(u'')):
139 elif isinstance(xs, type(u'')):
138 raise error.ProgrammingError(b"unicode %r found in %s" % (xs, name),
140 raise error.ProgrammingError(b"unicode %r found in %s" % (xs, name),
139 hint="use b'' to make it byte string")
141 hint="use b'' to make it byte string")
140
142
141 # attributes set by registrar.command
143 # attributes set by registrar.command
142 _cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo')
144 _cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo')
143
145
144 def _validatecmdtable(ui, cmdtable):
146 def _validatecmdtable(ui, cmdtable):
145 """Check if extension commands have required attributes"""
147 """Check if extension commands have required attributes"""
146 for c, e in cmdtable.iteritems():
148 for c, e in cmdtable.iteritems():
147 f = e[0]
149 f = e[0]
148 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
150 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
149 if not missing:
151 if not missing:
150 continue
152 continue
151 raise error.ProgrammingError(
153 raise error.ProgrammingError(
152 'missing attributes: %s' % ', '.join(missing),
154 'missing attributes: %s' % ', '.join(missing),
153 hint="use @command decorator to register '%s'" % c)
155 hint="use @command decorator to register '%s'" % c)
154
156
155 def _validatetables(ui, mod):
157 def _validatetables(ui, mod):
156 """Sanity check for loadable tables provided by extension module"""
158 """Sanity check for loadable tables provided by extension module"""
157 for t in ['cmdtable', 'colortable', 'configtable']:
159 for t in ['cmdtable', 'colortable', 'configtable']:
158 _rejectunicode(t, getattr(mod, t, {}))
160 _rejectunicode(t, getattr(mod, t, {}))
159 for t in ['filesetpredicate', 'internalmerge', 'revsetpredicate',
161 for t in ['filesetpredicate', 'internalmerge', 'revsetpredicate',
160 'templatefilter', 'templatefunc', 'templatekeyword']:
162 'templatefilter', 'templatefunc', 'templatekeyword']:
161 o = getattr(mod, t, None)
163 o = getattr(mod, t, None)
162 if o:
164 if o:
163 _rejectunicode(t, o._table)
165 _rejectunicode(t, o._table)
164 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
166 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
165
167
166 def load(ui, name, path):
168 def load(ui, name, path):
167 if name.startswith('hgext.') or name.startswith('hgext/'):
169 if name.startswith('hgext.') or name.startswith('hgext/'):
168 shortname = name[6:]
170 shortname = name[6:]
169 else:
171 else:
170 shortname = name
172 shortname = name
171 if shortname in _builtin:
173 if shortname in _builtin:
172 return None
174 return None
173 if shortname in _extensions:
175 if shortname in _extensions:
174 return _extensions[shortname]
176 return _extensions[shortname]
175 _extensions[shortname] = None
177 _extensions[shortname] = None
176 mod = _importext(name, path, bind(_reportimporterror, ui))
178 mod = _importext(name, path, bind(_reportimporterror, ui))
177
179
178 # Before we do anything with the extension, check against minimum stated
180 # Before we do anything with the extension, check against minimum stated
179 # compatibility. This gives extension authors a mechanism to have their
181 # compatibility. This gives extension authors a mechanism to have their
180 # extensions short circuit when loaded with a known incompatible version
182 # extensions short circuit when loaded with a known incompatible version
181 # of Mercurial.
183 # of Mercurial.
182 minver = getattr(mod, 'minimumhgversion', None)
184 minver = getattr(mod, 'minimumhgversion', None)
183 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
185 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
184 ui.warn(_('(third party extension %s requires version %s or newer '
186 ui.warn(_('(third party extension %s requires version %s or newer '
185 'of Mercurial; disabling)\n') % (shortname, minver))
187 'of Mercurial; disabling)\n') % (shortname, minver))
186 return
188 return
187 _validatetables(ui, mod)
189 _validatetables(ui, mod)
188
190
189 _extensions[shortname] = mod
191 _extensions[shortname] = mod
190 _order.append(shortname)
192 _order.append(shortname)
191 for fn in _aftercallbacks.get(shortname, []):
193 for fn in _aftercallbacks.get(shortname, []):
192 fn(loaded=True)
194 fn(loaded=True)
193 return mod
195 return mod
194
196
195 def _runuisetup(name, ui):
197 def _runuisetup(name, ui):
196 uisetup = getattr(_extensions[name], 'uisetup', None)
198 uisetup = getattr(_extensions[name], 'uisetup', None)
197 if uisetup:
199 if uisetup:
198 try:
200 try:
199 uisetup(ui)
201 uisetup(ui)
200 except Exception as inst:
202 except Exception as inst:
201 ui.traceback(force=True)
203 ui.traceback(force=True)
202 msg = stringutil.forcebytestr(inst)
204 msg = stringutil.forcebytestr(inst)
203 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
205 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
204 return False
206 return False
205 return True
207 return True
206
208
207 def _runextsetup(name, ui):
209 def _runextsetup(name, ui):
208 extsetup = getattr(_extensions[name], 'extsetup', None)
210 extsetup = getattr(_extensions[name], 'extsetup', None)
209 if extsetup:
211 if extsetup:
210 try:
212 try:
211 try:
213 try:
212 extsetup(ui)
214 extsetup(ui)
213 except TypeError:
215 except TypeError:
214 if pycompat.getargspec(extsetup).args:
216 if pycompat.getargspec(extsetup).args:
215 raise
217 raise
216 extsetup() # old extsetup with no ui argument
218 extsetup() # old extsetup with no ui argument
217 except Exception as inst:
219 except Exception as inst:
218 ui.traceback(force=True)
220 ui.traceback(force=True)
219 msg = stringutil.forcebytestr(inst)
221 msg = stringutil.forcebytestr(inst)
220 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
222 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
221 return False
223 return False
222 return True
224 return True
223
225
224 def loadall(ui, whitelist=None):
226 def loadall(ui, whitelist=None):
225 result = ui.configitems("extensions")
227 result = ui.configitems("extensions")
226 if whitelist is not None:
228 if whitelist is not None:
227 result = [(k, v) for (k, v) in result if k in whitelist]
229 result = [(k, v) for (k, v) in result if k in whitelist]
228 newindex = len(_order)
230 newindex = len(_order)
229 for (name, path) in result:
231 for (name, path) in result:
230 if path:
232 if path:
231 if path[0:1] == '!':
233 if path[0:1] == '!':
232 _disabledextensions[name] = path[1:]
234 _disabledextensions[name] = path[1:]
233 continue
235 continue
234 try:
236 try:
235 load(ui, name, path)
237 load(ui, name, path)
236 except Exception as inst:
238 except Exception as inst:
237 msg = stringutil.forcebytestr(inst)
239 msg = stringutil.forcebytestr(inst)
238 if path:
240 if path:
239 ui.warn(_("*** failed to import extension %s from %s: %s\n")
241 ui.warn(_("*** failed to import extension %s from %s: %s\n")
240 % (name, path, msg))
242 % (name, path, msg))
241 else:
243 else:
242 ui.warn(_("*** failed to import extension %s: %s\n")
244 ui.warn(_("*** failed to import extension %s: %s\n")
243 % (name, msg))
245 % (name, msg))
244 if isinstance(inst, error.Hint) and inst.hint:
246 if isinstance(inst, error.Hint) and inst.hint:
245 ui.warn(_("*** (%s)\n") % inst.hint)
247 ui.warn(_("*** (%s)\n") % inst.hint)
246 ui.traceback()
248 ui.traceback()
247 # list of (objname, loadermod, loadername) tuple:
249 # list of (objname, loadermod, loadername) tuple:
248 # - objname is the name of an object in extension module,
250 # - objname is the name of an object in extension module,
249 # from which extra information is loaded
251 # from which extra information is loaded
250 # - loadermod is the module where loader is placed
252 # - loadermod is the module where loader is placed
251 # - loadername is the name of the function,
253 # - loadername is the name of the function,
252 # which takes (ui, extensionname, extraobj) arguments
254 # which takes (ui, extensionname, extraobj) arguments
253 #
255 #
254 # This one is for the list of item that must be run before running any setup
256 # This one is for the list of item that must be run before running any setup
255 earlyextraloaders = [
257 earlyextraloaders = [
256 ('configtable', configitems, 'loadconfigtable'),
258 ('configtable', configitems, 'loadconfigtable'),
257 ]
259 ]
258 _loadextra(ui, newindex, earlyextraloaders)
260 _loadextra(ui, newindex, earlyextraloaders)
259
261
260 broken = set()
262 broken = set()
261 for name in _order[newindex:]:
263 for name in _order[newindex:]:
262 if not _runuisetup(name, ui):
264 if not _runuisetup(name, ui):
263 broken.add(name)
265 broken.add(name)
264
266
265 for name in _order[newindex:]:
267 for name in _order[newindex:]:
266 if name in broken:
268 if name in broken:
267 continue
269 continue
268 if not _runextsetup(name, ui):
270 if not _runextsetup(name, ui):
269 broken.add(name)
271 broken.add(name)
270
272
271 for name in broken:
273 for name in broken:
272 _extensions[name] = None
274 _extensions[name] = None
273
275
274 # Call aftercallbacks that were never met.
276 # Call aftercallbacks that were never met.
275 for shortname in _aftercallbacks:
277 for shortname in _aftercallbacks:
276 if shortname in _extensions:
278 if shortname in _extensions:
277 continue
279 continue
278
280
279 for fn in _aftercallbacks[shortname]:
281 for fn in _aftercallbacks[shortname]:
280 fn(loaded=False)
282 fn(loaded=False)
281
283
282 # loadall() is called multiple times and lingering _aftercallbacks
284 # loadall() is called multiple times and lingering _aftercallbacks
283 # entries could result in double execution. See issue4646.
285 # entries could result in double execution. See issue4646.
284 _aftercallbacks.clear()
286 _aftercallbacks.clear()
285
287
286 # delay importing avoids cyclic dependency (especially commands)
288 # delay importing avoids cyclic dependency (especially commands)
287 from . import (
289 from . import (
288 color,
290 color,
289 commands,
291 commands,
290 filemerge,
292 filemerge,
291 fileset,
293 fileset,
292 revset,
294 revset,
293 templatefilters,
295 templatefilters,
294 templatefuncs,
296 templatefuncs,
295 templatekw,
297 templatekw,
296 )
298 )
297
299
298 # list of (objname, loadermod, loadername) tuple:
300 # list of (objname, loadermod, loadername) tuple:
299 # - objname is the name of an object in extension module,
301 # - objname is the name of an object in extension module,
300 # from which extra information is loaded
302 # from which extra information is loaded
301 # - loadermod is the module where loader is placed
303 # - loadermod is the module where loader is placed
302 # - loadername is the name of the function,
304 # - loadername is the name of the function,
303 # which takes (ui, extensionname, extraobj) arguments
305 # which takes (ui, extensionname, extraobj) arguments
304 extraloaders = [
306 extraloaders = [
305 ('cmdtable', commands, 'loadcmdtable'),
307 ('cmdtable', commands, 'loadcmdtable'),
306 ('colortable', color, 'loadcolortable'),
308 ('colortable', color, 'loadcolortable'),
307 ('filesetpredicate', fileset, 'loadpredicate'),
309 ('filesetpredicate', fileset, 'loadpredicate'),
308 ('internalmerge', filemerge, 'loadinternalmerge'),
310 ('internalmerge', filemerge, 'loadinternalmerge'),
309 ('revsetpredicate', revset, 'loadpredicate'),
311 ('revsetpredicate', revset, 'loadpredicate'),
310 ('templatefilter', templatefilters, 'loadfilter'),
312 ('templatefilter', templatefilters, 'loadfilter'),
311 ('templatefunc', templatefuncs, 'loadfunction'),
313 ('templatefunc', templatefuncs, 'loadfunction'),
312 ('templatekeyword', templatekw, 'loadkeyword'),
314 ('templatekeyword', templatekw, 'loadkeyword'),
313 ]
315 ]
314 _loadextra(ui, newindex, extraloaders)
316 _loadextra(ui, newindex, extraloaders)
315
317
316 def _loadextra(ui, newindex, extraloaders):
318 def _loadextra(ui, newindex, extraloaders):
317 for name in _order[newindex:]:
319 for name in _order[newindex:]:
318 module = _extensions[name]
320 module = _extensions[name]
319 if not module:
321 if not module:
320 continue # loading this module failed
322 continue # loading this module failed
321
323
322 for objname, loadermod, loadername in extraloaders:
324 for objname, loadermod, loadername in extraloaders:
323 extraobj = getattr(module, objname, None)
325 extraobj = getattr(module, objname, None)
324 if extraobj is not None:
326 if extraobj is not None:
325 getattr(loadermod, loadername)(ui, name, extraobj)
327 getattr(loadermod, loadername)(ui, name, extraobj)
326
328
327 def afterloaded(extension, callback):
329 def afterloaded(extension, callback):
328 '''Run the specified function after a named extension is loaded.
330 '''Run the specified function after a named extension is loaded.
329
331
330 If the named extension is already loaded, the callback will be called
332 If the named extension is already loaded, the callback will be called
331 immediately.
333 immediately.
332
334
333 If the named extension never loads, the callback will be called after
335 If the named extension never loads, the callback will be called after
334 all extensions have been loaded.
336 all extensions have been loaded.
335
337
336 The callback receives the named argument ``loaded``, which is a boolean
338 The callback receives the named argument ``loaded``, which is a boolean
337 indicating whether the dependent extension actually loaded.
339 indicating whether the dependent extension actually loaded.
338 '''
340 '''
339
341
340 if extension in _extensions:
342 if extension in _extensions:
341 # Report loaded as False if the extension is disabled
343 # Report loaded as False if the extension is disabled
342 loaded = (_extensions[extension] is not None)
344 loaded = (_extensions[extension] is not None)
343 callback(loaded=loaded)
345 callback(loaded=loaded)
344 else:
346 else:
345 _aftercallbacks.setdefault(extension, []).append(callback)
347 _aftercallbacks.setdefault(extension, []).append(callback)
346
348
347 def bind(func, *args):
349 def bind(func, *args):
348 '''Partial function application
350 '''Partial function application
349
351
350 Returns a new function that is the partial application of args and kwargs
352 Returns a new function that is the partial application of args and kwargs
351 to func. For example,
353 to func. For example,
352
354
353 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
355 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
354 assert callable(func)
356 assert callable(func)
355 def closure(*a, **kw):
357 def closure(*a, **kw):
356 return func(*(args + a), **kw)
358 return func(*(args + a), **kw)
357 return closure
359 return closure
358
360
359 def _updatewrapper(wrap, origfn, unboundwrapper):
361 def _updatewrapper(wrap, origfn, unboundwrapper):
360 '''Copy and add some useful attributes to wrapper'''
362 '''Copy and add some useful attributes to wrapper'''
361 try:
363 try:
362 wrap.__name__ = origfn.__name__
364 wrap.__name__ = origfn.__name__
363 except AttributeError:
365 except AttributeError:
364 pass
366 pass
365 wrap.__module__ = getattr(origfn, '__module__')
367 wrap.__module__ = getattr(origfn, '__module__')
366 wrap.__doc__ = getattr(origfn, '__doc__')
368 wrap.__doc__ = getattr(origfn, '__doc__')
367 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
369 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
368 wrap._origfunc = origfn
370 wrap._origfunc = origfn
369 wrap._unboundwrapper = unboundwrapper
371 wrap._unboundwrapper = unboundwrapper
370
372
371 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
373 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
372 '''Wrap the command named `command' in table
374 '''Wrap the command named `command' in table
373
375
374 Replace command in the command table with wrapper. The wrapped command will
376 Replace command in the command table with wrapper. The wrapped command will
375 be inserted into the command table specified by the table argument.
377 be inserted into the command table specified by the table argument.
376
378
377 The wrapper will be called like
379 The wrapper will be called like
378
380
379 wrapper(orig, *args, **kwargs)
381 wrapper(orig, *args, **kwargs)
380
382
381 where orig is the original (wrapped) function, and *args, **kwargs
383 where orig is the original (wrapped) function, and *args, **kwargs
382 are the arguments passed to it.
384 are the arguments passed to it.
383
385
384 Optionally append to the command synopsis and docstring, used for help.
386 Optionally append to the command synopsis and docstring, used for help.
385 For example, if your extension wraps the ``bookmarks`` command to add the
387 For example, if your extension wraps the ``bookmarks`` command to add the
386 flags ``--remote`` and ``--all`` you might call this function like so:
388 flags ``--remote`` and ``--all`` you might call this function like so:
387
389
388 synopsis = ' [-a] [--remote]'
390 synopsis = ' [-a] [--remote]'
389 docstring = """
391 docstring = """
390
392
391 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
393 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
392 flags to the bookmarks command. Either flag will show the remote bookmarks
394 flags to the bookmarks command. Either flag will show the remote bookmarks
393 known to the repository; ``--remote`` will also suppress the output of the
395 known to the repository; ``--remote`` will also suppress the output of the
394 local bookmarks.
396 local bookmarks.
395 """
397 """
396
398
397 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
399 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
398 synopsis, docstring)
400 synopsis, docstring)
399 '''
401 '''
400 assert callable(wrapper)
402 assert callable(wrapper)
401 aliases, entry = cmdutil.findcmd(command, table)
403 aliases, entry = cmdutil.findcmd(command, table)
402 for alias, e in table.iteritems():
404 for alias, e in table.iteritems():
403 if e is entry:
405 if e is entry:
404 key = alias
406 key = alias
405 break
407 break
406
408
407 origfn = entry[0]
409 origfn = entry[0]
408 wrap = functools.partial(util.checksignature(wrapper),
410 wrap = functools.partial(util.checksignature(wrapper),
409 util.checksignature(origfn))
411 util.checksignature(origfn))
410 _updatewrapper(wrap, origfn, wrapper)
412 _updatewrapper(wrap, origfn, wrapper)
411 if docstring is not None:
413 if docstring is not None:
412 wrap.__doc__ += docstring
414 wrap.__doc__ += docstring
413
415
414 newentry = list(entry)
416 newentry = list(entry)
415 newentry[0] = wrap
417 newentry[0] = wrap
416 if synopsis is not None:
418 if synopsis is not None:
417 newentry[2] += synopsis
419 newentry[2] += synopsis
418 table[key] = tuple(newentry)
420 table[key] = tuple(newentry)
419 return entry
421 return entry
420
422
421 def wrapfilecache(cls, propname, wrapper):
423 def wrapfilecache(cls, propname, wrapper):
422 """Wraps a filecache property.
424 """Wraps a filecache property.
423
425
424 These can't be wrapped using the normal wrapfunction.
426 These can't be wrapped using the normal wrapfunction.
425 """
427 """
426 propname = pycompat.sysstr(propname)
428 propname = pycompat.sysstr(propname)
427 assert callable(wrapper)
429 assert callable(wrapper)
428 for currcls in cls.__mro__:
430 for currcls in cls.__mro__:
429 if propname in currcls.__dict__:
431 if propname in currcls.__dict__:
430 origfn = currcls.__dict__[propname].func
432 origfn = currcls.__dict__[propname].func
431 assert callable(origfn)
433 assert callable(origfn)
432 def wrap(*args, **kwargs):
434 def wrap(*args, **kwargs):
433 return wrapper(origfn, *args, **kwargs)
435 return wrapper(origfn, *args, **kwargs)
434 currcls.__dict__[propname].func = wrap
436 currcls.__dict__[propname].func = wrap
435 break
437 break
436
438
437 if currcls is object:
439 if currcls is object:
438 raise AttributeError(r"type '%s' has no property '%s'" % (
440 raise AttributeError(r"type '%s' has no property '%s'" % (
439 cls, propname))
441 cls, propname))
440
442
441 class wrappedfunction(object):
443 class wrappedfunction(object):
442 '''context manager for temporarily wrapping a function'''
444 '''context manager for temporarily wrapping a function'''
443
445
444 def __init__(self, container, funcname, wrapper):
446 def __init__(self, container, funcname, wrapper):
445 assert callable(wrapper)
447 assert callable(wrapper)
446 self._container = container
448 self._container = container
447 self._funcname = funcname
449 self._funcname = funcname
448 self._wrapper = wrapper
450 self._wrapper = wrapper
449
451
450 def __enter__(self):
452 def __enter__(self):
451 wrapfunction(self._container, self._funcname, self._wrapper)
453 wrapfunction(self._container, self._funcname, self._wrapper)
452
454
453 def __exit__(self, exctype, excvalue, traceback):
455 def __exit__(self, exctype, excvalue, traceback):
454 unwrapfunction(self._container, self._funcname, self._wrapper)
456 unwrapfunction(self._container, self._funcname, self._wrapper)
455
457
456 def wrapfunction(container, funcname, wrapper):
458 def wrapfunction(container, funcname, wrapper):
457 '''Wrap the function named funcname in container
459 '''Wrap the function named funcname in container
458
460
459 Replace the funcname member in the given container with the specified
461 Replace the funcname member in the given container with the specified
460 wrapper. The container is typically a module, class, or instance.
462 wrapper. The container is typically a module, class, or instance.
461
463
462 The wrapper will be called like
464 The wrapper will be called like
463
465
464 wrapper(orig, *args, **kwargs)
466 wrapper(orig, *args, **kwargs)
465
467
466 where orig is the original (wrapped) function, and *args, **kwargs
468 where orig is the original (wrapped) function, and *args, **kwargs
467 are the arguments passed to it.
469 are the arguments passed to it.
468
470
469 Wrapping methods of the repository object is not recommended since
471 Wrapping methods of the repository object is not recommended since
470 it conflicts with extensions that extend the repository by
472 it conflicts with extensions that extend the repository by
471 subclassing. All extensions that need to extend methods of
473 subclassing. All extensions that need to extend methods of
472 localrepository should use this subclassing trick: namely,
474 localrepository should use this subclassing trick: namely,
473 reposetup() should look like
475 reposetup() should look like
474
476
475 def reposetup(ui, repo):
477 def reposetup(ui, repo):
476 class myrepo(repo.__class__):
478 class myrepo(repo.__class__):
477 def whatever(self, *args, **kwargs):
479 def whatever(self, *args, **kwargs):
478 [...extension stuff...]
480 [...extension stuff...]
479 super(myrepo, self).whatever(*args, **kwargs)
481 super(myrepo, self).whatever(*args, **kwargs)
480 [...extension stuff...]
482 [...extension stuff...]
481
483
482 repo.__class__ = myrepo
484 repo.__class__ = myrepo
483
485
484 In general, combining wrapfunction() with subclassing does not
486 In general, combining wrapfunction() with subclassing does not
485 work. Since you cannot control what other extensions are loaded by
487 work. Since you cannot control what other extensions are loaded by
486 your end users, you should play nicely with others by using the
488 your end users, you should play nicely with others by using the
487 subclass trick.
489 subclass trick.
488 '''
490 '''
489 assert callable(wrapper)
491 assert callable(wrapper)
490
492
491 origfn = getattr(container, funcname)
493 origfn = getattr(container, funcname)
492 assert callable(origfn)
494 assert callable(origfn)
493 if inspect.ismodule(container):
495 if inspect.ismodule(container):
494 # origfn is not an instance or class method. "partial" can be used.
496 # origfn is not an instance or class method. "partial" can be used.
495 # "partial" won't insert a frame in traceback.
497 # "partial" won't insert a frame in traceback.
496 wrap = functools.partial(wrapper, origfn)
498 wrap = functools.partial(wrapper, origfn)
497 else:
499 else:
498 # "partial" cannot be safely used. Emulate its effect by using "bind".
500 # "partial" cannot be safely used. Emulate its effect by using "bind".
499 # The downside is one more frame in traceback.
501 # The downside is one more frame in traceback.
500 wrap = bind(wrapper, origfn)
502 wrap = bind(wrapper, origfn)
501 _updatewrapper(wrap, origfn, wrapper)
503 _updatewrapper(wrap, origfn, wrapper)
502 setattr(container, funcname, wrap)
504 setattr(container, funcname, wrap)
503 return origfn
505 return origfn
504
506
505 def unwrapfunction(container, funcname, wrapper=None):
507 def unwrapfunction(container, funcname, wrapper=None):
506 '''undo wrapfunction
508 '''undo wrapfunction
507
509
508 If wrappers is None, undo the last wrap. Otherwise removes the wrapper
510 If wrappers is None, undo the last wrap. Otherwise removes the wrapper
509 from the chain of wrappers.
511 from the chain of wrappers.
510
512
511 Return the removed wrapper.
513 Return the removed wrapper.
512 Raise IndexError if wrapper is None and nothing to unwrap; ValueError if
514 Raise IndexError if wrapper is None and nothing to unwrap; ValueError if
513 wrapper is not None but is not found in the wrapper chain.
515 wrapper is not None but is not found in the wrapper chain.
514 '''
516 '''
515 chain = getwrapperchain(container, funcname)
517 chain = getwrapperchain(container, funcname)
516 origfn = chain.pop()
518 origfn = chain.pop()
517 if wrapper is None:
519 if wrapper is None:
518 wrapper = chain[0]
520 wrapper = chain[0]
519 chain.remove(wrapper)
521 chain.remove(wrapper)
520 setattr(container, funcname, origfn)
522 setattr(container, funcname, origfn)
521 for w in reversed(chain):
523 for w in reversed(chain):
522 wrapfunction(container, funcname, w)
524 wrapfunction(container, funcname, w)
523 return wrapper
525 return wrapper
524
526
525 def getwrapperchain(container, funcname):
527 def getwrapperchain(container, funcname):
526 '''get a chain of wrappers of a function
528 '''get a chain of wrappers of a function
527
529
528 Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
530 Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
529
531
530 The wrapper functions are the ones passed to wrapfunction, whose first
532 The wrapper functions are the ones passed to wrapfunction, whose first
531 argument is origfunc.
533 argument is origfunc.
532 '''
534 '''
533 result = []
535 result = []
534 fn = getattr(container, funcname)
536 fn = getattr(container, funcname)
535 while fn:
537 while fn:
536 assert callable(fn)
538 assert callable(fn)
537 result.append(getattr(fn, '_unboundwrapper', fn))
539 result.append(getattr(fn, '_unboundwrapper', fn))
538 fn = getattr(fn, '_origfunc', None)
540 fn = getattr(fn, '_origfunc', None)
539 return result
541 return result
540
542
541 def _disabledpaths(strip_init=False):
543 def _disabledpaths(strip_init=False):
542 '''find paths of disabled extensions. returns a dict of {name: path}
544 '''find paths of disabled extensions. returns a dict of {name: path}
543 removes /__init__.py from packages if strip_init is True'''
545 removes /__init__.py from packages if strip_init is True'''
544 import hgext
546 import hgext
545 extpath = os.path.dirname(
547 extpath = os.path.dirname(
546 os.path.abspath(pycompat.fsencode(hgext.__file__)))
548 os.path.abspath(pycompat.fsencode(hgext.__file__)))
547 try: # might not be a filesystem path
549 try: # might not be a filesystem path
548 files = os.listdir(extpath)
550 files = os.listdir(extpath)
549 except OSError:
551 except OSError:
550 return {}
552 return {}
551
553
552 exts = {}
554 exts = {}
553 for e in files:
555 for e in files:
554 if e.endswith('.py'):
556 if e.endswith('.py'):
555 name = e.rsplit('.', 1)[0]
557 name = e.rsplit('.', 1)[0]
556 path = os.path.join(extpath, e)
558 path = os.path.join(extpath, e)
557 else:
559 else:
558 name = e
560 name = e
559 path = os.path.join(extpath, e, '__init__.py')
561 path = os.path.join(extpath, e, '__init__.py')
560 if not os.path.exists(path):
562 if not os.path.exists(path):
561 continue
563 continue
562 if strip_init:
564 if strip_init:
563 path = os.path.dirname(path)
565 path = os.path.dirname(path)
564 if name in exts or name in _order or name == '__init__':
566 if name in exts or name in _order or name == '__init__':
565 continue
567 continue
566 exts[name] = path
568 exts[name] = path
567 for name, path in _disabledextensions.iteritems():
569 for name, path in _disabledextensions.iteritems():
568 # If no path was provided for a disabled extension (e.g. "color=!"),
570 # If no path was provided for a disabled extension (e.g. "color=!"),
569 # don't replace the path we already found by the scan above.
571 # don't replace the path we already found by the scan above.
570 if path:
572 if path:
571 exts[name] = path
573 exts[name] = path
572 return exts
574 return exts
573
575
574 def _moduledoc(file):
576 def _moduledoc(file):
575 '''return the top-level python documentation for the given file
577 '''return the top-level python documentation for the given file
576
578
577 Loosely inspired by pydoc.source_synopsis(), but rewritten to
579 Loosely inspired by pydoc.source_synopsis(), but rewritten to
578 handle triple quotes and to return the whole text instead of just
580 handle triple quotes and to return the whole text instead of just
579 the synopsis'''
581 the synopsis'''
580 result = []
582 result = []
581
583
582 line = file.readline()
584 line = file.readline()
583 while line[:1] == '#' or not line.strip():
585 while line[:1] == '#' or not line.strip():
584 line = file.readline()
586 line = file.readline()
585 if not line:
587 if not line:
586 break
588 break
587
589
588 start = line[:3]
590 start = line[:3]
589 if start == '"""' or start == "'''":
591 if start == '"""' or start == "'''":
590 line = line[3:]
592 line = line[3:]
591 while line:
593 while line:
592 if line.rstrip().endswith(start):
594 if line.rstrip().endswith(start):
593 line = line.split(start)[0]
595 line = line.split(start)[0]
594 if line:
596 if line:
595 result.append(line)
597 result.append(line)
596 break
598 break
597 elif not line:
599 elif not line:
598 return None # unmatched delimiter
600 return None # unmatched delimiter
599 result.append(line)
601 result.append(line)
600 line = file.readline()
602 line = file.readline()
601 else:
603 else:
602 return None
604 return None
603
605
604 return ''.join(result)
606 return ''.join(result)
605
607
606 def _disabledhelp(path):
608 def _disabledhelp(path):
607 '''retrieve help synopsis of a disabled extension (without importing)'''
609 '''retrieve help synopsis of a disabled extension (without importing)'''
608 try:
610 try:
609 file = open(path)
611 file = open(path)
610 except IOError:
612 except IOError:
611 return
613 return
612 else:
614 else:
613 doc = _moduledoc(file)
615 doc = _moduledoc(file)
614 file.close()
616 file.close()
615
617
616 if doc: # extracting localized synopsis
618 if doc: # extracting localized synopsis
617 return gettext(doc)
619 return gettext(doc)
618 else:
620 else:
619 return _('(no help text available)')
621 return _('(no help text available)')
620
622
621 def disabled():
623 def disabled():
622 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
624 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
623 try:
625 try:
624 from hgext import __index__
626 from hgext import __index__
625 return dict((name, gettext(desc))
627 return dict((name, gettext(desc))
626 for name, desc in __index__.docs.iteritems()
628 for name, desc in __index__.docs.iteritems()
627 if name not in _order)
629 if name not in _order)
628 except (ImportError, AttributeError):
630 except (ImportError, AttributeError):
629 pass
631 pass
630
632
631 paths = _disabledpaths()
633 paths = _disabledpaths()
632 if not paths:
634 if not paths:
633 return {}
635 return {}
634
636
635 exts = {}
637 exts = {}
636 for name, path in paths.iteritems():
638 for name, path in paths.iteritems():
637 doc = _disabledhelp(path)
639 doc = _disabledhelp(path)
638 if doc:
640 if doc:
639 exts[name] = doc.splitlines()[0]
641 exts[name] = doc.splitlines()[0]
640
642
641 return exts
643 return exts
642
644
643 def disabledext(name):
645 def disabledext(name):
644 '''find a specific disabled extension from hgext. returns desc'''
646 '''find a specific disabled extension from hgext. returns desc'''
645 try:
647 try:
646 from hgext import __index__
648 from hgext import __index__
647 if name in _order: # enabled
649 if name in _order: # enabled
648 return
650 return
649 else:
651 else:
650 return gettext(__index__.docs.get(name))
652 return gettext(__index__.docs.get(name))
651 except (ImportError, AttributeError):
653 except (ImportError, AttributeError):
652 pass
654 pass
653
655
654 paths = _disabledpaths()
656 paths = _disabledpaths()
655 if name in paths:
657 if name in paths:
656 return _disabledhelp(paths[name])
658 return _disabledhelp(paths[name])
657
659
660 def _walkcommand(node):
661 """Scan @command() decorators in the tree starting at node"""
662 todo = collections.deque([node])
663 while todo:
664 node = todo.popleft()
665 if not isinstance(node, ast.FunctionDef):
666 todo.extend(ast.iter_child_nodes(node))
667 continue
668 for d in node.decorator_list:
669 if not isinstance(d, ast.Call):
670 continue
671 if not isinstance(d.func, ast.Name):
672 continue
673 if d.func.id != r'command':
674 continue
675 yield d
676
677 def _disabledcmdtable(path):
678 """Construct a dummy command table without loading the extension module
679
680 This may raise IOError or SyntaxError.
681 """
682 with open(path, 'rb') as src:
683 root = ast.parse(src.read(), path)
684 cmdtable = {}
685 for node in _walkcommand(root):
686 if not node.args:
687 continue
688 a = node.args[0]
689 if isinstance(a, ast.Str):
690 name = pycompat.sysbytes(a.s)
691 elif pycompat.ispy3 and isinstance(a, ast.Bytes):
692 name = a.s
693 else:
694 continue
695 cmdtable[name] = (None, [], b'')
696 return cmdtable
697
658 def _finddisabledcmd(ui, cmd, name, path, strict):
698 def _finddisabledcmd(ui, cmd, name, path, strict):
659 try:
699 try:
660 mod = loadpath(path, 'hgext.%s' % name)
700 cmdtable = _disabledcmdtable(path)
661 except Exception:
701 except (IOError, SyntaxError):
662 return
702 return
663 try:
703 try:
664 aliases, entry = cmdutil.findcmd(cmd,
704 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
665 getattr(mod, 'cmdtable', {}), strict)
666 except (error.AmbiguousCommand, error.UnknownCommand):
705 except (error.AmbiguousCommand, error.UnknownCommand):
667 return
706 return
668 except Exception:
669 ui.warn(_('warning: error finding commands in %s\n') % path)
670 ui.traceback()
671 return
672 for c in aliases:
707 for c in aliases:
673 if c.startswith(cmd):
708 if c.startswith(cmd):
674 cmd = c
709 cmd = c
675 break
710 break
676 else:
711 else:
677 cmd = aliases[0]
712 cmd = aliases[0]
678 doc = gettext(pycompat.getdoc(mod))
713 doc = _disabledhelp(path)
679 return (cmd, name, doc)
714 return (cmd, name, doc)
680
715
681 def disabledcmd(ui, cmd, strict=False):
716 def disabledcmd(ui, cmd, strict=False):
682 '''import disabled extensions until cmd is found.
717 '''find cmd from disabled extensions without importing.
683 returns (cmdname, extname, doc)'''
718 returns (cmdname, extname, doc)'''
684
719
685 paths = _disabledpaths(strip_init=True)
720 paths = _disabledpaths()
686 if not paths:
721 if not paths:
687 raise error.UnknownCommand(cmd)
722 raise error.UnknownCommand(cmd)
688
723
689 ext = None
724 ext = None
690 # first, search for an extension with the same name as the command
725 # first, search for an extension with the same name as the command
691 path = paths.pop(cmd, None)
726 path = paths.pop(cmd, None)
692 if path:
727 if path:
693 ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict)
728 ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict)
694 if not ext:
729 if not ext:
695 # otherwise, interrogate each extension until there's a match
730 # otherwise, interrogate each extension until there's a match
696 for name, path in paths.iteritems():
731 for name, path in paths.iteritems():
697 ext = _finddisabledcmd(ui, cmd, name, path, strict=strict)
732 ext = _finddisabledcmd(ui, cmd, name, path, strict=strict)
698 if ext:
733 if ext:
699 break
734 break
700 if ext:
735 if ext:
701 return ext
736 return ext
702
737
703 raise error.UnknownCommand(cmd)
738 raise error.UnknownCommand(cmd)
704
739
705 def enabled(shortname=True):
740 def enabled(shortname=True):
706 '''return a dict of {name: desc} of extensions'''
741 '''return a dict of {name: desc} of extensions'''
707 exts = {}
742 exts = {}
708 for ename, ext in extensions():
743 for ename, ext in extensions():
709 doc = (gettext(ext.__doc__) or _('(no help text available)'))
744 doc = (gettext(ext.__doc__) or _('(no help text available)'))
710 if shortname:
745 if shortname:
711 ename = ename.split('.')[-1]
746 ename = ename.split('.')[-1]
712 exts[ename] = doc.splitlines()[0].strip()
747 exts[ename] = doc.splitlines()[0].strip()
713
748
714 return exts
749 return exts
715
750
716 def notloaded():
751 def notloaded():
717 '''return short names of extensions that failed to load'''
752 '''return short names of extensions that failed to load'''
718 return [name for name, mod in _extensions.iteritems() if mod is None]
753 return [name for name, mod in _extensions.iteritems() if mod is None]
719
754
720 def moduleversion(module):
755 def moduleversion(module):
721 '''return version information from given module as a string'''
756 '''return version information from given module as a string'''
722 if (util.safehasattr(module, 'getversion')
757 if (util.safehasattr(module, 'getversion')
723 and callable(module.getversion)):
758 and callable(module.getversion)):
724 version = module.getversion()
759 version = module.getversion()
725 elif util.safehasattr(module, '__version__'):
760 elif util.safehasattr(module, '__version__'):
726 version = module.__version__
761 version = module.__version__
727 else:
762 else:
728 version = ''
763 version = ''
729 if isinstance(version, (list, tuple)):
764 if isinstance(version, (list, tuple)):
730 version = '.'.join(pycompat.bytestr(o) for o in version)
765 version = '.'.join(pycompat.bytestr(o) for o in version)
731 return version
766 return version
732
767
733 def ismoduleinternal(module):
768 def ismoduleinternal(module):
734 exttestedwith = getattr(module, 'testedwith', None)
769 exttestedwith = getattr(module, 'testedwith', None)
735 return exttestedwith == "ships-with-hg-core"
770 return exttestedwith == "ships-with-hg-core"
@@ -1,1729 +1,1734 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 commands, registrar
5 > from mercurial import commands, registrar
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = registrar.command(cmdtable)
7 > command = registrar.command(cmdtable)
8 > configtable = {}
8 > configtable = {}
9 > configitem = registrar.configitem(configtable)
9 > configitem = registrar.configitem(configtable)
10 > configitem(b'tests', b'foo', default=b"Foo")
10 > configitem(b'tests', b'foo', default=b"Foo")
11 > def uisetup(ui):
11 > def uisetup(ui):
12 > ui.write(b"uisetup called\\n")
12 > ui.write(b"uisetup called\\n")
13 > ui.flush()
13 > ui.flush()
14 > def reposetup(ui, repo):
14 > def reposetup(ui, repo):
15 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
15 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
16 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
16 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
17 > ui.flush()
17 > ui.flush()
18 > @command(b'foo', [], b'hg foo')
18 > @command(b'foo', [], b'hg foo')
19 > def foo(ui, *args, **kwargs):
19 > def foo(ui, *args, **kwargs):
20 > foo = ui.config(b'tests', b'foo')
20 > foo = ui.config(b'tests', b'foo')
21 > ui.write(foo)
21 > ui.write(foo)
22 > ui.write(b"\\n")
22 > ui.write(b"\\n")
23 > @command(b'bar', [], b'hg bar', norepo=True)
23 > @command(b'bar', [], b'hg bar', norepo=True)
24 > def bar(ui, *args, **kwargs):
24 > def bar(ui, *args, **kwargs):
25 > ui.write(b"Bar\\n")
25 > ui.write(b"Bar\\n")
26 > EOF
26 > EOF
27 $ abspath=`pwd`/foobar.py
27 $ abspath=`pwd`/foobar.py
28
28
29 $ mkdir barfoo
29 $ mkdir barfoo
30 $ cp foobar.py barfoo/__init__.py
30 $ cp foobar.py barfoo/__init__.py
31 $ barfoopath=`pwd`/barfoo
31 $ barfoopath=`pwd`/barfoo
32
32
33 $ hg init a
33 $ hg init a
34 $ cd a
34 $ cd a
35 $ echo foo > file
35 $ echo foo > file
36 $ hg add file
36 $ hg add file
37 $ hg commit -m 'add file'
37 $ hg commit -m 'add file'
38
38
39 $ echo '[extensions]' >> $HGRCPATH
39 $ echo '[extensions]' >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
41 $ hg foo
41 $ hg foo
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 reposetup called for a (chg !)
45 reposetup called for a (chg !)
46 ui == repo.ui (chg !)
46 ui == repo.ui (chg !)
47 Foo
47 Foo
48
48
49 $ cd ..
49 $ cd ..
50 $ hg clone a b
50 $ hg clone a b
51 uisetup called (no-chg !)
51 uisetup called (no-chg !)
52 reposetup called for a
52 reposetup called for a
53 ui == repo.ui
53 ui == repo.ui
54 reposetup called for b
54 reposetup called for b
55 ui == repo.ui
55 ui == repo.ui
56 updating to branch default
56 updating to branch default
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58
58
59 $ hg bar
59 $ hg bar
60 uisetup called (no-chg !)
60 uisetup called (no-chg !)
61 Bar
61 Bar
62 $ echo 'foobar = !' >> $HGRCPATH
62 $ echo 'foobar = !' >> $HGRCPATH
63
63
64 module/__init__.py-style
64 module/__init__.py-style
65
65
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
67 $ cd a
67 $ cd a
68 $ hg foo
68 $ hg foo
69 uisetup called
69 uisetup called
70 reposetup called for a
70 reposetup called for a
71 ui == repo.ui
71 ui == repo.ui
72 reposetup called for a (chg !)
72 reposetup called for a (chg !)
73 ui == repo.ui (chg !)
73 ui == repo.ui (chg !)
74 Foo
74 Foo
75 $ echo 'barfoo = !' >> $HGRCPATH
75 $ echo 'barfoo = !' >> $HGRCPATH
76
76
77 Check that extensions are loaded in phases:
77 Check that extensions are loaded in phases:
78
78
79 $ cat > foo.py <<EOF
79 $ cat > foo.py <<EOF
80 > import os
80 > import os
81 > name = os.path.basename(__file__).rsplit('.', 1)[0]
81 > name = os.path.basename(__file__).rsplit('.', 1)[0]
82 > print("1) %s imported" % name)
82 > print("1) %s imported" % name)
83 > def uisetup(ui):
83 > def uisetup(ui):
84 > print("2) %s uisetup" % name)
84 > print("2) %s uisetup" % name)
85 > def extsetup():
85 > def extsetup():
86 > print("3) %s extsetup" % name)
86 > print("3) %s extsetup" % name)
87 > def reposetup(ui, repo):
87 > def reposetup(ui, repo):
88 > print("4) %s reposetup" % name)
88 > print("4) %s reposetup" % name)
89 >
89 >
90 > # custom predicate to check registration of functions at loading
90 > # custom predicate to check registration of functions at loading
91 > from mercurial import (
91 > from mercurial import (
92 > registrar,
92 > registrar,
93 > smartset,
93 > smartset,
94 > )
94 > )
95 > revsetpredicate = registrar.revsetpredicate()
95 > revsetpredicate = registrar.revsetpredicate()
96 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
96 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
97 > def custompredicate(repo, subset, x):
97 > def custompredicate(repo, subset, x):
98 > return smartset.baseset([r for r in subset if r in {0}])
98 > return smartset.baseset([r for r in subset if r in {0}])
99 > EOF
99 > EOF
100
100
101 $ cp foo.py bar.py
101 $ cp foo.py bar.py
102 $ echo 'foo = foo.py' >> $HGRCPATH
102 $ echo 'foo = foo.py' >> $HGRCPATH
103 $ echo 'bar = bar.py' >> $HGRCPATH
103 $ echo 'bar = bar.py' >> $HGRCPATH
104
104
105 Check normal command's load order of extensions and registration of functions
105 Check normal command's load order of extensions and registration of functions
106
106
107 $ hg log -r "foo() and bar()" -q
107 $ hg log -r "foo() and bar()" -q
108 1) foo imported
108 1) foo imported
109 1) bar imported
109 1) bar imported
110 2) foo uisetup
110 2) foo uisetup
111 2) bar uisetup
111 2) bar uisetup
112 3) foo extsetup
112 3) foo extsetup
113 3) bar extsetup
113 3) bar extsetup
114 4) foo reposetup
114 4) foo reposetup
115 4) bar reposetup
115 4) bar reposetup
116 0:c24b9ac61126
116 0:c24b9ac61126
117
117
118 Check hgweb's load order of extensions and registration of functions
118 Check hgweb's load order of extensions and registration of functions
119
119
120 $ cat > hgweb.cgi <<EOF
120 $ cat > hgweb.cgi <<EOF
121 > #!$PYTHON
121 > #!$PYTHON
122 > from mercurial import demandimport; demandimport.enable()
122 > from mercurial import demandimport; demandimport.enable()
123 > from mercurial.hgweb import hgweb
123 > from mercurial.hgweb import hgweb
124 > from mercurial.hgweb import wsgicgi
124 > from mercurial.hgweb import wsgicgi
125 > application = hgweb('.', 'test repo')
125 > application = hgweb('.', 'test repo')
126 > wsgicgi.launch(application)
126 > wsgicgi.launch(application)
127 > EOF
127 > EOF
128 $ . "$TESTDIR/cgienv"
128 $ . "$TESTDIR/cgienv"
129
129
130 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
130 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
131 > | grep '^[0-9]) ' # ignores HTML output
131 > | grep '^[0-9]) ' # ignores HTML output
132 1) foo imported
132 1) foo imported
133 1) bar imported
133 1) bar imported
134 2) foo uisetup
134 2) foo uisetup
135 2) bar uisetup
135 2) bar uisetup
136 3) foo extsetup
136 3) foo extsetup
137 3) bar extsetup
137 3) bar extsetup
138 4) foo reposetup
138 4) foo reposetup
139 4) bar reposetup
139 4) bar reposetup
140
140
141 (check that revset predicate foo() and bar() are available)
141 (check that revset predicate foo() and bar() are available)
142
142
143 #if msys
143 #if msys
144 $ PATH_INFO='//shortlog'
144 $ PATH_INFO='//shortlog'
145 #else
145 #else
146 $ PATH_INFO='/shortlog'
146 $ PATH_INFO='/shortlog'
147 #endif
147 #endif
148 $ export PATH_INFO
148 $ export PATH_INFO
149 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
149 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
150 > | grep '<a href="/rev/[0-9a-z]*">'
150 > | grep '<a href="/rev/[0-9a-z]*">'
151 <a href="/rev/c24b9ac61126">add file</a>
151 <a href="/rev/c24b9ac61126">add file</a>
152
152
153 $ echo 'foo = !' >> $HGRCPATH
153 $ echo 'foo = !' >> $HGRCPATH
154 $ echo 'bar = !' >> $HGRCPATH
154 $ echo 'bar = !' >> $HGRCPATH
155
155
156 Check "from __future__ import absolute_import" support for external libraries
156 Check "from __future__ import absolute_import" support for external libraries
157
157
158 #if windows
158 #if windows
159 $ PATHSEP=";"
159 $ PATHSEP=";"
160 #else
160 #else
161 $ PATHSEP=":"
161 $ PATHSEP=":"
162 #endif
162 #endif
163 $ export PATHSEP
163 $ export PATHSEP
164
164
165 $ mkdir $TESTTMP/libroot
165 $ mkdir $TESTTMP/libroot
166 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
166 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
167 $ mkdir $TESTTMP/libroot/mod
167 $ mkdir $TESTTMP/libroot/mod
168 $ touch $TESTTMP/libroot/mod/__init__.py
168 $ touch $TESTTMP/libroot/mod/__init__.py
169 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
169 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
170
170
171 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
171 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
172 > from __future__ import absolute_import
172 > from __future__ import absolute_import
173 > import ambig # should load "libroot/ambig.py"
173 > import ambig # should load "libroot/ambig.py"
174 > s = ambig.s
174 > s = ambig.s
175 > EOF
175 > EOF
176 $ cat > loadabs.py <<EOF
176 $ cat > loadabs.py <<EOF
177 > import mod.ambigabs as ambigabs
177 > import mod.ambigabs as ambigabs
178 > def extsetup():
178 > def extsetup():
179 > print('ambigabs.s=%s' % ambigabs.s)
179 > print('ambigabs.s=%s' % ambigabs.s)
180 > EOF
180 > EOF
181 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
181 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
182 ambigabs.s=libroot/ambig.py
182 ambigabs.s=libroot/ambig.py
183 $TESTTMP/a
183 $TESTTMP/a
184
184
185 #if no-py3k
185 #if no-py3k
186 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
186 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
187 > import ambig # should load "libroot/mod/ambig.py"
187 > import ambig # should load "libroot/mod/ambig.py"
188 > s = ambig.s
188 > s = ambig.s
189 > EOF
189 > EOF
190 $ cat > loadrel.py <<EOF
190 $ cat > loadrel.py <<EOF
191 > import mod.ambigrel as ambigrel
191 > import mod.ambigrel as ambigrel
192 > def extsetup():
192 > def extsetup():
193 > print('ambigrel.s=%s' % ambigrel.s)
193 > print('ambigrel.s=%s' % ambigrel.s)
194 > EOF
194 > EOF
195 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
195 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
196 ambigrel.s=libroot/mod/ambig.py
196 ambigrel.s=libroot/mod/ambig.py
197 $TESTTMP/a
197 $TESTTMP/a
198 #endif
198 #endif
199
199
200 Check absolute/relative import of extension specific modules
200 Check absolute/relative import of extension specific modules
201
201
202 $ mkdir $TESTTMP/extroot
202 $ mkdir $TESTTMP/extroot
203 $ cat > $TESTTMP/extroot/bar.py <<EOF
203 $ cat > $TESTTMP/extroot/bar.py <<EOF
204 > s = 'this is extroot.bar'
204 > s = 'this is extroot.bar'
205 > EOF
205 > EOF
206 $ mkdir $TESTTMP/extroot/sub1
206 $ mkdir $TESTTMP/extroot/sub1
207 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
207 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
208 > s = 'this is extroot.sub1.__init__'
208 > s = 'this is extroot.sub1.__init__'
209 > EOF
209 > EOF
210 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
210 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
211 > s = 'this is extroot.sub1.baz'
211 > s = 'this is extroot.sub1.baz'
212 > EOF
212 > EOF
213 $ cat > $TESTTMP/extroot/__init__.py <<EOF
213 $ cat > $TESTTMP/extroot/__init__.py <<EOF
214 > s = 'this is extroot.__init__'
214 > s = 'this is extroot.__init__'
215 > import foo
215 > import foo
216 > def extsetup(ui):
216 > def extsetup(ui):
217 > ui.write('(extroot) ', foo.func(), '\n')
217 > ui.write('(extroot) ', foo.func(), '\n')
218 > ui.flush()
218 > ui.flush()
219 > EOF
219 > EOF
220
220
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 > # test absolute import
222 > # test absolute import
223 > buf = []
223 > buf = []
224 > def func():
224 > def func():
225 > # "not locals" case
225 > # "not locals" case
226 > import extroot.bar
226 > import extroot.bar
227 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
227 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
228 > return '\n(extroot) '.join(buf)
228 > return '\n(extroot) '.join(buf)
229 > # "fromlist == ('*',)" case
229 > # "fromlist == ('*',)" case
230 > from extroot.bar import *
230 > from extroot.bar import *
231 > buf.append('from extroot.bar import *: %s' % s)
231 > buf.append('from extroot.bar import *: %s' % s)
232 > # "not fromlist" and "if '.' in name" case
232 > # "not fromlist" and "if '.' in name" case
233 > import extroot.sub1.baz
233 > import extroot.sub1.baz
234 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
234 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
235 > # "not fromlist" and NOT "if '.' in name" case
235 > # "not fromlist" and NOT "if '.' in name" case
236 > import extroot
236 > import extroot
237 > buf.append('import extroot: %s' % extroot.s)
237 > buf.append('import extroot: %s' % extroot.s)
238 > # NOT "not fromlist" and NOT "level != -1" case
238 > # NOT "not fromlist" and NOT "level != -1" case
239 > from extroot.bar import s
239 > from extroot.bar import s
240 > buf.append('from extroot.bar import s: %s' % s)
240 > buf.append('from extroot.bar import s: %s' % s)
241 > EOF
241 > EOF
242 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
242 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
243 (extroot) from extroot.bar import *: this is extroot.bar
243 (extroot) from extroot.bar import *: this is extroot.bar
244 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
244 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
245 (extroot) import extroot: this is extroot.__init__
245 (extroot) import extroot: this is extroot.__init__
246 (extroot) from extroot.bar import s: this is extroot.bar
246 (extroot) from extroot.bar import s: this is extroot.bar
247 (extroot) import extroot.bar in func(): this is extroot.bar
247 (extroot) import extroot.bar in func(): this is extroot.bar
248 $TESTTMP/a
248 $TESTTMP/a
249
249
250 #if no-py3k
250 #if no-py3k
251 $ rm "$TESTTMP"/extroot/foo.*
251 $ rm "$TESTTMP"/extroot/foo.*
252 $ rm -Rf "$TESTTMP/extroot/__pycache__"
252 $ rm -Rf "$TESTTMP/extroot/__pycache__"
253 $ cat > $TESTTMP/extroot/foo.py <<EOF
253 $ cat > $TESTTMP/extroot/foo.py <<EOF
254 > # test relative import
254 > # test relative import
255 > buf = []
255 > buf = []
256 > def func():
256 > def func():
257 > # "not locals" case
257 > # "not locals" case
258 > import bar
258 > import bar
259 > buf.append('import bar in func(): %s' % bar.s)
259 > buf.append('import bar in func(): %s' % bar.s)
260 > return '\n(extroot) '.join(buf)
260 > return '\n(extroot) '.join(buf)
261 > # "fromlist == ('*',)" case
261 > # "fromlist == ('*',)" case
262 > from bar import *
262 > from bar import *
263 > buf.append('from bar import *: %s' % s)
263 > buf.append('from bar import *: %s' % s)
264 > # "not fromlist" and "if '.' in name" case
264 > # "not fromlist" and "if '.' in name" case
265 > import sub1.baz
265 > import sub1.baz
266 > buf.append('import sub1.baz: %s' % sub1.baz.s)
266 > buf.append('import sub1.baz: %s' % sub1.baz.s)
267 > # "not fromlist" and NOT "if '.' in name" case
267 > # "not fromlist" and NOT "if '.' in name" case
268 > import sub1
268 > import sub1
269 > buf.append('import sub1: %s' % sub1.s)
269 > buf.append('import sub1: %s' % sub1.s)
270 > # NOT "not fromlist" and NOT "level != -1" case
270 > # NOT "not fromlist" and NOT "level != -1" case
271 > from bar import s
271 > from bar import s
272 > buf.append('from bar import s: %s' % s)
272 > buf.append('from bar import s: %s' % s)
273 > EOF
273 > EOF
274 $ hg --config extensions.extroot=$TESTTMP/extroot root
274 $ hg --config extensions.extroot=$TESTTMP/extroot root
275 (extroot) from bar import *: this is extroot.bar
275 (extroot) from bar import *: this is extroot.bar
276 (extroot) import sub1.baz: this is extroot.sub1.baz
276 (extroot) import sub1.baz: this is extroot.sub1.baz
277 (extroot) import sub1: this is extroot.sub1.__init__
277 (extroot) import sub1: this is extroot.sub1.__init__
278 (extroot) from bar import s: this is extroot.bar
278 (extroot) from bar import s: this is extroot.bar
279 (extroot) import bar in func(): this is extroot.bar
279 (extroot) import bar in func(): this is extroot.bar
280 $TESTTMP/a
280 $TESTTMP/a
281 #endif
281 #endif
282
282
283 #if demandimport
283 #if demandimport
284
284
285 Examine whether module loading is delayed until actual referring, even
285 Examine whether module loading is delayed until actual referring, even
286 though module is imported with "absolute_import" feature.
286 though module is imported with "absolute_import" feature.
287
287
288 Files below in each packages are used for described purpose:
288 Files below in each packages are used for described purpose:
289
289
290 - "called": examine whether "from MODULE import ATTR" works correctly
290 - "called": examine whether "from MODULE import ATTR" works correctly
291 - "unused": examine whether loading is delayed correctly
291 - "unused": examine whether loading is delayed correctly
292 - "used": examine whether "from PACKAGE import MODULE" works correctly
292 - "used": examine whether "from PACKAGE import MODULE" works correctly
293
293
294 Package hierarchy is needed to examine whether demand importing works
294 Package hierarchy is needed to examine whether demand importing works
295 as expected for "from SUB.PACK.AGE import MODULE".
295 as expected for "from SUB.PACK.AGE import MODULE".
296
296
297 Setup "external library" to be imported with "absolute_import"
297 Setup "external library" to be imported with "absolute_import"
298 feature.
298 feature.
299
299
300 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
300 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
301 $ touch $TESTTMP/extlibroot/__init__.py
301 $ touch $TESTTMP/extlibroot/__init__.py
302 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
302 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
303 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
303 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
304
304
305 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
305 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
306 > def func():
306 > def func():
307 > return "this is extlibroot.lsub1.lsub2.called.func()"
307 > return "this is extlibroot.lsub1.lsub2.called.func()"
308 > EOF
308 > EOF
309 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
309 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
310 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
310 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
311 > EOF
311 > EOF
312 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
312 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
313 > detail = "this is extlibroot.lsub1.lsub2.used"
313 > detail = "this is extlibroot.lsub1.lsub2.used"
314 > EOF
314 > EOF
315
315
316 Setup sub-package of "external library", which causes instantiation of
316 Setup sub-package of "external library", which causes instantiation of
317 demandmod in "recurse down the module chain" code path. Relative
317 demandmod in "recurse down the module chain" code path. Relative
318 importing with "absolute_import" feature isn't tested, because "level
318 importing with "absolute_import" feature isn't tested, because "level
319 >=1 " doesn't cause instantiation of demandmod.
319 >=1 " doesn't cause instantiation of demandmod.
320
320
321 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
321 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
322 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
322 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
323 > detail = "this is extlibroot.recursedown.abs.used"
323 > detail = "this is extlibroot.recursedown.abs.used"
324 > EOF
324 > EOF
325 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
325 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
326 > from __future__ import absolute_import
326 > from __future__ import absolute_import
327 > from extlibroot.recursedown.abs.used import detail
327 > from extlibroot.recursedown.abs.used import detail
328 > EOF
328 > EOF
329
329
330 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
330 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
331 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
331 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
332 > detail = "this is extlibroot.recursedown.legacy.used"
332 > detail = "this is extlibroot.recursedown.legacy.used"
333 > EOF
333 > EOF
334 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
334 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
335 > # legacy style (level == -1) import
335 > # legacy style (level == -1) import
336 > from extlibroot.recursedown.legacy.used import detail
336 > from extlibroot.recursedown.legacy.used import detail
337 > EOF
337 > EOF
338
338
339 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
339 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
340 > from __future__ import absolute_import
340 > from __future__ import absolute_import
341 > from extlibroot.recursedown.abs import detail as absdetail
341 > from extlibroot.recursedown.abs import detail as absdetail
342 > from .legacy import detail as legacydetail
342 > from .legacy import detail as legacydetail
343 > EOF
343 > EOF
344
344
345 Setup package that re-exports an attribute of its submodule as the same
345 Setup package that re-exports an attribute of its submodule as the same
346 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
346 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
347 the submodule 'used' should be somehow accessible. (issue5617)
347 the submodule 'used' should be somehow accessible. (issue5617)
348
348
349 $ mkdir -p $TESTTMP/extlibroot/shadowing
349 $ mkdir -p $TESTTMP/extlibroot/shadowing
350 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
350 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
351 > detail = "this is extlibroot.shadowing.used"
351 > detail = "this is extlibroot.shadowing.used"
352 > EOF
352 > EOF
353 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
353 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
354 > from __future__ import absolute_import
354 > from __future__ import absolute_import
355 > from extlibroot.shadowing.used import detail
355 > from extlibroot.shadowing.used import detail
356 > EOF
356 > EOF
357 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
357 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
358 > from __future__ import absolute_import
358 > from __future__ import absolute_import
359 > from .used import detail as used
359 > from .used import detail as used
360 > EOF
360 > EOF
361
361
362 Setup extension local modules to be imported with "absolute_import"
362 Setup extension local modules to be imported with "absolute_import"
363 feature.
363 feature.
364
364
365 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
365 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
366 $ touch $TESTTMP/absextroot/xsub1/__init__.py
366 $ touch $TESTTMP/absextroot/xsub1/__init__.py
367 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
367 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
368
368
369 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
369 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
370 > def func():
370 > def func():
371 > return "this is absextroot.xsub1.xsub2.called.func()"
371 > return "this is absextroot.xsub1.xsub2.called.func()"
372 > EOF
372 > EOF
373 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
373 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
374 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
374 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
375 > EOF
375 > EOF
376 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
376 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
377 > detail = "this is absextroot.xsub1.xsub2.used"
377 > detail = "this is absextroot.xsub1.xsub2.used"
378 > EOF
378 > EOF
379
379
380 Setup extension local modules to examine whether demand importing
380 Setup extension local modules to examine whether demand importing
381 works as expected in "level > 1" case.
381 works as expected in "level > 1" case.
382
382
383 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
383 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
384 > detail = "this is absextroot.relimportee"
384 > detail = "this is absextroot.relimportee"
385 > EOF
385 > EOF
386 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
386 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
387 > from __future__ import absolute_import
387 > from __future__ import absolute_import
388 > from ... import relimportee
388 > from ... import relimportee
389 > detail = "this relimporter imports %r" % (relimportee.detail)
389 > detail = "this relimporter imports %r" % (relimportee.detail)
390 > EOF
390 > EOF
391
391
392 Setup modules, which actually import extension local modules at
392 Setup modules, which actually import extension local modules at
393 runtime.
393 runtime.
394
394
395 $ cat > $TESTTMP/absextroot/absolute.py << EOF
395 $ cat > $TESTTMP/absextroot/absolute.py << EOF
396 > from __future__ import absolute_import
396 > from __future__ import absolute_import
397 >
397 >
398 > # import extension local modules absolutely (level = 0)
398 > # import extension local modules absolutely (level = 0)
399 > from absextroot.xsub1.xsub2 import used, unused
399 > from absextroot.xsub1.xsub2 import used, unused
400 > from absextroot.xsub1.xsub2.called import func
400 > from absextroot.xsub1.xsub2.called import func
401 >
401 >
402 > def getresult():
402 > def getresult():
403 > result = []
403 > result = []
404 > result.append(used.detail)
404 > result.append(used.detail)
405 > result.append(func())
405 > result.append(func())
406 > return result
406 > return result
407 > EOF
407 > EOF
408
408
409 $ cat > $TESTTMP/absextroot/relative.py << EOF
409 $ cat > $TESTTMP/absextroot/relative.py << EOF
410 > from __future__ import absolute_import
410 > from __future__ import absolute_import
411 >
411 >
412 > # import extension local modules relatively (level == 1)
412 > # import extension local modules relatively (level == 1)
413 > from .xsub1.xsub2 import used, unused
413 > from .xsub1.xsub2 import used, unused
414 > from .xsub1.xsub2.called import func
414 > from .xsub1.xsub2.called import func
415 >
415 >
416 > # import a module, which implies "importing with level > 1"
416 > # import a module, which implies "importing with level > 1"
417 > from .xsub1.xsub2 import relimporter
417 > from .xsub1.xsub2 import relimporter
418 >
418 >
419 > def getresult():
419 > def getresult():
420 > result = []
420 > result = []
421 > result.append(used.detail)
421 > result.append(used.detail)
422 > result.append(func())
422 > result.append(func())
423 > result.append(relimporter.detail)
423 > result.append(relimporter.detail)
424 > return result
424 > return result
425 > EOF
425 > EOF
426
426
427 Setup main procedure of extension.
427 Setup main procedure of extension.
428
428
429 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
429 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
430 > from __future__ import absolute_import
430 > from __future__ import absolute_import
431 > from mercurial import registrar
431 > from mercurial import registrar
432 > cmdtable = {}
432 > cmdtable = {}
433 > command = registrar.command(cmdtable)
433 > command = registrar.command(cmdtable)
434 >
434 >
435 > # "absolute" and "relative" shouldn't be imported before actual
435 > # "absolute" and "relative" shouldn't be imported before actual
436 > # command execution, because (1) they import same modules, and (2)
436 > # command execution, because (1) they import same modules, and (2)
437 > # preceding import (= instantiate "demandmod" object instead of
437 > # preceding import (= instantiate "demandmod" object instead of
438 > # real "module" object) might hide problem of succeeding import.
438 > # real "module" object) might hide problem of succeeding import.
439 >
439 >
440 > @command(b'showabsolute', [], norepo=True)
440 > @command(b'showabsolute', [], norepo=True)
441 > def showabsolute(ui, *args, **opts):
441 > def showabsolute(ui, *args, **opts):
442 > from absextroot import absolute
442 > from absextroot import absolute
443 > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
443 > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
444 >
444 >
445 > @command(b'showrelative', [], norepo=True)
445 > @command(b'showrelative', [], norepo=True)
446 > def showrelative(ui, *args, **opts):
446 > def showrelative(ui, *args, **opts):
447 > from . import relative
447 > from . import relative
448 > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
448 > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
449 >
449 >
450 > # import modules from external library
450 > # import modules from external library
451 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
451 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
452 > from extlibroot.lsub1.lsub2.called import func as lfunc
452 > from extlibroot.lsub1.lsub2.called import func as lfunc
453 > from extlibroot.recursedown import absdetail, legacydetail
453 > from extlibroot.recursedown import absdetail, legacydetail
454 > from extlibroot.shadowing import proxied
454 > from extlibroot.shadowing import proxied
455 >
455 >
456 > def uisetup(ui):
456 > def uisetup(ui):
457 > result = []
457 > result = []
458 > result.append(lused.detail)
458 > result.append(lused.detail)
459 > result.append(lfunc())
459 > result.append(lfunc())
460 > result.append(absdetail)
460 > result.append(absdetail)
461 > result.append(legacydetail)
461 > result.append(legacydetail)
462 > result.append(proxied.detail)
462 > result.append(proxied.detail)
463 > ui.write(b'LIB: %s\n' % '\nLIB: '.join(result))
463 > ui.write(b'LIB: %s\n' % '\nLIB: '.join(result))
464 > EOF
464 > EOF
465
465
466 Examine module importing.
466 Examine module importing.
467
467
468 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
468 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
469 LIB: this is extlibroot.lsub1.lsub2.used
469 LIB: this is extlibroot.lsub1.lsub2.used
470 LIB: this is extlibroot.lsub1.lsub2.called.func()
470 LIB: this is extlibroot.lsub1.lsub2.called.func()
471 LIB: this is extlibroot.recursedown.abs.used
471 LIB: this is extlibroot.recursedown.abs.used
472 LIB: this is extlibroot.recursedown.legacy.used
472 LIB: this is extlibroot.recursedown.legacy.used
473 LIB: this is extlibroot.shadowing.used
473 LIB: this is extlibroot.shadowing.used
474 ABS: this is absextroot.xsub1.xsub2.used
474 ABS: this is absextroot.xsub1.xsub2.used
475 ABS: this is absextroot.xsub1.xsub2.called.func()
475 ABS: this is absextroot.xsub1.xsub2.called.func()
476
476
477 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
477 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
478 LIB: this is extlibroot.lsub1.lsub2.used
478 LIB: this is extlibroot.lsub1.lsub2.used
479 LIB: this is extlibroot.lsub1.lsub2.called.func()
479 LIB: this is extlibroot.lsub1.lsub2.called.func()
480 LIB: this is extlibroot.recursedown.abs.used
480 LIB: this is extlibroot.recursedown.abs.used
481 LIB: this is extlibroot.recursedown.legacy.used
481 LIB: this is extlibroot.recursedown.legacy.used
482 LIB: this is extlibroot.shadowing.used
482 LIB: this is extlibroot.shadowing.used
483 REL: this is absextroot.xsub1.xsub2.used
483 REL: this is absextroot.xsub1.xsub2.used
484 REL: this is absextroot.xsub1.xsub2.called.func()
484 REL: this is absextroot.xsub1.xsub2.called.func()
485 REL: this relimporter imports 'this is absextroot.relimportee'
485 REL: this relimporter imports 'this is absextroot.relimportee'
486
486
487 Examine whether sub-module is imported relatively as expected.
487 Examine whether sub-module is imported relatively as expected.
488
488
489 See also issue5208 for detail about example case on Python 3.x.
489 See also issue5208 for detail about example case on Python 3.x.
490
490
491 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
491 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
492 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
492 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
493
493
494 $ cat > $TESTTMP/notexist.py <<EOF
494 $ cat > $TESTTMP/notexist.py <<EOF
495 > text = 'notexist.py at root is loaded unintentionally\n'
495 > text = 'notexist.py at root is loaded unintentionally\n'
496 > EOF
496 > EOF
497
497
498 $ cat > $TESTTMP/checkrelativity.py <<EOF
498 $ cat > $TESTTMP/checkrelativity.py <<EOF
499 > from mercurial import registrar
499 > from mercurial import registrar
500 > cmdtable = {}
500 > cmdtable = {}
501 > command = registrar.command(cmdtable)
501 > command = registrar.command(cmdtable)
502 >
502 >
503 > # demand import avoids failure of importing notexist here
503 > # demand import avoids failure of importing notexist here
504 > import extlibroot.lsub1.lsub2.notexist
504 > import extlibroot.lsub1.lsub2.notexist
505 >
505 >
506 > @command(b'checkrelativity', [], norepo=True)
506 > @command(b'checkrelativity', [], norepo=True)
507 > def checkrelativity(ui, *args, **opts):
507 > def checkrelativity(ui, *args, **opts):
508 > try:
508 > try:
509 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
509 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
510 > return 1 # unintentional success
510 > return 1 # unintentional success
511 > except ImportError:
511 > except ImportError:
512 > pass # intentional failure
512 > pass # intentional failure
513 > EOF
513 > EOF
514
514
515 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
515 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
516
516
517 #endif
517 #endif
518
518
519 Make sure a broken uisetup doesn't globally break hg:
519 Make sure a broken uisetup doesn't globally break hg:
520 $ cat > $TESTTMP/baduisetup.py <<EOF
520 $ cat > $TESTTMP/baduisetup.py <<EOF
521 > def uisetup(ui):
521 > def uisetup(ui):
522 > 1/0
522 > 1/0
523 > EOF
523 > EOF
524
524
525 Even though the extension fails during uisetup, hg is still basically usable:
525 Even though the extension fails during uisetup, hg is still basically usable:
526 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
526 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
527 Traceback (most recent call last):
527 Traceback (most recent call last):
528 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
528 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
529 uisetup(ui)
529 uisetup(ui)
530 File "$TESTTMP/baduisetup.py", line 2, in uisetup
530 File "$TESTTMP/baduisetup.py", line 2, in uisetup
531 1/0
531 1/0
532 ZeroDivisionError: integer division or modulo by zero
532 ZeroDivisionError: integer division or modulo by zero
533 *** failed to set up extension baduisetup: integer division or modulo by zero
533 *** failed to set up extension baduisetup: integer division or modulo by zero
534 Mercurial Distributed SCM (version *) (glob)
534 Mercurial Distributed SCM (version *) (glob)
535 (see https://mercurial-scm.org for more information)
535 (see https://mercurial-scm.org for more information)
536
536
537 Copyright (C) 2005-* Matt Mackall and others (glob)
537 Copyright (C) 2005-* Matt Mackall and others (glob)
538 This is free software; see the source for copying conditions. There is NO
538 This is free software; see the source for copying conditions. There is NO
539 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
539 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
540
540
541 $ cd ..
541 $ cd ..
542
542
543 hide outer repo
543 hide outer repo
544 $ hg init
544 $ hg init
545
545
546 $ cat > empty.py <<EOF
546 $ cat > empty.py <<EOF
547 > '''empty cmdtable
547 > '''empty cmdtable
548 > '''
548 > '''
549 > cmdtable = {}
549 > cmdtable = {}
550 > EOF
550 > EOF
551 $ emptypath=`pwd`/empty.py
551 $ emptypath=`pwd`/empty.py
552 $ echo "empty = $emptypath" >> $HGRCPATH
552 $ echo "empty = $emptypath" >> $HGRCPATH
553 $ hg help empty
553 $ hg help empty
554 empty extension - empty cmdtable
554 empty extension - empty cmdtable
555
555
556 no commands defined
556 no commands defined
557
557
558
558
559 $ echo 'empty = !' >> $HGRCPATH
559 $ echo 'empty = !' >> $HGRCPATH
560
560
561 $ cat > debugextension.py <<EOF
561 $ cat > debugextension.py <<EOF
562 > '''only debugcommands
562 > '''only debugcommands
563 > '''
563 > '''
564 > from mercurial import registrar
564 > from mercurial import registrar
565 > cmdtable = {}
565 > cmdtable = {}
566 > command = registrar.command(cmdtable)
566 > command = registrar.command(cmdtable)
567 > @command(b'debugfoobar', [], b'hg debugfoobar')
567 > @command(b'debugfoobar', [], b'hg debugfoobar')
568 > def debugfoobar(ui, repo, *args, **opts):
568 > def debugfoobar(ui, repo, *args, **opts):
569 > "yet another debug command"
569 > "yet another debug command"
570 > pass
570 > pass
571 > @command(b'foo', [], b'hg foo')
571 > @command(b'foo', [], b'hg foo')
572 > def foo(ui, repo, *args, **opts):
572 > def foo(ui, repo, *args, **opts):
573 > """yet another foo command
573 > """yet another foo command
574 > This command has been DEPRECATED since forever.
574 > This command has been DEPRECATED since forever.
575 > """
575 > """
576 > pass
576 > pass
577 > EOF
577 > EOF
578 $ debugpath=`pwd`/debugextension.py
578 $ debugpath=`pwd`/debugextension.py
579 $ echo "debugextension = $debugpath" >> $HGRCPATH
579 $ echo "debugextension = $debugpath" >> $HGRCPATH
580
580
581 $ hg help debugextension
581 $ hg help debugextension
582 hg debugextensions
582 hg debugextensions
583
583
584 show information about active extensions
584 show information about active extensions
585
585
586 options:
586 options:
587
587
588 (some details hidden, use --verbose to show complete help)
588 (some details hidden, use --verbose to show complete help)
589
589
590
590
591 $ hg --verbose help debugextension
591 $ hg --verbose help debugextension
592 hg debugextensions
592 hg debugextensions
593
593
594 show information about active extensions
594 show information about active extensions
595
595
596 options:
596 options:
597
597
598 -T --template TEMPLATE display with template (EXPERIMENTAL)
598 -T --template TEMPLATE display with template (EXPERIMENTAL)
599
599
600 global options ([+] can be repeated):
600 global options ([+] can be repeated):
601
601
602 -R --repository REPO repository root directory or name of overlay bundle
602 -R --repository REPO repository root directory or name of overlay bundle
603 file
603 file
604 --cwd DIR change working directory
604 --cwd DIR change working directory
605 -y --noninteractive do not prompt, automatically pick the first choice for
605 -y --noninteractive do not prompt, automatically pick the first choice for
606 all prompts
606 all prompts
607 -q --quiet suppress output
607 -q --quiet suppress output
608 -v --verbose enable additional output
608 -v --verbose enable additional output
609 --color TYPE when to colorize (boolean, always, auto, never, or
609 --color TYPE when to colorize (boolean, always, auto, never, or
610 debug)
610 debug)
611 --config CONFIG [+] set/override config option (use 'section.name=value')
611 --config CONFIG [+] set/override config option (use 'section.name=value')
612 --debug enable debugging output
612 --debug enable debugging output
613 --debugger start debugger
613 --debugger start debugger
614 --encoding ENCODE set the charset encoding (default: ascii)
614 --encoding ENCODE set the charset encoding (default: ascii)
615 --encodingmode MODE set the charset encoding mode (default: strict)
615 --encodingmode MODE set the charset encoding mode (default: strict)
616 --traceback always print a traceback on exception
616 --traceback always print a traceback on exception
617 --time time how long the command takes
617 --time time how long the command takes
618 --profile print command execution profile
618 --profile print command execution profile
619 --version output version information and exit
619 --version output version information and exit
620 -h --help display help and exit
620 -h --help display help and exit
621 --hidden consider hidden changesets
621 --hidden consider hidden changesets
622 --pager TYPE when to paginate (boolean, always, auto, or never)
622 --pager TYPE when to paginate (boolean, always, auto, or never)
623 (default: auto)
623 (default: auto)
624
624
625
625
626
626
627
627
628
628
629
629
630 $ hg --debug help debugextension
630 $ hg --debug help debugextension
631 hg debugextensions
631 hg debugextensions
632
632
633 show information about active extensions
633 show information about active extensions
634
634
635 options:
635 options:
636
636
637 -T --template TEMPLATE display with template (EXPERIMENTAL)
637 -T --template TEMPLATE display with template (EXPERIMENTAL)
638
638
639 global options ([+] can be repeated):
639 global options ([+] can be repeated):
640
640
641 -R --repository REPO repository root directory or name of overlay bundle
641 -R --repository REPO repository root directory or name of overlay bundle
642 file
642 file
643 --cwd DIR change working directory
643 --cwd DIR change working directory
644 -y --noninteractive do not prompt, automatically pick the first choice for
644 -y --noninteractive do not prompt, automatically pick the first choice for
645 all prompts
645 all prompts
646 -q --quiet suppress output
646 -q --quiet suppress output
647 -v --verbose enable additional output
647 -v --verbose enable additional output
648 --color TYPE when to colorize (boolean, always, auto, never, or
648 --color TYPE when to colorize (boolean, always, auto, never, or
649 debug)
649 debug)
650 --config CONFIG [+] set/override config option (use 'section.name=value')
650 --config CONFIG [+] set/override config option (use 'section.name=value')
651 --debug enable debugging output
651 --debug enable debugging output
652 --debugger start debugger
652 --debugger start debugger
653 --encoding ENCODE set the charset encoding (default: ascii)
653 --encoding ENCODE set the charset encoding (default: ascii)
654 --encodingmode MODE set the charset encoding mode (default: strict)
654 --encodingmode MODE set the charset encoding mode (default: strict)
655 --traceback always print a traceback on exception
655 --traceback always print a traceback on exception
656 --time time how long the command takes
656 --time time how long the command takes
657 --profile print command execution profile
657 --profile print command execution profile
658 --version output version information and exit
658 --version output version information and exit
659 -h --help display help and exit
659 -h --help display help and exit
660 --hidden consider hidden changesets
660 --hidden consider hidden changesets
661 --pager TYPE when to paginate (boolean, always, auto, or never)
661 --pager TYPE when to paginate (boolean, always, auto, or never)
662 (default: auto)
662 (default: auto)
663
663
664
664
665
665
666
666
667
667
668 $ echo 'debugextension = !' >> $HGRCPATH
668 $ echo 'debugextension = !' >> $HGRCPATH
669
669
670 Asking for help about a deprecated extension should do something useful:
670 Asking for help about a deprecated extension should do something useful:
671
671
672 $ hg help glog
672 $ hg help glog
673 'glog' is provided by the following extension:
673 'glog' is provided by the following extension:
674
674
675 graphlog command to view revision graphs from a shell (DEPRECATED)
675 graphlog command to view revision graphs from a shell (DEPRECATED)
676
676
677 (use 'hg help extensions' for information on enabling extensions)
677 (use 'hg help extensions' for information on enabling extensions)
678
678
679 Extension module help vs command help:
679 Extension module help vs command help:
680
680
681 $ echo 'extdiff =' >> $HGRCPATH
681 $ echo 'extdiff =' >> $HGRCPATH
682 $ hg help extdiff
682 $ hg help extdiff
683 hg extdiff [OPT]... [FILE]...
683 hg extdiff [OPT]... [FILE]...
684
684
685 use external program to diff repository (or selected files)
685 use external program to diff repository (or selected files)
686
686
687 Show differences between revisions for the specified files, using an
687 Show differences between revisions for the specified files, using an
688 external program. The default program used is diff, with default options
688 external program. The default program used is diff, with default options
689 "-Npru".
689 "-Npru".
690
690
691 To select a different program, use the -p/--program option. The program
691 To select a different program, use the -p/--program option. The program
692 will be passed the names of two directories to compare. To pass additional
692 will be passed the names of two directories to compare. To pass additional
693 options to the program, use -o/--option. These will be passed before the
693 options to the program, use -o/--option. These will be passed before the
694 names of the directories to compare.
694 names of the directories to compare.
695
695
696 When two revision arguments are given, then changes are shown between
696 When two revision arguments are given, then changes are shown between
697 those revisions. If only one revision is specified then that revision is
697 those revisions. If only one revision is specified then that revision is
698 compared to the working directory, and, when no revisions are specified,
698 compared to the working directory, and, when no revisions are specified,
699 the working directory files are compared to its parent.
699 the working directory files are compared to its parent.
700
700
701 (use 'hg help -e extdiff' to show help for the extdiff extension)
701 (use 'hg help -e extdiff' to show help for the extdiff extension)
702
702
703 options ([+] can be repeated):
703 options ([+] can be repeated):
704
704
705 -p --program CMD comparison program to run
705 -p --program CMD comparison program to run
706 -o --option OPT [+] pass option to comparison program
706 -o --option OPT [+] pass option to comparison program
707 -r --rev REV [+] revision
707 -r --rev REV [+] revision
708 -c --change REV change made by revision
708 -c --change REV change made by revision
709 --patch compare patches for two revisions
709 --patch compare patches for two revisions
710 -I --include PATTERN [+] include names matching the given patterns
710 -I --include PATTERN [+] include names matching the given patterns
711 -X --exclude PATTERN [+] exclude names matching the given patterns
711 -X --exclude PATTERN [+] exclude names matching the given patterns
712 -S --subrepos recurse into subrepositories
712 -S --subrepos recurse into subrepositories
713
713
714 (some details hidden, use --verbose to show complete help)
714 (some details hidden, use --verbose to show complete help)
715
715
716
716
717
717
718
718
719
719
720
720
721
721
722
722
723
723
724
724
725 $ hg help --extension extdiff
725 $ hg help --extension extdiff
726 extdiff extension - command to allow external programs to compare revisions
726 extdiff extension - command to allow external programs to compare revisions
727
727
728 The extdiff Mercurial extension allows you to use external programs to compare
728 The extdiff Mercurial extension allows you to use external programs to compare
729 revisions, or revision with working directory. The external diff programs are
729 revisions, or revision with working directory. The external diff programs are
730 called with a configurable set of options and two non-option arguments: paths
730 called with a configurable set of options and two non-option arguments: paths
731 to directories containing snapshots of files to compare.
731 to directories containing snapshots of files to compare.
732
732
733 If there is more than one file being compared and the "child" revision is the
733 If there is more than one file being compared and the "child" revision is the
734 working directory, any modifications made in the external diff program will be
734 working directory, any modifications made in the external diff program will be
735 copied back to the working directory from the temporary directory.
735 copied back to the working directory from the temporary directory.
736
736
737 The extdiff extension also allows you to configure new diff commands, so you
737 The extdiff extension also allows you to configure new diff commands, so you
738 do not need to type 'hg extdiff -p kdiff3' always.
738 do not need to type 'hg extdiff -p kdiff3' always.
739
739
740 [extdiff]
740 [extdiff]
741 # add new command that runs GNU diff(1) in 'context diff' mode
741 # add new command that runs GNU diff(1) in 'context diff' mode
742 cdiff = gdiff -Nprc5
742 cdiff = gdiff -Nprc5
743 ## or the old way:
743 ## or the old way:
744 #cmd.cdiff = gdiff
744 #cmd.cdiff = gdiff
745 #opts.cdiff = -Nprc5
745 #opts.cdiff = -Nprc5
746
746
747 # add new command called meld, runs meld (no need to name twice). If
747 # add new command called meld, runs meld (no need to name twice). If
748 # the meld executable is not available, the meld tool in [merge-tools]
748 # the meld executable is not available, the meld tool in [merge-tools]
749 # will be used, if available
749 # will be used, if available
750 meld =
750 meld =
751
751
752 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
752 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
753 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
753 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
754 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
754 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
755 # your .vimrc
755 # your .vimrc
756 vimdiff = gvim -f "+next" \
756 vimdiff = gvim -f "+next" \
757 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
757 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
758
758
759 Tool arguments can include variables that are expanded at runtime:
759 Tool arguments can include variables that are expanded at runtime:
760
760
761 $parent1, $plabel1 - filename, descriptive label of first parent
761 $parent1, $plabel1 - filename, descriptive label of first parent
762 $child, $clabel - filename, descriptive label of child revision
762 $child, $clabel - filename, descriptive label of child revision
763 $parent2, $plabel2 - filename, descriptive label of second parent
763 $parent2, $plabel2 - filename, descriptive label of second parent
764 $root - repository root
764 $root - repository root
765 $parent is an alias for $parent1.
765 $parent is an alias for $parent1.
766
766
767 The extdiff extension will look in your [diff-tools] and [merge-tools]
767 The extdiff extension will look in your [diff-tools] and [merge-tools]
768 sections for diff tool arguments, when none are specified in [extdiff].
768 sections for diff tool arguments, when none are specified in [extdiff].
769
769
770 [extdiff]
770 [extdiff]
771 kdiff3 =
771 kdiff3 =
772
772
773 [diff-tools]
773 [diff-tools]
774 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
774 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
775
775
776 You can use -I/-X and list of file or directory names like normal 'hg diff'
776 You can use -I/-X and list of file or directory names like normal 'hg diff'
777 command. The extdiff extension makes snapshots of only needed files, so
777 command. The extdiff extension makes snapshots of only needed files, so
778 running the external diff program will actually be pretty fast (at least
778 running the external diff program will actually be pretty fast (at least
779 faster than having to compare the entire tree).
779 faster than having to compare the entire tree).
780
780
781 list of commands:
781 list of commands:
782
782
783 extdiff use external program to diff repository (or selected files)
783 extdiff use external program to diff repository (or selected files)
784
784
785 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
785 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
786
786
787
787
788
788
789
789
790
790
791
791
792
792
793
793
794
794
795
795
796
796
797
797
798
798
799
799
800
800
801
801
802 $ echo 'extdiff = !' >> $HGRCPATH
802 $ echo 'extdiff = !' >> $HGRCPATH
803
803
804 Test help topic with same name as extension
804 Test help topic with same name as extension
805
805
806 $ cat > multirevs.py <<EOF
806 $ cat > multirevs.py <<EOF
807 > from mercurial import commands, registrar
807 > from mercurial import commands, registrar
808 > cmdtable = {}
808 > cmdtable = {}
809 > command = registrar.command(cmdtable)
809 > command = registrar.command(cmdtable)
810 > """multirevs extension
810 > """multirevs extension
811 > Big multi-line module docstring."""
811 > Big multi-line module docstring."""
812 > @command(b'multirevs', [], b'ARG', norepo=True)
812 > @command(b'multirevs', [], b'ARG', norepo=True)
813 > def multirevs(ui, repo, arg, *args, **opts):
813 > def multirevs(ui, repo, arg, *args, **opts):
814 > """multirevs command"""
814 > """multirevs command"""
815 > pass
815 > pass
816 > EOF
816 > EOF
817 $ echo "multirevs = multirevs.py" >> $HGRCPATH
817 $ echo "multirevs = multirevs.py" >> $HGRCPATH
818
818
819 $ hg help multirevs | tail
819 $ hg help multirevs | tail
820 used):
820 used):
821
821
822 hg update :@
822 hg update :@
823
823
824 - Show diff between tags 1.3 and 1.5 (this works because the first and the
824 - Show diff between tags 1.3 and 1.5 (this works because the first and the
825 last revisions of the revset are used):
825 last revisions of the revset are used):
826
826
827 hg diff -r 1.3::1.5
827 hg diff -r 1.3::1.5
828
828
829 use 'hg help -c multirevs' to see help for the multirevs command
829 use 'hg help -c multirevs' to see help for the multirevs command
830
830
831
831
832
832
833
833
834
834
835
835
836 $ hg help -c multirevs
836 $ hg help -c multirevs
837 hg multirevs ARG
837 hg multirevs ARG
838
838
839 multirevs command
839 multirevs command
840
840
841 (some details hidden, use --verbose to show complete help)
841 (some details hidden, use --verbose to show complete help)
842
842
843
843
844
844
845 $ hg multirevs
845 $ hg multirevs
846 hg multirevs: invalid arguments
846 hg multirevs: invalid arguments
847 hg multirevs ARG
847 hg multirevs ARG
848
848
849 multirevs command
849 multirevs command
850
850
851 (use 'hg multirevs -h' to show more help)
851 (use 'hg multirevs -h' to show more help)
852 [255]
852 [255]
853
853
854
854
855
855
856 $ echo "multirevs = !" >> $HGRCPATH
856 $ echo "multirevs = !" >> $HGRCPATH
857
857
858 Issue811: Problem loading extensions twice (by site and by user)
858 Issue811: Problem loading extensions twice (by site and by user)
859
859
860 $ cat <<EOF >> $HGRCPATH
860 $ cat <<EOF >> $HGRCPATH
861 > mq =
861 > mq =
862 > strip =
862 > strip =
863 > hgext.mq =
863 > hgext.mq =
864 > hgext/mq =
864 > hgext/mq =
865 > EOF
865 > EOF
866
866
867 Show extensions:
867 Show extensions:
868 (note that mq force load strip, also checking it's not loaded twice)
868 (note that mq force load strip, also checking it's not loaded twice)
869
869
870 #if no-extraextensions
870 #if no-extraextensions
871 $ hg debugextensions
871 $ hg debugextensions
872 mq
872 mq
873 strip
873 strip
874 #endif
874 #endif
875
875
876 For extensions, which name matches one of its commands, help
876 For extensions, which name matches one of its commands, help
877 message should ask '-v -e' to get list of built-in aliases
877 message should ask '-v -e' to get list of built-in aliases
878 along with extension help itself
878 along with extension help itself
879
879
880 $ mkdir $TESTTMP/d
880 $ mkdir $TESTTMP/d
881 $ cat > $TESTTMP/d/dodo.py <<EOF
881 $ cat > $TESTTMP/d/dodo.py <<EOF
882 > """
882 > """
883 > This is an awesome 'dodo' extension. It does nothing and
883 > This is an awesome 'dodo' extension. It does nothing and
884 > writes 'Foo foo'
884 > writes 'Foo foo'
885 > """
885 > """
886 > from mercurial import commands, registrar
886 > from mercurial import commands, registrar
887 > cmdtable = {}
887 > cmdtable = {}
888 > command = registrar.command(cmdtable)
888 > command = registrar.command(cmdtable)
889 > @command(b'dodo', [], b'hg dodo')
889 > @command(b'dodo', [], b'hg dodo')
890 > def dodo(ui, *args, **kwargs):
890 > def dodo(ui, *args, **kwargs):
891 > """Does nothing"""
891 > """Does nothing"""
892 > ui.write(b"I do nothing. Yay\\n")
892 > ui.write(b"I do nothing. Yay\\n")
893 > @command(b'foofoo', [], b'hg foofoo')
893 > @command(b'foofoo', [], b'hg foofoo')
894 > def foofoo(ui, *args, **kwargs):
894 > def foofoo(ui, *args, **kwargs):
895 > """Writes 'Foo foo'"""
895 > """Writes 'Foo foo'"""
896 > ui.write(b"Foo foo\\n")
896 > ui.write(b"Foo foo\\n")
897 > EOF
897 > EOF
898 $ dodopath=$TESTTMP/d/dodo.py
898 $ dodopath=$TESTTMP/d/dodo.py
899
899
900 $ echo "dodo = $dodopath" >> $HGRCPATH
900 $ echo "dodo = $dodopath" >> $HGRCPATH
901
901
902 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
902 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
903 $ hg help -e dodo
903 $ hg help -e dodo
904 dodo extension -
904 dodo extension -
905
905
906 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
906 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
907
907
908 list of commands:
908 list of commands:
909
909
910 dodo Does nothing
910 dodo Does nothing
911 foofoo Writes 'Foo foo'
911 foofoo Writes 'Foo foo'
912
912
913 (use 'hg help -v -e dodo' to show built-in aliases and global options)
913 (use 'hg help -v -e dodo' to show built-in aliases and global options)
914
914
915 Make sure that '-v -e' prints list of built-in aliases along with
915 Make sure that '-v -e' prints list of built-in aliases along with
916 extension help itself
916 extension help itself
917 $ hg help -v -e dodo
917 $ hg help -v -e dodo
918 dodo extension -
918 dodo extension -
919
919
920 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
920 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
921
921
922 list of commands:
922 list of commands:
923
923
924 dodo Does nothing
924 dodo Does nothing
925 foofoo Writes 'Foo foo'
925 foofoo Writes 'Foo foo'
926
926
927 global options ([+] can be repeated):
927 global options ([+] can be repeated):
928
928
929 -R --repository REPO repository root directory or name of overlay bundle
929 -R --repository REPO repository root directory or name of overlay bundle
930 file
930 file
931 --cwd DIR change working directory
931 --cwd DIR change working directory
932 -y --noninteractive do not prompt, automatically pick the first choice for
932 -y --noninteractive do not prompt, automatically pick the first choice for
933 all prompts
933 all prompts
934 -q --quiet suppress output
934 -q --quiet suppress output
935 -v --verbose enable additional output
935 -v --verbose enable additional output
936 --color TYPE when to colorize (boolean, always, auto, never, or
936 --color TYPE when to colorize (boolean, always, auto, never, or
937 debug)
937 debug)
938 --config CONFIG [+] set/override config option (use 'section.name=value')
938 --config CONFIG [+] set/override config option (use 'section.name=value')
939 --debug enable debugging output
939 --debug enable debugging output
940 --debugger start debugger
940 --debugger start debugger
941 --encoding ENCODE set the charset encoding (default: ascii)
941 --encoding ENCODE set the charset encoding (default: ascii)
942 --encodingmode MODE set the charset encoding mode (default: strict)
942 --encodingmode MODE set the charset encoding mode (default: strict)
943 --traceback always print a traceback on exception
943 --traceback always print a traceback on exception
944 --time time how long the command takes
944 --time time how long the command takes
945 --profile print command execution profile
945 --profile print command execution profile
946 --version output version information and exit
946 --version output version information and exit
947 -h --help display help and exit
947 -h --help display help and exit
948 --hidden consider hidden changesets
948 --hidden consider hidden changesets
949 --pager TYPE when to paginate (boolean, always, auto, or never)
949 --pager TYPE when to paginate (boolean, always, auto, or never)
950 (default: auto)
950 (default: auto)
951
951
952 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
952 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
953 $ hg help -v dodo
953 $ hg help -v dodo
954 hg dodo
954 hg dodo
955
955
956 Does nothing
956 Does nothing
957
957
958 (use 'hg help -e dodo' to show help for the dodo extension)
958 (use 'hg help -e dodo' to show help for the dodo extension)
959
959
960 options:
960 options:
961
961
962 --mq operate on patch repository
962 --mq operate on patch repository
963
963
964 global options ([+] can be repeated):
964 global options ([+] can be repeated):
965
965
966 -R --repository REPO repository root directory or name of overlay bundle
966 -R --repository REPO repository root directory or name of overlay bundle
967 file
967 file
968 --cwd DIR change working directory
968 --cwd DIR change working directory
969 -y --noninteractive do not prompt, automatically pick the first choice for
969 -y --noninteractive do not prompt, automatically pick the first choice for
970 all prompts
970 all prompts
971 -q --quiet suppress output
971 -q --quiet suppress output
972 -v --verbose enable additional output
972 -v --verbose enable additional output
973 --color TYPE when to colorize (boolean, always, auto, never, or
973 --color TYPE when to colorize (boolean, always, auto, never, or
974 debug)
974 debug)
975 --config CONFIG [+] set/override config option (use 'section.name=value')
975 --config CONFIG [+] set/override config option (use 'section.name=value')
976 --debug enable debugging output
976 --debug enable debugging output
977 --debugger start debugger
977 --debugger start debugger
978 --encoding ENCODE set the charset encoding (default: ascii)
978 --encoding ENCODE set the charset encoding (default: ascii)
979 --encodingmode MODE set the charset encoding mode (default: strict)
979 --encodingmode MODE set the charset encoding mode (default: strict)
980 --traceback always print a traceback on exception
980 --traceback always print a traceback on exception
981 --time time how long the command takes
981 --time time how long the command takes
982 --profile print command execution profile
982 --profile print command execution profile
983 --version output version information and exit
983 --version output version information and exit
984 -h --help display help and exit
984 -h --help display help and exit
985 --hidden consider hidden changesets
985 --hidden consider hidden changesets
986 --pager TYPE when to paginate (boolean, always, auto, or never)
986 --pager TYPE when to paginate (boolean, always, auto, or never)
987 (default: auto)
987 (default: auto)
988
988
989 In case when extension name doesn't match any of its commands,
989 In case when extension name doesn't match any of its commands,
990 help message should ask for '-v' to get list of built-in aliases
990 help message should ask for '-v' to get list of built-in aliases
991 along with extension help
991 along with extension help
992 $ cat > $TESTTMP/d/dudu.py <<EOF
992 $ cat > $TESTTMP/d/dudu.py <<EOF
993 > """
993 > """
994 > This is an awesome 'dudu' extension. It does something and
994 > This is an awesome 'dudu' extension. It does something and
995 > also writes 'Beep beep'
995 > also writes 'Beep beep'
996 > """
996 > """
997 > from mercurial import commands, registrar
997 > from mercurial import commands, registrar
998 > cmdtable = {}
998 > cmdtable = {}
999 > command = registrar.command(cmdtable)
999 > command = registrar.command(cmdtable)
1000 > @command(b'something', [], b'hg something')
1000 > @command(b'something', [], b'hg something')
1001 > def something(ui, *args, **kwargs):
1001 > def something(ui, *args, **kwargs):
1002 > """Does something"""
1002 > """Does something"""
1003 > ui.write(b"I do something. Yaaay\\n")
1003 > ui.write(b"I do something. Yaaay\\n")
1004 > @command(b'beep', [], b'hg beep')
1004 > @command(b'beep', [], b'hg beep')
1005 > def beep(ui, *args, **kwargs):
1005 > def beep(ui, *args, **kwargs):
1006 > """Writes 'Beep beep'"""
1006 > """Writes 'Beep beep'"""
1007 > ui.write(b"Beep beep\\n")
1007 > ui.write(b"Beep beep\\n")
1008 > EOF
1008 > EOF
1009 $ dudupath=$TESTTMP/d/dudu.py
1009 $ dudupath=$TESTTMP/d/dudu.py
1010
1010
1011 $ echo "dudu = $dudupath" >> $HGRCPATH
1011 $ echo "dudu = $dudupath" >> $HGRCPATH
1012
1012
1013 $ hg help -e dudu
1013 $ hg help -e dudu
1014 dudu extension -
1014 dudu extension -
1015
1015
1016 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1016 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1017 beep'
1017 beep'
1018
1018
1019 list of commands:
1019 list of commands:
1020
1020
1021 beep Writes 'Beep beep'
1021 beep Writes 'Beep beep'
1022 something Does something
1022 something Does something
1023
1023
1024 (use 'hg help -v dudu' to show built-in aliases and global options)
1024 (use 'hg help -v dudu' to show built-in aliases and global options)
1025
1025
1026 In case when extension name doesn't match any of its commands,
1026 In case when extension name doesn't match any of its commands,
1027 help options '-v' and '-v -e' should be equivalent
1027 help options '-v' and '-v -e' should be equivalent
1028 $ hg help -v dudu
1028 $ hg help -v dudu
1029 dudu extension -
1029 dudu extension -
1030
1030
1031 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1031 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1032 beep'
1032 beep'
1033
1033
1034 list of commands:
1034 list of commands:
1035
1035
1036 beep Writes 'Beep beep'
1036 beep Writes 'Beep beep'
1037 something Does something
1037 something Does something
1038
1038
1039 global options ([+] can be repeated):
1039 global options ([+] can be repeated):
1040
1040
1041 -R --repository REPO repository root directory or name of overlay bundle
1041 -R --repository REPO repository root directory or name of overlay bundle
1042 file
1042 file
1043 --cwd DIR change working directory
1043 --cwd DIR change working directory
1044 -y --noninteractive do not prompt, automatically pick the first choice for
1044 -y --noninteractive do not prompt, automatically pick the first choice for
1045 all prompts
1045 all prompts
1046 -q --quiet suppress output
1046 -q --quiet suppress output
1047 -v --verbose enable additional output
1047 -v --verbose enable additional output
1048 --color TYPE when to colorize (boolean, always, auto, never, or
1048 --color TYPE when to colorize (boolean, always, auto, never, or
1049 debug)
1049 debug)
1050 --config CONFIG [+] set/override config option (use 'section.name=value')
1050 --config CONFIG [+] set/override config option (use 'section.name=value')
1051 --debug enable debugging output
1051 --debug enable debugging output
1052 --debugger start debugger
1052 --debugger start debugger
1053 --encoding ENCODE set the charset encoding (default: ascii)
1053 --encoding ENCODE set the charset encoding (default: ascii)
1054 --encodingmode MODE set the charset encoding mode (default: strict)
1054 --encodingmode MODE set the charset encoding mode (default: strict)
1055 --traceback always print a traceback on exception
1055 --traceback always print a traceback on exception
1056 --time time how long the command takes
1056 --time time how long the command takes
1057 --profile print command execution profile
1057 --profile print command execution profile
1058 --version output version information and exit
1058 --version output version information and exit
1059 -h --help display help and exit
1059 -h --help display help and exit
1060 --hidden consider hidden changesets
1060 --hidden consider hidden changesets
1061 --pager TYPE when to paginate (boolean, always, auto, or never)
1061 --pager TYPE when to paginate (boolean, always, auto, or never)
1062 (default: auto)
1062 (default: auto)
1063
1063
1064 $ hg help -v -e dudu
1064 $ hg help -v -e dudu
1065 dudu extension -
1065 dudu extension -
1066
1066
1067 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1067 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1068 beep'
1068 beep'
1069
1069
1070 list of commands:
1070 list of commands:
1071
1071
1072 beep Writes 'Beep beep'
1072 beep Writes 'Beep beep'
1073 something Does something
1073 something Does something
1074
1074
1075 global options ([+] can be repeated):
1075 global options ([+] can be repeated):
1076
1076
1077 -R --repository REPO repository root directory or name of overlay bundle
1077 -R --repository REPO repository root directory or name of overlay bundle
1078 file
1078 file
1079 --cwd DIR change working directory
1079 --cwd DIR change working directory
1080 -y --noninteractive do not prompt, automatically pick the first choice for
1080 -y --noninteractive do not prompt, automatically pick the first choice for
1081 all prompts
1081 all prompts
1082 -q --quiet suppress output
1082 -q --quiet suppress output
1083 -v --verbose enable additional output
1083 -v --verbose enable additional output
1084 --color TYPE when to colorize (boolean, always, auto, never, or
1084 --color TYPE when to colorize (boolean, always, auto, never, or
1085 debug)
1085 debug)
1086 --config CONFIG [+] set/override config option (use 'section.name=value')
1086 --config CONFIG [+] set/override config option (use 'section.name=value')
1087 --debug enable debugging output
1087 --debug enable debugging output
1088 --debugger start debugger
1088 --debugger start debugger
1089 --encoding ENCODE set the charset encoding (default: ascii)
1089 --encoding ENCODE set the charset encoding (default: ascii)
1090 --encodingmode MODE set the charset encoding mode (default: strict)
1090 --encodingmode MODE set the charset encoding mode (default: strict)
1091 --traceback always print a traceback on exception
1091 --traceback always print a traceback on exception
1092 --time time how long the command takes
1092 --time time how long the command takes
1093 --profile print command execution profile
1093 --profile print command execution profile
1094 --version output version information and exit
1094 --version output version information and exit
1095 -h --help display help and exit
1095 -h --help display help and exit
1096 --hidden consider hidden changesets
1096 --hidden consider hidden changesets
1097 --pager TYPE when to paginate (boolean, always, auto, or never)
1097 --pager TYPE when to paginate (boolean, always, auto, or never)
1098 (default: auto)
1098 (default: auto)
1099
1099
1100 Disabled extension commands:
1100 Disabled extension commands:
1101
1101
1102 $ ORGHGRCPATH=$HGRCPATH
1102 $ ORGHGRCPATH=$HGRCPATH
1103 $ HGRCPATH=
1103 $ HGRCPATH=
1104 $ export HGRCPATH
1104 $ export HGRCPATH
1105 $ hg help email
1105 $ hg help email
1106 'email' is provided by the following extension:
1106 'email' is provided by the following extension:
1107
1107
1108 patchbomb command to send changesets as (a series of) patch emails
1108 patchbomb command to send changesets as (a series of) patch emails
1109
1109
1110 (use 'hg help extensions' for information on enabling extensions)
1110 (use 'hg help extensions' for information on enabling extensions)
1111
1111
1112
1112
1113 $ hg qdel
1113 $ hg qdel
1114 hg: unknown command 'qdel'
1114 hg: unknown command 'qdel'
1115 'qdelete' is provided by the following extension:
1115 'qdelete' is provided by the following extension:
1116
1116
1117 mq manage a stack of patches
1117 mq manage a stack of patches
1118
1118
1119 (use 'hg help extensions' for information on enabling extensions)
1119 (use 'hg help extensions' for information on enabling extensions)
1120 [255]
1120 [255]
1121
1121
1122
1122
1123 $ hg churn
1123 $ hg churn
1124 hg: unknown command 'churn'
1124 hg: unknown command 'churn'
1125 'churn' is provided by the following extension:
1125 'churn' is provided by the following extension:
1126
1126
1127 churn command to display statistics about repository history
1127 churn command to display statistics about repository history
1128
1128
1129 (use 'hg help extensions' for information on enabling extensions)
1129 (use 'hg help extensions' for information on enabling extensions)
1130 [255]
1130 [255]
1131
1131
1132
1132
1133
1133
1134 Disabled extensions:
1134 Disabled extensions:
1135
1135
1136 $ hg help churn
1136 $ hg help churn
1137 churn extension - command to display statistics about repository history
1137 churn extension - command to display statistics about repository history
1138
1138
1139 (use 'hg help extensions' for information on enabling extensions)
1139 (use 'hg help extensions' for information on enabling extensions)
1140
1140
1141 $ hg help patchbomb
1141 $ hg help patchbomb
1142 patchbomb extension - command to send changesets as (a series of) patch emails
1142 patchbomb extension - command to send changesets as (a series of) patch emails
1143
1143
1144 The series is started off with a "[PATCH 0 of N]" introduction, which
1144 The series is started off with a "[PATCH 0 of N]" introduction, which
1145 describes the series as a whole.
1145 describes the series as a whole.
1146
1146
1147 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1147 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1148 line of the changeset description as the subject text. The message contains
1148 line of the changeset description as the subject text. The message contains
1149 two or three body parts:
1149 two or three body parts:
1150
1150
1151 - The changeset description.
1151 - The changeset description.
1152 - [Optional] The result of running diffstat on the patch.
1152 - [Optional] The result of running diffstat on the patch.
1153 - The patch itself, as generated by 'hg export'.
1153 - The patch itself, as generated by 'hg export'.
1154
1154
1155 Each message refers to the first in the series using the In-Reply-To and
1155 Each message refers to the first in the series using the In-Reply-To and
1156 References headers, so they will show up as a sequence in threaded mail and
1156 References headers, so they will show up as a sequence in threaded mail and
1157 news readers, and in mail archives.
1157 news readers, and in mail archives.
1158
1158
1159 To configure other defaults, add a section like this to your configuration
1159 To configure other defaults, add a section like this to your configuration
1160 file:
1160 file:
1161
1161
1162 [email]
1162 [email]
1163 from = My Name <my@email>
1163 from = My Name <my@email>
1164 to = recipient1, recipient2, ...
1164 to = recipient1, recipient2, ...
1165 cc = cc1, cc2, ...
1165 cc = cc1, cc2, ...
1166 bcc = bcc1, bcc2, ...
1166 bcc = bcc1, bcc2, ...
1167 reply-to = address1, address2, ...
1167 reply-to = address1, address2, ...
1168
1168
1169 Use "[patchbomb]" as configuration section name if you need to override global
1169 Use "[patchbomb]" as configuration section name if you need to override global
1170 "[email]" address settings.
1170 "[email]" address settings.
1171
1171
1172 Then you can use the 'hg email' command to mail a series of changesets as a
1172 Then you can use the 'hg email' command to mail a series of changesets as a
1173 patchbomb.
1173 patchbomb.
1174
1174
1175 You can also either configure the method option in the email section to be a
1175 You can also either configure the method option in the email section to be a
1176 sendmail compatible mailer or fill out the [smtp] section so that the
1176 sendmail compatible mailer or fill out the [smtp] section so that the
1177 patchbomb extension can automatically send patchbombs directly from the
1177 patchbomb extension can automatically send patchbombs directly from the
1178 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1178 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1179
1179
1180 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1180 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1181 supply one via configuration or the command line. You can override this to
1181 supply one via configuration or the command line. You can override this to
1182 never prompt by configuring an empty value:
1182 never prompt by configuring an empty value:
1183
1183
1184 [email]
1184 [email]
1185 cc =
1185 cc =
1186
1186
1187 You can control the default inclusion of an introduction message with the
1187 You can control the default inclusion of an introduction message with the
1188 "patchbomb.intro" configuration option. The configuration is always
1188 "patchbomb.intro" configuration option. The configuration is always
1189 overwritten by command line flags like --intro and --desc:
1189 overwritten by command line flags like --intro and --desc:
1190
1190
1191 [patchbomb]
1191 [patchbomb]
1192 intro=auto # include introduction message if more than 1 patch (default)
1192 intro=auto # include introduction message if more than 1 patch (default)
1193 intro=never # never include an introduction message
1193 intro=never # never include an introduction message
1194 intro=always # always include an introduction message
1194 intro=always # always include an introduction message
1195
1195
1196 You can specify a template for flags to be added in subject prefixes. Flags
1196 You can specify a template for flags to be added in subject prefixes. Flags
1197 specified by --flag option are exported as "{flags}" keyword:
1197 specified by --flag option are exported as "{flags}" keyword:
1198
1198
1199 [patchbomb]
1199 [patchbomb]
1200 flagtemplate = "{separate(' ',
1200 flagtemplate = "{separate(' ',
1201 ifeq(branch, 'default', '', branch|upper),
1201 ifeq(branch, 'default', '', branch|upper),
1202 flags)}"
1202 flags)}"
1203
1203
1204 You can set patchbomb to always ask for confirmation by setting
1204 You can set patchbomb to always ask for confirmation by setting
1205 "patchbomb.confirm" to true.
1205 "patchbomb.confirm" to true.
1206
1206
1207 (use 'hg help extensions' for information on enabling extensions)
1207 (use 'hg help extensions' for information on enabling extensions)
1208
1208
1209
1209
1210 Broken disabled extension and command:
1210 Broken disabled extension and command:
1211
1211
1212 $ mkdir hgext
1212 $ mkdir hgext
1213 $ echo > hgext/__init__.py
1213 $ echo > hgext/__init__.py
1214 $ cat > hgext/broken.py <<EOF
1214 $ cat > hgext/broken.py <<EOF
1215 > "broken extension'
1215 > "broken extension'
1216 > EOF
1216 > EOF
1217 $ cat > path.py <<EOF
1217 $ cat > path.py <<EOF
1218 > import os, sys
1218 > import os, sys
1219 > sys.path.insert(0, os.environ['HGEXTPATH'])
1219 > sys.path.insert(0, os.environ['HGEXTPATH'])
1220 > EOF
1220 > EOF
1221 $ HGEXTPATH=`pwd`
1221 $ HGEXTPATH=`pwd`
1222 $ export HGEXTPATH
1222 $ export HGEXTPATH
1223
1223
1224 $ hg --config extensions.path=./path.py help broken
1224 $ hg --config extensions.path=./path.py help broken
1225 broken extension - (no help text available)
1225 broken extension - (no help text available)
1226
1226
1227 (use 'hg help extensions' for information on enabling extensions)
1227 (use 'hg help extensions' for information on enabling extensions)
1228
1228
1229
1229
1230 $ cat > hgext/forest.py <<EOF
1230 $ cat > hgext/forest.py <<EOF
1231 > cmdtable = None
1231 > cmdtable = None
1232 > @command()
1233 > def f():
1234 > pass
1235 > @command(123)
1236 > def g():
1237 > pass
1232 > EOF
1238 > EOF
1233 $ hg --config extensions.path=./path.py help foo > /dev/null
1239 $ hg --config extensions.path=./path.py help foo > /dev/null
1234 warning: error finding commands in $TESTTMP/hgext/forest.py
1235 abort: no such help topic: foo
1240 abort: no such help topic: foo
1236 (try 'hg help --keyword foo')
1241 (try 'hg help --keyword foo')
1237 [255]
1242 [255]
1238
1243
1239 $ cat > throw.py <<EOF
1244 $ cat > throw.py <<EOF
1240 > from mercurial import commands, registrar, util
1245 > from mercurial import commands, registrar, util
1241 > cmdtable = {}
1246 > cmdtable = {}
1242 > command = registrar.command(cmdtable)
1247 > command = registrar.command(cmdtable)
1243 > class Bogon(Exception): pass
1248 > class Bogon(Exception): pass
1244 > @command(b'throw', [], b'hg throw', norepo=True)
1249 > @command(b'throw', [], b'hg throw', norepo=True)
1245 > def throw(ui, **opts):
1250 > def throw(ui, **opts):
1246 > """throws an exception"""
1251 > """throws an exception"""
1247 > raise Bogon()
1252 > raise Bogon()
1248 > EOF
1253 > EOF
1249
1254
1250 No declared supported version, extension complains:
1255 No declared supported version, extension complains:
1251 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1256 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1252 ** Unknown exception encountered with possibly-broken third-party extension throw
1257 ** Unknown exception encountered with possibly-broken third-party extension throw
1253 ** which supports versions unknown of Mercurial.
1258 ** which supports versions unknown of Mercurial.
1254 ** Please disable throw and try your action again.
1259 ** Please disable throw and try your action again.
1255 ** If that fixes the bug please report it to the extension author.
1260 ** If that fixes the bug please report it to the extension author.
1256 ** Python * (glob)
1261 ** Python * (glob)
1257 ** Mercurial Distributed SCM * (glob)
1262 ** Mercurial Distributed SCM * (glob)
1258 ** Extensions loaded: throw
1263 ** Extensions loaded: throw
1259
1264
1260 empty declaration of supported version, extension complains:
1265 empty declaration of supported version, extension complains:
1261 $ echo "testedwith = ''" >> throw.py
1266 $ echo "testedwith = ''" >> throw.py
1262 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1267 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1263 ** Unknown exception encountered with possibly-broken third-party extension throw
1268 ** Unknown exception encountered with possibly-broken third-party extension throw
1264 ** which supports versions unknown of Mercurial.
1269 ** which supports versions unknown of Mercurial.
1265 ** Please disable throw and try your action again.
1270 ** Please disable throw and try your action again.
1266 ** If that fixes the bug please report it to the extension author.
1271 ** If that fixes the bug please report it to the extension author.
1267 ** Python * (glob)
1272 ** Python * (glob)
1268 ** Mercurial Distributed SCM (*) (glob)
1273 ** Mercurial Distributed SCM (*) (glob)
1269 ** Extensions loaded: throw
1274 ** Extensions loaded: throw
1270
1275
1271 If the extension specifies a buglink, show that:
1276 If the extension specifies a buglink, show that:
1272 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1277 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1273 $ rm -f throw.pyc throw.pyo
1278 $ rm -f throw.pyc throw.pyo
1274 $ rm -Rf __pycache__
1279 $ rm -Rf __pycache__
1275 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1280 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1276 ** Unknown exception encountered with possibly-broken third-party extension throw
1281 ** Unknown exception encountered with possibly-broken third-party extension throw
1277 ** which supports versions unknown of Mercurial.
1282 ** which supports versions unknown of Mercurial.
1278 ** Please disable throw and try your action again.
1283 ** Please disable throw and try your action again.
1279 ** If that fixes the bug please report it to http://example.com/bts
1284 ** If that fixes the bug please report it to http://example.com/bts
1280 ** Python * (glob)
1285 ** Python * (glob)
1281 ** Mercurial Distributed SCM (*) (glob)
1286 ** Mercurial Distributed SCM (*) (glob)
1282 ** Extensions loaded: throw
1287 ** Extensions loaded: throw
1283
1288
1284 If the extensions declare outdated versions, accuse the older extension first:
1289 If the extensions declare outdated versions, accuse the older extension first:
1285 $ echo "from mercurial import util" >> older.py
1290 $ echo "from mercurial import util" >> older.py
1286 $ echo "util.version = lambda:b'2.2'" >> older.py
1291 $ echo "util.version = lambda:b'2.2'" >> older.py
1287 $ echo "testedwith = b'1.9.3'" >> older.py
1292 $ echo "testedwith = b'1.9.3'" >> older.py
1288 $ echo "testedwith = b'2.1.1'" >> throw.py
1293 $ echo "testedwith = b'2.1.1'" >> throw.py
1289 $ rm -f throw.pyc throw.pyo
1294 $ rm -f throw.pyc throw.pyo
1290 $ rm -Rf __pycache__
1295 $ rm -Rf __pycache__
1291 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1296 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1292 > throw 2>&1 | egrep '^\*\*'
1297 > throw 2>&1 | egrep '^\*\*'
1293 ** Unknown exception encountered with possibly-broken third-party extension older
1298 ** Unknown exception encountered with possibly-broken third-party extension older
1294 ** which supports versions 1.9 of Mercurial.
1299 ** which supports versions 1.9 of Mercurial.
1295 ** Please disable older and try your action again.
1300 ** Please disable older and try your action again.
1296 ** If that fixes the bug please report it to the extension author.
1301 ** If that fixes the bug please report it to the extension author.
1297 ** Python * (glob)
1302 ** Python * (glob)
1298 ** Mercurial Distributed SCM (version 2.2)
1303 ** Mercurial Distributed SCM (version 2.2)
1299 ** Extensions loaded: throw, older
1304 ** Extensions loaded: throw, older
1300
1305
1301 One extension only tested with older, one only with newer versions:
1306 One extension only tested with older, one only with newer versions:
1302 $ echo "util.version = lambda:b'2.1'" >> older.py
1307 $ echo "util.version = lambda:b'2.1'" >> older.py
1303 $ rm -f older.pyc older.pyo
1308 $ rm -f older.pyc older.pyo
1304 $ rm -Rf __pycache__
1309 $ rm -Rf __pycache__
1305 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1310 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1306 > throw 2>&1 | egrep '^\*\*'
1311 > throw 2>&1 | egrep '^\*\*'
1307 ** Unknown exception encountered with possibly-broken third-party extension older
1312 ** Unknown exception encountered with possibly-broken third-party extension older
1308 ** which supports versions 1.9 of Mercurial.
1313 ** which supports versions 1.9 of Mercurial.
1309 ** Please disable older and try your action again.
1314 ** Please disable older and try your action again.
1310 ** If that fixes the bug please report it to the extension author.
1315 ** If that fixes the bug please report it to the extension author.
1311 ** Python * (glob)
1316 ** Python * (glob)
1312 ** Mercurial Distributed SCM (version 2.1)
1317 ** Mercurial Distributed SCM (version 2.1)
1313 ** Extensions loaded: throw, older
1318 ** Extensions loaded: throw, older
1314
1319
1315 Older extension is tested with current version, the other only with newer:
1320 Older extension is tested with current version, the other only with newer:
1316 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1321 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1317 $ rm -f older.pyc older.pyo
1322 $ rm -f older.pyc older.pyo
1318 $ rm -Rf __pycache__
1323 $ rm -Rf __pycache__
1319 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1324 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1320 > throw 2>&1 | egrep '^\*\*'
1325 > throw 2>&1 | egrep '^\*\*'
1321 ** Unknown exception encountered with possibly-broken third-party extension throw
1326 ** Unknown exception encountered with possibly-broken third-party extension throw
1322 ** which supports versions 2.1 of Mercurial.
1327 ** which supports versions 2.1 of Mercurial.
1323 ** Please disable throw and try your action again.
1328 ** Please disable throw and try your action again.
1324 ** If that fixes the bug please report it to http://example.com/bts
1329 ** If that fixes the bug please report it to http://example.com/bts
1325 ** Python * (glob)
1330 ** Python * (glob)
1326 ** Mercurial Distributed SCM (version 1.9.3)
1331 ** Mercurial Distributed SCM (version 1.9.3)
1327 ** Extensions loaded: throw, older
1332 ** Extensions loaded: throw, older
1328
1333
1329 Ability to point to a different point
1334 Ability to point to a different point
1330 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1335 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1331 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1336 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1332 ** unknown exception encountered, please report by visiting
1337 ** unknown exception encountered, please report by visiting
1333 ** Your Local Goat Lenders
1338 ** Your Local Goat Lenders
1334 ** Python * (glob)
1339 ** Python * (glob)
1335 ** Mercurial Distributed SCM (*) (glob)
1340 ** Mercurial Distributed SCM (*) (glob)
1336 ** Extensions loaded: throw, older
1341 ** Extensions loaded: throw, older
1337
1342
1338 Declare the version as supporting this hg version, show regular bts link:
1343 Declare the version as supporting this hg version, show regular bts link:
1339 $ hgver=`hg debuginstall -T '{hgver}'`
1344 $ hgver=`hg debuginstall -T '{hgver}'`
1340 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1345 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1341 $ if [ -z "$hgver" ]; then
1346 $ if [ -z "$hgver" ]; then
1342 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1347 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1343 > fi
1348 > fi
1344 $ rm -f throw.pyc throw.pyo
1349 $ rm -f throw.pyc throw.pyo
1345 $ rm -Rf __pycache__
1350 $ rm -Rf __pycache__
1346 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1351 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1347 ** unknown exception encountered, please report by visiting
1352 ** unknown exception encountered, please report by visiting
1348 ** https://mercurial-scm.org/wiki/BugTracker
1353 ** https://mercurial-scm.org/wiki/BugTracker
1349 ** Python * (glob)
1354 ** Python * (glob)
1350 ** Mercurial Distributed SCM (*) (glob)
1355 ** Mercurial Distributed SCM (*) (glob)
1351 ** Extensions loaded: throw
1356 ** Extensions loaded: throw
1352
1357
1353 Patch version is ignored during compatibility check
1358 Patch version is ignored during compatibility check
1354 $ echo "testedwith = b'3.2'" >> throw.py
1359 $ echo "testedwith = b'3.2'" >> throw.py
1355 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1360 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1356 $ rm -f throw.pyc throw.pyo
1361 $ rm -f throw.pyc throw.pyo
1357 $ rm -Rf __pycache__
1362 $ rm -Rf __pycache__
1358 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1363 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1359 ** unknown exception encountered, please report by visiting
1364 ** unknown exception encountered, please report by visiting
1360 ** https://mercurial-scm.org/wiki/BugTracker
1365 ** https://mercurial-scm.org/wiki/BugTracker
1361 ** Python * (glob)
1366 ** Python * (glob)
1362 ** Mercurial Distributed SCM (*) (glob)
1367 ** Mercurial Distributed SCM (*) (glob)
1363 ** Extensions loaded: throw
1368 ** Extensions loaded: throw
1364
1369
1365 Test version number support in 'hg version':
1370 Test version number support in 'hg version':
1366 $ echo '__version__ = (1, 2, 3)' >> throw.py
1371 $ echo '__version__ = (1, 2, 3)' >> throw.py
1367 $ rm -f throw.pyc throw.pyo
1372 $ rm -f throw.pyc throw.pyo
1368 $ rm -Rf __pycache__
1373 $ rm -Rf __pycache__
1369 $ hg version -v
1374 $ hg version -v
1370 Mercurial Distributed SCM (version *) (glob)
1375 Mercurial Distributed SCM (version *) (glob)
1371 (see https://mercurial-scm.org for more information)
1376 (see https://mercurial-scm.org for more information)
1372
1377
1373 Copyright (C) 2005-* Matt Mackall and others (glob)
1378 Copyright (C) 2005-* Matt Mackall and others (glob)
1374 This is free software; see the source for copying conditions. There is NO
1379 This is free software; see the source for copying conditions. There is NO
1375 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1380 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1376
1381
1377 Enabled extensions:
1382 Enabled extensions:
1378
1383
1379
1384
1380 $ hg version -v --config extensions.throw=throw.py
1385 $ hg version -v --config extensions.throw=throw.py
1381 Mercurial Distributed SCM (version *) (glob)
1386 Mercurial Distributed SCM (version *) (glob)
1382 (see https://mercurial-scm.org for more information)
1387 (see https://mercurial-scm.org for more information)
1383
1388
1384 Copyright (C) 2005-* Matt Mackall and others (glob)
1389 Copyright (C) 2005-* Matt Mackall and others (glob)
1385 This is free software; see the source for copying conditions. There is NO
1390 This is free software; see the source for copying conditions. There is NO
1386 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1391 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1387
1392
1388 Enabled extensions:
1393 Enabled extensions:
1389
1394
1390 throw external 1.2.3
1395 throw external 1.2.3
1391 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1396 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1392 $ rm -f throw.pyc throw.pyo
1397 $ rm -f throw.pyc throw.pyo
1393 $ rm -Rf __pycache__
1398 $ rm -Rf __pycache__
1394 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1399 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1395 Mercurial Distributed SCM (version *) (glob)
1400 Mercurial Distributed SCM (version *) (glob)
1396 (see https://mercurial-scm.org for more information)
1401 (see https://mercurial-scm.org for more information)
1397
1402
1398 Copyright (C) 2005-* Matt Mackall and others (glob)
1403 Copyright (C) 2005-* Matt Mackall and others (glob)
1399 This is free software; see the source for copying conditions. There is NO
1404 This is free software; see the source for copying conditions. There is NO
1400 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1405 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1401
1406
1402 Enabled extensions:
1407 Enabled extensions:
1403
1408
1404 throw external 1.twentythree
1409 throw external 1.twentythree
1405 strip internal
1410 strip internal
1406
1411
1407 $ hg version -q --config extensions.throw=throw.py
1412 $ hg version -q --config extensions.throw=throw.py
1408 Mercurial Distributed SCM (version *) (glob)
1413 Mercurial Distributed SCM (version *) (glob)
1409
1414
1410 Test template output:
1415 Test template output:
1411
1416
1412 $ hg version --config extensions.strip= -T'{extensions}'
1417 $ hg version --config extensions.strip= -T'{extensions}'
1413 strip
1418 strip
1414
1419
1415 Test JSON output of version:
1420 Test JSON output of version:
1416
1421
1417 $ hg version -Tjson
1422 $ hg version -Tjson
1418 [
1423 [
1419 {
1424 {
1420 "extensions": [],
1425 "extensions": [],
1421 "ver": "*" (glob)
1426 "ver": "*" (glob)
1422 }
1427 }
1423 ]
1428 ]
1424
1429
1425 $ hg version --config extensions.throw=throw.py -Tjson
1430 $ hg version --config extensions.throw=throw.py -Tjson
1426 [
1431 [
1427 {
1432 {
1428 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1433 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1429 "ver": "3.2.2"
1434 "ver": "3.2.2"
1430 }
1435 }
1431 ]
1436 ]
1432
1437
1433 $ hg version --config extensions.strip= -Tjson
1438 $ hg version --config extensions.strip= -Tjson
1434 [
1439 [
1435 {
1440 {
1436 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1441 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1437 "ver": "*" (glob)
1442 "ver": "*" (glob)
1438 }
1443 }
1439 ]
1444 ]
1440
1445
1441 Test template output of version:
1446 Test template output of version:
1442
1447
1443 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1448 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1444 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1449 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1445 throw 1.twentythree (external)
1450 throw 1.twentythree (external)
1446 strip (internal)
1451 strip (internal)
1447
1452
1448 Refuse to load extensions with minimum version requirements
1453 Refuse to load extensions with minimum version requirements
1449
1454
1450 $ cat > minversion1.py << EOF
1455 $ cat > minversion1.py << EOF
1451 > from mercurial import util
1456 > from mercurial import util
1452 > util.version = lambda: b'3.5.2'
1457 > util.version = lambda: b'3.5.2'
1453 > minimumhgversion = b'3.6'
1458 > minimumhgversion = b'3.6'
1454 > EOF
1459 > EOF
1455 $ hg --config extensions.minversion=minversion1.py version
1460 $ hg --config extensions.minversion=minversion1.py version
1456 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1461 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1457 Mercurial Distributed SCM (version 3.5.2)
1462 Mercurial Distributed SCM (version 3.5.2)
1458 (see https://mercurial-scm.org for more information)
1463 (see https://mercurial-scm.org for more information)
1459
1464
1460 Copyright (C) 2005-* Matt Mackall and others (glob)
1465 Copyright (C) 2005-* Matt Mackall and others (glob)
1461 This is free software; see the source for copying conditions. There is NO
1466 This is free software; see the source for copying conditions. There is NO
1462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1467 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1463
1468
1464 $ cat > minversion2.py << EOF
1469 $ cat > minversion2.py << EOF
1465 > from mercurial import util
1470 > from mercurial import util
1466 > util.version = lambda: b'3.6'
1471 > util.version = lambda: b'3.6'
1467 > minimumhgversion = b'3.7'
1472 > minimumhgversion = b'3.7'
1468 > EOF
1473 > EOF
1469 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1474 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1470 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1475 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1471
1476
1472 Can load version that is only off by point release
1477 Can load version that is only off by point release
1473
1478
1474 $ cat > minversion2.py << EOF
1479 $ cat > minversion2.py << EOF
1475 > from mercurial import util
1480 > from mercurial import util
1476 > util.version = lambda: b'3.6.1'
1481 > util.version = lambda: b'3.6.1'
1477 > minimumhgversion = b'3.6'
1482 > minimumhgversion = b'3.6'
1478 > EOF
1483 > EOF
1479 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1484 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1480 [1]
1485 [1]
1481
1486
1482 Can load minimum version identical to current
1487 Can load minimum version identical to current
1483
1488
1484 $ cat > minversion3.py << EOF
1489 $ cat > minversion3.py << EOF
1485 > from mercurial import util
1490 > from mercurial import util
1486 > util.version = lambda: b'3.5'
1491 > util.version = lambda: b'3.5'
1487 > minimumhgversion = b'3.5'
1492 > minimumhgversion = b'3.5'
1488 > EOF
1493 > EOF
1489 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1494 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1490 [1]
1495 [1]
1491
1496
1492 Restore HGRCPATH
1497 Restore HGRCPATH
1493
1498
1494 $ HGRCPATH=$ORGHGRCPATH
1499 $ HGRCPATH=$ORGHGRCPATH
1495 $ export HGRCPATH
1500 $ export HGRCPATH
1496
1501
1497 Commands handling multiple repositories at a time should invoke only
1502 Commands handling multiple repositories at a time should invoke only
1498 "reposetup()" of extensions enabling in the target repository.
1503 "reposetup()" of extensions enabling in the target repository.
1499
1504
1500 $ mkdir reposetup-test
1505 $ mkdir reposetup-test
1501 $ cd reposetup-test
1506 $ cd reposetup-test
1502
1507
1503 $ cat > $TESTTMP/reposetuptest.py <<EOF
1508 $ cat > $TESTTMP/reposetuptest.py <<EOF
1504 > from mercurial import extensions
1509 > from mercurial import extensions
1505 > def reposetup(ui, repo):
1510 > def reposetup(ui, repo):
1506 > ui.write(b'reposetup() for %s\n' % (repo.root))
1511 > ui.write(b'reposetup() for %s\n' % (repo.root))
1507 > ui.flush()
1512 > ui.flush()
1508 > EOF
1513 > EOF
1509 $ hg init src
1514 $ hg init src
1510 $ echo a > src/a
1515 $ echo a > src/a
1511 $ hg -R src commit -Am '#0 at src/a'
1516 $ hg -R src commit -Am '#0 at src/a'
1512 adding a
1517 adding a
1513 $ echo '[extensions]' >> src/.hg/hgrc
1518 $ echo '[extensions]' >> src/.hg/hgrc
1514 $ echo '# enable extension locally' >> src/.hg/hgrc
1519 $ echo '# enable extension locally' >> src/.hg/hgrc
1515 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1520 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1516 $ hg -R src status
1521 $ hg -R src status
1517 reposetup() for $TESTTMP/reposetup-test/src
1522 reposetup() for $TESTTMP/reposetup-test/src
1518 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1523 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1519
1524
1520 $ hg --cwd src debugextensions
1525 $ hg --cwd src debugextensions
1521 reposetup() for $TESTTMP/reposetup-test/src
1526 reposetup() for $TESTTMP/reposetup-test/src
1522 dodo (untested!)
1527 dodo (untested!)
1523 dudu (untested!)
1528 dudu (untested!)
1524 mq
1529 mq
1525 reposetuptest (untested!)
1530 reposetuptest (untested!)
1526 strip
1531 strip
1527
1532
1528 $ hg clone -U src clone-dst1
1533 $ hg clone -U src clone-dst1
1529 reposetup() for $TESTTMP/reposetup-test/src
1534 reposetup() for $TESTTMP/reposetup-test/src
1530 $ hg init push-dst1
1535 $ hg init push-dst1
1531 $ hg -q -R src push push-dst1
1536 $ hg -q -R src push push-dst1
1532 reposetup() for $TESTTMP/reposetup-test/src
1537 reposetup() for $TESTTMP/reposetup-test/src
1533 $ hg init pull-src1
1538 $ hg init pull-src1
1534 $ hg -q -R pull-src1 pull src
1539 $ hg -q -R pull-src1 pull src
1535 reposetup() for $TESTTMP/reposetup-test/src
1540 reposetup() for $TESTTMP/reposetup-test/src
1536
1541
1537 $ cat <<EOF >> $HGRCPATH
1542 $ cat <<EOF >> $HGRCPATH
1538 > [extensions]
1543 > [extensions]
1539 > # disable extension globally and explicitly
1544 > # disable extension globally and explicitly
1540 > reposetuptest = !
1545 > reposetuptest = !
1541 > EOF
1546 > EOF
1542 $ hg clone -U src clone-dst2
1547 $ hg clone -U src clone-dst2
1543 reposetup() for $TESTTMP/reposetup-test/src
1548 reposetup() for $TESTTMP/reposetup-test/src
1544 $ hg init push-dst2
1549 $ hg init push-dst2
1545 $ hg -q -R src push push-dst2
1550 $ hg -q -R src push push-dst2
1546 reposetup() for $TESTTMP/reposetup-test/src
1551 reposetup() for $TESTTMP/reposetup-test/src
1547 $ hg init pull-src2
1552 $ hg init pull-src2
1548 $ hg -q -R pull-src2 pull src
1553 $ hg -q -R pull-src2 pull src
1549 reposetup() for $TESTTMP/reposetup-test/src
1554 reposetup() for $TESTTMP/reposetup-test/src
1550
1555
1551 $ cat <<EOF >> $HGRCPATH
1556 $ cat <<EOF >> $HGRCPATH
1552 > [extensions]
1557 > [extensions]
1553 > # enable extension globally
1558 > # enable extension globally
1554 > reposetuptest = $TESTTMP/reposetuptest.py
1559 > reposetuptest = $TESTTMP/reposetuptest.py
1555 > EOF
1560 > EOF
1556 $ hg clone -U src clone-dst3
1561 $ hg clone -U src clone-dst3
1557 reposetup() for $TESTTMP/reposetup-test/src
1562 reposetup() for $TESTTMP/reposetup-test/src
1558 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1563 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1559 $ hg init push-dst3
1564 $ hg init push-dst3
1560 reposetup() for $TESTTMP/reposetup-test/push-dst3
1565 reposetup() for $TESTTMP/reposetup-test/push-dst3
1561 $ hg -q -R src push push-dst3
1566 $ hg -q -R src push push-dst3
1562 reposetup() for $TESTTMP/reposetup-test/src
1567 reposetup() for $TESTTMP/reposetup-test/src
1563 reposetup() for $TESTTMP/reposetup-test/push-dst3
1568 reposetup() for $TESTTMP/reposetup-test/push-dst3
1564 $ hg init pull-src3
1569 $ hg init pull-src3
1565 reposetup() for $TESTTMP/reposetup-test/pull-src3
1570 reposetup() for $TESTTMP/reposetup-test/pull-src3
1566 $ hg -q -R pull-src3 pull src
1571 $ hg -q -R pull-src3 pull src
1567 reposetup() for $TESTTMP/reposetup-test/pull-src3
1572 reposetup() for $TESTTMP/reposetup-test/pull-src3
1568 reposetup() for $TESTTMP/reposetup-test/src
1573 reposetup() for $TESTTMP/reposetup-test/src
1569
1574
1570 $ echo '[extensions]' >> src/.hg/hgrc
1575 $ echo '[extensions]' >> src/.hg/hgrc
1571 $ echo '# disable extension locally' >> src/.hg/hgrc
1576 $ echo '# disable extension locally' >> src/.hg/hgrc
1572 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1577 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1573 $ hg clone -U src clone-dst4
1578 $ hg clone -U src clone-dst4
1574 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1579 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1575 $ hg init push-dst4
1580 $ hg init push-dst4
1576 reposetup() for $TESTTMP/reposetup-test/push-dst4
1581 reposetup() for $TESTTMP/reposetup-test/push-dst4
1577 $ hg -q -R src push push-dst4
1582 $ hg -q -R src push push-dst4
1578 reposetup() for $TESTTMP/reposetup-test/push-dst4
1583 reposetup() for $TESTTMP/reposetup-test/push-dst4
1579 $ hg init pull-src4
1584 $ hg init pull-src4
1580 reposetup() for $TESTTMP/reposetup-test/pull-src4
1585 reposetup() for $TESTTMP/reposetup-test/pull-src4
1581 $ hg -q -R pull-src4 pull src
1586 $ hg -q -R pull-src4 pull src
1582 reposetup() for $TESTTMP/reposetup-test/pull-src4
1587 reposetup() for $TESTTMP/reposetup-test/pull-src4
1583
1588
1584 disabling in command line overlays with all configuration
1589 disabling in command line overlays with all configuration
1585 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1590 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1586 $ hg --config extensions.reposetuptest=! init push-dst5
1591 $ hg --config extensions.reposetuptest=! init push-dst5
1587 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1592 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1588 $ hg --config extensions.reposetuptest=! init pull-src5
1593 $ hg --config extensions.reposetuptest=! init pull-src5
1589 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1594 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1590
1595
1591 $ cat <<EOF >> $HGRCPATH
1596 $ cat <<EOF >> $HGRCPATH
1592 > [extensions]
1597 > [extensions]
1593 > # disable extension globally and explicitly
1598 > # disable extension globally and explicitly
1594 > reposetuptest = !
1599 > reposetuptest = !
1595 > EOF
1600 > EOF
1596 $ hg init parent
1601 $ hg init parent
1597 $ hg init parent/sub1
1602 $ hg init parent/sub1
1598 $ echo 1 > parent/sub1/1
1603 $ echo 1 > parent/sub1/1
1599 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1604 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1600 adding 1
1605 adding 1
1601 $ hg init parent/sub2
1606 $ hg init parent/sub2
1602 $ hg init parent/sub2/sub21
1607 $ hg init parent/sub2/sub21
1603 $ echo 21 > parent/sub2/sub21/21
1608 $ echo 21 > parent/sub2/sub21/21
1604 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1609 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1605 adding 21
1610 adding 21
1606 $ cat > parent/sub2/.hgsub <<EOF
1611 $ cat > parent/sub2/.hgsub <<EOF
1607 > sub21 = sub21
1612 > sub21 = sub21
1608 > EOF
1613 > EOF
1609 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1614 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1610 adding .hgsub
1615 adding .hgsub
1611 $ hg init parent/sub3
1616 $ hg init parent/sub3
1612 $ echo 3 > parent/sub3/3
1617 $ echo 3 > parent/sub3/3
1613 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1618 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1614 adding 3
1619 adding 3
1615 $ cat > parent/.hgsub <<EOF
1620 $ cat > parent/.hgsub <<EOF
1616 > sub1 = sub1
1621 > sub1 = sub1
1617 > sub2 = sub2
1622 > sub2 = sub2
1618 > sub3 = sub3
1623 > sub3 = sub3
1619 > EOF
1624 > EOF
1620 $ hg -R parent commit -Am '#0 at parent'
1625 $ hg -R parent commit -Am '#0 at parent'
1621 adding .hgsub
1626 adding .hgsub
1622 $ echo '[extensions]' >> parent/.hg/hgrc
1627 $ echo '[extensions]' >> parent/.hg/hgrc
1623 $ echo '# enable extension locally' >> parent/.hg/hgrc
1628 $ echo '# enable extension locally' >> parent/.hg/hgrc
1624 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1629 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1625 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1630 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1626 $ hg -R parent status -S -A
1631 $ hg -R parent status -S -A
1627 reposetup() for $TESTTMP/reposetup-test/parent
1632 reposetup() for $TESTTMP/reposetup-test/parent
1628 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1633 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1629 C .hgsub
1634 C .hgsub
1630 C .hgsubstate
1635 C .hgsubstate
1631 C sub1/1
1636 C sub1/1
1632 C sub2/.hgsub
1637 C sub2/.hgsub
1633 C sub2/.hgsubstate
1638 C sub2/.hgsubstate
1634 C sub2/sub21/21
1639 C sub2/sub21/21
1635 C sub3/3
1640 C sub3/3
1636
1641
1637 $ cd ..
1642 $ cd ..
1638
1643
1639 Prohibit registration of commands that don't use @command (issue5137)
1644 Prohibit registration of commands that don't use @command (issue5137)
1640
1645
1641 $ hg init deprecated
1646 $ hg init deprecated
1642 $ cd deprecated
1647 $ cd deprecated
1643
1648
1644 $ cat <<EOF > deprecatedcmd.py
1649 $ cat <<EOF > deprecatedcmd.py
1645 > def deprecatedcmd(repo, ui):
1650 > def deprecatedcmd(repo, ui):
1646 > pass
1651 > pass
1647 > cmdtable = {
1652 > cmdtable = {
1648 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1653 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1649 > }
1654 > }
1650 > EOF
1655 > EOF
1651 $ cat <<EOF > .hg/hgrc
1656 $ cat <<EOF > .hg/hgrc
1652 > [extensions]
1657 > [extensions]
1653 > deprecatedcmd = `pwd`/deprecatedcmd.py
1658 > deprecatedcmd = `pwd`/deprecatedcmd.py
1654 > mq = !
1659 > mq = !
1655 > hgext.mq = !
1660 > hgext.mq = !
1656 > hgext/mq = !
1661 > hgext/mq = !
1657 > EOF
1662 > EOF
1658
1663
1659 $ hg deprecatedcmd > /dev/null
1664 $ hg deprecatedcmd > /dev/null
1660 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1665 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1661 *** (use @command decorator to register 'deprecatedcmd')
1666 *** (use @command decorator to register 'deprecatedcmd')
1662 hg: unknown command 'deprecatedcmd'
1667 hg: unknown command 'deprecatedcmd'
1663 [255]
1668 [255]
1664
1669
1665 the extension shouldn't be loaded at all so the mq works:
1670 the extension shouldn't be loaded at all so the mq works:
1666
1671
1667 $ hg qseries --config extensions.mq= > /dev/null
1672 $ hg qseries --config extensions.mq= > /dev/null
1668 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1673 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1669 *** (use @command decorator to register 'deprecatedcmd')
1674 *** (use @command decorator to register 'deprecatedcmd')
1670
1675
1671 $ cd ..
1676 $ cd ..
1672
1677
1673 Test synopsis and docstring extending
1678 Test synopsis and docstring extending
1674
1679
1675 $ hg init exthelp
1680 $ hg init exthelp
1676 $ cat > exthelp.py <<EOF
1681 $ cat > exthelp.py <<EOF
1677 > from mercurial import commands, extensions
1682 > from mercurial import commands, extensions
1678 > def exbookmarks(orig, *args, **opts):
1683 > def exbookmarks(orig, *args, **opts):
1679 > return orig(*args, **opts)
1684 > return orig(*args, **opts)
1680 > def uisetup(ui):
1685 > def uisetup(ui):
1681 > synopsis = b' GREPME [--foo] [-x]'
1686 > synopsis = b' GREPME [--foo] [-x]'
1682 > docstring = '''
1687 > docstring = '''
1683 > GREPME make sure that this is in the help!
1688 > GREPME make sure that this is in the help!
1684 > '''
1689 > '''
1685 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1690 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1686 > synopsis, docstring)
1691 > synopsis, docstring)
1687 > EOF
1692 > EOF
1688 $ abspath=`pwd`/exthelp.py
1693 $ abspath=`pwd`/exthelp.py
1689 $ echo '[extensions]' >> $HGRCPATH
1694 $ echo '[extensions]' >> $HGRCPATH
1690 $ echo "exthelp = $abspath" >> $HGRCPATH
1695 $ echo "exthelp = $abspath" >> $HGRCPATH
1691 $ cd exthelp
1696 $ cd exthelp
1692 $ hg help bookmarks | grep GREPME
1697 $ hg help bookmarks | grep GREPME
1693 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1698 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1694 GREPME make sure that this is in the help!
1699 GREPME make sure that this is in the help!
1695 $ cd ..
1700 $ cd ..
1696
1701
1697 Show deprecation warning for the use of cmdutil.command
1702 Show deprecation warning for the use of cmdutil.command
1698
1703
1699 $ cat > nonregistrar.py <<EOF
1704 $ cat > nonregistrar.py <<EOF
1700 > from mercurial import cmdutil
1705 > from mercurial import cmdutil
1701 > cmdtable = {}
1706 > cmdtable = {}
1702 > command = cmdutil.command(cmdtable)
1707 > command = cmdutil.command(cmdtable)
1703 > @command(b'foo', [], norepo=True)
1708 > @command(b'foo', [], norepo=True)
1704 > def foo(ui):
1709 > def foo(ui):
1705 > pass
1710 > pass
1706 > EOF
1711 > EOF
1707
1712
1708 Prohibit the use of unicode strings as the default value of options
1713 Prohibit the use of unicode strings as the default value of options
1709
1714
1710 $ hg init $TESTTMP/opt-unicode-default
1715 $ hg init $TESTTMP/opt-unicode-default
1711
1716
1712 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1717 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1713 > from mercurial import registrar
1718 > from mercurial import registrar
1714 > cmdtable = {}
1719 > cmdtable = {}
1715 > command = registrar.command(cmdtable)
1720 > command = registrar.command(cmdtable)
1716 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1721 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1717 > def ext(*args, **opts):
1722 > def ext(*args, **opts):
1718 > print(opts[b'opt'])
1723 > print(opts[b'opt'])
1719 > EOF
1724 > EOF
1720 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1725 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1721 > [extensions]
1726 > [extensions]
1722 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1727 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1723 > EOF
1728 > EOF
1724 $ hg -R $TESTTMP/opt-unicode-default dummy
1729 $ hg -R $TESTTMP/opt-unicode-default dummy
1725 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
1730 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
1726 *** (use b'' to make it byte string)
1731 *** (use b'' to make it byte string)
1727 hg: unknown command 'dummy'
1732 hg: unknown command 'dummy'
1728 (did you mean summary?)
1733 (did you mean summary?)
1729 [255]
1734 [255]
General Comments 0
You need to be logged in to leave comments. Login now