comparison tests/test-template-functions.t @ 38434:70f551a3f52e

tests: extract test-template-functions.t from test-command-template.t I decided to not split filters and functions into two test files since we sometimes reimplement a filter as a function.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 16 Jun 2018 13:17:11 +0900
parents tests/test-command-template.t@ddce7bdf7f3c
children 6a8ed5c7e112
comparison
equal deleted inserted replaced
38433:ddce7bdf7f3c 38434:70f551a3f52e
1 Test template filters and functions
2 ===================================
3
4 $ hg init a
5 $ cd a
6 $ echo a > a
7 $ hg add a
8 $ echo line 1 > b
9 $ echo line 2 >> b
10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
11
12 $ hg add b
13 $ echo other 1 > c
14 $ echo other 2 >> c
15 $ echo >> c
16 $ echo other 3 >> c
17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
18
19 $ hg add c
20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
21 $ echo c >> c
22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
23
24 $ echo foo > .hg/branch
25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
26
27 $ hg co -q 3
28 $ echo other 4 >> d
29 $ hg add d
30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
31
32 $ hg merge -q foo
33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
34
35 Second branch starting at nullrev:
36
37 $ hg update null
38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
39 $ echo second > second
40 $ hg add second
41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
42 created new head
43
44 $ echo third > third
45 $ hg add third
46 $ hg mv second fourth
47 $ hg commit -m third -d "2020-01-01 10:01"
48
49 $ hg phase -r 5 --public
50 $ hg phase -r 7 --secret --force
51
52 Filters work:
53
54 $ hg log --template '{author|domain}\n'
55
56 hostname
57
58
59
60
61 place
62 place
63 hostname
64
65 $ hg log --template '{author|person}\n'
66 test
67 User Name
68 person
69 person
70 person
71 person
72 other
73 A. N. Other
74 User Name
75
76 $ hg log --template '{author|user}\n'
77 test
78 user
79 person
80 person
81 person
82 person
83 other
84 other
85 user
86
87 $ hg log --template '{date|date}\n'
88 Wed Jan 01 10:01:00 2020 +0000
89 Mon Jan 12 13:46:40 1970 +0000
90 Sun Jan 18 08:40:01 1970 +0000
91 Sun Jan 18 08:40:00 1970 +0000
92 Sat Jan 17 04:53:20 1970 +0000
93 Fri Jan 16 01:06:40 1970 +0000
94 Wed Jan 14 21:20:00 1970 +0000
95 Tue Jan 13 17:33:20 1970 +0000
96 Mon Jan 12 13:46:40 1970 +0000
97
98 $ hg log --template '{date|isodate}\n'
99 2020-01-01 10:01 +0000
100 1970-01-12 13:46 +0000
101 1970-01-18 08:40 +0000
102 1970-01-18 08:40 +0000
103 1970-01-17 04:53 +0000
104 1970-01-16 01:06 +0000
105 1970-01-14 21:20 +0000
106 1970-01-13 17:33 +0000
107 1970-01-12 13:46 +0000
108
109 $ hg log --template '{date|isodatesec}\n'
110 2020-01-01 10:01:00 +0000
111 1970-01-12 13:46:40 +0000
112 1970-01-18 08:40:01 +0000
113 1970-01-18 08:40:00 +0000
114 1970-01-17 04:53:20 +0000
115 1970-01-16 01:06:40 +0000
116 1970-01-14 21:20:00 +0000
117 1970-01-13 17:33:20 +0000
118 1970-01-12 13:46:40 +0000
119
120 $ hg log --template '{date|rfc822date}\n'
121 Wed, 01 Jan 2020 10:01:00 +0000
122 Mon, 12 Jan 1970 13:46:40 +0000
123 Sun, 18 Jan 1970 08:40:01 +0000
124 Sun, 18 Jan 1970 08:40:00 +0000
125 Sat, 17 Jan 1970 04:53:20 +0000
126 Fri, 16 Jan 1970 01:06:40 +0000
127 Wed, 14 Jan 1970 21:20:00 +0000
128 Tue, 13 Jan 1970 17:33:20 +0000
129 Mon, 12 Jan 1970 13:46:40 +0000
130
131 $ hg log --template '{desc|firstline}\n'
132 third
133 second
134 merge
135 new head
136 new branch
137 no user, no domain
138 no person
139 other 1
140 line 1
141
142 $ hg log --template '{node|short}\n'
143 95c24699272e
144 29114dbae42b
145 d41e714fe50d
146 13207e5a10d9
147 bbe44766e73d
148 10e46f2dcbf4
149 97054abb4ab8
150 b608e9d1a3f0
151 1e4e1b8f71e0
152
153 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
154 <changeset author="test"/>
155 <changeset author="User Name &lt;user@hostname&gt;"/>
156 <changeset author="person"/>
157 <changeset author="person"/>
158 <changeset author="person"/>
159 <changeset author="person"/>
160 <changeset author="other@place"/>
161 <changeset author="A. N. Other &lt;other@place&gt;"/>
162 <changeset author="User Name &lt;user@hostname&gt;"/>
163
164 $ hg log --template '{rev}: {children}\n'
165 8:
166 7: 8:95c24699272e
167 6:
168 5: 6:d41e714fe50d
169 4: 6:d41e714fe50d
170 3: 4:bbe44766e73d 5:13207e5a10d9
171 2: 3:10e46f2dcbf4
172 1: 2:97054abb4ab8
173 0: 1:b608e9d1a3f0
174
175 Formatnode filter works:
176
177 $ hg -q log -r 0 --template '{node|formatnode}\n'
178 1e4e1b8f71e0
179
180 $ hg log -r 0 --template '{node|formatnode}\n'
181 1e4e1b8f71e0
182
183 $ hg -v log -r 0 --template '{node|formatnode}\n'
184 1e4e1b8f71e0
185
186 $ hg --debug log -r 0 --template '{node|formatnode}\n'
187 1e4e1b8f71e05681d422154f5421e385fec3454f
188
189 Age filter:
190
191 $ hg init unstable-hash
192 $ cd unstable-hash
193 $ hg log --template '{date|age}\n' > /dev/null || exit 1
194
195 >>> from __future__ import absolute_import
196 >>> import datetime
197 >>> fp = open('a', 'wb')
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
200 >>> fp.close()
201 $ hg add a
202 $ hg commit -m future -d "`cat a`"
203
204 $ hg log -l1 --template '{date|age}\n'
205 7 years from now
206
207 $ cd ..
208 $ rm -rf unstable-hash
209
210 Filename filters:
211
212 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
213 bar||foo|
214 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
215 foo|foo||
216 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
217 foo|foo|foo|
218
219 Add a dummy commit to make up for the instability of the above:
220
221 $ echo a > a
222 $ hg add a
223 $ hg ci -m future
224
225 Count filter:
226
227 $ hg log -l1 --template '{node|count} {node|short|count}\n'
228 40 12
229
230 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
231 0 1 4
232
233 $ hg log -G --template '{rev}: children: {children|count}, \
234 > tags: {tags|count}, file_adds: {file_adds|count}, \
235 > ancestors: {revset("ancestors(%s)", rev)|count}'
236 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
237 |
238 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
239 |
240 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
241
242 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
243 |\
244 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
245 | |
246 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
247 |/
248 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
249 |
250 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
251 |
252 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
253 |
254 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
255
256
257 $ hg log -l1 -T '{termwidth|count}\n'
258 hg: parse error: not countable
259 (template filter 'count' is not compatible with keyword 'termwidth')
260 [255]
261
262 Upper/lower filters:
263
264 $ hg log -r0 --template '{branch|upper}\n'
265 DEFAULT
266 $ hg log -r0 --template '{author|lower}\n'
267 user name <user@hostname>
268 $ hg log -r0 --template '{date|upper}\n'
269 1000000.00
270
271 Add a commit that does all possible modifications at once
272
273 $ echo modify >> third
274 $ touch b
275 $ hg add b
276 $ hg mv fourth fifth
277 $ hg rm a
278 $ hg ci -m "Modify, add, remove, rename"
279
280 Pass generator object created by template function to filter
281
282 $ hg log -l 1 --template '{if(author, author)|user}\n'
283 test
284
285 Test diff function:
286
287 $ hg diff -c 8
288 diff -r 29114dbae42b -r 95c24699272e fourth
289 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
290 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
291 @@ -0,0 +1,1 @@
292 +second
293 diff -r 29114dbae42b -r 95c24699272e second
294 --- a/second Mon Jan 12 13:46:40 1970 +0000
295 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
296 @@ -1,1 +0,0 @@
297 -second
298 diff -r 29114dbae42b -r 95c24699272e third
299 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
300 +++ b/third Wed Jan 01 10:01:00 2020 +0000
301 @@ -0,0 +1,1 @@
302 +third
303
304 $ hg log -r 8 -T "{diff()}"
305 diff -r 29114dbae42b -r 95c24699272e fourth
306 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
307 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
308 @@ -0,0 +1,1 @@
309 +second
310 diff -r 29114dbae42b -r 95c24699272e second
311 --- a/second Mon Jan 12 13:46:40 1970 +0000
312 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
313 @@ -1,1 +0,0 @@
314 -second
315 diff -r 29114dbae42b -r 95c24699272e third
316 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
317 +++ b/third Wed Jan 01 10:01:00 2020 +0000
318 @@ -0,0 +1,1 @@
319 +third
320
321 $ hg log -r 8 -T "{diff('glob:f*')}"
322 diff -r 29114dbae42b -r 95c24699272e fourth
323 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
324 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
325 @@ -0,0 +1,1 @@
326 +second
327
328 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
329 diff -r 29114dbae42b -r 95c24699272e second
330 --- a/second Mon Jan 12 13:46:40 1970 +0000
331 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
332 @@ -1,1 +0,0 @@
333 -second
334 diff -r 29114dbae42b -r 95c24699272e third
335 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
336 +++ b/third Wed Jan 01 10:01:00 2020 +0000
337 @@ -0,0 +1,1 @@
338 +third
339
340 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
341 diff -r 29114dbae42b -r 95c24699272e fourth
342 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
343 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
344 @@ -0,0 +1,1 @@
345 +second
346
347 $ cd ..
348
349 latesttag() function:
350
351 $ hg init latesttag
352 $ cd latesttag
353
354 $ echo a > file
355 $ hg ci -Am a -d '0 0'
356 adding file
357
358 $ echo b >> file
359 $ hg ci -m b -d '1 0'
360
361 $ echo c >> head1
362 $ hg ci -Am h1c -d '2 0'
363 adding head1
364
365 $ hg update -q 1
366 $ echo d >> head2
367 $ hg ci -Am h2d -d '3 0'
368 adding head2
369 created new head
370
371 $ echo e >> head2
372 $ hg ci -m h2e -d '4 0'
373
374 $ hg merge -q
375 $ hg ci -m merge -d '5 -3600'
376
377 $ hg tag -r 1 -m t1 -d '6 0' t1
378 $ hg tag -r 2 -m t2 -d '7 0' t2
379 $ hg tag -r 3 -m t3 -d '8 0' t3
380 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
381 $ hg tag -r 5 -m t5 -d '9 0' t5
382 $ hg tag -r 3 -m at3 -d '10 0' at3
383
384 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
385 @ 11: t3, C: 9, D: 8
386 |
387 o 10: t3, C: 8, D: 7
388 |
389 o 9: t3, C: 7, D: 6
390 |
391 o 8: t3, C: 6, D: 5
392 |
393 o 7: t3, C: 5, D: 4
394 |
395 o 6: t3, C: 4, D: 3
396 |
397 o 5: t3, C: 3, D: 2
398 |\
399 | o 4: t3, C: 1, D: 1
400 | |
401 | o 3: t3, C: 0, D: 0
402 | |
403 o | 2: t1, C: 1, D: 1
404 |/
405 o 1: t1, C: 0, D: 0
406 |
407 o 0: null, C: 1, D: 1
408
409
410 $ cd ..
411
412 Test manifest/get() can be join()-ed as string, though it's silly:
413
414 $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
415 1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
416 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
417 d.e.f.a.u.l.t
418
419 Test join() over string
420
421 $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
422 1.1
423
424 Test join() over uniterable
425
426 $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
427 hg: parse error: 11 is not iterable
428 [255]
429
430 Test min/max of integers
431
432 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
433 9
434 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
435 10
436
437 Test min/max over map operation:
438
439 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
440 at3
441 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
442 t3
443
444 Test min/max of strings:
445
446 $ hg log -R latesttag -l1 -T '{min(desc)}\n'
447 3
448 $ hg log -R latesttag -l1 -T '{max(desc)}\n'
449 t
450
451 Test min/max of non-iterable:
452
453 $ hg debugtemplate '{min(1)}'
454 hg: parse error: 1 is not iterable
455 (min first argument should be an iterable)
456 [255]
457 $ hg debugtemplate '{max(2)}'
458 hg: parse error: 2 is not iterable
459 (max first argument should be an iterable)
460 [255]
461
462 $ hg log -R latesttag -l1 -T '{min(date)}'
463 hg: parse error: date is not iterable
464 (min first argument should be an iterable)
465 [255]
466 $ hg log -R latesttag -l1 -T '{max(date)}'
467 hg: parse error: date is not iterable
468 (max first argument should be an iterable)
469 [255]
470
471 Test min/max of empty sequence:
472
473 $ hg debugtemplate '{min("")}'
474 hg: parse error: empty string
475 (min first argument should be an iterable)
476 [255]
477 $ hg debugtemplate '{max("")}'
478 hg: parse error: empty string
479 (max first argument should be an iterable)
480 [255]
481 $ hg debugtemplate '{min(dict())}'
482 hg: parse error: empty sequence
483 (min first argument should be an iterable)
484 [255]
485 $ hg debugtemplate '{max(dict())}'
486 hg: parse error: empty sequence
487 (max first argument should be an iterable)
488 [255]
489 $ hg debugtemplate '{min(dict() % "")}'
490 hg: parse error: empty sequence
491 (min first argument should be an iterable)
492 [255]
493 $ hg debugtemplate '{max(dict() % "")}'
494 hg: parse error: empty sequence
495 (max first argument should be an iterable)
496 [255]
497
498 Test min/max of if() result
499
500 $ cd latesttag
501 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
502 9
503 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
504 10
505 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
506 9
507 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
508 10
509 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
510 9
511 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
512 10
513 $ cd ..
514
515 Test laziness of if() then/else clause
516
517 $ hg debugtemplate '{count(0)}'
518 hg: parse error: not countable
519 (incompatible use of template filter 'count')
520 [255]
521 $ hg debugtemplate '{if(true, "", count(0))}'
522 $ hg debugtemplate '{if(false, count(0), "")}'
523 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
524 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
525 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
526 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
527
528 Test the sub function of templating for expansion:
529
530 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
531 xx
532
533 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
534 hg: parse error: sub got an invalid pattern: [
535 [255]
536 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
537 hg: parse error: sub got an invalid replacement: \1
538 [255]
539
540 Test the strip function with chars specified:
541
542 $ hg log -R latesttag --template '{desc}\n'
543 at3
544 t5
545 t4
546 t3
547 t2
548 t1
549 merge
550 h2e
551 h2d
552 h1c
553 b
554 a
555
556 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
557 at3
558 5
559 4
560 3
561 2
562 1
563 merg
564 h2
565 h2d
566 h1c
567 b
568 a
569
570 Test date format:
571
572 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
573 date: 70 01 01 10 +0000
574 date: 70 01 01 09 +0000
575 date: 70 01 01 04 +0000
576 date: 70 01 01 08 +0000
577 date: 70 01 01 07 +0000
578 date: 70 01 01 06 +0000
579 date: 70 01 01 05 +0100
580 date: 70 01 01 04 +0000
581 date: 70 01 01 03 +0000
582 date: 70 01 01 02 +0000
583 date: 70 01 01 01 +0000
584 date: 70 01 01 00 +0000
585
586 Test invalid date:
587
588 $ hg log -R latesttag -T '{date(rev)}\n'
589 hg: parse error: date expects a date information
590 [255]
591
592 Set up repository containing template fragments in commit metadata:
593
594 $ hg init r
595 $ cd r
596 $ echo a > a
597 $ hg ci -Am '{rev}'
598 adding a
599
600 $ hg branch -q 'text.{rev}'
601 $ echo aa >> aa
602 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
603
604 color effect can be specified without quoting:
605
606 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
607 \x1b[0;31mtext\x1b[0m (esc)
608
609 color effects can be nested (issue5413)
610
611 $ hg debugtemplate --color=always \
612 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
613 \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)
614
615 pad() should interact well with color codes (issue5416)
616
617 $ hg debugtemplate --color=always \
618 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
619 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
620
621 label should be no-op if color is disabled:
622
623 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
624 text
625 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
626 text
627
628 Test branches inside if statement:
629
630 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
631 no
632
633 Test dict constructor:
634
635 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
636 y=f7769ec2ab97 x=0
637 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
638 x=0
639 y=f7769ec2ab97
640 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
641 {"x": 0, "y": "f7769ec2ab97"}
642 $ hg log -r 0 -T '{dict()|json}\n'
643 {}
644
645 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
646 rev=0 node=f7769ec2ab97
647 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
648 rev=0 node=f7769ec2ab97
649
650 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
651 hg: parse error: duplicated dict key 'rev' inferred
652 [255]
653 $ hg log -r 0 -T '{dict(node, node|short)}\n'
654 hg: parse error: duplicated dict key 'node' inferred
655 [255]
656 $ hg log -r 0 -T '{dict(1 + 2)}'
657 hg: parse error: dict key cannot be inferred
658 [255]
659
660 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
661 hg: parse error: dict got multiple values for keyword argument 'x'
662 [255]
663
664 Test get function:
665
666 $ hg log -r 0 --template '{get(extras, "branch")}\n'
667 default
668 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
669 default
670 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
671 hg: parse error: not a dictionary
672 (get() expects a dict as first argument)
673 [255]
674
675 Test json filter applied to wrapped object:
676
677 $ hg log -r0 -T '{files|json}\n'
678 ["a"]
679 $ hg log -r0 -T '{extras|json}\n'
680 {"branch": "default"}
681 $ hg log -r0 -T '{date|json}\n'
682 [0, 0]
683
684 Test json filter applied to map result:
685
686 $ hg log -r0 -T '{json(extras % "{key}")}\n'
687 ["branch"]
688
689 Test localdate(date, tz) function:
690
691 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
692 1970-01-01 09:00 +0900
693 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
694 1970-01-01 00:00 +0000
695 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
696 hg: parse error: localdate expects a timezone
697 [255]
698 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
699 1970-01-01 02:00 +0200
700 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
701 1970-01-01 00:00 +0000
702 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
703 1970-01-01 00:00 +0000
704 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
705 hg: parse error: localdate expects a timezone
706 [255]
707 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
708 hg: parse error: localdate expects a timezone
709 [255]
710
711 Test shortest(node) function:
712
713 $ echo b > b
714 $ hg ci -qAm b
715 $ hg log --template '{shortest(node)}\n'
716 e777
717 bcc7
718 f776
719 $ hg log --template '{shortest(node, 10)}\n'
720 e777603221
721 bcc7ff960b
722 f7769ec2ab
723 $ hg log --template '{node|shortest}\n' -l1
724 e777
725
726 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
727 f7769ec2ab
728 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
729 hg: parse error: shortest() expects an integer minlength
730 [255]
731
732 $ hg log -r 'wdir()' -T '{node|shortest}\n'
733 ffff
734
735 $ hg log --template '{shortest("f")}\n' -l1
736 f
737
738 $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
739 0123456789012345678901234567890123456789
740
741 $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
742 01234567890123456789012345678901234567890123456789
743
744 $ hg log --template '{shortest("not a hex string")}\n' -l1
745 not a hex string
746
747 $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
748 not a hex string, but it's 40 bytes long
749
750 $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
751 ffff
752
753 $ hg log --template '{shortest("fffffff")}\n' -l1
754 ffff
755
756 $ hg log --template '{shortest("ff")}\n' -l1
757 ffff
758
759 $ cd ..
760
761 Test shortest(node) with the repo having short hash collision:
762
763 $ hg init hashcollision
764 $ cd hashcollision
765 $ cat <<EOF >> .hg/hgrc
766 > [experimental]
767 > evolution.createmarkers=True
768 > EOF
769 $ echo 0 > a
770 $ hg ci -qAm 0
771 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
772 > hg up -q 0
773 > echo $i > a
774 > hg ci -qm $i
775 > done
776 $ hg up -q null
777 $ hg log -r0: -T '{rev}:{node}\n'
778 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
779 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
780 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
781 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
782 4:10776689e627b465361ad5c296a20a487e153ca4
783 5:a00be79088084cb3aff086ab799f8790e01a976b
784 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
785 7:a0457b3450b8e1b778f1163b31a435802987fe5d
786 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
787 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
788 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
789 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
790 obsoleted 1 changesets
791 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
792 obsoleted 1 changesets
793 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
794 obsoleted 1 changesets
795
796 nodes starting with '11' (we don't have the revision number '11' though)
797
798 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
799 1:1142
800 2:1140
801 3:11d
802
803 '5:a00' is hidden, but still we have two nodes starting with 'a0'
804
805 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
806 6:a0b
807 7:a04
808
809 node '10' conflicts with the revision number '10' even if it is hidden
810 (we could exclude hidden revision numbers, but currently we don't)
811
812 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
813 4:107
814 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
815 4:107
816
817 node 'c562' should be unique if the other 'c562' nodes are hidden
818 (but we don't try the slow path to filter out hidden nodes for now)
819
820 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
821 8:c5625
822 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
823 8:c5625
824 9:c5623
825 10:c562d
826
827 $ cd ..
828
829 Test pad function
830
831 $ cd r
832
833 $ hg log --template '{pad(rev, 20)} {author|user}\n'
834 2 test
835 1 {node|short}
836 0 test
837
838 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
839 2 test
840 1 {node|short}
841 0 test
842
843 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
844 2------------------- test
845 1------------------- {node|short}
846 0------------------- test
847
848 Test template string in pad function
849
850 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
851 {0} test
852
853 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
854 \{rev} test
855
856 Test width argument passed to pad function
857
858 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
859 0 test
860 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
861 hg: parse error: pad() expects an integer width
862 [255]
863
864 Test invalid fillchar passed to pad function
865
866 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
867 hg: parse error: pad() expects a single fill character
868 [255]
869 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
870 hg: parse error: pad() expects a single fill character
871 [255]
872
873 Test boolean argument passed to pad function
874
875 no crash
876
877 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
878 ---------0
879
880 string/literal
881
882 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
883 ---------0
884 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
885 0---------
886 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
887 0---------
888
889 unknown keyword is evaluated to ''
890
891 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
892 0---------
893
894 Test separate function
895
896 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
897 a-b-c
898 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
899 0:f7769ec2ab97 test default
900 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
901 a \x1b[0;31mb\x1b[0m c d (esc)
902
903 Test boolean expression/literal passed to if function
904
905 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
906 rev 0 is True
907 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
908 literal 0 is True as well
909 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
910 empty string is False
911 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
912 empty list is False
913 $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n'
914 non-empty list is True
915 $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n'
916 list of empty strings is True
917 $ hg log -r 0 -T '{if(true, "true is True")}\n'
918 true is True
919 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
920 false is False
921 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
922 non-empty string is True
923
924 Test ifcontains function
925
926 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
927 2 is in the string
928 1 is not
929 0 is in the string
930
931 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
932 2 is in the string
933 1 is not
934 0 is in the string
935
936 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
937 2 did not add a
938 1 did not add a
939 0 added a
940
941 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
942 2 is parent of 1
943 1
944 0
945
946 $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n'
947 t
948 $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n'
949 t
950 $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n'
951 f
952 $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n'
953 t
954
955 Test revset function
956
957 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
958 2 current rev
959 1 not current rev
960 0 not current rev
961
962 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
963 2 match rev
964 1 match rev
965 0 not match rev
966
967 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
968 type not match
969
970 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
971 2 Parents: 1
972 1 Parents: 0
973 0 Parents:
974
975 $ cat >> .hg/hgrc <<EOF
976 > [revsetalias]
977 > myparents(\$1) = parents(\$1)
978 > EOF
979 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
980 2 Parents: 1
981 1 Parents: 0
982 0 Parents:
983
984 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
985 Rev: 2
986 Ancestor: 0
987 Ancestor: 1
988 Ancestor: 2
989
990 Rev: 1
991 Ancestor: 0
992 Ancestor: 1
993
994 Rev: 0
995 Ancestor: 0
996
997 $ hg log --template '{revset("TIP"|lower)}\n' -l1
998 2
999
1000 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
1001 2
1002
1003 a list template is evaluated for each item of revset/parents
1004
1005 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
1006 2 p: 1:bcc7ff960b8e
1007 1 p: 0:f7769ec2ab97
1008 0 p:
1009
1010 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
1011 2 p: 1:bcc7ff960b8e -1:000000000000
1012 1 p: 0:f7769ec2ab97 -1:000000000000
1013 0 p: -1:000000000000 -1:000000000000
1014
1015 therefore, 'revcache' should be recreated for each rev
1016
1017 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
1018 2 aa b
1019 p
1020 1
1021 p a
1022 0 a
1023 p
1024
1025 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
1026 2 aa b
1027 p
1028 1
1029 p a
1030 0 a
1031 p
1032
1033 a revset item must be evaluated as an integer revision, not an offset from tip
1034
1035 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
1036 -1:000000000000
1037 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
1038 -1:000000000000
1039
1040 join() should pick '{rev}' from revset items:
1041
1042 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
1043 4, 5
1044
1045 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
1046 default. join() should agree with the default formatting:
1047
1048 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
1049 5:13207e5a10d9, 4:bbe44766e73d
1050
1051 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
1052 5:13207e5a10d9fd28ec424934298e176197f2c67f,
1053 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1054
1055 Invalid arguments passed to revset()
1056
1057 $ hg log -T '{revset("%whatever", 0)}\n'
1058 hg: parse error: unexpected revspec format character w
1059 [255]
1060 $ hg log -T '{revset("%lwhatever", files)}\n'
1061 hg: parse error: unexpected revspec format character w
1062 [255]
1063 $ hg log -T '{revset("%s %s", 0)}\n'
1064 hg: parse error: missing argument for revspec
1065 [255]
1066 $ hg log -T '{revset("", 0)}\n'
1067 hg: parse error: too many revspec arguments specified
1068 [255]
1069 $ hg log -T '{revset("%s", 0, 1)}\n'
1070 hg: parse error: too many revspec arguments specified
1071 [255]
1072 $ hg log -T '{revset("%", 0)}\n'
1073 hg: parse error: incomplete revspec format character
1074 [255]
1075 $ hg log -T '{revset("%l", 0)}\n'
1076 hg: parse error: incomplete revspec format character
1077 [255]
1078 $ hg log -T '{revset("%d", 'foo')}\n'
1079 hg: parse error: invalid argument for revspec
1080 [255]
1081 $ hg log -T '{revset("%ld", files)}\n'
1082 hg: parse error: invalid argument for revspec
1083 [255]
1084 $ hg log -T '{revset("%ls", 0)}\n'
1085 hg: parse error: invalid argument for revspec
1086 [255]
1087 $ hg log -T '{revset("%b", 'foo')}\n'
1088 hg: parse error: invalid argument for revspec
1089 [255]
1090 $ hg log -T '{revset("%lb", files)}\n'
1091 hg: parse error: invalid argument for revspec
1092 [255]
1093 $ hg log -T '{revset("%r", 0)}\n'
1094 hg: parse error: invalid argument for revspec
1095 [255]
1096
1097 Test files function
1098
1099 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
1100 2
1101 a
1102 aa
1103 b
1104 1
1105 a
1106 0
1107 a
1108
1109 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
1110 2
1111 aa
1112 1
1113
1114 0
1115
1116 $ hg rm a
1117 $ hg log -r "wdir()" -T "{rev}\n{join(files('*'), '\n')}\n"
1118 2147483647
1119 aa
1120 b
1121 $ hg revert a
1122
1123 Test relpath function
1124
1125 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
1126 a
1127 $ cd ..
1128 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
1129 r/a
1130
1131 Test stringify on sub expressions
1132
1133 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
1134 fourth, second, third
1135 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
1136 abc
1137
1138 Test splitlines
1139
1140 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
1141 @ foo Modify, add, remove, rename
1142 |
1143 o foo future
1144 |
1145 o foo third
1146 |
1147 o foo second
1148
1149 o foo merge
1150 |\
1151 | o foo new head
1152 | |
1153 o | foo new branch
1154 |/
1155 o foo no user, no domain
1156 |
1157 o foo no person
1158 |
1159 o foo other 1
1160 | foo other 2
1161 | foo
1162 | foo other 3
1163 o foo line 1
1164 foo line 2
1165
1166 $ hg log -R a -r0 -T '{desc|splitlines}\n'
1167 line 1 line 2
1168 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
1169 line 1|line 2
1170
1171 Test startswith
1172 $ hg log -Gv -R a --template "{startswith(desc)}"
1173 hg: parse error: startswith expects two arguments
1174 [255]
1175
1176 $ hg log -Gv -R a --template "{startswith('line', desc)}"
1177 @
1178 |
1179 o
1180 |
1181 o
1182 |
1183 o
1184
1185 o
1186 |\
1187 | o
1188 | |
1189 o |
1190 |/
1191 o
1192 |
1193 o
1194 |
1195 o
1196 |
1197 o line 1
1198 line 2
1199
1200 Test word function (including index out of bounds graceful failure)
1201
1202 $ hg log -Gv -R a --template "{word('1', desc)}"
1203 @ add,
1204 |
1205 o
1206 |
1207 o
1208 |
1209 o
1210
1211 o
1212 |\
1213 | o head
1214 | |
1215 o | branch
1216 |/
1217 o user,
1218 |
1219 o person
1220 |
1221 o 1
1222 |
1223 o 1
1224
1225
1226 Test word third parameter used as splitter
1227
1228 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
1229 @ M
1230 |
1231 o future
1232 |
1233 o third
1234 |
1235 o sec
1236
1237 o merge
1238 |\
1239 | o new head
1240 | |
1241 o | new branch
1242 |/
1243 o n
1244 |
1245 o n
1246 |
1247 o
1248 |
1249 o line 1
1250 line 2
1251
1252 Test word error messages for not enough and too many arguments
1253
1254 $ hg log -Gv -R a --template "{word('0')}"
1255 hg: parse error: word expects two or three arguments, got 1
1256 [255]
1257
1258 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
1259 hg: parse error: word expects two or three arguments, got 7
1260 [255]
1261
1262 Test word for integer literal
1263
1264 $ hg log -R a --template "{word(2, desc)}\n" -r0
1265 line
1266
1267 Test word for invalid numbers
1268
1269 $ hg log -Gv -R a --template "{word('a', desc)}"
1270 hg: parse error: word expects an integer index
1271 [255]
1272
1273 Test word for out of range
1274
1275 $ hg log -R a --template "{word(10000, desc)}"
1276 $ hg log -R a --template "{word(-10000, desc)}"
1277
1278 Test indent and not adding to empty lines
1279
1280 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
1281 -----
1282 > line 1
1283 >> line 2
1284 -----
1285 > other 1
1286 >> other 2
1287
1288 >> other 3
1289
1290 Test with non-strings like dates
1291
1292 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
1293 1200000.00
1294 1300000.00
1295
1296 json filter should escape HTML tags so that the output can be embedded in hgweb:
1297
1298 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
1299 "\u003cfoo@example.org\u003e"
1300
1301 Set up repository for non-ascii encoding tests:
1302
1303 $ hg init nonascii
1304 $ cd nonascii
1305 $ $PYTHON <<EOF
1306 > open('latin1', 'wb').write(b'\xe9')
1307 > open('utf-8', 'wb').write(b'\xc3\xa9')
1308 > EOF
1309 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
1310 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
1311
1312 json filter should try round-trip conversion to utf-8:
1313
1314 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
1315 "\u00e9"
1316 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
1317 "non-ascii branch: \u00e9"
1318
1319 json filter should take input as utf-8 if it was converted from utf-8:
1320
1321 $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
1322 "\u00e9"
1323 $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
1324 "non-ascii branch: \u00e9"
1325
1326 json filter takes input as utf-8b:
1327
1328 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
1329 "\u00e9"
1330 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
1331 "\udce9"
1332
1333 utf8 filter:
1334
1335 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
1336 round-trip: c3a9
1337 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
1338 decoded: c3a9
1339 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
1340 abort: decoding near * (glob)
1341 [255]
1342 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
1343 coerced to string: 0
1344
1345 pad width:
1346
1347 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
1348 \xc3\xa9- (esc)
1349
1350 $ cd ..