##// END OF EJS Templates
templater: make templatepath() not return directory paths...
Martin von Zweigbergk -
r45819:9a308336 default
parent child Browse files
Show More
@@ -1,1114 +1,1114 b''
1 1 # templater.py - template expansion for output
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """Slightly complicated template engine for commands and hgweb
9 9
10 10 This module provides low-level interface to the template engine. See the
11 11 formatter and cmdutil modules if you are looking for high-level functions
12 12 such as ``cmdutil.rendertemplate(ctx, tmpl)``.
13 13
14 14 Internal Data Types
15 15 -------------------
16 16
17 17 Template keywords and functions take a dictionary of current symbols and
18 18 resources (a "mapping") and return result. Inputs and outputs must be one
19 19 of the following data types:
20 20
21 21 bytes
22 22 a byte string, which is generally a human-readable text in local encoding.
23 23
24 24 generator
25 25 a lazily-evaluated byte string, which is a possibly nested generator of
26 26 values of any printable types, and will be folded by ``stringify()``
27 27 or ``flatten()``.
28 28
29 29 None
30 30 sometimes represents an empty value, which can be stringified to ''.
31 31
32 32 True, False, int, float
33 33 can be stringified as such.
34 34
35 35 wrappedbytes, wrappedvalue
36 36 a wrapper for the above printable types.
37 37
38 38 date
39 39 represents a (unixtime, offset) tuple.
40 40
41 41 hybrid
42 42 represents a list/dict of printable values, which can also be converted
43 43 to mappings by % operator.
44 44
45 45 hybriditem
46 46 represents a scalar printable value, also supports % operator.
47 47
48 48 revslist
49 49 represents a list of revision numbers.
50 50
51 51 mappinggenerator, mappinglist
52 52 represents mappings (i.e. a list of dicts), which may have default
53 53 output format.
54 54
55 55 mappingdict
56 56 represents a single mapping (i.e. a dict), which may have default output
57 57 format.
58 58
59 59 mappingnone
60 60 represents None of Optional[mappable], which will be mapped to an empty
61 61 string by % operation.
62 62
63 63 mappedgenerator
64 64 a lazily-evaluated list of byte strings, which is e.g. a result of %
65 65 operation.
66 66 """
67 67
68 68 from __future__ import absolute_import, print_function
69 69
70 70 import abc
71 71 import os
72 72
73 73 from .i18n import _
74 74 from .pycompat import getattr
75 75 from . import (
76 76 config,
77 77 encoding,
78 78 error,
79 79 parser,
80 80 pycompat,
81 81 templatefilters,
82 82 templatefuncs,
83 83 templateutil,
84 84 util,
85 85 )
86 86 from .utils import (
87 87 resourceutil,
88 88 stringutil,
89 89 )
90 90
91 91 # template parsing
92 92
93 93 elements = {
94 94 # token-type: binding-strength, primary, prefix, infix, suffix
95 95 b"(": (20, None, (b"group", 1, b")"), (b"func", 1, b")"), None),
96 96 b".": (18, None, None, (b".", 18), None),
97 97 b"%": (15, None, None, (b"%", 15), None),
98 98 b"|": (15, None, None, (b"|", 15), None),
99 99 b"*": (5, None, None, (b"*", 5), None),
100 100 b"/": (5, None, None, (b"/", 5), None),
101 101 b"+": (4, None, None, (b"+", 4), None),
102 102 b"-": (4, None, (b"negate", 19), (b"-", 4), None),
103 103 b"=": (3, None, None, (b"keyvalue", 3), None),
104 104 b",": (2, None, None, (b"list", 2), None),
105 105 b")": (0, None, None, None, None),
106 106 b"integer": (0, b"integer", None, None, None),
107 107 b"symbol": (0, b"symbol", None, None, None),
108 108 b"string": (0, b"string", None, None, None),
109 109 b"template": (0, b"template", None, None, None),
110 110 b"end": (0, None, None, None, None),
111 111 }
112 112
113 113
114 114 def tokenize(program, start, end, term=None):
115 115 """Parse a template expression into a stream of tokens, which must end
116 116 with term if specified"""
117 117 pos = start
118 118 program = pycompat.bytestr(program)
119 119 while pos < end:
120 120 c = program[pos]
121 121 if c.isspace(): # skip inter-token whitespace
122 122 pass
123 123 elif c in b"(=,).%|+-*/": # handle simple operators
124 124 yield (c, None, pos)
125 125 elif c in b'"\'': # handle quoted templates
126 126 s = pos + 1
127 127 data, pos = _parsetemplate(program, s, end, c)
128 128 yield (b'template', data, s)
129 129 pos -= 1
130 130 elif c == b'r' and program[pos : pos + 2] in (b"r'", b'r"'):
131 131 # handle quoted strings
132 132 c = program[pos + 1]
133 133 s = pos = pos + 2
134 134 while pos < end: # find closing quote
135 135 d = program[pos]
136 136 if d == b'\\': # skip over escaped characters
137 137 pos += 2
138 138 continue
139 139 if d == c:
140 140 yield (b'string', program[s:pos], s)
141 141 break
142 142 pos += 1
143 143 else:
144 144 raise error.ParseError(_(b"unterminated string"), s)
145 145 elif c.isdigit():
146 146 s = pos
147 147 while pos < end:
148 148 d = program[pos]
149 149 if not d.isdigit():
150 150 break
151 151 pos += 1
152 152 yield (b'integer', program[s:pos], s)
153 153 pos -= 1
154 154 elif (
155 155 c == b'\\'
156 156 and program[pos : pos + 2] in (br"\'", br'\"')
157 157 or c == b'r'
158 158 and program[pos : pos + 3] in (br"r\'", br'r\"')
159 159 ):
160 160 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
161 161 # where some of nested templates were preprocessed as strings and
162 162 # then compiled. therefore, \"...\" was allowed. (issue4733)
163 163 #
164 164 # processing flow of _evalifliteral() at 5ab28a2e9962:
165 165 # outer template string -> stringify() -> compiletemplate()
166 166 # ------------------------ ------------ ------------------
167 167 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
168 168 # ~~~~~~~~
169 169 # escaped quoted string
170 170 if c == b'r':
171 171 pos += 1
172 172 token = b'string'
173 173 else:
174 174 token = b'template'
175 175 quote = program[pos : pos + 2]
176 176 s = pos = pos + 2
177 177 while pos < end: # find closing escaped quote
178 178 if program.startswith(b'\\\\\\', pos, end):
179 179 pos += 4 # skip over double escaped characters
180 180 continue
181 181 if program.startswith(quote, pos, end):
182 182 # interpret as if it were a part of an outer string
183 183 data = parser.unescapestr(program[s:pos])
184 184 if token == b'template':
185 185 data = _parsetemplate(data, 0, len(data))[0]
186 186 yield (token, data, s)
187 187 pos += 1
188 188 break
189 189 pos += 1
190 190 else:
191 191 raise error.ParseError(_(b"unterminated string"), s)
192 192 elif c.isalnum() or c in b'_':
193 193 s = pos
194 194 pos += 1
195 195 while pos < end: # find end of symbol
196 196 d = program[pos]
197 197 if not (d.isalnum() or d == b"_"):
198 198 break
199 199 pos += 1
200 200 sym = program[s:pos]
201 201 yield (b'symbol', sym, s)
202 202 pos -= 1
203 203 elif c == term:
204 204 yield (b'end', None, pos)
205 205 return
206 206 else:
207 207 raise error.ParseError(_(b"syntax error"), pos)
208 208 pos += 1
209 209 if term:
210 210 raise error.ParseError(_(b"unterminated template expansion"), start)
211 211 yield (b'end', None, pos)
212 212
213 213
214 214 def _parsetemplate(tmpl, start, stop, quote=b''):
215 215 r"""
216 216 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
217 217 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
218 218 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
219 219 ([('string', 'foo'), ('symbol', 'bar')], 9)
220 220 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
221 221 ([('string', 'foo')], 4)
222 222 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
223 223 ([('string', 'foo"'), ('string', 'bar')], 9)
224 224 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
225 225 ([('string', 'foo\\')], 6)
226 226 """
227 227 parsed = []
228 228 for typ, val, pos in _scantemplate(tmpl, start, stop, quote):
229 229 if typ == b'string':
230 230 parsed.append((typ, val))
231 231 elif typ == b'template':
232 232 parsed.append(val)
233 233 elif typ == b'end':
234 234 return parsed, pos
235 235 else:
236 236 raise error.ProgrammingError(b'unexpected type: %s' % typ)
237 237 raise error.ProgrammingError(b'unterminated scanning of template')
238 238
239 239
240 240 def scantemplate(tmpl, raw=False):
241 241 r"""Scan (type, start, end) positions of outermost elements in template
242 242
243 243 If raw=True, a backslash is not taken as an escape character just like
244 244 r'' string in Python. Note that this is different from r'' literal in
245 245 template in that no template fragment can appear in r'', e.g. r'{foo}'
246 246 is a literal '{foo}', but ('{foo}', raw=True) is a template expression
247 247 'foo'.
248 248
249 249 >>> list(scantemplate(b'foo{bar}"baz'))
250 250 [('string', 0, 3), ('template', 3, 8), ('string', 8, 12)]
251 251 >>> list(scantemplate(b'outer{"inner"}outer'))
252 252 [('string', 0, 5), ('template', 5, 14), ('string', 14, 19)]
253 253 >>> list(scantemplate(b'foo\\{escaped}'))
254 254 [('string', 0, 5), ('string', 5, 13)]
255 255 >>> list(scantemplate(b'foo\\{escaped}', raw=True))
256 256 [('string', 0, 4), ('template', 4, 13)]
257 257 """
258 258 last = None
259 259 for typ, val, pos in _scantemplate(tmpl, 0, len(tmpl), raw=raw):
260 260 if last:
261 261 yield last + (pos,)
262 262 if typ == b'end':
263 263 return
264 264 else:
265 265 last = (typ, pos)
266 266 raise error.ProgrammingError(b'unterminated scanning of template')
267 267
268 268
269 269 def _scantemplate(tmpl, start, stop, quote=b'', raw=False):
270 270 """Parse template string into chunks of strings and template expressions"""
271 271 sepchars = b'{' + quote
272 272 unescape = [parser.unescapestr, pycompat.identity][raw]
273 273 pos = start
274 274 p = parser.parser(elements)
275 275 try:
276 276 while pos < stop:
277 277 n = min(
278 278 (tmpl.find(c, pos, stop) for c in pycompat.bytestr(sepchars)),
279 279 key=lambda n: (n < 0, n),
280 280 )
281 281 if n < 0:
282 282 yield (b'string', unescape(tmpl[pos:stop]), pos)
283 283 pos = stop
284 284 break
285 285 c = tmpl[n : n + 1]
286 286 bs = 0 # count leading backslashes
287 287 if not raw:
288 288 bs = (n - pos) - len(tmpl[pos:n].rstrip(b'\\'))
289 289 if bs % 2 == 1:
290 290 # escaped (e.g. '\{', '\\\{', but not '\\{')
291 291 yield (b'string', unescape(tmpl[pos : n - 1]) + c, pos)
292 292 pos = n + 1
293 293 continue
294 294 if n > pos:
295 295 yield (b'string', unescape(tmpl[pos:n]), pos)
296 296 if c == quote:
297 297 yield (b'end', None, n + 1)
298 298 return
299 299
300 300 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, b'}'))
301 301 if not tmpl.startswith(b'}', pos):
302 302 raise error.ParseError(_(b"invalid token"), pos)
303 303 yield (b'template', parseres, n)
304 304 pos += 1
305 305
306 306 if quote:
307 307 raise error.ParseError(_(b"unterminated string"), start)
308 308 except error.ParseError as inst:
309 309 _addparseerrorhint(inst, tmpl)
310 310 raise
311 311 yield (b'end', None, pos)
312 312
313 313
314 314 def _addparseerrorhint(inst, tmpl):
315 315 if len(inst.args) <= 1:
316 316 return # no location
317 317 loc = inst.args[1]
318 318 # Offset the caret location by the number of newlines before the
319 319 # location of the error, since we will replace one-char newlines
320 320 # with the two-char literal r'\n'.
321 321 offset = tmpl[:loc].count(b'\n')
322 322 tmpl = tmpl.replace(b'\n', br'\n')
323 323 # We want the caret to point to the place in the template that
324 324 # failed to parse, but in a hint we get a open paren at the
325 325 # start. Therefore, we print "loc + 1" spaces (instead of "loc")
326 326 # to line up the caret with the location of the error.
327 327 inst.hint = tmpl + b'\n' + b' ' * (loc + 1 + offset) + b'^ ' + _(b'here')
328 328
329 329
330 330 def _unnesttemplatelist(tree):
331 331 """Expand list of templates to node tuple
332 332
333 333 >>> def f(tree):
334 334 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
335 335 >>> f((b'template', []))
336 336 (string '')
337 337 >>> f((b'template', [(b'string', b'foo')]))
338 338 (string 'foo')
339 339 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
340 340 (template
341 341 (string 'foo')
342 342 (symbol 'rev'))
343 343 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
344 344 (template
345 345 (symbol 'rev'))
346 346 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
347 347 (string 'foo')
348 348 """
349 349 if not isinstance(tree, tuple):
350 350 return tree
351 351 op = tree[0]
352 352 if op != b'template':
353 353 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
354 354
355 355 assert len(tree) == 2
356 356 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
357 357 if not xs:
358 358 return (b'string', b'') # empty template ""
359 359 elif len(xs) == 1 and xs[0][0] == b'string':
360 360 return xs[0] # fast path for string with no template fragment "x"
361 361 else:
362 362 return (op,) + xs
363 363
364 364
365 365 def parse(tmpl):
366 366 """Parse template string into tree"""
367 367 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
368 368 assert pos == len(tmpl), b'unquoted template should be consumed'
369 369 return _unnesttemplatelist((b'template', parsed))
370 370
371 371
372 372 def parseexpr(expr):
373 373 """Parse a template expression into tree
374 374
375 375 >>> parseexpr(b'"foo"')
376 376 ('string', 'foo')
377 377 >>> parseexpr(b'foo(bar)')
378 378 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
379 379 >>> parseexpr(b'foo(')
380 380 Traceback (most recent call last):
381 381 ...
382 382 ParseError: ('not a prefix: end', 4)
383 383 >>> parseexpr(b'"foo" "bar"')
384 384 Traceback (most recent call last):
385 385 ...
386 386 ParseError: ('invalid token', 7)
387 387 """
388 388 try:
389 389 return _parseexpr(expr)
390 390 except error.ParseError as inst:
391 391 _addparseerrorhint(inst, expr)
392 392 raise
393 393
394 394
395 395 def _parseexpr(expr):
396 396 p = parser.parser(elements)
397 397 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
398 398 if pos != len(expr):
399 399 raise error.ParseError(_(b'invalid token'), pos)
400 400 return _unnesttemplatelist(tree)
401 401
402 402
403 403 def prettyformat(tree):
404 404 return parser.prettyformat(tree, (b'integer', b'string', b'symbol'))
405 405
406 406
407 407 def compileexp(exp, context, curmethods):
408 408 """Compile parsed template tree to (func, data) pair"""
409 409 if not exp:
410 410 raise error.ParseError(_(b"missing argument"))
411 411 t = exp[0]
412 412 return curmethods[t](exp, context)
413 413
414 414
415 415 # template evaluation
416 416
417 417
418 418 def getsymbol(exp):
419 419 if exp[0] == b'symbol':
420 420 return exp[1]
421 421 raise error.ParseError(_(b"expected a symbol, got '%s'") % exp[0])
422 422
423 423
424 424 def getlist(x):
425 425 if not x:
426 426 return []
427 427 if x[0] == b'list':
428 428 return getlist(x[1]) + [x[2]]
429 429 return [x]
430 430
431 431
432 432 def gettemplate(exp, context):
433 433 """Compile given template tree or load named template from map file;
434 434 returns (func, data) pair"""
435 435 if exp[0] in (b'template', b'string'):
436 436 return compileexp(exp, context, methods)
437 437 if exp[0] == b'symbol':
438 438 # unlike runsymbol(), here 'symbol' is always taken as template name
439 439 # even if it exists in mapping. this allows us to override mapping
440 440 # by web templates, e.g. 'changelogtag' is redefined in map file.
441 441 return context._load(exp[1])
442 442 raise error.ParseError(_(b"expected template specifier"))
443 443
444 444
445 445 def _runrecursivesymbol(context, mapping, key):
446 446 raise error.Abort(_(b"recursive reference '%s' in template") % key)
447 447
448 448
449 449 def buildtemplate(exp, context):
450 450 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
451 451 return (templateutil.runtemplate, ctmpl)
452 452
453 453
454 454 def buildfilter(exp, context):
455 455 n = getsymbol(exp[2])
456 456 if n in context._filters:
457 457 filt = context._filters[n]
458 458 arg = compileexp(exp[1], context, methods)
459 459 return (templateutil.runfilter, (arg, filt))
460 460 if n in context._funcs:
461 461 f = context._funcs[n]
462 462 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
463 463 return (f, args)
464 464 raise error.ParseError(_(b"unknown function '%s'") % n)
465 465
466 466
467 467 def buildmap(exp, context):
468 468 darg = compileexp(exp[1], context, methods)
469 469 targ = gettemplate(exp[2], context)
470 470 return (templateutil.runmap, (darg, targ))
471 471
472 472
473 473 def buildmember(exp, context):
474 474 darg = compileexp(exp[1], context, methods)
475 475 memb = getsymbol(exp[2])
476 476 return (templateutil.runmember, (darg, memb))
477 477
478 478
479 479 def buildnegate(exp, context):
480 480 arg = compileexp(exp[1], context, exprmethods)
481 481 return (templateutil.runnegate, arg)
482 482
483 483
484 484 def buildarithmetic(exp, context, func):
485 485 left = compileexp(exp[1], context, exprmethods)
486 486 right = compileexp(exp[2], context, exprmethods)
487 487 return (templateutil.runarithmetic, (func, left, right))
488 488
489 489
490 490 def buildfunc(exp, context):
491 491 n = getsymbol(exp[1])
492 492 if n in context._funcs:
493 493 f = context._funcs[n]
494 494 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
495 495 return (f, args)
496 496 if n in context._filters:
497 497 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
498 498 if len(args) != 1:
499 499 raise error.ParseError(_(b"filter %s expects one argument") % n)
500 500 f = context._filters[n]
501 501 return (templateutil.runfilter, (args[0], f))
502 502 raise error.ParseError(_(b"unknown function '%s'") % n)
503 503
504 504
505 505 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
506 506 """Compile parsed tree of function arguments into list or dict of
507 507 (func, data) pairs
508 508
509 509 >>> context = engine(lambda t: (templateutil.runsymbol, t))
510 510 >>> def fargs(expr, argspec):
511 511 ... x = _parseexpr(expr)
512 512 ... n = getsymbol(x[1])
513 513 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
514 514 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
515 515 ['l', 'k']
516 516 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
517 517 >>> list(args.keys()), list(args[b'opts'].keys())
518 518 (['opts'], ['opts', 'k'])
519 519 """
520 520
521 521 def compiledict(xs):
522 522 return util.sortdict(
523 523 (k, compileexp(x, context, curmethods))
524 524 for k, x in pycompat.iteritems(xs)
525 525 )
526 526
527 527 def compilelist(xs):
528 528 return [compileexp(x, context, curmethods) for x in xs]
529 529
530 530 if not argspec:
531 531 # filter or function with no argspec: return list of positional args
532 532 return compilelist(getlist(exp))
533 533
534 534 # function with argspec: return dict of named args
535 535 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
536 536 treeargs = parser.buildargsdict(
537 537 getlist(exp),
538 538 funcname,
539 539 argspec,
540 540 keyvaluenode=b'keyvalue',
541 541 keynode=b'symbol',
542 542 )
543 543 compargs = util.sortdict()
544 544 if varkey:
545 545 compargs[varkey] = compilelist(treeargs.pop(varkey))
546 546 if optkey:
547 547 compargs[optkey] = compiledict(treeargs.pop(optkey))
548 548 compargs.update(compiledict(treeargs))
549 549 return compargs
550 550
551 551
552 552 def buildkeyvaluepair(exp, content):
553 553 raise error.ParseError(_(b"can't use a key-value pair in this context"))
554 554
555 555
556 556 def buildlist(exp, context):
557 557 raise error.ParseError(
558 558 _(b"can't use a list in this context"),
559 559 hint=_(b'check place of comma and parens'),
560 560 )
561 561
562 562
563 563 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
564 564 exprmethods = {
565 565 b"integer": lambda e, c: (templateutil.runinteger, e[1]),
566 566 b"string": lambda e, c: (templateutil.runstring, e[1]),
567 567 b"symbol": lambda e, c: (templateutil.runsymbol, e[1]),
568 568 b"template": buildtemplate,
569 569 b"group": lambda e, c: compileexp(e[1], c, exprmethods),
570 570 b".": buildmember,
571 571 b"|": buildfilter,
572 572 b"%": buildmap,
573 573 b"func": buildfunc,
574 574 b"keyvalue": buildkeyvaluepair,
575 575 b"list": buildlist,
576 576 b"+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
577 577 b"-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
578 578 b"negate": buildnegate,
579 579 b"*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
580 580 b"/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
581 581 }
582 582
583 583 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
584 584 methods = exprmethods.copy()
585 585 methods[b"integer"] = exprmethods[b"symbol"] # '{1}' as variable
586 586
587 587
588 588 class _aliasrules(parser.basealiasrules):
589 589 """Parsing and expansion rule set of template aliases"""
590 590
591 591 _section = _(b'template alias')
592 592 _parse = staticmethod(_parseexpr)
593 593
594 594 @staticmethod
595 595 def _trygetfunc(tree):
596 596 """Return (name, args) if tree is func(...) or ...|filter; otherwise
597 597 None"""
598 598 if tree[0] == b'func' and tree[1][0] == b'symbol':
599 599 return tree[1][1], getlist(tree[2])
600 600 if tree[0] == b'|' and tree[2][0] == b'symbol':
601 601 return tree[2][1], [tree[1]]
602 602
603 603
604 604 def expandaliases(tree, aliases):
605 605 """Return new tree of aliases are expanded"""
606 606 aliasmap = _aliasrules.buildmap(aliases)
607 607 return _aliasrules.expand(aliasmap, tree)
608 608
609 609
610 610 # template engine
611 611
612 612
613 613 def unquotestring(s):
614 614 '''unwrap quotes if any; otherwise returns unmodified string'''
615 615 if len(s) < 2 or s[0] not in b"'\"" or s[0] != s[-1]:
616 616 return s
617 617 return s[1:-1]
618 618
619 619
620 620 class resourcemapper(object): # pytype: disable=ignored-metaclass
621 621 """Mapper of internal template resources"""
622 622
623 623 __metaclass__ = abc.ABCMeta
624 624
625 625 @abc.abstractmethod
626 626 def availablekeys(self, mapping):
627 627 """Return a set of available resource keys based on the given mapping"""
628 628
629 629 @abc.abstractmethod
630 630 def knownkeys(self):
631 631 """Return a set of supported resource keys"""
632 632
633 633 @abc.abstractmethod
634 634 def lookup(self, mapping, key):
635 635 """Return a resource for the key if available; otherwise None"""
636 636
637 637 @abc.abstractmethod
638 638 def populatemap(self, context, origmapping, newmapping):
639 639 """Return a dict of additional mapping items which should be paired
640 640 with the given new mapping"""
641 641
642 642
643 643 class nullresourcemapper(resourcemapper):
644 644 def availablekeys(self, mapping):
645 645 return set()
646 646
647 647 def knownkeys(self):
648 648 return set()
649 649
650 650 def lookup(self, mapping, key):
651 651 return None
652 652
653 653 def populatemap(self, context, origmapping, newmapping):
654 654 return {}
655 655
656 656
657 657 class engine(object):
658 658 '''template expansion engine.
659 659
660 660 template expansion works like this. a map file contains key=value
661 661 pairs. if value is quoted, it is treated as string. otherwise, it
662 662 is treated as name of template file.
663 663
664 664 templater is asked to expand a key in map. it looks up key, and
665 665 looks for strings like this: {foo}. it expands {foo} by looking up
666 666 foo in map, and substituting it. expansion is recursive: it stops
667 667 when there is no more {foo} to replace.
668 668
669 669 expansion also allows formatting and filtering.
670 670
671 671 format uses key to expand each item in list. syntax is
672 672 {key%format}.
673 673
674 674 filter uses function to transform value. syntax is
675 675 {key|filter1|filter2|...}.'''
676 676
677 677 def __init__(self, loader, filters=None, defaults=None, resources=None):
678 678 self._loader = loader
679 679 if filters is None:
680 680 filters = {}
681 681 self._filters = filters
682 682 self._funcs = templatefuncs.funcs # make this a parameter if needed
683 683 if defaults is None:
684 684 defaults = {}
685 685 if resources is None:
686 686 resources = nullresourcemapper()
687 687 self._defaults = defaults
688 688 self._resources = resources
689 689 self._cache = {} # key: (func, data)
690 690 self._tmplcache = {} # literal template: (func, data)
691 691
692 692 def overlaymap(self, origmapping, newmapping):
693 693 """Create combined mapping from the original mapping and partial
694 694 mapping to override the original"""
695 695 # do not copy symbols which overrides the defaults depending on
696 696 # new resources, so the defaults will be re-evaluated (issue5612)
697 697 knownres = self._resources.knownkeys()
698 698 newres = self._resources.availablekeys(newmapping)
699 699 mapping = {
700 700 k: v
701 701 for k, v in pycompat.iteritems(origmapping)
702 702 if (
703 703 k in knownres # not a symbol per self.symbol()
704 704 or newres.isdisjoint(self._defaultrequires(k))
705 705 )
706 706 }
707 707 mapping.update(newmapping)
708 708 mapping.update(
709 709 self._resources.populatemap(self, origmapping, newmapping)
710 710 )
711 711 return mapping
712 712
713 713 def _defaultrequires(self, key):
714 714 """Resource keys required by the specified default symbol function"""
715 715 v = self._defaults.get(key)
716 716 if v is None or not callable(v):
717 717 return ()
718 718 return getattr(v, '_requires', ())
719 719
720 720 def symbol(self, mapping, key):
721 721 """Resolve symbol to value or function; None if nothing found"""
722 722 v = None
723 723 if key not in self._resources.knownkeys():
724 724 v = mapping.get(key)
725 725 if v is None:
726 726 v = self._defaults.get(key)
727 727 return v
728 728
729 729 def availableresourcekeys(self, mapping):
730 730 """Return a set of available resource keys based on the given mapping"""
731 731 return self._resources.availablekeys(mapping)
732 732
733 733 def knownresourcekeys(self):
734 734 """Return a set of supported resource keys"""
735 735 return self._resources.knownkeys()
736 736
737 737 def resource(self, mapping, key):
738 738 """Return internal data (e.g. cache) used for keyword/function
739 739 evaluation"""
740 740 v = self._resources.lookup(mapping, key)
741 741 if v is None:
742 742 raise templateutil.ResourceUnavailable(
743 743 _(b'template resource not available: %s') % key
744 744 )
745 745 return v
746 746
747 747 def _load(self, t):
748 748 '''load, parse, and cache a template'''
749 749 if t not in self._cache:
750 750 x = self._loader(t)
751 751 # put poison to cut recursion while compiling 't'
752 752 self._cache[t] = (_runrecursivesymbol, t)
753 753 try:
754 754 self._cache[t] = compileexp(x, self, methods)
755 755 except: # re-raises
756 756 del self._cache[t]
757 757 raise
758 758 return self._cache[t]
759 759
760 760 def _parse(self, tmpl):
761 761 """Parse and cache a literal template"""
762 762 if tmpl not in self._tmplcache:
763 763 x = parse(tmpl)
764 764 self._tmplcache[tmpl] = compileexp(x, self, methods)
765 765 return self._tmplcache[tmpl]
766 766
767 767 def preload(self, t):
768 768 """Load, parse, and cache the specified template if available"""
769 769 try:
770 770 self._load(t)
771 771 return True
772 772 except templateutil.TemplateNotFound:
773 773 return False
774 774
775 775 def process(self, t, mapping):
776 776 '''Perform expansion. t is name of map element to expand.
777 777 mapping contains added elements for use during expansion. Is a
778 778 generator.'''
779 779 func, data = self._load(t)
780 780 return self._expand(func, data, mapping)
781 781
782 782 def expand(self, tmpl, mapping):
783 783 """Perform expansion over a literal template
784 784
785 785 No user aliases will be expanded since this is supposed to be called
786 786 with an internal template string.
787 787 """
788 788 func, data = self._parse(tmpl)
789 789 return self._expand(func, data, mapping)
790 790
791 791 def _expand(self, func, data, mapping):
792 792 # populate additional items only if they don't exist in the given
793 793 # mapping. this is slightly different from overlaymap() because the
794 794 # initial 'revcache' may contain pre-computed items.
795 795 extramapping = self._resources.populatemap(self, {}, mapping)
796 796 if extramapping:
797 797 extramapping.update(mapping)
798 798 mapping = extramapping
799 799 return templateutil.flatten(self, mapping, func(self, mapping, data))
800 800
801 801
802 802 def stylelist():
803 803 path = templatedir()
804 804 if not path:
805 805 return _(b'no templates found, try `hg debuginstall` for more info')
806 806 dirlist = os.listdir(path)
807 807 stylelist = []
808 808 for file in dirlist:
809 809 split = file.split(b".")
810 810 if split[-1] in (b'orig', b'rej'):
811 811 continue
812 812 if split[0] == b"map-cmdline":
813 813 stylelist.append(split[1])
814 814 return b", ".join(sorted(stylelist))
815 815
816 816
817 817 def _readmapfile(mapfile):
818 818 """Load template elements from the given map file"""
819 819 if not os.path.exists(mapfile):
820 820 raise error.Abort(
821 821 _(b"style '%s' not found") % mapfile,
822 822 hint=_(b"available styles: %s") % stylelist(),
823 823 )
824 824
825 825 base = os.path.dirname(mapfile)
826 826 conf = config.config()
827 827
828 828 def include(rel, remap, sections):
829 829 templatedirs = [base, templatedir()]
830 830 for dir in templatedirs:
831 831 if dir is None:
832 832 continue
833 833 abs = os.path.normpath(os.path.join(dir, rel))
834 834 if os.path.isfile(abs):
835 835 data = util.posixfile(abs, b'rb').read()
836 836 conf.parse(
837 837 abs, data, sections=sections, remap=remap, include=include
838 838 )
839 839 break
840 840
841 841 data = util.posixfile(mapfile, b'rb').read()
842 842 conf.parse(mapfile, data, remap={b'': b'templates'}, include=include)
843 843
844 844 cache = {}
845 845 tmap = {}
846 846 aliases = []
847 847
848 848 val = conf.get(b'templates', b'__base__')
849 849 if val and val[0] not in b"'\"":
850 850 # treat as a pointer to a base class for this style
851 851 path = util.normpath(os.path.join(base, val))
852 852
853 853 # fallback check in template paths
854 854 if not os.path.exists(path):
855 855 dir = templatedir()
856 856 if dir is not None:
857 857 p2 = util.normpath(os.path.join(dir, val))
858 858 if os.path.isfile(p2):
859 859 path = p2
860 860 else:
861 861 p3 = util.normpath(os.path.join(p2, b"map"))
862 862 if os.path.isfile(p3):
863 863 path = p3
864 864
865 865 cache, tmap, aliases = _readmapfile(path)
866 866
867 867 for key, val in conf[b'templates'].items():
868 868 if not val:
869 869 raise error.ParseError(
870 870 _(b'missing value'), conf.source(b'templates', key)
871 871 )
872 872 if val[0] in b"'\"":
873 873 if val[0] != val[-1]:
874 874 raise error.ParseError(
875 875 _(b'unmatched quotes'), conf.source(b'templates', key)
876 876 )
877 877 cache[key] = unquotestring(val)
878 878 elif key != b'__base__':
879 879 tmap[key] = os.path.join(base, val)
880 880 aliases.extend(conf[b'templatealias'].items())
881 881 return cache, tmap, aliases
882 882
883 883
884 884 class loader(object):
885 885 """Load template fragments optionally from a map file"""
886 886
887 887 def __init__(self, cache, aliases):
888 888 if cache is None:
889 889 cache = {}
890 890 self.cache = cache.copy()
891 891 self._map = {}
892 892 self._aliasmap = _aliasrules.buildmap(aliases)
893 893
894 894 def __contains__(self, key):
895 895 return key in self.cache or key in self._map
896 896
897 897 def load(self, t):
898 898 """Get parsed tree for the given template name. Use a local cache."""
899 899 if t not in self.cache:
900 900 try:
901 901 self.cache[t] = util.readfile(self._map[t])
902 902 except KeyError as inst:
903 903 raise templateutil.TemplateNotFound(
904 904 _(b'"%s" not in template map') % inst.args[0]
905 905 )
906 906 except IOError as inst:
907 907 reason = _(b'template file %s: %s') % (
908 908 self._map[t],
909 909 stringutil.forcebytestr(inst.args[1]),
910 910 )
911 911 raise IOError(inst.args[0], encoding.strfromlocal(reason))
912 912 return self._parse(self.cache[t])
913 913
914 914 def _parse(self, tmpl):
915 915 x = parse(tmpl)
916 916 if self._aliasmap:
917 917 x = _aliasrules.expand(self._aliasmap, x)
918 918 return x
919 919
920 920 def _findsymbolsused(self, tree, syms):
921 921 if not tree:
922 922 return
923 923 op = tree[0]
924 924 if op == b'symbol':
925 925 s = tree[1]
926 926 if s in syms[0]:
927 927 return # avoid recursion: s -> cache[s] -> s
928 928 syms[0].add(s)
929 929 if s in self.cache or s in self._map:
930 930 # s may be a reference for named template
931 931 self._findsymbolsused(self.load(s), syms)
932 932 return
933 933 if op in {b'integer', b'string'}:
934 934 return
935 935 # '{arg|func}' == '{func(arg)}'
936 936 if op == b'|':
937 937 syms[1].add(getsymbol(tree[2]))
938 938 self._findsymbolsused(tree[1], syms)
939 939 return
940 940 if op == b'func':
941 941 syms[1].add(getsymbol(tree[1]))
942 942 self._findsymbolsused(tree[2], syms)
943 943 return
944 944 for x in tree[1:]:
945 945 self._findsymbolsused(x, syms)
946 946
947 947 def symbolsused(self, t):
948 948 """Look up (keywords, filters/functions) referenced from the name
949 949 template 't'
950 950
951 951 This may load additional templates from the map file.
952 952 """
953 953 syms = (set(), set())
954 954 self._findsymbolsused(self.load(t), syms)
955 955 return syms
956 956
957 957
958 958 class templater(object):
959 959 def __init__(
960 960 self,
961 961 filters=None,
962 962 defaults=None,
963 963 resources=None,
964 964 cache=None,
965 965 aliases=(),
966 966 minchunk=1024,
967 967 maxchunk=65536,
968 968 ):
969 969 """Create template engine optionally with preloaded template fragments
970 970
971 971 - ``filters``: a dict of functions to transform a value into another.
972 972 - ``defaults``: a dict of symbol values/functions; may be overridden
973 973 by a ``mapping`` dict.
974 974 - ``resources``: a resourcemapper object to look up internal data
975 975 (e.g. cache), inaccessible from user template.
976 976 - ``cache``: a dict of preloaded template fragments.
977 977 - ``aliases``: a list of alias (name, replacement) pairs.
978 978
979 979 self.cache may be updated later to register additional template
980 980 fragments.
981 981 """
982 982 allfilters = templatefilters.filters.copy()
983 983 if filters:
984 984 allfilters.update(filters)
985 985 self._loader = loader(cache, aliases)
986 986 self._proc = engine(self._loader.load, allfilters, defaults, resources)
987 987 self._minchunk, self._maxchunk = minchunk, maxchunk
988 988
989 989 @classmethod
990 990 def frommapfile(
991 991 cls,
992 992 mapfile,
993 993 filters=None,
994 994 defaults=None,
995 995 resources=None,
996 996 cache=None,
997 997 minchunk=1024,
998 998 maxchunk=65536,
999 999 ):
1000 1000 """Create templater from the specified map file"""
1001 1001 t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk)
1002 1002 cache, tmap, aliases = _readmapfile(mapfile)
1003 1003 t._loader.cache.update(cache)
1004 1004 t._loader._map = tmap
1005 1005 t._loader._aliasmap = _aliasrules.buildmap(aliases)
1006 1006 return t
1007 1007
1008 1008 def __contains__(self, key):
1009 1009 return key in self._loader
1010 1010
1011 1011 @property
1012 1012 def cache(self):
1013 1013 return self._loader.cache
1014 1014
1015 1015 # for highlight extension to insert one-time 'colorize' filter
1016 1016 @property
1017 1017 def _filters(self):
1018 1018 return self._proc._filters
1019 1019
1020 1020 @property
1021 1021 def defaults(self):
1022 1022 return self._proc._defaults
1023 1023
1024 1024 def load(self, t):
1025 1025 """Get parsed tree for the given template name. Use a local cache."""
1026 1026 return self._loader.load(t)
1027 1027
1028 1028 def symbolsuseddefault(self):
1029 1029 """Look up (keywords, filters/functions) referenced from the default
1030 1030 unnamed template
1031 1031
1032 1032 This may load additional templates from the map file.
1033 1033 """
1034 1034 return self.symbolsused(b'')
1035 1035
1036 1036 def symbolsused(self, t):
1037 1037 """Look up (keywords, filters/functions) referenced from the name
1038 1038 template 't'
1039 1039
1040 1040 This may load additional templates from the map file.
1041 1041 """
1042 1042 return self._loader.symbolsused(t)
1043 1043
1044 1044 def renderdefault(self, mapping):
1045 1045 """Render the default unnamed template and return result as string"""
1046 1046 return self.render(b'', mapping)
1047 1047
1048 1048 def render(self, t, mapping):
1049 1049 """Render the specified named template and return result as string"""
1050 1050 return b''.join(self.generate(t, mapping))
1051 1051
1052 1052 def generate(self, t, mapping):
1053 1053 """Return a generator that renders the specified named template and
1054 1054 yields chunks"""
1055 1055 stream = self._proc.process(t, mapping)
1056 1056 if self._minchunk:
1057 1057 stream = util.increasingchunks(
1058 1058 stream, min=self._minchunk, max=self._maxchunk
1059 1059 )
1060 1060 return stream
1061 1061
1062 1062
1063 1063 def templatedir():
1064 1064 '''return the directory used for template files, or None.'''
1065 1065 path = os.path.normpath(os.path.join(resourceutil.datapath, b'templates'))
1066 1066 return path if os.path.isdir(path) else None
1067 1067
1068 1068
1069 1069 def templatepath(name):
1070 1070 '''return location of template file. returns None if not found.'''
1071 1071 dir = templatedir()
1072 1072 if dir is None:
1073 1073 return None
1074 1074 f = os.path.join(templatedir(), name)
1075 if f and os.path.exists(f):
1075 if f and os.path.isfile(f):
1076 1076 return f
1077 1077 return None
1078 1078
1079 1079
1080 1080 def stylemap(styles, path=None):
1081 1081 """Return path to mapfile for a given style.
1082 1082
1083 1083 Searches mapfile in the following locations:
1084 1084 1. templatepath/style/map
1085 1085 2. templatepath/map-style
1086 1086 3. templatepath/map
1087 1087 """
1088 1088
1089 1089 if path is None:
1090 1090 path = templatedir()
1091 1091
1092 1092 if isinstance(styles, bytes):
1093 1093 styles = [styles]
1094 1094
1095 1095 if path is not None:
1096 1096 for style in styles:
1097 1097 # only plain name is allowed to honor template paths
1098 1098 if (
1099 1099 not style
1100 1100 or style in (pycompat.oscurdir, pycompat.ospardir)
1101 1101 or pycompat.ossep in style
1102 1102 or pycompat.osaltsep
1103 1103 and pycompat.osaltsep in style
1104 1104 ):
1105 1105 continue
1106 1106 locations = [os.path.join(style, b'map'), b'map-' + style]
1107 1107 locations.append(b'map')
1108 1108
1109 1109 for location in locations:
1110 1110 mapfile = os.path.join(path, location)
1111 1111 if os.path.isfile(mapfile):
1112 1112 return style, mapfile
1113 1113
1114 1114 raise RuntimeError(b"No hgweb templates found in %r" % path)
@@ -1,1992 +1,1993 b''
1 1 Test template map files and styles
2 2 ==================================
3 3
4 4 $ hg init a
5 5 $ cd a
6 6 $ echo a > a
7 7 $ hg add a
8 8 $ echo line 1 > b
9 9 $ echo line 2 >> b
10 10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
11 11
12 12 $ hg add b
13 13 $ echo other 1 > c
14 14 $ echo other 2 >> c
15 15 $ echo >> c
16 16 $ echo other 3 >> c
17 17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
18 18
19 19 $ hg add c
20 20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
21 21 $ echo c >> c
22 22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
23 23
24 24 $ echo foo > .hg/branch
25 25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
26 26
27 27 $ hg co -q 3
28 28 $ echo other 4 >> d
29 29 $ hg add d
30 30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
31 31
32 32 $ hg merge -q foo
33 33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
34 34
35 35 Second branch starting at nullrev:
36 36
37 37 $ hg update null
38 38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
39 39 $ echo second > second
40 40 $ hg add second
41 41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
42 42 created new head
43 43
44 44 $ echo third > third
45 45 $ hg add third
46 46 $ hg mv second fourth
47 47 $ hg commit -m third -d "2020-01-01 10:01"
48 48
49 49 Make sure user/global hgrc does not affect tests
50 50
51 51 $ echo '[ui]' > .hg/hgrc
52 52 $ echo 'logtemplate =' >> .hg/hgrc
53 53 $ echo 'style =' >> .hg/hgrc
54 54
55 55 Add some simple styles to settings
56 56
57 57 $ cat <<'EOF' >> .hg/hgrc
58 58 > [templates]
59 59 > simple = "{rev}\n"
60 60 > simple2 = {rev}\n
61 61 > rev = "should not precede {rev} keyword\n"
62 62 > EOF
63 63
64 64 $ hg log -l1 -Tsimple
65 65 8
66 66 $ hg log -l1 -Tsimple2
67 67 8
68 68 $ hg log -l1 -Trev
69 69 should not precede 8 keyword
70 70 $ hg log -l1 -T '{simple}'
71 71 8
72 72
73 73 Map file shouldn't see user templates:
74 74
75 75 $ cat <<EOF > tmpl
76 76 > changeset = 'nothing expanded:{simple}\n'
77 77 > EOF
78 78 $ hg log -l1 --style ./tmpl
79 79 nothing expanded:
80 80
81 81 Test templates and style maps in files:
82 82
83 83 $ echo "{rev}" > tmpl
84 84 $ hg log -l1 -T./tmpl
85 85 8
86 86 $ hg log -l1 -Tblah/blah
87 87 blah/blah (no-eol)
88 88
89 89 $ printf 'changeset = "{rev}\\n"\n' > map-simple
90 90 $ hg log -l1 -T./map-simple
91 91 8
92 92
93 93 a map file may have [templates] and [templatealias] sections:
94 94
95 95 $ cat <<'EOF' > map-simple
96 96 > [templates]
97 97 > changeset = "{a}\n"
98 98 > [templatealias]
99 99 > a = rev
100 100 > EOF
101 101 $ hg log -l1 -T./map-simple
102 102 8
103 103
104 104 so it can be included in hgrc
105 105
106 106 $ cat <<EOF > myhgrc
107 107 > %include $HGRCPATH
108 108 > %include map-simple
109 109 > [templates]
110 110 > foo = "{changeset}"
111 111 > EOF
112 112 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
113 113 8
114 114 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
115 115 8
116 116
117 117 Test template map inheritance
118 118
119 119 $ echo "__base__ = map-cmdline.default" > map-simple
120 120 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
121 121 $ hg log -l1 -T./map-simple
122 122 changeset: ***8***
123 123 tag: tip
124 124 user: test
125 125 date: Wed Jan 01 10:01:00 2020 +0000
126 126 summary: third
127 127
128 128 Test map inheritance with non-existent base
129 129
130 130 $ echo "__base__ = non-existent" > map-base-nonexistent
131 131 $ hg log -l1 -T./map-base-nonexistent
132 132 abort: style '$TESTTMP/a/non-existent' not found
133 133 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
134 134 [255]
135 135
136 136 Test map inheritance with directory as base
137 137
138 138 $ mkdir somedir
139 139 $ echo "__base__ = somedir" > map-base-dir
140 140 $ hg log -l1 -T./map-base-dir
141 141 abort: Is a directory: '$TESTTMP/a/somedir'
142 142 [255]
143 143
144 144 Test including a built-in template map
145 145
146 146 $ cat <<'EOF' > map-include-builtin
147 147 > %include map-cmdline.default
148 148 > [templates]
149 149 > changeset = "{changeset_quiet}\n"
150 150 > EOF
151 151 $ hg log -l1 -T./map-include-builtin
152 152 8:95c24699272e
153 153
154 154
155 155 Test including a nonexistent template map
156 156 BROKEN: This should probably be an error just like the bad __base__ above
157 157
158 158 $ cat <<'EOF' > map-include-nonexistent
159 159 > %include nonexistent
160 160 > [templates]
161 161 > changeset = "test\n"
162 162 > EOF
163 163 $ hg log -l1 -T./map-include-nonexistent
164 164 test
165 165
166 166 Test including a directory as template map
167 167 BROKEN: This should probably be an error just like the bad __base__ above
168 168
169 169 $ cat <<'EOF' > map-include-dir
170 170 > %include somedir
171 171 > [templates]
172 172 > changeset = "test\n"
173 173 > EOF
174 174 $ hg log -l1 -T./map-include-dir
175 175 test
176 176
177 177 Test docheader, docfooter and separator in template map
178 178
179 179 $ cat <<'EOF' > map-myjson
180 180 > docheader = '\{\n'
181 181 > docfooter = '\n}\n'
182 182 > separator = ',\n'
183 183 > changeset = ' {dict(rev, node|short)|json}'
184 184 > EOF
185 185 $ hg log -l2 -T./map-myjson
186 186 {
187 187 {"node": "95c24699272e", "rev": 8},
188 188 {"node": "29114dbae42b", "rev": 7}
189 189 }
190 190
191 191 Test docheader, docfooter and separator in [templates] section
192 192
193 193 $ cat <<'EOF' >> .hg/hgrc
194 194 > [templates]
195 195 > myjson = ' {dict(rev, node|short)|json}'
196 196 > myjson:docheader = '\{\n'
197 197 > myjson:docfooter = '\n}\n'
198 198 > myjson:separator = ',\n'
199 199 > :docheader = 'should not be selected as a docheader for literal templates\n'
200 200 > EOF
201 201 $ hg log -l2 -Tmyjson
202 202 {
203 203 {"node": "95c24699272e", "rev": 8},
204 204 {"node": "29114dbae42b", "rev": 7}
205 205 }
206 206 $ hg log -l1 -T'{rev}\n'
207 207 8
208 208
209 209 Template should precede style option
210 210
211 211 $ hg log -l1 --style default -T '{rev}\n'
212 212 8
213 213
214 214 Add a commit with empty description, to ensure that the templates
215 215 below will omit the description line.
216 216
217 217 $ echo c >> c
218 218 $ hg add c
219 219 $ hg commit -qm ' '
220 220
221 221 Default style is like normal output. Phases style should be the same
222 222 as default style, except for extra phase lines.
223 223
224 224 $ hg log > log.out
225 225 $ hg log --style default > style.out
226 226 $ cmp log.out style.out || diff -u log.out style.out
227 227 $ hg log -T phases > phases.out
228 228 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
229 229 +phase: draft
230 230 +phase: draft
231 231 +phase: draft
232 232 +phase: draft
233 233 +phase: draft
234 234 +phase: draft
235 235 +phase: draft
236 236 +phase: draft
237 237 +phase: draft
238 238 +phase: draft
239 239
240 240 $ hg log -v > log.out
241 241 $ hg log -v --style default > style.out
242 242 $ cmp log.out style.out || diff -u log.out style.out
243 243 $ hg log -v -T phases > phases.out
244 244 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
245 245 +phase: draft
246 246 +phase: draft
247 247 +phase: draft
248 248 +phase: draft
249 249 +phase: draft
250 250 +phase: draft
251 251 +phase: draft
252 252 +phase: draft
253 253 +phase: draft
254 254 +phase: draft
255 255
256 256 $ hg log -q > log.out
257 257 $ hg log -q --style default > style.out
258 258 $ cmp log.out style.out || diff -u log.out style.out
259 259 $ hg log -q -T phases > phases.out
260 260 $ cmp log.out phases.out || diff -u log.out phases.out
261 261
262 262 $ hg log --debug > log.out
263 263 $ hg log --debug --style default > style.out
264 264 $ cmp log.out style.out || diff -u log.out style.out
265 265 $ hg log --debug -T phases > phases.out
266 266 $ cmp log.out phases.out || diff -u log.out phases.out
267 267
268 268 Default style of working-directory revision should also be the same (but
269 269 date may change while running tests):
270 270
271 271 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
272 272 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
273 273 $ cmp log.out style.out || diff -u log.out style.out
274 274
275 275 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
276 276 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
277 277 $ cmp log.out style.out || diff -u log.out style.out
278 278
279 279 $ hg log -r 'wdir()' -q > log.out
280 280 $ hg log -r 'wdir()' -q --style default > style.out
281 281 $ cmp log.out style.out || diff -u log.out style.out
282 282
283 283 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
284 284 $ hg log -r 'wdir()' --debug --style default \
285 285 > | sed 's|^date:.*|date:|' > style.out
286 286 $ cmp log.out style.out || diff -u log.out style.out
287 287
288 288 Default style should also preserve color information (issue2866):
289 289
290 290 $ cp $HGRCPATH $HGRCPATH-bak
291 291 $ cat <<EOF >> $HGRCPATH
292 292 > [extensions]
293 293 > color=
294 294 > EOF
295 295
296 296 $ hg --color=debug log > log.out
297 297 $ hg --color=debug log --style default > style.out
298 298 $ cmp log.out style.out || diff -u log.out style.out
299 299 $ hg --color=debug log -T phases > phases.out
300 300 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
301 301 +[log.phase|phase: draft]
302 302 +[log.phase|phase: draft]
303 303 +[log.phase|phase: draft]
304 304 +[log.phase|phase: draft]
305 305 +[log.phase|phase: draft]
306 306 +[log.phase|phase: draft]
307 307 +[log.phase|phase: draft]
308 308 +[log.phase|phase: draft]
309 309 +[log.phase|phase: draft]
310 310 +[log.phase|phase: draft]
311 311
312 312 $ hg --color=debug -v log > log.out
313 313 $ hg --color=debug -v log --style default > style.out
314 314 $ cmp log.out style.out || diff -u log.out style.out
315 315 $ hg --color=debug -v log -T phases > phases.out
316 316 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
317 317 +[log.phase|phase: draft]
318 318 +[log.phase|phase: draft]
319 319 +[log.phase|phase: draft]
320 320 +[log.phase|phase: draft]
321 321 +[log.phase|phase: draft]
322 322 +[log.phase|phase: draft]
323 323 +[log.phase|phase: draft]
324 324 +[log.phase|phase: draft]
325 325 +[log.phase|phase: draft]
326 326 +[log.phase|phase: draft]
327 327
328 328 $ hg --color=debug -q log > log.out
329 329 $ hg --color=debug -q log --style default > style.out
330 330 $ cmp log.out style.out || diff -u log.out style.out
331 331 $ hg --color=debug -q log -T phases > phases.out
332 332 $ cmp log.out phases.out || diff -u log.out phases.out
333 333
334 334 $ hg --color=debug --debug log > log.out
335 335 $ hg --color=debug --debug log --style default > style.out
336 336 $ cmp log.out style.out || diff -u log.out style.out
337 337 $ hg --color=debug --debug log -T phases > phases.out
338 338 $ cmp log.out phases.out || diff -u log.out phases.out
339 339
340 340 $ mv $HGRCPATH-bak $HGRCPATH
341 341
342 342 Remove commit with empty commit message, so as to not pollute further
343 343 tests.
344 344
345 345 $ hg --config extensions.strip= strip -q .
346 346
347 347 Revision with no copies (used to print a traceback):
348 348
349 349 $ hg tip -v --template '\n'
350 350
351 351
352 352 Compact style works:
353 353
354 354 $ hg log -Tcompact
355 355 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
356 356 third
357 357
358 358 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
359 359 second
360 360
361 361 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
362 362 merge
363 363
364 364 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
365 365 new head
366 366
367 367 4 bbe44766e73d 1970-01-17 04:53 +0000 person
368 368 new branch
369 369
370 370 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
371 371 no user, no domain
372 372
373 373 2 97054abb4ab8 1970-01-14 21:20 +0000 other
374 374 no person
375 375
376 376 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
377 377 other 1
378 378
379 379 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
380 380 line 1
381 381
382 382
383 383 $ hg log -v --style compact
384 384 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
385 385 third
386 386
387 387 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
388 388 second
389 389
390 390 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
391 391 merge
392 392
393 393 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
394 394 new head
395 395
396 396 4 bbe44766e73d 1970-01-17 04:53 +0000 person
397 397 new branch
398 398
399 399 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
400 400 no user, no domain
401 401
402 402 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
403 403 no person
404 404
405 405 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
406 406 other 1
407 407 other 2
408 408
409 409 other 3
410 410
411 411 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
412 412 line 1
413 413 line 2
414 414
415 415
416 416 $ hg log --debug --style compact
417 417 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
418 418 third
419 419
420 420 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
421 421 second
422 422
423 423 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
424 424 merge
425 425
426 426 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
427 427 new head
428 428
429 429 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
430 430 new branch
431 431
432 432 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
433 433 no user, no domain
434 434
435 435 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
436 436 no person
437 437
438 438 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
439 439 other 1
440 440 other 2
441 441
442 442 other 3
443 443
444 444 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
445 445 line 1
446 446 line 2
447 447
448 448
449 449 Test xml styles:
450 450
451 451 $ hg log --style xml -r 'not all()'
452 452 <?xml version="1.0"?>
453 453 <log>
454 454 </log>
455 455
456 456 $ hg log --style xml
457 457 <?xml version="1.0"?>
458 458 <log>
459 459 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
460 460 <tag>tip</tag>
461 461 <author email="test">test</author>
462 462 <date>2020-01-01T10:01:00+00:00</date>
463 463 <msg xml:space="preserve">third</msg>
464 464 </logentry>
465 465 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
466 466 <parent revision="-1" node="0000000000000000000000000000000000000000" />
467 467 <author email="user@hostname">User Name</author>
468 468 <date>1970-01-12T13:46:40+00:00</date>
469 469 <msg xml:space="preserve">second</msg>
470 470 </logentry>
471 471 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
472 472 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
473 473 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
474 474 <author email="person">person</author>
475 475 <date>1970-01-18T08:40:01+00:00</date>
476 476 <msg xml:space="preserve">merge</msg>
477 477 </logentry>
478 478 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
479 479 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
480 480 <author email="person">person</author>
481 481 <date>1970-01-18T08:40:00+00:00</date>
482 482 <msg xml:space="preserve">new head</msg>
483 483 </logentry>
484 484 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
485 485 <branch>foo</branch>
486 486 <author email="person">person</author>
487 487 <date>1970-01-17T04:53:20+00:00</date>
488 488 <msg xml:space="preserve">new branch</msg>
489 489 </logentry>
490 490 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
491 491 <author email="person">person</author>
492 492 <date>1970-01-16T01:06:40+00:00</date>
493 493 <msg xml:space="preserve">no user, no domain</msg>
494 494 </logentry>
495 495 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
496 496 <author email="other@place">other</author>
497 497 <date>1970-01-14T21:20:00+00:00</date>
498 498 <msg xml:space="preserve">no person</msg>
499 499 </logentry>
500 500 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
501 501 <author email="other@place">A. N. Other</author>
502 502 <date>1970-01-13T17:33:20+00:00</date>
503 503 <msg xml:space="preserve">other 1
504 504 other 2
505 505
506 506 other 3</msg>
507 507 </logentry>
508 508 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
509 509 <author email="user@hostname">User Name</author>
510 510 <date>1970-01-12T13:46:40+00:00</date>
511 511 <msg xml:space="preserve">line 1
512 512 line 2</msg>
513 513 </logentry>
514 514 </log>
515 515
516 516 $ hg log -v --style xml
517 517 <?xml version="1.0"?>
518 518 <log>
519 519 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
520 520 <tag>tip</tag>
521 521 <author email="test">test</author>
522 522 <date>2020-01-01T10:01:00+00:00</date>
523 523 <msg xml:space="preserve">third</msg>
524 524 <paths>
525 525 <path action="A">fourth</path>
526 526 <path action="A">third</path>
527 527 <path action="R">second</path>
528 528 </paths>
529 529 <copies>
530 530 <copy source="second">fourth</copy>
531 531 </copies>
532 532 </logentry>
533 533 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
534 534 <parent revision="-1" node="0000000000000000000000000000000000000000" />
535 535 <author email="user@hostname">User Name</author>
536 536 <date>1970-01-12T13:46:40+00:00</date>
537 537 <msg xml:space="preserve">second</msg>
538 538 <paths>
539 539 <path action="A">second</path>
540 540 </paths>
541 541 </logentry>
542 542 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
543 543 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
544 544 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
545 545 <author email="person">person</author>
546 546 <date>1970-01-18T08:40:01+00:00</date>
547 547 <msg xml:space="preserve">merge</msg>
548 548 <paths>
549 549 </paths>
550 550 </logentry>
551 551 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
552 552 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
553 553 <author email="person">person</author>
554 554 <date>1970-01-18T08:40:00+00:00</date>
555 555 <msg xml:space="preserve">new head</msg>
556 556 <paths>
557 557 <path action="A">d</path>
558 558 </paths>
559 559 </logentry>
560 560 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
561 561 <branch>foo</branch>
562 562 <author email="person">person</author>
563 563 <date>1970-01-17T04:53:20+00:00</date>
564 564 <msg xml:space="preserve">new branch</msg>
565 565 <paths>
566 566 </paths>
567 567 </logentry>
568 568 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
569 569 <author email="person">person</author>
570 570 <date>1970-01-16T01:06:40+00:00</date>
571 571 <msg xml:space="preserve">no user, no domain</msg>
572 572 <paths>
573 573 <path action="M">c</path>
574 574 </paths>
575 575 </logentry>
576 576 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
577 577 <author email="other@place">other</author>
578 578 <date>1970-01-14T21:20:00+00:00</date>
579 579 <msg xml:space="preserve">no person</msg>
580 580 <paths>
581 581 <path action="A">c</path>
582 582 </paths>
583 583 </logentry>
584 584 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
585 585 <author email="other@place">A. N. Other</author>
586 586 <date>1970-01-13T17:33:20+00:00</date>
587 587 <msg xml:space="preserve">other 1
588 588 other 2
589 589
590 590 other 3</msg>
591 591 <paths>
592 592 <path action="A">b</path>
593 593 </paths>
594 594 </logentry>
595 595 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
596 596 <author email="user@hostname">User Name</author>
597 597 <date>1970-01-12T13:46:40+00:00</date>
598 598 <msg xml:space="preserve">line 1
599 599 line 2</msg>
600 600 <paths>
601 601 <path action="A">a</path>
602 602 </paths>
603 603 </logentry>
604 604 </log>
605 605
606 606 $ hg log --debug --style xml
607 607 <?xml version="1.0"?>
608 608 <log>
609 609 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
610 610 <tag>tip</tag>
611 611 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
612 612 <parent revision="-1" node="0000000000000000000000000000000000000000" />
613 613 <author email="test">test</author>
614 614 <date>2020-01-01T10:01:00+00:00</date>
615 615 <msg xml:space="preserve">third</msg>
616 616 <paths>
617 617 <path action="A">fourth</path>
618 618 <path action="A">third</path>
619 619 <path action="R">second</path>
620 620 </paths>
621 621 <copies>
622 622 <copy source="second">fourth</copy>
623 623 </copies>
624 624 <extra key="branch">default</extra>
625 625 </logentry>
626 626 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
627 627 <parent revision="-1" node="0000000000000000000000000000000000000000" />
628 628 <parent revision="-1" node="0000000000000000000000000000000000000000" />
629 629 <author email="user@hostname">User Name</author>
630 630 <date>1970-01-12T13:46:40+00:00</date>
631 631 <msg xml:space="preserve">second</msg>
632 632 <paths>
633 633 <path action="A">second</path>
634 634 </paths>
635 635 <extra key="branch">default</extra>
636 636 </logentry>
637 637 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
638 638 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
639 639 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
640 640 <author email="person">person</author>
641 641 <date>1970-01-18T08:40:01+00:00</date>
642 642 <msg xml:space="preserve">merge</msg>
643 643 <paths>
644 644 </paths>
645 645 <extra key="branch">default</extra>
646 646 </logentry>
647 647 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
648 648 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
649 649 <parent revision="-1" node="0000000000000000000000000000000000000000" />
650 650 <author email="person">person</author>
651 651 <date>1970-01-18T08:40:00+00:00</date>
652 652 <msg xml:space="preserve">new head</msg>
653 653 <paths>
654 654 <path action="A">d</path>
655 655 </paths>
656 656 <extra key="branch">default</extra>
657 657 </logentry>
658 658 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
659 659 <branch>foo</branch>
660 660 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
661 661 <parent revision="-1" node="0000000000000000000000000000000000000000" />
662 662 <author email="person">person</author>
663 663 <date>1970-01-17T04:53:20+00:00</date>
664 664 <msg xml:space="preserve">new branch</msg>
665 665 <paths>
666 666 </paths>
667 667 <extra key="branch">foo</extra>
668 668 </logentry>
669 669 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
670 670 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
671 671 <parent revision="-1" node="0000000000000000000000000000000000000000" />
672 672 <author email="person">person</author>
673 673 <date>1970-01-16T01:06:40+00:00</date>
674 674 <msg xml:space="preserve">no user, no domain</msg>
675 675 <paths>
676 676 <path action="M">c</path>
677 677 </paths>
678 678 <extra key="branch">default</extra>
679 679 </logentry>
680 680 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
681 681 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
682 682 <parent revision="-1" node="0000000000000000000000000000000000000000" />
683 683 <author email="other@place">other</author>
684 684 <date>1970-01-14T21:20:00+00:00</date>
685 685 <msg xml:space="preserve">no person</msg>
686 686 <paths>
687 687 <path action="A">c</path>
688 688 </paths>
689 689 <extra key="branch">default</extra>
690 690 </logentry>
691 691 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
692 692 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
693 693 <parent revision="-1" node="0000000000000000000000000000000000000000" />
694 694 <author email="other@place">A. N. Other</author>
695 695 <date>1970-01-13T17:33:20+00:00</date>
696 696 <msg xml:space="preserve">other 1
697 697 other 2
698 698
699 699 other 3</msg>
700 700 <paths>
701 701 <path action="A">b</path>
702 702 </paths>
703 703 <extra key="branch">default</extra>
704 704 </logentry>
705 705 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
706 706 <parent revision="-1" node="0000000000000000000000000000000000000000" />
707 707 <parent revision="-1" node="0000000000000000000000000000000000000000" />
708 708 <author email="user@hostname">User Name</author>
709 709 <date>1970-01-12T13:46:40+00:00</date>
710 710 <msg xml:space="preserve">line 1
711 711 line 2</msg>
712 712 <paths>
713 713 <path action="A">a</path>
714 714 </paths>
715 715 <extra key="branch">default</extra>
716 716 </logentry>
717 717 </log>
718 718
719 719
720 720 test CBOR style:
721 721
722 722 $ cat <<'EOF' > "$TESTTMP/decodecborarray.py"
723 723 > from __future__ import absolute_import
724 724 > from mercurial import (
725 725 > dispatch,
726 726 > )
727 727 > from mercurial.utils import (
728 728 > cborutil,
729 729 > procutil,
730 730 > stringutil,
731 731 > )
732 732 > dispatch.initstdio()
733 733 > data = procutil.stdin.read()
734 734 > # our CBOR decoder doesn't support parsing indefinite-length arrays,
735 735 > # but the log output is indefinite stream by nature.
736 736 > assert data[:1] == cborutil.BEGIN_INDEFINITE_ARRAY
737 737 > assert data[-1:] == cborutil.BREAK
738 738 > items = cborutil.decodeall(data[1:-1])
739 739 > procutil.stdout.write(stringutil.pprint(items, indent=1) + b'\n')
740 740 > EOF
741 741
742 742 $ hg log -k nosuch -Tcbor | "$PYTHON" "$TESTTMP/decodecborarray.py"
743 743 []
744 744
745 745 $ hg log -qr0:1 -Tcbor | "$PYTHON" "$TESTTMP/decodecborarray.py"
746 746 [
747 747 {
748 748 'node': '1e4e1b8f71e05681d422154f5421e385fec3454f',
749 749 'rev': 0
750 750 },
751 751 {
752 752 'node': 'b608e9d1a3f0273ccf70fb85fd6866b3482bf965',
753 753 'rev': 1
754 754 }
755 755 ]
756 756
757 757 $ hg log -vpr . -Tcbor --stat | "$PYTHON" "$TESTTMP/decodecborarray.py"
758 758 [
759 759 {
760 760 'bookmarks': [],
761 761 'branch': 'default',
762 762 'date': [
763 763 1577872860,
764 764 0
765 765 ],
766 766 'desc': 'third',
767 767 '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',
768 768 'diffstat': ' fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n',
769 769 'files': [
770 770 'fourth',
771 771 'second',
772 772 'third'
773 773 ],
774 774 'node': '95c24699272ef57d062b8bccc32c878bf841784a',
775 775 'parents': [
776 776 '29114dbae42b9f078cf2714dbe3a86bba8ec7453'
777 777 ],
778 778 'phase': 'draft',
779 779 'rev': 8,
780 780 'tags': [
781 781 'tip'
782 782 ],
783 783 'user': 'test'
784 784 }
785 785 ]
786 786
787 787 $ hg log -r . -T'cbor(rev, node|short)' | "$PYTHON" "$TESTTMP/decodecborarray.py"
788 788 [
789 789 {
790 790 'node': '95c24699272e',
791 791 'rev': 8
792 792 }
793 793 ]
794 794
795 795 $ hg log -r . -T'cbor()' | "$PYTHON" "$TESTTMP/decodecborarray.py"
796 796 [
797 797 {}
798 798 ]
799 799
800 800 Test JSON style:
801 801
802 802 $ hg log -k nosuch -Tjson
803 803 [
804 804 ]
805 805
806 806 $ hg log -qr . -Tjson
807 807 [
808 808 {
809 809 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
810 810 "rev": 8
811 811 }
812 812 ]
813 813
814 814 $ hg log -vpr . -Tjson --stat
815 815 [
816 816 {
817 817 "bookmarks": [],
818 818 "branch": "default",
819 819 "date": [1577872860, 0],
820 820 "desc": "third",
821 821 "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",
822 822 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
823 823 "files": ["fourth", "second", "third"],
824 824 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
825 825 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
826 826 "phase": "draft",
827 827 "rev": 8,
828 828 "tags": ["tip"],
829 829 "user": "test"
830 830 }
831 831 ]
832 832
833 833 honor --git but not format-breaking diffopts
834 834 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
835 835 [
836 836 {
837 837 "bookmarks": [],
838 838 "branch": "default",
839 839 "date": [1577872860, 0],
840 840 "desc": "third",
841 841 "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",
842 842 "files": ["fourth", "second", "third"],
843 843 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
844 844 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
845 845 "phase": "draft",
846 846 "rev": 8,
847 847 "tags": ["tip"],
848 848 "user": "test"
849 849 }
850 850 ]
851 851
852 852 $ hg log -T json
853 853 [
854 854 {
855 855 "bookmarks": [],
856 856 "branch": "default",
857 857 "date": [1577872860, 0],
858 858 "desc": "third",
859 859 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
860 860 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
861 861 "phase": "draft",
862 862 "rev": 8,
863 863 "tags": ["tip"],
864 864 "user": "test"
865 865 },
866 866 {
867 867 "bookmarks": [],
868 868 "branch": "default",
869 869 "date": [1000000, 0],
870 870 "desc": "second",
871 871 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
872 872 "parents": ["0000000000000000000000000000000000000000"],
873 873 "phase": "draft",
874 874 "rev": 7,
875 875 "tags": [],
876 876 "user": "User Name <user@hostname>"
877 877 },
878 878 {
879 879 "bookmarks": [],
880 880 "branch": "default",
881 881 "date": [1500001, 0],
882 882 "desc": "merge",
883 883 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
884 884 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
885 885 "phase": "draft",
886 886 "rev": 6,
887 887 "tags": [],
888 888 "user": "person"
889 889 },
890 890 {
891 891 "bookmarks": [],
892 892 "branch": "default",
893 893 "date": [1500000, 0],
894 894 "desc": "new head",
895 895 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
896 896 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
897 897 "phase": "draft",
898 898 "rev": 5,
899 899 "tags": [],
900 900 "user": "person"
901 901 },
902 902 {
903 903 "bookmarks": [],
904 904 "branch": "foo",
905 905 "date": [1400000, 0],
906 906 "desc": "new branch",
907 907 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
908 908 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
909 909 "phase": "draft",
910 910 "rev": 4,
911 911 "tags": [],
912 912 "user": "person"
913 913 },
914 914 {
915 915 "bookmarks": [],
916 916 "branch": "default",
917 917 "date": [1300000, 0],
918 918 "desc": "no user, no domain",
919 919 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
920 920 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
921 921 "phase": "draft",
922 922 "rev": 3,
923 923 "tags": [],
924 924 "user": "person"
925 925 },
926 926 {
927 927 "bookmarks": [],
928 928 "branch": "default",
929 929 "date": [1200000, 0],
930 930 "desc": "no person",
931 931 "node": "97054abb4ab824450e9164180baf491ae0078465",
932 932 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
933 933 "phase": "draft",
934 934 "rev": 2,
935 935 "tags": [],
936 936 "user": "other@place"
937 937 },
938 938 {
939 939 "bookmarks": [],
940 940 "branch": "default",
941 941 "date": [1100000, 0],
942 942 "desc": "other 1\nother 2\n\nother 3",
943 943 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
944 944 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
945 945 "phase": "draft",
946 946 "rev": 1,
947 947 "tags": [],
948 948 "user": "A. N. Other <other@place>"
949 949 },
950 950 {
951 951 "bookmarks": [],
952 952 "branch": "default",
953 953 "date": [1000000, 0],
954 954 "desc": "line 1\nline 2",
955 955 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
956 956 "parents": ["0000000000000000000000000000000000000000"],
957 957 "phase": "draft",
958 958 "rev": 0,
959 959 "tags": [],
960 960 "user": "User Name <user@hostname>"
961 961 }
962 962 ]
963 963
964 964 $ hg heads -v -Tjson
965 965 [
966 966 {
967 967 "bookmarks": [],
968 968 "branch": "default",
969 969 "date": [1577872860, 0],
970 970 "desc": "third",
971 971 "files": ["fourth", "second", "third"],
972 972 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
973 973 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
974 974 "phase": "draft",
975 975 "rev": 8,
976 976 "tags": ["tip"],
977 977 "user": "test"
978 978 },
979 979 {
980 980 "bookmarks": [],
981 981 "branch": "default",
982 982 "date": [1500001, 0],
983 983 "desc": "merge",
984 984 "files": [],
985 985 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
986 986 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
987 987 "phase": "draft",
988 988 "rev": 6,
989 989 "tags": [],
990 990 "user": "person"
991 991 },
992 992 {
993 993 "bookmarks": [],
994 994 "branch": "foo",
995 995 "date": [1400000, 0],
996 996 "desc": "new branch",
997 997 "files": [],
998 998 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
999 999 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1000 1000 "phase": "draft",
1001 1001 "rev": 4,
1002 1002 "tags": [],
1003 1003 "user": "person"
1004 1004 }
1005 1005 ]
1006 1006
1007 1007 $ hg log --debug -Tjson
1008 1008 [
1009 1009 {
1010 1010 "added": ["fourth", "third"],
1011 1011 "bookmarks": [],
1012 1012 "branch": "default",
1013 1013 "date": [1577872860, 0],
1014 1014 "desc": "third",
1015 1015 "extra": {"branch": "default"},
1016 1016 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1017 1017 "modified": [],
1018 1018 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1019 1019 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1020 1020 "phase": "draft",
1021 1021 "removed": ["second"],
1022 1022 "rev": 8,
1023 1023 "tags": ["tip"],
1024 1024 "user": "test"
1025 1025 },
1026 1026 {
1027 1027 "added": ["second"],
1028 1028 "bookmarks": [],
1029 1029 "branch": "default",
1030 1030 "date": [1000000, 0],
1031 1031 "desc": "second",
1032 1032 "extra": {"branch": "default"},
1033 1033 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1034 1034 "modified": [],
1035 1035 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1036 1036 "parents": ["0000000000000000000000000000000000000000"],
1037 1037 "phase": "draft",
1038 1038 "removed": [],
1039 1039 "rev": 7,
1040 1040 "tags": [],
1041 1041 "user": "User Name <user@hostname>"
1042 1042 },
1043 1043 {
1044 1044 "added": [],
1045 1045 "bookmarks": [],
1046 1046 "branch": "default",
1047 1047 "date": [1500001, 0],
1048 1048 "desc": "merge",
1049 1049 "extra": {"branch": "default"},
1050 1050 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1051 1051 "modified": [],
1052 1052 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1053 1053 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1054 1054 "phase": "draft",
1055 1055 "removed": [],
1056 1056 "rev": 6,
1057 1057 "tags": [],
1058 1058 "user": "person"
1059 1059 },
1060 1060 {
1061 1061 "added": ["d"],
1062 1062 "bookmarks": [],
1063 1063 "branch": "default",
1064 1064 "date": [1500000, 0],
1065 1065 "desc": "new head",
1066 1066 "extra": {"branch": "default"},
1067 1067 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1068 1068 "modified": [],
1069 1069 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1070 1070 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1071 1071 "phase": "draft",
1072 1072 "removed": [],
1073 1073 "rev": 5,
1074 1074 "tags": [],
1075 1075 "user": "person"
1076 1076 },
1077 1077 {
1078 1078 "added": [],
1079 1079 "bookmarks": [],
1080 1080 "branch": "foo",
1081 1081 "date": [1400000, 0],
1082 1082 "desc": "new branch",
1083 1083 "extra": {"branch": "foo"},
1084 1084 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1085 1085 "modified": [],
1086 1086 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1087 1087 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1088 1088 "phase": "draft",
1089 1089 "removed": [],
1090 1090 "rev": 4,
1091 1091 "tags": [],
1092 1092 "user": "person"
1093 1093 },
1094 1094 {
1095 1095 "added": [],
1096 1096 "bookmarks": [],
1097 1097 "branch": "default",
1098 1098 "date": [1300000, 0],
1099 1099 "desc": "no user, no domain",
1100 1100 "extra": {"branch": "default"},
1101 1101 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1102 1102 "modified": ["c"],
1103 1103 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1104 1104 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1105 1105 "phase": "draft",
1106 1106 "removed": [],
1107 1107 "rev": 3,
1108 1108 "tags": [],
1109 1109 "user": "person"
1110 1110 },
1111 1111 {
1112 1112 "added": ["c"],
1113 1113 "bookmarks": [],
1114 1114 "branch": "default",
1115 1115 "date": [1200000, 0],
1116 1116 "desc": "no person",
1117 1117 "extra": {"branch": "default"},
1118 1118 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1119 1119 "modified": [],
1120 1120 "node": "97054abb4ab824450e9164180baf491ae0078465",
1121 1121 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1122 1122 "phase": "draft",
1123 1123 "removed": [],
1124 1124 "rev": 2,
1125 1125 "tags": [],
1126 1126 "user": "other@place"
1127 1127 },
1128 1128 {
1129 1129 "added": ["b"],
1130 1130 "bookmarks": [],
1131 1131 "branch": "default",
1132 1132 "date": [1100000, 0],
1133 1133 "desc": "other 1\nother 2\n\nother 3",
1134 1134 "extra": {"branch": "default"},
1135 1135 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1136 1136 "modified": [],
1137 1137 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1138 1138 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1139 1139 "phase": "draft",
1140 1140 "removed": [],
1141 1141 "rev": 1,
1142 1142 "tags": [],
1143 1143 "user": "A. N. Other <other@place>"
1144 1144 },
1145 1145 {
1146 1146 "added": ["a"],
1147 1147 "bookmarks": [],
1148 1148 "branch": "default",
1149 1149 "date": [1000000, 0],
1150 1150 "desc": "line 1\nline 2",
1151 1151 "extra": {"branch": "default"},
1152 1152 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1153 1153 "modified": [],
1154 1154 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1155 1155 "parents": ["0000000000000000000000000000000000000000"],
1156 1156 "phase": "draft",
1157 1157 "removed": [],
1158 1158 "rev": 0,
1159 1159 "tags": [],
1160 1160 "user": "User Name <user@hostname>"
1161 1161 }
1162 1162 ]
1163 1163
1164 1164 $ hg log -l2 -T'json(rev, parents)'
1165 1165 [
1166 1166 {"parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], "rev": 8},
1167 1167 {"parents": ["0000000000000000000000000000000000000000"], "rev": 7}
1168 1168 ]
1169 1169
1170 1170 $ hg log -qr. -T'json(rev, parents)'
1171 1171 [
1172 1172 {"parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], "rev": 8}
1173 1173 ]
1174 1174
1175 1175 $ hg log -r. -T'json(diff)'
1176 1176 [
1177 1177 {"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"}
1178 1178 ]
1179 1179
1180 1180 $ hg log -r. -T'json(diffstat)'
1181 1181 [
1182 1182 {"diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n"}
1183 1183 ]
1184 1184
1185 1185 $ hg log -r. -T'json(manifest)'
1186 1186 [
1187 1187 {"manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64"}
1188 1188 ]
1189 1189
1190 1190 $ hg log -r. -T'json(extra)'
1191 1191 [
1192 1192 {"extra": {"branch": "default"}}
1193 1193 ]
1194 1194
1195 1195 $ hg log -r3 -T'json(modified)'
1196 1196 [
1197 1197 {"modified": ["c"]}
1198 1198 ]
1199 1199
1200 1200 $ hg log -r. -T'json(added)'
1201 1201 [
1202 1202 {"added": ["fourth", "third"]}
1203 1203 ]
1204 1204
1205 1205 $ hg log -r. -T'json(removed)'
1206 1206 [
1207 1207 {"removed": ["second"]}
1208 1208 ]
1209 1209
1210 1210 $ hg log -r. -T'json(files)'
1211 1211 [
1212 1212 {"files": ["fourth", "second", "third"]}
1213 1213 ]
1214 1214
1215 1215 --copies is the exception. copies dict is built only when --copies switch
1216 1216 is on:
1217 1217
1218 1218 $ hg log -r'.^:' -T'json(copies)' --copies
1219 1219 [
1220 1220 {"copies": {}},
1221 1221 {"copies": {"fourth": "second"}}
1222 1222 ]
1223 1223
1224 1224 $ hg log -r. -T'json()'
1225 1225 [
1226 1226 {}
1227 1227 ]
1228 1228
1229 1229 Other unsupported formatter styles:
1230 1230
1231 1231 $ hg log -qr . -Tpickle
1232 1232 abort: "pickle" not in template map
1233 1233 [255]
1234 1234 $ hg log -qr . -Tdebug
1235 1235 abort: "debug" not in template map
1236 1236 [255]
1237 1237
1238 1238 Unparsable function-style references:
1239 1239
1240 1240 $ hg log -qr . -T'json(-)'
1241 1241 hg: parse error at 6: not a prefix: )
1242 1242 (json(-)
1243 1243 ^ here)
1244 1244 [255]
1245 1245
1246 1246 For backward compatibility, the following examples are not parsed as
1247 1247 function-style references:
1248 1248
1249 1249 $ hg log -qr . -T'cbor(rev'
1250 1250 cbor(rev (no-eol)
1251 1251 $ hg log -qr . -T'json (rev)'
1252 1252 json (rev) (no-eol)
1253 1253 $ hg log -qr . -T'json(x="{rev}")'
1254 1254 json(x="8") (no-eol)
1255 1255
1256 1256 Error if style not readable:
1257 1257
1258 1258 #if unix-permissions no-root
1259 1259 $ touch q
1260 1260 $ chmod 0 q
1261 1261 $ hg log --style ./q
1262 1262 abort: Permission denied: './q'
1263 1263 [255]
1264 1264 #endif
1265 1265
1266 1266 Error if no style:
1267 1267
1268 1268 $ hg log --style notexist
1269 1269 abort: style 'notexist' not found
1270 1270 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1271 1271 [255]
1272 1272
1273 1273 $ hg log -T list
1274 1274 available styles: bisect, changelog, compact, default, phases, show, status, xml
1275 1275 abort: specify a template
1276 1276 [255]
1277 1277
1278 1278 Error if style is a directory:
1279 1279
1280 1280 $ hg log --style somedir
1281 1281 abort: Is a directory: 'somedir'
1282 1282 [255]
1283 1283
1284 1284 Error if style is a directory whose name is a built-in style:
1285 1285
1286 1286 $ hg log --style coal
1287 abort: Is a directory: '*/mercurial/templates/coal' (glob)
1287 abort: style 'coal' not found
1288 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1288 1289 [255]
1289 1290
1290 1291 Error if style missing key:
1291 1292
1292 1293 $ echo 'q = q' > t
1293 1294 $ hg log --style ./t
1294 1295 abort: "changeset" not in template map
1295 1296 [255]
1296 1297
1297 1298 Error if style missing value:
1298 1299
1299 1300 $ echo 'changeset =' > t
1300 1301 $ hg log --style t
1301 1302 hg: parse error at t:1: missing value
1302 1303 [255]
1303 1304
1304 1305 Error if include fails:
1305 1306
1306 1307 $ echo 'changeset = q' >> t
1307 1308 #if unix-permissions no-root
1308 1309 $ hg log --style ./t
1309 1310 abort: template file ./q: Permission denied
1310 1311 [255]
1311 1312 $ rm -f q
1312 1313 #endif
1313 1314
1314 1315 Include works:
1315 1316
1316 1317 $ echo '{rev}' > q
1317 1318 $ hg log --style ./t
1318 1319 8
1319 1320 7
1320 1321 6
1321 1322 5
1322 1323 4
1323 1324 3
1324 1325 2
1325 1326 1
1326 1327 0
1327 1328
1328 1329 $ hg phase -r 5 --public
1329 1330 $ hg phase -r 7 --secret --force
1330 1331
1331 1332 Missing non-standard names give no error (backward compatibility):
1332 1333
1333 1334 $ echo "changeset = '{c}'" > t
1334 1335 $ hg log --style ./t
1335 1336
1336 1337 Defining non-standard name works:
1337 1338
1338 1339 $ cat <<EOF > t
1339 1340 > changeset = '{c}'
1340 1341 > c = q
1341 1342 > EOF
1342 1343 $ hg log --style ./t
1343 1344 8
1344 1345 7
1345 1346 6
1346 1347 5
1347 1348 4
1348 1349 3
1349 1350 2
1350 1351 1
1351 1352 0
1352 1353
1353 1354 ui.style works:
1354 1355
1355 1356 $ echo '[ui]' > .hg/hgrc
1356 1357 $ echo 'style = t' >> .hg/hgrc
1357 1358 $ hg log
1358 1359 8
1359 1360 7
1360 1361 6
1361 1362 5
1362 1363 4
1363 1364 3
1364 1365 2
1365 1366 1
1366 1367 0
1367 1368
1368 1369 Issue338:
1369 1370
1370 1371 $ hg log --style=changelog > changelog
1371 1372
1372 1373 $ cat changelog
1373 1374 2020-01-01 test <test>
1374 1375
1375 1376 * fourth, second, third:
1376 1377 third
1377 1378 [95c24699272e] [tip]
1378 1379
1379 1380 1970-01-12 User Name <user@hostname>
1380 1381
1381 1382 * second:
1382 1383 second
1383 1384 [29114dbae42b]
1384 1385
1385 1386 1970-01-18 person <person>
1386 1387
1387 1388 * merge
1388 1389 [d41e714fe50d]
1389 1390
1390 1391 * d:
1391 1392 new head
1392 1393 [13207e5a10d9]
1393 1394
1394 1395 1970-01-17 person <person>
1395 1396
1396 1397 * new branch
1397 1398 [bbe44766e73d] <foo>
1398 1399
1399 1400 1970-01-16 person <person>
1400 1401
1401 1402 * c:
1402 1403 no user, no domain
1403 1404 [10e46f2dcbf4]
1404 1405
1405 1406 1970-01-14 other <other@place>
1406 1407
1407 1408 * c:
1408 1409 no person
1409 1410 [97054abb4ab8]
1410 1411
1411 1412 1970-01-13 A. N. Other <other@place>
1412 1413
1413 1414 * b:
1414 1415 other 1 other 2
1415 1416
1416 1417 other 3
1417 1418 [b608e9d1a3f0]
1418 1419
1419 1420 1970-01-12 User Name <user@hostname>
1420 1421
1421 1422 * a:
1422 1423 line 1 line 2
1423 1424 [1e4e1b8f71e0]
1424 1425
1425 1426
1426 1427 Issue2130: xml output for 'hg heads' is malformed
1427 1428
1428 1429 $ hg heads --style changelog
1429 1430 2020-01-01 test <test>
1430 1431
1431 1432 * fourth, second, third:
1432 1433 third
1433 1434 [95c24699272e] [tip]
1434 1435
1435 1436 1970-01-18 person <person>
1436 1437
1437 1438 * merge
1438 1439 [d41e714fe50d]
1439 1440
1440 1441 1970-01-17 person <person>
1441 1442
1442 1443 * new branch
1443 1444 [bbe44766e73d] <foo>
1444 1445
1445 1446
1446 1447 Add a dummy commit to make up for the instability of the above:
1447 1448
1448 1449 $ echo a > a
1449 1450 $ hg add a
1450 1451 $ hg ci -m future
1451 1452
1452 1453 Add a commit that does all possible modifications at once
1453 1454
1454 1455 $ echo modify >> third
1455 1456 $ touch b
1456 1457 $ hg add b
1457 1458 $ hg mv fourth fifth
1458 1459 $ hg rm a
1459 1460 $ hg ci -m "Modify, add, remove, rename"
1460 1461
1461 1462 Check the status template
1462 1463
1463 1464 $ cat <<EOF >> $HGRCPATH
1464 1465 > [extensions]
1465 1466 > color=
1466 1467 > EOF
1467 1468
1468 1469 $ hg log -T status -r 10
1469 1470 changeset: 10:0f9759ec227a
1470 1471 tag: tip
1471 1472 user: test
1472 1473 date: Thu Jan 01 00:00:00 1970 +0000
1473 1474 summary: Modify, add, remove, rename
1474 1475 files:
1475 1476 M third
1476 1477 A b
1477 1478 A fifth
1478 1479 R a
1479 1480 R fourth
1480 1481
1481 1482 $ hg log -T status -C -r 10
1482 1483 changeset: 10:0f9759ec227a
1483 1484 tag: tip
1484 1485 user: test
1485 1486 date: Thu Jan 01 00:00:00 1970 +0000
1486 1487 summary: Modify, add, remove, rename
1487 1488 files:
1488 1489 M third
1489 1490 A b
1490 1491 A fifth
1491 1492 fourth
1492 1493 R a
1493 1494 R fourth
1494 1495
1495 1496 $ hg log -T status -C -r 10 -v
1496 1497 changeset: 10:0f9759ec227a
1497 1498 tag: tip
1498 1499 user: test
1499 1500 date: Thu Jan 01 00:00:00 1970 +0000
1500 1501 description:
1501 1502 Modify, add, remove, rename
1502 1503
1503 1504 files:
1504 1505 M third
1505 1506 A b
1506 1507 A fifth
1507 1508 fourth
1508 1509 R a
1509 1510 R fourth
1510 1511
1511 1512 $ hg log -T status -C -r 10 --debug
1512 1513 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
1513 1514 tag: tip
1514 1515 phase: secret
1515 1516 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
1516 1517 parent: -1:0000000000000000000000000000000000000000
1517 1518 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
1518 1519 user: test
1519 1520 date: Thu Jan 01 00:00:00 1970 +0000
1520 1521 extra: branch=default
1521 1522 description:
1522 1523 Modify, add, remove, rename
1523 1524
1524 1525 files:
1525 1526 M third
1526 1527 A b
1527 1528 A fifth
1528 1529 fourth
1529 1530 R a
1530 1531 R fourth
1531 1532
1532 1533 $ hg log -T status -C -r 10 --quiet
1533 1534 10:0f9759ec227a
1534 1535 $ hg --color=debug log -T status -r 10
1535 1536 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1536 1537 [log.tag|tag: tip]
1537 1538 [log.user|user: test]
1538 1539 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1539 1540 [log.summary|summary: Modify, add, remove, rename]
1540 1541 [ui.note log.files|files:]
1541 1542 [status.modified|M third]
1542 1543 [status.added|A b]
1543 1544 [status.added|A fifth]
1544 1545 [status.removed|R a]
1545 1546 [status.removed|R fourth]
1546 1547
1547 1548 $ hg --color=debug log -T status -C -r 10
1548 1549 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1549 1550 [log.tag|tag: tip]
1550 1551 [log.user|user: test]
1551 1552 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1552 1553 [log.summary|summary: Modify, add, remove, rename]
1553 1554 [ui.note log.files|files:]
1554 1555 [status.modified|M third]
1555 1556 [status.added|A b]
1556 1557 [status.added|A fifth]
1557 1558 [status.copied| fourth]
1558 1559 [status.removed|R a]
1559 1560 [status.removed|R fourth]
1560 1561
1561 1562 $ hg --color=debug log -T status -C -r 10 -v
1562 1563 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1563 1564 [log.tag|tag: tip]
1564 1565 [log.user|user: test]
1565 1566 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1566 1567 [ui.note log.description|description:]
1567 1568 [ui.note log.description|Modify, add, remove, rename]
1568 1569
1569 1570 [ui.note log.files|files:]
1570 1571 [status.modified|M third]
1571 1572 [status.added|A b]
1572 1573 [status.added|A fifth]
1573 1574 [status.copied| fourth]
1574 1575 [status.removed|R a]
1575 1576 [status.removed|R fourth]
1576 1577
1577 1578 $ hg --color=debug log -T status -C -r 10 --debug
1578 1579 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
1579 1580 [log.tag|tag: tip]
1580 1581 [log.phase|phase: secret]
1581 1582 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
1582 1583 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1583 1584 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
1584 1585 [log.user|user: test]
1585 1586 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1586 1587 [ui.debug log.extra|extra: branch=default]
1587 1588 [ui.note log.description|description:]
1588 1589 [ui.note log.description|Modify, add, remove, rename]
1589 1590
1590 1591 [ui.note log.files|files:]
1591 1592 [status.modified|M third]
1592 1593 [status.added|A b]
1593 1594 [status.added|A fifth]
1594 1595 [status.copied| fourth]
1595 1596 [status.removed|R a]
1596 1597 [status.removed|R fourth]
1597 1598
1598 1599 $ hg --color=debug log -T status -C -r 10 --quiet
1599 1600 [log.node|10:0f9759ec227a]
1600 1601
1601 1602 Check the bisect template
1602 1603
1603 1604 $ hg bisect -g 1
1604 1605 $ hg bisect -b 3 --noupdate
1605 1606 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
1606 1607 $ hg log -T bisect -r 0:4
1607 1608 changeset: 0:1e4e1b8f71e0
1608 1609 bisect: good (implicit)
1609 1610 user: User Name <user@hostname>
1610 1611 date: Mon Jan 12 13:46:40 1970 +0000
1611 1612 summary: line 1
1612 1613
1613 1614 changeset: 1:b608e9d1a3f0
1614 1615 bisect: good
1615 1616 user: A. N. Other <other@place>
1616 1617 date: Tue Jan 13 17:33:20 1970 +0000
1617 1618 summary: other 1
1618 1619
1619 1620 changeset: 2:97054abb4ab8
1620 1621 bisect: untested
1621 1622 user: other@place
1622 1623 date: Wed Jan 14 21:20:00 1970 +0000
1623 1624 summary: no person
1624 1625
1625 1626 changeset: 3:10e46f2dcbf4
1626 1627 bisect: bad
1627 1628 user: person
1628 1629 date: Fri Jan 16 01:06:40 1970 +0000
1629 1630 summary: no user, no domain
1630 1631
1631 1632 changeset: 4:bbe44766e73d
1632 1633 bisect: bad (implicit)
1633 1634 branch: foo
1634 1635 user: person
1635 1636 date: Sat Jan 17 04:53:20 1970 +0000
1636 1637 summary: new branch
1637 1638
1638 1639 $ hg log --debug -T bisect -r 0:4
1639 1640 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
1640 1641 bisect: good (implicit)
1641 1642 phase: public
1642 1643 parent: -1:0000000000000000000000000000000000000000
1643 1644 parent: -1:0000000000000000000000000000000000000000
1644 1645 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1645 1646 user: User Name <user@hostname>
1646 1647 date: Mon Jan 12 13:46:40 1970 +0000
1647 1648 files+: a
1648 1649 extra: branch=default
1649 1650 description:
1650 1651 line 1
1651 1652 line 2
1652 1653
1653 1654
1654 1655 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1655 1656 bisect: good
1656 1657 phase: public
1657 1658 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
1658 1659 parent: -1:0000000000000000000000000000000000000000
1659 1660 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1660 1661 user: A. N. Other <other@place>
1661 1662 date: Tue Jan 13 17:33:20 1970 +0000
1662 1663 files+: b
1663 1664 extra: branch=default
1664 1665 description:
1665 1666 other 1
1666 1667 other 2
1667 1668
1668 1669 other 3
1669 1670
1670 1671
1671 1672 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
1672 1673 bisect: untested
1673 1674 phase: public
1674 1675 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1675 1676 parent: -1:0000000000000000000000000000000000000000
1676 1677 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1677 1678 user: other@place
1678 1679 date: Wed Jan 14 21:20:00 1970 +0000
1679 1680 files+: c
1680 1681 extra: branch=default
1681 1682 description:
1682 1683 no person
1683 1684
1684 1685
1685 1686 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
1686 1687 bisect: bad
1687 1688 phase: public
1688 1689 parent: 2:97054abb4ab824450e9164180baf491ae0078465
1689 1690 parent: -1:0000000000000000000000000000000000000000
1690 1691 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1691 1692 user: person
1692 1693 date: Fri Jan 16 01:06:40 1970 +0000
1693 1694 files: c
1694 1695 extra: branch=default
1695 1696 description:
1696 1697 no user, no domain
1697 1698
1698 1699
1699 1700 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1700 1701 bisect: bad (implicit)
1701 1702 branch: foo
1702 1703 phase: draft
1703 1704 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
1704 1705 parent: -1:0000000000000000000000000000000000000000
1705 1706 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1706 1707 user: person
1707 1708 date: Sat Jan 17 04:53:20 1970 +0000
1708 1709 extra: branch=foo
1709 1710 description:
1710 1711 new branch
1711 1712
1712 1713
1713 1714 $ hg log -v -T bisect -r 0:4
1714 1715 changeset: 0:1e4e1b8f71e0
1715 1716 bisect: good (implicit)
1716 1717 user: User Name <user@hostname>
1717 1718 date: Mon Jan 12 13:46:40 1970 +0000
1718 1719 files: a
1719 1720 description:
1720 1721 line 1
1721 1722 line 2
1722 1723
1723 1724
1724 1725 changeset: 1:b608e9d1a3f0
1725 1726 bisect: good
1726 1727 user: A. N. Other <other@place>
1727 1728 date: Tue Jan 13 17:33:20 1970 +0000
1728 1729 files: b
1729 1730 description:
1730 1731 other 1
1731 1732 other 2
1732 1733
1733 1734 other 3
1734 1735
1735 1736
1736 1737 changeset: 2:97054abb4ab8
1737 1738 bisect: untested
1738 1739 user: other@place
1739 1740 date: Wed Jan 14 21:20:00 1970 +0000
1740 1741 files: c
1741 1742 description:
1742 1743 no person
1743 1744
1744 1745
1745 1746 changeset: 3:10e46f2dcbf4
1746 1747 bisect: bad
1747 1748 user: person
1748 1749 date: Fri Jan 16 01:06:40 1970 +0000
1749 1750 files: c
1750 1751 description:
1751 1752 no user, no domain
1752 1753
1753 1754
1754 1755 changeset: 4:bbe44766e73d
1755 1756 bisect: bad (implicit)
1756 1757 branch: foo
1757 1758 user: person
1758 1759 date: Sat Jan 17 04:53:20 1970 +0000
1759 1760 description:
1760 1761 new branch
1761 1762
1762 1763
1763 1764 $ hg --color=debug log -T bisect -r 0:4
1764 1765 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
1765 1766 [log.bisect bisect.good|bisect: good (implicit)]
1766 1767 [log.user|user: User Name <user@hostname>]
1767 1768 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1768 1769 [log.summary|summary: line 1]
1769 1770
1770 1771 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
1771 1772 [log.bisect bisect.good|bisect: good]
1772 1773 [log.user|user: A. N. Other <other@place>]
1773 1774 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1774 1775 [log.summary|summary: other 1]
1775 1776
1776 1777 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
1777 1778 [log.bisect bisect.untested|bisect: untested]
1778 1779 [log.user|user: other@place]
1779 1780 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1780 1781 [log.summary|summary: no person]
1781 1782
1782 1783 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
1783 1784 [log.bisect bisect.bad|bisect: bad]
1784 1785 [log.user|user: person]
1785 1786 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1786 1787 [log.summary|summary: no user, no domain]
1787 1788
1788 1789 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
1789 1790 [log.bisect bisect.bad|bisect: bad (implicit)]
1790 1791 [log.branch|branch: foo]
1791 1792 [log.user|user: person]
1792 1793 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1793 1794 [log.summary|summary: new branch]
1794 1795
1795 1796 $ hg --color=debug log --debug -T bisect -r 0:4
1796 1797 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
1797 1798 [log.bisect bisect.good|bisect: good (implicit)]
1798 1799 [log.phase|phase: public]
1799 1800 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1800 1801 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1801 1802 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
1802 1803 [log.user|user: User Name <user@hostname>]
1803 1804 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1804 1805 [ui.debug log.files|files+: a]
1805 1806 [ui.debug log.extra|extra: branch=default]
1806 1807 [ui.note log.description|description:]
1807 1808 [ui.note log.description|line 1
1808 1809 line 2]
1809 1810
1810 1811
1811 1812 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
1812 1813 [log.bisect bisect.good|bisect: good]
1813 1814 [log.phase|phase: public]
1814 1815 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
1815 1816 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1816 1817 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
1817 1818 [log.user|user: A. N. Other <other@place>]
1818 1819 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1819 1820 [ui.debug log.files|files+: b]
1820 1821 [ui.debug log.extra|extra: branch=default]
1821 1822 [ui.note log.description|description:]
1822 1823 [ui.note log.description|other 1
1823 1824 other 2
1824 1825
1825 1826 other 3]
1826 1827
1827 1828
1828 1829 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
1829 1830 [log.bisect bisect.untested|bisect: untested]
1830 1831 [log.phase|phase: public]
1831 1832 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
1832 1833 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1833 1834 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
1834 1835 [log.user|user: other@place]
1835 1836 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1836 1837 [ui.debug log.files|files+: c]
1837 1838 [ui.debug log.extra|extra: branch=default]
1838 1839 [ui.note log.description|description:]
1839 1840 [ui.note log.description|no person]
1840 1841
1841 1842
1842 1843 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
1843 1844 [log.bisect bisect.bad|bisect: bad]
1844 1845 [log.phase|phase: public]
1845 1846 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
1846 1847 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1847 1848 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
1848 1849 [log.user|user: person]
1849 1850 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1850 1851 [ui.debug log.files|files: c]
1851 1852 [ui.debug log.extra|extra: branch=default]
1852 1853 [ui.note log.description|description:]
1853 1854 [ui.note log.description|no user, no domain]
1854 1855
1855 1856
1856 1857 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
1857 1858 [log.bisect bisect.bad|bisect: bad (implicit)]
1858 1859 [log.branch|branch: foo]
1859 1860 [log.phase|phase: draft]
1860 1861 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
1861 1862 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1862 1863 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
1863 1864 [log.user|user: person]
1864 1865 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1865 1866 [ui.debug log.extra|extra: branch=foo]
1866 1867 [ui.note log.description|description:]
1867 1868 [ui.note log.description|new branch]
1868 1869
1869 1870
1870 1871 $ hg --color=debug log -v -T bisect -r 0:4
1871 1872 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
1872 1873 [log.bisect bisect.good|bisect: good (implicit)]
1873 1874 [log.user|user: User Name <user@hostname>]
1874 1875 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1875 1876 [ui.note log.files|files: a]
1876 1877 [ui.note log.description|description:]
1877 1878 [ui.note log.description|line 1
1878 1879 line 2]
1879 1880
1880 1881
1881 1882 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
1882 1883 [log.bisect bisect.good|bisect: good]
1883 1884 [log.user|user: A. N. Other <other@place>]
1884 1885 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1885 1886 [ui.note log.files|files: b]
1886 1887 [ui.note log.description|description:]
1887 1888 [ui.note log.description|other 1
1888 1889 other 2
1889 1890
1890 1891 other 3]
1891 1892
1892 1893
1893 1894 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
1894 1895 [log.bisect bisect.untested|bisect: untested]
1895 1896 [log.user|user: other@place]
1896 1897 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1897 1898 [ui.note log.files|files: c]
1898 1899 [ui.note log.description|description:]
1899 1900 [ui.note log.description|no person]
1900 1901
1901 1902
1902 1903 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
1903 1904 [log.bisect bisect.bad|bisect: bad]
1904 1905 [log.user|user: person]
1905 1906 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1906 1907 [ui.note log.files|files: c]
1907 1908 [ui.note log.description|description:]
1908 1909 [ui.note log.description|no user, no domain]
1909 1910
1910 1911
1911 1912 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
1912 1913 [log.bisect bisect.bad|bisect: bad (implicit)]
1913 1914 [log.branch|branch: foo]
1914 1915 [log.user|user: person]
1915 1916 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1916 1917 [ui.note log.description|description:]
1917 1918 [ui.note log.description|new branch]
1918 1919
1919 1920
1920 1921 $ hg bisect --reset
1921 1922
1922 1923 $ cd ..
1923 1924
1924 1925 Set up latesttag repository:
1925 1926
1926 1927 $ hg init latesttag
1927 1928 $ cd latesttag
1928 1929
1929 1930 $ echo a > file
1930 1931 $ hg ci -Am a -d '0 0'
1931 1932 adding file
1932 1933
1933 1934 $ echo b >> file
1934 1935 $ hg ci -m b -d '1 0'
1935 1936
1936 1937 $ echo c >> head1
1937 1938 $ hg ci -Am h1c -d '2 0'
1938 1939 adding head1
1939 1940
1940 1941 $ hg update -q 1
1941 1942 $ echo d >> head2
1942 1943 $ hg ci -Am h2d -d '3 0'
1943 1944 adding head2
1944 1945 created new head
1945 1946
1946 1947 $ echo e >> head2
1947 1948 $ hg ci -m h2e -d '4 0'
1948 1949
1949 1950 $ hg merge -q
1950 1951 $ hg ci -m merge -d '5 -3600'
1951 1952
1952 1953 $ hg tag -r 1 -m t1 -d '6 0' t1
1953 1954 $ hg tag -r 2 -m t2 -d '7 0' t2
1954 1955 $ hg tag -r 3 -m t3 -d '8 0' t3
1955 1956 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
1956 1957 $ hg tag -r 5 -m t5 -d '9 0' t5
1957 1958 $ hg tag -r 3 -m at3 -d '10 0' at3
1958 1959
1959 1960 $ cd ..
1960 1961
1961 1962 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1962 1963 if it is a relative path
1963 1964
1964 1965 $ mkdir -p home/styles
1965 1966
1966 1967 $ cat > home/styles/teststyle <<EOF
1967 1968 > changeset = 'test {rev}:{node|short}\n'
1968 1969 > EOF
1969 1970
1970 1971 $ HOME=`pwd`/home; export HOME
1971 1972
1972 1973 $ cat > latesttag/.hg/hgrc <<EOF
1973 1974 > [ui]
1974 1975 > style = ~/styles/teststyle
1975 1976 > EOF
1976 1977
1977 1978 $ hg -R latesttag tip
1978 1979 test 11:97e5943b523a
1979 1980
1980 1981 Test recursive showlist template (issue1989):
1981 1982
1982 1983 $ cat > style1989 <<EOF
1983 1984 > changeset = '{file_mods}{manifest}{extras}'
1984 1985 > file_mod = 'M|{author|person}\n'
1985 1986 > manifest = '{rev},{author}\n'
1986 1987 > extra = '{key}: {author}\n'
1987 1988 > EOF
1988 1989
1989 1990 $ hg -R latesttag log -r tip --style=style1989
1990 1991 M|test
1991 1992 11,
1992 1993 branch: test
General Comments 0
You need to be logged in to leave comments. Login now