##// END OF EJS Templates
templatekw: allow getlatesttags() to match a specific tag pattern...
Matt Harbison -
r26482:d2e69584 default
parent child Browse files
Show More
@@ -1,509 +1,517 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):
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 if 'latesttags' not in cache:
128 cachename = 'latesttags'
129 if pattern is not None:
130 cachename += '-' + pattern
131 match = util.stringmatcher(pattern)[2]
132 else:
133 match = util.always
134
135 if cachename not in cache:
129 # Cache mapping from rev to a tuple with tag date, tag
136 # Cache mapping from rev to a tuple with tag date, tag
130 # distance and tag name
137 # distance and tag name
131 cache['latesttags'] = {-1: (0, 0, ['null'])}
138 cache[cachename] = {-1: (0, 0, ['null'])}
132 latesttags = cache['latesttags']
139 latesttags = cache[cachename]
133
140
134 rev = ctx.rev()
141 rev = ctx.rev()
135 todo = [rev]
142 todo = [rev]
136 while todo:
143 while todo:
137 rev = todo.pop()
144 rev = todo.pop()
138 if rev in latesttags:
145 if rev in latesttags:
139 continue
146 continue
140 ctx = repo[rev]
147 ctx = repo[rev]
141 tags = [t for t in ctx.tags()
148 tags = [t for t in ctx.tags()
142 if (repo.tagtype(t) and repo.tagtype(t) != 'local')]
149 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
150 and match(t))]
143 if tags:
151 if tags:
144 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)]
145 continue
153 continue
146 try:
154 try:
147 # 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
148 # comparison.
156 # comparison.
149 pdate, pdist, ptag = max(
157 pdate, pdist, ptag = max(
150 latesttags[p.rev()] for p in ctx.parents())
158 latesttags[p.rev()] for p in ctx.parents())
151 except KeyError:
159 except KeyError:
152 # Cache miss - recurse
160 # Cache miss - recurse
153 todo.append(rev)
161 todo.append(rev)
154 todo.extend(p.rev() for p in ctx.parents())
162 todo.extend(p.rev() for p in ctx.parents())
155 continue
163 continue
156 latesttags[rev] = pdate, pdist + 1, ptag
164 latesttags[rev] = pdate, pdist + 1, ptag
157 return latesttags[rev]
165 return latesttags[rev]
158
166
159 def getrenamedfn(repo, endrev=None):
167 def getrenamedfn(repo, endrev=None):
160 rcache = {}
168 rcache = {}
161 if endrev is None:
169 if endrev is None:
162 endrev = len(repo)
170 endrev = len(repo)
163
171
164 def getrenamed(fn, rev):
172 def getrenamed(fn, rev):
165 '''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
166 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
167 parses the manifest if linkrev != changerev.
175 parses the manifest if linkrev != changerev.
168 Returns rename info for fn at changerev rev.'''
176 Returns rename info for fn at changerev rev.'''
169 if fn not in rcache:
177 if fn not in rcache:
170 rcache[fn] = {}
178 rcache[fn] = {}
171 fl = repo.file(fn)
179 fl = repo.file(fn)
172 for i in fl:
180 for i in fl:
173 lr = fl.linkrev(i)
181 lr = fl.linkrev(i)
174 renamed = fl.renamed(fl.node(i))
182 renamed = fl.renamed(fl.node(i))
175 rcache[fn][lr] = renamed
183 rcache[fn][lr] = renamed
176 if lr >= endrev:
184 if lr >= endrev:
177 break
185 break
178 if rev in rcache[fn]:
186 if rev in rcache[fn]:
179 return rcache[fn][rev]
187 return rcache[fn][rev]
180
188
181 # 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
182 # filectx logic.
190 # filectx logic.
183 try:
191 try:
184 return repo[rev][fn].renamed()
192 return repo[rev][fn].renamed()
185 except error.LookupError:
193 except error.LookupError:
186 return None
194 return None
187
195
188 return getrenamed
196 return getrenamed
189
197
190
198
191 def showauthor(repo, ctx, templ, **args):
199 def showauthor(repo, ctx, templ, **args):
192 """:author: String. The unmodified author of the changeset."""
200 """:author: String. The unmodified author of the changeset."""
193 return ctx.user()
201 return ctx.user()
194
202
195 def showbisect(repo, ctx, templ, **args):
203 def showbisect(repo, ctx, templ, **args):
196 """:bisect: String. The changeset bisection status."""
204 """:bisect: String. The changeset bisection status."""
197 return hbisect.label(repo, ctx.node())
205 return hbisect.label(repo, ctx.node())
198
206
199 def showbranch(**args):
207 def showbranch(**args):
200 """: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
201 committed.
209 committed.
202 """
210 """
203 return args['ctx'].branch()
211 return args['ctx'].branch()
204
212
205 def showbranches(**args):
213 def showbranches(**args):
206 """: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
207 changeset was committed. Will be empty if the branch name was
215 changeset was committed. Will be empty if the branch name was
208 default. (DEPRECATED)
216 default. (DEPRECATED)
209 """
217 """
210 branch = args['ctx'].branch()
218 branch = args['ctx'].branch()
211 if branch != 'default':
219 if branch != 'default':
212 return showlist('branch', [branch], plural='branches', **args)
220 return showlist('branch', [branch], plural='branches', **args)
213 return showlist('branch', [], plural='branches', **args)
221 return showlist('branch', [], plural='branches', **args)
214
222
215 def showbookmarks(**args):
223 def showbookmarks(**args):
216 """:bookmarks: List of strings. Any bookmarks associated with the
224 """:bookmarks: List of strings. Any bookmarks associated with the
217 changeset. Also sets 'active', the name of the active bookmark.
225 changeset. Also sets 'active', the name of the active bookmark.
218 """
226 """
219 repo = args['ctx']._repo
227 repo = args['ctx']._repo
220 bookmarks = args['ctx'].bookmarks()
228 bookmarks = args['ctx'].bookmarks()
221 active = repo._activebookmark
229 active = repo._activebookmark
222 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
230 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
223 f = _showlist('bookmark', bookmarks, **args)
231 f = _showlist('bookmark', bookmarks, **args)
224 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
232 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
225
233
226 def showchildren(**args):
234 def showchildren(**args):
227 """:children: List of strings. The children of the changeset."""
235 """:children: List of strings. The children of the changeset."""
228 ctx = args['ctx']
236 ctx = args['ctx']
229 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
237 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
230 return showlist('children', childrevs, element='child', **args)
238 return showlist('children', childrevs, element='child', **args)
231
239
232 # Deprecated, but kept alive for help generation a purpose.
240 # Deprecated, but kept alive for help generation a purpose.
233 def showcurrentbookmark(**args):
241 def showcurrentbookmark(**args):
234 """:currentbookmark: String. The active bookmark, if it is
242 """:currentbookmark: String. The active bookmark, if it is
235 associated with the changeset (DEPRECATED)"""
243 associated with the changeset (DEPRECATED)"""
236 return showactivebookmark(**args)
244 return showactivebookmark(**args)
237
245
238 def showactivebookmark(**args):
246 def showactivebookmark(**args):
239 """:activebookmark: String. The active bookmark, if it is
247 """:activebookmark: String. The active bookmark, if it is
240 associated with the changeset"""
248 associated with the changeset"""
241 active = args['repo']._activebookmark
249 active = args['repo']._activebookmark
242 if active and active in args['ctx'].bookmarks():
250 if active and active in args['ctx'].bookmarks():
243 return active
251 return active
244 return ''
252 return ''
245
253
246 def showdate(repo, ctx, templ, **args):
254 def showdate(repo, ctx, templ, **args):
247 """:date: Date information. The date when the changeset was committed."""
255 """:date: Date information. The date when the changeset was committed."""
248 return ctx.date()
256 return ctx.date()
249
257
250 def showdescription(repo, ctx, templ, **args):
258 def showdescription(repo, ctx, templ, **args):
251 """:desc: String. The text of the changeset description."""
259 """:desc: String. The text of the changeset description."""
252 return ctx.description().strip()
260 return ctx.description().strip()
253
261
254 def showdiffstat(repo, ctx, templ, **args):
262 def showdiffstat(repo, ctx, templ, **args):
255 """:diffstat: String. Statistics of changes with the following format:
263 """:diffstat: String. Statistics of changes with the following format:
256 "modified files: +added/-removed lines"
264 "modified files: +added/-removed lines"
257 """
265 """
258 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
266 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
259 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
267 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
260 return '%s: +%s/-%s' % (len(stats), adds, removes)
268 return '%s: +%s/-%s' % (len(stats), adds, removes)
261
269
262 def showextras(**args):
270 def showextras(**args):
263 """:extras: List of dicts with key, value entries of the 'extras'
271 """:extras: List of dicts with key, value entries of the 'extras'
264 field of this changeset."""
272 field of this changeset."""
265 extras = args['ctx'].extra()
273 extras = args['ctx'].extra()
266 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
274 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
267 makemap = lambda k: {'key': k, 'value': extras[k]}
275 makemap = lambda k: {'key': k, 'value': extras[k]}
268 c = [makemap(k) for k in extras]
276 c = [makemap(k) for k in extras]
269 f = _showlist('extra', c, plural='extras', **args)
277 f = _showlist('extra', c, plural='extras', **args)
270 return _hybrid(f, extras, makemap,
278 return _hybrid(f, extras, makemap,
271 lambda x: '%s=%s' % (x['key'], x['value']))
279 lambda x: '%s=%s' % (x['key'], x['value']))
272
280
273 def showfileadds(**args):
281 def showfileadds(**args):
274 """:file_adds: List of strings. Files added by this changeset."""
282 """:file_adds: List of strings. Files added by this changeset."""
275 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
283 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
276 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
284 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
277 element='file', **args)
285 element='file', **args)
278
286
279 def showfilecopies(**args):
287 def showfilecopies(**args):
280 """:file_copies: List of strings. Files copied in this changeset with
288 """:file_copies: List of strings. Files copied in this changeset with
281 their sources.
289 their sources.
282 """
290 """
283 cache, ctx = args['cache'], args['ctx']
291 cache, ctx = args['cache'], args['ctx']
284 copies = args['revcache'].get('copies')
292 copies = args['revcache'].get('copies')
285 if copies is None:
293 if copies is None:
286 if 'getrenamed' not in cache:
294 if 'getrenamed' not in cache:
287 cache['getrenamed'] = getrenamedfn(args['repo'])
295 cache['getrenamed'] = getrenamedfn(args['repo'])
288 copies = []
296 copies = []
289 getrenamed = cache['getrenamed']
297 getrenamed = cache['getrenamed']
290 for fn in ctx.files():
298 for fn in ctx.files():
291 rename = getrenamed(fn, ctx.rev())
299 rename = getrenamed(fn, ctx.rev())
292 if rename:
300 if rename:
293 copies.append((fn, rename[0]))
301 copies.append((fn, rename[0]))
294
302
295 copies = util.sortdict(copies)
303 copies = util.sortdict(copies)
296 makemap = lambda k: {'name': k, 'source': copies[k]}
304 makemap = lambda k: {'name': k, 'source': copies[k]}
297 c = [makemap(k) for k in copies]
305 c = [makemap(k) for k in copies]
298 f = _showlist('file_copy', c, plural='file_copies', **args)
306 f = _showlist('file_copy', c, plural='file_copies', **args)
299 return _hybrid(f, copies, makemap,
307 return _hybrid(f, copies, makemap,
300 lambda x: '%s (%s)' % (x['name'], x['source']))
308 lambda x: '%s (%s)' % (x['name'], x['source']))
301
309
302 # showfilecopiesswitch() displays file copies only if copy records are
310 # showfilecopiesswitch() displays file copies only if copy records are
303 # provided before calling the templater, usually with a --copies
311 # provided before calling the templater, usually with a --copies
304 # command line switch.
312 # command line switch.
305 def showfilecopiesswitch(**args):
313 def showfilecopiesswitch(**args):
306 """:file_copies_switch: List of strings. Like "file_copies" but displayed
314 """:file_copies_switch: List of strings. Like "file_copies" but displayed
307 only if the --copied switch is set.
315 only if the --copied switch is set.
308 """
316 """
309 copies = args['revcache'].get('copies') or []
317 copies = args['revcache'].get('copies') or []
310 copies = util.sortdict(copies)
318 copies = util.sortdict(copies)
311 makemap = lambda k: {'name': k, 'source': copies[k]}
319 makemap = lambda k: {'name': k, 'source': copies[k]}
312 c = [makemap(k) for k in copies]
320 c = [makemap(k) for k in copies]
313 f = _showlist('file_copy', c, plural='file_copies', **args)
321 f = _showlist('file_copy', c, plural='file_copies', **args)
314 return _hybrid(f, copies, makemap,
322 return _hybrid(f, copies, makemap,
315 lambda x: '%s (%s)' % (x['name'], x['source']))
323 lambda x: '%s (%s)' % (x['name'], x['source']))
316
324
317 def showfiledels(**args):
325 def showfiledels(**args):
318 """:file_dels: List of strings. Files removed by this changeset."""
326 """:file_dels: List of strings. Files removed by this changeset."""
319 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
327 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
320 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
328 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
321 element='file', **args)
329 element='file', **args)
322
330
323 def showfilemods(**args):
331 def showfilemods(**args):
324 """:file_mods: List of strings. Files modified by this changeset."""
332 """:file_mods: List of strings. Files modified by this changeset."""
325 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
333 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
326 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
334 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
327 element='file', **args)
335 element='file', **args)
328
336
329 def showfiles(**args):
337 def showfiles(**args):
330 """: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
331 changeset.
339 changeset.
332 """
340 """
333 return showlist('file', args['ctx'].files(), **args)
341 return showlist('file', args['ctx'].files(), **args)
334
342
335 def showlatesttag(**args):
343 def showlatesttag(**args):
336 """: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
337 tagged ancestor of this changeset.
345 tagged ancestor of this changeset.
338 """
346 """
339 repo, ctx = args['repo'], args['ctx']
347 repo, ctx = args['repo'], args['ctx']
340 cache = args['cache']
348 cache = args['cache']
341 latesttags = getlatesttags(repo, ctx, cache)[2]
349 latesttags = getlatesttags(repo, ctx, cache)[2]
342
350
343 return showlist('latesttag', latesttags, separator=':', **args)
351 return showlist('latesttag', latesttags, separator=':', **args)
344
352
345 def showlatesttagdistance(repo, ctx, templ, cache, **args):
353 def showlatesttagdistance(repo, ctx, templ, cache, **args):
346 """:latesttagdistance: Integer. Longest path to the latest tag."""
354 """:latesttagdistance: Integer. Longest path to the latest tag."""
347 return getlatesttags(repo, ctx, cache)[1]
355 return getlatesttags(repo, ctx, cache)[1]
348
356
349 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
357 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
350 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
358 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
351 latesttag = getlatesttags(repo, ctx, cache)[2][0]
359 latesttag = getlatesttags(repo, ctx, cache)[2][0]
352 offset = 0
360 offset = 0
353 revs = [ctx.rev()]
361 revs = [ctx.rev()]
354
362
355 # The only() revset doesn't currently support wdir()
363 # The only() revset doesn't currently support wdir()
356 if ctx.rev() is None:
364 if ctx.rev() is None:
357 offset = 1
365 offset = 1
358 revs = [p.rev() for p in ctx.parents()]
366 revs = [p.rev() for p in ctx.parents()]
359
367
360 return len(repo.revs('only(%ld, %s)', revs, latesttag)) + offset
368 return len(repo.revs('only(%ld, %s)', revs, latesttag)) + offset
361
369
362 def showmanifest(**args):
370 def showmanifest(**args):
363 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
371 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
364 mnode = ctx.manifestnode()
372 mnode = ctx.manifestnode()
365 if mnode is None:
373 if mnode is None:
366 # just avoid crash, we might want to use the 'ff...' hash in future
374 # just avoid crash, we might want to use the 'ff...' hash in future
367 return
375 return
368 args = args.copy()
376 args = args.copy()
369 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
377 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
370 return templ('manifest', **args)
378 return templ('manifest', **args)
371
379
372 def shownode(repo, ctx, templ, **args):
380 def shownode(repo, ctx, templ, **args):
373 """:node: String. The changeset identification hash, as a 40 hexadecimal
381 """:node: String. The changeset identification hash, as a 40 hexadecimal
374 digit string.
382 digit string.
375 """
383 """
376 return ctx.hex()
384 return ctx.hex()
377
385
378 def showp1rev(repo, ctx, templ, **args):
386 def showp1rev(repo, ctx, templ, **args):
379 """:p1rev: Integer. The repository-local revision number of the changeset's
387 """:p1rev: Integer. The repository-local revision number of the changeset's
380 first parent, or -1 if the changeset has no parents."""
388 first parent, or -1 if the changeset has no parents."""
381 return ctx.p1().rev()
389 return ctx.p1().rev()
382
390
383 def showp2rev(repo, ctx, templ, **args):
391 def showp2rev(repo, ctx, templ, **args):
384 """:p2rev: Integer. The repository-local revision number of the changeset's
392 """:p2rev: Integer. The repository-local revision number of the changeset's
385 second parent, or -1 if the changeset has no second parent."""
393 second parent, or -1 if the changeset has no second parent."""
386 return ctx.p2().rev()
394 return ctx.p2().rev()
387
395
388 def showp1node(repo, ctx, templ, **args):
396 def showp1node(repo, ctx, templ, **args):
389 """:p1node: String. The identification hash of the changeset's first parent,
397 """:p1node: String. The identification hash of the changeset's first parent,
390 as a 40 digit hexadecimal string. If the changeset has no parents, all
398 as a 40 digit hexadecimal string. If the changeset has no parents, all
391 digits are 0."""
399 digits are 0."""
392 return ctx.p1().hex()
400 return ctx.p1().hex()
393
401
394 def showp2node(repo, ctx, templ, **args):
402 def showp2node(repo, ctx, templ, **args):
395 """:p2node: String. The identification hash of the changeset's second
403 """:p2node: String. The identification hash of the changeset's second
396 parent, as a 40 digit hexadecimal string. If the changeset has no second
404 parent, as a 40 digit hexadecimal string. If the changeset has no second
397 parent, all digits are 0."""
405 parent, all digits are 0."""
398 return ctx.p2().hex()
406 return ctx.p2().hex()
399
407
400 def showparents(**args):
408 def showparents(**args):
401 """:parents: List of strings. The parents of the changeset in "rev:node"
409 """:parents: List of strings. The parents of the changeset in "rev:node"
402 format. If the changeset has only one "natural" parent (the predecessor
410 format. If the changeset has only one "natural" parent (the predecessor
403 revision) nothing is shown."""
411 revision) nothing is shown."""
404 repo = args['repo']
412 repo = args['repo']
405 ctx = args['ctx']
413 ctx = args['ctx']
406 parents = [[('rev', p.rev()),
414 parents = [[('rev', p.rev()),
407 ('node', p.hex()),
415 ('node', p.hex()),
408 ('phase', p.phasestr())]
416 ('phase', p.phasestr())]
409 for p in scmutil.meaningfulparents(repo, ctx)]
417 for p in scmutil.meaningfulparents(repo, ctx)]
410 return showlist('parent', parents, **args)
418 return showlist('parent', parents, **args)
411
419
412 def showphase(repo, ctx, templ, **args):
420 def showphase(repo, ctx, templ, **args):
413 """:phase: String. The changeset phase name."""
421 """:phase: String. The changeset phase name."""
414 return ctx.phasestr()
422 return ctx.phasestr()
415
423
416 def showphaseidx(repo, ctx, templ, **args):
424 def showphaseidx(repo, ctx, templ, **args):
417 """:phaseidx: Integer. The changeset phase index."""
425 """:phaseidx: Integer. The changeset phase index."""
418 return ctx.phase()
426 return ctx.phase()
419
427
420 def showrev(repo, ctx, templ, **args):
428 def showrev(repo, ctx, templ, **args):
421 """:rev: Integer. The repository-local changeset revision number."""
429 """:rev: Integer. The repository-local changeset revision number."""
422 return scmutil.intrev(ctx.rev())
430 return scmutil.intrev(ctx.rev())
423
431
424 def showrevslist(name, revs, **args):
432 def showrevslist(name, revs, **args):
425 """helper to generate a list of revisions in which a mapped template will
433 """helper to generate a list of revisions in which a mapped template will
426 be evaluated"""
434 be evaluated"""
427 repo = args['ctx'].repo()
435 repo = args['ctx'].repo()
428 f = _showlist(name, revs, **args)
436 f = _showlist(name, revs, **args)
429 return _hybrid(f, revs,
437 return _hybrid(f, revs,
430 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
438 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
431
439
432 def showsubrepos(**args):
440 def showsubrepos(**args):
433 """:subrepos: List of strings. Updated subrepositories in the changeset."""
441 """:subrepos: List of strings. Updated subrepositories in the changeset."""
434 ctx = args['ctx']
442 ctx = args['ctx']
435 substate = ctx.substate
443 substate = ctx.substate
436 if not substate:
444 if not substate:
437 return showlist('subrepo', [], **args)
445 return showlist('subrepo', [], **args)
438 psubstate = ctx.parents()[0].substate or {}
446 psubstate = ctx.parents()[0].substate or {}
439 subrepos = []
447 subrepos = []
440 for sub in substate:
448 for sub in substate:
441 if sub not in psubstate or substate[sub] != psubstate[sub]:
449 if sub not in psubstate or substate[sub] != psubstate[sub]:
442 subrepos.append(sub) # modified or newly added in ctx
450 subrepos.append(sub) # modified or newly added in ctx
443 for sub in psubstate:
451 for sub in psubstate:
444 if sub not in substate:
452 if sub not in substate:
445 subrepos.append(sub) # removed in ctx
453 subrepos.append(sub) # removed in ctx
446 return showlist('subrepo', sorted(subrepos), **args)
454 return showlist('subrepo', sorted(subrepos), **args)
447
455
448 def shownames(namespace, **args):
456 def shownames(namespace, **args):
449 """helper method to generate a template keyword for a namespace"""
457 """helper method to generate a template keyword for a namespace"""
450 ctx = args['ctx']
458 ctx = args['ctx']
451 repo = ctx.repo()
459 repo = ctx.repo()
452 ns = repo.names[namespace]
460 ns = repo.names[namespace]
453 names = ns.names(repo, ctx.node())
461 names = ns.names(repo, ctx.node())
454 return showlist(ns.templatename, names, plural=namespace, **args)
462 return showlist(ns.templatename, names, plural=namespace, **args)
455
463
456 # don't remove "showtags" definition, even though namespaces will put
464 # don't remove "showtags" definition, even though namespaces will put
457 # a helper function for "tags" keyword into "keywords" map automatically,
465 # a helper function for "tags" keyword into "keywords" map automatically,
458 # because online help text is built without namespaces initialization
466 # because online help text is built without namespaces initialization
459 def showtags(**args):
467 def showtags(**args):
460 """:tags: List of strings. Any tags associated with the changeset."""
468 """:tags: List of strings. Any tags associated with the changeset."""
461 return shownames('tags', **args)
469 return shownames('tags', **args)
462
470
463 # keywords are callables like:
471 # keywords are callables like:
464 # fn(repo, ctx, templ, cache, revcache, **args)
472 # fn(repo, ctx, templ, cache, revcache, **args)
465 # with:
473 # with:
466 # repo - current repository instance
474 # repo - current repository instance
467 # ctx - the changectx being displayed
475 # ctx - the changectx being displayed
468 # templ - the templater instance
476 # templ - the templater instance
469 # cache - a cache dictionary for the whole templater run
477 # cache - a cache dictionary for the whole templater run
470 # revcache - a cache dictionary for the current revision
478 # revcache - a cache dictionary for the current revision
471 keywords = {
479 keywords = {
472 'activebookmark': showactivebookmark,
480 'activebookmark': showactivebookmark,
473 'author': showauthor,
481 'author': showauthor,
474 'bisect': showbisect,
482 'bisect': showbisect,
475 'branch': showbranch,
483 'branch': showbranch,
476 'branches': showbranches,
484 'branches': showbranches,
477 'bookmarks': showbookmarks,
485 'bookmarks': showbookmarks,
478 'changessincelatesttag': showchangessincelatesttag,
486 'changessincelatesttag': showchangessincelatesttag,
479 'children': showchildren,
487 'children': showchildren,
480 # currentbookmark is deprecated
488 # currentbookmark is deprecated
481 'currentbookmark': showcurrentbookmark,
489 'currentbookmark': showcurrentbookmark,
482 'date': showdate,
490 'date': showdate,
483 'desc': showdescription,
491 'desc': showdescription,
484 'diffstat': showdiffstat,
492 'diffstat': showdiffstat,
485 'extras': showextras,
493 'extras': showextras,
486 'file_adds': showfileadds,
494 'file_adds': showfileadds,
487 'file_copies': showfilecopies,
495 'file_copies': showfilecopies,
488 'file_copies_switch': showfilecopiesswitch,
496 'file_copies_switch': showfilecopiesswitch,
489 'file_dels': showfiledels,
497 'file_dels': showfiledels,
490 'file_mods': showfilemods,
498 'file_mods': showfilemods,
491 'files': showfiles,
499 'files': showfiles,
492 'latesttag': showlatesttag,
500 'latesttag': showlatesttag,
493 'latesttagdistance': showlatesttagdistance,
501 'latesttagdistance': showlatesttagdistance,
494 'manifest': showmanifest,
502 'manifest': showmanifest,
495 'node': shownode,
503 'node': shownode,
496 'p1rev': showp1rev,
504 'p1rev': showp1rev,
497 'p1node': showp1node,
505 'p1node': showp1node,
498 'p2rev': showp2rev,
506 'p2rev': showp2rev,
499 'p2node': showp2node,
507 'p2node': showp2node,
500 'parents': showparents,
508 'parents': showparents,
501 'phase': showphase,
509 'phase': showphase,
502 'phaseidx': showphaseidx,
510 'phaseidx': showphaseidx,
503 'rev': showrev,
511 'rev': showrev,
504 'subrepos': showsubrepos,
512 'subrepos': showsubrepos,
505 'tags': showtags,
513 'tags': showtags,
506 }
514 }
507
515
508 # tell hggettext to extract docstrings from these functions:
516 # tell hggettext to extract docstrings from these functions:
509 i18nfunctions = keywords.values()
517 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now