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