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