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