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