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