##// END OF EJS Templates
templater: do not preprocess template string in "if" expression (issue4714)...
Yuya Nishihara -
r25471:7298da81 stable
parent child Browse files
Show More
@@ -1,810 +1,811 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 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
91 91 if strtoken == 'string' and bs % 2 == 1:
92 92 # escaped (e.g. '\{', '\\\{', but not '\\{' nor r'\{')
93 93 parsed.append((strtoken, (tmpl[pos:n - 1] + "{")))
94 94 pos = n + 1
95 95 continue
96 96 if n > pos:
97 97 parsed.append((strtoken, tmpl[pos:n]))
98 98
99 99 pd = [tmpl, n + 1, stop]
100 100 parseres, pos = p.parse(pd)
101 101 parsed.append(parseres)
102 102
103 103 return [compileexp(e, context) for e in parsed]
104 104
105 105 def compileexp(exp, context):
106 106 t = exp[0]
107 107 if t in methods:
108 108 return methods[t](exp, context)
109 109 raise error.ParseError(_("unknown method '%s'") % t)
110 110
111 111 # template evaluation
112 112
113 113 def getsymbol(exp):
114 114 if exp[0] == 'symbol':
115 115 return exp[1]
116 116 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
117 117
118 118 def getlist(x):
119 119 if not x:
120 120 return []
121 121 if x[0] == 'list':
122 122 return getlist(x[1]) + [x[2]]
123 123 return [x]
124 124
125 125 def getfilter(exp, context):
126 126 f = getsymbol(exp)
127 127 if f not in context._filters:
128 128 raise error.ParseError(_("unknown function '%s'") % f)
129 129 return context._filters[f]
130 130
131 131 def gettemplate(exp, context):
132 132 if exp[0] == 'string' or exp[0] == 'rawstring':
133 133 return compiletemplate(exp[1], context, strtoken=exp[0])
134 134 if exp[0] == 'symbol':
135 135 return context._load(exp[1])
136 136 raise error.ParseError(_("expected template specifier"))
137 137
138 138 def runstring(context, mapping, data):
139 139 return data.decode("string-escape")
140 140
141 141 def runrawstring(context, mapping, data):
142 142 return data
143 143
144 144 def runsymbol(context, mapping, key):
145 145 v = mapping.get(key)
146 146 if v is None:
147 147 v = context._defaults.get(key)
148 148 if v is None:
149 149 try:
150 150 v = context.process(key, mapping)
151 151 except TemplateNotFound:
152 152 v = ''
153 153 if callable(v):
154 154 return v(**mapping)
155 155 if isinstance(v, types.GeneratorType):
156 156 v = list(v)
157 157 return v
158 158
159 159 def buildfilter(exp, context):
160 160 func, data = compileexp(exp[1], context)
161 161 filt = getfilter(exp[2], context)
162 162 return (runfilter, (func, data, filt))
163 163
164 164 def runfilter(context, mapping, data):
165 165 func, data, filt = data
166 166 # func() may return string, generator of strings or arbitrary object such
167 167 # as date tuple, but filter does not want generator.
168 168 thing = func(context, mapping, data)
169 169 if isinstance(thing, types.GeneratorType):
170 170 thing = stringify(thing)
171 171 try:
172 172 return filt(thing)
173 173 except (ValueError, AttributeError, TypeError):
174 174 if isinstance(data, tuple):
175 175 dt = data[1]
176 176 else:
177 177 dt = data
178 178 raise util.Abort(_("template filter '%s' is not compatible with "
179 179 "keyword '%s'") % (filt.func_name, dt))
180 180
181 181 def buildmap(exp, context):
182 182 func, data = compileexp(exp[1], context)
183 183 ctmpl = gettemplate(exp[2], context)
184 184 return (runmap, (func, data, ctmpl))
185 185
186 186 def runtemplate(context, mapping, template):
187 187 for func, data in template:
188 188 yield func(context, mapping, data)
189 189
190 190 def runmap(context, mapping, data):
191 191 func, data, ctmpl = data
192 192 d = func(context, mapping, data)
193 193 if callable(d):
194 194 d = d()
195 195
196 196 lm = mapping.copy()
197 197
198 198 for i in d:
199 199 if isinstance(i, dict):
200 200 lm.update(i)
201 201 lm['originalnode'] = mapping.get('node')
202 202 yield runtemplate(context, lm, ctmpl)
203 203 else:
204 204 # v is not an iterable of dicts, this happen when 'key'
205 205 # has been fully expanded already and format is useless.
206 206 # If so, return the expanded value.
207 207 yield i
208 208
209 209 def buildfunc(exp, context):
210 210 n = getsymbol(exp[1])
211 211 args = [compileexp(x, context) for x in getlist(exp[2])]
212 212 if n in funcs:
213 213 f = funcs[n]
214 214 return (f, args)
215 215 if n in context._filters:
216 216 if len(args) != 1:
217 217 raise error.ParseError(_("filter %s expects one argument") % n)
218 218 f = context._filters[n]
219 219 return (runfilter, (args[0][0], args[0][1], f))
220 220 raise error.ParseError(_("unknown function '%s'") % n)
221 221
222 222 def date(context, mapping, args):
223 223 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
224 224 strings."""
225 225 if not (1 <= len(args) <= 2):
226 226 # i18n: "date" is a keyword
227 227 raise error.ParseError(_("date expects one or two arguments"))
228 228
229 229 date = args[0][0](context, mapping, args[0][1])
230 230 fmt = None
231 231 if len(args) == 2:
232 232 fmt = stringify(args[1][0](context, mapping, args[1][1]))
233 233 try:
234 234 if fmt is None:
235 235 return util.datestr(date)
236 236 else:
237 237 return util.datestr(date, fmt)
238 238 except (TypeError, ValueError):
239 239 # i18n: "date" is a keyword
240 240 raise error.ParseError(_("date expects a date information"))
241 241
242 242 def diff(context, mapping, args):
243 243 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
244 244 specifying files to include or exclude."""
245 245 if len(args) > 2:
246 246 # i18n: "diff" is a keyword
247 247 raise error.ParseError(_("diff expects one, two or no arguments"))
248 248
249 249 def getpatterns(i):
250 250 if i < len(args):
251 251 s = args[i][1].strip()
252 252 if s:
253 253 return [s]
254 254 return []
255 255
256 256 ctx = mapping['ctx']
257 257 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
258 258
259 259 return ''.join(chunks)
260 260
261 261 def fill(context, mapping, args):
262 262 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
263 263 paragraphs with optional indentation. See the "fill" filter."""
264 264 if not (1 <= len(args) <= 4):
265 265 # i18n: "fill" is a keyword
266 266 raise error.ParseError(_("fill expects one to four arguments"))
267 267
268 268 text = stringify(args[0][0](context, mapping, args[0][1]))
269 269 width = 76
270 270 initindent = ''
271 271 hangindent = ''
272 272 if 2 <= len(args) <= 4:
273 273 try:
274 274 width = int(stringify(args[1][0](context, mapping, args[1][1])))
275 275 except ValueError:
276 276 # i18n: "fill" is a keyword
277 277 raise error.ParseError(_("fill expects an integer width"))
278 278 try:
279 279 initindent = stringify(_evalifliteral(args[2], context, mapping))
280 280 hangindent = stringify(_evalifliteral(args[3], context, mapping))
281 281 except IndexError:
282 282 pass
283 283
284 284 return templatefilters.fill(text, width, initindent, hangindent)
285 285
286 286 def pad(context, mapping, args):
287 287 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
288 288 fill character."""
289 289 if not (2 <= len(args) <= 4):
290 290 # i18n: "pad" is a keyword
291 291 raise error.ParseError(_("pad() expects two to four arguments"))
292 292
293 293 width = int(args[1][1])
294 294
295 295 text = stringify(args[0][0](context, mapping, args[0][1]))
296 296 if args[0][0] == runstring:
297 297 text = stringify(runtemplate(context, mapping,
298 298 compiletemplate(text, context)))
299 299
300 300 right = False
301 301 fillchar = ' '
302 302 if len(args) > 2:
303 303 fillchar = stringify(args[2][0](context, mapping, args[2][1]))
304 304 if len(args) > 3:
305 305 right = util.parsebool(args[3][1])
306 306
307 307 if right:
308 308 return text.rjust(width, fillchar)
309 309 else:
310 310 return text.ljust(width, fillchar)
311 311
312 312 def get(context, mapping, args):
313 313 """:get(dict, key): Get an attribute/key from an object. Some keywords
314 314 are complex types. This function allows you to obtain the value of an
315 315 attribute on these type."""
316 316 if len(args) != 2:
317 317 # i18n: "get" is a keyword
318 318 raise error.ParseError(_("get() expects two arguments"))
319 319
320 320 dictarg = args[0][0](context, mapping, args[0][1])
321 321 if not util.safehasattr(dictarg, 'get'):
322 322 # i18n: "get" is a keyword
323 323 raise error.ParseError(_("get() expects a dict as first argument"))
324 324
325 325 key = args[1][0](context, mapping, args[1][1])
326 326 yield dictarg.get(key)
327 327
328 328 def _evalifliteral(arg, context, mapping):
329 t = stringify(arg[0](context, mapping, arg[1]))
330 if arg[0] == runstring or arg[0] == runrawstring:
329 # get back to token tag to reinterpret string as template
330 strtoken = {runstring: 'string', runrawstring: 'rawstring'}.get(arg[0])
331 if strtoken:
331 332 yield runtemplate(context, mapping,
332 compiletemplate(t, context, strtoken='rawstring'))
333 compiletemplate(arg[1], context, strtoken))
333 334 else:
334 yield t
335 yield stringify(arg[0](context, mapping, arg[1]))
335 336
336 337 def if_(context, mapping, args):
337 338 """:if(expr, then[, else]): Conditionally execute based on the result of
338 339 an expression."""
339 340 if not (2 <= len(args) <= 3):
340 341 # i18n: "if" is a keyword
341 342 raise error.ParseError(_("if expects two or three arguments"))
342 343
343 344 test = stringify(args[0][0](context, mapping, args[0][1]))
344 345 if test:
345 346 yield _evalifliteral(args[1], context, mapping)
346 347 elif len(args) == 3:
347 348 yield _evalifliteral(args[2], context, mapping)
348 349
349 350 def ifcontains(context, mapping, args):
350 351 """:ifcontains(search, thing, then[, else]): Conditionally execute based
351 352 on whether the item "search" is in "thing"."""
352 353 if not (3 <= len(args) <= 4):
353 354 # i18n: "ifcontains" is a keyword
354 355 raise error.ParseError(_("ifcontains expects three or four arguments"))
355 356
356 357 item = stringify(args[0][0](context, mapping, args[0][1]))
357 358 items = args[1][0](context, mapping, args[1][1])
358 359
359 360 if item in items:
360 361 yield _evalifliteral(args[2], context, mapping)
361 362 elif len(args) == 4:
362 363 yield _evalifliteral(args[3], context, mapping)
363 364
364 365 def ifeq(context, mapping, args):
365 366 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
366 367 whether 2 items are equivalent."""
367 368 if not (3 <= len(args) <= 4):
368 369 # i18n: "ifeq" is a keyword
369 370 raise error.ParseError(_("ifeq expects three or four arguments"))
370 371
371 372 test = stringify(args[0][0](context, mapping, args[0][1]))
372 373 match = stringify(args[1][0](context, mapping, args[1][1]))
373 374 if test == match:
374 375 yield _evalifliteral(args[2], context, mapping)
375 376 elif len(args) == 4:
376 377 yield _evalifliteral(args[3], context, mapping)
377 378
378 379 def join(context, mapping, args):
379 380 """:join(list, sep): Join items in a list with a delimiter."""
380 381 if not (1 <= len(args) <= 2):
381 382 # i18n: "join" is a keyword
382 383 raise error.ParseError(_("join expects one or two arguments"))
383 384
384 385 joinset = args[0][0](context, mapping, args[0][1])
385 386 if callable(joinset):
386 387 jf = joinset.joinfmt
387 388 joinset = [jf(x) for x in joinset()]
388 389
389 390 joiner = " "
390 391 if len(args) > 1:
391 392 joiner = stringify(args[1][0](context, mapping, args[1][1]))
392 393
393 394 first = True
394 395 for x in joinset:
395 396 if first:
396 397 first = False
397 398 else:
398 399 yield joiner
399 400 yield x
400 401
401 402 def label(context, mapping, args):
402 403 """:label(label, expr): Apply a label to generated content. Content with
403 404 a label applied can result in additional post-processing, such as
404 405 automatic colorization."""
405 406 if len(args) != 2:
406 407 # i18n: "label" is a keyword
407 408 raise error.ParseError(_("label expects two arguments"))
408 409
409 410 # ignore args[0] (the label string) since this is supposed to be a a no-op
410 411 yield _evalifliteral(args[1], context, mapping)
411 412
412 413 def revset(context, mapping, args):
413 414 """:revset(query[, formatargs...]): Execute a revision set query. See
414 415 :hg:`help revset`."""
415 416 if not len(args) > 0:
416 417 # i18n: "revset" is a keyword
417 418 raise error.ParseError(_("revset expects one or more arguments"))
418 419
419 420 raw = args[0][1]
420 421 ctx = mapping['ctx']
421 422 repo = ctx.repo()
422 423
423 424 def query(expr):
424 425 m = revsetmod.match(repo.ui, expr)
425 426 return m(repo)
426 427
427 428 if len(args) > 1:
428 429 formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
429 430 revs = query(revsetmod.formatspec(raw, *formatargs))
430 431 revs = list([str(r) for r in revs])
431 432 else:
432 433 revsetcache = mapping['cache'].setdefault("revsetcache", {})
433 434 if raw in revsetcache:
434 435 revs = revsetcache[raw]
435 436 else:
436 437 revs = query(raw)
437 438 revs = list([str(r) for r in revs])
438 439 revsetcache[raw] = revs
439 440
440 441 return templatekw.showlist("revision", revs, **mapping)
441 442
442 443 def rstdoc(context, mapping, args):
443 444 """:rstdoc(text, style): Format ReStructuredText."""
444 445 if len(args) != 2:
445 446 # i18n: "rstdoc" is a keyword
446 447 raise error.ParseError(_("rstdoc expects two arguments"))
447 448
448 449 text = stringify(args[0][0](context, mapping, args[0][1]))
449 450 style = stringify(args[1][0](context, mapping, args[1][1]))
450 451
451 452 return minirst.format(text, style=style, keep=['verbose'])
452 453
453 454 def shortest(context, mapping, args):
454 455 """:shortest(node, minlength=4): Obtain the shortest representation of
455 456 a node."""
456 457 if not (1 <= len(args) <= 2):
457 458 # i18n: "shortest" is a keyword
458 459 raise error.ParseError(_("shortest() expects one or two arguments"))
459 460
460 461 node = stringify(args[0][0](context, mapping, args[0][1]))
461 462
462 463 minlength = 4
463 464 if len(args) > 1:
464 465 minlength = int(args[1][1])
465 466
466 467 cl = mapping['ctx']._repo.changelog
467 468 def isvalid(test):
468 469 try:
469 470 try:
470 471 cl.index.partialmatch(test)
471 472 except AttributeError:
472 473 # Pure mercurial doesn't support partialmatch on the index.
473 474 # Fallback to the slow way.
474 475 if cl._partialmatch(test) is None:
475 476 return False
476 477
477 478 try:
478 479 i = int(test)
479 480 # if we are a pure int, then starting with zero will not be
480 481 # confused as a rev; or, obviously, if the int is larger than
481 482 # the value of the tip rev
482 483 if test[0] == '0' or i > len(cl):
483 484 return True
484 485 return False
485 486 except ValueError:
486 487 return True
487 488 except error.RevlogError:
488 489 return False
489 490
490 491 shortest = node
491 492 startlength = max(6, minlength)
492 493 length = startlength
493 494 while True:
494 495 test = node[:length]
495 496 if isvalid(test):
496 497 shortest = test
497 498 if length == minlength or length > startlength:
498 499 return shortest
499 500 length -= 1
500 501 else:
501 502 length += 1
502 503 if len(shortest) <= length:
503 504 return shortest
504 505
505 506 def strip(context, mapping, args):
506 507 """:strip(text[, chars]): Strip characters from a string."""
507 508 if not (1 <= len(args) <= 2):
508 509 # i18n: "strip" is a keyword
509 510 raise error.ParseError(_("strip expects one or two arguments"))
510 511
511 512 text = stringify(args[0][0](context, mapping, args[0][1]))
512 513 if len(args) == 2:
513 514 chars = stringify(args[1][0](context, mapping, args[1][1]))
514 515 return text.strip(chars)
515 516 return text.strip()
516 517
517 518 def sub(context, mapping, args):
518 519 """:sub(pattern, replacement, expression): Perform text substitution
519 520 using regular expressions."""
520 521 if len(args) != 3:
521 522 # i18n: "sub" is a keyword
522 523 raise error.ParseError(_("sub expects three arguments"))
523 524
524 525 pat = stringify(args[0][0](context, mapping, args[0][1]))
525 526 rpl = stringify(args[1][0](context, mapping, args[1][1]))
526 527 src = stringify(_evalifliteral(args[2], context, mapping))
527 528 yield re.sub(pat, rpl, src)
528 529
529 530 def startswith(context, mapping, args):
530 531 """:startswith(pattern, text): Returns the value from the "text" argument
531 532 if it begins with the content from the "pattern" argument."""
532 533 if len(args) != 2:
533 534 # i18n: "startswith" is a keyword
534 535 raise error.ParseError(_("startswith expects two arguments"))
535 536
536 537 patn = stringify(args[0][0](context, mapping, args[0][1]))
537 538 text = stringify(args[1][0](context, mapping, args[1][1]))
538 539 if text.startswith(patn):
539 540 return text
540 541 return ''
541 542
542 543
543 544 def word(context, mapping, args):
544 545 """:word(number, text[, separator]): Return the nth word from a string."""
545 546 if not (2 <= len(args) <= 3):
546 547 # i18n: "word" is a keyword
547 548 raise error.ParseError(_("word expects two or three arguments, got %d")
548 549 % len(args))
549 550
550 551 try:
551 552 num = int(stringify(args[0][0](context, mapping, args[0][1])))
552 553 except ValueError:
553 554 # i18n: "word" is a keyword
554 555 raise error.ParseError(
555 556 _("Use strings like '3' for numbers passed to word function"))
556 557 text = stringify(args[1][0](context, mapping, args[1][1]))
557 558 if len(args) == 3:
558 559 splitter = stringify(args[2][0](context, mapping, args[2][1]))
559 560 else:
560 561 splitter = None
561 562
562 563 tokens = text.split(splitter)
563 564 if num >= len(tokens):
564 565 return ''
565 566 else:
566 567 return tokens[num]
567 568
568 569 methods = {
569 570 "string": lambda e, c: (runstring, e[1]),
570 571 "rawstring": lambda e, c: (runrawstring, e[1]),
571 572 "symbol": lambda e, c: (runsymbol, e[1]),
572 573 "group": lambda e, c: compileexp(e[1], c),
573 574 # ".": buildmember,
574 575 "|": buildfilter,
575 576 "%": buildmap,
576 577 "func": buildfunc,
577 578 }
578 579
579 580 funcs = {
580 581 "date": date,
581 582 "diff": diff,
582 583 "fill": fill,
583 584 "get": get,
584 585 "if": if_,
585 586 "ifcontains": ifcontains,
586 587 "ifeq": ifeq,
587 588 "join": join,
588 589 "label": label,
589 590 "pad": pad,
590 591 "revset": revset,
591 592 "rstdoc": rstdoc,
592 593 "shortest": shortest,
593 594 "startswith": startswith,
594 595 "strip": strip,
595 596 "sub": sub,
596 597 "word": word,
597 598 }
598 599
599 600 # template engine
600 601
601 602 stringify = templatefilters.stringify
602 603
603 604 def _flatten(thing):
604 605 '''yield a single stream from a possibly nested set of iterators'''
605 606 if isinstance(thing, str):
606 607 yield thing
607 608 elif not util.safehasattr(thing, '__iter__'):
608 609 if thing is not None:
609 610 yield str(thing)
610 611 else:
611 612 for i in thing:
612 613 if isinstance(i, str):
613 614 yield i
614 615 elif not util.safehasattr(i, '__iter__'):
615 616 if i is not None:
616 617 yield str(i)
617 618 elif i is not None:
618 619 for j in _flatten(i):
619 620 yield j
620 621
621 622 def parsestring(s, quoted=True):
622 623 '''unwrap quotes if quoted is True'''
623 624 if quoted:
624 625 if len(s) < 2 or s[0] != s[-1]:
625 626 raise SyntaxError(_('unmatched quotes'))
626 627 return s[1:-1]
627 628
628 629 return s
629 630
630 631 class engine(object):
631 632 '''template expansion engine.
632 633
633 634 template expansion works like this. a map file contains key=value
634 635 pairs. if value is quoted, it is treated as string. otherwise, it
635 636 is treated as name of template file.
636 637
637 638 templater is asked to expand a key in map. it looks up key, and
638 639 looks for strings like this: {foo}. it expands {foo} by looking up
639 640 foo in map, and substituting it. expansion is recursive: it stops
640 641 when there is no more {foo} to replace.
641 642
642 643 expansion also allows formatting and filtering.
643 644
644 645 format uses key to expand each item in list. syntax is
645 646 {key%format}.
646 647
647 648 filter uses function to transform value. syntax is
648 649 {key|filter1|filter2|...}.'''
649 650
650 651 def __init__(self, loader, filters={}, defaults={}):
651 652 self._loader = loader
652 653 self._filters = filters
653 654 self._defaults = defaults
654 655 self._cache = {}
655 656
656 657 def _load(self, t):
657 658 '''load, parse, and cache a template'''
658 659 if t not in self._cache:
659 660 self._cache[t] = compiletemplate(self._loader(t), self)
660 661 return self._cache[t]
661 662
662 663 def process(self, t, mapping):
663 664 '''Perform expansion. t is name of map element to expand.
664 665 mapping contains added elements for use during expansion. Is a
665 666 generator.'''
666 667 return _flatten(runtemplate(self, mapping, self._load(t)))
667 668
668 669 engines = {'default': engine}
669 670
670 671 def stylelist():
671 672 paths = templatepaths()
672 673 if not paths:
673 674 return _('no templates found, try `hg debuginstall` for more info')
674 675 dirlist = os.listdir(paths[0])
675 676 stylelist = []
676 677 for file in dirlist:
677 678 split = file.split(".")
678 679 if split[0] == "map-cmdline":
679 680 stylelist.append(split[1])
680 681 return ", ".join(sorted(stylelist))
681 682
682 683 class TemplateNotFound(util.Abort):
683 684 pass
684 685
685 686 class templater(object):
686 687
687 688 def __init__(self, mapfile, filters={}, defaults={}, cache={},
688 689 minchunk=1024, maxchunk=65536):
689 690 '''set up template engine.
690 691 mapfile is name of file to read map definitions from.
691 692 filters is dict of functions. each transforms a value into another.
692 693 defaults is dict of default map definitions.'''
693 694 self.mapfile = mapfile or 'template'
694 695 self.cache = cache.copy()
695 696 self.map = {}
696 697 if mapfile:
697 698 self.base = os.path.dirname(mapfile)
698 699 else:
699 700 self.base = ''
700 701 self.filters = templatefilters.filters.copy()
701 702 self.filters.update(filters)
702 703 self.defaults = defaults
703 704 self.minchunk, self.maxchunk = minchunk, maxchunk
704 705 self.ecache = {}
705 706
706 707 if not mapfile:
707 708 return
708 709 if not os.path.exists(mapfile):
709 710 raise util.Abort(_("style '%s' not found") % mapfile,
710 711 hint=_("available styles: %s") % stylelist())
711 712
712 713 conf = config.config()
713 714 conf.read(mapfile)
714 715
715 716 for key, val in conf[''].items():
716 717 if not val:
717 718 raise SyntaxError(_('%s: missing value') % conf.source('', key))
718 719 if val[0] in "'\"":
719 720 try:
720 721 self.cache[key] = parsestring(val)
721 722 except SyntaxError, inst:
722 723 raise SyntaxError('%s: %s' %
723 724 (conf.source('', key), inst.args[0]))
724 725 else:
725 726 val = 'default', val
726 727 if ':' in val[1]:
727 728 val = val[1].split(':', 1)
728 729 self.map[key] = val[0], os.path.join(self.base, val[1])
729 730
730 731 def __contains__(self, key):
731 732 return key in self.cache or key in self.map
732 733
733 734 def load(self, t):
734 735 '''Get the template for the given template name. Use a local cache.'''
735 736 if t not in self.cache:
736 737 try:
737 738 self.cache[t] = util.readfile(self.map[t][1])
738 739 except KeyError, inst:
739 740 raise TemplateNotFound(_('"%s" not in template map') %
740 741 inst.args[0])
741 742 except IOError, inst:
742 743 raise IOError(inst.args[0], _('template file %s: %s') %
743 744 (self.map[t][1], inst.args[1]))
744 745 return self.cache[t]
745 746
746 747 def __call__(self, t, **mapping):
747 748 ttype = t in self.map and self.map[t][0] or 'default'
748 749 if ttype not in self.ecache:
749 750 self.ecache[ttype] = engines[ttype](self.load,
750 751 self.filters, self.defaults)
751 752 proc = self.ecache[ttype]
752 753
753 754 stream = proc.process(t, mapping)
754 755 if self.minchunk:
755 756 stream = util.increasingchunks(stream, min=self.minchunk,
756 757 max=self.maxchunk)
757 758 return stream
758 759
759 760 def templatepaths():
760 761 '''return locations used for template files.'''
761 762 pathsrel = ['templates']
762 763 paths = [os.path.normpath(os.path.join(util.datapath, f))
763 764 for f in pathsrel]
764 765 return [p for p in paths if os.path.isdir(p)]
765 766
766 767 def templatepath(name):
767 768 '''return location of template file. returns None if not found.'''
768 769 for p in templatepaths():
769 770 f = os.path.join(p, name)
770 771 if os.path.exists(f):
771 772 return f
772 773 return None
773 774
774 775 def stylemap(styles, paths=None):
775 776 """Return path to mapfile for a given style.
776 777
777 778 Searches mapfile in the following locations:
778 779 1. templatepath/style/map
779 780 2. templatepath/map-style
780 781 3. templatepath/map
781 782 """
782 783
783 784 if paths is None:
784 785 paths = templatepaths()
785 786 elif isinstance(paths, str):
786 787 paths = [paths]
787 788
788 789 if isinstance(styles, str):
789 790 styles = [styles]
790 791
791 792 for style in styles:
792 793 # only plain name is allowed to honor template paths
793 794 if (not style
794 795 or style in (os.curdir, os.pardir)
795 796 or os.sep in style
796 797 or os.altsep and os.altsep in style):
797 798 continue
798 799 locations = [os.path.join(style, 'map'), 'map-' + style]
799 800 locations.append('map')
800 801
801 802 for path in paths:
802 803 for location in locations:
803 804 mapfile = os.path.join(path, location)
804 805 if os.path.isfile(mapfile):
805 806 return style, mapfile
806 807
807 808 raise RuntimeError("No hgweb templates found in %r" % paths)
808 809
809 810 # tell hggettext to extract docstrings from these functions:
810 811 i18nfunctions = funcs.values()
@@ -1,2668 +1,2679 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 2253 $ hg log -R latesttag -r 0 \
2254 2254 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2255 2255 >
2256 2256 <>\n<[>
2257 2257 <>\n<]>
2258 2258 <>\n<
2259 2259
2260 2260 $ hg log -R latesttag -r 0 -T esc \
2261 2261 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2262 2262 >
2263 2263 <>\n<[>
2264 2264 <>\n<]>
2265 2265 <>\n<
2266 2266
2267 2267 $ cat <<'EOF' > esctmpl
2268 2268 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2269 2269 > EOF
2270 2270 $ hg log -R latesttag -r 0 --style ./esctmpl
2271 2271 >
2272 2272 <>\n<[>
2273 2273 <>\n<]>
2274 2274 <>\n<
2275 2275
2276 2276 Test leading backslashes:
2277 2277
2278 2278 $ cd latesttag
2279 2279 $ hg log -r 2 -T '\{rev} {files % "\{file}"} {files % r"\{file}"}\n'
2280 2280 {rev} {file} \head1
2281 2281 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"} {files % r"\\{file}"}\n'
2282 2282 \2 \head1 \\head1
2283 2283 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"} {files % r"\\\{file}"}\n'
2284 2284 \{rev} \{file} \\\head1
2285 2285 $ cd ..
2286 2286
2287 Test leading backslashes in "if" expression (issue4714):
2288
2289 $ cd latesttag
2290 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
2291 {rev} \2
2292 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
2293 \2 \\2
2294 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
2295 \{rev} \\\2
2296 $ cd ..
2297
2287 2298 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
2288 2299
2289 2300 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
2290 2301 \x6e
2291 2302 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
2292 2303 \x5c\x786e
2293 2304 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
2294 2305 \x6e
2295 2306 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
2296 2307 \x5c\x786e
2297 2308
2298 2309 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
2299 2310 \x6e
2300 2311 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
2301 2312 \x5c\x786e
2302 2313 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
2303 2314 \x6e
2304 2315 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
2305 2316 \x5c\x786e
2306 2317
2307 2318 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
2308 2319 fourth
2309 2320 second
2310 2321 third
2311 2322 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
2312 2323 fourth\nsecond\nthird
2313 2324
2314 2325 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
2315 2326 <p>
2316 2327 1st
2317 2328 </p>
2318 2329 <p>
2319 2330 2nd
2320 2331 </p>
2321 2332 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
2322 2333 <p>
2323 2334 1st\n\n2nd
2324 2335 </p>
2325 2336 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
2326 2337 1st
2327 2338
2328 2339 2nd
2329 2340
2330 2341 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
2331 2342 o perso
2332 2343 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
2333 2344 no person
2334 2345 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
2335 2346 o perso
2336 2347 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
2337 2348 no perso
2338 2349
2339 2350 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
2340 2351 -o perso-
2341 2352 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
2342 2353 no person
2343 2354 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
2344 2355 \x2do perso\x2d
2345 2356 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
2346 2357 -o perso-
2347 2358 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
2348 2359 \x2do perso\x6e
2349 2360
2350 2361 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
2351 2362 fourth
2352 2363 second
2353 2364 third
2354 2365 $ hg log -R a -r 8 --template '{files % r"{file}\n"}\n'
2355 2366 fourth\nsecond\nthird\n
2356 2367
2357 2368 Test string escaping in nested expression:
2358 2369
2359 2370 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
2360 2371 fourth\x6esecond\x6ethird
2361 2372 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
2362 2373 fourth\x6esecond\x6ethird
2363 2374
2364 2375 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
2365 2376 fourth\x6esecond\x6ethird
2366 2377 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
2367 2378 fourth\x5c\x786esecond\x5c\x786ethird
2368 2379
2369 2380 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
2370 2381 3:\x6eo user, \x6eo domai\x6e
2371 2382 4:\x5c\x786eew bra\x5c\x786ech
2372 2383
2373 2384 Test recursive evaluation:
2374 2385
2375 2386 $ hg init r
2376 2387 $ cd r
2377 2388 $ echo a > a
2378 2389 $ hg ci -Am '{rev}'
2379 2390 adding a
2380 2391 $ hg log -r 0 --template '{if(rev, desc)}\n'
2381 2392 {rev}
2382 2393 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
2383 2394 test 0
2384 2395
2385 2396 $ hg branch -q 'text.{rev}'
2386 2397 $ echo aa >> aa
2387 2398 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
2388 2399
2389 2400 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
2390 2401 {node|short}desc to
2391 2402 text.{rev}be wrapped
2392 2403 text.{rev}desc to be
2393 2404 text.{rev}wrapped (no-eol)
2394 2405 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
2395 2406 bcc7ff960b8e:desc to
2396 2407 text.1:be wrapped
2397 2408 text.1:desc to be
2398 2409 text.1:wrapped (no-eol)
2399 2410
2400 2411 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
2401 2412 {node|short} (no-eol)
2402 2413 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
2403 2414 bcc-ff---b-e (no-eol)
2404 2415
2405 2416 $ cat >> .hg/hgrc <<EOF
2406 2417 > [extensions]
2407 2418 > color=
2408 2419 > [color]
2409 2420 > mode=ansi
2410 2421 > text.{rev} = red
2411 2422 > text.1 = green
2412 2423 > EOF
2413 2424 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
2414 2425 \x1b[0;31mtext\x1b[0m (esc)
2415 2426 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
2416 2427 \x1b[0;32mtext\x1b[0m (esc)
2417 2428
2418 2429 Test branches inside if statement:
2419 2430
2420 2431 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
2421 2432 no
2422 2433
2423 2434 Test get function:
2424 2435
2425 2436 $ hg log -r 0 --template '{get(extras, "branch")}\n'
2426 2437 default
2427 2438 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
2428 2439 hg: parse error: get() expects a dict as first argument
2429 2440 [255]
2430 2441
2431 2442 Test shortest(node) function:
2432 2443
2433 2444 $ echo b > b
2434 2445 $ hg ci -qAm b
2435 2446 $ hg log --template '{shortest(node)}\n'
2436 2447 e777
2437 2448 bcc7
2438 2449 f776
2439 2450 $ hg log --template '{shortest(node, 10)}\n'
2440 2451 e777603221
2441 2452 bcc7ff960b
2442 2453 f7769ec2ab
2443 2454
2444 2455 Test pad function
2445 2456
2446 2457 $ hg log --template '{pad(rev, 20)} {author|user}\n'
2447 2458 2 test
2448 2459 1 {node|short}
2449 2460 0 test
2450 2461
2451 2462 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
2452 2463 2 test
2453 2464 1 {node|short}
2454 2465 0 test
2455 2466
2456 2467 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
2457 2468 2------------------- test
2458 2469 1------------------- {node|short}
2459 2470 0------------------- test
2460 2471
2461 2472 Test ifcontains function
2462 2473
2463 2474 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
2464 2475 2 is in the string
2465 2476 1 is not
2466 2477 0 is in the string
2467 2478
2468 2479 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
2469 2480 2 did not add a
2470 2481 1 did not add a
2471 2482 0 added a
2472 2483
2473 2484 Test revset function
2474 2485
2475 2486 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
2476 2487 2 current rev
2477 2488 1 not current rev
2478 2489 0 not current rev
2479 2490
2480 2491 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
2481 2492 2 match rev
2482 2493 1 match rev
2483 2494 0 not match rev
2484 2495
2485 2496 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
2486 2497 2 Parents: 1
2487 2498 1 Parents: 0
2488 2499 0 Parents:
2489 2500
2490 2501 $ cat >> .hg/hgrc <<EOF
2491 2502 > [revsetalias]
2492 2503 > myparents(\$1) = parents(\$1)
2493 2504 > EOF
2494 2505 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
2495 2506 2 Parents: 1
2496 2507 1 Parents: 0
2497 2508 0 Parents:
2498 2509
2499 2510 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
2500 2511 Rev: 2
2501 2512 Ancestor: 0
2502 2513 Ancestor: 1
2503 2514 Ancestor: 2
2504 2515
2505 2516 Rev: 1
2506 2517 Ancestor: 0
2507 2518 Ancestor: 1
2508 2519
2509 2520 Rev: 0
2510 2521 Ancestor: 0
2511 2522
2512 2523 Test current bookmark templating
2513 2524
2514 2525 $ hg book foo
2515 2526 $ hg book bar
2516 2527 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
2517 2528 2 bar* foo
2518 2529 1
2519 2530 0
2520 2531 $ hg log --template "{rev} {currentbookmark}\n"
2521 2532 2 bar
2522 2533 1
2523 2534 0
2524 2535 $ hg bookmarks --inactive bar
2525 2536 $ hg log --template "{rev} {currentbookmark}\n"
2526 2537 2
2527 2538 1
2528 2539 0
2529 2540 $ hg book -r1 baz
2530 2541 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
2531 2542 2 bar foo
2532 2543 1 baz
2533 2544 0
2534 2545 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
2535 2546 2 t
2536 2547 1 f
2537 2548 0 f
2538 2549
2539 2550 Test stringify on sub expressions
2540 2551
2541 2552 $ cd ..
2542 2553 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
2543 2554 fourth, second, third
2544 2555 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
2545 2556 abc
2546 2557
2547 2558 Test splitlines
2548 2559
2549 2560 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
2550 2561 @ foo future
2551 2562 |
2552 2563 o foo third
2553 2564 |
2554 2565 o foo second
2555 2566
2556 2567 o foo merge
2557 2568 |\
2558 2569 | o foo new head
2559 2570 | |
2560 2571 o | foo new branch
2561 2572 |/
2562 2573 o foo no user, no domain
2563 2574 |
2564 2575 o foo no person
2565 2576 |
2566 2577 o foo other 1
2567 2578 | foo other 2
2568 2579 | foo
2569 2580 | foo other 3
2570 2581 o foo line 1
2571 2582 foo line 2
2572 2583
2573 2584 Test startswith
2574 2585 $ hg log -Gv -R a --template "{startswith(desc)}"
2575 2586 hg: parse error: startswith expects two arguments
2576 2587 [255]
2577 2588
2578 2589 $ hg log -Gv -R a --template "{startswith('line', desc)}"
2579 2590 @
2580 2591 |
2581 2592 o
2582 2593 |
2583 2594 o
2584 2595
2585 2596 o
2586 2597 |\
2587 2598 | o
2588 2599 | |
2589 2600 o |
2590 2601 |/
2591 2602 o
2592 2603 |
2593 2604 o
2594 2605 |
2595 2606 o
2596 2607 |
2597 2608 o line 1
2598 2609 line 2
2599 2610
2600 2611 Test bad template with better error message
2601 2612
2602 2613 $ hg log -Gv -R a --template '{desc|user()}'
2603 2614 hg: parse error: expected a symbol, got 'func'
2604 2615 [255]
2605 2616
2606 2617 Test word function (including index out of bounds graceful failure)
2607 2618
2608 2619 $ hg log -Gv -R a --template "{word('1', desc)}"
2609 2620 @
2610 2621 |
2611 2622 o
2612 2623 |
2613 2624 o
2614 2625
2615 2626 o
2616 2627 |\
2617 2628 | o head
2618 2629 | |
2619 2630 o | branch
2620 2631 |/
2621 2632 o user,
2622 2633 |
2623 2634 o person
2624 2635 |
2625 2636 o 1
2626 2637 |
2627 2638 o 1
2628 2639
2629 2640
2630 2641 Test word third parameter used as splitter
2631 2642
2632 2643 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
2633 2644 @ future
2634 2645 |
2635 2646 o third
2636 2647 |
2637 2648 o sec
2638 2649
2639 2650 o merge
2640 2651 |\
2641 2652 | o new head
2642 2653 | |
2643 2654 o | new branch
2644 2655 |/
2645 2656 o n
2646 2657 |
2647 2658 o n
2648 2659 |
2649 2660 o
2650 2661 |
2651 2662 o line 1
2652 2663 line 2
2653 2664
2654 2665 Test word error messages for not enough and too many arguments
2655 2666
2656 2667 $ hg log -Gv -R a --template "{word('0')}"
2657 2668 hg: parse error: word expects two or three arguments, got 1
2658 2669 [255]
2659 2670
2660 2671 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
2661 2672 hg: parse error: word expects two or three arguments, got 7
2662 2673 [255]
2663 2674
2664 2675 Test word for invalid numbers
2665 2676
2666 2677 $ hg log -Gv -R a --template "{word(2, desc)}"
2667 2678 hg: parse error: Use strings like '3' for numbers passed to word function
2668 2679 [255]
General Comments 0
You need to be logged in to leave comments. Login now