##// END OF EJS Templates
templater: do not process \-escapes at parsestring() (issue4290)...
Yuya Nishihara -
r24948:db7463aa stable
parent child Browse files
Show More
@@ -1,810 +1,809 b''
1 1 # templater.py - template expansion for output
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import os, re
10 10 import util, config, templatefilters, templatekw, parser, error
11 11 import revset as revsetmod
12 12 import types
13 13 import minirst
14 14
15 15 # template parsing
16 16
17 17 elements = {
18 18 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
19 19 ",": (2, None, ("list", 2)),
20 20 "|": (5, None, ("|", 5)),
21 21 "%": (6, None, ("%", 6)),
22 22 ")": (0, None, None),
23 23 "symbol": (0, ("symbol",), None),
24 24 "string": (0, ("string",), None),
25 25 "rawstring": (0, ("rawstring",), None),
26 26 "end": (0, None, None),
27 27 }
28 28
29 29 def tokenizer(data):
30 30 program, start, end = data
31 31 pos = start
32 32 while pos < end:
33 33 c = program[pos]
34 34 if c.isspace(): # skip inter-token whitespace
35 35 pass
36 36 elif c in "(,)%|": # handle simple operators
37 37 yield (c, None, pos)
38 38 elif (c in '"\'' or c == 'r' and
39 39 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
40 40 if c == 'r':
41 41 pos += 1
42 42 c = program[pos]
43 43 decode = False
44 44 else:
45 45 decode = True
46 46 pos += 1
47 47 s = pos
48 48 while pos < end: # find closing quote
49 49 d = program[pos]
50 50 if decode and d == '\\': # skip over escaped characters
51 51 pos += 2
52 52 continue
53 53 if d == c:
54 54 if not decode:
55 55 yield ('rawstring', program[s:pos], s)
56 56 break
57 57 yield ('string', program[s:pos], s)
58 58 break
59 59 pos += 1
60 60 else:
61 61 raise error.ParseError(_("unterminated string"), s)
62 62 elif c.isalnum() or c in '_':
63 63 s = pos
64 64 pos += 1
65 65 while pos < end: # find end of symbol
66 66 d = program[pos]
67 67 if not (d.isalnum() or d == "_"):
68 68 break
69 69 pos += 1
70 70 sym = program[s:pos]
71 71 yield ('symbol', sym, s)
72 72 pos -= 1
73 73 elif c == '}':
74 74 pos += 1
75 75 break
76 76 else:
77 77 raise error.ParseError(_("syntax error"), pos)
78 78 pos += 1
79 79 yield ('end', None, pos)
80 80
81 81 def compiletemplate(tmpl, context, strtoken="string"):
82 82 parsed = []
83 83 pos, stop = 0, len(tmpl)
84 84 p = parser.parser(tokenizer, elements)
85 85 while pos < stop:
86 86 n = tmpl.find('{', pos)
87 87 if n < 0:
88 88 parsed.append((strtoken, tmpl[pos:]))
89 89 break
90 90 if n > 0 and tmpl[n - 1] == '\\':
91 91 # escaped
92 92 parsed.append((strtoken, (tmpl[pos:n - 1] + "{")))
93 93 pos = n + 1
94 94 continue
95 95 if n > pos:
96 96 parsed.append((strtoken, tmpl[pos:n]))
97 97
98 98 pd = [tmpl, n + 1, stop]
99 99 parseres, pos = p.parse(pd)
100 100 parsed.append(parseres)
101 101
102 102 return [compileexp(e, context) for e in parsed]
103 103
104 104 def compileexp(exp, context):
105 105 t = exp[0]
106 106 if t in methods:
107 107 return methods[t](exp, context)
108 108 raise error.ParseError(_("unknown method '%s'") % t)
109 109
110 110 # template evaluation
111 111
112 112 def getsymbol(exp):
113 113 if exp[0] == 'symbol':
114 114 return exp[1]
115 115 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
116 116
117 117 def getlist(x):
118 118 if not x:
119 119 return []
120 120 if x[0] == 'list':
121 121 return getlist(x[1]) + [x[2]]
122 122 return [x]
123 123
124 124 def getfilter(exp, context):
125 125 f = getsymbol(exp)
126 126 if f not in context._filters:
127 127 raise error.ParseError(_("unknown function '%s'") % f)
128 128 return context._filters[f]
129 129
130 130 def gettemplate(exp, context):
131 131 if exp[0] == 'string' or exp[0] == 'rawstring':
132 132 return compiletemplate(exp[1], context, strtoken=exp[0])
133 133 if exp[0] == 'symbol':
134 134 return context._load(exp[1])
135 135 raise error.ParseError(_("expected template specifier"))
136 136
137 137 def runstring(context, mapping, data):
138 138 return data.decode("string-escape")
139 139
140 140 def runrawstring(context, mapping, data):
141 141 return data
142 142
143 143 def runsymbol(context, mapping, key):
144 144 v = mapping.get(key)
145 145 if v is None:
146 146 v = context._defaults.get(key)
147 147 if v is None:
148 148 try:
149 149 v = context.process(key, mapping)
150 150 except TemplateNotFound:
151 151 v = ''
152 152 if callable(v):
153 153 return v(**mapping)
154 154 if isinstance(v, types.GeneratorType):
155 155 v = list(v)
156 156 return v
157 157
158 158 def buildfilter(exp, context):
159 159 func, data = compileexp(exp[1], context)
160 160 filt = getfilter(exp[2], context)
161 161 return (runfilter, (func, data, filt))
162 162
163 163 def runfilter(context, mapping, data):
164 164 func, data, filt = data
165 165 # func() may return string, generator of strings or arbitrary object such
166 166 # as date tuple, but filter does not want generator.
167 167 thing = func(context, mapping, data)
168 168 if isinstance(thing, types.GeneratorType):
169 169 thing = stringify(thing)
170 170 try:
171 171 return filt(thing)
172 172 except (ValueError, AttributeError, TypeError):
173 173 if isinstance(data, tuple):
174 174 dt = data[1]
175 175 else:
176 176 dt = data
177 177 raise util.Abort(_("template filter '%s' is not compatible with "
178 178 "keyword '%s'") % (filt.func_name, dt))
179 179
180 180 def buildmap(exp, context):
181 181 func, data = compileexp(exp[1], context)
182 182 ctmpl = gettemplate(exp[2], context)
183 183 return (runmap, (func, data, ctmpl))
184 184
185 185 def runtemplate(context, mapping, template):
186 186 for func, data in template:
187 187 yield func(context, mapping, data)
188 188
189 189 def runmap(context, mapping, data):
190 190 func, data, ctmpl = data
191 191 d = func(context, mapping, data)
192 192 if callable(d):
193 193 d = d()
194 194
195 195 lm = mapping.copy()
196 196
197 197 for i in d:
198 198 if isinstance(i, dict):
199 199 lm.update(i)
200 200 lm['originalnode'] = mapping.get('node')
201 201 yield runtemplate(context, lm, ctmpl)
202 202 else:
203 203 # v is not an iterable of dicts, this happen when 'key'
204 204 # has been fully expanded already and format is useless.
205 205 # If so, return the expanded value.
206 206 yield i
207 207
208 208 def buildfunc(exp, context):
209 209 n = getsymbol(exp[1])
210 210 args = [compileexp(x, context) for x in getlist(exp[2])]
211 211 if n in funcs:
212 212 f = funcs[n]
213 213 return (f, args)
214 214 if n in context._filters:
215 215 if len(args) != 1:
216 216 raise error.ParseError(_("filter %s expects one argument") % n)
217 217 f = context._filters[n]
218 218 return (runfilter, (args[0][0], args[0][1], f))
219 219 raise error.ParseError(_("unknown function '%s'") % n)
220 220
221 221 def date(context, mapping, args):
222 222 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
223 223 strings."""
224 224 if not (1 <= len(args) <= 2):
225 225 # i18n: "date" is a keyword
226 226 raise error.ParseError(_("date expects one or two arguments"))
227 227
228 228 date = args[0][0](context, mapping, args[0][1])
229 229 fmt = None
230 230 if len(args) == 2:
231 231 fmt = stringify(args[1][0](context, mapping, args[1][1]))
232 232 try:
233 233 if fmt is None:
234 234 return util.datestr(date)
235 235 else:
236 236 return util.datestr(date, fmt)
237 237 except (TypeError, ValueError):
238 238 # i18n: "date" is a keyword
239 239 raise error.ParseError(_("date expects a date information"))
240 240
241 241 def diff(context, mapping, args):
242 242 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
243 243 specifying files to include or exclude."""
244 244 if len(args) > 2:
245 245 # i18n: "diff" is a keyword
246 246 raise error.ParseError(_("diff expects one, two or no arguments"))
247 247
248 248 def getpatterns(i):
249 249 if i < len(args):
250 250 s = args[i][1].strip()
251 251 if s:
252 252 return [s]
253 253 return []
254 254
255 255 ctx = mapping['ctx']
256 256 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
257 257
258 258 return ''.join(chunks)
259 259
260 260 def fill(context, mapping, args):
261 261 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
262 262 paragraphs with optional indentation. See the "fill" filter."""
263 263 if not (1 <= len(args) <= 4):
264 264 # i18n: "fill" is a keyword
265 265 raise error.ParseError(_("fill expects one to four arguments"))
266 266
267 267 text = stringify(args[0][0](context, mapping, args[0][1]))
268 268 width = 76
269 269 initindent = ''
270 270 hangindent = ''
271 271 if 2 <= len(args) <= 4:
272 272 try:
273 273 width = int(stringify(args[1][0](context, mapping, args[1][1])))
274 274 except ValueError:
275 275 # i18n: "fill" is a keyword
276 276 raise error.ParseError(_("fill expects an integer width"))
277 277 try:
278 278 initindent = stringify(_evalifliteral(args[2], context, mapping))
279 279 hangindent = stringify(_evalifliteral(args[3], context, mapping))
280 280 except IndexError:
281 281 pass
282 282
283 283 return templatefilters.fill(text, width, initindent, hangindent)
284 284
285 285 def pad(context, mapping, args):
286 286 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
287 287 fill character."""
288 288 if not (2 <= len(args) <= 4):
289 289 # i18n: "pad" is a keyword
290 290 raise error.ParseError(_("pad() expects two to four arguments"))
291 291
292 292 width = int(args[1][1])
293 293
294 294 text = stringify(args[0][0](context, mapping, args[0][1]))
295 295 if args[0][0] == runstring:
296 296 text = stringify(runtemplate(context, mapping,
297 297 compiletemplate(text, context)))
298 298
299 299 right = False
300 300 fillchar = ' '
301 301 if len(args) > 2:
302 302 fillchar = stringify(args[2][0](context, mapping, args[2][1]))
303 303 if len(args) > 3:
304 304 right = util.parsebool(args[3][1])
305 305
306 306 if right:
307 307 return text.rjust(width, fillchar)
308 308 else:
309 309 return text.ljust(width, fillchar)
310 310
311 311 def get(context, mapping, args):
312 312 """:get(dict, key): Get an attribute/key from an object. Some keywords
313 313 are complex types. This function allows you to obtain the value of an
314 314 attribute on these type."""
315 315 if len(args) != 2:
316 316 # i18n: "get" is a keyword
317 317 raise error.ParseError(_("get() expects two arguments"))
318 318
319 319 dictarg = args[0][0](context, mapping, args[0][1])
320 320 if not util.safehasattr(dictarg, 'get'):
321 321 # i18n: "get" is a keyword
322 322 raise error.ParseError(_("get() expects a dict as first argument"))
323 323
324 324 key = args[1][0](context, mapping, args[1][1])
325 325 yield dictarg.get(key)
326 326
327 327 def _evalifliteral(arg, context, mapping):
328 328 t = stringify(arg[0](context, mapping, arg[1]))
329 329 if arg[0] == runstring or arg[0] == runrawstring:
330 330 yield runtemplate(context, mapping,
331 331 compiletemplate(t, context, strtoken='rawstring'))
332 332 else:
333 333 yield t
334 334
335 335 def if_(context, mapping, args):
336 336 """:if(expr, then[, else]): Conditionally execute based on the result of
337 337 an expression."""
338 338 if not (2 <= len(args) <= 3):
339 339 # i18n: "if" is a keyword
340 340 raise error.ParseError(_("if expects two or three arguments"))
341 341
342 342 test = stringify(args[0][0](context, mapping, args[0][1]))
343 343 if test:
344 344 yield _evalifliteral(args[1], context, mapping)
345 345 elif len(args) == 3:
346 346 yield _evalifliteral(args[2], context, mapping)
347 347
348 348 def ifcontains(context, mapping, args):
349 349 """:ifcontains(search, thing, then[, else]): Conditionally execute based
350 350 on whether the item "search" is in "thing"."""
351 351 if not (3 <= len(args) <= 4):
352 352 # i18n: "ifcontains" is a keyword
353 353 raise error.ParseError(_("ifcontains expects three or four arguments"))
354 354
355 355 item = stringify(args[0][0](context, mapping, args[0][1]))
356 356 items = args[1][0](context, mapping, args[1][1])
357 357
358 358 if item in items:
359 359 yield _evalifliteral(args[2], context, mapping)
360 360 elif len(args) == 4:
361 361 yield _evalifliteral(args[3], context, mapping)
362 362
363 363 def ifeq(context, mapping, args):
364 364 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
365 365 whether 2 items are equivalent."""
366 366 if not (3 <= len(args) <= 4):
367 367 # i18n: "ifeq" is a keyword
368 368 raise error.ParseError(_("ifeq expects three or four arguments"))
369 369
370 370 test = stringify(args[0][0](context, mapping, args[0][1]))
371 371 match = stringify(args[1][0](context, mapping, args[1][1]))
372 372 if test == match:
373 373 yield _evalifliteral(args[2], context, mapping)
374 374 elif len(args) == 4:
375 375 yield _evalifliteral(args[3], context, mapping)
376 376
377 377 def join(context, mapping, args):
378 378 """:join(list, sep): Join items in a list with a delimiter."""
379 379 if not (1 <= len(args) <= 2):
380 380 # i18n: "join" is a keyword
381 381 raise error.ParseError(_("join expects one or two arguments"))
382 382
383 383 joinset = args[0][0](context, mapping, args[0][1])
384 384 if callable(joinset):
385 385 jf = joinset.joinfmt
386 386 joinset = [jf(x) for x in joinset()]
387 387
388 388 joiner = " "
389 389 if len(args) > 1:
390 390 joiner = stringify(args[1][0](context, mapping, args[1][1]))
391 391
392 392 first = True
393 393 for x in joinset:
394 394 if first:
395 395 first = False
396 396 else:
397 397 yield joiner
398 398 yield x
399 399
400 400 def label(context, mapping, args):
401 401 """:label(label, expr): Apply a label to generated content. Content with
402 402 a label applied can result in additional post-processing, such as
403 403 automatic colorization."""
404 404 if len(args) != 2:
405 405 # i18n: "label" is a keyword
406 406 raise error.ParseError(_("label expects two arguments"))
407 407
408 408 # ignore args[0] (the label string) since this is supposed to be a a no-op
409 409 yield _evalifliteral(args[1], context, mapping)
410 410
411 411 def revset(context, mapping, args):
412 412 """:revset(query[, formatargs...]): Execute a revision set query. See
413 413 :hg:`help revset`."""
414 414 if not len(args) > 0:
415 415 # i18n: "revset" is a keyword
416 416 raise error.ParseError(_("revset expects one or more arguments"))
417 417
418 418 raw = args[0][1]
419 419 ctx = mapping['ctx']
420 420 repo = ctx.repo()
421 421
422 422 def query(expr):
423 423 m = revsetmod.match(repo.ui, expr)
424 424 return m(repo)
425 425
426 426 if len(args) > 1:
427 427 formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
428 428 revs = query(revsetmod.formatspec(raw, *formatargs))
429 429 revs = list([str(r) for r in revs])
430 430 else:
431 431 revsetcache = mapping['cache'].setdefault("revsetcache", {})
432 432 if raw in revsetcache:
433 433 revs = revsetcache[raw]
434 434 else:
435 435 revs = query(raw)
436 436 revs = list([str(r) for r in revs])
437 437 revsetcache[raw] = revs
438 438
439 439 return templatekw.showlist("revision", revs, **mapping)
440 440
441 441 def rstdoc(context, mapping, args):
442 442 """:rstdoc(text, style): Format ReStructuredText."""
443 443 if len(args) != 2:
444 444 # i18n: "rstdoc" is a keyword
445 445 raise error.ParseError(_("rstdoc expects two arguments"))
446 446
447 447 text = stringify(args[0][0](context, mapping, args[0][1]))
448 448 style = stringify(args[1][0](context, mapping, args[1][1]))
449 449
450 450 return minirst.format(text, style=style, keep=['verbose'])
451 451
452 452 def shortest(context, mapping, args):
453 453 """:shortest(node, minlength=4): Obtain the shortest representation of
454 454 a node."""
455 455 if not (1 <= len(args) <= 2):
456 456 # i18n: "shortest" is a keyword
457 457 raise error.ParseError(_("shortest() expects one or two arguments"))
458 458
459 459 node = stringify(args[0][0](context, mapping, args[0][1]))
460 460
461 461 minlength = 4
462 462 if len(args) > 1:
463 463 minlength = int(args[1][1])
464 464
465 465 cl = mapping['ctx']._repo.changelog
466 466 def isvalid(test):
467 467 try:
468 468 try:
469 469 cl.index.partialmatch(test)
470 470 except AttributeError:
471 471 # Pure mercurial doesn't support partialmatch on the index.
472 472 # Fallback to the slow way.
473 473 if cl._partialmatch(test) is None:
474 474 return False
475 475
476 476 try:
477 477 i = int(test)
478 478 # if we are a pure int, then starting with zero will not be
479 479 # confused as a rev; or, obviously, if the int is larger than
480 480 # the value of the tip rev
481 481 if test[0] == '0' or i > len(cl):
482 482 return True
483 483 return False
484 484 except ValueError:
485 485 return True
486 486 except error.RevlogError:
487 487 return False
488 488
489 489 shortest = node
490 490 startlength = max(6, minlength)
491 491 length = startlength
492 492 while True:
493 493 test = node[:length]
494 494 if isvalid(test):
495 495 shortest = test
496 496 if length == minlength or length > startlength:
497 497 return shortest
498 498 length -= 1
499 499 else:
500 500 length += 1
501 501 if len(shortest) <= length:
502 502 return shortest
503 503
504 504 def strip(context, mapping, args):
505 505 """:strip(text[, chars]): Strip characters from a string."""
506 506 if not (1 <= len(args) <= 2):
507 507 # i18n: "strip" is a keyword
508 508 raise error.ParseError(_("strip expects one or two arguments"))
509 509
510 510 text = stringify(args[0][0](context, mapping, args[0][1]))
511 511 if len(args) == 2:
512 512 chars = stringify(args[1][0](context, mapping, args[1][1]))
513 513 return text.strip(chars)
514 514 return text.strip()
515 515
516 516 def sub(context, mapping, args):
517 517 """:sub(pattern, replacement, expression): Perform text substitution
518 518 using regular expressions."""
519 519 if len(args) != 3:
520 520 # i18n: "sub" is a keyword
521 521 raise error.ParseError(_("sub expects three arguments"))
522 522
523 523 pat = stringify(args[0][0](context, mapping, args[0][1]))
524 524 rpl = stringify(args[1][0](context, mapping, args[1][1]))
525 525 src = stringify(_evalifliteral(args[2], context, mapping))
526 526 yield re.sub(pat, rpl, src)
527 527
528 528 def startswith(context, mapping, args):
529 529 """:startswith(pattern, text): Returns the value from the "text" argument
530 530 if it begins with the content from the "pattern" argument."""
531 531 if len(args) != 2:
532 532 # i18n: "startswith" is a keyword
533 533 raise error.ParseError(_("startswith expects two arguments"))
534 534
535 535 patn = stringify(args[0][0](context, mapping, args[0][1]))
536 536 text = stringify(args[1][0](context, mapping, args[1][1]))
537 537 if text.startswith(patn):
538 538 return text
539 539 return ''
540 540
541 541
542 542 def word(context, mapping, args):
543 543 """:word(number, text[, separator]): Return the nth word from a string."""
544 544 if not (2 <= len(args) <= 3):
545 545 # i18n: "word" is a keyword
546 546 raise error.ParseError(_("word expects two or three arguments, got %d")
547 547 % len(args))
548 548
549 549 try:
550 550 num = int(stringify(args[0][0](context, mapping, args[0][1])))
551 551 except ValueError:
552 552 # i18n: "word" is a keyword
553 553 raise error.ParseError(
554 554 _("Use strings like '3' for numbers passed to word function"))
555 555 text = stringify(args[1][0](context, mapping, args[1][1]))
556 556 if len(args) == 3:
557 557 splitter = stringify(args[2][0](context, mapping, args[2][1]))
558 558 else:
559 559 splitter = None
560 560
561 561 tokens = text.split(splitter)
562 562 if num >= len(tokens):
563 563 return ''
564 564 else:
565 565 return tokens[num]
566 566
567 567 methods = {
568 568 "string": lambda e, c: (runstring, e[1]),
569 569 "rawstring": lambda e, c: (runrawstring, e[1]),
570 570 "symbol": lambda e, c: (runsymbol, e[1]),
571 571 "group": lambda e, c: compileexp(e[1], c),
572 572 # ".": buildmember,
573 573 "|": buildfilter,
574 574 "%": buildmap,
575 575 "func": buildfunc,
576 576 }
577 577
578 578 funcs = {
579 579 "date": date,
580 580 "diff": diff,
581 581 "fill": fill,
582 582 "get": get,
583 583 "if": if_,
584 584 "ifcontains": ifcontains,
585 585 "ifeq": ifeq,
586 586 "join": join,
587 587 "label": label,
588 588 "pad": pad,
589 589 "revset": revset,
590 590 "rstdoc": rstdoc,
591 591 "shortest": shortest,
592 592 "startswith": startswith,
593 593 "strip": strip,
594 594 "sub": sub,
595 595 "word": word,
596 596 }
597 597
598 598 # template engine
599 599
600 600 stringify = templatefilters.stringify
601 601
602 602 def _flatten(thing):
603 603 '''yield a single stream from a possibly nested set of iterators'''
604 604 if isinstance(thing, str):
605 605 yield thing
606 606 elif not util.safehasattr(thing, '__iter__'):
607 607 if thing is not None:
608 608 yield str(thing)
609 609 else:
610 610 for i in thing:
611 611 if isinstance(i, str):
612 612 yield i
613 613 elif not util.safehasattr(i, '__iter__'):
614 614 if i is not None:
615 615 yield str(i)
616 616 elif i is not None:
617 617 for j in _flatten(i):
618 618 yield j
619 619
620 620 def parsestring(s, quoted=True):
621 '''parse a string using simple c-like syntax.
622 string must be in quotes if quoted is True.'''
621 '''unwrap quotes if quoted is True'''
623 622 if quoted:
624 623 if len(s) < 2 or s[0] != s[-1]:
625 624 raise SyntaxError(_('unmatched quotes'))
626 return s[1:-1].decode('string_escape')
625 return s[1:-1]
627 626
628 return s.decode('string_escape')
627 return s
629 628
630 629 class engine(object):
631 630 '''template expansion engine.
632 631
633 632 template expansion works like this. a map file contains key=value
634 633 pairs. if value is quoted, it is treated as string. otherwise, it
635 634 is treated as name of template file.
636 635
637 636 templater is asked to expand a key in map. it looks up key, and
638 637 looks for strings like this: {foo}. it expands {foo} by looking up
639 638 foo in map, and substituting it. expansion is recursive: it stops
640 639 when there is no more {foo} to replace.
641 640
642 641 expansion also allows formatting and filtering.
643 642
644 643 format uses key to expand each item in list. syntax is
645 644 {key%format}.
646 645
647 646 filter uses function to transform value. syntax is
648 647 {key|filter1|filter2|...}.'''
649 648
650 649 def __init__(self, loader, filters={}, defaults={}):
651 650 self._loader = loader
652 651 self._filters = filters
653 652 self._defaults = defaults
654 653 self._cache = {}
655 654
656 655 def _load(self, t):
657 656 '''load, parse, and cache a template'''
658 657 if t not in self._cache:
659 658 self._cache[t] = compiletemplate(self._loader(t), self)
660 659 return self._cache[t]
661 660
662 661 def process(self, t, mapping):
663 662 '''Perform expansion. t is name of map element to expand.
664 663 mapping contains added elements for use during expansion. Is a
665 664 generator.'''
666 665 return _flatten(runtemplate(self, mapping, self._load(t)))
667 666
668 667 engines = {'default': engine}
669 668
670 669 def stylelist():
671 670 paths = templatepaths()
672 671 if not paths:
673 672 return _('no templates found, try `hg debuginstall` for more info')
674 673 dirlist = os.listdir(paths[0])
675 674 stylelist = []
676 675 for file in dirlist:
677 676 split = file.split(".")
678 677 if split[0] == "map-cmdline":
679 678 stylelist.append(split[1])
680 679 return ", ".join(sorted(stylelist))
681 680
682 681 class TemplateNotFound(util.Abort):
683 682 pass
684 683
685 684 class templater(object):
686 685
687 686 def __init__(self, mapfile, filters={}, defaults={}, cache={},
688 687 minchunk=1024, maxchunk=65536):
689 688 '''set up template engine.
690 689 mapfile is name of file to read map definitions from.
691 690 filters is dict of functions. each transforms a value into another.
692 691 defaults is dict of default map definitions.'''
693 692 self.mapfile = mapfile or 'template'
694 693 self.cache = cache.copy()
695 694 self.map = {}
696 695 if mapfile:
697 696 self.base = os.path.dirname(mapfile)
698 697 else:
699 698 self.base = ''
700 699 self.filters = templatefilters.filters.copy()
701 700 self.filters.update(filters)
702 701 self.defaults = defaults
703 702 self.minchunk, self.maxchunk = minchunk, maxchunk
704 703 self.ecache = {}
705 704
706 705 if not mapfile:
707 706 return
708 707 if not os.path.exists(mapfile):
709 708 raise util.Abort(_("style '%s' not found") % mapfile,
710 709 hint=_("available styles: %s") % stylelist())
711 710
712 711 conf = config.config()
713 712 conf.read(mapfile)
714 713
715 714 for key, val in conf[''].items():
716 715 if not val:
717 716 raise SyntaxError(_('%s: missing value') % conf.source('', key))
718 717 if val[0] in "'\"":
719 718 try:
720 719 self.cache[key] = parsestring(val)
721 720 except SyntaxError, inst:
722 721 raise SyntaxError('%s: %s' %
723 722 (conf.source('', key), inst.args[0]))
724 723 else:
725 724 val = 'default', val
726 725 if ':' in val[1]:
727 726 val = val[1].split(':', 1)
728 727 self.map[key] = val[0], os.path.join(self.base, val[1])
729 728
730 729 def __contains__(self, key):
731 730 return key in self.cache or key in self.map
732 731
733 732 def load(self, t):
734 733 '''Get the template for the given template name. Use a local cache.'''
735 734 if t not in self.cache:
736 735 try:
737 736 self.cache[t] = util.readfile(self.map[t][1])
738 737 except KeyError, inst:
739 738 raise TemplateNotFound(_('"%s" not in template map') %
740 739 inst.args[0])
741 740 except IOError, inst:
742 741 raise IOError(inst.args[0], _('template file %s: %s') %
743 742 (self.map[t][1], inst.args[1]))
744 743 return self.cache[t]
745 744
746 745 def __call__(self, t, **mapping):
747 746 ttype = t in self.map and self.map[t][0] or 'default'
748 747 if ttype not in self.ecache:
749 748 self.ecache[ttype] = engines[ttype](self.load,
750 749 self.filters, self.defaults)
751 750 proc = self.ecache[ttype]
752 751
753 752 stream = proc.process(t, mapping)
754 753 if self.minchunk:
755 754 stream = util.increasingchunks(stream, min=self.minchunk,
756 755 max=self.maxchunk)
757 756 return stream
758 757
759 758 def templatepaths():
760 759 '''return locations used for template files.'''
761 760 pathsrel = ['templates']
762 761 paths = [os.path.normpath(os.path.join(util.datapath, f))
763 762 for f in pathsrel]
764 763 return [p for p in paths if os.path.isdir(p)]
765 764
766 765 def templatepath(name):
767 766 '''return location of template file. returns None if not found.'''
768 767 for p in templatepaths():
769 768 f = os.path.join(p, name)
770 769 if os.path.exists(f):
771 770 return f
772 771 return None
773 772
774 773 def stylemap(styles, paths=None):
775 774 """Return path to mapfile for a given style.
776 775
777 776 Searches mapfile in the following locations:
778 777 1. templatepath/style/map
779 778 2. templatepath/map-style
780 779 3. templatepath/map
781 780 """
782 781
783 782 if paths is None:
784 783 paths = templatepaths()
785 784 elif isinstance(paths, str):
786 785 paths = [paths]
787 786
788 787 if isinstance(styles, str):
789 788 styles = [styles]
790 789
791 790 for style in styles:
792 791 # only plain name is allowed to honor template paths
793 792 if (not style
794 793 or style in (os.curdir, os.pardir)
795 794 or os.sep in style
796 795 or os.altsep and os.altsep in style):
797 796 continue
798 797 locations = [os.path.join(style, 'map'), 'map-' + style]
799 798 locations.append('map')
800 799
801 800 for path in paths:
802 801 for location in locations:
803 802 mapfile = os.path.join(path, location)
804 803 if os.path.isfile(mapfile):
805 804 return style, mapfile
806 805
807 806 raise RuntimeError("No hgweb templates found in %r" % paths)
808 807
809 808 # tell hggettext to extract docstrings from these functions:
810 809 i18nfunctions = funcs.values()
@@ -1,2634 +1,2657 b''
1 1 $ hg init a
2 2 $ cd a
3 3 $ echo a > a
4 4 $ hg add a
5 5 $ echo line 1 > b
6 6 $ echo line 2 >> b
7 7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8 8
9 9 $ hg add b
10 10 $ echo other 1 > c
11 11 $ echo other 2 >> c
12 12 $ echo >> c
13 13 $ echo other 3 >> c
14 14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15 15
16 16 $ hg add c
17 17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 18 $ echo c >> c
19 19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 20
21 21 $ echo foo > .hg/branch
22 22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23 23
24 24 $ hg co -q 3
25 25 $ echo other 4 >> d
26 26 $ hg add d
27 27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28 28
29 29 $ hg merge -q foo
30 30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31 31
32 32 Second branch starting at nullrev:
33 33
34 34 $ hg update null
35 35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
36 36 $ echo second > second
37 37 $ hg add second
38 38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
39 39 created new head
40 40
41 41 $ echo third > third
42 42 $ hg add third
43 43 $ hg mv second fourth
44 44 $ hg commit -m third -d "2020-01-01 10:01"
45 45
46 46 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
47 47 fourth (second)
48 48 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
49 49 second -> fourth
50 50 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
51 51 8 t
52 52 7 f
53 53
54 54 Quoting for ui.logtemplate
55 55
56 56 $ hg tip --config "ui.logtemplate={rev}\n"
57 57 8
58 58 $ hg tip --config "ui.logtemplate='{rev}\n'"
59 59 8
60 60 $ hg tip --config 'ui.logtemplate="{rev}\n"'
61 61 8
62 62
63 63 Make sure user/global hgrc does not affect tests
64 64
65 65 $ echo '[ui]' > .hg/hgrc
66 66 $ echo 'logtemplate =' >> .hg/hgrc
67 67 $ echo 'style =' >> .hg/hgrc
68 68
69 69 Add some simple styles to settings
70 70
71 71 $ echo '[templates]' >> .hg/hgrc
72 72 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
73 73 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
74 74
75 75 $ hg log -l1 -Tsimple
76 76 8
77 77 $ hg log -l1 -Tsimple2
78 78 8
79 79
80 80 Test templates and style maps in files:
81 81
82 82 $ echo "{rev}" > tmpl
83 83 $ hg log -l1 -T./tmpl
84 84 8
85 85 $ hg log -l1 -Tblah/blah
86 86 blah/blah (no-eol)
87 87
88 88 $ printf 'changeset = "{rev}\\n"\n' > map-simple
89 89 $ hg log -l1 -T./map-simple
90 90 8
91 91
92 92 Template should precede style option
93 93
94 94 $ hg log -l1 --style default -T '{rev}\n'
95 95 8
96 96
97 97 Add a commit with empty description, to ensure that the templates
98 98 following below omit it properly.
99 99
100 100 $ echo c >> c
101 101 $ hg add c
102 102 $ hg commit -qm ' '
103 103
104 104 Default style is like normal output. Phases style should be the same
105 105 as default style, except for extra phase lines.
106 106
107 107 $ hg log > log.out
108 108 $ hg log --style default > style.out
109 109 $ cmp log.out style.out || diff -u log.out style.out
110 110 $ hg log -T phases > phases.out
111 111 $ diff -u log.out phases.out | grep "phase:"
112 112 +phase: draft
113 113 +phase: draft
114 114 +phase: draft
115 115 +phase: draft
116 116 +phase: draft
117 117 +phase: draft
118 118 +phase: draft
119 119 +phase: draft
120 120 +phase: draft
121 121 +phase: draft
122 122
123 123 $ hg log -v > log.out
124 124 $ hg log -v --style default > style.out
125 125 $ cmp log.out style.out || diff -u log.out style.out
126 126 $ hg log -v -T phases > phases.out
127 127 $ diff -u log.out phases.out | grep phase:
128 128 +phase: draft
129 129 +phase: draft
130 130 +phase: draft
131 131 +phase: draft
132 132 +phase: draft
133 133 +phase: draft
134 134 +phase: draft
135 135 +phase: draft
136 136 +phase: draft
137 137 +phase: draft
138 138
139 139 $ hg log -q > log.out
140 140 $ hg log -q --style default > style.out
141 141 $ cmp log.out style.out || diff -u log.out style.out
142 142 $ hg log -q -T phases > phases.out
143 143 $ cmp log.out phases.out || diff -u log.out phases.out
144 144
145 145 $ hg log --debug > log.out
146 146 $ hg log --debug --style default > style.out
147 147 $ cmp log.out style.out || diff -u log.out style.out
148 148 $ hg log --debug -T phases > phases.out
149 149 $ cmp log.out phases.out || diff -u log.out phases.out
150 150
151 151 Default style should also preserve color information (issue2866):
152 152
153 153 $ cp $HGRCPATH $HGRCPATH-bak
154 154 $ cat <<EOF >> $HGRCPATH
155 155 > [extensions]
156 156 > color=
157 157 > EOF
158 158
159 159 $ hg --color=debug log > log.out
160 160 $ hg --color=debug log --style default > style.out
161 161 $ cmp log.out style.out || diff -u log.out style.out
162 162 $ hg --color=debug log -T phases > phases.out
163 163 $ diff -u log.out phases.out | grep phase:
164 164 +[log.phase|phase: draft]
165 165 +[log.phase|phase: draft]
166 166 +[log.phase|phase: draft]
167 167 +[log.phase|phase: draft]
168 168 +[log.phase|phase: draft]
169 169 +[log.phase|phase: draft]
170 170 +[log.phase|phase: draft]
171 171 +[log.phase|phase: draft]
172 172 +[log.phase|phase: draft]
173 173 +[log.phase|phase: draft]
174 174
175 175 $ hg --color=debug -v log > log.out
176 176 $ hg --color=debug -v log --style default > style.out
177 177 $ cmp log.out style.out || diff -u log.out style.out
178 178 $ hg --color=debug -v log -T phases > phases.out
179 179 $ diff -u log.out phases.out | grep phase:
180 180 +[log.phase|phase: draft]
181 181 +[log.phase|phase: draft]
182 182 +[log.phase|phase: draft]
183 183 +[log.phase|phase: draft]
184 184 +[log.phase|phase: draft]
185 185 +[log.phase|phase: draft]
186 186 +[log.phase|phase: draft]
187 187 +[log.phase|phase: draft]
188 188 +[log.phase|phase: draft]
189 189 +[log.phase|phase: draft]
190 190
191 191 $ hg --color=debug -q log > log.out
192 192 $ hg --color=debug -q log --style default > style.out
193 193 $ cmp log.out style.out || diff -u log.out style.out
194 194 $ hg --color=debug -q log -T phases > phases.out
195 195 $ cmp log.out phases.out || diff -u log.out phases.out
196 196
197 197 $ hg --color=debug --debug log > log.out
198 198 $ hg --color=debug --debug log --style default > style.out
199 199 $ cmp log.out style.out || diff -u log.out style.out
200 200 $ hg --color=debug --debug log -T phases > phases.out
201 201 $ cmp log.out phases.out || diff -u log.out phases.out
202 202
203 203 $ mv $HGRCPATH-bak $HGRCPATH
204 204
205 205 Remove commit with empty commit message, so as to not pollute further
206 206 tests.
207 207
208 208 $ hg --config extensions.strip= strip -q .
209 209
210 210 Revision with no copies (used to print a traceback):
211 211
212 212 $ hg tip -v --template '\n'
213 213
214 214
215 215 Compact style works:
216 216
217 217 $ hg log -Tcompact
218 218 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
219 219 third
220 220
221 221 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
222 222 second
223 223
224 224 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
225 225 merge
226 226
227 227 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
228 228 new head
229 229
230 230 4 bbe44766e73d 1970-01-17 04:53 +0000 person
231 231 new branch
232 232
233 233 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
234 234 no user, no domain
235 235
236 236 2 97054abb4ab8 1970-01-14 21:20 +0000 other
237 237 no person
238 238
239 239 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
240 240 other 1
241 241
242 242 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
243 243 line 1
244 244
245 245
246 246 $ hg log -v --style compact
247 247 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
248 248 third
249 249
250 250 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
251 251 second
252 252
253 253 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
254 254 merge
255 255
256 256 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
257 257 new head
258 258
259 259 4 bbe44766e73d 1970-01-17 04:53 +0000 person
260 260 new branch
261 261
262 262 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
263 263 no user, no domain
264 264
265 265 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
266 266 no person
267 267
268 268 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
269 269 other 1
270 270 other 2
271 271
272 272 other 3
273 273
274 274 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
275 275 line 1
276 276 line 2
277 277
278 278
279 279 $ hg log --debug --style compact
280 280 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
281 281 third
282 282
283 283 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
284 284 second
285 285
286 286 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
287 287 merge
288 288
289 289 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
290 290 new head
291 291
292 292 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
293 293 new branch
294 294
295 295 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
296 296 no user, no domain
297 297
298 298 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
299 299 no person
300 300
301 301 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
302 302 other 1
303 303 other 2
304 304
305 305 other 3
306 306
307 307 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
308 308 line 1
309 309 line 2
310 310
311 311
312 312 Test xml styles:
313 313
314 314 $ hg log --style xml
315 315 <?xml version="1.0"?>
316 316 <log>
317 317 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
318 318 <tag>tip</tag>
319 319 <author email="test">test</author>
320 320 <date>2020-01-01T10:01:00+00:00</date>
321 321 <msg xml:space="preserve">third</msg>
322 322 </logentry>
323 323 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
324 324 <parent revision="-1" node="0000000000000000000000000000000000000000" />
325 325 <author email="user@hostname">User Name</author>
326 326 <date>1970-01-12T13:46:40+00:00</date>
327 327 <msg xml:space="preserve">second</msg>
328 328 </logentry>
329 329 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
330 330 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
331 331 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
332 332 <author email="person">person</author>
333 333 <date>1970-01-18T08:40:01+00:00</date>
334 334 <msg xml:space="preserve">merge</msg>
335 335 </logentry>
336 336 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
337 337 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
338 338 <author email="person">person</author>
339 339 <date>1970-01-18T08:40:00+00:00</date>
340 340 <msg xml:space="preserve">new head</msg>
341 341 </logentry>
342 342 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
343 343 <branch>foo</branch>
344 344 <author email="person">person</author>
345 345 <date>1970-01-17T04:53:20+00:00</date>
346 346 <msg xml:space="preserve">new branch</msg>
347 347 </logentry>
348 348 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
349 349 <author email="person">person</author>
350 350 <date>1970-01-16T01:06:40+00:00</date>
351 351 <msg xml:space="preserve">no user, no domain</msg>
352 352 </logentry>
353 353 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
354 354 <author email="other@place">other</author>
355 355 <date>1970-01-14T21:20:00+00:00</date>
356 356 <msg xml:space="preserve">no person</msg>
357 357 </logentry>
358 358 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
359 359 <author email="other@place">A. N. Other</author>
360 360 <date>1970-01-13T17:33:20+00:00</date>
361 361 <msg xml:space="preserve">other 1
362 362 other 2
363 363
364 364 other 3</msg>
365 365 </logentry>
366 366 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
367 367 <author email="user@hostname">User Name</author>
368 368 <date>1970-01-12T13:46:40+00:00</date>
369 369 <msg xml:space="preserve">line 1
370 370 line 2</msg>
371 371 </logentry>
372 372 </log>
373 373
374 374 $ hg log -v --style xml
375 375 <?xml version="1.0"?>
376 376 <log>
377 377 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
378 378 <tag>tip</tag>
379 379 <author email="test">test</author>
380 380 <date>2020-01-01T10:01:00+00:00</date>
381 381 <msg xml:space="preserve">third</msg>
382 382 <paths>
383 383 <path action="A">fourth</path>
384 384 <path action="A">third</path>
385 385 <path action="R">second</path>
386 386 </paths>
387 387 <copies>
388 388 <copy source="second">fourth</copy>
389 389 </copies>
390 390 </logentry>
391 391 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
392 392 <parent revision="-1" node="0000000000000000000000000000000000000000" />
393 393 <author email="user@hostname">User Name</author>
394 394 <date>1970-01-12T13:46:40+00:00</date>
395 395 <msg xml:space="preserve">second</msg>
396 396 <paths>
397 397 <path action="A">second</path>
398 398 </paths>
399 399 </logentry>
400 400 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
401 401 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
402 402 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
403 403 <author email="person">person</author>
404 404 <date>1970-01-18T08:40:01+00:00</date>
405 405 <msg xml:space="preserve">merge</msg>
406 406 <paths>
407 407 </paths>
408 408 </logentry>
409 409 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
410 410 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
411 411 <author email="person">person</author>
412 412 <date>1970-01-18T08:40:00+00:00</date>
413 413 <msg xml:space="preserve">new head</msg>
414 414 <paths>
415 415 <path action="A">d</path>
416 416 </paths>
417 417 </logentry>
418 418 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
419 419 <branch>foo</branch>
420 420 <author email="person">person</author>
421 421 <date>1970-01-17T04:53:20+00:00</date>
422 422 <msg xml:space="preserve">new branch</msg>
423 423 <paths>
424 424 </paths>
425 425 </logentry>
426 426 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
427 427 <author email="person">person</author>
428 428 <date>1970-01-16T01:06:40+00:00</date>
429 429 <msg xml:space="preserve">no user, no domain</msg>
430 430 <paths>
431 431 <path action="M">c</path>
432 432 </paths>
433 433 </logentry>
434 434 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
435 435 <author email="other@place">other</author>
436 436 <date>1970-01-14T21:20:00+00:00</date>
437 437 <msg xml:space="preserve">no person</msg>
438 438 <paths>
439 439 <path action="A">c</path>
440 440 </paths>
441 441 </logentry>
442 442 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
443 443 <author email="other@place">A. N. Other</author>
444 444 <date>1970-01-13T17:33:20+00:00</date>
445 445 <msg xml:space="preserve">other 1
446 446 other 2
447 447
448 448 other 3</msg>
449 449 <paths>
450 450 <path action="A">b</path>
451 451 </paths>
452 452 </logentry>
453 453 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
454 454 <author email="user@hostname">User Name</author>
455 455 <date>1970-01-12T13:46:40+00:00</date>
456 456 <msg xml:space="preserve">line 1
457 457 line 2</msg>
458 458 <paths>
459 459 <path action="A">a</path>
460 460 </paths>
461 461 </logentry>
462 462 </log>
463 463
464 464 $ hg log --debug --style xml
465 465 <?xml version="1.0"?>
466 466 <log>
467 467 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
468 468 <tag>tip</tag>
469 469 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
470 470 <parent revision="-1" node="0000000000000000000000000000000000000000" />
471 471 <author email="test">test</author>
472 472 <date>2020-01-01T10:01:00+00:00</date>
473 473 <msg xml:space="preserve">third</msg>
474 474 <paths>
475 475 <path action="A">fourth</path>
476 476 <path action="A">third</path>
477 477 <path action="R">second</path>
478 478 </paths>
479 479 <copies>
480 480 <copy source="second">fourth</copy>
481 481 </copies>
482 482 <extra key="branch">default</extra>
483 483 </logentry>
484 484 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
485 485 <parent revision="-1" node="0000000000000000000000000000000000000000" />
486 486 <parent revision="-1" node="0000000000000000000000000000000000000000" />
487 487 <author email="user@hostname">User Name</author>
488 488 <date>1970-01-12T13:46:40+00:00</date>
489 489 <msg xml:space="preserve">second</msg>
490 490 <paths>
491 491 <path action="A">second</path>
492 492 </paths>
493 493 <extra key="branch">default</extra>
494 494 </logentry>
495 495 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
496 496 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
497 497 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
498 498 <author email="person">person</author>
499 499 <date>1970-01-18T08:40:01+00:00</date>
500 500 <msg xml:space="preserve">merge</msg>
501 501 <paths>
502 502 </paths>
503 503 <extra key="branch">default</extra>
504 504 </logentry>
505 505 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
506 506 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
507 507 <parent revision="-1" node="0000000000000000000000000000000000000000" />
508 508 <author email="person">person</author>
509 509 <date>1970-01-18T08:40:00+00:00</date>
510 510 <msg xml:space="preserve">new head</msg>
511 511 <paths>
512 512 <path action="A">d</path>
513 513 </paths>
514 514 <extra key="branch">default</extra>
515 515 </logentry>
516 516 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
517 517 <branch>foo</branch>
518 518 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
519 519 <parent revision="-1" node="0000000000000000000000000000000000000000" />
520 520 <author email="person">person</author>
521 521 <date>1970-01-17T04:53:20+00:00</date>
522 522 <msg xml:space="preserve">new branch</msg>
523 523 <paths>
524 524 </paths>
525 525 <extra key="branch">foo</extra>
526 526 </logentry>
527 527 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
528 528 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
529 529 <parent revision="-1" node="0000000000000000000000000000000000000000" />
530 530 <author email="person">person</author>
531 531 <date>1970-01-16T01:06:40+00:00</date>
532 532 <msg xml:space="preserve">no user, no domain</msg>
533 533 <paths>
534 534 <path action="M">c</path>
535 535 </paths>
536 536 <extra key="branch">default</extra>
537 537 </logentry>
538 538 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
539 539 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
540 540 <parent revision="-1" node="0000000000000000000000000000000000000000" />
541 541 <author email="other@place">other</author>
542 542 <date>1970-01-14T21:20:00+00:00</date>
543 543 <msg xml:space="preserve">no person</msg>
544 544 <paths>
545 545 <path action="A">c</path>
546 546 </paths>
547 547 <extra key="branch">default</extra>
548 548 </logentry>
549 549 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
550 550 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
551 551 <parent revision="-1" node="0000000000000000000000000000000000000000" />
552 552 <author email="other@place">A. N. Other</author>
553 553 <date>1970-01-13T17:33:20+00:00</date>
554 554 <msg xml:space="preserve">other 1
555 555 other 2
556 556
557 557 other 3</msg>
558 558 <paths>
559 559 <path action="A">b</path>
560 560 </paths>
561 561 <extra key="branch">default</extra>
562 562 </logentry>
563 563 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
564 564 <parent revision="-1" node="0000000000000000000000000000000000000000" />
565 565 <parent revision="-1" node="0000000000000000000000000000000000000000" />
566 566 <author email="user@hostname">User Name</author>
567 567 <date>1970-01-12T13:46:40+00:00</date>
568 568 <msg xml:space="preserve">line 1
569 569 line 2</msg>
570 570 <paths>
571 571 <path action="A">a</path>
572 572 </paths>
573 573 <extra key="branch">default</extra>
574 574 </logentry>
575 575 </log>
576 576
577 577
578 578 Test JSON style:
579 579
580 580 $ hg log -k nosuch -Tjson
581 581 []
582 582
583 583 $ hg log -qr . -Tjson
584 584 [
585 585 {
586 586 "rev": 8,
587 587 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
588 588 }
589 589 ]
590 590
591 591 $ hg log -vpr . -Tjson --stat
592 592 [
593 593 {
594 594 "rev": 8,
595 595 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
596 596 "branch": "default",
597 597 "phase": "draft",
598 598 "user": "test",
599 599 "date": [1577872860, 0],
600 600 "desc": "third",
601 601 "bookmarks": [],
602 602 "tags": ["tip"],
603 603 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
604 604 "files": ["fourth", "second", "third"],
605 605 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
606 606 "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"
607 607 }
608 608 ]
609 609
610 610 honor --git but not format-breaking diffopts
611 611 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
612 612 [
613 613 {
614 614 "rev": 8,
615 615 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
616 616 "branch": "default",
617 617 "phase": "draft",
618 618 "user": "test",
619 619 "date": [1577872860, 0],
620 620 "desc": "third",
621 621 "bookmarks": [],
622 622 "tags": ["tip"],
623 623 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
624 624 "files": ["fourth", "second", "third"],
625 625 "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"
626 626 }
627 627 ]
628 628
629 629 $ hg log -T json
630 630 [
631 631 {
632 632 "rev": 8,
633 633 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
634 634 "branch": "default",
635 635 "phase": "draft",
636 636 "user": "test",
637 637 "date": [1577872860, 0],
638 638 "desc": "third",
639 639 "bookmarks": [],
640 640 "tags": ["tip"],
641 641 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
642 642 },
643 643 {
644 644 "rev": 7,
645 645 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
646 646 "branch": "default",
647 647 "phase": "draft",
648 648 "user": "User Name <user@hostname>",
649 649 "date": [1000000, 0],
650 650 "desc": "second",
651 651 "bookmarks": [],
652 652 "tags": [],
653 653 "parents": ["0000000000000000000000000000000000000000"]
654 654 },
655 655 {
656 656 "rev": 6,
657 657 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
658 658 "branch": "default",
659 659 "phase": "draft",
660 660 "user": "person",
661 661 "date": [1500001, 0],
662 662 "desc": "merge",
663 663 "bookmarks": [],
664 664 "tags": [],
665 665 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
666 666 },
667 667 {
668 668 "rev": 5,
669 669 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
670 670 "branch": "default",
671 671 "phase": "draft",
672 672 "user": "person",
673 673 "date": [1500000, 0],
674 674 "desc": "new head",
675 675 "bookmarks": [],
676 676 "tags": [],
677 677 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
678 678 },
679 679 {
680 680 "rev": 4,
681 681 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
682 682 "branch": "foo",
683 683 "phase": "draft",
684 684 "user": "person",
685 685 "date": [1400000, 0],
686 686 "desc": "new branch",
687 687 "bookmarks": [],
688 688 "tags": [],
689 689 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
690 690 },
691 691 {
692 692 "rev": 3,
693 693 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
694 694 "branch": "default",
695 695 "phase": "draft",
696 696 "user": "person",
697 697 "date": [1300000, 0],
698 698 "desc": "no user, no domain",
699 699 "bookmarks": [],
700 700 "tags": [],
701 701 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
702 702 },
703 703 {
704 704 "rev": 2,
705 705 "node": "97054abb4ab824450e9164180baf491ae0078465",
706 706 "branch": "default",
707 707 "phase": "draft",
708 708 "user": "other@place",
709 709 "date": [1200000, 0],
710 710 "desc": "no person",
711 711 "bookmarks": [],
712 712 "tags": [],
713 713 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
714 714 },
715 715 {
716 716 "rev": 1,
717 717 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
718 718 "branch": "default",
719 719 "phase": "draft",
720 720 "user": "A. N. Other <other@place>",
721 721 "date": [1100000, 0],
722 722 "desc": "other 1\nother 2\n\nother 3",
723 723 "bookmarks": [],
724 724 "tags": [],
725 725 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
726 726 },
727 727 {
728 728 "rev": 0,
729 729 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
730 730 "branch": "default",
731 731 "phase": "draft",
732 732 "user": "User Name <user@hostname>",
733 733 "date": [1000000, 0],
734 734 "desc": "line 1\nline 2",
735 735 "bookmarks": [],
736 736 "tags": [],
737 737 "parents": ["0000000000000000000000000000000000000000"]
738 738 }
739 739 ]
740 740
741 741 $ hg heads -v -Tjson
742 742 [
743 743 {
744 744 "rev": 8,
745 745 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
746 746 "branch": "default",
747 747 "phase": "draft",
748 748 "user": "test",
749 749 "date": [1577872860, 0],
750 750 "desc": "third",
751 751 "bookmarks": [],
752 752 "tags": ["tip"],
753 753 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
754 754 "files": ["fourth", "second", "third"]
755 755 },
756 756 {
757 757 "rev": 6,
758 758 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
759 759 "branch": "default",
760 760 "phase": "draft",
761 761 "user": "person",
762 762 "date": [1500001, 0],
763 763 "desc": "merge",
764 764 "bookmarks": [],
765 765 "tags": [],
766 766 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
767 767 "files": []
768 768 },
769 769 {
770 770 "rev": 4,
771 771 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
772 772 "branch": "foo",
773 773 "phase": "draft",
774 774 "user": "person",
775 775 "date": [1400000, 0],
776 776 "desc": "new branch",
777 777 "bookmarks": [],
778 778 "tags": [],
779 779 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
780 780 "files": []
781 781 }
782 782 ]
783 783
784 784 $ hg log --debug -Tjson
785 785 [
786 786 {
787 787 "rev": 8,
788 788 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
789 789 "branch": "default",
790 790 "phase": "draft",
791 791 "user": "test",
792 792 "date": [1577872860, 0],
793 793 "desc": "third",
794 794 "bookmarks": [],
795 795 "tags": ["tip"],
796 796 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
797 797 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
798 798 "extra": {"branch": "default"},
799 799 "modified": [],
800 800 "added": ["fourth", "third"],
801 801 "removed": ["second"]
802 802 },
803 803 {
804 804 "rev": 7,
805 805 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
806 806 "branch": "default",
807 807 "phase": "draft",
808 808 "user": "User Name <user@hostname>",
809 809 "date": [1000000, 0],
810 810 "desc": "second",
811 811 "bookmarks": [],
812 812 "tags": [],
813 813 "parents": ["0000000000000000000000000000000000000000"],
814 814 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
815 815 "extra": {"branch": "default"},
816 816 "modified": [],
817 817 "added": ["second"],
818 818 "removed": []
819 819 },
820 820 {
821 821 "rev": 6,
822 822 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
823 823 "branch": "default",
824 824 "phase": "draft",
825 825 "user": "person",
826 826 "date": [1500001, 0],
827 827 "desc": "merge",
828 828 "bookmarks": [],
829 829 "tags": [],
830 830 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
831 831 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
832 832 "extra": {"branch": "default"},
833 833 "modified": [],
834 834 "added": [],
835 835 "removed": []
836 836 },
837 837 {
838 838 "rev": 5,
839 839 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
840 840 "branch": "default",
841 841 "phase": "draft",
842 842 "user": "person",
843 843 "date": [1500000, 0],
844 844 "desc": "new head",
845 845 "bookmarks": [],
846 846 "tags": [],
847 847 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
848 848 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
849 849 "extra": {"branch": "default"},
850 850 "modified": [],
851 851 "added": ["d"],
852 852 "removed": []
853 853 },
854 854 {
855 855 "rev": 4,
856 856 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
857 857 "branch": "foo",
858 858 "phase": "draft",
859 859 "user": "person",
860 860 "date": [1400000, 0],
861 861 "desc": "new branch",
862 862 "bookmarks": [],
863 863 "tags": [],
864 864 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
865 865 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
866 866 "extra": {"branch": "foo"},
867 867 "modified": [],
868 868 "added": [],
869 869 "removed": []
870 870 },
871 871 {
872 872 "rev": 3,
873 873 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
874 874 "branch": "default",
875 875 "phase": "draft",
876 876 "user": "person",
877 877 "date": [1300000, 0],
878 878 "desc": "no user, no domain",
879 879 "bookmarks": [],
880 880 "tags": [],
881 881 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
882 882 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
883 883 "extra": {"branch": "default"},
884 884 "modified": ["c"],
885 885 "added": [],
886 886 "removed": []
887 887 },
888 888 {
889 889 "rev": 2,
890 890 "node": "97054abb4ab824450e9164180baf491ae0078465",
891 891 "branch": "default",
892 892 "phase": "draft",
893 893 "user": "other@place",
894 894 "date": [1200000, 0],
895 895 "desc": "no person",
896 896 "bookmarks": [],
897 897 "tags": [],
898 898 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
899 899 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
900 900 "extra": {"branch": "default"},
901 901 "modified": [],
902 902 "added": ["c"],
903 903 "removed": []
904 904 },
905 905 {
906 906 "rev": 1,
907 907 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
908 908 "branch": "default",
909 909 "phase": "draft",
910 910 "user": "A. N. Other <other@place>",
911 911 "date": [1100000, 0],
912 912 "desc": "other 1\nother 2\n\nother 3",
913 913 "bookmarks": [],
914 914 "tags": [],
915 915 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
916 916 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
917 917 "extra": {"branch": "default"},
918 918 "modified": [],
919 919 "added": ["b"],
920 920 "removed": []
921 921 },
922 922 {
923 923 "rev": 0,
924 924 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
925 925 "branch": "default",
926 926 "phase": "draft",
927 927 "user": "User Name <user@hostname>",
928 928 "date": [1000000, 0],
929 929 "desc": "line 1\nline 2",
930 930 "bookmarks": [],
931 931 "tags": [],
932 932 "parents": ["0000000000000000000000000000000000000000"],
933 933 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
934 934 "extra": {"branch": "default"},
935 935 "modified": [],
936 936 "added": ["a"],
937 937 "removed": []
938 938 }
939 939 ]
940 940
941 941 Error if style not readable:
942 942
943 943 #if unix-permissions no-root
944 944 $ touch q
945 945 $ chmod 0 q
946 946 $ hg log --style ./q
947 947 abort: Permission denied: ./q
948 948 [255]
949 949 #endif
950 950
951 951 Error if no style:
952 952
953 953 $ hg log --style notexist
954 954 abort: style 'notexist' not found
955 955 (available styles: bisect, changelog, compact, default, phases, xml)
956 956 [255]
957 957
958 958 $ hg log -T list
959 959 available styles: bisect, changelog, compact, default, phases, xml
960 960 abort: specify a template
961 961 [255]
962 962
963 963 Error if style missing key:
964 964
965 965 $ echo 'q = q' > t
966 966 $ hg log --style ./t
967 967 abort: "changeset" not in template map
968 968 [255]
969 969
970 970 Error if style missing value:
971 971
972 972 $ echo 'changeset =' > t
973 973 $ hg log --style t
974 974 abort: t:1: missing value
975 975 [255]
976 976
977 977 Error if include fails:
978 978
979 979 $ echo 'changeset = q' >> t
980 980 #if unix-permissions no-root
981 981 $ hg log --style ./t
982 982 abort: template file ./q: Permission denied
983 983 [255]
984 984 $ rm q
985 985 #endif
986 986
987 987 Include works:
988 988
989 989 $ echo '{rev}' > q
990 990 $ hg log --style ./t
991 991 8
992 992 7
993 993 6
994 994 5
995 995 4
996 996 3
997 997 2
998 998 1
999 999 0
1000 1000
1001 1001 Check that {phase} works correctly on parents:
1002 1002
1003 1003 $ cat << EOF > parentphase
1004 1004 > changeset_debug = '{rev} ({phase}):{parents}\n'
1005 1005 > parent = ' {rev} ({phase})'
1006 1006 > EOF
1007 1007 $ hg phase -r 5 --public
1008 1008 $ hg phase -r 7 --secret --force
1009 1009 $ hg log --debug -G --style ./parentphase
1010 1010 @ 8 (secret): 7 (secret) -1 (public)
1011 1011 |
1012 1012 o 7 (secret): -1 (public) -1 (public)
1013 1013
1014 1014 o 6 (draft): 5 (public) 4 (draft)
1015 1015 |\
1016 1016 | o 5 (public): 3 (public) -1 (public)
1017 1017 | |
1018 1018 o | 4 (draft): 3 (public) -1 (public)
1019 1019 |/
1020 1020 o 3 (public): 2 (public) -1 (public)
1021 1021 |
1022 1022 o 2 (public): 1 (public) -1 (public)
1023 1023 |
1024 1024 o 1 (public): 0 (public) -1 (public)
1025 1025 |
1026 1026 o 0 (public): -1 (public) -1 (public)
1027 1027
1028 1028
1029 1029 Missing non-standard names give no error (backward compatibility):
1030 1030
1031 1031 $ echo "changeset = '{c}'" > t
1032 1032 $ hg log --style ./t
1033 1033
1034 1034 Defining non-standard name works:
1035 1035
1036 1036 $ cat <<EOF > t
1037 1037 > changeset = '{c}'
1038 1038 > c = q
1039 1039 > EOF
1040 1040 $ hg log --style ./t
1041 1041 8
1042 1042 7
1043 1043 6
1044 1044 5
1045 1045 4
1046 1046 3
1047 1047 2
1048 1048 1
1049 1049 0
1050 1050
1051 1051 ui.style works:
1052 1052
1053 1053 $ echo '[ui]' > .hg/hgrc
1054 1054 $ echo 'style = t' >> .hg/hgrc
1055 1055 $ hg log
1056 1056 8
1057 1057 7
1058 1058 6
1059 1059 5
1060 1060 4
1061 1061 3
1062 1062 2
1063 1063 1
1064 1064 0
1065 1065
1066 1066
1067 1067 Issue338:
1068 1068
1069 1069 $ hg log --style=changelog > changelog
1070 1070
1071 1071 $ cat changelog
1072 1072 2020-01-01 test <test>
1073 1073
1074 1074 * fourth, second, third:
1075 1075 third
1076 1076 [95c24699272e] [tip]
1077 1077
1078 1078 1970-01-12 User Name <user@hostname>
1079 1079
1080 1080 * second:
1081 1081 second
1082 1082 [29114dbae42b]
1083 1083
1084 1084 1970-01-18 person <person>
1085 1085
1086 1086 * merge
1087 1087 [d41e714fe50d]
1088 1088
1089 1089 * d:
1090 1090 new head
1091 1091 [13207e5a10d9]
1092 1092
1093 1093 1970-01-17 person <person>
1094 1094
1095 1095 * new branch
1096 1096 [bbe44766e73d] <foo>
1097 1097
1098 1098 1970-01-16 person <person>
1099 1099
1100 1100 * c:
1101 1101 no user, no domain
1102 1102 [10e46f2dcbf4]
1103 1103
1104 1104 1970-01-14 other <other@place>
1105 1105
1106 1106 * c:
1107 1107 no person
1108 1108 [97054abb4ab8]
1109 1109
1110 1110 1970-01-13 A. N. Other <other@place>
1111 1111
1112 1112 * b:
1113 1113 other 1 other 2
1114 1114
1115 1115 other 3
1116 1116 [b608e9d1a3f0]
1117 1117
1118 1118 1970-01-12 User Name <user@hostname>
1119 1119
1120 1120 * a:
1121 1121 line 1 line 2
1122 1122 [1e4e1b8f71e0]
1123 1123
1124 1124
1125 1125 Issue2130: xml output for 'hg heads' is malformed
1126 1126
1127 1127 $ hg heads --style changelog
1128 1128 2020-01-01 test <test>
1129 1129
1130 1130 * fourth, second, third:
1131 1131 third
1132 1132 [95c24699272e] [tip]
1133 1133
1134 1134 1970-01-18 person <person>
1135 1135
1136 1136 * merge
1137 1137 [d41e714fe50d]
1138 1138
1139 1139 1970-01-17 person <person>
1140 1140
1141 1141 * new branch
1142 1142 [bbe44766e73d] <foo>
1143 1143
1144 1144
1145 1145 Keys work:
1146 1146
1147 1147 $ for key in author branch branches date desc file_adds file_dels file_mods \
1148 1148 > file_copies file_copies_switch files \
1149 1149 > manifest node parents rev tags diffstat extras \
1150 1150 > p1rev p2rev p1node p2node; do
1151 1151 > for mode in '' --verbose --debug; do
1152 1152 > hg log $mode --template "$key$mode: {$key}\n"
1153 1153 > done
1154 1154 > done
1155 1155 author: test
1156 1156 author: User Name <user@hostname>
1157 1157 author: person
1158 1158 author: person
1159 1159 author: person
1160 1160 author: person
1161 1161 author: other@place
1162 1162 author: A. N. Other <other@place>
1163 1163 author: User Name <user@hostname>
1164 1164 author--verbose: test
1165 1165 author--verbose: User Name <user@hostname>
1166 1166 author--verbose: person
1167 1167 author--verbose: person
1168 1168 author--verbose: person
1169 1169 author--verbose: person
1170 1170 author--verbose: other@place
1171 1171 author--verbose: A. N. Other <other@place>
1172 1172 author--verbose: User Name <user@hostname>
1173 1173 author--debug: test
1174 1174 author--debug: User Name <user@hostname>
1175 1175 author--debug: person
1176 1176 author--debug: person
1177 1177 author--debug: person
1178 1178 author--debug: person
1179 1179 author--debug: other@place
1180 1180 author--debug: A. N. Other <other@place>
1181 1181 author--debug: User Name <user@hostname>
1182 1182 branch: default
1183 1183 branch: default
1184 1184 branch: default
1185 1185 branch: default
1186 1186 branch: foo
1187 1187 branch: default
1188 1188 branch: default
1189 1189 branch: default
1190 1190 branch: default
1191 1191 branch--verbose: default
1192 1192 branch--verbose: default
1193 1193 branch--verbose: default
1194 1194 branch--verbose: default
1195 1195 branch--verbose: foo
1196 1196 branch--verbose: default
1197 1197 branch--verbose: default
1198 1198 branch--verbose: default
1199 1199 branch--verbose: default
1200 1200 branch--debug: default
1201 1201 branch--debug: default
1202 1202 branch--debug: default
1203 1203 branch--debug: default
1204 1204 branch--debug: foo
1205 1205 branch--debug: default
1206 1206 branch--debug: default
1207 1207 branch--debug: default
1208 1208 branch--debug: default
1209 1209 branches:
1210 1210 branches:
1211 1211 branches:
1212 1212 branches:
1213 1213 branches: foo
1214 1214 branches:
1215 1215 branches:
1216 1216 branches:
1217 1217 branches:
1218 1218 branches--verbose:
1219 1219 branches--verbose:
1220 1220 branches--verbose:
1221 1221 branches--verbose:
1222 1222 branches--verbose: foo
1223 1223 branches--verbose:
1224 1224 branches--verbose:
1225 1225 branches--verbose:
1226 1226 branches--verbose:
1227 1227 branches--debug:
1228 1228 branches--debug:
1229 1229 branches--debug:
1230 1230 branches--debug:
1231 1231 branches--debug: foo
1232 1232 branches--debug:
1233 1233 branches--debug:
1234 1234 branches--debug:
1235 1235 branches--debug:
1236 1236 date: 1577872860.00
1237 1237 date: 1000000.00
1238 1238 date: 1500001.00
1239 1239 date: 1500000.00
1240 1240 date: 1400000.00
1241 1241 date: 1300000.00
1242 1242 date: 1200000.00
1243 1243 date: 1100000.00
1244 1244 date: 1000000.00
1245 1245 date--verbose: 1577872860.00
1246 1246 date--verbose: 1000000.00
1247 1247 date--verbose: 1500001.00
1248 1248 date--verbose: 1500000.00
1249 1249 date--verbose: 1400000.00
1250 1250 date--verbose: 1300000.00
1251 1251 date--verbose: 1200000.00
1252 1252 date--verbose: 1100000.00
1253 1253 date--verbose: 1000000.00
1254 1254 date--debug: 1577872860.00
1255 1255 date--debug: 1000000.00
1256 1256 date--debug: 1500001.00
1257 1257 date--debug: 1500000.00
1258 1258 date--debug: 1400000.00
1259 1259 date--debug: 1300000.00
1260 1260 date--debug: 1200000.00
1261 1261 date--debug: 1100000.00
1262 1262 date--debug: 1000000.00
1263 1263 desc: third
1264 1264 desc: second
1265 1265 desc: merge
1266 1266 desc: new head
1267 1267 desc: new branch
1268 1268 desc: no user, no domain
1269 1269 desc: no person
1270 1270 desc: other 1
1271 1271 other 2
1272 1272
1273 1273 other 3
1274 1274 desc: line 1
1275 1275 line 2
1276 1276 desc--verbose: third
1277 1277 desc--verbose: second
1278 1278 desc--verbose: merge
1279 1279 desc--verbose: new head
1280 1280 desc--verbose: new branch
1281 1281 desc--verbose: no user, no domain
1282 1282 desc--verbose: no person
1283 1283 desc--verbose: other 1
1284 1284 other 2
1285 1285
1286 1286 other 3
1287 1287 desc--verbose: line 1
1288 1288 line 2
1289 1289 desc--debug: third
1290 1290 desc--debug: second
1291 1291 desc--debug: merge
1292 1292 desc--debug: new head
1293 1293 desc--debug: new branch
1294 1294 desc--debug: no user, no domain
1295 1295 desc--debug: no person
1296 1296 desc--debug: other 1
1297 1297 other 2
1298 1298
1299 1299 other 3
1300 1300 desc--debug: line 1
1301 1301 line 2
1302 1302 file_adds: fourth third
1303 1303 file_adds: second
1304 1304 file_adds:
1305 1305 file_adds: d
1306 1306 file_adds:
1307 1307 file_adds:
1308 1308 file_adds: c
1309 1309 file_adds: b
1310 1310 file_adds: a
1311 1311 file_adds--verbose: fourth third
1312 1312 file_adds--verbose: second
1313 1313 file_adds--verbose:
1314 1314 file_adds--verbose: d
1315 1315 file_adds--verbose:
1316 1316 file_adds--verbose:
1317 1317 file_adds--verbose: c
1318 1318 file_adds--verbose: b
1319 1319 file_adds--verbose: a
1320 1320 file_adds--debug: fourth third
1321 1321 file_adds--debug: second
1322 1322 file_adds--debug:
1323 1323 file_adds--debug: d
1324 1324 file_adds--debug:
1325 1325 file_adds--debug:
1326 1326 file_adds--debug: c
1327 1327 file_adds--debug: b
1328 1328 file_adds--debug: a
1329 1329 file_dels: second
1330 1330 file_dels:
1331 1331 file_dels:
1332 1332 file_dels:
1333 1333 file_dels:
1334 1334 file_dels:
1335 1335 file_dels:
1336 1336 file_dels:
1337 1337 file_dels:
1338 1338 file_dels--verbose: second
1339 1339 file_dels--verbose:
1340 1340 file_dels--verbose:
1341 1341 file_dels--verbose:
1342 1342 file_dels--verbose:
1343 1343 file_dels--verbose:
1344 1344 file_dels--verbose:
1345 1345 file_dels--verbose:
1346 1346 file_dels--verbose:
1347 1347 file_dels--debug: second
1348 1348 file_dels--debug:
1349 1349 file_dels--debug:
1350 1350 file_dels--debug:
1351 1351 file_dels--debug:
1352 1352 file_dels--debug:
1353 1353 file_dels--debug:
1354 1354 file_dels--debug:
1355 1355 file_dels--debug:
1356 1356 file_mods:
1357 1357 file_mods:
1358 1358 file_mods:
1359 1359 file_mods:
1360 1360 file_mods:
1361 1361 file_mods: c
1362 1362 file_mods:
1363 1363 file_mods:
1364 1364 file_mods:
1365 1365 file_mods--verbose:
1366 1366 file_mods--verbose:
1367 1367 file_mods--verbose:
1368 1368 file_mods--verbose:
1369 1369 file_mods--verbose:
1370 1370 file_mods--verbose: c
1371 1371 file_mods--verbose:
1372 1372 file_mods--verbose:
1373 1373 file_mods--verbose:
1374 1374 file_mods--debug:
1375 1375 file_mods--debug:
1376 1376 file_mods--debug:
1377 1377 file_mods--debug:
1378 1378 file_mods--debug:
1379 1379 file_mods--debug: c
1380 1380 file_mods--debug:
1381 1381 file_mods--debug:
1382 1382 file_mods--debug:
1383 1383 file_copies: fourth (second)
1384 1384 file_copies:
1385 1385 file_copies:
1386 1386 file_copies:
1387 1387 file_copies:
1388 1388 file_copies:
1389 1389 file_copies:
1390 1390 file_copies:
1391 1391 file_copies:
1392 1392 file_copies--verbose: fourth (second)
1393 1393 file_copies--verbose:
1394 1394 file_copies--verbose:
1395 1395 file_copies--verbose:
1396 1396 file_copies--verbose:
1397 1397 file_copies--verbose:
1398 1398 file_copies--verbose:
1399 1399 file_copies--verbose:
1400 1400 file_copies--verbose:
1401 1401 file_copies--debug: fourth (second)
1402 1402 file_copies--debug:
1403 1403 file_copies--debug:
1404 1404 file_copies--debug:
1405 1405 file_copies--debug:
1406 1406 file_copies--debug:
1407 1407 file_copies--debug:
1408 1408 file_copies--debug:
1409 1409 file_copies--debug:
1410 1410 file_copies_switch:
1411 1411 file_copies_switch:
1412 1412 file_copies_switch:
1413 1413 file_copies_switch:
1414 1414 file_copies_switch:
1415 1415 file_copies_switch:
1416 1416 file_copies_switch:
1417 1417 file_copies_switch:
1418 1418 file_copies_switch:
1419 1419 file_copies_switch--verbose:
1420 1420 file_copies_switch--verbose:
1421 1421 file_copies_switch--verbose:
1422 1422 file_copies_switch--verbose:
1423 1423 file_copies_switch--verbose:
1424 1424 file_copies_switch--verbose:
1425 1425 file_copies_switch--verbose:
1426 1426 file_copies_switch--verbose:
1427 1427 file_copies_switch--verbose:
1428 1428 file_copies_switch--debug:
1429 1429 file_copies_switch--debug:
1430 1430 file_copies_switch--debug:
1431 1431 file_copies_switch--debug:
1432 1432 file_copies_switch--debug:
1433 1433 file_copies_switch--debug:
1434 1434 file_copies_switch--debug:
1435 1435 file_copies_switch--debug:
1436 1436 file_copies_switch--debug:
1437 1437 files: fourth second third
1438 1438 files: second
1439 1439 files:
1440 1440 files: d
1441 1441 files:
1442 1442 files: c
1443 1443 files: c
1444 1444 files: b
1445 1445 files: a
1446 1446 files--verbose: fourth second third
1447 1447 files--verbose: second
1448 1448 files--verbose:
1449 1449 files--verbose: d
1450 1450 files--verbose:
1451 1451 files--verbose: c
1452 1452 files--verbose: c
1453 1453 files--verbose: b
1454 1454 files--verbose: a
1455 1455 files--debug: fourth second third
1456 1456 files--debug: second
1457 1457 files--debug:
1458 1458 files--debug: d
1459 1459 files--debug:
1460 1460 files--debug: c
1461 1461 files--debug: c
1462 1462 files--debug: b
1463 1463 files--debug: a
1464 1464 manifest: 6:94961b75a2da
1465 1465 manifest: 5:f2dbc354b94e
1466 1466 manifest: 4:4dc3def4f9b4
1467 1467 manifest: 4:4dc3def4f9b4
1468 1468 manifest: 3:cb5a1327723b
1469 1469 manifest: 3:cb5a1327723b
1470 1470 manifest: 2:6e0e82995c35
1471 1471 manifest: 1:4e8d705b1e53
1472 1472 manifest: 0:a0c8bcbbb45c
1473 1473 manifest--verbose: 6:94961b75a2da
1474 1474 manifest--verbose: 5:f2dbc354b94e
1475 1475 manifest--verbose: 4:4dc3def4f9b4
1476 1476 manifest--verbose: 4:4dc3def4f9b4
1477 1477 manifest--verbose: 3:cb5a1327723b
1478 1478 manifest--verbose: 3:cb5a1327723b
1479 1479 manifest--verbose: 2:6e0e82995c35
1480 1480 manifest--verbose: 1:4e8d705b1e53
1481 1481 manifest--verbose: 0:a0c8bcbbb45c
1482 1482 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1483 1483 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1484 1484 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1485 1485 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1486 1486 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1487 1487 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1488 1488 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1489 1489 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1490 1490 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1491 1491 node: 95c24699272ef57d062b8bccc32c878bf841784a
1492 1492 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1493 1493 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1494 1494 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1495 1495 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1496 1496 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1497 1497 node: 97054abb4ab824450e9164180baf491ae0078465
1498 1498 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1499 1499 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1500 1500 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1501 1501 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1502 1502 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1503 1503 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1504 1504 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1505 1505 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1506 1506 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1507 1507 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1508 1508 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1509 1509 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1510 1510 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1511 1511 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1512 1512 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1513 1513 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1514 1514 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1515 1515 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1516 1516 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1517 1517 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1518 1518 parents:
1519 1519 parents: -1:000000000000
1520 1520 parents: 5:13207e5a10d9 4:bbe44766e73d
1521 1521 parents: 3:10e46f2dcbf4
1522 1522 parents:
1523 1523 parents:
1524 1524 parents:
1525 1525 parents:
1526 1526 parents:
1527 1527 parents--verbose:
1528 1528 parents--verbose: -1:000000000000
1529 1529 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1530 1530 parents--verbose: 3:10e46f2dcbf4
1531 1531 parents--verbose:
1532 1532 parents--verbose:
1533 1533 parents--verbose:
1534 1534 parents--verbose:
1535 1535 parents--verbose:
1536 1536 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1537 1537 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1538 1538 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1539 1539 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1540 1540 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1541 1541 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1542 1542 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1543 1543 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1544 1544 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1545 1545 rev: 8
1546 1546 rev: 7
1547 1547 rev: 6
1548 1548 rev: 5
1549 1549 rev: 4
1550 1550 rev: 3
1551 1551 rev: 2
1552 1552 rev: 1
1553 1553 rev: 0
1554 1554 rev--verbose: 8
1555 1555 rev--verbose: 7
1556 1556 rev--verbose: 6
1557 1557 rev--verbose: 5
1558 1558 rev--verbose: 4
1559 1559 rev--verbose: 3
1560 1560 rev--verbose: 2
1561 1561 rev--verbose: 1
1562 1562 rev--verbose: 0
1563 1563 rev--debug: 8
1564 1564 rev--debug: 7
1565 1565 rev--debug: 6
1566 1566 rev--debug: 5
1567 1567 rev--debug: 4
1568 1568 rev--debug: 3
1569 1569 rev--debug: 2
1570 1570 rev--debug: 1
1571 1571 rev--debug: 0
1572 1572 tags: tip
1573 1573 tags:
1574 1574 tags:
1575 1575 tags:
1576 1576 tags:
1577 1577 tags:
1578 1578 tags:
1579 1579 tags:
1580 1580 tags:
1581 1581 tags--verbose: tip
1582 1582 tags--verbose:
1583 1583 tags--verbose:
1584 1584 tags--verbose:
1585 1585 tags--verbose:
1586 1586 tags--verbose:
1587 1587 tags--verbose:
1588 1588 tags--verbose:
1589 1589 tags--verbose:
1590 1590 tags--debug: tip
1591 1591 tags--debug:
1592 1592 tags--debug:
1593 1593 tags--debug:
1594 1594 tags--debug:
1595 1595 tags--debug:
1596 1596 tags--debug:
1597 1597 tags--debug:
1598 1598 tags--debug:
1599 1599 diffstat: 3: +2/-1
1600 1600 diffstat: 1: +1/-0
1601 1601 diffstat: 0: +0/-0
1602 1602 diffstat: 1: +1/-0
1603 1603 diffstat: 0: +0/-0
1604 1604 diffstat: 1: +1/-0
1605 1605 diffstat: 1: +4/-0
1606 1606 diffstat: 1: +2/-0
1607 1607 diffstat: 1: +1/-0
1608 1608 diffstat--verbose: 3: +2/-1
1609 1609 diffstat--verbose: 1: +1/-0
1610 1610 diffstat--verbose: 0: +0/-0
1611 1611 diffstat--verbose: 1: +1/-0
1612 1612 diffstat--verbose: 0: +0/-0
1613 1613 diffstat--verbose: 1: +1/-0
1614 1614 diffstat--verbose: 1: +4/-0
1615 1615 diffstat--verbose: 1: +2/-0
1616 1616 diffstat--verbose: 1: +1/-0
1617 1617 diffstat--debug: 3: +2/-1
1618 1618 diffstat--debug: 1: +1/-0
1619 1619 diffstat--debug: 0: +0/-0
1620 1620 diffstat--debug: 1: +1/-0
1621 1621 diffstat--debug: 0: +0/-0
1622 1622 diffstat--debug: 1: +1/-0
1623 1623 diffstat--debug: 1: +4/-0
1624 1624 diffstat--debug: 1: +2/-0
1625 1625 diffstat--debug: 1: +1/-0
1626 1626 extras: branch=default
1627 1627 extras: branch=default
1628 1628 extras: branch=default
1629 1629 extras: branch=default
1630 1630 extras: branch=foo
1631 1631 extras: branch=default
1632 1632 extras: branch=default
1633 1633 extras: branch=default
1634 1634 extras: branch=default
1635 1635 extras--verbose: branch=default
1636 1636 extras--verbose: branch=default
1637 1637 extras--verbose: branch=default
1638 1638 extras--verbose: branch=default
1639 1639 extras--verbose: branch=foo
1640 1640 extras--verbose: branch=default
1641 1641 extras--verbose: branch=default
1642 1642 extras--verbose: branch=default
1643 1643 extras--verbose: branch=default
1644 1644 extras--debug: branch=default
1645 1645 extras--debug: branch=default
1646 1646 extras--debug: branch=default
1647 1647 extras--debug: branch=default
1648 1648 extras--debug: branch=foo
1649 1649 extras--debug: branch=default
1650 1650 extras--debug: branch=default
1651 1651 extras--debug: branch=default
1652 1652 extras--debug: branch=default
1653 1653 p1rev: 7
1654 1654 p1rev: -1
1655 1655 p1rev: 5
1656 1656 p1rev: 3
1657 1657 p1rev: 3
1658 1658 p1rev: 2
1659 1659 p1rev: 1
1660 1660 p1rev: 0
1661 1661 p1rev: -1
1662 1662 p1rev--verbose: 7
1663 1663 p1rev--verbose: -1
1664 1664 p1rev--verbose: 5
1665 1665 p1rev--verbose: 3
1666 1666 p1rev--verbose: 3
1667 1667 p1rev--verbose: 2
1668 1668 p1rev--verbose: 1
1669 1669 p1rev--verbose: 0
1670 1670 p1rev--verbose: -1
1671 1671 p1rev--debug: 7
1672 1672 p1rev--debug: -1
1673 1673 p1rev--debug: 5
1674 1674 p1rev--debug: 3
1675 1675 p1rev--debug: 3
1676 1676 p1rev--debug: 2
1677 1677 p1rev--debug: 1
1678 1678 p1rev--debug: 0
1679 1679 p1rev--debug: -1
1680 1680 p2rev: -1
1681 1681 p2rev: -1
1682 1682 p2rev: 4
1683 1683 p2rev: -1
1684 1684 p2rev: -1
1685 1685 p2rev: -1
1686 1686 p2rev: -1
1687 1687 p2rev: -1
1688 1688 p2rev: -1
1689 1689 p2rev--verbose: -1
1690 1690 p2rev--verbose: -1
1691 1691 p2rev--verbose: 4
1692 1692 p2rev--verbose: -1
1693 1693 p2rev--verbose: -1
1694 1694 p2rev--verbose: -1
1695 1695 p2rev--verbose: -1
1696 1696 p2rev--verbose: -1
1697 1697 p2rev--verbose: -1
1698 1698 p2rev--debug: -1
1699 1699 p2rev--debug: -1
1700 1700 p2rev--debug: 4
1701 1701 p2rev--debug: -1
1702 1702 p2rev--debug: -1
1703 1703 p2rev--debug: -1
1704 1704 p2rev--debug: -1
1705 1705 p2rev--debug: -1
1706 1706 p2rev--debug: -1
1707 1707 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1708 1708 p1node: 0000000000000000000000000000000000000000
1709 1709 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1710 1710 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1711 1711 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1712 1712 p1node: 97054abb4ab824450e9164180baf491ae0078465
1713 1713 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1714 1714 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1715 1715 p1node: 0000000000000000000000000000000000000000
1716 1716 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1717 1717 p1node--verbose: 0000000000000000000000000000000000000000
1718 1718 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1719 1719 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1720 1720 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1721 1721 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1722 1722 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1723 1723 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1724 1724 p1node--verbose: 0000000000000000000000000000000000000000
1725 1725 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1726 1726 p1node--debug: 0000000000000000000000000000000000000000
1727 1727 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1728 1728 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1729 1729 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1730 1730 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1731 1731 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1732 1732 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1733 1733 p1node--debug: 0000000000000000000000000000000000000000
1734 1734 p2node: 0000000000000000000000000000000000000000
1735 1735 p2node: 0000000000000000000000000000000000000000
1736 1736 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1737 1737 p2node: 0000000000000000000000000000000000000000
1738 1738 p2node: 0000000000000000000000000000000000000000
1739 1739 p2node: 0000000000000000000000000000000000000000
1740 1740 p2node: 0000000000000000000000000000000000000000
1741 1741 p2node: 0000000000000000000000000000000000000000
1742 1742 p2node: 0000000000000000000000000000000000000000
1743 1743 p2node--verbose: 0000000000000000000000000000000000000000
1744 1744 p2node--verbose: 0000000000000000000000000000000000000000
1745 1745 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1746 1746 p2node--verbose: 0000000000000000000000000000000000000000
1747 1747 p2node--verbose: 0000000000000000000000000000000000000000
1748 1748 p2node--verbose: 0000000000000000000000000000000000000000
1749 1749 p2node--verbose: 0000000000000000000000000000000000000000
1750 1750 p2node--verbose: 0000000000000000000000000000000000000000
1751 1751 p2node--verbose: 0000000000000000000000000000000000000000
1752 1752 p2node--debug: 0000000000000000000000000000000000000000
1753 1753 p2node--debug: 0000000000000000000000000000000000000000
1754 1754 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1755 1755 p2node--debug: 0000000000000000000000000000000000000000
1756 1756 p2node--debug: 0000000000000000000000000000000000000000
1757 1757 p2node--debug: 0000000000000000000000000000000000000000
1758 1758 p2node--debug: 0000000000000000000000000000000000000000
1759 1759 p2node--debug: 0000000000000000000000000000000000000000
1760 1760 p2node--debug: 0000000000000000000000000000000000000000
1761 1761
1762 1762 Filters work:
1763 1763
1764 1764 $ hg log --template '{author|domain}\n'
1765 1765
1766 1766 hostname
1767 1767
1768 1768
1769 1769
1770 1770
1771 1771 place
1772 1772 place
1773 1773 hostname
1774 1774
1775 1775 $ hg log --template '{author|person}\n'
1776 1776 test
1777 1777 User Name
1778 1778 person
1779 1779 person
1780 1780 person
1781 1781 person
1782 1782 other
1783 1783 A. N. Other
1784 1784 User Name
1785 1785
1786 1786 $ hg log --template '{author|user}\n'
1787 1787 test
1788 1788 user
1789 1789 person
1790 1790 person
1791 1791 person
1792 1792 person
1793 1793 other
1794 1794 other
1795 1795 user
1796 1796
1797 1797 $ hg log --template '{date|date}\n'
1798 1798 Wed Jan 01 10:01:00 2020 +0000
1799 1799 Mon Jan 12 13:46:40 1970 +0000
1800 1800 Sun Jan 18 08:40:01 1970 +0000
1801 1801 Sun Jan 18 08:40:00 1970 +0000
1802 1802 Sat Jan 17 04:53:20 1970 +0000
1803 1803 Fri Jan 16 01:06:40 1970 +0000
1804 1804 Wed Jan 14 21:20:00 1970 +0000
1805 1805 Tue Jan 13 17:33:20 1970 +0000
1806 1806 Mon Jan 12 13:46:40 1970 +0000
1807 1807
1808 1808 $ hg log --template '{date|isodate}\n'
1809 1809 2020-01-01 10:01 +0000
1810 1810 1970-01-12 13:46 +0000
1811 1811 1970-01-18 08:40 +0000
1812 1812 1970-01-18 08:40 +0000
1813 1813 1970-01-17 04:53 +0000
1814 1814 1970-01-16 01:06 +0000
1815 1815 1970-01-14 21:20 +0000
1816 1816 1970-01-13 17:33 +0000
1817 1817 1970-01-12 13:46 +0000
1818 1818
1819 1819 $ hg log --template '{date|isodatesec}\n'
1820 1820 2020-01-01 10:01:00 +0000
1821 1821 1970-01-12 13:46:40 +0000
1822 1822 1970-01-18 08:40:01 +0000
1823 1823 1970-01-18 08:40:00 +0000
1824 1824 1970-01-17 04:53:20 +0000
1825 1825 1970-01-16 01:06:40 +0000
1826 1826 1970-01-14 21:20:00 +0000
1827 1827 1970-01-13 17:33:20 +0000
1828 1828 1970-01-12 13:46:40 +0000
1829 1829
1830 1830 $ hg log --template '{date|rfc822date}\n'
1831 1831 Wed, 01 Jan 2020 10:01:00 +0000
1832 1832 Mon, 12 Jan 1970 13:46:40 +0000
1833 1833 Sun, 18 Jan 1970 08:40:01 +0000
1834 1834 Sun, 18 Jan 1970 08:40:00 +0000
1835 1835 Sat, 17 Jan 1970 04:53:20 +0000
1836 1836 Fri, 16 Jan 1970 01:06:40 +0000
1837 1837 Wed, 14 Jan 1970 21:20:00 +0000
1838 1838 Tue, 13 Jan 1970 17:33:20 +0000
1839 1839 Mon, 12 Jan 1970 13:46:40 +0000
1840 1840
1841 1841 $ hg log --template '{desc|firstline}\n'
1842 1842 third
1843 1843 second
1844 1844 merge
1845 1845 new head
1846 1846 new branch
1847 1847 no user, no domain
1848 1848 no person
1849 1849 other 1
1850 1850 line 1
1851 1851
1852 1852 $ hg log --template '{node|short}\n'
1853 1853 95c24699272e
1854 1854 29114dbae42b
1855 1855 d41e714fe50d
1856 1856 13207e5a10d9
1857 1857 bbe44766e73d
1858 1858 10e46f2dcbf4
1859 1859 97054abb4ab8
1860 1860 b608e9d1a3f0
1861 1861 1e4e1b8f71e0
1862 1862
1863 1863 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1864 1864 <changeset author="test"/>
1865 1865 <changeset author="User Name &lt;user@hostname&gt;"/>
1866 1866 <changeset author="person"/>
1867 1867 <changeset author="person"/>
1868 1868 <changeset author="person"/>
1869 1869 <changeset author="person"/>
1870 1870 <changeset author="other@place"/>
1871 1871 <changeset author="A. N. Other &lt;other@place&gt;"/>
1872 1872 <changeset author="User Name &lt;user@hostname&gt;"/>
1873 1873
1874 1874 $ hg log --template '{rev}: {children}\n'
1875 1875 8:
1876 1876 7: 8:95c24699272e
1877 1877 6:
1878 1878 5: 6:d41e714fe50d
1879 1879 4: 6:d41e714fe50d
1880 1880 3: 4:bbe44766e73d 5:13207e5a10d9
1881 1881 2: 3:10e46f2dcbf4
1882 1882 1: 2:97054abb4ab8
1883 1883 0: 1:b608e9d1a3f0
1884 1884
1885 1885 Formatnode filter works:
1886 1886
1887 1887 $ hg -q log -r 0 --template '{node|formatnode}\n'
1888 1888 1e4e1b8f71e0
1889 1889
1890 1890 $ hg log -r 0 --template '{node|formatnode}\n'
1891 1891 1e4e1b8f71e0
1892 1892
1893 1893 $ hg -v log -r 0 --template '{node|formatnode}\n'
1894 1894 1e4e1b8f71e0
1895 1895
1896 1896 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1897 1897 1e4e1b8f71e05681d422154f5421e385fec3454f
1898 1898
1899 1899 Age filter:
1900 1900
1901 1901 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1902 1902
1903 1903 >>> from datetime import datetime, timedelta
1904 1904 >>> fp = open('a', 'w')
1905 1905 >>> n = datetime.now() + timedelta(366 * 7)
1906 1906 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1907 1907 >>> fp.close()
1908 1908 $ hg add a
1909 1909 $ hg commit -m future -d "`cat a`"
1910 1910
1911 1911 $ hg log -l1 --template '{date|age}\n'
1912 1912 7 years from now
1913 1913
1914 1914 Count filter:
1915 1915
1916 1916 $ hg log -l1 --template '{node|count} {node|short|count}\n'
1917 1917 40 12
1918 1918
1919 1919 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
1920 1920 0 1 4
1921 1921
1922 1922 $ hg log -G --template '{rev}: children: {children|count}, \
1923 1923 > tags: {tags|count}, file_adds: {file_adds|count}, \
1924 1924 > ancestors: {revset("ancestors(%s)", rev)|count}'
1925 1925 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
1926 1926 |
1927 1927 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
1928 1928 |
1929 1929 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
1930 1930
1931 1931 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
1932 1932 |\
1933 1933 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
1934 1934 | |
1935 1935 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
1936 1936 |/
1937 1937 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
1938 1938 |
1939 1939 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
1940 1940 |
1941 1941 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
1942 1942 |
1943 1943 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
1944 1944
1945 1945
1946 1946 Upper/lower filters:
1947 1947
1948 1948 $ hg log -r0 --template '{branch|upper}\n'
1949 1949 DEFAULT
1950 1950 $ hg log -r0 --template '{author|lower}\n'
1951 1951 user name <user@hostname>
1952 1952 $ hg log -r0 --template '{date|upper}\n'
1953 1953 abort: template filter 'upper' is not compatible with keyword 'date'
1954 1954 [255]
1955 1955
1956 1956 Error on syntax:
1957 1957
1958 1958 $ echo 'x = "f' >> t
1959 1959 $ hg log
1960 1960 abort: t:3: unmatched quotes
1961 1961 [255]
1962 1962
1963 1963 Behind the scenes, this will throw TypeError
1964 1964
1965 1965 $ hg log -l 3 --template '{date|obfuscate}\n'
1966 1966 abort: template filter 'obfuscate' is not compatible with keyword 'date'
1967 1967 [255]
1968 1968
1969 1969 Behind the scenes, this will throw a ValueError
1970 1970
1971 1971 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
1972 1972 abort: template filter 'shortdate' is not compatible with keyword 'desc'
1973 1973 [255]
1974 1974
1975 1975 Behind the scenes, this will throw AttributeError
1976 1976
1977 1977 $ hg log -l 3 --template 'line: {date|escape}\n'
1978 1978 abort: template filter 'escape' is not compatible with keyword 'date'
1979 1979 [255]
1980 1980
1981 1981 Behind the scenes, this will throw ValueError
1982 1982
1983 1983 $ hg tip --template '{author|email|date}\n'
1984 1984 abort: template filter 'datefilter' is not compatible with keyword 'author'
1985 1985 [255]
1986 1986
1987 1987 Thrown an error if a template function doesn't exist
1988 1988
1989 1989 $ hg tip --template '{foo()}\n'
1990 1990 hg: parse error: unknown function 'foo'
1991 1991 [255]
1992 1992
1993 1993 Pass generator object created by template function to filter
1994 1994
1995 1995 $ hg log -l 1 --template '{if(author, author)|user}\n'
1996 1996 test
1997 1997
1998 1998 Test diff function:
1999 1999
2000 2000 $ hg diff -c 8
2001 2001 diff -r 29114dbae42b -r 95c24699272e fourth
2002 2002 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2003 2003 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2004 2004 @@ -0,0 +1,1 @@
2005 2005 +second
2006 2006 diff -r 29114dbae42b -r 95c24699272e second
2007 2007 --- a/second Mon Jan 12 13:46:40 1970 +0000
2008 2008 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2009 2009 @@ -1,1 +0,0 @@
2010 2010 -second
2011 2011 diff -r 29114dbae42b -r 95c24699272e third
2012 2012 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2013 2013 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2014 2014 @@ -0,0 +1,1 @@
2015 2015 +third
2016 2016
2017 2017 $ hg log -r 8 -T "{diff()}"
2018 2018 diff -r 29114dbae42b -r 95c24699272e fourth
2019 2019 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2020 2020 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2021 2021 @@ -0,0 +1,1 @@
2022 2022 +second
2023 2023 diff -r 29114dbae42b -r 95c24699272e second
2024 2024 --- a/second Mon Jan 12 13:46:40 1970 +0000
2025 2025 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2026 2026 @@ -1,1 +0,0 @@
2027 2027 -second
2028 2028 diff -r 29114dbae42b -r 95c24699272e third
2029 2029 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2030 2030 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2031 2031 @@ -0,0 +1,1 @@
2032 2032 +third
2033 2033
2034 2034 $ hg log -r 8 -T "{diff('glob:f*')}"
2035 2035 diff -r 29114dbae42b -r 95c24699272e fourth
2036 2036 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2037 2037 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2038 2038 @@ -0,0 +1,1 @@
2039 2039 +second
2040 2040
2041 2041 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2042 2042 diff -r 29114dbae42b -r 95c24699272e second
2043 2043 --- a/second Mon Jan 12 13:46:40 1970 +0000
2044 2044 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2045 2045 @@ -1,1 +0,0 @@
2046 2046 -second
2047 2047 diff -r 29114dbae42b -r 95c24699272e third
2048 2048 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2049 2049 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2050 2050 @@ -0,0 +1,1 @@
2051 2051 +third
2052 2052
2053 2053 $ cd ..
2054 2054
2055 2055
2056 2056 latesttag:
2057 2057
2058 2058 $ hg init latesttag
2059 2059 $ cd latesttag
2060 2060
2061 2061 $ echo a > file
2062 2062 $ hg ci -Am a -d '0 0'
2063 2063 adding file
2064 2064
2065 2065 $ echo b >> file
2066 2066 $ hg ci -m b -d '1 0'
2067 2067
2068 2068 $ echo c >> head1
2069 2069 $ hg ci -Am h1c -d '2 0'
2070 2070 adding head1
2071 2071
2072 2072 $ hg update -q 1
2073 2073 $ echo d >> head2
2074 2074 $ hg ci -Am h2d -d '3 0'
2075 2075 adding head2
2076 2076 created new head
2077 2077
2078 2078 $ echo e >> head2
2079 2079 $ hg ci -m h2e -d '4 0'
2080 2080
2081 2081 $ hg merge -q
2082 2082 $ hg ci -m merge -d '5 -3600'
2083 2083
2084 2084 No tag set:
2085 2085
2086 2086 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2087 2087 5: null+5
2088 2088 4: null+4
2089 2089 3: null+3
2090 2090 2: null+3
2091 2091 1: null+2
2092 2092 0: null+1
2093 2093
2094 2094 One common tag: longest path wins:
2095 2095
2096 2096 $ hg tag -r 1 -m t1 -d '6 0' t1
2097 2097 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2098 2098 6: t1+4
2099 2099 5: t1+3
2100 2100 4: t1+2
2101 2101 3: t1+1
2102 2102 2: t1+1
2103 2103 1: t1+0
2104 2104 0: null+1
2105 2105
2106 2106 One ancestor tag: more recent wins:
2107 2107
2108 2108 $ hg tag -r 2 -m t2 -d '7 0' t2
2109 2109 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2110 2110 7: t2+3
2111 2111 6: t2+2
2112 2112 5: t2+1
2113 2113 4: t1+2
2114 2114 3: t1+1
2115 2115 2: t2+0
2116 2116 1: t1+0
2117 2117 0: null+1
2118 2118
2119 2119 Two branch tags: more recent wins:
2120 2120
2121 2121 $ hg tag -r 3 -m t3 -d '8 0' t3
2122 2122 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2123 2123 8: t3+5
2124 2124 7: t3+4
2125 2125 6: t3+3
2126 2126 5: t3+2
2127 2127 4: t3+1
2128 2128 3: t3+0
2129 2129 2: t2+0
2130 2130 1: t1+0
2131 2131 0: null+1
2132 2132
2133 2133 Merged tag overrides:
2134 2134
2135 2135 $ hg tag -r 5 -m t5 -d '9 0' t5
2136 2136 $ hg tag -r 3 -m at3 -d '10 0' at3
2137 2137 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2138 2138 10: t5+5
2139 2139 9: t5+4
2140 2140 8: t5+3
2141 2141 7: t5+2
2142 2142 6: t5+1
2143 2143 5: t5+0
2144 2144 4: at3:t3+1
2145 2145 3: at3:t3+0
2146 2146 2: t2+0
2147 2147 1: t1+0
2148 2148 0: null+1
2149 2149
2150 2150 $ cd ..
2151 2151
2152 2152
2153 2153 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2154 2154 if it is a relative path
2155 2155
2156 2156 $ mkdir -p home/styles
2157 2157
2158 2158 $ cat > home/styles/teststyle <<EOF
2159 2159 > changeset = 'test {rev}:{node|short}\n'
2160 2160 > EOF
2161 2161
2162 2162 $ HOME=`pwd`/home; export HOME
2163 2163
2164 2164 $ cat > latesttag/.hg/hgrc <<EOF
2165 2165 > [ui]
2166 2166 > style = ~/styles/teststyle
2167 2167 > EOF
2168 2168
2169 2169 $ hg -R latesttag tip
2170 2170 test 10:9b4a630e5f5f
2171 2171
2172 2172 Test recursive showlist template (issue1989):
2173 2173
2174 2174 $ cat > style1989 <<EOF
2175 2175 > changeset = '{file_mods}{manifest}{extras}'
2176 2176 > file_mod = 'M|{author|person}\n'
2177 2177 > manifest = '{rev},{author}\n'
2178 2178 > extra = '{key}: {author}\n'
2179 2179 > EOF
2180 2180
2181 2181 $ hg -R latesttag log -r tip --style=style1989
2182 2182 M|test
2183 2183 10,test
2184 2184 branch: test
2185 2185
2186 2186 Test new-style inline templating:
2187 2187
2188 2188 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2189 2189 modified files: .hgtags
2190 2190
2191 2191 Test the sub function of templating for expansion:
2192 2192
2193 2193 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2194 2194 xx
2195 2195
2196 2196 Test the strip function with chars specified:
2197 2197
2198 2198 $ hg log -R latesttag --template '{desc}\n'
2199 2199 at3
2200 2200 t5
2201 2201 t3
2202 2202 t2
2203 2203 t1
2204 2204 merge
2205 2205 h2e
2206 2206 h2d
2207 2207 h1c
2208 2208 b
2209 2209 a
2210 2210
2211 2211 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2212 2212 at3
2213 2213 5
2214 2214 3
2215 2215 2
2216 2216 1
2217 2217 merg
2218 2218 h2
2219 2219 h2d
2220 2220 h1c
2221 2221 b
2222 2222 a
2223 2223
2224 2224 Test date format:
2225 2225
2226 2226 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2227 2227 date: 70 01 01 10 +0000
2228 2228 date: 70 01 01 09 +0000
2229 2229 date: 70 01 01 08 +0000
2230 2230 date: 70 01 01 07 +0000
2231 2231 date: 70 01 01 06 +0000
2232 2232 date: 70 01 01 05 +0100
2233 2233 date: 70 01 01 04 +0000
2234 2234 date: 70 01 01 03 +0000
2235 2235 date: 70 01 01 02 +0000
2236 2236 date: 70 01 01 01 +0000
2237 2237 date: 70 01 01 00 +0000
2238 2238
2239 2239 Test invalid date:
2240 2240
2241 2241 $ hg log -R latesttag -T '{date(rev)}\n'
2242 2242 hg: parse error: date expects a date information
2243 2243 [255]
2244 2244
2245 2245 Test string escaping:
2246 2246
2247 2247 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2248 2248 >
2249 2249 <>\n<[>
2250 2250 <>\n<]>
2251 2251 <>\n<
2252 2252
2253 $ hg log -R latesttag -r 0 \
2254 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2255 >
2256 <>\n<[>
2257 <>\n<]>
2258 <>\n<
2259
2260 $ hg log -R latesttag -r 0 -T esc \
2261 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2262 >
2263 <>\n<[>
2264 <>\n<]>
2265 <>\n<
2266
2267 $ cat <<'EOF' > esctmpl
2268 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2269 > EOF
2270 $ hg log -R latesttag -r 0 --style ./esctmpl
2271 >
2272 <>\n<[>
2273 <>\n<]>
2274 <>\n<
2275
2253 2276 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
2254 2277
2255 2278 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
2256 2279 \x6e
2257 2280 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
2258 2281 \x5c\x786e
2259 2282 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
2260 2283 \x6e
2261 2284 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
2262 2285 \x5c\x786e
2263 2286
2264 2287 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
2265 2288 \x6e
2266 2289 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
2267 2290 \x5c\x786e
2268 2291 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
2269 2292 \x6e
2270 2293 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
2271 2294 \x5c\x786e
2272 2295
2273 2296 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
2274 2297 fourth
2275 2298 second
2276 2299 third
2277 2300 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
2278 2301 fourth\nsecond\nthird
2279 2302
2280 2303 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
2281 2304 <p>
2282 2305 1st
2283 2306 </p>
2284 2307 <p>
2285 2308 2nd
2286 2309 </p>
2287 2310 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
2288 2311 <p>
2289 2312 1st\n\n2nd
2290 2313 </p>
2291 2314 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
2292 2315 1st
2293 2316
2294 2317 2nd
2295 2318
2296 2319 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
2297 2320 o perso
2298 2321 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
2299 2322 no person
2300 2323 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
2301 2324 o perso
2302 2325 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
2303 2326 no perso
2304 2327
2305 2328 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
2306 2329 -o perso-
2307 2330 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
2308 2331 no person
2309 2332 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
2310 2333 \x2do perso\x2d
2311 2334 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
2312 2335 -o perso-
2313 2336 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
2314 2337 \x2do perso\x6e
2315 2338
2316 2339 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
2317 2340 fourth
2318 2341 second
2319 2342 third
2320 2343 $ hg log -R a -r 8 --template '{files % r"{file}\n"}\n'
2321 2344 fourth\nsecond\nthird\n
2322 2345
2323 2346 Test string escaping in nested expression:
2324 2347
2325 2348 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
2326 2349 fourth\x6esecond\x6ethird
2327 2350 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
2328 2351 fourth\x6esecond\x6ethird
2329 2352
2330 2353 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
2331 2354 fourth\x6esecond\x6ethird
2332 2355 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
2333 2356 fourth\x5c\x786esecond\x5c\x786ethird
2334 2357
2335 2358 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
2336 2359 3:\x6eo user, \x6eo domai\x6e
2337 2360 4:\x5c\x786eew bra\x5c\x786ech
2338 2361
2339 2362 Test recursive evaluation:
2340 2363
2341 2364 $ hg init r
2342 2365 $ cd r
2343 2366 $ echo a > a
2344 2367 $ hg ci -Am '{rev}'
2345 2368 adding a
2346 2369 $ hg log -r 0 --template '{if(rev, desc)}\n'
2347 2370 {rev}
2348 2371 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
2349 2372 test 0
2350 2373
2351 2374 $ hg branch -q 'text.{rev}'
2352 2375 $ echo aa >> aa
2353 2376 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
2354 2377
2355 2378 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
2356 2379 {node|short}desc to
2357 2380 text.{rev}be wrapped
2358 2381 text.{rev}desc to be
2359 2382 text.{rev}wrapped (no-eol)
2360 2383 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
2361 2384 bcc7ff960b8e:desc to
2362 2385 text.1:be wrapped
2363 2386 text.1:desc to be
2364 2387 text.1:wrapped (no-eol)
2365 2388
2366 2389 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
2367 2390 {node|short} (no-eol)
2368 2391 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
2369 2392 bcc-ff---b-e (no-eol)
2370 2393
2371 2394 $ cat >> .hg/hgrc <<EOF
2372 2395 > [extensions]
2373 2396 > color=
2374 2397 > [color]
2375 2398 > mode=ansi
2376 2399 > text.{rev} = red
2377 2400 > text.1 = green
2378 2401 > EOF
2379 2402 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
2380 2403 \x1b[0;31mtext\x1b[0m (esc)
2381 2404 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
2382 2405 \x1b[0;32mtext\x1b[0m (esc)
2383 2406
2384 2407 Test branches inside if statement:
2385 2408
2386 2409 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
2387 2410 no
2388 2411
2389 2412 Test get function:
2390 2413
2391 2414 $ hg log -r 0 --template '{get(extras, "branch")}\n'
2392 2415 default
2393 2416 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
2394 2417 hg: parse error: get() expects a dict as first argument
2395 2418 [255]
2396 2419
2397 2420 Test shortest(node) function:
2398 2421
2399 2422 $ echo b > b
2400 2423 $ hg ci -qAm b
2401 2424 $ hg log --template '{shortest(node)}\n'
2402 2425 e777
2403 2426 bcc7
2404 2427 f776
2405 2428 $ hg log --template '{shortest(node, 10)}\n'
2406 2429 e777603221
2407 2430 bcc7ff960b
2408 2431 f7769ec2ab
2409 2432
2410 2433 Test pad function
2411 2434
2412 2435 $ hg log --template '{pad(rev, 20)} {author|user}\n'
2413 2436 2 test
2414 2437 1 {node|short}
2415 2438 0 test
2416 2439
2417 2440 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
2418 2441 2 test
2419 2442 1 {node|short}
2420 2443 0 test
2421 2444
2422 2445 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
2423 2446 2------------------- test
2424 2447 1------------------- {node|short}
2425 2448 0------------------- test
2426 2449
2427 2450 Test ifcontains function
2428 2451
2429 2452 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
2430 2453 2 is in the string
2431 2454 1 is not
2432 2455 0 is in the string
2433 2456
2434 2457 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
2435 2458 2 did not add a
2436 2459 1 did not add a
2437 2460 0 added a
2438 2461
2439 2462 Test revset function
2440 2463
2441 2464 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
2442 2465 2 current rev
2443 2466 1 not current rev
2444 2467 0 not current rev
2445 2468
2446 2469 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
2447 2470 2 match rev
2448 2471 1 match rev
2449 2472 0 not match rev
2450 2473
2451 2474 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
2452 2475 2 Parents: 1
2453 2476 1 Parents: 0
2454 2477 0 Parents:
2455 2478
2456 2479 $ cat >> .hg/hgrc <<EOF
2457 2480 > [revsetalias]
2458 2481 > myparents(\$1) = parents(\$1)
2459 2482 > EOF
2460 2483 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
2461 2484 2 Parents: 1
2462 2485 1 Parents: 0
2463 2486 0 Parents:
2464 2487
2465 2488 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
2466 2489 Rev: 2
2467 2490 Ancestor: 0
2468 2491 Ancestor: 1
2469 2492 Ancestor: 2
2470 2493
2471 2494 Rev: 1
2472 2495 Ancestor: 0
2473 2496 Ancestor: 1
2474 2497
2475 2498 Rev: 0
2476 2499 Ancestor: 0
2477 2500
2478 2501 Test current bookmark templating
2479 2502
2480 2503 $ hg book foo
2481 2504 $ hg book bar
2482 2505 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
2483 2506 2 bar* foo
2484 2507 1
2485 2508 0
2486 2509 $ hg log --template "{rev} {currentbookmark}\n"
2487 2510 2 bar
2488 2511 1
2489 2512 0
2490 2513 $ hg bookmarks --inactive bar
2491 2514 $ hg log --template "{rev} {currentbookmark}\n"
2492 2515 2
2493 2516 1
2494 2517 0
2495 2518 $ hg book -r1 baz
2496 2519 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
2497 2520 2 bar foo
2498 2521 1 baz
2499 2522 0
2500 2523 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
2501 2524 2 t
2502 2525 1 f
2503 2526 0 f
2504 2527
2505 2528 Test stringify on sub expressions
2506 2529
2507 2530 $ cd ..
2508 2531 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
2509 2532 fourth, second, third
2510 2533 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
2511 2534 abc
2512 2535
2513 2536 Test splitlines
2514 2537
2515 2538 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
2516 2539 @ foo future
2517 2540 |
2518 2541 o foo third
2519 2542 |
2520 2543 o foo second
2521 2544
2522 2545 o foo merge
2523 2546 |\
2524 2547 | o foo new head
2525 2548 | |
2526 2549 o | foo new branch
2527 2550 |/
2528 2551 o foo no user, no domain
2529 2552 |
2530 2553 o foo no person
2531 2554 |
2532 2555 o foo other 1
2533 2556 | foo other 2
2534 2557 | foo
2535 2558 | foo other 3
2536 2559 o foo line 1
2537 2560 foo line 2
2538 2561
2539 2562 Test startswith
2540 2563 $ hg log -Gv -R a --template "{startswith(desc)}"
2541 2564 hg: parse error: startswith expects two arguments
2542 2565 [255]
2543 2566
2544 2567 $ hg log -Gv -R a --template "{startswith('line', desc)}"
2545 2568 @
2546 2569 |
2547 2570 o
2548 2571 |
2549 2572 o
2550 2573
2551 2574 o
2552 2575 |\
2553 2576 | o
2554 2577 | |
2555 2578 o |
2556 2579 |/
2557 2580 o
2558 2581 |
2559 2582 o
2560 2583 |
2561 2584 o
2562 2585 |
2563 2586 o line 1
2564 2587 line 2
2565 2588
2566 2589 Test bad template with better error message
2567 2590
2568 2591 $ hg log -Gv -R a --template '{desc|user()}'
2569 2592 hg: parse error: expected a symbol, got 'func'
2570 2593 [255]
2571 2594
2572 2595 Test word function (including index out of bounds graceful failure)
2573 2596
2574 2597 $ hg log -Gv -R a --template "{word('1', desc)}"
2575 2598 @
2576 2599 |
2577 2600 o
2578 2601 |
2579 2602 o
2580 2603
2581 2604 o
2582 2605 |\
2583 2606 | o head
2584 2607 | |
2585 2608 o | branch
2586 2609 |/
2587 2610 o user,
2588 2611 |
2589 2612 o person
2590 2613 |
2591 2614 o 1
2592 2615 |
2593 2616 o 1
2594 2617
2595 2618
2596 2619 Test word third parameter used as splitter
2597 2620
2598 2621 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
2599 2622 @ future
2600 2623 |
2601 2624 o third
2602 2625 |
2603 2626 o sec
2604 2627
2605 2628 o merge
2606 2629 |\
2607 2630 | o new head
2608 2631 | |
2609 2632 o | new branch
2610 2633 |/
2611 2634 o n
2612 2635 |
2613 2636 o n
2614 2637 |
2615 2638 o
2616 2639 |
2617 2640 o line 1
2618 2641 line 2
2619 2642
2620 2643 Test word error messages for not enough and too many arguments
2621 2644
2622 2645 $ hg log -Gv -R a --template "{word('0')}"
2623 2646 hg: parse error: word expects two or three arguments, got 1
2624 2647 [255]
2625 2648
2626 2649 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
2627 2650 hg: parse error: word expects two or three arguments, got 7
2628 2651 [255]
2629 2652
2630 2653 Test word for invalid numbers
2631 2654
2632 2655 $ hg log -Gv -R a --template "{word(2, desc)}"
2633 2656 hg: parse error: Use strings like '3' for numbers passed to word function
2634 2657 [255]
General Comments 0
You need to be logged in to leave comments. Login now