##// END OF EJS Templates
extensions: refuse to load extensions if minimum hg version not met...
Gregory Szorc -
r27142:060f83d2 default
parent child Browse files
Show More
@@ -1,459 +1,470 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 os
11 import os
12
12
13 from .i18n import (
13 from .i18n import (
14 _,
14 _,
15 gettext,
15 gettext,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 cmdutil,
19 cmdutil,
20 error,
20 error,
21 util,
21 util,
22 )
22 )
23
23
24 _extensions = {}
24 _extensions = {}
25 _aftercallbacks = {}
25 _aftercallbacks = {}
26 _order = []
26 _order = []
27 _builtin = set(['hbisect', 'bookmarks', 'parentrevspec', 'progress', 'interhg',
27 _builtin = set(['hbisect', 'bookmarks', 'parentrevspec', 'progress', 'interhg',
28 'inotify'])
28 'inotify'])
29
29
30 def extensions(ui=None):
30 def extensions(ui=None):
31 if ui:
31 if ui:
32 def enabled(name):
32 def enabled(name):
33 for format in ['%s', 'hgext.%s']:
33 for format in ['%s', 'hgext.%s']:
34 conf = ui.config('extensions', format % name)
34 conf = ui.config('extensions', format % name)
35 if conf is not None and not conf.startswith('!'):
35 if conf is not None and not conf.startswith('!'):
36 return True
36 return True
37 else:
37 else:
38 enabled = lambda name: True
38 enabled = lambda name: True
39 for name in _order:
39 for name in _order:
40 module = _extensions[name]
40 module = _extensions[name]
41 if module and enabled(name):
41 if module and enabled(name):
42 yield name, module
42 yield name, module
43
43
44 def find(name):
44 def find(name):
45 '''return module with given extension name'''
45 '''return module with given extension name'''
46 mod = None
46 mod = None
47 try:
47 try:
48 mod = _extensions[name]
48 mod = _extensions[name]
49 except KeyError:
49 except KeyError:
50 for k, v in _extensions.iteritems():
50 for k, v in _extensions.iteritems():
51 if k.endswith('.' + name) or k.endswith('/' + name):
51 if k.endswith('.' + name) or k.endswith('/' + name):
52 mod = v
52 mod = v
53 break
53 break
54 if not mod:
54 if not mod:
55 raise KeyError(name)
55 raise KeyError(name)
56 return mod
56 return mod
57
57
58 def loadpath(path, module_name):
58 def loadpath(path, module_name):
59 module_name = module_name.replace('.', '_')
59 module_name = module_name.replace('.', '_')
60 path = util.normpath(util.expandpath(path))
60 path = util.normpath(util.expandpath(path))
61 if os.path.isdir(path):
61 if os.path.isdir(path):
62 # module/__init__.py style
62 # module/__init__.py style
63 d, f = os.path.split(path)
63 d, f = os.path.split(path)
64 fd, fpath, desc = imp.find_module(f, [d])
64 fd, fpath, desc = imp.find_module(f, [d])
65 return imp.load_module(module_name, fd, fpath, desc)
65 return imp.load_module(module_name, fd, fpath, desc)
66 else:
66 else:
67 try:
67 try:
68 return imp.load_source(module_name, path)
68 return imp.load_source(module_name, path)
69 except IOError as exc:
69 except IOError as exc:
70 if not exc.filename:
70 if not exc.filename:
71 exc.filename = path # python does not fill this
71 exc.filename = path # python does not fill this
72 raise
72 raise
73
73
74 def load(ui, name, path):
74 def load(ui, name, path):
75 if name.startswith('hgext.') or name.startswith('hgext/'):
75 if name.startswith('hgext.') or name.startswith('hgext/'):
76 shortname = name[6:]
76 shortname = name[6:]
77 else:
77 else:
78 shortname = name
78 shortname = name
79 if shortname in _builtin:
79 if shortname in _builtin:
80 return None
80 return None
81 if shortname in _extensions:
81 if shortname in _extensions:
82 return _extensions[shortname]
82 return _extensions[shortname]
83 _extensions[shortname] = None
83 _extensions[shortname] = None
84 if path:
84 if path:
85 # the module will be loaded in sys.modules
85 # the module will be loaded in sys.modules
86 # choose an unique name so that it doesn't
86 # choose an unique name so that it doesn't
87 # conflicts with other modules
87 # conflicts with other modules
88 mod = loadpath(path, 'hgext.%s' % name)
88 mod = loadpath(path, 'hgext.%s' % name)
89 else:
89 else:
90 def importh(name):
90 def importh(name):
91 mod = __import__(name)
91 mod = __import__(name)
92 components = name.split('.')
92 components = name.split('.')
93 for comp in components[1:]:
93 for comp in components[1:]:
94 mod = getattr(mod, comp)
94 mod = getattr(mod, comp)
95 return mod
95 return mod
96 try:
96 try:
97 mod = importh("hgext.%s" % name)
97 mod = importh("hgext.%s" % name)
98 except ImportError as err:
98 except ImportError as err:
99 ui.debug('could not import hgext.%s (%s): trying %s\n'
99 ui.debug('could not import hgext.%s (%s): trying %s\n'
100 % (name, err, name))
100 % (name, err, name))
101 if ui.debugflag:
101 if ui.debugflag:
102 ui.traceback()
102 ui.traceback()
103 mod = importh(name)
103 mod = importh(name)
104
105 # Before we do anything with the extension, check against minimum stated
106 # compatibility. This gives extension authors a mechanism to have their
107 # extensions short circuit when loaded with a known incompatible version
108 # of Mercurial.
109 minver = getattr(mod, 'minimumhgversion', None)
110 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
111 ui.warn(_('(third party extension %s requires version %s or newer '
112 'of Mercurial; disabling)\n') % (shortname, minver))
113 return
114
104 _extensions[shortname] = mod
115 _extensions[shortname] = mod
105 _order.append(shortname)
116 _order.append(shortname)
106 for fn in _aftercallbacks.get(shortname, []):
117 for fn in _aftercallbacks.get(shortname, []):
107 fn(loaded=True)
118 fn(loaded=True)
108 return mod
119 return mod
109
120
110 def loadall(ui):
121 def loadall(ui):
111 result = ui.configitems("extensions")
122 result = ui.configitems("extensions")
112 newindex = len(_order)
123 newindex = len(_order)
113 for (name, path) in result:
124 for (name, path) in result:
114 if path:
125 if path:
115 if path[0] == '!':
126 if path[0] == '!':
116 continue
127 continue
117 try:
128 try:
118 load(ui, name, path)
129 load(ui, name, path)
119 except KeyboardInterrupt:
130 except KeyboardInterrupt:
120 raise
131 raise
121 except Exception as inst:
132 except Exception as inst:
122 if path:
133 if path:
123 ui.warn(_("*** failed to import extension %s from %s: %s\n")
134 ui.warn(_("*** failed to import extension %s from %s: %s\n")
124 % (name, path, inst))
135 % (name, path, inst))
125 else:
136 else:
126 ui.warn(_("*** failed to import extension %s: %s\n")
137 ui.warn(_("*** failed to import extension %s: %s\n")
127 % (name, inst))
138 % (name, inst))
128 ui.traceback()
139 ui.traceback()
129
140
130 for name in _order[newindex:]:
141 for name in _order[newindex:]:
131 uisetup = getattr(_extensions[name], 'uisetup', None)
142 uisetup = getattr(_extensions[name], 'uisetup', None)
132 if uisetup:
143 if uisetup:
133 uisetup(ui)
144 uisetup(ui)
134
145
135 for name in _order[newindex:]:
146 for name in _order[newindex:]:
136 extsetup = getattr(_extensions[name], 'extsetup', None)
147 extsetup = getattr(_extensions[name], 'extsetup', None)
137 if extsetup:
148 if extsetup:
138 try:
149 try:
139 extsetup(ui)
150 extsetup(ui)
140 except TypeError:
151 except TypeError:
141 if extsetup.func_code.co_argcount != 0:
152 if extsetup.func_code.co_argcount != 0:
142 raise
153 raise
143 extsetup() # old extsetup with no ui argument
154 extsetup() # old extsetup with no ui argument
144
155
145 # Call aftercallbacks that were never met.
156 # Call aftercallbacks that were never met.
146 for shortname in _aftercallbacks:
157 for shortname in _aftercallbacks:
147 if shortname in _extensions:
158 if shortname in _extensions:
148 continue
159 continue
149
160
150 for fn in _aftercallbacks[shortname]:
161 for fn in _aftercallbacks[shortname]:
151 fn(loaded=False)
162 fn(loaded=False)
152
163
153 # loadall() is called multiple times and lingering _aftercallbacks
164 # loadall() is called multiple times and lingering _aftercallbacks
154 # entries could result in double execution. See issue4646.
165 # entries could result in double execution. See issue4646.
155 _aftercallbacks.clear()
166 _aftercallbacks.clear()
156
167
157 def afterloaded(extension, callback):
168 def afterloaded(extension, callback):
158 '''Run the specified function after a named extension is loaded.
169 '''Run the specified function after a named extension is loaded.
159
170
160 If the named extension is already loaded, the callback will be called
171 If the named extension is already loaded, the callback will be called
161 immediately.
172 immediately.
162
173
163 If the named extension never loads, the callback will be called after
174 If the named extension never loads, the callback will be called after
164 all extensions have been loaded.
175 all extensions have been loaded.
165
176
166 The callback receives the named argument ``loaded``, which is a boolean
177 The callback receives the named argument ``loaded``, which is a boolean
167 indicating whether the dependent extension actually loaded.
178 indicating whether the dependent extension actually loaded.
168 '''
179 '''
169
180
170 if extension in _extensions:
181 if extension in _extensions:
171 callback(loaded=True)
182 callback(loaded=True)
172 else:
183 else:
173 _aftercallbacks.setdefault(extension, []).append(callback)
184 _aftercallbacks.setdefault(extension, []).append(callback)
174
185
175 def bind(func, *args):
186 def bind(func, *args):
176 '''Partial function application
187 '''Partial function application
177
188
178 Returns a new function that is the partial application of args and kwargs
189 Returns a new function that is the partial application of args and kwargs
179 to func. For example,
190 to func. For example,
180
191
181 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
192 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
182 assert callable(func)
193 assert callable(func)
183 def closure(*a, **kw):
194 def closure(*a, **kw):
184 return func(*(args + a), **kw)
195 return func(*(args + a), **kw)
185 return closure
196 return closure
186
197
187 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
198 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
188 '''Wrap the command named `command' in table
199 '''Wrap the command named `command' in table
189
200
190 Replace command in the command table with wrapper. The wrapped command will
201 Replace command in the command table with wrapper. The wrapped command will
191 be inserted into the command table specified by the table argument.
202 be inserted into the command table specified by the table argument.
192
203
193 The wrapper will be called like
204 The wrapper will be called like
194
205
195 wrapper(orig, *args, **kwargs)
206 wrapper(orig, *args, **kwargs)
196
207
197 where orig is the original (wrapped) function, and *args, **kwargs
208 where orig is the original (wrapped) function, and *args, **kwargs
198 are the arguments passed to it.
209 are the arguments passed to it.
199
210
200 Optionally append to the command synopsis and docstring, used for help.
211 Optionally append to the command synopsis and docstring, used for help.
201 For example, if your extension wraps the ``bookmarks`` command to add the
212 For example, if your extension wraps the ``bookmarks`` command to add the
202 flags ``--remote`` and ``--all`` you might call this function like so:
213 flags ``--remote`` and ``--all`` you might call this function like so:
203
214
204 synopsis = ' [-a] [--remote]'
215 synopsis = ' [-a] [--remote]'
205 docstring = """
216 docstring = """
206
217
207 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
218 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
208 flags to the bookmarks command. Either flag will show the remote bookmarks
219 flags to the bookmarks command. Either flag will show the remote bookmarks
209 known to the repository; ``--remote`` will also suppress the output of the
220 known to the repository; ``--remote`` will also suppress the output of the
210 local bookmarks.
221 local bookmarks.
211 """
222 """
212
223
213 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
224 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
214 synopsis, docstring)
225 synopsis, docstring)
215 '''
226 '''
216 assert callable(wrapper)
227 assert callable(wrapper)
217 aliases, entry = cmdutil.findcmd(command, table)
228 aliases, entry = cmdutil.findcmd(command, table)
218 for alias, e in table.iteritems():
229 for alias, e in table.iteritems():
219 if e is entry:
230 if e is entry:
220 key = alias
231 key = alias
221 break
232 break
222
233
223 origfn = entry[0]
234 origfn = entry[0]
224 wrap = bind(util.checksignature(wrapper), util.checksignature(origfn))
235 wrap = bind(util.checksignature(wrapper), util.checksignature(origfn))
225
236
226 wrap.__module__ = getattr(origfn, '__module__')
237 wrap.__module__ = getattr(origfn, '__module__')
227
238
228 doc = getattr(origfn, '__doc__')
239 doc = getattr(origfn, '__doc__')
229 if docstring is not None:
240 if docstring is not None:
230 doc += docstring
241 doc += docstring
231 wrap.__doc__ = doc
242 wrap.__doc__ = doc
232
243
233 newentry = list(entry)
244 newentry = list(entry)
234 newentry[0] = wrap
245 newentry[0] = wrap
235 if synopsis is not None:
246 if synopsis is not None:
236 newentry[2] += synopsis
247 newentry[2] += synopsis
237 table[key] = tuple(newentry)
248 table[key] = tuple(newentry)
238 return entry
249 return entry
239
250
240 def wrapfunction(container, funcname, wrapper):
251 def wrapfunction(container, funcname, wrapper):
241 '''Wrap the function named funcname in container
252 '''Wrap the function named funcname in container
242
253
243 Replace the funcname member in the given container with the specified
254 Replace the funcname member in the given container with the specified
244 wrapper. The container is typically a module, class, or instance.
255 wrapper. The container is typically a module, class, or instance.
245
256
246 The wrapper will be called like
257 The wrapper will be called like
247
258
248 wrapper(orig, *args, **kwargs)
259 wrapper(orig, *args, **kwargs)
249
260
250 where orig is the original (wrapped) function, and *args, **kwargs
261 where orig is the original (wrapped) function, and *args, **kwargs
251 are the arguments passed to it.
262 are the arguments passed to it.
252
263
253 Wrapping methods of the repository object is not recommended since
264 Wrapping methods of the repository object is not recommended since
254 it conflicts with extensions that extend the repository by
265 it conflicts with extensions that extend the repository by
255 subclassing. All extensions that need to extend methods of
266 subclassing. All extensions that need to extend methods of
256 localrepository should use this subclassing trick: namely,
267 localrepository should use this subclassing trick: namely,
257 reposetup() should look like
268 reposetup() should look like
258
269
259 def reposetup(ui, repo):
270 def reposetup(ui, repo):
260 class myrepo(repo.__class__):
271 class myrepo(repo.__class__):
261 def whatever(self, *args, **kwargs):
272 def whatever(self, *args, **kwargs):
262 [...extension stuff...]
273 [...extension stuff...]
263 super(myrepo, self).whatever(*args, **kwargs)
274 super(myrepo, self).whatever(*args, **kwargs)
264 [...extension stuff...]
275 [...extension stuff...]
265
276
266 repo.__class__ = myrepo
277 repo.__class__ = myrepo
267
278
268 In general, combining wrapfunction() with subclassing does not
279 In general, combining wrapfunction() with subclassing does not
269 work. Since you cannot control what other extensions are loaded by
280 work. Since you cannot control what other extensions are loaded by
270 your end users, you should play nicely with others by using the
281 your end users, you should play nicely with others by using the
271 subclass trick.
282 subclass trick.
272 '''
283 '''
273 assert callable(wrapper)
284 assert callable(wrapper)
274
285
275 origfn = getattr(container, funcname)
286 origfn = getattr(container, funcname)
276 assert callable(origfn)
287 assert callable(origfn)
277 setattr(container, funcname, bind(wrapper, origfn))
288 setattr(container, funcname, bind(wrapper, origfn))
278 return origfn
289 return origfn
279
290
280 def _disabledpaths(strip_init=False):
291 def _disabledpaths(strip_init=False):
281 '''find paths of disabled extensions. returns a dict of {name: path}
292 '''find paths of disabled extensions. returns a dict of {name: path}
282 removes /__init__.py from packages if strip_init is True'''
293 removes /__init__.py from packages if strip_init is True'''
283 import hgext
294 import hgext
284 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
295 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
285 try: # might not be a filesystem path
296 try: # might not be a filesystem path
286 files = os.listdir(extpath)
297 files = os.listdir(extpath)
287 except OSError:
298 except OSError:
288 return {}
299 return {}
289
300
290 exts = {}
301 exts = {}
291 for e in files:
302 for e in files:
292 if e.endswith('.py'):
303 if e.endswith('.py'):
293 name = e.rsplit('.', 1)[0]
304 name = e.rsplit('.', 1)[0]
294 path = os.path.join(extpath, e)
305 path = os.path.join(extpath, e)
295 else:
306 else:
296 name = e
307 name = e
297 path = os.path.join(extpath, e, '__init__.py')
308 path = os.path.join(extpath, e, '__init__.py')
298 if not os.path.exists(path):
309 if not os.path.exists(path):
299 continue
310 continue
300 if strip_init:
311 if strip_init:
301 path = os.path.dirname(path)
312 path = os.path.dirname(path)
302 if name in exts or name in _order or name == '__init__':
313 if name in exts or name in _order or name == '__init__':
303 continue
314 continue
304 exts[name] = path
315 exts[name] = path
305 return exts
316 return exts
306
317
307 def _moduledoc(file):
318 def _moduledoc(file):
308 '''return the top-level python documentation for the given file
319 '''return the top-level python documentation for the given file
309
320
310 Loosely inspired by pydoc.source_synopsis(), but rewritten to
321 Loosely inspired by pydoc.source_synopsis(), but rewritten to
311 handle triple quotes and to return the whole text instead of just
322 handle triple quotes and to return the whole text instead of just
312 the synopsis'''
323 the synopsis'''
313 result = []
324 result = []
314
325
315 line = file.readline()
326 line = file.readline()
316 while line[:1] == '#' or not line.strip():
327 while line[:1] == '#' or not line.strip():
317 line = file.readline()
328 line = file.readline()
318 if not line:
329 if not line:
319 break
330 break
320
331
321 start = line[:3]
332 start = line[:3]
322 if start == '"""' or start == "'''":
333 if start == '"""' or start == "'''":
323 line = line[3:]
334 line = line[3:]
324 while line:
335 while line:
325 if line.rstrip().endswith(start):
336 if line.rstrip().endswith(start):
326 line = line.split(start)[0]
337 line = line.split(start)[0]
327 if line:
338 if line:
328 result.append(line)
339 result.append(line)
329 break
340 break
330 elif not line:
341 elif not line:
331 return None # unmatched delimiter
342 return None # unmatched delimiter
332 result.append(line)
343 result.append(line)
333 line = file.readline()
344 line = file.readline()
334 else:
345 else:
335 return None
346 return None
336
347
337 return ''.join(result)
348 return ''.join(result)
338
349
339 def _disabledhelp(path):
350 def _disabledhelp(path):
340 '''retrieve help synopsis of a disabled extension (without importing)'''
351 '''retrieve help synopsis of a disabled extension (without importing)'''
341 try:
352 try:
342 file = open(path)
353 file = open(path)
343 except IOError:
354 except IOError:
344 return
355 return
345 else:
356 else:
346 doc = _moduledoc(file)
357 doc = _moduledoc(file)
347 file.close()
358 file.close()
348
359
349 if doc: # extracting localized synopsis
360 if doc: # extracting localized synopsis
350 return gettext(doc).splitlines()[0]
361 return gettext(doc).splitlines()[0]
351 else:
362 else:
352 return _('(no help text available)')
363 return _('(no help text available)')
353
364
354 def disabled():
365 def disabled():
355 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
366 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
356 try:
367 try:
357 from hgext import __index__
368 from hgext import __index__
358 return dict((name, gettext(desc))
369 return dict((name, gettext(desc))
359 for name, desc in __index__.docs.iteritems()
370 for name, desc in __index__.docs.iteritems()
360 if name not in _order)
371 if name not in _order)
361 except (ImportError, AttributeError):
372 except (ImportError, AttributeError):
362 pass
373 pass
363
374
364 paths = _disabledpaths()
375 paths = _disabledpaths()
365 if not paths:
376 if not paths:
366 return {}
377 return {}
367
378
368 exts = {}
379 exts = {}
369 for name, path in paths.iteritems():
380 for name, path in paths.iteritems():
370 doc = _disabledhelp(path)
381 doc = _disabledhelp(path)
371 if doc:
382 if doc:
372 exts[name] = doc
383 exts[name] = doc
373
384
374 return exts
385 return exts
375
386
376 def disabledext(name):
387 def disabledext(name):
377 '''find a specific disabled extension from hgext. returns desc'''
388 '''find a specific disabled extension from hgext. returns desc'''
378 try:
389 try:
379 from hgext import __index__
390 from hgext import __index__
380 if name in _order: # enabled
391 if name in _order: # enabled
381 return
392 return
382 else:
393 else:
383 return gettext(__index__.docs.get(name))
394 return gettext(__index__.docs.get(name))
384 except (ImportError, AttributeError):
395 except (ImportError, AttributeError):
385 pass
396 pass
386
397
387 paths = _disabledpaths()
398 paths = _disabledpaths()
388 if name in paths:
399 if name in paths:
389 return _disabledhelp(paths[name])
400 return _disabledhelp(paths[name])
390
401
391 def disabledcmd(ui, cmd, strict=False):
402 def disabledcmd(ui, cmd, strict=False):
392 '''import disabled extensions until cmd is found.
403 '''import disabled extensions until cmd is found.
393 returns (cmdname, extname, module)'''
404 returns (cmdname, extname, module)'''
394
405
395 paths = _disabledpaths(strip_init=True)
406 paths = _disabledpaths(strip_init=True)
396 if not paths:
407 if not paths:
397 raise error.UnknownCommand(cmd)
408 raise error.UnknownCommand(cmd)
398
409
399 def findcmd(cmd, name, path):
410 def findcmd(cmd, name, path):
400 try:
411 try:
401 mod = loadpath(path, 'hgext.%s' % name)
412 mod = loadpath(path, 'hgext.%s' % name)
402 except Exception:
413 except Exception:
403 return
414 return
404 try:
415 try:
405 aliases, entry = cmdutil.findcmd(cmd,
416 aliases, entry = cmdutil.findcmd(cmd,
406 getattr(mod, 'cmdtable', {}), strict)
417 getattr(mod, 'cmdtable', {}), strict)
407 except (error.AmbiguousCommand, error.UnknownCommand):
418 except (error.AmbiguousCommand, error.UnknownCommand):
408 return
419 return
409 except Exception:
420 except Exception:
410 ui.warn(_('warning: error finding commands in %s\n') % path)
421 ui.warn(_('warning: error finding commands in %s\n') % path)
411 ui.traceback()
422 ui.traceback()
412 return
423 return
413 for c in aliases:
424 for c in aliases:
414 if c.startswith(cmd):
425 if c.startswith(cmd):
415 cmd = c
426 cmd = c
416 break
427 break
417 else:
428 else:
418 cmd = aliases[0]
429 cmd = aliases[0]
419 return (cmd, name, mod)
430 return (cmd, name, mod)
420
431
421 ext = None
432 ext = None
422 # first, search for an extension with the same name as the command
433 # first, search for an extension with the same name as the command
423 path = paths.pop(cmd, None)
434 path = paths.pop(cmd, None)
424 if path:
435 if path:
425 ext = findcmd(cmd, cmd, path)
436 ext = findcmd(cmd, cmd, path)
426 if not ext:
437 if not ext:
427 # otherwise, interrogate each extension until there's a match
438 # otherwise, interrogate each extension until there's a match
428 for name, path in paths.iteritems():
439 for name, path in paths.iteritems():
429 ext = findcmd(cmd, name, path)
440 ext = findcmd(cmd, name, path)
430 if ext:
441 if ext:
431 break
442 break
432 if ext and 'DEPRECATED' not in ext.__doc__:
443 if ext and 'DEPRECATED' not in ext.__doc__:
433 return ext
444 return ext
434
445
435 raise error.UnknownCommand(cmd)
446 raise error.UnknownCommand(cmd)
436
447
437 def enabled(shortname=True):
448 def enabled(shortname=True):
438 '''return a dict of {name: desc} of extensions'''
449 '''return a dict of {name: desc} of extensions'''
439 exts = {}
450 exts = {}
440 for ename, ext in extensions():
451 for ename, ext in extensions():
441 doc = (gettext(ext.__doc__) or _('(no help text available)'))
452 doc = (gettext(ext.__doc__) or _('(no help text available)'))
442 if shortname:
453 if shortname:
443 ename = ename.split('.')[-1]
454 ename = ename.split('.')[-1]
444 exts[ename] = doc.splitlines()[0].strip()
455 exts[ename] = doc.splitlines()[0].strip()
445
456
446 return exts
457 return exts
447
458
448 def moduleversion(module):
459 def moduleversion(module):
449 '''return version information from given module as a string'''
460 '''return version information from given module as a string'''
450 if (util.safehasattr(module, 'getversion')
461 if (util.safehasattr(module, 'getversion')
451 and callable(module.getversion)):
462 and callable(module.getversion)):
452 version = module.getversion()
463 version = module.getversion()
453 elif util.safehasattr(module, '__version__'):
464 elif util.safehasattr(module, '__version__'):
454 version = module.__version__
465 version = module.__version__
455 else:
466 else:
456 version = ''
467 version = ''
457 if isinstance(version, (list, tuple)):
468 if isinstance(version, (list, tuple)):
458 version = '.'.join(str(o) for o in version)
469 version = '.'.join(str(o) for o in version)
459 return version
470 return version
@@ -1,1171 +1,1215 b''
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import cmdutil, commands
5 > from mercurial import cmdutil, commands
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = cmdutil.command(cmdtable)
7 > command = cmdutil.command(cmdtable)
8 > def uisetup(ui):
8 > def uisetup(ui):
9 > ui.write("uisetup called\\n")
9 > ui.write("uisetup called\\n")
10 > def reposetup(ui, repo):
10 > def reposetup(ui, repo):
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 > @command('foo', [], 'hg foo')
13 > @command('foo', [], 'hg foo')
14 > def foo(ui, *args, **kwargs):
14 > def foo(ui, *args, **kwargs):
15 > ui.write("Foo\\n")
15 > ui.write("Foo\\n")
16 > @command('bar', [], 'hg bar', norepo=True)
16 > @command('bar', [], 'hg bar', norepo=True)
17 > def bar(ui, *args, **kwargs):
17 > def bar(ui, *args, **kwargs):
18 > ui.write("Bar\\n")
18 > ui.write("Bar\\n")
19 > EOF
19 > EOF
20 $ abspath=`pwd`/foobar.py
20 $ abspath=`pwd`/foobar.py
21
21
22 $ mkdir barfoo
22 $ mkdir barfoo
23 $ cp foobar.py barfoo/__init__.py
23 $ cp foobar.py barfoo/__init__.py
24 $ barfoopath=`pwd`/barfoo
24 $ barfoopath=`pwd`/barfoo
25
25
26 $ hg init a
26 $ hg init a
27 $ cd a
27 $ cd a
28 $ echo foo > file
28 $ echo foo > file
29 $ hg add file
29 $ hg add file
30 $ hg commit -m 'add file'
30 $ hg commit -m 'add file'
31
31
32 $ echo '[extensions]' >> $HGRCPATH
32 $ echo '[extensions]' >> $HGRCPATH
33 $ echo "foobar = $abspath" >> $HGRCPATH
33 $ echo "foobar = $abspath" >> $HGRCPATH
34 $ hg foo
34 $ hg foo
35 uisetup called
35 uisetup called
36 reposetup called for a
36 reposetup called for a
37 ui == repo.ui
37 ui == repo.ui
38 Foo
38 Foo
39
39
40 $ cd ..
40 $ cd ..
41 $ hg clone a b
41 $ hg clone a b
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 reposetup called for b
45 reposetup called for b
46 ui == repo.ui
46 ui == repo.ui
47 updating to branch default
47 updating to branch default
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49
49
50 $ hg bar
50 $ hg bar
51 uisetup called
51 uisetup called
52 Bar
52 Bar
53 $ echo 'foobar = !' >> $HGRCPATH
53 $ echo 'foobar = !' >> $HGRCPATH
54
54
55 module/__init__.py-style
55 module/__init__.py-style
56
56
57 $ echo "barfoo = $barfoopath" >> $HGRCPATH
57 $ echo "barfoo = $barfoopath" >> $HGRCPATH
58 $ cd a
58 $ cd a
59 $ hg foo
59 $ hg foo
60 uisetup called
60 uisetup called
61 reposetup called for a
61 reposetup called for a
62 ui == repo.ui
62 ui == repo.ui
63 Foo
63 Foo
64 $ echo 'barfoo = !' >> $HGRCPATH
64 $ echo 'barfoo = !' >> $HGRCPATH
65
65
66 Check that extensions are loaded in phases:
66 Check that extensions are loaded in phases:
67
67
68 $ cat > foo.py <<EOF
68 $ cat > foo.py <<EOF
69 > import os
69 > import os
70 > name = os.path.basename(__file__).rsplit('.', 1)[0]
70 > name = os.path.basename(__file__).rsplit('.', 1)[0]
71 > print "1) %s imported" % name
71 > print "1) %s imported" % name
72 > def uisetup(ui):
72 > def uisetup(ui):
73 > print "2) %s uisetup" % name
73 > print "2) %s uisetup" % name
74 > def extsetup():
74 > def extsetup():
75 > print "3) %s extsetup" % name
75 > print "3) %s extsetup" % name
76 > def reposetup(ui, repo):
76 > def reposetup(ui, repo):
77 > print "4) %s reposetup" % name
77 > print "4) %s reposetup" % name
78 > EOF
78 > EOF
79
79
80 $ cp foo.py bar.py
80 $ cp foo.py bar.py
81 $ echo 'foo = foo.py' >> $HGRCPATH
81 $ echo 'foo = foo.py' >> $HGRCPATH
82 $ echo 'bar = bar.py' >> $HGRCPATH
82 $ echo 'bar = bar.py' >> $HGRCPATH
83
83
84 Command with no output, we just want to see the extensions loaded:
84 Command with no output, we just want to see the extensions loaded:
85
85
86 $ hg paths
86 $ hg paths
87 1) foo imported
87 1) foo imported
88 1) bar imported
88 1) bar imported
89 2) foo uisetup
89 2) foo uisetup
90 2) bar uisetup
90 2) bar uisetup
91 3) foo extsetup
91 3) foo extsetup
92 3) bar extsetup
92 3) bar extsetup
93 4) foo reposetup
93 4) foo reposetup
94 4) bar reposetup
94 4) bar reposetup
95
95
96 Check hgweb's load order:
96 Check hgweb's load order:
97
97
98 $ cat > hgweb.cgi <<EOF
98 $ cat > hgweb.cgi <<EOF
99 > #!/usr/bin/env python
99 > #!/usr/bin/env python
100 > from mercurial import demandimport; demandimport.enable()
100 > from mercurial import demandimport; demandimport.enable()
101 > from mercurial.hgweb import hgweb
101 > from mercurial.hgweb import hgweb
102 > from mercurial.hgweb import wsgicgi
102 > from mercurial.hgweb import wsgicgi
103 > application = hgweb('.', 'test repo')
103 > application = hgweb('.', 'test repo')
104 > wsgicgi.launch(application)
104 > wsgicgi.launch(application)
105 > EOF
105 > EOF
106
106
107 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
107 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
108 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
108 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
109 > | grep '^[0-9]) ' # ignores HTML output
109 > | grep '^[0-9]) ' # ignores HTML output
110 1) foo imported
110 1) foo imported
111 1) bar imported
111 1) bar imported
112 2) foo uisetup
112 2) foo uisetup
113 2) bar uisetup
113 2) bar uisetup
114 3) foo extsetup
114 3) foo extsetup
115 3) bar extsetup
115 3) bar extsetup
116 4) foo reposetup
116 4) foo reposetup
117 4) bar reposetup
117 4) bar reposetup
118
118
119 $ echo 'foo = !' >> $HGRCPATH
119 $ echo 'foo = !' >> $HGRCPATH
120 $ echo 'bar = !' >> $HGRCPATH
120 $ echo 'bar = !' >> $HGRCPATH
121
121
122 Check "from __future__ import absolute_import" support for external libraries
122 Check "from __future__ import absolute_import" support for external libraries
123
123
124 #if windows
124 #if windows
125 $ PATHSEP=";"
125 $ PATHSEP=";"
126 #else
126 #else
127 $ PATHSEP=":"
127 $ PATHSEP=":"
128 #endif
128 #endif
129 $ export PATHSEP
129 $ export PATHSEP
130
130
131 $ mkdir $TESTTMP/libroot
131 $ mkdir $TESTTMP/libroot
132 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
132 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
133 $ mkdir $TESTTMP/libroot/mod
133 $ mkdir $TESTTMP/libroot/mod
134 $ touch $TESTTMP/libroot/mod/__init__.py
134 $ touch $TESTTMP/libroot/mod/__init__.py
135 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
135 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
136
136
137 #if absimport
137 #if absimport
138 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
138 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
139 > from __future__ import absolute_import
139 > from __future__ import absolute_import
140 > import ambig # should load "libroot/ambig.py"
140 > import ambig # should load "libroot/ambig.py"
141 > s = ambig.s
141 > s = ambig.s
142 > EOF
142 > EOF
143 $ cat > loadabs.py <<EOF
143 $ cat > loadabs.py <<EOF
144 > import mod.ambigabs as ambigabs
144 > import mod.ambigabs as ambigabs
145 > def extsetup():
145 > def extsetup():
146 > print 'ambigabs.s=%s' % ambigabs.s
146 > print 'ambigabs.s=%s' % ambigabs.s
147 > EOF
147 > EOF
148 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
148 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
149 ambigabs.s=libroot/ambig.py
149 ambigabs.s=libroot/ambig.py
150 $TESTTMP/a (glob)
150 $TESTTMP/a (glob)
151 #endif
151 #endif
152
152
153 #if no-py3k
153 #if no-py3k
154 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
154 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
155 > import ambig # should load "libroot/mod/ambig.py"
155 > import ambig # should load "libroot/mod/ambig.py"
156 > s = ambig.s
156 > s = ambig.s
157 > EOF
157 > EOF
158 $ cat > loadrel.py <<EOF
158 $ cat > loadrel.py <<EOF
159 > import mod.ambigrel as ambigrel
159 > import mod.ambigrel as ambigrel
160 > def extsetup():
160 > def extsetup():
161 > print 'ambigrel.s=%s' % ambigrel.s
161 > print 'ambigrel.s=%s' % ambigrel.s
162 > EOF
162 > EOF
163 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
163 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
164 ambigrel.s=libroot/mod/ambig.py
164 ambigrel.s=libroot/mod/ambig.py
165 $TESTTMP/a (glob)
165 $TESTTMP/a (glob)
166 #endif
166 #endif
167
167
168 Check absolute/relative import of extension specific modules
168 Check absolute/relative import of extension specific modules
169
169
170 $ mkdir $TESTTMP/extroot
170 $ mkdir $TESTTMP/extroot
171 $ cat > $TESTTMP/extroot/bar.py <<EOF
171 $ cat > $TESTTMP/extroot/bar.py <<EOF
172 > s = 'this is extroot.bar'
172 > s = 'this is extroot.bar'
173 > EOF
173 > EOF
174 $ mkdir $TESTTMP/extroot/sub1
174 $ mkdir $TESTTMP/extroot/sub1
175 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
175 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
176 > s = 'this is extroot.sub1.__init__'
176 > s = 'this is extroot.sub1.__init__'
177 > EOF
177 > EOF
178 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
178 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
179 > s = 'this is extroot.sub1.baz'
179 > s = 'this is extroot.sub1.baz'
180 > EOF
180 > EOF
181 $ cat > $TESTTMP/extroot/__init__.py <<EOF
181 $ cat > $TESTTMP/extroot/__init__.py <<EOF
182 > s = 'this is extroot.__init__'
182 > s = 'this is extroot.__init__'
183 > import foo
183 > import foo
184 > def extsetup(ui):
184 > def extsetup(ui):
185 > ui.write('(extroot) ', foo.func(), '\n')
185 > ui.write('(extroot) ', foo.func(), '\n')
186 > EOF
186 > EOF
187
187
188 $ cat > $TESTTMP/extroot/foo.py <<EOF
188 $ cat > $TESTTMP/extroot/foo.py <<EOF
189 > # test absolute import
189 > # test absolute import
190 > buf = []
190 > buf = []
191 > def func():
191 > def func():
192 > # "not locals" case
192 > # "not locals" case
193 > import extroot.bar
193 > import extroot.bar
194 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
194 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
195 > return '\n(extroot) '.join(buf)
195 > return '\n(extroot) '.join(buf)
196 > # "fromlist == ('*',)" case
196 > # "fromlist == ('*',)" case
197 > from extroot.bar import *
197 > from extroot.bar import *
198 > buf.append('from extroot.bar import *: %s' % s)
198 > buf.append('from extroot.bar import *: %s' % s)
199 > # "not fromlist" and "if '.' in name" case
199 > # "not fromlist" and "if '.' in name" case
200 > import extroot.sub1.baz
200 > import extroot.sub1.baz
201 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
201 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
202 > # "not fromlist" and NOT "if '.' in name" case
202 > # "not fromlist" and NOT "if '.' in name" case
203 > import extroot
203 > import extroot
204 > buf.append('import extroot: %s' % extroot.s)
204 > buf.append('import extroot: %s' % extroot.s)
205 > # NOT "not fromlist" and NOT "level != -1" case
205 > # NOT "not fromlist" and NOT "level != -1" case
206 > from extroot.bar import s
206 > from extroot.bar import s
207 > buf.append('from extroot.bar import s: %s' % s)
207 > buf.append('from extroot.bar import s: %s' % s)
208 > EOF
208 > EOF
209 $ hg --config extensions.extroot=$TESTTMP/extroot root
209 $ hg --config extensions.extroot=$TESTTMP/extroot root
210 (extroot) from extroot.bar import *: this is extroot.bar
210 (extroot) from extroot.bar import *: this is extroot.bar
211 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
211 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
212 (extroot) import extroot: this is extroot.__init__
212 (extroot) import extroot: this is extroot.__init__
213 (extroot) from extroot.bar import s: this is extroot.bar
213 (extroot) from extroot.bar import s: this is extroot.bar
214 (extroot) import extroot.bar in func(): this is extroot.bar
214 (extroot) import extroot.bar in func(): this is extroot.bar
215 $TESTTMP/a (glob)
215 $TESTTMP/a (glob)
216
216
217 #if no-py3k
217 #if no-py3k
218 $ rm "$TESTTMP"/extroot/foo.*
218 $ rm "$TESTTMP"/extroot/foo.*
219 $ cat > $TESTTMP/extroot/foo.py <<EOF
219 $ cat > $TESTTMP/extroot/foo.py <<EOF
220 > # test relative import
220 > # test relative import
221 > buf = []
221 > buf = []
222 > def func():
222 > def func():
223 > # "not locals" case
223 > # "not locals" case
224 > import bar
224 > import bar
225 > buf.append('import bar in func(): %s' % bar.s)
225 > buf.append('import bar in func(): %s' % bar.s)
226 > return '\n(extroot) '.join(buf)
226 > return '\n(extroot) '.join(buf)
227 > # "fromlist == ('*',)" case
227 > # "fromlist == ('*',)" case
228 > from bar import *
228 > from bar import *
229 > buf.append('from bar import *: %s' % s)
229 > buf.append('from bar import *: %s' % s)
230 > # "not fromlist" and "if '.' in name" case
230 > # "not fromlist" and "if '.' in name" case
231 > import sub1.baz
231 > import sub1.baz
232 > buf.append('import sub1.baz: %s' % sub1.baz.s)
232 > buf.append('import sub1.baz: %s' % sub1.baz.s)
233 > # "not fromlist" and NOT "if '.' in name" case
233 > # "not fromlist" and NOT "if '.' in name" case
234 > import sub1
234 > import sub1
235 > buf.append('import sub1: %s' % sub1.s)
235 > buf.append('import sub1: %s' % sub1.s)
236 > # NOT "not fromlist" and NOT "level != -1" case
236 > # NOT "not fromlist" and NOT "level != -1" case
237 > from bar import s
237 > from bar import s
238 > buf.append('from bar import s: %s' % s)
238 > buf.append('from bar import s: %s' % s)
239 > EOF
239 > EOF
240 $ hg --config extensions.extroot=$TESTTMP/extroot root
240 $ hg --config extensions.extroot=$TESTTMP/extroot root
241 (extroot) from bar import *: this is extroot.bar
241 (extroot) from bar import *: this is extroot.bar
242 (extroot) import sub1.baz: this is extroot.sub1.baz
242 (extroot) import sub1.baz: this is extroot.sub1.baz
243 (extroot) import sub1: this is extroot.sub1.__init__
243 (extroot) import sub1: this is extroot.sub1.__init__
244 (extroot) from bar import s: this is extroot.bar
244 (extroot) from bar import s: this is extroot.bar
245 (extroot) import bar in func(): this is extroot.bar
245 (extroot) import bar in func(): this is extroot.bar
246 $TESTTMP/a (glob)
246 $TESTTMP/a (glob)
247 #endif
247 #endif
248
248
249 $ cd ..
249 $ cd ..
250
250
251 hide outer repo
251 hide outer repo
252 $ hg init
252 $ hg init
253
253
254 $ cat > empty.py <<EOF
254 $ cat > empty.py <<EOF
255 > '''empty cmdtable
255 > '''empty cmdtable
256 > '''
256 > '''
257 > cmdtable = {}
257 > cmdtable = {}
258 > EOF
258 > EOF
259 $ emptypath=`pwd`/empty.py
259 $ emptypath=`pwd`/empty.py
260 $ echo "empty = $emptypath" >> $HGRCPATH
260 $ echo "empty = $emptypath" >> $HGRCPATH
261 $ hg help empty
261 $ hg help empty
262 empty extension - empty cmdtable
262 empty extension - empty cmdtable
263
263
264 no commands defined
264 no commands defined
265
265
266
266
267 $ echo 'empty = !' >> $HGRCPATH
267 $ echo 'empty = !' >> $HGRCPATH
268
268
269 $ cat > debugextension.py <<EOF
269 $ cat > debugextension.py <<EOF
270 > '''only debugcommands
270 > '''only debugcommands
271 > '''
271 > '''
272 > from mercurial import cmdutil
272 > from mercurial import cmdutil
273 > cmdtable = {}
273 > cmdtable = {}
274 > command = cmdutil.command(cmdtable)
274 > command = cmdutil.command(cmdtable)
275 > @command('debugfoobar', [], 'hg debugfoobar')
275 > @command('debugfoobar', [], 'hg debugfoobar')
276 > def debugfoobar(ui, repo, *args, **opts):
276 > def debugfoobar(ui, repo, *args, **opts):
277 > "yet another debug command"
277 > "yet another debug command"
278 > pass
278 > pass
279 > @command('foo', [], 'hg foo')
279 > @command('foo', [], 'hg foo')
280 > def foo(ui, repo, *args, **opts):
280 > def foo(ui, repo, *args, **opts):
281 > """yet another foo command
281 > """yet another foo command
282 > This command has been DEPRECATED since forever.
282 > This command has been DEPRECATED since forever.
283 > """
283 > """
284 > pass
284 > pass
285 > EOF
285 > EOF
286 $ debugpath=`pwd`/debugextension.py
286 $ debugpath=`pwd`/debugextension.py
287 $ echo "debugextension = $debugpath" >> $HGRCPATH
287 $ echo "debugextension = $debugpath" >> $HGRCPATH
288
288
289 $ hg help debugextension
289 $ hg help debugextension
290 hg debugextensions
290 hg debugextensions
291
291
292 show information about active extensions
292 show information about active extensions
293
293
294 options:
294 options:
295
295
296 (some details hidden, use --verbose to show complete help)
296 (some details hidden, use --verbose to show complete help)
297
297
298
298
299 $ hg --verbose help debugextension
299 $ hg --verbose help debugextension
300 hg debugextensions
300 hg debugextensions
301
301
302 show information about active extensions
302 show information about active extensions
303
303
304 options:
304 options:
305
305
306 -T --template TEMPLATE display with template (EXPERIMENTAL)
306 -T --template TEMPLATE display with template (EXPERIMENTAL)
307
307
308 global options ([+] can be repeated):
308 global options ([+] can be repeated):
309
309
310 -R --repository REPO repository root directory or name of overlay bundle
310 -R --repository REPO repository root directory or name of overlay bundle
311 file
311 file
312 --cwd DIR change working directory
312 --cwd DIR change working directory
313 -y --noninteractive do not prompt, automatically pick the first choice for
313 -y --noninteractive do not prompt, automatically pick the first choice for
314 all prompts
314 all prompts
315 -q --quiet suppress output
315 -q --quiet suppress output
316 -v --verbose enable additional output
316 -v --verbose enable additional output
317 --config CONFIG [+] set/override config option (use 'section.name=value')
317 --config CONFIG [+] set/override config option (use 'section.name=value')
318 --debug enable debugging output
318 --debug enable debugging output
319 --debugger start debugger
319 --debugger start debugger
320 --encoding ENCODE set the charset encoding (default: ascii)
320 --encoding ENCODE set the charset encoding (default: ascii)
321 --encodingmode MODE set the charset encoding mode (default: strict)
321 --encodingmode MODE set the charset encoding mode (default: strict)
322 --traceback always print a traceback on exception
322 --traceback always print a traceback on exception
323 --time time how long the command takes
323 --time time how long the command takes
324 --profile print command execution profile
324 --profile print command execution profile
325 --version output version information and exit
325 --version output version information and exit
326 -h --help display help and exit
326 -h --help display help and exit
327 --hidden consider hidden changesets
327 --hidden consider hidden changesets
328
328
329
329
330
330
331
331
332
332
333
333
334 $ hg --debug help debugextension
334 $ hg --debug help debugextension
335 hg debugextensions
335 hg debugextensions
336
336
337 show information about active extensions
337 show information about active extensions
338
338
339 options:
339 options:
340
340
341 -T --template TEMPLATE display with template (EXPERIMENTAL)
341 -T --template TEMPLATE display with template (EXPERIMENTAL)
342
342
343 global options ([+] can be repeated):
343 global options ([+] can be repeated):
344
344
345 -R --repository REPO repository root directory or name of overlay bundle
345 -R --repository REPO repository root directory or name of overlay bundle
346 file
346 file
347 --cwd DIR change working directory
347 --cwd DIR change working directory
348 -y --noninteractive do not prompt, automatically pick the first choice for
348 -y --noninteractive do not prompt, automatically pick the first choice for
349 all prompts
349 all prompts
350 -q --quiet suppress output
350 -q --quiet suppress output
351 -v --verbose enable additional output
351 -v --verbose enable additional output
352 --config CONFIG [+] set/override config option (use 'section.name=value')
352 --config CONFIG [+] set/override config option (use 'section.name=value')
353 --debug enable debugging output
353 --debug enable debugging output
354 --debugger start debugger
354 --debugger start debugger
355 --encoding ENCODE set the charset encoding (default: ascii)
355 --encoding ENCODE set the charset encoding (default: ascii)
356 --encodingmode MODE set the charset encoding mode (default: strict)
356 --encodingmode MODE set the charset encoding mode (default: strict)
357 --traceback always print a traceback on exception
357 --traceback always print a traceback on exception
358 --time time how long the command takes
358 --time time how long the command takes
359 --profile print command execution profile
359 --profile print command execution profile
360 --version output version information and exit
360 --version output version information and exit
361 -h --help display help and exit
361 -h --help display help and exit
362 --hidden consider hidden changesets
362 --hidden consider hidden changesets
363
363
364
364
365
365
366
366
367
367
368 $ echo 'debugextension = !' >> $HGRCPATH
368 $ echo 'debugextension = !' >> $HGRCPATH
369
369
370 Extension module help vs command help:
370 Extension module help vs command help:
371
371
372 $ echo 'extdiff =' >> $HGRCPATH
372 $ echo 'extdiff =' >> $HGRCPATH
373 $ hg help extdiff
373 $ hg help extdiff
374 hg extdiff [OPT]... [FILE]...
374 hg extdiff [OPT]... [FILE]...
375
375
376 use external program to diff repository (or selected files)
376 use external program to diff repository (or selected files)
377
377
378 Show differences between revisions for the specified files, using an
378 Show differences between revisions for the specified files, using an
379 external program. The default program used is diff, with default options
379 external program. The default program used is diff, with default options
380 "-Npru".
380 "-Npru".
381
381
382 To select a different program, use the -p/--program option. The program
382 To select a different program, use the -p/--program option. The program
383 will be passed the names of two directories to compare. To pass additional
383 will be passed the names of two directories to compare. To pass additional
384 options to the program, use -o/--option. These will be passed before the
384 options to the program, use -o/--option. These will be passed before the
385 names of the directories to compare.
385 names of the directories to compare.
386
386
387 When two revision arguments are given, then changes are shown between
387 When two revision arguments are given, then changes are shown between
388 those revisions. If only one revision is specified then that revision is
388 those revisions. If only one revision is specified then that revision is
389 compared to the working directory, and, when no revisions are specified,
389 compared to the working directory, and, when no revisions are specified,
390 the working directory files are compared to its parent.
390 the working directory files are compared to its parent.
391
391
392 (use "hg help -e extdiff" to show help for the extdiff extension)
392 (use "hg help -e extdiff" to show help for the extdiff extension)
393
393
394 options ([+] can be repeated):
394 options ([+] can be repeated):
395
395
396 -p --program CMD comparison program to run
396 -p --program CMD comparison program to run
397 -o --option OPT [+] pass option to comparison program
397 -o --option OPT [+] pass option to comparison program
398 -r --rev REV [+] revision
398 -r --rev REV [+] revision
399 -c --change REV change made by revision
399 -c --change REV change made by revision
400 --patch compare patches for two revisions
400 --patch compare patches for two revisions
401 -I --include PATTERN [+] include names matching the given patterns
401 -I --include PATTERN [+] include names matching the given patterns
402 -X --exclude PATTERN [+] exclude names matching the given patterns
402 -X --exclude PATTERN [+] exclude names matching the given patterns
403 -S --subrepos recurse into subrepositories
403 -S --subrepos recurse into subrepositories
404
404
405 (some details hidden, use --verbose to show complete help)
405 (some details hidden, use --verbose to show complete help)
406
406
407
407
408
408
409
409
410
410
411
411
412
412
413
413
414
414
415
415
416 $ hg help --extension extdiff
416 $ hg help --extension extdiff
417 extdiff extension - command to allow external programs to compare revisions
417 extdiff extension - command to allow external programs to compare revisions
418
418
419 The extdiff Mercurial extension allows you to use external programs to compare
419 The extdiff Mercurial extension allows you to use external programs to compare
420 revisions, or revision with working directory. The external diff programs are
420 revisions, or revision with working directory. The external diff programs are
421 called with a configurable set of options and two non-option arguments: paths
421 called with a configurable set of options and two non-option arguments: paths
422 to directories containing snapshots of files to compare.
422 to directories containing snapshots of files to compare.
423
423
424 The extdiff extension also allows you to configure new diff commands, so you
424 The extdiff extension also allows you to configure new diff commands, so you
425 do not need to type "hg extdiff -p kdiff3" always.
425 do not need to type "hg extdiff -p kdiff3" always.
426
426
427 [extdiff]
427 [extdiff]
428 # add new command that runs GNU diff(1) in 'context diff' mode
428 # add new command that runs GNU diff(1) in 'context diff' mode
429 cdiff = gdiff -Nprc5
429 cdiff = gdiff -Nprc5
430 ## or the old way:
430 ## or the old way:
431 #cmd.cdiff = gdiff
431 #cmd.cdiff = gdiff
432 #opts.cdiff = -Nprc5
432 #opts.cdiff = -Nprc5
433
433
434 # add new command called meld, runs meld (no need to name twice). If
434 # add new command called meld, runs meld (no need to name twice). If
435 # the meld executable is not available, the meld tool in [merge-tools]
435 # the meld executable is not available, the meld tool in [merge-tools]
436 # will be used, if available
436 # will be used, if available
437 meld =
437 meld =
438
438
439 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
439 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
440 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
440 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
441 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
441 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
442 # your .vimrc
442 # your .vimrc
443 vimdiff = gvim -f "+next" \
443 vimdiff = gvim -f "+next" \
444 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
444 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
445
445
446 Tool arguments can include variables that are expanded at runtime:
446 Tool arguments can include variables that are expanded at runtime:
447
447
448 $parent1, $plabel1 - filename, descriptive label of first parent
448 $parent1, $plabel1 - filename, descriptive label of first parent
449 $child, $clabel - filename, descriptive label of child revision
449 $child, $clabel - filename, descriptive label of child revision
450 $parent2, $plabel2 - filename, descriptive label of second parent
450 $parent2, $plabel2 - filename, descriptive label of second parent
451 $root - repository root
451 $root - repository root
452 $parent is an alias for $parent1.
452 $parent is an alias for $parent1.
453
453
454 The extdiff extension will look in your [diff-tools] and [merge-tools]
454 The extdiff extension will look in your [diff-tools] and [merge-tools]
455 sections for diff tool arguments, when none are specified in [extdiff].
455 sections for diff tool arguments, when none are specified in [extdiff].
456
456
457 [extdiff]
457 [extdiff]
458 kdiff3 =
458 kdiff3 =
459
459
460 [diff-tools]
460 [diff-tools]
461 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
461 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
462
462
463 You can use -I/-X and list of file or directory names like normal "hg diff"
463 You can use -I/-X and list of file or directory names like normal "hg diff"
464 command. The extdiff extension makes snapshots of only needed files, so
464 command. The extdiff extension makes snapshots of only needed files, so
465 running the external diff program will actually be pretty fast (at least
465 running the external diff program will actually be pretty fast (at least
466 faster than having to compare the entire tree).
466 faster than having to compare the entire tree).
467
467
468 list of commands:
468 list of commands:
469
469
470 extdiff use external program to diff repository (or selected files)
470 extdiff use external program to diff repository (or selected files)
471
471
472 (use "hg help -v -e extdiff" to show built-in aliases and global options)
472 (use "hg help -v -e extdiff" to show built-in aliases and global options)
473
473
474
474
475
475
476
476
477
477
478
478
479
479
480
480
481
481
482
482
483
483
484
484
485
485
486
486
487
487
488
488
489 $ echo 'extdiff = !' >> $HGRCPATH
489 $ echo 'extdiff = !' >> $HGRCPATH
490
490
491 Test help topic with same name as extension
491 Test help topic with same name as extension
492
492
493 $ cat > multirevs.py <<EOF
493 $ cat > multirevs.py <<EOF
494 > from mercurial import cmdutil, commands
494 > from mercurial import cmdutil, commands
495 > cmdtable = {}
495 > cmdtable = {}
496 > command = cmdutil.command(cmdtable)
496 > command = cmdutil.command(cmdtable)
497 > """multirevs extension
497 > """multirevs extension
498 > Big multi-line module docstring."""
498 > Big multi-line module docstring."""
499 > @command('multirevs', [], 'ARG', norepo=True)
499 > @command('multirevs', [], 'ARG', norepo=True)
500 > def multirevs(ui, repo, arg, *args, **opts):
500 > def multirevs(ui, repo, arg, *args, **opts):
501 > """multirevs command"""
501 > """multirevs command"""
502 > pass
502 > pass
503 > EOF
503 > EOF
504 $ echo "multirevs = multirevs.py" >> $HGRCPATH
504 $ echo "multirevs = multirevs.py" >> $HGRCPATH
505
505
506 $ hg help multirevs
506 $ hg help multirevs
507 Specifying Multiple Revisions
507 Specifying Multiple Revisions
508 """""""""""""""""""""""""""""
508 """""""""""""""""""""""""""""
509
509
510 When Mercurial accepts more than one revision, they may be specified
510 When Mercurial accepts more than one revision, they may be specified
511 individually, or provided as a topologically continuous range, separated
511 individually, or provided as a topologically continuous range, separated
512 by the ":" character.
512 by the ":" character.
513
513
514 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
514 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
515 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
515 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
516 specified, it defaults to revision number 0. If END is not specified, it
516 specified, it defaults to revision number 0. If END is not specified, it
517 defaults to the tip. The range ":" thus means "all revisions".
517 defaults to the tip. The range ":" thus means "all revisions".
518
518
519 If BEGIN is greater than END, revisions are treated in reverse order.
519 If BEGIN is greater than END, revisions are treated in reverse order.
520
520
521 A range acts as a closed interval. This means that a range of 3:5 gives 3,
521 A range acts as a closed interval. This means that a range of 3:5 gives 3,
522 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
522 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
523
523
524 use "hg help -c multirevs" to see help for the multirevs command
524 use "hg help -c multirevs" to see help for the multirevs command
525
525
526
526
527
527
528
528
529
529
530
530
531 $ hg help -c multirevs
531 $ hg help -c multirevs
532 hg multirevs ARG
532 hg multirevs ARG
533
533
534 multirevs command
534 multirevs command
535
535
536 (some details hidden, use --verbose to show complete help)
536 (some details hidden, use --verbose to show complete help)
537
537
538
538
539
539
540 $ hg multirevs
540 $ hg multirevs
541 hg multirevs: invalid arguments
541 hg multirevs: invalid arguments
542 hg multirevs ARG
542 hg multirevs ARG
543
543
544 multirevs command
544 multirevs command
545
545
546 (use "hg multirevs -h" to show more help)
546 (use "hg multirevs -h" to show more help)
547 [255]
547 [255]
548
548
549
549
550
550
551 $ echo "multirevs = !" >> $HGRCPATH
551 $ echo "multirevs = !" >> $HGRCPATH
552
552
553 Issue811: Problem loading extensions twice (by site and by user)
553 Issue811: Problem loading extensions twice (by site and by user)
554
554
555 $ cat <<EOF >> $HGRCPATH
555 $ cat <<EOF >> $HGRCPATH
556 > mq =
556 > mq =
557 > strip =
557 > strip =
558 > hgext.mq =
558 > hgext.mq =
559 > hgext/mq =
559 > hgext/mq =
560 > EOF
560 > EOF
561
561
562 Show extensions:
562 Show extensions:
563 (note that mq force load strip, also checking it's not loaded twice)
563 (note that mq force load strip, also checking it's not loaded twice)
564
564
565 $ hg debugextensions
565 $ hg debugextensions
566 mq
566 mq
567 strip
567 strip
568
568
569 For extensions, which name matches one of its commands, help
569 For extensions, which name matches one of its commands, help
570 message should ask '-v -e' to get list of built-in aliases
570 message should ask '-v -e' to get list of built-in aliases
571 along with extension help itself
571 along with extension help itself
572
572
573 $ mkdir $TESTTMP/d
573 $ mkdir $TESTTMP/d
574 $ cat > $TESTTMP/d/dodo.py <<EOF
574 $ cat > $TESTTMP/d/dodo.py <<EOF
575 > """
575 > """
576 > This is an awesome 'dodo' extension. It does nothing and
576 > This is an awesome 'dodo' extension. It does nothing and
577 > writes 'Foo foo'
577 > writes 'Foo foo'
578 > """
578 > """
579 > from mercurial import cmdutil, commands
579 > from mercurial import cmdutil, commands
580 > cmdtable = {}
580 > cmdtable = {}
581 > command = cmdutil.command(cmdtable)
581 > command = cmdutil.command(cmdtable)
582 > @command('dodo', [], 'hg dodo')
582 > @command('dodo', [], 'hg dodo')
583 > def dodo(ui, *args, **kwargs):
583 > def dodo(ui, *args, **kwargs):
584 > """Does nothing"""
584 > """Does nothing"""
585 > ui.write("I do nothing. Yay\\n")
585 > ui.write("I do nothing. Yay\\n")
586 > @command('foofoo', [], 'hg foofoo')
586 > @command('foofoo', [], 'hg foofoo')
587 > def foofoo(ui, *args, **kwargs):
587 > def foofoo(ui, *args, **kwargs):
588 > """Writes 'Foo foo'"""
588 > """Writes 'Foo foo'"""
589 > ui.write("Foo foo\\n")
589 > ui.write("Foo foo\\n")
590 > EOF
590 > EOF
591 $ dodopath=$TESTTMP/d/dodo.py
591 $ dodopath=$TESTTMP/d/dodo.py
592
592
593 $ echo "dodo = $dodopath" >> $HGRCPATH
593 $ echo "dodo = $dodopath" >> $HGRCPATH
594
594
595 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
595 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
596 $ hg help -e dodo
596 $ hg help -e dodo
597 dodo extension -
597 dodo extension -
598
598
599 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
599 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
600
600
601 list of commands:
601 list of commands:
602
602
603 dodo Does nothing
603 dodo Does nothing
604 foofoo Writes 'Foo foo'
604 foofoo Writes 'Foo foo'
605
605
606 (use "hg help -v -e dodo" to show built-in aliases and global options)
606 (use "hg help -v -e dodo" to show built-in aliases and global options)
607
607
608 Make sure that '-v -e' prints list of built-in aliases along with
608 Make sure that '-v -e' prints list of built-in aliases along with
609 extension help itself
609 extension help itself
610 $ hg help -v -e dodo
610 $ hg help -v -e dodo
611 dodo extension -
611 dodo extension -
612
612
613 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
613 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
614
614
615 list of commands:
615 list of commands:
616
616
617 dodo Does nothing
617 dodo Does nothing
618 foofoo Writes 'Foo foo'
618 foofoo Writes 'Foo foo'
619
619
620 global options ([+] can be repeated):
620 global options ([+] can be repeated):
621
621
622 -R --repository REPO repository root directory or name of overlay bundle
622 -R --repository REPO repository root directory or name of overlay bundle
623 file
623 file
624 --cwd DIR change working directory
624 --cwd DIR change working directory
625 -y --noninteractive do not prompt, automatically pick the first choice for
625 -y --noninteractive do not prompt, automatically pick the first choice for
626 all prompts
626 all prompts
627 -q --quiet suppress output
627 -q --quiet suppress output
628 -v --verbose enable additional output
628 -v --verbose enable additional output
629 --config CONFIG [+] set/override config option (use 'section.name=value')
629 --config CONFIG [+] set/override config option (use 'section.name=value')
630 --debug enable debugging output
630 --debug enable debugging output
631 --debugger start debugger
631 --debugger start debugger
632 --encoding ENCODE set the charset encoding (default: ascii)
632 --encoding ENCODE set the charset encoding (default: ascii)
633 --encodingmode MODE set the charset encoding mode (default: strict)
633 --encodingmode MODE set the charset encoding mode (default: strict)
634 --traceback always print a traceback on exception
634 --traceback always print a traceback on exception
635 --time time how long the command takes
635 --time time how long the command takes
636 --profile print command execution profile
636 --profile print command execution profile
637 --version output version information and exit
637 --version output version information and exit
638 -h --help display help and exit
638 -h --help display help and exit
639 --hidden consider hidden changesets
639 --hidden consider hidden changesets
640
640
641 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
641 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
642 $ hg help -v dodo
642 $ hg help -v dodo
643 hg dodo
643 hg dodo
644
644
645 Does nothing
645 Does nothing
646
646
647 (use "hg help -e dodo" to show help for the dodo extension)
647 (use "hg help -e dodo" to show help for the dodo extension)
648
648
649 options:
649 options:
650
650
651 --mq operate on patch repository
651 --mq operate on patch repository
652
652
653 global options ([+] can be repeated):
653 global options ([+] can be repeated):
654
654
655 -R --repository REPO repository root directory or name of overlay bundle
655 -R --repository REPO repository root directory or name of overlay bundle
656 file
656 file
657 --cwd DIR change working directory
657 --cwd DIR change working directory
658 -y --noninteractive do not prompt, automatically pick the first choice for
658 -y --noninteractive do not prompt, automatically pick the first choice for
659 all prompts
659 all prompts
660 -q --quiet suppress output
660 -q --quiet suppress output
661 -v --verbose enable additional output
661 -v --verbose enable additional output
662 --config CONFIG [+] set/override config option (use 'section.name=value')
662 --config CONFIG [+] set/override config option (use 'section.name=value')
663 --debug enable debugging output
663 --debug enable debugging output
664 --debugger start debugger
664 --debugger start debugger
665 --encoding ENCODE set the charset encoding (default: ascii)
665 --encoding ENCODE set the charset encoding (default: ascii)
666 --encodingmode MODE set the charset encoding mode (default: strict)
666 --encodingmode MODE set the charset encoding mode (default: strict)
667 --traceback always print a traceback on exception
667 --traceback always print a traceback on exception
668 --time time how long the command takes
668 --time time how long the command takes
669 --profile print command execution profile
669 --profile print command execution profile
670 --version output version information and exit
670 --version output version information and exit
671 -h --help display help and exit
671 -h --help display help and exit
672 --hidden consider hidden changesets
672 --hidden consider hidden changesets
673
673
674 In case when extension name doesn't match any of its commands,
674 In case when extension name doesn't match any of its commands,
675 help message should ask for '-v' to get list of built-in aliases
675 help message should ask for '-v' to get list of built-in aliases
676 along with extension help
676 along with extension help
677 $ cat > $TESTTMP/d/dudu.py <<EOF
677 $ cat > $TESTTMP/d/dudu.py <<EOF
678 > """
678 > """
679 > This is an awesome 'dudu' extension. It does something and
679 > This is an awesome 'dudu' extension. It does something and
680 > also writes 'Beep beep'
680 > also writes 'Beep beep'
681 > """
681 > """
682 > from mercurial import cmdutil, commands
682 > from mercurial import cmdutil, commands
683 > cmdtable = {}
683 > cmdtable = {}
684 > command = cmdutil.command(cmdtable)
684 > command = cmdutil.command(cmdtable)
685 > @command('something', [], 'hg something')
685 > @command('something', [], 'hg something')
686 > def something(ui, *args, **kwargs):
686 > def something(ui, *args, **kwargs):
687 > """Does something"""
687 > """Does something"""
688 > ui.write("I do something. Yaaay\\n")
688 > ui.write("I do something. Yaaay\\n")
689 > @command('beep', [], 'hg beep')
689 > @command('beep', [], 'hg beep')
690 > def beep(ui, *args, **kwargs):
690 > def beep(ui, *args, **kwargs):
691 > """Writes 'Beep beep'"""
691 > """Writes 'Beep beep'"""
692 > ui.write("Beep beep\\n")
692 > ui.write("Beep beep\\n")
693 > EOF
693 > EOF
694 $ dudupath=$TESTTMP/d/dudu.py
694 $ dudupath=$TESTTMP/d/dudu.py
695
695
696 $ echo "dudu = $dudupath" >> $HGRCPATH
696 $ echo "dudu = $dudupath" >> $HGRCPATH
697
697
698 $ hg help -e dudu
698 $ hg help -e dudu
699 dudu extension -
699 dudu extension -
700
700
701 This is an awesome 'dudu' extension. It does something and also writes 'Beep
701 This is an awesome 'dudu' extension. It does something and also writes 'Beep
702 beep'
702 beep'
703
703
704 list of commands:
704 list of commands:
705
705
706 beep Writes 'Beep beep'
706 beep Writes 'Beep beep'
707 something Does something
707 something Does something
708
708
709 (use "hg help -v dudu" to show built-in aliases and global options)
709 (use "hg help -v dudu" to show built-in aliases and global options)
710
710
711 In case when extension name doesn't match any of its commands,
711 In case when extension name doesn't match any of its commands,
712 help options '-v' and '-v -e' should be equivalent
712 help options '-v' and '-v -e' should be equivalent
713 $ hg help -v dudu
713 $ hg help -v dudu
714 dudu extension -
714 dudu extension -
715
715
716 This is an awesome 'dudu' extension. It does something and also writes 'Beep
716 This is an awesome 'dudu' extension. It does something and also writes 'Beep
717 beep'
717 beep'
718
718
719 list of commands:
719 list of commands:
720
720
721 beep Writes 'Beep beep'
721 beep Writes 'Beep beep'
722 something Does something
722 something Does something
723
723
724 global options ([+] can be repeated):
724 global options ([+] can be repeated):
725
725
726 -R --repository REPO repository root directory or name of overlay bundle
726 -R --repository REPO repository root directory or name of overlay bundle
727 file
727 file
728 --cwd DIR change working directory
728 --cwd DIR change working directory
729 -y --noninteractive do not prompt, automatically pick the first choice for
729 -y --noninteractive do not prompt, automatically pick the first choice for
730 all prompts
730 all prompts
731 -q --quiet suppress output
731 -q --quiet suppress output
732 -v --verbose enable additional output
732 -v --verbose enable additional output
733 --config CONFIG [+] set/override config option (use 'section.name=value')
733 --config CONFIG [+] set/override config option (use 'section.name=value')
734 --debug enable debugging output
734 --debug enable debugging output
735 --debugger start debugger
735 --debugger start debugger
736 --encoding ENCODE set the charset encoding (default: ascii)
736 --encoding ENCODE set the charset encoding (default: ascii)
737 --encodingmode MODE set the charset encoding mode (default: strict)
737 --encodingmode MODE set the charset encoding mode (default: strict)
738 --traceback always print a traceback on exception
738 --traceback always print a traceback on exception
739 --time time how long the command takes
739 --time time how long the command takes
740 --profile print command execution profile
740 --profile print command execution profile
741 --version output version information and exit
741 --version output version information and exit
742 -h --help display help and exit
742 -h --help display help and exit
743 --hidden consider hidden changesets
743 --hidden consider hidden changesets
744
744
745 $ hg help -v -e dudu
745 $ hg help -v -e dudu
746 dudu extension -
746 dudu extension -
747
747
748 This is an awesome 'dudu' extension. It does something and also writes 'Beep
748 This is an awesome 'dudu' extension. It does something and also writes 'Beep
749 beep'
749 beep'
750
750
751 list of commands:
751 list of commands:
752
752
753 beep Writes 'Beep beep'
753 beep Writes 'Beep beep'
754 something Does something
754 something Does something
755
755
756 global options ([+] can be repeated):
756 global options ([+] can be repeated):
757
757
758 -R --repository REPO repository root directory or name of overlay bundle
758 -R --repository REPO repository root directory or name of overlay bundle
759 file
759 file
760 --cwd DIR change working directory
760 --cwd DIR change working directory
761 -y --noninteractive do not prompt, automatically pick the first choice for
761 -y --noninteractive do not prompt, automatically pick the first choice for
762 all prompts
762 all prompts
763 -q --quiet suppress output
763 -q --quiet suppress output
764 -v --verbose enable additional output
764 -v --verbose enable additional output
765 --config CONFIG [+] set/override config option (use 'section.name=value')
765 --config CONFIG [+] set/override config option (use 'section.name=value')
766 --debug enable debugging output
766 --debug enable debugging output
767 --debugger start debugger
767 --debugger start debugger
768 --encoding ENCODE set the charset encoding (default: ascii)
768 --encoding ENCODE set the charset encoding (default: ascii)
769 --encodingmode MODE set the charset encoding mode (default: strict)
769 --encodingmode MODE set the charset encoding mode (default: strict)
770 --traceback always print a traceback on exception
770 --traceback always print a traceback on exception
771 --time time how long the command takes
771 --time time how long the command takes
772 --profile print command execution profile
772 --profile print command execution profile
773 --version output version information and exit
773 --version output version information and exit
774 -h --help display help and exit
774 -h --help display help and exit
775 --hidden consider hidden changesets
775 --hidden consider hidden changesets
776
776
777 Disabled extension commands:
777 Disabled extension commands:
778
778
779 $ ORGHGRCPATH=$HGRCPATH
779 $ ORGHGRCPATH=$HGRCPATH
780 $ HGRCPATH=
780 $ HGRCPATH=
781 $ export HGRCPATH
781 $ export HGRCPATH
782 $ hg help email
782 $ hg help email
783 'email' is provided by the following extension:
783 'email' is provided by the following extension:
784
784
785 patchbomb command to send changesets as (a series of) patch emails
785 patchbomb command to send changesets as (a series of) patch emails
786
786
787 (use "hg help extensions" for information on enabling extensions)
787 (use "hg help extensions" for information on enabling extensions)
788
788
789
789
790 $ hg qdel
790 $ hg qdel
791 hg: unknown command 'qdel'
791 hg: unknown command 'qdel'
792 'qdelete' is provided by the following extension:
792 'qdelete' is provided by the following extension:
793
793
794 mq manage a stack of patches
794 mq manage a stack of patches
795
795
796 (use "hg help extensions" for information on enabling extensions)
796 (use "hg help extensions" for information on enabling extensions)
797 [255]
797 [255]
798
798
799
799
800 $ hg churn
800 $ hg churn
801 hg: unknown command 'churn'
801 hg: unknown command 'churn'
802 'churn' is provided by the following extension:
802 'churn' is provided by the following extension:
803
803
804 churn command to display statistics about repository history
804 churn command to display statistics about repository history
805
805
806 (use "hg help extensions" for information on enabling extensions)
806 (use "hg help extensions" for information on enabling extensions)
807 [255]
807 [255]
808
808
809
809
810
810
811 Disabled extensions:
811 Disabled extensions:
812
812
813 $ hg help churn
813 $ hg help churn
814 churn extension - command to display statistics about repository history
814 churn extension - command to display statistics about repository history
815
815
816 (use "hg help extensions" for information on enabling extensions)
816 (use "hg help extensions" for information on enabling extensions)
817
817
818 $ hg help patchbomb
818 $ hg help patchbomb
819 patchbomb extension - command to send changesets as (a series of) patch emails
819 patchbomb extension - command to send changesets as (a series of) patch emails
820
820
821 (use "hg help extensions" for information on enabling extensions)
821 (use "hg help extensions" for information on enabling extensions)
822
822
823
823
824 Broken disabled extension and command:
824 Broken disabled extension and command:
825
825
826 $ mkdir hgext
826 $ mkdir hgext
827 $ echo > hgext/__init__.py
827 $ echo > hgext/__init__.py
828 $ cat > hgext/broken.py <<EOF
828 $ cat > hgext/broken.py <<EOF
829 > "broken extension'
829 > "broken extension'
830 > EOF
830 > EOF
831 $ cat > path.py <<EOF
831 $ cat > path.py <<EOF
832 > import os, sys
832 > import os, sys
833 > sys.path.insert(0, os.environ['HGEXTPATH'])
833 > sys.path.insert(0, os.environ['HGEXTPATH'])
834 > EOF
834 > EOF
835 $ HGEXTPATH=`pwd`
835 $ HGEXTPATH=`pwd`
836 $ export HGEXTPATH
836 $ export HGEXTPATH
837
837
838 $ hg --config extensions.path=./path.py help broken
838 $ hg --config extensions.path=./path.py help broken
839 broken extension - (no help text available)
839 broken extension - (no help text available)
840
840
841 (use "hg help extensions" for information on enabling extensions)
841 (use "hg help extensions" for information on enabling extensions)
842
842
843
843
844 $ cat > hgext/forest.py <<EOF
844 $ cat > hgext/forest.py <<EOF
845 > cmdtable = None
845 > cmdtable = None
846 > EOF
846 > EOF
847 $ hg --config extensions.path=./path.py help foo > /dev/null
847 $ hg --config extensions.path=./path.py help foo > /dev/null
848 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
848 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
849 abort: no such help topic: foo
849 abort: no such help topic: foo
850 (try "hg help --keyword foo")
850 (try "hg help --keyword foo")
851 [255]
851 [255]
852
852
853 $ cat > throw.py <<EOF
853 $ cat > throw.py <<EOF
854 > from mercurial import cmdutil, commands, util
854 > from mercurial import cmdutil, commands, util
855 > cmdtable = {}
855 > cmdtable = {}
856 > command = cmdutil.command(cmdtable)
856 > command = cmdutil.command(cmdtable)
857 > class Bogon(Exception): pass
857 > class Bogon(Exception): pass
858 > @command('throw', [], 'hg throw', norepo=True)
858 > @command('throw', [], 'hg throw', norepo=True)
859 > def throw(ui, **opts):
859 > def throw(ui, **opts):
860 > """throws an exception"""
860 > """throws an exception"""
861 > raise Bogon()
861 > raise Bogon()
862 > EOF
862 > EOF
863
863
864 No declared supported version, extension complains:
864 No declared supported version, extension complains:
865 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
865 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
866 ** Unknown exception encountered with possibly-broken third-party extension throw
866 ** Unknown exception encountered with possibly-broken third-party extension throw
867 ** which supports versions unknown of Mercurial.
867 ** which supports versions unknown of Mercurial.
868 ** Please disable throw and try your action again.
868 ** Please disable throw and try your action again.
869 ** If that fixes the bug please report it to the extension author.
869 ** If that fixes the bug please report it to the extension author.
870 ** Python * (glob)
870 ** Python * (glob)
871 ** Mercurial Distributed SCM * (glob)
871 ** Mercurial Distributed SCM * (glob)
872 ** Extensions loaded: throw
872 ** Extensions loaded: throw
873
873
874 empty declaration of supported version, extension complains:
874 empty declaration of supported version, extension complains:
875 $ echo "testedwith = ''" >> throw.py
875 $ echo "testedwith = ''" >> throw.py
876 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
876 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
877 ** Unknown exception encountered with possibly-broken third-party extension throw
877 ** Unknown exception encountered with possibly-broken third-party extension throw
878 ** which supports versions unknown of Mercurial.
878 ** which supports versions unknown of Mercurial.
879 ** Please disable throw and try your action again.
879 ** Please disable throw and try your action again.
880 ** If that fixes the bug please report it to the extension author.
880 ** If that fixes the bug please report it to the extension author.
881 ** Python * (glob)
881 ** Python * (glob)
882 ** Mercurial Distributed SCM (*) (glob)
882 ** Mercurial Distributed SCM (*) (glob)
883 ** Extensions loaded: throw
883 ** Extensions loaded: throw
884
884
885 If the extension specifies a buglink, show that:
885 If the extension specifies a buglink, show that:
886 $ echo 'buglink = "http://example.com/bts"' >> throw.py
886 $ echo 'buglink = "http://example.com/bts"' >> throw.py
887 $ rm -f throw.pyc throw.pyo
887 $ rm -f throw.pyc throw.pyo
888 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
888 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
889 ** Unknown exception encountered with possibly-broken third-party extension throw
889 ** Unknown exception encountered with possibly-broken third-party extension throw
890 ** which supports versions unknown of Mercurial.
890 ** which supports versions unknown of Mercurial.
891 ** Please disable throw and try your action again.
891 ** Please disable throw and try your action again.
892 ** If that fixes the bug please report it to http://example.com/bts
892 ** If that fixes the bug please report it to http://example.com/bts
893 ** Python * (glob)
893 ** Python * (glob)
894 ** Mercurial Distributed SCM (*) (glob)
894 ** Mercurial Distributed SCM (*) (glob)
895 ** Extensions loaded: throw
895 ** Extensions loaded: throw
896
896
897 If the extensions declare outdated versions, accuse the older extension first:
897 If the extensions declare outdated versions, accuse the older extension first:
898 $ echo "from mercurial import util" >> older.py
898 $ echo "from mercurial import util" >> older.py
899 $ echo "util.version = lambda:'2.2'" >> older.py
899 $ echo "util.version = lambda:'2.2'" >> older.py
900 $ echo "testedwith = '1.9.3'" >> older.py
900 $ echo "testedwith = '1.9.3'" >> older.py
901 $ echo "testedwith = '2.1.1'" >> throw.py
901 $ echo "testedwith = '2.1.1'" >> throw.py
902 $ rm -f throw.pyc throw.pyo
902 $ rm -f throw.pyc throw.pyo
903 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
903 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
904 > throw 2>&1 | egrep '^\*\*'
904 > throw 2>&1 | egrep '^\*\*'
905 ** Unknown exception encountered with possibly-broken third-party extension older
905 ** Unknown exception encountered with possibly-broken third-party extension older
906 ** which supports versions 1.9 of Mercurial.
906 ** which supports versions 1.9 of Mercurial.
907 ** Please disable older and try your action again.
907 ** Please disable older and try your action again.
908 ** If that fixes the bug please report it to the extension author.
908 ** If that fixes the bug please report it to the extension author.
909 ** Python * (glob)
909 ** Python * (glob)
910 ** Mercurial Distributed SCM (version 2.2)
910 ** Mercurial Distributed SCM (version 2.2)
911 ** Extensions loaded: throw, older
911 ** Extensions loaded: throw, older
912
912
913 One extension only tested with older, one only with newer versions:
913 One extension only tested with older, one only with newer versions:
914 $ echo "util.version = lambda:'2.1'" >> older.py
914 $ echo "util.version = lambda:'2.1'" >> older.py
915 $ rm -f older.pyc older.pyo
915 $ rm -f older.pyc older.pyo
916 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
916 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
917 > throw 2>&1 | egrep '^\*\*'
917 > throw 2>&1 | egrep '^\*\*'
918 ** Unknown exception encountered with possibly-broken third-party extension older
918 ** Unknown exception encountered with possibly-broken third-party extension older
919 ** which supports versions 1.9 of Mercurial.
919 ** which supports versions 1.9 of Mercurial.
920 ** Please disable older and try your action again.
920 ** Please disable older and try your action again.
921 ** If that fixes the bug please report it to the extension author.
921 ** If that fixes the bug please report it to the extension author.
922 ** Python * (glob)
922 ** Python * (glob)
923 ** Mercurial Distributed SCM (version 2.1)
923 ** Mercurial Distributed SCM (version 2.1)
924 ** Extensions loaded: throw, older
924 ** Extensions loaded: throw, older
925
925
926 Older extension is tested with current version, the other only with newer:
926 Older extension is tested with current version, the other only with newer:
927 $ echo "util.version = lambda:'1.9.3'" >> older.py
927 $ echo "util.version = lambda:'1.9.3'" >> older.py
928 $ rm -f older.pyc older.pyo
928 $ rm -f older.pyc older.pyo
929 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
929 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
930 > throw 2>&1 | egrep '^\*\*'
930 > throw 2>&1 | egrep '^\*\*'
931 ** Unknown exception encountered with possibly-broken third-party extension throw
931 ** Unknown exception encountered with possibly-broken third-party extension throw
932 ** which supports versions 2.1 of Mercurial.
932 ** which supports versions 2.1 of Mercurial.
933 ** Please disable throw and try your action again.
933 ** Please disable throw and try your action again.
934 ** If that fixes the bug please report it to http://example.com/bts
934 ** If that fixes the bug please report it to http://example.com/bts
935 ** Python * (glob)
935 ** Python * (glob)
936 ** Mercurial Distributed SCM (version 1.9.3)
936 ** Mercurial Distributed SCM (version 1.9.3)
937 ** Extensions loaded: throw, older
937 ** Extensions loaded: throw, older
938
938
939 Ability to point to a different point
939 Ability to point to a different point
940 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
940 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
941 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
941 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
942 ** unknown exception encountered, please report by visiting
942 ** unknown exception encountered, please report by visiting
943 ** Your Local Goat Lenders
943 ** Your Local Goat Lenders
944 ** Python * (glob)
944 ** Python * (glob)
945 ** Mercurial Distributed SCM (*) (glob)
945 ** Mercurial Distributed SCM (*) (glob)
946 ** Extensions loaded: throw, older
946 ** Extensions loaded: throw, older
947
947
948 Declare the version as supporting this hg version, show regular bts link:
948 Declare the version as supporting this hg version, show regular bts link:
949 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
949 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
950 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
950 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
951 $ if [ -z "$hgver" ]; then
951 $ if [ -z "$hgver" ]; then
952 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
952 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
953 > fi
953 > fi
954 $ rm -f throw.pyc throw.pyo
954 $ rm -f throw.pyc throw.pyo
955 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
955 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
956 ** unknown exception encountered, please report by visiting
956 ** unknown exception encountered, please report by visiting
957 ** https://mercurial-scm.org/wiki/BugTracker
957 ** https://mercurial-scm.org/wiki/BugTracker
958 ** Python * (glob)
958 ** Python * (glob)
959 ** Mercurial Distributed SCM (*) (glob)
959 ** Mercurial Distributed SCM (*) (glob)
960 ** Extensions loaded: throw
960 ** Extensions loaded: throw
961
961
962 Patch version is ignored during compatibility check
962 Patch version is ignored during compatibility check
963 $ echo "testedwith = '3.2'" >> throw.py
963 $ echo "testedwith = '3.2'" >> throw.py
964 $ echo "util.version = lambda:'3.2.2'" >> throw.py
964 $ echo "util.version = lambda:'3.2.2'" >> throw.py
965 $ rm -f throw.pyc throw.pyo
965 $ rm -f throw.pyc throw.pyo
966 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
966 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
967 ** unknown exception encountered, please report by visiting
967 ** unknown exception encountered, please report by visiting
968 ** https://mercurial-scm.org/wiki/BugTracker
968 ** https://mercurial-scm.org/wiki/BugTracker
969 ** Python * (glob)
969 ** Python * (glob)
970 ** Mercurial Distributed SCM (*) (glob)
970 ** Mercurial Distributed SCM (*) (glob)
971 ** Extensions loaded: throw
971 ** Extensions loaded: throw
972
972
973 Test version number support in 'hg version':
973 Test version number support in 'hg version':
974 $ echo '__version__ = (1, 2, 3)' >> throw.py
974 $ echo '__version__ = (1, 2, 3)' >> throw.py
975 $ rm -f throw.pyc throw.pyo
975 $ rm -f throw.pyc throw.pyo
976 $ hg version -v
976 $ hg version -v
977 Mercurial Distributed SCM (version *) (glob)
977 Mercurial Distributed SCM (version *) (glob)
978 (see https://mercurial-scm.org for more information)
978 (see https://mercurial-scm.org for more information)
979
979
980 Copyright (C) 2005-* Matt Mackall and others (glob)
980 Copyright (C) 2005-* Matt Mackall and others (glob)
981 This is free software; see the source for copying conditions. There is NO
981 This is free software; see the source for copying conditions. There is NO
982 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
982 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
983
983
984 Enabled extensions:
984 Enabled extensions:
985
985
986
986
987 $ hg version -v --config extensions.throw=throw.py
987 $ hg version -v --config extensions.throw=throw.py
988 Mercurial Distributed SCM (version *) (glob)
988 Mercurial Distributed SCM (version *) (glob)
989 (see https://mercurial-scm.org for more information)
989 (see https://mercurial-scm.org for more information)
990
990
991 Copyright (C) 2005-* Matt Mackall and others (glob)
991 Copyright (C) 2005-* Matt Mackall and others (glob)
992 This is free software; see the source for copying conditions. There is NO
992 This is free software; see the source for copying conditions. There is NO
993 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
993 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
994
994
995 Enabled extensions:
995 Enabled extensions:
996
996
997 throw 1.2.3
997 throw 1.2.3
998 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
998 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
999 $ rm -f throw.pyc throw.pyo
999 $ rm -f throw.pyc throw.pyo
1000 $ hg version -v --config extensions.throw=throw.py
1000 $ hg version -v --config extensions.throw=throw.py
1001 Mercurial Distributed SCM (version *) (glob)
1001 Mercurial Distributed SCM (version *) (glob)
1002 (see https://mercurial-scm.org for more information)
1002 (see https://mercurial-scm.org for more information)
1003
1003
1004 Copyright (C) 2005-* Matt Mackall and others (glob)
1004 Copyright (C) 2005-* Matt Mackall and others (glob)
1005 This is free software; see the source for copying conditions. There is NO
1005 This is free software; see the source for copying conditions. There is NO
1006 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1006 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1007
1007
1008 Enabled extensions:
1008 Enabled extensions:
1009
1009
1010 throw 1.twentythree
1010 throw 1.twentythree
1011
1011
1012 Refuse to load extensions with minimum version requirements
1013
1014 $ cat > minversion1.py << EOF
1015 > from mercurial import util
1016 > util.version = lambda: '3.5.2'
1017 > minimumhgversion = '3.6'
1018 > EOF
1019 $ hg --config extensions.minversion=minversion1.py version
1020 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1021 Mercurial Distributed SCM (version 3.5.2)
1022 (see https://mercurial-scm.org for more information)
1023
1024 Copyright (C) 2005-* Matt Mackall and others (glob)
1025 This is free software; see the source for copying conditions. There is NO
1026 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1027
1028 $ cat > minversion2.py << EOF
1029 > from mercurial import util
1030 > util.version = lambda: '3.6'
1031 > minimumhgversion = '3.7'
1032 > EOF
1033 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1034 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1035
1036 Can load version that is only off by point release
1037
1038 $ cat > minversion2.py << EOF
1039 > from mercurial import util
1040 > util.version = lambda: '3.6.1'
1041 > minimumhgversion = '3.6'
1042 > EOF
1043 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1044 [1]
1045
1046 Can load minimum version identical to current
1047
1048 $ cat > minversion3.py << EOF
1049 > from mercurial import util
1050 > util.version = lambda: '3.5'
1051 > minimumhgversion = '3.5'
1052 > EOF
1053 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1054 [1]
1055
1012 Restore HGRCPATH
1056 Restore HGRCPATH
1013
1057
1014 $ HGRCPATH=$ORGHGRCPATH
1058 $ HGRCPATH=$ORGHGRCPATH
1015 $ export HGRCPATH
1059 $ export HGRCPATH
1016
1060
1017 Commands handling multiple repositories at a time should invoke only
1061 Commands handling multiple repositories at a time should invoke only
1018 "reposetup()" of extensions enabling in the target repository.
1062 "reposetup()" of extensions enabling in the target repository.
1019
1063
1020 $ mkdir reposetup-test
1064 $ mkdir reposetup-test
1021 $ cd reposetup-test
1065 $ cd reposetup-test
1022
1066
1023 $ cat > $TESTTMP/reposetuptest.py <<EOF
1067 $ cat > $TESTTMP/reposetuptest.py <<EOF
1024 > from mercurial import extensions
1068 > from mercurial import extensions
1025 > def reposetup(ui, repo):
1069 > def reposetup(ui, repo):
1026 > ui.write('reposetup() for %s\n' % (repo.root))
1070 > ui.write('reposetup() for %s\n' % (repo.root))
1027 > EOF
1071 > EOF
1028 $ hg init src
1072 $ hg init src
1029 $ echo a > src/a
1073 $ echo a > src/a
1030 $ hg -R src commit -Am '#0 at src/a'
1074 $ hg -R src commit -Am '#0 at src/a'
1031 adding a
1075 adding a
1032 $ echo '[extensions]' >> src/.hg/hgrc
1076 $ echo '[extensions]' >> src/.hg/hgrc
1033 $ echo '# enable extension locally' >> src/.hg/hgrc
1077 $ echo '# enable extension locally' >> src/.hg/hgrc
1034 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1078 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1035 $ hg -R src status
1079 $ hg -R src status
1036 reposetup() for $TESTTMP/reposetup-test/src (glob)
1080 reposetup() for $TESTTMP/reposetup-test/src (glob)
1037
1081
1038 $ hg clone -U src clone-dst1
1082 $ hg clone -U src clone-dst1
1039 reposetup() for $TESTTMP/reposetup-test/src (glob)
1083 reposetup() for $TESTTMP/reposetup-test/src (glob)
1040 $ hg init push-dst1
1084 $ hg init push-dst1
1041 $ hg -q -R src push push-dst1
1085 $ hg -q -R src push push-dst1
1042 reposetup() for $TESTTMP/reposetup-test/src (glob)
1086 reposetup() for $TESTTMP/reposetup-test/src (glob)
1043 $ hg init pull-src1
1087 $ hg init pull-src1
1044 $ hg -q -R pull-src1 pull src
1088 $ hg -q -R pull-src1 pull src
1045 reposetup() for $TESTTMP/reposetup-test/src (glob)
1089 reposetup() for $TESTTMP/reposetup-test/src (glob)
1046
1090
1047 $ cat <<EOF >> $HGRCPATH
1091 $ cat <<EOF >> $HGRCPATH
1048 > [extensions]
1092 > [extensions]
1049 > # disable extension globally and explicitly
1093 > # disable extension globally and explicitly
1050 > reposetuptest = !
1094 > reposetuptest = !
1051 > EOF
1095 > EOF
1052 $ hg clone -U src clone-dst2
1096 $ hg clone -U src clone-dst2
1053 reposetup() for $TESTTMP/reposetup-test/src (glob)
1097 reposetup() for $TESTTMP/reposetup-test/src (glob)
1054 $ hg init push-dst2
1098 $ hg init push-dst2
1055 $ hg -q -R src push push-dst2
1099 $ hg -q -R src push push-dst2
1056 reposetup() for $TESTTMP/reposetup-test/src (glob)
1100 reposetup() for $TESTTMP/reposetup-test/src (glob)
1057 $ hg init pull-src2
1101 $ hg init pull-src2
1058 $ hg -q -R pull-src2 pull src
1102 $ hg -q -R pull-src2 pull src
1059 reposetup() for $TESTTMP/reposetup-test/src (glob)
1103 reposetup() for $TESTTMP/reposetup-test/src (glob)
1060
1104
1061 $ cat <<EOF >> $HGRCPATH
1105 $ cat <<EOF >> $HGRCPATH
1062 > [extensions]
1106 > [extensions]
1063 > # enable extension globally
1107 > # enable extension globally
1064 > reposetuptest = $TESTTMP/reposetuptest.py
1108 > reposetuptest = $TESTTMP/reposetuptest.py
1065 > EOF
1109 > EOF
1066 $ hg clone -U src clone-dst3
1110 $ hg clone -U src clone-dst3
1067 reposetup() for $TESTTMP/reposetup-test/src (glob)
1111 reposetup() for $TESTTMP/reposetup-test/src (glob)
1068 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1112 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1069 $ hg init push-dst3
1113 $ hg init push-dst3
1070 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1114 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1071 $ hg -q -R src push push-dst3
1115 $ hg -q -R src push push-dst3
1072 reposetup() for $TESTTMP/reposetup-test/src (glob)
1116 reposetup() for $TESTTMP/reposetup-test/src (glob)
1073 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1117 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1074 $ hg init pull-src3
1118 $ hg init pull-src3
1075 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1119 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1076 $ hg -q -R pull-src3 pull src
1120 $ hg -q -R pull-src3 pull src
1077 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1121 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1078 reposetup() for $TESTTMP/reposetup-test/src (glob)
1122 reposetup() for $TESTTMP/reposetup-test/src (glob)
1079
1123
1080 $ echo '[extensions]' >> src/.hg/hgrc
1124 $ echo '[extensions]' >> src/.hg/hgrc
1081 $ echo '# disable extension locally' >> src/.hg/hgrc
1125 $ echo '# disable extension locally' >> src/.hg/hgrc
1082 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1126 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1083 $ hg clone -U src clone-dst4
1127 $ hg clone -U src clone-dst4
1084 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1128 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1085 $ hg init push-dst4
1129 $ hg init push-dst4
1086 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1130 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1087 $ hg -q -R src push push-dst4
1131 $ hg -q -R src push push-dst4
1088 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1132 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1089 $ hg init pull-src4
1133 $ hg init pull-src4
1090 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1134 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1091 $ hg -q -R pull-src4 pull src
1135 $ hg -q -R pull-src4 pull src
1092 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1136 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1093
1137
1094 disabling in command line overlays with all configuration
1138 disabling in command line overlays with all configuration
1095 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1139 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1096 $ hg --config extensions.reposetuptest=! init push-dst5
1140 $ hg --config extensions.reposetuptest=! init push-dst5
1097 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1141 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1098 $ hg --config extensions.reposetuptest=! init pull-src5
1142 $ hg --config extensions.reposetuptest=! init pull-src5
1099 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1143 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1100
1144
1101 $ cat <<EOF >> $HGRCPATH
1145 $ cat <<EOF >> $HGRCPATH
1102 > [extensions]
1146 > [extensions]
1103 > # disable extension globally and explicitly
1147 > # disable extension globally and explicitly
1104 > reposetuptest = !
1148 > reposetuptest = !
1105 > EOF
1149 > EOF
1106 $ hg init parent
1150 $ hg init parent
1107 $ hg init parent/sub1
1151 $ hg init parent/sub1
1108 $ echo 1 > parent/sub1/1
1152 $ echo 1 > parent/sub1/1
1109 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1153 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1110 adding 1
1154 adding 1
1111 $ hg init parent/sub2
1155 $ hg init parent/sub2
1112 $ hg init parent/sub2/sub21
1156 $ hg init parent/sub2/sub21
1113 $ echo 21 > parent/sub2/sub21/21
1157 $ echo 21 > parent/sub2/sub21/21
1114 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1158 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1115 adding 21
1159 adding 21
1116 $ cat > parent/sub2/.hgsub <<EOF
1160 $ cat > parent/sub2/.hgsub <<EOF
1117 > sub21 = sub21
1161 > sub21 = sub21
1118 > EOF
1162 > EOF
1119 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1163 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1120 adding .hgsub
1164 adding .hgsub
1121 $ hg init parent/sub3
1165 $ hg init parent/sub3
1122 $ echo 3 > parent/sub3/3
1166 $ echo 3 > parent/sub3/3
1123 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1167 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1124 adding 3
1168 adding 3
1125 $ cat > parent/.hgsub <<EOF
1169 $ cat > parent/.hgsub <<EOF
1126 > sub1 = sub1
1170 > sub1 = sub1
1127 > sub2 = sub2
1171 > sub2 = sub2
1128 > sub3 = sub3
1172 > sub3 = sub3
1129 > EOF
1173 > EOF
1130 $ hg -R parent commit -Am '#0 at parent'
1174 $ hg -R parent commit -Am '#0 at parent'
1131 adding .hgsub
1175 adding .hgsub
1132 $ echo '[extensions]' >> parent/.hg/hgrc
1176 $ echo '[extensions]' >> parent/.hg/hgrc
1133 $ echo '# enable extension locally' >> parent/.hg/hgrc
1177 $ echo '# enable extension locally' >> parent/.hg/hgrc
1134 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1178 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1135 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1179 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1136 $ hg -R parent status -S -A
1180 $ hg -R parent status -S -A
1137 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1181 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1138 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1182 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1139 C .hgsub
1183 C .hgsub
1140 C .hgsubstate
1184 C .hgsubstate
1141 C sub1/1
1185 C sub1/1
1142 C sub2/.hgsub
1186 C sub2/.hgsub
1143 C sub2/.hgsubstate
1187 C sub2/.hgsubstate
1144 C sub2/sub21/21
1188 C sub2/sub21/21
1145 C sub3/3
1189 C sub3/3
1146
1190
1147 $ cd ..
1191 $ cd ..
1148
1192
1149 Test synopsis and docstring extending
1193 Test synopsis and docstring extending
1150
1194
1151 $ hg init exthelp
1195 $ hg init exthelp
1152 $ cat > exthelp.py <<EOF
1196 $ cat > exthelp.py <<EOF
1153 > from mercurial import commands, extensions
1197 > from mercurial import commands, extensions
1154 > def exbookmarks(orig, *args, **opts):
1198 > def exbookmarks(orig, *args, **opts):
1155 > return orig(*args, **opts)
1199 > return orig(*args, **opts)
1156 > def uisetup(ui):
1200 > def uisetup(ui):
1157 > synopsis = ' GREPME [--foo] [-x]'
1201 > synopsis = ' GREPME [--foo] [-x]'
1158 > docstring = '''
1202 > docstring = '''
1159 > GREPME make sure that this is in the help!
1203 > GREPME make sure that this is in the help!
1160 > '''
1204 > '''
1161 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1205 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1162 > synopsis, docstring)
1206 > synopsis, docstring)
1163 > EOF
1207 > EOF
1164 $ abspath=`pwd`/exthelp.py
1208 $ abspath=`pwd`/exthelp.py
1165 $ echo '[extensions]' >> $HGRCPATH
1209 $ echo '[extensions]' >> $HGRCPATH
1166 $ echo "exthelp = $abspath" >> $HGRCPATH
1210 $ echo "exthelp = $abspath" >> $HGRCPATH
1167 $ cd exthelp
1211 $ cd exthelp
1168 $ hg help bookmarks | grep GREPME
1212 $ hg help bookmarks | grep GREPME
1169 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1213 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1170 GREPME make sure that this is in the help!
1214 GREPME make sure that this is in the help!
1171
1215
General Comments 0
You need to be logged in to leave comments. Login now