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