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