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