##// END OF EJS Templates
phases: add a phase template keyword
Pierre-Yves David -
r15422:042e11c4 default
parent child Browse files
Show More
@@ -1,320 +1,325 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 node import hex
9 import patch, util, error
9 import patch, util, error
10 import hbisect
10 import hbisect
11
11
12 def showlist(name, values, plural=None, **args):
12 def showlist(name, values, plural=None, **args):
13 '''expand set of values.
13 '''expand set of values.
14 name is name of key in template map.
14 name is name of key in template map.
15 values is list of strings or dicts.
15 values is list of strings or dicts.
16 plural is plural of name, if not simply name + 's'.
16 plural is plural of name, if not simply name + 's'.
17
17
18 expansion works like this, given name 'foo'.
18 expansion works like this, given name 'foo'.
19
19
20 if values is empty, expand 'no_foos'.
20 if values is empty, expand 'no_foos'.
21
21
22 if 'foo' not in template map, return values as a string,
22 if 'foo' not in template map, return values as a string,
23 joined by space.
23 joined by space.
24
24
25 expand 'start_foos'.
25 expand 'start_foos'.
26
26
27 for each value, expand 'foo'. if 'last_foo' in template
27 for each value, expand 'foo'. if 'last_foo' in template
28 map, expand it instead of 'foo' for last key.
28 map, expand it instead of 'foo' for last key.
29
29
30 expand 'end_foos'.
30 expand 'end_foos'.
31 '''
31 '''
32 templ = args['templ']
32 templ = args['templ']
33 if plural:
33 if plural:
34 names = plural
34 names = plural
35 else: names = name + 's'
35 else: names = name + 's'
36 if not values:
36 if not values:
37 noname = 'no_' + names
37 noname = 'no_' + names
38 if noname in templ:
38 if noname in templ:
39 yield templ(noname, **args)
39 yield templ(noname, **args)
40 return
40 return
41 if name not in templ:
41 if name not in templ:
42 if isinstance(values[0], str):
42 if isinstance(values[0], str):
43 yield ' '.join(values)
43 yield ' '.join(values)
44 else:
44 else:
45 for v in values:
45 for v in values:
46 yield dict(v, **args)
46 yield dict(v, **args)
47 return
47 return
48 startname = 'start_' + names
48 startname = 'start_' + names
49 if startname in templ:
49 if startname in templ:
50 yield templ(startname, **args)
50 yield templ(startname, **args)
51 vargs = args.copy()
51 vargs = args.copy()
52 def one(v, tag=name):
52 def one(v, tag=name):
53 try:
53 try:
54 vargs.update(v)
54 vargs.update(v)
55 except (AttributeError, ValueError):
55 except (AttributeError, ValueError):
56 try:
56 try:
57 for a, b in v:
57 for a, b in v:
58 vargs[a] = b
58 vargs[a] = b
59 except ValueError:
59 except ValueError:
60 vargs[name] = v
60 vargs[name] = v
61 return templ(tag, **vargs)
61 return templ(tag, **vargs)
62 lastname = 'last_' + name
62 lastname = 'last_' + name
63 if lastname in templ:
63 if lastname in templ:
64 last = values.pop()
64 last = values.pop()
65 else:
65 else:
66 last = None
66 last = None
67 for v in values:
67 for v in values:
68 yield one(v)
68 yield one(v)
69 if last is not None:
69 if last is not None:
70 yield one(last, tag=lastname)
70 yield one(last, tag=lastname)
71 endname = 'end_' + names
71 endname = 'end_' + names
72 if endname in templ:
72 if endname in templ:
73 yield templ(endname, **args)
73 yield templ(endname, **args)
74
74
75 def getfiles(repo, ctx, revcache):
75 def getfiles(repo, ctx, revcache):
76 if 'files' not in revcache:
76 if 'files' not in revcache:
77 revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
77 revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
78 return revcache['files']
78 return revcache['files']
79
79
80 def getlatesttags(repo, ctx, cache):
80 def getlatesttags(repo, ctx, cache):
81 '''return date, distance and name for the latest tag of rev'''
81 '''return date, distance and name for the latest tag of rev'''
82
82
83 if 'latesttags' not in cache:
83 if 'latesttags' not in cache:
84 # Cache mapping from rev to a tuple with tag date, tag
84 # Cache mapping from rev to a tuple with tag date, tag
85 # distance and tag name
85 # distance and tag name
86 cache['latesttags'] = {-1: (0, 0, 'null')}
86 cache['latesttags'] = {-1: (0, 0, 'null')}
87 latesttags = cache['latesttags']
87 latesttags = cache['latesttags']
88
88
89 rev = ctx.rev()
89 rev = ctx.rev()
90 todo = [rev]
90 todo = [rev]
91 while todo:
91 while todo:
92 rev = todo.pop()
92 rev = todo.pop()
93 if rev in latesttags:
93 if rev in latesttags:
94 continue
94 continue
95 ctx = repo[rev]
95 ctx = repo[rev]
96 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
96 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
97 if tags:
97 if tags:
98 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
98 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
99 continue
99 continue
100 try:
100 try:
101 # The tuples are laid out so the right one can be found by
101 # The tuples are laid out so the right one can be found by
102 # comparison.
102 # comparison.
103 pdate, pdist, ptag = max(
103 pdate, pdist, ptag = max(
104 latesttags[p.rev()] for p in ctx.parents())
104 latesttags[p.rev()] for p in ctx.parents())
105 except KeyError:
105 except KeyError:
106 # Cache miss - recurse
106 # Cache miss - recurse
107 todo.append(rev)
107 todo.append(rev)
108 todo.extend(p.rev() for p in ctx.parents())
108 todo.extend(p.rev() for p in ctx.parents())
109 continue
109 continue
110 latesttags[rev] = pdate, pdist + 1, ptag
110 latesttags[rev] = pdate, pdist + 1, ptag
111 return latesttags[rev]
111 return latesttags[rev]
112
112
113 def getrenamedfn(repo, endrev=None):
113 def getrenamedfn(repo, endrev=None):
114 rcache = {}
114 rcache = {}
115 if endrev is None:
115 if endrev is None:
116 endrev = len(repo)
116 endrev = len(repo)
117
117
118 def getrenamed(fn, rev):
118 def getrenamed(fn, rev):
119 '''looks up all renames for a file (up to endrev) the first
119 '''looks up all renames for a file (up to endrev) the first
120 time the file is given. It indexes on the changerev and only
120 time the file is given. It indexes on the changerev and only
121 parses the manifest if linkrev != changerev.
121 parses the manifest if linkrev != changerev.
122 Returns rename info for fn at changerev rev.'''
122 Returns rename info for fn at changerev rev.'''
123 if fn not in rcache:
123 if fn not in rcache:
124 rcache[fn] = {}
124 rcache[fn] = {}
125 fl = repo.file(fn)
125 fl = repo.file(fn)
126 for i in fl:
126 for i in fl:
127 lr = fl.linkrev(i)
127 lr = fl.linkrev(i)
128 renamed = fl.renamed(fl.node(i))
128 renamed = fl.renamed(fl.node(i))
129 rcache[fn][lr] = renamed
129 rcache[fn][lr] = renamed
130 if lr >= endrev:
130 if lr >= endrev:
131 break
131 break
132 if rev in rcache[fn]:
132 if rev in rcache[fn]:
133 return rcache[fn][rev]
133 return rcache[fn][rev]
134
134
135 # If linkrev != rev (i.e. rev not found in rcache) fallback to
135 # If linkrev != rev (i.e. rev not found in rcache) fallback to
136 # filectx logic.
136 # filectx logic.
137 try:
137 try:
138 return repo[rev][fn].renamed()
138 return repo[rev][fn].renamed()
139 except error.LookupError:
139 except error.LookupError:
140 return None
140 return None
141
141
142 return getrenamed
142 return getrenamed
143
143
144
144
145 def showauthor(repo, ctx, templ, **args):
145 def showauthor(repo, ctx, templ, **args):
146 """:author: String. The unmodified author of the changeset."""
146 """:author: String. The unmodified author of the changeset."""
147 return ctx.user()
147 return ctx.user()
148
148
149 def showbisect(repo, ctx, templ, **args):
149 def showbisect(repo, ctx, templ, **args):
150 """:bisect: String. The changeset bisection status."""
150 """:bisect: String. The changeset bisection status."""
151 return hbisect.label(repo, ctx.node())
151 return hbisect.label(repo, ctx.node())
152
152
153 def showbranch(**args):
153 def showbranch(**args):
154 """:branch: String. The name of the branch on which the changeset was
154 """:branch: String. The name of the branch on which the changeset was
155 committed.
155 committed.
156 """
156 """
157 return args['ctx'].branch()
157 return args['ctx'].branch()
158
158
159 def showbranches(**args):
159 def showbranches(**args):
160 """:branches: List of strings. The name of the branch on which the
160 """:branches: List of strings. The name of the branch on which the
161 changeset was committed. Will be empty if the branch name was
161 changeset was committed. Will be empty if the branch name was
162 default.
162 default.
163 """
163 """
164 branch = args['ctx'].branch()
164 branch = args['ctx'].branch()
165 if branch != 'default':
165 if branch != 'default':
166 return showlist('branch', [branch], plural='branches', **args)
166 return showlist('branch', [branch], plural='branches', **args)
167
167
168 def showbookmarks(**args):
168 def showbookmarks(**args):
169 """:bookmarks: List of strings. Any bookmarks associated with the
169 """:bookmarks: List of strings. Any bookmarks associated with the
170 changeset.
170 changeset.
171 """
171 """
172 bookmarks = args['ctx'].bookmarks()
172 bookmarks = args['ctx'].bookmarks()
173 return showlist('bookmark', bookmarks, **args)
173 return showlist('bookmark', bookmarks, **args)
174
174
175 def showchildren(**args):
175 def showchildren(**args):
176 """:children: List of strings. The children of the changeset."""
176 """:children: List of strings. The children of the changeset."""
177 ctx = args['ctx']
177 ctx = args['ctx']
178 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
178 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
179 return showlist('children', childrevs, **args)
179 return showlist('children', childrevs, **args)
180
180
181 def showdate(repo, ctx, templ, **args):
181 def showdate(repo, ctx, templ, **args):
182 """:date: Date information. The date when the changeset was committed."""
182 """:date: Date information. The date when the changeset was committed."""
183 return ctx.date()
183 return ctx.date()
184
184
185 def showdescription(repo, ctx, templ, **args):
185 def showdescription(repo, ctx, templ, **args):
186 """:desc: String. The text of the changeset description."""
186 """:desc: String. The text of the changeset description."""
187 return ctx.description().strip()
187 return ctx.description().strip()
188
188
189 def showdiffstat(repo, ctx, templ, **args):
189 def showdiffstat(repo, ctx, templ, **args):
190 """:diffstat: String. Statistics of changes with the following format:
190 """:diffstat: String. Statistics of changes with the following format:
191 "modified files: +added/-removed lines"
191 "modified files: +added/-removed lines"
192 """
192 """
193 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
193 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
194 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
194 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
195 return '%s: +%s/-%s' % (len(stats), adds, removes)
195 return '%s: +%s/-%s' % (len(stats), adds, removes)
196
196
197 def showextras(**args):
197 def showextras(**args):
198 templ = args['templ']
198 templ = args['templ']
199 for key, value in sorted(args['ctx'].extra().items()):
199 for key, value in sorted(args['ctx'].extra().items()):
200 args = args.copy()
200 args = args.copy()
201 args.update(dict(key=key, value=value))
201 args.update(dict(key=key, value=value))
202 yield templ('extra', **args)
202 yield templ('extra', **args)
203
203
204 def showfileadds(**args):
204 def showfileadds(**args):
205 """:file_adds: List of strings. Files added by this changeset."""
205 """:file_adds: List of strings. Files added by this changeset."""
206 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
206 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
207 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
207 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
208
208
209 def showfilecopies(**args):
209 def showfilecopies(**args):
210 """:file_copies: List of strings. Files copied in this changeset with
210 """:file_copies: List of strings. Files copied in this changeset with
211 their sources.
211 their sources.
212 """
212 """
213 cache, ctx = args['cache'], args['ctx']
213 cache, ctx = args['cache'], args['ctx']
214 copies = args['revcache'].get('copies')
214 copies = args['revcache'].get('copies')
215 if copies is None:
215 if copies is None:
216 if 'getrenamed' not in cache:
216 if 'getrenamed' not in cache:
217 cache['getrenamed'] = getrenamedfn(args['repo'])
217 cache['getrenamed'] = getrenamedfn(args['repo'])
218 copies = []
218 copies = []
219 getrenamed = cache['getrenamed']
219 getrenamed = cache['getrenamed']
220 for fn in ctx.files():
220 for fn in ctx.files():
221 rename = getrenamed(fn, ctx.rev())
221 rename = getrenamed(fn, ctx.rev())
222 if rename:
222 if rename:
223 copies.append((fn, rename[0]))
223 copies.append((fn, rename[0]))
224
224
225 c = [{'name': x[0], 'source': x[1]} for x in copies]
225 c = [{'name': x[0], 'source': x[1]} for x in copies]
226 return showlist('file_copy', c, plural='file_copies', **args)
226 return showlist('file_copy', c, plural='file_copies', **args)
227
227
228 # showfilecopiesswitch() displays file copies only if copy records are
228 # showfilecopiesswitch() displays file copies only if copy records are
229 # provided before calling the templater, usually with a --copies
229 # provided before calling the templater, usually with a --copies
230 # command line switch.
230 # command line switch.
231 def showfilecopiesswitch(**args):
231 def showfilecopiesswitch(**args):
232 """:file_copies_switch: List of strings. Like "file_copies" but displayed
232 """:file_copies_switch: List of strings. Like "file_copies" but displayed
233 only if the --copied switch is set.
233 only if the --copied switch is set.
234 """
234 """
235 copies = args['revcache'].get('copies') or []
235 copies = args['revcache'].get('copies') or []
236 c = [{'name': x[0], 'source': x[1]} for x in copies]
236 c = [{'name': x[0], 'source': x[1]} for x in copies]
237 return showlist('file_copy', c, plural='file_copies', **args)
237 return showlist('file_copy', c, plural='file_copies', **args)
238
238
239 def showfiledels(**args):
239 def showfiledels(**args):
240 """:file_dels: List of strings. Files removed by this changeset."""
240 """:file_dels: List of strings. Files removed by this changeset."""
241 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
241 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
242 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
242 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
243
243
244 def showfilemods(**args):
244 def showfilemods(**args):
245 """:file_mods: List of strings. Files modified by this changeset."""
245 """:file_mods: List of strings. Files modified by this changeset."""
246 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
246 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
247 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
247 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
248
248
249 def showfiles(**args):
249 def showfiles(**args):
250 """:files: List of strings. All files modified, added, or removed by this
250 """:files: List of strings. All files modified, added, or removed by this
251 changeset.
251 changeset.
252 """
252 """
253 return showlist('file', args['ctx'].files(), **args)
253 return showlist('file', args['ctx'].files(), **args)
254
254
255 def showlatesttag(repo, ctx, templ, cache, **args):
255 def showlatesttag(repo, ctx, templ, cache, **args):
256 """:latesttag: String. Most recent global tag in the ancestors of this
256 """:latesttag: String. Most recent global tag in the ancestors of this
257 changeset.
257 changeset.
258 """
258 """
259 return getlatesttags(repo, ctx, cache)[2]
259 return getlatesttags(repo, ctx, cache)[2]
260
260
261 def showlatesttagdistance(repo, ctx, templ, cache, **args):
261 def showlatesttagdistance(repo, ctx, templ, cache, **args):
262 """:latesttagdistance: Integer. Longest path to the latest tag."""
262 """:latesttagdistance: Integer. Longest path to the latest tag."""
263 return getlatesttags(repo, ctx, cache)[1]
263 return getlatesttags(repo, ctx, cache)[1]
264
264
265 def showmanifest(**args):
265 def showmanifest(**args):
266 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
266 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
267 args = args.copy()
267 args = args.copy()
268 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
268 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
269 node=hex(ctx.changeset()[0])))
269 node=hex(ctx.changeset()[0])))
270 return templ('manifest', **args)
270 return templ('manifest', **args)
271
271
272 def shownode(repo, ctx, templ, **args):
272 def shownode(repo, ctx, templ, **args):
273 """:node: String. The changeset identification hash, as a 40 hexadecimal
273 """:node: String. The changeset identification hash, as a 40 hexadecimal
274 digit string.
274 digit string.
275 """
275 """
276 return ctx.hex()
276 return ctx.hex()
277
277
278 def showphase(repo, ctx, templ, **args):
279 """:rev: Integer. The changeset phase."""
280 return ctx.phase()
281
278 def showrev(repo, ctx, templ, **args):
282 def showrev(repo, ctx, templ, **args):
279 """:rev: Integer. The repository-local changeset revision number."""
283 """:rev: Integer. The repository-local changeset revision number."""
280 return ctx.rev()
284 return ctx.rev()
281
285
282 def showtags(**args):
286 def showtags(**args):
283 """:tags: List of strings. Any tags associated with the changeset."""
287 """:tags: List of strings. Any tags associated with the changeset."""
284 return showlist('tag', args['ctx'].tags(), **args)
288 return showlist('tag', args['ctx'].tags(), **args)
285
289
286 # keywords are callables like:
290 # keywords are callables like:
287 # fn(repo, ctx, templ, cache, revcache, **args)
291 # fn(repo, ctx, templ, cache, revcache, **args)
288 # with:
292 # with:
289 # repo - current repository instance
293 # repo - current repository instance
290 # ctx - the changectx being displayed
294 # ctx - the changectx being displayed
291 # templ - the templater instance
295 # templ - the templater instance
292 # cache - a cache dictionary for the whole templater run
296 # cache - a cache dictionary for the whole templater run
293 # revcache - a cache dictionary for the current revision
297 # revcache - a cache dictionary for the current revision
294 keywords = {
298 keywords = {
295 'author': showauthor,
299 'author': showauthor,
296 'bisect': showbisect,
300 'bisect': showbisect,
297 'branch': showbranch,
301 'branch': showbranch,
298 'branches': showbranches,
302 'branches': showbranches,
299 'bookmarks': showbookmarks,
303 'bookmarks': showbookmarks,
300 'children': showchildren,
304 'children': showchildren,
301 'date': showdate,
305 'date': showdate,
302 'desc': showdescription,
306 'desc': showdescription,
303 'diffstat': showdiffstat,
307 'diffstat': showdiffstat,
304 'extras': showextras,
308 'extras': showextras,
305 'file_adds': showfileadds,
309 'file_adds': showfileadds,
306 'file_copies': showfilecopies,
310 'file_copies': showfilecopies,
307 'file_copies_switch': showfilecopiesswitch,
311 'file_copies_switch': showfilecopiesswitch,
308 'file_dels': showfiledels,
312 'file_dels': showfiledels,
309 'file_mods': showfilemods,
313 'file_mods': showfilemods,
310 'files': showfiles,
314 'files': showfiles,
311 'latesttag': showlatesttag,
315 'latesttag': showlatesttag,
312 'latesttagdistance': showlatesttagdistance,
316 'latesttagdistance': showlatesttagdistance,
313 'manifest': showmanifest,
317 'manifest': showmanifest,
314 'node': shownode,
318 'node': shownode,
319 'phase': showphase,
315 'rev': showrev,
320 'rev': showrev,
316 'tags': showtags,
321 'tags': showtags,
317 }
322 }
318
323
319 # tell hggettext to extract docstrings from these functions:
324 # tell hggettext to extract docstrings from these functions:
320 i18nfunctions = keywords.values()
325 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now