##// END OF EJS Templates
templates: add filter to reverse list...
Manuel Jacob -
r50389:c96ed402 default
parent child Browse files
Show More
@@ -1,551 +1,559
1 # templatefilters.py - common template expansion filters
1 # templatefilters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2008 Olivia Mackall <olivia@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
8
9 import os
9 import os
10 import re
10 import re
11 import time
11 import time
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import hex
14 from .node import hex
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pycompat,
18 pycompat,
19 registrar,
19 registrar,
20 smartset,
20 smartset,
21 templateutil,
21 templateutil,
22 url,
22 url,
23 util,
23 util,
24 )
24 )
25 from .utils import (
25 from .utils import (
26 cborutil,
26 cborutil,
27 dateutil,
27 dateutil,
28 stringutil,
28 stringutil,
29 )
29 )
30
30
31 urlerr = util.urlerr
31 urlerr = util.urlerr
32 urlreq = util.urlreq
32 urlreq = util.urlreq
33
33
34 # filters are callables like:
34 # filters are callables like:
35 # fn(obj)
35 # fn(obj)
36 # with:
36 # with:
37 # obj - object to be filtered (text, date, list and so on)
37 # obj - object to be filtered (text, date, list and so on)
38 filters = {}
38 filters = {}
39
39
40 templatefilter = registrar.templatefilter(filters)
40 templatefilter = registrar.templatefilter(filters)
41
41
42
42
43 @templatefilter(b'addbreaks', intype=bytes)
43 @templatefilter(b'addbreaks', intype=bytes)
44 def addbreaks(text):
44 def addbreaks(text):
45 """Any text. Add an XHTML "<br />" tag before the end of
45 """Any text. Add an XHTML "<br />" tag before the end of
46 every line except the last.
46 every line except the last.
47 """
47 """
48 return text.replace(b'\n', b'<br/>\n')
48 return text.replace(b'\n', b'<br/>\n')
49
49
50
50
51 agescales = [
51 agescales = [
52 (b"year", 3600 * 24 * 365, b'Y'),
52 (b"year", 3600 * 24 * 365, b'Y'),
53 (b"month", 3600 * 24 * 30, b'M'),
53 (b"month", 3600 * 24 * 30, b'M'),
54 (b"week", 3600 * 24 * 7, b'W'),
54 (b"week", 3600 * 24 * 7, b'W'),
55 (b"day", 3600 * 24, b'd'),
55 (b"day", 3600 * 24, b'd'),
56 (b"hour", 3600, b'h'),
56 (b"hour", 3600, b'h'),
57 (b"minute", 60, b'm'),
57 (b"minute", 60, b'm'),
58 (b"second", 1, b's'),
58 (b"second", 1, b's'),
59 ]
59 ]
60
60
61
61
62 @templatefilter(b'age', intype=templateutil.date)
62 @templatefilter(b'age', intype=templateutil.date)
63 def age(date, abbrev=False):
63 def age(date, abbrev=False):
64 """Date. Returns a human-readable date/time difference between the
64 """Date. Returns a human-readable date/time difference between the
65 given date/time and the current date/time.
65 given date/time and the current date/time.
66 """
66 """
67
67
68 def plural(t, c):
68 def plural(t, c):
69 if c == 1:
69 if c == 1:
70 return t
70 return t
71 return t + b"s"
71 return t + b"s"
72
72
73 def fmt(t, c, a):
73 def fmt(t, c, a):
74 if abbrev:
74 if abbrev:
75 return b"%d%s" % (c, a)
75 return b"%d%s" % (c, a)
76 return b"%d %s" % (c, plural(t, c))
76 return b"%d %s" % (c, plural(t, c))
77
77
78 now = time.time()
78 now = time.time()
79 then = date[0]
79 then = date[0]
80 future = False
80 future = False
81 if then > now:
81 if then > now:
82 future = True
82 future = True
83 delta = max(1, int(then - now))
83 delta = max(1, int(then - now))
84 if delta > agescales[0][1] * 30:
84 if delta > agescales[0][1] * 30:
85 return b'in the distant future'
85 return b'in the distant future'
86 else:
86 else:
87 delta = max(1, int(now - then))
87 delta = max(1, int(now - then))
88 if delta > agescales[0][1] * 2:
88 if delta > agescales[0][1] * 2:
89 return dateutil.shortdate(date)
89 return dateutil.shortdate(date)
90
90
91 for t, s, a in agescales:
91 for t, s, a in agescales:
92 n = delta // s
92 n = delta // s
93 if n >= 2 or s == 1:
93 if n >= 2 or s == 1:
94 if future:
94 if future:
95 return b'%s from now' % fmt(t, n, a)
95 return b'%s from now' % fmt(t, n, a)
96 return b'%s ago' % fmt(t, n, a)
96 return b'%s ago' % fmt(t, n, a)
97
97
98
98
99 @templatefilter(b'basename', intype=bytes)
99 @templatefilter(b'basename', intype=bytes)
100 def basename(path):
100 def basename(path):
101 """Any text. Treats the text as a path, and returns the last
101 """Any text. Treats the text as a path, and returns the last
102 component of the path after splitting by the path separator.
102 component of the path after splitting by the path separator.
103 For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "".
103 For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "".
104 """
104 """
105 return os.path.basename(path)
105 return os.path.basename(path)
106
106
107
107
108 def _tocborencodable(obj):
108 def _tocborencodable(obj):
109 if isinstance(obj, smartset.abstractsmartset):
109 if isinstance(obj, smartset.abstractsmartset):
110 return list(obj)
110 return list(obj)
111 return obj
111 return obj
112
112
113
113
114 @templatefilter(b'cbor')
114 @templatefilter(b'cbor')
115 def cbor(obj):
115 def cbor(obj):
116 """Any object. Serializes the object to CBOR bytes."""
116 """Any object. Serializes the object to CBOR bytes."""
117 # cborutil is stricter about type than json() filter
117 # cborutil is stricter about type than json() filter
118 obj = pycompat.rapply(_tocborencodable, obj)
118 obj = pycompat.rapply(_tocborencodable, obj)
119 return b''.join(cborutil.streamencode(obj))
119 return b''.join(cborutil.streamencode(obj))
120
120
121
121
122 @templatefilter(b'commondir')
122 @templatefilter(b'commondir')
123 def commondir(filelist):
123 def commondir(filelist):
124 """List of text. Treats each list item as file name with /
124 """List of text. Treats each list item as file name with /
125 as path separator and returns the longest common directory
125 as path separator and returns the longest common directory
126 prefix shared by all list items.
126 prefix shared by all list items.
127 Returns the empty string if no common prefix exists.
127 Returns the empty string if no common prefix exists.
128
128
129 The list items are not normalized, i.e. "foo/../bar" is handled as
129 The list items are not normalized, i.e. "foo/../bar" is handled as
130 file "bar" in the directory "foo/..". Leading slashes are ignored.
130 file "bar" in the directory "foo/..". Leading slashes are ignored.
131
131
132 For example, ["foo/bar/baz", "foo/baz/bar"] becomes "foo" and
132 For example, ["foo/bar/baz", "foo/baz/bar"] becomes "foo" and
133 ["foo/bar", "baz"] becomes "".
133 ["foo/bar", "baz"] becomes "".
134 """
134 """
135
135
136 def common(a, b):
136 def common(a, b):
137 if len(a) > len(b):
137 if len(a) > len(b):
138 a = b[: len(a)]
138 a = b[: len(a)]
139 elif len(b) > len(a):
139 elif len(b) > len(a):
140 b = b[: len(a)]
140 b = b[: len(a)]
141 if a == b:
141 if a == b:
142 return a
142 return a
143 for i in range(len(a)):
143 for i in range(len(a)):
144 if a[i] != b[i]:
144 if a[i] != b[i]:
145 return a[:i]
145 return a[:i]
146 return a
146 return a
147
147
148 try:
148 try:
149 if not filelist:
149 if not filelist:
150 return b""
150 return b""
151 dirlist = [f.lstrip(b'/').split(b'/')[:-1] for f in filelist]
151 dirlist = [f.lstrip(b'/').split(b'/')[:-1] for f in filelist]
152 if len(dirlist) == 1:
152 if len(dirlist) == 1:
153 return b'/'.join(dirlist[0])
153 return b'/'.join(dirlist[0])
154 a = min(dirlist)
154 a = min(dirlist)
155 b = max(dirlist)
155 b = max(dirlist)
156 # The common prefix of a and b is shared with all
156 # The common prefix of a and b is shared with all
157 # elements of the list since Python sorts lexicographical
157 # elements of the list since Python sorts lexicographical
158 # and [1, x] after [1].
158 # and [1, x] after [1].
159 return b'/'.join(common(a, b))
159 return b'/'.join(common(a, b))
160 except TypeError:
160 except TypeError:
161 raise error.ParseError(_(b'argument is not a list of text'))
161 raise error.ParseError(_(b'argument is not a list of text'))
162
162
163
163
164 @templatefilter(b'count')
164 @templatefilter(b'count')
165 def count(i):
165 def count(i):
166 """List or text. Returns the length as an integer."""
166 """List or text. Returns the length as an integer."""
167 try:
167 try:
168 return len(i)
168 return len(i)
169 except TypeError:
169 except TypeError:
170 raise error.ParseError(_(b'not countable'))
170 raise error.ParseError(_(b'not countable'))
171
171
172
172
173 @templatefilter(b'dirname', intype=bytes)
173 @templatefilter(b'dirname', intype=bytes)
174 def dirname(path):
174 def dirname(path):
175 """Any text. Treats the text as a path, and strips the last
175 """Any text. Treats the text as a path, and strips the last
176 component of the path after splitting by the path separator.
176 component of the path after splitting by the path separator.
177 """
177 """
178 return os.path.dirname(path)
178 return os.path.dirname(path)
179
179
180
180
181 @templatefilter(b'domain', intype=bytes)
181 @templatefilter(b'domain', intype=bytes)
182 def domain(author):
182 def domain(author):
183 """Any text. Finds the first string that looks like an email
183 """Any text. Finds the first string that looks like an email
184 address, and extracts just the domain component. Example: ``User
184 address, and extracts just the domain component. Example: ``User
185 <user@example.com>`` becomes ``example.com``.
185 <user@example.com>`` becomes ``example.com``.
186 """
186 """
187 f = author.find(b'@')
187 f = author.find(b'@')
188 if f == -1:
188 if f == -1:
189 return b''
189 return b''
190 author = author[f + 1 :]
190 author = author[f + 1 :]
191 f = author.find(b'>')
191 f = author.find(b'>')
192 if f >= 0:
192 if f >= 0:
193 author = author[:f]
193 author = author[:f]
194 return author
194 return author
195
195
196
196
197 @templatefilter(b'email', intype=bytes)
197 @templatefilter(b'email', intype=bytes)
198 def email(text):
198 def email(text):
199 """Any text. Extracts the first string that looks like an email
199 """Any text. Extracts the first string that looks like an email
200 address. Example: ``User <user@example.com>`` becomes
200 address. Example: ``User <user@example.com>`` becomes
201 ``user@example.com``.
201 ``user@example.com``.
202 """
202 """
203 return stringutil.email(text)
203 return stringutil.email(text)
204
204
205
205
206 @templatefilter(b'escape', intype=bytes)
206 @templatefilter(b'escape', intype=bytes)
207 def escape(text):
207 def escape(text):
208 """Any text. Replaces the special XML/XHTML characters "&", "<"
208 """Any text. Replaces the special XML/XHTML characters "&", "<"
209 and ">" with XML entities, and filters out NUL characters.
209 and ">" with XML entities, and filters out NUL characters.
210 """
210 """
211 return url.escape(text.replace(b'\0', b''), True)
211 return url.escape(text.replace(b'\0', b''), True)
212
212
213
213
214 para_re = None
214 para_re = None
215 space_re = None
215 space_re = None
216
216
217
217
218 def fill(text, width, initindent=b'', hangindent=b''):
218 def fill(text, width, initindent=b'', hangindent=b''):
219 '''fill many paragraphs with optional indentation.'''
219 '''fill many paragraphs with optional indentation.'''
220 global para_re, space_re
220 global para_re, space_re
221 if para_re is None:
221 if para_re is None:
222 para_re = re.compile(b'(\n\n|\n\\s*[-*]\\s*)', re.M)
222 para_re = re.compile(b'(\n\n|\n\\s*[-*]\\s*)', re.M)
223 space_re = re.compile(br' +')
223 space_re = re.compile(br' +')
224
224
225 def findparas():
225 def findparas():
226 start = 0
226 start = 0
227 while True:
227 while True:
228 m = para_re.search(text, start)
228 m = para_re.search(text, start)
229 if not m:
229 if not m:
230 uctext = encoding.unifromlocal(text[start:])
230 uctext = encoding.unifromlocal(text[start:])
231 w = len(uctext)
231 w = len(uctext)
232 while w > 0 and uctext[w - 1].isspace():
232 while w > 0 and uctext[w - 1].isspace():
233 w -= 1
233 w -= 1
234 yield (
234 yield (
235 encoding.unitolocal(uctext[:w]),
235 encoding.unitolocal(uctext[:w]),
236 encoding.unitolocal(uctext[w:]),
236 encoding.unitolocal(uctext[w:]),
237 )
237 )
238 break
238 break
239 yield text[start : m.start(0)], m.group(1)
239 yield text[start : m.start(0)], m.group(1)
240 start = m.end(1)
240 start = m.end(1)
241
241
242 return b"".join(
242 return b"".join(
243 [
243 [
244 stringutil.wrap(
244 stringutil.wrap(
245 space_re.sub(b' ', stringutil.wrap(para, width)),
245 space_re.sub(b' ', stringutil.wrap(para, width)),
246 width,
246 width,
247 initindent,
247 initindent,
248 hangindent,
248 hangindent,
249 )
249 )
250 + rest
250 + rest
251 for para, rest in findparas()
251 for para, rest in findparas()
252 ]
252 ]
253 )
253 )
254
254
255
255
256 @templatefilter(b'fill68', intype=bytes)
256 @templatefilter(b'fill68', intype=bytes)
257 def fill68(text):
257 def fill68(text):
258 """Any text. Wraps the text to fit in 68 columns."""
258 """Any text. Wraps the text to fit in 68 columns."""
259 return fill(text, 68)
259 return fill(text, 68)
260
260
261
261
262 @templatefilter(b'fill76', intype=bytes)
262 @templatefilter(b'fill76', intype=bytes)
263 def fill76(text):
263 def fill76(text):
264 """Any text. Wraps the text to fit in 76 columns."""
264 """Any text. Wraps the text to fit in 76 columns."""
265 return fill(text, 76)
265 return fill(text, 76)
266
266
267
267
268 @templatefilter(b'firstline', intype=bytes)
268 @templatefilter(b'firstline', intype=bytes)
269 def firstline(text):
269 def firstline(text):
270 """Any text. Returns the first line of text."""
270 """Any text. Returns the first line of text."""
271 return stringutil.firstline(text)
271 return stringutil.firstline(text)
272
272
273
273
274 @templatefilter(b'hex', intype=bytes)
274 @templatefilter(b'hex', intype=bytes)
275 def hexfilter(text):
275 def hexfilter(text):
276 """Any text. Convert a binary Mercurial node identifier into
276 """Any text. Convert a binary Mercurial node identifier into
277 its long hexadecimal representation.
277 its long hexadecimal representation.
278 """
278 """
279 return hex(text)
279 return hex(text)
280
280
281
281
282 @templatefilter(b'hgdate', intype=templateutil.date)
282 @templatefilter(b'hgdate', intype=templateutil.date)
283 def hgdate(text):
283 def hgdate(text):
284 """Date. Returns the date as a pair of numbers: "1157407993
284 """Date. Returns the date as a pair of numbers: "1157407993
285 25200" (Unix timestamp, timezone offset).
285 25200" (Unix timestamp, timezone offset).
286 """
286 """
287 return b"%d %d" % text
287 return b"%d %d" % text
288
288
289
289
290 @templatefilter(b'isodate', intype=templateutil.date)
290 @templatefilter(b'isodate', intype=templateutil.date)
291 def isodate(text):
291 def isodate(text):
292 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
292 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
293 +0200".
293 +0200".
294 """
294 """
295 return dateutil.datestr(text, b'%Y-%m-%d %H:%M %1%2')
295 return dateutil.datestr(text, b'%Y-%m-%d %H:%M %1%2')
296
296
297
297
298 @templatefilter(b'isodatesec', intype=templateutil.date)
298 @templatefilter(b'isodatesec', intype=templateutil.date)
299 def isodatesec(text):
299 def isodatesec(text):
300 """Date. Returns the date in ISO 8601 format, including
300 """Date. Returns the date in ISO 8601 format, including
301 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
301 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
302 filter.
302 filter.
303 """
303 """
304 return dateutil.datestr(text, b'%Y-%m-%d %H:%M:%S %1%2')
304 return dateutil.datestr(text, b'%Y-%m-%d %H:%M:%S %1%2')
305
305
306
306
307 def indent(text, prefix, firstline=b''):
307 def indent(text, prefix, firstline=b''):
308 '''indent each non-empty line of text after first with prefix.'''
308 '''indent each non-empty line of text after first with prefix.'''
309 lines = text.splitlines()
309 lines = text.splitlines()
310 num_lines = len(lines)
310 num_lines = len(lines)
311 endswithnewline = text[-1:] == b'\n'
311 endswithnewline = text[-1:] == b'\n'
312
312
313 def indenter():
313 def indenter():
314 for i in range(num_lines):
314 for i in range(num_lines):
315 l = lines[i]
315 l = lines[i]
316 if l.strip():
316 if l.strip():
317 yield prefix if i else firstline
317 yield prefix if i else firstline
318 yield l
318 yield l
319 if i < num_lines - 1 or endswithnewline:
319 if i < num_lines - 1 or endswithnewline:
320 yield b'\n'
320 yield b'\n'
321
321
322 return b"".join(indenter())
322 return b"".join(indenter())
323
323
324
324
325 @templatefilter(b'json')
325 @templatefilter(b'json')
326 def json(obj, paranoid=True):
326 def json(obj, paranoid=True):
327 """Any object. Serializes the object to a JSON formatted text."""
327 """Any object. Serializes the object to a JSON formatted text."""
328 if obj is None:
328 if obj is None:
329 return b'null'
329 return b'null'
330 elif obj is False:
330 elif obj is False:
331 return b'false'
331 return b'false'
332 elif obj is True:
332 elif obj is True:
333 return b'true'
333 return b'true'
334 elif isinstance(obj, (int, int, float)):
334 elif isinstance(obj, (int, int, float)):
335 return pycompat.bytestr(obj)
335 return pycompat.bytestr(obj)
336 elif isinstance(obj, bytes):
336 elif isinstance(obj, bytes):
337 return b'"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
337 return b'"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
338 elif isinstance(obj, type(u'')):
338 elif isinstance(obj, type(u'')):
339 raise error.ProgrammingError(
339 raise error.ProgrammingError(
340 b'Mercurial only does output with bytes: %r' % obj
340 b'Mercurial only does output with bytes: %r' % obj
341 )
341 )
342 elif util.safehasattr(obj, b'keys'):
342 elif util.safehasattr(obj, b'keys'):
343 out = [
343 out = [
344 b'"%s": %s'
344 b'"%s": %s'
345 % (encoding.jsonescape(k, paranoid=paranoid), json(v, paranoid))
345 % (encoding.jsonescape(k, paranoid=paranoid), json(v, paranoid))
346 for k, v in sorted(obj.items())
346 for k, v in sorted(obj.items())
347 ]
347 ]
348 return b'{' + b', '.join(out) + b'}'
348 return b'{' + b', '.join(out) + b'}'
349 elif util.safehasattr(obj, b'__iter__'):
349 elif util.safehasattr(obj, b'__iter__'):
350 out = [json(i, paranoid) for i in obj]
350 out = [json(i, paranoid) for i in obj]
351 return b'[' + b', '.join(out) + b']'
351 return b'[' + b', '.join(out) + b']'
352 raise error.ProgrammingError(b'cannot encode %r' % obj)
352 raise error.ProgrammingError(b'cannot encode %r' % obj)
353
353
354
354
355 @templatefilter(b'lower', intype=bytes)
355 @templatefilter(b'lower', intype=bytes)
356 def lower(text):
356 def lower(text):
357 """Any text. Converts the text to lowercase."""
357 """Any text. Converts the text to lowercase."""
358 return encoding.lower(text)
358 return encoding.lower(text)
359
359
360
360
361 @templatefilter(b'nonempty', intype=bytes)
361 @templatefilter(b'nonempty', intype=bytes)
362 def nonempty(text):
362 def nonempty(text):
363 """Any text. Returns '(none)' if the string is empty."""
363 """Any text. Returns '(none)' if the string is empty."""
364 return text or b"(none)"
364 return text or b"(none)"
365
365
366
366
367 @templatefilter(b'obfuscate', intype=bytes)
367 @templatefilter(b'obfuscate', intype=bytes)
368 def obfuscate(text):
368 def obfuscate(text):
369 """Any text. Returns the input text rendered as a sequence of
369 """Any text. Returns the input text rendered as a sequence of
370 XML entities.
370 XML entities.
371 """
371 """
372 text = str(text, pycompat.sysstr(encoding.encoding), r'replace')
372 text = str(text, pycompat.sysstr(encoding.encoding), r'replace')
373 return b''.join([b'&#%d;' % ord(c) for c in text])
373 return b''.join([b'&#%d;' % ord(c) for c in text])
374
374
375
375
376 @templatefilter(b'permissions', intype=bytes)
376 @templatefilter(b'permissions', intype=bytes)
377 def permissions(flags):
377 def permissions(flags):
378 if b"l" in flags:
378 if b"l" in flags:
379 return b"lrwxrwxrwx"
379 return b"lrwxrwxrwx"
380 if b"x" in flags:
380 if b"x" in flags:
381 return b"-rwxr-xr-x"
381 return b"-rwxr-xr-x"
382 return b"-rw-r--r--"
382 return b"-rw-r--r--"
383
383
384
384
385 @templatefilter(b'person', intype=bytes)
385 @templatefilter(b'person', intype=bytes)
386 def person(author):
386 def person(author):
387 """Any text. Returns the name before an email address,
387 """Any text. Returns the name before an email address,
388 interpreting it as per RFC 5322.
388 interpreting it as per RFC 5322.
389 """
389 """
390 return stringutil.person(author)
390 return stringutil.person(author)
391
391
392
392
393 @templatefilter(b'reverse')
394 def reverse(list_):
395 """List. Reverses the order of list items."""
396 if isinstance(list_, list):
397 return templateutil.hybridlist(list_[::-1], name=b'item')
398 raise error.ParseError(_(b'not reversible'))
399
400
393 @templatefilter(b'revescape', intype=bytes)
401 @templatefilter(b'revescape', intype=bytes)
394 def revescape(text):
402 def revescape(text):
395 """Any text. Escapes all "special" characters, except @.
403 """Any text. Escapes all "special" characters, except @.
396 Forward slashes are escaped twice to prevent web servers from prematurely
404 Forward slashes are escaped twice to prevent web servers from prematurely
397 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
405 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
398 """
406 """
399 return urlreq.quote(text, safe=b'/@').replace(b'/', b'%252F')
407 return urlreq.quote(text, safe=b'/@').replace(b'/', b'%252F')
400
408
401
409
402 @templatefilter(b'rfc3339date', intype=templateutil.date)
410 @templatefilter(b'rfc3339date', intype=templateutil.date)
403 def rfc3339date(text):
411 def rfc3339date(text):
404 """Date. Returns a date using the Internet date format
412 """Date. Returns a date using the Internet date format
405 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
413 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
406 """
414 """
407 return dateutil.datestr(text, b"%Y-%m-%dT%H:%M:%S%1:%2")
415 return dateutil.datestr(text, b"%Y-%m-%dT%H:%M:%S%1:%2")
408
416
409
417
410 @templatefilter(b'rfc822date', intype=templateutil.date)
418 @templatefilter(b'rfc822date', intype=templateutil.date)
411 def rfc822date(text):
419 def rfc822date(text):
412 """Date. Returns a date using the same format used in email
420 """Date. Returns a date using the same format used in email
413 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
421 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
414 """
422 """
415 return dateutil.datestr(text, b"%a, %d %b %Y %H:%M:%S %1%2")
423 return dateutil.datestr(text, b"%a, %d %b %Y %H:%M:%S %1%2")
416
424
417
425
418 @templatefilter(b'short', intype=bytes)
426 @templatefilter(b'short', intype=bytes)
419 def short(text):
427 def short(text):
420 """Changeset hash. Returns the short form of a changeset hash,
428 """Changeset hash. Returns the short form of a changeset hash,
421 i.e. a 12 hexadecimal digit string.
429 i.e. a 12 hexadecimal digit string.
422 """
430 """
423 return text[:12]
431 return text[:12]
424
432
425
433
426 @templatefilter(b'shortbisect', intype=bytes)
434 @templatefilter(b'shortbisect', intype=bytes)
427 def shortbisect(label):
435 def shortbisect(label):
428 """Any text. Treats `label` as a bisection status, and
436 """Any text. Treats `label` as a bisection status, and
429 returns a single-character representing the status (G: good, B: bad,
437 returns a single-character representing the status (G: good, B: bad,
430 S: skipped, U: untested, I: ignored). Returns single space if `text`
438 S: skipped, U: untested, I: ignored). Returns single space if `text`
431 is not a valid bisection status.
439 is not a valid bisection status.
432 """
440 """
433 if label:
441 if label:
434 return label[0:1].upper()
442 return label[0:1].upper()
435 return b' '
443 return b' '
436
444
437
445
438 @templatefilter(b'shortdate', intype=templateutil.date)
446 @templatefilter(b'shortdate', intype=templateutil.date)
439 def shortdate(text):
447 def shortdate(text):
440 """Date. Returns a date like "2006-09-18"."""
448 """Date. Returns a date like "2006-09-18"."""
441 return dateutil.shortdate(text)
449 return dateutil.shortdate(text)
442
450
443
451
444 @templatefilter(b'slashpath', intype=bytes)
452 @templatefilter(b'slashpath', intype=bytes)
445 def slashpath(path):
453 def slashpath(path):
446 """Any text. Replaces the native path separator with slash."""
454 """Any text. Replaces the native path separator with slash."""
447 return util.pconvert(path)
455 return util.pconvert(path)
448
456
449
457
450 @templatefilter(b'splitlines', intype=bytes)
458 @templatefilter(b'splitlines', intype=bytes)
451 def splitlines(text):
459 def splitlines(text):
452 """Any text. Split text into a list of lines."""
460 """Any text. Split text into a list of lines."""
453 return templateutil.hybridlist(text.splitlines(), name=b'line')
461 return templateutil.hybridlist(text.splitlines(), name=b'line')
454
462
455
463
456 @templatefilter(b'stringescape', intype=bytes)
464 @templatefilter(b'stringescape', intype=bytes)
457 def stringescape(text):
465 def stringescape(text):
458 return stringutil.escapestr(text)
466 return stringutil.escapestr(text)
459
467
460
468
461 @templatefilter(b'stringify', intype=bytes)
469 @templatefilter(b'stringify', intype=bytes)
462 def stringify(thing):
470 def stringify(thing):
463 """Any type. Turns the value into text by converting values into
471 """Any type. Turns the value into text by converting values into
464 text and concatenating them.
472 text and concatenating them.
465 """
473 """
466 return thing # coerced by the intype
474 return thing # coerced by the intype
467
475
468
476
469 @templatefilter(b'stripdir', intype=bytes)
477 @templatefilter(b'stripdir', intype=bytes)
470 def stripdir(text):
478 def stripdir(text):
471 """Treat the text as path and strip a directory level, if
479 """Treat the text as path and strip a directory level, if
472 possible. For example, "foo" and "foo/bar" becomes "foo".
480 possible. For example, "foo" and "foo/bar" becomes "foo".
473 """
481 """
474 dir = os.path.dirname(text)
482 dir = os.path.dirname(text)
475 if dir == b"":
483 if dir == b"":
476 return os.path.basename(text)
484 return os.path.basename(text)
477 else:
485 else:
478 return dir
486 return dir
479
487
480
488
481 @templatefilter(b'tabindent', intype=bytes)
489 @templatefilter(b'tabindent', intype=bytes)
482 def tabindent(text):
490 def tabindent(text):
483 """Any text. Returns the text, with every non-empty line
491 """Any text. Returns the text, with every non-empty line
484 except the first starting with a tab character.
492 except the first starting with a tab character.
485 """
493 """
486 return indent(text, b'\t')
494 return indent(text, b'\t')
487
495
488
496
489 @templatefilter(b'upper', intype=bytes)
497 @templatefilter(b'upper', intype=bytes)
490 def upper(text):
498 def upper(text):
491 """Any text. Converts the text to uppercase."""
499 """Any text. Converts the text to uppercase."""
492 return encoding.upper(text)
500 return encoding.upper(text)
493
501
494
502
495 @templatefilter(b'urlescape', intype=bytes)
503 @templatefilter(b'urlescape', intype=bytes)
496 def urlescape(text):
504 def urlescape(text):
497 """Any text. Escapes all "special" characters. For example,
505 """Any text. Escapes all "special" characters. For example,
498 "foo bar" becomes "foo%20bar".
506 "foo bar" becomes "foo%20bar".
499 """
507 """
500 return urlreq.quote(text)
508 return urlreq.quote(text)
501
509
502
510
503 @templatefilter(b'user', intype=bytes)
511 @templatefilter(b'user', intype=bytes)
504 def userfilter(text):
512 def userfilter(text):
505 """Any text. Returns a short representation of a user name or email
513 """Any text. Returns a short representation of a user name or email
506 address."""
514 address."""
507 return stringutil.shortuser(text)
515 return stringutil.shortuser(text)
508
516
509
517
510 @templatefilter(b'emailuser', intype=bytes)
518 @templatefilter(b'emailuser', intype=bytes)
511 def emailuser(text):
519 def emailuser(text):
512 """Any text. Returns the user portion of an email address."""
520 """Any text. Returns the user portion of an email address."""
513 return stringutil.emailuser(text)
521 return stringutil.emailuser(text)
514
522
515
523
516 @templatefilter(b'utf8', intype=bytes)
524 @templatefilter(b'utf8', intype=bytes)
517 def utf8(text):
525 def utf8(text):
518 """Any text. Converts from the local character encoding to UTF-8."""
526 """Any text. Converts from the local character encoding to UTF-8."""
519 return encoding.fromlocal(text)
527 return encoding.fromlocal(text)
520
528
521
529
522 @templatefilter(b'xmlescape', intype=bytes)
530 @templatefilter(b'xmlescape', intype=bytes)
523 def xmlescape(text):
531 def xmlescape(text):
524 text = (
532 text = (
525 text.replace(b'&', b'&amp;')
533 text.replace(b'&', b'&amp;')
526 .replace(b'<', b'&lt;')
534 .replace(b'<', b'&lt;')
527 .replace(b'>', b'&gt;')
535 .replace(b'>', b'&gt;')
528 .replace(b'"', b'&quot;')
536 .replace(b'"', b'&quot;')
529 .replace(b"'", b'&#39;')
537 .replace(b"'", b'&#39;')
530 ) # &apos; invalid in HTML
538 ) # &apos; invalid in HTML
531 return re.sub(b'[\x00-\x08\x0B\x0C\x0E-\x1F]', b' ', text)
539 return re.sub(b'[\x00-\x08\x0B\x0C\x0E-\x1F]', b' ', text)
532
540
533
541
534 def websub(text, websubtable):
542 def websub(text, websubtable):
535 """:websub: Any text. Only applies to hgweb. Applies the regular
543 """:websub: Any text. Only applies to hgweb. Applies the regular
536 expression replacements defined in the websub section.
544 expression replacements defined in the websub section.
537 """
545 """
538 if websubtable:
546 if websubtable:
539 for regexp, format in websubtable:
547 for regexp, format in websubtable:
540 text = regexp.sub(format, text)
548 text = regexp.sub(format, text)
541 return text
549 return text
542
550
543
551
544 def loadfilter(ui, extname, registrarobj):
552 def loadfilter(ui, extname, registrarobj):
545 """Load template filter from specified registrarobj"""
553 """Load template filter from specified registrarobj"""
546 for name, func in registrarobj._table.items():
554 for name, func in registrarobj._table.items():
547 filters[name] = func
555 filters[name] = func
548
556
549
557
550 # tell hggettext to extract docstrings from these functions:
558 # tell hggettext to extract docstrings from these functions:
551 i18nfunctions = filters.values()
559 i18nfunctions = filters.values()
@@ -1,1721 +1,1736
1 Test template filters and functions
1 Test template filters and functions
2 ===================================
2 ===================================
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo a > a
6 $ echo a > a
7 $ hg add a
7 $ hg add a
8 $ echo line 1 > b
8 $ echo line 1 > b
9 $ echo line 2 >> b
9 $ echo line 2 >> b
10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
11
11
12 $ hg add b
12 $ hg add b
13 $ echo other 1 > c
13 $ echo other 1 > c
14 $ echo other 2 >> c
14 $ echo other 2 >> c
15 $ echo >> c
15 $ echo >> c
16 $ echo other 3 >> c
16 $ echo other 3 >> c
17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
18
18
19 $ hg add c
19 $ hg add c
20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
21 $ echo c >> c
21 $ echo c >> c
22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
23
23
24 $ echo foo > .hg/branch
24 $ echo foo > .hg/branch
25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
26
26
27 $ hg co -q 3
27 $ hg co -q 3
28 $ echo other 4 >> d
28 $ echo other 4 >> d
29 $ hg add d
29 $ hg add d
30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
31
31
32 $ hg merge -q foo
32 $ hg merge -q foo
33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
34
34
35 Second branch starting at nullrev:
35 Second branch starting at nullrev:
36
36
37 $ hg update null
37 $ hg update null
38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
39 $ echo second > second
39 $ echo second > second
40 $ hg add second
40 $ hg add second
41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
42 created new head
42 created new head
43
43
44 $ echo third > third
44 $ echo third > third
45 $ hg add third
45 $ hg add third
46 $ hg mv second fourth
46 $ hg mv second fourth
47 $ hg commit -m third -d "2020-01-01 10:01"
47 $ hg commit -m third -d "2020-01-01 10:01"
48
48
49 $ hg phase -r 5 --public
49 $ hg phase -r 5 --public
50 $ hg phase -r 7 --secret --force
50 $ hg phase -r 7 --secret --force
51
51
52 Filters work:
52 Filters work:
53
53
54 $ hg log --template '{author|domain}\n'
54 $ hg log --template '{author|domain}\n'
55
55
56 hostname
56 hostname
57
57
58
58
59
59
60
60
61 place
61 place
62 place
62 place
63 hostname
63 hostname
64
64
65 $ hg log --template '{author|person}\n'
65 $ hg log --template '{author|person}\n'
66 test
66 test
67 User Name
67 User Name
68 person
68 person
69 person
69 person
70 person
70 person
71 person
71 person
72 other
72 other
73 A. N. Other
73 A. N. Other
74 User Name
74 User Name
75
75
76 $ hg log --template '{author|user}\n'
76 $ hg log --template '{author|user}\n'
77 test
77 test
78 user
78 user
79 person
79 person
80 person
80 person
81 person
81 person
82 person
82 person
83 other
83 other
84 other
84 other
85 user
85 user
86
86
87 $ hg log --template '{date|date}\n'
87 $ hg log --template '{date|date}\n'
88 Wed Jan 01 10:01:00 2020 +0000
88 Wed Jan 01 10:01:00 2020 +0000
89 Mon Jan 12 13:46:40 1970 +0000
89 Mon Jan 12 13:46:40 1970 +0000
90 Sun Jan 18 08:40:01 1970 +0000
90 Sun Jan 18 08:40:01 1970 +0000
91 Sun Jan 18 08:40:00 1970 +0000
91 Sun Jan 18 08:40:00 1970 +0000
92 Sat Jan 17 04:53:20 1970 +0000
92 Sat Jan 17 04:53:20 1970 +0000
93 Fri Jan 16 01:06:40 1970 +0000
93 Fri Jan 16 01:06:40 1970 +0000
94 Wed Jan 14 21:20:00 1970 +0000
94 Wed Jan 14 21:20:00 1970 +0000
95 Tue Jan 13 17:33:20 1970 +0000
95 Tue Jan 13 17:33:20 1970 +0000
96 Mon Jan 12 13:46:40 1970 +0000
96 Mon Jan 12 13:46:40 1970 +0000
97
97
98 $ hg log --template '{date|isodate}\n'
98 $ hg log --template '{date|isodate}\n'
99 2020-01-01 10:01 +0000
99 2020-01-01 10:01 +0000
100 1970-01-12 13:46 +0000
100 1970-01-12 13:46 +0000
101 1970-01-18 08:40 +0000
101 1970-01-18 08:40 +0000
102 1970-01-18 08:40 +0000
102 1970-01-18 08:40 +0000
103 1970-01-17 04:53 +0000
103 1970-01-17 04:53 +0000
104 1970-01-16 01:06 +0000
104 1970-01-16 01:06 +0000
105 1970-01-14 21:20 +0000
105 1970-01-14 21:20 +0000
106 1970-01-13 17:33 +0000
106 1970-01-13 17:33 +0000
107 1970-01-12 13:46 +0000
107 1970-01-12 13:46 +0000
108
108
109 $ hg log --template '{date|isodatesec}\n'
109 $ hg log --template '{date|isodatesec}\n'
110 2020-01-01 10:01:00 +0000
110 2020-01-01 10:01:00 +0000
111 1970-01-12 13:46:40 +0000
111 1970-01-12 13:46:40 +0000
112 1970-01-18 08:40:01 +0000
112 1970-01-18 08:40:01 +0000
113 1970-01-18 08:40:00 +0000
113 1970-01-18 08:40:00 +0000
114 1970-01-17 04:53:20 +0000
114 1970-01-17 04:53:20 +0000
115 1970-01-16 01:06:40 +0000
115 1970-01-16 01:06:40 +0000
116 1970-01-14 21:20:00 +0000
116 1970-01-14 21:20:00 +0000
117 1970-01-13 17:33:20 +0000
117 1970-01-13 17:33:20 +0000
118 1970-01-12 13:46:40 +0000
118 1970-01-12 13:46:40 +0000
119
119
120 $ hg log --template '{date|rfc822date}\n'
120 $ hg log --template '{date|rfc822date}\n'
121 Wed, 01 Jan 2020 10:01:00 +0000
121 Wed, 01 Jan 2020 10:01:00 +0000
122 Mon, 12 Jan 1970 13:46:40 +0000
122 Mon, 12 Jan 1970 13:46:40 +0000
123 Sun, 18 Jan 1970 08:40:01 +0000
123 Sun, 18 Jan 1970 08:40:01 +0000
124 Sun, 18 Jan 1970 08:40:00 +0000
124 Sun, 18 Jan 1970 08:40:00 +0000
125 Sat, 17 Jan 1970 04:53:20 +0000
125 Sat, 17 Jan 1970 04:53:20 +0000
126 Fri, 16 Jan 1970 01:06:40 +0000
126 Fri, 16 Jan 1970 01:06:40 +0000
127 Wed, 14 Jan 1970 21:20:00 +0000
127 Wed, 14 Jan 1970 21:20:00 +0000
128 Tue, 13 Jan 1970 17:33:20 +0000
128 Tue, 13 Jan 1970 17:33:20 +0000
129 Mon, 12 Jan 1970 13:46:40 +0000
129 Mon, 12 Jan 1970 13:46:40 +0000
130
130
131 $ hg log --template '{desc|firstline}\n'
131 $ hg log --template '{desc|firstline}\n'
132 third
132 third
133 second
133 second
134 merge
134 merge
135 new head
135 new head
136 new branch
136 new branch
137 no user, no domain
137 no user, no domain
138 no person
138 no person
139 other 1
139 other 1
140 line 1
140 line 1
141
141
142 $ hg log --template '{node|short}\n'
142 $ hg log --template '{node|short}\n'
143 95c24699272e
143 95c24699272e
144 29114dbae42b
144 29114dbae42b
145 d41e714fe50d
145 d41e714fe50d
146 13207e5a10d9
146 13207e5a10d9
147 bbe44766e73d
147 bbe44766e73d
148 10e46f2dcbf4
148 10e46f2dcbf4
149 97054abb4ab8
149 97054abb4ab8
150 b608e9d1a3f0
150 b608e9d1a3f0
151 1e4e1b8f71e0
151 1e4e1b8f71e0
152
152
153 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
153 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
154 <changeset author="test"/>
154 <changeset author="test"/>
155 <changeset author="User Name &lt;user@hostname&gt;"/>
155 <changeset author="User Name &lt;user@hostname&gt;"/>
156 <changeset author="person"/>
156 <changeset author="person"/>
157 <changeset author="person"/>
157 <changeset author="person"/>
158 <changeset author="person"/>
158 <changeset author="person"/>
159 <changeset author="person"/>
159 <changeset author="person"/>
160 <changeset author="other@place"/>
160 <changeset author="other@place"/>
161 <changeset author="A. N. Other &lt;other@place&gt;"/>
161 <changeset author="A. N. Other &lt;other@place&gt;"/>
162 <changeset author="User Name &lt;user@hostname&gt;"/>
162 <changeset author="User Name &lt;user@hostname&gt;"/>
163
163
164 $ hg log --template '{rev}: {children}\n'
164 $ hg log --template '{rev}: {children}\n'
165 8:
165 8:
166 7: 8:95c24699272e
166 7: 8:95c24699272e
167 6:
167 6:
168 5: 6:d41e714fe50d
168 5: 6:d41e714fe50d
169 4: 6:d41e714fe50d
169 4: 6:d41e714fe50d
170 3: 4:bbe44766e73d 5:13207e5a10d9
170 3: 4:bbe44766e73d 5:13207e5a10d9
171 2: 3:10e46f2dcbf4
171 2: 3:10e46f2dcbf4
172 1: 2:97054abb4ab8
172 1: 2:97054abb4ab8
173 0: 1:b608e9d1a3f0
173 0: 1:b608e9d1a3f0
174
174
175 Formatnode filter works:
175 Formatnode filter works:
176
176
177 $ hg -q log -r 0 --template '{node|formatnode}\n'
177 $ hg -q log -r 0 --template '{node|formatnode}\n'
178 1e4e1b8f71e0
178 1e4e1b8f71e0
179
179
180 $ hg log -r 0 --template '{node|formatnode}\n'
180 $ hg log -r 0 --template '{node|formatnode}\n'
181 1e4e1b8f71e0
181 1e4e1b8f71e0
182
182
183 $ hg -v log -r 0 --template '{node|formatnode}\n'
183 $ hg -v log -r 0 --template '{node|formatnode}\n'
184 1e4e1b8f71e0
184 1e4e1b8f71e0
185
185
186 $ hg --debug log -r 0 --template '{node|formatnode}\n'
186 $ hg --debug log -r 0 --template '{node|formatnode}\n'
187 1e4e1b8f71e05681d422154f5421e385fec3454f
187 1e4e1b8f71e05681d422154f5421e385fec3454f
188
188
189 Age filter:
189 Age filter:
190
190
191 $ hg init unstable-hash
191 $ hg init unstable-hash
192 $ cd unstable-hash
192 $ cd unstable-hash
193 $ hg log --template '{date|age}\n' > /dev/null || exit 1
193 $ hg log --template '{date|age}\n' > /dev/null || exit 1
194
194
195 >>> import datetime
195 >>> import datetime
196 >>> fp = open('a', 'wb')
196 >>> fp = open('a', 'wb')
197 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
197 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
198 >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
198 >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
199 >>> fp.close()
199 >>> fp.close()
200 $ hg add a
200 $ hg add a
201 $ hg commit -m future -d "`cat a`"
201 $ hg commit -m future -d "`cat a`"
202
202
203 $ hg log -l1 --template '{date|age}\n'
203 $ hg log -l1 --template '{date|age}\n'
204 7 years from now
204 7 years from now
205
205
206 $ cd ..
206 $ cd ..
207 $ rm -rf unstable-hash
207 $ rm -rf unstable-hash
208
208
209 Filename filters:
209 Filename filters:
210
210
211 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
211 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
212 bar||foo|
212 bar||foo|
213 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
213 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
214 foo|foo||
214 foo|foo||
215 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
215 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
216 foo|foo|foo|
216 foo|foo|foo|
217
217
218 commondir() filter:
218 commondir() filter:
219
219
220 $ hg debugtemplate '{""|splitlines|commondir}\n'
220 $ hg debugtemplate '{""|splitlines|commondir}\n'
221
221
222 $ hg debugtemplate '{"foo/bar\nfoo/baz\nfoo/foobar\n"|splitlines|commondir}\n'
222 $ hg debugtemplate '{"foo/bar\nfoo/baz\nfoo/foobar\n"|splitlines|commondir}\n'
223 foo
223 foo
224 $ hg debugtemplate '{"foo/bar\nfoo/bar\n"|splitlines|commondir}\n'
224 $ hg debugtemplate '{"foo/bar\nfoo/bar\n"|splitlines|commondir}\n'
225 foo
225 foo
226 $ hg debugtemplate '{"/foo/bar\n/foo/bar\n"|splitlines|commondir}\n'
226 $ hg debugtemplate '{"/foo/bar\n/foo/bar\n"|splitlines|commondir}\n'
227 foo
227 foo
228 $ hg debugtemplate '{"/foo\n/foo\n"|splitlines|commondir}\n'
228 $ hg debugtemplate '{"/foo\n/foo\n"|splitlines|commondir}\n'
229
229
230 $ hg debugtemplate '{"foo/bar\nbar/baz"|splitlines|commondir}\n'
230 $ hg debugtemplate '{"foo/bar\nbar/baz"|splitlines|commondir}\n'
231
231
232 $ hg debugtemplate '{"foo/bar\nbar/baz\nbar/foo\n"|splitlines|commondir}\n'
232 $ hg debugtemplate '{"foo/bar\nbar/baz\nbar/foo\n"|splitlines|commondir}\n'
233
233
234 $ hg debugtemplate '{"foo/../bar\nfoo/bar"|splitlines|commondir}\n'
234 $ hg debugtemplate '{"foo/../bar\nfoo/bar"|splitlines|commondir}\n'
235 foo
235 foo
236 $ hg debugtemplate '{"foo\n/foo"|splitlines|commondir}\n'
236 $ hg debugtemplate '{"foo\n/foo"|splitlines|commondir}\n'
237
237
238
238
239 $ hg log -r null -T '{rev|commondir}'
239 $ hg log -r null -T '{rev|commondir}'
240 hg: parse error: argument is not a list of text
240 hg: parse error: argument is not a list of text
241 (template filter 'commondir' is not compatible with keyword 'rev')
241 (template filter 'commondir' is not compatible with keyword 'rev')
242 [10]
242 [10]
243
243
244 Add a dummy commit to make up for the instability of the above:
244 Add a dummy commit to make up for the instability of the above:
245
245
246 $ echo a > a
246 $ echo a > a
247 $ hg add a
247 $ hg add a
248 $ hg ci -m future
248 $ hg ci -m future
249
249
250 Count filter:
250 Count filter:
251
251
252 $ hg log -l1 --template '{node|count} {node|short|count}\n'
252 $ hg log -l1 --template '{node|count} {node|short|count}\n'
253 40 12
253 40 12
254
254
255 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
255 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
256 0 1 4
256 0 1 4
257
257
258 $ hg log -G --template '{rev}: children: {children|count}, \
258 $ hg log -G --template '{rev}: children: {children|count}, \
259 > tags: {tags|count}, file_adds: {file_adds|count}, \
259 > tags: {tags|count}, file_adds: {file_adds|count}, \
260 > ancestors: {revset("ancestors(%s)", rev)|count}'
260 > ancestors: {revset("ancestors(%s)", rev)|count}'
261 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
261 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
262 |
262 |
263 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
263 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
264 |
264 |
265 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
265 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
266
266
267 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
267 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
268 |\
268 |\
269 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
269 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
270 | |
270 | |
271 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
271 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
272 |/
272 |/
273 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
273 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
274 |
274 |
275 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
275 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
276 |
276 |
277 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
277 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
278 |
278 |
279 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
279 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
280
280
281
281
282 $ hg log -l1 -T '{termwidth|count}\n'
282 $ hg log -l1 -T '{termwidth|count}\n'
283 hg: parse error: not countable
283 hg: parse error: not countable
284 (template filter 'count' is not compatible with keyword 'termwidth')
284 (template filter 'count' is not compatible with keyword 'termwidth')
285 [10]
285 [10]
286
286
287 Upper/lower filters:
287 Upper/lower filters:
288
288
289 $ hg log -r0 --template '{branch|upper}\n'
289 $ hg log -r0 --template '{branch|upper}\n'
290 DEFAULT
290 DEFAULT
291 $ hg log -r0 --template '{author|lower}\n'
291 $ hg log -r0 --template '{author|lower}\n'
292 user name <user@hostname>
292 user name <user@hostname>
293 $ hg log -r0 --template '{date|upper}\n'
293 $ hg log -r0 --template '{date|upper}\n'
294 1000000.00
294 1000000.00
295
295
296 Add a commit that does all possible modifications at once
296 Add a commit that does all possible modifications at once
297
297
298 $ echo modify >> third
298 $ echo modify >> third
299 $ touch b
299 $ touch b
300 $ hg add b
300 $ hg add b
301 $ hg mv fourth fifth
301 $ hg mv fourth fifth
302 $ hg rm a
302 $ hg rm a
303 $ hg ci -m "Modify, add, remove, rename"
303 $ hg ci -m "Modify, add, remove, rename"
304
304
305 Pass generator object created by template function to filter
305 Pass generator object created by template function to filter
306
306
307 $ hg log -l 1 --template '{if(author, author)|user}\n'
307 $ hg log -l 1 --template '{if(author, author)|user}\n'
308 test
308 test
309
309
310 Test diff function:
310 Test diff function:
311
311
312 $ hg diff -c 8
312 $ hg diff -c 8
313 diff -r 29114dbae42b -r 95c24699272e fourth
313 diff -r 29114dbae42b -r 95c24699272e fourth
314 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
314 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
315 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
315 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
316 @@ -0,0 +1,1 @@
316 @@ -0,0 +1,1 @@
317 +second
317 +second
318 diff -r 29114dbae42b -r 95c24699272e second
318 diff -r 29114dbae42b -r 95c24699272e second
319 --- a/second Mon Jan 12 13:46:40 1970 +0000
319 --- a/second Mon Jan 12 13:46:40 1970 +0000
320 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
320 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
321 @@ -1,1 +0,0 @@
321 @@ -1,1 +0,0 @@
322 -second
322 -second
323 diff -r 29114dbae42b -r 95c24699272e third
323 diff -r 29114dbae42b -r 95c24699272e third
324 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
324 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
325 +++ b/third Wed Jan 01 10:01:00 2020 +0000
325 +++ b/third Wed Jan 01 10:01:00 2020 +0000
326 @@ -0,0 +1,1 @@
326 @@ -0,0 +1,1 @@
327 +third
327 +third
328
328
329 $ hg log -r 8 -T "{diff()}"
329 $ hg log -r 8 -T "{diff()}"
330 diff -r 29114dbae42b -r 95c24699272e fourth
330 diff -r 29114dbae42b -r 95c24699272e fourth
331 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
331 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
332 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
332 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
333 @@ -0,0 +1,1 @@
333 @@ -0,0 +1,1 @@
334 +second
334 +second
335 diff -r 29114dbae42b -r 95c24699272e second
335 diff -r 29114dbae42b -r 95c24699272e second
336 --- a/second Mon Jan 12 13:46:40 1970 +0000
336 --- a/second Mon Jan 12 13:46:40 1970 +0000
337 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
337 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
338 @@ -1,1 +0,0 @@
338 @@ -1,1 +0,0 @@
339 -second
339 -second
340 diff -r 29114dbae42b -r 95c24699272e third
340 diff -r 29114dbae42b -r 95c24699272e third
341 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
341 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
342 +++ b/third Wed Jan 01 10:01:00 2020 +0000
342 +++ b/third Wed Jan 01 10:01:00 2020 +0000
343 @@ -0,0 +1,1 @@
343 @@ -0,0 +1,1 @@
344 +third
344 +third
345
345
346 $ hg log -r 8 -T "{diff('glob:f*')}"
346 $ hg log -r 8 -T "{diff('glob:f*')}"
347 diff -r 29114dbae42b -r 95c24699272e fourth
347 diff -r 29114dbae42b -r 95c24699272e fourth
348 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
348 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
349 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
349 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
350 @@ -0,0 +1,1 @@
350 @@ -0,0 +1,1 @@
351 +second
351 +second
352
352
353 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
353 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
354 diff -r 29114dbae42b -r 95c24699272e second
354 diff -r 29114dbae42b -r 95c24699272e second
355 --- a/second Mon Jan 12 13:46:40 1970 +0000
355 --- a/second Mon Jan 12 13:46:40 1970 +0000
356 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
356 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
357 @@ -1,1 +0,0 @@
357 @@ -1,1 +0,0 @@
358 -second
358 -second
359 diff -r 29114dbae42b -r 95c24699272e third
359 diff -r 29114dbae42b -r 95c24699272e third
360 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
360 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
361 +++ b/third Wed Jan 01 10:01:00 2020 +0000
361 +++ b/third Wed Jan 01 10:01:00 2020 +0000
362 @@ -0,0 +1,1 @@
362 @@ -0,0 +1,1 @@
363 +third
363 +third
364
364
365 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
365 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
366 diff -r 29114dbae42b -r 95c24699272e fourth
366 diff -r 29114dbae42b -r 95c24699272e fourth
367 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
367 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
368 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
368 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
369 @@ -0,0 +1,1 @@
369 @@ -0,0 +1,1 @@
370 +second
370 +second
371
371
372 $ hg --config diff.git=true log -r 8 -T "{diff()}"
372 $ hg --config diff.git=true log -r 8 -T "{diff()}"
373 diff --git a/second b/fourth
373 diff --git a/second b/fourth
374 rename from second
374 rename from second
375 rename to fourth
375 rename to fourth
376 diff --git a/third b/third
376 diff --git a/third b/third
377 new file mode 100644
377 new file mode 100644
378 --- /dev/null
378 --- /dev/null
379 +++ b/third
379 +++ b/third
380 @@ -0,0 +1,1 @@
380 @@ -0,0 +1,1 @@
381 +third
381 +third
382
382
383 $ cd ..
383 $ cd ..
384
384
385 latesttag() function:
385 latesttag() function:
386
386
387 $ hg init latesttag
387 $ hg init latesttag
388 $ cd latesttag
388 $ cd latesttag
389
389
390 $ echo a > file
390 $ echo a > file
391 $ hg ci -Am a -d '0 0'
391 $ hg ci -Am a -d '0 0'
392 adding file
392 adding file
393
393
394 $ echo b >> file
394 $ echo b >> file
395 $ hg ci -m b -d '1 0'
395 $ hg ci -m b -d '1 0'
396
396
397 $ echo c >> head1
397 $ echo c >> head1
398 $ hg ci -Am h1c -d '2 0'
398 $ hg ci -Am h1c -d '2 0'
399 adding head1
399 adding head1
400
400
401 $ hg update -q 1
401 $ hg update -q 1
402 $ echo d >> head2
402 $ echo d >> head2
403 $ hg ci -Am h2d -d '3 0'
403 $ hg ci -Am h2d -d '3 0'
404 adding head2
404 adding head2
405 created new head
405 created new head
406
406
407 $ echo e >> head2
407 $ echo e >> head2
408 $ hg ci -m h2e -d '4 0'
408 $ hg ci -m h2e -d '4 0'
409
409
410 $ hg merge -q
410 $ hg merge -q
411 $ hg ci -m merge -d '5 -3600'
411 $ hg ci -m merge -d '5 -3600'
412
412
413 $ hg tag -r 1 -m t1 -d '6 0' t1
413 $ hg tag -r 1 -m t1 -d '6 0' t1
414 $ hg tag -r 2 -m t2 -d '7 0' t2
414 $ hg tag -r 2 -m t2 -d '7 0' t2
415 $ hg tag -r 3 -m t3 -d '8 0' t3
415 $ hg tag -r 3 -m t3 -d '8 0' t3
416 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
416 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
417 $ hg tag -r 5 -m t5 -d '9 0' t5
417 $ hg tag -r 5 -m t5 -d '9 0' t5
418 $ hg tag -r 3 -m at3 -d '10 0' at3
418 $ hg tag -r 3 -m at3 -d '10 0' at3
419
419
420 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
420 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
421 @ 11: t3, C: 9, D: 8
421 @ 11: t3, C: 9, D: 8
422 |
422 |
423 o 10: t3, C: 8, D: 7
423 o 10: t3, C: 8, D: 7
424 |
424 |
425 o 9: t3, C: 7, D: 6
425 o 9: t3, C: 7, D: 6
426 |
426 |
427 o 8: t3, C: 6, D: 5
427 o 8: t3, C: 6, D: 5
428 |
428 |
429 o 7: t3, C: 5, D: 4
429 o 7: t3, C: 5, D: 4
430 |
430 |
431 o 6: t3, C: 4, D: 3
431 o 6: t3, C: 4, D: 3
432 |
432 |
433 o 5: t3, C: 3, D: 2
433 o 5: t3, C: 3, D: 2
434 |\
434 |\
435 | o 4: t3, C: 1, D: 1
435 | o 4: t3, C: 1, D: 1
436 | |
436 | |
437 | o 3: t3, C: 0, D: 0
437 | o 3: t3, C: 0, D: 0
438 | |
438 | |
439 o | 2: t1, C: 1, D: 1
439 o | 2: t1, C: 1, D: 1
440 |/
440 |/
441 o 1: t1, C: 0, D: 0
441 o 1: t1, C: 0, D: 0
442 |
442 |
443 o 0: null, C: 1, D: 1
443 o 0: null, C: 1, D: 1
444
444
445
445
446 $ cd ..
446 $ cd ..
447
447
448 Test filter() empty values:
448 Test filter() empty values:
449
449
450 $ hg log -R a -r 1 -T '{filter(desc|splitlines) % "{line}\n"}'
450 $ hg log -R a -r 1 -T '{filter(desc|splitlines) % "{line}\n"}'
451 other 1
451 other 1
452 other 2
452 other 2
453 other 3
453 other 3
454 $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1) % "{ifeq(key, "a", "{value}\n")}")}'
454 $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1) % "{ifeq(key, "a", "{value}\n")}")}'
455 0
455 0
456
456
457 0 should not be falsy
457 0 should not be falsy
458
458
459 $ hg log -R a -r 0 -T '{filter(revset("0:2"))}\n'
459 $ hg log -R a -r 0 -T '{filter(revset("0:2"))}\n'
460 0 1 2
460 0 1 2
461
461
462 Test filter() by expression:
462 Test filter() by expression:
463
463
464 $ hg log -R a -r 1 -T '{filter(desc|splitlines, ifcontains("1", line, "t"))}\n'
464 $ hg log -R a -r 1 -T '{filter(desc|splitlines, ifcontains("1", line, "t"))}\n'
465 other 1
465 other 1
466 $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1), ifeq(key, "b", "t"))}\n'
466 $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1), ifeq(key, "b", "t"))}\n'
467 b=1
467 b=1
468
468
469 Test filter() shouldn't crash:
469 Test filter() shouldn't crash:
470
470
471 $ hg log -R a -r 0 -T '{filter(extras)}\n'
471 $ hg log -R a -r 0 -T '{filter(extras)}\n'
472 branch=default
472 branch=default
473 $ hg log -R a -r 0 -T '{filter(files)}\n'
473 $ hg log -R a -r 0 -T '{filter(files)}\n'
474 a
474 a
475
475
476 Test filter() unsupported arguments:
476 Test filter() unsupported arguments:
477
477
478 $ hg log -R a -r 0 -T '{filter()}\n'
478 $ hg log -R a -r 0 -T '{filter()}\n'
479 hg: parse error: filter expects one or two arguments
479 hg: parse error: filter expects one or two arguments
480 [10]
480 [10]
481 $ hg log -R a -r 0 -T '{filter(date)}\n'
481 $ hg log -R a -r 0 -T '{filter(date)}\n'
482 hg: parse error: date is not iterable
482 hg: parse error: date is not iterable
483 [10]
483 [10]
484 $ hg log -R a -r 0 -T '{filter(rev)}\n'
484 $ hg log -R a -r 0 -T '{filter(rev)}\n'
485 hg: parse error: 0 is not iterable
485 hg: parse error: 0 is not iterable
486 [10]
486 [10]
487 $ hg log -R a -r 0 -T '{filter(desc|firstline)}\n'
487 $ hg log -R a -r 0 -T '{filter(desc|firstline)}\n'
488 hg: parse error: 'line 1' is not filterable
488 hg: parse error: 'line 1' is not filterable
489 [10]
489 [10]
490 $ hg log -R a -r 0 -T '{filter(manifest)}\n'
490 $ hg log -R a -r 0 -T '{filter(manifest)}\n'
491 hg: parse error: '0:a0c8bcbbb45c' is not filterable
491 hg: parse error: '0:a0c8bcbbb45c' is not filterable
492 [10]
492 [10]
493 $ hg log -R a -r 0 -T '{filter(succsandmarkers)}\n'
493 $ hg log -R a -r 0 -T '{filter(succsandmarkers)}\n'
494 hg: parse error: not filterable without template
494 hg: parse error: not filterable without template
495 [10]
495 [10]
496 $ hg log -R a -r 0 -T '{filter(desc|splitlines % "{line}", "")}\n'
496 $ hg log -R a -r 0 -T '{filter(desc|splitlines % "{line}", "")}\n'
497 hg: parse error: not filterable by expression
497 hg: parse error: not filterable by expression
498 [10]
498 [10]
499
499
500 Test manifest/get() can be join()-ed as string, though it's silly:
500 Test manifest/get() can be join()-ed as string, though it's silly:
501
501
502 $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
502 $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
503 1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
503 1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
504 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
504 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
505 d.e.f.a.u.l.t
505 d.e.f.a.u.l.t
506
506
507 Test join() over string
507 Test join() over string
508
508
509 $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
509 $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
510 1.1
510 1.1
511
511
512 Test join() over uniterable
512 Test join() over uniterable
513
513
514 $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
514 $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
515 hg: parse error: 11 is not iterable
515 hg: parse error: 11 is not iterable
516 [10]
516 [10]
517
517
518 Test min/max of integers
518 Test min/max of integers
519
519
520 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
520 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
521 9
521 9
522 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
522 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
523 10
523 10
524
524
525 Test min/max over map operation:
525 Test min/max over map operation:
526
526
527 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
527 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
528 at3
528 at3
529 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
529 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
530 t3
530 t3
531
531
532 Test min/max of strings:
532 Test min/max of strings:
533
533
534 $ hg log -R latesttag -l1 -T '{min(desc)}\n'
534 $ hg log -R latesttag -l1 -T '{min(desc)}\n'
535 3
535 3
536 $ hg log -R latesttag -l1 -T '{max(desc)}\n'
536 $ hg log -R latesttag -l1 -T '{max(desc)}\n'
537 t
537 t
538
538
539 Test min/max of non-iterable:
539 Test min/max of non-iterable:
540
540
541 $ hg debugtemplate '{min(1)}'
541 $ hg debugtemplate '{min(1)}'
542 hg: parse error: 1 is not iterable
542 hg: parse error: 1 is not iterable
543 (min first argument should be an iterable)
543 (min first argument should be an iterable)
544 [10]
544 [10]
545 $ hg debugtemplate '{max(2)}'
545 $ hg debugtemplate '{max(2)}'
546 hg: parse error: 2 is not iterable
546 hg: parse error: 2 is not iterable
547 (max first argument should be an iterable)
547 (max first argument should be an iterable)
548 [10]
548 [10]
549
549
550 $ hg log -R latesttag -l1 -T '{min(date)}'
550 $ hg log -R latesttag -l1 -T '{min(date)}'
551 hg: parse error: date is not iterable
551 hg: parse error: date is not iterable
552 (min first argument should be an iterable)
552 (min first argument should be an iterable)
553 [10]
553 [10]
554 $ hg log -R latesttag -l1 -T '{max(date)}'
554 $ hg log -R latesttag -l1 -T '{max(date)}'
555 hg: parse error: date is not iterable
555 hg: parse error: date is not iterable
556 (max first argument should be an iterable)
556 (max first argument should be an iterable)
557 [10]
557 [10]
558
558
559 Test min/max of empty sequence:
559 Test min/max of empty sequence:
560
560
561 $ hg debugtemplate '{min("")}'
561 $ hg debugtemplate '{min("")}'
562 hg: parse error: empty string
562 hg: parse error: empty string
563 (min first argument should be an iterable)
563 (min first argument should be an iterable)
564 [10]
564 [10]
565 $ hg debugtemplate '{max("")}'
565 $ hg debugtemplate '{max("")}'
566 hg: parse error: empty string
566 hg: parse error: empty string
567 (max first argument should be an iterable)
567 (max first argument should be an iterable)
568 [10]
568 [10]
569 $ hg debugtemplate '{min(dict())}'
569 $ hg debugtemplate '{min(dict())}'
570 hg: parse error: empty sequence
570 hg: parse error: empty sequence
571 (min first argument should be an iterable)
571 (min first argument should be an iterable)
572 [10]
572 [10]
573 $ hg debugtemplate '{max(dict())}'
573 $ hg debugtemplate '{max(dict())}'
574 hg: parse error: empty sequence
574 hg: parse error: empty sequence
575 (max first argument should be an iterable)
575 (max first argument should be an iterable)
576 [10]
576 [10]
577 $ hg debugtemplate '{min(dict() % "")}'
577 $ hg debugtemplate '{min(dict() % "")}'
578 hg: parse error: empty sequence
578 hg: parse error: empty sequence
579 (min first argument should be an iterable)
579 (min first argument should be an iterable)
580 [10]
580 [10]
581 $ hg debugtemplate '{max(dict() % "")}'
581 $ hg debugtemplate '{max(dict() % "")}'
582 hg: parse error: empty sequence
582 hg: parse error: empty sequence
583 (max first argument should be an iterable)
583 (max first argument should be an iterable)
584 [10]
584 [10]
585
585
586 Test min/max of if() result
586 Test min/max of if() result
587
587
588 $ cd latesttag
588 $ cd latesttag
589 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
589 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
590 9
590 9
591 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
591 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
592 10
592 10
593 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
593 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
594 9
594 9
595 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
595 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
596 10
596 10
597 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
597 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
598 9
598 9
599 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
599 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
600 10
600 10
601 $ cd ..
601 $ cd ..
602
602
603 Test laziness of if() then/else clause
603 Test laziness of if() then/else clause
604
604
605 $ hg debugtemplate '{count(0)}'
605 $ hg debugtemplate '{count(0)}'
606 hg: parse error: not countable
606 hg: parse error: not countable
607 (incompatible use of template filter 'count')
607 (incompatible use of template filter 'count')
608 [10]
608 [10]
609 $ hg debugtemplate '{if(true, "", count(0))}'
609 $ hg debugtemplate '{if(true, "", count(0))}'
610 $ hg debugtemplate '{if(false, count(0), "")}'
610 $ hg debugtemplate '{if(false, count(0), "")}'
611 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
611 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
612 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
612 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
613 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
613 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
614 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
614 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
615
615
616 Test search() function:
616 Test search() function:
617
617
618 $ hg log -R a -r2 -T '{desc}\n'
618 $ hg log -R a -r2 -T '{desc}\n'
619 no person
619 no person
620
620
621 $ hg log -R a -r2 -T '{search(r"p.*", desc)}\n'
621 $ hg log -R a -r2 -T '{search(r"p.*", desc)}\n'
622 person
622 person
623
623
624 as bool
624 as bool
625
625
626 $ hg log -R a -r2 -T '{if(search(r"p.*", desc), "", "not ")}found\n'
626 $ hg log -R a -r2 -T '{if(search(r"p.*", desc), "", "not ")}found\n'
627 found
627 found
628 $ hg log -R a -r2 -T '{if(search(r"q", desc), "", "not ")}found\n'
628 $ hg log -R a -r2 -T '{if(search(r"q", desc), "", "not ")}found\n'
629 not found
629 not found
630
630
631 match as json
631 match as json
632
632
633 $ hg log -R a -r2 -T '{search(r"(no) p.*", desc)|json}\n'
633 $ hg log -R a -r2 -T '{search(r"(no) p.*", desc)|json}\n'
634 {"0": "no person", "1": "no"}
634 {"0": "no person", "1": "no"}
635 $ hg log -R a -r2 -T '{search(r"q", desc)|json}\n'
635 $ hg log -R a -r2 -T '{search(r"q", desc)|json}\n'
636 null
636 null
637
637
638 group reference
638 group reference
639
639
640 $ hg log -R a -r2 -T '{search(r"(no) (p.*)", desc) % "{1|upper} {2|hex}"}\n'
640 $ hg log -R a -r2 -T '{search(r"(no) (p.*)", desc) % "{1|upper} {2|hex}"}\n'
641 NO 706572736f6e
641 NO 706572736f6e
642 $ hg log -R a -r2 -T '{search(r"(?P<foo>[a-z]*)", desc) % "{foo}"}\n'
642 $ hg log -R a -r2 -T '{search(r"(?P<foo>[a-z]*)", desc) % "{foo}"}\n'
643 no
643 no
644 $ hg log -R a -r2 -T '{search(r"(?P<foo>[a-z]*)", desc).foo}\n'
644 $ hg log -R a -r2 -T '{search(r"(?P<foo>[a-z]*)", desc).foo}\n'
645 no
645 no
646
646
647 group reference with no match
647 group reference with no match
648
648
649 $ hg log -R a -r2 -T '{search(r"q", desc) % "match: {0}"}\n'
649 $ hg log -R a -r2 -T '{search(r"q", desc) % "match: {0}"}\n'
650
650
651
651
652 bad group names
652 bad group names
653
653
654 $ hg log -R a -r2 -T '{search(r"(?P<0>.)", desc) % "{0}"}\n'
654 $ hg log -R a -r2 -T '{search(r"(?P<0>.)", desc) % "{0}"}\n'
655 hg: parse error: search got an invalid pattern: (?P<0>.)
655 hg: parse error: search got an invalid pattern: (?P<0>.)
656 [10]
656 [10]
657 $ hg log -R a -r2 -T '{search(r"(?P<repo>.)", desc) % "{repo}"}\n'
657 $ hg log -R a -r2 -T '{search(r"(?P<repo>.)", desc) % "{repo}"}\n'
658 hg: parse error: invalid group 'repo' in search pattern: (?P<repo>.)
658 hg: parse error: invalid group 'repo' in search pattern: (?P<repo>.)
659 [10]
659 [10]
660
660
661 Test the sub function of templating for expansion:
661 Test the sub function of templating for expansion:
662
662
663 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
663 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
664 xx
664 xx
665
665
666 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
666 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
667 hg: parse error: sub got an invalid pattern: [
667 hg: parse error: sub got an invalid pattern: [
668 [10]
668 [10]
669 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
669 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
670 hg: parse error: sub got an invalid replacement: \1
670 hg: parse error: sub got an invalid replacement: \1
671 [10]
671 [10]
672
672
673 Test the strip function with chars specified:
673 Test the strip function with chars specified:
674
674
675 $ hg log -R latesttag --template '{desc}\n'
675 $ hg log -R latesttag --template '{desc}\n'
676 at3
676 at3
677 t5
677 t5
678 t4
678 t4
679 t3
679 t3
680 t2
680 t2
681 t1
681 t1
682 merge
682 merge
683 h2e
683 h2e
684 h2d
684 h2d
685 h1c
685 h1c
686 b
686 b
687 a
687 a
688
688
689 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
689 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
690 at3
690 at3
691 5
691 5
692 4
692 4
693 3
693 3
694 2
694 2
695 1
695 1
696 merg
696 merg
697 h2
697 h2
698 h2d
698 h2d
699 h1c
699 h1c
700 b
700 b
701 a
701 a
702
702
703 Test date format:
703 Test date format:
704
704
705 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
705 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
706 date: 70 01 01 10 +0000
706 date: 70 01 01 10 +0000
707 date: 70 01 01 09 +0000
707 date: 70 01 01 09 +0000
708 date: 70 01 01 04 +0000
708 date: 70 01 01 04 +0000
709 date: 70 01 01 08 +0000
709 date: 70 01 01 08 +0000
710 date: 70 01 01 07 +0000
710 date: 70 01 01 07 +0000
711 date: 70 01 01 06 +0000
711 date: 70 01 01 06 +0000
712 date: 70 01 01 05 +0100
712 date: 70 01 01 05 +0100
713 date: 70 01 01 04 +0000
713 date: 70 01 01 04 +0000
714 date: 70 01 01 03 +0000
714 date: 70 01 01 03 +0000
715 date: 70 01 01 02 +0000
715 date: 70 01 01 02 +0000
716 date: 70 01 01 01 +0000
716 date: 70 01 01 01 +0000
717 date: 70 01 01 00 +0000
717 date: 70 01 01 00 +0000
718
718
719 Test invalid date:
719 Test invalid date:
720
720
721 $ hg log -R latesttag -T '{date(rev)}\n'
721 $ hg log -R latesttag -T '{date(rev)}\n'
722 hg: parse error: date expects a date information
722 hg: parse error: date expects a date information
723 [10]
723 [10]
724
724
725 Set up repository containing template fragments in commit metadata:
725 Set up repository containing template fragments in commit metadata:
726
726
727 $ hg init r
727 $ hg init r
728 $ cd r
728 $ cd r
729 $ echo a > a
729 $ echo a > a
730 $ hg ci -Am '{rev}'
730 $ hg ci -Am '{rev}'
731 adding a
731 adding a
732
732
733 $ hg branch -q 'text.{rev}'
733 $ hg branch -q 'text.{rev}'
734 $ echo aa >> aa
734 $ echo aa >> aa
735 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
735 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
736
736
737 color effect can be specified without quoting:
737 color effect can be specified without quoting:
738
738
739 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
739 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
740 \x1b[0;31mtext\x1b[0m (esc)
740 \x1b[0;31mtext\x1b[0m (esc)
741
741
742 color effects can be nested (issue5413)
742 color effects can be nested (issue5413)
743
743
744 $ hg debugtemplate --color=always \
744 $ hg debugtemplate --color=always \
745 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
745 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
746 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
746 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
747
747
748 pad() should interact well with color codes (issue5416)
748 pad() should interact well with color codes (issue5416)
749
749
750 $ hg debugtemplate --color=always \
750 $ hg debugtemplate --color=always \
751 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
751 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
752 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
752 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
753
753
754 pad() with truncate has to strip color codes, though
754 pad() with truncate has to strip color codes, though
755
755
756 $ hg debugtemplate --color=always \
756 $ hg debugtemplate --color=always \
757 > '{pad(label(red, "scarlet"), 5, truncate=true)}\n'
757 > '{pad(label(red, "scarlet"), 5, truncate=true)}\n'
758 scarl
758 scarl
759
759
760 label should be no-op if color is disabled:
760 label should be no-op if color is disabled:
761
761
762 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
762 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
763 text
763 text
764 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
764 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
765 text
765 text
766
766
767 Test branches inside if statement:
767 Test branches inside if statement:
768
768
769 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
769 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
770 no
770 no
771
771
772 Test dict constructor:
772 Test dict constructor:
773
773
774 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
774 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
775 y=f7769ec2ab97 x=0
775 y=f7769ec2ab97 x=0
776 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
776 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
777 x=0
777 x=0
778 y=f7769ec2ab97
778 y=f7769ec2ab97
779 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
779 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
780 {"x": 0, "y": "f7769ec2ab97"}
780 {"x": 0, "y": "f7769ec2ab97"}
781 $ hg log -r 0 -T '{dict()|json}\n'
781 $ hg log -r 0 -T '{dict()|json}\n'
782 {}
782 {}
783
783
784 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
784 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
785 rev=0 node=f7769ec2ab97
785 rev=0 node=f7769ec2ab97
786 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
786 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
787 rev=0 node=f7769ec2ab97
787 rev=0 node=f7769ec2ab97
788
788
789 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
789 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
790 hg: parse error: duplicated dict key 'rev' inferred
790 hg: parse error: duplicated dict key 'rev' inferred
791 [10]
791 [10]
792 $ hg log -r 0 -T '{dict(node, node|short)}\n'
792 $ hg log -r 0 -T '{dict(node, node|short)}\n'
793 hg: parse error: duplicated dict key 'node' inferred
793 hg: parse error: duplicated dict key 'node' inferred
794 [10]
794 [10]
795 $ hg log -r 0 -T '{dict(1 + 2)}'
795 $ hg log -r 0 -T '{dict(1 + 2)}'
796 hg: parse error: dict key cannot be inferred
796 hg: parse error: dict key cannot be inferred
797 [10]
797 [10]
798
798
799 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
799 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
800 hg: parse error: dict got multiple values for keyword argument 'x'
800 hg: parse error: dict got multiple values for keyword argument 'x'
801 [10]
801 [10]
802
802
803 Test get function:
803 Test get function:
804
804
805 $ hg log -r 0 --template '{get(extras, "branch")}\n'
805 $ hg log -r 0 --template '{get(extras, "branch")}\n'
806 default
806 default
807 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
807 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
808 default
808 default
809 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
809 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
810 hg: parse error: not a dictionary
810 hg: parse error: not a dictionary
811 (get() expects a dict as first argument)
811 (get() expects a dict as first argument)
812 [10]
812 [10]
813
813
814 Test json filter applied to wrapped object:
814 Test json filter applied to wrapped object:
815
815
816 $ hg log -r0 -T '{files|json}\n'
816 $ hg log -r0 -T '{files|json}\n'
817 ["a"]
817 ["a"]
818 $ hg log -r0 -T '{extras|json}\n'
818 $ hg log -r0 -T '{extras|json}\n'
819 {"branch": "default"}
819 {"branch": "default"}
820 $ hg log -r0 -T '{date|json}\n'
820 $ hg log -r0 -T '{date|json}\n'
821 [0, 0]
821 [0, 0]
822 $ hg log -r0 -T '{revset(":")|json}\n'
822 $ hg log -r0 -T '{revset(":")|json}\n'
823 [0, 1]
823 [0, 1]
824
824
825 Test json filter applied to map result:
825 Test json filter applied to map result:
826
826
827 $ hg log -r0 -T '{json(extras % "{key}")}\n'
827 $ hg log -r0 -T '{json(extras % "{key}")}\n'
828 ["branch"]
828 ["branch"]
829
829
830 Test localdate(date, tz) function:
830 Test localdate(date, tz) function:
831
831
832 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
832 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
833 1970-01-01 09:00 +0900
833 1970-01-01 09:00 +0900
834 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
834 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
835 1970-01-01 00:00 +0000
835 1970-01-01 00:00 +0000
836 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
836 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
837 hg: parse error: localdate expects a timezone
837 hg: parse error: localdate expects a timezone
838 [10]
838 [10]
839 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
839 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
840 1970-01-01 02:00 +0200
840 1970-01-01 02:00 +0200
841 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
841 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
842 1970-01-01 00:00 +0000
842 1970-01-01 00:00 +0000
843 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
843 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
844 1970-01-01 00:00 +0000
844 1970-01-01 00:00 +0000
845 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
845 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
846 hg: parse error: localdate expects a timezone
846 hg: parse error: localdate expects a timezone
847 [10]
847 [10]
848 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
848 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
849 hg: parse error: localdate expects a timezone
849 hg: parse error: localdate expects a timezone
850 [10]
850 [10]
851
851
852 Test shortest(node) function:
852 Test shortest(node) function:
853
853
854 $ echo b > b
854 $ echo b > b
855 $ hg ci -qAm b
855 $ hg ci -qAm b
856 $ hg log --template '{shortest(node)}\n'
856 $ hg log --template '{shortest(node)}\n'
857 e777
857 e777
858 bcc7
858 bcc7
859 f776
859 f776
860 $ hg log --template '{shortest(node, 10)}\n'
860 $ hg log --template '{shortest(node, 10)}\n'
861 e777603221
861 e777603221
862 bcc7ff960b
862 bcc7ff960b
863 f7769ec2ab
863 f7769ec2ab
864 $ hg log --template '{shortest(node, 1)}\n' -r null
864 $ hg log --template '{shortest(node, 1)}\n' -r null
865 00
865 00
866 $ hg log --template '{node|shortest}\n' -l1
866 $ hg log --template '{node|shortest}\n' -l1
867 e777
867 e777
868
868
869 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
869 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
870 f7769ec2ab
870 f7769ec2ab
871 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
871 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
872 hg: parse error: shortest() expects an integer minlength
872 hg: parse error: shortest() expects an integer minlength
873 [10]
873 [10]
874
874
875 $ hg log -r 'wdir()' -T '{node|shortest}\n'
875 $ hg log -r 'wdir()' -T '{node|shortest}\n'
876 ffff
876 ffff
877
877
878 $ hg log --template '{shortest("f")}\n' -l1
878 $ hg log --template '{shortest("f")}\n' -l1
879 f
879 f
880
880
881 $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
881 $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
882 0123456789012345678901234567890123456789
882 0123456789012345678901234567890123456789
883
883
884 $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
884 $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
885 01234567890123456789012345678901234567890123456789
885 01234567890123456789012345678901234567890123456789
886
886
887 $ hg log --template '{shortest("not a hex string")}\n' -l1
887 $ hg log --template '{shortest("not a hex string")}\n' -l1
888 not a hex string
888 not a hex string
889
889
890 $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
890 $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
891 not a hex string, but it's 40 bytes long
891 not a hex string, but it's 40 bytes long
892
892
893 $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
893 $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
894 ffff
894 ffff
895
895
896 $ hg log --template '{shortest("fffffff")}\n' -l1
896 $ hg log --template '{shortest("fffffff")}\n' -l1
897 ffff
897 ffff
898
898
899 $ hg log --template '{shortest("ff")}\n' -l1
899 $ hg log --template '{shortest("ff")}\n' -l1
900 ffff
900 ffff
901
901
902 $ cd ..
902 $ cd ..
903
903
904 Test shortest(node) with the repo having short hash collision:
904 Test shortest(node) with the repo having short hash collision:
905
905
906 $ hg init hashcollision
906 $ hg init hashcollision
907 $ cd hashcollision
907 $ cd hashcollision
908 $ cat <<EOF >> .hg/hgrc
908 $ cat <<EOF >> .hg/hgrc
909 > [experimental]
909 > [experimental]
910 > evolution.createmarkers=True
910 > evolution.createmarkers=True
911 > EOF
911 > EOF
912 $ echo 0 > a
912 $ echo 0 > a
913 $ hg ci -qAm 0
913 $ hg ci -qAm 0
914 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
914 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
915 > hg up -q 0
915 > hg up -q 0
916 > echo $i > a
916 > echo $i > a
917 > hg ci -qm $i
917 > hg ci -qm $i
918 > done
918 > done
919 $ hg up -q null
919 $ hg up -q null
920 $ hg log -r0: -T '{rev}:{node}\n'
920 $ hg log -r0: -T '{rev}:{node}\n'
921 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
921 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
922 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
922 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
923 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
923 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
924 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
924 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
925 4:10776689e627b465361ad5c296a20a487e153ca4
925 4:10776689e627b465361ad5c296a20a487e153ca4
926 5:a00be79088084cb3aff086ab799f8790e01a976b
926 5:a00be79088084cb3aff086ab799f8790e01a976b
927 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
927 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
928 7:a0457b3450b8e1b778f1163b31a435802987fe5d
928 7:a0457b3450b8e1b778f1163b31a435802987fe5d
929 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
929 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
930 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
930 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
931 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
931 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
932 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
932 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
933 1 new obsolescence markers
933 1 new obsolescence markers
934 obsoleted 1 changesets
934 obsoleted 1 changesets
935 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
935 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
936 1 new obsolescence markers
936 1 new obsolescence markers
937 obsoleted 1 changesets
937 obsoleted 1 changesets
938 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
938 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
939 1 new obsolescence markers
939 1 new obsolescence markers
940 obsoleted 1 changesets
940 obsoleted 1 changesets
941
941
942 nodes starting with '11' (we don't have the revision number '11' though)
942 nodes starting with '11' (we don't have the revision number '11' though)
943
943
944 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
944 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
945 1:1142
945 1:1142
946 2:1140
946 2:1140
947 3:11d
947 3:11d
948
948
949 '5:a00' is hidden, but still we have two nodes starting with 'a0'
949 '5:a00' is hidden, but still we have two nodes starting with 'a0'
950
950
951 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
951 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
952 6:a0b
952 6:a0b
953 7:a04
953 7:a04
954
954
955 node '10' conflicts with the revision number '10' even if it is hidden
955 node '10' conflicts with the revision number '10' even if it is hidden
956 (we could exclude hidden revision numbers, but currently we don't)
956 (we could exclude hidden revision numbers, but currently we don't)
957
957
958 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
958 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
959 4:107
959 4:107
960 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
960 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
961 4:107
961 4:107
962
962
963 $ hg --config experimental.revisions.prefixhexnode=yes log -r 4 -T '{rev}:{shortest(node, 0)}\n'
963 $ hg --config experimental.revisions.prefixhexnode=yes log -r 4 -T '{rev}:{shortest(node, 0)}\n'
964 4:x10
964 4:x10
965 $ hg --config experimental.revisions.prefixhexnode=yes log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
965 $ hg --config experimental.revisions.prefixhexnode=yes log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
966 4:x10
966 4:x10
967
967
968 node 'c562' should be unique if the other 'c562' nodes are hidden
968 node 'c562' should be unique if the other 'c562' nodes are hidden
969 (but we don't try the slow path to filter out hidden nodes for now)
969 (but we don't try the slow path to filter out hidden nodes for now)
970
970
971 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
971 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
972 8:c5625
972 8:c5625
973 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
973 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
974 8:c5625
974 8:c5625
975 9:c5623
975 9:c5623
976 10:c562d
976 10:c562d
977
977
978 $ cd ..
978 $ cd ..
979
979
980 Test prefixhexnode when the first character of the hash is 0.
980 Test prefixhexnode when the first character of the hash is 0.
981 $ hg init hashcollision2
981 $ hg init hashcollision2
982 $ cd hashcollision2
982 $ cd hashcollision2
983 $ cat <<EOF >> .hg/hgrc
983 $ cat <<EOF >> .hg/hgrc
984 > [experimental]
984 > [experimental]
985 > evolution.createmarkers=True
985 > evolution.createmarkers=True
986 > EOF
986 > EOF
987 $ echo 0 > a
987 $ echo 0 > a
988 $ hg ci -qAm 0
988 $ hg ci -qAm 0
989 $ echo 21 > a
989 $ echo 21 > a
990 $ hg ci -qm 21
990 $ hg ci -qm 21
991 $ hg up -q null
991 $ hg up -q null
992 $ hg log -r0: -T '{rev}:{node}\n'
992 $ hg log -r0: -T '{rev}:{node}\n'
993 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
993 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
994 1:0cf177ba2b1dc3862a00fb81715fec90950201be
994 1:0cf177ba2b1dc3862a00fb81715fec90950201be
995
995
996 we need the 'x' prefix to ensure we aren't colliding with rev0. We identify
996 we need the 'x' prefix to ensure we aren't colliding with rev0. We identify
997 the collision with nullid if we aren't using disambiguatewithin, so we need to set
997 the collision with nullid if we aren't using disambiguatewithin, so we need to set
998 that as well.
998 that as well.
999 $ hg --config experimental.revisions.disambiguatewithin='descendants(0)' \
999 $ hg --config experimental.revisions.disambiguatewithin='descendants(0)' \
1000 > --config experimental.revisions.prefixhexnode=yes \
1000 > --config experimental.revisions.prefixhexnode=yes \
1001 > log -r 1 -T '{rev}:{shortest(node, 0)}\n'
1001 > log -r 1 -T '{rev}:{shortest(node, 0)}\n'
1002 1:x0
1002 1:x0
1003
1003
1004 $ hg debugobsolete 0cf177ba2b1dc3862a00fb81715fec90950201be
1004 $ hg debugobsolete 0cf177ba2b1dc3862a00fb81715fec90950201be
1005 1 new obsolescence markers
1005 1 new obsolescence markers
1006 obsoleted 1 changesets
1006 obsoleted 1 changesets
1007 $ hg up -q 0
1007 $ hg up -q 0
1008 $ echo 61 > a
1008 $ echo 61 > a
1009 $ hg ci -m 61
1009 $ hg ci -m 61
1010 $ hg log -r0: -T '{rev}:{node}\n'
1010 $ hg log -r0: -T '{rev}:{node}\n'
1011 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
1011 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
1012 2:01384dde84b3a511ae0835f35ac40bd806c99bb8
1012 2:01384dde84b3a511ae0835f35ac40bd806c99bb8
1013
1013
1014 we still have the 'x' prefix because '0' is still the shortest prefix, since
1014 we still have the 'x' prefix because '0' is still the shortest prefix, since
1015 rev1's '0c' is hidden.
1015 rev1's '0c' is hidden.
1016 $ hg --config experimental.revisions.disambiguatewithin=0:-1-0 \
1016 $ hg --config experimental.revisions.disambiguatewithin=0:-1-0 \
1017 > --config experimental.revisions.prefixhexnode=yes \
1017 > --config experimental.revisions.prefixhexnode=yes \
1018 > log -r 0:-1-0 -T '{rev}:{shortest(node, 0)}\n'
1018 > log -r 0:-1-0 -T '{rev}:{shortest(node, 0)}\n'
1019 2:x0
1019 2:x0
1020
1020
1021 we don't have the 'x' prefix on 2 because '01' is not a synonym for rev1.
1021 we don't have the 'x' prefix on 2 because '01' is not a synonym for rev1.
1022 $ hg --config experimental.revisions.disambiguatewithin=0:-1-0 \
1022 $ hg --config experimental.revisions.disambiguatewithin=0:-1-0 \
1023 > --config experimental.revisions.prefixhexnode=yes \
1023 > --config experimental.revisions.prefixhexnode=yes \
1024 > log -r 0:-1-0 -T '{rev}:{shortest(node, 0)}\n' --hidden
1024 > log -r 0:-1-0 -T '{rev}:{shortest(node, 0)}\n' --hidden
1025 1:0c
1025 1:0c
1026 2:01
1026 2:01
1027
1027
1028 $ cd ..
1028 $ cd ..
1029
1029
1030 Test pad function
1030 Test pad function
1031
1031
1032 $ cd r
1032 $ cd r
1033
1033
1034 $ hg log --template '{pad(rev, 20)} {author|user}\n'
1034 $ hg log --template '{pad(rev, 20)} {author|user}\n'
1035 2 test
1035 2 test
1036 1 {node|short}
1036 1 {node|short}
1037 0 test
1037 0 test
1038
1038
1039 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
1039 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
1040 2 test
1040 2 test
1041 1 {node|short}
1041 1 {node|short}
1042 0 test
1042 0 test
1043
1043
1044 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
1044 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
1045 2------------------- test
1045 2------------------- test
1046 1------------------- {node|short}
1046 1------------------- {node|short}
1047 0------------------- test
1047 0------------------- test
1048
1048
1049 $ hg log --template '{pad(author, 5, "-", False, True)}\n'
1049 $ hg log --template '{pad(author, 5, "-", False, True)}\n'
1050 test-
1050 test-
1051 {node
1051 {node
1052 test-
1052 test-
1053 $ hg log --template '{pad(author, 5, "-", True, True)}\n'
1053 $ hg log --template '{pad(author, 5, "-", True, True)}\n'
1054 -test
1054 -test
1055 hort}
1055 hort}
1056 -test
1056 -test
1057
1057
1058 Test template string in pad function
1058 Test template string in pad function
1059
1059
1060 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
1060 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
1061 {0} test
1061 {0} test
1062
1062
1063 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
1063 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
1064 \{rev} test
1064 \{rev} test
1065
1065
1066 Test width argument passed to pad function
1066 Test width argument passed to pad function
1067
1067
1068 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
1068 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
1069 0 test
1069 0 test
1070 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
1070 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
1071 hg: parse error: pad() expects an integer width
1071 hg: parse error: pad() expects an integer width
1072 [10]
1072 [10]
1073
1073
1074 Test invalid fillchar passed to pad function
1074 Test invalid fillchar passed to pad function
1075
1075
1076 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
1076 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
1077 hg: parse error: pad() expects a single fill character
1077 hg: parse error: pad() expects a single fill character
1078 [10]
1078 [10]
1079 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
1079 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
1080 hg: parse error: pad() expects a single fill character
1080 hg: parse error: pad() expects a single fill character
1081 [10]
1081 [10]
1082
1082
1083 Test boolean argument passed to pad function
1083 Test boolean argument passed to pad function
1084
1084
1085 no crash
1085 no crash
1086
1086
1087 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
1087 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
1088 ---------0
1088 ---------0
1089
1089
1090 string/literal
1090 string/literal
1091
1091
1092 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
1092 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
1093 ---------0
1093 ---------0
1094 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
1094 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
1095 0---------
1095 0---------
1096 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
1096 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
1097 0---------
1097 0---------
1098
1098
1099 unknown keyword is evaluated to ''
1099 unknown keyword is evaluated to ''
1100
1100
1101 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
1101 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
1102 0---------
1102 0---------
1103
1103
1104 Test separate function
1104 Test separate function
1105
1105
1106 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
1106 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
1107 a-b-c
1107 a-b-c
1108 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
1108 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
1109 0:f7769ec2ab97 test default
1109 0:f7769ec2ab97 test default
1110 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
1110 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
1111 a \x1b[0;31mb\x1b[0m c d (esc)
1111 a \x1b[0;31mb\x1b[0m c d (esc)
1112
1112
1113 Test boolean expression/literal passed to if function
1113 Test boolean expression/literal passed to if function
1114
1114
1115 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
1115 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
1116 rev 0 is True
1116 rev 0 is True
1117 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
1117 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
1118 literal 0 is True as well
1118 literal 0 is True as well
1119 $ hg log -r 0 -T '{if(min(revset(r"0")), "0 of hybriditem is also True")}\n'
1119 $ hg log -r 0 -T '{if(min(revset(r"0")), "0 of hybriditem is also True")}\n'
1120 0 of hybriditem is also True
1120 0 of hybriditem is also True
1121 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
1121 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
1122 empty string is False
1122 empty string is False
1123 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
1123 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
1124 empty list is False
1124 empty list is False
1125 $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n'
1125 $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n'
1126 non-empty list is True
1126 non-empty list is True
1127 $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n'
1127 $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n'
1128 list of empty strings is True
1128 list of empty strings is True
1129 $ hg log -r 0 -T '{if(true, "true is True")}\n'
1129 $ hg log -r 0 -T '{if(true, "true is True")}\n'
1130 true is True
1130 true is True
1131 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
1131 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
1132 false is False
1132 false is False
1133 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
1133 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
1134 non-empty string is True
1134 non-empty string is True
1135
1135
1136 Test ifcontains function
1136 Test ifcontains function
1137
1137
1138 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
1138 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
1139 2 is in the string
1139 2 is in the string
1140 1 is not
1140 1 is not
1141 0 is in the string
1141 0 is in the string
1142
1142
1143 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
1143 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
1144 2 is in the string
1144 2 is in the string
1145 1 is not
1145 1 is not
1146 0 is in the string
1146 0 is in the string
1147
1147
1148 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
1148 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
1149 2 did not add a
1149 2 did not add a
1150 1 did not add a
1150 1 did not add a
1151 0 added a
1151 0 added a
1152
1152
1153 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
1153 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
1154 2 is parent of 1
1154 2 is parent of 1
1155 1
1155 1
1156 0
1156 0
1157
1157
1158 $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n'
1158 $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n'
1159 t
1159 t
1160 $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n'
1160 $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n'
1161 t
1161 t
1162 $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n'
1162 $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n'
1163 f
1163 f
1164 $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n'
1164 $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n'
1165 t
1165 t
1166
1166
1167 Test revset function
1167 Test revset function
1168
1168
1169 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
1169 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
1170 2 current rev
1170 2 current rev
1171 1 not current rev
1171 1 not current rev
1172 0 not current rev
1172 0 not current rev
1173
1173
1174 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
1174 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
1175 2 match rev
1175 2 match rev
1176 1 match rev
1176 1 match rev
1177 0 not match rev
1177 0 not match rev
1178
1178
1179 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
1179 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
1180 type not match
1180 type not match
1181
1181
1182 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
1182 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
1183 2 Parents: 1
1183 2 Parents: 1
1184 1 Parents: 0
1184 1 Parents: 0
1185 0 Parents:
1185 0 Parents:
1186
1186
1187 $ cat >> .hg/hgrc <<EOF
1187 $ cat >> .hg/hgrc <<EOF
1188 > [revsetalias]
1188 > [revsetalias]
1189 > myparents(\$1) = parents(\$1)
1189 > myparents(\$1) = parents(\$1)
1190 > EOF
1190 > EOF
1191 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
1191 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
1192 2 Parents: 1
1192 2 Parents: 1
1193 1 Parents: 0
1193 1 Parents: 0
1194 0 Parents:
1194 0 Parents:
1195
1195
1196 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
1196 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
1197 Rev: 2
1197 Rev: 2
1198 Ancestor: 0
1198 Ancestor: 0
1199 Ancestor: 1
1199 Ancestor: 1
1200 Ancestor: 2
1200 Ancestor: 2
1201
1201
1202 Rev: 1
1202 Rev: 1
1203 Ancestor: 0
1203 Ancestor: 0
1204 Ancestor: 1
1204 Ancestor: 1
1205
1205
1206 Rev: 0
1206 Rev: 0
1207 Ancestor: 0
1207 Ancestor: 0
1208
1208
1209 $ hg log --template '{revset("TIP"|lower)}\n' -l1
1209 $ hg log --template '{revset("TIP"|lower)}\n' -l1
1210 2
1210 2
1211
1211
1212 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
1212 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
1213 2
1213 2
1214
1214
1215 a list template is evaluated for each item of revset/parents
1215 a list template is evaluated for each item of revset/parents
1216
1216
1217 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
1217 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
1218 2 p: 1:bcc7ff960b8e
1218 2 p: 1:bcc7ff960b8e
1219 1 p: 0:f7769ec2ab97
1219 1 p: 0:f7769ec2ab97
1220 0 p:
1220 0 p:
1221
1221
1222 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
1222 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
1223 2 p: 1:bcc7ff960b8e -1:000000000000
1223 2 p: 1:bcc7ff960b8e -1:000000000000
1224 1 p: 0:f7769ec2ab97 -1:000000000000
1224 1 p: 0:f7769ec2ab97 -1:000000000000
1225 0 p: -1:000000000000 -1:000000000000
1225 0 p: -1:000000000000 -1:000000000000
1226
1226
1227 therefore, 'revcache' should be recreated for each rev
1227 therefore, 'revcache' should be recreated for each rev
1228
1228
1229 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
1229 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
1230 2 aa b
1230 2 aa b
1231 p
1231 p
1232 1
1232 1
1233 p a
1233 p a
1234 0 a
1234 0 a
1235 p
1235 p
1236
1236
1237 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
1237 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
1238 2 aa b
1238 2 aa b
1239 p
1239 p
1240 1
1240 1
1241 p a
1241 p a
1242 0 a
1242 0 a
1243 p
1243 p
1244
1244
1245 a revset item must be evaluated as an integer revision, not an offset from tip
1245 a revset item must be evaluated as an integer revision, not an offset from tip
1246
1246
1247 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
1247 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
1248 -1:000000000000
1248 -1:000000000000
1249 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
1249 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
1250 -1:000000000000
1250 -1:000000000000
1251
1251
1252 join() should pick '{rev}' from revset items:
1252 join() should pick '{rev}' from revset items:
1253
1253
1254 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
1254 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
1255 4, 5
1255 4, 5
1256
1256
1257 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
1257 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
1258 default. join() should agree with the default formatting:
1258 default. join() should agree with the default formatting:
1259
1259
1260 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
1260 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
1261 5:13207e5a10d9, 4:bbe44766e73d
1261 5:13207e5a10d9, 4:bbe44766e73d
1262
1262
1263 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
1263 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
1264 5:13207e5a10d9fd28ec424934298e176197f2c67f,
1264 5:13207e5a10d9fd28ec424934298e176197f2c67f,
1265 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1265 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1266
1266
1267 for historical reasons, revset() supports old-style list template
1267 for historical reasons, revset() supports old-style list template
1268
1268
1269 $ hg log -T '{revset(":")}\n' -l1 \
1269 $ hg log -T '{revset(":")}\n' -l1 \
1270 > --config templates.start_revisions='"["' \
1270 > --config templates.start_revisions='"["' \
1271 > --config templates.end_revisions='"]"' \
1271 > --config templates.end_revisions='"]"' \
1272 > --config templates.revision='"{revision}, "' \
1272 > --config templates.revision='"{revision}, "' \
1273 > --config templates.last_revision='"{revision}"'
1273 > --config templates.last_revision='"{revision}"'
1274 [0, 1, 2]
1274 [0, 1, 2]
1275 $ hg log -T '{revset(":") % " {revision}"}\n' -l1
1275 $ hg log -T '{revset(":") % " {revision}"}\n' -l1
1276 0 1 2
1276 0 1 2
1277
1277
1278 but a filtered one doesn't
1278 but a filtered one doesn't
1279
1279
1280 $ hg log -T '{filter(revset(":"), ifeq(rev, 1, "", "y"))}\n' -l1 \
1280 $ hg log -T '{filter(revset(":"), ifeq(rev, 1, "", "y"))}\n' -l1 \
1281 > --config templates.start_revisions='"["' \
1281 > --config templates.start_revisions='"["' \
1282 > --config templates.end_revisions='"]"' \
1282 > --config templates.end_revisions='"]"' \
1283 > --config templates.revision='"{revision}, "' \
1283 > --config templates.revision='"{revision}, "' \
1284 > --config templates.last_revision='"{revision}"'
1284 > --config templates.last_revision='"{revision}"'
1285 0 2
1285 0 2
1286 $ hg log -T '{filter(revset(":"), ifeq(rev, 1, "", "y")) % "x{revision}"}\n' -l1
1286 $ hg log -T '{filter(revset(":"), ifeq(rev, 1, "", "y")) % "x{revision}"}\n' -l1
1287 xx
1287 xx
1288
1288
1289 %d parameter handling:
1289 %d parameter handling:
1290
1290
1291 $ hg log -T '{revset("%d", rev)}\n' -r'wdir()'
1291 $ hg log -T '{revset("%d", rev)}\n' -r'wdir()'
1292 2147483647
1292 2147483647
1293 $ hg log -T '{revset("%d", rev)}\n' -r'null'
1293 $ hg log -T '{revset("%d", rev)}\n' -r'null'
1294 -1
1294 -1
1295 $ hg log -T '{revset("%d", rev + 1)}\n' -r'tip'
1295 $ hg log -T '{revset("%d", rev + 1)}\n' -r'tip'
1296 abort: unknown revision '3'
1296 abort: unknown revision '3'
1297 [10]
1297 [10]
1298 $ hg log -T '{revset("%d", rev - 1)}\n' -r'null'
1298 $ hg log -T '{revset("%d", rev - 1)}\n' -r'null'
1299 abort: unknown revision '-2'
1299 abort: unknown revision '-2'
1300 [10]
1300 [10]
1301
1301
1302 Invalid arguments passed to revset()
1302 Invalid arguments passed to revset()
1303
1303
1304 $ hg log -T '{revset("%whatever", 0)}\n'
1304 $ hg log -T '{revset("%whatever", 0)}\n'
1305 hg: parse error: unexpected revspec format character w
1305 hg: parse error: unexpected revspec format character w
1306 [10]
1306 [10]
1307 $ hg log -T '{revset("%lwhatever", files)}\n'
1307 $ hg log -T '{revset("%lwhatever", files)}\n'
1308 hg: parse error: unexpected revspec format character w
1308 hg: parse error: unexpected revspec format character w
1309 [10]
1309 [10]
1310 $ hg log -T '{revset("%s %s", 0)}\n'
1310 $ hg log -T '{revset("%s %s", 0)}\n'
1311 hg: parse error: missing argument for revspec
1311 hg: parse error: missing argument for revspec
1312 [10]
1312 [10]
1313 $ hg log -T '{revset("", 0)}\n'
1313 $ hg log -T '{revset("", 0)}\n'
1314 hg: parse error: too many revspec arguments specified
1314 hg: parse error: too many revspec arguments specified
1315 [10]
1315 [10]
1316 $ hg log -T '{revset("%s", 0, 1)}\n'
1316 $ hg log -T '{revset("%s", 0, 1)}\n'
1317 hg: parse error: too many revspec arguments specified
1317 hg: parse error: too many revspec arguments specified
1318 [10]
1318 [10]
1319 $ hg log -T '{revset("%", 0)}\n'
1319 $ hg log -T '{revset("%", 0)}\n'
1320 hg: parse error: incomplete revspec format character
1320 hg: parse error: incomplete revspec format character
1321 [10]
1321 [10]
1322 $ hg log -T '{revset("%l", 0)}\n'
1322 $ hg log -T '{revset("%l", 0)}\n'
1323 hg: parse error: incomplete revspec format character
1323 hg: parse error: incomplete revspec format character
1324 [10]
1324 [10]
1325 $ hg log -T '{revset("%d", 'foo')}\n'
1325 $ hg log -T '{revset("%d", 'foo')}\n'
1326 hg: parse error: invalid argument for revspec
1326 hg: parse error: invalid argument for revspec
1327 [10]
1327 [10]
1328 $ hg log -T '{revset("%ld", files)}\n'
1328 $ hg log -T '{revset("%ld", files)}\n'
1329 hg: parse error: invalid argument for revspec
1329 hg: parse error: invalid argument for revspec
1330 [10]
1330 [10]
1331 $ hg log -T '{revset("%ls", 0)}\n'
1331 $ hg log -T '{revset("%ls", 0)}\n'
1332 hg: parse error: invalid argument for revspec
1332 hg: parse error: invalid argument for revspec
1333 [10]
1333 [10]
1334 $ hg log -T '{revset("%b", 'foo')}\n'
1334 $ hg log -T '{revset("%b", 'foo')}\n'
1335 hg: parse error: invalid argument for revspec
1335 hg: parse error: invalid argument for revspec
1336 [10]
1336 [10]
1337 $ hg log -T '{revset("%lb", files)}\n'
1337 $ hg log -T '{revset("%lb", files)}\n'
1338 hg: parse error: invalid argument for revspec
1338 hg: parse error: invalid argument for revspec
1339 [10]
1339 [10]
1340 $ hg log -T '{revset("%r", 0)}\n'
1340 $ hg log -T '{revset("%r", 0)}\n'
1341 hg: parse error: invalid argument for revspec
1341 hg: parse error: invalid argument for revspec
1342 [10]
1342 [10]
1343
1343
1344 Invalid operation on revset()
1344 Invalid operation on revset()
1345
1345
1346 $ hg log -T '{get(revset(":"), "foo")}\n'
1346 $ hg log -T '{get(revset(":"), "foo")}\n'
1347 hg: parse error: not a dictionary
1347 hg: parse error: not a dictionary
1348 (get() expects a dict as first argument)
1348 (get() expects a dict as first argument)
1349 [10]
1349 [10]
1350
1350
1351 Test files function
1351 Test files function
1352
1352
1353 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
1353 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
1354 2
1354 2
1355 a
1355 a
1356 aa
1356 aa
1357 b
1357 b
1358 1
1358 1
1359 a
1359 a
1360 0
1360 0
1361 a
1361 a
1362
1362
1363 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
1363 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
1364 2
1364 2
1365 aa
1365 aa
1366 1
1366 1
1367
1367
1368 0
1368 0
1369
1369
1370
1370
1371 $ hg log -l1 -T "{files('aa') % '{file}\n'}"
1371 $ hg log -l1 -T "{files('aa') % '{file}\n'}"
1372 aa
1372 aa
1373 $ hg log -l1 -T "{files('aa') % '{path}\n'}"
1373 $ hg log -l1 -T "{files('aa') % '{path}\n'}"
1374 aa
1374 aa
1375
1375
1376 $ hg rm a
1376 $ hg rm a
1377 $ hg log -r "wdir()" -T "{rev}\n{join(files('*'), '\n')}\n"
1377 $ hg log -r "wdir()" -T "{rev}\n{join(files('*'), '\n')}\n"
1378 2147483647
1378 2147483647
1379 aa
1379 aa
1380 b
1380 b
1381 $ hg revert a
1381 $ hg revert a
1382
1382
1383 Test relpath function
1383 Test relpath function
1384
1384
1385 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
1385 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
1386 a
1386 a
1387 $ cd ..
1387 $ cd ..
1388 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
1388 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
1389 r/a
1389 r/a
1390
1390
1391 Test stringify on sub expressions
1391 Test stringify on sub expressions
1392
1392
1393 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
1393 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
1394 fourth, second, third
1394 fourth, second, third
1395 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
1395 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
1396 abc
1396 abc
1397
1397
1398 Test splitlines
1398 Test splitlines
1399
1399
1400 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
1400 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
1401 @ foo Modify, add, remove, rename
1401 @ foo Modify, add, remove, rename
1402 |
1402 |
1403 o foo future
1403 o foo future
1404 |
1404 |
1405 o foo third
1405 o foo third
1406 |
1406 |
1407 o foo second
1407 o foo second
1408
1408
1409 o foo merge
1409 o foo merge
1410 |\
1410 |\
1411 | o foo new head
1411 | o foo new head
1412 | |
1412 | |
1413 o | foo new branch
1413 o | foo new branch
1414 |/
1414 |/
1415 o foo no user, no domain
1415 o foo no user, no domain
1416 |
1416 |
1417 o foo no person
1417 o foo no person
1418 |
1418 |
1419 o foo other 1
1419 o foo other 1
1420 | foo other 2
1420 | foo other 2
1421 | foo
1421 | foo
1422 | foo other 3
1422 | foo other 3
1423 o foo line 1
1423 o foo line 1
1424 foo line 2
1424 foo line 2
1425
1425
1426 $ hg log -R a -r0 -T '{desc|splitlines}\n'
1426 $ hg log -R a -r0 -T '{desc|splitlines}\n'
1427 line 1 line 2
1427 line 1 line 2
1428 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
1428 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
1429 line 1|line 2
1429 line 1|line 2
1430
1430
1431 Test startswith
1431 Test startswith
1432 $ hg log -Gv -R a --template "{startswith(desc)}"
1432 $ hg log -Gv -R a --template "{startswith(desc)}"
1433 hg: parse error: startswith expects two arguments
1433 hg: parse error: startswith expects two arguments
1434 [10]
1434 [10]
1435
1435
1436 $ hg log -Gv -R a --template "{startswith('line', desc)}"
1436 $ hg log -Gv -R a --template "{startswith('line', desc)}"
1437 @
1437 @
1438 |
1438 |
1439 o
1439 o
1440 |
1440 |
1441 o
1441 o
1442 |
1442 |
1443 o
1443 o
1444
1444
1445 o
1445 o
1446 |\
1446 |\
1447 | o
1447 | o
1448 | |
1448 | |
1449 o |
1449 o |
1450 |/
1450 |/
1451 o
1451 o
1452 |
1452 |
1453 o
1453 o
1454 |
1454 |
1455 o
1455 o
1456 |
1456 |
1457 o line 1
1457 o line 1
1458 line 2
1458 line 2
1459
1459
1460 Test word function (including index out of bounds graceful failure)
1460 Test word function (including index out of bounds graceful failure)
1461
1461
1462 $ hg log -Gv -R a --template "{word('1', desc)}"
1462 $ hg log -Gv -R a --template "{word('1', desc)}"
1463 @ add,
1463 @ add,
1464 |
1464 |
1465 o
1465 o
1466 |
1466 |
1467 o
1467 o
1468 |
1468 |
1469 o
1469 o
1470
1470
1471 o
1471 o
1472 |\
1472 |\
1473 | o head
1473 | o head
1474 | |
1474 | |
1475 o | branch
1475 o | branch
1476 |/
1476 |/
1477 o user,
1477 o user,
1478 |
1478 |
1479 o person
1479 o person
1480 |
1480 |
1481 o 1
1481 o 1
1482 |
1482 |
1483 o 1
1483 o 1
1484
1484
1485
1485
1486 Test word third parameter used as splitter
1486 Test word third parameter used as splitter
1487
1487
1488 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
1488 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
1489 @ M
1489 @ M
1490 |
1490 |
1491 o future
1491 o future
1492 |
1492 |
1493 o third
1493 o third
1494 |
1494 |
1495 o sec
1495 o sec
1496
1496
1497 o merge
1497 o merge
1498 |\
1498 |\
1499 | o new head
1499 | o new head
1500 | |
1500 | |
1501 o | new branch
1501 o | new branch
1502 |/
1502 |/
1503 o n
1503 o n
1504 |
1504 |
1505 o n
1505 o n
1506 |
1506 |
1507 o
1507 o
1508 |
1508 |
1509 o line 1
1509 o line 1
1510 line 2
1510 line 2
1511
1511
1512 Test word error messages for not enough and too many arguments
1512 Test word error messages for not enough and too many arguments
1513
1513
1514 $ hg log -Gv -R a --template "{word('0')}"
1514 $ hg log -Gv -R a --template "{word('0')}"
1515 hg: parse error: word expects two or three arguments, got 1
1515 hg: parse error: word expects two or three arguments, got 1
1516 [10]
1516 [10]
1517
1517
1518 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
1518 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
1519 hg: parse error: word expects two or three arguments, got 7
1519 hg: parse error: word expects two or three arguments, got 7
1520 [10]
1520 [10]
1521
1521
1522 Test word for integer literal
1522 Test word for integer literal
1523
1523
1524 $ hg log -R a --template "{word(2, desc)}\n" -r0
1524 $ hg log -R a --template "{word(2, desc)}\n" -r0
1525 line
1525 line
1526
1526
1527 Test word for invalid numbers
1527 Test word for invalid numbers
1528
1528
1529 $ hg log -Gv -R a --template "{word('a', desc)}"
1529 $ hg log -Gv -R a --template "{word('a', desc)}"
1530 hg: parse error: word expects an integer index
1530 hg: parse error: word expects an integer index
1531 [10]
1531 [10]
1532
1532
1533 Test word for out of range
1533 Test word for out of range
1534
1534
1535 $ hg log -R a --template "{word(10000, desc)}"
1535 $ hg log -R a --template "{word(10000, desc)}"
1536 $ hg log -R a --template "{word(-10000, desc)}"
1536 $ hg log -R a --template "{word(-10000, desc)}"
1537
1537
1538 Test indent and not adding to empty lines
1538 Test indent and not adding to empty lines
1539
1539
1540 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
1540 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
1541 -----
1541 -----
1542 > line 1
1542 > line 1
1543 >> line 2
1543 >> line 2
1544 -----
1544 -----
1545 > other 1
1545 > other 1
1546 >> other 2
1546 >> other 2
1547
1547
1548 >> other 3
1548 >> other 3
1549
1549
1550 Test indent with empty first line
1550 Test indent with empty first line
1551
1551
1552 $ hg version -T "{indent('', '>> ')}\n"
1552 $ hg version -T "{indent('', '>> ')}\n"
1553
1553
1554
1554
1555 $ hg version -T "{indent('
1555 $ hg version -T "{indent('
1556 > second', '>> ')}\n"
1556 > second', '>> ')}\n"
1557
1557
1558 >> second
1558 >> second
1559
1559
1560 $ hg version -T "{indent('
1560 $ hg version -T "{indent('
1561 > second', '>> ', ' > ')}\n"
1561 > second', '>> ', ' > ')}\n"
1562
1562
1563 >> second
1563 >> second
1564
1564
1565 Test with non-strings like dates
1565 Test with non-strings like dates
1566
1566
1567 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
1567 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
1568 1200000.00
1568 1200000.00
1569 1300000.00
1569 1300000.00
1570
1570
1571 Test cbor filter:
1571 Test cbor filter:
1572
1572
1573 $ cat <<'EOF' > "$TESTTMP/decodecbor.py"
1573 $ cat <<'EOF' > "$TESTTMP/decodecbor.py"
1574 > from mercurial import (
1574 > from mercurial import (
1575 > dispatch,
1575 > dispatch,
1576 > )
1576 > )
1577 > from mercurial.utils import (
1577 > from mercurial.utils import (
1578 > cborutil,
1578 > cborutil,
1579 > procutil,
1579 > procutil,
1580 > stringutil,
1580 > stringutil,
1581 > )
1581 > )
1582 > dispatch.initstdio()
1582 > dispatch.initstdio()
1583 > items = cborutil.decodeall(procutil.stdin.read())
1583 > items = cborutil.decodeall(procutil.stdin.read())
1584 > procutil.stdout.write(stringutil.pprint(items, indent=1) + b'\n')
1584 > procutil.stdout.write(stringutil.pprint(items, indent=1) + b'\n')
1585 > EOF
1585 > EOF
1586
1586
1587 $ hg log -T "{rev|cbor}" -R a -l2 | "$PYTHON" "$TESTTMP/decodecbor.py"
1587 $ hg log -T "{rev|cbor}" -R a -l2 | "$PYTHON" "$TESTTMP/decodecbor.py"
1588 [
1588 [
1589 10,
1589 10,
1590 9
1590 9
1591 ]
1591 ]
1592
1592
1593 $ hg log -T "{extras|cbor}" -R a -l1 | "$PYTHON" "$TESTTMP/decodecbor.py"
1593 $ hg log -T "{extras|cbor}" -R a -l1 | "$PYTHON" "$TESTTMP/decodecbor.py"
1594 [
1594 [
1595 {
1595 {
1596 'branch': 'default'
1596 'branch': 'default'
1597 }
1597 }
1598 ]
1598 ]
1599
1599
1600 $ hg log -T "{revset(':')|cbor}" -R a -l1 | "$PYTHON" "$TESTTMP/decodecbor.py"
1600 $ hg log -T "{revset(':')|cbor}" -R a -l1 | "$PYTHON" "$TESTTMP/decodecbor.py"
1601 [
1601 [
1602 [
1602 [
1603 0,
1603 0,
1604 1,
1604 1,
1605 2,
1605 2,
1606 3,
1606 3,
1607 4,
1607 4,
1608 5,
1608 5,
1609 6,
1609 6,
1610 7,
1610 7,
1611 8,
1611 8,
1612 9,
1612 9,
1613 10
1613 10
1614 ]
1614 ]
1615 ]
1615 ]
1616
1616
1617 $ hg log -T "{dict(foo=revset('.'))|cbor}" -R a -l1 | "$PYTHON" "$TESTTMP/decodecbor.py"
1617 $ hg log -T "{dict(foo=revset('.'))|cbor}" -R a -l1 | "$PYTHON" "$TESTTMP/decodecbor.py"
1618 [
1618 [
1619 {
1619 {
1620 'foo': [
1620 'foo': [
1621 10
1621 10
1622 ]
1622 ]
1623 }
1623 }
1624 ]
1624 ]
1625
1625
1626 json filter should escape HTML tags so that the output can be embedded in hgweb:
1626 json filter should escape HTML tags so that the output can be embedded in hgweb:
1627
1627
1628 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
1628 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
1629 "\u003cfoo@example.org\u003e"
1629 "\u003cfoo@example.org\u003e"
1630
1630
1631 Set up repository for non-ascii encoding tests:
1631 Set up repository for non-ascii encoding tests:
1632
1632
1633 $ hg init nonascii
1633 $ hg init nonascii
1634 $ cd nonascii
1634 $ cd nonascii
1635 $ "$PYTHON" <<EOF
1635 $ "$PYTHON" <<EOF
1636 > open('latin1', 'wb').write(b'\xe9')
1636 > open('latin1', 'wb').write(b'\xe9')
1637 > open('utf-8', 'wb').write(b'\xc3\xa9')
1637 > open('utf-8', 'wb').write(b'\xc3\xa9')
1638 > EOF
1638 > EOF
1639 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
1639 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
1640 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
1640 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
1641
1641
1642 json filter should try round-trip conversion to utf-8:
1642 json filter should try round-trip conversion to utf-8:
1643
1643
1644 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
1644 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
1645 "\u00e9"
1645 "\u00e9"
1646 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
1646 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
1647 "non-ascii branch: \u00e9"
1647 "non-ascii branch: \u00e9"
1648
1648
1649 json filter should take input as utf-8 if it was converted from utf-8:
1649 json filter should take input as utf-8 if it was converted from utf-8:
1650
1650
1651 $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
1651 $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
1652 "\u00e9"
1652 "\u00e9"
1653 $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
1653 $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
1654 "non-ascii branch: \u00e9"
1654 "non-ascii branch: \u00e9"
1655
1655
1656 json filter takes input as utf-8b:
1656 json filter takes input as utf-8b:
1657
1657
1658 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
1658 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
1659 "\u00e9"
1659 "\u00e9"
1660 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
1660 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
1661 "\udce9"
1661 "\udce9"
1662
1662
1663 cbor filter is bytes transparent, which should handle bytes subtypes
1663 cbor filter is bytes transparent, which should handle bytes subtypes
1664 as bytes:
1664 as bytes:
1665
1665
1666 $ HGENCODING=ascii hg log -T "{branch|cbor}" -r0 \
1666 $ HGENCODING=ascii hg log -T "{branch|cbor}" -r0 \
1667 > | "$PYTHON" "$TESTTMP/decodecbor.py"
1667 > | "$PYTHON" "$TESTTMP/decodecbor.py"
1668 [
1668 [
1669 '?'
1669 '?'
1670 ]
1670 ]
1671 $ HGENCODING=latin-1 hg log -T "{branch|cbor}" -r0 \
1671 $ HGENCODING=latin-1 hg log -T "{branch|cbor}" -r0 \
1672 > | "$PYTHON" "$TESTTMP/decodecbor.py"
1672 > | "$PYTHON" "$TESTTMP/decodecbor.py"
1673 [
1673 [
1674 '\xe9'
1674 '\xe9'
1675 ]
1675 ]
1676
1676
1677 utf8 filter:
1677 utf8 filter:
1678
1678
1679 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
1679 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
1680 round-trip: c3a9
1680 round-trip: c3a9
1681 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
1681 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
1682 decoded: c3a9
1682 decoded: c3a9
1683 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
1683 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
1684 abort: decoding near * (glob)
1684 abort: decoding near * (glob)
1685 [255]
1685 [255]
1686 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
1686 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
1687 coerced to string: 0
1687 coerced to string: 0
1688
1688
1689 pad width:
1689 pad width:
1690
1690
1691 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
1691 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
1692 \xc3\xa9- (esc)
1692 \xc3\xa9- (esc)
1693
1693
1694 read config options:
1694 read config options:
1695
1695
1696 $ hg log -T "{config('templateconfig', 'knob', 'foo')}\n"
1696 $ hg log -T "{config('templateconfig', 'knob', 'foo')}\n"
1697 foo
1697 foo
1698 $ hg log -T "{config('templateconfig', 'knob', 'foo')}\n" \
1698 $ hg log -T "{config('templateconfig', 'knob', 'foo')}\n" \
1699 > --config templateconfig.knob=bar
1699 > --config templateconfig.knob=bar
1700 bar
1700 bar
1701 $ hg log -T "{configbool('templateconfig', 'knob', True)}\n"
1701 $ hg log -T "{configbool('templateconfig', 'knob', True)}\n"
1702 True
1702 True
1703 $ hg log -T "{configbool('templateconfig', 'knob', True)}\n" \
1703 $ hg log -T "{configbool('templateconfig', 'knob', True)}\n" \
1704 > --config templateconfig.knob=0
1704 > --config templateconfig.knob=0
1705 False
1705 False
1706 $ hg log -T "{configint('templateconfig', 'knob', 123)}\n"
1706 $ hg log -T "{configint('templateconfig', 'knob', 123)}\n"
1707 123
1707 123
1708 $ hg log -T "{configint('templateconfig', 'knob', 123)}\n" \
1708 $ hg log -T "{configint('templateconfig', 'knob', 123)}\n" \
1709 > --config templateconfig.knob=456
1709 > --config templateconfig.knob=456
1710 456
1710 456
1711 $ hg log -T "{config('templateconfig', 'knob')}\n"
1711 $ hg log -T "{config('templateconfig', 'knob')}\n"
1712 devel-warn: config item requires an explicit default value: 'templateconfig.knob' at: * (glob)
1712 devel-warn: config item requires an explicit default value: 'templateconfig.knob' at: * (glob)
1713
1713
1714 $ hg log -T "{configbool('ui', 'interactive')}\n"
1714 $ hg log -T "{configbool('ui', 'interactive')}\n"
1715 False
1715 False
1716 $ hg log -T "{configbool('ui', 'interactive')}\n" --config ui.interactive=1
1716 $ hg log -T "{configbool('ui', 'interactive')}\n" --config ui.interactive=1
1717 True
1717 True
1718 $ hg log -T "{config('templateconfig', 'knob', if(true, 'foo', 'bar'))}\n"
1718 $ hg log -T "{config('templateconfig', 'knob', if(true, 'foo', 'bar'))}\n"
1719 foo
1719 foo
1720
1720
1721 reverse filter:
1722
1723 $ hg log -T "{'abc\ndef\nghi'|splitlines|reverse}\n"
1724 ghi def abc
1725
1726 $ hg log -T "{'abc'|reverse}\n"
1727 hg: parse error: not reversible
1728 (incompatible use of template filter 'reverse')
1729 [10]
1730
1731 $ hg log -T "{date|reverse}\n"
1732 hg: parse error: not reversible
1733 (template filter 'reverse' is not compatible with keyword 'date')
1734 [10]
1735
1721 $ cd ..
1736 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now