##// END OF EJS Templates
exthelper: reintroduce the ability to register revsets...
Matt Harbison -
r41096:0358cca1 default
parent child Browse files
Show More
@@ -1,277 +1,279
1 1 # Copyright 2012 Logilab SA <contact@logilab.fr>
2 2 # Pierre-Yves David <pierre-yves.david@ens-lyon.org>
3 3 # Octobus <contact@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 #####################################################################
9 9 ### Extension helper ###
10 10 #####################################################################
11 11
12 12 from __future__ import absolute_import
13 13
14 14 from . import (
15 15 commands,
16 16 error,
17 17 extensions,
18 18 registrar,
19 19 )
20 20
21 21 class exthelper(object):
22 22 """Helper for modular extension setup
23 23
24 24 A single helper should be instantiated for each extension. Helper
25 25 methods are then used as decorators for various purpose.
26 26
27 27 All decorators return the original function and may be chained.
28 28 """
29 29
30 30 def __init__(self):
31 31 self._uipopulatecallables = []
32 32 self._uicallables = []
33 33 self._extcallables = []
34 34 self._repocallables = []
35 35 self._commandwrappers = []
36 36 self._extcommandwrappers = []
37 37 self._functionwrappers = []
38 38 self._duckpunchers = []
39 39 self.cmdtable = {}
40 40 self.command = registrar.command(self.cmdtable)
41 41 self.configtable = {}
42 42 self.configitem = registrar.configitem(self.configtable)
43 self.revsetpredicate = registrar.revsetpredicate()
43 44
44 45 def merge(self, other):
45 46 self._uicallables.extend(other._uicallables)
46 47 self._uipopulatecallables.extend(other._uipopulatecallables)
47 48 self._extcallables.extend(other._extcallables)
48 49 self._repocallables.extend(other._repocallables)
50 self.revsetpredicate._table.update(other.revsetpredicate._table)
49 51 self._commandwrappers.extend(other._commandwrappers)
50 52 self._extcommandwrappers.extend(other._extcommandwrappers)
51 53 self._functionwrappers.extend(other._functionwrappers)
52 54 self._duckpunchers.extend(other._duckpunchers)
53 55 self.cmdtable.update(other.cmdtable)
54 56 for section, items in other.configtable.iteritems():
55 57 if section in self.configtable:
56 58 self.configtable[section].update(items)
57 59 else:
58 60 self.configtable[section] = items
59 61
60 62 def finaluisetup(self, ui):
61 63 """Method to be used as the extension uisetup
62 64
63 65 The following operations belong here:
64 66
65 67 - Changes to ui.__class__ . The ui object that will be used to run the
66 68 command has not yet been created. Changes made here will affect ui
67 69 objects created after this, and in particular the ui that will be
68 70 passed to runcommand
69 71 - Command wraps (extensions.wrapcommand)
70 72 - Changes that need to be visible to other extensions: because
71 73 initialization occurs in phases (all extensions run uisetup, then all
72 74 run extsetup), a change made here will be visible to other extensions
73 75 during extsetup
74 76 - Monkeypatch or wrap function (extensions.wrapfunction) of dispatch
75 77 module members
76 78 - Setup of pre-* and post-* hooks
77 79 - pushkey setup
78 80 """
79 81 for cont, funcname, func in self._duckpunchers:
80 82 setattr(cont, funcname, func)
81 83 for command, wrapper, opts in self._commandwrappers:
82 84 entry = extensions.wrapcommand(commands.table, command, wrapper)
83 85 if opts:
84 86 for opt in opts:
85 87 entry[1].append(opt)
86 88 for cont, funcname, wrapper in self._functionwrappers:
87 89 extensions.wrapfunction(cont, funcname, wrapper)
88 90 for c in self._uicallables:
89 91 c(ui)
90 92
91 93 def finaluipopulate(self, ui):
92 94 """Method to be used as the extension uipopulate
93 95
94 96 This is called once per ui instance to:
95 97
96 98 - Set up additional ui members
97 99 - Update configuration by ``ui.setconfig()``
98 100 - Extend the class dynamically
99 101 """
100 102 for c in self._uipopulatecallables:
101 103 c(ui)
102 104
103 105 def finalextsetup(self, ui):
104 106 """Method to be used as a the extension extsetup
105 107
106 108 The following operations belong here:
107 109
108 110 - Changes depending on the status of other extensions. (if
109 111 extensions.find('mq'))
110 112 - Add a global option to all commands
111 113 """
112 114 knownexts = {}
113 115
114 116 for ext, command, wrapper, opts in self._extcommandwrappers:
115 117 if ext not in knownexts:
116 118 try:
117 119 e = extensions.find(ext)
118 120 except KeyError:
119 121 # Extension isn't enabled, so don't bother trying to wrap
120 122 # it.
121 123 continue
122 124 knownexts[ext] = e.cmdtable
123 125 entry = extensions.wrapcommand(knownexts[ext], command, wrapper)
124 126 if opts:
125 127 for opt in opts:
126 128 entry[1].append(opt)
127 129
128 130 for c in self._extcallables:
129 131 c(ui)
130 132
131 133 def finalreposetup(self, ui, repo):
132 134 """Method to be used as the extension reposetup
133 135
134 136 The following operations belong here:
135 137
136 138 - All hooks but pre-* and post-*
137 139 - Modify configuration variables
138 140 - Changes to repo.__class__, repo.dirstate.__class__
139 141 """
140 142 for c in self._repocallables:
141 143 c(ui, repo)
142 144
143 145 def uisetup(self, call):
144 146 """Decorated function will be executed during uisetup
145 147
146 148 example::
147 149
148 150 @eh.uisetup
149 151 def setupbabar(ui):
150 152 print 'this is uisetup!'
151 153 """
152 154 self._uicallables.append(call)
153 155 return call
154 156
155 157 def uipopulate(self, call):
156 158 """Decorated function will be executed during uipopulate
157 159
158 160 example::
159 161
160 162 @eh.uipopulate
161 163 def setupfoo(ui):
162 164 print 'this is uipopulate!'
163 165 """
164 166 self._uipopulatecallables.append(call)
165 167 return call
166 168
167 169 def extsetup(self, call):
168 170 """Decorated function will be executed during extsetup
169 171
170 172 example::
171 173
172 174 @eh.extsetup
173 175 def setupcelestine(ui):
174 176 print 'this is extsetup!'
175 177 """
176 178 self._extcallables.append(call)
177 179 return call
178 180
179 181 def reposetup(self, call):
180 182 """Decorated function will be executed during reposetup
181 183
182 184 example::
183 185
184 186 @eh.reposetup
185 187 def setupzephir(ui, repo):
186 188 print 'this is reposetup!'
187 189 """
188 190 self._repocallables.append(call)
189 191 return call
190 192
191 193 def wrapcommand(self, command, extension=None, opts=None):
192 194 """Decorated function is a command wrapper
193 195
194 196 The name of the command must be given as the decorator argument.
195 197 The wrapping is installed during `uisetup`.
196 198
197 199 If the second option `extension` argument is provided, the wrapping
198 200 will be applied in the extension commandtable. This argument must be a
199 201 string that will be searched using `extension.find` if not found and
200 202 Abort error is raised. If the wrapping applies to an extension, it is
201 203 installed during `extsetup`.
202 204
203 205 example::
204 206
205 207 @eh.wrapcommand('summary')
206 208 def wrapsummary(orig, ui, repo, *args, **kwargs):
207 209 ui.note('Barry!')
208 210 return orig(ui, repo, *args, **kwargs)
209 211
210 212 The `opts` argument allows specifying a list of tuples for additional
211 213 arguments for the command. See ``mercurial.fancyopts.fancyopts()`` for
212 214 the format of the tuple.
213 215
214 216 """
215 217 if opts is None:
216 218 opts = []
217 219 else:
218 220 for opt in opts:
219 221 if not isinstance(opt, tuple):
220 222 raise error.ProgrammingError('opts must be list of tuples')
221 223 if len(opt) not in (4, 5):
222 224 msg = 'each opt tuple must contain 4 or 5 values'
223 225 raise error.ProgrammingError(msg)
224 226
225 227 def dec(wrapper):
226 228 if extension is None:
227 229 self._commandwrappers.append((command, wrapper, opts))
228 230 else:
229 231 self._extcommandwrappers.append((extension, command, wrapper,
230 232 opts))
231 233 return wrapper
232 234 return dec
233 235
234 236 def wrapfunction(self, container, funcname):
235 237 """Decorated function is a function wrapper
236 238
237 239 This function takes two arguments, the container and the name of the
238 240 function to wrap. The wrapping is performed during `uisetup`.
239 241 (there is no extension support)
240 242
241 243 example::
242 244
243 245 @eh.function(discovery, 'checkheads')
244 246 def wrapfunction(orig, *args, **kwargs):
245 247 ui.note('His head smashed in and his heart cut out')
246 248 return orig(*args, **kwargs)
247 249 """
248 250 def dec(wrapper):
249 251 self._functionwrappers.append((container, funcname, wrapper))
250 252 return wrapper
251 253 return dec
252 254
253 255 def addattr(self, container, funcname):
254 256 """Decorated function is to be added to the container
255 257
256 258 This function takes two arguments, the container and the name of the
257 259 function to wrap. The wrapping is performed during `uisetup`.
258 260
259 261 Adding attributes to a container like this is discouraged, because the
260 262 container modification is visible even in repositories that do not
261 263 have the extension loaded. Therefore, care must be taken that the
262 264 function doesn't make assumptions that the extension was loaded for the
263 265 current repository. For `ui` and `repo` instances, a better option is
264 266 to subclass the instance in `uipopulate` and `reposetup` respectively.
265 267
266 268 https://www.mercurial-scm.org/wiki/WritingExtensions
267 269
268 270 example::
269 271
270 272 @eh.addattr(context.changectx, 'babar')
271 273 def babar(ctx):
272 274 return 'babar' in ctx.description
273 275 """
274 276 def dec(func):
275 277 self._duckpunchers.append((container, funcname, func))
276 278 return func
277 279 return dec
@@ -1,1846 +1,1856
1 1 Test basic extension support
2 2 $ cat > unflush.py <<EOF
3 3 > import sys
4 4 > from mercurial import pycompat
5 5 > if pycompat.ispy3:
6 6 > # no changes required
7 7 > sys.exit(0)
8 8 > with open(sys.argv[1], 'rb') as f:
9 9 > data = f.read()
10 10 > with open(sys.argv[1], 'wb') as f:
11 11 > f.write(data.replace(b', flush=True', b''))
12 12 > EOF
13 13
14 14 $ cat > foobar.py <<EOF
15 15 > import os
16 16 > from mercurial import commands, exthelper, registrar
17 17 >
18 18 > eh = exthelper.exthelper()
19 19 > eh.configitem(b'tests', b'foo', default=b"Foo")
20 20 >
21 21 > uisetup = eh.finaluisetup
22 22 > uipopulate = eh.finaluipopulate
23 23 > reposetup = eh.finalreposetup
24 24 > cmdtable = eh.cmdtable
25 25 > configtable = eh.configtable
26 26 >
27 27 > @eh.uisetup
28 28 > def _uisetup(ui):
29 29 > ui.debug(b"uisetup called [debug]\\n")
30 30 > ui.write(b"uisetup called\\n")
31 31 > ui.status(b"uisetup called [status]\\n")
32 32 > ui.flush()
33 33 > @eh.uipopulate
34 34 > def _uipopulate(ui):
35 35 > ui._populatecnt = getattr(ui, "_populatecnt", 0) + 1
36 36 > ui.write(b"uipopulate called (%d times)\n" % ui._populatecnt)
37 37 > @eh.reposetup
38 38 > def _reposetup(ui, repo):
39 39 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
40 40 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
41 41 > ui.flush()
42 42 > @eh.command(b'foo', [], b'hg foo')
43 43 > def foo(ui, *args, **kwargs):
44 44 > foo = ui.config(b'tests', b'foo')
45 45 > ui.write(foo)
46 46 > ui.write(b"\\n")
47 47 > @eh.command(b'bar', [], b'hg bar', norepo=True)
48 48 > def bar(ui, *args, **kwargs):
49 49 > ui.write(b"Bar\\n")
50 50 > EOF
51 51 $ abspath=`pwd`/foobar.py
52 52
53 53 $ mkdir barfoo
54 54 $ cp foobar.py barfoo/__init__.py
55 55 $ barfoopath=`pwd`/barfoo
56 56
57 57 $ hg init a
58 58 $ cd a
59 59 $ echo foo > file
60 60 $ hg add file
61 61 $ hg commit -m 'add file'
62 62
63 63 $ echo '[extensions]' >> $HGRCPATH
64 64 $ echo "foobar = $abspath" >> $HGRCPATH
65 65 $ hg foo
66 66 uisetup called
67 67 uisetup called [status]
68 68 uipopulate called (1 times)
69 69 uipopulate called (1 times)
70 70 uipopulate called (1 times)
71 71 reposetup called for a
72 72 ui == repo.ui
73 73 uipopulate called (1 times) (chg !)
74 74 uipopulate called (1 times) (chg !)
75 75 uipopulate called (1 times) (chg !)
76 76 uipopulate called (1 times) (chg !)
77 77 uipopulate called (1 times) (chg !)
78 78 reposetup called for a (chg !)
79 79 ui == repo.ui (chg !)
80 80 Foo
81 81 $ hg foo --quiet
82 82 uisetup called (no-chg !)
83 83 uipopulate called (1 times)
84 84 uipopulate called (1 times)
85 85 uipopulate called (1 times) (chg !)
86 86 uipopulate called (1 times) (chg !)
87 87 uipopulate called (1 times) (chg !)
88 88 reposetup called for a (chg !)
89 89 ui == repo.ui
90 90 Foo
91 91 $ hg foo --debug
92 92 uisetup called [debug] (no-chg !)
93 93 uisetup called (no-chg !)
94 94 uisetup called [status] (no-chg !)
95 95 uipopulate called (1 times)
96 96 uipopulate called (1 times)
97 97 uipopulate called (1 times) (chg !)
98 98 uipopulate called (1 times) (chg !)
99 99 uipopulate called (1 times) (chg !)
100 100 reposetup called for a (chg !)
101 101 ui == repo.ui
102 102 Foo
103 103
104 104 $ cd ..
105 105 $ hg clone a b
106 106 uisetup called (no-chg !)
107 107 uisetup called [status] (no-chg !)
108 108 uipopulate called (1 times)
109 109 uipopulate called (1 times) (chg !)
110 110 uipopulate called (1 times) (chg !)
111 111 reposetup called for a
112 112 ui == repo.ui
113 113 uipopulate called (1 times)
114 114 reposetup called for b
115 115 ui == repo.ui
116 116 updating to branch default
117 117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118
119 119 $ hg bar
120 120 uisetup called (no-chg !)
121 121 uisetup called [status] (no-chg !)
122 122 uipopulate called (1 times)
123 123 uipopulate called (1 times) (chg !)
124 124 Bar
125 125 $ echo 'foobar = !' >> $HGRCPATH
126 126
127 127 module/__init__.py-style
128 128
129 129 $ echo "barfoo = $barfoopath" >> $HGRCPATH
130 130 $ cd a
131 131 $ hg foo
132 132 uisetup called
133 133 uisetup called [status]
134 134 uipopulate called (1 times)
135 135 uipopulate called (1 times)
136 136 uipopulate called (1 times)
137 137 reposetup called for a
138 138 ui == repo.ui
139 139 uipopulate called (1 times) (chg !)
140 140 uipopulate called (1 times) (chg !)
141 141 uipopulate called (1 times) (chg !)
142 142 uipopulate called (1 times) (chg !)
143 143 uipopulate called (1 times) (chg !)
144 144 reposetup called for a (chg !)
145 145 ui == repo.ui (chg !)
146 146 Foo
147 147 $ echo 'barfoo = !' >> $HGRCPATH
148 148
149 149 Check that extensions are loaded in phases:
150 150
151 151 $ cat > foo.py <<EOF
152 152 > from __future__ import print_function
153 153 > import os
154 > from mercurial import exthelper
154 155 > name = os.path.basename(__file__).rsplit('.', 1)[0]
155 156 > print("1) %s imported" % name, flush=True)
156 > def uisetup(ui):
157 > eh = exthelper.exthelper()
158 > @eh.uisetup
159 > def _uisetup(ui):
157 160 > print("2) %s uisetup" % name, flush=True)
158 > def extsetup():
161 > @eh.extsetup
162 > def _extsetup(ui):
159 163 > print("3) %s extsetup" % name, flush=True)
160 > def uipopulate(ui):
164 > @eh.uipopulate
165 > def _uipopulate(ui):
161 166 > print("4) %s uipopulate" % name, flush=True)
162 > def reposetup(ui, repo):
167 > @eh.reposetup
168 > def _reposetup(ui, repo):
163 169 > print("5) %s reposetup" % name, flush=True)
164 170 >
171 > extsetup = eh.finalextsetup
172 > reposetup = eh.finalreposetup
173 > uipopulate = eh.finaluipopulate
174 > uisetup = eh.finaluisetup
175 > revsetpredicate = eh.revsetpredicate
176 >
165 177 > bytesname = name.encode('utf-8')
166 178 > # custom predicate to check registration of functions at loading
167 179 > from mercurial import (
168 > registrar,
169 180 > smartset,
170 181 > )
171 > revsetpredicate = registrar.revsetpredicate()
172 > @revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
182 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
173 183 > def custompredicate(repo, subset, x):
174 184 > return smartset.baseset([r for r in subset if r in {0}])
175 185 > EOF
176 186 $ "$PYTHON" $TESTTMP/unflush.py foo.py
177 187
178 188 $ cp foo.py bar.py
179 189 $ echo 'foo = foo.py' >> $HGRCPATH
180 190 $ echo 'bar = bar.py' >> $HGRCPATH
181 191
182 192 Check normal command's load order of extensions and registration of functions
183 193
184 194 $ hg log -r "foo() and bar()" -q
185 195 1) foo imported
186 196 1) bar imported
187 197 2) foo uisetup
188 198 2) bar uisetup
189 199 3) foo extsetup
190 200 3) bar extsetup
191 201 4) foo uipopulate
192 202 4) bar uipopulate
193 203 4) foo uipopulate
194 204 4) bar uipopulate
195 205 4) foo uipopulate
196 206 4) bar uipopulate
197 207 5) foo reposetup
198 208 5) bar reposetup
199 209 0:c24b9ac61126
200 210
201 211 Check hgweb's load order of extensions and registration of functions
202 212
203 213 $ cat > hgweb.cgi <<EOF
204 214 > #!$PYTHON
205 215 > from mercurial import demandimport; demandimport.enable()
206 216 > from mercurial.hgweb import hgweb
207 217 > from mercurial.hgweb import wsgicgi
208 218 > application = hgweb(b'.', b'test repo')
209 219 > wsgicgi.launch(application)
210 220 > EOF
211 221 $ . "$TESTDIR/cgienv"
212 222
213 223 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
214 224 > | grep '^[0-9]) ' # ignores HTML output
215 225 1) foo imported
216 226 1) bar imported
217 227 2) foo uisetup
218 228 2) bar uisetup
219 229 3) foo extsetup
220 230 3) bar extsetup
221 231 4) foo uipopulate
222 232 4) bar uipopulate
223 233 4) foo uipopulate
224 234 4) bar uipopulate
225 235 5) foo reposetup
226 236 5) bar reposetup
227 237
228 238 (check that revset predicate foo() and bar() are available)
229 239
230 240 #if msys
231 241 $ PATH_INFO='//shortlog'
232 242 #else
233 243 $ PATH_INFO='/shortlog'
234 244 #endif
235 245 $ export PATH_INFO
236 246 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
237 247 > | grep '<a href="/rev/[0-9a-z]*">'
238 248 <a href="/rev/c24b9ac61126">add file</a>
239 249
240 250 $ echo 'foo = !' >> $HGRCPATH
241 251 $ echo 'bar = !' >> $HGRCPATH
242 252
243 253 Check "from __future__ import absolute_import" support for external libraries
244 254
245 255 (import-checker.py reports issues for some of heredoc python code
246 256 fragments below, because import-checker.py does not know test specific
247 257 package hierarchy. NO_CHECK_* should be used as a limit mark of
248 258 heredoc, in order to make import-checker.py ignore them. For
249 259 simplicity, all python code fragments below are generated with such
250 260 limit mark, regardless of importing module or not.)
251 261
252 262 #if windows
253 263 $ PATHSEP=";"
254 264 #else
255 265 $ PATHSEP=":"
256 266 #endif
257 267 $ export PATHSEP
258 268
259 269 $ mkdir $TESTTMP/libroot
260 270 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
261 271 $ mkdir $TESTTMP/libroot/mod
262 272 $ touch $TESTTMP/libroot/mod/__init__.py
263 273 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
264 274
265 275 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
266 276 > from __future__ import absolute_import, print_function
267 277 > import ambig # should load "libroot/ambig.py"
268 278 > s = ambig.s
269 279 > NO_CHECK_EOF
270 280 $ cat > loadabs.py <<NO_CHECK_EOF
271 281 > import mod.ambigabs as ambigabs
272 282 > def extsetup():
273 283 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
274 284 > NO_CHECK_EOF
275 285 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
276 286 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
277 287 ambigabs.s=libroot/ambig.py
278 288 $TESTTMP/a
279 289
280 290 #if no-py3
281 291 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
282 292 > from __future__ import print_function
283 293 > import ambig # should load "libroot/mod/ambig.py"
284 294 > s = ambig.s
285 295 > NO_CHECK_EOF
286 296 $ cat > loadrel.py <<NO_CHECK_EOF
287 297 > import mod.ambigrel as ambigrel
288 298 > def extsetup():
289 299 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
290 300 > NO_CHECK_EOF
291 301 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
292 302 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
293 303 ambigrel.s=libroot/mod/ambig.py
294 304 $TESTTMP/a
295 305 #endif
296 306
297 307 Check absolute/relative import of extension specific modules
298 308
299 309 $ mkdir $TESTTMP/extroot
300 310 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
301 311 > s = b'this is extroot.bar'
302 312 > NO_CHECK_EOF
303 313 $ mkdir $TESTTMP/extroot/sub1
304 314 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
305 315 > s = b'this is extroot.sub1.__init__'
306 316 > NO_CHECK_EOF
307 317 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
308 318 > s = b'this is extroot.sub1.baz'
309 319 > NO_CHECK_EOF
310 320 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
311 321 > from __future__ import absolute_import
312 322 > s = b'this is extroot.__init__'
313 323 > from . import foo
314 324 > def extsetup(ui):
315 325 > ui.write(b'(extroot) ', foo.func(), b'\n')
316 326 > ui.flush()
317 327 > NO_CHECK_EOF
318 328
319 329 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
320 330 > # test absolute import
321 331 > buf = []
322 332 > def func():
323 333 > # "not locals" case
324 334 > import extroot.bar
325 335 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
326 336 > return b'\n(extroot) '.join(buf)
327 337 > # b"fromlist == ('*',)" case
328 338 > from extroot.bar import *
329 339 > buf.append(b'from extroot.bar import *: %s' % s)
330 340 > # "not fromlist" and "if '.' in name" case
331 341 > import extroot.sub1.baz
332 342 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
333 343 > # "not fromlist" and NOT "if '.' in name" case
334 344 > import extroot
335 345 > buf.append(b'import extroot: %s' % extroot.s)
336 346 > # NOT "not fromlist" and NOT "level != -1" case
337 347 > from extroot.bar import s
338 348 > buf.append(b'from extroot.bar import s: %s' % s)
339 349 > NO_CHECK_EOF
340 350 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
341 351 (extroot) from extroot.bar import *: this is extroot.bar
342 352 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
343 353 (extroot) import extroot: this is extroot.__init__
344 354 (extroot) from extroot.bar import s: this is extroot.bar
345 355 (extroot) import extroot.bar in func(): this is extroot.bar
346 356 $TESTTMP/a
347 357
348 358 #if no-py3
349 359 $ rm "$TESTTMP"/extroot/foo.*
350 360 $ rm -Rf "$TESTTMP/extroot/__pycache__"
351 361 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
352 362 > # test relative import
353 363 > buf = []
354 364 > def func():
355 365 > # "not locals" case
356 366 > import bar
357 367 > buf.append('import bar in func(): %s' % bar.s)
358 368 > return '\n(extroot) '.join(buf)
359 369 > # "fromlist == ('*',)" case
360 370 > from bar import *
361 371 > buf.append('from bar import *: %s' % s)
362 372 > # "not fromlist" and "if '.' in name" case
363 373 > import sub1.baz
364 374 > buf.append('import sub1.baz: %s' % sub1.baz.s)
365 375 > # "not fromlist" and NOT "if '.' in name" case
366 376 > import sub1
367 377 > buf.append('import sub1: %s' % sub1.s)
368 378 > # NOT "not fromlist" and NOT "level != -1" case
369 379 > from bar import s
370 380 > buf.append('from bar import s: %s' % s)
371 381 > NO_CHECK_EOF
372 382 $ hg --config extensions.extroot=$TESTTMP/extroot root
373 383 (extroot) from bar import *: this is extroot.bar
374 384 (extroot) import sub1.baz: this is extroot.sub1.baz
375 385 (extroot) import sub1: this is extroot.sub1.__init__
376 386 (extroot) from bar import s: this is extroot.bar
377 387 (extroot) import bar in func(): this is extroot.bar
378 388 $TESTTMP/a
379 389 #endif
380 390
381 391 #if demandimport
382 392
383 393 Examine whether module loading is delayed until actual referring, even
384 394 though module is imported with "absolute_import" feature.
385 395
386 396 Files below in each packages are used for described purpose:
387 397
388 398 - "called": examine whether "from MODULE import ATTR" works correctly
389 399 - "unused": examine whether loading is delayed correctly
390 400 - "used": examine whether "from PACKAGE import MODULE" works correctly
391 401
392 402 Package hierarchy is needed to examine whether demand importing works
393 403 as expected for "from SUB.PACK.AGE import MODULE".
394 404
395 405 Setup "external library" to be imported with "absolute_import"
396 406 feature.
397 407
398 408 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
399 409 $ touch $TESTTMP/extlibroot/__init__.py
400 410 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
401 411 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
402 412
403 413 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
404 414 > def func():
405 415 > return b"this is extlibroot.lsub1.lsub2.called.func()"
406 416 > NO_CHECK_EOF
407 417 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
408 418 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
409 419 > NO_CHECK_EOF
410 420 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
411 421 > detail = b"this is extlibroot.lsub1.lsub2.used"
412 422 > NO_CHECK_EOF
413 423
414 424 Setup sub-package of "external library", which causes instantiation of
415 425 demandmod in "recurse down the module chain" code path. Relative
416 426 importing with "absolute_import" feature isn't tested, because "level
417 427 >=1 " doesn't cause instantiation of demandmod.
418 428
419 429 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
420 430 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
421 431 > detail = b"this is extlibroot.recursedown.abs.used"
422 432 > NO_CHECK_EOF
423 433 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
424 434 > from __future__ import absolute_import
425 435 > from extlibroot.recursedown.abs.used import detail
426 436 > NO_CHECK_EOF
427 437
428 438 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
429 439 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
430 440 > detail = b"this is extlibroot.recursedown.legacy.used"
431 441 > NO_CHECK_EOF
432 442 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
433 443 > # legacy style (level == -1) import
434 444 > from extlibroot.recursedown.legacy.used import detail
435 445 > NO_CHECK_EOF
436 446
437 447 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
438 448 > from __future__ import absolute_import
439 449 > from extlibroot.recursedown.abs import detail as absdetail
440 450 > from .legacy import detail as legacydetail
441 451 > NO_CHECK_EOF
442 452
443 453 Setup package that re-exports an attribute of its submodule as the same
444 454 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
445 455 the submodule 'used' should be somehow accessible. (issue5617)
446 456
447 457 $ mkdir -p $TESTTMP/extlibroot/shadowing
448 458 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
449 459 > detail = b"this is extlibroot.shadowing.used"
450 460 > NO_CHECK_EOF
451 461 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
452 462 > from __future__ import absolute_import
453 463 > from extlibroot.shadowing.used import detail
454 464 > NO_CHECK_EOF
455 465 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
456 466 > from __future__ import absolute_import
457 467 > from .used import detail as used
458 468 > NO_CHECK_EOF
459 469
460 470 Setup extension local modules to be imported with "absolute_import"
461 471 feature.
462 472
463 473 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
464 474 $ touch $TESTTMP/absextroot/xsub1/__init__.py
465 475 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
466 476
467 477 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
468 478 > def func():
469 479 > return b"this is absextroot.xsub1.xsub2.called.func()"
470 480 > NO_CHECK_EOF
471 481 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
472 482 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
473 483 > NO_CHECK_EOF
474 484 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
475 485 > detail = b"this is absextroot.xsub1.xsub2.used"
476 486 > NO_CHECK_EOF
477 487
478 488 Setup extension local modules to examine whether demand importing
479 489 works as expected in "level > 1" case.
480 490
481 491 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
482 492 > detail = b"this is absextroot.relimportee"
483 493 > NO_CHECK_EOF
484 494 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
485 495 > from __future__ import absolute_import
486 496 > from mercurial import pycompat
487 497 > from ... import relimportee
488 498 > detail = b"this relimporter imports %r" % (
489 499 > pycompat.bytestr(relimportee.detail))
490 500 > NO_CHECK_EOF
491 501
492 502 Setup modules, which actually import extension local modules at
493 503 runtime.
494 504
495 505 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
496 506 > from __future__ import absolute_import
497 507 >
498 508 > # import extension local modules absolutely (level = 0)
499 509 > from absextroot.xsub1.xsub2 import used, unused
500 510 > from absextroot.xsub1.xsub2.called import func
501 511 >
502 512 > def getresult():
503 513 > result = []
504 514 > result.append(used.detail)
505 515 > result.append(func())
506 516 > return result
507 517 > NO_CHECK_EOF
508 518
509 519 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
510 520 > from __future__ import absolute_import
511 521 >
512 522 > # import extension local modules relatively (level == 1)
513 523 > from .xsub1.xsub2 import used, unused
514 524 > from .xsub1.xsub2.called import func
515 525 >
516 526 > # import a module, which implies "importing with level > 1"
517 527 > from .xsub1.xsub2 import relimporter
518 528 >
519 529 > def getresult():
520 530 > result = []
521 531 > result.append(used.detail)
522 532 > result.append(func())
523 533 > result.append(relimporter.detail)
524 534 > return result
525 535 > NO_CHECK_EOF
526 536
527 537 Setup main procedure of extension.
528 538
529 539 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
530 540 > from __future__ import absolute_import
531 541 > from mercurial import registrar
532 542 > cmdtable = {}
533 543 > command = registrar.command(cmdtable)
534 544 >
535 545 > # "absolute" and "relative" shouldn't be imported before actual
536 546 > # command execution, because (1) they import same modules, and (2)
537 547 > # preceding import (= instantiate "demandmod" object instead of
538 548 > # real "module" object) might hide problem of succeeding import.
539 549 >
540 550 > @command(b'showabsolute', [], norepo=True)
541 551 > def showabsolute(ui, *args, **opts):
542 552 > from absextroot import absolute
543 553 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
544 554 >
545 555 > @command(b'showrelative', [], norepo=True)
546 556 > def showrelative(ui, *args, **opts):
547 557 > from . import relative
548 558 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
549 559 >
550 560 > # import modules from external library
551 561 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
552 562 > from extlibroot.lsub1.lsub2.called import func as lfunc
553 563 > from extlibroot.recursedown import absdetail, legacydetail
554 564 > from extlibroot.shadowing import proxied
555 565 >
556 566 > def uisetup(ui):
557 567 > result = []
558 568 > result.append(lused.detail)
559 569 > result.append(lfunc())
560 570 > result.append(absdetail)
561 571 > result.append(legacydetail)
562 572 > result.append(proxied.detail)
563 573 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
564 574 > NO_CHECK_EOF
565 575
566 576 Examine module importing.
567 577
568 578 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
569 579 LIB: this is extlibroot.lsub1.lsub2.used
570 580 LIB: this is extlibroot.lsub1.lsub2.called.func()
571 581 LIB: this is extlibroot.recursedown.abs.used
572 582 LIB: this is extlibroot.recursedown.legacy.used
573 583 LIB: this is extlibroot.shadowing.used
574 584 ABS: this is absextroot.xsub1.xsub2.used
575 585 ABS: this is absextroot.xsub1.xsub2.called.func()
576 586
577 587 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
578 588 LIB: this is extlibroot.lsub1.lsub2.used
579 589 LIB: this is extlibroot.lsub1.lsub2.called.func()
580 590 LIB: this is extlibroot.recursedown.abs.used
581 591 LIB: this is extlibroot.recursedown.legacy.used
582 592 LIB: this is extlibroot.shadowing.used
583 593 REL: this is absextroot.xsub1.xsub2.used
584 594 REL: this is absextroot.xsub1.xsub2.called.func()
585 595 REL: this relimporter imports 'this is absextroot.relimportee'
586 596
587 597 Examine whether sub-module is imported relatively as expected.
588 598
589 599 See also issue5208 for detail about example case on Python 3.x.
590 600
591 601 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
592 602 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
593 603
594 604 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
595 605 > text = 'notexist.py at root is loaded unintentionally\n'
596 606 > NO_CHECK_EOF
597 607
598 608 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
599 609 > from mercurial import registrar
600 610 > cmdtable = {}
601 611 > command = registrar.command(cmdtable)
602 612 >
603 613 > # demand import avoids failure of importing notexist here
604 614 > import extlibroot.lsub1.lsub2.notexist
605 615 >
606 616 > @command(b'checkrelativity', [], norepo=True)
607 617 > def checkrelativity(ui, *args, **opts):
608 618 > try:
609 619 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
610 620 > return 1 # unintentional success
611 621 > except ImportError:
612 622 > pass # intentional failure
613 623 > NO_CHECK_EOF
614 624
615 625 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
616 626
617 627 #endif
618 628
619 629 (Here, module importing tests are finished. Therefore, use other than
620 630 NO_CHECK_* limit mark for heredoc python files, in order to apply
621 631 import-checker.py or so on their contents)
622 632
623 633 Make sure a broken uisetup doesn't globally break hg:
624 634 $ cat > $TESTTMP/baduisetup.py <<EOF
625 635 > def uisetup(ui):
626 636 > 1/0
627 637 > EOF
628 638
629 639 Even though the extension fails during uisetup, hg is still basically usable:
630 640 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
631 641 Traceback (most recent call last):
632 642 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
633 643 uisetup(ui)
634 644 File "$TESTTMP/baduisetup.py", line 2, in uisetup
635 645 1/0
636 646 ZeroDivisionError: * by zero (glob)
637 647 *** failed to set up extension baduisetup: * by zero (glob)
638 648 Mercurial Distributed SCM (version *) (glob)
639 649 (see https://mercurial-scm.org for more information)
640 650
641 651 Copyright (C) 2005-* Matt Mackall and others (glob)
642 652 This is free software; see the source for copying conditions. There is NO
643 653 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
644 654
645 655 $ cd ..
646 656
647 657 hide outer repo
648 658 $ hg init
649 659
650 660 $ cat > empty.py <<EOF
651 661 > '''empty cmdtable
652 662 > '''
653 663 > cmdtable = {}
654 664 > EOF
655 665 $ emptypath=`pwd`/empty.py
656 666 $ echo "empty = $emptypath" >> $HGRCPATH
657 667 $ hg help empty
658 668 empty extension - empty cmdtable
659 669
660 670 no commands defined
661 671
662 672
663 673 $ echo 'empty = !' >> $HGRCPATH
664 674
665 675 $ cat > debugextension.py <<EOF
666 676 > '''only debugcommands
667 677 > '''
668 678 > from mercurial import registrar
669 679 > cmdtable = {}
670 680 > command = registrar.command(cmdtable)
671 681 > @command(b'debugfoobar', [], b'hg debugfoobar')
672 682 > def debugfoobar(ui, repo, *args, **opts):
673 683 > "yet another debug command"
674 684 > pass
675 685 > @command(b'foo', [], b'hg foo')
676 686 > def foo(ui, repo, *args, **opts):
677 687 > """yet another foo command
678 688 > This command has been DEPRECATED since forever.
679 689 > """
680 690 > pass
681 691 > EOF
682 692 $ debugpath=`pwd`/debugextension.py
683 693 $ echo "debugextension = $debugpath" >> $HGRCPATH
684 694
685 695 $ hg help debugextension
686 696 hg debugextensions
687 697
688 698 show information about active extensions
689 699
690 700 options:
691 701
692 702 -T --template TEMPLATE display with template
693 703
694 704 (some details hidden, use --verbose to show complete help)
695 705
696 706
697 707 $ hg --verbose help debugextension
698 708 hg debugextensions
699 709
700 710 show information about active extensions
701 711
702 712 options:
703 713
704 714 -T --template TEMPLATE display with template
705 715
706 716 global options ([+] can be repeated):
707 717
708 718 -R --repository REPO repository root directory or name of overlay bundle
709 719 file
710 720 --cwd DIR change working directory
711 721 -y --noninteractive do not prompt, automatically pick the first choice for
712 722 all prompts
713 723 -q --quiet suppress output
714 724 -v --verbose enable additional output
715 725 --color TYPE when to colorize (boolean, always, auto, never, or
716 726 debug)
717 727 --config CONFIG [+] set/override config option (use 'section.name=value')
718 728 --debug enable debugging output
719 729 --debugger start debugger
720 730 --encoding ENCODE set the charset encoding (default: ascii)
721 731 --encodingmode MODE set the charset encoding mode (default: strict)
722 732 --traceback always print a traceback on exception
723 733 --time time how long the command takes
724 734 --profile print command execution profile
725 735 --version output version information and exit
726 736 -h --help display help and exit
727 737 --hidden consider hidden changesets
728 738 --pager TYPE when to paginate (boolean, always, auto, or never)
729 739 (default: auto)
730 740
731 741
732 742
733 743
734 744
735 745
736 746 $ hg --debug help debugextension
737 747 hg debugextensions
738 748
739 749 show information about active extensions
740 750
741 751 options:
742 752
743 753 -T --template TEMPLATE display with template
744 754
745 755 global options ([+] can be repeated):
746 756
747 757 -R --repository REPO repository root directory or name of overlay bundle
748 758 file
749 759 --cwd DIR change working directory
750 760 -y --noninteractive do not prompt, automatically pick the first choice for
751 761 all prompts
752 762 -q --quiet suppress output
753 763 -v --verbose enable additional output
754 764 --color TYPE when to colorize (boolean, always, auto, never, or
755 765 debug)
756 766 --config CONFIG [+] set/override config option (use 'section.name=value')
757 767 --debug enable debugging output
758 768 --debugger start debugger
759 769 --encoding ENCODE set the charset encoding (default: ascii)
760 770 --encodingmode MODE set the charset encoding mode (default: strict)
761 771 --traceback always print a traceback on exception
762 772 --time time how long the command takes
763 773 --profile print command execution profile
764 774 --version output version information and exit
765 775 -h --help display help and exit
766 776 --hidden consider hidden changesets
767 777 --pager TYPE when to paginate (boolean, always, auto, or never)
768 778 (default: auto)
769 779
770 780
771 781
772 782
773 783
774 784 $ echo 'debugextension = !' >> $HGRCPATH
775 785
776 786 Asking for help about a deprecated extension should do something useful:
777 787
778 788 $ hg help glog
779 789 'glog' is provided by the following extension:
780 790
781 791 graphlog command to view revision graphs from a shell (DEPRECATED)
782 792
783 793 (use 'hg help extensions' for information on enabling extensions)
784 794
785 795 Extension module help vs command help:
786 796
787 797 $ echo 'extdiff =' >> $HGRCPATH
788 798 $ hg help extdiff
789 799 hg extdiff [OPT]... [FILE]...
790 800
791 801 use external program to diff repository (or selected files)
792 802
793 803 Show differences between revisions for the specified files, using an
794 804 external program. The default program used is diff, with default options
795 805 "-Npru".
796 806
797 807 To select a different program, use the -p/--program option. The program
798 808 will be passed the names of two directories to compare. To pass additional
799 809 options to the program, use -o/--option. These will be passed before the
800 810 names of the directories to compare.
801 811
802 812 When two revision arguments are given, then changes are shown between
803 813 those revisions. If only one revision is specified then that revision is
804 814 compared to the working directory, and, when no revisions are specified,
805 815 the working directory files are compared to its parent.
806 816
807 817 (use 'hg help -e extdiff' to show help for the extdiff extension)
808 818
809 819 options ([+] can be repeated):
810 820
811 821 -p --program CMD comparison program to run
812 822 -o --option OPT [+] pass option to comparison program
813 823 -r --rev REV [+] revision
814 824 -c --change REV change made by revision
815 825 --patch compare patches for two revisions
816 826 -I --include PATTERN [+] include names matching the given patterns
817 827 -X --exclude PATTERN [+] exclude names matching the given patterns
818 828 -S --subrepos recurse into subrepositories
819 829
820 830 (some details hidden, use --verbose to show complete help)
821 831
822 832
823 833
824 834
825 835
826 836
827 837
828 838
829 839
830 840
831 841 $ hg help --extension extdiff
832 842 extdiff extension - command to allow external programs to compare revisions
833 843
834 844 The extdiff Mercurial extension allows you to use external programs to compare
835 845 revisions, or revision with working directory. The external diff programs are
836 846 called with a configurable set of options and two non-option arguments: paths
837 847 to directories containing snapshots of files to compare.
838 848
839 849 If there is more than one file being compared and the "child" revision is the
840 850 working directory, any modifications made in the external diff program will be
841 851 copied back to the working directory from the temporary directory.
842 852
843 853 The extdiff extension also allows you to configure new diff commands, so you
844 854 do not need to type 'hg extdiff -p kdiff3' always.
845 855
846 856 [extdiff]
847 857 # add new command that runs GNU diff(1) in 'context diff' mode
848 858 cdiff = gdiff -Nprc5
849 859 ## or the old way:
850 860 #cmd.cdiff = gdiff
851 861 #opts.cdiff = -Nprc5
852 862
853 863 # add new command called meld, runs meld (no need to name twice). If
854 864 # the meld executable is not available, the meld tool in [merge-tools]
855 865 # will be used, if available
856 866 meld =
857 867
858 868 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
859 869 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
860 870 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
861 871 # your .vimrc
862 872 vimdiff = gvim -f "+next" \
863 873 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
864 874
865 875 Tool arguments can include variables that are expanded at runtime:
866 876
867 877 $parent1, $plabel1 - filename, descriptive label of first parent
868 878 $child, $clabel - filename, descriptive label of child revision
869 879 $parent2, $plabel2 - filename, descriptive label of second parent
870 880 $root - repository root
871 881 $parent is an alias for $parent1.
872 882
873 883 The extdiff extension will look in your [diff-tools] and [merge-tools]
874 884 sections for diff tool arguments, when none are specified in [extdiff].
875 885
876 886 [extdiff]
877 887 kdiff3 =
878 888
879 889 [diff-tools]
880 890 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
881 891
882 892 You can use -I/-X and list of file or directory names like normal 'hg diff'
883 893 command. The extdiff extension makes snapshots of only needed files, so
884 894 running the external diff program will actually be pretty fast (at least
885 895 faster than having to compare the entire tree).
886 896
887 897 list of commands:
888 898
889 899 extdiff use external program to diff repository (or selected files)
890 900
891 901 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
892 902
893 903
894 904
895 905
896 906
897 907
898 908
899 909
900 910
901 911
902 912
903 913
904 914
905 915
906 916
907 917
908 918 $ echo 'extdiff = !' >> $HGRCPATH
909 919
910 920 Test help topic with same name as extension
911 921
912 922 $ cat > multirevs.py <<EOF
913 923 > from mercurial import commands, registrar
914 924 > cmdtable = {}
915 925 > command = registrar.command(cmdtable)
916 926 > """multirevs extension
917 927 > Big multi-line module docstring."""
918 928 > @command(b'multirevs', [], b'ARG', norepo=True)
919 929 > def multirevs(ui, repo, arg, *args, **opts):
920 930 > """multirevs command"""
921 931 > pass
922 932 > EOF
923 933 $ echo "multirevs = multirevs.py" >> $HGRCPATH
924 934
925 935 $ hg help multirevs | tail
926 936 used):
927 937
928 938 hg update :@
929 939
930 940 - Show diff between tags 1.3 and 1.5 (this works because the first and the
931 941 last revisions of the revset are used):
932 942
933 943 hg diff -r 1.3::1.5
934 944
935 945 use 'hg help -c multirevs' to see help for the multirevs command
936 946
937 947
938 948
939 949
940 950
941 951
942 952 $ hg help -c multirevs
943 953 hg multirevs ARG
944 954
945 955 multirevs command
946 956
947 957 (some details hidden, use --verbose to show complete help)
948 958
949 959
950 960
951 961 $ hg multirevs
952 962 hg multirevs: invalid arguments
953 963 hg multirevs ARG
954 964
955 965 multirevs command
956 966
957 967 (use 'hg multirevs -h' to show more help)
958 968 [255]
959 969
960 970
961 971
962 972 $ echo "multirevs = !" >> $HGRCPATH
963 973
964 974 Issue811: Problem loading extensions twice (by site and by user)
965 975
966 976 $ cat <<EOF >> $HGRCPATH
967 977 > mq =
968 978 > strip =
969 979 > hgext.mq =
970 980 > hgext/mq =
971 981 > EOF
972 982
973 983 Show extensions:
974 984 (note that mq force load strip, also checking it's not loaded twice)
975 985
976 986 #if no-extraextensions
977 987 $ hg debugextensions
978 988 mq
979 989 strip
980 990 #endif
981 991
982 992 For extensions, which name matches one of its commands, help
983 993 message should ask '-v -e' to get list of built-in aliases
984 994 along with extension help itself
985 995
986 996 $ mkdir $TESTTMP/d
987 997 $ cat > $TESTTMP/d/dodo.py <<EOF
988 998 > """
989 999 > This is an awesome 'dodo' extension. It does nothing and
990 1000 > writes 'Foo foo'
991 1001 > """
992 1002 > from mercurial import commands, registrar
993 1003 > cmdtable = {}
994 1004 > command = registrar.command(cmdtable)
995 1005 > @command(b'dodo', [], b'hg dodo')
996 1006 > def dodo(ui, *args, **kwargs):
997 1007 > """Does nothing"""
998 1008 > ui.write(b"I do nothing. Yay\\n")
999 1009 > @command(b'foofoo', [], b'hg foofoo')
1000 1010 > def foofoo(ui, *args, **kwargs):
1001 1011 > """Writes 'Foo foo'"""
1002 1012 > ui.write(b"Foo foo\\n")
1003 1013 > EOF
1004 1014 $ dodopath=$TESTTMP/d/dodo.py
1005 1015
1006 1016 $ echo "dodo = $dodopath" >> $HGRCPATH
1007 1017
1008 1018 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1009 1019 $ hg help -e dodo
1010 1020 dodo extension -
1011 1021
1012 1022 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1013 1023
1014 1024 list of commands:
1015 1025
1016 1026 dodo Does nothing
1017 1027 foofoo Writes 'Foo foo'
1018 1028
1019 1029 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1020 1030
1021 1031 Make sure that '-v -e' prints list of built-in aliases along with
1022 1032 extension help itself
1023 1033 $ hg help -v -e dodo
1024 1034 dodo extension -
1025 1035
1026 1036 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1027 1037
1028 1038 list of commands:
1029 1039
1030 1040 dodo Does nothing
1031 1041 foofoo Writes 'Foo foo'
1032 1042
1033 1043 global options ([+] can be repeated):
1034 1044
1035 1045 -R --repository REPO repository root directory or name of overlay bundle
1036 1046 file
1037 1047 --cwd DIR change working directory
1038 1048 -y --noninteractive do not prompt, automatically pick the first choice for
1039 1049 all prompts
1040 1050 -q --quiet suppress output
1041 1051 -v --verbose enable additional output
1042 1052 --color TYPE when to colorize (boolean, always, auto, never, or
1043 1053 debug)
1044 1054 --config CONFIG [+] set/override config option (use 'section.name=value')
1045 1055 --debug enable debugging output
1046 1056 --debugger start debugger
1047 1057 --encoding ENCODE set the charset encoding (default: ascii)
1048 1058 --encodingmode MODE set the charset encoding mode (default: strict)
1049 1059 --traceback always print a traceback on exception
1050 1060 --time time how long the command takes
1051 1061 --profile print command execution profile
1052 1062 --version output version information and exit
1053 1063 -h --help display help and exit
1054 1064 --hidden consider hidden changesets
1055 1065 --pager TYPE when to paginate (boolean, always, auto, or never)
1056 1066 (default: auto)
1057 1067
1058 1068 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1059 1069 $ hg help -v dodo
1060 1070 hg dodo
1061 1071
1062 1072 Does nothing
1063 1073
1064 1074 (use 'hg help -e dodo' to show help for the dodo extension)
1065 1075
1066 1076 options:
1067 1077
1068 1078 --mq operate on patch repository
1069 1079
1070 1080 global options ([+] can be repeated):
1071 1081
1072 1082 -R --repository REPO repository root directory or name of overlay bundle
1073 1083 file
1074 1084 --cwd DIR change working directory
1075 1085 -y --noninteractive do not prompt, automatically pick the first choice for
1076 1086 all prompts
1077 1087 -q --quiet suppress output
1078 1088 -v --verbose enable additional output
1079 1089 --color TYPE when to colorize (boolean, always, auto, never, or
1080 1090 debug)
1081 1091 --config CONFIG [+] set/override config option (use 'section.name=value')
1082 1092 --debug enable debugging output
1083 1093 --debugger start debugger
1084 1094 --encoding ENCODE set the charset encoding (default: ascii)
1085 1095 --encodingmode MODE set the charset encoding mode (default: strict)
1086 1096 --traceback always print a traceback on exception
1087 1097 --time time how long the command takes
1088 1098 --profile print command execution profile
1089 1099 --version output version information and exit
1090 1100 -h --help display help and exit
1091 1101 --hidden consider hidden changesets
1092 1102 --pager TYPE when to paginate (boolean, always, auto, or never)
1093 1103 (default: auto)
1094 1104
1095 1105 In case when extension name doesn't match any of its commands,
1096 1106 help message should ask for '-v' to get list of built-in aliases
1097 1107 along with extension help
1098 1108 $ cat > $TESTTMP/d/dudu.py <<EOF
1099 1109 > """
1100 1110 > This is an awesome 'dudu' extension. It does something and
1101 1111 > also writes 'Beep beep'
1102 1112 > """
1103 1113 > from mercurial import commands, registrar
1104 1114 > cmdtable = {}
1105 1115 > command = registrar.command(cmdtable)
1106 1116 > @command(b'something', [], b'hg something')
1107 1117 > def something(ui, *args, **kwargs):
1108 1118 > """Does something"""
1109 1119 > ui.write(b"I do something. Yaaay\\n")
1110 1120 > @command(b'beep', [], b'hg beep')
1111 1121 > def beep(ui, *args, **kwargs):
1112 1122 > """Writes 'Beep beep'"""
1113 1123 > ui.write(b"Beep beep\\n")
1114 1124 > EOF
1115 1125 $ dudupath=$TESTTMP/d/dudu.py
1116 1126
1117 1127 $ echo "dudu = $dudupath" >> $HGRCPATH
1118 1128
1119 1129 $ hg help -e dudu
1120 1130 dudu extension -
1121 1131
1122 1132 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1123 1133 beep'
1124 1134
1125 1135 list of commands:
1126 1136
1127 1137 beep Writes 'Beep beep'
1128 1138 something Does something
1129 1139
1130 1140 (use 'hg help -v dudu' to show built-in aliases and global options)
1131 1141
1132 1142 In case when extension name doesn't match any of its commands,
1133 1143 help options '-v' and '-v -e' should be equivalent
1134 1144 $ hg help -v dudu
1135 1145 dudu extension -
1136 1146
1137 1147 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1138 1148 beep'
1139 1149
1140 1150 list of commands:
1141 1151
1142 1152 beep Writes 'Beep beep'
1143 1153 something Does something
1144 1154
1145 1155 global options ([+] can be repeated):
1146 1156
1147 1157 -R --repository REPO repository root directory or name of overlay bundle
1148 1158 file
1149 1159 --cwd DIR change working directory
1150 1160 -y --noninteractive do not prompt, automatically pick the first choice for
1151 1161 all prompts
1152 1162 -q --quiet suppress output
1153 1163 -v --verbose enable additional output
1154 1164 --color TYPE when to colorize (boolean, always, auto, never, or
1155 1165 debug)
1156 1166 --config CONFIG [+] set/override config option (use 'section.name=value')
1157 1167 --debug enable debugging output
1158 1168 --debugger start debugger
1159 1169 --encoding ENCODE set the charset encoding (default: ascii)
1160 1170 --encodingmode MODE set the charset encoding mode (default: strict)
1161 1171 --traceback always print a traceback on exception
1162 1172 --time time how long the command takes
1163 1173 --profile print command execution profile
1164 1174 --version output version information and exit
1165 1175 -h --help display help and exit
1166 1176 --hidden consider hidden changesets
1167 1177 --pager TYPE when to paginate (boolean, always, auto, or never)
1168 1178 (default: auto)
1169 1179
1170 1180 $ hg help -v -e dudu
1171 1181 dudu extension -
1172 1182
1173 1183 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1174 1184 beep'
1175 1185
1176 1186 list of commands:
1177 1187
1178 1188 beep Writes 'Beep beep'
1179 1189 something Does something
1180 1190
1181 1191 global options ([+] can be repeated):
1182 1192
1183 1193 -R --repository REPO repository root directory or name of overlay bundle
1184 1194 file
1185 1195 --cwd DIR change working directory
1186 1196 -y --noninteractive do not prompt, automatically pick the first choice for
1187 1197 all prompts
1188 1198 -q --quiet suppress output
1189 1199 -v --verbose enable additional output
1190 1200 --color TYPE when to colorize (boolean, always, auto, never, or
1191 1201 debug)
1192 1202 --config CONFIG [+] set/override config option (use 'section.name=value')
1193 1203 --debug enable debugging output
1194 1204 --debugger start debugger
1195 1205 --encoding ENCODE set the charset encoding (default: ascii)
1196 1206 --encodingmode MODE set the charset encoding mode (default: strict)
1197 1207 --traceback always print a traceback on exception
1198 1208 --time time how long the command takes
1199 1209 --profile print command execution profile
1200 1210 --version output version information and exit
1201 1211 -h --help display help and exit
1202 1212 --hidden consider hidden changesets
1203 1213 --pager TYPE when to paginate (boolean, always, auto, or never)
1204 1214 (default: auto)
1205 1215
1206 1216 Disabled extension commands:
1207 1217
1208 1218 $ ORGHGRCPATH=$HGRCPATH
1209 1219 $ HGRCPATH=
1210 1220 $ export HGRCPATH
1211 1221 $ hg help email
1212 1222 'email' is provided by the following extension:
1213 1223
1214 1224 patchbomb command to send changesets as (a series of) patch emails
1215 1225
1216 1226 (use 'hg help extensions' for information on enabling extensions)
1217 1227
1218 1228
1219 1229 $ hg qdel
1220 1230 hg: unknown command 'qdel'
1221 1231 'qdelete' is provided by the following extension:
1222 1232
1223 1233 mq manage a stack of patches
1224 1234
1225 1235 (use 'hg help extensions' for information on enabling extensions)
1226 1236 [255]
1227 1237
1228 1238
1229 1239 $ hg churn
1230 1240 hg: unknown command 'churn'
1231 1241 'churn' is provided by the following extension:
1232 1242
1233 1243 churn command to display statistics about repository history
1234 1244
1235 1245 (use 'hg help extensions' for information on enabling extensions)
1236 1246 [255]
1237 1247
1238 1248
1239 1249
1240 1250 Disabled extensions:
1241 1251
1242 1252 $ hg help churn
1243 1253 churn extension - command to display statistics about repository history
1244 1254
1245 1255 (use 'hg help extensions' for information on enabling extensions)
1246 1256
1247 1257 $ hg help patchbomb
1248 1258 patchbomb extension - command to send changesets as (a series of) patch emails
1249 1259
1250 1260 The series is started off with a "[PATCH 0 of N]" introduction, which
1251 1261 describes the series as a whole.
1252 1262
1253 1263 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1254 1264 line of the changeset description as the subject text. The message contains
1255 1265 two or three body parts:
1256 1266
1257 1267 - The changeset description.
1258 1268 - [Optional] The result of running diffstat on the patch.
1259 1269 - The patch itself, as generated by 'hg export'.
1260 1270
1261 1271 Each message refers to the first in the series using the In-Reply-To and
1262 1272 References headers, so they will show up as a sequence in threaded mail and
1263 1273 news readers, and in mail archives.
1264 1274
1265 1275 To configure other defaults, add a section like this to your configuration
1266 1276 file:
1267 1277
1268 1278 [email]
1269 1279 from = My Name <my@email>
1270 1280 to = recipient1, recipient2, ...
1271 1281 cc = cc1, cc2, ...
1272 1282 bcc = bcc1, bcc2, ...
1273 1283 reply-to = address1, address2, ...
1274 1284
1275 1285 Use "[patchbomb]" as configuration section name if you need to override global
1276 1286 "[email]" address settings.
1277 1287
1278 1288 Then you can use the 'hg email' command to mail a series of changesets as a
1279 1289 patchbomb.
1280 1290
1281 1291 You can also either configure the method option in the email section to be a
1282 1292 sendmail compatible mailer or fill out the [smtp] section so that the
1283 1293 patchbomb extension can automatically send patchbombs directly from the
1284 1294 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1285 1295
1286 1296 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1287 1297 supply one via configuration or the command line. You can override this to
1288 1298 never prompt by configuring an empty value:
1289 1299
1290 1300 [email]
1291 1301 cc =
1292 1302
1293 1303 You can control the default inclusion of an introduction message with the
1294 1304 "patchbomb.intro" configuration option. The configuration is always
1295 1305 overwritten by command line flags like --intro and --desc:
1296 1306
1297 1307 [patchbomb]
1298 1308 intro=auto # include introduction message if more than 1 patch (default)
1299 1309 intro=never # never include an introduction message
1300 1310 intro=always # always include an introduction message
1301 1311
1302 1312 You can specify a template for flags to be added in subject prefixes. Flags
1303 1313 specified by --flag option are exported as "{flags}" keyword:
1304 1314
1305 1315 [patchbomb]
1306 1316 flagtemplate = "{separate(' ',
1307 1317 ifeq(branch, 'default', '', branch|upper),
1308 1318 flags)}"
1309 1319
1310 1320 You can set patchbomb to always ask for confirmation by setting
1311 1321 "patchbomb.confirm" to true.
1312 1322
1313 1323 (use 'hg help extensions' for information on enabling extensions)
1314 1324
1315 1325
1316 1326 Broken disabled extension and command:
1317 1327
1318 1328 $ mkdir hgext
1319 1329 $ echo > hgext/__init__.py
1320 1330 $ cat > hgext/broken.py <<NO_CHECK_EOF
1321 1331 > "broken extension'
1322 1332 > NO_CHECK_EOF
1323 1333 $ cat > path.py <<EOF
1324 1334 > import os
1325 1335 > import sys
1326 1336 > sys.path.insert(0, os.environ['HGEXTPATH'])
1327 1337 > EOF
1328 1338 $ HGEXTPATH=`pwd`
1329 1339 $ export HGEXTPATH
1330 1340
1331 1341 $ hg --config extensions.path=./path.py help broken
1332 1342 broken extension - (no help text available)
1333 1343
1334 1344 (use 'hg help extensions' for information on enabling extensions)
1335 1345
1336 1346
1337 1347 $ cat > hgext/forest.py <<EOF
1338 1348 > cmdtable = None
1339 1349 > @command()
1340 1350 > def f():
1341 1351 > pass
1342 1352 > @command(123)
1343 1353 > def g():
1344 1354 > pass
1345 1355 > EOF
1346 1356 $ hg --config extensions.path=./path.py help foo
1347 1357 abort: no such help topic: foo
1348 1358 (try 'hg help --keyword foo')
1349 1359 [255]
1350 1360
1351 1361 $ cat > throw.py <<EOF
1352 1362 > from mercurial import commands, registrar, util
1353 1363 > cmdtable = {}
1354 1364 > command = registrar.command(cmdtable)
1355 1365 > class Bogon(Exception): pass
1356 1366 > @command(b'throw', [], b'hg throw', norepo=True)
1357 1367 > def throw(ui, **opts):
1358 1368 > """throws an exception"""
1359 1369 > raise Bogon()
1360 1370 > EOF
1361 1371
1362 1372 No declared supported version, extension complains:
1363 1373 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1364 1374 ** Unknown exception encountered with possibly-broken third-party extension throw
1365 1375 ** which supports versions unknown of Mercurial.
1366 1376 ** Please disable throw and try your action again.
1367 1377 ** If that fixes the bug please report it to the extension author.
1368 1378 ** Python * (glob)
1369 1379 ** Mercurial Distributed SCM * (glob)
1370 1380 ** Extensions loaded: throw
1371 1381
1372 1382 empty declaration of supported version, extension complains:
1373 1383 $ echo "testedwith = ''" >> throw.py
1374 1384 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1375 1385 ** Unknown exception encountered with possibly-broken third-party extension throw
1376 1386 ** which supports versions unknown of Mercurial.
1377 1387 ** Please disable throw and try your action again.
1378 1388 ** If that fixes the bug please report it to the extension author.
1379 1389 ** Python * (glob)
1380 1390 ** Mercurial Distributed SCM (*) (glob)
1381 1391 ** Extensions loaded: throw
1382 1392
1383 1393 If the extension specifies a buglink, show that:
1384 1394 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1385 1395 $ rm -f throw.pyc throw.pyo
1386 1396 $ rm -Rf __pycache__
1387 1397 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1388 1398 ** Unknown exception encountered with possibly-broken third-party extension throw
1389 1399 ** which supports versions unknown of Mercurial.
1390 1400 ** Please disable throw and try your action again.
1391 1401 ** If that fixes the bug please report it to http://example.com/bts
1392 1402 ** Python * (glob)
1393 1403 ** Mercurial Distributed SCM (*) (glob)
1394 1404 ** Extensions loaded: throw
1395 1405
1396 1406 If the extensions declare outdated versions, accuse the older extension first:
1397 1407 $ echo "from mercurial import util" >> older.py
1398 1408 $ echo "util.version = lambda:b'2.2'" >> older.py
1399 1409 $ echo "testedwith = b'1.9.3'" >> older.py
1400 1410 $ echo "testedwith = b'2.1.1'" >> throw.py
1401 1411 $ rm -f throw.pyc throw.pyo
1402 1412 $ rm -Rf __pycache__
1403 1413 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1404 1414 > throw 2>&1 | egrep '^\*\*'
1405 1415 ** Unknown exception encountered with possibly-broken third-party extension older
1406 1416 ** which supports versions 1.9 of Mercurial.
1407 1417 ** Please disable older and try your action again.
1408 1418 ** If that fixes the bug please report it to the extension author.
1409 1419 ** Python * (glob)
1410 1420 ** Mercurial Distributed SCM (version 2.2)
1411 1421 ** Extensions loaded: throw, older
1412 1422
1413 1423 One extension only tested with older, one only with newer versions:
1414 1424 $ echo "util.version = lambda:b'2.1'" >> older.py
1415 1425 $ rm -f older.pyc older.pyo
1416 1426 $ rm -Rf __pycache__
1417 1427 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1418 1428 > throw 2>&1 | egrep '^\*\*'
1419 1429 ** Unknown exception encountered with possibly-broken third-party extension older
1420 1430 ** which supports versions 1.9 of Mercurial.
1421 1431 ** Please disable older and try your action again.
1422 1432 ** If that fixes the bug please report it to the extension author.
1423 1433 ** Python * (glob)
1424 1434 ** Mercurial Distributed SCM (version 2.1)
1425 1435 ** Extensions loaded: throw, older
1426 1436
1427 1437 Older extension is tested with current version, the other only with newer:
1428 1438 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1429 1439 $ rm -f older.pyc older.pyo
1430 1440 $ rm -Rf __pycache__
1431 1441 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1432 1442 > throw 2>&1 | egrep '^\*\*'
1433 1443 ** Unknown exception encountered with possibly-broken third-party extension throw
1434 1444 ** which supports versions 2.1 of Mercurial.
1435 1445 ** Please disable throw and try your action again.
1436 1446 ** If that fixes the bug please report it to http://example.com/bts
1437 1447 ** Python * (glob)
1438 1448 ** Mercurial Distributed SCM (version 1.9.3)
1439 1449 ** Extensions loaded: throw, older
1440 1450
1441 1451 Ability to point to a different point
1442 1452 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1443 1453 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1444 1454 ** unknown exception encountered, please report by visiting
1445 1455 ** Your Local Goat Lenders
1446 1456 ** Python * (glob)
1447 1457 ** Mercurial Distributed SCM (*) (glob)
1448 1458 ** Extensions loaded: throw, older
1449 1459
1450 1460 Declare the version as supporting this hg version, show regular bts link:
1451 1461 $ hgver=`hg debuginstall -T '{hgver}'`
1452 1462 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1453 1463 $ if [ -z "$hgver" ]; then
1454 1464 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1455 1465 > fi
1456 1466 $ rm -f throw.pyc throw.pyo
1457 1467 $ rm -Rf __pycache__
1458 1468 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1459 1469 ** unknown exception encountered, please report by visiting
1460 1470 ** https://mercurial-scm.org/wiki/BugTracker
1461 1471 ** Python * (glob)
1462 1472 ** Mercurial Distributed SCM (*) (glob)
1463 1473 ** Extensions loaded: throw
1464 1474
1465 1475 Patch version is ignored during compatibility check
1466 1476 $ echo "testedwith = b'3.2'" >> throw.py
1467 1477 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1468 1478 $ rm -f throw.pyc throw.pyo
1469 1479 $ rm -Rf __pycache__
1470 1480 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1471 1481 ** unknown exception encountered, please report by visiting
1472 1482 ** https://mercurial-scm.org/wiki/BugTracker
1473 1483 ** Python * (glob)
1474 1484 ** Mercurial Distributed SCM (*) (glob)
1475 1485 ** Extensions loaded: throw
1476 1486
1477 1487 Test version number support in 'hg version':
1478 1488 $ echo '__version__ = (1, 2, 3)' >> throw.py
1479 1489 $ rm -f throw.pyc throw.pyo
1480 1490 $ rm -Rf __pycache__
1481 1491 $ hg version -v
1482 1492 Mercurial Distributed SCM (version *) (glob)
1483 1493 (see https://mercurial-scm.org for more information)
1484 1494
1485 1495 Copyright (C) 2005-* Matt Mackall and others (glob)
1486 1496 This is free software; see the source for copying conditions. There is NO
1487 1497 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1488 1498
1489 1499 Enabled extensions:
1490 1500
1491 1501
1492 1502 $ hg version -v --config extensions.throw=throw.py
1493 1503 Mercurial Distributed SCM (version *) (glob)
1494 1504 (see https://mercurial-scm.org for more information)
1495 1505
1496 1506 Copyright (C) 2005-* Matt Mackall and others (glob)
1497 1507 This is free software; see the source for copying conditions. There is NO
1498 1508 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1499 1509
1500 1510 Enabled extensions:
1501 1511
1502 1512 throw external 1.2.3
1503 1513 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1504 1514 $ rm -f throw.pyc throw.pyo
1505 1515 $ rm -Rf __pycache__
1506 1516 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1507 1517 Mercurial Distributed SCM (version *) (glob)
1508 1518 (see https://mercurial-scm.org for more information)
1509 1519
1510 1520 Copyright (C) 2005-* Matt Mackall and others (glob)
1511 1521 This is free software; see the source for copying conditions. There is NO
1512 1522 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1513 1523
1514 1524 Enabled extensions:
1515 1525
1516 1526 throw external 1.twentythree
1517 1527 strip internal
1518 1528
1519 1529 $ hg version -q --config extensions.throw=throw.py
1520 1530 Mercurial Distributed SCM (version *) (glob)
1521 1531
1522 1532 Test template output:
1523 1533
1524 1534 $ hg version --config extensions.strip= -T'{extensions}'
1525 1535 strip
1526 1536
1527 1537 Test JSON output of version:
1528 1538
1529 1539 $ hg version -Tjson
1530 1540 [
1531 1541 {
1532 1542 "extensions": [],
1533 1543 "ver": "*" (glob)
1534 1544 }
1535 1545 ]
1536 1546
1537 1547 $ hg version --config extensions.throw=throw.py -Tjson
1538 1548 [
1539 1549 {
1540 1550 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1541 1551 "ver": "3.2.2"
1542 1552 }
1543 1553 ]
1544 1554
1545 1555 $ hg version --config extensions.strip= -Tjson
1546 1556 [
1547 1557 {
1548 1558 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1549 1559 "ver": "*" (glob)
1550 1560 }
1551 1561 ]
1552 1562
1553 1563 Test template output of version:
1554 1564
1555 1565 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1556 1566 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1557 1567 throw 1.twentythree (external)
1558 1568 strip (internal)
1559 1569
1560 1570 Refuse to load extensions with minimum version requirements
1561 1571
1562 1572 $ cat > minversion1.py << EOF
1563 1573 > from mercurial import util
1564 1574 > util.version = lambda: b'3.5.2'
1565 1575 > minimumhgversion = b'3.6'
1566 1576 > EOF
1567 1577 $ hg --config extensions.minversion=minversion1.py version
1568 1578 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1569 1579 Mercurial Distributed SCM (version 3.5.2)
1570 1580 (see https://mercurial-scm.org for more information)
1571 1581
1572 1582 Copyright (C) 2005-* Matt Mackall and others (glob)
1573 1583 This is free software; see the source for copying conditions. There is NO
1574 1584 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1575 1585
1576 1586 $ cat > minversion2.py << EOF
1577 1587 > from mercurial import util
1578 1588 > util.version = lambda: b'3.6'
1579 1589 > minimumhgversion = b'3.7'
1580 1590 > EOF
1581 1591 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1582 1592 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1583 1593
1584 1594 Can load version that is only off by point release
1585 1595
1586 1596 $ cat > minversion2.py << EOF
1587 1597 > from mercurial import util
1588 1598 > util.version = lambda: b'3.6.1'
1589 1599 > minimumhgversion = b'3.6'
1590 1600 > EOF
1591 1601 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1592 1602 [1]
1593 1603
1594 1604 Can load minimum version identical to current
1595 1605
1596 1606 $ cat > minversion3.py << EOF
1597 1607 > from mercurial import util
1598 1608 > util.version = lambda: b'3.5'
1599 1609 > minimumhgversion = b'3.5'
1600 1610 > EOF
1601 1611 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1602 1612 [1]
1603 1613
1604 1614 Restore HGRCPATH
1605 1615
1606 1616 $ HGRCPATH=$ORGHGRCPATH
1607 1617 $ export HGRCPATH
1608 1618
1609 1619 Commands handling multiple repositories at a time should invoke only
1610 1620 "reposetup()" of extensions enabling in the target repository.
1611 1621
1612 1622 $ mkdir reposetup-test
1613 1623 $ cd reposetup-test
1614 1624
1615 1625 $ cat > $TESTTMP/reposetuptest.py <<EOF
1616 1626 > from mercurial import extensions
1617 1627 > def reposetup(ui, repo):
1618 1628 > ui.write(b'reposetup() for %s\n' % (repo.root))
1619 1629 > ui.flush()
1620 1630 > EOF
1621 1631 $ hg init src
1622 1632 $ echo a > src/a
1623 1633 $ hg -R src commit -Am '#0 at src/a'
1624 1634 adding a
1625 1635 $ echo '[extensions]' >> src/.hg/hgrc
1626 1636 $ echo '# enable extension locally' >> src/.hg/hgrc
1627 1637 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1628 1638 $ hg -R src status
1629 1639 reposetup() for $TESTTMP/reposetup-test/src
1630 1640 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1631 1641
1632 1642 #if no-extraextensions
1633 1643 $ hg --cwd src debugextensions
1634 1644 reposetup() for $TESTTMP/reposetup-test/src
1635 1645 dodo (untested!)
1636 1646 dudu (untested!)
1637 1647 mq
1638 1648 reposetuptest (untested!)
1639 1649 strip
1640 1650 #endif
1641 1651
1642 1652 $ hg clone -U src clone-dst1
1643 1653 reposetup() for $TESTTMP/reposetup-test/src
1644 1654 $ hg init push-dst1
1645 1655 $ hg -q -R src push push-dst1
1646 1656 reposetup() for $TESTTMP/reposetup-test/src
1647 1657 $ hg init pull-src1
1648 1658 $ hg -q -R pull-src1 pull src
1649 1659 reposetup() for $TESTTMP/reposetup-test/src
1650 1660
1651 1661 $ cat <<EOF >> $HGRCPATH
1652 1662 > [extensions]
1653 1663 > # disable extension globally and explicitly
1654 1664 > reposetuptest = !
1655 1665 > EOF
1656 1666 $ hg clone -U src clone-dst2
1657 1667 reposetup() for $TESTTMP/reposetup-test/src
1658 1668 $ hg init push-dst2
1659 1669 $ hg -q -R src push push-dst2
1660 1670 reposetup() for $TESTTMP/reposetup-test/src
1661 1671 $ hg init pull-src2
1662 1672 $ hg -q -R pull-src2 pull src
1663 1673 reposetup() for $TESTTMP/reposetup-test/src
1664 1674
1665 1675 $ cat <<EOF >> $HGRCPATH
1666 1676 > [extensions]
1667 1677 > # enable extension globally
1668 1678 > reposetuptest = $TESTTMP/reposetuptest.py
1669 1679 > EOF
1670 1680 $ hg clone -U src clone-dst3
1671 1681 reposetup() for $TESTTMP/reposetup-test/src
1672 1682 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1673 1683 $ hg init push-dst3
1674 1684 reposetup() for $TESTTMP/reposetup-test/push-dst3
1675 1685 $ hg -q -R src push push-dst3
1676 1686 reposetup() for $TESTTMP/reposetup-test/src
1677 1687 reposetup() for $TESTTMP/reposetup-test/push-dst3
1678 1688 $ hg init pull-src3
1679 1689 reposetup() for $TESTTMP/reposetup-test/pull-src3
1680 1690 $ hg -q -R pull-src3 pull src
1681 1691 reposetup() for $TESTTMP/reposetup-test/pull-src3
1682 1692 reposetup() for $TESTTMP/reposetup-test/src
1683 1693
1684 1694 $ echo '[extensions]' >> src/.hg/hgrc
1685 1695 $ echo '# disable extension locally' >> src/.hg/hgrc
1686 1696 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1687 1697 $ hg clone -U src clone-dst4
1688 1698 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1689 1699 $ hg init push-dst4
1690 1700 reposetup() for $TESTTMP/reposetup-test/push-dst4
1691 1701 $ hg -q -R src push push-dst4
1692 1702 reposetup() for $TESTTMP/reposetup-test/push-dst4
1693 1703 $ hg init pull-src4
1694 1704 reposetup() for $TESTTMP/reposetup-test/pull-src4
1695 1705 $ hg -q -R pull-src4 pull src
1696 1706 reposetup() for $TESTTMP/reposetup-test/pull-src4
1697 1707
1698 1708 disabling in command line overlays with all configuration
1699 1709 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1700 1710 $ hg --config extensions.reposetuptest=! init push-dst5
1701 1711 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1702 1712 $ hg --config extensions.reposetuptest=! init pull-src5
1703 1713 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1704 1714
1705 1715 $ cat <<EOF >> $HGRCPATH
1706 1716 > [extensions]
1707 1717 > # disable extension globally and explicitly
1708 1718 > reposetuptest = !
1709 1719 > EOF
1710 1720 $ hg init parent
1711 1721 $ hg init parent/sub1
1712 1722 $ echo 1 > parent/sub1/1
1713 1723 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1714 1724 adding 1
1715 1725 $ hg init parent/sub2
1716 1726 $ hg init parent/sub2/sub21
1717 1727 $ echo 21 > parent/sub2/sub21/21
1718 1728 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1719 1729 adding 21
1720 1730 $ cat > parent/sub2/.hgsub <<EOF
1721 1731 > sub21 = sub21
1722 1732 > EOF
1723 1733 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1724 1734 adding .hgsub
1725 1735 $ hg init parent/sub3
1726 1736 $ echo 3 > parent/sub3/3
1727 1737 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1728 1738 adding 3
1729 1739 $ cat > parent/.hgsub <<EOF
1730 1740 > sub1 = sub1
1731 1741 > sub2 = sub2
1732 1742 > sub3 = sub3
1733 1743 > EOF
1734 1744 $ hg -R parent commit -Am '#0 at parent'
1735 1745 adding .hgsub
1736 1746 $ echo '[extensions]' >> parent/.hg/hgrc
1737 1747 $ echo '# enable extension locally' >> parent/.hg/hgrc
1738 1748 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1739 1749 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1740 1750 $ hg -R parent status -S -A
1741 1751 reposetup() for $TESTTMP/reposetup-test/parent
1742 1752 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1743 1753 C .hgsub
1744 1754 C .hgsubstate
1745 1755 C sub1/1
1746 1756 C sub2/.hgsub
1747 1757 C sub2/.hgsubstate
1748 1758 C sub2/sub21/21
1749 1759 C sub3/3
1750 1760
1751 1761 $ cd ..
1752 1762
1753 1763 Prohibit registration of commands that don't use @command (issue5137)
1754 1764
1755 1765 $ hg init deprecated
1756 1766 $ cd deprecated
1757 1767
1758 1768 $ cat <<EOF > deprecatedcmd.py
1759 1769 > def deprecatedcmd(repo, ui):
1760 1770 > pass
1761 1771 > cmdtable = {
1762 1772 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1763 1773 > }
1764 1774 > EOF
1765 1775 $ cat <<EOF > .hg/hgrc
1766 1776 > [extensions]
1767 1777 > deprecatedcmd = `pwd`/deprecatedcmd.py
1768 1778 > mq = !
1769 1779 > hgext.mq = !
1770 1780 > hgext/mq = !
1771 1781 > EOF
1772 1782
1773 1783 $ hg deprecatedcmd > /dev/null
1774 1784 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1775 1785 *** (use @command decorator to register 'deprecatedcmd')
1776 1786 hg: unknown command 'deprecatedcmd'
1777 1787 (use 'hg help' for a list of commands)
1778 1788 [255]
1779 1789
1780 1790 the extension shouldn't be loaded at all so the mq works:
1781 1791
1782 1792 $ hg qseries --config extensions.mq= > /dev/null
1783 1793 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1784 1794 *** (use @command decorator to register 'deprecatedcmd')
1785 1795
1786 1796 $ cd ..
1787 1797
1788 1798 Test synopsis and docstring extending
1789 1799
1790 1800 $ hg init exthelp
1791 1801 $ cat > exthelp.py <<EOF
1792 1802 > from mercurial import commands, extensions
1793 1803 > def exbookmarks(orig, *args, **opts):
1794 1804 > return orig(*args, **opts)
1795 1805 > def uisetup(ui):
1796 1806 > synopsis = b' GREPME [--foo] [-x]'
1797 1807 > docstring = '''
1798 1808 > GREPME make sure that this is in the help!
1799 1809 > '''
1800 1810 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1801 1811 > synopsis, docstring)
1802 1812 > EOF
1803 1813 $ abspath=`pwd`/exthelp.py
1804 1814 $ echo '[extensions]' >> $HGRCPATH
1805 1815 $ echo "exthelp = $abspath" >> $HGRCPATH
1806 1816 $ cd exthelp
1807 1817 $ hg help bookmarks | grep GREPME
1808 1818 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1809 1819 GREPME make sure that this is in the help!
1810 1820 $ cd ..
1811 1821
1812 1822 Show deprecation warning for the use of cmdutil.command
1813 1823
1814 1824 $ cat > nonregistrar.py <<EOF
1815 1825 > from mercurial import cmdutil
1816 1826 > cmdtable = {}
1817 1827 > command = cmdutil.command(cmdtable)
1818 1828 > @command(b'foo', [], norepo=True)
1819 1829 > def foo(ui):
1820 1830 > pass
1821 1831 > EOF
1822 1832
1823 1833 Prohibit the use of unicode strings as the default value of options
1824 1834
1825 1835 $ hg init $TESTTMP/opt-unicode-default
1826 1836
1827 1837 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1828 1838 > from __future__ import print_function
1829 1839 > from mercurial import registrar
1830 1840 > cmdtable = {}
1831 1841 > command = registrar.command(cmdtable)
1832 1842 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1833 1843 > def ext(*args, **opts):
1834 1844 > print(opts[b'opt'], flush=True)
1835 1845 > EOF
1836 1846 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1837 1847 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1838 1848 > [extensions]
1839 1849 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1840 1850 > EOF
1841 1851 $ hg -R $TESTTMP/opt-unicode-default dummy
1842 1852 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1843 1853 *** (use b'' to make it byte string)
1844 1854 hg: unknown command 'dummy'
1845 1855 (did you mean summary?)
1846 1856 [255]
General Comments 0
You need to be logged in to leave comments. Login now