diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -164,8 +164,8 @@ obj = _peerlookup(path).instance(ui, path, create, intents=intents) ui = getattr(obj, "ui", ui) if ui.configbool('devel', 'debug.extensions'): - log = functools.partial( - ui.debug, 'debug.extensions: ', label='debug.extensions') + log = lambda msg, *values: ui.debug('debug.extensions: ', + msg % values, label='debug.extensions') else: log = lambda *a, **kw: None for f in presetupfuncs or []: @@ -175,7 +175,9 @@ log(' - running reposetup for %s\n' % (name,)) hook = getattr(module, 'reposetup', None) if hook: - hook(ui, obj) + with util.timedcm('reposetup %r', name) as stats: + hook(ui, obj) + log(' > reposetup for %r took %s\n', name, stats) if not obj.local(): for f in wirepeersetupfuncs: f(ui, obj) diff --git a/tests/test-extension.t b/tests/test-extension-timing.t copy from tests/test-extension.t copy to tests/test-extension-timing.t --- a/tests/test-extension.t +++ b/tests/test-extension-timing.t @@ -40,1716 +40,51 @@ $ echo '[extensions]' >> $HGRCPATH $ echo "foobar = $abspath" >> $HGRCPATH - $ hg foo + +Test extension setup timings + + $ hg foo --traceback --config devel.debug.extensions=yes --debug 2>&1 + debug.extensions: loading extensions + debug.extensions: - processing 1 entries + debug.extensions: - loading extension: 'foobar' + debug.extensions: > 'foobar' extension loaded in * (glob) + debug.extensions: - validating extension tables: 'foobar' + debug.extensions: - invoking registered callbacks: 'foobar' + debug.extensions: > callbacks completed in * (glob) + debug.extensions: > loaded 1 extensions, total time * (glob) + debug.extensions: - loading configtable attributes + debug.extensions: - executing uisetup hooks + debug.extensions: - running uisetup for 'foobar' + uisetup called [debug] uisetup called uisetup called [status] - reposetup called for a - ui == repo.ui - reposetup called for a (chg !) - ui == repo.ui (chg !) - Foo - $ hg foo --quiet - uisetup called (no-chg !) - reposetup called for a (chg !) - ui == repo.ui - Foo - $ hg foo --debug - uisetup called [debug] (no-chg !) - uisetup called (no-chg !) - uisetup called [status] (no-chg !) - reposetup called for a (chg !) - ui == repo.ui - Foo - - $ cd .. - $ hg clone a b - uisetup called (no-chg !) - uisetup called [status] (no-chg !) - reposetup called for a - ui == repo.ui - reposetup called for b - ui == repo.ui - updating to branch default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - - $ hg bar - uisetup called (no-chg !) - uisetup called [status] (no-chg !) - Bar - $ echo 'foobar = !' >> $HGRCPATH - -module/__init__.py-style - - $ echo "barfoo = $barfoopath" >> $HGRCPATH - $ cd a - $ hg foo - uisetup called - uisetup called [status] + debug.extensions: > uisetup for 'foobar' took * (glob) + debug.extensions: - executing extsetup hooks + debug.extensions: - running extsetup for 'foobar' + debug.extensions: > extsetup for 'foobar' took * (glob) + debug.extensions: - executing remaining aftercallbacks + debug.extensions: > remaining aftercallbacks completed in * (glob) + debug.extensions: - loading extension registration objects + debug.extensions: > extension registration object loading took * (glob) + debug.extensions: extension loading complete + debug.extensions: loading additional extensions + debug.extensions: - processing 1 entries + debug.extensions: > loaded 0 extensions, total time * (glob) + debug.extensions: - loading configtable attributes + debug.extensions: - executing uisetup hooks + debug.extensions: - executing extsetup hooks + debug.extensions: - executing remaining aftercallbacks + debug.extensions: > remaining aftercallbacks completed in * (glob) + debug.extensions: - loading extension registration objects + debug.extensions: > extension registration object loading took * (glob) + debug.extensions: extension loading complete + debug.extensions: - executing reposetup hooks + debug.extensions: - running reposetup for foobar reposetup called for a ui == repo.ui - reposetup called for a (chg !) - ui == repo.ui (chg !) + debug.extensions: > reposetup for 'foobar' took * (glob) Foo - $ echo 'barfoo = !' >> $HGRCPATH - -Check that extensions are loaded in phases: - - $ cat > foo.py < import os - > name = os.path.basename(__file__).rsplit('.', 1)[0] - > print("1) %s imported" % name) - > def uisetup(ui): - > print("2) %s uisetup" % name) - > def extsetup(): - > print("3) %s extsetup" % name) - > def reposetup(ui, repo): - > print("4) %s reposetup" % name) - > - > # custom predicate to check registration of functions at loading - > from mercurial import ( - > registrar, - > smartset, - > ) - > revsetpredicate = registrar.revsetpredicate() - > @revsetpredicate(name, safe=True) # safe=True for query via hgweb - > def custompredicate(repo, subset, x): - > return smartset.baseset([r for r in subset if r in {0}]) - > EOF - - $ cp foo.py bar.py - $ echo 'foo = foo.py' >> $HGRCPATH - $ echo 'bar = bar.py' >> $HGRCPATH - -Check normal command's load order of extensions and registration of functions - - $ hg log -r "foo() and bar()" -q - 1) foo imported - 1) bar imported - 2) foo uisetup - 2) bar uisetup - 3) foo extsetup - 3) bar extsetup - 4) foo reposetup - 4) bar reposetup - 0:c24b9ac61126 - -Check hgweb's load order of extensions and registration of functions - - $ cat > hgweb.cgi < #!$PYTHON - > from mercurial import demandimport; demandimport.enable() - > from mercurial.hgweb import hgweb - > from mercurial.hgweb import wsgicgi - > application = hgweb('.', 'test repo') - > wsgicgi.launch(application) - > EOF - $ . "$TESTDIR/cgienv" - - $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \ - > | grep '^[0-9]) ' # ignores HTML output - 1) foo imported - 1) bar imported - 2) foo uisetup - 2) bar uisetup - 3) foo extsetup - 3) bar extsetup - 4) foo reposetup - 4) bar reposetup - -(check that revset predicate foo() and bar() are available) - -#if msys - $ PATH_INFO='//shortlog' -#else - $ PATH_INFO='/shortlog' -#endif - $ export PATH_INFO - $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \ - > | grep '' - add file - - $ echo 'foo = !' >> $HGRCPATH - $ echo 'bar = !' >> $HGRCPATH - -Check "from __future__ import absolute_import" support for external libraries - -#if windows - $ PATHSEP=";" -#else - $ PATHSEP=":" -#endif - $ export PATHSEP - - $ mkdir $TESTTMP/libroot - $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py - $ mkdir $TESTTMP/libroot/mod - $ touch $TESTTMP/libroot/mod/__init__.py - $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py - - $ cat > $TESTTMP/libroot/mod/ambigabs.py < from __future__ import absolute_import - > import ambig # should load "libroot/ambig.py" - > s = ambig.s - > EOF - $ cat > loadabs.py < import mod.ambigabs as ambigabs - > def extsetup(): - > print('ambigabs.s=%s' % ambigabs.s) - > EOF - $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root) - ambigabs.s=libroot/ambig.py - $TESTTMP/a - -#if no-py3k - $ cat > $TESTTMP/libroot/mod/ambigrel.py < import ambig # should load "libroot/mod/ambig.py" - > s = ambig.s - > EOF - $ cat > loadrel.py < import mod.ambigrel as ambigrel - > def extsetup(): - > print('ambigrel.s=%s' % ambigrel.s) - > EOF - $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root) - ambigrel.s=libroot/mod/ambig.py - $TESTTMP/a -#endif - -Check absolute/relative import of extension specific modules - - $ mkdir $TESTTMP/extroot - $ cat > $TESTTMP/extroot/bar.py < s = 'this is extroot.bar' - > EOF - $ mkdir $TESTTMP/extroot/sub1 - $ cat > $TESTTMP/extroot/sub1/__init__.py < s = 'this is extroot.sub1.__init__' - > EOF - $ cat > $TESTTMP/extroot/sub1/baz.py < s = 'this is extroot.sub1.baz' - > EOF - $ cat > $TESTTMP/extroot/__init__.py < s = 'this is extroot.__init__' - > import foo - > def extsetup(ui): - > ui.write('(extroot) ', foo.func(), '\n') - > ui.flush() - > EOF - - $ cat > $TESTTMP/extroot/foo.py < # test absolute import - > buf = [] - > def func(): - > # "not locals" case - > import extroot.bar - > buf.append('import extroot.bar in func(): %s' % extroot.bar.s) - > return '\n(extroot) '.join(buf) - > # "fromlist == ('*',)" case - > from extroot.bar import * - > buf.append('from extroot.bar import *: %s' % s) - > # "not fromlist" and "if '.' in name" case - > import extroot.sub1.baz - > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s) - > # "not fromlist" and NOT "if '.' in name" case - > import extroot - > buf.append('import extroot: %s' % extroot.s) - > # NOT "not fromlist" and NOT "level != -1" case - > from extroot.bar import s - > buf.append('from extroot.bar import s: %s' % s) - > EOF - $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root) - (extroot) from extroot.bar import *: this is extroot.bar - (extroot) import extroot.sub1.baz: this is extroot.sub1.baz - (extroot) import extroot: this is extroot.__init__ - (extroot) from extroot.bar import s: this is extroot.bar - (extroot) import extroot.bar in func(): this is extroot.bar - $TESTTMP/a - -#if no-py3k - $ rm "$TESTTMP"/extroot/foo.* - $ rm -Rf "$TESTTMP/extroot/__pycache__" - $ cat > $TESTTMP/extroot/foo.py < # test relative import - > buf = [] - > def func(): - > # "not locals" case - > import bar - > buf.append('import bar in func(): %s' % bar.s) - > return '\n(extroot) '.join(buf) - > # "fromlist == ('*',)" case - > from bar import * - > buf.append('from bar import *: %s' % s) - > # "not fromlist" and "if '.' in name" case - > import sub1.baz - > buf.append('import sub1.baz: %s' % sub1.baz.s) - > # "not fromlist" and NOT "if '.' in name" case - > import sub1 - > buf.append('import sub1: %s' % sub1.s) - > # NOT "not fromlist" and NOT "level != -1" case - > from bar import s - > buf.append('from bar import s: %s' % s) - > EOF - $ hg --config extensions.extroot=$TESTTMP/extroot root - (extroot) from bar import *: this is extroot.bar - (extroot) import sub1.baz: this is extroot.sub1.baz - (extroot) import sub1: this is extroot.sub1.__init__ - (extroot) from bar import s: this is extroot.bar - (extroot) import bar in func(): this is extroot.bar - $TESTTMP/a -#endif - -#if demandimport - -Examine whether module loading is delayed until actual referring, even -though module is imported with "absolute_import" feature. - -Files below in each packages are used for described purpose: - -- "called": examine whether "from MODULE import ATTR" works correctly -- "unused": examine whether loading is delayed correctly -- "used": examine whether "from PACKAGE import MODULE" works correctly - -Package hierarchy is needed to examine whether demand importing works -as expected for "from SUB.PACK.AGE import MODULE". - -Setup "external library" to be imported with "absolute_import" -feature. - - $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2 - $ touch $TESTTMP/extlibroot/__init__.py - $ touch $TESTTMP/extlibroot/lsub1/__init__.py - $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py - - $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py < def func(): - > return "this is extlibroot.lsub1.lsub2.called.func()" - > EOF - $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py < raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally") - > EOF - $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py < detail = "this is extlibroot.lsub1.lsub2.used" - > EOF - -Setup sub-package of "external library", which causes instantiation of -demandmod in "recurse down the module chain" code path. Relative -importing with "absolute_import" feature isn't tested, because "level ->=1 " doesn't cause instantiation of demandmod. - - $ mkdir -p $TESTTMP/extlibroot/recursedown/abs - $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py < detail = "this is extlibroot.recursedown.abs.used" - > EOF - $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py < from __future__ import absolute_import - > from extlibroot.recursedown.abs.used import detail - > EOF - - $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy - $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py < detail = "this is extlibroot.recursedown.legacy.used" - > EOF - $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py < # legacy style (level == -1) import - > from extlibroot.recursedown.legacy.used import detail - > EOF - - $ cat > $TESTTMP/extlibroot/recursedown/__init__.py < from __future__ import absolute_import - > from extlibroot.recursedown.abs import detail as absdetail - > from .legacy import detail as legacydetail - > EOF - -Setup package that re-exports an attribute of its submodule as the same -name. This leaves 'shadowing.used' pointing to 'used.detail', but still -the submodule 'used' should be somehow accessible. (issue5617) - - $ mkdir -p $TESTTMP/extlibroot/shadowing - $ cat > $TESTTMP/extlibroot/shadowing/used.py < detail = "this is extlibroot.shadowing.used" - > EOF - $ cat > $TESTTMP/extlibroot/shadowing/proxied.py < from __future__ import absolute_import - > from extlibroot.shadowing.used import detail - > EOF - $ cat > $TESTTMP/extlibroot/shadowing/__init__.py < from __future__ import absolute_import - > from .used import detail as used - > EOF - -Setup extension local modules to be imported with "absolute_import" -feature. - - $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2 - $ touch $TESTTMP/absextroot/xsub1/__init__.py - $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py - - $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py < def func(): - > return "this is absextroot.xsub1.xsub2.called.func()" - > EOF - $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py < raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally") - > EOF - $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py < detail = "this is absextroot.xsub1.xsub2.used" - > EOF - -Setup extension local modules to examine whether demand importing -works as expected in "level > 1" case. - - $ cat > $TESTTMP/absextroot/relimportee.py < detail = "this is absextroot.relimportee" - > EOF - $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py < from __future__ import absolute_import - > from ... import relimportee - > detail = "this relimporter imports %r" % (relimportee.detail) - > EOF - -Setup modules, which actually import extension local modules at -runtime. - - $ cat > $TESTTMP/absextroot/absolute.py << EOF - > from __future__ import absolute_import - > - > # import extension local modules absolutely (level = 0) - > from absextroot.xsub1.xsub2 import used, unused - > from absextroot.xsub1.xsub2.called import func - > - > def getresult(): - > result = [] - > result.append(used.detail) - > result.append(func()) - > return result - > EOF - - $ cat > $TESTTMP/absextroot/relative.py << EOF - > from __future__ import absolute_import - > - > # import extension local modules relatively (level == 1) - > from .xsub1.xsub2 import used, unused - > from .xsub1.xsub2.called import func - > - > # import a module, which implies "importing with level > 1" - > from .xsub1.xsub2 import relimporter - > - > def getresult(): - > result = [] - > result.append(used.detail) - > result.append(func()) - > result.append(relimporter.detail) - > return result - > EOF - -Setup main procedure of extension. - - $ cat > $TESTTMP/absextroot/__init__.py < from __future__ import absolute_import - > from mercurial import registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > - > # "absolute" and "relative" shouldn't be imported before actual - > # command execution, because (1) they import same modules, and (2) - > # preceding import (= instantiate "demandmod" object instead of - > # real "module" object) might hide problem of succeeding import. - > - > @command(b'showabsolute', [], norepo=True) - > def showabsolute(ui, *args, **opts): - > from absextroot import absolute - > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult())) - > - > @command(b'showrelative', [], norepo=True) - > def showrelative(ui, *args, **opts): - > from . import relative - > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult())) - > - > # import modules from external library - > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused - > from extlibroot.lsub1.lsub2.called import func as lfunc - > from extlibroot.recursedown import absdetail, legacydetail - > from extlibroot.shadowing import proxied - > - > def uisetup(ui): - > result = [] - > result.append(lused.detail) - > result.append(lfunc()) - > result.append(absdetail) - > result.append(legacydetail) - > result.append(proxied.detail) - > ui.write(b'LIB: %s\n' % '\nLIB: '.join(result)) - > EOF - -Examine module importing. - - $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute) - LIB: this is extlibroot.lsub1.lsub2.used - LIB: this is extlibroot.lsub1.lsub2.called.func() - LIB: this is extlibroot.recursedown.abs.used - LIB: this is extlibroot.recursedown.legacy.used - LIB: this is extlibroot.shadowing.used - ABS: this is absextroot.xsub1.xsub2.used - ABS: this is absextroot.xsub1.xsub2.called.func() - - $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative) - LIB: this is extlibroot.lsub1.lsub2.used - LIB: this is extlibroot.lsub1.lsub2.called.func() - LIB: this is extlibroot.recursedown.abs.used - LIB: this is extlibroot.recursedown.legacy.used - LIB: this is extlibroot.shadowing.used - REL: this is absextroot.xsub1.xsub2.used - REL: this is absextroot.xsub1.xsub2.called.func() - REL: this relimporter imports 'this is absextroot.relimportee' - -Examine whether sub-module is imported relatively as expected. - -See also issue5208 for detail about example case on Python 3.x. - - $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py - $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found - - $ cat > $TESTTMP/notexist.py < text = 'notexist.py at root is loaded unintentionally\n' - > EOF - - $ cat > $TESTTMP/checkrelativity.py < from mercurial import registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > - > # demand import avoids failure of importing notexist here - > import extlibroot.lsub1.lsub2.notexist - > - > @command(b'checkrelativity', [], norepo=True) - > def checkrelativity(ui, *args, **opts): - > try: - > ui.write(extlibroot.lsub1.lsub2.notexist.text) - > return 1 # unintentional success - > except ImportError: - > pass # intentional failure - > EOF - - $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) - -#endif - -Make sure a broken uisetup doesn't globally break hg: - $ cat > $TESTTMP/baduisetup.py < def uisetup(ui): - > 1/0 - > EOF - -Even though the extension fails during uisetup, hg is still basically usable: - $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version - Traceback (most recent call last): - File "*/mercurial/extensions.py", line *, in _runuisetup (glob) - uisetup(ui) - File "$TESTTMP/baduisetup.py", line 2, in uisetup - 1/0 - ZeroDivisionError: integer division or modulo by zero - *** failed to set up extension baduisetup: integer division or modulo by zero - Mercurial Distributed SCM (version *) (glob) - (see https://mercurial-scm.org for more information) - - Copyright (C) 2005-* Matt Mackall and others (glob) - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ cd .. -hide outer repo - $ hg init - - $ cat > empty.py < '''empty cmdtable - > ''' - > cmdtable = {} - > EOF - $ emptypath=`pwd`/empty.py - $ echo "empty = $emptypath" >> $HGRCPATH - $ hg help empty - empty extension - empty cmdtable - - no commands defined - - - $ echo 'empty = !' >> $HGRCPATH - - $ cat > debugextension.py < '''only debugcommands - > ''' - > from mercurial import registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > @command(b'debugfoobar', [], b'hg debugfoobar') - > def debugfoobar(ui, repo, *args, **opts): - > "yet another debug command" - > pass - > @command(b'foo', [], b'hg foo') - > def foo(ui, repo, *args, **opts): - > """yet another foo command - > This command has been DEPRECATED since forever. - > """ - > pass - > EOF - $ debugpath=`pwd`/debugextension.py - $ echo "debugextension = $debugpath" >> $HGRCPATH - - $ hg help debugextension - hg debugextensions - - show information about active extensions - - options: - - (some details hidden, use --verbose to show complete help) - - - $ hg --verbose help debugextension - hg debugextensions - - show information about active extensions - - options: - - -T --template TEMPLATE display with template (EXPERIMENTAL) - - global options ([+] can be repeated): - - -R --repository REPO repository root directory or name of overlay bundle - file - --cwd DIR change working directory - -y --noninteractive do not prompt, automatically pick the first choice for - all prompts - -q --quiet suppress output - -v --verbose enable additional output - --color TYPE when to colorize (boolean, always, auto, never, or - debug) - --config CONFIG [+] set/override config option (use 'section.name=value') - --debug enable debugging output - --debugger start debugger - --encoding ENCODE set the charset encoding (default: ascii) - --encodingmode MODE set the charset encoding mode (default: strict) - --traceback always print a traceback on exception - --time time how long the command takes - --profile print command execution profile - --version output version information and exit - -h --help display help and exit - --hidden consider hidden changesets - --pager TYPE when to paginate (boolean, always, auto, or never) - (default: auto) - - - - - - - $ hg --debug help debugextension - hg debugextensions - - show information about active extensions - - options: - - -T --template TEMPLATE display with template (EXPERIMENTAL) - - global options ([+] can be repeated): - - -R --repository REPO repository root directory or name of overlay bundle - file - --cwd DIR change working directory - -y --noninteractive do not prompt, automatically pick the first choice for - all prompts - -q --quiet suppress output - -v --verbose enable additional output - --color TYPE when to colorize (boolean, always, auto, never, or - debug) - --config CONFIG [+] set/override config option (use 'section.name=value') - --debug enable debugging output - --debugger start debugger - --encoding ENCODE set the charset encoding (default: ascii) - --encodingmode MODE set the charset encoding mode (default: strict) - --traceback always print a traceback on exception - --time time how long the command takes - --profile print command execution profile - --version output version information and exit - -h --help display help and exit - --hidden consider hidden changesets - --pager TYPE when to paginate (boolean, always, auto, or never) - (default: auto) - - - - - - $ echo 'debugextension = !' >> $HGRCPATH - -Asking for help about a deprecated extension should do something useful: - - $ hg help glog - 'glog' is provided by the following extension: - - graphlog command to view revision graphs from a shell (DEPRECATED) - - (use 'hg help extensions' for information on enabling extensions) - -Extension module help vs command help: - - $ echo 'extdiff =' >> $HGRCPATH - $ hg help extdiff - hg extdiff [OPT]... [FILE]... - - use external program to diff repository (or selected files) - - Show differences between revisions for the specified files, using an - external program. The default program used is diff, with default options - "-Npru". - - To select a different program, use the -p/--program option. The program - will be passed the names of two directories to compare. To pass additional - options to the program, use -o/--option. These will be passed before the - names of the directories to compare. - - When two revision arguments are given, then changes are shown between - those revisions. If only one revision is specified then that revision is - compared to the working directory, and, when no revisions are specified, - the working directory files are compared to its parent. - - (use 'hg help -e extdiff' to show help for the extdiff extension) - - options ([+] can be repeated): - - -p --program CMD comparison program to run - -o --option OPT [+] pass option to comparison program - -r --rev REV [+] revision - -c --change REV change made by revision - --patch compare patches for two revisions - -I --include PATTERN [+] include names matching the given patterns - -X --exclude PATTERN [+] exclude names matching the given patterns - -S --subrepos recurse into subrepositories - - (some details hidden, use --verbose to show complete help) - - - - - - - - - - - $ hg help --extension extdiff - extdiff extension - command to allow external programs to compare revisions - - The extdiff Mercurial extension allows you to use external programs to compare - revisions, or revision with working directory. The external diff programs are - called with a configurable set of options and two non-option arguments: paths - to directories containing snapshots of files to compare. - - If there is more than one file being compared and the "child" revision is the - working directory, any modifications made in the external diff program will be - copied back to the working directory from the temporary directory. - - The extdiff extension also allows you to configure new diff commands, so you - do not need to type 'hg extdiff -p kdiff3' always. - - [extdiff] - # add new command that runs GNU diff(1) in 'context diff' mode - cdiff = gdiff -Nprc5 - ## or the old way: - #cmd.cdiff = gdiff - #opts.cdiff = -Nprc5 - - # add new command called meld, runs meld (no need to name twice). If - # the meld executable is not available, the meld tool in [merge-tools] - # will be used, if available - meld = - - # add new command called vimdiff, runs gvimdiff with DirDiff plugin - # (see http://www.vim.org/scripts/script.php?script_id=102) Non - # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in - # your .vimrc - vimdiff = gvim -f "+next" \ - "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))" - - Tool arguments can include variables that are expanded at runtime: - - $parent1, $plabel1 - filename, descriptive label of first parent - $child, $clabel - filename, descriptive label of child revision - $parent2, $plabel2 - filename, descriptive label of second parent - $root - repository root - $parent is an alias for $parent1. - - The extdiff extension will look in your [diff-tools] and [merge-tools] - sections for diff tool arguments, when none are specified in [extdiff]. - - [extdiff] - kdiff3 = - - [diff-tools] - kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child - - You can use -I/-X and list of file or directory names like normal 'hg diff' - command. The extdiff extension makes snapshots of only needed files, so - running the external diff program will actually be pretty fast (at least - faster than having to compare the entire tree). - - list of commands: - - extdiff use external program to diff repository (or selected files) - - (use 'hg help -v -e extdiff' to show built-in aliases and global options) - - - - - - - - - - - - - - - - - $ echo 'extdiff = !' >> $HGRCPATH - -Test help topic with same name as extension - - $ cat > multirevs.py < from mercurial import commands, registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > """multirevs extension - > Big multi-line module docstring.""" - > @command(b'multirevs', [], b'ARG', norepo=True) - > def multirevs(ui, repo, arg, *args, **opts): - > """multirevs command""" - > pass - > EOF - $ echo "multirevs = multirevs.py" >> $HGRCPATH - - $ hg help multirevs | tail - used): - - hg update :@ - - - Show diff between tags 1.3 and 1.5 (this works because the first and the - last revisions of the revset are used): - - hg diff -r 1.3::1.5 - - use 'hg help -c multirevs' to see help for the multirevs command - - - - - - - $ hg help -c multirevs - hg multirevs ARG - - multirevs command - - (some details hidden, use --verbose to show complete help) - - - - $ hg multirevs - hg multirevs: invalid arguments - hg multirevs ARG - - multirevs command - - (use 'hg multirevs -h' to show more help) - [255] - - - - $ echo "multirevs = !" >> $HGRCPATH - -Issue811: Problem loading extensions twice (by site and by user) - - $ cat <> $HGRCPATH - > mq = - > strip = - > hgext.mq = - > hgext/mq = - > EOF - -Show extensions: -(note that mq force load strip, also checking it's not loaded twice) - -#if no-extraextensions - $ hg debugextensions - mq - strip -#endif - -For extensions, which name matches one of its commands, help -message should ask '-v -e' to get list of built-in aliases -along with extension help itself - - $ mkdir $TESTTMP/d - $ cat > $TESTTMP/d/dodo.py < """ - > This is an awesome 'dodo' extension. It does nothing and - > writes 'Foo foo' - > """ - > from mercurial import commands, registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > @command(b'dodo', [], b'hg dodo') - > def dodo(ui, *args, **kwargs): - > """Does nothing""" - > ui.write(b"I do nothing. Yay\\n") - > @command(b'foofoo', [], b'hg foofoo') - > def foofoo(ui, *args, **kwargs): - > """Writes 'Foo foo'""" - > ui.write(b"Foo foo\\n") - > EOF - $ dodopath=$TESTTMP/d/dodo.py - - $ echo "dodo = $dodopath" >> $HGRCPATH - -Make sure that user is asked to enter '-v -e' to get list of built-in aliases - $ hg help -e dodo - dodo extension - - - This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo' - - list of commands: - - dodo Does nothing - foofoo Writes 'Foo foo' - - (use 'hg help -v -e dodo' to show built-in aliases and global options) - -Make sure that '-v -e' prints list of built-in aliases along with -extension help itself - $ hg help -v -e dodo - dodo extension - - - This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo' - - list of commands: - - dodo Does nothing - foofoo Writes 'Foo foo' - - global options ([+] can be repeated): - - -R --repository REPO repository root directory or name of overlay bundle - file - --cwd DIR change working directory - -y --noninteractive do not prompt, automatically pick the first choice for - all prompts - -q --quiet suppress output - -v --verbose enable additional output - --color TYPE when to colorize (boolean, always, auto, never, or - debug) - --config CONFIG [+] set/override config option (use 'section.name=value') - --debug enable debugging output - --debugger start debugger - --encoding ENCODE set the charset encoding (default: ascii) - --encodingmode MODE set the charset encoding mode (default: strict) - --traceback always print a traceback on exception - --time time how long the command takes - --profile print command execution profile - --version output version information and exit - -h --help display help and exit - --hidden consider hidden changesets - --pager TYPE when to paginate (boolean, always, auto, or never) - (default: auto) - -Make sure that single '-v' option shows help and built-ins only for 'dodo' command - $ hg help -v dodo - hg dodo - - Does nothing - - (use 'hg help -e dodo' to show help for the dodo extension) - - options: - - --mq operate on patch repository - - global options ([+] can be repeated): - - -R --repository REPO repository root directory or name of overlay bundle - file - --cwd DIR change working directory - -y --noninteractive do not prompt, automatically pick the first choice for - all prompts - -q --quiet suppress output - -v --verbose enable additional output - --color TYPE when to colorize (boolean, always, auto, never, or - debug) - --config CONFIG [+] set/override config option (use 'section.name=value') - --debug enable debugging output - --debugger start debugger - --encoding ENCODE set the charset encoding (default: ascii) - --encodingmode MODE set the charset encoding mode (default: strict) - --traceback always print a traceback on exception - --time time how long the command takes - --profile print command execution profile - --version output version information and exit - -h --help display help and exit - --hidden consider hidden changesets - --pager TYPE when to paginate (boolean, always, auto, or never) - (default: auto) - -In case when extension name doesn't match any of its commands, -help message should ask for '-v' to get list of built-in aliases -along with extension help - $ cat > $TESTTMP/d/dudu.py < """ - > This is an awesome 'dudu' extension. It does something and - > also writes 'Beep beep' - > """ - > from mercurial import commands, registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > @command(b'something', [], b'hg something') - > def something(ui, *args, **kwargs): - > """Does something""" - > ui.write(b"I do something. Yaaay\\n") - > @command(b'beep', [], b'hg beep') - > def beep(ui, *args, **kwargs): - > """Writes 'Beep beep'""" - > ui.write(b"Beep beep\\n") - > EOF - $ dudupath=$TESTTMP/d/dudu.py - - $ echo "dudu = $dudupath" >> $HGRCPATH - - $ hg help -e dudu - dudu extension - - - This is an awesome 'dudu' extension. It does something and also writes 'Beep - beep' - - list of commands: - - beep Writes 'Beep beep' - something Does something - - (use 'hg help -v dudu' to show built-in aliases and global options) - -In case when extension name doesn't match any of its commands, -help options '-v' and '-v -e' should be equivalent - $ hg help -v dudu - dudu extension - - - This is an awesome 'dudu' extension. It does something and also writes 'Beep - beep' - - list of commands: - - beep Writes 'Beep beep' - something Does something - - global options ([+] can be repeated): - - -R --repository REPO repository root directory or name of overlay bundle - file - --cwd DIR change working directory - -y --noninteractive do not prompt, automatically pick the first choice for - all prompts - -q --quiet suppress output - -v --verbose enable additional output - --color TYPE when to colorize (boolean, always, auto, never, or - debug) - --config CONFIG [+] set/override config option (use 'section.name=value') - --debug enable debugging output - --debugger start debugger - --encoding ENCODE set the charset encoding (default: ascii) - --encodingmode MODE set the charset encoding mode (default: strict) - --traceback always print a traceback on exception - --time time how long the command takes - --profile print command execution profile - --version output version information and exit - -h --help display help and exit - --hidden consider hidden changesets - --pager TYPE when to paginate (boolean, always, auto, or never) - (default: auto) - - $ hg help -v -e dudu - dudu extension - - - This is an awesome 'dudu' extension. It does something and also writes 'Beep - beep' - - list of commands: - - beep Writes 'Beep beep' - something Does something - - global options ([+] can be repeated): - - -R --repository REPO repository root directory or name of overlay bundle - file - --cwd DIR change working directory - -y --noninteractive do not prompt, automatically pick the first choice for - all prompts - -q --quiet suppress output - -v --verbose enable additional output - --color TYPE when to colorize (boolean, always, auto, never, or - debug) - --config CONFIG [+] set/override config option (use 'section.name=value') - --debug enable debugging output - --debugger start debugger - --encoding ENCODE set the charset encoding (default: ascii) - --encodingmode MODE set the charset encoding mode (default: strict) - --traceback always print a traceback on exception - --time time how long the command takes - --profile print command execution profile - --version output version information and exit - -h --help display help and exit - --hidden consider hidden changesets - --pager TYPE when to paginate (boolean, always, auto, or never) - (default: auto) - -Disabled extension commands: - - $ ORGHGRCPATH=$HGRCPATH - $ HGRCPATH= - $ export HGRCPATH - $ hg help email - 'email' is provided by the following extension: - - patchbomb command to send changesets as (a series of) patch emails - - (use 'hg help extensions' for information on enabling extensions) - - - $ hg qdel - hg: unknown command 'qdel' - 'qdelete' is provided by the following extension: - - mq manage a stack of patches - - (use 'hg help extensions' for information on enabling extensions) - [255] - - - $ hg churn - hg: unknown command 'churn' - 'churn' is provided by the following extension: - - churn command to display statistics about repository history - - (use 'hg help extensions' for information on enabling extensions) - [255] - - - -Disabled extensions: - - $ hg help churn - churn extension - command to display statistics about repository history - - (use 'hg help extensions' for information on enabling extensions) - - $ hg help patchbomb - patchbomb extension - command to send changesets as (a series of) patch emails - - The series is started off with a "[PATCH 0 of N]" introduction, which - describes the series as a whole. - - Each patch email has a Subject line of "[PATCH M of N] ...", using the first - line of the changeset description as the subject text. The message contains - two or three body parts: - - - The changeset description. - - [Optional] The result of running diffstat on the patch. - - The patch itself, as generated by 'hg export'. - - Each message refers to the first in the series using the In-Reply-To and - References headers, so they will show up as a sequence in threaded mail and - news readers, and in mail archives. - - To configure other defaults, add a section like this to your configuration - file: - - [email] - from = My Name - to = recipient1, recipient2, ... - cc = cc1, cc2, ... - bcc = bcc1, bcc2, ... - reply-to = address1, address2, ... - - Use "[patchbomb]" as configuration section name if you need to override global - "[email]" address settings. - - Then you can use the 'hg email' command to mail a series of changesets as a - patchbomb. - - You can also either configure the method option in the email section to be a - sendmail compatible mailer or fill out the [smtp] section so that the - patchbomb extension can automatically send patchbombs directly from the - commandline. See the [email] and [smtp] sections in hgrc(5) for details. - - By default, 'hg email' will prompt for a "To" or "CC" header if you do not - supply one via configuration or the command line. You can override this to - never prompt by configuring an empty value: - - [email] - cc = - - You can control the default inclusion of an introduction message with the - "patchbomb.intro" configuration option. The configuration is always - overwritten by command line flags like --intro and --desc: - - [patchbomb] - intro=auto # include introduction message if more than 1 patch (default) - intro=never # never include an introduction message - intro=always # always include an introduction message - - You can specify a template for flags to be added in subject prefixes. Flags - specified by --flag option are exported as "{flags}" keyword: - - [patchbomb] - flagtemplate = "{separate(' ', - ifeq(branch, 'default', '', branch|upper), - flags)}" - - You can set patchbomb to always ask for confirmation by setting - "patchbomb.confirm" to true. - - (use 'hg help extensions' for information on enabling extensions) - - -Broken disabled extension and command: - - $ mkdir hgext - $ echo > hgext/__init__.py - $ cat > hgext/broken.py < "broken extension' - > EOF - $ cat > path.py < import os, sys - > sys.path.insert(0, os.environ['HGEXTPATH']) - > EOF - $ HGEXTPATH=`pwd` - $ export HGEXTPATH - - $ hg --config extensions.path=./path.py help broken - broken extension - (no help text available) - - (use 'hg help extensions' for information on enabling extensions) - - - $ cat > hgext/forest.py < cmdtable = None - > @command() - > def f(): - > pass - > @command(123) - > def g(): - > pass - > EOF - $ hg --config extensions.path=./path.py help foo - abort: no such help topic: foo - (try 'hg help --keyword foo') - [255] - - $ cat > throw.py < from mercurial import commands, registrar, util - > cmdtable = {} - > command = registrar.command(cmdtable) - > class Bogon(Exception): pass - > @command(b'throw', [], b'hg throw', norepo=True) - > def throw(ui, **opts): - > """throws an exception""" - > raise Bogon() - > EOF - -No declared supported version, extension complains: - $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' - ** Unknown exception encountered with possibly-broken third-party extension throw - ** which supports versions unknown of Mercurial. - ** Please disable throw and try your action again. - ** If that fixes the bug please report it to the extension author. - ** Python * (glob) - ** Mercurial Distributed SCM * (glob) - ** Extensions loaded: throw - -empty declaration of supported version, extension complains: - $ echo "testedwith = ''" >> throw.py - $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' - ** Unknown exception encountered with possibly-broken third-party extension throw - ** which supports versions unknown of Mercurial. - ** Please disable throw and try your action again. - ** If that fixes the bug please report it to the extension author. - ** Python * (glob) - ** Mercurial Distributed SCM (*) (glob) - ** Extensions loaded: throw - -If the extension specifies a buglink, show that: - $ echo 'buglink = "http://example.com/bts"' >> throw.py - $ rm -f throw.pyc throw.pyo - $ rm -Rf __pycache__ - $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' - ** Unknown exception encountered with possibly-broken third-party extension throw - ** which supports versions unknown of Mercurial. - ** Please disable throw and try your action again. - ** If that fixes the bug please report it to http://example.com/bts - ** Python * (glob) - ** Mercurial Distributed SCM (*) (glob) - ** Extensions loaded: throw - -If the extensions declare outdated versions, accuse the older extension first: - $ echo "from mercurial import util" >> older.py - $ echo "util.version = lambda:b'2.2'" >> older.py - $ echo "testedwith = b'1.9.3'" >> older.py - $ echo "testedwith = b'2.1.1'" >> throw.py - $ rm -f throw.pyc throw.pyo - $ rm -Rf __pycache__ - $ hg --config extensions.throw=throw.py --config extensions.older=older.py \ - > throw 2>&1 | egrep '^\*\*' - ** Unknown exception encountered with possibly-broken third-party extension older - ** which supports versions 1.9 of Mercurial. - ** Please disable older and try your action again. - ** If that fixes the bug please report it to the extension author. - ** Python * (glob) - ** Mercurial Distributed SCM (version 2.2) - ** Extensions loaded: throw, older - -One extension only tested with older, one only with newer versions: - $ echo "util.version = lambda:b'2.1'" >> older.py - $ rm -f older.pyc older.pyo - $ rm -Rf __pycache__ - $ hg --config extensions.throw=throw.py --config extensions.older=older.py \ - > throw 2>&1 | egrep '^\*\*' - ** Unknown exception encountered with possibly-broken third-party extension older - ** which supports versions 1.9 of Mercurial. - ** Please disable older and try your action again. - ** If that fixes the bug please report it to the extension author. - ** Python * (glob) - ** Mercurial Distributed SCM (version 2.1) - ** Extensions loaded: throw, older - -Older extension is tested with current version, the other only with newer: - $ echo "util.version = lambda:b'1.9.3'" >> older.py - $ rm -f older.pyc older.pyo - $ rm -Rf __pycache__ - $ hg --config extensions.throw=throw.py --config extensions.older=older.py \ - > throw 2>&1 | egrep '^\*\*' - ** Unknown exception encountered with possibly-broken third-party extension throw - ** which supports versions 2.1 of Mercurial. - ** Please disable throw and try your action again. - ** If that fixes the bug please report it to http://example.com/bts - ** Python * (glob) - ** Mercurial Distributed SCM (version 1.9.3) - ** Extensions loaded: throw, older - -Ability to point to a different point - $ hg --config extensions.throw=throw.py --config extensions.older=older.py \ - > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*' - ** unknown exception encountered, please report by visiting - ** Your Local Goat Lenders - ** Python * (glob) - ** Mercurial Distributed SCM (*) (glob) - ** Extensions loaded: throw, older - -Declare the version as supporting this hg version, show regular bts link: - $ hgver=`hg debuginstall -T '{hgver}'` - $ echo 'testedwith = """'"$hgver"'"""' >> throw.py - $ if [ -z "$hgver" ]; then - > echo "unable to fetch a mercurial version. Make sure __version__ is correct"; - > fi - $ rm -f throw.pyc throw.pyo - $ rm -Rf __pycache__ - $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' - ** unknown exception encountered, please report by visiting - ** https://mercurial-scm.org/wiki/BugTracker - ** Python * (glob) - ** Mercurial Distributed SCM (*) (glob) - ** Extensions loaded: throw - -Patch version is ignored during compatibility check - $ echo "testedwith = b'3.2'" >> throw.py - $ echo "util.version = lambda:b'3.2.2'" >> throw.py - $ rm -f throw.pyc throw.pyo - $ rm -Rf __pycache__ - $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' - ** unknown exception encountered, please report by visiting - ** https://mercurial-scm.org/wiki/BugTracker - ** Python * (glob) - ** Mercurial Distributed SCM (*) (glob) - ** Extensions loaded: throw - -Test version number support in 'hg version': - $ echo '__version__ = (1, 2, 3)' >> throw.py - $ rm -f throw.pyc throw.pyo - $ rm -Rf __pycache__ - $ hg version -v - Mercurial Distributed SCM (version *) (glob) - (see https://mercurial-scm.org for more information) - - Copyright (C) 2005-* Matt Mackall and others (glob) - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - Enabled extensions: - - - $ hg version -v --config extensions.throw=throw.py - Mercurial Distributed SCM (version *) (glob) - (see https://mercurial-scm.org for more information) - - Copyright (C) 2005-* Matt Mackall and others (glob) - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - Enabled extensions: - - throw external 1.2.3 - $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py - $ rm -f throw.pyc throw.pyo - $ rm -Rf __pycache__ - $ hg version -v --config extensions.throw=throw.py --config extensions.strip= - Mercurial Distributed SCM (version *) (glob) - (see https://mercurial-scm.org for more information) - - Copyright (C) 2005-* Matt Mackall and others (glob) - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - Enabled extensions: - - throw external 1.twentythree - strip internal - - $ hg version -q --config extensions.throw=throw.py - Mercurial Distributed SCM (version *) (glob) - -Test template output: - - $ hg version --config extensions.strip= -T'{extensions}' - strip - -Test JSON output of version: - - $ hg version -Tjson - [ - { - "extensions": [], - "ver": "*" (glob) - } - ] - - $ hg version --config extensions.throw=throw.py -Tjson - [ - { - "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}], - "ver": "3.2.2" - } - ] - - $ hg version --config extensions.strip= -Tjson - [ - { - "extensions": [{"bundled": true, "name": "strip", "ver": null}], - "ver": "*" (glob) - } - ] - -Test template output of version: - - $ hg version --config extensions.throw=throw.py --config extensions.strip= \ - > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}' - throw 1.twentythree (external) - strip (internal) - -Refuse to load extensions with minimum version requirements - - $ cat > minversion1.py << EOF - > from mercurial import util - > util.version = lambda: b'3.5.2' - > minimumhgversion = b'3.6' - > EOF - $ hg --config extensions.minversion=minversion1.py version - (third party extension minversion requires version 3.6 or newer of Mercurial; disabling) - Mercurial Distributed SCM (version 3.5.2) - (see https://mercurial-scm.org for more information) - - Copyright (C) 2005-* Matt Mackall and others (glob) - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - $ cat > minversion2.py << EOF - > from mercurial import util - > util.version = lambda: b'3.6' - > minimumhgversion = b'3.7' - > EOF - $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third' - (third party extension minversion requires version 3.7 or newer of Mercurial; disabling) - -Can load version that is only off by point release - - $ cat > minversion2.py << EOF - > from mercurial import util - > util.version = lambda: b'3.6.1' - > minimumhgversion = b'3.6' - > EOF - $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third' - [1] - -Can load minimum version identical to current - - $ cat > minversion3.py << EOF - > from mercurial import util - > util.version = lambda: b'3.5' - > minimumhgversion = b'3.5' - > EOF - $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third' - [1] - -Restore HGRCPATH - - $ HGRCPATH=$ORGHGRCPATH - $ export HGRCPATH - -Commands handling multiple repositories at a time should invoke only -"reposetup()" of extensions enabling in the target repository. - - $ mkdir reposetup-test - $ cd reposetup-test - - $ cat > $TESTTMP/reposetuptest.py < from mercurial import extensions - > def reposetup(ui, repo): - > ui.write(b'reposetup() for %s\n' % (repo.root)) - > ui.flush() - > EOF - $ hg init src - $ echo a > src/a - $ hg -R src commit -Am '#0 at src/a' - adding a - $ echo '[extensions]' >> src/.hg/hgrc - $ echo '# enable extension locally' >> src/.hg/hgrc - $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc - $ hg -R src status - reposetup() for $TESTTMP/reposetup-test/src - reposetup() for $TESTTMP/reposetup-test/src (chg !) - -#if no-extraextensions - $ hg --cwd src debugextensions - reposetup() for $TESTTMP/reposetup-test/src - dodo (untested!) - dudu (untested!) - mq - reposetuptest (untested!) - strip -#endif - - $ hg clone -U src clone-dst1 - reposetup() for $TESTTMP/reposetup-test/src - $ hg init push-dst1 - $ hg -q -R src push push-dst1 - reposetup() for $TESTTMP/reposetup-test/src - $ hg init pull-src1 - $ hg -q -R pull-src1 pull src - reposetup() for $TESTTMP/reposetup-test/src - - $ cat <> $HGRCPATH - > [extensions] - > # disable extension globally and explicitly - > reposetuptest = ! - > EOF - $ hg clone -U src clone-dst2 - reposetup() for $TESTTMP/reposetup-test/src - $ hg init push-dst2 - $ hg -q -R src push push-dst2 - reposetup() for $TESTTMP/reposetup-test/src - $ hg init pull-src2 - $ hg -q -R pull-src2 pull src - reposetup() for $TESTTMP/reposetup-test/src - - $ cat <> $HGRCPATH - > [extensions] - > # enable extension globally - > reposetuptest = $TESTTMP/reposetuptest.py - > EOF - $ hg clone -U src clone-dst3 - reposetup() for $TESTTMP/reposetup-test/src - reposetup() for $TESTTMP/reposetup-test/clone-dst3 - $ hg init push-dst3 - reposetup() for $TESTTMP/reposetup-test/push-dst3 - $ hg -q -R src push push-dst3 - reposetup() for $TESTTMP/reposetup-test/src - reposetup() for $TESTTMP/reposetup-test/push-dst3 - $ hg init pull-src3 - reposetup() for $TESTTMP/reposetup-test/pull-src3 - $ hg -q -R pull-src3 pull src - reposetup() for $TESTTMP/reposetup-test/pull-src3 - reposetup() for $TESTTMP/reposetup-test/src - - $ echo '[extensions]' >> src/.hg/hgrc - $ echo '# disable extension locally' >> src/.hg/hgrc - $ echo 'reposetuptest = !' >> src/.hg/hgrc - $ hg clone -U src clone-dst4 - reposetup() for $TESTTMP/reposetup-test/clone-dst4 - $ hg init push-dst4 - reposetup() for $TESTTMP/reposetup-test/push-dst4 - $ hg -q -R src push push-dst4 - reposetup() for $TESTTMP/reposetup-test/push-dst4 - $ hg init pull-src4 - reposetup() for $TESTTMP/reposetup-test/pull-src4 - $ hg -q -R pull-src4 pull src - reposetup() for $TESTTMP/reposetup-test/pull-src4 - -disabling in command line overlays with all configuration - $ hg --config extensions.reposetuptest=! clone -U src clone-dst5 - $ hg --config extensions.reposetuptest=! init push-dst5 - $ hg --config extensions.reposetuptest=! -q -R src push push-dst5 - $ hg --config extensions.reposetuptest=! init pull-src5 - $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src - - $ cat <> $HGRCPATH - > [extensions] - > # disable extension globally and explicitly - > reposetuptest = ! - > EOF - $ hg init parent - $ hg init parent/sub1 - $ echo 1 > parent/sub1/1 - $ hg -R parent/sub1 commit -Am '#0 at parent/sub1' - adding 1 - $ hg init parent/sub2 - $ hg init parent/sub2/sub21 - $ echo 21 > parent/sub2/sub21/21 - $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21' - adding 21 - $ cat > parent/sub2/.hgsub < sub21 = sub21 - > EOF - $ hg -R parent/sub2 commit -Am '#0 at parent/sub2' - adding .hgsub - $ hg init parent/sub3 - $ echo 3 > parent/sub3/3 - $ hg -R parent/sub3 commit -Am '#0 at parent/sub3' - adding 3 - $ cat > parent/.hgsub < sub1 = sub1 - > sub2 = sub2 - > sub3 = sub3 - > EOF - $ hg -R parent commit -Am '#0 at parent' - adding .hgsub - $ echo '[extensions]' >> parent/.hg/hgrc - $ echo '# enable extension locally' >> parent/.hg/hgrc - $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc - $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc - $ hg -R parent status -S -A - reposetup() for $TESTTMP/reposetup-test/parent - reposetup() for $TESTTMP/reposetup-test/parent/sub2 - C .hgsub - C .hgsubstate - C sub1/1 - C sub2/.hgsub - C sub2/.hgsubstate - C sub2/sub21/21 - C sub3/3 - - $ cd .. - -Prohibit registration of commands that don't use @command (issue5137) - - $ hg init deprecated - $ cd deprecated - - $ cat < deprecatedcmd.py - > def deprecatedcmd(repo, ui): - > pass - > cmdtable = { - > b'deprecatedcmd': (deprecatedcmd, [], b''), - > } - > EOF - $ cat < .hg/hgrc - > [extensions] - > deprecatedcmd = `pwd`/deprecatedcmd.py - > mq = ! - > hgext.mq = ! - > hgext/mq = ! - > EOF - - $ hg deprecatedcmd > /dev/null - *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo - *** (use @command decorator to register 'deprecatedcmd') - hg: unknown command 'deprecatedcmd' - (use 'hg help' for a list of commands) - [255] - - the extension shouldn't be loaded at all so the mq works: - - $ hg qseries --config extensions.mq= > /dev/null - *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo - *** (use @command decorator to register 'deprecatedcmd') - - $ cd .. - -Test synopsis and docstring extending - - $ hg init exthelp - $ cat > exthelp.py < from mercurial import commands, extensions - > def exbookmarks(orig, *args, **opts): - > return orig(*args, **opts) - > def uisetup(ui): - > synopsis = b' GREPME [--foo] [-x]' - > docstring = ''' - > GREPME make sure that this is in the help! - > ''' - > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks, - > synopsis, docstring) - > EOF - $ abspath=`pwd`/exthelp.py - $ echo '[extensions]' >> $HGRCPATH - $ echo "exthelp = $abspath" >> $HGRCPATH - $ cd exthelp - $ hg help bookmarks | grep GREPME - hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x] - GREPME make sure that this is in the help! - $ cd .. - -Show deprecation warning for the use of cmdutil.command - - $ cat > nonregistrar.py < from mercurial import cmdutil - > cmdtable = {} - > command = cmdutil.command(cmdtable) - > @command(b'foo', [], norepo=True) - > def foo(ui): - > pass - > EOF - -Prohibit the use of unicode strings as the default value of options - - $ hg init $TESTTMP/opt-unicode-default - - $ cat > $TESTTMP/test_unicode_default_value.py << EOF - > from mercurial import registrar - > cmdtable = {} - > command = registrar.command(cmdtable) - > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]') - > def ext(*args, **opts): - > print(opts[b'opt']) - > EOF - $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF - > [extensions] - > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py - > EOF - $ hg -R $TESTTMP/opt-unicode-default dummy - *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy - *** (use b'' to make it byte string) - hg: unknown command 'dummy' - (did you mean summary?) - [255] + $ echo 'foobar = !' >> $HGRCPATH