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