##// END OF EJS Templates
templatekw: avoid slow creation of changectx objects in showgraphnode()...
Yuya Nishihara -
r27215:5b8da564 default
parent child Browse files
Show More
@@ -1,552 +1,554 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, nullid
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 showgraphnode(repo, ctx, **args):
343 def showgraphnode(repo, ctx, **args):
344 """:graphnode: String. The character representing the changeset node in
344 """:graphnode: String. The character representing the changeset node in
345 an ASCII revision graph"""
345 an ASCII revision graph"""
346 wpnodes = [pctx.node() for pctx in repo[None].parents()]
346 wpnodes = repo.dirstate.parents()
347 if wpnodes[1] == nullid:
348 wpnodes = wpnodes[:1]
347 if ctx.node() in wpnodes:
349 if ctx.node() in wpnodes:
348 return '@'
350 return '@'
349 elif ctx.obsolete():
351 elif ctx.obsolete():
350 return 'x'
352 return 'x'
351 elif ctx.closesbranch():
353 elif ctx.closesbranch():
352 return '_'
354 return '_'
353 else:
355 else:
354 return 'o'
356 return 'o'
355
357
356 def showlatesttag(**args):
358 def showlatesttag(**args):
357 """:latesttag: List of strings. The global tags on the most recent globally
359 """:latesttag: List of strings. The global tags on the most recent globally
358 tagged ancestor of this changeset.
360 tagged ancestor of this changeset.
359 """
361 """
360 return showlatesttags(None, **args)
362 return showlatesttags(None, **args)
361
363
362 def showlatesttags(pattern, **args):
364 def showlatesttags(pattern, **args):
363 """helper method for the latesttag keyword and function"""
365 """helper method for the latesttag keyword and function"""
364 repo, ctx = args['repo'], args['ctx']
366 repo, ctx = args['repo'], args['ctx']
365 cache = args['cache']
367 cache = args['cache']
366 latesttags = getlatesttags(repo, ctx, cache, pattern)
368 latesttags = getlatesttags(repo, ctx, cache, pattern)
367
369
368 # latesttag[0] is an implementation detail for sorting csets on different
370 # latesttag[0] is an implementation detail for sorting csets on different
369 # branches in a stable manner- it is the date the tagged cset was created,
371 # branches in a stable manner- it is the date the tagged cset was created,
370 # not the date the tag was created. Therefore it isn't made visible here.
372 # not the date the tag was created. Therefore it isn't made visible here.
371 makemap = lambda v: {
373 makemap = lambda v: {
372 'changes': _showchangessincetag,
374 'changes': _showchangessincetag,
373 'distance': latesttags[1],
375 'distance': latesttags[1],
374 'latesttag': v, # BC with {latesttag % '{latesttag}'}
376 'latesttag': v, # BC with {latesttag % '{latesttag}'}
375 'tag': v
377 'tag': v
376 }
378 }
377
379
378 tags = latesttags[2]
380 tags = latesttags[2]
379 f = _showlist('latesttag', tags, separator=':', **args)
381 f = _showlist('latesttag', tags, separator=':', **args)
380 return _hybrid(f, tags, makemap, lambda x: x['latesttag'])
382 return _hybrid(f, tags, makemap, lambda x: x['latesttag'])
381
383
382 def showlatesttagdistance(repo, ctx, templ, cache, **args):
384 def showlatesttagdistance(repo, ctx, templ, cache, **args):
383 """:latesttagdistance: Integer. Longest path to the latest tag."""
385 """:latesttagdistance: Integer. Longest path to the latest tag."""
384 return getlatesttags(repo, ctx, cache)[1]
386 return getlatesttags(repo, ctx, cache)[1]
385
387
386 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
388 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
387 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
389 """:changessincelatesttag: Integer. All ancestors not in the latest tag."""
388 latesttag = getlatesttags(repo, ctx, cache)[2][0]
390 latesttag = getlatesttags(repo, ctx, cache)[2][0]
389
391
390 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
392 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
391
393
392 def _showchangessincetag(repo, ctx, **args):
394 def _showchangessincetag(repo, ctx, **args):
393 offset = 0
395 offset = 0
394 revs = [ctx.rev()]
396 revs = [ctx.rev()]
395 tag = args['tag']
397 tag = args['tag']
396
398
397 # The only() revset doesn't currently support wdir()
399 # The only() revset doesn't currently support wdir()
398 if ctx.rev() is None:
400 if ctx.rev() is None:
399 offset = 1
401 offset = 1
400 revs = [p.rev() for p in ctx.parents()]
402 revs = [p.rev() for p in ctx.parents()]
401
403
402 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
404 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
403
405
404 def showmanifest(**args):
406 def showmanifest(**args):
405 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
407 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
406 mnode = ctx.manifestnode()
408 mnode = ctx.manifestnode()
407 if mnode is None:
409 if mnode is None:
408 # just avoid crash, we might want to use the 'ff...' hash in future
410 # just avoid crash, we might want to use the 'ff...' hash in future
409 return
411 return
410 args = args.copy()
412 args = args.copy()
411 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
413 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
412 return templ('manifest', **args)
414 return templ('manifest', **args)
413
415
414 def shownode(repo, ctx, templ, **args):
416 def shownode(repo, ctx, templ, **args):
415 """:node: String. The changeset identification hash, as a 40 hexadecimal
417 """:node: String. The changeset identification hash, as a 40 hexadecimal
416 digit string.
418 digit string.
417 """
419 """
418 return ctx.hex()
420 return ctx.hex()
419
421
420 def showp1rev(repo, ctx, templ, **args):
422 def showp1rev(repo, ctx, templ, **args):
421 """:p1rev: Integer. The repository-local revision number of the changeset's
423 """:p1rev: Integer. The repository-local revision number of the changeset's
422 first parent, or -1 if the changeset has no parents."""
424 first parent, or -1 if the changeset has no parents."""
423 return ctx.p1().rev()
425 return ctx.p1().rev()
424
426
425 def showp2rev(repo, ctx, templ, **args):
427 def showp2rev(repo, ctx, templ, **args):
426 """:p2rev: Integer. The repository-local revision number of the changeset's
428 """:p2rev: Integer. The repository-local revision number of the changeset's
427 second parent, or -1 if the changeset has no second parent."""
429 second parent, or -1 if the changeset has no second parent."""
428 return ctx.p2().rev()
430 return ctx.p2().rev()
429
431
430 def showp1node(repo, ctx, templ, **args):
432 def showp1node(repo, ctx, templ, **args):
431 """:p1node: String. The identification hash of the changeset's first parent,
433 """:p1node: String. The identification hash of the changeset's first parent,
432 as a 40 digit hexadecimal string. If the changeset has no parents, all
434 as a 40 digit hexadecimal string. If the changeset has no parents, all
433 digits are 0."""
435 digits are 0."""
434 return ctx.p1().hex()
436 return ctx.p1().hex()
435
437
436 def showp2node(repo, ctx, templ, **args):
438 def showp2node(repo, ctx, templ, **args):
437 """:p2node: String. The identification hash of the changeset's second
439 """:p2node: String. The identification hash of the changeset's second
438 parent, as a 40 digit hexadecimal string. If the changeset has no second
440 parent, as a 40 digit hexadecimal string. If the changeset has no second
439 parent, all digits are 0."""
441 parent, all digits are 0."""
440 return ctx.p2().hex()
442 return ctx.p2().hex()
441
443
442 def showparents(**args):
444 def showparents(**args):
443 """:parents: List of strings. The parents of the changeset in "rev:node"
445 """:parents: List of strings. The parents of the changeset in "rev:node"
444 format. If the changeset has only one "natural" parent (the predecessor
446 format. If the changeset has only one "natural" parent (the predecessor
445 revision) nothing is shown."""
447 revision) nothing is shown."""
446 repo = args['repo']
448 repo = args['repo']
447 ctx = args['ctx']
449 ctx = args['ctx']
448 parents = [[('rev', p.rev()),
450 parents = [[('rev', p.rev()),
449 ('node', p.hex()),
451 ('node', p.hex()),
450 ('phase', p.phasestr())]
452 ('phase', p.phasestr())]
451 for p in scmutil.meaningfulparents(repo, ctx)]
453 for p in scmutil.meaningfulparents(repo, ctx)]
452 return showlist('parent', parents, **args)
454 return showlist('parent', parents, **args)
453
455
454 def showphase(repo, ctx, templ, **args):
456 def showphase(repo, ctx, templ, **args):
455 """:phase: String. The changeset phase name."""
457 """:phase: String. The changeset phase name."""
456 return ctx.phasestr()
458 return ctx.phasestr()
457
459
458 def showphaseidx(repo, ctx, templ, **args):
460 def showphaseidx(repo, ctx, templ, **args):
459 """:phaseidx: Integer. The changeset phase index."""
461 """:phaseidx: Integer. The changeset phase index."""
460 return ctx.phase()
462 return ctx.phase()
461
463
462 def showrev(repo, ctx, templ, **args):
464 def showrev(repo, ctx, templ, **args):
463 """:rev: Integer. The repository-local changeset revision number."""
465 """:rev: Integer. The repository-local changeset revision number."""
464 return scmutil.intrev(ctx.rev())
466 return scmutil.intrev(ctx.rev())
465
467
466 def showrevslist(name, revs, **args):
468 def showrevslist(name, revs, **args):
467 """helper to generate a list of revisions in which a mapped template will
469 """helper to generate a list of revisions in which a mapped template will
468 be evaluated"""
470 be evaluated"""
469 repo = args['ctx'].repo()
471 repo = args['ctx'].repo()
470 f = _showlist(name, revs, **args)
472 f = _showlist(name, revs, **args)
471 return _hybrid(f, revs,
473 return _hybrid(f, revs,
472 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
474 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}})
473
475
474 def showsubrepos(**args):
476 def showsubrepos(**args):
475 """:subrepos: List of strings. Updated subrepositories in the changeset."""
477 """:subrepos: List of strings. Updated subrepositories in the changeset."""
476 ctx = args['ctx']
478 ctx = args['ctx']
477 substate = ctx.substate
479 substate = ctx.substate
478 if not substate:
480 if not substate:
479 return showlist('subrepo', [], **args)
481 return showlist('subrepo', [], **args)
480 psubstate = ctx.parents()[0].substate or {}
482 psubstate = ctx.parents()[0].substate or {}
481 subrepos = []
483 subrepos = []
482 for sub in substate:
484 for sub in substate:
483 if sub not in psubstate or substate[sub] != psubstate[sub]:
485 if sub not in psubstate or substate[sub] != psubstate[sub]:
484 subrepos.append(sub) # modified or newly added in ctx
486 subrepos.append(sub) # modified or newly added in ctx
485 for sub in psubstate:
487 for sub in psubstate:
486 if sub not in substate:
488 if sub not in substate:
487 subrepos.append(sub) # removed in ctx
489 subrepos.append(sub) # removed in ctx
488 return showlist('subrepo', sorted(subrepos), **args)
490 return showlist('subrepo', sorted(subrepos), **args)
489
491
490 def shownames(namespace, **args):
492 def shownames(namespace, **args):
491 """helper method to generate a template keyword for a namespace"""
493 """helper method to generate a template keyword for a namespace"""
492 ctx = args['ctx']
494 ctx = args['ctx']
493 repo = ctx.repo()
495 repo = ctx.repo()
494 ns = repo.names[namespace]
496 ns = repo.names[namespace]
495 names = ns.names(repo, ctx.node())
497 names = ns.names(repo, ctx.node())
496 return showlist(ns.templatename, names, plural=namespace, **args)
498 return showlist(ns.templatename, names, plural=namespace, **args)
497
499
498 # don't remove "showtags" definition, even though namespaces will put
500 # don't remove "showtags" definition, even though namespaces will put
499 # a helper function for "tags" keyword into "keywords" map automatically,
501 # a helper function for "tags" keyword into "keywords" map automatically,
500 # because online help text is built without namespaces initialization
502 # because online help text is built without namespaces initialization
501 def showtags(**args):
503 def showtags(**args):
502 """:tags: List of strings. Any tags associated with the changeset."""
504 """:tags: List of strings. Any tags associated with the changeset."""
503 return shownames('tags', **args)
505 return shownames('tags', **args)
504
506
505 # keywords are callables like:
507 # keywords are callables like:
506 # fn(repo, ctx, templ, cache, revcache, **args)
508 # fn(repo, ctx, templ, cache, revcache, **args)
507 # with:
509 # with:
508 # repo - current repository instance
510 # repo - current repository instance
509 # ctx - the changectx being displayed
511 # ctx - the changectx being displayed
510 # templ - the templater instance
512 # templ - the templater instance
511 # cache - a cache dictionary for the whole templater run
513 # cache - a cache dictionary for the whole templater run
512 # revcache - a cache dictionary for the current revision
514 # revcache - a cache dictionary for the current revision
513 keywords = {
515 keywords = {
514 'activebookmark': showactivebookmark,
516 'activebookmark': showactivebookmark,
515 'author': showauthor,
517 'author': showauthor,
516 'bisect': showbisect,
518 'bisect': showbisect,
517 'branch': showbranch,
519 'branch': showbranch,
518 'branches': showbranches,
520 'branches': showbranches,
519 'bookmarks': showbookmarks,
521 'bookmarks': showbookmarks,
520 'changessincelatesttag': showchangessincelatesttag,
522 'changessincelatesttag': showchangessincelatesttag,
521 'children': showchildren,
523 'children': showchildren,
522 # currentbookmark is deprecated
524 # currentbookmark is deprecated
523 'currentbookmark': showcurrentbookmark,
525 'currentbookmark': showcurrentbookmark,
524 'date': showdate,
526 'date': showdate,
525 'desc': showdescription,
527 'desc': showdescription,
526 'diffstat': showdiffstat,
528 'diffstat': showdiffstat,
527 'extras': showextras,
529 'extras': showextras,
528 'file_adds': showfileadds,
530 'file_adds': showfileadds,
529 'file_copies': showfilecopies,
531 'file_copies': showfilecopies,
530 'file_copies_switch': showfilecopiesswitch,
532 'file_copies_switch': showfilecopiesswitch,
531 'file_dels': showfiledels,
533 'file_dels': showfiledels,
532 'file_mods': showfilemods,
534 'file_mods': showfilemods,
533 'files': showfiles,
535 'files': showfiles,
534 'graphnode': showgraphnode,
536 'graphnode': showgraphnode,
535 'latesttag': showlatesttag,
537 'latesttag': showlatesttag,
536 'latesttagdistance': showlatesttagdistance,
538 'latesttagdistance': showlatesttagdistance,
537 'manifest': showmanifest,
539 'manifest': showmanifest,
538 'node': shownode,
540 'node': shownode,
539 'p1rev': showp1rev,
541 'p1rev': showp1rev,
540 'p1node': showp1node,
542 'p1node': showp1node,
541 'p2rev': showp2rev,
543 'p2rev': showp2rev,
542 'p2node': showp2node,
544 'p2node': showp2node,
543 'parents': showparents,
545 'parents': showparents,
544 'phase': showphase,
546 'phase': showphase,
545 'phaseidx': showphaseidx,
547 'phaseidx': showphaseidx,
546 'rev': showrev,
548 'rev': showrev,
547 'subrepos': showsubrepos,
549 'subrepos': showsubrepos,
548 'tags': showtags,
550 'tags': showtags,
549 }
551 }
550
552
551 # tell hggettext to extract docstrings from these functions:
553 # tell hggettext to extract docstrings from these functions:
552 i18nfunctions = keywords.values()
554 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now