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