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