##// END OF EJS Templates
templates: add 'bisect' keyword to return a cset's bisect status...
"Yann E. MORIN" -
r15155:f4a8d754 default
parent child Browse files
Show More
@@ -0,0 +1,117 b''
1 # Here we create a simple DAG which has just enough of the required
2 # topology to test all the bisection status labels:
3 #
4 # 13--14
5 # /
6 # 0--1--2--3---------9--10--11--12
7 # \ /
8 # 4--5--6--7--8
9
10
11 $ hg init
12
13 $ echo '0' >a
14 $ hg add a
15 $ hg ci -u test -d '0 0' -m '0'
16 $ echo '1' >a
17 $ hg ci -u test -d '0 1' -m '1'
18
19 branch 2-3
20
21 $ echo '2' >b
22 $ hg add b
23 $ hg ci -u test -d '0 2' -m '2'
24 $ echo '3' >b
25 $ hg ci -u test -d '0 3' -m '3'
26
27 branch 4-8
28
29 $ hg up -r 1
30 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
31 $ echo '4' >c
32 $ hg add c
33 $ hg ci -u test -d '0 4' -m '4'
34 created new head
35 $ echo '5' >c
36 $ hg ci -u test -d '0 5' -m '5'
37 $ echo '6' >c
38 $ hg ci -u test -d '0 6' -m '6'
39 $ echo '7' >c
40 $ hg ci -u test -d '0 7' -m '7'
41 $ echo '8' >c
42 $ hg ci -u test -d '0 8' -m '8'
43
44 merge
45
46 $ hg merge -r 3
47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 (branch merge, don't forget to commit)
49 $ hg ci -u test -d '0 9' -m '9=8+3'
50
51 $ echo '10' >a
52 $ hg ci -u test -d '0 10' -m '10'
53 $ echo '11' >a
54 $ hg ci -u test -d '0 11' -m '11'
55 $ echo '12' >a
56 $ hg ci -u test -d '0 12' -m '12'
57
58 unrelated branch
59
60 $ hg up -r 3
61 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
62 $ echo '13' >d
63 $ hg add d
64 $ hg ci -u test -d '0 13' -m '13'
65 created new head
66 $ echo '14' >d
67 $ hg ci -u test -d '0 14' -m '14'
68
69 mark changesets
70
71 $ hg bisect --reset
72 $ hg bisect --good 4
73 $ hg bisect --good 6
74 $ hg bisect --bad 12
75 Testing changeset 9:8bcbdb072033 (6 changesets remaining, ~2 tests)
76 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
77 $ hg bisect --bad 10
78 Testing changeset 8:3cd112f87d77 (4 changesets remaining, ~2 tests)
79 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
80 $ hg bisect --skip 7
81 Testing changeset 8:3cd112f87d77 (4 changesets remaining, ~2 tests)
82 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
83
84 test template
85
86 $ hg log --template '{rev}:{node|short} {bisect}\n'
87 14:cecd84203acc
88 13:86f7c8cdb6df
89 12:a76089b5f47c bad
90 11:5c3eb122d29c bad (implicit)
91 10:b097cef2be03 bad
92 9:8bcbdb072033 untested
93 8:3cd112f87d77 untested
94 7:577e237a73bd skipped
95 6:e597fa2707c5 good
96 5:b9cea37a76bc good (implicit)
97 4:da6b357259d7 good
98 3:e7f031aee8ca ignored
99 2:b1ad1b6bcc5c ignored
100 1:37f42ae8b45e good (implicit)
101 0:b4e73ffab476 good (implicit)
102 $ hg log --template '{bisect|shortbisect} {rev}:{node|short}\n'
103 14:cecd84203acc
104 13:86f7c8cdb6df
105 B 12:a76089b5f47c
106 B 11:5c3eb122d29c
107 B 10:b097cef2be03
108 U 9:8bcbdb072033
109 U 8:3cd112f87d77
110 S 7:577e237a73bd
111 G 6:e597fa2707c5
112 G 5:b9cea37a76bc
113 G 4:da6b357259d7
114 I 3:e7f031aee8ca
115 I 2:b1ad1b6bcc5c
116 G 1:37f42ae8b45e
117 G 0:b4e73ffab476
@@ -1,362 +1,372 b''
1 1 # template-filters.py - common template expansion filters
2 2 #
3 3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import cgi, re, os, time, urllib
9 9 import encoding, node, util
10 import hbisect
10 11
11 12 def addbreaks(text):
12 13 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
13 14 every line except the last.
14 15 """
15 16 return text.replace('\n', '<br/>\n')
16 17
17 18 agescales = [("year", 3600 * 24 * 365),
18 19 ("month", 3600 * 24 * 30),
19 20 ("week", 3600 * 24 * 7),
20 21 ("day", 3600 * 24),
21 22 ("hour", 3600),
22 23 ("minute", 60),
23 24 ("second", 1)]
24 25
25 26 def age(date):
26 27 """:age: Date. Returns a human-readable date/time difference between the
27 28 given date/time and the current date/time.
28 29 """
29 30
30 31 def plural(t, c):
31 32 if c == 1:
32 33 return t
33 34 return t + "s"
34 35 def fmt(t, c):
35 36 return "%d %s" % (c, plural(t, c))
36 37
37 38 now = time.time()
38 39 then = date[0]
39 40 future = False
40 41 if then > now:
41 42 future = True
42 43 delta = max(1, int(then - now))
43 44 if delta > agescales[0][1] * 30:
44 45 return 'in the distant future'
45 46 else:
46 47 delta = max(1, int(now - then))
47 48 if delta > agescales[0][1] * 2:
48 49 return util.shortdate(date)
49 50
50 51 for t, s in agescales:
51 52 n = delta // s
52 53 if n >= 2 or s == 1:
53 54 if future:
54 55 return '%s from now' % fmt(t, n)
55 56 return '%s ago' % fmt(t, n)
56 57
57 58 def basename(path):
58 59 """:basename: Any text. Treats the text as a path, and returns the last
59 60 component of the path after splitting by the path separator
60 61 (ignoring trailing separators). For example, "foo/bar/baz" becomes
61 62 "baz" and "foo/bar//" becomes "bar".
62 63 """
63 64 return os.path.basename(path)
64 65
65 66 def datefilter(text):
66 67 """:date: Date. Returns a date in a Unix date format, including the
67 68 timezone: "Mon Sep 04 15:13:13 2006 0700".
68 69 """
69 70 return util.datestr(text)
70 71
71 72 def domain(author):
72 73 """:domain: Any text. Finds the first string that looks like an email
73 74 address, and extracts just the domain component. Example: ``User
74 75 <user@example.com>`` becomes ``example.com``.
75 76 """
76 77 f = author.find('@')
77 78 if f == -1:
78 79 return ''
79 80 author = author[f + 1:]
80 81 f = author.find('>')
81 82 if f >= 0:
82 83 author = author[:f]
83 84 return author
84 85
85 86 def email(text):
86 87 """:email: Any text. Extracts the first string that looks like an email
87 88 address. Example: ``User <user@example.com>`` becomes
88 89 ``user@example.com``.
89 90 """
90 91 return util.email(text)
91 92
92 93 def escape(text):
93 94 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
94 95 and ">" with XML entities.
95 96 """
96 97 return cgi.escape(text, True)
97 98
98 99 para_re = None
99 100 space_re = None
100 101
101 102 def fill(text, width):
102 103 '''fill many paragraphs.'''
103 104 global para_re, space_re
104 105 if para_re is None:
105 106 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
106 107 space_re = re.compile(r' +')
107 108
108 109 def findparas():
109 110 start = 0
110 111 while True:
111 112 m = para_re.search(text, start)
112 113 if not m:
113 114 uctext = unicode(text[start:], encoding.encoding)
114 115 w = len(uctext)
115 116 while 0 < w and uctext[w - 1].isspace():
116 117 w -= 1
117 118 yield (uctext[:w].encode(encoding.encoding),
118 119 uctext[w:].encode(encoding.encoding))
119 120 break
120 121 yield text[start:m.start(0)], m.group(1)
121 122 start = m.end(1)
122 123
123 124 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
124 125 for para, rest in findparas()])
125 126
126 127 def fill68(text):
127 128 """:fill68: Any text. Wraps the text to fit in 68 columns."""
128 129 return fill(text, 68)
129 130
130 131 def fill76(text):
131 132 """:fill76: Any text. Wraps the text to fit in 76 columns."""
132 133 return fill(text, 76)
133 134
134 135 def firstline(text):
135 136 """:firstline: Any text. Returns the first line of text."""
136 137 try:
137 138 return text.splitlines(True)[0].rstrip('\r\n')
138 139 except IndexError:
139 140 return ''
140 141
141 142 def hexfilter(text):
142 143 """:hex: Any text. Convert a binary Mercurial node identifier into
143 144 its long hexadecimal representation.
144 145 """
145 146 return node.hex(text)
146 147
147 148 def hgdate(text):
148 149 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
149 150 25200" (Unix timestamp, timezone offset).
150 151 """
151 152 return "%d %d" % text
152 153
153 154 def isodate(text):
154 155 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
155 156 +0200".
156 157 """
157 158 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
158 159
159 160 def isodatesec(text):
160 161 """:isodatesec: Date. Returns the date in ISO 8601 format, including
161 162 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
162 163 filter.
163 164 """
164 165 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
165 166
166 167 def indent(text, prefix):
167 168 '''indent each non-empty line of text after first with prefix.'''
168 169 lines = text.splitlines()
169 170 num_lines = len(lines)
170 171 endswithnewline = text[-1:] == '\n'
171 172 def indenter():
172 173 for i in xrange(num_lines):
173 174 l = lines[i]
174 175 if i and l.strip():
175 176 yield prefix
176 177 yield l
177 178 if i < num_lines - 1 or endswithnewline:
178 179 yield '\n'
179 180 return "".join(indenter())
180 181
181 182 def json(obj):
182 183 if obj is None or obj is False or obj is True:
183 184 return {None: 'null', False: 'false', True: 'true'}[obj]
184 185 elif isinstance(obj, int) or isinstance(obj, float):
185 186 return str(obj)
186 187 elif isinstance(obj, str):
187 188 u = unicode(obj, encoding.encoding, 'replace')
188 189 return '"%s"' % jsonescape(u)
189 190 elif isinstance(obj, unicode):
190 191 return '"%s"' % jsonescape(obj)
191 192 elif util.safehasattr(obj, 'keys'):
192 193 out = []
193 194 for k, v in obj.iteritems():
194 195 s = '%s: %s' % (json(k), json(v))
195 196 out.append(s)
196 197 return '{' + ', '.join(out) + '}'
197 198 elif util.safehasattr(obj, '__iter__'):
198 199 out = []
199 200 for i in obj:
200 201 out.append(json(i))
201 202 return '[' + ', '.join(out) + ']'
202 203 else:
203 204 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
204 205
205 206 def _uescape(c):
206 207 if ord(c) < 0x80:
207 208 return c
208 209 else:
209 210 return '\\u%04x' % ord(c)
210 211
211 212 _escapes = [
212 213 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
213 214 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
214 215 ]
215 216
216 217 def jsonescape(s):
217 218 for k, v in _escapes:
218 219 s = s.replace(k, v)
219 220 return ''.join(_uescape(c) for c in s)
220 221
221 222 def localdate(text):
222 223 """:localdate: Date. Converts a date to local date."""
223 224 return (text[0], util.makedate()[1])
224 225
225 226 def nonempty(str):
226 227 """:nonempty: Any text. Returns '(none)' if the string is empty."""
227 228 return str or "(none)"
228 229
229 230 def obfuscate(text):
230 231 """:obfuscate: Any text. Returns the input text rendered as a sequence of
231 232 XML entities.
232 233 """
233 234 text = unicode(text, encoding.encoding, 'replace')
234 235 return ''.join(['&#%d;' % ord(c) for c in text])
235 236
236 237 def permissions(flags):
237 238 if "l" in flags:
238 239 return "lrwxrwxrwx"
239 240 if "x" in flags:
240 241 return "-rwxr-xr-x"
241 242 return "-rw-r--r--"
242 243
243 244 def person(author):
244 245 """:person: Any text. Returns the text before an email address."""
245 246 if not '@' in author:
246 247 return author
247 248 f = author.find('<')
248 249 if f != -1:
249 250 return author[:f].rstrip()
250 251 f = author.find('@')
251 252 return author[:f].replace('.', ' ')
252 253
253 254 def rfc3339date(text):
254 255 """:rfc3339date: Date. Returns a date using the Internet date format
255 256 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
256 257 """
257 258 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
258 259
259 260 def rfc822date(text):
260 261 """:rfc822date: Date. Returns a date using the same format used in email
261 262 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
262 263 """
263 264 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
264 265
265 266 def short(text):
266 267 """:short: Changeset hash. Returns the short form of a changeset hash,
267 268 i.e. a 12 hexadecimal digit string.
268 269 """
269 270 return text[:12]
270 271
272 def shortbisect(text):
273 """:shortbisect: Any text. Treats `text` as a bisection status, and
274 returns a single-character representing the status (G: good, B: bad,
275 S: skipped, U: untested, I: ignored). Returns single space if `text`
276 is not a valid bisection status.
277 """
278 return hbisect.shortlabel(text) or ' '
279
271 280 def shortdate(text):
272 281 """:shortdate: Date. Returns a date like "2006-09-18"."""
273 282 return util.shortdate(text)
274 283
275 284 def stringescape(text):
276 285 return text.encode('string_escape')
277 286
278 287 def stringify(thing):
279 288 """:stringify: Any type. Turns the value into text by converting values into
280 289 text and concatenating them.
281 290 """
282 291 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
283 292 return "".join([stringify(t) for t in thing if t is not None])
284 293 return str(thing)
285 294
286 295 def strip(text):
287 296 """:strip: Any text. Strips all leading and trailing whitespace."""
288 297 return text.strip()
289 298
290 299 def stripdir(text):
291 300 """:stripdir: Treat the text as path and strip a directory level, if
292 301 possible. For example, "foo" and "foo/bar" becomes "foo".
293 302 """
294 303 dir = os.path.dirname(text)
295 304 if dir == "":
296 305 return os.path.basename(text)
297 306 else:
298 307 return dir
299 308
300 309 def tabindent(text):
301 310 """:tabindent: Any text. Returns the text, with every line except the
302 311 first starting with a tab character.
303 312 """
304 313 return indent(text, '\t')
305 314
306 315 def urlescape(text):
307 316 """:urlescape: Any text. Escapes all "special" characters. For example,
308 317 "foo bar" becomes "foo%20bar".
309 318 """
310 319 return urllib.quote(text)
311 320
312 321 def userfilter(text):
313 322 """:user: Any text. Returns the user portion of an email address."""
314 323 return util.shortuser(text)
315 324
316 325 def xmlescape(text):
317 326 text = (text
318 327 .replace('&', '&amp;')
319 328 .replace('<', '&lt;')
320 329 .replace('>', '&gt;')
321 330 .replace('"', '&quot;')
322 331 .replace("'", '&#39;')) # &apos; invalid in HTML
323 332 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
324 333
325 334 filters = {
326 335 "addbreaks": addbreaks,
327 336 "age": age,
328 337 "basename": basename,
329 338 "date": datefilter,
330 339 "domain": domain,
331 340 "email": email,
332 341 "escape": escape,
333 342 "fill68": fill68,
334 343 "fill76": fill76,
335 344 "firstline": firstline,
336 345 "hex": hexfilter,
337 346 "hgdate": hgdate,
338 347 "isodate": isodate,
339 348 "isodatesec": isodatesec,
340 349 "json": json,
341 350 "jsonescape": jsonescape,
342 351 "localdate": localdate,
343 352 "nonempty": nonempty,
344 353 "obfuscate": obfuscate,
345 354 "permissions": permissions,
346 355 "person": person,
347 356 "rfc3339date": rfc3339date,
348 357 "rfc822date": rfc822date,
349 358 "short": short,
359 "shortbisect": shortbisect,
350 360 "shortdate": shortdate,
351 361 "stringescape": stringescape,
352 362 "stringify": stringify,
353 363 "strip": strip,
354 364 "stripdir": stripdir,
355 365 "tabindent": tabindent,
356 366 "urlescape": urlescape,
357 367 "user": userfilter,
358 368 "xmlescape": xmlescape,
359 369 }
360 370
361 371 # tell hggettext to extract docstrings from these functions:
362 372 i18nfunctions = filters.values()
@@ -1,314 +1,320 b''
1 1 # templatekw.py - common changeset template keywords
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex
9 9 import patch, util, error
10 import hbisect
10 11
11 12 def showlist(name, values, plural=None, **args):
12 13 '''expand set of values.
13 14 name is name of key in template map.
14 15 values is list of strings or dicts.
15 16 plural is plural of name, if not simply name + 's'.
16 17
17 18 expansion works like this, given name 'foo'.
18 19
19 20 if values is empty, expand 'no_foos'.
20 21
21 22 if 'foo' not in template map, return values as a string,
22 23 joined by space.
23 24
24 25 expand 'start_foos'.
25 26
26 27 for each value, expand 'foo'. if 'last_foo' in template
27 28 map, expand it instead of 'foo' for last key.
28 29
29 30 expand 'end_foos'.
30 31 '''
31 32 templ = args['templ']
32 33 if plural:
33 34 names = plural
34 35 else: names = name + 's'
35 36 if not values:
36 37 noname = 'no_' + names
37 38 if noname in templ:
38 39 yield templ(noname, **args)
39 40 return
40 41 if name not in templ:
41 42 if isinstance(values[0], str):
42 43 yield ' '.join(values)
43 44 else:
44 45 for v in values:
45 46 yield dict(v, **args)
46 47 return
47 48 startname = 'start_' + names
48 49 if startname in templ:
49 50 yield templ(startname, **args)
50 51 vargs = args.copy()
51 52 def one(v, tag=name):
52 53 try:
53 54 vargs.update(v)
54 55 except (AttributeError, ValueError):
55 56 try:
56 57 for a, b in v:
57 58 vargs[a] = b
58 59 except ValueError:
59 60 vargs[name] = v
60 61 return templ(tag, **vargs)
61 62 lastname = 'last_' + name
62 63 if lastname in templ:
63 64 last = values.pop()
64 65 else:
65 66 last = None
66 67 for v in values:
67 68 yield one(v)
68 69 if last is not None:
69 70 yield one(last, tag=lastname)
70 71 endname = 'end_' + names
71 72 if endname in templ:
72 73 yield templ(endname, **args)
73 74
74 75 def getfiles(repo, ctx, revcache):
75 76 if 'files' not in revcache:
76 77 revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
77 78 return revcache['files']
78 79
79 80 def getlatesttags(repo, ctx, cache):
80 81 '''return date, distance and name for the latest tag of rev'''
81 82
82 83 if 'latesttags' not in cache:
83 84 # Cache mapping from rev to a tuple with tag date, tag
84 85 # distance and tag name
85 86 cache['latesttags'] = {-1: (0, 0, 'null')}
86 87 latesttags = cache['latesttags']
87 88
88 89 rev = ctx.rev()
89 90 todo = [rev]
90 91 while todo:
91 92 rev = todo.pop()
92 93 if rev in latesttags:
93 94 continue
94 95 ctx = repo[rev]
95 96 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
96 97 if tags:
97 98 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
98 99 continue
99 100 try:
100 101 # The tuples are laid out so the right one can be found by
101 102 # comparison.
102 103 pdate, pdist, ptag = max(
103 104 latesttags[p.rev()] for p in ctx.parents())
104 105 except KeyError:
105 106 # Cache miss - recurse
106 107 todo.append(rev)
107 108 todo.extend(p.rev() for p in ctx.parents())
108 109 continue
109 110 latesttags[rev] = pdate, pdist + 1, ptag
110 111 return latesttags[rev]
111 112
112 113 def getrenamedfn(repo, endrev=None):
113 114 rcache = {}
114 115 if endrev is None:
115 116 endrev = len(repo)
116 117
117 118 def getrenamed(fn, rev):
118 119 '''looks up all renames for a file (up to endrev) the first
119 120 time the file is given. It indexes on the changerev and only
120 121 parses the manifest if linkrev != changerev.
121 122 Returns rename info for fn at changerev rev.'''
122 123 if fn not in rcache:
123 124 rcache[fn] = {}
124 125 fl = repo.file(fn)
125 126 for i in fl:
126 127 lr = fl.linkrev(i)
127 128 renamed = fl.renamed(fl.node(i))
128 129 rcache[fn][lr] = renamed
129 130 if lr >= endrev:
130 131 break
131 132 if rev in rcache[fn]:
132 133 return rcache[fn][rev]
133 134
134 135 # If linkrev != rev (i.e. rev not found in rcache) fallback to
135 136 # filectx logic.
136 137 try:
137 138 return repo[rev][fn].renamed()
138 139 except error.LookupError:
139 140 return None
140 141
141 142 return getrenamed
142 143
143 144
144 145 def showauthor(repo, ctx, templ, **args):
145 146 """:author: String. The unmodified author of the changeset."""
146 147 return ctx.user()
147 148
149 def showbisect(repo, ctx, templ, **args):
150 """:bisect: String. The changeset bisection status."""
151 return hbisect.label(repo, ctx.node())
152
148 153 def showbranch(**args):
149 154 """:branch: String. The name of the branch on which the changeset was
150 155 committed.
151 156 """
152 157 return args['ctx'].branch()
153 158
154 159 def showbranches(**args):
155 160 """:branches: List of strings. The name of the branch on which the
156 161 changeset was committed. Will be empty if the branch name was
157 162 default.
158 163 """
159 164 branch = args['ctx'].branch()
160 165 if branch != 'default':
161 166 return showlist('branch', [branch], plural='branches', **args)
162 167
163 168 def showbookmarks(**args):
164 169 """:bookmarks: List of strings. Any bookmarks associated with the
165 170 changeset.
166 171 """
167 172 bookmarks = args['ctx'].bookmarks()
168 173 return showlist('bookmark', bookmarks, **args)
169 174
170 175 def showchildren(**args):
171 176 """:children: List of strings. The children of the changeset."""
172 177 ctx = args['ctx']
173 178 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
174 179 return showlist('children', childrevs, **args)
175 180
176 181 def showdate(repo, ctx, templ, **args):
177 182 """:date: Date information. The date when the changeset was committed."""
178 183 return ctx.date()
179 184
180 185 def showdescription(repo, ctx, templ, **args):
181 186 """:desc: String. The text of the changeset description."""
182 187 return ctx.description().strip()
183 188
184 189 def showdiffstat(repo, ctx, templ, **args):
185 190 """:diffstat: String. Statistics of changes with the following format:
186 191 "modified files: +added/-removed lines"
187 192 """
188 193 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
189 194 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
190 195 return '%s: +%s/-%s' % (len(stats), adds, removes)
191 196
192 197 def showextras(**args):
193 198 templ = args['templ']
194 199 for key, value in sorted(args['ctx'].extra().items()):
195 200 args = args.copy()
196 201 args.update(dict(key=key, value=value))
197 202 yield templ('extra', **args)
198 203
199 204 def showfileadds(**args):
200 205 """:file_adds: List of strings. Files added by this changeset."""
201 206 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
202 207 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
203 208
204 209 def showfilecopies(**args):
205 210 """:file_copies: List of strings. Files copied in this changeset with
206 211 their sources.
207 212 """
208 213 cache, ctx = args['cache'], args['ctx']
209 214 copies = args['revcache'].get('copies')
210 215 if copies is None:
211 216 if 'getrenamed' not in cache:
212 217 cache['getrenamed'] = getrenamedfn(args['repo'])
213 218 copies = []
214 219 getrenamed = cache['getrenamed']
215 220 for fn in ctx.files():
216 221 rename = getrenamed(fn, ctx.rev())
217 222 if rename:
218 223 copies.append((fn, rename[0]))
219 224
220 225 c = [{'name': x[0], 'source': x[1]} for x in copies]
221 226 return showlist('file_copy', c, plural='file_copies', **args)
222 227
223 228 # showfilecopiesswitch() displays file copies only if copy records are
224 229 # provided before calling the templater, usually with a --copies
225 230 # command line switch.
226 231 def showfilecopiesswitch(**args):
227 232 """:file_copies_switch: List of strings. Like "file_copies" but displayed
228 233 only if the --copied switch is set.
229 234 """
230 235 copies = args['revcache'].get('copies') or []
231 236 c = [{'name': x[0], 'source': x[1]} for x in copies]
232 237 return showlist('file_copy', c, plural='file_copies', **args)
233 238
234 239 def showfiledels(**args):
235 240 """:file_dels: List of strings. Files removed by this changeset."""
236 241 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
237 242 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
238 243
239 244 def showfilemods(**args):
240 245 """:file_mods: List of strings. Files modified by this changeset."""
241 246 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
242 247 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
243 248
244 249 def showfiles(**args):
245 250 """:files: List of strings. All files modified, added, or removed by this
246 251 changeset.
247 252 """
248 253 return showlist('file', args['ctx'].files(), **args)
249 254
250 255 def showlatesttag(repo, ctx, templ, cache, **args):
251 256 """:latesttag: String. Most recent global tag in the ancestors of this
252 257 changeset.
253 258 """
254 259 return getlatesttags(repo, ctx, cache)[2]
255 260
256 261 def showlatesttagdistance(repo, ctx, templ, cache, **args):
257 262 """:latesttagdistance: Integer. Longest path to the latest tag."""
258 263 return getlatesttags(repo, ctx, cache)[1]
259 264
260 265 def showmanifest(**args):
261 266 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
262 267 args = args.copy()
263 268 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
264 269 node=hex(ctx.changeset()[0])))
265 270 return templ('manifest', **args)
266 271
267 272 def shownode(repo, ctx, templ, **args):
268 273 """:node: String. The changeset identification hash, as a 40 hexadecimal
269 274 digit string.
270 275 """
271 276 return ctx.hex()
272 277
273 278 def showrev(repo, ctx, templ, **args):
274 279 """:rev: Integer. The repository-local changeset revision number."""
275 280 return ctx.rev()
276 281
277 282 def showtags(**args):
278 283 """:tags: List of strings. Any tags associated with the changeset."""
279 284 return showlist('tag', args['ctx'].tags(), **args)
280 285
281 286 # keywords are callables like:
282 287 # fn(repo, ctx, templ, cache, revcache, **args)
283 288 # with:
284 289 # repo - current repository instance
285 290 # ctx - the changectx being displayed
286 291 # templ - the templater instance
287 292 # cache - a cache dictionary for the whole templater run
288 293 # revcache - a cache dictionary for the current revision
289 294 keywords = {
290 295 'author': showauthor,
296 'bisect': showbisect,
291 297 'branch': showbranch,
292 298 'branches': showbranches,
293 299 'bookmarks': showbookmarks,
294 300 'children': showchildren,
295 301 'date': showdate,
296 302 'desc': showdescription,
297 303 'diffstat': showdiffstat,
298 304 'extras': showextras,
299 305 'file_adds': showfileadds,
300 306 'file_copies': showfilecopies,
301 307 'file_copies_switch': showfilecopiesswitch,
302 308 'file_dels': showfiledels,
303 309 'file_mods': showfilemods,
304 310 'files': showfiles,
305 311 'latesttag': showlatesttag,
306 312 'latesttagdistance': showlatesttagdistance,
307 313 'manifest': showmanifest,
308 314 'node': shownode,
309 315 'rev': showrev,
310 316 'tags': showtags,
311 317 }
312 318
313 319 # tell hggettext to extract docstrings from these functions:
314 320 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now