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