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