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