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