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