##// END OF EJS Templates
templatekw: remove dockeywords hack...
Yuya Nishihara -
r26436:a2291c9c default
parent child Browse files
Show More
@@ -1,535 +1,535 b''
1 # help.py - help data for mercurial
1 # help.py - help data for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import gettext, _
8 from i18n import gettext, _
9 import itertools, os, textwrap
9 import itertools, os, textwrap
10 import error
10 import error
11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
12 import templater
12 import templater
13 import encoding, util, minirst
13 import encoding, util, minirst
14 import cmdutil
14 import cmdutil
15 import hgweb.webcommands as webcommands
15 import hgweb.webcommands as webcommands
16
16
17 _exclkeywords = [
17 _exclkeywords = [
18 "(DEPRECATED)",
18 "(DEPRECATED)",
19 "(EXPERIMENTAL)",
19 "(EXPERIMENTAL)",
20 # i18n: "(DEPRECATED)" is a keyword, must be translated consistently
20 # i18n: "(DEPRECATED)" is a keyword, must be translated consistently
21 _("(DEPRECATED)"),
21 _("(DEPRECATED)"),
22 # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently
22 # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently
23 _("(EXPERIMENTAL)"),
23 _("(EXPERIMENTAL)"),
24 ]
24 ]
25
25
26 def listexts(header, exts, indent=1, showdeprecated=False):
26 def listexts(header, exts, indent=1, showdeprecated=False):
27 '''return a text listing of the given extensions'''
27 '''return a text listing of the given extensions'''
28 rst = []
28 rst = []
29 if exts:
29 if exts:
30 rst.append('\n%s\n\n' % header)
30 rst.append('\n%s\n\n' % header)
31 for name, desc in sorted(exts.iteritems()):
31 for name, desc in sorted(exts.iteritems()):
32 if not showdeprecated and any(w in desc for w in _exclkeywords):
32 if not showdeprecated and any(w in desc for w in _exclkeywords):
33 continue
33 continue
34 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
34 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
35 return rst
35 return rst
36
36
37 def extshelp(ui):
37 def extshelp(ui):
38 rst = loaddoc('extensions')(ui).splitlines(True)
38 rst = loaddoc('extensions')(ui).splitlines(True)
39 rst.extend(listexts(
39 rst.extend(listexts(
40 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
40 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
41 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
41 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
42 doc = ''.join(rst)
42 doc = ''.join(rst)
43 return doc
43 return doc
44
44
45 def optrst(header, options, verbose):
45 def optrst(header, options, verbose):
46 data = []
46 data = []
47 multioccur = False
47 multioccur = False
48 for option in options:
48 for option in options:
49 if len(option) == 5:
49 if len(option) == 5:
50 shortopt, longopt, default, desc, optlabel = option
50 shortopt, longopt, default, desc, optlabel = option
51 else:
51 else:
52 shortopt, longopt, default, desc = option
52 shortopt, longopt, default, desc = option
53 optlabel = _("VALUE") # default label
53 optlabel = _("VALUE") # default label
54
54
55 if not verbose and any(w in desc for w in _exclkeywords):
55 if not verbose and any(w in desc for w in _exclkeywords):
56 continue
56 continue
57
57
58 so = ''
58 so = ''
59 if shortopt:
59 if shortopt:
60 so = '-' + shortopt
60 so = '-' + shortopt
61 lo = '--' + longopt
61 lo = '--' + longopt
62 if default:
62 if default:
63 desc += _(" (default: %s)") % default
63 desc += _(" (default: %s)") % default
64
64
65 if isinstance(default, list):
65 if isinstance(default, list):
66 lo += " %s [+]" % optlabel
66 lo += " %s [+]" % optlabel
67 multioccur = True
67 multioccur = True
68 elif (default is not None) and not isinstance(default, bool):
68 elif (default is not None) and not isinstance(default, bool):
69 lo += " %s" % optlabel
69 lo += " %s" % optlabel
70
70
71 data.append((so, lo, desc))
71 data.append((so, lo, desc))
72
72
73 if multioccur:
73 if multioccur:
74 header += (_(" ([+] can be repeated)"))
74 header += (_(" ([+] can be repeated)"))
75
75
76 rst = ['\n%s:\n\n' % header]
76 rst = ['\n%s:\n\n' % header]
77 rst.extend(minirst.maketable(data, 1))
77 rst.extend(minirst.maketable(data, 1))
78
78
79 return ''.join(rst)
79 return ''.join(rst)
80
80
81 def indicateomitted(rst, omitted, notomitted=None):
81 def indicateomitted(rst, omitted, notomitted=None):
82 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
82 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
83 if notomitted:
83 if notomitted:
84 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
84 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
85
85
86 def topicmatch(ui, kw):
86 def topicmatch(ui, kw):
87 """Return help topics matching kw.
87 """Return help topics matching kw.
88
88
89 Returns {'section': [(name, summary), ...], ...} where section is
89 Returns {'section': [(name, summary), ...], ...} where section is
90 one of topics, commands, extensions, or extensioncommands.
90 one of topics, commands, extensions, or extensioncommands.
91 """
91 """
92 kw = encoding.lower(kw)
92 kw = encoding.lower(kw)
93 def lowercontains(container):
93 def lowercontains(container):
94 return kw in encoding.lower(container) # translated in helptable
94 return kw in encoding.lower(container) # translated in helptable
95 results = {'topics': [],
95 results = {'topics': [],
96 'commands': [],
96 'commands': [],
97 'extensions': [],
97 'extensions': [],
98 'extensioncommands': [],
98 'extensioncommands': [],
99 }
99 }
100 for names, header, doc in helptable:
100 for names, header, doc in helptable:
101 # Old extensions may use a str as doc.
101 # Old extensions may use a str as doc.
102 if (sum(map(lowercontains, names))
102 if (sum(map(lowercontains, names))
103 or lowercontains(header)
103 or lowercontains(header)
104 or (callable(doc) and lowercontains(doc(ui)))):
104 or (callable(doc) and lowercontains(doc(ui)))):
105 results['topics'].append((names[0], header))
105 results['topics'].append((names[0], header))
106 import commands # avoid cycle
106 import commands # avoid cycle
107 for cmd, entry in commands.table.iteritems():
107 for cmd, entry in commands.table.iteritems():
108 if len(entry) == 3:
108 if len(entry) == 3:
109 summary = entry[2]
109 summary = entry[2]
110 else:
110 else:
111 summary = ''
111 summary = ''
112 # translate docs *before* searching there
112 # translate docs *before* searching there
113 docs = _(getattr(entry[0], '__doc__', None)) or ''
113 docs = _(getattr(entry[0], '__doc__', None)) or ''
114 if kw in cmd or lowercontains(summary) or lowercontains(docs):
114 if kw in cmd or lowercontains(summary) or lowercontains(docs):
115 doclines = docs.splitlines()
115 doclines = docs.splitlines()
116 if doclines:
116 if doclines:
117 summary = doclines[0]
117 summary = doclines[0]
118 cmdname = cmd.split('|')[0].lstrip('^')
118 cmdname = cmd.split('|')[0].lstrip('^')
119 results['commands'].append((cmdname, summary))
119 results['commands'].append((cmdname, summary))
120 for name, docs in itertools.chain(
120 for name, docs in itertools.chain(
121 extensions.enabled(False).iteritems(),
121 extensions.enabled(False).iteritems(),
122 extensions.disabled().iteritems()):
122 extensions.disabled().iteritems()):
123 # extensions.load ignores the UI argument
123 # extensions.load ignores the UI argument
124 mod = extensions.load(None, name, '')
124 mod = extensions.load(None, name, '')
125 name = name.split('.')[-1]
125 name = name.split('.')[-1]
126 if lowercontains(name) or lowercontains(docs):
126 if lowercontains(name) or lowercontains(docs):
127 # extension docs are already translated
127 # extension docs are already translated
128 results['extensions'].append((name, docs.splitlines()[0]))
128 results['extensions'].append((name, docs.splitlines()[0]))
129 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
129 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
130 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
130 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
131 cmdname = cmd.split('|')[0].lstrip('^')
131 cmdname = cmd.split('|')[0].lstrip('^')
132 if entry[0].__doc__:
132 if entry[0].__doc__:
133 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
133 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
134 else:
134 else:
135 cmddoc = _('(no help text available)')
135 cmddoc = _('(no help text available)')
136 results['extensioncommands'].append((cmdname, cmddoc))
136 results['extensioncommands'].append((cmdname, cmddoc))
137 return results
137 return results
138
138
139 def loaddoc(topic):
139 def loaddoc(topic):
140 """Return a delayed loader for help/topic.txt."""
140 """Return a delayed loader for help/topic.txt."""
141
141
142 def loader(ui):
142 def loader(ui):
143 docdir = os.path.join(util.datapath, 'help')
143 docdir = os.path.join(util.datapath, 'help')
144 path = os.path.join(docdir, topic + ".txt")
144 path = os.path.join(docdir, topic + ".txt")
145 doc = gettext(util.readfile(path))
145 doc = gettext(util.readfile(path))
146 for rewriter in helphooks.get(topic, []):
146 for rewriter in helphooks.get(topic, []):
147 doc = rewriter(ui, topic, doc)
147 doc = rewriter(ui, topic, doc)
148 return doc
148 return doc
149
149
150 return loader
150 return loader
151
151
152 helptable = sorted([
152 helptable = sorted([
153 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
153 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
154 (["dates"], _("Date Formats"), loaddoc('dates')),
154 (["dates"], _("Date Formats"), loaddoc('dates')),
155 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
155 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
156 (['environment', 'env'], _('Environment Variables'),
156 (['environment', 'env'], _('Environment Variables'),
157 loaddoc('environment')),
157 loaddoc('environment')),
158 (['revisions', 'revs'], _('Specifying Single Revisions'),
158 (['revisions', 'revs'], _('Specifying Single Revisions'),
159 loaddoc('revisions')),
159 loaddoc('revisions')),
160 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
160 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
161 loaddoc('multirevs')),
161 loaddoc('multirevs')),
162 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
162 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
163 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
163 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
164 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
164 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
165 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
165 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
166 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
166 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
167 loaddoc('templates')),
167 loaddoc('templates')),
168 (['urls'], _('URL Paths'), loaddoc('urls')),
168 (['urls'], _('URL Paths'), loaddoc('urls')),
169 (["extensions"], _("Using Additional Features"), extshelp),
169 (["extensions"], _("Using Additional Features"), extshelp),
170 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
170 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
171 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
171 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
172 (["glossary"], _("Glossary"), loaddoc('glossary')),
172 (["glossary"], _("Glossary"), loaddoc('glossary')),
173 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
173 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
174 loaddoc('hgignore')),
174 loaddoc('hgignore')),
175 (["phases"], _("Working with Phases"), loaddoc('phases')),
175 (["phases"], _("Working with Phases"), loaddoc('phases')),
176 (['scripting'], _('Using Mercurial from scripts and automation'),
176 (['scripting'], _('Using Mercurial from scripts and automation'),
177 loaddoc('scripting')),
177 loaddoc('scripting')),
178 ])
178 ])
179
179
180 # Map topics to lists of callable taking the current topic help and
180 # Map topics to lists of callable taking the current topic help and
181 # returning the updated version
181 # returning the updated version
182 helphooks = {}
182 helphooks = {}
183
183
184 def addtopichook(topic, rewriter):
184 def addtopichook(topic, rewriter):
185 helphooks.setdefault(topic, []).append(rewriter)
185 helphooks.setdefault(topic, []).append(rewriter)
186
186
187 def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
187 def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
188 """Extract docstring from the items key to function mapping, build a
188 """Extract docstring from the items key to function mapping, build a
189 single documentation block and use it to overwrite the marker in doc.
189 single documentation block and use it to overwrite the marker in doc.
190 """
190 """
191 entries = []
191 entries = []
192 for name in sorted(items):
192 for name in sorted(items):
193 text = (items[name].__doc__ or '').rstrip()
193 text = (items[name].__doc__ or '').rstrip()
194 if (not text
194 if (not text
195 or not ui.verbose and any(w in text for w in _exclkeywords)):
195 or not ui.verbose and any(w in text for w in _exclkeywords)):
196 continue
196 continue
197 text = gettext(text)
197 text = gettext(text)
198 if dedent:
198 if dedent:
199 text = textwrap.dedent(text)
199 text = textwrap.dedent(text)
200 lines = text.splitlines()
200 lines = text.splitlines()
201 doclines = [(lines[0])]
201 doclines = [(lines[0])]
202 for l in lines[1:]:
202 for l in lines[1:]:
203 # Stop once we find some Python doctest
203 # Stop once we find some Python doctest
204 if l.strip().startswith('>>>'):
204 if l.strip().startswith('>>>'):
205 break
205 break
206 if dedent:
206 if dedent:
207 doclines.append(l.rstrip())
207 doclines.append(l.rstrip())
208 else:
208 else:
209 doclines.append(' ' + l.strip())
209 doclines.append(' ' + l.strip())
210 entries.append('\n'.join(doclines))
210 entries.append('\n'.join(doclines))
211 entries = '\n\n'.join(entries)
211 entries = '\n\n'.join(entries)
212 return doc.replace(marker, entries)
212 return doc.replace(marker, entries)
213
213
214 def addtopicsymbols(topic, marker, symbols, dedent=False):
214 def addtopicsymbols(topic, marker, symbols, dedent=False):
215 def add(ui, topic, doc):
215 def add(ui, topic, doc):
216 return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
216 return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
217 addtopichook(topic, add)
217 addtopichook(topic, add)
218
218
219 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
219 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
220 addtopicsymbols('merge-tools', '.. internaltoolsmarker',
220 addtopicsymbols('merge-tools', '.. internaltoolsmarker',
221 filemerge.internalsdoc)
221 filemerge.internalsdoc)
222 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
222 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
223 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
223 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
224 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
224 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
225 addtopicsymbols('templates', '.. functionsmarker', templater.funcs)
225 addtopicsymbols('templates', '.. functionsmarker', templater.funcs)
226 addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
226 addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
227 dedent=True)
227 dedent=True)
228
228
229 def help_(ui, name, unknowncmd=False, full=True, **opts):
229 def help_(ui, name, unknowncmd=False, full=True, **opts):
230 '''
230 '''
231 Generate the help for 'name' as unformatted restructured text. If
231 Generate the help for 'name' as unformatted restructured text. If
232 'name' is None, describe the commands available.
232 'name' is None, describe the commands available.
233 '''
233 '''
234
234
235 import commands # avoid cycle
235 import commands # avoid cycle
236
236
237 def helpcmd(name):
237 def helpcmd(name):
238 try:
238 try:
239 aliases, entry = cmdutil.findcmd(name, commands.table,
239 aliases, entry = cmdutil.findcmd(name, commands.table,
240 strict=unknowncmd)
240 strict=unknowncmd)
241 except error.AmbiguousCommand as inst:
241 except error.AmbiguousCommand as inst:
242 # py3k fix: except vars can't be used outside the scope of the
242 # py3k fix: except vars can't be used outside the scope of the
243 # except block, nor can be used inside a lambda. python issue4617
243 # except block, nor can be used inside a lambda. python issue4617
244 prefix = inst.args[0]
244 prefix = inst.args[0]
245 select = lambda c: c.lstrip('^').startswith(prefix)
245 select = lambda c: c.lstrip('^').startswith(prefix)
246 rst = helplist(select)
246 rst = helplist(select)
247 return rst
247 return rst
248
248
249 rst = []
249 rst = []
250
250
251 # check if it's an invalid alias and display its error if it is
251 # check if it's an invalid alias and display its error if it is
252 if getattr(entry[0], 'badalias', None):
252 if getattr(entry[0], 'badalias', None):
253 rst.append(entry[0].badalias + '\n')
253 rst.append(entry[0].badalias + '\n')
254 if entry[0].unknowncmd:
254 if entry[0].unknowncmd:
255 try:
255 try:
256 rst.extend(helpextcmd(entry[0].cmdname))
256 rst.extend(helpextcmd(entry[0].cmdname))
257 except error.UnknownCommand:
257 except error.UnknownCommand:
258 pass
258 pass
259 return rst
259 return rst
260
260
261 # synopsis
261 # synopsis
262 if len(entry) > 2:
262 if len(entry) > 2:
263 if entry[2].startswith('hg'):
263 if entry[2].startswith('hg'):
264 rst.append("%s\n" % entry[2])
264 rst.append("%s\n" % entry[2])
265 else:
265 else:
266 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
266 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
267 else:
267 else:
268 rst.append('hg %s\n' % aliases[0])
268 rst.append('hg %s\n' % aliases[0])
269 # aliases
269 # aliases
270 if full and not ui.quiet and len(aliases) > 1:
270 if full and not ui.quiet and len(aliases) > 1:
271 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
271 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
272 rst.append('\n')
272 rst.append('\n')
273
273
274 # description
274 # description
275 doc = gettext(entry[0].__doc__)
275 doc = gettext(entry[0].__doc__)
276 if not doc:
276 if not doc:
277 doc = _("(no help text available)")
277 doc = _("(no help text available)")
278 if util.safehasattr(entry[0], 'definition'): # aliased command
278 if util.safehasattr(entry[0], 'definition'): # aliased command
279 if entry[0].definition.startswith('!'): # shell alias
279 if entry[0].definition.startswith('!'): # shell alias
280 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
280 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
281 else:
281 else:
282 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
282 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
283 doc = doc.splitlines(True)
283 doc = doc.splitlines(True)
284 if ui.quiet or not full:
284 if ui.quiet or not full:
285 rst.append(doc[0])
285 rst.append(doc[0])
286 else:
286 else:
287 rst.extend(doc)
287 rst.extend(doc)
288 rst.append('\n')
288 rst.append('\n')
289
289
290 # check if this command shadows a non-trivial (multi-line)
290 # check if this command shadows a non-trivial (multi-line)
291 # extension help text
291 # extension help text
292 try:
292 try:
293 mod = extensions.find(name)
293 mod = extensions.find(name)
294 doc = gettext(mod.__doc__) or ''
294 doc = gettext(mod.__doc__) or ''
295 if '\n' in doc.strip():
295 if '\n' in doc.strip():
296 msg = _('(use "hg help -e %s" to show help for '
296 msg = _('(use "hg help -e %s" to show help for '
297 'the %s extension)') % (name, name)
297 'the %s extension)') % (name, name)
298 rst.append('\n%s\n' % msg)
298 rst.append('\n%s\n' % msg)
299 except KeyError:
299 except KeyError:
300 pass
300 pass
301
301
302 # options
302 # options
303 if not ui.quiet and entry[1]:
303 if not ui.quiet and entry[1]:
304 rst.append(optrst(_("options"), entry[1], ui.verbose))
304 rst.append(optrst(_("options"), entry[1], ui.verbose))
305
305
306 if ui.verbose:
306 if ui.verbose:
307 rst.append(optrst(_("global options"),
307 rst.append(optrst(_("global options"),
308 commands.globalopts, ui.verbose))
308 commands.globalopts, ui.verbose))
309
309
310 if not ui.verbose:
310 if not ui.verbose:
311 if not full:
311 if not full:
312 rst.append(_('\n(use "hg %s -h" to show more help)\n')
312 rst.append(_('\n(use "hg %s -h" to show more help)\n')
313 % name)
313 % name)
314 elif not ui.quiet:
314 elif not ui.quiet:
315 rst.append(_('\n(some details hidden, use --verbose '
315 rst.append(_('\n(some details hidden, use --verbose '
316 'to show complete help)'))
316 'to show complete help)'))
317
317
318 return rst
318 return rst
319
319
320
320
321 def helplist(select=None):
321 def helplist(select=None):
322 # list of commands
322 # list of commands
323 if name == "shortlist":
323 if name == "shortlist":
324 header = _('basic commands:\n\n')
324 header = _('basic commands:\n\n')
325 elif name == "debug":
325 elif name == "debug":
326 header = _('debug commands (internal and unsupported):\n\n')
326 header = _('debug commands (internal and unsupported):\n\n')
327 else:
327 else:
328 header = _('list of commands:\n\n')
328 header = _('list of commands:\n\n')
329
329
330 h = {}
330 h = {}
331 cmds = {}
331 cmds = {}
332 for c, e in commands.table.iteritems():
332 for c, e in commands.table.iteritems():
333 f = c.split("|", 1)[0]
333 f = c.split("|", 1)[0]
334 if select and not select(f):
334 if select and not select(f):
335 continue
335 continue
336 if (not select and name != 'shortlist' and
336 if (not select and name != 'shortlist' and
337 e[0].__module__ != commands.__name__):
337 e[0].__module__ != commands.__name__):
338 continue
338 continue
339 if name == "shortlist" and not f.startswith("^"):
339 if name == "shortlist" and not f.startswith("^"):
340 continue
340 continue
341 f = f.lstrip("^")
341 f = f.lstrip("^")
342 if not ui.debugflag and f.startswith("debug") and name != "debug":
342 if not ui.debugflag and f.startswith("debug") and name != "debug":
343 continue
343 continue
344 doc = e[0].__doc__
344 doc = e[0].__doc__
345 if not ui.verbose and doc and any(w in doc for w in _exclkeywords):
345 if not ui.verbose and doc and any(w in doc for w in _exclkeywords):
346 continue
346 continue
347 doc = gettext(doc)
347 doc = gettext(doc)
348 if not doc:
348 if not doc:
349 doc = _("(no help text available)")
349 doc = _("(no help text available)")
350 h[f] = doc.splitlines()[0].rstrip()
350 h[f] = doc.splitlines()[0].rstrip()
351 cmds[f] = c.lstrip("^")
351 cmds[f] = c.lstrip("^")
352
352
353 rst = []
353 rst = []
354 if not h:
354 if not h:
355 if not ui.quiet:
355 if not ui.quiet:
356 rst.append(_('no commands defined\n'))
356 rst.append(_('no commands defined\n'))
357 return rst
357 return rst
358
358
359 if not ui.quiet:
359 if not ui.quiet:
360 rst.append(header)
360 rst.append(header)
361 fns = sorted(h)
361 fns = sorted(h)
362 for f in fns:
362 for f in fns:
363 if ui.verbose:
363 if ui.verbose:
364 commacmds = cmds[f].replace("|",", ")
364 commacmds = cmds[f].replace("|",", ")
365 rst.append(" :%s: %s\n" % (commacmds, h[f]))
365 rst.append(" :%s: %s\n" % (commacmds, h[f]))
366 else:
366 else:
367 rst.append(' :%s: %s\n' % (f, h[f]))
367 rst.append(' :%s: %s\n' % (f, h[f]))
368
368
369 if not name:
369 if not name:
370 exts = listexts(_('enabled extensions:'), extensions.enabled())
370 exts = listexts(_('enabled extensions:'), extensions.enabled())
371 if exts:
371 if exts:
372 rst.append('\n')
372 rst.append('\n')
373 rst.extend(exts)
373 rst.extend(exts)
374
374
375 rst.append(_("\nadditional help topics:\n\n"))
375 rst.append(_("\nadditional help topics:\n\n"))
376 topics = []
376 topics = []
377 for names, header, doc in helptable:
377 for names, header, doc in helptable:
378 topics.append((names[0], header))
378 topics.append((names[0], header))
379 for t, desc in topics:
379 for t, desc in topics:
380 rst.append(" :%s: %s\n" % (t, desc))
380 rst.append(" :%s: %s\n" % (t, desc))
381
381
382 if ui.quiet:
382 if ui.quiet:
383 pass
383 pass
384 elif ui.verbose:
384 elif ui.verbose:
385 rst.append('\n%s\n' % optrst(_("global options"),
385 rst.append('\n%s\n' % optrst(_("global options"),
386 commands.globalopts, ui.verbose))
386 commands.globalopts, ui.verbose))
387 if name == 'shortlist':
387 if name == 'shortlist':
388 rst.append(_('\n(use "hg help" for the full list '
388 rst.append(_('\n(use "hg help" for the full list '
389 'of commands)\n'))
389 'of commands)\n'))
390 else:
390 else:
391 if name == 'shortlist':
391 if name == 'shortlist':
392 rst.append(_('\n(use "hg help" for the full list of commands '
392 rst.append(_('\n(use "hg help" for the full list of commands '
393 'or "hg -v" for details)\n'))
393 'or "hg -v" for details)\n'))
394 elif name and not full:
394 elif name and not full:
395 rst.append(_('\n(use "hg help %s" to show the full help '
395 rst.append(_('\n(use "hg help %s" to show the full help '
396 'text)\n') % name)
396 'text)\n') % name)
397 elif name and cmds and name in cmds.keys():
397 elif name and cmds and name in cmds.keys():
398 rst.append(_('\n(use "hg help -v -e %s" to show built-in '
398 rst.append(_('\n(use "hg help -v -e %s" to show built-in '
399 'aliases and global options)\n') % name)
399 'aliases and global options)\n') % name)
400 else:
400 else:
401 rst.append(_('\n(use "hg help -v%s" to show built-in aliases '
401 rst.append(_('\n(use "hg help -v%s" to show built-in aliases '
402 'and global options)\n')
402 'and global options)\n')
403 % (name and " " + name or ""))
403 % (name and " " + name or ""))
404 return rst
404 return rst
405
405
406 def helptopic(name):
406 def helptopic(name):
407 for names, header, doc in helptable:
407 for names, header, doc in helptable:
408 if name in names:
408 if name in names:
409 break
409 break
410 else:
410 else:
411 raise error.UnknownCommand(name)
411 raise error.UnknownCommand(name)
412
412
413 rst = [minirst.section(header)]
413 rst = [minirst.section(header)]
414
414
415 # description
415 # description
416 if not doc:
416 if not doc:
417 rst.append(" %s\n" % _("(no help text available)"))
417 rst.append(" %s\n" % _("(no help text available)"))
418 if callable(doc):
418 if callable(doc):
419 rst += [" %s\n" % l for l in doc(ui).splitlines()]
419 rst += [" %s\n" % l for l in doc(ui).splitlines()]
420
420
421 if not ui.verbose:
421 if not ui.verbose:
422 omitted = _('(some details hidden, use --verbose'
422 omitted = _('(some details hidden, use --verbose'
423 ' to show complete help)')
423 ' to show complete help)')
424 indicateomitted(rst, omitted)
424 indicateomitted(rst, omitted)
425
425
426 try:
426 try:
427 cmdutil.findcmd(name, commands.table)
427 cmdutil.findcmd(name, commands.table)
428 rst.append(_('\nuse "hg help -c %s" to see help for '
428 rst.append(_('\nuse "hg help -c %s" to see help for '
429 'the %s command\n') % (name, name))
429 'the %s command\n') % (name, name))
430 except error.UnknownCommand:
430 except error.UnknownCommand:
431 pass
431 pass
432 return rst
432 return rst
433
433
434 def helpext(name):
434 def helpext(name):
435 try:
435 try:
436 mod = extensions.find(name)
436 mod = extensions.find(name)
437 doc = gettext(mod.__doc__) or _('no help text available')
437 doc = gettext(mod.__doc__) or _('no help text available')
438 except KeyError:
438 except KeyError:
439 mod = None
439 mod = None
440 doc = extensions.disabledext(name)
440 doc = extensions.disabledext(name)
441 if not doc:
441 if not doc:
442 raise error.UnknownCommand(name)
442 raise error.UnknownCommand(name)
443
443
444 if '\n' not in doc:
444 if '\n' not in doc:
445 head, tail = doc, ""
445 head, tail = doc, ""
446 else:
446 else:
447 head, tail = doc.split('\n', 1)
447 head, tail = doc.split('\n', 1)
448 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
448 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
449 if tail:
449 if tail:
450 rst.extend(tail.splitlines(True))
450 rst.extend(tail.splitlines(True))
451 rst.append('\n')
451 rst.append('\n')
452
452
453 if not ui.verbose:
453 if not ui.verbose:
454 omitted = _('(some details hidden, use --verbose'
454 omitted = _('(some details hidden, use --verbose'
455 ' to show complete help)')
455 ' to show complete help)')
456 indicateomitted(rst, omitted)
456 indicateomitted(rst, omitted)
457
457
458 if mod:
458 if mod:
459 try:
459 try:
460 ct = mod.cmdtable
460 ct = mod.cmdtable
461 except AttributeError:
461 except AttributeError:
462 ct = {}
462 ct = {}
463 modcmds = set([c.split('|', 1)[0] for c in ct])
463 modcmds = set([c.split('|', 1)[0] for c in ct])
464 rst.extend(helplist(modcmds.__contains__))
464 rst.extend(helplist(modcmds.__contains__))
465 else:
465 else:
466 rst.append(_('(use "hg help extensions" for information on enabling'
466 rst.append(_('(use "hg help extensions" for information on enabling'
467 ' extensions)\n'))
467 ' extensions)\n'))
468 return rst
468 return rst
469
469
470 def helpextcmd(name):
470 def helpextcmd(name):
471 cmd, ext, mod = extensions.disabledcmd(ui, name,
471 cmd, ext, mod = extensions.disabledcmd(ui, name,
472 ui.configbool('ui', 'strict'))
472 ui.configbool('ui', 'strict'))
473 doc = gettext(mod.__doc__).splitlines()[0]
473 doc = gettext(mod.__doc__).splitlines()[0]
474
474
475 rst = listexts(_("'%s' is provided by the following "
475 rst = listexts(_("'%s' is provided by the following "
476 "extension:") % cmd, {ext: doc}, indent=4)
476 "extension:") % cmd, {ext: doc}, indent=4)
477 rst.append('\n')
477 rst.append('\n')
478 rst.append(_('(use "hg help extensions" for information on enabling '
478 rst.append(_('(use "hg help extensions" for information on enabling '
479 'extensions)\n'))
479 'extensions)\n'))
480 return rst
480 return rst
481
481
482
482
483 rst = []
483 rst = []
484 kw = opts.get('keyword')
484 kw = opts.get('keyword')
485 if kw:
485 if kw:
486 matches = topicmatch(ui, name)
486 matches = topicmatch(ui, name)
487 helpareas = []
487 helpareas = []
488 if opts.get('extension'):
488 if opts.get('extension'):
489 helpareas += [('extensions', _('Extensions'))]
489 helpareas += [('extensions', _('Extensions'))]
490 if opts.get('command'):
490 if opts.get('command'):
491 helpareas += [('commands', _('Commands'))]
491 helpareas += [('commands', _('Commands'))]
492 if not helpareas:
492 if not helpareas:
493 helpareas = [('topics', _('Topics')),
493 helpareas = [('topics', _('Topics')),
494 ('commands', _('Commands')),
494 ('commands', _('Commands')),
495 ('extensions', _('Extensions')),
495 ('extensions', _('Extensions')),
496 ('extensioncommands', _('Extension Commands'))]
496 ('extensioncommands', _('Extension Commands'))]
497 for t, title in helpareas:
497 for t, title in helpareas:
498 if matches[t]:
498 if matches[t]:
499 rst.append('%s:\n\n' % title)
499 rst.append('%s:\n\n' % title)
500 rst.extend(minirst.maketable(sorted(matches[t]), 1))
500 rst.extend(minirst.maketable(sorted(matches[t]), 1))
501 rst.append('\n')
501 rst.append('\n')
502 if not rst:
502 if not rst:
503 msg = _('no matches')
503 msg = _('no matches')
504 hint = _('try "hg help" for a list of topics')
504 hint = _('try "hg help" for a list of topics')
505 raise util.Abort(msg, hint=hint)
505 raise util.Abort(msg, hint=hint)
506 elif name and name != 'shortlist':
506 elif name and name != 'shortlist':
507 queries = []
507 queries = []
508 if unknowncmd:
508 if unknowncmd:
509 queries += [helpextcmd]
509 queries += [helpextcmd]
510 if opts.get('extension'):
510 if opts.get('extension'):
511 queries += [helpext]
511 queries += [helpext]
512 if opts.get('command'):
512 if opts.get('command'):
513 queries += [helpcmd]
513 queries += [helpcmd]
514 if not queries:
514 if not queries:
515 queries = (helptopic, helpcmd, helpext, helpextcmd)
515 queries = (helptopic, helpcmd, helpext, helpextcmd)
516 for f in queries:
516 for f in queries:
517 try:
517 try:
518 rst = f(name)
518 rst = f(name)
519 break
519 break
520 except error.UnknownCommand:
520 except error.UnknownCommand:
521 pass
521 pass
522 else:
522 else:
523 if unknowncmd:
523 if unknowncmd:
524 raise error.UnknownCommand(name)
524 raise error.UnknownCommand(name)
525 else:
525 else:
526 msg = _('no such help topic: %s') % name
526 msg = _('no such help topic: %s') % name
527 hint = _('try "hg help --keyword %s"') % name
527 hint = _('try "hg help --keyword %s"') % name
528 raise util.Abort(msg, hint=hint)
528 raise util.Abort(msg, hint=hint)
529 else:
529 else:
530 # program name
530 # program name
531 if not ui.quiet:
531 if not ui.quiet:
532 rst = [_("Mercurial Distributed SCM\n"), '\n']
532 rst = [_("Mercurial Distributed SCM\n"), '\n']
533 rst.extend(helplist())
533 rst.extend(helplist())
534
534
535 return ''.join(rst)
535 return ''.join(rst)
@@ -1,514 +1,509 b''
1 # templatekw.py - common changeset template keywords
1 # templatekw.py - common changeset template keywords
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .node import hex
10 from .node import hex
11 from . import (
11 from . import (
12 error,
12 error,
13 hbisect,
13 hbisect,
14 patch,
14 patch,
15 scmutil,
15 scmutil,
16 util,
16 util,
17 )
17 )
18
18
19 # This helper class allows us to handle both:
19 # This helper class allows us to handle both:
20 # "{files}" (legacy command-line-specific list hack) and
20 # "{files}" (legacy command-line-specific list hack) and
21 # "{files % '{file}\n'}" (hgweb-style with inlining and function support)
21 # "{files % '{file}\n'}" (hgweb-style with inlining and function support)
22 # and to access raw values:
22 # and to access raw values:
23 # "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
23 # "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
24 # "{get(extras, key)}"
24 # "{get(extras, key)}"
25
25
26 class _hybrid(object):
26 class _hybrid(object):
27 def __init__(self, gen, values, makemap, joinfmt=None):
27 def __init__(self, gen, values, makemap, joinfmt=None):
28 self.gen = gen
28 self.gen = gen
29 self.values = values
29 self.values = values
30 self._makemap = makemap
30 self._makemap = makemap
31 if joinfmt:
31 if joinfmt:
32 self.joinfmt = joinfmt
32 self.joinfmt = joinfmt
33 else:
33 else:
34 self.joinfmt = lambda x: x.values()[0]
34 self.joinfmt = lambda x: x.values()[0]
35 def __iter__(self):
35 def __iter__(self):
36 return self.gen
36 return self.gen
37 def __call__(self):
37 def __call__(self):
38 makemap = self._makemap
38 makemap = self._makemap
39 for x in self.values:
39 for x in self.values:
40 yield makemap(x)
40 yield makemap(x)
41 def __contains__(self, x):
41 def __contains__(self, x):
42 return x in self.values
42 return x in self.values
43 def __len__(self):
43 def __len__(self):
44 return len(self.values)
44 return len(self.values)
45 def __getattr__(self, name):
45 def __getattr__(self, name):
46 if name != 'get':
46 if name != 'get':
47 raise AttributeError(name)
47 raise AttributeError(name)
48 return getattr(self.values, name)
48 return getattr(self.values, name)
49
49
50 def showlist(name, values, plural=None, element=None, separator=' ', **args):
50 def showlist(name, values, plural=None, element=None, separator=' ', **args):
51 if not element:
51 if not element:
52 element = name
52 element = name
53 f = _showlist(name, values, plural, separator, **args)
53 f = _showlist(name, values, plural, separator, **args)
54 return _hybrid(f, values, lambda x: {element: x})
54 return _hybrid(f, values, lambda x: {element: x})
55
55
56 def _showlist(name, values, plural=None, separator=' ', **args):
56 def _showlist(name, values, plural=None, separator=' ', **args):
57 '''expand set of values.
57 '''expand set of values.
58 name is name of key in template map.
58 name is name of key in template map.
59 values is list of strings or dicts.
59 values is list of strings or dicts.
60 plural is plural of name, if not simply name + 's'.
60 plural is plural of name, if not simply name + 's'.
61 separator is used to join values as a string
61 separator is used to join values as a string
62
62
63 expansion works like this, given name 'foo'.
63 expansion works like this, given name 'foo'.
64
64
65 if values is empty, expand 'no_foos'.
65 if values is empty, expand 'no_foos'.
66
66
67 if 'foo' not in template map, return values as a string,
67 if 'foo' not in template map, return values as a string,
68 joined by 'separator'.
68 joined by 'separator'.
69
69
70 expand 'start_foos'.
70 expand 'start_foos'.
71
71
72 for each value, expand 'foo'. if 'last_foo' in template
72 for each value, expand 'foo'. if 'last_foo' in template
73 map, expand it instead of 'foo' for last key.
73 map, expand it instead of 'foo' for last key.
74
74
75 expand 'end_foos'.
75 expand 'end_foos'.
76 '''
76 '''
77 templ = args['templ']
77 templ = args['templ']
78 if plural:
78 if plural:
79 names = plural
79 names = plural
80 else: names = name + 's'
80 else: names = name + 's'
81 if not values:
81 if not values:
82 noname = 'no_' + names
82 noname = 'no_' + names
83 if noname in templ:
83 if noname in templ:
84 yield templ(noname, **args)
84 yield templ(noname, **args)
85 return
85 return
86 if name not in templ:
86 if name not in templ:
87 if isinstance(values[0], str):
87 if isinstance(values[0], str):
88 yield separator.join(values)
88 yield separator.join(values)
89 else:
89 else:
90 for v in values:
90 for v in values:
91 yield dict(v, **args)
91 yield dict(v, **args)
92 return
92 return
93 startname = 'start_' + names
93 startname = 'start_' + names
94 if startname in templ:
94 if startname in templ:
95 yield templ(startname, **args)
95 yield templ(startname, **args)
96 vargs = args.copy()
96 vargs = args.copy()
97 def one(v, tag=name):
97 def one(v, tag=name):
98 try:
98 try:
99 vargs.update(v)
99 vargs.update(v)
100 except (AttributeError, ValueError):
100 except (AttributeError, ValueError):
101 try:
101 try:
102 for a, b in v:
102 for a, b in v:
103 vargs[a] = b
103 vargs[a] = b
104 except ValueError:
104 except ValueError:
105 vargs[name] = v
105 vargs[name] = v
106 return templ(tag, **vargs)
106 return templ(tag, **vargs)
107 lastname = 'last_' + name
107 lastname = 'last_' + name
108 if lastname in templ:
108 if lastname in templ:
109 last = values.pop()
109 last = values.pop()
110 else:
110 else:
111 last = None
111 last = None
112 for v in values:
112 for v in values:
113 yield one(v)
113 yield one(v)
114 if last is not None:
114 if last is not None:
115 yield one(last, tag=lastname)
115 yield one(last, tag=lastname)
116 endname = 'end_' + names
116 endname = 'end_' + names
117 if endname in templ:
117 if endname in templ:
118 yield templ(endname, **args)
118 yield templ(endname, **args)
119
119
120 def getfiles(repo, ctx, revcache):
120 def getfiles(repo, ctx, revcache):
121 if 'files' not in revcache:
121 if 'files' not in revcache:
122 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
122 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
123 return revcache['files']
123 return revcache['files']
124
124
125 def getlatesttags(repo, ctx, cache):
125 def getlatesttags(repo, ctx, cache):
126 '''return date, distance and name for the latest tag of rev'''
126 '''return date, distance and name for the latest tag of rev'''
127
127
128 if 'latesttags' not in cache:
128 if 'latesttags' not in cache:
129 # Cache mapping from rev to a tuple with tag date, tag
129 # Cache mapping from rev to a tuple with tag date, tag
130 # distance and tag name
130 # distance and tag name
131 cache['latesttags'] = {-1: (0, 0, ['null'])}
131 cache['latesttags'] = {-1: (0, 0, ['null'])}
132 latesttags = cache['latesttags']
132 latesttags = cache['latesttags']
133
133
134 rev = ctx.rev()
134 rev = ctx.rev()
135 todo = [rev]
135 todo = [rev]
136 while todo:
136 while todo:
137 rev = todo.pop()
137 rev = todo.pop()
138 if rev in latesttags:
138 if rev in latesttags:
139 continue
139 continue
140 ctx = repo[rev]
140 ctx = repo[rev]
141 tags = [t for t in ctx.tags()
141 tags = [t for t in ctx.tags()
142 if (repo.tagtype(t) and repo.tagtype(t) != 'local')]
142 if (repo.tagtype(t) and repo.tagtype(t) != 'local')]
143 if tags:
143 if tags:
144 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
144 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
145 continue
145 continue
146 try:
146 try:
147 # The tuples are laid out so the right one can be found by
147 # The tuples are laid out so the right one can be found by
148 # comparison.
148 # comparison.
149 pdate, pdist, ptag = max(
149 pdate, pdist, ptag = max(
150 latesttags[p.rev()] for p in ctx.parents())
150 latesttags[p.rev()] for p in ctx.parents())
151 except KeyError:
151 except KeyError:
152 # Cache miss - recurse
152 # Cache miss - recurse
153 todo.append(rev)
153 todo.append(rev)
154 todo.extend(p.rev() for p in ctx.parents())
154 todo.extend(p.rev() for p in ctx.parents())
155 continue
155 continue
156 latesttags[rev] = pdate, pdist + 1, ptag
156 latesttags[rev] = pdate, pdist + 1, ptag
157 return latesttags[rev]
157 return latesttags[rev]
158
158
159 def getrenamedfn(repo, endrev=None):
159 def getrenamedfn(repo, endrev=None):
160 rcache = {}
160 rcache = {}
161 if endrev is None:
161 if endrev is None:
162 endrev = len(repo)
162 endrev = len(repo)
163
163
164 def getrenamed(fn, rev):
164 def getrenamed(fn, rev):
165 '''looks up all renames for a file (up to endrev) the first
165 '''looks up all renames for a file (up to endrev) the first
166 time the file is given. It indexes on the changerev and only
166 time the file is given. It indexes on the changerev and only
167 parses the manifest if linkrev != changerev.
167 parses the manifest if linkrev != changerev.
168 Returns rename info for fn at changerev rev.'''
168 Returns rename info for fn at changerev rev.'''
169 if fn not in rcache:
169 if fn not in rcache:
170 rcache[fn] = {}
170 rcache[fn] = {}
171 fl = repo.file(fn)
171 fl = repo.file(fn)
172 for i in fl:
172 for i in fl:
173 lr = fl.linkrev(i)
173 lr = fl.linkrev(i)
174 renamed = fl.renamed(fl.node(i))
174 renamed = fl.renamed(fl.node(i))
175 rcache[fn][lr] = renamed
175 rcache[fn][lr] = renamed
176 if lr >= endrev:
176 if lr >= endrev:
177 break
177 break
178 if rev in rcache[fn]:
178 if rev in rcache[fn]:
179 return rcache[fn][rev]
179 return rcache[fn][rev]
180
180
181 # If linkrev != rev (i.e. rev not found in rcache) fallback to
181 # If linkrev != rev (i.e. rev not found in rcache) fallback to
182 # filectx logic.
182 # filectx logic.
183 try:
183 try:
184 return repo[rev][fn].renamed()
184 return repo[rev][fn].renamed()
185 except error.LookupError:
185 except error.LookupError:
186 return None
186 return None
187
187
188 return getrenamed
188 return getrenamed
189
189
190
190
191 def showauthor(repo, ctx, templ, **args):
191 def showauthor(repo, ctx, templ, **args):
192 """:author: String. The unmodified author of the changeset."""
192 """:author: String. The unmodified author of the changeset."""
193 return ctx.user()
193 return ctx.user()
194
194
195 def showbisect(repo, ctx, templ, **args):
195 def showbisect(repo, ctx, templ, **args):
196 """:bisect: String. The changeset bisection status."""
196 """:bisect: String. The changeset bisection status."""
197 return hbisect.label(repo, ctx.node())
197 return hbisect.label(repo, ctx.node())
198
198
199 def showbranch(**args):
199 def showbranch(**args):
200 """:branch: String. The name of the branch on which the changeset was
200 """:branch: String. The name of the branch on which the changeset was
201 committed.
201 committed.
202 """
202 """
203 return args['ctx'].branch()
203 return args['ctx'].branch()
204
204
205 def showbranches(**args):
205 def showbranches(**args):
206 """:branches: List of strings. The name of the branch on which the
206 """:branches: List of strings. The name of the branch on which the
207 changeset was committed. Will be empty if the branch name was
207 changeset was committed. Will be empty if the branch name was
208 default.
208 default.
209 """
209 """
210 branch = args['ctx'].branch()
210 branch = args['ctx'].branch()
211 if branch != 'default':
211 if branch != 'default':
212 return showlist('branch', [branch], plural='branches', **args)
212 return showlist('branch', [branch], plural='branches', **args)
213 return showlist('branch', [], plural='branches', **args)
213 return showlist('branch', [], plural='branches', **args)
214
214
215 def showbookmarks(**args):
215 def showbookmarks(**args):
216 """:bookmarks: List of strings. Any bookmarks associated with the
216 """:bookmarks: List of strings. Any bookmarks associated with the
217 changeset. Also sets 'active', the name of the active bookmark.
217 changeset. Also sets 'active', the name of the active bookmark.
218 """
218 """
219 repo = args['ctx']._repo
219 repo = args['ctx']._repo
220 bookmarks = args['ctx'].bookmarks()
220 bookmarks = args['ctx'].bookmarks()
221 active = repo._activebookmark
221 active = repo._activebookmark
222 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
222 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
223 f = _showlist('bookmark', bookmarks, **args)
223 f = _showlist('bookmark', bookmarks, **args)
224 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
224 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
225
225
226 def showchildren(**args):
226 def showchildren(**args):
227 """:children: List of strings. The children of the changeset."""
227 """:children: List of strings. The children of the changeset."""
228 ctx = args['ctx']
228 ctx = args['ctx']
229 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
229 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
230 return showlist('children', childrevs, element='child', **args)
230 return showlist('children', childrevs, element='child', **args)
231
231
232 # Deprecated, but kept alive for help generation a purpose.
232 # Deprecated, but kept alive for help generation a purpose.
233 def showcurrentbookmark(**args):
233 def showcurrentbookmark(**args):
234 """:currentbookmark: String. The active bookmark, if it is
234 """:currentbookmark: String. The active bookmark, if it is
235 associated with the changeset (DEPRECATED)"""
235 associated with the changeset (DEPRECATED)"""
236 return showactivebookmark(**args)
236 return showactivebookmark(**args)
237
237
238 def showactivebookmark(**args):
238 def showactivebookmark(**args):
239 """:activebookmark: String. The active bookmark, if it is
239 """:activebookmark: String. The active bookmark, if it is
240 associated with the changeset"""
240 associated with the changeset"""
241 active = args['repo']._activebookmark
241 active = args['repo']._activebookmark
242 if active and active in args['ctx'].bookmarks():
242 if active and active in args['ctx'].bookmarks():
243 return active
243 return active
244 return ''
244 return ''
245
245
246 def showdate(repo, ctx, templ, **args):
246 def showdate(repo, ctx, templ, **args):
247 """:date: Date information. The date when the changeset was committed."""
247 """:date: Date information. The date when the changeset was committed."""
248 return ctx.date()
248 return ctx.date()
249
249
250 def showdescription(repo, ctx, templ, **args):
250 def showdescription(repo, ctx, templ, **args):
251 """:desc: String. The text of the changeset description."""
251 """:desc: String. The text of the changeset description."""
252 return ctx.description().strip()
252 return ctx.description().strip()
253
253
254 def showdiffstat(repo, ctx, templ, **args):
254 def showdiffstat(repo, ctx, templ, **args):
255 """:diffstat: String. Statistics of changes with the following format:
255 """:diffstat: String. Statistics of changes with the following format:
256 "modified files: +added/-removed lines"
256 "modified files: +added/-removed lines"
257 """
257 """
258 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
258 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
259 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
259 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
260 return '%s: +%s/-%s' % (len(stats), adds, removes)
260 return '%s: +%s/-%s' % (len(stats), adds, removes)
261
261
262 def showextras(**args):
262 def showextras(**args):
263 """:extras: List of dicts with key, value entries of the 'extras'
263 """:extras: List of dicts with key, value entries of the 'extras'
264 field of this changeset."""
264 field of this changeset."""
265 extras = args['ctx'].extra()
265 extras = args['ctx'].extra()
266 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
266 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
267 makemap = lambda k: {'key': k, 'value': extras[k]}
267 makemap = lambda k: {'key': k, 'value': extras[k]}
268 c = [makemap(k) for k in extras]
268 c = [makemap(k) for k in extras]
269 f = _showlist('extra', c, plural='extras', **args)
269 f = _showlist('extra', c, plural='extras', **args)
270 return _hybrid(f, extras, makemap,
270 return _hybrid(f, extras, makemap,
271 lambda x: '%s=%s' % (x['key'], x['value']))
271 lambda x: '%s=%s' % (x['key'], x['value']))
272
272
273 def showfileadds(**args):
273 def showfileadds(**args):
274 """:file_adds: List of strings. Files added by this changeset."""
274 """:file_adds: List of strings. Files added by this changeset."""
275 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
275 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
276 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
276 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
277 element='file', **args)
277 element='file', **args)
278
278
279 def showfilecopies(**args):
279 def showfilecopies(**args):
280 """:file_copies: List of strings. Files copied in this changeset with
280 """:file_copies: List of strings. Files copied in this changeset with
281 their sources.
281 their sources.
282 """
282 """
283 cache, ctx = args['cache'], args['ctx']
283 cache, ctx = args['cache'], args['ctx']
284 copies = args['revcache'].get('copies')
284 copies = args['revcache'].get('copies')
285 if copies is None:
285 if copies is None:
286 if 'getrenamed' not in cache:
286 if 'getrenamed' not in cache:
287 cache['getrenamed'] = getrenamedfn(args['repo'])
287 cache['getrenamed'] = getrenamedfn(args['repo'])
288 copies = []
288 copies = []
289 getrenamed = cache['getrenamed']
289 getrenamed = cache['getrenamed']
290 for fn in ctx.files():
290 for fn in ctx.files():
291 rename = getrenamed(fn, ctx.rev())
291 rename = getrenamed(fn, ctx.rev())
292 if rename:
292 if rename:
293 copies.append((fn, rename[0]))
293 copies.append((fn, rename[0]))
294
294
295 copies = util.sortdict(copies)
295 copies = util.sortdict(copies)
296 makemap = lambda k: {'name': k, 'source': copies[k]}
296 makemap = lambda k: {'name': k, 'source': copies[k]}
297 c = [makemap(k) for k in copies]
297 c = [makemap(k) for k in copies]
298 f = _showlist('file_copy', c, plural='file_copies', **args)
298 f = _showlist('file_copy', c, plural='file_copies', **args)
299 return _hybrid(f, copies, makemap,
299 return _hybrid(f, copies, makemap,
300 lambda x: '%s (%s)' % (x['name'], x['source']))
300 lambda x: '%s (%s)' % (x['name'], x['source']))
301
301
302 # showfilecopiesswitch() displays file copies only if copy records are
302 # showfilecopiesswitch() displays file copies only if copy records are
303 # provided before calling the templater, usually with a --copies
303 # provided before calling the templater, usually with a --copies
304 # command line switch.
304 # command line switch.
305 def showfilecopiesswitch(**args):
305 def showfilecopiesswitch(**args):
306 """:file_copies_switch: List of strings. Like "file_copies" but displayed
306 """:file_copies_switch: List of strings. Like "file_copies" but displayed
307 only if the --copied switch is set.
307 only if the --copied switch is set.
308 """
308 """
309 copies = args['revcache'].get('copies') or []
309 copies = args['revcache'].get('copies') or []
310 copies = util.sortdict(copies)
310 copies = util.sortdict(copies)
311 makemap = lambda k: {'name': k, 'source': copies[k]}
311 makemap = lambda k: {'name': k, 'source': copies[k]}
312 c = [makemap(k) for k in copies]
312 c = [makemap(k) for k in copies]
313 f = _showlist('file_copy', c, plural='file_copies', **args)
313 f = _showlist('file_copy', c, plural='file_copies', **args)
314 return _hybrid(f, copies, makemap,
314 return _hybrid(f, copies, makemap,
315 lambda x: '%s (%s)' % (x['name'], x['source']))
315 lambda x: '%s (%s)' % (x['name'], x['source']))
316
316
317 def showfiledels(**args):
317 def showfiledels(**args):
318 """:file_dels: List of strings. Files removed by this changeset."""
318 """:file_dels: List of strings. Files removed by this changeset."""
319 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
319 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
320 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
320 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
321 element='file', **args)
321 element='file', **args)
322
322
323 def showfilemods(**args):
323 def showfilemods(**args):
324 """:file_mods: List of strings. Files modified by this changeset."""
324 """:file_mods: List of strings. Files modified by this changeset."""
325 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
325 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
326 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
326 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
327 element='file', **args)
327 element='file', **args)
328
328
329 def showfiles(**args):
329 def showfiles(**args):
330 """:files: List of strings. All files modified, added, or removed by this
330 """:files: List of strings. All files modified, added, or removed by this
331 changeset.
331 changeset.
332 """
332 """
333 return showlist('file', args['ctx'].files(), **args)
333 return showlist('file', args['ctx'].files(), **args)
334
334
335 def showlatesttag(**args):
335 def showlatesttag(**args):
336 """:latesttag: List of strings. The global tags on the most recent globally
336 """:latesttag: List of strings. The global tags on the most recent globally
337 tagged ancestor of this changeset.
337 tagged ancestor of this changeset.
338 """
338 """
339 repo, ctx = args['repo'], args['ctx']
339 repo, ctx = args['repo'], args['ctx']
340 cache = args['cache']
340 cache = args['cache']
341 latesttags = getlatesttags(repo, ctx, cache)[2]
341 latesttags = getlatesttags(repo, ctx, cache)[2]
342
342
343 return showlist('latesttag', latesttags, separator=':', **args)
343 return showlist('latesttag', latesttags, separator=':', **args)
344
344
345 def showlatesttagdistance(repo, ctx, templ, cache, **args):
345 def showlatesttagdistance(repo, ctx, templ, cache, **args):
346 """:latesttagdistance: Integer. Longest path to the latest tag."""
346 """:latesttagdistance: Integer. Longest path to the latest tag."""
347 return getlatesttags(repo, ctx, cache)[1]
347 return getlatesttags(repo, ctx, cache)[1]
348
348
349 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
349 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
350 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
350 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
351 latesttag = getlatesttags(repo, ctx, cache)[2][0]
351 latesttag = getlatesttags(repo, ctx, cache)[2][0]
352 offset = 0
352 offset = 0
353 revs = [ctx.rev()]
353 revs = [ctx.rev()]
354
354
355 # The only() revset doesn't currently support wdir()
355 # The only() revset doesn't currently support wdir()
356 if ctx.rev() is None:
356 if ctx.rev() is None:
357 offset = 1
357 offset = 1
358 revs = [p.rev() for p in ctx.parents()]
358 revs = [p.rev() for p in ctx.parents()]
359
359
360 return len(repo.revs('only(%ld, %s)', revs, latesttag)) + offset
360 return len(repo.revs('only(%ld, %s)', revs, latesttag)) + offset
361
361
362 def showmanifest(**args):
362 def showmanifest(**args):
363 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
363 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
364 mnode = ctx.manifestnode()
364 mnode = ctx.manifestnode()
365 if mnode is None:
365 if mnode is None:
366 # just avoid crash, we might want to use the 'ff...' hash in future
366 # just avoid crash, we might want to use the 'ff...' hash in future
367 return
367 return
368 args = args.copy()
368 args = args.copy()
369 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
369 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
370 return templ('manifest', **args)
370 return templ('manifest', **args)
371
371
372 def shownode(repo, ctx, templ, **args):
372 def shownode(repo, ctx, templ, **args):
373 """:node: String. The changeset identification hash, as a 40 hexadecimal
373 """:node: String. The changeset identification hash, as a 40 hexadecimal
374 digit string.
374 digit string.
375 """
375 """
376 return ctx.hex()
376 return ctx.hex()
377
377
378 def showp1rev(repo, ctx, templ, **args):
378 def showp1rev(repo, ctx, templ, **args):
379 """:p1rev: Integer. The repository-local revision number of the changeset's
379 """:p1rev: Integer. The repository-local revision number of the changeset's
380 first parent, or -1 if the changeset has no parents."""
380 first parent, or -1 if the changeset has no parents."""
381 return ctx.p1().rev()
381 return ctx.p1().rev()
382
382
383 def showp2rev(repo, ctx, templ, **args):
383 def showp2rev(repo, ctx, templ, **args):
384 """:p2rev: Integer. The repository-local revision number of the changeset's
384 """:p2rev: Integer. The repository-local revision number of the changeset's
385 second parent, or -1 if the changeset has no second parent."""
385 second parent, or -1 if the changeset has no second parent."""
386 return ctx.p2().rev()
386 return ctx.p2().rev()
387
387
388 def showp1node(repo, ctx, templ, **args):
388 def showp1node(repo, ctx, templ, **args):
389 """:p1node: String. The identification hash of the changeset's first parent,
389 """:p1node: String. The identification hash of the changeset's first parent,
390 as a 40 digit hexadecimal string. If the changeset has no parents, all
390 as a 40 digit hexadecimal string. If the changeset has no parents, all
391 digits are 0."""
391 digits are 0."""
392 return ctx.p1().hex()
392 return ctx.p1().hex()
393
393
394 def showp2node(repo, ctx, templ, **args):
394 def showp2node(repo, ctx, templ, **args):
395 """:p2node: String. The identification hash of the changeset's second
395 """:p2node: String. The identification hash of the changeset's second
396 parent, as a 40 digit hexadecimal string. If the changeset has no second
396 parent, as a 40 digit hexadecimal string. If the changeset has no second
397 parent, all digits are 0."""
397 parent, all digits are 0."""
398 return ctx.p2().hex()
398 return ctx.p2().hex()
399
399
400 def showparents(**args):
400 def showparents(**args):
401 """:parents: List of strings. The parents of the changeset in "rev:node"
401 """:parents: List of strings. The parents of the changeset in "rev:node"
402 format. If the changeset has only one "natural" parent (the predecessor
402 format. If the changeset has only one "natural" parent (the predecessor
403 revision) nothing is shown."""
403 revision) nothing is shown."""
404 repo = args['repo']
404 repo = args['repo']
405 ctx = args['ctx']
405 ctx = args['ctx']
406 parents = [[('rev', p.rev()),
406 parents = [[('rev', p.rev()),
407 ('node', p.hex()),
407 ('node', p.hex()),
408 ('phase', p.phasestr())]
408 ('phase', p.phasestr())]
409 for p in scmutil.meaningfulparents(repo, ctx)]
409 for p in scmutil.meaningfulparents(repo, ctx)]
410 return showlist('parent', parents, **args)
410 return showlist('parent', parents, **args)
411
411
412 def showphase(repo, ctx, templ, **args):
412 def showphase(repo, ctx, templ, **args):
413 """:phase: String. The changeset phase name."""
413 """:phase: String. The changeset phase name."""
414 return ctx.phasestr()
414 return ctx.phasestr()
415
415
416 def showphaseidx(repo, ctx, templ, **args):
416 def showphaseidx(repo, ctx, templ, **args):
417 """:phaseidx: Integer. The changeset phase index."""
417 """:phaseidx: Integer. The changeset phase index."""
418 return ctx.phase()
418 return ctx.phase()
419
419
420 def showrev(repo, ctx, templ, **args):
420 def showrev(repo, ctx, templ, **args):
421 """:rev: Integer. The repository-local changeset revision number."""
421 """:rev: Integer. The repository-local changeset revision number."""
422 return scmutil.intrev(ctx.rev())
422 return scmutil.intrev(ctx.rev())
423
423
424 def showrevslist(name, revs, **args):
424 def showrevslist(name, revs, **args):
425 """helper to generate a list of revisions in which a mapped template will
425 """helper to generate a list of revisions in which a mapped template will
426 be evaluated"""
426 be evaluated"""
427 repo = args['ctx'].repo()
427 repo = args['ctx'].repo()
428 f = _showlist(name, revs, **args)
428 f = _showlist(name, revs, **args)
429 return _hybrid(f, revs,
429 return _hybrid(f, revs,
430 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
430 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
431
431
432 def showsubrepos(**args):
432 def showsubrepos(**args):
433 """:subrepos: List of strings. Updated subrepositories in the changeset."""
433 """:subrepos: List of strings. Updated subrepositories in the changeset."""
434 ctx = args['ctx']
434 ctx = args['ctx']
435 substate = ctx.substate
435 substate = ctx.substate
436 if not substate:
436 if not substate:
437 return showlist('subrepo', [], **args)
437 return showlist('subrepo', [], **args)
438 psubstate = ctx.parents()[0].substate or {}
438 psubstate = ctx.parents()[0].substate or {}
439 subrepos = []
439 subrepos = []
440 for sub in substate:
440 for sub in substate:
441 if sub not in psubstate or substate[sub] != psubstate[sub]:
441 if sub not in psubstate or substate[sub] != psubstate[sub]:
442 subrepos.append(sub) # modified or newly added in ctx
442 subrepos.append(sub) # modified or newly added in ctx
443 for sub in psubstate:
443 for sub in psubstate:
444 if sub not in substate:
444 if sub not in substate:
445 subrepos.append(sub) # removed in ctx
445 subrepos.append(sub) # removed in ctx
446 return showlist('subrepo', sorted(subrepos), **args)
446 return showlist('subrepo', sorted(subrepos), **args)
447
447
448 def shownames(namespace, **args):
448 def shownames(namespace, **args):
449 """helper method to generate a template keyword for a namespace"""
449 """helper method to generate a template keyword for a namespace"""
450 ctx = args['ctx']
450 ctx = args['ctx']
451 repo = ctx.repo()
451 repo = ctx.repo()
452 ns = repo.names[namespace]
452 ns = repo.names[namespace]
453 names = ns.names(repo, ctx.node())
453 names = ns.names(repo, ctx.node())
454 return showlist(ns.templatename, names, plural=namespace, **args)
454 return showlist(ns.templatename, names, plural=namespace, **args)
455
455
456 # don't remove "showtags" definition, even though namespaces will put
456 # don't remove "showtags" definition, even though namespaces will put
457 # a helper function for "tags" keyword into "keywords" map automatically,
457 # a helper function for "tags" keyword into "keywords" map automatically,
458 # because online help text is built without namespaces initialization
458 # because online help text is built without namespaces initialization
459 def showtags(**args):
459 def showtags(**args):
460 """:tags: List of strings. Any tags associated with the changeset."""
460 """:tags: List of strings. Any tags associated with the changeset."""
461 return shownames('tags', **args)
461 return shownames('tags', **args)
462
462
463 # keywords are callables like:
463 # keywords are callables like:
464 # fn(repo, ctx, templ, cache, revcache, **args)
464 # fn(repo, ctx, templ, cache, revcache, **args)
465 # with:
465 # with:
466 # repo - current repository instance
466 # repo - current repository instance
467 # ctx - the changectx being displayed
467 # ctx - the changectx being displayed
468 # templ - the templater instance
468 # templ - the templater instance
469 # cache - a cache dictionary for the whole templater run
469 # cache - a cache dictionary for the whole templater run
470 # revcache - a cache dictionary for the current revision
470 # revcache - a cache dictionary for the current revision
471 keywords = {
471 keywords = {
472 'activebookmark': showactivebookmark,
472 'activebookmark': showactivebookmark,
473 'author': showauthor,
473 'author': showauthor,
474 'bisect': showbisect,
474 'bisect': showbisect,
475 'branch': showbranch,
475 'branch': showbranch,
476 'branches': showbranches,
476 'branches': showbranches,
477 'bookmarks': showbookmarks,
477 'bookmarks': showbookmarks,
478 'changessincelatesttag': showchangessincelatesttag,
478 'changessincelatesttag': showchangessincelatesttag,
479 'children': showchildren,
479 'children': showchildren,
480 # currentbookmark is deprecated
480 # currentbookmark is deprecated
481 'currentbookmark': showcurrentbookmark,
481 'currentbookmark': showcurrentbookmark,
482 'date': showdate,
482 'date': showdate,
483 'desc': showdescription,
483 'desc': showdescription,
484 'diffstat': showdiffstat,
484 'diffstat': showdiffstat,
485 'extras': showextras,
485 'extras': showextras,
486 'file_adds': showfileadds,
486 'file_adds': showfileadds,
487 'file_copies': showfilecopies,
487 'file_copies': showfilecopies,
488 'file_copies_switch': showfilecopiesswitch,
488 'file_copies_switch': showfilecopiesswitch,
489 'file_dels': showfiledels,
489 'file_dels': showfiledels,
490 'file_mods': showfilemods,
490 'file_mods': showfilemods,
491 'files': showfiles,
491 'files': showfiles,
492 'latesttag': showlatesttag,
492 'latesttag': showlatesttag,
493 'latesttagdistance': showlatesttagdistance,
493 'latesttagdistance': showlatesttagdistance,
494 'manifest': showmanifest,
494 'manifest': showmanifest,
495 'node': shownode,
495 'node': shownode,
496 'p1rev': showp1rev,
496 'p1rev': showp1rev,
497 'p1node': showp1node,
497 'p1node': showp1node,
498 'p2rev': showp2rev,
498 'p2rev': showp2rev,
499 'p2node': showp2node,
499 'p2node': showp2node,
500 'parents': showparents,
500 'parents': showparents,
501 'phase': showphase,
501 'phase': showphase,
502 'phaseidx': showphaseidx,
502 'phaseidx': showphaseidx,
503 'rev': showrev,
503 'rev': showrev,
504 'subrepos': showsubrepos,
504 'subrepos': showsubrepos,
505 'tags': showtags,
505 'tags': showtags,
506 }
506 }
507
507
508 dockeywords = {
509 }
510 dockeywords.update(keywords)
511 del dockeywords['branches']
512
513 # tell hggettext to extract docstrings from these functions:
508 # tell hggettext to extract docstrings from these functions:
514 i18nfunctions = dockeywords.values()
509 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now