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