##// END OF EJS Templates
templater: only recursively evaluate string literals as templates (issue4103)
Matt Mackall -
r20067:3d8bfe2e stable
parent child Browse files
Show More
@@ -1,559 +1,557 b''
1 1 # color.py color output for the status and qseries commands
2 2 #
3 3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.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 '''colorize output from some commands
9 9
10 10 This extension modifies the status and resolve commands to add color
11 11 to their output to reflect file status, the qseries command to add
12 12 color to reflect patch status (applied, unapplied, missing), and to
13 13 diff-related commands to highlight additions, removals, diff headers,
14 14 and trailing whitespace.
15 15
16 16 Other effects in addition to color, like bold and underlined text, are
17 17 also available. By default, the terminfo database is used to find the
18 18 terminal codes used to change color and effect. If terminfo is not
19 19 available, then effects are rendered with the ECMA-48 SGR control
20 20 function (aka ANSI escape codes).
21 21
22 22 Default effects may be overridden from your configuration file::
23 23
24 24 [color]
25 25 status.modified = blue bold underline red_background
26 26 status.added = green bold
27 27 status.removed = red bold blue_background
28 28 status.deleted = cyan bold underline
29 29 status.unknown = magenta bold underline
30 30 status.ignored = black bold
31 31
32 32 # 'none' turns off all effects
33 33 status.clean = none
34 34 status.copied = none
35 35
36 36 qseries.applied = blue bold underline
37 37 qseries.unapplied = black bold
38 38 qseries.missing = red bold
39 39
40 40 diff.diffline = bold
41 41 diff.extended = cyan bold
42 42 diff.file_a = red bold
43 43 diff.file_b = green bold
44 44 diff.hunk = magenta
45 45 diff.deleted = red
46 46 diff.inserted = green
47 47 diff.changed = white
48 48 diff.trailingwhitespace = bold red_background
49 49
50 50 resolve.unresolved = red bold
51 51 resolve.resolved = green bold
52 52
53 53 bookmarks.current = green
54 54
55 55 branches.active = none
56 56 branches.closed = black bold
57 57 branches.current = green
58 58 branches.inactive = none
59 59
60 60 tags.normal = green
61 61 tags.local = black bold
62 62
63 63 rebase.rebased = blue
64 64 rebase.remaining = red bold
65 65
66 66 shelve.age = cyan
67 67 shelve.newest = green bold
68 68 shelve.name = blue bold
69 69
70 70 histedit.remaining = red bold
71 71
72 72 The available effects in terminfo mode are 'blink', 'bold', 'dim',
73 73 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
74 74 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
75 75 'underline'. How each is rendered depends on the terminal emulator.
76 76 Some may not be available for a given terminal type, and will be
77 77 silently ignored.
78 78
79 79 Note that on some systems, terminfo mode may cause problems when using
80 80 color with the pager extension and less -R. less with the -R option
81 81 will only display ECMA-48 color codes, and terminfo mode may sometimes
82 82 emit codes that less doesn't understand. You can work around this by
83 83 either using ansi mode (or auto mode), or by using less -r (which will
84 84 pass through all terminal control codes, not just color control
85 85 codes).
86 86
87 87 Because there are only eight standard colors, this module allows you
88 88 to define color names for other color slots which might be available
89 89 for your terminal type, assuming terminfo mode. For instance::
90 90
91 91 color.brightblue = 12
92 92 color.pink = 207
93 93 color.orange = 202
94 94
95 95 to set 'brightblue' to color slot 12 (useful for 16 color terminals
96 96 that have brighter colors defined in the upper eight) and, 'pink' and
97 97 'orange' to colors in 256-color xterm's default color cube. These
98 98 defined colors may then be used as any of the pre-defined eight,
99 99 including appending '_background' to set the background to that color.
100 100
101 101 By default, the color extension will use ANSI mode (or win32 mode on
102 102 Windows) if it detects a terminal. To override auto mode (to enable
103 103 terminfo mode, for example), set the following configuration option::
104 104
105 105 [color]
106 106 mode = terminfo
107 107
108 108 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
109 109 disable color.
110 110 '''
111 111
112 112 import os
113 113
114 114 from mercurial import commands, dispatch, extensions, ui as uimod, util
115 115 from mercurial import templater, error
116 116 from mercurial.i18n import _
117 117
118 118 testedwith = 'internal'
119 119
120 120 # start and stop parameters for effects
121 121 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
122 122 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
123 123 'italic': 3, 'underline': 4, 'inverse': 7,
124 124 'black_background': 40, 'red_background': 41,
125 125 'green_background': 42, 'yellow_background': 43,
126 126 'blue_background': 44, 'purple_background': 45,
127 127 'cyan_background': 46, 'white_background': 47}
128 128
129 129 def _terminfosetup(ui, mode):
130 130 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
131 131
132 132 global _terminfo_params
133 133 # If we failed to load curses, we go ahead and return.
134 134 if not _terminfo_params:
135 135 return
136 136 # Otherwise, see what the config file says.
137 137 if mode not in ('auto', 'terminfo'):
138 138 return
139 139
140 140 _terminfo_params.update((key[6:], (False, int(val)))
141 141 for key, val in ui.configitems('color')
142 142 if key.startswith('color.'))
143 143
144 144 try:
145 145 curses.setupterm()
146 146 except curses.error, e:
147 147 _terminfo_params = {}
148 148 return
149 149
150 150 for key, (b, e) in _terminfo_params.items():
151 151 if not b:
152 152 continue
153 153 if not curses.tigetstr(e):
154 154 # Most terminals don't support dim, invis, etc, so don't be
155 155 # noisy and use ui.debug().
156 156 ui.debug("no terminfo entry for %s\n" % e)
157 157 del _terminfo_params[key]
158 158 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
159 159 # Only warn about missing terminfo entries if we explicitly asked for
160 160 # terminfo mode.
161 161 if mode == "terminfo":
162 162 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
163 163 "ECMA-48 color\n"))
164 164 _terminfo_params = {}
165 165
166 166 def _modesetup(ui, coloropt):
167 167 global _terminfo_params
168 168
169 169 auto = coloropt == 'auto'
170 170 always = not auto and util.parsebool(coloropt)
171 171 if not always and not auto:
172 172 return None
173 173
174 174 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
175 175
176 176 mode = ui.config('color', 'mode', 'auto')
177 177 realmode = mode
178 178 if mode == 'auto':
179 179 if os.name == 'nt' and 'TERM' not in os.environ:
180 180 # looks line a cmd.exe console, use win32 API or nothing
181 181 realmode = 'win32'
182 182 else:
183 183 realmode = 'ansi'
184 184
185 185 if realmode == 'win32':
186 186 _terminfo_params = {}
187 187 if not w32effects:
188 188 if mode == 'win32':
189 189 # only warn if color.mode is explicitly set to win32
190 190 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
191 191 return None
192 192 _effects.update(w32effects)
193 193 elif realmode == 'ansi':
194 194 _terminfo_params = {}
195 195 elif realmode == 'terminfo':
196 196 _terminfosetup(ui, mode)
197 197 if not _terminfo_params:
198 198 if mode == 'terminfo':
199 199 ## FIXME Shouldn't we return None in this case too?
200 200 # only warn if color.mode is explicitly set to win32
201 201 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
202 202 realmode = 'ansi'
203 203 else:
204 204 return None
205 205
206 206 if always or (auto and formatted):
207 207 return realmode
208 208 return None
209 209
210 210 try:
211 211 import curses
212 212 # Mapping from effect name to terminfo attribute name or color number.
213 213 # This will also force-load the curses module.
214 214 _terminfo_params = {'none': (True, 'sgr0'),
215 215 'standout': (True, 'smso'),
216 216 'underline': (True, 'smul'),
217 217 'reverse': (True, 'rev'),
218 218 'inverse': (True, 'rev'),
219 219 'blink': (True, 'blink'),
220 220 'dim': (True, 'dim'),
221 221 'bold': (True, 'bold'),
222 222 'invisible': (True, 'invis'),
223 223 'italic': (True, 'sitm'),
224 224 'black': (False, curses.COLOR_BLACK),
225 225 'red': (False, curses.COLOR_RED),
226 226 'green': (False, curses.COLOR_GREEN),
227 227 'yellow': (False, curses.COLOR_YELLOW),
228 228 'blue': (False, curses.COLOR_BLUE),
229 229 'magenta': (False, curses.COLOR_MAGENTA),
230 230 'cyan': (False, curses.COLOR_CYAN),
231 231 'white': (False, curses.COLOR_WHITE)}
232 232 except ImportError:
233 233 _terminfo_params = False
234 234
235 235 _styles = {'grep.match': 'red bold',
236 236 'grep.linenumber': 'green',
237 237 'grep.rev': 'green',
238 238 'grep.change': 'green',
239 239 'grep.sep': 'cyan',
240 240 'grep.filename': 'magenta',
241 241 'grep.user': 'magenta',
242 242 'grep.date': 'magenta',
243 243 'bookmarks.current': 'green',
244 244 'branches.active': 'none',
245 245 'branches.closed': 'black bold',
246 246 'branches.current': 'green',
247 247 'branches.inactive': 'none',
248 248 'diff.changed': 'white',
249 249 'diff.deleted': 'red',
250 250 'diff.diffline': 'bold',
251 251 'diff.extended': 'cyan bold',
252 252 'diff.file_a': 'red bold',
253 253 'diff.file_b': 'green bold',
254 254 'diff.hunk': 'magenta',
255 255 'diff.inserted': 'green',
256 256 'diff.trailingwhitespace': 'bold red_background',
257 257 'diffstat.deleted': 'red',
258 258 'diffstat.inserted': 'green',
259 259 'histedit.remaining': 'red bold',
260 260 'ui.prompt': 'yellow',
261 261 'log.changeset': 'yellow',
262 262 'rebase.rebased': 'blue',
263 263 'rebase.remaining': 'red bold',
264 264 'resolve.resolved': 'green bold',
265 265 'resolve.unresolved': 'red bold',
266 266 'shelve.age': 'cyan',
267 267 'shelve.newest': 'green bold',
268 268 'shelve.name': 'blue bold',
269 269 'status.added': 'green bold',
270 270 'status.clean': 'none',
271 271 'status.copied': 'none',
272 272 'status.deleted': 'cyan bold underline',
273 273 'status.ignored': 'black bold',
274 274 'status.modified': 'blue bold',
275 275 'status.removed': 'red bold',
276 276 'status.unknown': 'magenta bold underline',
277 277 'tags.normal': 'green',
278 278 'tags.local': 'black bold'}
279 279
280 280
281 281 def _effect_str(effect):
282 282 '''Helper function for render_effects().'''
283 283
284 284 bg = False
285 285 if effect.endswith('_background'):
286 286 bg = True
287 287 effect = effect[:-11]
288 288 attr, val = _terminfo_params[effect]
289 289 if attr:
290 290 return curses.tigetstr(val)
291 291 elif bg:
292 292 return curses.tparm(curses.tigetstr('setab'), val)
293 293 else:
294 294 return curses.tparm(curses.tigetstr('setaf'), val)
295 295
296 296 def render_effects(text, effects):
297 297 'Wrap text in commands to turn on each effect.'
298 298 if not text:
299 299 return text
300 300 if not _terminfo_params:
301 301 start = [str(_effects[e]) for e in ['none'] + effects.split()]
302 302 start = '\033[' + ';'.join(start) + 'm'
303 303 stop = '\033[' + str(_effects['none']) + 'm'
304 304 else:
305 305 start = ''.join(_effect_str(effect)
306 306 for effect in ['none'] + effects.split())
307 307 stop = _effect_str('none')
308 308 return ''.join([start, text, stop])
309 309
310 310 def extstyles():
311 311 for name, ext in extensions.extensions():
312 312 _styles.update(getattr(ext, 'colortable', {}))
313 313
314 314 def configstyles(ui):
315 315 for status, cfgeffects in ui.configitems('color'):
316 316 if '.' not in status or status.startswith('color.'):
317 317 continue
318 318 cfgeffects = ui.configlist('color', status)
319 319 if cfgeffects:
320 320 good = []
321 321 for e in cfgeffects:
322 322 if not _terminfo_params and e in _effects:
323 323 good.append(e)
324 324 elif e in _terminfo_params or e[:-11] in _terminfo_params:
325 325 good.append(e)
326 326 else:
327 327 ui.warn(_("ignoring unknown color/effect %r "
328 328 "(configured in color.%s)\n")
329 329 % (e, status))
330 330 _styles[status] = ' '.join(good)
331 331
332 332 class colorui(uimod.ui):
333 333 def popbuffer(self, labeled=False):
334 334 if self._colormode is None:
335 335 return super(colorui, self).popbuffer(labeled)
336 336
337 337 if labeled:
338 338 return ''.join(self.label(a, label) for a, label
339 339 in self._buffers.pop())
340 340 return ''.join(a for a, label in self._buffers.pop())
341 341
342 342 _colormode = 'ansi'
343 343 def write(self, *args, **opts):
344 344 if self._colormode is None:
345 345 return super(colorui, self).write(*args, **opts)
346 346
347 347 label = opts.get('label', '')
348 348 if self._buffers:
349 349 self._buffers[-1].extend([(str(a), label) for a in args])
350 350 elif self._colormode == 'win32':
351 351 for a in args:
352 352 win32print(a, super(colorui, self).write, **opts)
353 353 else:
354 354 return super(colorui, self).write(
355 355 *[self.label(str(a), label) for a in args], **opts)
356 356
357 357 def write_err(self, *args, **opts):
358 358 if self._colormode is None:
359 359 return super(colorui, self).write_err(*args, **opts)
360 360
361 361 label = opts.get('label', '')
362 362 if self._colormode == 'win32':
363 363 for a in args:
364 364 win32print(a, super(colorui, self).write_err, **opts)
365 365 else:
366 366 return super(colorui, self).write_err(
367 367 *[self.label(str(a), label) for a in args], **opts)
368 368
369 369 def label(self, msg, label):
370 370 if self._colormode is None:
371 371 return super(colorui, self).label(msg, label)
372 372
373 373 effects = []
374 374 for l in label.split():
375 375 s = _styles.get(l, '')
376 376 if s:
377 377 effects.append(s)
378 378 effects = ' '.join(effects)
379 379 if effects:
380 380 return '\n'.join([render_effects(s, effects)
381 381 for s in msg.split('\n')])
382 382 return msg
383 383
384 384 def templatelabel(context, mapping, args):
385 385 if len(args) != 2:
386 386 # i18n: "label" is a keyword
387 387 raise error.ParseError(_("label expects two arguments"))
388 388
389 thing = templater.stringify(args[1][0](context, mapping, args[1][1]))
390 thing = templater.runtemplate(context, mapping,
391 templater.compiletemplate(thing, context))
389 thing = templater._evalifliteral(args[1], context, mapping)
392 390
393 391 # apparently, repo could be a string that is the favicon?
394 392 repo = mapping.get('repo', '')
395 393 if isinstance(repo, str):
396 394 return thing
397 395
398 396 label = templater.stringify(args[0][0](context, mapping, args[0][1]))
399 397 label = templater.runtemplate(context, mapping,
400 398 templater.compiletemplate(label, context))
401 399
402 400 thing = templater.stringify(thing)
403 401 label = templater.stringify(label)
404 402
405 403 return repo.ui.label(thing, label)
406 404
407 405 def uisetup(ui):
408 406 if ui.plain():
409 407 return
410 408 if not isinstance(ui, colorui):
411 409 colorui.__bases__ = (ui.__class__,)
412 410 ui.__class__ = colorui
413 411 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
414 412 mode = _modesetup(ui_, opts['color'])
415 413 colorui._colormode = mode
416 414 if mode:
417 415 extstyles()
418 416 configstyles(ui_)
419 417 return orig(ui_, opts, cmd, cmdfunc)
420 418 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
421 419 templater.funcs['label'] = templatelabel
422 420
423 421 def extsetup(ui):
424 422 commands.globalopts.append(
425 423 ('', 'color', 'auto',
426 424 # i18n: 'always', 'auto', and 'never' are keywords and should
427 425 # not be translated
428 426 _("when to colorize (boolean, always, auto, or never)"),
429 427 _('TYPE')))
430 428
431 429 if os.name != 'nt':
432 430 w32effects = None
433 431 else:
434 432 import re, ctypes
435 433
436 434 _kernel32 = ctypes.windll.kernel32
437 435
438 436 _WORD = ctypes.c_ushort
439 437
440 438 _INVALID_HANDLE_VALUE = -1
441 439
442 440 class _COORD(ctypes.Structure):
443 441 _fields_ = [('X', ctypes.c_short),
444 442 ('Y', ctypes.c_short)]
445 443
446 444 class _SMALL_RECT(ctypes.Structure):
447 445 _fields_ = [('Left', ctypes.c_short),
448 446 ('Top', ctypes.c_short),
449 447 ('Right', ctypes.c_short),
450 448 ('Bottom', ctypes.c_short)]
451 449
452 450 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
453 451 _fields_ = [('dwSize', _COORD),
454 452 ('dwCursorPosition', _COORD),
455 453 ('wAttributes', _WORD),
456 454 ('srWindow', _SMALL_RECT),
457 455 ('dwMaximumWindowSize', _COORD)]
458 456
459 457 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
460 458 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
461 459
462 460 _FOREGROUND_BLUE = 0x0001
463 461 _FOREGROUND_GREEN = 0x0002
464 462 _FOREGROUND_RED = 0x0004
465 463 _FOREGROUND_INTENSITY = 0x0008
466 464
467 465 _BACKGROUND_BLUE = 0x0010
468 466 _BACKGROUND_GREEN = 0x0020
469 467 _BACKGROUND_RED = 0x0040
470 468 _BACKGROUND_INTENSITY = 0x0080
471 469
472 470 _COMMON_LVB_REVERSE_VIDEO = 0x4000
473 471 _COMMON_LVB_UNDERSCORE = 0x8000
474 472
475 473 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
476 474 w32effects = {
477 475 'none': -1,
478 476 'black': 0,
479 477 'red': _FOREGROUND_RED,
480 478 'green': _FOREGROUND_GREEN,
481 479 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
482 480 'blue': _FOREGROUND_BLUE,
483 481 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
484 482 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
485 483 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
486 484 'bold': _FOREGROUND_INTENSITY,
487 485 'black_background': 0x100, # unused value > 0x0f
488 486 'red_background': _BACKGROUND_RED,
489 487 'green_background': _BACKGROUND_GREEN,
490 488 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
491 489 'blue_background': _BACKGROUND_BLUE,
492 490 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
493 491 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
494 492 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
495 493 _BACKGROUND_BLUE),
496 494 'bold_background': _BACKGROUND_INTENSITY,
497 495 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
498 496 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
499 497 }
500 498
501 499 passthrough = set([_FOREGROUND_INTENSITY,
502 500 _BACKGROUND_INTENSITY,
503 501 _COMMON_LVB_UNDERSCORE,
504 502 _COMMON_LVB_REVERSE_VIDEO])
505 503
506 504 stdout = _kernel32.GetStdHandle(
507 505 _STD_OUTPUT_HANDLE) # don't close the handle returned
508 506 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
509 507 w32effects = None
510 508 else:
511 509 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
512 510 if not _kernel32.GetConsoleScreenBufferInfo(
513 511 stdout, ctypes.byref(csbi)):
514 512 # stdout may not support GetConsoleScreenBufferInfo()
515 513 # when called from subprocess or redirected
516 514 w32effects = None
517 515 else:
518 516 origattr = csbi.wAttributes
519 517 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
520 518 re.MULTILINE | re.DOTALL)
521 519
522 520 def win32print(text, orig, **opts):
523 521 label = opts.get('label', '')
524 522 attr = origattr
525 523
526 524 def mapcolor(val, attr):
527 525 if val == -1:
528 526 return origattr
529 527 elif val in passthrough:
530 528 return attr | val
531 529 elif val > 0x0f:
532 530 return (val & 0x70) | (attr & 0x8f)
533 531 else:
534 532 return (val & 0x07) | (attr & 0xf8)
535 533
536 534 # determine console attributes based on labels
537 535 for l in label.split():
538 536 style = _styles.get(l, '')
539 537 for effect in style.split():
540 538 attr = mapcolor(w32effects[effect], attr)
541 539
542 540 # hack to ensure regexp finds data
543 541 if not text.startswith('\033['):
544 542 text = '\033[m' + text
545 543
546 544 # Look for ANSI-like codes embedded in text
547 545 m = re.match(ansire, text)
548 546
549 547 try:
550 548 while m:
551 549 for sattr in m.group(1).split(';'):
552 550 if sattr:
553 551 attr = mapcolor(int(sattr), attr)
554 552 _kernel32.SetConsoleTextAttribute(stdout, attr)
555 553 orig(m.group(2), **opts)
556 554 m = re.match(ansire, m.group(3))
557 555 finally:
558 556 # Explicitly reset original attributes
559 557 _kernel32.SetConsoleTextAttribute(stdout, origattr)
@@ -1,585 +1,587 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 sys, os, re
10 10 import util, config, templatefilters, parser, error
11 11 import types
12 12 import minirst
13 13
14 14 # template parsing
15 15
16 16 elements = {
17 17 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
18 18 ",": (2, None, ("list", 2)),
19 19 "|": (5, None, ("|", 5)),
20 20 "%": (6, None, ("%", 6)),
21 21 ")": (0, None, None),
22 22 "symbol": (0, ("symbol",), None),
23 23 "string": (0, ("string",), None),
24 24 "end": (0, None, None),
25 25 }
26 26
27 27 def tokenizer(data):
28 28 program, start, end = data
29 29 pos = start
30 30 while pos < end:
31 31 c = program[pos]
32 32 if c.isspace(): # skip inter-token whitespace
33 33 pass
34 34 elif c in "(,)%|": # handle simple operators
35 35 yield (c, None, pos)
36 36 elif (c in '"\'' or c == 'r' and
37 37 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
38 38 if c == 'r':
39 39 pos += 1
40 40 c = program[pos]
41 41 decode = False
42 42 else:
43 43 decode = True
44 44 pos += 1
45 45 s = pos
46 46 while pos < end: # find closing quote
47 47 d = program[pos]
48 48 if decode and d == '\\': # skip over escaped characters
49 49 pos += 2
50 50 continue
51 51 if d == c:
52 52 if not decode:
53 53 yield ('string', program[s:pos].replace('\\', r'\\'), s)
54 54 break
55 55 yield ('string', program[s:pos], s)
56 56 break
57 57 pos += 1
58 58 else:
59 59 raise error.ParseError(_("unterminated string"), s)
60 60 elif c.isalnum() or c in '_':
61 61 s = pos
62 62 pos += 1
63 63 while pos < end: # find end of symbol
64 64 d = program[pos]
65 65 if not (d.isalnum() or d == "_"):
66 66 break
67 67 pos += 1
68 68 sym = program[s:pos]
69 69 yield ('symbol', sym, s)
70 70 pos -= 1
71 71 elif c == '}':
72 72 pos += 1
73 73 break
74 74 else:
75 75 raise error.ParseError(_("syntax error"), pos)
76 76 pos += 1
77 77 yield ('end', None, pos)
78 78
79 79 def compiletemplate(tmpl, context):
80 80 parsed = []
81 81 pos, stop = 0, len(tmpl)
82 82 p = parser.parser(tokenizer, elements)
83 83 while pos < stop:
84 84 n = tmpl.find('{', pos)
85 85 if n < 0:
86 86 parsed.append(("string", tmpl[pos:].decode("string-escape")))
87 87 break
88 88 if n > 0 and tmpl[n - 1] == '\\':
89 89 # escaped
90 90 parsed.append(("string",
91 91 (tmpl[pos:n - 1] + "{").decode("string-escape")))
92 92 pos = n + 1
93 93 continue
94 94 if n > pos:
95 95 parsed.append(("string", tmpl[pos:n].decode("string-escape")))
96 96
97 97 pd = [tmpl, n + 1, stop]
98 98 parseres, pos = p.parse(pd)
99 99 parsed.append(parseres)
100 100
101 101 return [compileexp(e, context) for e in parsed]
102 102
103 103 def compileexp(exp, context):
104 104 t = exp[0]
105 105 if t in methods:
106 106 return methods[t](exp, context)
107 107 raise error.ParseError(_("unknown method '%s'") % t)
108 108
109 109 # template evaluation
110 110
111 111 def getsymbol(exp):
112 112 if exp[0] == 'symbol':
113 113 return exp[1]
114 114 raise error.ParseError(_("expected a symbol"))
115 115
116 116 def getlist(x):
117 117 if not x:
118 118 return []
119 119 if x[0] == 'list':
120 120 return getlist(x[1]) + [x[2]]
121 121 return [x]
122 122
123 123 def getfilter(exp, context):
124 124 f = getsymbol(exp)
125 125 if f not in context._filters:
126 126 raise error.ParseError(_("unknown function '%s'") % f)
127 127 return context._filters[f]
128 128
129 129 def gettemplate(exp, context):
130 130 if exp[0] == 'string':
131 131 return compiletemplate(exp[1], context)
132 132 if exp[0] == 'symbol':
133 133 return context._load(exp[1])
134 134 raise error.ParseError(_("expected template specifier"))
135 135
136 136 def runstring(context, mapping, data):
137 137 return data
138 138
139 139 def runsymbol(context, mapping, key):
140 140 v = mapping.get(key)
141 141 if v is None:
142 142 v = context._defaults.get(key)
143 143 if v is None:
144 144 try:
145 145 v = context.process(key, mapping)
146 146 except TemplateNotFound:
147 147 v = ''
148 148 if util.safehasattr(v, '__call__'):
149 149 return v(**mapping)
150 150 if isinstance(v, types.GeneratorType):
151 151 v = list(v)
152 152 mapping[key] = v
153 153 return v
154 154 return v
155 155
156 156 def buildfilter(exp, context):
157 157 func, data = compileexp(exp[1], context)
158 158 filt = getfilter(exp[2], context)
159 159 return (runfilter, (func, data, filt))
160 160
161 161 def runfilter(context, mapping, data):
162 162 func, data, filt = data
163 163 try:
164 164 return filt(func(context, mapping, data))
165 165 except (ValueError, AttributeError, TypeError):
166 166 if isinstance(data, tuple):
167 167 dt = data[1]
168 168 else:
169 169 dt = data
170 170 raise util.Abort(_("template filter '%s' is not compatible with "
171 171 "keyword '%s'") % (filt.func_name, dt))
172 172
173 173 def buildmap(exp, context):
174 174 func, data = compileexp(exp[1], context)
175 175 ctmpl = gettemplate(exp[2], context)
176 176 return (runmap, (func, data, ctmpl))
177 177
178 178 def runtemplate(context, mapping, template):
179 179 for func, data in template:
180 180 yield func(context, mapping, data)
181 181
182 182 def runmap(context, mapping, data):
183 183 func, data, ctmpl = data
184 184 d = func(context, mapping, data)
185 185 if util.safehasattr(d, '__call__'):
186 186 d = d()
187 187
188 188 lm = mapping.copy()
189 189
190 190 for i in d:
191 191 if isinstance(i, dict):
192 192 lm.update(i)
193 193 lm['originalnode'] = mapping.get('node')
194 194 yield runtemplate(context, lm, ctmpl)
195 195 else:
196 196 # v is not an iterable of dicts, this happen when 'key'
197 197 # has been fully expanded already and format is useless.
198 198 # If so, return the expanded value.
199 199 yield i
200 200
201 201 def buildfunc(exp, context):
202 202 n = getsymbol(exp[1])
203 203 args = [compileexp(x, context) for x in getlist(exp[2])]
204 204 if n in funcs:
205 205 f = funcs[n]
206 206 return (f, args)
207 207 if n in context._filters:
208 208 if len(args) != 1:
209 209 raise error.ParseError(_("filter %s expects one argument") % n)
210 210 f = context._filters[n]
211 211 return (runfilter, (args[0][0], args[0][1], f))
212 212
213 213 def date(context, mapping, args):
214 214 if not (1 <= len(args) <= 2):
215 215 raise error.ParseError(_("date expects one or two arguments"))
216 216
217 217 date = args[0][0](context, mapping, args[0][1])
218 218 if len(args) == 2:
219 219 fmt = stringify(args[1][0](context, mapping, args[1][1]))
220 220 return util.datestr(date, fmt)
221 221 return util.datestr(date)
222 222
223 223 def fill(context, mapping, args):
224 224 if not (1 <= len(args) <= 4):
225 225 raise error.ParseError(_("fill expects one to four arguments"))
226 226
227 227 text = stringify(args[0][0](context, mapping, args[0][1]))
228 228 width = 76
229 229 initindent = ''
230 230 hangindent = ''
231 231 if 2 <= len(args) <= 4:
232 232 try:
233 233 width = int(stringify(args[1][0](context, mapping, args[1][1])))
234 234 except ValueError:
235 235 raise error.ParseError(_("fill expects an integer width"))
236 236 try:
237 237 initindent = stringify(args[2][0](context, mapping, args[2][1]))
238 238 initindent = stringify(runtemplate(context, mapping,
239 239 compiletemplate(initindent, context)))
240 240 hangindent = stringify(args[3][0](context, mapping, args[3][1]))
241 241 hangindent = stringify(runtemplate(context, mapping,
242 242 compiletemplate(hangindent, context)))
243 243 except IndexError:
244 244 pass
245 245
246 246 return templatefilters.fill(text, width, initindent, hangindent)
247 247
248 248 def get(context, mapping, args):
249 249 if len(args) != 2:
250 250 # i18n: "get" is a keyword
251 251 raise error.ParseError(_("get() expects two arguments"))
252 252
253 253 dictarg = args[0][0](context, mapping, args[0][1])
254 254 if not util.safehasattr(dictarg, 'get'):
255 255 # i18n: "get" is a keyword
256 256 raise error.ParseError(_("get() expects a dict as first argument"))
257 257
258 258 key = args[1][0](context, mapping, args[1][1])
259 259 yield dictarg.get(key)
260 260
261 def _evalifliteral(arg, context, mapping):
262 t = stringify(arg[0](context, mapping, arg[1]))
263 if arg[0] == runstring:
264 yield runtemplate(context, mapping, compiletemplate(t, context))
265 else:
266 yield t
267
261 268 def if_(context, mapping, args):
262 269 if not (2 <= len(args) <= 3):
263 270 # i18n: "if" is a keyword
264 271 raise error.ParseError(_("if expects two or three arguments"))
265 272
266 273 test = stringify(args[0][0](context, mapping, args[0][1]))
267 274 if test:
268 t = stringify(args[1][0](context, mapping, args[1][1]))
269 yield runtemplate(context, mapping, compiletemplate(t, context))
275 yield _evalifliteral(args[1], context, mapping)
270 276 elif len(args) == 3:
271 t = stringify(args[2][0](context, mapping, args[2][1]))
272 yield runtemplate(context, mapping, compiletemplate(t, context))
277 yield _evalifliteral(args[2], context, mapping)
273 278
274 279 def ifeq(context, mapping, args):
275 280 if not (3 <= len(args) <= 4):
276 281 # i18n: "ifeq" is a keyword
277 282 raise error.ParseError(_("ifeq expects three or four arguments"))
278 283
279 284 test = stringify(args[0][0](context, mapping, args[0][1]))
280 285 match = stringify(args[1][0](context, mapping, args[1][1]))
281 286 if test == match:
282 t = stringify(args[2][0](context, mapping, args[2][1]))
283 yield runtemplate(context, mapping, compiletemplate(t, context))
287 yield _evalifliteral(args[2], context, mapping)
284 288 elif len(args) == 4:
285 t = stringify(args[3][0](context, mapping, args[3][1]))
286 yield runtemplate(context, mapping, compiletemplate(t, context))
289 yield _evalifliteral(args[3], context, mapping)
287 290
288 291 def join(context, mapping, args):
289 292 if not (1 <= len(args) <= 2):
290 293 # i18n: "join" is a keyword
291 294 raise error.ParseError(_("join expects one or two arguments"))
292 295
293 296 joinset = args[0][0](context, mapping, args[0][1])
294 297 if util.safehasattr(joinset, '__call__'):
295 298 jf = joinset.joinfmt
296 299 joinset = [jf(x) for x in joinset()]
297 300
298 301 joiner = " "
299 302 if len(args) > 1:
300 303 joiner = args[1][0](context, mapping, args[1][1])
301 304
302 305 first = True
303 306 for x in joinset:
304 307 if first:
305 308 first = False
306 309 else:
307 310 yield joiner
308 311 yield x
309 312
310 313 def label(context, mapping, args):
311 314 if len(args) != 2:
312 315 # i18n: "label" is a keyword
313 316 raise error.ParseError(_("label expects two arguments"))
314 317
315 318 # ignore args[0] (the label string) since this is supposed to be a a no-op
316 t = stringify(args[1][0](context, mapping, args[1][1]))
317 yield runtemplate(context, mapping, compiletemplate(t, context))
319 yield _evalifliteral(args[1], context, mapping)
318 320
319 321 def rstdoc(context, mapping, args):
320 322 if len(args) != 2:
321 323 # i18n: "rstdoc" is a keyword
322 324 raise error.ParseError(_("rstdoc expects two arguments"))
323 325
324 326 text = stringify(args[0][0](context, mapping, args[0][1]))
325 327 style = stringify(args[1][0](context, mapping, args[1][1]))
326 328
327 329 return minirst.format(text, style=style, keep=['verbose'])
328 330
329 331 def strip(context, mapping, args):
330 332 if not (1 <= len(args) <= 2):
331 333 raise error.ParseError(_("strip expects one or two arguments"))
332 334
333 335 text = args[0][0](context, mapping, args[0][1])
334 336 if len(args) == 2:
335 337 chars = args[1][0](context, mapping, args[1][1])
336 338 return text.strip(chars)
337 339 return text.strip()
338 340
339 341 def sub(context, mapping, args):
340 342 if len(args) != 3:
341 343 # i18n: "sub" is a keyword
342 344 raise error.ParseError(_("sub expects three arguments"))
343 345
344 346 pat = stringify(args[0][0](context, mapping, args[0][1]))
345 347 rpl = stringify(args[1][0](context, mapping, args[1][1]))
346 348 src = stringify(args[2][0](context, mapping, args[2][1]))
347 349 src = stringify(runtemplate(context, mapping,
348 350 compiletemplate(src, context)))
349 351 yield re.sub(pat, rpl, src)
350 352
351 353 methods = {
352 354 "string": lambda e, c: (runstring, e[1]),
353 355 "symbol": lambda e, c: (runsymbol, e[1]),
354 356 "group": lambda e, c: compileexp(e[1], c),
355 357 # ".": buildmember,
356 358 "|": buildfilter,
357 359 "%": buildmap,
358 360 "func": buildfunc,
359 361 }
360 362
361 363 funcs = {
362 364 "date": date,
363 365 "fill": fill,
364 366 "get": get,
365 367 "if": if_,
366 368 "ifeq": ifeq,
367 369 "join": join,
368 370 "label": label,
369 371 "rstdoc": rstdoc,
370 372 "strip": strip,
371 373 "sub": sub,
372 374 }
373 375
374 376 # template engine
375 377
376 378 path = ['templates', '../templates']
377 379 stringify = templatefilters.stringify
378 380
379 381 def _flatten(thing):
380 382 '''yield a single stream from a possibly nested set of iterators'''
381 383 if isinstance(thing, str):
382 384 yield thing
383 385 elif not util.safehasattr(thing, '__iter__'):
384 386 if thing is not None:
385 387 yield str(thing)
386 388 else:
387 389 for i in thing:
388 390 if isinstance(i, str):
389 391 yield i
390 392 elif not util.safehasattr(i, '__iter__'):
391 393 if i is not None:
392 394 yield str(i)
393 395 elif i is not None:
394 396 for j in _flatten(i):
395 397 yield j
396 398
397 399 def parsestring(s, quoted=True):
398 400 '''parse a string using simple c-like syntax.
399 401 string must be in quotes if quoted is True.'''
400 402 if quoted:
401 403 if len(s) < 2 or s[0] != s[-1]:
402 404 raise SyntaxError(_('unmatched quotes'))
403 405 return s[1:-1].decode('string_escape')
404 406
405 407 return s.decode('string_escape')
406 408
407 409 class engine(object):
408 410 '''template expansion engine.
409 411
410 412 template expansion works like this. a map file contains key=value
411 413 pairs. if value is quoted, it is treated as string. otherwise, it
412 414 is treated as name of template file.
413 415
414 416 templater is asked to expand a key in map. it looks up key, and
415 417 looks for strings like this: {foo}. it expands {foo} by looking up
416 418 foo in map, and substituting it. expansion is recursive: it stops
417 419 when there is no more {foo} to replace.
418 420
419 421 expansion also allows formatting and filtering.
420 422
421 423 format uses key to expand each item in list. syntax is
422 424 {key%format}.
423 425
424 426 filter uses function to transform value. syntax is
425 427 {key|filter1|filter2|...}.'''
426 428
427 429 def __init__(self, loader, filters={}, defaults={}):
428 430 self._loader = loader
429 431 self._filters = filters
430 432 self._defaults = defaults
431 433 self._cache = {}
432 434
433 435 def _load(self, t):
434 436 '''load, parse, and cache a template'''
435 437 if t not in self._cache:
436 438 self._cache[t] = compiletemplate(self._loader(t), self)
437 439 return self._cache[t]
438 440
439 441 def process(self, t, mapping):
440 442 '''Perform expansion. t is name of map element to expand.
441 443 mapping contains added elements for use during expansion. Is a
442 444 generator.'''
443 445 return _flatten(runtemplate(self, mapping, self._load(t)))
444 446
445 447 engines = {'default': engine}
446 448
447 449 def stylelist():
448 450 path = templatepath()[0]
449 451 dirlist = os.listdir(path)
450 452 stylelist = []
451 453 for file in dirlist:
452 454 split = file.split(".")
453 455 if split[0] == "map-cmdline":
454 456 stylelist.append(split[1])
455 457 return ", ".join(sorted(stylelist))
456 458
457 459 class TemplateNotFound(util.Abort):
458 460 pass
459 461
460 462 class templater(object):
461 463
462 464 def __init__(self, mapfile, filters={}, defaults={}, cache={},
463 465 minchunk=1024, maxchunk=65536):
464 466 '''set up template engine.
465 467 mapfile is name of file to read map definitions from.
466 468 filters is dict of functions. each transforms a value into another.
467 469 defaults is dict of default map definitions.'''
468 470 self.mapfile = mapfile or 'template'
469 471 self.cache = cache.copy()
470 472 self.map = {}
471 473 self.base = (mapfile and os.path.dirname(mapfile)) or ''
472 474 self.filters = templatefilters.filters.copy()
473 475 self.filters.update(filters)
474 476 self.defaults = defaults
475 477 self.minchunk, self.maxchunk = minchunk, maxchunk
476 478 self.ecache = {}
477 479
478 480 if not mapfile:
479 481 return
480 482 if not os.path.exists(mapfile):
481 483 raise util.Abort(_("style '%s' not found") % mapfile,
482 484 hint=_("available styles: %s") % stylelist())
483 485
484 486 conf = config.config()
485 487 conf.read(mapfile)
486 488
487 489 for key, val in conf[''].items():
488 490 if not val:
489 491 raise SyntaxError(_('%s: missing value') % conf.source('', key))
490 492 if val[0] in "'\"":
491 493 try:
492 494 self.cache[key] = parsestring(val)
493 495 except SyntaxError, inst:
494 496 raise SyntaxError('%s: %s' %
495 497 (conf.source('', key), inst.args[0]))
496 498 else:
497 499 val = 'default', val
498 500 if ':' in val[1]:
499 501 val = val[1].split(':', 1)
500 502 self.map[key] = val[0], os.path.join(self.base, val[1])
501 503
502 504 def __contains__(self, key):
503 505 return key in self.cache or key in self.map
504 506
505 507 def load(self, t):
506 508 '''Get the template for the given template name. Use a local cache.'''
507 509 if t not in self.cache:
508 510 try:
509 511 self.cache[t] = util.readfile(self.map[t][1])
510 512 except KeyError, inst:
511 513 raise TemplateNotFound(_('"%s" not in template map') %
512 514 inst.args[0])
513 515 except IOError, inst:
514 516 raise IOError(inst.args[0], _('template file %s: %s') %
515 517 (self.map[t][1], inst.args[1]))
516 518 return self.cache[t]
517 519
518 520 def __call__(self, t, **mapping):
519 521 ttype = t in self.map and self.map[t][0] or 'default'
520 522 if ttype not in self.ecache:
521 523 self.ecache[ttype] = engines[ttype](self.load,
522 524 self.filters, self.defaults)
523 525 proc = self.ecache[ttype]
524 526
525 527 stream = proc.process(t, mapping)
526 528 if self.minchunk:
527 529 stream = util.increasingchunks(stream, min=self.minchunk,
528 530 max=self.maxchunk)
529 531 return stream
530 532
531 533 def templatepath(name=None):
532 534 '''return location of template file or directory (if no name).
533 535 returns None if not found.'''
534 536 normpaths = []
535 537
536 538 # executable version (py2exe) doesn't support __file__
537 539 if util.mainfrozen():
538 540 module = sys.executable
539 541 else:
540 542 module = __file__
541 543 for f in path:
542 544 if f.startswith('/'):
543 545 p = f
544 546 else:
545 547 fl = f.split('/')
546 548 p = os.path.join(os.path.dirname(module), *fl)
547 549 if name:
548 550 p = os.path.join(p, name)
549 551 if name and os.path.exists(p):
550 552 return os.path.normpath(p)
551 553 elif os.path.isdir(p):
552 554 normpaths.append(os.path.normpath(p))
553 555
554 556 return normpaths
555 557
556 558 def stylemap(styles, paths=None):
557 559 """Return path to mapfile for a given style.
558 560
559 561 Searches mapfile in the following locations:
560 562 1. templatepath/style/map
561 563 2. templatepath/map-style
562 564 3. templatepath/map
563 565 """
564 566
565 567 if paths is None:
566 568 paths = templatepath()
567 569 elif isinstance(paths, str):
568 570 paths = [paths]
569 571
570 572 if isinstance(styles, str):
571 573 styles = [styles]
572 574
573 575 for style in styles:
574 576 if not style:
575 577 continue
576 578 locations = [os.path.join(style, 'map'), 'map-' + style]
577 579 locations.append('map')
578 580
579 581 for path in paths:
580 582 for location in locations:
581 583 mapfile = os.path.join(path, location)
582 584 if os.path.isfile(mapfile):
583 585 return style, mapfile
584 586
585 587 raise RuntimeError("No hgweb templates found in %r" % paths)
@@ -1,1596 +1,1608 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 --template '{file_copies % "{source} -> {name}\n"}' -r .
49 49 second -> fourth
50 50
51 51 Quoting for ui.logtemplate
52 52
53 53 $ hg tip --config "ui.logtemplate={rev}\n"
54 54 8
55 55 $ hg tip --config "ui.logtemplate='{rev}\n'"
56 56 8
57 57 $ hg tip --config 'ui.logtemplate="{rev}\n"'
58 58 8
59 59
60 60 Make sure user/global hgrc does not affect tests
61 61
62 62 $ echo '[ui]' > .hg/hgrc
63 63 $ echo 'logtemplate =' >> .hg/hgrc
64 64 $ echo 'style =' >> .hg/hgrc
65 65
66 66 Default style is like normal output:
67 67
68 68 $ hg log > log.out
69 69 $ hg log --style default > style.out
70 70 $ cmp log.out style.out || diff -u log.out style.out
71 71
72 72 $ hg log -v > log.out
73 73 $ hg log -v --style default > style.out
74 74 $ cmp log.out style.out || diff -u log.out style.out
75 75
76 76 $ hg log --debug > log.out
77 77 $ hg log --debug --style default > style.out
78 78 $ cmp log.out style.out || diff -u log.out style.out
79 79
80 80 Revision with no copies (used to print a traceback):
81 81
82 82 $ hg tip -v --template '\n'
83 83
84 84
85 85 Compact style works:
86 86
87 87 $ hg log --style compact
88 88 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
89 89 third
90 90
91 91 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
92 92 second
93 93
94 94 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
95 95 merge
96 96
97 97 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
98 98 new head
99 99
100 100 4 bbe44766e73d 1970-01-17 04:53 +0000 person
101 101 new branch
102 102
103 103 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
104 104 no user, no domain
105 105
106 106 2 97054abb4ab8 1970-01-14 21:20 +0000 other
107 107 no person
108 108
109 109 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
110 110 other 1
111 111
112 112 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
113 113 line 1
114 114
115 115
116 116 $ hg log -v --style compact
117 117 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
118 118 third
119 119
120 120 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
121 121 second
122 122
123 123 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
124 124 merge
125 125
126 126 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
127 127 new head
128 128
129 129 4 bbe44766e73d 1970-01-17 04:53 +0000 person
130 130 new branch
131 131
132 132 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
133 133 no user, no domain
134 134
135 135 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
136 136 no person
137 137
138 138 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
139 139 other 1
140 140 other 2
141 141
142 142 other 3
143 143
144 144 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
145 145 line 1
146 146 line 2
147 147
148 148
149 149 $ hg log --debug --style compact
150 150 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
151 151 third
152 152
153 153 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
154 154 second
155 155
156 156 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
157 157 merge
158 158
159 159 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
160 160 new head
161 161
162 162 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
163 163 new branch
164 164
165 165 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
166 166 no user, no domain
167 167
168 168 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
169 169 no person
170 170
171 171 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
172 172 other 1
173 173 other 2
174 174
175 175 other 3
176 176
177 177 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
178 178 line 1
179 179 line 2
180 180
181 181
182 182 Test xml styles:
183 183
184 184 $ hg log --style xml
185 185 <?xml version="1.0"?>
186 186 <log>
187 187 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
188 188 <tag>tip</tag>
189 189 <author email="test">test</author>
190 190 <date>2020-01-01T10:01:00+00:00</date>
191 191 <msg xml:space="preserve">third</msg>
192 192 </logentry>
193 193 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
194 194 <parent revision="-1" node="0000000000000000000000000000000000000000" />
195 195 <author email="user@hostname">User Name</author>
196 196 <date>1970-01-12T13:46:40+00:00</date>
197 197 <msg xml:space="preserve">second</msg>
198 198 </logentry>
199 199 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
200 200 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
201 201 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
202 202 <author email="person">person</author>
203 203 <date>1970-01-18T08:40:01+00:00</date>
204 204 <msg xml:space="preserve">merge</msg>
205 205 </logentry>
206 206 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
207 207 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
208 208 <author email="person">person</author>
209 209 <date>1970-01-18T08:40:00+00:00</date>
210 210 <msg xml:space="preserve">new head</msg>
211 211 </logentry>
212 212 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
213 213 <branch>foo</branch>
214 214 <author email="person">person</author>
215 215 <date>1970-01-17T04:53:20+00:00</date>
216 216 <msg xml:space="preserve">new branch</msg>
217 217 </logentry>
218 218 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
219 219 <author email="person">person</author>
220 220 <date>1970-01-16T01:06:40+00:00</date>
221 221 <msg xml:space="preserve">no user, no domain</msg>
222 222 </logentry>
223 223 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
224 224 <author email="other@place">other</author>
225 225 <date>1970-01-14T21:20:00+00:00</date>
226 226 <msg xml:space="preserve">no person</msg>
227 227 </logentry>
228 228 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
229 229 <author email="other@place">A. N. Other</author>
230 230 <date>1970-01-13T17:33:20+00:00</date>
231 231 <msg xml:space="preserve">other 1
232 232 other 2
233 233
234 234 other 3</msg>
235 235 </logentry>
236 236 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
237 237 <author email="user@hostname">User Name</author>
238 238 <date>1970-01-12T13:46:40+00:00</date>
239 239 <msg xml:space="preserve">line 1
240 240 line 2</msg>
241 241 </logentry>
242 242 </log>
243 243
244 244 $ hg log -v --style xml
245 245 <?xml version="1.0"?>
246 246 <log>
247 247 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
248 248 <tag>tip</tag>
249 249 <author email="test">test</author>
250 250 <date>2020-01-01T10:01:00+00:00</date>
251 251 <msg xml:space="preserve">third</msg>
252 252 <paths>
253 253 <path action="A">fourth</path>
254 254 <path action="A">third</path>
255 255 <path action="R">second</path>
256 256 </paths>
257 257 <copies>
258 258 <copy source="second">fourth</copy>
259 259 </copies>
260 260 </logentry>
261 261 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
262 262 <parent revision="-1" node="0000000000000000000000000000000000000000" />
263 263 <author email="user@hostname">User Name</author>
264 264 <date>1970-01-12T13:46:40+00:00</date>
265 265 <msg xml:space="preserve">second</msg>
266 266 <paths>
267 267 <path action="A">second</path>
268 268 </paths>
269 269 </logentry>
270 270 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
271 271 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
272 272 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
273 273 <author email="person">person</author>
274 274 <date>1970-01-18T08:40:01+00:00</date>
275 275 <msg xml:space="preserve">merge</msg>
276 276 <paths>
277 277 </paths>
278 278 </logentry>
279 279 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
280 280 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
281 281 <author email="person">person</author>
282 282 <date>1970-01-18T08:40:00+00:00</date>
283 283 <msg xml:space="preserve">new head</msg>
284 284 <paths>
285 285 <path action="A">d</path>
286 286 </paths>
287 287 </logentry>
288 288 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
289 289 <branch>foo</branch>
290 290 <author email="person">person</author>
291 291 <date>1970-01-17T04:53:20+00:00</date>
292 292 <msg xml:space="preserve">new branch</msg>
293 293 <paths>
294 294 </paths>
295 295 </logentry>
296 296 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
297 297 <author email="person">person</author>
298 298 <date>1970-01-16T01:06:40+00:00</date>
299 299 <msg xml:space="preserve">no user, no domain</msg>
300 300 <paths>
301 301 <path action="M">c</path>
302 302 </paths>
303 303 </logentry>
304 304 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
305 305 <author email="other@place">other</author>
306 306 <date>1970-01-14T21:20:00+00:00</date>
307 307 <msg xml:space="preserve">no person</msg>
308 308 <paths>
309 309 <path action="A">c</path>
310 310 </paths>
311 311 </logentry>
312 312 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
313 313 <author email="other@place">A. N. Other</author>
314 314 <date>1970-01-13T17:33:20+00:00</date>
315 315 <msg xml:space="preserve">other 1
316 316 other 2
317 317
318 318 other 3</msg>
319 319 <paths>
320 320 <path action="A">b</path>
321 321 </paths>
322 322 </logentry>
323 323 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
324 324 <author email="user@hostname">User Name</author>
325 325 <date>1970-01-12T13:46:40+00:00</date>
326 326 <msg xml:space="preserve">line 1
327 327 line 2</msg>
328 328 <paths>
329 329 <path action="A">a</path>
330 330 </paths>
331 331 </logentry>
332 332 </log>
333 333
334 334 $ hg log --debug --style xml
335 335 <?xml version="1.0"?>
336 336 <log>
337 337 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
338 338 <tag>tip</tag>
339 339 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
340 340 <parent revision="-1" node="0000000000000000000000000000000000000000" />
341 341 <author email="test">test</author>
342 342 <date>2020-01-01T10:01:00+00:00</date>
343 343 <msg xml:space="preserve">third</msg>
344 344 <paths>
345 345 <path action="A">fourth</path>
346 346 <path action="A">third</path>
347 347 <path action="R">second</path>
348 348 </paths>
349 349 <copies>
350 350 <copy source="second">fourth</copy>
351 351 </copies>
352 352 <extra key="branch">default</extra>
353 353 </logentry>
354 354 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
355 355 <parent revision="-1" node="0000000000000000000000000000000000000000" />
356 356 <parent revision="-1" node="0000000000000000000000000000000000000000" />
357 357 <author email="user@hostname">User Name</author>
358 358 <date>1970-01-12T13:46:40+00:00</date>
359 359 <msg xml:space="preserve">second</msg>
360 360 <paths>
361 361 <path action="A">second</path>
362 362 </paths>
363 363 <extra key="branch">default</extra>
364 364 </logentry>
365 365 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
366 366 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
367 367 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
368 368 <author email="person">person</author>
369 369 <date>1970-01-18T08:40:01+00:00</date>
370 370 <msg xml:space="preserve">merge</msg>
371 371 <paths>
372 372 </paths>
373 373 <extra key="branch">default</extra>
374 374 </logentry>
375 375 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
376 376 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
377 377 <parent revision="-1" node="0000000000000000000000000000000000000000" />
378 378 <author email="person">person</author>
379 379 <date>1970-01-18T08:40:00+00:00</date>
380 380 <msg xml:space="preserve">new head</msg>
381 381 <paths>
382 382 <path action="A">d</path>
383 383 </paths>
384 384 <extra key="branch">default</extra>
385 385 </logentry>
386 386 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
387 387 <branch>foo</branch>
388 388 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
389 389 <parent revision="-1" node="0000000000000000000000000000000000000000" />
390 390 <author email="person">person</author>
391 391 <date>1970-01-17T04:53:20+00:00</date>
392 392 <msg xml:space="preserve">new branch</msg>
393 393 <paths>
394 394 </paths>
395 395 <extra key="branch">foo</extra>
396 396 </logentry>
397 397 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
398 398 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
399 399 <parent revision="-1" node="0000000000000000000000000000000000000000" />
400 400 <author email="person">person</author>
401 401 <date>1970-01-16T01:06:40+00:00</date>
402 402 <msg xml:space="preserve">no user, no domain</msg>
403 403 <paths>
404 404 <path action="M">c</path>
405 405 </paths>
406 406 <extra key="branch">default</extra>
407 407 </logentry>
408 408 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
409 409 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
410 410 <parent revision="-1" node="0000000000000000000000000000000000000000" />
411 411 <author email="other@place">other</author>
412 412 <date>1970-01-14T21:20:00+00:00</date>
413 413 <msg xml:space="preserve">no person</msg>
414 414 <paths>
415 415 <path action="A">c</path>
416 416 </paths>
417 417 <extra key="branch">default</extra>
418 418 </logentry>
419 419 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
420 420 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
421 421 <parent revision="-1" node="0000000000000000000000000000000000000000" />
422 422 <author email="other@place">A. N. Other</author>
423 423 <date>1970-01-13T17:33:20+00:00</date>
424 424 <msg xml:space="preserve">other 1
425 425 other 2
426 426
427 427 other 3</msg>
428 428 <paths>
429 429 <path action="A">b</path>
430 430 </paths>
431 431 <extra key="branch">default</extra>
432 432 </logentry>
433 433 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
434 434 <parent revision="-1" node="0000000000000000000000000000000000000000" />
435 435 <parent revision="-1" node="0000000000000000000000000000000000000000" />
436 436 <author email="user@hostname">User Name</author>
437 437 <date>1970-01-12T13:46:40+00:00</date>
438 438 <msg xml:space="preserve">line 1
439 439 line 2</msg>
440 440 <paths>
441 441 <path action="A">a</path>
442 442 </paths>
443 443 <extra key="branch">default</extra>
444 444 </logentry>
445 445 </log>
446 446
447 447
448 448 Error if style not readable:
449 449
450 450 #if unix-permissions
451 451 $ touch q
452 452 $ chmod 0 q
453 453 $ hg log --style ./q
454 454 abort: Permission denied: ./q
455 455 [255]
456 456 #endif
457 457
458 458 Error if no style:
459 459
460 460 $ hg log --style notexist
461 461 abort: style 'notexist' not found
462 462 (available styles: bisect, changelog, compact, default, phases, xml)
463 463 [255]
464 464
465 465 Error if style missing key:
466 466
467 467 $ echo 'q = q' > t
468 468 $ hg log --style ./t
469 469 abort: "changeset" not in template map
470 470 [255]
471 471
472 472 Error if style missing value:
473 473
474 474 $ echo 'changeset =' > t
475 475 $ hg log --style t
476 476 abort: t:1: missing value
477 477 [255]
478 478
479 479 Error if include fails:
480 480
481 481 $ echo 'changeset = q' >> t
482 482 #if unix-permissions
483 483 $ hg log --style ./t
484 484 abort: template file ./q: Permission denied
485 485 [255]
486 486 $ rm q
487 487 #endif
488 488
489 489 Include works:
490 490
491 491 $ echo '{rev}' > q
492 492 $ hg log --style ./t
493 493 8
494 494 7
495 495 6
496 496 5
497 497 4
498 498 3
499 499 2
500 500 1
501 501 0
502 502
503 503 Missing non-standard names give no error (backward compatibility):
504 504
505 505 $ echo "changeset = '{c}'" > t
506 506 $ hg log --style ./t
507 507
508 508 Defining non-standard name works:
509 509
510 510 $ cat <<EOF > t
511 511 > changeset = '{c}'
512 512 > c = q
513 513 > EOF
514 514 $ hg log --style ./t
515 515 8
516 516 7
517 517 6
518 518 5
519 519 4
520 520 3
521 521 2
522 522 1
523 523 0
524 524
525 525 ui.style works:
526 526
527 527 $ echo '[ui]' > .hg/hgrc
528 528 $ echo 'style = t' >> .hg/hgrc
529 529 $ hg log
530 530 8
531 531 7
532 532 6
533 533 5
534 534 4
535 535 3
536 536 2
537 537 1
538 538 0
539 539
540 540
541 541 Issue338:
542 542
543 543 $ hg log --style=changelog > changelog
544 544
545 545 $ cat changelog
546 546 2020-01-01 test <test>
547 547
548 548 * fourth, second, third:
549 549 third
550 550 [95c24699272e] [tip]
551 551
552 552 1970-01-12 User Name <user@hostname>
553 553
554 554 * second:
555 555 second
556 556 [29114dbae42b]
557 557
558 558 1970-01-18 person <person>
559 559
560 560 * merge
561 561 [d41e714fe50d]
562 562
563 563 * d:
564 564 new head
565 565 [13207e5a10d9]
566 566
567 567 1970-01-17 person <person>
568 568
569 569 * new branch
570 570 [bbe44766e73d] <foo>
571 571
572 572 1970-01-16 person <person>
573 573
574 574 * c:
575 575 no user, no domain
576 576 [10e46f2dcbf4]
577 577
578 578 1970-01-14 other <other@place>
579 579
580 580 * c:
581 581 no person
582 582 [97054abb4ab8]
583 583
584 584 1970-01-13 A. N. Other <other@place>
585 585
586 586 * b:
587 587 other 1 other 2
588 588
589 589 other 3
590 590 [b608e9d1a3f0]
591 591
592 592 1970-01-12 User Name <user@hostname>
593 593
594 594 * a:
595 595 line 1 line 2
596 596 [1e4e1b8f71e0]
597 597
598 598
599 599 Issue2130: xml output for 'hg heads' is malformed
600 600
601 601 $ hg heads --style changelog
602 602 2020-01-01 test <test>
603 603
604 604 * fourth, second, third:
605 605 third
606 606 [95c24699272e] [tip]
607 607
608 608 1970-01-18 person <person>
609 609
610 610 * merge
611 611 [d41e714fe50d]
612 612
613 613 1970-01-17 person <person>
614 614
615 615 * new branch
616 616 [bbe44766e73d] <foo>
617 617
618 618
619 619 Keys work:
620 620
621 621 $ for key in author branch branches date desc file_adds file_dels file_mods \
622 622 > file_copies file_copies_switch files \
623 623 > manifest node parents rev tags diffstat extras \
624 624 > p1rev p2rev p1node p2node; do
625 625 > for mode in '' --verbose --debug; do
626 626 > hg log $mode --template "$key$mode: {$key}\n"
627 627 > done
628 628 > done
629 629 author: test
630 630 author: User Name <user@hostname>
631 631 author: person
632 632 author: person
633 633 author: person
634 634 author: person
635 635 author: other@place
636 636 author: A. N. Other <other@place>
637 637 author: User Name <user@hostname>
638 638 author--verbose: test
639 639 author--verbose: User Name <user@hostname>
640 640 author--verbose: person
641 641 author--verbose: person
642 642 author--verbose: person
643 643 author--verbose: person
644 644 author--verbose: other@place
645 645 author--verbose: A. N. Other <other@place>
646 646 author--verbose: User Name <user@hostname>
647 647 author--debug: test
648 648 author--debug: User Name <user@hostname>
649 649 author--debug: person
650 650 author--debug: person
651 651 author--debug: person
652 652 author--debug: person
653 653 author--debug: other@place
654 654 author--debug: A. N. Other <other@place>
655 655 author--debug: User Name <user@hostname>
656 656 branch: default
657 657 branch: default
658 658 branch: default
659 659 branch: default
660 660 branch: foo
661 661 branch: default
662 662 branch: default
663 663 branch: default
664 664 branch: default
665 665 branch--verbose: default
666 666 branch--verbose: default
667 667 branch--verbose: default
668 668 branch--verbose: default
669 669 branch--verbose: foo
670 670 branch--verbose: default
671 671 branch--verbose: default
672 672 branch--verbose: default
673 673 branch--verbose: default
674 674 branch--debug: default
675 675 branch--debug: default
676 676 branch--debug: default
677 677 branch--debug: default
678 678 branch--debug: foo
679 679 branch--debug: default
680 680 branch--debug: default
681 681 branch--debug: default
682 682 branch--debug: default
683 683 branches:
684 684 branches:
685 685 branches:
686 686 branches:
687 687 branches: foo
688 688 branches:
689 689 branches:
690 690 branches:
691 691 branches:
692 692 branches--verbose:
693 693 branches--verbose:
694 694 branches--verbose:
695 695 branches--verbose:
696 696 branches--verbose: foo
697 697 branches--verbose:
698 698 branches--verbose:
699 699 branches--verbose:
700 700 branches--verbose:
701 701 branches--debug:
702 702 branches--debug:
703 703 branches--debug:
704 704 branches--debug:
705 705 branches--debug: foo
706 706 branches--debug:
707 707 branches--debug:
708 708 branches--debug:
709 709 branches--debug:
710 710 date: 1577872860.00
711 711 date: 1000000.00
712 712 date: 1500001.00
713 713 date: 1500000.00
714 714 date: 1400000.00
715 715 date: 1300000.00
716 716 date: 1200000.00
717 717 date: 1100000.00
718 718 date: 1000000.00
719 719 date--verbose: 1577872860.00
720 720 date--verbose: 1000000.00
721 721 date--verbose: 1500001.00
722 722 date--verbose: 1500000.00
723 723 date--verbose: 1400000.00
724 724 date--verbose: 1300000.00
725 725 date--verbose: 1200000.00
726 726 date--verbose: 1100000.00
727 727 date--verbose: 1000000.00
728 728 date--debug: 1577872860.00
729 729 date--debug: 1000000.00
730 730 date--debug: 1500001.00
731 731 date--debug: 1500000.00
732 732 date--debug: 1400000.00
733 733 date--debug: 1300000.00
734 734 date--debug: 1200000.00
735 735 date--debug: 1100000.00
736 736 date--debug: 1000000.00
737 737 desc: third
738 738 desc: second
739 739 desc: merge
740 740 desc: new head
741 741 desc: new branch
742 742 desc: no user, no domain
743 743 desc: no person
744 744 desc: other 1
745 745 other 2
746 746
747 747 other 3
748 748 desc: line 1
749 749 line 2
750 750 desc--verbose: third
751 751 desc--verbose: second
752 752 desc--verbose: merge
753 753 desc--verbose: new head
754 754 desc--verbose: new branch
755 755 desc--verbose: no user, no domain
756 756 desc--verbose: no person
757 757 desc--verbose: other 1
758 758 other 2
759 759
760 760 other 3
761 761 desc--verbose: line 1
762 762 line 2
763 763 desc--debug: third
764 764 desc--debug: second
765 765 desc--debug: merge
766 766 desc--debug: new head
767 767 desc--debug: new branch
768 768 desc--debug: no user, no domain
769 769 desc--debug: no person
770 770 desc--debug: other 1
771 771 other 2
772 772
773 773 other 3
774 774 desc--debug: line 1
775 775 line 2
776 776 file_adds: fourth third
777 777 file_adds: second
778 778 file_adds:
779 779 file_adds: d
780 780 file_adds:
781 781 file_adds:
782 782 file_adds: c
783 783 file_adds: b
784 784 file_adds: a
785 785 file_adds--verbose: fourth third
786 786 file_adds--verbose: second
787 787 file_adds--verbose:
788 788 file_adds--verbose: d
789 789 file_adds--verbose:
790 790 file_adds--verbose:
791 791 file_adds--verbose: c
792 792 file_adds--verbose: b
793 793 file_adds--verbose: a
794 794 file_adds--debug: fourth third
795 795 file_adds--debug: second
796 796 file_adds--debug:
797 797 file_adds--debug: d
798 798 file_adds--debug:
799 799 file_adds--debug:
800 800 file_adds--debug: c
801 801 file_adds--debug: b
802 802 file_adds--debug: a
803 803 file_dels: second
804 804 file_dels:
805 805 file_dels:
806 806 file_dels:
807 807 file_dels:
808 808 file_dels:
809 809 file_dels:
810 810 file_dels:
811 811 file_dels:
812 812 file_dels--verbose: second
813 813 file_dels--verbose:
814 814 file_dels--verbose:
815 815 file_dels--verbose:
816 816 file_dels--verbose:
817 817 file_dels--verbose:
818 818 file_dels--verbose:
819 819 file_dels--verbose:
820 820 file_dels--verbose:
821 821 file_dels--debug: second
822 822 file_dels--debug:
823 823 file_dels--debug:
824 824 file_dels--debug:
825 825 file_dels--debug:
826 826 file_dels--debug:
827 827 file_dels--debug:
828 828 file_dels--debug:
829 829 file_dels--debug:
830 830 file_mods:
831 831 file_mods:
832 832 file_mods:
833 833 file_mods:
834 834 file_mods:
835 835 file_mods: c
836 836 file_mods:
837 837 file_mods:
838 838 file_mods:
839 839 file_mods--verbose:
840 840 file_mods--verbose:
841 841 file_mods--verbose:
842 842 file_mods--verbose:
843 843 file_mods--verbose:
844 844 file_mods--verbose: c
845 845 file_mods--verbose:
846 846 file_mods--verbose:
847 847 file_mods--verbose:
848 848 file_mods--debug:
849 849 file_mods--debug:
850 850 file_mods--debug:
851 851 file_mods--debug:
852 852 file_mods--debug:
853 853 file_mods--debug: c
854 854 file_mods--debug:
855 855 file_mods--debug:
856 856 file_mods--debug:
857 857 file_copies: fourth (second)
858 858 file_copies:
859 859 file_copies:
860 860 file_copies:
861 861 file_copies:
862 862 file_copies:
863 863 file_copies:
864 864 file_copies:
865 865 file_copies:
866 866 file_copies--verbose: fourth (second)
867 867 file_copies--verbose:
868 868 file_copies--verbose:
869 869 file_copies--verbose:
870 870 file_copies--verbose:
871 871 file_copies--verbose:
872 872 file_copies--verbose:
873 873 file_copies--verbose:
874 874 file_copies--verbose:
875 875 file_copies--debug: fourth (second)
876 876 file_copies--debug:
877 877 file_copies--debug:
878 878 file_copies--debug:
879 879 file_copies--debug:
880 880 file_copies--debug:
881 881 file_copies--debug:
882 882 file_copies--debug:
883 883 file_copies--debug:
884 884 file_copies_switch:
885 885 file_copies_switch:
886 886 file_copies_switch:
887 887 file_copies_switch:
888 888 file_copies_switch:
889 889 file_copies_switch:
890 890 file_copies_switch:
891 891 file_copies_switch:
892 892 file_copies_switch:
893 893 file_copies_switch--verbose:
894 894 file_copies_switch--verbose:
895 895 file_copies_switch--verbose:
896 896 file_copies_switch--verbose:
897 897 file_copies_switch--verbose:
898 898 file_copies_switch--verbose:
899 899 file_copies_switch--verbose:
900 900 file_copies_switch--verbose:
901 901 file_copies_switch--verbose:
902 902 file_copies_switch--debug:
903 903 file_copies_switch--debug:
904 904 file_copies_switch--debug:
905 905 file_copies_switch--debug:
906 906 file_copies_switch--debug:
907 907 file_copies_switch--debug:
908 908 file_copies_switch--debug:
909 909 file_copies_switch--debug:
910 910 file_copies_switch--debug:
911 911 files: fourth second third
912 912 files: second
913 913 files:
914 914 files: d
915 915 files:
916 916 files: c
917 917 files: c
918 918 files: b
919 919 files: a
920 920 files--verbose: fourth second third
921 921 files--verbose: second
922 922 files--verbose:
923 923 files--verbose: d
924 924 files--verbose:
925 925 files--verbose: c
926 926 files--verbose: c
927 927 files--verbose: b
928 928 files--verbose: a
929 929 files--debug: fourth second third
930 930 files--debug: second
931 931 files--debug:
932 932 files--debug: d
933 933 files--debug:
934 934 files--debug: c
935 935 files--debug: c
936 936 files--debug: b
937 937 files--debug: a
938 938 manifest: 6:94961b75a2da
939 939 manifest: 5:f2dbc354b94e
940 940 manifest: 4:4dc3def4f9b4
941 941 manifest: 4:4dc3def4f9b4
942 942 manifest: 3:cb5a1327723b
943 943 manifest: 3:cb5a1327723b
944 944 manifest: 2:6e0e82995c35
945 945 manifest: 1:4e8d705b1e53
946 946 manifest: 0:a0c8bcbbb45c
947 947 manifest--verbose: 6:94961b75a2da
948 948 manifest--verbose: 5:f2dbc354b94e
949 949 manifest--verbose: 4:4dc3def4f9b4
950 950 manifest--verbose: 4:4dc3def4f9b4
951 951 manifest--verbose: 3:cb5a1327723b
952 952 manifest--verbose: 3:cb5a1327723b
953 953 manifest--verbose: 2:6e0e82995c35
954 954 manifest--verbose: 1:4e8d705b1e53
955 955 manifest--verbose: 0:a0c8bcbbb45c
956 956 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
957 957 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
958 958 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
959 959 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
960 960 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
961 961 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
962 962 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
963 963 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
964 964 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
965 965 node: 95c24699272ef57d062b8bccc32c878bf841784a
966 966 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
967 967 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
968 968 node: 13207e5a10d9fd28ec424934298e176197f2c67f
969 969 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
970 970 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
971 971 node: 97054abb4ab824450e9164180baf491ae0078465
972 972 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
973 973 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
974 974 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
975 975 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
976 976 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
977 977 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
978 978 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
979 979 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
980 980 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
981 981 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
982 982 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
983 983 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
984 984 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
985 985 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
986 986 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
987 987 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
988 988 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
989 989 node--debug: 97054abb4ab824450e9164180baf491ae0078465
990 990 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
991 991 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
992 992 parents:
993 993 parents: -1:000000000000
994 994 parents: 5:13207e5a10d9 4:bbe44766e73d
995 995 parents: 3:10e46f2dcbf4
996 996 parents:
997 997 parents:
998 998 parents:
999 999 parents:
1000 1000 parents:
1001 1001 parents--verbose:
1002 1002 parents--verbose: -1:000000000000
1003 1003 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1004 1004 parents--verbose: 3:10e46f2dcbf4
1005 1005 parents--verbose:
1006 1006 parents--verbose:
1007 1007 parents--verbose:
1008 1008 parents--verbose:
1009 1009 parents--verbose:
1010 1010 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1011 1011 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1012 1012 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1013 1013 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1014 1014 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1015 1015 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1016 1016 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1017 1017 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1018 1018 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1019 1019 rev: 8
1020 1020 rev: 7
1021 1021 rev: 6
1022 1022 rev: 5
1023 1023 rev: 4
1024 1024 rev: 3
1025 1025 rev: 2
1026 1026 rev: 1
1027 1027 rev: 0
1028 1028 rev--verbose: 8
1029 1029 rev--verbose: 7
1030 1030 rev--verbose: 6
1031 1031 rev--verbose: 5
1032 1032 rev--verbose: 4
1033 1033 rev--verbose: 3
1034 1034 rev--verbose: 2
1035 1035 rev--verbose: 1
1036 1036 rev--verbose: 0
1037 1037 rev--debug: 8
1038 1038 rev--debug: 7
1039 1039 rev--debug: 6
1040 1040 rev--debug: 5
1041 1041 rev--debug: 4
1042 1042 rev--debug: 3
1043 1043 rev--debug: 2
1044 1044 rev--debug: 1
1045 1045 rev--debug: 0
1046 1046 tags: tip
1047 1047 tags:
1048 1048 tags:
1049 1049 tags:
1050 1050 tags:
1051 1051 tags:
1052 1052 tags:
1053 1053 tags:
1054 1054 tags:
1055 1055 tags--verbose: tip
1056 1056 tags--verbose:
1057 1057 tags--verbose:
1058 1058 tags--verbose:
1059 1059 tags--verbose:
1060 1060 tags--verbose:
1061 1061 tags--verbose:
1062 1062 tags--verbose:
1063 1063 tags--verbose:
1064 1064 tags--debug: tip
1065 1065 tags--debug:
1066 1066 tags--debug:
1067 1067 tags--debug:
1068 1068 tags--debug:
1069 1069 tags--debug:
1070 1070 tags--debug:
1071 1071 tags--debug:
1072 1072 tags--debug:
1073 1073 diffstat: 3: +2/-1
1074 1074 diffstat: 1: +1/-0
1075 1075 diffstat: 0: +0/-0
1076 1076 diffstat: 1: +1/-0
1077 1077 diffstat: 0: +0/-0
1078 1078 diffstat: 1: +1/-0
1079 1079 diffstat: 1: +4/-0
1080 1080 diffstat: 1: +2/-0
1081 1081 diffstat: 1: +1/-0
1082 1082 diffstat--verbose: 3: +2/-1
1083 1083 diffstat--verbose: 1: +1/-0
1084 1084 diffstat--verbose: 0: +0/-0
1085 1085 diffstat--verbose: 1: +1/-0
1086 1086 diffstat--verbose: 0: +0/-0
1087 1087 diffstat--verbose: 1: +1/-0
1088 1088 diffstat--verbose: 1: +4/-0
1089 1089 diffstat--verbose: 1: +2/-0
1090 1090 diffstat--verbose: 1: +1/-0
1091 1091 diffstat--debug: 3: +2/-1
1092 1092 diffstat--debug: 1: +1/-0
1093 1093 diffstat--debug: 0: +0/-0
1094 1094 diffstat--debug: 1: +1/-0
1095 1095 diffstat--debug: 0: +0/-0
1096 1096 diffstat--debug: 1: +1/-0
1097 1097 diffstat--debug: 1: +4/-0
1098 1098 diffstat--debug: 1: +2/-0
1099 1099 diffstat--debug: 1: +1/-0
1100 1100 extras: branch=default
1101 1101 extras: branch=default
1102 1102 extras: branch=default
1103 1103 extras: branch=default
1104 1104 extras: branch=foo
1105 1105 extras: branch=default
1106 1106 extras: branch=default
1107 1107 extras: branch=default
1108 1108 extras: branch=default
1109 1109 extras--verbose: branch=default
1110 1110 extras--verbose: branch=default
1111 1111 extras--verbose: branch=default
1112 1112 extras--verbose: branch=default
1113 1113 extras--verbose: branch=foo
1114 1114 extras--verbose: branch=default
1115 1115 extras--verbose: branch=default
1116 1116 extras--verbose: branch=default
1117 1117 extras--verbose: branch=default
1118 1118 extras--debug: branch=default
1119 1119 extras--debug: branch=default
1120 1120 extras--debug: branch=default
1121 1121 extras--debug: branch=default
1122 1122 extras--debug: branch=foo
1123 1123 extras--debug: branch=default
1124 1124 extras--debug: branch=default
1125 1125 extras--debug: branch=default
1126 1126 extras--debug: branch=default
1127 1127 p1rev: 7
1128 1128 p1rev: -1
1129 1129 p1rev: 5
1130 1130 p1rev: 3
1131 1131 p1rev: 3
1132 1132 p1rev: 2
1133 1133 p1rev: 1
1134 1134 p1rev: 0
1135 1135 p1rev: -1
1136 1136 p1rev--verbose: 7
1137 1137 p1rev--verbose: -1
1138 1138 p1rev--verbose: 5
1139 1139 p1rev--verbose: 3
1140 1140 p1rev--verbose: 3
1141 1141 p1rev--verbose: 2
1142 1142 p1rev--verbose: 1
1143 1143 p1rev--verbose: 0
1144 1144 p1rev--verbose: -1
1145 1145 p1rev--debug: 7
1146 1146 p1rev--debug: -1
1147 1147 p1rev--debug: 5
1148 1148 p1rev--debug: 3
1149 1149 p1rev--debug: 3
1150 1150 p1rev--debug: 2
1151 1151 p1rev--debug: 1
1152 1152 p1rev--debug: 0
1153 1153 p1rev--debug: -1
1154 1154 p2rev: -1
1155 1155 p2rev: -1
1156 1156 p2rev: 4
1157 1157 p2rev: -1
1158 1158 p2rev: -1
1159 1159 p2rev: -1
1160 1160 p2rev: -1
1161 1161 p2rev: -1
1162 1162 p2rev: -1
1163 1163 p2rev--verbose: -1
1164 1164 p2rev--verbose: -1
1165 1165 p2rev--verbose: 4
1166 1166 p2rev--verbose: -1
1167 1167 p2rev--verbose: -1
1168 1168 p2rev--verbose: -1
1169 1169 p2rev--verbose: -1
1170 1170 p2rev--verbose: -1
1171 1171 p2rev--verbose: -1
1172 1172 p2rev--debug: -1
1173 1173 p2rev--debug: -1
1174 1174 p2rev--debug: 4
1175 1175 p2rev--debug: -1
1176 1176 p2rev--debug: -1
1177 1177 p2rev--debug: -1
1178 1178 p2rev--debug: -1
1179 1179 p2rev--debug: -1
1180 1180 p2rev--debug: -1
1181 1181 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1182 1182 p1node: 0000000000000000000000000000000000000000
1183 1183 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1184 1184 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1185 1185 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1186 1186 p1node: 97054abb4ab824450e9164180baf491ae0078465
1187 1187 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1188 1188 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1189 1189 p1node: 0000000000000000000000000000000000000000
1190 1190 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1191 1191 p1node--verbose: 0000000000000000000000000000000000000000
1192 1192 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1193 1193 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1194 1194 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1195 1195 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1196 1196 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1197 1197 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1198 1198 p1node--verbose: 0000000000000000000000000000000000000000
1199 1199 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1200 1200 p1node--debug: 0000000000000000000000000000000000000000
1201 1201 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1202 1202 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1203 1203 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1204 1204 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1205 1205 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1206 1206 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1207 1207 p1node--debug: 0000000000000000000000000000000000000000
1208 1208 p2node: 0000000000000000000000000000000000000000
1209 1209 p2node: 0000000000000000000000000000000000000000
1210 1210 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1211 1211 p2node: 0000000000000000000000000000000000000000
1212 1212 p2node: 0000000000000000000000000000000000000000
1213 1213 p2node: 0000000000000000000000000000000000000000
1214 1214 p2node: 0000000000000000000000000000000000000000
1215 1215 p2node: 0000000000000000000000000000000000000000
1216 1216 p2node: 0000000000000000000000000000000000000000
1217 1217 p2node--verbose: 0000000000000000000000000000000000000000
1218 1218 p2node--verbose: 0000000000000000000000000000000000000000
1219 1219 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1220 1220 p2node--verbose: 0000000000000000000000000000000000000000
1221 1221 p2node--verbose: 0000000000000000000000000000000000000000
1222 1222 p2node--verbose: 0000000000000000000000000000000000000000
1223 1223 p2node--verbose: 0000000000000000000000000000000000000000
1224 1224 p2node--verbose: 0000000000000000000000000000000000000000
1225 1225 p2node--verbose: 0000000000000000000000000000000000000000
1226 1226 p2node--debug: 0000000000000000000000000000000000000000
1227 1227 p2node--debug: 0000000000000000000000000000000000000000
1228 1228 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1229 1229 p2node--debug: 0000000000000000000000000000000000000000
1230 1230 p2node--debug: 0000000000000000000000000000000000000000
1231 1231 p2node--debug: 0000000000000000000000000000000000000000
1232 1232 p2node--debug: 0000000000000000000000000000000000000000
1233 1233 p2node--debug: 0000000000000000000000000000000000000000
1234 1234 p2node--debug: 0000000000000000000000000000000000000000
1235 1235
1236 1236 Filters work:
1237 1237
1238 1238 $ hg log --template '{author|domain}\n'
1239 1239
1240 1240 hostname
1241 1241
1242 1242
1243 1243
1244 1244
1245 1245 place
1246 1246 place
1247 1247 hostname
1248 1248
1249 1249 $ hg log --template '{author|person}\n'
1250 1250 test
1251 1251 User Name
1252 1252 person
1253 1253 person
1254 1254 person
1255 1255 person
1256 1256 other
1257 1257 A. N. Other
1258 1258 User Name
1259 1259
1260 1260 $ hg log --template '{author|user}\n'
1261 1261 test
1262 1262 user
1263 1263 person
1264 1264 person
1265 1265 person
1266 1266 person
1267 1267 other
1268 1268 other
1269 1269 user
1270 1270
1271 1271 $ hg log --template '{date|date}\n'
1272 1272 Wed Jan 01 10:01:00 2020 +0000
1273 1273 Mon Jan 12 13:46:40 1970 +0000
1274 1274 Sun Jan 18 08:40:01 1970 +0000
1275 1275 Sun Jan 18 08:40:00 1970 +0000
1276 1276 Sat Jan 17 04:53:20 1970 +0000
1277 1277 Fri Jan 16 01:06:40 1970 +0000
1278 1278 Wed Jan 14 21:20:00 1970 +0000
1279 1279 Tue Jan 13 17:33:20 1970 +0000
1280 1280 Mon Jan 12 13:46:40 1970 +0000
1281 1281
1282 1282 $ hg log --template '{date|isodate}\n'
1283 1283 2020-01-01 10:01 +0000
1284 1284 1970-01-12 13:46 +0000
1285 1285 1970-01-18 08:40 +0000
1286 1286 1970-01-18 08:40 +0000
1287 1287 1970-01-17 04:53 +0000
1288 1288 1970-01-16 01:06 +0000
1289 1289 1970-01-14 21:20 +0000
1290 1290 1970-01-13 17:33 +0000
1291 1291 1970-01-12 13:46 +0000
1292 1292
1293 1293 $ hg log --template '{date|isodatesec}\n'
1294 1294 2020-01-01 10:01:00 +0000
1295 1295 1970-01-12 13:46:40 +0000
1296 1296 1970-01-18 08:40:01 +0000
1297 1297 1970-01-18 08:40:00 +0000
1298 1298 1970-01-17 04:53:20 +0000
1299 1299 1970-01-16 01:06:40 +0000
1300 1300 1970-01-14 21:20:00 +0000
1301 1301 1970-01-13 17:33:20 +0000
1302 1302 1970-01-12 13:46:40 +0000
1303 1303
1304 1304 $ hg log --template '{date|rfc822date}\n'
1305 1305 Wed, 01 Jan 2020 10:01:00 +0000
1306 1306 Mon, 12 Jan 1970 13:46:40 +0000
1307 1307 Sun, 18 Jan 1970 08:40:01 +0000
1308 1308 Sun, 18 Jan 1970 08:40:00 +0000
1309 1309 Sat, 17 Jan 1970 04:53:20 +0000
1310 1310 Fri, 16 Jan 1970 01:06:40 +0000
1311 1311 Wed, 14 Jan 1970 21:20:00 +0000
1312 1312 Tue, 13 Jan 1970 17:33:20 +0000
1313 1313 Mon, 12 Jan 1970 13:46:40 +0000
1314 1314
1315 1315 $ hg log --template '{desc|firstline}\n'
1316 1316 third
1317 1317 second
1318 1318 merge
1319 1319 new head
1320 1320 new branch
1321 1321 no user, no domain
1322 1322 no person
1323 1323 other 1
1324 1324 line 1
1325 1325
1326 1326 $ hg log --template '{node|short}\n'
1327 1327 95c24699272e
1328 1328 29114dbae42b
1329 1329 d41e714fe50d
1330 1330 13207e5a10d9
1331 1331 bbe44766e73d
1332 1332 10e46f2dcbf4
1333 1333 97054abb4ab8
1334 1334 b608e9d1a3f0
1335 1335 1e4e1b8f71e0
1336 1336
1337 1337 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1338 1338 <changeset author="test"/>
1339 1339 <changeset author="User Name &lt;user@hostname&gt;"/>
1340 1340 <changeset author="person"/>
1341 1341 <changeset author="person"/>
1342 1342 <changeset author="person"/>
1343 1343 <changeset author="person"/>
1344 1344 <changeset author="other@place"/>
1345 1345 <changeset author="A. N. Other &lt;other@place&gt;"/>
1346 1346 <changeset author="User Name &lt;user@hostname&gt;"/>
1347 1347
1348 1348 $ hg log --template '{rev}: {children}\n'
1349 1349 8:
1350 1350 7: 8:95c24699272e
1351 1351 6:
1352 1352 5: 6:d41e714fe50d
1353 1353 4: 6:d41e714fe50d
1354 1354 3: 4:bbe44766e73d 5:13207e5a10d9
1355 1355 2: 3:10e46f2dcbf4
1356 1356 1: 2:97054abb4ab8
1357 1357 0: 1:b608e9d1a3f0
1358 1358
1359 1359 Formatnode filter works:
1360 1360
1361 1361 $ hg -q log -r 0 --template '{node|formatnode}\n'
1362 1362 1e4e1b8f71e0
1363 1363
1364 1364 $ hg log -r 0 --template '{node|formatnode}\n'
1365 1365 1e4e1b8f71e0
1366 1366
1367 1367 $ hg -v log -r 0 --template '{node|formatnode}\n'
1368 1368 1e4e1b8f71e0
1369 1369
1370 1370 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1371 1371 1e4e1b8f71e05681d422154f5421e385fec3454f
1372 1372
1373 1373 Age filter:
1374 1374
1375 1375 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1376 1376
1377 1377 >>> from datetime import datetime, timedelta
1378 1378 >>> fp = open('a', 'w')
1379 1379 >>> n = datetime.now() + timedelta(366 * 7)
1380 1380 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1381 1381 >>> fp.close()
1382 1382 $ hg add a
1383 1383 $ hg commit -m future -d "`cat a`"
1384 1384
1385 1385 $ hg log -l1 --template '{date|age}\n'
1386 1386 7 years from now
1387 1387
1388 1388 Error on syntax:
1389 1389
1390 1390 $ echo 'x = "f' >> t
1391 1391 $ hg log
1392 1392 abort: t:3: unmatched quotes
1393 1393 [255]
1394 1394
1395 1395 Behind the scenes, this will throw TypeError
1396 1396
1397 1397 $ hg log -l 3 --template '{date|obfuscate}\n'
1398 1398 abort: template filter 'obfuscate' is not compatible with keyword 'date'
1399 1399 [255]
1400 1400
1401 1401 Behind the scenes, this will throw a ValueError
1402 1402
1403 1403 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
1404 1404 abort: template filter 'shortdate' is not compatible with keyword 'desc'
1405 1405 [255]
1406 1406
1407 1407 Behind the scenes, this will throw AttributeError
1408 1408
1409 1409 $ hg log -l 3 --template 'line: {date|escape}\n'
1410 1410 abort: template filter 'escape' is not compatible with keyword 'date'
1411 1411 [255]
1412 1412
1413 1413 Behind the scenes, this will throw ValueError
1414 1414
1415 1415 $ hg tip --template '{author|email|date}\n'
1416 1416 abort: template filter 'datefilter' is not compatible with keyword 'author'
1417 1417 [255]
1418 1418
1419 1419 $ cd ..
1420 1420
1421 1421
1422 1422 latesttag:
1423 1423
1424 1424 $ hg init latesttag
1425 1425 $ cd latesttag
1426 1426
1427 1427 $ echo a > file
1428 1428 $ hg ci -Am a -d '0 0'
1429 1429 adding file
1430 1430
1431 1431 $ echo b >> file
1432 1432 $ hg ci -m b -d '1 0'
1433 1433
1434 1434 $ echo c >> head1
1435 1435 $ hg ci -Am h1c -d '2 0'
1436 1436 adding head1
1437 1437
1438 1438 $ hg update -q 1
1439 1439 $ echo d >> head2
1440 1440 $ hg ci -Am h2d -d '3 0'
1441 1441 adding head2
1442 1442 created new head
1443 1443
1444 1444 $ echo e >> head2
1445 1445 $ hg ci -m h2e -d '4 0'
1446 1446
1447 1447 $ hg merge -q
1448 1448 $ hg ci -m merge -d '5 0'
1449 1449
1450 1450 No tag set:
1451 1451
1452 1452 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1453 1453 5: null+5
1454 1454 4: null+4
1455 1455 3: null+3
1456 1456 2: null+3
1457 1457 1: null+2
1458 1458 0: null+1
1459 1459
1460 1460 One common tag: longuest path wins:
1461 1461
1462 1462 $ hg tag -r 1 -m t1 -d '6 0' t1
1463 1463 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1464 1464 6: t1+4
1465 1465 5: t1+3
1466 1466 4: t1+2
1467 1467 3: t1+1
1468 1468 2: t1+1
1469 1469 1: t1+0
1470 1470 0: null+1
1471 1471
1472 1472 One ancestor tag: more recent wins:
1473 1473
1474 1474 $ hg tag -r 2 -m t2 -d '7 0' t2
1475 1475 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1476 1476 7: t2+3
1477 1477 6: t2+2
1478 1478 5: t2+1
1479 1479 4: t1+2
1480 1480 3: t1+1
1481 1481 2: t2+0
1482 1482 1: t1+0
1483 1483 0: null+1
1484 1484
1485 1485 Two branch tags: more recent wins:
1486 1486
1487 1487 $ hg tag -r 3 -m t3 -d '8 0' t3
1488 1488 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1489 1489 8: t3+5
1490 1490 7: t3+4
1491 1491 6: t3+3
1492 1492 5: t3+2
1493 1493 4: t3+1
1494 1494 3: t3+0
1495 1495 2: t2+0
1496 1496 1: t1+0
1497 1497 0: null+1
1498 1498
1499 1499 Merged tag overrides:
1500 1500
1501 1501 $ hg tag -r 5 -m t5 -d '9 0' t5
1502 1502 $ hg tag -r 3 -m at3 -d '10 0' at3
1503 1503 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1504 1504 10: t5+5
1505 1505 9: t5+4
1506 1506 8: t5+3
1507 1507 7: t5+2
1508 1508 6: t5+1
1509 1509 5: t5+0
1510 1510 4: at3:t3+1
1511 1511 3: at3:t3+0
1512 1512 2: t2+0
1513 1513 1: t1+0
1514 1514 0: null+1
1515 1515
1516 1516 $ cd ..
1517 1517
1518 1518
1519 1519 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1520 1520 if it is a relative path
1521 1521
1522 1522 $ mkdir -p home/styles
1523 1523
1524 1524 $ cat > home/styles/teststyle <<EOF
1525 1525 > changeset = 'test {rev}:{node|short}\n'
1526 1526 > EOF
1527 1527
1528 1528 $ HOME=`pwd`/home; export HOME
1529 1529
1530 1530 $ cat > latesttag/.hg/hgrc <<EOF
1531 1531 > [ui]
1532 1532 > style = ~/styles/teststyle
1533 1533 > EOF
1534 1534
1535 1535 $ hg -R latesttag tip
1536 1536 test 10:dee8f28249af
1537 1537
1538 1538 Test recursive showlist template (issue1989):
1539 1539
1540 1540 $ cat > style1989 <<EOF
1541 1541 > changeset = '{file_mods}{manifest}{extras}'
1542 1542 > file_mod = 'M|{author|person}\n'
1543 1543 > manifest = '{rev},{author}\n'
1544 1544 > extra = '{key}: {author}\n'
1545 1545 > EOF
1546 1546
1547 1547 $ hg -R latesttag log -r tip --style=style1989
1548 1548 M|test
1549 1549 10,test
1550 1550 branch: test
1551 1551
1552 1552 Test new-style inline templating:
1553 1553
1554 1554 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
1555 1555 modified files: .hgtags
1556 1556
1557 1557 Test the sub function of templating for expansion:
1558 1558
1559 1559 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
1560 1560 xx
1561 1561
1562 1562 Test the strip function with chars specified:
1563 1563
1564 1564 $ hg log -R latesttag --template '{desc}\n'
1565 1565 at3
1566 1566 t5
1567 1567 t3
1568 1568 t2
1569 1569 t1
1570 1570 merge
1571 1571 h2e
1572 1572 h2d
1573 1573 h1c
1574 1574 b
1575 1575 a
1576 1576
1577 1577 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
1578 1578 at3
1579 1579 5
1580 1580 3
1581 1581 2
1582 1582 1
1583 1583 merg
1584 1584 h2
1585 1585 h2d
1586 1586 h1c
1587 1587 b
1588 1588 a
1589 1589
1590 1590 Test string escaping:
1591 1591
1592 1592 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
1593 1593 >
1594 1594 <>\n<[>
1595 1595 <>\n<]>
1596 1596 <>\n<
1597
1598 Test recursive evaluation:
1599
1600 $ hg init r
1601 $ cd r
1602 $ echo a > a
1603 $ hg ci -Am '{rev}'
1604 adding a
1605 $ hg log -r 0 --template '{if(rev, desc)}\n'
1606 {rev}
1607 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
1608 test 0
General Comments 0
You need to be logged in to leave comments. Login now