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