##// END OF EJS Templates
templater: add parsing rule for key-value pair...
Yuya Nishihara -
r31885:d18b624c default
parent child Browse files
Show More
@@ -1,1295 +1,1300
1 1 # templater.py - template expansion for output
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import os
11 11 import re
12 12 import types
13 13
14 14 from .i18n import _
15 15 from . import (
16 16 color,
17 17 config,
18 18 encoding,
19 19 error,
20 20 minirst,
21 21 parser,
22 22 pycompat,
23 23 registrar,
24 24 revset as revsetmod,
25 25 revsetlang,
26 26 templatefilters,
27 27 templatekw,
28 28 util,
29 29 )
30 30
31 31 # template parsing
32 32
33 33 elements = {
34 34 # token-type: binding-strength, primary, prefix, infix, suffix
35 35 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
36 36 "%": (16, None, None, ("%", 16), None),
37 37 "|": (15, None, None, ("|", 15), None),
38 38 "*": (5, None, None, ("*", 5), None),
39 39 "/": (5, None, None, ("/", 5), None),
40 40 "+": (4, None, None, ("+", 4), None),
41 41 "-": (4, None, ("negate", 19), ("-", 4), None),
42 "=": (3, None, None, ("keyvalue", 3), None),
42 43 ",": (2, None, None, ("list", 2), None),
43 44 ")": (0, None, None, None, None),
44 45 "integer": (0, "integer", None, None, None),
45 46 "symbol": (0, "symbol", None, None, None),
46 47 "string": (0, "string", None, None, None),
47 48 "template": (0, "template", None, None, None),
48 49 "end": (0, None, None, None, None),
49 50 }
50 51
51 52 def tokenize(program, start, end, term=None):
52 53 """Parse a template expression into a stream of tokens, which must end
53 54 with term if specified"""
54 55 pos = start
55 56 while pos < end:
56 57 c = program[pos]
57 58 if c.isspace(): # skip inter-token whitespace
58 59 pass
59 elif c in "(,)%|+-*/": # handle simple operators
60 elif c in "(=,)%|+-*/": # handle simple operators
60 61 yield (c, None, pos)
61 62 elif c in '"\'': # handle quoted templates
62 63 s = pos + 1
63 64 data, pos = _parsetemplate(program, s, end, c)
64 65 yield ('template', data, s)
65 66 pos -= 1
66 67 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
67 68 # handle quoted strings
68 69 c = program[pos + 1]
69 70 s = pos = pos + 2
70 71 while pos < end: # find closing quote
71 72 d = program[pos]
72 73 if d == '\\': # skip over escaped characters
73 74 pos += 2
74 75 continue
75 76 if d == c:
76 77 yield ('string', program[s:pos], s)
77 78 break
78 79 pos += 1
79 80 else:
80 81 raise error.ParseError(_("unterminated string"), s)
81 82 elif c.isdigit():
82 83 s = pos
83 84 while pos < end:
84 85 d = program[pos]
85 86 if not d.isdigit():
86 87 break
87 88 pos += 1
88 89 yield ('integer', program[s:pos], s)
89 90 pos -= 1
90 91 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
91 92 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
92 93 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
93 94 # where some of nested templates were preprocessed as strings and
94 95 # then compiled. therefore, \"...\" was allowed. (issue4733)
95 96 #
96 97 # processing flow of _evalifliteral() at 5ab28a2e9962:
97 98 # outer template string -> stringify() -> compiletemplate()
98 99 # ------------------------ ------------ ------------------
99 100 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
100 101 # ~~~~~~~~
101 102 # escaped quoted string
102 103 if c == 'r':
103 104 pos += 1
104 105 token = 'string'
105 106 else:
106 107 token = 'template'
107 108 quote = program[pos:pos + 2]
108 109 s = pos = pos + 2
109 110 while pos < end: # find closing escaped quote
110 111 if program.startswith('\\\\\\', pos, end):
111 112 pos += 4 # skip over double escaped characters
112 113 continue
113 114 if program.startswith(quote, pos, end):
114 115 # interpret as if it were a part of an outer string
115 116 data = parser.unescapestr(program[s:pos])
116 117 if token == 'template':
117 118 data = _parsetemplate(data, 0, len(data))[0]
118 119 yield (token, data, s)
119 120 pos += 1
120 121 break
121 122 pos += 1
122 123 else:
123 124 raise error.ParseError(_("unterminated string"), s)
124 125 elif c.isalnum() or c in '_':
125 126 s = pos
126 127 pos += 1
127 128 while pos < end: # find end of symbol
128 129 d = program[pos]
129 130 if not (d.isalnum() or d == "_"):
130 131 break
131 132 pos += 1
132 133 sym = program[s:pos]
133 134 yield ('symbol', sym, s)
134 135 pos -= 1
135 136 elif c == term:
136 137 yield ('end', None, pos + 1)
137 138 return
138 139 else:
139 140 raise error.ParseError(_("syntax error"), pos)
140 141 pos += 1
141 142 if term:
142 143 raise error.ParseError(_("unterminated template expansion"), start)
143 144 yield ('end', None, pos)
144 145
145 146 def _parsetemplate(tmpl, start, stop, quote=''):
146 147 r"""
147 148 >>> _parsetemplate('foo{bar}"baz', 0, 12)
148 149 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
149 150 >>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"')
150 151 ([('string', 'foo'), ('symbol', 'bar')], 9)
151 152 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
152 153 ([('string', 'foo')], 4)
153 154 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
154 155 ([('string', 'foo"'), ('string', 'bar')], 9)
155 156 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
156 157 ([('string', 'foo\\')], 6)
157 158 """
158 159 parsed = []
159 160 sepchars = '{' + quote
160 161 pos = start
161 162 p = parser.parser(elements)
162 163 while pos < stop:
163 164 n = min((tmpl.find(c, pos, stop) for c in sepchars),
164 165 key=lambda n: (n < 0, n))
165 166 if n < 0:
166 167 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
167 168 pos = stop
168 169 break
169 170 c = tmpl[n]
170 171 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
171 172 if bs % 2 == 1:
172 173 # escaped (e.g. '\{', '\\\{', but not '\\{')
173 174 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
174 175 pos = n + 1
175 176 continue
176 177 if n > pos:
177 178 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
178 179 if c == quote:
179 180 return parsed, n + 1
180 181
181 182 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
182 183 parsed.append(parseres)
183 184
184 185 if quote:
185 186 raise error.ParseError(_("unterminated string"), start)
186 187 return parsed, pos
187 188
188 189 def _unnesttemplatelist(tree):
189 190 """Expand list of templates to node tuple
190 191
191 192 >>> def f(tree):
192 193 ... print prettyformat(_unnesttemplatelist(tree))
193 194 >>> f(('template', []))
194 195 ('string', '')
195 196 >>> f(('template', [('string', 'foo')]))
196 197 ('string', 'foo')
197 198 >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
198 199 (template
199 200 ('string', 'foo')
200 201 ('symbol', 'rev'))
201 202 >>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
202 203 (template
203 204 ('symbol', 'rev'))
204 205 >>> f(('template', [('template', [('string', 'foo')])]))
205 206 ('string', 'foo')
206 207 """
207 208 if not isinstance(tree, tuple):
208 209 return tree
209 210 op = tree[0]
210 211 if op != 'template':
211 212 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
212 213
213 214 assert len(tree) == 2
214 215 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
215 216 if not xs:
216 217 return ('string', '') # empty template ""
217 218 elif len(xs) == 1 and xs[0][0] == 'string':
218 219 return xs[0] # fast path for string with no template fragment "x"
219 220 else:
220 221 return (op,) + xs
221 222
222 223 def parse(tmpl):
223 224 """Parse template string into tree"""
224 225 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
225 226 assert pos == len(tmpl), 'unquoted template should be consumed'
226 227 return _unnesttemplatelist(('template', parsed))
227 228
228 229 def _parseexpr(expr):
229 230 """Parse a template expression into tree
230 231
231 232 >>> _parseexpr('"foo"')
232 233 ('string', 'foo')
233 234 >>> _parseexpr('foo(bar)')
234 235 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
235 236 >>> _parseexpr('foo(')
236 237 Traceback (most recent call last):
237 238 ...
238 239 ParseError: ('not a prefix: end', 4)
239 240 >>> _parseexpr('"foo" "bar"')
240 241 Traceback (most recent call last):
241 242 ...
242 243 ParseError: ('invalid token', 7)
243 244 """
244 245 p = parser.parser(elements)
245 246 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
246 247 if pos != len(expr):
247 248 raise error.ParseError(_('invalid token'), pos)
248 249 return _unnesttemplatelist(tree)
249 250
250 251 def prettyformat(tree):
251 252 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
252 253
253 254 def compileexp(exp, context, curmethods):
254 255 """Compile parsed template tree to (func, data) pair"""
255 256 t = exp[0]
256 257 if t in curmethods:
257 258 return curmethods[t](exp, context)
258 259 raise error.ParseError(_("unknown method '%s'") % t)
259 260
260 261 # template evaluation
261 262
262 263 def getsymbol(exp):
263 264 if exp[0] == 'symbol':
264 265 return exp[1]
265 266 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
266 267
267 268 def getlist(x):
268 269 if not x:
269 270 return []
270 271 if x[0] == 'list':
271 272 return getlist(x[1]) + [x[2]]
272 273 return [x]
273 274
274 275 def gettemplate(exp, context):
275 276 """Compile given template tree or load named template from map file;
276 277 returns (func, data) pair"""
277 278 if exp[0] in ('template', 'string'):
278 279 return compileexp(exp, context, methods)
279 280 if exp[0] == 'symbol':
280 281 # unlike runsymbol(), here 'symbol' is always taken as template name
281 282 # even if it exists in mapping. this allows us to override mapping
282 283 # by web templates, e.g. 'changelogtag' is redefined in map file.
283 284 return context._load(exp[1])
284 285 raise error.ParseError(_("expected template specifier"))
285 286
286 287 def evalfuncarg(context, mapping, arg):
287 288 func, data = arg
288 289 # func() may return string, generator of strings or arbitrary object such
289 290 # as date tuple, but filter does not want generator.
290 291 thing = func(context, mapping, data)
291 292 if isinstance(thing, types.GeneratorType):
292 293 thing = stringify(thing)
293 294 return thing
294 295
295 296 def evalboolean(context, mapping, arg):
296 297 """Evaluate given argument as boolean, but also takes boolean literals"""
297 298 func, data = arg
298 299 if func is runsymbol:
299 300 thing = func(context, mapping, data, default=None)
300 301 if thing is None:
301 302 # not a template keyword, takes as a boolean literal
302 303 thing = util.parsebool(data)
303 304 else:
304 305 thing = func(context, mapping, data)
305 306 if isinstance(thing, bool):
306 307 return thing
307 308 # other objects are evaluated as strings, which means 0 is True, but
308 309 # empty dict/list should be False as they are expected to be ''
309 310 return bool(stringify(thing))
310 311
311 312 def evalinteger(context, mapping, arg, err):
312 313 v = evalfuncarg(context, mapping, arg)
313 314 try:
314 315 return int(v)
315 316 except (TypeError, ValueError):
316 317 raise error.ParseError(err)
317 318
318 319 def evalstring(context, mapping, arg):
319 320 func, data = arg
320 321 return stringify(func(context, mapping, data))
321 322
322 323 def evalstringliteral(context, mapping, arg):
323 324 """Evaluate given argument as string template, but returns symbol name
324 325 if it is unknown"""
325 326 func, data = arg
326 327 if func is runsymbol:
327 328 thing = func(context, mapping, data, default=data)
328 329 else:
329 330 thing = func(context, mapping, data)
330 331 return stringify(thing)
331 332
332 333 def runinteger(context, mapping, data):
333 334 return int(data)
334 335
335 336 def runstring(context, mapping, data):
336 337 return data
337 338
338 339 def _recursivesymbolblocker(key):
339 340 def showrecursion(**args):
340 341 raise error.Abort(_("recursive reference '%s' in template") % key)
341 342 return showrecursion
342 343
343 344 def _runrecursivesymbol(context, mapping, key):
344 345 raise error.Abort(_("recursive reference '%s' in template") % key)
345 346
346 347 def runsymbol(context, mapping, key, default=''):
347 348 v = mapping.get(key)
348 349 if v is None:
349 350 v = context._defaults.get(key)
350 351 if v is None:
351 352 # put poison to cut recursion. we can't move this to parsing phase
352 353 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
353 354 safemapping = mapping.copy()
354 355 safemapping[key] = _recursivesymbolblocker(key)
355 356 try:
356 357 v = context.process(key, safemapping)
357 358 except TemplateNotFound:
358 359 v = default
359 360 if callable(v):
360 361 return v(**mapping)
361 362 return v
362 363
363 364 def buildtemplate(exp, context):
364 365 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
365 366 return (runtemplate, ctmpl)
366 367
367 368 def runtemplate(context, mapping, template):
368 369 for func, data in template:
369 370 yield func(context, mapping, data)
370 371
371 372 def buildfilter(exp, context):
372 373 arg = compileexp(exp[1], context, methods)
373 374 n = getsymbol(exp[2])
374 375 if n in context._filters:
375 376 filt = context._filters[n]
376 377 return (runfilter, (arg, filt))
377 378 if n in funcs:
378 379 f = funcs[n]
379 380 return (f, [arg])
380 381 raise error.ParseError(_("unknown function '%s'") % n)
381 382
382 383 def runfilter(context, mapping, data):
383 384 arg, filt = data
384 385 thing = evalfuncarg(context, mapping, arg)
385 386 try:
386 387 return filt(thing)
387 388 except (ValueError, AttributeError, TypeError):
388 389 if isinstance(arg[1], tuple):
389 390 dt = arg[1][1]
390 391 else:
391 392 dt = arg[1]
392 393 raise error.Abort(_("template filter '%s' is not compatible with "
393 394 "keyword '%s'") % (filt.func_name, dt))
394 395
395 396 def buildmap(exp, context):
396 397 func, data = compileexp(exp[1], context, methods)
397 398 tfunc, tdata = gettemplate(exp[2], context)
398 399 return (runmap, (func, data, tfunc, tdata))
399 400
400 401 def runmap(context, mapping, data):
401 402 func, data, tfunc, tdata = data
402 403 d = func(context, mapping, data)
403 404 if util.safehasattr(d, 'itermaps'):
404 405 diter = d.itermaps()
405 406 else:
406 407 try:
407 408 diter = iter(d)
408 409 except TypeError:
409 410 if func is runsymbol:
410 411 raise error.ParseError(_("keyword '%s' is not iterable") % data)
411 412 else:
412 413 raise error.ParseError(_("%r is not iterable") % d)
413 414
414 415 for i, v in enumerate(diter):
415 416 lm = mapping.copy()
416 417 lm['index'] = i
417 418 if isinstance(v, dict):
418 419 lm.update(v)
419 420 lm['originalnode'] = mapping.get('node')
420 421 yield tfunc(context, lm, tdata)
421 422 else:
422 423 # v is not an iterable of dicts, this happen when 'key'
423 424 # has been fully expanded already and format is useless.
424 425 # If so, return the expanded value.
425 426 yield v
426 427
427 428 def buildnegate(exp, context):
428 429 arg = compileexp(exp[1], context, exprmethods)
429 430 return (runnegate, arg)
430 431
431 432 def runnegate(context, mapping, data):
432 433 data = evalinteger(context, mapping, data,
433 434 _('negation needs an integer argument'))
434 435 return -data
435 436
436 437 def buildarithmetic(exp, context, func):
437 438 left = compileexp(exp[1], context, exprmethods)
438 439 right = compileexp(exp[2], context, exprmethods)
439 440 return (runarithmetic, (func, left, right))
440 441
441 442 def runarithmetic(context, mapping, data):
442 443 func, left, right = data
443 444 left = evalinteger(context, mapping, left,
444 445 _('arithmetic only defined on integers'))
445 446 right = evalinteger(context, mapping, right,
446 447 _('arithmetic only defined on integers'))
447 448 try:
448 449 return func(left, right)
449 450 except ZeroDivisionError:
450 451 raise error.Abort(_('division by zero is not defined'))
451 452
452 453 def buildfunc(exp, context):
453 454 n = getsymbol(exp[1])
454 455 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
455 456 if n in funcs:
456 457 f = funcs[n]
457 458 return (f, args)
458 459 if n in context._filters:
459 460 if len(args) != 1:
460 461 raise error.ParseError(_("filter %s expects one argument") % n)
461 462 f = context._filters[n]
462 463 return (runfilter, (args[0], f))
463 464 raise error.ParseError(_("unknown function '%s'") % n)
464 465
466 def buildkeyvaluepair(exp, content):
467 raise error.ParseError(_("can't use a key-value pair in this context"))
468
465 469 # dict of template built-in functions
466 470 funcs = {}
467 471
468 472 templatefunc = registrar.templatefunc(funcs)
469 473
470 474 @templatefunc('date(date[, fmt])')
471 475 def date(context, mapping, args):
472 476 """Format a date. See :hg:`help dates` for formatting
473 477 strings. The default is a Unix date format, including the timezone:
474 478 "Mon Sep 04 15:13:13 2006 0700"."""
475 479 if not (1 <= len(args) <= 2):
476 480 # i18n: "date" is a keyword
477 481 raise error.ParseError(_("date expects one or two arguments"))
478 482
479 483 date = evalfuncarg(context, mapping, args[0])
480 484 fmt = None
481 485 if len(args) == 2:
482 486 fmt = evalstring(context, mapping, args[1])
483 487 try:
484 488 if fmt is None:
485 489 return util.datestr(date)
486 490 else:
487 491 return util.datestr(date, fmt)
488 492 except (TypeError, ValueError):
489 493 # i18n: "date" is a keyword
490 494 raise error.ParseError(_("date expects a date information"))
491 495
492 496 @templatefunc('diff([includepattern [, excludepattern]])')
493 497 def diff(context, mapping, args):
494 498 """Show a diff, optionally
495 499 specifying files to include or exclude."""
496 500 if len(args) > 2:
497 501 # i18n: "diff" is a keyword
498 502 raise error.ParseError(_("diff expects zero, one, or two arguments"))
499 503
500 504 def getpatterns(i):
501 505 if i < len(args):
502 506 s = evalstring(context, mapping, args[i]).strip()
503 507 if s:
504 508 return [s]
505 509 return []
506 510
507 511 ctx = mapping['ctx']
508 512 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
509 513
510 514 return ''.join(chunks)
511 515
512 516 @templatefunc('files(pattern)')
513 517 def files(context, mapping, args):
514 518 """All files of the current changeset matching the pattern. See
515 519 :hg:`help patterns`."""
516 520 if not len(args) == 1:
517 521 # i18n: "files" is a keyword
518 522 raise error.ParseError(_("files expects one argument"))
519 523
520 524 raw = evalstring(context, mapping, args[0])
521 525 ctx = mapping['ctx']
522 526 m = ctx.match([raw])
523 527 files = list(ctx.matches(m))
524 528 return templatekw.showlist("file", files, **mapping)
525 529
526 530 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
527 531 def fill(context, mapping, args):
528 532 """Fill many
529 533 paragraphs with optional indentation. See the "fill" filter."""
530 534 if not (1 <= len(args) <= 4):
531 535 # i18n: "fill" is a keyword
532 536 raise error.ParseError(_("fill expects one to four arguments"))
533 537
534 538 text = evalstring(context, mapping, args[0])
535 539 width = 76
536 540 initindent = ''
537 541 hangindent = ''
538 542 if 2 <= len(args) <= 4:
539 543 width = evalinteger(context, mapping, args[1],
540 544 # i18n: "fill" is a keyword
541 545 _("fill expects an integer width"))
542 546 try:
543 547 initindent = evalstring(context, mapping, args[2])
544 548 hangindent = evalstring(context, mapping, args[3])
545 549 except IndexError:
546 550 pass
547 551
548 552 return templatefilters.fill(text, width, initindent, hangindent)
549 553
550 554 @templatefunc('formatnode(node)')
551 555 def formatnode(context, mapping, args):
552 556 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
553 557 if len(args) != 1:
554 558 # i18n: "formatnode" is a keyword
555 559 raise error.ParseError(_("formatnode expects one argument"))
556 560
557 561 ui = mapping['ui']
558 562 node = evalstring(context, mapping, args[0])
559 563 if ui.debugflag:
560 564 return node
561 565 return templatefilters.short(node)
562 566
563 567 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])')
564 568 def pad(context, mapping, args):
565 569 """Pad text with a
566 570 fill character."""
567 571 if not (2 <= len(args) <= 4):
568 572 # i18n: "pad" is a keyword
569 573 raise error.ParseError(_("pad() expects two to four arguments"))
570 574
571 575 width = evalinteger(context, mapping, args[1],
572 576 # i18n: "pad" is a keyword
573 577 _("pad() expects an integer width"))
574 578
575 579 text = evalstring(context, mapping, args[0])
576 580
577 581 left = False
578 582 fillchar = ' '
579 583 if len(args) > 2:
580 584 fillchar = evalstring(context, mapping, args[2])
581 585 if len(color.stripeffects(fillchar)) != 1:
582 586 # i18n: "pad" is a keyword
583 587 raise error.ParseError(_("pad() expects a single fill character"))
584 588 if len(args) > 3:
585 589 left = evalboolean(context, mapping, args[3])
586 590
587 591 fillwidth = width - encoding.colwidth(color.stripeffects(text))
588 592 if fillwidth <= 0:
589 593 return text
590 594 if left:
591 595 return fillchar * fillwidth + text
592 596 else:
593 597 return text + fillchar * fillwidth
594 598
595 599 @templatefunc('indent(text, indentchars[, firstline])')
596 600 def indent(context, mapping, args):
597 601 """Indents all non-empty lines
598 602 with the characters given in the indentchars string. An optional
599 603 third parameter will override the indent for the first line only
600 604 if present."""
601 605 if not (2 <= len(args) <= 3):
602 606 # i18n: "indent" is a keyword
603 607 raise error.ParseError(_("indent() expects two or three arguments"))
604 608
605 609 text = evalstring(context, mapping, args[0])
606 610 indent = evalstring(context, mapping, args[1])
607 611
608 612 if len(args) == 3:
609 613 firstline = evalstring(context, mapping, args[2])
610 614 else:
611 615 firstline = indent
612 616
613 617 # the indent function doesn't indent the first line, so we do it here
614 618 return templatefilters.indent(firstline + text, indent)
615 619
616 620 @templatefunc('get(dict, key)')
617 621 def get(context, mapping, args):
618 622 """Get an attribute/key from an object. Some keywords
619 623 are complex types. This function allows you to obtain the value of an
620 624 attribute on these types."""
621 625 if len(args) != 2:
622 626 # i18n: "get" is a keyword
623 627 raise error.ParseError(_("get() expects two arguments"))
624 628
625 629 dictarg = evalfuncarg(context, mapping, args[0])
626 630 if not util.safehasattr(dictarg, 'get'):
627 631 # i18n: "get" is a keyword
628 632 raise error.ParseError(_("get() expects a dict as first argument"))
629 633
630 634 key = evalfuncarg(context, mapping, args[1])
631 635 return dictarg.get(key)
632 636
633 637 @templatefunc('if(expr, then[, else])')
634 638 def if_(context, mapping, args):
635 639 """Conditionally execute based on the result of
636 640 an expression."""
637 641 if not (2 <= len(args) <= 3):
638 642 # i18n: "if" is a keyword
639 643 raise error.ParseError(_("if expects two or three arguments"))
640 644
641 645 test = evalboolean(context, mapping, args[0])
642 646 if test:
643 647 yield args[1][0](context, mapping, args[1][1])
644 648 elif len(args) == 3:
645 649 yield args[2][0](context, mapping, args[2][1])
646 650
647 651 @templatefunc('ifcontains(needle, haystack, then[, else])')
648 652 def ifcontains(context, mapping, args):
649 653 """Conditionally execute based
650 654 on whether the item "needle" is in "haystack"."""
651 655 if not (3 <= len(args) <= 4):
652 656 # i18n: "ifcontains" is a keyword
653 657 raise error.ParseError(_("ifcontains expects three or four arguments"))
654 658
655 659 needle = evalstring(context, mapping, args[0])
656 660 haystack = evalfuncarg(context, mapping, args[1])
657 661
658 662 if needle in haystack:
659 663 yield args[2][0](context, mapping, args[2][1])
660 664 elif len(args) == 4:
661 665 yield args[3][0](context, mapping, args[3][1])
662 666
663 667 @templatefunc('ifeq(expr1, expr2, then[, else])')
664 668 def ifeq(context, mapping, args):
665 669 """Conditionally execute based on
666 670 whether 2 items are equivalent."""
667 671 if not (3 <= len(args) <= 4):
668 672 # i18n: "ifeq" is a keyword
669 673 raise error.ParseError(_("ifeq expects three or four arguments"))
670 674
671 675 test = evalstring(context, mapping, args[0])
672 676 match = evalstring(context, mapping, args[1])
673 677 if test == match:
674 678 yield args[2][0](context, mapping, args[2][1])
675 679 elif len(args) == 4:
676 680 yield args[3][0](context, mapping, args[3][1])
677 681
678 682 @templatefunc('join(list, sep)')
679 683 def join(context, mapping, args):
680 684 """Join items in a list with a delimiter."""
681 685 if not (1 <= len(args) <= 2):
682 686 # i18n: "join" is a keyword
683 687 raise error.ParseError(_("join expects one or two arguments"))
684 688
685 689 joinset = args[0][0](context, mapping, args[0][1])
686 690 if util.safehasattr(joinset, 'itermaps'):
687 691 jf = joinset.joinfmt
688 692 joinset = [jf(x) for x in joinset.itermaps()]
689 693
690 694 joiner = " "
691 695 if len(args) > 1:
692 696 joiner = evalstring(context, mapping, args[1])
693 697
694 698 first = True
695 699 for x in joinset:
696 700 if first:
697 701 first = False
698 702 else:
699 703 yield joiner
700 704 yield x
701 705
702 706 @templatefunc('label(label, expr)')
703 707 def label(context, mapping, args):
704 708 """Apply a label to generated content. Content with
705 709 a label applied can result in additional post-processing, such as
706 710 automatic colorization."""
707 711 if len(args) != 2:
708 712 # i18n: "label" is a keyword
709 713 raise error.ParseError(_("label expects two arguments"))
710 714
711 715 ui = mapping['ui']
712 716 thing = evalstring(context, mapping, args[1])
713 717 # preserve unknown symbol as literal so effects like 'red', 'bold',
714 718 # etc. don't need to be quoted
715 719 label = evalstringliteral(context, mapping, args[0])
716 720
717 721 return ui.label(thing, label)
718 722
719 723 @templatefunc('latesttag([pattern])')
720 724 def latesttag(context, mapping, args):
721 725 """The global tags matching the given pattern on the
722 726 most recent globally tagged ancestor of this changeset.
723 727 If no such tags exist, the "{tag}" template resolves to
724 728 the string "null"."""
725 729 if len(args) > 1:
726 730 # i18n: "latesttag" is a keyword
727 731 raise error.ParseError(_("latesttag expects at most one argument"))
728 732
729 733 pattern = None
730 734 if len(args) == 1:
731 735 pattern = evalstring(context, mapping, args[0])
732 736
733 737 return templatekw.showlatesttags(pattern, **mapping)
734 738
735 739 @templatefunc('localdate(date[, tz])')
736 740 def localdate(context, mapping, args):
737 741 """Converts a date to the specified timezone.
738 742 The default is local date."""
739 743 if not (1 <= len(args) <= 2):
740 744 # i18n: "localdate" is a keyword
741 745 raise error.ParseError(_("localdate expects one or two arguments"))
742 746
743 747 date = evalfuncarg(context, mapping, args[0])
744 748 try:
745 749 date = util.parsedate(date)
746 750 except AttributeError: # not str nor date tuple
747 751 # i18n: "localdate" is a keyword
748 752 raise error.ParseError(_("localdate expects a date information"))
749 753 if len(args) >= 2:
750 754 tzoffset = None
751 755 tz = evalfuncarg(context, mapping, args[1])
752 756 if isinstance(tz, str):
753 757 tzoffset, remainder = util.parsetimezone(tz)
754 758 if remainder:
755 759 tzoffset = None
756 760 if tzoffset is None:
757 761 try:
758 762 tzoffset = int(tz)
759 763 except (TypeError, ValueError):
760 764 # i18n: "localdate" is a keyword
761 765 raise error.ParseError(_("localdate expects a timezone"))
762 766 else:
763 767 tzoffset = util.makedate()[1]
764 768 return (date[0], tzoffset)
765 769
766 770 @templatefunc('mod(a, b)')
767 771 def mod(context, mapping, args):
768 772 """Calculate a mod b such that a / b + a mod b == a"""
769 773 if not len(args) == 2:
770 774 # i18n: "mod" is a keyword
771 775 raise error.ParseError(_("mod expects two arguments"))
772 776
773 777 func = lambda a, b: a % b
774 778 return runarithmetic(context, mapping, (func, args[0], args[1]))
775 779
776 780 @templatefunc('relpath(path)')
777 781 def relpath(context, mapping, args):
778 782 """Convert a repository-absolute path into a filesystem path relative to
779 783 the current working directory."""
780 784 if len(args) != 1:
781 785 # i18n: "relpath" is a keyword
782 786 raise error.ParseError(_("relpath expects one argument"))
783 787
784 788 repo = mapping['ctx'].repo()
785 789 path = evalstring(context, mapping, args[0])
786 790 return repo.pathto(path)
787 791
788 792 @templatefunc('revset(query[, formatargs...])')
789 793 def revset(context, mapping, args):
790 794 """Execute a revision set query. See
791 795 :hg:`help revset`."""
792 796 if not len(args) > 0:
793 797 # i18n: "revset" is a keyword
794 798 raise error.ParseError(_("revset expects one or more arguments"))
795 799
796 800 raw = evalstring(context, mapping, args[0])
797 801 ctx = mapping['ctx']
798 802 repo = ctx.repo()
799 803
800 804 def query(expr):
801 805 m = revsetmod.match(repo.ui, expr)
802 806 return m(repo)
803 807
804 808 if len(args) > 1:
805 809 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
806 810 revs = query(revsetlang.formatspec(raw, *formatargs))
807 811 revs = list(revs)
808 812 else:
809 813 revsetcache = mapping['cache'].setdefault("revsetcache", {})
810 814 if raw in revsetcache:
811 815 revs = revsetcache[raw]
812 816 else:
813 817 revs = query(raw)
814 818 revs = list(revs)
815 819 revsetcache[raw] = revs
816 820
817 821 return templatekw.showrevslist("revision", revs, **mapping)
818 822
819 823 @templatefunc('rstdoc(text, style)')
820 824 def rstdoc(context, mapping, args):
821 825 """Format reStructuredText."""
822 826 if len(args) != 2:
823 827 # i18n: "rstdoc" is a keyword
824 828 raise error.ParseError(_("rstdoc expects two arguments"))
825 829
826 830 text = evalstring(context, mapping, args[0])
827 831 style = evalstring(context, mapping, args[1])
828 832
829 833 return minirst.format(text, style=style, keep=['verbose'])
830 834
831 835 @templatefunc('separate(sep, args)')
832 836 def separate(context, mapping, args):
833 837 """Add a separator between non-empty arguments."""
834 838 if not args:
835 839 # i18n: "separate" is a keyword
836 840 raise error.ParseError(_("separate expects at least one argument"))
837 841
838 842 sep = evalstring(context, mapping, args[0])
839 843 first = True
840 844 for arg in args[1:]:
841 845 argstr = evalstring(context, mapping, arg)
842 846 if not argstr:
843 847 continue
844 848 if first:
845 849 first = False
846 850 else:
847 851 yield sep
848 852 yield argstr
849 853
850 854 @templatefunc('shortest(node, minlength=4)')
851 855 def shortest(context, mapping, args):
852 856 """Obtain the shortest representation of
853 857 a node."""
854 858 if not (1 <= len(args) <= 2):
855 859 # i18n: "shortest" is a keyword
856 860 raise error.ParseError(_("shortest() expects one or two arguments"))
857 861
858 862 node = evalstring(context, mapping, args[0])
859 863
860 864 minlength = 4
861 865 if len(args) > 1:
862 866 minlength = evalinteger(context, mapping, args[1],
863 867 # i18n: "shortest" is a keyword
864 868 _("shortest() expects an integer minlength"))
865 869
866 870 # _partialmatch() of filtered changelog could take O(len(repo)) time,
867 871 # which would be unacceptably slow. so we look for hash collision in
868 872 # unfiltered space, which means some hashes may be slightly longer.
869 873 cl = mapping['ctx']._repo.unfiltered().changelog
870 874 def isvalid(test):
871 875 try:
872 876 if cl._partialmatch(test) is None:
873 877 return False
874 878
875 879 try:
876 880 i = int(test)
877 881 # if we are a pure int, then starting with zero will not be
878 882 # confused as a rev; or, obviously, if the int is larger than
879 883 # the value of the tip rev
880 884 if test[0] == '0' or i > len(cl):
881 885 return True
882 886 return False
883 887 except ValueError:
884 888 return True
885 889 except error.RevlogError:
886 890 return False
887 891
888 892 shortest = node
889 893 startlength = max(6, minlength)
890 894 length = startlength
891 895 while True:
892 896 test = node[:length]
893 897 if isvalid(test):
894 898 shortest = test
895 899 if length == minlength or length > startlength:
896 900 return shortest
897 901 length -= 1
898 902 else:
899 903 length += 1
900 904 if len(shortest) <= length:
901 905 return shortest
902 906
903 907 @templatefunc('strip(text[, chars])')
904 908 def strip(context, mapping, args):
905 909 """Strip characters from a string. By default,
906 910 strips all leading and trailing whitespace."""
907 911 if not (1 <= len(args) <= 2):
908 912 # i18n: "strip" is a keyword
909 913 raise error.ParseError(_("strip expects one or two arguments"))
910 914
911 915 text = evalstring(context, mapping, args[0])
912 916 if len(args) == 2:
913 917 chars = evalstring(context, mapping, args[1])
914 918 return text.strip(chars)
915 919 return text.strip()
916 920
917 921 @templatefunc('sub(pattern, replacement, expression)')
918 922 def sub(context, mapping, args):
919 923 """Perform text substitution
920 924 using regular expressions."""
921 925 if len(args) != 3:
922 926 # i18n: "sub" is a keyword
923 927 raise error.ParseError(_("sub expects three arguments"))
924 928
925 929 pat = evalstring(context, mapping, args[0])
926 930 rpl = evalstring(context, mapping, args[1])
927 931 src = evalstring(context, mapping, args[2])
928 932 try:
929 933 patre = re.compile(pat)
930 934 except re.error:
931 935 # i18n: "sub" is a keyword
932 936 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
933 937 try:
934 938 yield patre.sub(rpl, src)
935 939 except re.error:
936 940 # i18n: "sub" is a keyword
937 941 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
938 942
939 943 @templatefunc('startswith(pattern, text)')
940 944 def startswith(context, mapping, args):
941 945 """Returns the value from the "text" argument
942 946 if it begins with the content from the "pattern" argument."""
943 947 if len(args) != 2:
944 948 # i18n: "startswith" is a keyword
945 949 raise error.ParseError(_("startswith expects two arguments"))
946 950
947 951 patn = evalstring(context, mapping, args[0])
948 952 text = evalstring(context, mapping, args[1])
949 953 if text.startswith(patn):
950 954 return text
951 955 return ''
952 956
953 957 @templatefunc('word(number, text[, separator])')
954 958 def word(context, mapping, args):
955 959 """Return the nth word from a string."""
956 960 if not (2 <= len(args) <= 3):
957 961 # i18n: "word" is a keyword
958 962 raise error.ParseError(_("word expects two or three arguments, got %d")
959 963 % len(args))
960 964
961 965 num = evalinteger(context, mapping, args[0],
962 966 # i18n: "word" is a keyword
963 967 _("word expects an integer index"))
964 968 text = evalstring(context, mapping, args[1])
965 969 if len(args) == 3:
966 970 splitter = evalstring(context, mapping, args[2])
967 971 else:
968 972 splitter = None
969 973
970 974 tokens = text.split(splitter)
971 975 if num >= len(tokens) or num < -len(tokens):
972 976 return ''
973 977 else:
974 978 return tokens[num]
975 979
976 980 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
977 981 exprmethods = {
978 982 "integer": lambda e, c: (runinteger, e[1]),
979 983 "string": lambda e, c: (runstring, e[1]),
980 984 "symbol": lambda e, c: (runsymbol, e[1]),
981 985 "template": buildtemplate,
982 986 "group": lambda e, c: compileexp(e[1], c, exprmethods),
983 987 # ".": buildmember,
984 988 "|": buildfilter,
985 989 "%": buildmap,
986 990 "func": buildfunc,
991 "keyvalue": buildkeyvaluepair,
987 992 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
988 993 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
989 994 "negate": buildnegate,
990 995 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
991 996 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
992 997 }
993 998
994 999 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
995 1000 methods = exprmethods.copy()
996 1001 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
997 1002
998 1003 class _aliasrules(parser.basealiasrules):
999 1004 """Parsing and expansion rule set of template aliases"""
1000 1005 _section = _('template alias')
1001 1006 _parse = staticmethod(_parseexpr)
1002 1007
1003 1008 @staticmethod
1004 1009 def _trygetfunc(tree):
1005 1010 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1006 1011 None"""
1007 1012 if tree[0] == 'func' and tree[1][0] == 'symbol':
1008 1013 return tree[1][1], getlist(tree[2])
1009 1014 if tree[0] == '|' and tree[2][0] == 'symbol':
1010 1015 return tree[2][1], [tree[1]]
1011 1016
1012 1017 def expandaliases(tree, aliases):
1013 1018 """Return new tree of aliases are expanded"""
1014 1019 aliasmap = _aliasrules.buildmap(aliases)
1015 1020 return _aliasrules.expand(aliasmap, tree)
1016 1021
1017 1022 # template engine
1018 1023
1019 1024 stringify = templatefilters.stringify
1020 1025
1021 1026 def _flatten(thing):
1022 1027 '''yield a single stream from a possibly nested set of iterators'''
1023 1028 thing = templatekw.unwraphybrid(thing)
1024 1029 if isinstance(thing, str):
1025 1030 yield thing
1026 1031 elif thing is None:
1027 1032 pass
1028 1033 elif not util.safehasattr(thing, '__iter__'):
1029 1034 yield str(thing)
1030 1035 else:
1031 1036 for i in thing:
1032 1037 i = templatekw.unwraphybrid(i)
1033 1038 if isinstance(i, str):
1034 1039 yield i
1035 1040 elif i is None:
1036 1041 pass
1037 1042 elif not util.safehasattr(i, '__iter__'):
1038 1043 yield str(i)
1039 1044 else:
1040 1045 for j in _flatten(i):
1041 1046 yield j
1042 1047
1043 1048 def unquotestring(s):
1044 1049 '''unwrap quotes if any; otherwise returns unmodified string'''
1045 1050 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1046 1051 return s
1047 1052 return s[1:-1]
1048 1053
1049 1054 class engine(object):
1050 1055 '''template expansion engine.
1051 1056
1052 1057 template expansion works like this. a map file contains key=value
1053 1058 pairs. if value is quoted, it is treated as string. otherwise, it
1054 1059 is treated as name of template file.
1055 1060
1056 1061 templater is asked to expand a key in map. it looks up key, and
1057 1062 looks for strings like this: {foo}. it expands {foo} by looking up
1058 1063 foo in map, and substituting it. expansion is recursive: it stops
1059 1064 when there is no more {foo} to replace.
1060 1065
1061 1066 expansion also allows formatting and filtering.
1062 1067
1063 1068 format uses key to expand each item in list. syntax is
1064 1069 {key%format}.
1065 1070
1066 1071 filter uses function to transform value. syntax is
1067 1072 {key|filter1|filter2|...}.'''
1068 1073
1069 1074 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1070 1075 self._loader = loader
1071 1076 if filters is None:
1072 1077 filters = {}
1073 1078 self._filters = filters
1074 1079 if defaults is None:
1075 1080 defaults = {}
1076 1081 self._defaults = defaults
1077 1082 self._aliasmap = _aliasrules.buildmap(aliases)
1078 1083 self._cache = {} # key: (func, data)
1079 1084
1080 1085 def _load(self, t):
1081 1086 '''load, parse, and cache a template'''
1082 1087 if t not in self._cache:
1083 1088 # put poison to cut recursion while compiling 't'
1084 1089 self._cache[t] = (_runrecursivesymbol, t)
1085 1090 try:
1086 1091 x = parse(self._loader(t))
1087 1092 if self._aliasmap:
1088 1093 x = _aliasrules.expand(self._aliasmap, x)
1089 1094 self._cache[t] = compileexp(x, self, methods)
1090 1095 except: # re-raises
1091 1096 del self._cache[t]
1092 1097 raise
1093 1098 return self._cache[t]
1094 1099
1095 1100 def process(self, t, mapping):
1096 1101 '''Perform expansion. t is name of map element to expand.
1097 1102 mapping contains added elements for use during expansion. Is a
1098 1103 generator.'''
1099 1104 func, data = self._load(t)
1100 1105 return _flatten(func(self, mapping, data))
1101 1106
1102 1107 engines = {'default': engine}
1103 1108
1104 1109 def stylelist():
1105 1110 paths = templatepaths()
1106 1111 if not paths:
1107 1112 return _('no templates found, try `hg debuginstall` for more info')
1108 1113 dirlist = os.listdir(paths[0])
1109 1114 stylelist = []
1110 1115 for file in dirlist:
1111 1116 split = file.split(".")
1112 1117 if split[-1] in ('orig', 'rej'):
1113 1118 continue
1114 1119 if split[0] == "map-cmdline":
1115 1120 stylelist.append(split[1])
1116 1121 return ", ".join(sorted(stylelist))
1117 1122
1118 1123 def _readmapfile(mapfile):
1119 1124 """Load template elements from the given map file"""
1120 1125 if not os.path.exists(mapfile):
1121 1126 raise error.Abort(_("style '%s' not found") % mapfile,
1122 1127 hint=_("available styles: %s") % stylelist())
1123 1128
1124 1129 base = os.path.dirname(mapfile)
1125 1130 conf = config.config(includepaths=templatepaths())
1126 1131 conf.read(mapfile)
1127 1132
1128 1133 cache = {}
1129 1134 tmap = {}
1130 1135 for key, val in conf[''].items():
1131 1136 if not val:
1132 1137 raise error.ParseError(_('missing value'), conf.source('', key))
1133 1138 if val[0] in "'\"":
1134 1139 if val[0] != val[-1]:
1135 1140 raise error.ParseError(_('unmatched quotes'),
1136 1141 conf.source('', key))
1137 1142 cache[key] = unquotestring(val)
1138 1143 elif key == "__base__":
1139 1144 # treat as a pointer to a base class for this style
1140 1145 path = util.normpath(os.path.join(base, val))
1141 1146
1142 1147 # fallback check in template paths
1143 1148 if not os.path.exists(path):
1144 1149 for p in templatepaths():
1145 1150 p2 = util.normpath(os.path.join(p, val))
1146 1151 if os.path.isfile(p2):
1147 1152 path = p2
1148 1153 break
1149 1154 p3 = util.normpath(os.path.join(p2, "map"))
1150 1155 if os.path.isfile(p3):
1151 1156 path = p3
1152 1157 break
1153 1158
1154 1159 bcache, btmap = _readmapfile(path)
1155 1160 for k in bcache:
1156 1161 if k not in cache:
1157 1162 cache[k] = bcache[k]
1158 1163 for k in btmap:
1159 1164 if k not in tmap:
1160 1165 tmap[k] = btmap[k]
1161 1166 else:
1162 1167 val = 'default', val
1163 1168 if ':' in val[1]:
1164 1169 val = val[1].split(':', 1)
1165 1170 tmap[key] = val[0], os.path.join(base, val[1])
1166 1171 return cache, tmap
1167 1172
1168 1173 class TemplateNotFound(error.Abort):
1169 1174 pass
1170 1175
1171 1176 class templater(object):
1172 1177
1173 1178 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1174 1179 minchunk=1024, maxchunk=65536):
1175 1180 '''set up template engine.
1176 1181 filters is dict of functions. each transforms a value into another.
1177 1182 defaults is dict of default map definitions.
1178 1183 aliases is list of alias (name, replacement) pairs.
1179 1184 '''
1180 1185 if filters is None:
1181 1186 filters = {}
1182 1187 if defaults is None:
1183 1188 defaults = {}
1184 1189 if cache is None:
1185 1190 cache = {}
1186 1191 self.cache = cache.copy()
1187 1192 self.map = {}
1188 1193 self.filters = templatefilters.filters.copy()
1189 1194 self.filters.update(filters)
1190 1195 self.defaults = defaults
1191 1196 self._aliases = aliases
1192 1197 self.minchunk, self.maxchunk = minchunk, maxchunk
1193 1198 self.ecache = {}
1194 1199
1195 1200 @classmethod
1196 1201 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1197 1202 minchunk=1024, maxchunk=65536):
1198 1203 """Create templater from the specified map file"""
1199 1204 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1200 1205 cache, tmap = _readmapfile(mapfile)
1201 1206 t.cache.update(cache)
1202 1207 t.map = tmap
1203 1208 return t
1204 1209
1205 1210 def __contains__(self, key):
1206 1211 return key in self.cache or key in self.map
1207 1212
1208 1213 def load(self, t):
1209 1214 '''Get the template for the given template name. Use a local cache.'''
1210 1215 if t not in self.cache:
1211 1216 try:
1212 1217 self.cache[t] = util.readfile(self.map[t][1])
1213 1218 except KeyError as inst:
1214 1219 raise TemplateNotFound(_('"%s" not in template map') %
1215 1220 inst.args[0])
1216 1221 except IOError as inst:
1217 1222 raise IOError(inst.args[0], _('template file %s: %s') %
1218 1223 (self.map[t][1], inst.args[1]))
1219 1224 return self.cache[t]
1220 1225
1221 1226 def __call__(self, t, **mapping):
1222 1227 ttype = t in self.map and self.map[t][0] or 'default'
1223 1228 if ttype not in self.ecache:
1224 1229 try:
1225 1230 ecls = engines[ttype]
1226 1231 except KeyError:
1227 1232 raise error.Abort(_('invalid template engine: %s') % ttype)
1228 1233 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1229 1234 self._aliases)
1230 1235 proc = self.ecache[ttype]
1231 1236
1232 1237 stream = proc.process(t, mapping)
1233 1238 if self.minchunk:
1234 1239 stream = util.increasingchunks(stream, min=self.minchunk,
1235 1240 max=self.maxchunk)
1236 1241 return stream
1237 1242
1238 1243 def templatepaths():
1239 1244 '''return locations used for template files.'''
1240 1245 pathsrel = ['templates']
1241 1246 paths = [os.path.normpath(os.path.join(util.datapath, f))
1242 1247 for f in pathsrel]
1243 1248 return [p for p in paths if os.path.isdir(p)]
1244 1249
1245 1250 def templatepath(name):
1246 1251 '''return location of template file. returns None if not found.'''
1247 1252 for p in templatepaths():
1248 1253 f = os.path.join(p, name)
1249 1254 if os.path.exists(f):
1250 1255 return f
1251 1256 return None
1252 1257
1253 1258 def stylemap(styles, paths=None):
1254 1259 """Return path to mapfile for a given style.
1255 1260
1256 1261 Searches mapfile in the following locations:
1257 1262 1. templatepath/style/map
1258 1263 2. templatepath/map-style
1259 1264 3. templatepath/map
1260 1265 """
1261 1266
1262 1267 if paths is None:
1263 1268 paths = templatepaths()
1264 1269 elif isinstance(paths, str):
1265 1270 paths = [paths]
1266 1271
1267 1272 if isinstance(styles, str):
1268 1273 styles = [styles]
1269 1274
1270 1275 for style in styles:
1271 1276 # only plain name is allowed to honor template paths
1272 1277 if (not style
1273 1278 or style in (os.curdir, os.pardir)
1274 1279 or pycompat.ossep in style
1275 1280 or pycompat.osaltsep and pycompat.osaltsep in style):
1276 1281 continue
1277 1282 locations = [os.path.join(style, 'map'), 'map-' + style]
1278 1283 locations.append('map')
1279 1284
1280 1285 for path in paths:
1281 1286 for location in locations:
1282 1287 mapfile = os.path.join(path, location)
1283 1288 if os.path.isfile(mapfile):
1284 1289 return style, mapfile
1285 1290
1286 1291 raise RuntimeError("No hgweb templates found in %r" % paths)
1287 1292
1288 1293 def loadfunction(ui, extname, registrarobj):
1289 1294 """Load template function from specified registrarobj
1290 1295 """
1291 1296 for name, func in registrarobj._table.iteritems():
1292 1297 funcs[name] = func
1293 1298
1294 1299 # tell hggettext to extract docstrings from these functions:
1295 1300 i18nfunctions = funcs.values()
@@ -1,4169 +1,4181
1 1 $ hg init a
2 2 $ cd a
3 3 $ echo a > a
4 4 $ hg add a
5 5 $ echo line 1 > b
6 6 $ echo line 2 >> b
7 7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8 8
9 9 $ hg add b
10 10 $ echo other 1 > c
11 11 $ echo other 2 >> c
12 12 $ echo >> c
13 13 $ echo other 3 >> c
14 14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15 15
16 16 $ hg add c
17 17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 18 $ echo c >> c
19 19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 20
21 21 $ echo foo > .hg/branch
22 22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23 23
24 24 $ hg co -q 3
25 25 $ echo other 4 >> d
26 26 $ hg add d
27 27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28 28
29 29 $ hg merge -q foo
30 30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31 31
32 32 Test arithmetic operators have the right precedence:
33 33
34 34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
35 35 2020 1964
36 36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
37 37 9860 5908
38 38
39 39 Test division:
40 40
41 41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
42 42 (template
43 43 (/
44 44 ('integer', '5')
45 45 ('integer', '2'))
46 46 ('string', ' ')
47 47 (func
48 48 ('symbol', 'mod')
49 49 (list
50 50 ('integer', '5')
51 51 ('integer', '2')))
52 52 ('string', '\n'))
53 53 2 1
54 54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
55 55 (template
56 56 (/
57 57 ('integer', '5')
58 58 (negate
59 59 ('integer', '2')))
60 60 ('string', ' ')
61 61 (func
62 62 ('symbol', 'mod')
63 63 (list
64 64 ('integer', '5')
65 65 (negate
66 66 ('integer', '2'))))
67 67 ('string', '\n'))
68 68 -3 -1
69 69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
70 70 (template
71 71 (/
72 72 (negate
73 73 ('integer', '5'))
74 74 ('integer', '2'))
75 75 ('string', ' ')
76 76 (func
77 77 ('symbol', 'mod')
78 78 (list
79 79 (negate
80 80 ('integer', '5'))
81 81 ('integer', '2')))
82 82 ('string', '\n'))
83 83 -3 1
84 84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
85 85 (template
86 86 (/
87 87 (negate
88 88 ('integer', '5'))
89 89 (negate
90 90 ('integer', '2')))
91 91 ('string', ' ')
92 92 (func
93 93 ('symbol', 'mod')
94 94 (list
95 95 (negate
96 96 ('integer', '5'))
97 97 (negate
98 98 ('integer', '2'))))
99 99 ('string', '\n'))
100 100 2 -1
101 101
102 102 Filters bind closer than arithmetic:
103 103
104 104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
105 105 (template
106 106 (-
107 107 (|
108 108 (func
109 109 ('symbol', 'revset')
110 110 ('string', '.'))
111 111 ('symbol', 'count'))
112 112 ('integer', '1'))
113 113 ('string', '\n'))
114 114 0
115 115
116 116 But negate binds closer still:
117 117
118 118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
119 119 (template
120 120 (-
121 121 ('integer', '1')
122 122 (|
123 123 ('integer', '3')
124 124 ('symbol', 'stringify')))
125 125 ('string', '\n'))
126 126 hg: parse error: arithmetic only defined on integers
127 127 [255]
128 128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
129 129 (template
130 130 (|
131 131 (negate
132 132 ('integer', '3'))
133 133 ('symbol', 'stringify'))
134 134 ('string', '\n'))
135 135 -3
136 136
137 Keyword arguments:
138
139 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
140 (template
141 (keyvalue
142 ('symbol', 'foo')
143 (|
144 ('symbol', 'bar')
145 ('symbol', 'baz'))))
146 hg: parse error: can't use a key-value pair in this context
147 [255]
148
137 149 Second branch starting at nullrev:
138 150
139 151 $ hg update null
140 152 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
141 153 $ echo second > second
142 154 $ hg add second
143 155 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
144 156 created new head
145 157
146 158 $ echo third > third
147 159 $ hg add third
148 160 $ hg mv second fourth
149 161 $ hg commit -m third -d "2020-01-01 10:01"
150 162
151 163 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
152 164 fourth (second)
153 165 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
154 166 second -> fourth
155 167 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
156 168 8 t
157 169 7 f
158 170
159 171 Working-directory revision has special identifiers, though they are still
160 172 experimental:
161 173
162 174 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
163 175 2147483647:ffffffffffffffffffffffffffffffffffffffff
164 176
165 177 Some keywords are invalid for working-directory revision, but they should
166 178 never cause crash:
167 179
168 180 $ hg log -r 'wdir()' -T '{manifest}\n'
169 181
170 182
171 183 Quoting for ui.logtemplate
172 184
173 185 $ hg tip --config "ui.logtemplate={rev}\n"
174 186 8
175 187 $ hg tip --config "ui.logtemplate='{rev}\n'"
176 188 8
177 189 $ hg tip --config 'ui.logtemplate="{rev}\n"'
178 190 8
179 191 $ hg tip --config 'ui.logtemplate=n{rev}\n'
180 192 n8
181 193
182 194 Make sure user/global hgrc does not affect tests
183 195
184 196 $ echo '[ui]' > .hg/hgrc
185 197 $ echo 'logtemplate =' >> .hg/hgrc
186 198 $ echo 'style =' >> .hg/hgrc
187 199
188 200 Add some simple styles to settings
189 201
190 202 $ echo '[templates]' >> .hg/hgrc
191 203 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
192 204 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
193 205
194 206 $ hg log -l1 -Tsimple
195 207 8
196 208 $ hg log -l1 -Tsimple2
197 209 8
198 210
199 211 Test templates and style maps in files:
200 212
201 213 $ echo "{rev}" > tmpl
202 214 $ hg log -l1 -T./tmpl
203 215 8
204 216 $ hg log -l1 -Tblah/blah
205 217 blah/blah (no-eol)
206 218
207 219 $ printf 'changeset = "{rev}\\n"\n' > map-simple
208 220 $ hg log -l1 -T./map-simple
209 221 8
210 222
211 223 Test template map inheritance
212 224
213 225 $ echo "__base__ = map-cmdline.default" > map-simple
214 226 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
215 227 $ hg log -l1 -T./map-simple
216 228 changeset: ***8***
217 229 tag: tip
218 230 user: test
219 231 date: Wed Jan 01 10:01:00 2020 +0000
220 232 summary: third
221 233
222 234
223 235 Template should precede style option
224 236
225 237 $ hg log -l1 --style default -T '{rev}\n'
226 238 8
227 239
228 240 Add a commit with empty description, to ensure that the templates
229 241 below will omit the description line.
230 242
231 243 $ echo c >> c
232 244 $ hg add c
233 245 $ hg commit -qm ' '
234 246
235 247 Default style is like normal output. Phases style should be the same
236 248 as default style, except for extra phase lines.
237 249
238 250 $ hg log > log.out
239 251 $ hg log --style default > style.out
240 252 $ cmp log.out style.out || diff -u log.out style.out
241 253 $ hg log -T phases > phases.out
242 254 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
243 255 +phase: draft
244 256 +phase: draft
245 257 +phase: draft
246 258 +phase: draft
247 259 +phase: draft
248 260 +phase: draft
249 261 +phase: draft
250 262 +phase: draft
251 263 +phase: draft
252 264 +phase: draft
253 265
254 266 $ hg log -v > log.out
255 267 $ hg log -v --style default > style.out
256 268 $ cmp log.out style.out || diff -u log.out style.out
257 269 $ hg log -v -T phases > phases.out
258 270 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
259 271 +phase: draft
260 272 +phase: draft
261 273 +phase: draft
262 274 +phase: draft
263 275 +phase: draft
264 276 +phase: draft
265 277 +phase: draft
266 278 +phase: draft
267 279 +phase: draft
268 280 +phase: draft
269 281
270 282 $ hg log -q > log.out
271 283 $ hg log -q --style default > style.out
272 284 $ cmp log.out style.out || diff -u log.out style.out
273 285 $ hg log -q -T phases > phases.out
274 286 $ cmp log.out phases.out || diff -u log.out phases.out
275 287
276 288 $ hg log --debug > log.out
277 289 $ hg log --debug --style default > style.out
278 290 $ cmp log.out style.out || diff -u log.out style.out
279 291 $ hg log --debug -T phases > phases.out
280 292 $ cmp log.out phases.out || diff -u log.out phases.out
281 293
282 294 Default style of working-directory revision should also be the same (but
283 295 date may change while running tests):
284 296
285 297 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
286 298 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
287 299 $ cmp log.out style.out || diff -u log.out style.out
288 300
289 301 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
290 302 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
291 303 $ cmp log.out style.out || diff -u log.out style.out
292 304
293 305 $ hg log -r 'wdir()' -q > log.out
294 306 $ hg log -r 'wdir()' -q --style default > style.out
295 307 $ cmp log.out style.out || diff -u log.out style.out
296 308
297 309 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
298 310 $ hg log -r 'wdir()' --debug --style default \
299 311 > | sed 's|^date:.*|date:|' > style.out
300 312 $ cmp log.out style.out || diff -u log.out style.out
301 313
302 314 Default style should also preserve color information (issue2866):
303 315
304 316 $ cp $HGRCPATH $HGRCPATH-bak
305 317 $ cat <<EOF >> $HGRCPATH
306 318 > [extensions]
307 319 > color=
308 320 > EOF
309 321
310 322 $ hg --color=debug log > log.out
311 323 $ hg --color=debug log --style default > style.out
312 324 $ cmp log.out style.out || diff -u log.out style.out
313 325 $ hg --color=debug log -T phases > phases.out
314 326 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
315 327 +[log.phase|phase: draft]
316 328 +[log.phase|phase: draft]
317 329 +[log.phase|phase: draft]
318 330 +[log.phase|phase: draft]
319 331 +[log.phase|phase: draft]
320 332 +[log.phase|phase: draft]
321 333 +[log.phase|phase: draft]
322 334 +[log.phase|phase: draft]
323 335 +[log.phase|phase: draft]
324 336 +[log.phase|phase: draft]
325 337
326 338 $ hg --color=debug -v log > log.out
327 339 $ hg --color=debug -v log --style default > style.out
328 340 $ cmp log.out style.out || diff -u log.out style.out
329 341 $ hg --color=debug -v log -T phases > phases.out
330 342 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
331 343 +[log.phase|phase: draft]
332 344 +[log.phase|phase: draft]
333 345 +[log.phase|phase: draft]
334 346 +[log.phase|phase: draft]
335 347 +[log.phase|phase: draft]
336 348 +[log.phase|phase: draft]
337 349 +[log.phase|phase: draft]
338 350 +[log.phase|phase: draft]
339 351 +[log.phase|phase: draft]
340 352 +[log.phase|phase: draft]
341 353
342 354 $ hg --color=debug -q log > log.out
343 355 $ hg --color=debug -q log --style default > style.out
344 356 $ cmp log.out style.out || diff -u log.out style.out
345 357 $ hg --color=debug -q log -T phases > phases.out
346 358 $ cmp log.out phases.out || diff -u log.out phases.out
347 359
348 360 $ hg --color=debug --debug log > log.out
349 361 $ hg --color=debug --debug log --style default > style.out
350 362 $ cmp log.out style.out || diff -u log.out style.out
351 363 $ hg --color=debug --debug log -T phases > phases.out
352 364 $ cmp log.out phases.out || diff -u log.out phases.out
353 365
354 366 $ mv $HGRCPATH-bak $HGRCPATH
355 367
356 368 Remove commit with empty commit message, so as to not pollute further
357 369 tests.
358 370
359 371 $ hg --config extensions.strip= strip -q .
360 372
361 373 Revision with no copies (used to print a traceback):
362 374
363 375 $ hg tip -v --template '\n'
364 376
365 377
366 378 Compact style works:
367 379
368 380 $ hg log -Tcompact
369 381 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
370 382 third
371 383
372 384 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
373 385 second
374 386
375 387 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
376 388 merge
377 389
378 390 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
379 391 new head
380 392
381 393 4 bbe44766e73d 1970-01-17 04:53 +0000 person
382 394 new branch
383 395
384 396 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
385 397 no user, no domain
386 398
387 399 2 97054abb4ab8 1970-01-14 21:20 +0000 other
388 400 no person
389 401
390 402 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
391 403 other 1
392 404
393 405 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
394 406 line 1
395 407
396 408
397 409 $ hg log -v --style compact
398 410 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
399 411 third
400 412
401 413 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
402 414 second
403 415
404 416 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
405 417 merge
406 418
407 419 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
408 420 new head
409 421
410 422 4 bbe44766e73d 1970-01-17 04:53 +0000 person
411 423 new branch
412 424
413 425 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
414 426 no user, no domain
415 427
416 428 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
417 429 no person
418 430
419 431 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
420 432 other 1
421 433 other 2
422 434
423 435 other 3
424 436
425 437 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
426 438 line 1
427 439 line 2
428 440
429 441
430 442 $ hg log --debug --style compact
431 443 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
432 444 third
433 445
434 446 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
435 447 second
436 448
437 449 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
438 450 merge
439 451
440 452 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
441 453 new head
442 454
443 455 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
444 456 new branch
445 457
446 458 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
447 459 no user, no domain
448 460
449 461 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
450 462 no person
451 463
452 464 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
453 465 other 1
454 466 other 2
455 467
456 468 other 3
457 469
458 470 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
459 471 line 1
460 472 line 2
461 473
462 474
463 475 Test xml styles:
464 476
465 477 $ hg log --style xml -r 'not all()'
466 478 <?xml version="1.0"?>
467 479 <log>
468 480 </log>
469 481
470 482 $ hg log --style xml
471 483 <?xml version="1.0"?>
472 484 <log>
473 485 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
474 486 <tag>tip</tag>
475 487 <author email="test">test</author>
476 488 <date>2020-01-01T10:01:00+00:00</date>
477 489 <msg xml:space="preserve">third</msg>
478 490 </logentry>
479 491 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
480 492 <parent revision="-1" node="0000000000000000000000000000000000000000" />
481 493 <author email="user@hostname">User Name</author>
482 494 <date>1970-01-12T13:46:40+00:00</date>
483 495 <msg xml:space="preserve">second</msg>
484 496 </logentry>
485 497 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
486 498 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
487 499 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
488 500 <author email="person">person</author>
489 501 <date>1970-01-18T08:40:01+00:00</date>
490 502 <msg xml:space="preserve">merge</msg>
491 503 </logentry>
492 504 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
493 505 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
494 506 <author email="person">person</author>
495 507 <date>1970-01-18T08:40:00+00:00</date>
496 508 <msg xml:space="preserve">new head</msg>
497 509 </logentry>
498 510 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
499 511 <branch>foo</branch>
500 512 <author email="person">person</author>
501 513 <date>1970-01-17T04:53:20+00:00</date>
502 514 <msg xml:space="preserve">new branch</msg>
503 515 </logentry>
504 516 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
505 517 <author email="person">person</author>
506 518 <date>1970-01-16T01:06:40+00:00</date>
507 519 <msg xml:space="preserve">no user, no domain</msg>
508 520 </logentry>
509 521 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
510 522 <author email="other@place">other</author>
511 523 <date>1970-01-14T21:20:00+00:00</date>
512 524 <msg xml:space="preserve">no person</msg>
513 525 </logentry>
514 526 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
515 527 <author email="other@place">A. N. Other</author>
516 528 <date>1970-01-13T17:33:20+00:00</date>
517 529 <msg xml:space="preserve">other 1
518 530 other 2
519 531
520 532 other 3</msg>
521 533 </logentry>
522 534 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
523 535 <author email="user@hostname">User Name</author>
524 536 <date>1970-01-12T13:46:40+00:00</date>
525 537 <msg xml:space="preserve">line 1
526 538 line 2</msg>
527 539 </logentry>
528 540 </log>
529 541
530 542 $ hg log -v --style xml
531 543 <?xml version="1.0"?>
532 544 <log>
533 545 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
534 546 <tag>tip</tag>
535 547 <author email="test">test</author>
536 548 <date>2020-01-01T10:01:00+00:00</date>
537 549 <msg xml:space="preserve">third</msg>
538 550 <paths>
539 551 <path action="A">fourth</path>
540 552 <path action="A">third</path>
541 553 <path action="R">second</path>
542 554 </paths>
543 555 <copies>
544 556 <copy source="second">fourth</copy>
545 557 </copies>
546 558 </logentry>
547 559 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
548 560 <parent revision="-1" node="0000000000000000000000000000000000000000" />
549 561 <author email="user@hostname">User Name</author>
550 562 <date>1970-01-12T13:46:40+00:00</date>
551 563 <msg xml:space="preserve">second</msg>
552 564 <paths>
553 565 <path action="A">second</path>
554 566 </paths>
555 567 </logentry>
556 568 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
557 569 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
558 570 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
559 571 <author email="person">person</author>
560 572 <date>1970-01-18T08:40:01+00:00</date>
561 573 <msg xml:space="preserve">merge</msg>
562 574 <paths>
563 575 </paths>
564 576 </logentry>
565 577 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
566 578 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
567 579 <author email="person">person</author>
568 580 <date>1970-01-18T08:40:00+00:00</date>
569 581 <msg xml:space="preserve">new head</msg>
570 582 <paths>
571 583 <path action="A">d</path>
572 584 </paths>
573 585 </logentry>
574 586 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
575 587 <branch>foo</branch>
576 588 <author email="person">person</author>
577 589 <date>1970-01-17T04:53:20+00:00</date>
578 590 <msg xml:space="preserve">new branch</msg>
579 591 <paths>
580 592 </paths>
581 593 </logentry>
582 594 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
583 595 <author email="person">person</author>
584 596 <date>1970-01-16T01:06:40+00:00</date>
585 597 <msg xml:space="preserve">no user, no domain</msg>
586 598 <paths>
587 599 <path action="M">c</path>
588 600 </paths>
589 601 </logentry>
590 602 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
591 603 <author email="other@place">other</author>
592 604 <date>1970-01-14T21:20:00+00:00</date>
593 605 <msg xml:space="preserve">no person</msg>
594 606 <paths>
595 607 <path action="A">c</path>
596 608 </paths>
597 609 </logentry>
598 610 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
599 611 <author email="other@place">A. N. Other</author>
600 612 <date>1970-01-13T17:33:20+00:00</date>
601 613 <msg xml:space="preserve">other 1
602 614 other 2
603 615
604 616 other 3</msg>
605 617 <paths>
606 618 <path action="A">b</path>
607 619 </paths>
608 620 </logentry>
609 621 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
610 622 <author email="user@hostname">User Name</author>
611 623 <date>1970-01-12T13:46:40+00:00</date>
612 624 <msg xml:space="preserve">line 1
613 625 line 2</msg>
614 626 <paths>
615 627 <path action="A">a</path>
616 628 </paths>
617 629 </logentry>
618 630 </log>
619 631
620 632 $ hg log --debug --style xml
621 633 <?xml version="1.0"?>
622 634 <log>
623 635 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
624 636 <tag>tip</tag>
625 637 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
626 638 <parent revision="-1" node="0000000000000000000000000000000000000000" />
627 639 <author email="test">test</author>
628 640 <date>2020-01-01T10:01:00+00:00</date>
629 641 <msg xml:space="preserve">third</msg>
630 642 <paths>
631 643 <path action="A">fourth</path>
632 644 <path action="A">third</path>
633 645 <path action="R">second</path>
634 646 </paths>
635 647 <copies>
636 648 <copy source="second">fourth</copy>
637 649 </copies>
638 650 <extra key="branch">default</extra>
639 651 </logentry>
640 652 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
641 653 <parent revision="-1" node="0000000000000000000000000000000000000000" />
642 654 <parent revision="-1" node="0000000000000000000000000000000000000000" />
643 655 <author email="user@hostname">User Name</author>
644 656 <date>1970-01-12T13:46:40+00:00</date>
645 657 <msg xml:space="preserve">second</msg>
646 658 <paths>
647 659 <path action="A">second</path>
648 660 </paths>
649 661 <extra key="branch">default</extra>
650 662 </logentry>
651 663 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
652 664 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
653 665 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
654 666 <author email="person">person</author>
655 667 <date>1970-01-18T08:40:01+00:00</date>
656 668 <msg xml:space="preserve">merge</msg>
657 669 <paths>
658 670 </paths>
659 671 <extra key="branch">default</extra>
660 672 </logentry>
661 673 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
662 674 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
663 675 <parent revision="-1" node="0000000000000000000000000000000000000000" />
664 676 <author email="person">person</author>
665 677 <date>1970-01-18T08:40:00+00:00</date>
666 678 <msg xml:space="preserve">new head</msg>
667 679 <paths>
668 680 <path action="A">d</path>
669 681 </paths>
670 682 <extra key="branch">default</extra>
671 683 </logentry>
672 684 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
673 685 <branch>foo</branch>
674 686 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
675 687 <parent revision="-1" node="0000000000000000000000000000000000000000" />
676 688 <author email="person">person</author>
677 689 <date>1970-01-17T04:53:20+00:00</date>
678 690 <msg xml:space="preserve">new branch</msg>
679 691 <paths>
680 692 </paths>
681 693 <extra key="branch">foo</extra>
682 694 </logentry>
683 695 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
684 696 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
685 697 <parent revision="-1" node="0000000000000000000000000000000000000000" />
686 698 <author email="person">person</author>
687 699 <date>1970-01-16T01:06:40+00:00</date>
688 700 <msg xml:space="preserve">no user, no domain</msg>
689 701 <paths>
690 702 <path action="M">c</path>
691 703 </paths>
692 704 <extra key="branch">default</extra>
693 705 </logentry>
694 706 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
695 707 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
696 708 <parent revision="-1" node="0000000000000000000000000000000000000000" />
697 709 <author email="other@place">other</author>
698 710 <date>1970-01-14T21:20:00+00:00</date>
699 711 <msg xml:space="preserve">no person</msg>
700 712 <paths>
701 713 <path action="A">c</path>
702 714 </paths>
703 715 <extra key="branch">default</extra>
704 716 </logentry>
705 717 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
706 718 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
707 719 <parent revision="-1" node="0000000000000000000000000000000000000000" />
708 720 <author email="other@place">A. N. Other</author>
709 721 <date>1970-01-13T17:33:20+00:00</date>
710 722 <msg xml:space="preserve">other 1
711 723 other 2
712 724
713 725 other 3</msg>
714 726 <paths>
715 727 <path action="A">b</path>
716 728 </paths>
717 729 <extra key="branch">default</extra>
718 730 </logentry>
719 731 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
720 732 <parent revision="-1" node="0000000000000000000000000000000000000000" />
721 733 <parent revision="-1" node="0000000000000000000000000000000000000000" />
722 734 <author email="user@hostname">User Name</author>
723 735 <date>1970-01-12T13:46:40+00:00</date>
724 736 <msg xml:space="preserve">line 1
725 737 line 2</msg>
726 738 <paths>
727 739 <path action="A">a</path>
728 740 </paths>
729 741 <extra key="branch">default</extra>
730 742 </logentry>
731 743 </log>
732 744
733 745
734 746 Test JSON style:
735 747
736 748 $ hg log -k nosuch -Tjson
737 749 []
738 750
739 751 $ hg log -qr . -Tjson
740 752 [
741 753 {
742 754 "rev": 8,
743 755 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
744 756 }
745 757 ]
746 758
747 759 $ hg log -vpr . -Tjson --stat
748 760 [
749 761 {
750 762 "rev": 8,
751 763 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
752 764 "branch": "default",
753 765 "phase": "draft",
754 766 "user": "test",
755 767 "date": [1577872860, 0],
756 768 "desc": "third",
757 769 "bookmarks": [],
758 770 "tags": ["tip"],
759 771 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
760 772 "files": ["fourth", "second", "third"],
761 773 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
762 774 "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n"
763 775 }
764 776 ]
765 777
766 778 honor --git but not format-breaking diffopts
767 779 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
768 780 [
769 781 {
770 782 "rev": 8,
771 783 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
772 784 "branch": "default",
773 785 "phase": "draft",
774 786 "user": "test",
775 787 "date": [1577872860, 0],
776 788 "desc": "third",
777 789 "bookmarks": [],
778 790 "tags": ["tip"],
779 791 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
780 792 "files": ["fourth", "second", "third"],
781 793 "diff": "diff --git a/second b/fourth\nrename from second\nrename to fourth\ndiff --git a/third b/third\nnew file mode 100644\n--- /dev/null\n+++ b/third\n@@ -0,0 +1,1 @@\n+third\n"
782 794 }
783 795 ]
784 796
785 797 $ hg log -T json
786 798 [
787 799 {
788 800 "rev": 8,
789 801 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
790 802 "branch": "default",
791 803 "phase": "draft",
792 804 "user": "test",
793 805 "date": [1577872860, 0],
794 806 "desc": "third",
795 807 "bookmarks": [],
796 808 "tags": ["tip"],
797 809 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
798 810 },
799 811 {
800 812 "rev": 7,
801 813 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
802 814 "branch": "default",
803 815 "phase": "draft",
804 816 "user": "User Name <user@hostname>",
805 817 "date": [1000000, 0],
806 818 "desc": "second",
807 819 "bookmarks": [],
808 820 "tags": [],
809 821 "parents": ["0000000000000000000000000000000000000000"]
810 822 },
811 823 {
812 824 "rev": 6,
813 825 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
814 826 "branch": "default",
815 827 "phase": "draft",
816 828 "user": "person",
817 829 "date": [1500001, 0],
818 830 "desc": "merge",
819 831 "bookmarks": [],
820 832 "tags": [],
821 833 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
822 834 },
823 835 {
824 836 "rev": 5,
825 837 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
826 838 "branch": "default",
827 839 "phase": "draft",
828 840 "user": "person",
829 841 "date": [1500000, 0],
830 842 "desc": "new head",
831 843 "bookmarks": [],
832 844 "tags": [],
833 845 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
834 846 },
835 847 {
836 848 "rev": 4,
837 849 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
838 850 "branch": "foo",
839 851 "phase": "draft",
840 852 "user": "person",
841 853 "date": [1400000, 0],
842 854 "desc": "new branch",
843 855 "bookmarks": [],
844 856 "tags": [],
845 857 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
846 858 },
847 859 {
848 860 "rev": 3,
849 861 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
850 862 "branch": "default",
851 863 "phase": "draft",
852 864 "user": "person",
853 865 "date": [1300000, 0],
854 866 "desc": "no user, no domain",
855 867 "bookmarks": [],
856 868 "tags": [],
857 869 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
858 870 },
859 871 {
860 872 "rev": 2,
861 873 "node": "97054abb4ab824450e9164180baf491ae0078465",
862 874 "branch": "default",
863 875 "phase": "draft",
864 876 "user": "other@place",
865 877 "date": [1200000, 0],
866 878 "desc": "no person",
867 879 "bookmarks": [],
868 880 "tags": [],
869 881 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
870 882 },
871 883 {
872 884 "rev": 1,
873 885 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
874 886 "branch": "default",
875 887 "phase": "draft",
876 888 "user": "A. N. Other <other@place>",
877 889 "date": [1100000, 0],
878 890 "desc": "other 1\nother 2\n\nother 3",
879 891 "bookmarks": [],
880 892 "tags": [],
881 893 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
882 894 },
883 895 {
884 896 "rev": 0,
885 897 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
886 898 "branch": "default",
887 899 "phase": "draft",
888 900 "user": "User Name <user@hostname>",
889 901 "date": [1000000, 0],
890 902 "desc": "line 1\nline 2",
891 903 "bookmarks": [],
892 904 "tags": [],
893 905 "parents": ["0000000000000000000000000000000000000000"]
894 906 }
895 907 ]
896 908
897 909 $ hg heads -v -Tjson
898 910 [
899 911 {
900 912 "rev": 8,
901 913 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
902 914 "branch": "default",
903 915 "phase": "draft",
904 916 "user": "test",
905 917 "date": [1577872860, 0],
906 918 "desc": "third",
907 919 "bookmarks": [],
908 920 "tags": ["tip"],
909 921 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
910 922 "files": ["fourth", "second", "third"]
911 923 },
912 924 {
913 925 "rev": 6,
914 926 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
915 927 "branch": "default",
916 928 "phase": "draft",
917 929 "user": "person",
918 930 "date": [1500001, 0],
919 931 "desc": "merge",
920 932 "bookmarks": [],
921 933 "tags": [],
922 934 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
923 935 "files": []
924 936 },
925 937 {
926 938 "rev": 4,
927 939 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
928 940 "branch": "foo",
929 941 "phase": "draft",
930 942 "user": "person",
931 943 "date": [1400000, 0],
932 944 "desc": "new branch",
933 945 "bookmarks": [],
934 946 "tags": [],
935 947 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
936 948 "files": []
937 949 }
938 950 ]
939 951
940 952 $ hg log --debug -Tjson
941 953 [
942 954 {
943 955 "rev": 8,
944 956 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
945 957 "branch": "default",
946 958 "phase": "draft",
947 959 "user": "test",
948 960 "date": [1577872860, 0],
949 961 "desc": "third",
950 962 "bookmarks": [],
951 963 "tags": ["tip"],
952 964 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
953 965 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
954 966 "extra": {"branch": "default"},
955 967 "modified": [],
956 968 "added": ["fourth", "third"],
957 969 "removed": ["second"]
958 970 },
959 971 {
960 972 "rev": 7,
961 973 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
962 974 "branch": "default",
963 975 "phase": "draft",
964 976 "user": "User Name <user@hostname>",
965 977 "date": [1000000, 0],
966 978 "desc": "second",
967 979 "bookmarks": [],
968 980 "tags": [],
969 981 "parents": ["0000000000000000000000000000000000000000"],
970 982 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
971 983 "extra": {"branch": "default"},
972 984 "modified": [],
973 985 "added": ["second"],
974 986 "removed": []
975 987 },
976 988 {
977 989 "rev": 6,
978 990 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
979 991 "branch": "default",
980 992 "phase": "draft",
981 993 "user": "person",
982 994 "date": [1500001, 0],
983 995 "desc": "merge",
984 996 "bookmarks": [],
985 997 "tags": [],
986 998 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
987 999 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
988 1000 "extra": {"branch": "default"},
989 1001 "modified": [],
990 1002 "added": [],
991 1003 "removed": []
992 1004 },
993 1005 {
994 1006 "rev": 5,
995 1007 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
996 1008 "branch": "default",
997 1009 "phase": "draft",
998 1010 "user": "person",
999 1011 "date": [1500000, 0],
1000 1012 "desc": "new head",
1001 1013 "bookmarks": [],
1002 1014 "tags": [],
1003 1015 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1004 1016 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1005 1017 "extra": {"branch": "default"},
1006 1018 "modified": [],
1007 1019 "added": ["d"],
1008 1020 "removed": []
1009 1021 },
1010 1022 {
1011 1023 "rev": 4,
1012 1024 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1013 1025 "branch": "foo",
1014 1026 "phase": "draft",
1015 1027 "user": "person",
1016 1028 "date": [1400000, 0],
1017 1029 "desc": "new branch",
1018 1030 "bookmarks": [],
1019 1031 "tags": [],
1020 1032 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1021 1033 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1022 1034 "extra": {"branch": "foo"},
1023 1035 "modified": [],
1024 1036 "added": [],
1025 1037 "removed": []
1026 1038 },
1027 1039 {
1028 1040 "rev": 3,
1029 1041 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1030 1042 "branch": "default",
1031 1043 "phase": "draft",
1032 1044 "user": "person",
1033 1045 "date": [1300000, 0],
1034 1046 "desc": "no user, no domain",
1035 1047 "bookmarks": [],
1036 1048 "tags": [],
1037 1049 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1038 1050 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1039 1051 "extra": {"branch": "default"},
1040 1052 "modified": ["c"],
1041 1053 "added": [],
1042 1054 "removed": []
1043 1055 },
1044 1056 {
1045 1057 "rev": 2,
1046 1058 "node": "97054abb4ab824450e9164180baf491ae0078465",
1047 1059 "branch": "default",
1048 1060 "phase": "draft",
1049 1061 "user": "other@place",
1050 1062 "date": [1200000, 0],
1051 1063 "desc": "no person",
1052 1064 "bookmarks": [],
1053 1065 "tags": [],
1054 1066 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1055 1067 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1056 1068 "extra": {"branch": "default"},
1057 1069 "modified": [],
1058 1070 "added": ["c"],
1059 1071 "removed": []
1060 1072 },
1061 1073 {
1062 1074 "rev": 1,
1063 1075 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1064 1076 "branch": "default",
1065 1077 "phase": "draft",
1066 1078 "user": "A. N. Other <other@place>",
1067 1079 "date": [1100000, 0],
1068 1080 "desc": "other 1\nother 2\n\nother 3",
1069 1081 "bookmarks": [],
1070 1082 "tags": [],
1071 1083 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1072 1084 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1073 1085 "extra": {"branch": "default"},
1074 1086 "modified": [],
1075 1087 "added": ["b"],
1076 1088 "removed": []
1077 1089 },
1078 1090 {
1079 1091 "rev": 0,
1080 1092 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1081 1093 "branch": "default",
1082 1094 "phase": "draft",
1083 1095 "user": "User Name <user@hostname>",
1084 1096 "date": [1000000, 0],
1085 1097 "desc": "line 1\nline 2",
1086 1098 "bookmarks": [],
1087 1099 "tags": [],
1088 1100 "parents": ["0000000000000000000000000000000000000000"],
1089 1101 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1090 1102 "extra": {"branch": "default"},
1091 1103 "modified": [],
1092 1104 "added": ["a"],
1093 1105 "removed": []
1094 1106 }
1095 1107 ]
1096 1108
1097 1109 Error if style not readable:
1098 1110
1099 1111 #if unix-permissions no-root
1100 1112 $ touch q
1101 1113 $ chmod 0 q
1102 1114 $ hg log --style ./q
1103 1115 abort: Permission denied: ./q
1104 1116 [255]
1105 1117 #endif
1106 1118
1107 1119 Error if no style:
1108 1120
1109 1121 $ hg log --style notexist
1110 1122 abort: style 'notexist' not found
1111 1123 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1112 1124 [255]
1113 1125
1114 1126 $ hg log -T list
1115 1127 available styles: bisect, changelog, compact, default, phases, show, status, xml
1116 1128 abort: specify a template
1117 1129 [255]
1118 1130
1119 1131 Error if style missing key:
1120 1132
1121 1133 $ echo 'q = q' > t
1122 1134 $ hg log --style ./t
1123 1135 abort: "changeset" not in template map
1124 1136 [255]
1125 1137
1126 1138 Error if style missing value:
1127 1139
1128 1140 $ echo 'changeset =' > t
1129 1141 $ hg log --style t
1130 1142 hg: parse error at t:1: missing value
1131 1143 [255]
1132 1144
1133 1145 Error if include fails:
1134 1146
1135 1147 $ echo 'changeset = q' >> t
1136 1148 #if unix-permissions no-root
1137 1149 $ hg log --style ./t
1138 1150 abort: template file ./q: Permission denied
1139 1151 [255]
1140 1152 $ rm -f q
1141 1153 #endif
1142 1154
1143 1155 Include works:
1144 1156
1145 1157 $ echo '{rev}' > q
1146 1158 $ hg log --style ./t
1147 1159 8
1148 1160 7
1149 1161 6
1150 1162 5
1151 1163 4
1152 1164 3
1153 1165 2
1154 1166 1
1155 1167 0
1156 1168
1157 1169 Check that recursive reference does not fall into RuntimeError (issue4758):
1158 1170
1159 1171 common mistake:
1160 1172
1161 1173 $ hg log -T '{changeset}\n'
1162 1174 abort: recursive reference 'changeset' in template
1163 1175 [255]
1164 1176
1165 1177 circular reference:
1166 1178
1167 1179 $ cat << EOF > issue4758
1168 1180 > changeset = '{foo}'
1169 1181 > foo = '{changeset}'
1170 1182 > EOF
1171 1183 $ hg log --style ./issue4758
1172 1184 abort: recursive reference 'foo' in template
1173 1185 [255]
1174 1186
1175 1187 buildmap() -> gettemplate(), where no thunk was made:
1176 1188
1177 1189 $ hg log -T '{files % changeset}\n'
1178 1190 abort: recursive reference 'changeset' in template
1179 1191 [255]
1180 1192
1181 1193 not a recursion if a keyword of the same name exists:
1182 1194
1183 1195 $ cat << EOF > issue4758
1184 1196 > changeset = '{tags % rev}'
1185 1197 > rev = '{rev} {tag}\n'
1186 1198 > EOF
1187 1199 $ hg log --style ./issue4758 -r tip
1188 1200 8 tip
1189 1201
1190 1202 Check that {phase} works correctly on parents:
1191 1203
1192 1204 $ cat << EOF > parentphase
1193 1205 > changeset_debug = '{rev} ({phase}):{parents}\n'
1194 1206 > parent = ' {rev} ({phase})'
1195 1207 > EOF
1196 1208 $ hg phase -r 5 --public
1197 1209 $ hg phase -r 7 --secret --force
1198 1210 $ hg log --debug -G --style ./parentphase
1199 1211 @ 8 (secret): 7 (secret) -1 (public)
1200 1212 |
1201 1213 o 7 (secret): -1 (public) -1 (public)
1202 1214
1203 1215 o 6 (draft): 5 (public) 4 (draft)
1204 1216 |\
1205 1217 | o 5 (public): 3 (public) -1 (public)
1206 1218 | |
1207 1219 o | 4 (draft): 3 (public) -1 (public)
1208 1220 |/
1209 1221 o 3 (public): 2 (public) -1 (public)
1210 1222 |
1211 1223 o 2 (public): 1 (public) -1 (public)
1212 1224 |
1213 1225 o 1 (public): 0 (public) -1 (public)
1214 1226 |
1215 1227 o 0 (public): -1 (public) -1 (public)
1216 1228
1217 1229
1218 1230 Missing non-standard names give no error (backward compatibility):
1219 1231
1220 1232 $ echo "changeset = '{c}'" > t
1221 1233 $ hg log --style ./t
1222 1234
1223 1235 Defining non-standard name works:
1224 1236
1225 1237 $ cat <<EOF > t
1226 1238 > changeset = '{c}'
1227 1239 > c = q
1228 1240 > EOF
1229 1241 $ hg log --style ./t
1230 1242 8
1231 1243 7
1232 1244 6
1233 1245 5
1234 1246 4
1235 1247 3
1236 1248 2
1237 1249 1
1238 1250 0
1239 1251
1240 1252 ui.style works:
1241 1253
1242 1254 $ echo '[ui]' > .hg/hgrc
1243 1255 $ echo 'style = t' >> .hg/hgrc
1244 1256 $ hg log
1245 1257 8
1246 1258 7
1247 1259 6
1248 1260 5
1249 1261 4
1250 1262 3
1251 1263 2
1252 1264 1
1253 1265 0
1254 1266
1255 1267
1256 1268 Issue338:
1257 1269
1258 1270 $ hg log --style=changelog > changelog
1259 1271
1260 1272 $ cat changelog
1261 1273 2020-01-01 test <test>
1262 1274
1263 1275 * fourth, second, third:
1264 1276 third
1265 1277 [95c24699272e] [tip]
1266 1278
1267 1279 1970-01-12 User Name <user@hostname>
1268 1280
1269 1281 * second:
1270 1282 second
1271 1283 [29114dbae42b]
1272 1284
1273 1285 1970-01-18 person <person>
1274 1286
1275 1287 * merge
1276 1288 [d41e714fe50d]
1277 1289
1278 1290 * d:
1279 1291 new head
1280 1292 [13207e5a10d9]
1281 1293
1282 1294 1970-01-17 person <person>
1283 1295
1284 1296 * new branch
1285 1297 [bbe44766e73d] <foo>
1286 1298
1287 1299 1970-01-16 person <person>
1288 1300
1289 1301 * c:
1290 1302 no user, no domain
1291 1303 [10e46f2dcbf4]
1292 1304
1293 1305 1970-01-14 other <other@place>
1294 1306
1295 1307 * c:
1296 1308 no person
1297 1309 [97054abb4ab8]
1298 1310
1299 1311 1970-01-13 A. N. Other <other@place>
1300 1312
1301 1313 * b:
1302 1314 other 1 other 2
1303 1315
1304 1316 other 3
1305 1317 [b608e9d1a3f0]
1306 1318
1307 1319 1970-01-12 User Name <user@hostname>
1308 1320
1309 1321 * a:
1310 1322 line 1 line 2
1311 1323 [1e4e1b8f71e0]
1312 1324
1313 1325
1314 1326 Issue2130: xml output for 'hg heads' is malformed
1315 1327
1316 1328 $ hg heads --style changelog
1317 1329 2020-01-01 test <test>
1318 1330
1319 1331 * fourth, second, third:
1320 1332 third
1321 1333 [95c24699272e] [tip]
1322 1334
1323 1335 1970-01-18 person <person>
1324 1336
1325 1337 * merge
1326 1338 [d41e714fe50d]
1327 1339
1328 1340 1970-01-17 person <person>
1329 1341
1330 1342 * new branch
1331 1343 [bbe44766e73d] <foo>
1332 1344
1333 1345
1334 1346 Keys work:
1335 1347
1336 1348 $ for key in author branch branches date desc file_adds file_dels file_mods \
1337 1349 > file_copies file_copies_switch files \
1338 1350 > manifest node parents rev tags diffstat extras \
1339 1351 > p1rev p2rev p1node p2node; do
1340 1352 > for mode in '' --verbose --debug; do
1341 1353 > hg log $mode --template "$key$mode: {$key}\n"
1342 1354 > done
1343 1355 > done
1344 1356 author: test
1345 1357 author: User Name <user@hostname>
1346 1358 author: person
1347 1359 author: person
1348 1360 author: person
1349 1361 author: person
1350 1362 author: other@place
1351 1363 author: A. N. Other <other@place>
1352 1364 author: User Name <user@hostname>
1353 1365 author--verbose: test
1354 1366 author--verbose: User Name <user@hostname>
1355 1367 author--verbose: person
1356 1368 author--verbose: person
1357 1369 author--verbose: person
1358 1370 author--verbose: person
1359 1371 author--verbose: other@place
1360 1372 author--verbose: A. N. Other <other@place>
1361 1373 author--verbose: User Name <user@hostname>
1362 1374 author--debug: test
1363 1375 author--debug: User Name <user@hostname>
1364 1376 author--debug: person
1365 1377 author--debug: person
1366 1378 author--debug: person
1367 1379 author--debug: person
1368 1380 author--debug: other@place
1369 1381 author--debug: A. N. Other <other@place>
1370 1382 author--debug: User Name <user@hostname>
1371 1383 branch: default
1372 1384 branch: default
1373 1385 branch: default
1374 1386 branch: default
1375 1387 branch: foo
1376 1388 branch: default
1377 1389 branch: default
1378 1390 branch: default
1379 1391 branch: default
1380 1392 branch--verbose: default
1381 1393 branch--verbose: default
1382 1394 branch--verbose: default
1383 1395 branch--verbose: default
1384 1396 branch--verbose: foo
1385 1397 branch--verbose: default
1386 1398 branch--verbose: default
1387 1399 branch--verbose: default
1388 1400 branch--verbose: default
1389 1401 branch--debug: default
1390 1402 branch--debug: default
1391 1403 branch--debug: default
1392 1404 branch--debug: default
1393 1405 branch--debug: foo
1394 1406 branch--debug: default
1395 1407 branch--debug: default
1396 1408 branch--debug: default
1397 1409 branch--debug: default
1398 1410 branches:
1399 1411 branches:
1400 1412 branches:
1401 1413 branches:
1402 1414 branches: foo
1403 1415 branches:
1404 1416 branches:
1405 1417 branches:
1406 1418 branches:
1407 1419 branches--verbose:
1408 1420 branches--verbose:
1409 1421 branches--verbose:
1410 1422 branches--verbose:
1411 1423 branches--verbose: foo
1412 1424 branches--verbose:
1413 1425 branches--verbose:
1414 1426 branches--verbose:
1415 1427 branches--verbose:
1416 1428 branches--debug:
1417 1429 branches--debug:
1418 1430 branches--debug:
1419 1431 branches--debug:
1420 1432 branches--debug: foo
1421 1433 branches--debug:
1422 1434 branches--debug:
1423 1435 branches--debug:
1424 1436 branches--debug:
1425 1437 date: 1577872860.00
1426 1438 date: 1000000.00
1427 1439 date: 1500001.00
1428 1440 date: 1500000.00
1429 1441 date: 1400000.00
1430 1442 date: 1300000.00
1431 1443 date: 1200000.00
1432 1444 date: 1100000.00
1433 1445 date: 1000000.00
1434 1446 date--verbose: 1577872860.00
1435 1447 date--verbose: 1000000.00
1436 1448 date--verbose: 1500001.00
1437 1449 date--verbose: 1500000.00
1438 1450 date--verbose: 1400000.00
1439 1451 date--verbose: 1300000.00
1440 1452 date--verbose: 1200000.00
1441 1453 date--verbose: 1100000.00
1442 1454 date--verbose: 1000000.00
1443 1455 date--debug: 1577872860.00
1444 1456 date--debug: 1000000.00
1445 1457 date--debug: 1500001.00
1446 1458 date--debug: 1500000.00
1447 1459 date--debug: 1400000.00
1448 1460 date--debug: 1300000.00
1449 1461 date--debug: 1200000.00
1450 1462 date--debug: 1100000.00
1451 1463 date--debug: 1000000.00
1452 1464 desc: third
1453 1465 desc: second
1454 1466 desc: merge
1455 1467 desc: new head
1456 1468 desc: new branch
1457 1469 desc: no user, no domain
1458 1470 desc: no person
1459 1471 desc: other 1
1460 1472 other 2
1461 1473
1462 1474 other 3
1463 1475 desc: line 1
1464 1476 line 2
1465 1477 desc--verbose: third
1466 1478 desc--verbose: second
1467 1479 desc--verbose: merge
1468 1480 desc--verbose: new head
1469 1481 desc--verbose: new branch
1470 1482 desc--verbose: no user, no domain
1471 1483 desc--verbose: no person
1472 1484 desc--verbose: other 1
1473 1485 other 2
1474 1486
1475 1487 other 3
1476 1488 desc--verbose: line 1
1477 1489 line 2
1478 1490 desc--debug: third
1479 1491 desc--debug: second
1480 1492 desc--debug: merge
1481 1493 desc--debug: new head
1482 1494 desc--debug: new branch
1483 1495 desc--debug: no user, no domain
1484 1496 desc--debug: no person
1485 1497 desc--debug: other 1
1486 1498 other 2
1487 1499
1488 1500 other 3
1489 1501 desc--debug: line 1
1490 1502 line 2
1491 1503 file_adds: fourth third
1492 1504 file_adds: second
1493 1505 file_adds:
1494 1506 file_adds: d
1495 1507 file_adds:
1496 1508 file_adds:
1497 1509 file_adds: c
1498 1510 file_adds: b
1499 1511 file_adds: a
1500 1512 file_adds--verbose: fourth third
1501 1513 file_adds--verbose: second
1502 1514 file_adds--verbose:
1503 1515 file_adds--verbose: d
1504 1516 file_adds--verbose:
1505 1517 file_adds--verbose:
1506 1518 file_adds--verbose: c
1507 1519 file_adds--verbose: b
1508 1520 file_adds--verbose: a
1509 1521 file_adds--debug: fourth third
1510 1522 file_adds--debug: second
1511 1523 file_adds--debug:
1512 1524 file_adds--debug: d
1513 1525 file_adds--debug:
1514 1526 file_adds--debug:
1515 1527 file_adds--debug: c
1516 1528 file_adds--debug: b
1517 1529 file_adds--debug: a
1518 1530 file_dels: second
1519 1531 file_dels:
1520 1532 file_dels:
1521 1533 file_dels:
1522 1534 file_dels:
1523 1535 file_dels:
1524 1536 file_dels:
1525 1537 file_dels:
1526 1538 file_dels:
1527 1539 file_dels--verbose: second
1528 1540 file_dels--verbose:
1529 1541 file_dels--verbose:
1530 1542 file_dels--verbose:
1531 1543 file_dels--verbose:
1532 1544 file_dels--verbose:
1533 1545 file_dels--verbose:
1534 1546 file_dels--verbose:
1535 1547 file_dels--verbose:
1536 1548 file_dels--debug: second
1537 1549 file_dels--debug:
1538 1550 file_dels--debug:
1539 1551 file_dels--debug:
1540 1552 file_dels--debug:
1541 1553 file_dels--debug:
1542 1554 file_dels--debug:
1543 1555 file_dels--debug:
1544 1556 file_dels--debug:
1545 1557 file_mods:
1546 1558 file_mods:
1547 1559 file_mods:
1548 1560 file_mods:
1549 1561 file_mods:
1550 1562 file_mods: c
1551 1563 file_mods:
1552 1564 file_mods:
1553 1565 file_mods:
1554 1566 file_mods--verbose:
1555 1567 file_mods--verbose:
1556 1568 file_mods--verbose:
1557 1569 file_mods--verbose:
1558 1570 file_mods--verbose:
1559 1571 file_mods--verbose: c
1560 1572 file_mods--verbose:
1561 1573 file_mods--verbose:
1562 1574 file_mods--verbose:
1563 1575 file_mods--debug:
1564 1576 file_mods--debug:
1565 1577 file_mods--debug:
1566 1578 file_mods--debug:
1567 1579 file_mods--debug:
1568 1580 file_mods--debug: c
1569 1581 file_mods--debug:
1570 1582 file_mods--debug:
1571 1583 file_mods--debug:
1572 1584 file_copies: fourth (second)
1573 1585 file_copies:
1574 1586 file_copies:
1575 1587 file_copies:
1576 1588 file_copies:
1577 1589 file_copies:
1578 1590 file_copies:
1579 1591 file_copies:
1580 1592 file_copies:
1581 1593 file_copies--verbose: fourth (second)
1582 1594 file_copies--verbose:
1583 1595 file_copies--verbose:
1584 1596 file_copies--verbose:
1585 1597 file_copies--verbose:
1586 1598 file_copies--verbose:
1587 1599 file_copies--verbose:
1588 1600 file_copies--verbose:
1589 1601 file_copies--verbose:
1590 1602 file_copies--debug: fourth (second)
1591 1603 file_copies--debug:
1592 1604 file_copies--debug:
1593 1605 file_copies--debug:
1594 1606 file_copies--debug:
1595 1607 file_copies--debug:
1596 1608 file_copies--debug:
1597 1609 file_copies--debug:
1598 1610 file_copies--debug:
1599 1611 file_copies_switch:
1600 1612 file_copies_switch:
1601 1613 file_copies_switch:
1602 1614 file_copies_switch:
1603 1615 file_copies_switch:
1604 1616 file_copies_switch:
1605 1617 file_copies_switch:
1606 1618 file_copies_switch:
1607 1619 file_copies_switch:
1608 1620 file_copies_switch--verbose:
1609 1621 file_copies_switch--verbose:
1610 1622 file_copies_switch--verbose:
1611 1623 file_copies_switch--verbose:
1612 1624 file_copies_switch--verbose:
1613 1625 file_copies_switch--verbose:
1614 1626 file_copies_switch--verbose:
1615 1627 file_copies_switch--verbose:
1616 1628 file_copies_switch--verbose:
1617 1629 file_copies_switch--debug:
1618 1630 file_copies_switch--debug:
1619 1631 file_copies_switch--debug:
1620 1632 file_copies_switch--debug:
1621 1633 file_copies_switch--debug:
1622 1634 file_copies_switch--debug:
1623 1635 file_copies_switch--debug:
1624 1636 file_copies_switch--debug:
1625 1637 file_copies_switch--debug:
1626 1638 files: fourth second third
1627 1639 files: second
1628 1640 files:
1629 1641 files: d
1630 1642 files:
1631 1643 files: c
1632 1644 files: c
1633 1645 files: b
1634 1646 files: a
1635 1647 files--verbose: fourth second third
1636 1648 files--verbose: second
1637 1649 files--verbose:
1638 1650 files--verbose: d
1639 1651 files--verbose:
1640 1652 files--verbose: c
1641 1653 files--verbose: c
1642 1654 files--verbose: b
1643 1655 files--verbose: a
1644 1656 files--debug: fourth second third
1645 1657 files--debug: second
1646 1658 files--debug:
1647 1659 files--debug: d
1648 1660 files--debug:
1649 1661 files--debug: c
1650 1662 files--debug: c
1651 1663 files--debug: b
1652 1664 files--debug: a
1653 1665 manifest: 6:94961b75a2da
1654 1666 manifest: 5:f2dbc354b94e
1655 1667 manifest: 4:4dc3def4f9b4
1656 1668 manifest: 4:4dc3def4f9b4
1657 1669 manifest: 3:cb5a1327723b
1658 1670 manifest: 3:cb5a1327723b
1659 1671 manifest: 2:6e0e82995c35
1660 1672 manifest: 1:4e8d705b1e53
1661 1673 manifest: 0:a0c8bcbbb45c
1662 1674 manifest--verbose: 6:94961b75a2da
1663 1675 manifest--verbose: 5:f2dbc354b94e
1664 1676 manifest--verbose: 4:4dc3def4f9b4
1665 1677 manifest--verbose: 4:4dc3def4f9b4
1666 1678 manifest--verbose: 3:cb5a1327723b
1667 1679 manifest--verbose: 3:cb5a1327723b
1668 1680 manifest--verbose: 2:6e0e82995c35
1669 1681 manifest--verbose: 1:4e8d705b1e53
1670 1682 manifest--verbose: 0:a0c8bcbbb45c
1671 1683 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1672 1684 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1673 1685 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1674 1686 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1675 1687 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1676 1688 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1677 1689 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1678 1690 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1679 1691 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1680 1692 node: 95c24699272ef57d062b8bccc32c878bf841784a
1681 1693 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1682 1694 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1683 1695 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1684 1696 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1685 1697 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1686 1698 node: 97054abb4ab824450e9164180baf491ae0078465
1687 1699 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1688 1700 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1689 1701 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1690 1702 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1691 1703 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1692 1704 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1693 1705 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1694 1706 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1695 1707 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1696 1708 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1697 1709 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1698 1710 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1699 1711 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1700 1712 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1701 1713 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1702 1714 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1703 1715 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1704 1716 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1705 1717 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1706 1718 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1707 1719 parents:
1708 1720 parents: -1:000000000000
1709 1721 parents: 5:13207e5a10d9 4:bbe44766e73d
1710 1722 parents: 3:10e46f2dcbf4
1711 1723 parents:
1712 1724 parents:
1713 1725 parents:
1714 1726 parents:
1715 1727 parents:
1716 1728 parents--verbose:
1717 1729 parents--verbose: -1:000000000000
1718 1730 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1719 1731 parents--verbose: 3:10e46f2dcbf4
1720 1732 parents--verbose:
1721 1733 parents--verbose:
1722 1734 parents--verbose:
1723 1735 parents--verbose:
1724 1736 parents--verbose:
1725 1737 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1726 1738 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1727 1739 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1728 1740 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1729 1741 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1730 1742 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1731 1743 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1732 1744 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1733 1745 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1734 1746 rev: 8
1735 1747 rev: 7
1736 1748 rev: 6
1737 1749 rev: 5
1738 1750 rev: 4
1739 1751 rev: 3
1740 1752 rev: 2
1741 1753 rev: 1
1742 1754 rev: 0
1743 1755 rev--verbose: 8
1744 1756 rev--verbose: 7
1745 1757 rev--verbose: 6
1746 1758 rev--verbose: 5
1747 1759 rev--verbose: 4
1748 1760 rev--verbose: 3
1749 1761 rev--verbose: 2
1750 1762 rev--verbose: 1
1751 1763 rev--verbose: 0
1752 1764 rev--debug: 8
1753 1765 rev--debug: 7
1754 1766 rev--debug: 6
1755 1767 rev--debug: 5
1756 1768 rev--debug: 4
1757 1769 rev--debug: 3
1758 1770 rev--debug: 2
1759 1771 rev--debug: 1
1760 1772 rev--debug: 0
1761 1773 tags: tip
1762 1774 tags:
1763 1775 tags:
1764 1776 tags:
1765 1777 tags:
1766 1778 tags:
1767 1779 tags:
1768 1780 tags:
1769 1781 tags:
1770 1782 tags--verbose: tip
1771 1783 tags--verbose:
1772 1784 tags--verbose:
1773 1785 tags--verbose:
1774 1786 tags--verbose:
1775 1787 tags--verbose:
1776 1788 tags--verbose:
1777 1789 tags--verbose:
1778 1790 tags--verbose:
1779 1791 tags--debug: tip
1780 1792 tags--debug:
1781 1793 tags--debug:
1782 1794 tags--debug:
1783 1795 tags--debug:
1784 1796 tags--debug:
1785 1797 tags--debug:
1786 1798 tags--debug:
1787 1799 tags--debug:
1788 1800 diffstat: 3: +2/-1
1789 1801 diffstat: 1: +1/-0
1790 1802 diffstat: 0: +0/-0
1791 1803 diffstat: 1: +1/-0
1792 1804 diffstat: 0: +0/-0
1793 1805 diffstat: 1: +1/-0
1794 1806 diffstat: 1: +4/-0
1795 1807 diffstat: 1: +2/-0
1796 1808 diffstat: 1: +1/-0
1797 1809 diffstat--verbose: 3: +2/-1
1798 1810 diffstat--verbose: 1: +1/-0
1799 1811 diffstat--verbose: 0: +0/-0
1800 1812 diffstat--verbose: 1: +1/-0
1801 1813 diffstat--verbose: 0: +0/-0
1802 1814 diffstat--verbose: 1: +1/-0
1803 1815 diffstat--verbose: 1: +4/-0
1804 1816 diffstat--verbose: 1: +2/-0
1805 1817 diffstat--verbose: 1: +1/-0
1806 1818 diffstat--debug: 3: +2/-1
1807 1819 diffstat--debug: 1: +1/-0
1808 1820 diffstat--debug: 0: +0/-0
1809 1821 diffstat--debug: 1: +1/-0
1810 1822 diffstat--debug: 0: +0/-0
1811 1823 diffstat--debug: 1: +1/-0
1812 1824 diffstat--debug: 1: +4/-0
1813 1825 diffstat--debug: 1: +2/-0
1814 1826 diffstat--debug: 1: +1/-0
1815 1827 extras: branch=default
1816 1828 extras: branch=default
1817 1829 extras: branch=default
1818 1830 extras: branch=default
1819 1831 extras: branch=foo
1820 1832 extras: branch=default
1821 1833 extras: branch=default
1822 1834 extras: branch=default
1823 1835 extras: branch=default
1824 1836 extras--verbose: branch=default
1825 1837 extras--verbose: branch=default
1826 1838 extras--verbose: branch=default
1827 1839 extras--verbose: branch=default
1828 1840 extras--verbose: branch=foo
1829 1841 extras--verbose: branch=default
1830 1842 extras--verbose: branch=default
1831 1843 extras--verbose: branch=default
1832 1844 extras--verbose: branch=default
1833 1845 extras--debug: branch=default
1834 1846 extras--debug: branch=default
1835 1847 extras--debug: branch=default
1836 1848 extras--debug: branch=default
1837 1849 extras--debug: branch=foo
1838 1850 extras--debug: branch=default
1839 1851 extras--debug: branch=default
1840 1852 extras--debug: branch=default
1841 1853 extras--debug: branch=default
1842 1854 p1rev: 7
1843 1855 p1rev: -1
1844 1856 p1rev: 5
1845 1857 p1rev: 3
1846 1858 p1rev: 3
1847 1859 p1rev: 2
1848 1860 p1rev: 1
1849 1861 p1rev: 0
1850 1862 p1rev: -1
1851 1863 p1rev--verbose: 7
1852 1864 p1rev--verbose: -1
1853 1865 p1rev--verbose: 5
1854 1866 p1rev--verbose: 3
1855 1867 p1rev--verbose: 3
1856 1868 p1rev--verbose: 2
1857 1869 p1rev--verbose: 1
1858 1870 p1rev--verbose: 0
1859 1871 p1rev--verbose: -1
1860 1872 p1rev--debug: 7
1861 1873 p1rev--debug: -1
1862 1874 p1rev--debug: 5
1863 1875 p1rev--debug: 3
1864 1876 p1rev--debug: 3
1865 1877 p1rev--debug: 2
1866 1878 p1rev--debug: 1
1867 1879 p1rev--debug: 0
1868 1880 p1rev--debug: -1
1869 1881 p2rev: -1
1870 1882 p2rev: -1
1871 1883 p2rev: 4
1872 1884 p2rev: -1
1873 1885 p2rev: -1
1874 1886 p2rev: -1
1875 1887 p2rev: -1
1876 1888 p2rev: -1
1877 1889 p2rev: -1
1878 1890 p2rev--verbose: -1
1879 1891 p2rev--verbose: -1
1880 1892 p2rev--verbose: 4
1881 1893 p2rev--verbose: -1
1882 1894 p2rev--verbose: -1
1883 1895 p2rev--verbose: -1
1884 1896 p2rev--verbose: -1
1885 1897 p2rev--verbose: -1
1886 1898 p2rev--verbose: -1
1887 1899 p2rev--debug: -1
1888 1900 p2rev--debug: -1
1889 1901 p2rev--debug: 4
1890 1902 p2rev--debug: -1
1891 1903 p2rev--debug: -1
1892 1904 p2rev--debug: -1
1893 1905 p2rev--debug: -1
1894 1906 p2rev--debug: -1
1895 1907 p2rev--debug: -1
1896 1908 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1897 1909 p1node: 0000000000000000000000000000000000000000
1898 1910 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1899 1911 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1900 1912 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1901 1913 p1node: 97054abb4ab824450e9164180baf491ae0078465
1902 1914 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1903 1915 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1904 1916 p1node: 0000000000000000000000000000000000000000
1905 1917 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1906 1918 p1node--verbose: 0000000000000000000000000000000000000000
1907 1919 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1908 1920 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1909 1921 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1910 1922 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1911 1923 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1912 1924 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1913 1925 p1node--verbose: 0000000000000000000000000000000000000000
1914 1926 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1915 1927 p1node--debug: 0000000000000000000000000000000000000000
1916 1928 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1917 1929 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1918 1930 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1919 1931 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1920 1932 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1921 1933 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1922 1934 p1node--debug: 0000000000000000000000000000000000000000
1923 1935 p2node: 0000000000000000000000000000000000000000
1924 1936 p2node: 0000000000000000000000000000000000000000
1925 1937 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1926 1938 p2node: 0000000000000000000000000000000000000000
1927 1939 p2node: 0000000000000000000000000000000000000000
1928 1940 p2node: 0000000000000000000000000000000000000000
1929 1941 p2node: 0000000000000000000000000000000000000000
1930 1942 p2node: 0000000000000000000000000000000000000000
1931 1943 p2node: 0000000000000000000000000000000000000000
1932 1944 p2node--verbose: 0000000000000000000000000000000000000000
1933 1945 p2node--verbose: 0000000000000000000000000000000000000000
1934 1946 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1935 1947 p2node--verbose: 0000000000000000000000000000000000000000
1936 1948 p2node--verbose: 0000000000000000000000000000000000000000
1937 1949 p2node--verbose: 0000000000000000000000000000000000000000
1938 1950 p2node--verbose: 0000000000000000000000000000000000000000
1939 1951 p2node--verbose: 0000000000000000000000000000000000000000
1940 1952 p2node--verbose: 0000000000000000000000000000000000000000
1941 1953 p2node--debug: 0000000000000000000000000000000000000000
1942 1954 p2node--debug: 0000000000000000000000000000000000000000
1943 1955 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1944 1956 p2node--debug: 0000000000000000000000000000000000000000
1945 1957 p2node--debug: 0000000000000000000000000000000000000000
1946 1958 p2node--debug: 0000000000000000000000000000000000000000
1947 1959 p2node--debug: 0000000000000000000000000000000000000000
1948 1960 p2node--debug: 0000000000000000000000000000000000000000
1949 1961 p2node--debug: 0000000000000000000000000000000000000000
1950 1962
1951 1963 Filters work:
1952 1964
1953 1965 $ hg log --template '{author|domain}\n'
1954 1966
1955 1967 hostname
1956 1968
1957 1969
1958 1970
1959 1971
1960 1972 place
1961 1973 place
1962 1974 hostname
1963 1975
1964 1976 $ hg log --template '{author|person}\n'
1965 1977 test
1966 1978 User Name
1967 1979 person
1968 1980 person
1969 1981 person
1970 1982 person
1971 1983 other
1972 1984 A. N. Other
1973 1985 User Name
1974 1986
1975 1987 $ hg log --template '{author|user}\n'
1976 1988 test
1977 1989 user
1978 1990 person
1979 1991 person
1980 1992 person
1981 1993 person
1982 1994 other
1983 1995 other
1984 1996 user
1985 1997
1986 1998 $ hg log --template '{date|date}\n'
1987 1999 Wed Jan 01 10:01:00 2020 +0000
1988 2000 Mon Jan 12 13:46:40 1970 +0000
1989 2001 Sun Jan 18 08:40:01 1970 +0000
1990 2002 Sun Jan 18 08:40:00 1970 +0000
1991 2003 Sat Jan 17 04:53:20 1970 +0000
1992 2004 Fri Jan 16 01:06:40 1970 +0000
1993 2005 Wed Jan 14 21:20:00 1970 +0000
1994 2006 Tue Jan 13 17:33:20 1970 +0000
1995 2007 Mon Jan 12 13:46:40 1970 +0000
1996 2008
1997 2009 $ hg log --template '{date|isodate}\n'
1998 2010 2020-01-01 10:01 +0000
1999 2011 1970-01-12 13:46 +0000
2000 2012 1970-01-18 08:40 +0000
2001 2013 1970-01-18 08:40 +0000
2002 2014 1970-01-17 04:53 +0000
2003 2015 1970-01-16 01:06 +0000
2004 2016 1970-01-14 21:20 +0000
2005 2017 1970-01-13 17:33 +0000
2006 2018 1970-01-12 13:46 +0000
2007 2019
2008 2020 $ hg log --template '{date|isodatesec}\n'
2009 2021 2020-01-01 10:01:00 +0000
2010 2022 1970-01-12 13:46:40 +0000
2011 2023 1970-01-18 08:40:01 +0000
2012 2024 1970-01-18 08:40:00 +0000
2013 2025 1970-01-17 04:53:20 +0000
2014 2026 1970-01-16 01:06:40 +0000
2015 2027 1970-01-14 21:20:00 +0000
2016 2028 1970-01-13 17:33:20 +0000
2017 2029 1970-01-12 13:46:40 +0000
2018 2030
2019 2031 $ hg log --template '{date|rfc822date}\n'
2020 2032 Wed, 01 Jan 2020 10:01:00 +0000
2021 2033 Mon, 12 Jan 1970 13:46:40 +0000
2022 2034 Sun, 18 Jan 1970 08:40:01 +0000
2023 2035 Sun, 18 Jan 1970 08:40:00 +0000
2024 2036 Sat, 17 Jan 1970 04:53:20 +0000
2025 2037 Fri, 16 Jan 1970 01:06:40 +0000
2026 2038 Wed, 14 Jan 1970 21:20:00 +0000
2027 2039 Tue, 13 Jan 1970 17:33:20 +0000
2028 2040 Mon, 12 Jan 1970 13:46:40 +0000
2029 2041
2030 2042 $ hg log --template '{desc|firstline}\n'
2031 2043 third
2032 2044 second
2033 2045 merge
2034 2046 new head
2035 2047 new branch
2036 2048 no user, no domain
2037 2049 no person
2038 2050 other 1
2039 2051 line 1
2040 2052
2041 2053 $ hg log --template '{node|short}\n'
2042 2054 95c24699272e
2043 2055 29114dbae42b
2044 2056 d41e714fe50d
2045 2057 13207e5a10d9
2046 2058 bbe44766e73d
2047 2059 10e46f2dcbf4
2048 2060 97054abb4ab8
2049 2061 b608e9d1a3f0
2050 2062 1e4e1b8f71e0
2051 2063
2052 2064 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2053 2065 <changeset author="test"/>
2054 2066 <changeset author="User Name &lt;user@hostname&gt;"/>
2055 2067 <changeset author="person"/>
2056 2068 <changeset author="person"/>
2057 2069 <changeset author="person"/>
2058 2070 <changeset author="person"/>
2059 2071 <changeset author="other@place"/>
2060 2072 <changeset author="A. N. Other &lt;other@place&gt;"/>
2061 2073 <changeset author="User Name &lt;user@hostname&gt;"/>
2062 2074
2063 2075 $ hg log --template '{rev}: {children}\n'
2064 2076 8:
2065 2077 7: 8:95c24699272e
2066 2078 6:
2067 2079 5: 6:d41e714fe50d
2068 2080 4: 6:d41e714fe50d
2069 2081 3: 4:bbe44766e73d 5:13207e5a10d9
2070 2082 2: 3:10e46f2dcbf4
2071 2083 1: 2:97054abb4ab8
2072 2084 0: 1:b608e9d1a3f0
2073 2085
2074 2086 Formatnode filter works:
2075 2087
2076 2088 $ hg -q log -r 0 --template '{node|formatnode}\n'
2077 2089 1e4e1b8f71e0
2078 2090
2079 2091 $ hg log -r 0 --template '{node|formatnode}\n'
2080 2092 1e4e1b8f71e0
2081 2093
2082 2094 $ hg -v log -r 0 --template '{node|formatnode}\n'
2083 2095 1e4e1b8f71e0
2084 2096
2085 2097 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2086 2098 1e4e1b8f71e05681d422154f5421e385fec3454f
2087 2099
2088 2100 Age filter:
2089 2101
2090 2102 $ hg init unstable-hash
2091 2103 $ cd unstable-hash
2092 2104 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2093 2105
2094 2106 >>> from datetime import datetime, timedelta
2095 2107 >>> fp = open('a', 'w')
2096 2108 >>> n = datetime.now() + timedelta(366 * 7)
2097 2109 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2098 2110 >>> fp.close()
2099 2111 $ hg add a
2100 2112 $ hg commit -m future -d "`cat a`"
2101 2113
2102 2114 $ hg log -l1 --template '{date|age}\n'
2103 2115 7 years from now
2104 2116
2105 2117 $ cd ..
2106 2118 $ rm -rf unstable-hash
2107 2119
2108 2120 Add a dummy commit to make up for the instability of the above:
2109 2121
2110 2122 $ echo a > a
2111 2123 $ hg add a
2112 2124 $ hg ci -m future
2113 2125
2114 2126 Count filter:
2115 2127
2116 2128 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2117 2129 40 12
2118 2130
2119 2131 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2120 2132 0 1 4
2121 2133
2122 2134 $ hg log -G --template '{rev}: children: {children|count}, \
2123 2135 > tags: {tags|count}, file_adds: {file_adds|count}, \
2124 2136 > ancestors: {revset("ancestors(%s)", rev)|count}'
2125 2137 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2126 2138 |
2127 2139 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2128 2140 |
2129 2141 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2130 2142
2131 2143 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2132 2144 |\
2133 2145 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2134 2146 | |
2135 2147 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2136 2148 |/
2137 2149 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2138 2150 |
2139 2151 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2140 2152 |
2141 2153 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2142 2154 |
2143 2155 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2144 2156
2145 2157
2146 2158 Upper/lower filters:
2147 2159
2148 2160 $ hg log -r0 --template '{branch|upper}\n'
2149 2161 DEFAULT
2150 2162 $ hg log -r0 --template '{author|lower}\n'
2151 2163 user name <user@hostname>
2152 2164 $ hg log -r0 --template '{date|upper}\n'
2153 2165 abort: template filter 'upper' is not compatible with keyword 'date'
2154 2166 [255]
2155 2167
2156 2168 Add a commit that does all possible modifications at once
2157 2169
2158 2170 $ echo modify >> third
2159 2171 $ touch b
2160 2172 $ hg add b
2161 2173 $ hg mv fourth fifth
2162 2174 $ hg rm a
2163 2175 $ hg ci -m "Modify, add, remove, rename"
2164 2176
2165 2177 Check the status template
2166 2178
2167 2179 $ cat <<EOF >> $HGRCPATH
2168 2180 > [extensions]
2169 2181 > color=
2170 2182 > EOF
2171 2183
2172 2184 $ hg log -T status -r 10
2173 2185 changeset: 10:0f9759ec227a
2174 2186 tag: tip
2175 2187 user: test
2176 2188 date: Thu Jan 01 00:00:00 1970 +0000
2177 2189 summary: Modify, add, remove, rename
2178 2190 files:
2179 2191 M third
2180 2192 A b
2181 2193 A fifth
2182 2194 R a
2183 2195 R fourth
2184 2196
2185 2197 $ hg log -T status -C -r 10
2186 2198 changeset: 10:0f9759ec227a
2187 2199 tag: tip
2188 2200 user: test
2189 2201 date: Thu Jan 01 00:00:00 1970 +0000
2190 2202 summary: Modify, add, remove, rename
2191 2203 files:
2192 2204 M third
2193 2205 A b
2194 2206 A fifth
2195 2207 fourth
2196 2208 R a
2197 2209 R fourth
2198 2210
2199 2211 $ hg log -T status -C -r 10 -v
2200 2212 changeset: 10:0f9759ec227a
2201 2213 tag: tip
2202 2214 user: test
2203 2215 date: Thu Jan 01 00:00:00 1970 +0000
2204 2216 description:
2205 2217 Modify, add, remove, rename
2206 2218
2207 2219 files:
2208 2220 M third
2209 2221 A b
2210 2222 A fifth
2211 2223 fourth
2212 2224 R a
2213 2225 R fourth
2214 2226
2215 2227 $ hg log -T status -C -r 10 --debug
2216 2228 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2217 2229 tag: tip
2218 2230 phase: secret
2219 2231 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2220 2232 parent: -1:0000000000000000000000000000000000000000
2221 2233 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2222 2234 user: test
2223 2235 date: Thu Jan 01 00:00:00 1970 +0000
2224 2236 extra: branch=default
2225 2237 description:
2226 2238 Modify, add, remove, rename
2227 2239
2228 2240 files:
2229 2241 M third
2230 2242 A b
2231 2243 A fifth
2232 2244 fourth
2233 2245 R a
2234 2246 R fourth
2235 2247
2236 2248 $ hg log -T status -C -r 10 --quiet
2237 2249 10:0f9759ec227a
2238 2250 $ hg --color=debug log -T status -r 10
2239 2251 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2240 2252 [log.tag|tag: tip]
2241 2253 [log.user|user: test]
2242 2254 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2243 2255 [log.summary|summary: Modify, add, remove, rename]
2244 2256 [ui.note log.files|files:]
2245 2257 [status.modified|M third]
2246 2258 [status.added|A b]
2247 2259 [status.added|A fifth]
2248 2260 [status.removed|R a]
2249 2261 [status.removed|R fourth]
2250 2262
2251 2263 $ hg --color=debug log -T status -C -r 10
2252 2264 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2253 2265 [log.tag|tag: tip]
2254 2266 [log.user|user: test]
2255 2267 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2256 2268 [log.summary|summary: Modify, add, remove, rename]
2257 2269 [ui.note log.files|files:]
2258 2270 [status.modified|M third]
2259 2271 [status.added|A b]
2260 2272 [status.added|A fifth]
2261 2273 [status.copied| fourth]
2262 2274 [status.removed|R a]
2263 2275 [status.removed|R fourth]
2264 2276
2265 2277 $ hg --color=debug log -T status -C -r 10 -v
2266 2278 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2267 2279 [log.tag|tag: tip]
2268 2280 [log.user|user: test]
2269 2281 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2270 2282 [ui.note log.description|description:]
2271 2283 [ui.note log.description|Modify, add, remove, rename]
2272 2284
2273 2285 [ui.note log.files|files:]
2274 2286 [status.modified|M third]
2275 2287 [status.added|A b]
2276 2288 [status.added|A fifth]
2277 2289 [status.copied| fourth]
2278 2290 [status.removed|R a]
2279 2291 [status.removed|R fourth]
2280 2292
2281 2293 $ hg --color=debug log -T status -C -r 10 --debug
2282 2294 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2283 2295 [log.tag|tag: tip]
2284 2296 [log.phase|phase: secret]
2285 2297 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2286 2298 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2287 2299 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2288 2300 [log.user|user: test]
2289 2301 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2290 2302 [ui.debug log.extra|extra: branch=default]
2291 2303 [ui.note log.description|description:]
2292 2304 [ui.note log.description|Modify, add, remove, rename]
2293 2305
2294 2306 [ui.note log.files|files:]
2295 2307 [status.modified|M third]
2296 2308 [status.added|A b]
2297 2309 [status.added|A fifth]
2298 2310 [status.copied| fourth]
2299 2311 [status.removed|R a]
2300 2312 [status.removed|R fourth]
2301 2313
2302 2314 $ hg --color=debug log -T status -C -r 10 --quiet
2303 2315 [log.node|10:0f9759ec227a]
2304 2316
2305 2317 Check the bisect template
2306 2318
2307 2319 $ hg bisect -g 1
2308 2320 $ hg bisect -b 3 --noupdate
2309 2321 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2310 2322 $ hg log -T bisect -r 0:4
2311 2323 changeset: 0:1e4e1b8f71e0
2312 2324 bisect: good (implicit)
2313 2325 user: User Name <user@hostname>
2314 2326 date: Mon Jan 12 13:46:40 1970 +0000
2315 2327 summary: line 1
2316 2328
2317 2329 changeset: 1:b608e9d1a3f0
2318 2330 bisect: good
2319 2331 user: A. N. Other <other@place>
2320 2332 date: Tue Jan 13 17:33:20 1970 +0000
2321 2333 summary: other 1
2322 2334
2323 2335 changeset: 2:97054abb4ab8
2324 2336 bisect: untested
2325 2337 user: other@place
2326 2338 date: Wed Jan 14 21:20:00 1970 +0000
2327 2339 summary: no person
2328 2340
2329 2341 changeset: 3:10e46f2dcbf4
2330 2342 bisect: bad
2331 2343 user: person
2332 2344 date: Fri Jan 16 01:06:40 1970 +0000
2333 2345 summary: no user, no domain
2334 2346
2335 2347 changeset: 4:bbe44766e73d
2336 2348 bisect: bad (implicit)
2337 2349 branch: foo
2338 2350 user: person
2339 2351 date: Sat Jan 17 04:53:20 1970 +0000
2340 2352 summary: new branch
2341 2353
2342 2354 $ hg log --debug -T bisect -r 0:4
2343 2355 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2344 2356 bisect: good (implicit)
2345 2357 phase: public
2346 2358 parent: -1:0000000000000000000000000000000000000000
2347 2359 parent: -1:0000000000000000000000000000000000000000
2348 2360 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2349 2361 user: User Name <user@hostname>
2350 2362 date: Mon Jan 12 13:46:40 1970 +0000
2351 2363 files+: a
2352 2364 extra: branch=default
2353 2365 description:
2354 2366 line 1
2355 2367 line 2
2356 2368
2357 2369
2358 2370 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2359 2371 bisect: good
2360 2372 phase: public
2361 2373 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2362 2374 parent: -1:0000000000000000000000000000000000000000
2363 2375 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2364 2376 user: A. N. Other <other@place>
2365 2377 date: Tue Jan 13 17:33:20 1970 +0000
2366 2378 files+: b
2367 2379 extra: branch=default
2368 2380 description:
2369 2381 other 1
2370 2382 other 2
2371 2383
2372 2384 other 3
2373 2385
2374 2386
2375 2387 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2376 2388 bisect: untested
2377 2389 phase: public
2378 2390 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2379 2391 parent: -1:0000000000000000000000000000000000000000
2380 2392 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2381 2393 user: other@place
2382 2394 date: Wed Jan 14 21:20:00 1970 +0000
2383 2395 files+: c
2384 2396 extra: branch=default
2385 2397 description:
2386 2398 no person
2387 2399
2388 2400
2389 2401 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2390 2402 bisect: bad
2391 2403 phase: public
2392 2404 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2393 2405 parent: -1:0000000000000000000000000000000000000000
2394 2406 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2395 2407 user: person
2396 2408 date: Fri Jan 16 01:06:40 1970 +0000
2397 2409 files: c
2398 2410 extra: branch=default
2399 2411 description:
2400 2412 no user, no domain
2401 2413
2402 2414
2403 2415 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2404 2416 bisect: bad (implicit)
2405 2417 branch: foo
2406 2418 phase: draft
2407 2419 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2408 2420 parent: -1:0000000000000000000000000000000000000000
2409 2421 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2410 2422 user: person
2411 2423 date: Sat Jan 17 04:53:20 1970 +0000
2412 2424 extra: branch=foo
2413 2425 description:
2414 2426 new branch
2415 2427
2416 2428
2417 2429 $ hg log -v -T bisect -r 0:4
2418 2430 changeset: 0:1e4e1b8f71e0
2419 2431 bisect: good (implicit)
2420 2432 user: User Name <user@hostname>
2421 2433 date: Mon Jan 12 13:46:40 1970 +0000
2422 2434 files: a
2423 2435 description:
2424 2436 line 1
2425 2437 line 2
2426 2438
2427 2439
2428 2440 changeset: 1:b608e9d1a3f0
2429 2441 bisect: good
2430 2442 user: A. N. Other <other@place>
2431 2443 date: Tue Jan 13 17:33:20 1970 +0000
2432 2444 files: b
2433 2445 description:
2434 2446 other 1
2435 2447 other 2
2436 2448
2437 2449 other 3
2438 2450
2439 2451
2440 2452 changeset: 2:97054abb4ab8
2441 2453 bisect: untested
2442 2454 user: other@place
2443 2455 date: Wed Jan 14 21:20:00 1970 +0000
2444 2456 files: c
2445 2457 description:
2446 2458 no person
2447 2459
2448 2460
2449 2461 changeset: 3:10e46f2dcbf4
2450 2462 bisect: bad
2451 2463 user: person
2452 2464 date: Fri Jan 16 01:06:40 1970 +0000
2453 2465 files: c
2454 2466 description:
2455 2467 no user, no domain
2456 2468
2457 2469
2458 2470 changeset: 4:bbe44766e73d
2459 2471 bisect: bad (implicit)
2460 2472 branch: foo
2461 2473 user: person
2462 2474 date: Sat Jan 17 04:53:20 1970 +0000
2463 2475 description:
2464 2476 new branch
2465 2477
2466 2478
2467 2479 $ hg --color=debug log -T bisect -r 0:4
2468 2480 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2469 2481 [log.bisect bisect.good|bisect: good (implicit)]
2470 2482 [log.user|user: User Name <user@hostname>]
2471 2483 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2472 2484 [log.summary|summary: line 1]
2473 2485
2474 2486 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2475 2487 [log.bisect bisect.good|bisect: good]
2476 2488 [log.user|user: A. N. Other <other@place>]
2477 2489 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2478 2490 [log.summary|summary: other 1]
2479 2491
2480 2492 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2481 2493 [log.bisect bisect.untested|bisect: untested]
2482 2494 [log.user|user: other@place]
2483 2495 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2484 2496 [log.summary|summary: no person]
2485 2497
2486 2498 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2487 2499 [log.bisect bisect.bad|bisect: bad]
2488 2500 [log.user|user: person]
2489 2501 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2490 2502 [log.summary|summary: no user, no domain]
2491 2503
2492 2504 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2493 2505 [log.bisect bisect.bad|bisect: bad (implicit)]
2494 2506 [log.branch|branch: foo]
2495 2507 [log.user|user: person]
2496 2508 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2497 2509 [log.summary|summary: new branch]
2498 2510
2499 2511 $ hg --color=debug log --debug -T bisect -r 0:4
2500 2512 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2501 2513 [log.bisect bisect.good|bisect: good (implicit)]
2502 2514 [log.phase|phase: public]
2503 2515 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2504 2516 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2505 2517 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2506 2518 [log.user|user: User Name <user@hostname>]
2507 2519 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2508 2520 [ui.debug log.files|files+: a]
2509 2521 [ui.debug log.extra|extra: branch=default]
2510 2522 [ui.note log.description|description:]
2511 2523 [ui.note log.description|line 1
2512 2524 line 2]
2513 2525
2514 2526
2515 2527 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2516 2528 [log.bisect bisect.good|bisect: good]
2517 2529 [log.phase|phase: public]
2518 2530 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2519 2531 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2520 2532 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2521 2533 [log.user|user: A. N. Other <other@place>]
2522 2534 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2523 2535 [ui.debug log.files|files+: b]
2524 2536 [ui.debug log.extra|extra: branch=default]
2525 2537 [ui.note log.description|description:]
2526 2538 [ui.note log.description|other 1
2527 2539 other 2
2528 2540
2529 2541 other 3]
2530 2542
2531 2543
2532 2544 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2533 2545 [log.bisect bisect.untested|bisect: untested]
2534 2546 [log.phase|phase: public]
2535 2547 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2536 2548 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2537 2549 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2538 2550 [log.user|user: other@place]
2539 2551 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2540 2552 [ui.debug log.files|files+: c]
2541 2553 [ui.debug log.extra|extra: branch=default]
2542 2554 [ui.note log.description|description:]
2543 2555 [ui.note log.description|no person]
2544 2556
2545 2557
2546 2558 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2547 2559 [log.bisect bisect.bad|bisect: bad]
2548 2560 [log.phase|phase: public]
2549 2561 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2550 2562 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2551 2563 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2552 2564 [log.user|user: person]
2553 2565 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2554 2566 [ui.debug log.files|files: c]
2555 2567 [ui.debug log.extra|extra: branch=default]
2556 2568 [ui.note log.description|description:]
2557 2569 [ui.note log.description|no user, no domain]
2558 2570
2559 2571
2560 2572 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2561 2573 [log.bisect bisect.bad|bisect: bad (implicit)]
2562 2574 [log.branch|branch: foo]
2563 2575 [log.phase|phase: draft]
2564 2576 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2565 2577 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2566 2578 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2567 2579 [log.user|user: person]
2568 2580 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2569 2581 [ui.debug log.extra|extra: branch=foo]
2570 2582 [ui.note log.description|description:]
2571 2583 [ui.note log.description|new branch]
2572 2584
2573 2585
2574 2586 $ hg --color=debug log -v -T bisect -r 0:4
2575 2587 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2576 2588 [log.bisect bisect.good|bisect: good (implicit)]
2577 2589 [log.user|user: User Name <user@hostname>]
2578 2590 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2579 2591 [ui.note log.files|files: a]
2580 2592 [ui.note log.description|description:]
2581 2593 [ui.note log.description|line 1
2582 2594 line 2]
2583 2595
2584 2596
2585 2597 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2586 2598 [log.bisect bisect.good|bisect: good]
2587 2599 [log.user|user: A. N. Other <other@place>]
2588 2600 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2589 2601 [ui.note log.files|files: b]
2590 2602 [ui.note log.description|description:]
2591 2603 [ui.note log.description|other 1
2592 2604 other 2
2593 2605
2594 2606 other 3]
2595 2607
2596 2608
2597 2609 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2598 2610 [log.bisect bisect.untested|bisect: untested]
2599 2611 [log.user|user: other@place]
2600 2612 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2601 2613 [ui.note log.files|files: c]
2602 2614 [ui.note log.description|description:]
2603 2615 [ui.note log.description|no person]
2604 2616
2605 2617
2606 2618 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2607 2619 [log.bisect bisect.bad|bisect: bad]
2608 2620 [log.user|user: person]
2609 2621 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2610 2622 [ui.note log.files|files: c]
2611 2623 [ui.note log.description|description:]
2612 2624 [ui.note log.description|no user, no domain]
2613 2625
2614 2626
2615 2627 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2616 2628 [log.bisect bisect.bad|bisect: bad (implicit)]
2617 2629 [log.branch|branch: foo]
2618 2630 [log.user|user: person]
2619 2631 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2620 2632 [ui.note log.description|description:]
2621 2633 [ui.note log.description|new branch]
2622 2634
2623 2635
2624 2636 $ hg bisect --reset
2625 2637
2626 2638 Error on syntax:
2627 2639
2628 2640 $ echo 'x = "f' >> t
2629 2641 $ hg log
2630 2642 hg: parse error at t:3: unmatched quotes
2631 2643 [255]
2632 2644
2633 2645 $ hg log -T '{date'
2634 2646 hg: parse error at 1: unterminated template expansion
2635 2647 [255]
2636 2648
2637 2649 Behind the scenes, this will throw TypeError
2638 2650
2639 2651 $ hg log -l 3 --template '{date|obfuscate}\n'
2640 2652 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2641 2653 [255]
2642 2654
2643 2655 Behind the scenes, this will throw a ValueError
2644 2656
2645 2657 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2646 2658 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2647 2659 [255]
2648 2660
2649 2661 Behind the scenes, this will throw AttributeError
2650 2662
2651 2663 $ hg log -l 3 --template 'line: {date|escape}\n'
2652 2664 abort: template filter 'escape' is not compatible with keyword 'date'
2653 2665 [255]
2654 2666
2655 2667 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2656 2668 hg: parse error: localdate expects a date information
2657 2669 [255]
2658 2670
2659 2671 Behind the scenes, this will throw ValueError
2660 2672
2661 2673 $ hg tip --template '{author|email|date}\n'
2662 2674 hg: parse error: date expects a date information
2663 2675 [255]
2664 2676
2665 2677 Error in nested template:
2666 2678
2667 2679 $ hg log -T '{"date'
2668 2680 hg: parse error at 2: unterminated string
2669 2681 [255]
2670 2682
2671 $ hg log -T '{"foo{date|=}"}'
2683 $ hg log -T '{"foo{date|?}"}'
2672 2684 hg: parse error at 11: syntax error
2673 2685 [255]
2674 2686
2675 2687 Thrown an error if a template function doesn't exist
2676 2688
2677 2689 $ hg tip --template '{foo()}\n'
2678 2690 hg: parse error: unknown function 'foo'
2679 2691 [255]
2680 2692
2681 2693 Pass generator object created by template function to filter
2682 2694
2683 2695 $ hg log -l 1 --template '{if(author, author)|user}\n'
2684 2696 test
2685 2697
2686 2698 Test index keyword:
2687 2699
2688 2700 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2689 2701 10 0:a 1:b 2:fifth 3:fourth 4:third
2690 2702 11 0:a
2691 2703
2692 2704 $ hg branches -T '{index} {branch}\n'
2693 2705 0 default
2694 2706 1 foo
2695 2707
2696 2708 Test diff function:
2697 2709
2698 2710 $ hg diff -c 8
2699 2711 diff -r 29114dbae42b -r 95c24699272e fourth
2700 2712 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2701 2713 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2702 2714 @@ -0,0 +1,1 @@
2703 2715 +second
2704 2716 diff -r 29114dbae42b -r 95c24699272e second
2705 2717 --- a/second Mon Jan 12 13:46:40 1970 +0000
2706 2718 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2707 2719 @@ -1,1 +0,0 @@
2708 2720 -second
2709 2721 diff -r 29114dbae42b -r 95c24699272e third
2710 2722 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2711 2723 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2712 2724 @@ -0,0 +1,1 @@
2713 2725 +third
2714 2726
2715 2727 $ hg log -r 8 -T "{diff()}"
2716 2728 diff -r 29114dbae42b -r 95c24699272e fourth
2717 2729 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2718 2730 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2719 2731 @@ -0,0 +1,1 @@
2720 2732 +second
2721 2733 diff -r 29114dbae42b -r 95c24699272e second
2722 2734 --- a/second Mon Jan 12 13:46:40 1970 +0000
2723 2735 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2724 2736 @@ -1,1 +0,0 @@
2725 2737 -second
2726 2738 diff -r 29114dbae42b -r 95c24699272e third
2727 2739 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2728 2740 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2729 2741 @@ -0,0 +1,1 @@
2730 2742 +third
2731 2743
2732 2744 $ hg log -r 8 -T "{diff('glob:f*')}"
2733 2745 diff -r 29114dbae42b -r 95c24699272e fourth
2734 2746 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2735 2747 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2736 2748 @@ -0,0 +1,1 @@
2737 2749 +second
2738 2750
2739 2751 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2740 2752 diff -r 29114dbae42b -r 95c24699272e second
2741 2753 --- a/second Mon Jan 12 13:46:40 1970 +0000
2742 2754 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2743 2755 @@ -1,1 +0,0 @@
2744 2756 -second
2745 2757 diff -r 29114dbae42b -r 95c24699272e third
2746 2758 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2747 2759 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2748 2760 @@ -0,0 +1,1 @@
2749 2761 +third
2750 2762
2751 2763 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2752 2764 diff -r 29114dbae42b -r 95c24699272e fourth
2753 2765 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2754 2766 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2755 2767 @@ -0,0 +1,1 @@
2756 2768 +second
2757 2769
2758 2770 $ cd ..
2759 2771
2760 2772
2761 2773 latesttag:
2762 2774
2763 2775 $ hg init latesttag
2764 2776 $ cd latesttag
2765 2777
2766 2778 $ echo a > file
2767 2779 $ hg ci -Am a -d '0 0'
2768 2780 adding file
2769 2781
2770 2782 $ echo b >> file
2771 2783 $ hg ci -m b -d '1 0'
2772 2784
2773 2785 $ echo c >> head1
2774 2786 $ hg ci -Am h1c -d '2 0'
2775 2787 adding head1
2776 2788
2777 2789 $ hg update -q 1
2778 2790 $ echo d >> head2
2779 2791 $ hg ci -Am h2d -d '3 0'
2780 2792 adding head2
2781 2793 created new head
2782 2794
2783 2795 $ echo e >> head2
2784 2796 $ hg ci -m h2e -d '4 0'
2785 2797
2786 2798 $ hg merge -q
2787 2799 $ hg ci -m merge -d '5 -3600'
2788 2800
2789 2801 No tag set:
2790 2802
2791 2803 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2792 2804 5: null+5
2793 2805 4: null+4
2794 2806 3: null+3
2795 2807 2: null+3
2796 2808 1: null+2
2797 2809 0: null+1
2798 2810
2799 2811 One common tag: longest path wins:
2800 2812
2801 2813 $ hg tag -r 1 -m t1 -d '6 0' t1
2802 2814 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2803 2815 6: t1+4
2804 2816 5: t1+3
2805 2817 4: t1+2
2806 2818 3: t1+1
2807 2819 2: t1+1
2808 2820 1: t1+0
2809 2821 0: null+1
2810 2822
2811 2823 One ancestor tag: more recent wins:
2812 2824
2813 2825 $ hg tag -r 2 -m t2 -d '7 0' t2
2814 2826 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2815 2827 7: t2+3
2816 2828 6: t2+2
2817 2829 5: t2+1
2818 2830 4: t1+2
2819 2831 3: t1+1
2820 2832 2: t2+0
2821 2833 1: t1+0
2822 2834 0: null+1
2823 2835
2824 2836 Two branch tags: more recent wins:
2825 2837
2826 2838 $ hg tag -r 3 -m t3 -d '8 0' t3
2827 2839 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2828 2840 8: t3+5
2829 2841 7: t3+4
2830 2842 6: t3+3
2831 2843 5: t3+2
2832 2844 4: t3+1
2833 2845 3: t3+0
2834 2846 2: t2+0
2835 2847 1: t1+0
2836 2848 0: null+1
2837 2849
2838 2850 Merged tag overrides:
2839 2851
2840 2852 $ hg tag -r 5 -m t5 -d '9 0' t5
2841 2853 $ hg tag -r 3 -m at3 -d '10 0' at3
2842 2854 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2843 2855 10: t5+5
2844 2856 9: t5+4
2845 2857 8: t5+3
2846 2858 7: t5+2
2847 2859 6: t5+1
2848 2860 5: t5+0
2849 2861 4: at3:t3+1
2850 2862 3: at3:t3+0
2851 2863 2: t2+0
2852 2864 1: t1+0
2853 2865 0: null+1
2854 2866
2855 2867 $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2856 2868 10: t5+5,5
2857 2869 9: t5+4,4
2858 2870 8: t5+3,3
2859 2871 7: t5+2,2
2860 2872 6: t5+1,1
2861 2873 5: t5+0,0
2862 2874 4: at3+1,1 t3+1,1
2863 2875 3: at3+0,0 t3+0,0
2864 2876 2: t2+0,0
2865 2877 1: t1+0,0
2866 2878 0: null+1,1
2867 2879
2868 2880 $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
2869 2881 10: t3, C: 8, D: 7
2870 2882 9: t3, C: 7, D: 6
2871 2883 8: t3, C: 6, D: 5
2872 2884 7: t3, C: 5, D: 4
2873 2885 6: t3, C: 4, D: 3
2874 2886 5: t3, C: 3, D: 2
2875 2887 4: t3, C: 1, D: 1
2876 2888 3: t3, C: 0, D: 0
2877 2889 2: t1, C: 1, D: 1
2878 2890 1: t1, C: 0, D: 0
2879 2891 0: null, C: 1, D: 1
2880 2892
2881 2893 $ cd ..
2882 2894
2883 2895
2884 2896 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2885 2897 if it is a relative path
2886 2898
2887 2899 $ mkdir -p home/styles
2888 2900
2889 2901 $ cat > home/styles/teststyle <<EOF
2890 2902 > changeset = 'test {rev}:{node|short}\n'
2891 2903 > EOF
2892 2904
2893 2905 $ HOME=`pwd`/home; export HOME
2894 2906
2895 2907 $ cat > latesttag/.hg/hgrc <<EOF
2896 2908 > [ui]
2897 2909 > style = ~/styles/teststyle
2898 2910 > EOF
2899 2911
2900 2912 $ hg -R latesttag tip
2901 2913 test 10:9b4a630e5f5f
2902 2914
2903 2915 Test recursive showlist template (issue1989):
2904 2916
2905 2917 $ cat > style1989 <<EOF
2906 2918 > changeset = '{file_mods}{manifest}{extras}'
2907 2919 > file_mod = 'M|{author|person}\n'
2908 2920 > manifest = '{rev},{author}\n'
2909 2921 > extra = '{key}: {author}\n'
2910 2922 > EOF
2911 2923
2912 2924 $ hg -R latesttag log -r tip --style=style1989
2913 2925 M|test
2914 2926 10,test
2915 2927 branch: test
2916 2928
2917 2929 Test new-style inline templating:
2918 2930
2919 2931 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2920 2932 modified files: .hgtags
2921 2933
2922 2934
2923 2935 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
2924 2936 hg: parse error: keyword 'rev' is not iterable
2925 2937 [255]
2926 2938 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
2927 2939 hg: parse error: None is not iterable
2928 2940 [255]
2929 2941
2930 2942 Test the sub function of templating for expansion:
2931 2943
2932 2944 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2933 2945 xx
2934 2946
2935 2947 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
2936 2948 hg: parse error: sub got an invalid pattern: [
2937 2949 [255]
2938 2950 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
2939 2951 hg: parse error: sub got an invalid replacement: \1
2940 2952 [255]
2941 2953
2942 2954 Test the strip function with chars specified:
2943 2955
2944 2956 $ hg log -R latesttag --template '{desc}\n'
2945 2957 at3
2946 2958 t5
2947 2959 t3
2948 2960 t2
2949 2961 t1
2950 2962 merge
2951 2963 h2e
2952 2964 h2d
2953 2965 h1c
2954 2966 b
2955 2967 a
2956 2968
2957 2969 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2958 2970 at3
2959 2971 5
2960 2972 3
2961 2973 2
2962 2974 1
2963 2975 merg
2964 2976 h2
2965 2977 h2d
2966 2978 h1c
2967 2979 b
2968 2980 a
2969 2981
2970 2982 Test date format:
2971 2983
2972 2984 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2973 2985 date: 70 01 01 10 +0000
2974 2986 date: 70 01 01 09 +0000
2975 2987 date: 70 01 01 08 +0000
2976 2988 date: 70 01 01 07 +0000
2977 2989 date: 70 01 01 06 +0000
2978 2990 date: 70 01 01 05 +0100
2979 2991 date: 70 01 01 04 +0000
2980 2992 date: 70 01 01 03 +0000
2981 2993 date: 70 01 01 02 +0000
2982 2994 date: 70 01 01 01 +0000
2983 2995 date: 70 01 01 00 +0000
2984 2996
2985 2997 Test invalid date:
2986 2998
2987 2999 $ hg log -R latesttag -T '{date(rev)}\n'
2988 3000 hg: parse error: date expects a date information
2989 3001 [255]
2990 3002
2991 3003 Test integer literal:
2992 3004
2993 3005 $ hg debugtemplate -v '{(0)}\n'
2994 3006 (template
2995 3007 (group
2996 3008 ('integer', '0'))
2997 3009 ('string', '\n'))
2998 3010 0
2999 3011 $ hg debugtemplate -v '{(123)}\n'
3000 3012 (template
3001 3013 (group
3002 3014 ('integer', '123'))
3003 3015 ('string', '\n'))
3004 3016 123
3005 3017 $ hg debugtemplate -v '{(-4)}\n'
3006 3018 (template
3007 3019 (group
3008 3020 (negate
3009 3021 ('integer', '4')))
3010 3022 ('string', '\n'))
3011 3023 -4
3012 3024 $ hg debugtemplate '{(-)}\n'
3013 3025 hg: parse error at 3: not a prefix: )
3014 3026 [255]
3015 3027 $ hg debugtemplate '{(-a)}\n'
3016 3028 hg: parse error: negation needs an integer argument
3017 3029 [255]
3018 3030
3019 3031 top-level integer literal is interpreted as symbol (i.e. variable name):
3020 3032
3021 3033 $ hg debugtemplate -D 1=one -v '{1}\n'
3022 3034 (template
3023 3035 ('integer', '1')
3024 3036 ('string', '\n'))
3025 3037 one
3026 3038 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3027 3039 (template
3028 3040 (func
3029 3041 ('symbol', 'if')
3030 3042 (list
3031 3043 ('string', 't')
3032 3044 (template
3033 3045 ('integer', '1'))))
3034 3046 ('string', '\n'))
3035 3047 one
3036 3048 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3037 3049 (template
3038 3050 (|
3039 3051 ('integer', '1')
3040 3052 ('symbol', 'stringify'))
3041 3053 ('string', '\n'))
3042 3054 one
3043 3055
3044 3056 unless explicit symbol is expected:
3045 3057
3046 3058 $ hg log -Ra -r0 -T '{desc|1}\n'
3047 3059 hg: parse error: expected a symbol, got 'integer'
3048 3060 [255]
3049 3061 $ hg log -Ra -r0 -T '{1()}\n'
3050 3062 hg: parse error: expected a symbol, got 'integer'
3051 3063 [255]
3052 3064
3053 3065 Test string literal:
3054 3066
3055 3067 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3056 3068 (template
3057 3069 ('string', 'string with no template fragment')
3058 3070 ('string', '\n'))
3059 3071 string with no template fragment
3060 3072 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3061 3073 (template
3062 3074 (template
3063 3075 ('string', 'template: ')
3064 3076 ('symbol', 'rev'))
3065 3077 ('string', '\n'))
3066 3078 template: 0
3067 3079 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3068 3080 (template
3069 3081 ('string', 'rawstring: {rev}')
3070 3082 ('string', '\n'))
3071 3083 rawstring: {rev}
3072 3084 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3073 3085 (template
3074 3086 (%
3075 3087 ('symbol', 'files')
3076 3088 ('string', 'rawstring: {file}'))
3077 3089 ('string', '\n'))
3078 3090 rawstring: {file}
3079 3091
3080 3092 Test string escaping:
3081 3093
3082 3094 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3083 3095 >
3084 3096 <>\n<[>
3085 3097 <>\n<]>
3086 3098 <>\n<
3087 3099
3088 3100 $ hg log -R latesttag -r 0 \
3089 3101 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3090 3102 >
3091 3103 <>\n<[>
3092 3104 <>\n<]>
3093 3105 <>\n<
3094 3106
3095 3107 $ hg log -R latesttag -r 0 -T esc \
3096 3108 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3097 3109 >
3098 3110 <>\n<[>
3099 3111 <>\n<]>
3100 3112 <>\n<
3101 3113
3102 3114 $ cat <<'EOF' > esctmpl
3103 3115 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3104 3116 > EOF
3105 3117 $ hg log -R latesttag -r 0 --style ./esctmpl
3106 3118 >
3107 3119 <>\n<[>
3108 3120 <>\n<]>
3109 3121 <>\n<
3110 3122
3111 3123 Test string escaping of quotes:
3112 3124
3113 3125 $ hg log -Ra -r0 -T '{"\""}\n'
3114 3126 "
3115 3127 $ hg log -Ra -r0 -T '{"\\\""}\n'
3116 3128 \"
3117 3129 $ hg log -Ra -r0 -T '{r"\""}\n'
3118 3130 \"
3119 3131 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3120 3132 \\\"
3121 3133
3122 3134
3123 3135 $ hg log -Ra -r0 -T '{"\""}\n'
3124 3136 "
3125 3137 $ hg log -Ra -r0 -T '{"\\\""}\n'
3126 3138 \"
3127 3139 $ hg log -Ra -r0 -T '{r"\""}\n'
3128 3140 \"
3129 3141 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3130 3142 \\\"
3131 3143
3132 3144 Test exception in quoted template. single backslash before quotation mark is
3133 3145 stripped before parsing:
3134 3146
3135 3147 $ cat <<'EOF' > escquotetmpl
3136 3148 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3137 3149 > EOF
3138 3150 $ cd latesttag
3139 3151 $ hg log -r 2 --style ../escquotetmpl
3140 3152 " \" \" \\" head1
3141 3153
3142 3154 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3143 3155 valid
3144 3156 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3145 3157 valid
3146 3158
3147 3159 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3148 3160 _evalifliteral() templates (issue4733):
3149 3161
3150 3162 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3151 3163 "2
3152 3164 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3153 3165 "2
3154 3166 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3155 3167 "2
3156 3168
3157 3169 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3158 3170 \"
3159 3171 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3160 3172 \"
3161 3173 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3162 3174 \"
3163 3175
3164 3176 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3165 3177 \\\"
3166 3178 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3167 3179 \\\"
3168 3180 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3169 3181 \\\"
3170 3182
3171 3183 escaped single quotes and errors:
3172 3184
3173 3185 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3174 3186 foo
3175 3187 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3176 3188 foo
3177 3189 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3178 3190 hg: parse error at 21: unterminated string
3179 3191 [255]
3180 3192 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3181 3193 hg: parse error: trailing \ in string
3182 3194 [255]
3183 3195 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3184 3196 hg: parse error: trailing \ in string
3185 3197 [255]
3186 3198
3187 3199 $ cd ..
3188 3200
3189 3201 Test leading backslashes:
3190 3202
3191 3203 $ cd latesttag
3192 3204 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3193 3205 {rev} {file}
3194 3206 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3195 3207 \2 \head1
3196 3208 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3197 3209 \{rev} \{file}
3198 3210 $ cd ..
3199 3211
3200 3212 Test leading backslashes in "if" expression (issue4714):
3201 3213
3202 3214 $ cd latesttag
3203 3215 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3204 3216 {rev} \{rev}
3205 3217 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3206 3218 \2 \\{rev}
3207 3219 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3208 3220 \{rev} \\\{rev}
3209 3221 $ cd ..
3210 3222
3211 3223 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3212 3224
3213 3225 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3214 3226 \x6e
3215 3227 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3216 3228 \x5c\x786e
3217 3229 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3218 3230 \x6e
3219 3231 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3220 3232 \x5c\x786e
3221 3233
3222 3234 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3223 3235 \x6e
3224 3236 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3225 3237 \x5c\x786e
3226 3238 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3227 3239 \x6e
3228 3240 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3229 3241 \x5c\x786e
3230 3242
3231 3243 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3232 3244 fourth
3233 3245 second
3234 3246 third
3235 3247 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3236 3248 fourth\nsecond\nthird
3237 3249
3238 3250 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3239 3251 <p>
3240 3252 1st
3241 3253 </p>
3242 3254 <p>
3243 3255 2nd
3244 3256 </p>
3245 3257 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3246 3258 <p>
3247 3259 1st\n\n2nd
3248 3260 </p>
3249 3261 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3250 3262 1st
3251 3263
3252 3264 2nd
3253 3265
3254 3266 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3255 3267 o perso
3256 3268 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3257 3269 no person
3258 3270 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3259 3271 o perso
3260 3272 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3261 3273 no perso
3262 3274
3263 3275 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3264 3276 -o perso-
3265 3277 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3266 3278 no person
3267 3279 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3268 3280 \x2do perso\x2d
3269 3281 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3270 3282 -o perso-
3271 3283 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3272 3284 \x2do perso\x6e
3273 3285
3274 3286 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3275 3287 fourth
3276 3288 second
3277 3289 third
3278 3290
3279 3291 Test string escaping in nested expression:
3280 3292
3281 3293 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3282 3294 fourth\x6esecond\x6ethird
3283 3295 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3284 3296 fourth\x6esecond\x6ethird
3285 3297
3286 3298 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3287 3299 fourth\x6esecond\x6ethird
3288 3300 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3289 3301 fourth\x5c\x786esecond\x5c\x786ethird
3290 3302
3291 3303 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3292 3304 3:\x6eo user, \x6eo domai\x6e
3293 3305 4:\x5c\x786eew bra\x5c\x786ech
3294 3306
3295 3307 Test quotes in nested expression are evaluated just like a $(command)
3296 3308 substitution in POSIX shells:
3297 3309
3298 3310 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3299 3311 8:95c24699272e
3300 3312 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3301 3313 {8} "95c24699272e"
3302 3314
3303 3315 Test recursive evaluation:
3304 3316
3305 3317 $ hg init r
3306 3318 $ cd r
3307 3319 $ echo a > a
3308 3320 $ hg ci -Am '{rev}'
3309 3321 adding a
3310 3322 $ hg log -r 0 --template '{if(rev, desc)}\n'
3311 3323 {rev}
3312 3324 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3313 3325 test 0
3314 3326
3315 3327 $ hg branch -q 'text.{rev}'
3316 3328 $ echo aa >> aa
3317 3329 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3318 3330
3319 3331 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3320 3332 {node|short}desc to
3321 3333 text.{rev}be wrapped
3322 3334 text.{rev}desc to be
3323 3335 text.{rev}wrapped (no-eol)
3324 3336 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3325 3337 bcc7ff960b8e:desc to
3326 3338 text.1:be wrapped
3327 3339 text.1:desc to be
3328 3340 text.1:wrapped (no-eol)
3329 3341 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3330 3342 hg: parse error: fill expects an integer width
3331 3343 [255]
3332 3344
3333 3345 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3334 3346 bcc7ff960b8e:desc to be
3335 3347 termwidth.1:wrapped desc
3336 3348 termwidth.1:to be wrapped (no-eol)
3337 3349
3338 3350 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3339 3351 {node|short} (no-eol)
3340 3352 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3341 3353 bcc-ff---b-e (no-eol)
3342 3354
3343 3355 $ cat >> .hg/hgrc <<EOF
3344 3356 > [extensions]
3345 3357 > color=
3346 3358 > [color]
3347 3359 > mode=ansi
3348 3360 > text.{rev} = red
3349 3361 > text.1 = green
3350 3362 > EOF
3351 3363 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3352 3364 \x1b[0;31mtext\x1b[0m (esc)
3353 3365 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3354 3366 \x1b[0;32mtext\x1b[0m (esc)
3355 3367
3356 3368 color effect can be specified without quoting:
3357 3369
3358 3370 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3359 3371 \x1b[0;31mtext\x1b[0m (esc)
3360 3372
3361 3373 color effects can be nested (issue5413)
3362 3374
3363 3375 $ hg debugtemplate --color=always \
3364 3376 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3365 3377 \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)
3366 3378
3367 3379 pad() should interact well with color codes (issue5416)
3368 3380
3369 3381 $ hg debugtemplate --color=always \
3370 3382 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3371 3383 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3372 3384
3373 3385 label should be no-op if color is disabled:
3374 3386
3375 3387 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3376 3388 text
3377 3389 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3378 3390 text
3379 3391
3380 3392 Test branches inside if statement:
3381 3393
3382 3394 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3383 3395 no
3384 3396
3385 3397 Test get function:
3386 3398
3387 3399 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3388 3400 default
3389 3401 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3390 3402 default
3391 3403 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3392 3404 hg: parse error: get() expects a dict as first argument
3393 3405 [255]
3394 3406
3395 3407 Test json filter applied to hybrid object:
3396 3408
3397 3409 $ hg log -r0 -T '{files|json}\n'
3398 3410 ["a"]
3399 3411 $ hg log -r0 -T '{extras|json}\n'
3400 3412 {"branch": "default"}
3401 3413
3402 3414 Test localdate(date, tz) function:
3403 3415
3404 3416 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3405 3417 1970-01-01 09:00 +0900
3406 3418 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3407 3419 1970-01-01 00:00 +0000
3408 3420 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3409 3421 hg: parse error: localdate expects a timezone
3410 3422 [255]
3411 3423 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3412 3424 1970-01-01 02:00 +0200
3413 3425 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3414 3426 1970-01-01 00:00 +0000
3415 3427 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3416 3428 1970-01-01 00:00 +0000
3417 3429 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3418 3430 hg: parse error: localdate expects a timezone
3419 3431 [255]
3420 3432 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3421 3433 hg: parse error: localdate expects a timezone
3422 3434 [255]
3423 3435
3424 3436 Test shortest(node) function:
3425 3437
3426 3438 $ echo b > b
3427 3439 $ hg ci -qAm b
3428 3440 $ hg log --template '{shortest(node)}\n'
3429 3441 e777
3430 3442 bcc7
3431 3443 f776
3432 3444 $ hg log --template '{shortest(node, 10)}\n'
3433 3445 e777603221
3434 3446 bcc7ff960b
3435 3447 f7769ec2ab
3436 3448 $ hg log --template '{node|shortest}\n' -l1
3437 3449 e777
3438 3450
3439 3451 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3440 3452 f7769ec2ab
3441 3453 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3442 3454 hg: parse error: shortest() expects an integer minlength
3443 3455 [255]
3444 3456
3445 3457 $ cd ..
3446 3458
3447 3459 Test shortest(node) with the repo having short hash collision:
3448 3460
3449 3461 $ hg init hashcollision
3450 3462 $ cd hashcollision
3451 3463 $ cat <<EOF >> .hg/hgrc
3452 3464 > [experimental]
3453 3465 > evolution = createmarkers
3454 3466 > EOF
3455 3467 $ echo 0 > a
3456 3468 $ hg ci -qAm 0
3457 3469 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3458 3470 > hg up -q 0
3459 3471 > echo $i > a
3460 3472 > hg ci -qm $i
3461 3473 > done
3462 3474 $ hg up -q null
3463 3475 $ hg log -r0: -T '{rev}:{node}\n'
3464 3476 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3465 3477 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3466 3478 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3467 3479 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3468 3480 4:10776689e627b465361ad5c296a20a487e153ca4
3469 3481 5:a00be79088084cb3aff086ab799f8790e01a976b
3470 3482 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3471 3483 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3472 3484 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3473 3485 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3474 3486 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3475 3487 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3476 3488 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3477 3489 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3478 3490
3479 3491 nodes starting with '11' (we don't have the revision number '11' though)
3480 3492
3481 3493 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3482 3494 1:1142
3483 3495 2:1140
3484 3496 3:11d
3485 3497
3486 3498 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3487 3499
3488 3500 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3489 3501 6:a0b
3490 3502 7:a04
3491 3503
3492 3504 node '10' conflicts with the revision number '10' even if it is hidden
3493 3505 (we could exclude hidden revision numbers, but currently we don't)
3494 3506
3495 3507 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3496 3508 4:107
3497 3509 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3498 3510 4:107
3499 3511
3500 3512 node 'c562' should be unique if the other 'c562' nodes are hidden
3501 3513 (but we don't try the slow path to filter out hidden nodes for now)
3502 3514
3503 3515 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3504 3516 8:c5625
3505 3517 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3506 3518 8:c5625
3507 3519 9:c5623
3508 3520 10:c562d
3509 3521
3510 3522 $ cd ..
3511 3523
3512 3524 Test pad function
3513 3525
3514 3526 $ cd r
3515 3527
3516 3528 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3517 3529 2 test
3518 3530 1 {node|short}
3519 3531 0 test
3520 3532
3521 3533 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3522 3534 2 test
3523 3535 1 {node|short}
3524 3536 0 test
3525 3537
3526 3538 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3527 3539 2------------------- test
3528 3540 1------------------- {node|short}
3529 3541 0------------------- test
3530 3542
3531 3543 Test template string in pad function
3532 3544
3533 3545 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3534 3546 {0} test
3535 3547
3536 3548 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3537 3549 \{rev} test
3538 3550
3539 3551 Test width argument passed to pad function
3540 3552
3541 3553 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3542 3554 0 test
3543 3555 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3544 3556 hg: parse error: pad() expects an integer width
3545 3557 [255]
3546 3558
3547 3559 Test invalid fillchar passed to pad function
3548 3560
3549 3561 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3550 3562 hg: parse error: pad() expects a single fill character
3551 3563 [255]
3552 3564 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3553 3565 hg: parse error: pad() expects a single fill character
3554 3566 [255]
3555 3567
3556 3568 Test boolean argument passed to pad function
3557 3569
3558 3570 no crash
3559 3571
3560 3572 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3561 3573 ---------0
3562 3574
3563 3575 string/literal
3564 3576
3565 3577 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3566 3578 ---------0
3567 3579 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3568 3580 0---------
3569 3581 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3570 3582 0---------
3571 3583
3572 3584 unknown keyword is evaluated to ''
3573 3585
3574 3586 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3575 3587 0---------
3576 3588
3577 3589 Test separate function
3578 3590
3579 3591 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3580 3592 a-b-c
3581 3593 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3582 3594 0:f7769ec2ab97 test default
3583 3595 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3584 3596 a \x1b[0;31mb\x1b[0m c d (esc)
3585 3597
3586 3598 Test boolean expression/literal passed to if function
3587 3599
3588 3600 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3589 3601 rev 0 is True
3590 3602 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3591 3603 literal 0 is True as well
3592 3604 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3593 3605 empty string is False
3594 3606 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3595 3607 empty list is False
3596 3608 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3597 3609 true is True
3598 3610 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3599 3611 false is False
3600 3612 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3601 3613 non-empty string is True
3602 3614
3603 3615 Test ifcontains function
3604 3616
3605 3617 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3606 3618 2 is in the string
3607 3619 1 is not
3608 3620 0 is in the string
3609 3621
3610 3622 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3611 3623 2 is in the string
3612 3624 1 is not
3613 3625 0 is in the string
3614 3626
3615 3627 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3616 3628 2 did not add a
3617 3629 1 did not add a
3618 3630 0 added a
3619 3631
3620 3632 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3621 3633 2 is parent of 1
3622 3634 1
3623 3635 0
3624 3636
3625 3637 Test revset function
3626 3638
3627 3639 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3628 3640 2 current rev
3629 3641 1 not current rev
3630 3642 0 not current rev
3631 3643
3632 3644 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3633 3645 2 match rev
3634 3646 1 match rev
3635 3647 0 not match rev
3636 3648
3637 3649 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3638 3650 2 Parents: 1
3639 3651 1 Parents: 0
3640 3652 0 Parents:
3641 3653
3642 3654 $ cat >> .hg/hgrc <<EOF
3643 3655 > [revsetalias]
3644 3656 > myparents(\$1) = parents(\$1)
3645 3657 > EOF
3646 3658 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3647 3659 2 Parents: 1
3648 3660 1 Parents: 0
3649 3661 0 Parents:
3650 3662
3651 3663 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3652 3664 Rev: 2
3653 3665 Ancestor: 0
3654 3666 Ancestor: 1
3655 3667 Ancestor: 2
3656 3668
3657 3669 Rev: 1
3658 3670 Ancestor: 0
3659 3671 Ancestor: 1
3660 3672
3661 3673 Rev: 0
3662 3674 Ancestor: 0
3663 3675
3664 3676 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3665 3677 2
3666 3678
3667 3679 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3668 3680 2
3669 3681
3670 3682 a list template is evaluated for each item of revset/parents
3671 3683
3672 3684 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3673 3685 2 p: 1:bcc7ff960b8e
3674 3686 1 p: 0:f7769ec2ab97
3675 3687 0 p:
3676 3688
3677 3689 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3678 3690 2 p: 1:bcc7ff960b8e -1:000000000000
3679 3691 1 p: 0:f7769ec2ab97 -1:000000000000
3680 3692 0 p: -1:000000000000 -1:000000000000
3681 3693
3682 3694 therefore, 'revcache' should be recreated for each rev
3683 3695
3684 3696 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3685 3697 2 aa b
3686 3698 p
3687 3699 1
3688 3700 p a
3689 3701 0 a
3690 3702 p
3691 3703
3692 3704 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3693 3705 2 aa b
3694 3706 p
3695 3707 1
3696 3708 p a
3697 3709 0 a
3698 3710 p
3699 3711
3700 3712 a revset item must be evaluated as an integer revision, not an offset from tip
3701 3713
3702 3714 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3703 3715 -1:000000000000
3704 3716 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3705 3717 -1:000000000000
3706 3718
3707 3719 join() should pick '{rev}' from revset items:
3708 3720
3709 3721 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3710 3722 4, 5
3711 3723
3712 3724 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3713 3725 default. join() should agree with the default formatting:
3714 3726
3715 3727 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3716 3728 5:13207e5a10d9, 4:bbe44766e73d
3717 3729
3718 3730 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3719 3731 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3720 3732 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3721 3733
3722 3734 Test files function
3723 3735
3724 3736 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3725 3737 2
3726 3738 a
3727 3739 aa
3728 3740 b
3729 3741 1
3730 3742 a
3731 3743 0
3732 3744 a
3733 3745
3734 3746 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3735 3747 2
3736 3748 aa
3737 3749 1
3738 3750
3739 3751 0
3740 3752
3741 3753
3742 3754 Test relpath function
3743 3755
3744 3756 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
3745 3757 a
3746 3758 $ cd ..
3747 3759 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
3748 3760 r/a
3749 3761 $ cd r
3750 3762
3751 3763 Test active bookmark templating
3752 3764
3753 3765 $ hg book foo
3754 3766 $ hg book bar
3755 3767 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3756 3768 2 bar* foo
3757 3769 1
3758 3770 0
3759 3771 $ hg log --template "{rev} {activebookmark}\n"
3760 3772 2 bar
3761 3773 1
3762 3774 0
3763 3775 $ hg bookmarks --inactive bar
3764 3776 $ hg log --template "{rev} {activebookmark}\n"
3765 3777 2
3766 3778 1
3767 3779 0
3768 3780 $ hg book -r1 baz
3769 3781 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
3770 3782 2 bar foo
3771 3783 1 baz
3772 3784 0
3773 3785 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
3774 3786 2 t
3775 3787 1 f
3776 3788 0 f
3777 3789
3778 3790 Test namespaces dict
3779 3791
3780 3792 $ hg log -T '{rev}{namespaces % " {namespace}={join(names, ",")}"}\n'
3781 3793 2 bookmarks=bar,foo tags=tip branches=text.{rev}
3782 3794 1 bookmarks=baz tags= branches=text.{rev}
3783 3795 0 bookmarks= tags= branches=default
3784 3796 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
3785 3797 bookmarks: bar foo
3786 3798 tags: tip
3787 3799 branches: text.{rev}
3788 3800 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
3789 3801 bookmarks:
3790 3802 bar
3791 3803 foo
3792 3804 tags:
3793 3805 tip
3794 3806 branches:
3795 3807 text.{rev}
3796 3808 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
3797 3809 bar
3798 3810 foo
3799 3811
3800 3812 Test stringify on sub expressions
3801 3813
3802 3814 $ cd ..
3803 3815 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
3804 3816 fourth, second, third
3805 3817 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
3806 3818 abc
3807 3819
3808 3820 Test splitlines
3809 3821
3810 3822 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
3811 3823 @ foo Modify, add, remove, rename
3812 3824 |
3813 3825 o foo future
3814 3826 |
3815 3827 o foo third
3816 3828 |
3817 3829 o foo second
3818 3830
3819 3831 o foo merge
3820 3832 |\
3821 3833 | o foo new head
3822 3834 | |
3823 3835 o | foo new branch
3824 3836 |/
3825 3837 o foo no user, no domain
3826 3838 |
3827 3839 o foo no person
3828 3840 |
3829 3841 o foo other 1
3830 3842 | foo other 2
3831 3843 | foo
3832 3844 | foo other 3
3833 3845 o foo line 1
3834 3846 foo line 2
3835 3847
3836 3848 Test startswith
3837 3849 $ hg log -Gv -R a --template "{startswith(desc)}"
3838 3850 hg: parse error: startswith expects two arguments
3839 3851 [255]
3840 3852
3841 3853 $ hg log -Gv -R a --template "{startswith('line', desc)}"
3842 3854 @
3843 3855 |
3844 3856 o
3845 3857 |
3846 3858 o
3847 3859 |
3848 3860 o
3849 3861
3850 3862 o
3851 3863 |\
3852 3864 | o
3853 3865 | |
3854 3866 o |
3855 3867 |/
3856 3868 o
3857 3869 |
3858 3870 o
3859 3871 |
3860 3872 o
3861 3873 |
3862 3874 o line 1
3863 3875 line 2
3864 3876
3865 3877 Test bad template with better error message
3866 3878
3867 3879 $ hg log -Gv -R a --template '{desc|user()}'
3868 3880 hg: parse error: expected a symbol, got 'func'
3869 3881 [255]
3870 3882
3871 3883 Test word function (including index out of bounds graceful failure)
3872 3884
3873 3885 $ hg log -Gv -R a --template "{word('1', desc)}"
3874 3886 @ add,
3875 3887 |
3876 3888 o
3877 3889 |
3878 3890 o
3879 3891 |
3880 3892 o
3881 3893
3882 3894 o
3883 3895 |\
3884 3896 | o head
3885 3897 | |
3886 3898 o | branch
3887 3899 |/
3888 3900 o user,
3889 3901 |
3890 3902 o person
3891 3903 |
3892 3904 o 1
3893 3905 |
3894 3906 o 1
3895 3907
3896 3908
3897 3909 Test word third parameter used as splitter
3898 3910
3899 3911 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
3900 3912 @ M
3901 3913 |
3902 3914 o future
3903 3915 |
3904 3916 o third
3905 3917 |
3906 3918 o sec
3907 3919
3908 3920 o merge
3909 3921 |\
3910 3922 | o new head
3911 3923 | |
3912 3924 o | new branch
3913 3925 |/
3914 3926 o n
3915 3927 |
3916 3928 o n
3917 3929 |
3918 3930 o
3919 3931 |
3920 3932 o line 1
3921 3933 line 2
3922 3934
3923 3935 Test word error messages for not enough and too many arguments
3924 3936
3925 3937 $ hg log -Gv -R a --template "{word('0')}"
3926 3938 hg: parse error: word expects two or three arguments, got 1
3927 3939 [255]
3928 3940
3929 3941 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
3930 3942 hg: parse error: word expects two or three arguments, got 7
3931 3943 [255]
3932 3944
3933 3945 Test word for integer literal
3934 3946
3935 3947 $ hg log -R a --template "{word(2, desc)}\n" -r0
3936 3948 line
3937 3949
3938 3950 Test word for invalid numbers
3939 3951
3940 3952 $ hg log -Gv -R a --template "{word('a', desc)}"
3941 3953 hg: parse error: word expects an integer index
3942 3954 [255]
3943 3955
3944 3956 Test word for out of range
3945 3957
3946 3958 $ hg log -R a --template "{word(10000, desc)}"
3947 3959 $ hg log -R a --template "{word(-10000, desc)}"
3948 3960
3949 3961 Test indent and not adding to empty lines
3950 3962
3951 3963 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
3952 3964 -----
3953 3965 > line 1
3954 3966 >> line 2
3955 3967 -----
3956 3968 > other 1
3957 3969 >> other 2
3958 3970
3959 3971 >> other 3
3960 3972
3961 3973 Test with non-strings like dates
3962 3974
3963 3975 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
3964 3976 1200000.00
3965 3977 1300000.00
3966 3978
3967 3979 Test broken string escapes:
3968 3980
3969 3981 $ hg log -T "bogus\\" -R a
3970 3982 hg: parse error: trailing \ in string
3971 3983 [255]
3972 3984 $ hg log -T "\\xy" -R a
3973 3985 hg: parse error: invalid \x escape
3974 3986 [255]
3975 3987
3976 3988 json filter should escape HTML tags so that the output can be embedded in hgweb:
3977 3989
3978 3990 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
3979 3991 "\u003cfoo@example.org\u003e"
3980 3992
3981 3993 Templater supports aliases of symbol and func() styles:
3982 3994
3983 3995 $ hg clone -q a aliases
3984 3996 $ cd aliases
3985 3997 $ cat <<EOF >> .hg/hgrc
3986 3998 > [templatealias]
3987 3999 > r = rev
3988 4000 > rn = "{r}:{node|short}"
3989 4001 > status(c, files) = files % "{c} {file}\n"
3990 4002 > utcdate(d) = localdate(d, "UTC")
3991 4003 > EOF
3992 4004
3993 4005 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
3994 4006 (template
3995 4007 ('symbol', 'rn')
3996 4008 ('string', ' ')
3997 4009 (|
3998 4010 (func
3999 4011 ('symbol', 'utcdate')
4000 4012 ('symbol', 'date'))
4001 4013 ('symbol', 'isodate'))
4002 4014 ('string', '\n'))
4003 4015 * expanded:
4004 4016 (template
4005 4017 (template
4006 4018 ('symbol', 'rev')
4007 4019 ('string', ':')
4008 4020 (|
4009 4021 ('symbol', 'node')
4010 4022 ('symbol', 'short')))
4011 4023 ('string', ' ')
4012 4024 (|
4013 4025 (func
4014 4026 ('symbol', 'localdate')
4015 4027 (list
4016 4028 ('symbol', 'date')
4017 4029 ('string', 'UTC')))
4018 4030 ('symbol', 'isodate'))
4019 4031 ('string', '\n'))
4020 4032 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4021 4033
4022 4034 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4023 4035 (template
4024 4036 (func
4025 4037 ('symbol', 'status')
4026 4038 (list
4027 4039 ('string', 'A')
4028 4040 ('symbol', 'file_adds'))))
4029 4041 * expanded:
4030 4042 (template
4031 4043 (%
4032 4044 ('symbol', 'file_adds')
4033 4045 (template
4034 4046 ('string', 'A')
4035 4047 ('string', ' ')
4036 4048 ('symbol', 'file')
4037 4049 ('string', '\n'))))
4038 4050 A a
4039 4051
4040 4052 A unary function alias can be called as a filter:
4041 4053
4042 4054 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4043 4055 (template
4044 4056 (|
4045 4057 (|
4046 4058 ('symbol', 'date')
4047 4059 ('symbol', 'utcdate'))
4048 4060 ('symbol', 'isodate'))
4049 4061 ('string', '\n'))
4050 4062 * expanded:
4051 4063 (template
4052 4064 (|
4053 4065 (func
4054 4066 ('symbol', 'localdate')
4055 4067 (list
4056 4068 ('symbol', 'date')
4057 4069 ('string', 'UTC')))
4058 4070 ('symbol', 'isodate'))
4059 4071 ('string', '\n'))
4060 4072 1970-01-12 13:46 +0000
4061 4073
4062 4074 Aliases should be applied only to command arguments and templates in hgrc.
4063 4075 Otherwise, our stock styles and web templates could be corrupted:
4064 4076
4065 4077 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4066 4078 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4067 4079
4068 4080 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4069 4081 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4070 4082
4071 4083 $ cat <<EOF > tmpl
4072 4084 > changeset = 'nothing expanded:{rn}\n'
4073 4085 > EOF
4074 4086 $ hg log -r0 --style ./tmpl
4075 4087 nothing expanded:
4076 4088
4077 4089 Aliases in formatter:
4078 4090
4079 4091 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4080 4092 default 6:d41e714fe50d
4081 4093 foo 4:bbe44766e73d
4082 4094
4083 4095 Aliases should honor HGPLAIN:
4084 4096
4085 4097 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4086 4098 nothing expanded:
4087 4099 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4088 4100 0:1e4e1b8f71e0
4089 4101
4090 4102 Unparsable alias:
4091 4103
4092 4104 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4093 4105 (template
4094 4106 ('symbol', 'bad'))
4095 4107 abort: bad definition of template alias "bad": at 2: not a prefix: end
4096 4108 [255]
4097 4109 $ hg log --config templatealias.bad='x(' -T '{bad}'
4098 4110 abort: bad definition of template alias "bad": at 2: not a prefix: end
4099 4111 [255]
4100 4112
4101 4113 $ cd ..
4102 4114
4103 4115 Set up repository for non-ascii encoding tests:
4104 4116
4105 4117 $ hg init nonascii
4106 4118 $ cd nonascii
4107 4119 $ python <<EOF
4108 4120 > open('latin1', 'w').write('\xe9')
4109 4121 > open('utf-8', 'w').write('\xc3\xa9')
4110 4122 > EOF
4111 4123 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4112 4124 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4113 4125
4114 4126 json filter should try round-trip conversion to utf-8:
4115 4127
4116 4128 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4117 4129 "\u00e9"
4118 4130 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4119 4131 "non-ascii branch: \u00e9"
4120 4132
4121 4133 json filter takes input as utf-8b:
4122 4134
4123 4135 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4124 4136 "\u00e9"
4125 4137 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4126 4138 "\udce9"
4127 4139
4128 4140 utf8 filter:
4129 4141
4130 4142 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4131 4143 round-trip: c3a9
4132 4144 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4133 4145 decoded: c3a9
4134 4146 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4135 4147 abort: decoding near * (glob)
4136 4148 [255]
4137 4149 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4138 4150 abort: template filter 'utf8' is not compatible with keyword 'rev'
4139 4151 [255]
4140 4152
4141 4153 pad width:
4142 4154
4143 4155 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4144 4156 \xc3\xa9- (esc)
4145 4157
4146 4158 $ cd ..
4147 4159
4148 4160 Test that template function in extension is registered as expected
4149 4161
4150 4162 $ cd a
4151 4163
4152 4164 $ cat <<EOF > $TESTTMP/customfunc.py
4153 4165 > from mercurial import registrar
4154 4166 >
4155 4167 > templatefunc = registrar.templatefunc()
4156 4168 >
4157 4169 > @templatefunc('custom()')
4158 4170 > def custom(context, mapping, args):
4159 4171 > return 'custom'
4160 4172 > EOF
4161 4173 $ cat <<EOF > .hg/hgrc
4162 4174 > [extensions]
4163 4175 > customfunc = $TESTTMP/customfunc.py
4164 4176 > EOF
4165 4177
4166 4178 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4167 4179 custom
4168 4180
4169 4181 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now