##// END OF EJS Templates
templatekw: factor out the changessincetag calculation to a private method...
Matt Harbison -
r26483:e94f9304 default
parent child Browse files
Show More
@@ -1,517 +1,522 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 .node import hex
10 from .node import hex
11 from . import (
11 from . import (
12 error,
12 error,
13 hbisect,
13 hbisect,
14 patch,
14 patch,
15 scmutil,
15 scmutil,
16 util,
16 util,
17 )
17 )
18
18
19 # This helper class allows us to handle both:
19 # This helper class allows us to handle both:
20 # "{files}" (legacy command-line-specific list hack) and
20 # "{files}" (legacy command-line-specific list hack) and
21 # "{files % '{file}\n'}" (hgweb-style with inlining and function support)
21 # "{files % '{file}\n'}" (hgweb-style with inlining and function support)
22 # and to access raw values:
22 # and to access raw values:
23 # "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
23 # "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
24 # "{get(extras, key)}"
24 # "{get(extras, key)}"
25
25
26 class _hybrid(object):
26 class _hybrid(object):
27 def __init__(self, gen, values, makemap, joinfmt=None):
27 def __init__(self, gen, values, makemap, joinfmt=None):
28 self.gen = gen
28 self.gen = gen
29 self.values = values
29 self.values = values
30 self._makemap = makemap
30 self._makemap = makemap
31 if joinfmt:
31 if joinfmt:
32 self.joinfmt = joinfmt
32 self.joinfmt = joinfmt
33 else:
33 else:
34 self.joinfmt = lambda x: x.values()[0]
34 self.joinfmt = lambda x: x.values()[0]
35 def __iter__(self):
35 def __iter__(self):
36 return self.gen
36 return self.gen
37 def __call__(self):
37 def __call__(self):
38 makemap = self._makemap
38 makemap = self._makemap
39 for x in self.values:
39 for x in self.values:
40 yield makemap(x)
40 yield makemap(x)
41 def __contains__(self, x):
41 def __contains__(self, x):
42 return x in self.values
42 return x in self.values
43 def __len__(self):
43 def __len__(self):
44 return len(self.values)
44 return len(self.values)
45 def __getattr__(self, name):
45 def __getattr__(self, name):
46 if name != 'get':
46 if name != 'get':
47 raise AttributeError(name)
47 raise AttributeError(name)
48 return getattr(self.values, name)
48 return getattr(self.values, name)
49
49
50 def showlist(name, values, plural=None, element=None, separator=' ', **args):
50 def showlist(name, values, plural=None, element=None, separator=' ', **args):
51 if not element:
51 if not element:
52 element = name
52 element = name
53 f = _showlist(name, values, plural, separator, **args)
53 f = _showlist(name, values, plural, separator, **args)
54 return _hybrid(f, values, lambda x: {element: x})
54 return _hybrid(f, values, lambda x: {element: x})
55
55
56 def _showlist(name, values, plural=None, separator=' ', **args):
56 def _showlist(name, values, plural=None, separator=' ', **args):
57 '''expand set of values.
57 '''expand set of values.
58 name is name of key in template map.
58 name is name of key in template map.
59 values is list of strings or dicts.
59 values is list of strings or dicts.
60 plural is plural of name, if not simply name + 's'.
60 plural is plural of name, if not simply name + 's'.
61 separator is used to join values as a string
61 separator is used to join values as a string
62
62
63 expansion works like this, given name 'foo'.
63 expansion works like this, given name 'foo'.
64
64
65 if values is empty, expand 'no_foos'.
65 if values is empty, expand 'no_foos'.
66
66
67 if 'foo' not in template map, return values as a string,
67 if 'foo' not in template map, return values as a string,
68 joined by 'separator'.
68 joined by 'separator'.
69
69
70 expand 'start_foos'.
70 expand 'start_foos'.
71
71
72 for each value, expand 'foo'. if 'last_foo' in template
72 for each value, expand 'foo'. if 'last_foo' in template
73 map, expand it instead of 'foo' for last key.
73 map, expand it instead of 'foo' for last key.
74
74
75 expand 'end_foos'.
75 expand 'end_foos'.
76 '''
76 '''
77 templ = args['templ']
77 templ = args['templ']
78 if plural:
78 if plural:
79 names = plural
79 names = plural
80 else: names = name + 's'
80 else: names = name + 's'
81 if not values:
81 if not values:
82 noname = 'no_' + names
82 noname = 'no_' + names
83 if noname in templ:
83 if noname in templ:
84 yield templ(noname, **args)
84 yield templ(noname, **args)
85 return
85 return
86 if name not in templ:
86 if name not in templ:
87 if isinstance(values[0], str):
87 if isinstance(values[0], str):
88 yield separator.join(values)
88 yield separator.join(values)
89 else:
89 else:
90 for v in values:
90 for v in values:
91 yield dict(v, **args)
91 yield dict(v, **args)
92 return
92 return
93 startname = 'start_' + names
93 startname = 'start_' + names
94 if startname in templ:
94 if startname in templ:
95 yield templ(startname, **args)
95 yield templ(startname, **args)
96 vargs = args.copy()
96 vargs = args.copy()
97 def one(v, tag=name):
97 def one(v, tag=name):
98 try:
98 try:
99 vargs.update(v)
99 vargs.update(v)
100 except (AttributeError, ValueError):
100 except (AttributeError, ValueError):
101 try:
101 try:
102 for a, b in v:
102 for a, b in v:
103 vargs[a] = b
103 vargs[a] = b
104 except ValueError:
104 except ValueError:
105 vargs[name] = v
105 vargs[name] = v
106 return templ(tag, **vargs)
106 return templ(tag, **vargs)
107 lastname = 'last_' + name
107 lastname = 'last_' + name
108 if lastname in templ:
108 if lastname in templ:
109 last = values.pop()
109 last = values.pop()
110 else:
110 else:
111 last = None
111 last = None
112 for v in values:
112 for v in values:
113 yield one(v)
113 yield one(v)
114 if last is not None:
114 if last is not None:
115 yield one(last, tag=lastname)
115 yield one(last, tag=lastname)
116 endname = 'end_' + names
116 endname = 'end_' + names
117 if endname in templ:
117 if endname in templ:
118 yield templ(endname, **args)
118 yield templ(endname, **args)
119
119
120 def getfiles(repo, ctx, revcache):
120 def getfiles(repo, ctx, revcache):
121 if 'files' not in revcache:
121 if 'files' not in revcache:
122 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
122 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
123 return revcache['files']
123 return revcache['files']
124
124
125 def getlatesttags(repo, ctx, cache, pattern=None):
125 def getlatesttags(repo, ctx, cache, pattern=None):
126 '''return date, distance and name for the latest tag of rev'''
126 '''return date, distance and name for the latest tag of rev'''
127
127
128 cachename = 'latesttags'
128 cachename = 'latesttags'
129 if pattern is not None:
129 if pattern is not None:
130 cachename += '-' + pattern
130 cachename += '-' + pattern
131 match = util.stringmatcher(pattern)[2]
131 match = util.stringmatcher(pattern)[2]
132 else:
132 else:
133 match = util.always
133 match = util.always
134
134
135 if cachename not in cache:
135 if cachename not in cache:
136 # Cache mapping from rev to a tuple with tag date, tag
136 # Cache mapping from rev to a tuple with tag date, tag
137 # distance and tag name
137 # distance and tag name
138 cache[cachename] = {-1: (0, 0, ['null'])}
138 cache[cachename] = {-1: (0, 0, ['null'])}
139 latesttags = cache[cachename]
139 latesttags = cache[cachename]
140
140
141 rev = ctx.rev()
141 rev = ctx.rev()
142 todo = [rev]
142 todo = [rev]
143 while todo:
143 while todo:
144 rev = todo.pop()
144 rev = todo.pop()
145 if rev in latesttags:
145 if rev in latesttags:
146 continue
146 continue
147 ctx = repo[rev]
147 ctx = repo[rev]
148 tags = [t for t in ctx.tags()
148 tags = [t for t in ctx.tags()
149 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
149 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
150 and match(t))]
150 and match(t))]
151 if tags:
151 if tags:
152 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
152 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
153 continue
153 continue
154 try:
154 try:
155 # The tuples are laid out so the right one can be found by
155 # The tuples are laid out so the right one can be found by
156 # comparison.
156 # comparison.
157 pdate, pdist, ptag = max(
157 pdate, pdist, ptag = max(
158 latesttags[p.rev()] for p in ctx.parents())
158 latesttags[p.rev()] for p in ctx.parents())
159 except KeyError:
159 except KeyError:
160 # Cache miss - recurse
160 # Cache miss - recurse
161 todo.append(rev)
161 todo.append(rev)
162 todo.extend(p.rev() for p in ctx.parents())
162 todo.extend(p.rev() for p in ctx.parents())
163 continue
163 continue
164 latesttags[rev] = pdate, pdist + 1, ptag
164 latesttags[rev] = pdate, pdist + 1, ptag
165 return latesttags[rev]
165 return latesttags[rev]
166
166
167 def getrenamedfn(repo, endrev=None):
167 def getrenamedfn(repo, endrev=None):
168 rcache = {}
168 rcache = {}
169 if endrev is None:
169 if endrev is None:
170 endrev = len(repo)
170 endrev = len(repo)
171
171
172 def getrenamed(fn, rev):
172 def getrenamed(fn, rev):
173 '''looks up all renames for a file (up to endrev) the first
173 '''looks up all renames for a file (up to endrev) the first
174 time the file is given. It indexes on the changerev and only
174 time the file is given. It indexes on the changerev and only
175 parses the manifest if linkrev != changerev.
175 parses the manifest if linkrev != changerev.
176 Returns rename info for fn at changerev rev.'''
176 Returns rename info for fn at changerev rev.'''
177 if fn not in rcache:
177 if fn not in rcache:
178 rcache[fn] = {}
178 rcache[fn] = {}
179 fl = repo.file(fn)
179 fl = repo.file(fn)
180 for i in fl:
180 for i in fl:
181 lr = fl.linkrev(i)
181 lr = fl.linkrev(i)
182 renamed = fl.renamed(fl.node(i))
182 renamed = fl.renamed(fl.node(i))
183 rcache[fn][lr] = renamed
183 rcache[fn][lr] = renamed
184 if lr >= endrev:
184 if lr >= endrev:
185 break
185 break
186 if rev in rcache[fn]:
186 if rev in rcache[fn]:
187 return rcache[fn][rev]
187 return rcache[fn][rev]
188
188
189 # If linkrev != rev (i.e. rev not found in rcache) fallback to
189 # If linkrev != rev (i.e. rev not found in rcache) fallback to
190 # filectx logic.
190 # filectx logic.
191 try:
191 try:
192 return repo[rev][fn].renamed()
192 return repo[rev][fn].renamed()
193 except error.LookupError:
193 except error.LookupError:
194 return None
194 return None
195
195
196 return getrenamed
196 return getrenamed
197
197
198
198
199 def showauthor(repo, ctx, templ, **args):
199 def showauthor(repo, ctx, templ, **args):
200 """:author: String. The unmodified author of the changeset."""
200 """:author: String. The unmodified author of the changeset."""
201 return ctx.user()
201 return ctx.user()
202
202
203 def showbisect(repo, ctx, templ, **args):
203 def showbisect(repo, ctx, templ, **args):
204 """:bisect: String. The changeset bisection status."""
204 """:bisect: String. The changeset bisection status."""
205 return hbisect.label(repo, ctx.node())
205 return hbisect.label(repo, ctx.node())
206
206
207 def showbranch(**args):
207 def showbranch(**args):
208 """:branch: String. The name of the branch on which the changeset was
208 """:branch: String. The name of the branch on which the changeset was
209 committed.
209 committed.
210 """
210 """
211 return args['ctx'].branch()
211 return args['ctx'].branch()
212
212
213 def showbranches(**args):
213 def showbranches(**args):
214 """:branches: List of strings. The name of the branch on which the
214 """:branches: List of strings. The name of the branch on which the
215 changeset was committed. Will be empty if the branch name was
215 changeset was committed. Will be empty if the branch name was
216 default. (DEPRECATED)
216 default. (DEPRECATED)
217 """
217 """
218 branch = args['ctx'].branch()
218 branch = args['ctx'].branch()
219 if branch != 'default':
219 if branch != 'default':
220 return showlist('branch', [branch], plural='branches', **args)
220 return showlist('branch', [branch], plural='branches', **args)
221 return showlist('branch', [], plural='branches', **args)
221 return showlist('branch', [], plural='branches', **args)
222
222
223 def showbookmarks(**args):
223 def showbookmarks(**args):
224 """:bookmarks: List of strings. Any bookmarks associated with the
224 """:bookmarks: List of strings. Any bookmarks associated with the
225 changeset. Also sets 'active', the name of the active bookmark.
225 changeset. Also sets 'active', the name of the active bookmark.
226 """
226 """
227 repo = args['ctx']._repo
227 repo = args['ctx']._repo
228 bookmarks = args['ctx'].bookmarks()
228 bookmarks = args['ctx'].bookmarks()
229 active = repo._activebookmark
229 active = repo._activebookmark
230 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
230 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
231 f = _showlist('bookmark', bookmarks, **args)
231 f = _showlist('bookmark', bookmarks, **args)
232 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
232 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
233
233
234 def showchildren(**args):
234 def showchildren(**args):
235 """:children: List of strings. The children of the changeset."""
235 """:children: List of strings. The children of the changeset."""
236 ctx = args['ctx']
236 ctx = args['ctx']
237 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
237 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
238 return showlist('children', childrevs, element='child', **args)
238 return showlist('children', childrevs, element='child', **args)
239
239
240 # Deprecated, but kept alive for help generation a purpose.
240 # Deprecated, but kept alive for help generation a purpose.
241 def showcurrentbookmark(**args):
241 def showcurrentbookmark(**args):
242 """:currentbookmark: String. The active bookmark, if it is
242 """:currentbookmark: String. The active bookmark, if it is
243 associated with the changeset (DEPRECATED)"""
243 associated with the changeset (DEPRECATED)"""
244 return showactivebookmark(**args)
244 return showactivebookmark(**args)
245
245
246 def showactivebookmark(**args):
246 def showactivebookmark(**args):
247 """:activebookmark: String. The active bookmark, if it is
247 """:activebookmark: String. The active bookmark, if it is
248 associated with the changeset"""
248 associated with the changeset"""
249 active = args['repo']._activebookmark
249 active = args['repo']._activebookmark
250 if active and active in args['ctx'].bookmarks():
250 if active and active in args['ctx'].bookmarks():
251 return active
251 return active
252 return ''
252 return ''
253
253
254 def showdate(repo, ctx, templ, **args):
254 def showdate(repo, ctx, templ, **args):
255 """:date: Date information. The date when the changeset was committed."""
255 """:date: Date information. The date when the changeset was committed."""
256 return ctx.date()
256 return ctx.date()
257
257
258 def showdescription(repo, ctx, templ, **args):
258 def showdescription(repo, ctx, templ, **args):
259 """:desc: String. The text of the changeset description."""
259 """:desc: String. The text of the changeset description."""
260 return ctx.description().strip()
260 return ctx.description().strip()
261
261
262 def showdiffstat(repo, ctx, templ, **args):
262 def showdiffstat(repo, ctx, templ, **args):
263 """:diffstat: String. Statistics of changes with the following format:
263 """:diffstat: String. Statistics of changes with the following format:
264 "modified files: +added/-removed lines"
264 "modified files: +added/-removed lines"
265 """
265 """
266 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
266 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
267 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
267 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
268 return '%s: +%s/-%s' % (len(stats), adds, removes)
268 return '%s: +%s/-%s' % (len(stats), adds, removes)
269
269
270 def showextras(**args):
270 def showextras(**args):
271 """:extras: List of dicts with key, value entries of the 'extras'
271 """:extras: List of dicts with key, value entries of the 'extras'
272 field of this changeset."""
272 field of this changeset."""
273 extras = args['ctx'].extra()
273 extras = args['ctx'].extra()
274 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
274 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
275 makemap = lambda k: {'key': k, 'value': extras[k]}
275 makemap = lambda k: {'key': k, 'value': extras[k]}
276 c = [makemap(k) for k in extras]
276 c = [makemap(k) for k in extras]
277 f = _showlist('extra', c, plural='extras', **args)
277 f = _showlist('extra', c, plural='extras', **args)
278 return _hybrid(f, extras, makemap,
278 return _hybrid(f, extras, makemap,
279 lambda x: '%s=%s' % (x['key'], x['value']))
279 lambda x: '%s=%s' % (x['key'], x['value']))
280
280
281 def showfileadds(**args):
281 def showfileadds(**args):
282 """:file_adds: List of strings. Files added by this changeset."""
282 """:file_adds: List of strings. Files added by this changeset."""
283 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
283 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
284 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
284 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
285 element='file', **args)
285 element='file', **args)
286
286
287 def showfilecopies(**args):
287 def showfilecopies(**args):
288 """:file_copies: List of strings. Files copied in this changeset with
288 """:file_copies: List of strings. Files copied in this changeset with
289 their sources.
289 their sources.
290 """
290 """
291 cache, ctx = args['cache'], args['ctx']
291 cache, ctx = args['cache'], args['ctx']
292 copies = args['revcache'].get('copies')
292 copies = args['revcache'].get('copies')
293 if copies is None:
293 if copies is None:
294 if 'getrenamed' not in cache:
294 if 'getrenamed' not in cache:
295 cache['getrenamed'] = getrenamedfn(args['repo'])
295 cache['getrenamed'] = getrenamedfn(args['repo'])
296 copies = []
296 copies = []
297 getrenamed = cache['getrenamed']
297 getrenamed = cache['getrenamed']
298 for fn in ctx.files():
298 for fn in ctx.files():
299 rename = getrenamed(fn, ctx.rev())
299 rename = getrenamed(fn, ctx.rev())
300 if rename:
300 if rename:
301 copies.append((fn, rename[0]))
301 copies.append((fn, rename[0]))
302
302
303 copies = util.sortdict(copies)
303 copies = util.sortdict(copies)
304 makemap = lambda k: {'name': k, 'source': copies[k]}
304 makemap = lambda k: {'name': k, 'source': copies[k]}
305 c = [makemap(k) for k in copies]
305 c = [makemap(k) for k in copies]
306 f = _showlist('file_copy', c, plural='file_copies', **args)
306 f = _showlist('file_copy', c, plural='file_copies', **args)
307 return _hybrid(f, copies, makemap,
307 return _hybrid(f, copies, makemap,
308 lambda x: '%s (%s)' % (x['name'], x['source']))
308 lambda x: '%s (%s)' % (x['name'], x['source']))
309
309
310 # showfilecopiesswitch() displays file copies only if copy records are
310 # showfilecopiesswitch() displays file copies only if copy records are
311 # provided before calling the templater, usually with a --copies
311 # provided before calling the templater, usually with a --copies
312 # command line switch.
312 # command line switch.
313 def showfilecopiesswitch(**args):
313 def showfilecopiesswitch(**args):
314 """:file_copies_switch: List of strings. Like "file_copies" but displayed
314 """:file_copies_switch: List of strings. Like "file_copies" but displayed
315 only if the --copied switch is set.
315 only if the --copied switch is set.
316 """
316 """
317 copies = args['revcache'].get('copies') or []
317 copies = args['revcache'].get('copies') or []
318 copies = util.sortdict(copies)
318 copies = util.sortdict(copies)
319 makemap = lambda k: {'name': k, 'source': copies[k]}
319 makemap = lambda k: {'name': k, 'source': copies[k]}
320 c = [makemap(k) for k in copies]
320 c = [makemap(k) for k in copies]
321 f = _showlist('file_copy', c, plural='file_copies', **args)
321 f = _showlist('file_copy', c, plural='file_copies', **args)
322 return _hybrid(f, copies, makemap,
322 return _hybrid(f, copies, makemap,
323 lambda x: '%s (%s)' % (x['name'], x['source']))
323 lambda x: '%s (%s)' % (x['name'], x['source']))
324
324
325 def showfiledels(**args):
325 def showfiledels(**args):
326 """:file_dels: List of strings. Files removed by this changeset."""
326 """:file_dels: List of strings. Files removed by this changeset."""
327 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
327 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
328 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
328 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
329 element='file', **args)
329 element='file', **args)
330
330
331 def showfilemods(**args):
331 def showfilemods(**args):
332 """:file_mods: List of strings. Files modified by this changeset."""
332 """:file_mods: List of strings. Files modified by this changeset."""
333 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
333 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
334 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
334 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
335 element='file', **args)
335 element='file', **args)
336
336
337 def showfiles(**args):
337 def showfiles(**args):
338 """:files: List of strings. All files modified, added, or removed by this
338 """:files: List of strings. All files modified, added, or removed by this
339 changeset.
339 changeset.
340 """
340 """
341 return showlist('file', args['ctx'].files(), **args)
341 return showlist('file', args['ctx'].files(), **args)
342
342
343 def showlatesttag(**args):
343 def showlatesttag(**args):
344 """:latesttag: List of strings. The global tags on the most recent globally
344 """:latesttag: List of strings. The global tags on the most recent globally
345 tagged ancestor of this changeset.
345 tagged ancestor of this changeset.
346 """
346 """
347 repo, ctx = args['repo'], args['ctx']
347 repo, ctx = args['repo'], args['ctx']
348 cache = args['cache']
348 cache = args['cache']
349 latesttags = getlatesttags(repo, ctx, cache)[2]
349 latesttags = getlatesttags(repo, ctx, cache)[2]
350
350
351 return showlist('latesttag', latesttags, separator=':', **args)
351 return showlist('latesttag', latesttags, separator=':', **args)
352
352
353 def showlatesttagdistance(repo, ctx, templ, cache, **args):
353 def showlatesttagdistance(repo, ctx, templ, cache, **args):
354 """:latesttagdistance: Integer. Longest path to the latest tag."""
354 """:latesttagdistance: Integer. Longest path to the latest tag."""
355 return getlatesttags(repo, ctx, cache)[1]
355 return getlatesttags(repo, ctx, cache)[1]
356
356
357 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
357 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
358 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
358 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
359 latesttag = getlatesttags(repo, ctx, cache)[2][0]
359 latesttag = getlatesttags(repo, ctx, cache)[2][0]
360
361 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
362
363 def _showchangessincetag(repo, ctx, **args):
360 offset = 0
364 offset = 0
361 revs = [ctx.rev()]
365 revs = [ctx.rev()]
366 tag = args['tag']
362
367
363 # The only() revset doesn't currently support wdir()
368 # The only() revset doesn't currently support wdir()
364 if ctx.rev() is None:
369 if ctx.rev() is None:
365 offset = 1
370 offset = 1
366 revs = [p.rev() for p in ctx.parents()]
371 revs = [p.rev() for p in ctx.parents()]
367
372
368 return len(repo.revs('only(%ld, %s)', revs, latesttag)) + offset
373 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
369
374
370 def showmanifest(**args):
375 def showmanifest(**args):
371 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
376 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
372 mnode = ctx.manifestnode()
377 mnode = ctx.manifestnode()
373 if mnode is None:
378 if mnode is None:
374 # just avoid crash, we might want to use the 'ff...' hash in future
379 # just avoid crash, we might want to use the 'ff...' hash in future
375 return
380 return
376 args = args.copy()
381 args = args.copy()
377 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
382 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
378 return templ('manifest', **args)
383 return templ('manifest', **args)
379
384
380 def shownode(repo, ctx, templ, **args):
385 def shownode(repo, ctx, templ, **args):
381 """:node: String. The changeset identification hash, as a 40 hexadecimal
386 """:node: String. The changeset identification hash, as a 40 hexadecimal
382 digit string.
387 digit string.
383 """
388 """
384 return ctx.hex()
389 return ctx.hex()
385
390
386 def showp1rev(repo, ctx, templ, **args):
391 def showp1rev(repo, ctx, templ, **args):
387 """:p1rev: Integer. The repository-local revision number of the changeset's
392 """:p1rev: Integer. The repository-local revision number of the changeset's
388 first parent, or -1 if the changeset has no parents."""
393 first parent, or -1 if the changeset has no parents."""
389 return ctx.p1().rev()
394 return ctx.p1().rev()
390
395
391 def showp2rev(repo, ctx, templ, **args):
396 def showp2rev(repo, ctx, templ, **args):
392 """:p2rev: Integer. The repository-local revision number of the changeset's
397 """:p2rev: Integer. The repository-local revision number of the changeset's
393 second parent, or -1 if the changeset has no second parent."""
398 second parent, or -1 if the changeset has no second parent."""
394 return ctx.p2().rev()
399 return ctx.p2().rev()
395
400
396 def showp1node(repo, ctx, templ, **args):
401 def showp1node(repo, ctx, templ, **args):
397 """:p1node: String. The identification hash of the changeset's first parent,
402 """:p1node: String. The identification hash of the changeset's first parent,
398 as a 40 digit hexadecimal string. If the changeset has no parents, all
403 as a 40 digit hexadecimal string. If the changeset has no parents, all
399 digits are 0."""
404 digits are 0."""
400 return ctx.p1().hex()
405 return ctx.p1().hex()
401
406
402 def showp2node(repo, ctx, templ, **args):
407 def showp2node(repo, ctx, templ, **args):
403 """:p2node: String. The identification hash of the changeset's second
408 """:p2node: String. The identification hash of the changeset's second
404 parent, as a 40 digit hexadecimal string. If the changeset has no second
409 parent, as a 40 digit hexadecimal string. If the changeset has no second
405 parent, all digits are 0."""
410 parent, all digits are 0."""
406 return ctx.p2().hex()
411 return ctx.p2().hex()
407
412
408 def showparents(**args):
413 def showparents(**args):
409 """:parents: List of strings. The parents of the changeset in "rev:node"
414 """:parents: List of strings. The parents of the changeset in "rev:node"
410 format. If the changeset has only one "natural" parent (the predecessor
415 format. If the changeset has only one "natural" parent (the predecessor
411 revision) nothing is shown."""
416 revision) nothing is shown."""
412 repo = args['repo']
417 repo = args['repo']
413 ctx = args['ctx']
418 ctx = args['ctx']
414 parents = [[('rev', p.rev()),
419 parents = [[('rev', p.rev()),
415 ('node', p.hex()),
420 ('node', p.hex()),
416 ('phase', p.phasestr())]
421 ('phase', p.phasestr())]
417 for p in scmutil.meaningfulparents(repo, ctx)]
422 for p in scmutil.meaningfulparents(repo, ctx)]
418 return showlist('parent', parents, **args)
423 return showlist('parent', parents, **args)
419
424
420 def showphase(repo, ctx, templ, **args):
425 def showphase(repo, ctx, templ, **args):
421 """:phase: String. The changeset phase name."""
426 """:phase: String. The changeset phase name."""
422 return ctx.phasestr()
427 return ctx.phasestr()
423
428
424 def showphaseidx(repo, ctx, templ, **args):
429 def showphaseidx(repo, ctx, templ, **args):
425 """:phaseidx: Integer. The changeset phase index."""
430 """:phaseidx: Integer. The changeset phase index."""
426 return ctx.phase()
431 return ctx.phase()
427
432
428 def showrev(repo, ctx, templ, **args):
433 def showrev(repo, ctx, templ, **args):
429 """:rev: Integer. The repository-local changeset revision number."""
434 """:rev: Integer. The repository-local changeset revision number."""
430 return scmutil.intrev(ctx.rev())
435 return scmutil.intrev(ctx.rev())
431
436
432 def showrevslist(name, revs, **args):
437 def showrevslist(name, revs, **args):
433 """helper to generate a list of revisions in which a mapped template will
438 """helper to generate a list of revisions in which a mapped template will
434 be evaluated"""
439 be evaluated"""
435 repo = args['ctx'].repo()
440 repo = args['ctx'].repo()
436 f = _showlist(name, revs, **args)
441 f = _showlist(name, revs, **args)
437 return _hybrid(f, revs,
442 return _hybrid(f, revs,
438 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
443 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
439
444
440 def showsubrepos(**args):
445 def showsubrepos(**args):
441 """:subrepos: List of strings. Updated subrepositories in the changeset."""
446 """:subrepos: List of strings. Updated subrepositories in the changeset."""
442 ctx = args['ctx']
447 ctx = args['ctx']
443 substate = ctx.substate
448 substate = ctx.substate
444 if not substate:
449 if not substate:
445 return showlist('subrepo', [], **args)
450 return showlist('subrepo', [], **args)
446 psubstate = ctx.parents()[0].substate or {}
451 psubstate = ctx.parents()[0].substate or {}
447 subrepos = []
452 subrepos = []
448 for sub in substate:
453 for sub in substate:
449 if sub not in psubstate or substate[sub] != psubstate[sub]:
454 if sub not in psubstate or substate[sub] != psubstate[sub]:
450 subrepos.append(sub) # modified or newly added in ctx
455 subrepos.append(sub) # modified or newly added in ctx
451 for sub in psubstate:
456 for sub in psubstate:
452 if sub not in substate:
457 if sub not in substate:
453 subrepos.append(sub) # removed in ctx
458 subrepos.append(sub) # removed in ctx
454 return showlist('subrepo', sorted(subrepos), **args)
459 return showlist('subrepo', sorted(subrepos), **args)
455
460
456 def shownames(namespace, **args):
461 def shownames(namespace, **args):
457 """helper method to generate a template keyword for a namespace"""
462 """helper method to generate a template keyword for a namespace"""
458 ctx = args['ctx']
463 ctx = args['ctx']
459 repo = ctx.repo()
464 repo = ctx.repo()
460 ns = repo.names[namespace]
465 ns = repo.names[namespace]
461 names = ns.names(repo, ctx.node())
466 names = ns.names(repo, ctx.node())
462 return showlist(ns.templatename, names, plural=namespace, **args)
467 return showlist(ns.templatename, names, plural=namespace, **args)
463
468
464 # don't remove "showtags" definition, even though namespaces will put
469 # don't remove "showtags" definition, even though namespaces will put
465 # a helper function for "tags" keyword into "keywords" map automatically,
470 # a helper function for "tags" keyword into "keywords" map automatically,
466 # because online help text is built without namespaces initialization
471 # because online help text is built without namespaces initialization
467 def showtags(**args):
472 def showtags(**args):
468 """:tags: List of strings. Any tags associated with the changeset."""
473 """:tags: List of strings. Any tags associated with the changeset."""
469 return shownames('tags', **args)
474 return shownames('tags', **args)
470
475
471 # keywords are callables like:
476 # keywords are callables like:
472 # fn(repo, ctx, templ, cache, revcache, **args)
477 # fn(repo, ctx, templ, cache, revcache, **args)
473 # with:
478 # with:
474 # repo - current repository instance
479 # repo - current repository instance
475 # ctx - the changectx being displayed
480 # ctx - the changectx being displayed
476 # templ - the templater instance
481 # templ - the templater instance
477 # cache - a cache dictionary for the whole templater run
482 # cache - a cache dictionary for the whole templater run
478 # revcache - a cache dictionary for the current revision
483 # revcache - a cache dictionary for the current revision
479 keywords = {
484 keywords = {
480 'activebookmark': showactivebookmark,
485 'activebookmark': showactivebookmark,
481 'author': showauthor,
486 'author': showauthor,
482 'bisect': showbisect,
487 'bisect': showbisect,
483 'branch': showbranch,
488 'branch': showbranch,
484 'branches': showbranches,
489 'branches': showbranches,
485 'bookmarks': showbookmarks,
490 'bookmarks': showbookmarks,
486 'changessincelatesttag': showchangessincelatesttag,
491 'changessincelatesttag': showchangessincelatesttag,
487 'children': showchildren,
492 'children': showchildren,
488 # currentbookmark is deprecated
493 # currentbookmark is deprecated
489 'currentbookmark': showcurrentbookmark,
494 'currentbookmark': showcurrentbookmark,
490 'date': showdate,
495 'date': showdate,
491 'desc': showdescription,
496 'desc': showdescription,
492 'diffstat': showdiffstat,
497 'diffstat': showdiffstat,
493 'extras': showextras,
498 'extras': showextras,
494 'file_adds': showfileadds,
499 'file_adds': showfileadds,
495 'file_copies': showfilecopies,
500 'file_copies': showfilecopies,
496 'file_copies_switch': showfilecopiesswitch,
501 'file_copies_switch': showfilecopiesswitch,
497 'file_dels': showfiledels,
502 'file_dels': showfiledels,
498 'file_mods': showfilemods,
503 'file_mods': showfilemods,
499 'files': showfiles,
504 'files': showfiles,
500 'latesttag': showlatesttag,
505 'latesttag': showlatesttag,
501 'latesttagdistance': showlatesttagdistance,
506 'latesttagdistance': showlatesttagdistance,
502 'manifest': showmanifest,
507 'manifest': showmanifest,
503 'node': shownode,
508 'node': shownode,
504 'p1rev': showp1rev,
509 'p1rev': showp1rev,
505 'p1node': showp1node,
510 'p1node': showp1node,
506 'p2rev': showp2rev,
511 'p2rev': showp2rev,
507 'p2node': showp2node,
512 'p2node': showp2node,
508 'parents': showparents,
513 'parents': showparents,
509 'phase': showphase,
514 'phase': showphase,
510 'phaseidx': showphaseidx,
515 'phaseidx': showphaseidx,
511 'rev': showrev,
516 'rev': showrev,
512 'subrepos': showsubrepos,
517 'subrepos': showsubrepos,
513 'tags': showtags,
518 'tags': showtags,
514 }
519 }
515
520
516 # tell hggettext to extract docstrings from these functions:
521 # tell hggettext to extract docstrings from these functions:
517 i18nfunctions = keywords.values()
522 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now