##// END OF EJS Templates
templatekw: fix documentation typos
Matt Harbison -
r41143:70e3e3da stable
parent child Browse files
Show More
@@ -1,857 +1,857 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 .i18n import _
10 from .i18n import _
11 from .node import (
11 from .node import (
12 hex,
12 hex,
13 nullid,
13 nullid,
14 wdirid,
14 wdirid,
15 wdirrev,
15 wdirrev,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 diffutil,
19 diffutil,
20 encoding,
20 encoding,
21 error,
21 error,
22 hbisect,
22 hbisect,
23 i18n,
23 i18n,
24 obsutil,
24 obsutil,
25 patch,
25 patch,
26 pycompat,
26 pycompat,
27 registrar,
27 registrar,
28 scmutil,
28 scmutil,
29 templateutil,
29 templateutil,
30 util,
30 util,
31 )
31 )
32 from .utils import (
32 from .utils import (
33 stringutil,
33 stringutil,
34 )
34 )
35
35
36 _hybrid = templateutil.hybrid
36 _hybrid = templateutil.hybrid
37 hybriddict = templateutil.hybriddict
37 hybriddict = templateutil.hybriddict
38 hybridlist = templateutil.hybridlist
38 hybridlist = templateutil.hybridlist
39 compatdict = templateutil.compatdict
39 compatdict = templateutil.compatdict
40 compatlist = templateutil.compatlist
40 compatlist = templateutil.compatlist
41 _showcompatlist = templateutil._showcompatlist
41 _showcompatlist = templateutil._showcompatlist
42
42
43 def getlatesttags(context, mapping, pattern=None):
43 def getlatesttags(context, mapping, pattern=None):
44 '''return date, distance and name for the latest tag of rev'''
44 '''return date, distance and name for the latest tag of rev'''
45 repo = context.resource(mapping, 'repo')
45 repo = context.resource(mapping, 'repo')
46 ctx = context.resource(mapping, 'ctx')
46 ctx = context.resource(mapping, 'ctx')
47 cache = context.resource(mapping, 'cache')
47 cache = context.resource(mapping, 'cache')
48
48
49 cachename = 'latesttags'
49 cachename = 'latesttags'
50 if pattern is not None:
50 if pattern is not None:
51 cachename += '-' + pattern
51 cachename += '-' + pattern
52 match = stringutil.stringmatcher(pattern)[2]
52 match = stringutil.stringmatcher(pattern)[2]
53 else:
53 else:
54 match = util.always
54 match = util.always
55
55
56 if cachename not in cache:
56 if cachename not in cache:
57 # Cache mapping from rev to a tuple with tag date, tag
57 # Cache mapping from rev to a tuple with tag date, tag
58 # distance and tag name
58 # distance and tag name
59 cache[cachename] = {-1: (0, 0, ['null'])}
59 cache[cachename] = {-1: (0, 0, ['null'])}
60 latesttags = cache[cachename]
60 latesttags = cache[cachename]
61
61
62 rev = ctx.rev()
62 rev = ctx.rev()
63 todo = [rev]
63 todo = [rev]
64 while todo:
64 while todo:
65 rev = todo.pop()
65 rev = todo.pop()
66 if rev in latesttags:
66 if rev in latesttags:
67 continue
67 continue
68 ctx = repo[rev]
68 ctx = repo[rev]
69 tags = [t for t in ctx.tags()
69 tags = [t for t in ctx.tags()
70 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
70 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
71 and match(t))]
71 and match(t))]
72 if tags:
72 if tags:
73 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
73 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
74 continue
74 continue
75 try:
75 try:
76 ptags = [latesttags[p.rev()] for p in ctx.parents()]
76 ptags = [latesttags[p.rev()] for p in ctx.parents()]
77 if len(ptags) > 1:
77 if len(ptags) > 1:
78 if ptags[0][2] == ptags[1][2]:
78 if ptags[0][2] == ptags[1][2]:
79 # The tuples are laid out so the right one can be found by
79 # The tuples are laid out so the right one can be found by
80 # comparison in this case.
80 # comparison in this case.
81 pdate, pdist, ptag = max(ptags)
81 pdate, pdist, ptag = max(ptags)
82 else:
82 else:
83 def key(x):
83 def key(x):
84 changessincetag = len(repo.revs('only(%d, %s)',
84 changessincetag = len(repo.revs('only(%d, %s)',
85 ctx.rev(), x[2][0]))
85 ctx.rev(), x[2][0]))
86 # Smallest number of changes since tag wins. Date is
86 # Smallest number of changes since tag wins. Date is
87 # used as tiebreaker.
87 # used as tiebreaker.
88 return [-changessincetag, x[0]]
88 return [-changessincetag, x[0]]
89 pdate, pdist, ptag = max(ptags, key=key)
89 pdate, pdist, ptag = max(ptags, key=key)
90 else:
90 else:
91 pdate, pdist, ptag = ptags[0]
91 pdate, pdist, ptag = ptags[0]
92 except KeyError:
92 except KeyError:
93 # Cache miss - recurse
93 # Cache miss - recurse
94 todo.append(rev)
94 todo.append(rev)
95 todo.extend(p.rev() for p in ctx.parents())
95 todo.extend(p.rev() for p in ctx.parents())
96 continue
96 continue
97 latesttags[rev] = pdate, pdist + 1, ptag
97 latesttags[rev] = pdate, pdist + 1, ptag
98 return latesttags[rev]
98 return latesttags[rev]
99
99
100 def getrenamedfn(repo, endrev=None):
100 def getrenamedfn(repo, endrev=None):
101 rcache = {}
101 rcache = {}
102 if endrev is None:
102 if endrev is None:
103 endrev = len(repo)
103 endrev = len(repo)
104
104
105 def getrenamed(fn, rev):
105 def getrenamed(fn, rev):
106 '''looks up all renames for a file (up to endrev) the first
106 '''looks up all renames for a file (up to endrev) the first
107 time the file is given. It indexes on the changerev and only
107 time the file is given. It indexes on the changerev and only
108 parses the manifest if linkrev != changerev.
108 parses the manifest if linkrev != changerev.
109 Returns rename info for fn at changerev rev.'''
109 Returns rename info for fn at changerev rev.'''
110 if fn not in rcache:
110 if fn not in rcache:
111 rcache[fn] = {}
111 rcache[fn] = {}
112 fl = repo.file(fn)
112 fl = repo.file(fn)
113 for i in fl:
113 for i in fl:
114 lr = fl.linkrev(i)
114 lr = fl.linkrev(i)
115 renamed = fl.renamed(fl.node(i))
115 renamed = fl.renamed(fl.node(i))
116 rcache[fn][lr] = renamed and renamed[0]
116 rcache[fn][lr] = renamed and renamed[0]
117 if lr >= endrev:
117 if lr >= endrev:
118 break
118 break
119 if rev in rcache[fn]:
119 if rev in rcache[fn]:
120 return rcache[fn][rev]
120 return rcache[fn][rev]
121
121
122 # If linkrev != rev (i.e. rev not found in rcache) fallback to
122 # If linkrev != rev (i.e. rev not found in rcache) fallback to
123 # filectx logic.
123 # filectx logic.
124 try:
124 try:
125 renamed = repo[rev][fn].renamed()
125 renamed = repo[rev][fn].renamed()
126 return renamed and renamed[0]
126 return renamed and renamed[0]
127 except error.LookupError:
127 except error.LookupError:
128 return None
128 return None
129
129
130 return getrenamed
130 return getrenamed
131
131
132 def getlogcolumns():
132 def getlogcolumns():
133 """Return a dict of log column labels"""
133 """Return a dict of log column labels"""
134 _ = pycompat.identity # temporarily disable gettext
134 _ = pycompat.identity # temporarily disable gettext
135 # i18n: column positioning for "hg log"
135 # i18n: column positioning for "hg log"
136 columns = _('bookmark: %s\n'
136 columns = _('bookmark: %s\n'
137 'branch: %s\n'
137 'branch: %s\n'
138 'changeset: %s\n'
138 'changeset: %s\n'
139 'copies: %s\n'
139 'copies: %s\n'
140 'date: %s\n'
140 'date: %s\n'
141 'extra: %s=%s\n'
141 'extra: %s=%s\n'
142 'files+: %s\n'
142 'files+: %s\n'
143 'files-: %s\n'
143 'files-: %s\n'
144 'files: %s\n'
144 'files: %s\n'
145 'instability: %s\n'
145 'instability: %s\n'
146 'manifest: %s\n'
146 'manifest: %s\n'
147 'obsolete: %s\n'
147 'obsolete: %s\n'
148 'parent: %s\n'
148 'parent: %s\n'
149 'phase: %s\n'
149 'phase: %s\n'
150 'summary: %s\n'
150 'summary: %s\n'
151 'tag: %s\n'
151 'tag: %s\n'
152 'user: %s\n')
152 'user: %s\n')
153 return dict(zip([s.split(':', 1)[0] for s in columns.splitlines()],
153 return dict(zip([s.split(':', 1)[0] for s in columns.splitlines()],
154 i18n._(columns).splitlines(True)))
154 i18n._(columns).splitlines(True)))
155
155
156 # default templates internally used for rendering of lists
156 # default templates internally used for rendering of lists
157 defaulttempl = {
157 defaulttempl = {
158 'parent': '{rev}:{node|formatnode} ',
158 'parent': '{rev}:{node|formatnode} ',
159 'manifest': '{rev}:{node|formatnode}',
159 'manifest': '{rev}:{node|formatnode}',
160 'file_copy': '{name} ({source})',
160 'file_copy': '{name} ({source})',
161 'envvar': '{key}={value}',
161 'envvar': '{key}={value}',
162 'extra': '{key}={value|stringescape}'
162 'extra': '{key}={value|stringescape}'
163 }
163 }
164 # filecopy is preserved for compatibility reasons
164 # filecopy is preserved for compatibility reasons
165 defaulttempl['filecopy'] = defaulttempl['file_copy']
165 defaulttempl['filecopy'] = defaulttempl['file_copy']
166
166
167 # keywords are callables (see registrar.templatekeyword for details)
167 # keywords are callables (see registrar.templatekeyword for details)
168 keywords = {}
168 keywords = {}
169 templatekeyword = registrar.templatekeyword(keywords)
169 templatekeyword = registrar.templatekeyword(keywords)
170
170
171 @templatekeyword('author', requires={'ctx'})
171 @templatekeyword('author', requires={'ctx'})
172 def showauthor(context, mapping):
172 def showauthor(context, mapping):
173 """Alias for ``{user}``"""
173 """Alias for ``{user}``"""
174 return showuser(context, mapping)
174 return showuser(context, mapping)
175
175
176 @templatekeyword('bisect', requires={'repo', 'ctx'})
176 @templatekeyword('bisect', requires={'repo', 'ctx'})
177 def showbisect(context, mapping):
177 def showbisect(context, mapping):
178 """String. The changeset bisection status."""
178 """String. The changeset bisection status."""
179 repo = context.resource(mapping, 'repo')
179 repo = context.resource(mapping, 'repo')
180 ctx = context.resource(mapping, 'ctx')
180 ctx = context.resource(mapping, 'ctx')
181 return hbisect.label(repo, ctx.node())
181 return hbisect.label(repo, ctx.node())
182
182
183 @templatekeyword('branch', requires={'ctx'})
183 @templatekeyword('branch', requires={'ctx'})
184 def showbranch(context, mapping):
184 def showbranch(context, mapping):
185 """String. The name of the branch on which the changeset was
185 """String. The name of the branch on which the changeset was
186 committed.
186 committed.
187 """
187 """
188 ctx = context.resource(mapping, 'ctx')
188 ctx = context.resource(mapping, 'ctx')
189 return ctx.branch()
189 return ctx.branch()
190
190
191 @templatekeyword('branches', requires={'ctx'})
191 @templatekeyword('branches', requires={'ctx'})
192 def showbranches(context, mapping):
192 def showbranches(context, mapping):
193 """List of strings. The name of the branch on which the
193 """List of strings. The name of the branch on which the
194 changeset was committed. Will be empty if the branch name was
194 changeset was committed. Will be empty if the branch name was
195 default. (DEPRECATED)
195 default. (DEPRECATED)
196 """
196 """
197 ctx = context.resource(mapping, 'ctx')
197 ctx = context.resource(mapping, 'ctx')
198 branch = ctx.branch()
198 branch = ctx.branch()
199 if branch != 'default':
199 if branch != 'default':
200 return compatlist(context, mapping, 'branch', [branch],
200 return compatlist(context, mapping, 'branch', [branch],
201 plural='branches')
201 plural='branches')
202 return compatlist(context, mapping, 'branch', [], plural='branches')
202 return compatlist(context, mapping, 'branch', [], plural='branches')
203
203
204 @templatekeyword('bookmarks', requires={'repo', 'ctx'})
204 @templatekeyword('bookmarks', requires={'repo', 'ctx'})
205 def showbookmarks(context, mapping):
205 def showbookmarks(context, mapping):
206 """List of strings. Any bookmarks associated with the
206 """List of strings. Any bookmarks associated with the
207 changeset. Also sets 'active', the name of the active bookmark.
207 changeset. Also sets 'active', the name of the active bookmark.
208 """
208 """
209 repo = context.resource(mapping, 'repo')
209 repo = context.resource(mapping, 'repo')
210 ctx = context.resource(mapping, 'ctx')
210 ctx = context.resource(mapping, 'ctx')
211 bookmarks = ctx.bookmarks()
211 bookmarks = ctx.bookmarks()
212 active = repo._activebookmark
212 active = repo._activebookmark
213 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
213 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
214 f = _showcompatlist(context, mapping, 'bookmark', bookmarks)
214 f = _showcompatlist(context, mapping, 'bookmark', bookmarks)
215 return _hybrid(f, bookmarks, makemap, pycompat.identity)
215 return _hybrid(f, bookmarks, makemap, pycompat.identity)
216
216
217 @templatekeyword('children', requires={'ctx'})
217 @templatekeyword('children', requires={'ctx'})
218 def showchildren(context, mapping):
218 def showchildren(context, mapping):
219 """List of strings. The children of the changeset."""
219 """List of strings. The children of the changeset."""
220 ctx = context.resource(mapping, 'ctx')
220 ctx = context.resource(mapping, 'ctx')
221 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()]
221 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()]
222 return compatlist(context, mapping, 'children', childrevs, element='child')
222 return compatlist(context, mapping, 'children', childrevs, element='child')
223
223
224 # Deprecated, but kept alive for help generation a purpose.
224 # Deprecated, but kept alive for help generation a purpose.
225 @templatekeyword('currentbookmark', requires={'repo', 'ctx'})
225 @templatekeyword('currentbookmark', requires={'repo', 'ctx'})
226 def showcurrentbookmark(context, mapping):
226 def showcurrentbookmark(context, mapping):
227 """String. The active bookmark, if it is associated with the changeset.
227 """String. The active bookmark, if it is associated with the changeset.
228 (DEPRECATED)"""
228 (DEPRECATED)"""
229 return showactivebookmark(context, mapping)
229 return showactivebookmark(context, mapping)
230
230
231 @templatekeyword('activebookmark', requires={'repo', 'ctx'})
231 @templatekeyword('activebookmark', requires={'repo', 'ctx'})
232 def showactivebookmark(context, mapping):
232 def showactivebookmark(context, mapping):
233 """String. The active bookmark, if it is associated with the changeset."""
233 """String. The active bookmark, if it is associated with the changeset."""
234 repo = context.resource(mapping, 'repo')
234 repo = context.resource(mapping, 'repo')
235 ctx = context.resource(mapping, 'ctx')
235 ctx = context.resource(mapping, 'ctx')
236 active = repo._activebookmark
236 active = repo._activebookmark
237 if active and active in ctx.bookmarks():
237 if active and active in ctx.bookmarks():
238 return active
238 return active
239 return ''
239 return ''
240
240
241 @templatekeyword('date', requires={'ctx'})
241 @templatekeyword('date', requires={'ctx'})
242 def showdate(context, mapping):
242 def showdate(context, mapping):
243 """Date information. The date when the changeset was committed."""
243 """Date information. The date when the changeset was committed."""
244 ctx = context.resource(mapping, 'ctx')
244 ctx = context.resource(mapping, 'ctx')
245 # the default string format is '<float(unixtime)><tzoffset>' because
245 # the default string format is '<float(unixtime)><tzoffset>' because
246 # python-hglib splits date at decimal separator.
246 # python-hglib splits date at decimal separator.
247 return templateutil.date(ctx.date(), showfmt='%d.0%d')
247 return templateutil.date(ctx.date(), showfmt='%d.0%d')
248
248
249 @templatekeyword('desc', requires={'ctx'})
249 @templatekeyword('desc', requires={'ctx'})
250 def showdescription(context, mapping):
250 def showdescription(context, mapping):
251 """String. The text of the changeset description."""
251 """String. The text of the changeset description."""
252 ctx = context.resource(mapping, 'ctx')
252 ctx = context.resource(mapping, 'ctx')
253 s = ctx.description()
253 s = ctx.description()
254 if isinstance(s, encoding.localstr):
254 if isinstance(s, encoding.localstr):
255 # try hard to preserve utf-8 bytes
255 # try hard to preserve utf-8 bytes
256 return encoding.tolocal(encoding.fromlocal(s).strip())
256 return encoding.tolocal(encoding.fromlocal(s).strip())
257 elif isinstance(s, encoding.safelocalstr):
257 elif isinstance(s, encoding.safelocalstr):
258 return encoding.safelocalstr(s.strip())
258 return encoding.safelocalstr(s.strip())
259 else:
259 else:
260 return s.strip()
260 return s.strip()
261
261
262 @templatekeyword('diffstat', requires={'ui', 'ctx'})
262 @templatekeyword('diffstat', requires={'ui', 'ctx'})
263 def showdiffstat(context, mapping):
263 def showdiffstat(context, mapping):
264 """String. Statistics of changes with the following format:
264 """String. Statistics of changes with the following format:
265 "modified files: +added/-removed lines"
265 "modified files: +added/-removed lines"
266 """
266 """
267 ui = context.resource(mapping, 'ui')
267 ui = context.resource(mapping, 'ui')
268 ctx = context.resource(mapping, 'ctx')
268 ctx = context.resource(mapping, 'ctx')
269 diffopts = diffutil.diffallopts(ui, {'noprefix': False})
269 diffopts = diffutil.diffallopts(ui, {'noprefix': False})
270 diff = ctx.diff(opts=diffopts)
270 diff = ctx.diff(opts=diffopts)
271 stats = patch.diffstatdata(util.iterlines(diff))
271 stats = patch.diffstatdata(util.iterlines(diff))
272 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
272 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
273 return '%d: +%d/-%d' % (len(stats), adds, removes)
273 return '%d: +%d/-%d' % (len(stats), adds, removes)
274
274
275 @templatekeyword('envvars', requires={'ui'})
275 @templatekeyword('envvars', requires={'ui'})
276 def showenvvars(context, mapping):
276 def showenvvars(context, mapping):
277 """A dictionary of environment variables. (EXPERIMENTAL)"""
277 """A dictionary of environment variables. (EXPERIMENTAL)"""
278 ui = context.resource(mapping, 'ui')
278 ui = context.resource(mapping, 'ui')
279 env = ui.exportableenviron()
279 env = ui.exportableenviron()
280 env = util.sortdict((k, env[k]) for k in sorted(env))
280 env = util.sortdict((k, env[k]) for k in sorted(env))
281 return compatdict(context, mapping, 'envvar', env, plural='envvars')
281 return compatdict(context, mapping, 'envvar', env, plural='envvars')
282
282
283 @templatekeyword('extras', requires={'ctx'})
283 @templatekeyword('extras', requires={'ctx'})
284 def showextras(context, mapping):
284 def showextras(context, mapping):
285 """List of dicts with key, value entries of the 'extras'
285 """List of dicts with key, value entries of the 'extras'
286 field of this changeset."""
286 field of this changeset."""
287 ctx = context.resource(mapping, 'ctx')
287 ctx = context.resource(mapping, 'ctx')
288 extras = ctx.extra()
288 extras = ctx.extra()
289 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
289 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
290 makemap = lambda k: {'key': k, 'value': extras[k]}
290 makemap = lambda k: {'key': k, 'value': extras[k]}
291 c = [makemap(k) for k in extras]
291 c = [makemap(k) for k in extras]
292 f = _showcompatlist(context, mapping, 'extra', c, plural='extras')
292 f = _showcompatlist(context, mapping, 'extra', c, plural='extras')
293 return _hybrid(f, extras, makemap,
293 return _hybrid(f, extras, makemap,
294 lambda k: '%s=%s' % (k, stringutil.escapestr(extras[k])))
294 lambda k: '%s=%s' % (k, stringutil.escapestr(extras[k])))
295
295
296 def _getfilestatus(context, mapping, listall=False):
296 def _getfilestatus(context, mapping, listall=False):
297 ctx = context.resource(mapping, 'ctx')
297 ctx = context.resource(mapping, 'ctx')
298 revcache = context.resource(mapping, 'revcache')
298 revcache = context.resource(mapping, 'revcache')
299 if 'filestatus' not in revcache or revcache['filestatusall'] < listall:
299 if 'filestatus' not in revcache or revcache['filestatusall'] < listall:
300 stat = ctx.p1().status(ctx, listignored=listall, listclean=listall,
300 stat = ctx.p1().status(ctx, listignored=listall, listclean=listall,
301 listunknown=listall)
301 listunknown=listall)
302 revcache['filestatus'] = stat
302 revcache['filestatus'] = stat
303 revcache['filestatusall'] = listall
303 revcache['filestatusall'] = listall
304 return revcache['filestatus']
304 return revcache['filestatus']
305
305
306 def _getfilestatusmap(context, mapping, listall=False):
306 def _getfilestatusmap(context, mapping, listall=False):
307 revcache = context.resource(mapping, 'revcache')
307 revcache = context.resource(mapping, 'revcache')
308 if 'filestatusmap' not in revcache or revcache['filestatusall'] < listall:
308 if 'filestatusmap' not in revcache or revcache['filestatusall'] < listall:
309 stat = _getfilestatus(context, mapping, listall=listall)
309 stat = _getfilestatus(context, mapping, listall=listall)
310 revcache['filestatusmap'] = statmap = {}
310 revcache['filestatusmap'] = statmap = {}
311 for char, files in zip(pycompat.iterbytestr('MAR!?IC'), stat):
311 for char, files in zip(pycompat.iterbytestr('MAR!?IC'), stat):
312 statmap.update((f, char) for f in files)
312 statmap.update((f, char) for f in files)
313 return revcache['filestatusmap'] # {path: statchar}
313 return revcache['filestatusmap'] # {path: statchar}
314
314
315 def _showfilesbystat(context, mapping, name, index):
315 def _showfilesbystat(context, mapping, name, index):
316 stat = _getfilestatus(context, mapping)
316 stat = _getfilestatus(context, mapping)
317 files = stat[index]
317 files = stat[index]
318 return templateutil.compatfileslist(context, mapping, name, files)
318 return templateutil.compatfileslist(context, mapping, name, files)
319
319
320 @templatekeyword('file_adds', requires={'ctx', 'revcache'})
320 @templatekeyword('file_adds', requires={'ctx', 'revcache'})
321 def showfileadds(context, mapping):
321 def showfileadds(context, mapping):
322 """List of strings. Files added by this changeset."""
322 """List of strings. Files added by this changeset."""
323 return _showfilesbystat(context, mapping, 'file_add', 1)
323 return _showfilesbystat(context, mapping, 'file_add', 1)
324
324
325 @templatekeyword('file_copies',
325 @templatekeyword('file_copies',
326 requires={'repo', 'ctx', 'cache', 'revcache'})
326 requires={'repo', 'ctx', 'cache', 'revcache'})
327 def showfilecopies(context, mapping):
327 def showfilecopies(context, mapping):
328 """List of strings. Files copied in this changeset with
328 """List of strings. Files copied in this changeset with
329 their sources.
329 their sources.
330 """
330 """
331 repo = context.resource(mapping, 'repo')
331 repo = context.resource(mapping, 'repo')
332 ctx = context.resource(mapping, 'ctx')
332 ctx = context.resource(mapping, 'ctx')
333 cache = context.resource(mapping, 'cache')
333 cache = context.resource(mapping, 'cache')
334 copies = context.resource(mapping, 'revcache').get('copies')
334 copies = context.resource(mapping, 'revcache').get('copies')
335 if copies is None:
335 if copies is None:
336 if 'getrenamed' not in cache:
336 if 'getrenamed' not in cache:
337 cache['getrenamed'] = getrenamedfn(repo)
337 cache['getrenamed'] = getrenamedfn(repo)
338 copies = []
338 copies = []
339 getrenamed = cache['getrenamed']
339 getrenamed = cache['getrenamed']
340 for fn in ctx.files():
340 for fn in ctx.files():
341 rename = getrenamed(fn, ctx.rev())
341 rename = getrenamed(fn, ctx.rev())
342 if rename:
342 if rename:
343 copies.append((fn, rename))
343 copies.append((fn, rename))
344 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
344 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
345 copies)
345 copies)
346
346
347 # showfilecopiesswitch() displays file copies only if copy records are
347 # showfilecopiesswitch() displays file copies only if copy records are
348 # provided before calling the templater, usually with a --copies
348 # provided before calling the templater, usually with a --copies
349 # command line switch.
349 # command line switch.
350 @templatekeyword('file_copies_switch', requires={'revcache'})
350 @templatekeyword('file_copies_switch', requires={'revcache'})
351 def showfilecopiesswitch(context, mapping):
351 def showfilecopiesswitch(context, mapping):
352 """List of strings. Like "file_copies" but displayed
352 """List of strings. Like "file_copies" but displayed
353 only if the --copied switch is set.
353 only if the --copied switch is set.
354 """
354 """
355 copies = context.resource(mapping, 'revcache').get('copies') or []
355 copies = context.resource(mapping, 'revcache').get('copies') or []
356 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
356 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
357 copies)
357 copies)
358
358
359 @templatekeyword('file_dels', requires={'ctx', 'revcache'})
359 @templatekeyword('file_dels', requires={'ctx', 'revcache'})
360 def showfiledels(context, mapping):
360 def showfiledels(context, mapping):
361 """List of strings. Files removed by this changeset."""
361 """List of strings. Files removed by this changeset."""
362 return _showfilesbystat(context, mapping, 'file_del', 2)
362 return _showfilesbystat(context, mapping, 'file_del', 2)
363
363
364 @templatekeyword('file_mods', requires={'ctx', 'revcache'})
364 @templatekeyword('file_mods', requires={'ctx', 'revcache'})
365 def showfilemods(context, mapping):
365 def showfilemods(context, mapping):
366 """List of strings. Files modified by this changeset."""
366 """List of strings. Files modified by this changeset."""
367 return _showfilesbystat(context, mapping, 'file_mod', 0)
367 return _showfilesbystat(context, mapping, 'file_mod', 0)
368
368
369 @templatekeyword('files', requires={'ctx'})
369 @templatekeyword('files', requires={'ctx'})
370 def showfiles(context, mapping):
370 def showfiles(context, mapping):
371 """List of strings. All files modified, added, or removed by this
371 """List of strings. All files modified, added, or removed by this
372 changeset.
372 changeset.
373 """
373 """
374 ctx = context.resource(mapping, 'ctx')
374 ctx = context.resource(mapping, 'ctx')
375 return templateutil.compatfileslist(context, mapping, 'file', ctx.files())
375 return templateutil.compatfileslist(context, mapping, 'file', ctx.files())
376
376
377 @templatekeyword('graphnode', requires={'repo', 'ctx'})
377 @templatekeyword('graphnode', requires={'repo', 'ctx'})
378 def showgraphnode(context, mapping):
378 def showgraphnode(context, mapping):
379 """String. The character representing the changeset node in an ASCII
379 """String. The character representing the changeset node in an ASCII
380 revision graph."""
380 revision graph."""
381 repo = context.resource(mapping, 'repo')
381 repo = context.resource(mapping, 'repo')
382 ctx = context.resource(mapping, 'ctx')
382 ctx = context.resource(mapping, 'ctx')
383 return getgraphnode(repo, ctx)
383 return getgraphnode(repo, ctx)
384
384
385 def getgraphnode(repo, ctx):
385 def getgraphnode(repo, ctx):
386 return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx)
386 return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx)
387
387
388 def getgraphnodecurrent(repo, ctx):
388 def getgraphnodecurrent(repo, ctx):
389 wpnodes = repo.dirstate.parents()
389 wpnodes = repo.dirstate.parents()
390 if wpnodes[1] == nullid:
390 if wpnodes[1] == nullid:
391 wpnodes = wpnodes[:1]
391 wpnodes = wpnodes[:1]
392 if ctx.node() in wpnodes:
392 if ctx.node() in wpnodes:
393 return '@'
393 return '@'
394 else:
394 else:
395 return ''
395 return ''
396
396
397 def getgraphnodesymbol(ctx):
397 def getgraphnodesymbol(ctx):
398 if ctx.obsolete():
398 if ctx.obsolete():
399 return 'x'
399 return 'x'
400 elif ctx.isunstable():
400 elif ctx.isunstable():
401 return '*'
401 return '*'
402 elif ctx.closesbranch():
402 elif ctx.closesbranch():
403 return '_'
403 return '_'
404 else:
404 else:
405 return 'o'
405 return 'o'
406
406
407 @templatekeyword('graphwidth', requires=())
407 @templatekeyword('graphwidth', requires=())
408 def showgraphwidth(context, mapping):
408 def showgraphwidth(context, mapping):
409 """Integer. The width of the graph drawn by 'log --graph' or zero."""
409 """Integer. The width of the graph drawn by 'log --graph' or zero."""
410 # just hosts documentation; should be overridden by template mapping
410 # just hosts documentation; should be overridden by template mapping
411 return 0
411 return 0
412
412
413 @templatekeyword('index', requires=())
413 @templatekeyword('index', requires=())
414 def showindex(context, mapping):
414 def showindex(context, mapping):
415 """Integer. The current iteration of the loop. (0 indexed)"""
415 """Integer. The current iteration of the loop. (0 indexed)"""
416 # just hosts documentation; should be overridden by template mapping
416 # just hosts documentation; should be overridden by template mapping
417 raise error.Abort(_("can't use index in this context"))
417 raise error.Abort(_("can't use index in this context"))
418
418
419 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'})
419 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'})
420 def showlatesttag(context, mapping):
420 def showlatesttag(context, mapping):
421 """List of strings. The global tags on the most recent globally
421 """List of strings. The global tags on the most recent globally
422 tagged ancestor of this changeset. If no such tags exist, the list
422 tagged ancestor of this changeset. If no such tags exist, the list
423 consists of the single string "null".
423 consists of the single string "null".
424 """
424 """
425 return showlatesttags(context, mapping, None)
425 return showlatesttags(context, mapping, None)
426
426
427 def showlatesttags(context, mapping, pattern):
427 def showlatesttags(context, mapping, pattern):
428 """helper method for the latesttag keyword and function"""
428 """helper method for the latesttag keyword and function"""
429 latesttags = getlatesttags(context, mapping, pattern)
429 latesttags = getlatesttags(context, mapping, pattern)
430
430
431 # latesttag[0] is an implementation detail for sorting csets on different
431 # latesttag[0] is an implementation detail for sorting csets on different
432 # branches in a stable manner- it is the date the tagged cset was created,
432 # branches in a stable manner- it is the date the tagged cset was created,
433 # not the date the tag was created. Therefore it isn't made visible here.
433 # not the date the tag was created. Therefore it isn't made visible here.
434 makemap = lambda v: {
434 makemap = lambda v: {
435 'changes': _showchangessincetag,
435 'changes': _showchangessincetag,
436 'distance': latesttags[1],
436 'distance': latesttags[1],
437 'latesttag': v, # BC with {latesttag % '{latesttag}'}
437 'latesttag': v, # BC with {latesttag % '{latesttag}'}
438 'tag': v
438 'tag': v
439 }
439 }
440
440
441 tags = latesttags[2]
441 tags = latesttags[2]
442 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':')
442 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':')
443 return _hybrid(f, tags, makemap, pycompat.identity)
443 return _hybrid(f, tags, makemap, pycompat.identity)
444
444
445 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'})
445 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'})
446 def showlatesttagdistance(context, mapping):
446 def showlatesttagdistance(context, mapping):
447 """Integer. Longest path to the latest tag."""
447 """Integer. Longest path to the latest tag."""
448 return getlatesttags(context, mapping)[1]
448 return getlatesttags(context, mapping)[1]
449
449
450 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
450 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
451 def showchangessincelatesttag(context, mapping):
451 def showchangessincelatesttag(context, mapping):
452 """Integer. All ancestors not in the latest tag."""
452 """Integer. All ancestors not in the latest tag."""
453 tag = getlatesttags(context, mapping)[2][0]
453 tag = getlatesttags(context, mapping)[2][0]
454 mapping = context.overlaymap(mapping, {'tag': tag})
454 mapping = context.overlaymap(mapping, {'tag': tag})
455 return _showchangessincetag(context, mapping)
455 return _showchangessincetag(context, mapping)
456
456
457 def _showchangessincetag(context, mapping):
457 def _showchangessincetag(context, mapping):
458 repo = context.resource(mapping, 'repo')
458 repo = context.resource(mapping, 'repo')
459 ctx = context.resource(mapping, 'ctx')
459 ctx = context.resource(mapping, 'ctx')
460 offset = 0
460 offset = 0
461 revs = [ctx.rev()]
461 revs = [ctx.rev()]
462 tag = context.symbol(mapping, 'tag')
462 tag = context.symbol(mapping, 'tag')
463
463
464 # The only() revset doesn't currently support wdir()
464 # The only() revset doesn't currently support wdir()
465 if ctx.rev() is None:
465 if ctx.rev() is None:
466 offset = 1
466 offset = 1
467 revs = [p.rev() for p in ctx.parents()]
467 revs = [p.rev() for p in ctx.parents()]
468
468
469 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
469 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
470
470
471 # teach templater latesttags.changes is switched to (context, mapping) API
471 # teach templater latesttags.changes is switched to (context, mapping) API
472 _showchangessincetag._requires = {'repo', 'ctx'}
472 _showchangessincetag._requires = {'repo', 'ctx'}
473
473
474 @templatekeyword('manifest', requires={'repo', 'ctx'})
474 @templatekeyword('manifest', requires={'repo', 'ctx'})
475 def showmanifest(context, mapping):
475 def showmanifest(context, mapping):
476 repo = context.resource(mapping, 'repo')
476 repo = context.resource(mapping, 'repo')
477 ctx = context.resource(mapping, 'ctx')
477 ctx = context.resource(mapping, 'ctx')
478 mnode = ctx.manifestnode()
478 mnode = ctx.manifestnode()
479 if mnode is None:
479 if mnode is None:
480 mnode = wdirid
480 mnode = wdirid
481 mrev = wdirrev
481 mrev = wdirrev
482 else:
482 else:
483 mrev = repo.manifestlog.rev(mnode)
483 mrev = repo.manifestlog.rev(mnode)
484 mhex = hex(mnode)
484 mhex = hex(mnode)
485 mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex})
485 mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex})
486 f = context.process('manifest', mapping)
486 f = context.process('manifest', mapping)
487 return templateutil.hybriditem(f, None, f,
487 return templateutil.hybriditem(f, None, f,
488 lambda x: {'rev': mrev, 'node': mhex})
488 lambda x: {'rev': mrev, 'node': mhex})
489
489
490 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'})
490 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'})
491 def showobsfate(context, mapping):
491 def showobsfate(context, mapping):
492 # this function returns a list containing pre-formatted obsfate strings.
492 # this function returns a list containing pre-formatted obsfate strings.
493 #
493 #
494 # This function will be replaced by templates fragments when we will have
494 # This function will be replaced by templates fragments when we will have
495 # the verbosity templatekw available.
495 # the verbosity templatekw available.
496 succsandmarkers = showsuccsandmarkers(context, mapping)
496 succsandmarkers = showsuccsandmarkers(context, mapping)
497
497
498 ui = context.resource(mapping, 'ui')
498 ui = context.resource(mapping, 'ui')
499 repo = context.resource(mapping, 'repo')
499 repo = context.resource(mapping, 'repo')
500 values = []
500 values = []
501
501
502 for x in succsandmarkers.tovalue(context, mapping):
502 for x in succsandmarkers.tovalue(context, mapping):
503 v = obsutil.obsfateprinter(ui, repo, x['successors'], x['markers'],
503 v = obsutil.obsfateprinter(ui, repo, x['successors'], x['markers'],
504 scmutil.formatchangeid)
504 scmutil.formatchangeid)
505 values.append(v)
505 values.append(v)
506
506
507 return compatlist(context, mapping, "fate", values)
507 return compatlist(context, mapping, "fate", values)
508
508
509 def shownames(context, mapping, namespace):
509 def shownames(context, mapping, namespace):
510 """helper method to generate a template keyword for a namespace"""
510 """helper method to generate a template keyword for a namespace"""
511 repo = context.resource(mapping, 'repo')
511 repo = context.resource(mapping, 'repo')
512 ctx = context.resource(mapping, 'ctx')
512 ctx = context.resource(mapping, 'ctx')
513 ns = repo.names[namespace]
513 ns = repo.names[namespace]
514 names = ns.names(repo, ctx.node())
514 names = ns.names(repo, ctx.node())
515 return compatlist(context, mapping, ns.templatename, names,
515 return compatlist(context, mapping, ns.templatename, names,
516 plural=namespace)
516 plural=namespace)
517
517
518 @templatekeyword('namespaces', requires={'repo', 'ctx'})
518 @templatekeyword('namespaces', requires={'repo', 'ctx'})
519 def shownamespaces(context, mapping):
519 def shownamespaces(context, mapping):
520 """Dict of lists. Names attached to this changeset per
520 """Dict of lists. Names attached to this changeset per
521 namespace."""
521 namespace."""
522 repo = context.resource(mapping, 'repo')
522 repo = context.resource(mapping, 'repo')
523 ctx = context.resource(mapping, 'ctx')
523 ctx = context.resource(mapping, 'ctx')
524
524
525 namespaces = util.sortdict()
525 namespaces = util.sortdict()
526 def makensmapfn(ns):
526 def makensmapfn(ns):
527 # 'name' for iterating over namespaces, templatename for local reference
527 # 'name' for iterating over namespaces, templatename for local reference
528 return lambda v: {'name': v, ns.templatename: v}
528 return lambda v: {'name': v, ns.templatename: v}
529
529
530 for k, ns in repo.names.iteritems():
530 for k, ns in repo.names.iteritems():
531 names = ns.names(repo, ctx.node())
531 names = ns.names(repo, ctx.node())
532 f = _showcompatlist(context, mapping, 'name', names)
532 f = _showcompatlist(context, mapping, 'name', names)
533 namespaces[k] = _hybrid(f, names, makensmapfn(ns), pycompat.identity)
533 namespaces[k] = _hybrid(f, names, makensmapfn(ns), pycompat.identity)
534
534
535 f = _showcompatlist(context, mapping, 'namespace', list(namespaces))
535 f = _showcompatlist(context, mapping, 'namespace', list(namespaces))
536
536
537 def makemap(ns):
537 def makemap(ns):
538 return {
538 return {
539 'namespace': ns,
539 'namespace': ns,
540 'names': namespaces[ns],
540 'names': namespaces[ns],
541 'builtin': repo.names[ns].builtin,
541 'builtin': repo.names[ns].builtin,
542 'colorname': repo.names[ns].colorname,
542 'colorname': repo.names[ns].colorname,
543 }
543 }
544
544
545 return _hybrid(f, namespaces, makemap, pycompat.identity)
545 return _hybrid(f, namespaces, makemap, pycompat.identity)
546
546
547 @templatekeyword('node', requires={'ctx'})
547 @templatekeyword('node', requires={'ctx'})
548 def shownode(context, mapping):
548 def shownode(context, mapping):
549 """String. The changeset identification hash, as a 40 hexadecimal
549 """String. The changeset identification hash, as a 40 hexadecimal
550 digit string.
550 digit string.
551 """
551 """
552 ctx = context.resource(mapping, 'ctx')
552 ctx = context.resource(mapping, 'ctx')
553 return ctx.hex()
553 return ctx.hex()
554
554
555 @templatekeyword('obsolete', requires={'ctx'})
555 @templatekeyword('obsolete', requires={'ctx'})
556 def showobsolete(context, mapping):
556 def showobsolete(context, mapping):
557 """String. Whether the changeset is obsolete. (EXPERIMENTAL)"""
557 """String. Whether the changeset is obsolete. (EXPERIMENTAL)"""
558 ctx = context.resource(mapping, 'ctx')
558 ctx = context.resource(mapping, 'ctx')
559 if ctx.obsolete():
559 if ctx.obsolete():
560 return 'obsolete'
560 return 'obsolete'
561 return ''
561 return ''
562
562
563 @templatekeyword('path', requires={'fctx'})
563 @templatekeyword('path', requires={'fctx'})
564 def showpath(context, mapping):
564 def showpath(context, mapping):
565 """String. Repository-absolute path of the current file. (EXPERIMENTAL)"""
565 """String. Repository-absolute path of the current file. (EXPERIMENTAL)"""
566 fctx = context.resource(mapping, 'fctx')
566 fctx = context.resource(mapping, 'fctx')
567 return fctx.path()
567 return fctx.path()
568
568
569 @templatekeyword('peerurls', requires={'repo'})
569 @templatekeyword('peerurls', requires={'repo'})
570 def showpeerurls(context, mapping):
570 def showpeerurls(context, mapping):
571 """A dictionary of repository locations defined in the [paths] section
571 """A dictionary of repository locations defined in the [paths] section
572 of your configuration file."""
572 of your configuration file."""
573 repo = context.resource(mapping, 'repo')
573 repo = context.resource(mapping, 'repo')
574 # see commands.paths() for naming of dictionary keys
574 # see commands.paths() for naming of dictionary keys
575 paths = repo.ui.paths
575 paths = repo.ui.paths
576 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
576 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
577 def makemap(k):
577 def makemap(k):
578 p = paths[k]
578 p = paths[k]
579 d = {'name': k, 'url': p.rawloc}
579 d = {'name': k, 'url': p.rawloc}
580 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
580 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
581 return d
581 return d
582 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
582 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
583
583
584 @templatekeyword("predecessors", requires={'repo', 'ctx'})
584 @templatekeyword("predecessors", requires={'repo', 'ctx'})
585 def showpredecessors(context, mapping):
585 def showpredecessors(context, mapping):
586 """Returns the list if the closest visible successors. (EXPERIMENTAL)"""
586 """Returns the list of the closest visible successors. (EXPERIMENTAL)"""
587 repo = context.resource(mapping, 'repo')
587 repo = context.resource(mapping, 'repo')
588 ctx = context.resource(mapping, 'ctx')
588 ctx = context.resource(mapping, 'ctx')
589 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
589 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
590 predecessors = pycompat.maplist(hex, predecessors)
590 predecessors = pycompat.maplist(hex, predecessors)
591
591
592 return _hybrid(None, predecessors,
592 return _hybrid(None, predecessors,
593 lambda x: {'ctx': repo[x]},
593 lambda x: {'ctx': repo[x]},
594 lambda x: scmutil.formatchangeid(repo[x]))
594 lambda x: scmutil.formatchangeid(repo[x]))
595
595
596 @templatekeyword('reporoot', requires={'repo'})
596 @templatekeyword('reporoot', requires={'repo'})
597 def showreporoot(context, mapping):
597 def showreporoot(context, mapping):
598 """String. The root directory of the current repository."""
598 """String. The root directory of the current repository."""
599 repo = context.resource(mapping, 'repo')
599 repo = context.resource(mapping, 'repo')
600 return repo.root
600 return repo.root
601
601
602 @templatekeyword('size', requires={'fctx'})
602 @templatekeyword('size', requires={'fctx'})
603 def showsize(context, mapping):
603 def showsize(context, mapping):
604 """Integer. Size of the current file in bytes. (EXPERIMENTAL)"""
604 """Integer. Size of the current file in bytes. (EXPERIMENTAL)"""
605 fctx = context.resource(mapping, 'fctx')
605 fctx = context.resource(mapping, 'fctx')
606 return fctx.size()
606 return fctx.size()
607
607
608 # requires 'fctx' to denote {status} depends on (ctx, path) pair
608 # requires 'fctx' to denote {status} depends on (ctx, path) pair
609 @templatekeyword('status', requires={'ctx', 'fctx', 'revcache'})
609 @templatekeyword('status', requires={'ctx', 'fctx', 'revcache'})
610 def showstatus(context, mapping):
610 def showstatus(context, mapping):
611 """String. Status code of the current file. (EXPERIMENTAL)"""
611 """String. Status code of the current file. (EXPERIMENTAL)"""
612 path = templateutil.runsymbol(context, mapping, 'path')
612 path = templateutil.runsymbol(context, mapping, 'path')
613 path = templateutil.stringify(context, mapping, path)
613 path = templateutil.stringify(context, mapping, path)
614 if not path:
614 if not path:
615 return
615 return
616 statmap = _getfilestatusmap(context, mapping)
616 statmap = _getfilestatusmap(context, mapping)
617 if path not in statmap:
617 if path not in statmap:
618 statmap = _getfilestatusmap(context, mapping, listall=True)
618 statmap = _getfilestatusmap(context, mapping, listall=True)
619 return statmap.get(path)
619 return statmap.get(path)
620
620
621 @templatekeyword("successorssets", requires={'repo', 'ctx'})
621 @templatekeyword("successorssets", requires={'repo', 'ctx'})
622 def showsuccessorssets(context, mapping):
622 def showsuccessorssets(context, mapping):
623 """Returns a string of sets of successors for a changectx. Format used
623 """Returns a string of sets of successors for a changectx. Format used
624 is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and ctx2
624 is: [ctx1, ctx2], [ctx3] if ctx has been split into ctx1 and ctx2
625 while also diverged into ctx3. (EXPERIMENTAL)"""
625 while also diverged into ctx3. (EXPERIMENTAL)"""
626 repo = context.resource(mapping, 'repo')
626 repo = context.resource(mapping, 'repo')
627 ctx = context.resource(mapping, 'ctx')
627 ctx = context.resource(mapping, 'ctx')
628 if not ctx.obsolete():
628 if not ctx.obsolete():
629 return ''
629 return ''
630
630
631 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
631 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
632 ssets = [[hex(n) for n in ss] for ss in ssets]
632 ssets = [[hex(n) for n in ss] for ss in ssets]
633
633
634 data = []
634 data = []
635 for ss in ssets:
635 for ss in ssets:
636 h = _hybrid(None, ss, lambda x: {'ctx': repo[x]},
636 h = _hybrid(None, ss, lambda x: {'ctx': repo[x]},
637 lambda x: scmutil.formatchangeid(repo[x]))
637 lambda x: scmutil.formatchangeid(repo[x]))
638 data.append(h)
638 data.append(h)
639
639
640 # Format the successorssets
640 # Format the successorssets
641 def render(d):
641 def render(d):
642 return templateutil.stringify(context, mapping, d)
642 return templateutil.stringify(context, mapping, d)
643
643
644 def gen(data):
644 def gen(data):
645 yield "; ".join(render(d) for d in data)
645 yield "; ".join(render(d) for d in data)
646
646
647 return _hybrid(gen(data), data, lambda x: {'successorset': x},
647 return _hybrid(gen(data), data, lambda x: {'successorset': x},
648 pycompat.identity)
648 pycompat.identity)
649
649
650 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'})
650 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'})
651 def showsuccsandmarkers(context, mapping):
651 def showsuccsandmarkers(context, mapping):
652 """Returns a list of dict for each final successor of ctx. The dict
652 """Returns a list of dict for each final successor of ctx. The dict
653 contains successors node id in "successors" keys and the list of
653 contains successors node id in "successors" keys and the list of
654 obs-markers from ctx to the set of successors in "markers".
654 obs-markers from ctx to the set of successors in "markers".
655 (EXPERIMENTAL)
655 (EXPERIMENTAL)
656 """
656 """
657 repo = context.resource(mapping, 'repo')
657 repo = context.resource(mapping, 'repo')
658 ctx = context.resource(mapping, 'ctx')
658 ctx = context.resource(mapping, 'ctx')
659
659
660 values = obsutil.successorsandmarkers(repo, ctx)
660 values = obsutil.successorsandmarkers(repo, ctx)
661
661
662 if values is None:
662 if values is None:
663 values = []
663 values = []
664
664
665 # Format successors and markers to avoid exposing binary to templates
665 # Format successors and markers to avoid exposing binary to templates
666 data = []
666 data = []
667 for i in values:
667 for i in values:
668 # Format successors
668 # Format successors
669 successors = i['successors']
669 successors = i['successors']
670
670
671 successors = [hex(n) for n in successors]
671 successors = [hex(n) for n in successors]
672 successors = _hybrid(None, successors,
672 successors = _hybrid(None, successors,
673 lambda x: {'ctx': repo[x]},
673 lambda x: {'ctx': repo[x]},
674 lambda x: scmutil.formatchangeid(repo[x]))
674 lambda x: scmutil.formatchangeid(repo[x]))
675
675
676 # Format markers
676 # Format markers
677 finalmarkers = []
677 finalmarkers = []
678 for m in i['markers']:
678 for m in i['markers']:
679 hexprec = hex(m[0])
679 hexprec = hex(m[0])
680 hexsucs = tuple(hex(n) for n in m[1])
680 hexsucs = tuple(hex(n) for n in m[1])
681 hexparents = None
681 hexparents = None
682 if m[5] is not None:
682 if m[5] is not None:
683 hexparents = tuple(hex(n) for n in m[5])
683 hexparents = tuple(hex(n) for n in m[5])
684 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
684 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
685 finalmarkers.append(newmarker)
685 finalmarkers.append(newmarker)
686
686
687 data.append({'successors': successors, 'markers': finalmarkers})
687 data.append({'successors': successors, 'markers': finalmarkers})
688
688
689 return templateutil.mappinglist(data)
689 return templateutil.mappinglist(data)
690
690
691 @templatekeyword('p1rev', requires={'ctx'})
691 @templatekeyword('p1rev', requires={'ctx'})
692 def showp1rev(context, mapping):
692 def showp1rev(context, mapping):
693 """Integer. The repository-local revision number of the changeset's
693 """Integer. The repository-local revision number of the changeset's
694 first parent, or -1 if the changeset has no parents."""
694 first parent, or -1 if the changeset has no parents."""
695 ctx = context.resource(mapping, 'ctx')
695 ctx = context.resource(mapping, 'ctx')
696 return ctx.p1().rev()
696 return ctx.p1().rev()
697
697
698 @templatekeyword('p2rev', requires={'ctx'})
698 @templatekeyword('p2rev', requires={'ctx'})
699 def showp2rev(context, mapping):
699 def showp2rev(context, mapping):
700 """Integer. The repository-local revision number of the changeset's
700 """Integer. The repository-local revision number of the changeset's
701 second parent, or -1 if the changeset has no second parent."""
701 second parent, or -1 if the changeset has no second parent."""
702 ctx = context.resource(mapping, 'ctx')
702 ctx = context.resource(mapping, 'ctx')
703 return ctx.p2().rev()
703 return ctx.p2().rev()
704
704
705 @templatekeyword('p1node', requires={'ctx'})
705 @templatekeyword('p1node', requires={'ctx'})
706 def showp1node(context, mapping):
706 def showp1node(context, mapping):
707 """String. The identification hash of the changeset's first parent,
707 """String. The identification hash of the changeset's first parent,
708 as a 40 digit hexadecimal string. If the changeset has no parents, all
708 as a 40 digit hexadecimal string. If the changeset has no parents, all
709 digits are 0."""
709 digits are 0."""
710 ctx = context.resource(mapping, 'ctx')
710 ctx = context.resource(mapping, 'ctx')
711 return ctx.p1().hex()
711 return ctx.p1().hex()
712
712
713 @templatekeyword('p2node', requires={'ctx'})
713 @templatekeyword('p2node', requires={'ctx'})
714 def showp2node(context, mapping):
714 def showp2node(context, mapping):
715 """String. The identification hash of the changeset's second
715 """String. The identification hash of the changeset's second
716 parent, as a 40 digit hexadecimal string. If the changeset has no second
716 parent, as a 40 digit hexadecimal string. If the changeset has no second
717 parent, all digits are 0."""
717 parent, all digits are 0."""
718 ctx = context.resource(mapping, 'ctx')
718 ctx = context.resource(mapping, 'ctx')
719 return ctx.p2().hex()
719 return ctx.p2().hex()
720
720
721 @templatekeyword('parents', requires={'repo', 'ctx'})
721 @templatekeyword('parents', requires={'repo', 'ctx'})
722 def showparents(context, mapping):
722 def showparents(context, mapping):
723 """List of strings. The parents of the changeset in "rev:node"
723 """List of strings. The parents of the changeset in "rev:node"
724 format. If the changeset has only one "natural" parent (the predecessor
724 format. If the changeset has only one "natural" parent (the predecessor
725 revision) nothing is shown."""
725 revision) nothing is shown."""
726 repo = context.resource(mapping, 'repo')
726 repo = context.resource(mapping, 'repo')
727 ctx = context.resource(mapping, 'ctx')
727 ctx = context.resource(mapping, 'ctx')
728 pctxs = scmutil.meaningfulparents(repo, ctx)
728 pctxs = scmutil.meaningfulparents(repo, ctx)
729 prevs = [p.rev() for p in pctxs]
729 prevs = [p.rev() for p in pctxs]
730 parents = [[('rev', p.rev()),
730 parents = [[('rev', p.rev()),
731 ('node', p.hex()),
731 ('node', p.hex()),
732 ('phase', p.phasestr())]
732 ('phase', p.phasestr())]
733 for p in pctxs]
733 for p in pctxs]
734 f = _showcompatlist(context, mapping, 'parent', parents)
734 f = _showcompatlist(context, mapping, 'parent', parents)
735 return _hybrid(f, prevs, lambda x: {'ctx': repo[x]},
735 return _hybrid(f, prevs, lambda x: {'ctx': repo[x]},
736 lambda x: scmutil.formatchangeid(repo[x]), keytype=int)
736 lambda x: scmutil.formatchangeid(repo[x]), keytype=int)
737
737
738 @templatekeyword('phase', requires={'ctx'})
738 @templatekeyword('phase', requires={'ctx'})
739 def showphase(context, mapping):
739 def showphase(context, mapping):
740 """String. The changeset phase name."""
740 """String. The changeset phase name."""
741 ctx = context.resource(mapping, 'ctx')
741 ctx = context.resource(mapping, 'ctx')
742 return ctx.phasestr()
742 return ctx.phasestr()
743
743
744 @templatekeyword('phaseidx', requires={'ctx'})
744 @templatekeyword('phaseidx', requires={'ctx'})
745 def showphaseidx(context, mapping):
745 def showphaseidx(context, mapping):
746 """Integer. The changeset phase index. (ADVANCED)"""
746 """Integer. The changeset phase index. (ADVANCED)"""
747 ctx = context.resource(mapping, 'ctx')
747 ctx = context.resource(mapping, 'ctx')
748 return ctx.phase()
748 return ctx.phase()
749
749
750 @templatekeyword('rev', requires={'ctx'})
750 @templatekeyword('rev', requires={'ctx'})
751 def showrev(context, mapping):
751 def showrev(context, mapping):
752 """Integer. The repository-local changeset revision number."""
752 """Integer. The repository-local changeset revision number."""
753 ctx = context.resource(mapping, 'ctx')
753 ctx = context.resource(mapping, 'ctx')
754 return scmutil.intrev(ctx)
754 return scmutil.intrev(ctx)
755
755
756 def showrevslist(context, mapping, name, revs):
756 def showrevslist(context, mapping, name, revs):
757 """helper to generate a list of revisions in which a mapped template will
757 """helper to generate a list of revisions in which a mapped template will
758 be evaluated"""
758 be evaluated"""
759 repo = context.resource(mapping, 'repo')
759 repo = context.resource(mapping, 'repo')
760 f = _showcompatlist(context, mapping, name, ['%d' % r for r in revs])
760 f = _showcompatlist(context, mapping, name, ['%d' % r for r in revs])
761 return _hybrid(f, revs,
761 return _hybrid(f, revs,
762 lambda x: {name: x, 'ctx': repo[x]},
762 lambda x: {name: x, 'ctx': repo[x]},
763 pycompat.identity, keytype=int)
763 pycompat.identity, keytype=int)
764
764
765 @templatekeyword('subrepos', requires={'ctx'})
765 @templatekeyword('subrepos', requires={'ctx'})
766 def showsubrepos(context, mapping):
766 def showsubrepos(context, mapping):
767 """List of strings. Updated subrepositories in the changeset."""
767 """List of strings. Updated subrepositories in the changeset."""
768 ctx = context.resource(mapping, 'ctx')
768 ctx = context.resource(mapping, 'ctx')
769 substate = ctx.substate
769 substate = ctx.substate
770 if not substate:
770 if not substate:
771 return compatlist(context, mapping, 'subrepo', [])
771 return compatlist(context, mapping, 'subrepo', [])
772 psubstate = ctx.parents()[0].substate or {}
772 psubstate = ctx.parents()[0].substate or {}
773 subrepos = []
773 subrepos = []
774 for sub in substate:
774 for sub in substate:
775 if sub not in psubstate or substate[sub] != psubstate[sub]:
775 if sub not in psubstate or substate[sub] != psubstate[sub]:
776 subrepos.append(sub) # modified or newly added in ctx
776 subrepos.append(sub) # modified or newly added in ctx
777 for sub in psubstate:
777 for sub in psubstate:
778 if sub not in substate:
778 if sub not in substate:
779 subrepos.append(sub) # removed in ctx
779 subrepos.append(sub) # removed in ctx
780 return compatlist(context, mapping, 'subrepo', sorted(subrepos))
780 return compatlist(context, mapping, 'subrepo', sorted(subrepos))
781
781
782 # don't remove "showtags" definition, even though namespaces will put
782 # don't remove "showtags" definition, even though namespaces will put
783 # a helper function for "tags" keyword into "keywords" map automatically,
783 # a helper function for "tags" keyword into "keywords" map automatically,
784 # because online help text is built without namespaces initialization
784 # because online help text is built without namespaces initialization
785 @templatekeyword('tags', requires={'repo', 'ctx'})
785 @templatekeyword('tags', requires={'repo', 'ctx'})
786 def showtags(context, mapping):
786 def showtags(context, mapping):
787 """List of strings. Any tags associated with the changeset."""
787 """List of strings. Any tags associated with the changeset."""
788 return shownames(context, mapping, 'tags')
788 return shownames(context, mapping, 'tags')
789
789
790 @templatekeyword('termwidth', requires={'ui'})
790 @templatekeyword('termwidth', requires={'ui'})
791 def showtermwidth(context, mapping):
791 def showtermwidth(context, mapping):
792 """Integer. The width of the current terminal."""
792 """Integer. The width of the current terminal."""
793 ui = context.resource(mapping, 'ui')
793 ui = context.resource(mapping, 'ui')
794 return ui.termwidth()
794 return ui.termwidth()
795
795
796 @templatekeyword('user', requires={'ctx'})
796 @templatekeyword('user', requires={'ctx'})
797 def showuser(context, mapping):
797 def showuser(context, mapping):
798 """String. The unmodified author of the changeset."""
798 """String. The unmodified author of the changeset."""
799 ctx = context.resource(mapping, 'ctx')
799 ctx = context.resource(mapping, 'ctx')
800 return ctx.user()
800 return ctx.user()
801
801
802 @templatekeyword('instabilities', requires={'ctx'})
802 @templatekeyword('instabilities', requires={'ctx'})
803 def showinstabilities(context, mapping):
803 def showinstabilities(context, mapping):
804 """List of strings. Evolution instabilities affecting the changeset.
804 """List of strings. Evolution instabilities affecting the changeset.
805 (EXPERIMENTAL)
805 (EXPERIMENTAL)
806 """
806 """
807 ctx = context.resource(mapping, 'ctx')
807 ctx = context.resource(mapping, 'ctx')
808 return compatlist(context, mapping, 'instability', ctx.instabilities(),
808 return compatlist(context, mapping, 'instability', ctx.instabilities(),
809 plural='instabilities')
809 plural='instabilities')
810
810
811 @templatekeyword('verbosity', requires={'ui'})
811 @templatekeyword('verbosity', requires={'ui'})
812 def showverbosity(context, mapping):
812 def showverbosity(context, mapping):
813 """String. The current output verbosity in 'debug', 'quiet', 'verbose',
813 """String. The current output verbosity in 'debug', 'quiet', 'verbose',
814 or ''."""
814 or ''."""
815 ui = context.resource(mapping, 'ui')
815 ui = context.resource(mapping, 'ui')
816 # see logcmdutil.changesettemplater for priority of these flags
816 # see logcmdutil.changesettemplater for priority of these flags
817 if ui.debugflag:
817 if ui.debugflag:
818 return 'debug'
818 return 'debug'
819 elif ui.quiet:
819 elif ui.quiet:
820 return 'quiet'
820 return 'quiet'
821 elif ui.verbose:
821 elif ui.verbose:
822 return 'verbose'
822 return 'verbose'
823 return ''
823 return ''
824
824
825 @templatekeyword('whyunstable', requires={'repo', 'ctx'})
825 @templatekeyword('whyunstable', requires={'repo', 'ctx'})
826 def showwhyunstable(context, mapping):
826 def showwhyunstable(context, mapping):
827 """List of dicts explaining all instabilities of a changeset.
827 """List of dicts explaining all instabilities of a changeset.
828 (EXPERIMENTAL)
828 (EXPERIMENTAL)
829 """
829 """
830 repo = context.resource(mapping, 'repo')
830 repo = context.resource(mapping, 'repo')
831 ctx = context.resource(mapping, 'ctx')
831 ctx = context.resource(mapping, 'ctx')
832
832
833 def formatnode(ctx):
833 def formatnode(ctx):
834 return '%s (%s)' % (scmutil.formatchangeid(ctx), ctx.phasestr())
834 return '%s (%s)' % (scmutil.formatchangeid(ctx), ctx.phasestr())
835
835
836 entries = obsutil.whyunstable(repo, ctx)
836 entries = obsutil.whyunstable(repo, ctx)
837
837
838 for entry in entries:
838 for entry in entries:
839 if entry.get('divergentnodes'):
839 if entry.get('divergentnodes'):
840 dnodes = entry['divergentnodes']
840 dnodes = entry['divergentnodes']
841 dnhybrid = _hybrid(None, [dnode.hex() for dnode in dnodes],
841 dnhybrid = _hybrid(None, [dnode.hex() for dnode in dnodes],
842 lambda x: {'ctx': repo[x]},
842 lambda x: {'ctx': repo[x]},
843 lambda x: formatnode(repo[x]))
843 lambda x: formatnode(repo[x]))
844 entry['divergentnodes'] = dnhybrid
844 entry['divergentnodes'] = dnhybrid
845
845
846 tmpl = ('{instability}:{if(divergentnodes, " ")}{divergentnodes} '
846 tmpl = ('{instability}:{if(divergentnodes, " ")}{divergentnodes} '
847 '{reason} {node|short}')
847 '{reason} {node|short}')
848 return templateutil.mappinglist(entries, tmpl=tmpl, sep='\n')
848 return templateutil.mappinglist(entries, tmpl=tmpl, sep='\n')
849
849
850 def loadkeyword(ui, extname, registrarobj):
850 def loadkeyword(ui, extname, registrarobj):
851 """Load template keyword from specified registrarobj
851 """Load template keyword from specified registrarobj
852 """
852 """
853 for name, func in registrarobj._table.iteritems():
853 for name, func in registrarobj._table.iteritems():
854 keywords[name] = func
854 keywords[name] = func
855
855
856 # tell hggettext to extract docstrings from these functions:
856 # tell hggettext to extract docstrings from these functions:
857 i18nfunctions = keywords.values()
857 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now