##// END OF EJS Templates
templater: look up symbols/resources as if they were separated (issue5699)...
Yuya Nishihara -
r35486:a33be093 default
parent child Browse files
Show More
@@ -1,540 +1,542 b''
1 # formatter.py - generic output formatting for mercurial
1 # formatter.py - generic output formatting for mercurial
2 #
2 #
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
3 # Copyright 2012 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 """Generic output formatting for Mercurial
8 """Generic output formatting for Mercurial
9
9
10 The formatter provides API to show data in various ways. The following
10 The formatter provides API to show data in various ways. The following
11 functions should be used in place of ui.write():
11 functions should be used in place of ui.write():
12
12
13 - fm.write() for unconditional output
13 - fm.write() for unconditional output
14 - fm.condwrite() to show some extra data conditionally in plain output
14 - fm.condwrite() to show some extra data conditionally in plain output
15 - fm.context() to provide changectx to template output
15 - fm.context() to provide changectx to template output
16 - fm.data() to provide extra data to JSON or template output
16 - fm.data() to provide extra data to JSON or template output
17 - fm.plain() to show raw text that isn't provided to JSON or template output
17 - fm.plain() to show raw text that isn't provided to JSON or template output
18
18
19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
20 beforehand so the data is converted to the appropriate data type. Use
20 beforehand so the data is converted to the appropriate data type. Use
21 fm.isplain() if you need to convert or format data conditionally which isn't
21 fm.isplain() if you need to convert or format data conditionally which isn't
22 supported by the formatter API.
22 supported by the formatter API.
23
23
24 To build nested structure (i.e. a list of dicts), use fm.nested().
24 To build nested structure (i.e. a list of dicts), use fm.nested().
25
25
26 See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
26 See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
27
27
28 fm.condwrite() vs 'if cond:':
28 fm.condwrite() vs 'if cond:':
29
29
30 In most cases, use fm.condwrite() so users can selectively show the data
30 In most cases, use fm.condwrite() so users can selectively show the data
31 in template output. If it's costly to build data, use plain 'if cond:' with
31 in template output. If it's costly to build data, use plain 'if cond:' with
32 fm.write().
32 fm.write().
33
33
34 fm.nested() vs fm.formatdict() (or fm.formatlist()):
34 fm.nested() vs fm.formatdict() (or fm.formatlist()):
35
35
36 fm.nested() should be used to form a tree structure (a list of dicts of
36 fm.nested() should be used to form a tree structure (a list of dicts of
37 lists of dicts...) which can be accessed through template keywords, e.g.
37 lists of dicts...) which can be accessed through template keywords, e.g.
38 "{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
38 "{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
39 exports a dict-type object to template, which can be accessed by e.g.
39 exports a dict-type object to template, which can be accessed by e.g.
40 "{get(foo, key)}" function.
40 "{get(foo, key)}" function.
41
41
42 Doctest helper:
42 Doctest helper:
43
43
44 >>> def show(fn, verbose=False, **opts):
44 >>> def show(fn, verbose=False, **opts):
45 ... import sys
45 ... import sys
46 ... from . import ui as uimod
46 ... from . import ui as uimod
47 ... ui = uimod.ui()
47 ... ui = uimod.ui()
48 ... ui.verbose = verbose
48 ... ui.verbose = verbose
49 ... ui.pushbuffer()
49 ... ui.pushbuffer()
50 ... try:
50 ... try:
51 ... return fn(ui, ui.formatter(pycompat.sysbytes(fn.__name__),
51 ... return fn(ui, ui.formatter(pycompat.sysbytes(fn.__name__),
52 ... pycompat.byteskwargs(opts)))
52 ... pycompat.byteskwargs(opts)))
53 ... finally:
53 ... finally:
54 ... print(pycompat.sysstr(ui.popbuffer()), end='')
54 ... print(pycompat.sysstr(ui.popbuffer()), end='')
55
55
56 Basic example:
56 Basic example:
57
57
58 >>> def files(ui, fm):
58 >>> def files(ui, fm):
59 ... files = [(b'foo', 123, (0, 0)), (b'bar', 456, (1, 0))]
59 ... files = [(b'foo', 123, (0, 0)), (b'bar', 456, (1, 0))]
60 ... for f in files:
60 ... for f in files:
61 ... fm.startitem()
61 ... fm.startitem()
62 ... fm.write(b'path', b'%s', f[0])
62 ... fm.write(b'path', b'%s', f[0])
63 ... fm.condwrite(ui.verbose, b'date', b' %s',
63 ... fm.condwrite(ui.verbose, b'date', b' %s',
64 ... fm.formatdate(f[2], b'%Y-%m-%d %H:%M:%S'))
64 ... fm.formatdate(f[2], b'%Y-%m-%d %H:%M:%S'))
65 ... fm.data(size=f[1])
65 ... fm.data(size=f[1])
66 ... fm.plain(b'\\n')
66 ... fm.plain(b'\\n')
67 ... fm.end()
67 ... fm.end()
68 >>> show(files)
68 >>> show(files)
69 foo
69 foo
70 bar
70 bar
71 >>> show(files, verbose=True)
71 >>> show(files, verbose=True)
72 foo 1970-01-01 00:00:00
72 foo 1970-01-01 00:00:00
73 bar 1970-01-01 00:00:01
73 bar 1970-01-01 00:00:01
74 >>> show(files, template=b'json')
74 >>> show(files, template=b'json')
75 [
75 [
76 {
76 {
77 "date": [0, 0],
77 "date": [0, 0],
78 "path": "foo",
78 "path": "foo",
79 "size": 123
79 "size": 123
80 },
80 },
81 {
81 {
82 "date": [1, 0],
82 "date": [1, 0],
83 "path": "bar",
83 "path": "bar",
84 "size": 456
84 "size": 456
85 }
85 }
86 ]
86 ]
87 >>> show(files, template=b'path: {path}\\ndate: {date|rfc3339date}\\n')
87 >>> show(files, template=b'path: {path}\\ndate: {date|rfc3339date}\\n')
88 path: foo
88 path: foo
89 date: 1970-01-01T00:00:00+00:00
89 date: 1970-01-01T00:00:00+00:00
90 path: bar
90 path: bar
91 date: 1970-01-01T00:00:01+00:00
91 date: 1970-01-01T00:00:01+00:00
92
92
93 Nested example:
93 Nested example:
94
94
95 >>> def subrepos(ui, fm):
95 >>> def subrepos(ui, fm):
96 ... fm.startitem()
96 ... fm.startitem()
97 ... fm.write(b'repo', b'[%s]\\n', b'baz')
97 ... fm.write(b'reponame', b'[%s]\\n', b'baz')
98 ... files(ui, fm.nested(b'files'))
98 ... files(ui, fm.nested(b'files'))
99 ... fm.end()
99 ... fm.end()
100 >>> show(subrepos)
100 >>> show(subrepos)
101 [baz]
101 [baz]
102 foo
102 foo
103 bar
103 bar
104 >>> show(subrepos, template=b'{repo}: {join(files % "{path}", ", ")}\\n')
104 >>> show(subrepos, template=b'{reponame}: {join(files % "{path}", ", ")}\\n')
105 baz: foo, bar
105 baz: foo, bar
106 """
106 """
107
107
108 from __future__ import absolute_import, print_function
108 from __future__ import absolute_import, print_function
109
109
110 import collections
110 import collections
111 import contextlib
111 import contextlib
112 import itertools
112 import itertools
113 import os
113 import os
114
114
115 from .i18n import _
115 from .i18n import _
116 from .node import (
116 from .node import (
117 hex,
117 hex,
118 short,
118 short,
119 )
119 )
120
120
121 from . import (
121 from . import (
122 error,
122 error,
123 pycompat,
123 pycompat,
124 templatefilters,
124 templatefilters,
125 templatekw,
125 templatekw,
126 templater,
126 templater,
127 util,
127 util,
128 )
128 )
129
129
130 pickle = util.pickle
130 pickle = util.pickle
131
131
132 class _nullconverter(object):
132 class _nullconverter(object):
133 '''convert non-primitive data types to be processed by formatter'''
133 '''convert non-primitive data types to be processed by formatter'''
134
134
135 # set to True if context object should be stored as item
135 # set to True if context object should be stored as item
136 storecontext = False
136 storecontext = False
137
137
138 @staticmethod
138 @staticmethod
139 def formatdate(date, fmt):
139 def formatdate(date, fmt):
140 '''convert date tuple to appropriate format'''
140 '''convert date tuple to appropriate format'''
141 return date
141 return date
142 @staticmethod
142 @staticmethod
143 def formatdict(data, key, value, fmt, sep):
143 def formatdict(data, key, value, fmt, sep):
144 '''convert dict or key-value pairs to appropriate dict format'''
144 '''convert dict or key-value pairs to appropriate dict format'''
145 # use plain dict instead of util.sortdict so that data can be
145 # use plain dict instead of util.sortdict so that data can be
146 # serialized as a builtin dict in pickle output
146 # serialized as a builtin dict in pickle output
147 return dict(data)
147 return dict(data)
148 @staticmethod
148 @staticmethod
149 def formatlist(data, name, fmt, sep):
149 def formatlist(data, name, fmt, sep):
150 '''convert iterable to appropriate list format'''
150 '''convert iterable to appropriate list format'''
151 return list(data)
151 return list(data)
152
152
153 class baseformatter(object):
153 class baseformatter(object):
154 def __init__(self, ui, topic, opts, converter):
154 def __init__(self, ui, topic, opts, converter):
155 self._ui = ui
155 self._ui = ui
156 self._topic = topic
156 self._topic = topic
157 self._style = opts.get("style")
157 self._style = opts.get("style")
158 self._template = opts.get("template")
158 self._template = opts.get("template")
159 self._converter = converter
159 self._converter = converter
160 self._item = None
160 self._item = None
161 # function to convert node to string suitable for this output
161 # function to convert node to string suitable for this output
162 self.hexfunc = hex
162 self.hexfunc = hex
163 def __enter__(self):
163 def __enter__(self):
164 return self
164 return self
165 def __exit__(self, exctype, excvalue, traceback):
165 def __exit__(self, exctype, excvalue, traceback):
166 if exctype is None:
166 if exctype is None:
167 self.end()
167 self.end()
168 def _showitem(self):
168 def _showitem(self):
169 '''show a formatted item once all data is collected'''
169 '''show a formatted item once all data is collected'''
170 def startitem(self):
170 def startitem(self):
171 '''begin an item in the format list'''
171 '''begin an item in the format list'''
172 if self._item is not None:
172 if self._item is not None:
173 self._showitem()
173 self._showitem()
174 self._item = {}
174 self._item = {}
175 def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
175 def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
176 '''convert date tuple to appropriate format'''
176 '''convert date tuple to appropriate format'''
177 return self._converter.formatdate(date, fmt)
177 return self._converter.formatdate(date, fmt)
178 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
178 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
179 '''convert dict or key-value pairs to appropriate dict format'''
179 '''convert dict or key-value pairs to appropriate dict format'''
180 return self._converter.formatdict(data, key, value, fmt, sep)
180 return self._converter.formatdict(data, key, value, fmt, sep)
181 def formatlist(self, data, name, fmt='%s', sep=' '):
181 def formatlist(self, data, name, fmt='%s', sep=' '):
182 '''convert iterable to appropriate list format'''
182 '''convert iterable to appropriate list format'''
183 # name is mandatory argument for now, but it could be optional if
183 # name is mandatory argument for now, but it could be optional if
184 # we have default template keyword, e.g. {item}
184 # we have default template keyword, e.g. {item}
185 return self._converter.formatlist(data, name, fmt, sep)
185 return self._converter.formatlist(data, name, fmt, sep)
186 def context(self, **ctxs):
186 def context(self, **ctxs):
187 '''insert context objects to be used to render template keywords'''
187 '''insert context objects to be used to render template keywords'''
188 ctxs = pycompat.byteskwargs(ctxs)
188 ctxs = pycompat.byteskwargs(ctxs)
189 assert all(k == 'ctx' for k in ctxs)
189 assert all(k == 'ctx' for k in ctxs)
190 if self._converter.storecontext:
190 if self._converter.storecontext:
191 self._item.update(ctxs)
191 self._item.update(ctxs)
192 def data(self, **data):
192 def data(self, **data):
193 '''insert data into item that's not shown in default output'''
193 '''insert data into item that's not shown in default output'''
194 data = pycompat.byteskwargs(data)
194 data = pycompat.byteskwargs(data)
195 self._item.update(data)
195 self._item.update(data)
196 def write(self, fields, deftext, *fielddata, **opts):
196 def write(self, fields, deftext, *fielddata, **opts):
197 '''do default text output while assigning data to item'''
197 '''do default text output while assigning data to item'''
198 fieldkeys = fields.split()
198 fieldkeys = fields.split()
199 assert len(fieldkeys) == len(fielddata)
199 assert len(fieldkeys) == len(fielddata)
200 self._item.update(zip(fieldkeys, fielddata))
200 self._item.update(zip(fieldkeys, fielddata))
201 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
201 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
202 '''do conditional write (primarily for plain formatter)'''
202 '''do conditional write (primarily for plain formatter)'''
203 fieldkeys = fields.split()
203 fieldkeys = fields.split()
204 assert len(fieldkeys) == len(fielddata)
204 assert len(fieldkeys) == len(fielddata)
205 self._item.update(zip(fieldkeys, fielddata))
205 self._item.update(zip(fieldkeys, fielddata))
206 def plain(self, text, **opts):
206 def plain(self, text, **opts):
207 '''show raw text for non-templated mode'''
207 '''show raw text for non-templated mode'''
208 def isplain(self):
208 def isplain(self):
209 '''check for plain formatter usage'''
209 '''check for plain formatter usage'''
210 return False
210 return False
211 def nested(self, field):
211 def nested(self, field):
212 '''sub formatter to store nested data in the specified field'''
212 '''sub formatter to store nested data in the specified field'''
213 self._item[field] = data = []
213 self._item[field] = data = []
214 return _nestedformatter(self._ui, self._converter, data)
214 return _nestedformatter(self._ui, self._converter, data)
215 def end(self):
215 def end(self):
216 '''end output for the formatter'''
216 '''end output for the formatter'''
217 if self._item is not None:
217 if self._item is not None:
218 self._showitem()
218 self._showitem()
219
219
220 def nullformatter(ui, topic):
220 def nullformatter(ui, topic):
221 '''formatter that prints nothing'''
221 '''formatter that prints nothing'''
222 return baseformatter(ui, topic, opts={}, converter=_nullconverter)
222 return baseformatter(ui, topic, opts={}, converter=_nullconverter)
223
223
224 class _nestedformatter(baseformatter):
224 class _nestedformatter(baseformatter):
225 '''build sub items and store them in the parent formatter'''
225 '''build sub items and store them in the parent formatter'''
226 def __init__(self, ui, converter, data):
226 def __init__(self, ui, converter, data):
227 baseformatter.__init__(self, ui, topic='', opts={}, converter=converter)
227 baseformatter.__init__(self, ui, topic='', opts={}, converter=converter)
228 self._data = data
228 self._data = data
229 def _showitem(self):
229 def _showitem(self):
230 self._data.append(self._item)
230 self._data.append(self._item)
231
231
232 def _iteritems(data):
232 def _iteritems(data):
233 '''iterate key-value pairs in stable order'''
233 '''iterate key-value pairs in stable order'''
234 if isinstance(data, dict):
234 if isinstance(data, dict):
235 return sorted(data.iteritems())
235 return sorted(data.iteritems())
236 return data
236 return data
237
237
238 class _plainconverter(object):
238 class _plainconverter(object):
239 '''convert non-primitive data types to text'''
239 '''convert non-primitive data types to text'''
240
240
241 storecontext = False
241 storecontext = False
242
242
243 @staticmethod
243 @staticmethod
244 def formatdate(date, fmt):
244 def formatdate(date, fmt):
245 '''stringify date tuple in the given format'''
245 '''stringify date tuple in the given format'''
246 return util.datestr(date, fmt)
246 return util.datestr(date, fmt)
247 @staticmethod
247 @staticmethod
248 def formatdict(data, key, value, fmt, sep):
248 def formatdict(data, key, value, fmt, sep):
249 '''stringify key-value pairs separated by sep'''
249 '''stringify key-value pairs separated by sep'''
250 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
250 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
251 @staticmethod
251 @staticmethod
252 def formatlist(data, name, fmt, sep):
252 def formatlist(data, name, fmt, sep):
253 '''stringify iterable separated by sep'''
253 '''stringify iterable separated by sep'''
254 return sep.join(fmt % e for e in data)
254 return sep.join(fmt % e for e in data)
255
255
256 class plainformatter(baseformatter):
256 class plainformatter(baseformatter):
257 '''the default text output scheme'''
257 '''the default text output scheme'''
258 def __init__(self, ui, out, topic, opts):
258 def __init__(self, ui, out, topic, opts):
259 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
259 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
260 if ui.debugflag:
260 if ui.debugflag:
261 self.hexfunc = hex
261 self.hexfunc = hex
262 else:
262 else:
263 self.hexfunc = short
263 self.hexfunc = short
264 if ui is out:
264 if ui is out:
265 self._write = ui.write
265 self._write = ui.write
266 else:
266 else:
267 self._write = lambda s, **opts: out.write(s)
267 self._write = lambda s, **opts: out.write(s)
268 def startitem(self):
268 def startitem(self):
269 pass
269 pass
270 def data(self, **data):
270 def data(self, **data):
271 pass
271 pass
272 def write(self, fields, deftext, *fielddata, **opts):
272 def write(self, fields, deftext, *fielddata, **opts):
273 self._write(deftext % fielddata, **opts)
273 self._write(deftext % fielddata, **opts)
274 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
274 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
275 '''do conditional write'''
275 '''do conditional write'''
276 if cond:
276 if cond:
277 self._write(deftext % fielddata, **opts)
277 self._write(deftext % fielddata, **opts)
278 def plain(self, text, **opts):
278 def plain(self, text, **opts):
279 self._write(text, **opts)
279 self._write(text, **opts)
280 def isplain(self):
280 def isplain(self):
281 return True
281 return True
282 def nested(self, field):
282 def nested(self, field):
283 # nested data will be directly written to ui
283 # nested data will be directly written to ui
284 return self
284 return self
285 def end(self):
285 def end(self):
286 pass
286 pass
287
287
288 class debugformatter(baseformatter):
288 class debugformatter(baseformatter):
289 def __init__(self, ui, out, topic, opts):
289 def __init__(self, ui, out, topic, opts):
290 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
290 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
291 self._out = out
291 self._out = out
292 self._out.write("%s = [\n" % self._topic)
292 self._out.write("%s = [\n" % self._topic)
293 def _showitem(self):
293 def _showitem(self):
294 self._out.write(" " + repr(self._item) + ",\n")
294 self._out.write(" " + repr(self._item) + ",\n")
295 def end(self):
295 def end(self):
296 baseformatter.end(self)
296 baseformatter.end(self)
297 self._out.write("]\n")
297 self._out.write("]\n")
298
298
299 class pickleformatter(baseformatter):
299 class pickleformatter(baseformatter):
300 def __init__(self, ui, out, topic, opts):
300 def __init__(self, ui, out, topic, opts):
301 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
301 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
302 self._out = out
302 self._out = out
303 self._data = []
303 self._data = []
304 def _showitem(self):
304 def _showitem(self):
305 self._data.append(self._item)
305 self._data.append(self._item)
306 def end(self):
306 def end(self):
307 baseformatter.end(self)
307 baseformatter.end(self)
308 self._out.write(pickle.dumps(self._data))
308 self._out.write(pickle.dumps(self._data))
309
309
310 class jsonformatter(baseformatter):
310 class jsonformatter(baseformatter):
311 def __init__(self, ui, out, topic, opts):
311 def __init__(self, ui, out, topic, opts):
312 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
312 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
313 self._out = out
313 self._out = out
314 self._out.write("[")
314 self._out.write("[")
315 self._first = True
315 self._first = True
316 def _showitem(self):
316 def _showitem(self):
317 if self._first:
317 if self._first:
318 self._first = False
318 self._first = False
319 else:
319 else:
320 self._out.write(",")
320 self._out.write(",")
321
321
322 self._out.write("\n {\n")
322 self._out.write("\n {\n")
323 first = True
323 first = True
324 for k, v in sorted(self._item.items()):
324 for k, v in sorted(self._item.items()):
325 if first:
325 if first:
326 first = False
326 first = False
327 else:
327 else:
328 self._out.write(",\n")
328 self._out.write(",\n")
329 u = templatefilters.json(v, paranoid=False)
329 u = templatefilters.json(v, paranoid=False)
330 self._out.write(' "%s": %s' % (k, u))
330 self._out.write(' "%s": %s' % (k, u))
331 self._out.write("\n }")
331 self._out.write("\n }")
332 def end(self):
332 def end(self):
333 baseformatter.end(self)
333 baseformatter.end(self)
334 self._out.write("\n]\n")
334 self._out.write("\n]\n")
335
335
336 class _templateconverter(object):
336 class _templateconverter(object):
337 '''convert non-primitive data types to be processed by templater'''
337 '''convert non-primitive data types to be processed by templater'''
338
338
339 storecontext = True
339 storecontext = True
340
340
341 @staticmethod
341 @staticmethod
342 def formatdate(date, fmt):
342 def formatdate(date, fmt):
343 '''return date tuple'''
343 '''return date tuple'''
344 return date
344 return date
345 @staticmethod
345 @staticmethod
346 def formatdict(data, key, value, fmt, sep):
346 def formatdict(data, key, value, fmt, sep):
347 '''build object that can be evaluated as either plain string or dict'''
347 '''build object that can be evaluated as either plain string or dict'''
348 data = util.sortdict(_iteritems(data))
348 data = util.sortdict(_iteritems(data))
349 def f():
349 def f():
350 yield _plainconverter.formatdict(data, key, value, fmt, sep)
350 yield _plainconverter.formatdict(data, key, value, fmt, sep)
351 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
351 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
352 @staticmethod
352 @staticmethod
353 def formatlist(data, name, fmt, sep):
353 def formatlist(data, name, fmt, sep):
354 '''build object that can be evaluated as either plain string or list'''
354 '''build object that can be evaluated as either plain string or list'''
355 data = list(data)
355 data = list(data)
356 def f():
356 def f():
357 yield _plainconverter.formatlist(data, name, fmt, sep)
357 yield _plainconverter.formatlist(data, name, fmt, sep)
358 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f)
358 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f)
359
359
360 class templateformatter(baseformatter):
360 class templateformatter(baseformatter):
361 def __init__(self, ui, out, topic, opts):
361 def __init__(self, ui, out, topic, opts):
362 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
362 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
363 self._out = out
363 self._out = out
364 spec = lookuptemplate(ui, topic, opts.get('template', ''))
364 spec = lookuptemplate(ui, topic, opts.get('template', ''))
365 self._tref = spec.ref
365 self._tref = spec.ref
366 self._t = loadtemplater(ui, spec, resources=templateresources(ui),
366 self._t = loadtemplater(ui, spec, resources=templateresources(ui),
367 cache=templatekw.defaulttempl)
367 cache=templatekw.defaulttempl)
368 self._parts = templatepartsmap(spec, self._t,
368 self._parts = templatepartsmap(spec, self._t,
369 ['docheader', 'docfooter', 'separator'])
369 ['docheader', 'docfooter', 'separator'])
370 self._counter = itertools.count()
370 self._counter = itertools.count()
371 self._renderitem('docheader', {})
371 self._renderitem('docheader', {})
372
372
373 def _showitem(self):
373 def _showitem(self):
374 item = self._item.copy()
374 item = self._item.copy()
375 item['index'] = index = next(self._counter)
375 item['index'] = index = next(self._counter)
376 if index > 0:
376 if index > 0:
377 self._renderitem('separator', {})
377 self._renderitem('separator', {})
378 self._renderitem(self._tref, item)
378 self._renderitem(self._tref, item)
379
379
380 def _renderitem(self, part, item):
380 def _renderitem(self, part, item):
381 if part not in self._parts:
381 if part not in self._parts:
382 return
382 return
383 ref = self._parts[part]
383 ref = self._parts[part]
384
384
385 # TODO: add support for filectx. probably each template keyword or
385 # TODO: add support for filectx. probably each template keyword or
386 # function will have to declare dependent resources. e.g.
386 # function will have to declare dependent resources. e.g.
387 # @templatekeyword(..., requires=('ctx',))
387 # @templatekeyword(..., requires=('ctx',))
388 props = {}
388 props = {}
389 if 'ctx' in item:
389 if 'ctx' in item:
390 props.update(templatekw.keywords)
390 props.update(templatekw.keywords)
391 # explicitly-defined fields precede templatekw
391 # explicitly-defined fields precede templatekw
392 props.update(item)
392 props.update(item)
393 if 'ctx' in item:
393 if 'ctx' in item:
394 # but template resources must be always available
394 # but template resources must be always available
395 props['repo'] = props['ctx'].repo()
395 props['repo'] = props['ctx'].repo()
396 props['revcache'] = {}
396 props['revcache'] = {}
397 props = pycompat.strkwargs(props)
397 props = pycompat.strkwargs(props)
398 g = self._t(ref, **props)
398 g = self._t(ref, **props)
399 self._out.write(templater.stringify(g))
399 self._out.write(templater.stringify(g))
400
400
401 def end(self):
401 def end(self):
402 baseformatter.end(self)
402 baseformatter.end(self)
403 self._renderitem('docfooter', {})
403 self._renderitem('docfooter', {})
404
404
405 templatespec = collections.namedtuple(r'templatespec',
405 templatespec = collections.namedtuple(r'templatespec',
406 r'ref tmpl mapfile')
406 r'ref tmpl mapfile')
407
407
408 def lookuptemplate(ui, topic, tmpl):
408 def lookuptemplate(ui, topic, tmpl):
409 """Find the template matching the given -T/--template spec 'tmpl'
409 """Find the template matching the given -T/--template spec 'tmpl'
410
410
411 'tmpl' can be any of the following:
411 'tmpl' can be any of the following:
412
412
413 - a literal template (e.g. '{rev}')
413 - a literal template (e.g. '{rev}')
414 - a map-file name or path (e.g. 'changelog')
414 - a map-file name or path (e.g. 'changelog')
415 - a reference to [templates] in config file
415 - a reference to [templates] in config file
416 - a path to raw template file
416 - a path to raw template file
417
417
418 A map file defines a stand-alone template environment. If a map file
418 A map file defines a stand-alone template environment. If a map file
419 selected, all templates defined in the file will be loaded, and the
419 selected, all templates defined in the file will be loaded, and the
420 template matching the given topic will be rendered. Aliases won't be
420 template matching the given topic will be rendered. Aliases won't be
421 loaded from user config, but from the map file.
421 loaded from user config, but from the map file.
422
422
423 If no map file selected, all templates in [templates] section will be
423 If no map file selected, all templates in [templates] section will be
424 available as well as aliases in [templatealias].
424 available as well as aliases in [templatealias].
425 """
425 """
426
426
427 # looks like a literal template?
427 # looks like a literal template?
428 if '{' in tmpl:
428 if '{' in tmpl:
429 return templatespec('', tmpl, None)
429 return templatespec('', tmpl, None)
430
430
431 # perhaps a stock style?
431 # perhaps a stock style?
432 if not os.path.split(tmpl)[0]:
432 if not os.path.split(tmpl)[0]:
433 mapname = (templater.templatepath('map-cmdline.' + tmpl)
433 mapname = (templater.templatepath('map-cmdline.' + tmpl)
434 or templater.templatepath(tmpl))
434 or templater.templatepath(tmpl))
435 if mapname and os.path.isfile(mapname):
435 if mapname and os.path.isfile(mapname):
436 return templatespec(topic, None, mapname)
436 return templatespec(topic, None, mapname)
437
437
438 # perhaps it's a reference to [templates]
438 # perhaps it's a reference to [templates]
439 if ui.config('templates', tmpl):
439 if ui.config('templates', tmpl):
440 return templatespec(tmpl, None, None)
440 return templatespec(tmpl, None, None)
441
441
442 if tmpl == 'list':
442 if tmpl == 'list':
443 ui.write(_("available styles: %s\n") % templater.stylelist())
443 ui.write(_("available styles: %s\n") % templater.stylelist())
444 raise error.Abort(_("specify a template"))
444 raise error.Abort(_("specify a template"))
445
445
446 # perhaps it's a path to a map or a template
446 # perhaps it's a path to a map or a template
447 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
447 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
448 # is it a mapfile for a style?
448 # is it a mapfile for a style?
449 if os.path.basename(tmpl).startswith("map-"):
449 if os.path.basename(tmpl).startswith("map-"):
450 return templatespec(topic, None, os.path.realpath(tmpl))
450 return templatespec(topic, None, os.path.realpath(tmpl))
451 with util.posixfile(tmpl, 'rb') as f:
451 with util.posixfile(tmpl, 'rb') as f:
452 tmpl = f.read()
452 tmpl = f.read()
453 return templatespec('', tmpl, None)
453 return templatespec('', tmpl, None)
454
454
455 # constant string?
455 # constant string?
456 return templatespec('', tmpl, None)
456 return templatespec('', tmpl, None)
457
457
458 def templatepartsmap(spec, t, partnames):
458 def templatepartsmap(spec, t, partnames):
459 """Create a mapping of {part: ref}"""
459 """Create a mapping of {part: ref}"""
460 partsmap = {spec.ref: spec.ref} # initial ref must exist in t
460 partsmap = {spec.ref: spec.ref} # initial ref must exist in t
461 if spec.mapfile:
461 if spec.mapfile:
462 partsmap.update((p, p) for p in partnames if p in t)
462 partsmap.update((p, p) for p in partnames if p in t)
463 elif spec.ref:
463 elif spec.ref:
464 for part in partnames:
464 for part in partnames:
465 ref = '%s:%s' % (spec.ref, part) # select config sub-section
465 ref = '%s:%s' % (spec.ref, part) # select config sub-section
466 if ref in t:
466 if ref in t:
467 partsmap[part] = ref
467 partsmap[part] = ref
468 return partsmap
468 return partsmap
469
469
470 def loadtemplater(ui, spec, resources=None, cache=None):
470 def loadtemplater(ui, spec, resources=None, cache=None):
471 """Create a templater from either a literal template or loading from
471 """Create a templater from either a literal template or loading from
472 a map file"""
472 a map file"""
473 assert not (spec.tmpl and spec.mapfile)
473 assert not (spec.tmpl and spec.mapfile)
474 if spec.mapfile:
474 if spec.mapfile:
475 frommapfile = templater.templater.frommapfile
475 frommapfile = templater.templater.frommapfile
476 return frommapfile(spec.mapfile, resources=resources, cache=cache)
476 return frommapfile(spec.mapfile, resources=resources, cache=cache)
477 return maketemplater(ui, spec.tmpl, resources=resources, cache=cache)
477 return maketemplater(ui, spec.tmpl, resources=resources, cache=cache)
478
478
479 def maketemplater(ui, tmpl, resources=None, cache=None):
479 def maketemplater(ui, tmpl, resources=None, cache=None):
480 """Create a templater from a string template 'tmpl'"""
480 """Create a templater from a string template 'tmpl'"""
481 aliases = ui.configitems('templatealias')
481 aliases = ui.configitems('templatealias')
482 t = templater.templater(resources=resources, cache=cache, aliases=aliases)
482 t = templater.templater(resources=resources, cache=cache, aliases=aliases)
483 t.cache.update((k, templater.unquotestring(v))
483 t.cache.update((k, templater.unquotestring(v))
484 for k, v in ui.configitems('templates'))
484 for k, v in ui.configitems('templates'))
485 if tmpl:
485 if tmpl:
486 t.cache[''] = tmpl
486 t.cache[''] = tmpl
487 return t
487 return t
488
488
489 def templateresources(ui, repo=None):
489 def templateresources(ui, repo=None):
490 """Create a dict of template resources designed for the default templatekw
490 """Create a dict of template resources designed for the default templatekw
491 and function"""
491 and function"""
492 return {
492 return {
493 'cache': {}, # for templatekw/funcs to store reusable data
493 'cache': {}, # for templatekw/funcs to store reusable data
494 'ctx': None,
494 'repo': repo,
495 'repo': repo,
496 'revcache': None, # per-ctx cache; set later
495 'ui': ui,
497 'ui': ui,
496 }
498 }
497
499
498 def formatter(ui, out, topic, opts):
500 def formatter(ui, out, topic, opts):
499 template = opts.get("template", "")
501 template = opts.get("template", "")
500 if template == "json":
502 if template == "json":
501 return jsonformatter(ui, out, topic, opts)
503 return jsonformatter(ui, out, topic, opts)
502 elif template == "pickle":
504 elif template == "pickle":
503 return pickleformatter(ui, out, topic, opts)
505 return pickleformatter(ui, out, topic, opts)
504 elif template == "debug":
506 elif template == "debug":
505 return debugformatter(ui, out, topic, opts)
507 return debugformatter(ui, out, topic, opts)
506 elif template != "":
508 elif template != "":
507 return templateformatter(ui, out, topic, opts)
509 return templateformatter(ui, out, topic, opts)
508 # developer config: ui.formatdebug
510 # developer config: ui.formatdebug
509 elif ui.configbool('ui', 'formatdebug'):
511 elif ui.configbool('ui', 'formatdebug'):
510 return debugformatter(ui, out, topic, opts)
512 return debugformatter(ui, out, topic, opts)
511 # deprecated config: ui.formatjson
513 # deprecated config: ui.formatjson
512 elif ui.configbool('ui', 'formatjson'):
514 elif ui.configbool('ui', 'formatjson'):
513 return jsonformatter(ui, out, topic, opts)
515 return jsonformatter(ui, out, topic, opts)
514 return plainformatter(ui, out, topic, opts)
516 return plainformatter(ui, out, topic, opts)
515
517
516 @contextlib.contextmanager
518 @contextlib.contextmanager
517 def openformatter(ui, filename, topic, opts):
519 def openformatter(ui, filename, topic, opts):
518 """Create a formatter that writes outputs to the specified file
520 """Create a formatter that writes outputs to the specified file
519
521
520 Must be invoked using the 'with' statement.
522 Must be invoked using the 'with' statement.
521 """
523 """
522 with util.posixfile(filename, 'wb') as out:
524 with util.posixfile(filename, 'wb') as out:
523 with formatter(ui, out, topic, opts) as fm:
525 with formatter(ui, out, topic, opts) as fm:
524 yield fm
526 yield fm
525
527
526 @contextlib.contextmanager
528 @contextlib.contextmanager
527 def _neverending(fm):
529 def _neverending(fm):
528 yield fm
530 yield fm
529
531
530 def maybereopen(fm, filename, opts):
532 def maybereopen(fm, filename, opts):
531 """Create a formatter backed by file if filename specified, else return
533 """Create a formatter backed by file if filename specified, else return
532 the given formatter
534 the given formatter
533
535
534 Must be invoked using the 'with' statement. This will never call fm.end()
536 Must be invoked using the 'with' statement. This will never call fm.end()
535 of the given formatter.
537 of the given formatter.
536 """
538 """
537 if filename:
539 if filename:
538 return openformatter(fm._ui, filename, fm._topic, opts)
540 return openformatter(fm._ui, filename, fm._topic, opts)
539 else:
541 else:
540 return _neverending(fm)
542 return _neverending(fm)
@@ -1,1566 +1,1570 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import os
10 import os
11 import re
11 import re
12 import types
12 import types
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 color,
16 color,
17 config,
17 config,
18 encoding,
18 encoding,
19 error,
19 error,
20 minirst,
20 minirst,
21 obsutil,
21 obsutil,
22 parser,
22 parser,
23 pycompat,
23 pycompat,
24 registrar,
24 registrar,
25 revset as revsetmod,
25 revset as revsetmod,
26 revsetlang,
26 revsetlang,
27 scmutil,
27 scmutil,
28 templatefilters,
28 templatefilters,
29 templatekw,
29 templatekw,
30 util,
30 util,
31 )
31 )
32
32
33 # template parsing
33 # template parsing
34
34
35 elements = {
35 elements = {
36 # token-type: binding-strength, primary, prefix, infix, suffix
36 # token-type: binding-strength, primary, prefix, infix, suffix
37 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
37 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
38 ".": (18, None, None, (".", 18), None),
38 ".": (18, None, None, (".", 18), None),
39 "%": (15, None, None, ("%", 15), None),
39 "%": (15, None, None, ("%", 15), None),
40 "|": (15, None, None, ("|", 15), None),
40 "|": (15, None, None, ("|", 15), None),
41 "*": (5, None, None, ("*", 5), None),
41 "*": (5, None, None, ("*", 5), None),
42 "/": (5, None, None, ("/", 5), None),
42 "/": (5, None, None, ("/", 5), None),
43 "+": (4, None, None, ("+", 4), None),
43 "+": (4, None, None, ("+", 4), None),
44 "-": (4, None, ("negate", 19), ("-", 4), None),
44 "-": (4, None, ("negate", 19), ("-", 4), None),
45 "=": (3, None, None, ("keyvalue", 3), None),
45 "=": (3, None, None, ("keyvalue", 3), None),
46 ",": (2, None, None, ("list", 2), None),
46 ",": (2, None, None, ("list", 2), None),
47 ")": (0, None, None, None, None),
47 ")": (0, None, None, None, None),
48 "integer": (0, "integer", None, None, None),
48 "integer": (0, "integer", None, None, None),
49 "symbol": (0, "symbol", None, None, None),
49 "symbol": (0, "symbol", None, None, None),
50 "string": (0, "string", None, None, None),
50 "string": (0, "string", None, None, None),
51 "template": (0, "template", None, None, None),
51 "template": (0, "template", None, None, None),
52 "end": (0, None, None, None, None),
52 "end": (0, None, None, None, None),
53 }
53 }
54
54
55 def tokenize(program, start, end, term=None):
55 def tokenize(program, start, end, term=None):
56 """Parse a template expression into a stream of tokens, which must end
56 """Parse a template expression into a stream of tokens, which must end
57 with term if specified"""
57 with term if specified"""
58 pos = start
58 pos = start
59 program = pycompat.bytestr(program)
59 program = pycompat.bytestr(program)
60 while pos < end:
60 while pos < end:
61 c = program[pos]
61 c = program[pos]
62 if c.isspace(): # skip inter-token whitespace
62 if c.isspace(): # skip inter-token whitespace
63 pass
63 pass
64 elif c in "(=,).%|+-*/": # handle simple operators
64 elif c in "(=,).%|+-*/": # handle simple operators
65 yield (c, None, pos)
65 yield (c, None, pos)
66 elif c in '"\'': # handle quoted templates
66 elif c in '"\'': # handle quoted templates
67 s = pos + 1
67 s = pos + 1
68 data, pos = _parsetemplate(program, s, end, c)
68 data, pos = _parsetemplate(program, s, end, c)
69 yield ('template', data, s)
69 yield ('template', data, s)
70 pos -= 1
70 pos -= 1
71 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
71 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
72 # handle quoted strings
72 # handle quoted strings
73 c = program[pos + 1]
73 c = program[pos + 1]
74 s = pos = pos + 2
74 s = pos = pos + 2
75 while pos < end: # find closing quote
75 while pos < end: # find closing quote
76 d = program[pos]
76 d = program[pos]
77 if d == '\\': # skip over escaped characters
77 if d == '\\': # skip over escaped characters
78 pos += 2
78 pos += 2
79 continue
79 continue
80 if d == c:
80 if d == c:
81 yield ('string', program[s:pos], s)
81 yield ('string', program[s:pos], s)
82 break
82 break
83 pos += 1
83 pos += 1
84 else:
84 else:
85 raise error.ParseError(_("unterminated string"), s)
85 raise error.ParseError(_("unterminated string"), s)
86 elif c.isdigit():
86 elif c.isdigit():
87 s = pos
87 s = pos
88 while pos < end:
88 while pos < end:
89 d = program[pos]
89 d = program[pos]
90 if not d.isdigit():
90 if not d.isdigit():
91 break
91 break
92 pos += 1
92 pos += 1
93 yield ('integer', program[s:pos], s)
93 yield ('integer', program[s:pos], s)
94 pos -= 1
94 pos -= 1
95 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
95 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
96 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
96 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
97 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
97 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
98 # where some of nested templates were preprocessed as strings and
98 # where some of nested templates were preprocessed as strings and
99 # then compiled. therefore, \"...\" was allowed. (issue4733)
99 # then compiled. therefore, \"...\" was allowed. (issue4733)
100 #
100 #
101 # processing flow of _evalifliteral() at 5ab28a2e9962:
101 # processing flow of _evalifliteral() at 5ab28a2e9962:
102 # outer template string -> stringify() -> compiletemplate()
102 # outer template string -> stringify() -> compiletemplate()
103 # ------------------------ ------------ ------------------
103 # ------------------------ ------------ ------------------
104 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
104 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
105 # ~~~~~~~~
105 # ~~~~~~~~
106 # escaped quoted string
106 # escaped quoted string
107 if c == 'r':
107 if c == 'r':
108 pos += 1
108 pos += 1
109 token = 'string'
109 token = 'string'
110 else:
110 else:
111 token = 'template'
111 token = 'template'
112 quote = program[pos:pos + 2]
112 quote = program[pos:pos + 2]
113 s = pos = pos + 2
113 s = pos = pos + 2
114 while pos < end: # find closing escaped quote
114 while pos < end: # find closing escaped quote
115 if program.startswith('\\\\\\', pos, end):
115 if program.startswith('\\\\\\', pos, end):
116 pos += 4 # skip over double escaped characters
116 pos += 4 # skip over double escaped characters
117 continue
117 continue
118 if program.startswith(quote, pos, end):
118 if program.startswith(quote, pos, end):
119 # interpret as if it were a part of an outer string
119 # interpret as if it were a part of an outer string
120 data = parser.unescapestr(program[s:pos])
120 data = parser.unescapestr(program[s:pos])
121 if token == 'template':
121 if token == 'template':
122 data = _parsetemplate(data, 0, len(data))[0]
122 data = _parsetemplate(data, 0, len(data))[0]
123 yield (token, data, s)
123 yield (token, data, s)
124 pos += 1
124 pos += 1
125 break
125 break
126 pos += 1
126 pos += 1
127 else:
127 else:
128 raise error.ParseError(_("unterminated string"), s)
128 raise error.ParseError(_("unterminated string"), s)
129 elif c.isalnum() or c in '_':
129 elif c.isalnum() or c in '_':
130 s = pos
130 s = pos
131 pos += 1
131 pos += 1
132 while pos < end: # find end of symbol
132 while pos < end: # find end of symbol
133 d = program[pos]
133 d = program[pos]
134 if not (d.isalnum() or d == "_"):
134 if not (d.isalnum() or d == "_"):
135 break
135 break
136 pos += 1
136 pos += 1
137 sym = program[s:pos]
137 sym = program[s:pos]
138 yield ('symbol', sym, s)
138 yield ('symbol', sym, s)
139 pos -= 1
139 pos -= 1
140 elif c == term:
140 elif c == term:
141 yield ('end', None, pos + 1)
141 yield ('end', None, pos + 1)
142 return
142 return
143 else:
143 else:
144 raise error.ParseError(_("syntax error"), pos)
144 raise error.ParseError(_("syntax error"), pos)
145 pos += 1
145 pos += 1
146 if term:
146 if term:
147 raise error.ParseError(_("unterminated template expansion"), start)
147 raise error.ParseError(_("unterminated template expansion"), start)
148 yield ('end', None, pos)
148 yield ('end', None, pos)
149
149
150 def _parsetemplate(tmpl, start, stop, quote=''):
150 def _parsetemplate(tmpl, start, stop, quote=''):
151 r"""
151 r"""
152 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
152 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
153 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
153 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
154 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
154 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
155 ([('string', 'foo'), ('symbol', 'bar')], 9)
155 ([('string', 'foo'), ('symbol', 'bar')], 9)
156 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
156 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
157 ([('string', 'foo')], 4)
157 ([('string', 'foo')], 4)
158 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
158 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
159 ([('string', 'foo"'), ('string', 'bar')], 9)
159 ([('string', 'foo"'), ('string', 'bar')], 9)
160 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
160 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
161 ([('string', 'foo\\')], 6)
161 ([('string', 'foo\\')], 6)
162 """
162 """
163 parsed = []
163 parsed = []
164 sepchars = '{' + quote
164 sepchars = '{' + quote
165 pos = start
165 pos = start
166 p = parser.parser(elements)
166 p = parser.parser(elements)
167 while pos < stop:
167 while pos < stop:
168 n = min((tmpl.find(c, pos, stop) for c in sepchars),
168 n = min((tmpl.find(c, pos, stop) for c in sepchars),
169 key=lambda n: (n < 0, n))
169 key=lambda n: (n < 0, n))
170 if n < 0:
170 if n < 0:
171 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
171 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
172 pos = stop
172 pos = stop
173 break
173 break
174 c = tmpl[n:n + 1]
174 c = tmpl[n:n + 1]
175 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
175 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
176 if bs % 2 == 1:
176 if bs % 2 == 1:
177 # escaped (e.g. '\{', '\\\{', but not '\\{')
177 # escaped (e.g. '\{', '\\\{', but not '\\{')
178 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
178 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
179 pos = n + 1
179 pos = n + 1
180 continue
180 continue
181 if n > pos:
181 if n > pos:
182 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
182 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
183 if c == quote:
183 if c == quote:
184 return parsed, n + 1
184 return parsed, n + 1
185
185
186 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
186 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
187 parsed.append(parseres)
187 parsed.append(parseres)
188
188
189 if quote:
189 if quote:
190 raise error.ParseError(_("unterminated string"), start)
190 raise error.ParseError(_("unterminated string"), start)
191 return parsed, pos
191 return parsed, pos
192
192
193 def _unnesttemplatelist(tree):
193 def _unnesttemplatelist(tree):
194 """Expand list of templates to node tuple
194 """Expand list of templates to node tuple
195
195
196 >>> def f(tree):
196 >>> def f(tree):
197 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
197 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
198 >>> f((b'template', []))
198 >>> f((b'template', []))
199 (string '')
199 (string '')
200 >>> f((b'template', [(b'string', b'foo')]))
200 >>> f((b'template', [(b'string', b'foo')]))
201 (string 'foo')
201 (string 'foo')
202 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
202 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
203 (template
203 (template
204 (string 'foo')
204 (string 'foo')
205 (symbol 'rev'))
205 (symbol 'rev'))
206 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
206 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
207 (template
207 (template
208 (symbol 'rev'))
208 (symbol 'rev'))
209 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
209 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
210 (string 'foo')
210 (string 'foo')
211 """
211 """
212 if not isinstance(tree, tuple):
212 if not isinstance(tree, tuple):
213 return tree
213 return tree
214 op = tree[0]
214 op = tree[0]
215 if op != 'template':
215 if op != 'template':
216 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
216 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
217
217
218 assert len(tree) == 2
218 assert len(tree) == 2
219 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
219 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
220 if not xs:
220 if not xs:
221 return ('string', '') # empty template ""
221 return ('string', '') # empty template ""
222 elif len(xs) == 1 and xs[0][0] == 'string':
222 elif len(xs) == 1 and xs[0][0] == 'string':
223 return xs[0] # fast path for string with no template fragment "x"
223 return xs[0] # fast path for string with no template fragment "x"
224 else:
224 else:
225 return (op,) + xs
225 return (op,) + xs
226
226
227 def parse(tmpl):
227 def parse(tmpl):
228 """Parse template string into tree"""
228 """Parse template string into tree"""
229 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
229 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
230 assert pos == len(tmpl), 'unquoted template should be consumed'
230 assert pos == len(tmpl), 'unquoted template should be consumed'
231 return _unnesttemplatelist(('template', parsed))
231 return _unnesttemplatelist(('template', parsed))
232
232
233 def _parseexpr(expr):
233 def _parseexpr(expr):
234 """Parse a template expression into tree
234 """Parse a template expression into tree
235
235
236 >>> _parseexpr(b'"foo"')
236 >>> _parseexpr(b'"foo"')
237 ('string', 'foo')
237 ('string', 'foo')
238 >>> _parseexpr(b'foo(bar)')
238 >>> _parseexpr(b'foo(bar)')
239 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
239 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
240 >>> _parseexpr(b'foo(')
240 >>> _parseexpr(b'foo(')
241 Traceback (most recent call last):
241 Traceback (most recent call last):
242 ...
242 ...
243 ParseError: ('not a prefix: end', 4)
243 ParseError: ('not a prefix: end', 4)
244 >>> _parseexpr(b'"foo" "bar"')
244 >>> _parseexpr(b'"foo" "bar"')
245 Traceback (most recent call last):
245 Traceback (most recent call last):
246 ...
246 ...
247 ParseError: ('invalid token', 7)
247 ParseError: ('invalid token', 7)
248 """
248 """
249 p = parser.parser(elements)
249 p = parser.parser(elements)
250 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
250 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
251 if pos != len(expr):
251 if pos != len(expr):
252 raise error.ParseError(_('invalid token'), pos)
252 raise error.ParseError(_('invalid token'), pos)
253 return _unnesttemplatelist(tree)
253 return _unnesttemplatelist(tree)
254
254
255 def prettyformat(tree):
255 def prettyformat(tree):
256 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
256 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
257
257
258 def compileexp(exp, context, curmethods):
258 def compileexp(exp, context, curmethods):
259 """Compile parsed template tree to (func, data) pair"""
259 """Compile parsed template tree to (func, data) pair"""
260 t = exp[0]
260 t = exp[0]
261 if t in curmethods:
261 if t in curmethods:
262 return curmethods[t](exp, context)
262 return curmethods[t](exp, context)
263 raise error.ParseError(_("unknown method '%s'") % t)
263 raise error.ParseError(_("unknown method '%s'") % t)
264
264
265 # template evaluation
265 # template evaluation
266
266
267 def getsymbol(exp):
267 def getsymbol(exp):
268 if exp[0] == 'symbol':
268 if exp[0] == 'symbol':
269 return exp[1]
269 return exp[1]
270 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
270 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
271
271
272 def getlist(x):
272 def getlist(x):
273 if not x:
273 if not x:
274 return []
274 return []
275 if x[0] == 'list':
275 if x[0] == 'list':
276 return getlist(x[1]) + [x[2]]
276 return getlist(x[1]) + [x[2]]
277 return [x]
277 return [x]
278
278
279 def gettemplate(exp, context):
279 def gettemplate(exp, context):
280 """Compile given template tree or load named template from map file;
280 """Compile given template tree or load named template from map file;
281 returns (func, data) pair"""
281 returns (func, data) pair"""
282 if exp[0] in ('template', 'string'):
282 if exp[0] in ('template', 'string'):
283 return compileexp(exp, context, methods)
283 return compileexp(exp, context, methods)
284 if exp[0] == 'symbol':
284 if exp[0] == 'symbol':
285 # unlike runsymbol(), here 'symbol' is always taken as template name
285 # unlike runsymbol(), here 'symbol' is always taken as template name
286 # even if it exists in mapping. this allows us to override mapping
286 # even if it exists in mapping. this allows us to override mapping
287 # by web templates, e.g. 'changelogtag' is redefined in map file.
287 # by web templates, e.g. 'changelogtag' is redefined in map file.
288 return context._load(exp[1])
288 return context._load(exp[1])
289 raise error.ParseError(_("expected template specifier"))
289 raise error.ParseError(_("expected template specifier"))
290
290
291 def findsymbolicname(arg):
291 def findsymbolicname(arg):
292 """Find symbolic name for the given compiled expression; returns None
292 """Find symbolic name for the given compiled expression; returns None
293 if nothing found reliably"""
293 if nothing found reliably"""
294 while True:
294 while True:
295 func, data = arg
295 func, data = arg
296 if func is runsymbol:
296 if func is runsymbol:
297 return data
297 return data
298 elif func is runfilter:
298 elif func is runfilter:
299 arg = data[0]
299 arg = data[0]
300 else:
300 else:
301 return None
301 return None
302
302
303 def evalrawexp(context, mapping, arg):
303 def evalrawexp(context, mapping, arg):
304 """Evaluate given argument as a bare template object which may require
304 """Evaluate given argument as a bare template object which may require
305 further processing (such as folding generator of strings)"""
305 further processing (such as folding generator of strings)"""
306 func, data = arg
306 func, data = arg
307 return func(context, mapping, data)
307 return func(context, mapping, data)
308
308
309 def evalfuncarg(context, mapping, arg):
309 def evalfuncarg(context, mapping, arg):
310 """Evaluate given argument as value type"""
310 """Evaluate given argument as value type"""
311 thing = evalrawexp(context, mapping, arg)
311 thing = evalrawexp(context, mapping, arg)
312 thing = templatekw.unwrapvalue(thing)
312 thing = templatekw.unwrapvalue(thing)
313 # evalrawexp() may return string, generator of strings or arbitrary object
313 # evalrawexp() may return string, generator of strings or arbitrary object
314 # such as date tuple, but filter does not want generator.
314 # such as date tuple, but filter does not want generator.
315 if isinstance(thing, types.GeneratorType):
315 if isinstance(thing, types.GeneratorType):
316 thing = stringify(thing)
316 thing = stringify(thing)
317 return thing
317 return thing
318
318
319 def evalboolean(context, mapping, arg):
319 def evalboolean(context, mapping, arg):
320 """Evaluate given argument as boolean, but also takes boolean literals"""
320 """Evaluate given argument as boolean, but also takes boolean literals"""
321 func, data = arg
321 func, data = arg
322 if func is runsymbol:
322 if func is runsymbol:
323 thing = func(context, mapping, data, default=None)
323 thing = func(context, mapping, data, default=None)
324 if thing is None:
324 if thing is None:
325 # not a template keyword, takes as a boolean literal
325 # not a template keyword, takes as a boolean literal
326 thing = util.parsebool(data)
326 thing = util.parsebool(data)
327 else:
327 else:
328 thing = func(context, mapping, data)
328 thing = func(context, mapping, data)
329 thing = templatekw.unwrapvalue(thing)
329 thing = templatekw.unwrapvalue(thing)
330 if isinstance(thing, bool):
330 if isinstance(thing, bool):
331 return thing
331 return thing
332 # other objects are evaluated as strings, which means 0 is True, but
332 # other objects are evaluated as strings, which means 0 is True, but
333 # empty dict/list should be False as they are expected to be ''
333 # empty dict/list should be False as they are expected to be ''
334 return bool(stringify(thing))
334 return bool(stringify(thing))
335
335
336 def evalinteger(context, mapping, arg, err=None):
336 def evalinteger(context, mapping, arg, err=None):
337 v = evalfuncarg(context, mapping, arg)
337 v = evalfuncarg(context, mapping, arg)
338 try:
338 try:
339 return int(v)
339 return int(v)
340 except (TypeError, ValueError):
340 except (TypeError, ValueError):
341 raise error.ParseError(err or _('not an integer'))
341 raise error.ParseError(err or _('not an integer'))
342
342
343 def evalstring(context, mapping, arg):
343 def evalstring(context, mapping, arg):
344 return stringify(evalrawexp(context, mapping, arg))
344 return stringify(evalrawexp(context, mapping, arg))
345
345
346 def evalstringliteral(context, mapping, arg):
346 def evalstringliteral(context, mapping, arg):
347 """Evaluate given argument as string template, but returns symbol name
347 """Evaluate given argument as string template, but returns symbol name
348 if it is unknown"""
348 if it is unknown"""
349 func, data = arg
349 func, data = arg
350 if func is runsymbol:
350 if func is runsymbol:
351 thing = func(context, mapping, data, default=data)
351 thing = func(context, mapping, data, default=data)
352 else:
352 else:
353 thing = func(context, mapping, data)
353 thing = func(context, mapping, data)
354 return stringify(thing)
354 return stringify(thing)
355
355
356 _evalfuncbytype = {
356 _evalfuncbytype = {
357 bool: evalboolean,
357 bool: evalboolean,
358 bytes: evalstring,
358 bytes: evalstring,
359 int: evalinteger,
359 int: evalinteger,
360 }
360 }
361
361
362 def evalastype(context, mapping, arg, typ):
362 def evalastype(context, mapping, arg, typ):
363 """Evaluate given argument and coerce its type"""
363 """Evaluate given argument and coerce its type"""
364 try:
364 try:
365 f = _evalfuncbytype[typ]
365 f = _evalfuncbytype[typ]
366 except KeyError:
366 except KeyError:
367 raise error.ProgrammingError('invalid type specified: %r' % typ)
367 raise error.ProgrammingError('invalid type specified: %r' % typ)
368 return f(context, mapping, arg)
368 return f(context, mapping, arg)
369
369
370 def runinteger(context, mapping, data):
370 def runinteger(context, mapping, data):
371 return int(data)
371 return int(data)
372
372
373 def runstring(context, mapping, data):
373 def runstring(context, mapping, data):
374 return data
374 return data
375
375
376 def _recursivesymbolblocker(key):
376 def _recursivesymbolblocker(key):
377 def showrecursion(**args):
377 def showrecursion(**args):
378 raise error.Abort(_("recursive reference '%s' in template") % key)
378 raise error.Abort(_("recursive reference '%s' in template") % key)
379 return showrecursion
379 return showrecursion
380
380
381 def _runrecursivesymbol(context, mapping, key):
381 def _runrecursivesymbol(context, mapping, key):
382 raise error.Abort(_("recursive reference '%s' in template") % key)
382 raise error.Abort(_("recursive reference '%s' in template") % key)
383
383
384 def runsymbol(context, mapping, key, default=''):
384 def runsymbol(context, mapping, key, default=''):
385 v = context.symbol(mapping, key)
385 v = context.symbol(mapping, key)
386 if v is None:
386 if v is None:
387 # put poison to cut recursion. we can't move this to parsing phase
387 # put poison to cut recursion. we can't move this to parsing phase
388 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
388 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
389 safemapping = mapping.copy()
389 safemapping = mapping.copy()
390 safemapping[key] = _recursivesymbolblocker(key)
390 safemapping[key] = _recursivesymbolblocker(key)
391 try:
391 try:
392 v = context.process(key, safemapping)
392 v = context.process(key, safemapping)
393 except TemplateNotFound:
393 except TemplateNotFound:
394 v = default
394 v = default
395 if callable(v):
395 if callable(v):
396 # TODO: templatekw functions will be updated to take (context, mapping)
396 # TODO: templatekw functions will be updated to take (context, mapping)
397 # pair instead of **props
397 # pair instead of **props
398 props = context._resources.copy()
398 props = context._resources.copy()
399 props.update(mapping)
399 props.update(mapping)
400 return v(**props)
400 return v(**props)
401 return v
401 return v
402
402
403 def buildtemplate(exp, context):
403 def buildtemplate(exp, context):
404 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
404 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
405 return (runtemplate, ctmpl)
405 return (runtemplate, ctmpl)
406
406
407 def runtemplate(context, mapping, template):
407 def runtemplate(context, mapping, template):
408 for arg in template:
408 for arg in template:
409 yield evalrawexp(context, mapping, arg)
409 yield evalrawexp(context, mapping, arg)
410
410
411 def buildfilter(exp, context):
411 def buildfilter(exp, context):
412 n = getsymbol(exp[2])
412 n = getsymbol(exp[2])
413 if n in context._filters:
413 if n in context._filters:
414 filt = context._filters[n]
414 filt = context._filters[n]
415 arg = compileexp(exp[1], context, methods)
415 arg = compileexp(exp[1], context, methods)
416 return (runfilter, (arg, filt))
416 return (runfilter, (arg, filt))
417 if n in funcs:
417 if n in funcs:
418 f = funcs[n]
418 f = funcs[n]
419 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
419 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
420 return (f, args)
420 return (f, args)
421 raise error.ParseError(_("unknown function '%s'") % n)
421 raise error.ParseError(_("unknown function '%s'") % n)
422
422
423 def runfilter(context, mapping, data):
423 def runfilter(context, mapping, data):
424 arg, filt = data
424 arg, filt = data
425 thing = evalfuncarg(context, mapping, arg)
425 thing = evalfuncarg(context, mapping, arg)
426 try:
426 try:
427 return filt(thing)
427 return filt(thing)
428 except (ValueError, AttributeError, TypeError):
428 except (ValueError, AttributeError, TypeError):
429 sym = findsymbolicname(arg)
429 sym = findsymbolicname(arg)
430 if sym:
430 if sym:
431 msg = (_("template filter '%s' is not compatible with keyword '%s'")
431 msg = (_("template filter '%s' is not compatible with keyword '%s'")
432 % (pycompat.sysbytes(filt.__name__), sym))
432 % (pycompat.sysbytes(filt.__name__), sym))
433 else:
433 else:
434 msg = (_("incompatible use of template filter '%s'")
434 msg = (_("incompatible use of template filter '%s'")
435 % pycompat.sysbytes(filt.__name__))
435 % pycompat.sysbytes(filt.__name__))
436 raise error.Abort(msg)
436 raise error.Abort(msg)
437
437
438 def buildmap(exp, context):
438 def buildmap(exp, context):
439 darg = compileexp(exp[1], context, methods)
439 darg = compileexp(exp[1], context, methods)
440 targ = gettemplate(exp[2], context)
440 targ = gettemplate(exp[2], context)
441 return (runmap, (darg, targ))
441 return (runmap, (darg, targ))
442
442
443 def runmap(context, mapping, data):
443 def runmap(context, mapping, data):
444 darg, targ = data
444 darg, targ = data
445 d = evalrawexp(context, mapping, darg)
445 d = evalrawexp(context, mapping, darg)
446 if util.safehasattr(d, 'itermaps'):
446 if util.safehasattr(d, 'itermaps'):
447 diter = d.itermaps()
447 diter = d.itermaps()
448 else:
448 else:
449 try:
449 try:
450 diter = iter(d)
450 diter = iter(d)
451 except TypeError:
451 except TypeError:
452 sym = findsymbolicname(darg)
452 sym = findsymbolicname(darg)
453 if sym:
453 if sym:
454 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
454 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
455 else:
455 else:
456 raise error.ParseError(_("%r is not iterable") % d)
456 raise error.ParseError(_("%r is not iterable") % d)
457
457
458 for i, v in enumerate(diter):
458 for i, v in enumerate(diter):
459 lm = mapping.copy()
459 lm = mapping.copy()
460 lm['index'] = i
460 lm['index'] = i
461 if isinstance(v, dict):
461 if isinstance(v, dict):
462 lm.update(v)
462 lm.update(v)
463 lm['originalnode'] = mapping.get('node')
463 lm['originalnode'] = mapping.get('node')
464 yield evalrawexp(context, lm, targ)
464 yield evalrawexp(context, lm, targ)
465 else:
465 else:
466 # v is not an iterable of dicts, this happen when 'key'
466 # v is not an iterable of dicts, this happen when 'key'
467 # has been fully expanded already and format is useless.
467 # has been fully expanded already and format is useless.
468 # If so, return the expanded value.
468 # If so, return the expanded value.
469 yield v
469 yield v
470
470
471 def buildmember(exp, context):
471 def buildmember(exp, context):
472 darg = compileexp(exp[1], context, methods)
472 darg = compileexp(exp[1], context, methods)
473 memb = getsymbol(exp[2])
473 memb = getsymbol(exp[2])
474 return (runmember, (darg, memb))
474 return (runmember, (darg, memb))
475
475
476 def runmember(context, mapping, data):
476 def runmember(context, mapping, data):
477 darg, memb = data
477 darg, memb = data
478 d = evalrawexp(context, mapping, darg)
478 d = evalrawexp(context, mapping, darg)
479 if util.safehasattr(d, 'tomap'):
479 if util.safehasattr(d, 'tomap'):
480 lm = mapping.copy()
480 lm = mapping.copy()
481 lm.update(d.tomap())
481 lm.update(d.tomap())
482 return runsymbol(context, lm, memb)
482 return runsymbol(context, lm, memb)
483 if util.safehasattr(d, 'get'):
483 if util.safehasattr(d, 'get'):
484 return _getdictitem(d, memb)
484 return _getdictitem(d, memb)
485
485
486 sym = findsymbolicname(darg)
486 sym = findsymbolicname(darg)
487 if sym:
487 if sym:
488 raise error.ParseError(_("keyword '%s' has no member") % sym)
488 raise error.ParseError(_("keyword '%s' has no member") % sym)
489 else:
489 else:
490 raise error.ParseError(_("%r has no member") % d)
490 raise error.ParseError(_("%r has no member") % d)
491
491
492 def buildnegate(exp, context):
492 def buildnegate(exp, context):
493 arg = compileexp(exp[1], context, exprmethods)
493 arg = compileexp(exp[1], context, exprmethods)
494 return (runnegate, arg)
494 return (runnegate, arg)
495
495
496 def runnegate(context, mapping, data):
496 def runnegate(context, mapping, data):
497 data = evalinteger(context, mapping, data,
497 data = evalinteger(context, mapping, data,
498 _('negation needs an integer argument'))
498 _('negation needs an integer argument'))
499 return -data
499 return -data
500
500
501 def buildarithmetic(exp, context, func):
501 def buildarithmetic(exp, context, func):
502 left = compileexp(exp[1], context, exprmethods)
502 left = compileexp(exp[1], context, exprmethods)
503 right = compileexp(exp[2], context, exprmethods)
503 right = compileexp(exp[2], context, exprmethods)
504 return (runarithmetic, (func, left, right))
504 return (runarithmetic, (func, left, right))
505
505
506 def runarithmetic(context, mapping, data):
506 def runarithmetic(context, mapping, data):
507 func, left, right = data
507 func, left, right = data
508 left = evalinteger(context, mapping, left,
508 left = evalinteger(context, mapping, left,
509 _('arithmetic only defined on integers'))
509 _('arithmetic only defined on integers'))
510 right = evalinteger(context, mapping, right,
510 right = evalinteger(context, mapping, right,
511 _('arithmetic only defined on integers'))
511 _('arithmetic only defined on integers'))
512 try:
512 try:
513 return func(left, right)
513 return func(left, right)
514 except ZeroDivisionError:
514 except ZeroDivisionError:
515 raise error.Abort(_('division by zero is not defined'))
515 raise error.Abort(_('division by zero is not defined'))
516
516
517 def buildfunc(exp, context):
517 def buildfunc(exp, context):
518 n = getsymbol(exp[1])
518 n = getsymbol(exp[1])
519 if n in funcs:
519 if n in funcs:
520 f = funcs[n]
520 f = funcs[n]
521 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
521 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
522 return (f, args)
522 return (f, args)
523 if n in context._filters:
523 if n in context._filters:
524 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
524 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
525 if len(args) != 1:
525 if len(args) != 1:
526 raise error.ParseError(_("filter %s expects one argument") % n)
526 raise error.ParseError(_("filter %s expects one argument") % n)
527 f = context._filters[n]
527 f = context._filters[n]
528 return (runfilter, (args[0], f))
528 return (runfilter, (args[0], f))
529 raise error.ParseError(_("unknown function '%s'") % n)
529 raise error.ParseError(_("unknown function '%s'") % n)
530
530
531 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
531 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
532 """Compile parsed tree of function arguments into list or dict of
532 """Compile parsed tree of function arguments into list or dict of
533 (func, data) pairs
533 (func, data) pairs
534
534
535 >>> context = engine(lambda t: (runsymbol, t))
535 >>> context = engine(lambda t: (runsymbol, t))
536 >>> def fargs(expr, argspec):
536 >>> def fargs(expr, argspec):
537 ... x = _parseexpr(expr)
537 ... x = _parseexpr(expr)
538 ... n = getsymbol(x[1])
538 ... n = getsymbol(x[1])
539 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
539 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
540 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
540 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
541 ['l', 'k']
541 ['l', 'k']
542 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
542 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
543 >>> list(args.keys()), list(args[b'opts'].keys())
543 >>> list(args.keys()), list(args[b'opts'].keys())
544 (['opts'], ['opts', 'k'])
544 (['opts'], ['opts', 'k'])
545 """
545 """
546 def compiledict(xs):
546 def compiledict(xs):
547 return util.sortdict((k, compileexp(x, context, curmethods))
547 return util.sortdict((k, compileexp(x, context, curmethods))
548 for k, x in xs.iteritems())
548 for k, x in xs.iteritems())
549 def compilelist(xs):
549 def compilelist(xs):
550 return [compileexp(x, context, curmethods) for x in xs]
550 return [compileexp(x, context, curmethods) for x in xs]
551
551
552 if not argspec:
552 if not argspec:
553 # filter or function with no argspec: return list of positional args
553 # filter or function with no argspec: return list of positional args
554 return compilelist(getlist(exp))
554 return compilelist(getlist(exp))
555
555
556 # function with argspec: return dict of named args
556 # function with argspec: return dict of named args
557 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
557 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
558 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
558 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
559 keyvaluenode='keyvalue', keynode='symbol')
559 keyvaluenode='keyvalue', keynode='symbol')
560 compargs = util.sortdict()
560 compargs = util.sortdict()
561 if varkey:
561 if varkey:
562 compargs[varkey] = compilelist(treeargs.pop(varkey))
562 compargs[varkey] = compilelist(treeargs.pop(varkey))
563 if optkey:
563 if optkey:
564 compargs[optkey] = compiledict(treeargs.pop(optkey))
564 compargs[optkey] = compiledict(treeargs.pop(optkey))
565 compargs.update(compiledict(treeargs))
565 compargs.update(compiledict(treeargs))
566 return compargs
566 return compargs
567
567
568 def buildkeyvaluepair(exp, content):
568 def buildkeyvaluepair(exp, content):
569 raise error.ParseError(_("can't use a key-value pair in this context"))
569 raise error.ParseError(_("can't use a key-value pair in this context"))
570
570
571 # dict of template built-in functions
571 # dict of template built-in functions
572 funcs = {}
572 funcs = {}
573
573
574 templatefunc = registrar.templatefunc(funcs)
574 templatefunc = registrar.templatefunc(funcs)
575
575
576 @templatefunc('date(date[, fmt])')
576 @templatefunc('date(date[, fmt])')
577 def date(context, mapping, args):
577 def date(context, mapping, args):
578 """Format a date. See :hg:`help dates` for formatting
578 """Format a date. See :hg:`help dates` for formatting
579 strings. The default is a Unix date format, including the timezone:
579 strings. The default is a Unix date format, including the timezone:
580 "Mon Sep 04 15:13:13 2006 0700"."""
580 "Mon Sep 04 15:13:13 2006 0700"."""
581 if not (1 <= len(args) <= 2):
581 if not (1 <= len(args) <= 2):
582 # i18n: "date" is a keyword
582 # i18n: "date" is a keyword
583 raise error.ParseError(_("date expects one or two arguments"))
583 raise error.ParseError(_("date expects one or two arguments"))
584
584
585 date = evalfuncarg(context, mapping, args[0])
585 date = evalfuncarg(context, mapping, args[0])
586 fmt = None
586 fmt = None
587 if len(args) == 2:
587 if len(args) == 2:
588 fmt = evalstring(context, mapping, args[1])
588 fmt = evalstring(context, mapping, args[1])
589 try:
589 try:
590 if fmt is None:
590 if fmt is None:
591 return util.datestr(date)
591 return util.datestr(date)
592 else:
592 else:
593 return util.datestr(date, fmt)
593 return util.datestr(date, fmt)
594 except (TypeError, ValueError):
594 except (TypeError, ValueError):
595 # i18n: "date" is a keyword
595 # i18n: "date" is a keyword
596 raise error.ParseError(_("date expects a date information"))
596 raise error.ParseError(_("date expects a date information"))
597
597
598 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
598 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
599 def dict_(context, mapping, args):
599 def dict_(context, mapping, args):
600 """Construct a dict from key-value pairs. A key may be omitted if
600 """Construct a dict from key-value pairs. A key may be omitted if
601 a value expression can provide an unambiguous name."""
601 a value expression can provide an unambiguous name."""
602 data = util.sortdict()
602 data = util.sortdict()
603
603
604 for v in args['args']:
604 for v in args['args']:
605 k = findsymbolicname(v)
605 k = findsymbolicname(v)
606 if not k:
606 if not k:
607 raise error.ParseError(_('dict key cannot be inferred'))
607 raise error.ParseError(_('dict key cannot be inferred'))
608 if k in data or k in args['kwargs']:
608 if k in data or k in args['kwargs']:
609 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
609 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
610 data[k] = evalfuncarg(context, mapping, v)
610 data[k] = evalfuncarg(context, mapping, v)
611
611
612 data.update((k, evalfuncarg(context, mapping, v))
612 data.update((k, evalfuncarg(context, mapping, v))
613 for k, v in args['kwargs'].iteritems())
613 for k, v in args['kwargs'].iteritems())
614 return templatekw.hybriddict(data)
614 return templatekw.hybriddict(data)
615
615
616 @templatefunc('diff([includepattern [, excludepattern]])')
616 @templatefunc('diff([includepattern [, excludepattern]])')
617 def diff(context, mapping, args):
617 def diff(context, mapping, args):
618 """Show a diff, optionally
618 """Show a diff, optionally
619 specifying files to include or exclude."""
619 specifying files to include or exclude."""
620 if len(args) > 2:
620 if len(args) > 2:
621 # i18n: "diff" is a keyword
621 # i18n: "diff" is a keyword
622 raise error.ParseError(_("diff expects zero, one, or two arguments"))
622 raise error.ParseError(_("diff expects zero, one, or two arguments"))
623
623
624 def getpatterns(i):
624 def getpatterns(i):
625 if i < len(args):
625 if i < len(args):
626 s = evalstring(context, mapping, args[i]).strip()
626 s = evalstring(context, mapping, args[i]).strip()
627 if s:
627 if s:
628 return [s]
628 return [s]
629 return []
629 return []
630
630
631 ctx = context.resource(mapping, 'ctx')
631 ctx = context.resource(mapping, 'ctx')
632 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
632 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
633
633
634 return ''.join(chunks)
634 return ''.join(chunks)
635
635
636 @templatefunc('extdata(source)', argspec='source')
636 @templatefunc('extdata(source)', argspec='source')
637 def extdata(context, mapping, args):
637 def extdata(context, mapping, args):
638 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
638 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
639 if 'source' not in args:
639 if 'source' not in args:
640 # i18n: "extdata" is a keyword
640 # i18n: "extdata" is a keyword
641 raise error.ParseError(_('extdata expects one argument'))
641 raise error.ParseError(_('extdata expects one argument'))
642
642
643 source = evalstring(context, mapping, args['source'])
643 source = evalstring(context, mapping, args['source'])
644 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
644 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
645 ctx = context.resource(mapping, 'ctx')
645 ctx = context.resource(mapping, 'ctx')
646 if source in cache:
646 if source in cache:
647 data = cache[source]
647 data = cache[source]
648 else:
648 else:
649 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
649 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
650 return data.get(ctx.rev(), '')
650 return data.get(ctx.rev(), '')
651
651
652 @templatefunc('files(pattern)')
652 @templatefunc('files(pattern)')
653 def files(context, mapping, args):
653 def files(context, mapping, args):
654 """All files of the current changeset matching the pattern. See
654 """All files of the current changeset matching the pattern. See
655 :hg:`help patterns`."""
655 :hg:`help patterns`."""
656 if not len(args) == 1:
656 if not len(args) == 1:
657 # i18n: "files" is a keyword
657 # i18n: "files" is a keyword
658 raise error.ParseError(_("files expects one argument"))
658 raise error.ParseError(_("files expects one argument"))
659
659
660 raw = evalstring(context, mapping, args[0])
660 raw = evalstring(context, mapping, args[0])
661 ctx = context.resource(mapping, 'ctx')
661 ctx = context.resource(mapping, 'ctx')
662 m = ctx.match([raw])
662 m = ctx.match([raw])
663 files = list(ctx.matches(m))
663 files = list(ctx.matches(m))
664 # TODO: pass (context, mapping) pair to keyword function
664 # TODO: pass (context, mapping) pair to keyword function
665 props = context._resources.copy()
665 props = context._resources.copy()
666 props.update(mapping)
666 props.update(mapping)
667 return templatekw.showlist("file", files, props)
667 return templatekw.showlist("file", files, props)
668
668
669 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
669 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
670 def fill(context, mapping, args):
670 def fill(context, mapping, args):
671 """Fill many
671 """Fill many
672 paragraphs with optional indentation. See the "fill" filter."""
672 paragraphs with optional indentation. See the "fill" filter."""
673 if not (1 <= len(args) <= 4):
673 if not (1 <= len(args) <= 4):
674 # i18n: "fill" is a keyword
674 # i18n: "fill" is a keyword
675 raise error.ParseError(_("fill expects one to four arguments"))
675 raise error.ParseError(_("fill expects one to four arguments"))
676
676
677 text = evalstring(context, mapping, args[0])
677 text = evalstring(context, mapping, args[0])
678 width = 76
678 width = 76
679 initindent = ''
679 initindent = ''
680 hangindent = ''
680 hangindent = ''
681 if 2 <= len(args) <= 4:
681 if 2 <= len(args) <= 4:
682 width = evalinteger(context, mapping, args[1],
682 width = evalinteger(context, mapping, args[1],
683 # i18n: "fill" is a keyword
683 # i18n: "fill" is a keyword
684 _("fill expects an integer width"))
684 _("fill expects an integer width"))
685 try:
685 try:
686 initindent = evalstring(context, mapping, args[2])
686 initindent = evalstring(context, mapping, args[2])
687 hangindent = evalstring(context, mapping, args[3])
687 hangindent = evalstring(context, mapping, args[3])
688 except IndexError:
688 except IndexError:
689 pass
689 pass
690
690
691 return templatefilters.fill(text, width, initindent, hangindent)
691 return templatefilters.fill(text, width, initindent, hangindent)
692
692
693 @templatefunc('formatnode(node)')
693 @templatefunc('formatnode(node)')
694 def formatnode(context, mapping, args):
694 def formatnode(context, mapping, args):
695 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
695 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
696 if len(args) != 1:
696 if len(args) != 1:
697 # i18n: "formatnode" is a keyword
697 # i18n: "formatnode" is a keyword
698 raise error.ParseError(_("formatnode expects one argument"))
698 raise error.ParseError(_("formatnode expects one argument"))
699
699
700 ui = context.resource(mapping, 'ui')
700 ui = context.resource(mapping, 'ui')
701 node = evalstring(context, mapping, args[0])
701 node = evalstring(context, mapping, args[0])
702 if ui.debugflag:
702 if ui.debugflag:
703 return node
703 return node
704 return templatefilters.short(node)
704 return templatefilters.short(node)
705
705
706 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
706 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
707 argspec='text width fillchar left')
707 argspec='text width fillchar left')
708 def pad(context, mapping, args):
708 def pad(context, mapping, args):
709 """Pad text with a
709 """Pad text with a
710 fill character."""
710 fill character."""
711 if 'text' not in args or 'width' not in args:
711 if 'text' not in args or 'width' not in args:
712 # i18n: "pad" is a keyword
712 # i18n: "pad" is a keyword
713 raise error.ParseError(_("pad() expects two to four arguments"))
713 raise error.ParseError(_("pad() expects two to four arguments"))
714
714
715 width = evalinteger(context, mapping, args['width'],
715 width = evalinteger(context, mapping, args['width'],
716 # i18n: "pad" is a keyword
716 # i18n: "pad" is a keyword
717 _("pad() expects an integer width"))
717 _("pad() expects an integer width"))
718
718
719 text = evalstring(context, mapping, args['text'])
719 text = evalstring(context, mapping, args['text'])
720
720
721 left = False
721 left = False
722 fillchar = ' '
722 fillchar = ' '
723 if 'fillchar' in args:
723 if 'fillchar' in args:
724 fillchar = evalstring(context, mapping, args['fillchar'])
724 fillchar = evalstring(context, mapping, args['fillchar'])
725 if len(color.stripeffects(fillchar)) != 1:
725 if len(color.stripeffects(fillchar)) != 1:
726 # i18n: "pad" is a keyword
726 # i18n: "pad" is a keyword
727 raise error.ParseError(_("pad() expects a single fill character"))
727 raise error.ParseError(_("pad() expects a single fill character"))
728 if 'left' in args:
728 if 'left' in args:
729 left = evalboolean(context, mapping, args['left'])
729 left = evalboolean(context, mapping, args['left'])
730
730
731 fillwidth = width - encoding.colwidth(color.stripeffects(text))
731 fillwidth = width - encoding.colwidth(color.stripeffects(text))
732 if fillwidth <= 0:
732 if fillwidth <= 0:
733 return text
733 return text
734 if left:
734 if left:
735 return fillchar * fillwidth + text
735 return fillchar * fillwidth + text
736 else:
736 else:
737 return text + fillchar * fillwidth
737 return text + fillchar * fillwidth
738
738
739 @templatefunc('indent(text, indentchars[, firstline])')
739 @templatefunc('indent(text, indentchars[, firstline])')
740 def indent(context, mapping, args):
740 def indent(context, mapping, args):
741 """Indents all non-empty lines
741 """Indents all non-empty lines
742 with the characters given in the indentchars string. An optional
742 with the characters given in the indentchars string. An optional
743 third parameter will override the indent for the first line only
743 third parameter will override the indent for the first line only
744 if present."""
744 if present."""
745 if not (2 <= len(args) <= 3):
745 if not (2 <= len(args) <= 3):
746 # i18n: "indent" is a keyword
746 # i18n: "indent" is a keyword
747 raise error.ParseError(_("indent() expects two or three arguments"))
747 raise error.ParseError(_("indent() expects two or three arguments"))
748
748
749 text = evalstring(context, mapping, args[0])
749 text = evalstring(context, mapping, args[0])
750 indent = evalstring(context, mapping, args[1])
750 indent = evalstring(context, mapping, args[1])
751
751
752 if len(args) == 3:
752 if len(args) == 3:
753 firstline = evalstring(context, mapping, args[2])
753 firstline = evalstring(context, mapping, args[2])
754 else:
754 else:
755 firstline = indent
755 firstline = indent
756
756
757 # the indent function doesn't indent the first line, so we do it here
757 # the indent function doesn't indent the first line, so we do it here
758 return templatefilters.indent(firstline + text, indent)
758 return templatefilters.indent(firstline + text, indent)
759
759
760 @templatefunc('get(dict, key)')
760 @templatefunc('get(dict, key)')
761 def get(context, mapping, args):
761 def get(context, mapping, args):
762 """Get an attribute/key from an object. Some keywords
762 """Get an attribute/key from an object. Some keywords
763 are complex types. This function allows you to obtain the value of an
763 are complex types. This function allows you to obtain the value of an
764 attribute on these types."""
764 attribute on these types."""
765 if len(args) != 2:
765 if len(args) != 2:
766 # i18n: "get" is a keyword
766 # i18n: "get" is a keyword
767 raise error.ParseError(_("get() expects two arguments"))
767 raise error.ParseError(_("get() expects two arguments"))
768
768
769 dictarg = evalfuncarg(context, mapping, args[0])
769 dictarg = evalfuncarg(context, mapping, args[0])
770 if not util.safehasattr(dictarg, 'get'):
770 if not util.safehasattr(dictarg, 'get'):
771 # i18n: "get" is a keyword
771 # i18n: "get" is a keyword
772 raise error.ParseError(_("get() expects a dict as first argument"))
772 raise error.ParseError(_("get() expects a dict as first argument"))
773
773
774 key = evalfuncarg(context, mapping, args[1])
774 key = evalfuncarg(context, mapping, args[1])
775 return _getdictitem(dictarg, key)
775 return _getdictitem(dictarg, key)
776
776
777 def _getdictitem(dictarg, key):
777 def _getdictitem(dictarg, key):
778 val = dictarg.get(key)
778 val = dictarg.get(key)
779 if val is None:
779 if val is None:
780 return
780 return
781 return templatekw.wraphybridvalue(dictarg, key, val)
781 return templatekw.wraphybridvalue(dictarg, key, val)
782
782
783 @templatefunc('if(expr, then[, else])')
783 @templatefunc('if(expr, then[, else])')
784 def if_(context, mapping, args):
784 def if_(context, mapping, args):
785 """Conditionally execute based on the result of
785 """Conditionally execute based on the result of
786 an expression."""
786 an expression."""
787 if not (2 <= len(args) <= 3):
787 if not (2 <= len(args) <= 3):
788 # i18n: "if" is a keyword
788 # i18n: "if" is a keyword
789 raise error.ParseError(_("if expects two or three arguments"))
789 raise error.ParseError(_("if expects two or three arguments"))
790
790
791 test = evalboolean(context, mapping, args[0])
791 test = evalboolean(context, mapping, args[0])
792 if test:
792 if test:
793 yield evalrawexp(context, mapping, args[1])
793 yield evalrawexp(context, mapping, args[1])
794 elif len(args) == 3:
794 elif len(args) == 3:
795 yield evalrawexp(context, mapping, args[2])
795 yield evalrawexp(context, mapping, args[2])
796
796
797 @templatefunc('ifcontains(needle, haystack, then[, else])')
797 @templatefunc('ifcontains(needle, haystack, then[, else])')
798 def ifcontains(context, mapping, args):
798 def ifcontains(context, mapping, args):
799 """Conditionally execute based
799 """Conditionally execute based
800 on whether the item "needle" is in "haystack"."""
800 on whether the item "needle" is in "haystack"."""
801 if not (3 <= len(args) <= 4):
801 if not (3 <= len(args) <= 4):
802 # i18n: "ifcontains" is a keyword
802 # i18n: "ifcontains" is a keyword
803 raise error.ParseError(_("ifcontains expects three or four arguments"))
803 raise error.ParseError(_("ifcontains expects three or four arguments"))
804
804
805 haystack = evalfuncarg(context, mapping, args[1])
805 haystack = evalfuncarg(context, mapping, args[1])
806 try:
806 try:
807 needle = evalastype(context, mapping, args[0],
807 needle = evalastype(context, mapping, args[0],
808 getattr(haystack, 'keytype', None) or bytes)
808 getattr(haystack, 'keytype', None) or bytes)
809 found = (needle in haystack)
809 found = (needle in haystack)
810 except error.ParseError:
810 except error.ParseError:
811 found = False
811 found = False
812
812
813 if found:
813 if found:
814 yield evalrawexp(context, mapping, args[2])
814 yield evalrawexp(context, mapping, args[2])
815 elif len(args) == 4:
815 elif len(args) == 4:
816 yield evalrawexp(context, mapping, args[3])
816 yield evalrawexp(context, mapping, args[3])
817
817
818 @templatefunc('ifeq(expr1, expr2, then[, else])')
818 @templatefunc('ifeq(expr1, expr2, then[, else])')
819 def ifeq(context, mapping, args):
819 def ifeq(context, mapping, args):
820 """Conditionally execute based on
820 """Conditionally execute based on
821 whether 2 items are equivalent."""
821 whether 2 items are equivalent."""
822 if not (3 <= len(args) <= 4):
822 if not (3 <= len(args) <= 4):
823 # i18n: "ifeq" is a keyword
823 # i18n: "ifeq" is a keyword
824 raise error.ParseError(_("ifeq expects three or four arguments"))
824 raise error.ParseError(_("ifeq expects three or four arguments"))
825
825
826 test = evalstring(context, mapping, args[0])
826 test = evalstring(context, mapping, args[0])
827 match = evalstring(context, mapping, args[1])
827 match = evalstring(context, mapping, args[1])
828 if test == match:
828 if test == match:
829 yield evalrawexp(context, mapping, args[2])
829 yield evalrawexp(context, mapping, args[2])
830 elif len(args) == 4:
830 elif len(args) == 4:
831 yield evalrawexp(context, mapping, args[3])
831 yield evalrawexp(context, mapping, args[3])
832
832
833 @templatefunc('join(list, sep)')
833 @templatefunc('join(list, sep)')
834 def join(context, mapping, args):
834 def join(context, mapping, args):
835 """Join items in a list with a delimiter."""
835 """Join items in a list with a delimiter."""
836 if not (1 <= len(args) <= 2):
836 if not (1 <= len(args) <= 2):
837 # i18n: "join" is a keyword
837 # i18n: "join" is a keyword
838 raise error.ParseError(_("join expects one or two arguments"))
838 raise error.ParseError(_("join expects one or two arguments"))
839
839
840 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
840 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
841 # abuses generator as a keyword that returns a list of dicts.
841 # abuses generator as a keyword that returns a list of dicts.
842 joinset = evalrawexp(context, mapping, args[0])
842 joinset = evalrawexp(context, mapping, args[0])
843 joinset = templatekw.unwrapvalue(joinset)
843 joinset = templatekw.unwrapvalue(joinset)
844 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
844 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
845 joiner = " "
845 joiner = " "
846 if len(args) > 1:
846 if len(args) > 1:
847 joiner = evalstring(context, mapping, args[1])
847 joiner = evalstring(context, mapping, args[1])
848
848
849 first = True
849 first = True
850 for x in joinset:
850 for x in joinset:
851 if first:
851 if first:
852 first = False
852 first = False
853 else:
853 else:
854 yield joiner
854 yield joiner
855 yield joinfmt(x)
855 yield joinfmt(x)
856
856
857 @templatefunc('label(label, expr)')
857 @templatefunc('label(label, expr)')
858 def label(context, mapping, args):
858 def label(context, mapping, args):
859 """Apply a label to generated content. Content with
859 """Apply a label to generated content. Content with
860 a label applied can result in additional post-processing, such as
860 a label applied can result in additional post-processing, such as
861 automatic colorization."""
861 automatic colorization."""
862 if len(args) != 2:
862 if len(args) != 2:
863 # i18n: "label" is a keyword
863 # i18n: "label" is a keyword
864 raise error.ParseError(_("label expects two arguments"))
864 raise error.ParseError(_("label expects two arguments"))
865
865
866 ui = context.resource(mapping, 'ui')
866 ui = context.resource(mapping, 'ui')
867 thing = evalstring(context, mapping, args[1])
867 thing = evalstring(context, mapping, args[1])
868 # preserve unknown symbol as literal so effects like 'red', 'bold',
868 # preserve unknown symbol as literal so effects like 'red', 'bold',
869 # etc. don't need to be quoted
869 # etc. don't need to be quoted
870 label = evalstringliteral(context, mapping, args[0])
870 label = evalstringliteral(context, mapping, args[0])
871
871
872 return ui.label(thing, label)
872 return ui.label(thing, label)
873
873
874 @templatefunc('latesttag([pattern])')
874 @templatefunc('latesttag([pattern])')
875 def latesttag(context, mapping, args):
875 def latesttag(context, mapping, args):
876 """The global tags matching the given pattern on the
876 """The global tags matching the given pattern on the
877 most recent globally tagged ancestor of this changeset.
877 most recent globally tagged ancestor of this changeset.
878 If no such tags exist, the "{tag}" template resolves to
878 If no such tags exist, the "{tag}" template resolves to
879 the string "null"."""
879 the string "null"."""
880 if len(args) > 1:
880 if len(args) > 1:
881 # i18n: "latesttag" is a keyword
881 # i18n: "latesttag" is a keyword
882 raise error.ParseError(_("latesttag expects at most one argument"))
882 raise error.ParseError(_("latesttag expects at most one argument"))
883
883
884 pattern = None
884 pattern = None
885 if len(args) == 1:
885 if len(args) == 1:
886 pattern = evalstring(context, mapping, args[0])
886 pattern = evalstring(context, mapping, args[0])
887
887
888 # TODO: pass (context, mapping) pair to keyword function
888 # TODO: pass (context, mapping) pair to keyword function
889 props = context._resources.copy()
889 props = context._resources.copy()
890 props.update(mapping)
890 props.update(mapping)
891 return templatekw.showlatesttags(pattern, **pycompat.strkwargs(props))
891 return templatekw.showlatesttags(pattern, **pycompat.strkwargs(props))
892
892
893 @templatefunc('localdate(date[, tz])')
893 @templatefunc('localdate(date[, tz])')
894 def localdate(context, mapping, args):
894 def localdate(context, mapping, args):
895 """Converts a date to the specified timezone.
895 """Converts a date to the specified timezone.
896 The default is local date."""
896 The default is local date."""
897 if not (1 <= len(args) <= 2):
897 if not (1 <= len(args) <= 2):
898 # i18n: "localdate" is a keyword
898 # i18n: "localdate" is a keyword
899 raise error.ParseError(_("localdate expects one or two arguments"))
899 raise error.ParseError(_("localdate expects one or two arguments"))
900
900
901 date = evalfuncarg(context, mapping, args[0])
901 date = evalfuncarg(context, mapping, args[0])
902 try:
902 try:
903 date = util.parsedate(date)
903 date = util.parsedate(date)
904 except AttributeError: # not str nor date tuple
904 except AttributeError: # not str nor date tuple
905 # i18n: "localdate" is a keyword
905 # i18n: "localdate" is a keyword
906 raise error.ParseError(_("localdate expects a date information"))
906 raise error.ParseError(_("localdate expects a date information"))
907 if len(args) >= 2:
907 if len(args) >= 2:
908 tzoffset = None
908 tzoffset = None
909 tz = evalfuncarg(context, mapping, args[1])
909 tz = evalfuncarg(context, mapping, args[1])
910 if isinstance(tz, str):
910 if isinstance(tz, str):
911 tzoffset, remainder = util.parsetimezone(tz)
911 tzoffset, remainder = util.parsetimezone(tz)
912 if remainder:
912 if remainder:
913 tzoffset = None
913 tzoffset = None
914 if tzoffset is None:
914 if tzoffset is None:
915 try:
915 try:
916 tzoffset = int(tz)
916 tzoffset = int(tz)
917 except (TypeError, ValueError):
917 except (TypeError, ValueError):
918 # i18n: "localdate" is a keyword
918 # i18n: "localdate" is a keyword
919 raise error.ParseError(_("localdate expects a timezone"))
919 raise error.ParseError(_("localdate expects a timezone"))
920 else:
920 else:
921 tzoffset = util.makedate()[1]
921 tzoffset = util.makedate()[1]
922 return (date[0], tzoffset)
922 return (date[0], tzoffset)
923
923
924 @templatefunc('max(iterable)')
924 @templatefunc('max(iterable)')
925 def max_(context, mapping, args, **kwargs):
925 def max_(context, mapping, args, **kwargs):
926 """Return the max of an iterable"""
926 """Return the max of an iterable"""
927 if len(args) != 1:
927 if len(args) != 1:
928 # i18n: "max" is a keyword
928 # i18n: "max" is a keyword
929 raise error.ParseError(_("max expects one argument"))
929 raise error.ParseError(_("max expects one argument"))
930
930
931 iterable = evalfuncarg(context, mapping, args[0])
931 iterable = evalfuncarg(context, mapping, args[0])
932 try:
932 try:
933 x = max(iterable)
933 x = max(iterable)
934 except (TypeError, ValueError):
934 except (TypeError, ValueError):
935 # i18n: "max" is a keyword
935 # i18n: "max" is a keyword
936 raise error.ParseError(_("max first argument should be an iterable"))
936 raise error.ParseError(_("max first argument should be an iterable"))
937 return templatekw.wraphybridvalue(iterable, x, x)
937 return templatekw.wraphybridvalue(iterable, x, x)
938
938
939 @templatefunc('min(iterable)')
939 @templatefunc('min(iterable)')
940 def min_(context, mapping, args, **kwargs):
940 def min_(context, mapping, args, **kwargs):
941 """Return the min of an iterable"""
941 """Return the min of an iterable"""
942 if len(args) != 1:
942 if len(args) != 1:
943 # i18n: "min" is a keyword
943 # i18n: "min" is a keyword
944 raise error.ParseError(_("min expects one argument"))
944 raise error.ParseError(_("min expects one argument"))
945
945
946 iterable = evalfuncarg(context, mapping, args[0])
946 iterable = evalfuncarg(context, mapping, args[0])
947 try:
947 try:
948 x = min(iterable)
948 x = min(iterable)
949 except (TypeError, ValueError):
949 except (TypeError, ValueError):
950 # i18n: "min" is a keyword
950 # i18n: "min" is a keyword
951 raise error.ParseError(_("min first argument should be an iterable"))
951 raise error.ParseError(_("min first argument should be an iterable"))
952 return templatekw.wraphybridvalue(iterable, x, x)
952 return templatekw.wraphybridvalue(iterable, x, x)
953
953
954 @templatefunc('mod(a, b)')
954 @templatefunc('mod(a, b)')
955 def mod(context, mapping, args):
955 def mod(context, mapping, args):
956 """Calculate a mod b such that a / b + a mod b == a"""
956 """Calculate a mod b such that a / b + a mod b == a"""
957 if not len(args) == 2:
957 if not len(args) == 2:
958 # i18n: "mod" is a keyword
958 # i18n: "mod" is a keyword
959 raise error.ParseError(_("mod expects two arguments"))
959 raise error.ParseError(_("mod expects two arguments"))
960
960
961 func = lambda a, b: a % b
961 func = lambda a, b: a % b
962 return runarithmetic(context, mapping, (func, args[0], args[1]))
962 return runarithmetic(context, mapping, (func, args[0], args[1]))
963
963
964 @templatefunc('obsfateoperations(markers)')
964 @templatefunc('obsfateoperations(markers)')
965 def obsfateoperations(context, mapping, args):
965 def obsfateoperations(context, mapping, args):
966 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
966 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
967 if len(args) != 1:
967 if len(args) != 1:
968 # i18n: "obsfateoperations" is a keyword
968 # i18n: "obsfateoperations" is a keyword
969 raise error.ParseError(_("obsfateoperations expects one argument"))
969 raise error.ParseError(_("obsfateoperations expects one argument"))
970
970
971 markers = evalfuncarg(context, mapping, args[0])
971 markers = evalfuncarg(context, mapping, args[0])
972
972
973 try:
973 try:
974 data = obsutil.markersoperations(markers)
974 data = obsutil.markersoperations(markers)
975 return templatekw.hybridlist(data, name='operation')
975 return templatekw.hybridlist(data, name='operation')
976 except (TypeError, KeyError):
976 except (TypeError, KeyError):
977 # i18n: "obsfateoperations" is a keyword
977 # i18n: "obsfateoperations" is a keyword
978 errmsg = _("obsfateoperations first argument should be an iterable")
978 errmsg = _("obsfateoperations first argument should be an iterable")
979 raise error.ParseError(errmsg)
979 raise error.ParseError(errmsg)
980
980
981 @templatefunc('obsfatedate(markers)')
981 @templatefunc('obsfatedate(markers)')
982 def obsfatedate(context, mapping, args):
982 def obsfatedate(context, mapping, args):
983 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
983 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
984 if len(args) != 1:
984 if len(args) != 1:
985 # i18n: "obsfatedate" is a keyword
985 # i18n: "obsfatedate" is a keyword
986 raise error.ParseError(_("obsfatedate expects one argument"))
986 raise error.ParseError(_("obsfatedate expects one argument"))
987
987
988 markers = evalfuncarg(context, mapping, args[0])
988 markers = evalfuncarg(context, mapping, args[0])
989
989
990 try:
990 try:
991 data = obsutil.markersdates(markers)
991 data = obsutil.markersdates(markers)
992 return templatekw.hybridlist(data, name='date', fmt='%d %d')
992 return templatekw.hybridlist(data, name='date', fmt='%d %d')
993 except (TypeError, KeyError):
993 except (TypeError, KeyError):
994 # i18n: "obsfatedate" is a keyword
994 # i18n: "obsfatedate" is a keyword
995 errmsg = _("obsfatedate first argument should be an iterable")
995 errmsg = _("obsfatedate first argument should be an iterable")
996 raise error.ParseError(errmsg)
996 raise error.ParseError(errmsg)
997
997
998 @templatefunc('obsfateusers(markers)')
998 @templatefunc('obsfateusers(markers)')
999 def obsfateusers(context, mapping, args):
999 def obsfateusers(context, mapping, args):
1000 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
1000 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
1001 if len(args) != 1:
1001 if len(args) != 1:
1002 # i18n: "obsfateusers" is a keyword
1002 # i18n: "obsfateusers" is a keyword
1003 raise error.ParseError(_("obsfateusers expects one argument"))
1003 raise error.ParseError(_("obsfateusers expects one argument"))
1004
1004
1005 markers = evalfuncarg(context, mapping, args[0])
1005 markers = evalfuncarg(context, mapping, args[0])
1006
1006
1007 try:
1007 try:
1008 data = obsutil.markersusers(markers)
1008 data = obsutil.markersusers(markers)
1009 return templatekw.hybridlist(data, name='user')
1009 return templatekw.hybridlist(data, name='user')
1010 except (TypeError, KeyError, ValueError):
1010 except (TypeError, KeyError, ValueError):
1011 # i18n: "obsfateusers" is a keyword
1011 # i18n: "obsfateusers" is a keyword
1012 msg = _("obsfateusers first argument should be an iterable of "
1012 msg = _("obsfateusers first argument should be an iterable of "
1013 "obsmakers")
1013 "obsmakers")
1014 raise error.ParseError(msg)
1014 raise error.ParseError(msg)
1015
1015
1016 @templatefunc('obsfateverb(successors, markers)')
1016 @templatefunc('obsfateverb(successors, markers)')
1017 def obsfateverb(context, mapping, args):
1017 def obsfateverb(context, mapping, args):
1018 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
1018 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
1019 if len(args) != 2:
1019 if len(args) != 2:
1020 # i18n: "obsfateverb" is a keyword
1020 # i18n: "obsfateverb" is a keyword
1021 raise error.ParseError(_("obsfateverb expects two arguments"))
1021 raise error.ParseError(_("obsfateverb expects two arguments"))
1022
1022
1023 successors = evalfuncarg(context, mapping, args[0])
1023 successors = evalfuncarg(context, mapping, args[0])
1024 markers = evalfuncarg(context, mapping, args[1])
1024 markers = evalfuncarg(context, mapping, args[1])
1025
1025
1026 try:
1026 try:
1027 return obsutil.obsfateverb(successors, markers)
1027 return obsutil.obsfateverb(successors, markers)
1028 except TypeError:
1028 except TypeError:
1029 # i18n: "obsfateverb" is a keyword
1029 # i18n: "obsfateverb" is a keyword
1030 errmsg = _("obsfateverb first argument should be countable")
1030 errmsg = _("obsfateverb first argument should be countable")
1031 raise error.ParseError(errmsg)
1031 raise error.ParseError(errmsg)
1032
1032
1033 @templatefunc('relpath(path)')
1033 @templatefunc('relpath(path)')
1034 def relpath(context, mapping, args):
1034 def relpath(context, mapping, args):
1035 """Convert a repository-absolute path into a filesystem path relative to
1035 """Convert a repository-absolute path into a filesystem path relative to
1036 the current working directory."""
1036 the current working directory."""
1037 if len(args) != 1:
1037 if len(args) != 1:
1038 # i18n: "relpath" is a keyword
1038 # i18n: "relpath" is a keyword
1039 raise error.ParseError(_("relpath expects one argument"))
1039 raise error.ParseError(_("relpath expects one argument"))
1040
1040
1041 repo = context.resource(mapping, 'ctx').repo()
1041 repo = context.resource(mapping, 'ctx').repo()
1042 path = evalstring(context, mapping, args[0])
1042 path = evalstring(context, mapping, args[0])
1043 return repo.pathto(path)
1043 return repo.pathto(path)
1044
1044
1045 @templatefunc('revset(query[, formatargs...])')
1045 @templatefunc('revset(query[, formatargs...])')
1046 def revset(context, mapping, args):
1046 def revset(context, mapping, args):
1047 """Execute a revision set query. See
1047 """Execute a revision set query. See
1048 :hg:`help revset`."""
1048 :hg:`help revset`."""
1049 if not len(args) > 0:
1049 if not len(args) > 0:
1050 # i18n: "revset" is a keyword
1050 # i18n: "revset" is a keyword
1051 raise error.ParseError(_("revset expects one or more arguments"))
1051 raise error.ParseError(_("revset expects one or more arguments"))
1052
1052
1053 raw = evalstring(context, mapping, args[0])
1053 raw = evalstring(context, mapping, args[0])
1054 ctx = context.resource(mapping, 'ctx')
1054 ctx = context.resource(mapping, 'ctx')
1055 repo = ctx.repo()
1055 repo = ctx.repo()
1056
1056
1057 def query(expr):
1057 def query(expr):
1058 m = revsetmod.match(repo.ui, expr, repo=repo)
1058 m = revsetmod.match(repo.ui, expr, repo=repo)
1059 return m(repo)
1059 return m(repo)
1060
1060
1061 if len(args) > 1:
1061 if len(args) > 1:
1062 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
1062 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
1063 revs = query(revsetlang.formatspec(raw, *formatargs))
1063 revs = query(revsetlang.formatspec(raw, *formatargs))
1064 revs = list(revs)
1064 revs = list(revs)
1065 else:
1065 else:
1066 cache = context.resource(mapping, 'cache')
1066 cache = context.resource(mapping, 'cache')
1067 revsetcache = cache.setdefault("revsetcache", {})
1067 revsetcache = cache.setdefault("revsetcache", {})
1068 if raw in revsetcache:
1068 if raw in revsetcache:
1069 revs = revsetcache[raw]
1069 revs = revsetcache[raw]
1070 else:
1070 else:
1071 revs = query(raw)
1071 revs = query(raw)
1072 revs = list(revs)
1072 revs = list(revs)
1073 revsetcache[raw] = revs
1073 revsetcache[raw] = revs
1074
1074
1075 # TODO: pass (context, mapping) pair to keyword function
1075 # TODO: pass (context, mapping) pair to keyword function
1076 props = context._resources.copy()
1076 props = context._resources.copy()
1077 props.update(mapping)
1077 props.update(mapping)
1078 return templatekw.showrevslist("revision", revs,
1078 return templatekw.showrevslist("revision", revs,
1079 **pycompat.strkwargs(props))
1079 **pycompat.strkwargs(props))
1080
1080
1081 @templatefunc('rstdoc(text, style)')
1081 @templatefunc('rstdoc(text, style)')
1082 def rstdoc(context, mapping, args):
1082 def rstdoc(context, mapping, args):
1083 """Format reStructuredText."""
1083 """Format reStructuredText."""
1084 if len(args) != 2:
1084 if len(args) != 2:
1085 # i18n: "rstdoc" is a keyword
1085 # i18n: "rstdoc" is a keyword
1086 raise error.ParseError(_("rstdoc expects two arguments"))
1086 raise error.ParseError(_("rstdoc expects two arguments"))
1087
1087
1088 text = evalstring(context, mapping, args[0])
1088 text = evalstring(context, mapping, args[0])
1089 style = evalstring(context, mapping, args[1])
1089 style = evalstring(context, mapping, args[1])
1090
1090
1091 return minirst.format(text, style=style, keep=['verbose'])
1091 return minirst.format(text, style=style, keep=['verbose'])
1092
1092
1093 @templatefunc('separate(sep, args)', argspec='sep *args')
1093 @templatefunc('separate(sep, args)', argspec='sep *args')
1094 def separate(context, mapping, args):
1094 def separate(context, mapping, args):
1095 """Add a separator between non-empty arguments."""
1095 """Add a separator between non-empty arguments."""
1096 if 'sep' not in args:
1096 if 'sep' not in args:
1097 # i18n: "separate" is a keyword
1097 # i18n: "separate" is a keyword
1098 raise error.ParseError(_("separate expects at least one argument"))
1098 raise error.ParseError(_("separate expects at least one argument"))
1099
1099
1100 sep = evalstring(context, mapping, args['sep'])
1100 sep = evalstring(context, mapping, args['sep'])
1101 first = True
1101 first = True
1102 for arg in args['args']:
1102 for arg in args['args']:
1103 argstr = evalstring(context, mapping, arg)
1103 argstr = evalstring(context, mapping, arg)
1104 if not argstr:
1104 if not argstr:
1105 continue
1105 continue
1106 if first:
1106 if first:
1107 first = False
1107 first = False
1108 else:
1108 else:
1109 yield sep
1109 yield sep
1110 yield argstr
1110 yield argstr
1111
1111
1112 @templatefunc('shortest(node, minlength=4)')
1112 @templatefunc('shortest(node, minlength=4)')
1113 def shortest(context, mapping, args):
1113 def shortest(context, mapping, args):
1114 """Obtain the shortest representation of
1114 """Obtain the shortest representation of
1115 a node."""
1115 a node."""
1116 if not (1 <= len(args) <= 2):
1116 if not (1 <= len(args) <= 2):
1117 # i18n: "shortest" is a keyword
1117 # i18n: "shortest" is a keyword
1118 raise error.ParseError(_("shortest() expects one or two arguments"))
1118 raise error.ParseError(_("shortest() expects one or two arguments"))
1119
1119
1120 node = evalstring(context, mapping, args[0])
1120 node = evalstring(context, mapping, args[0])
1121
1121
1122 minlength = 4
1122 minlength = 4
1123 if len(args) > 1:
1123 if len(args) > 1:
1124 minlength = evalinteger(context, mapping, args[1],
1124 minlength = evalinteger(context, mapping, args[1],
1125 # i18n: "shortest" is a keyword
1125 # i18n: "shortest" is a keyword
1126 _("shortest() expects an integer minlength"))
1126 _("shortest() expects an integer minlength"))
1127
1127
1128 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1128 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1129 # which would be unacceptably slow. so we look for hash collision in
1129 # which would be unacceptably slow. so we look for hash collision in
1130 # unfiltered space, which means some hashes may be slightly longer.
1130 # unfiltered space, which means some hashes may be slightly longer.
1131 cl = context.resource(mapping, 'ctx')._repo.unfiltered().changelog
1131 cl = context.resource(mapping, 'ctx')._repo.unfiltered().changelog
1132 return cl.shortest(node, minlength)
1132 return cl.shortest(node, minlength)
1133
1133
1134 @templatefunc('strip(text[, chars])')
1134 @templatefunc('strip(text[, chars])')
1135 def strip(context, mapping, args):
1135 def strip(context, mapping, args):
1136 """Strip characters from a string. By default,
1136 """Strip characters from a string. By default,
1137 strips all leading and trailing whitespace."""
1137 strips all leading and trailing whitespace."""
1138 if not (1 <= len(args) <= 2):
1138 if not (1 <= len(args) <= 2):
1139 # i18n: "strip" is a keyword
1139 # i18n: "strip" is a keyword
1140 raise error.ParseError(_("strip expects one or two arguments"))
1140 raise error.ParseError(_("strip expects one or two arguments"))
1141
1141
1142 text = evalstring(context, mapping, args[0])
1142 text = evalstring(context, mapping, args[0])
1143 if len(args) == 2:
1143 if len(args) == 2:
1144 chars = evalstring(context, mapping, args[1])
1144 chars = evalstring(context, mapping, args[1])
1145 return text.strip(chars)
1145 return text.strip(chars)
1146 return text.strip()
1146 return text.strip()
1147
1147
1148 @templatefunc('sub(pattern, replacement, expression)')
1148 @templatefunc('sub(pattern, replacement, expression)')
1149 def sub(context, mapping, args):
1149 def sub(context, mapping, args):
1150 """Perform text substitution
1150 """Perform text substitution
1151 using regular expressions."""
1151 using regular expressions."""
1152 if len(args) != 3:
1152 if len(args) != 3:
1153 # i18n: "sub" is a keyword
1153 # i18n: "sub" is a keyword
1154 raise error.ParseError(_("sub expects three arguments"))
1154 raise error.ParseError(_("sub expects three arguments"))
1155
1155
1156 pat = evalstring(context, mapping, args[0])
1156 pat = evalstring(context, mapping, args[0])
1157 rpl = evalstring(context, mapping, args[1])
1157 rpl = evalstring(context, mapping, args[1])
1158 src = evalstring(context, mapping, args[2])
1158 src = evalstring(context, mapping, args[2])
1159 try:
1159 try:
1160 patre = re.compile(pat)
1160 patre = re.compile(pat)
1161 except re.error:
1161 except re.error:
1162 # i18n: "sub" is a keyword
1162 # i18n: "sub" is a keyword
1163 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1163 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1164 try:
1164 try:
1165 yield patre.sub(rpl, src)
1165 yield patre.sub(rpl, src)
1166 except re.error:
1166 except re.error:
1167 # i18n: "sub" is a keyword
1167 # i18n: "sub" is a keyword
1168 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1168 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1169
1169
1170 @templatefunc('startswith(pattern, text)')
1170 @templatefunc('startswith(pattern, text)')
1171 def startswith(context, mapping, args):
1171 def startswith(context, mapping, args):
1172 """Returns the value from the "text" argument
1172 """Returns the value from the "text" argument
1173 if it begins with the content from the "pattern" argument."""
1173 if it begins with the content from the "pattern" argument."""
1174 if len(args) != 2:
1174 if len(args) != 2:
1175 # i18n: "startswith" is a keyword
1175 # i18n: "startswith" is a keyword
1176 raise error.ParseError(_("startswith expects two arguments"))
1176 raise error.ParseError(_("startswith expects two arguments"))
1177
1177
1178 patn = evalstring(context, mapping, args[0])
1178 patn = evalstring(context, mapping, args[0])
1179 text = evalstring(context, mapping, args[1])
1179 text = evalstring(context, mapping, args[1])
1180 if text.startswith(patn):
1180 if text.startswith(patn):
1181 return text
1181 return text
1182 return ''
1182 return ''
1183
1183
1184 @templatefunc('word(number, text[, separator])')
1184 @templatefunc('word(number, text[, separator])')
1185 def word(context, mapping, args):
1185 def word(context, mapping, args):
1186 """Return the nth word from a string."""
1186 """Return the nth word from a string."""
1187 if not (2 <= len(args) <= 3):
1187 if not (2 <= len(args) <= 3):
1188 # i18n: "word" is a keyword
1188 # i18n: "word" is a keyword
1189 raise error.ParseError(_("word expects two or three arguments, got %d")
1189 raise error.ParseError(_("word expects two or three arguments, got %d")
1190 % len(args))
1190 % len(args))
1191
1191
1192 num = evalinteger(context, mapping, args[0],
1192 num = evalinteger(context, mapping, args[0],
1193 # i18n: "word" is a keyword
1193 # i18n: "word" is a keyword
1194 _("word expects an integer index"))
1194 _("word expects an integer index"))
1195 text = evalstring(context, mapping, args[1])
1195 text = evalstring(context, mapping, args[1])
1196 if len(args) == 3:
1196 if len(args) == 3:
1197 splitter = evalstring(context, mapping, args[2])
1197 splitter = evalstring(context, mapping, args[2])
1198 else:
1198 else:
1199 splitter = None
1199 splitter = None
1200
1200
1201 tokens = text.split(splitter)
1201 tokens = text.split(splitter)
1202 if num >= len(tokens) or num < -len(tokens):
1202 if num >= len(tokens) or num < -len(tokens):
1203 return ''
1203 return ''
1204 else:
1204 else:
1205 return tokens[num]
1205 return tokens[num]
1206
1206
1207 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1207 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1208 exprmethods = {
1208 exprmethods = {
1209 "integer": lambda e, c: (runinteger, e[1]),
1209 "integer": lambda e, c: (runinteger, e[1]),
1210 "string": lambda e, c: (runstring, e[1]),
1210 "string": lambda e, c: (runstring, e[1]),
1211 "symbol": lambda e, c: (runsymbol, e[1]),
1211 "symbol": lambda e, c: (runsymbol, e[1]),
1212 "template": buildtemplate,
1212 "template": buildtemplate,
1213 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1213 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1214 ".": buildmember,
1214 ".": buildmember,
1215 "|": buildfilter,
1215 "|": buildfilter,
1216 "%": buildmap,
1216 "%": buildmap,
1217 "func": buildfunc,
1217 "func": buildfunc,
1218 "keyvalue": buildkeyvaluepair,
1218 "keyvalue": buildkeyvaluepair,
1219 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1219 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1220 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1220 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1221 "negate": buildnegate,
1221 "negate": buildnegate,
1222 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1222 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1223 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1223 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1224 }
1224 }
1225
1225
1226 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1226 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1227 methods = exprmethods.copy()
1227 methods = exprmethods.copy()
1228 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1228 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1229
1229
1230 class _aliasrules(parser.basealiasrules):
1230 class _aliasrules(parser.basealiasrules):
1231 """Parsing and expansion rule set of template aliases"""
1231 """Parsing and expansion rule set of template aliases"""
1232 _section = _('template alias')
1232 _section = _('template alias')
1233 _parse = staticmethod(_parseexpr)
1233 _parse = staticmethod(_parseexpr)
1234
1234
1235 @staticmethod
1235 @staticmethod
1236 def _trygetfunc(tree):
1236 def _trygetfunc(tree):
1237 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1237 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1238 None"""
1238 None"""
1239 if tree[0] == 'func' and tree[1][0] == 'symbol':
1239 if tree[0] == 'func' and tree[1][0] == 'symbol':
1240 return tree[1][1], getlist(tree[2])
1240 return tree[1][1], getlist(tree[2])
1241 if tree[0] == '|' and tree[2][0] == 'symbol':
1241 if tree[0] == '|' and tree[2][0] == 'symbol':
1242 return tree[2][1], [tree[1]]
1242 return tree[2][1], [tree[1]]
1243
1243
1244 def expandaliases(tree, aliases):
1244 def expandaliases(tree, aliases):
1245 """Return new tree of aliases are expanded"""
1245 """Return new tree of aliases are expanded"""
1246 aliasmap = _aliasrules.buildmap(aliases)
1246 aliasmap = _aliasrules.buildmap(aliases)
1247 return _aliasrules.expand(aliasmap, tree)
1247 return _aliasrules.expand(aliasmap, tree)
1248
1248
1249 # template engine
1249 # template engine
1250
1250
1251 stringify = templatefilters.stringify
1251 stringify = templatefilters.stringify
1252
1252
1253 def _flatten(thing):
1253 def _flatten(thing):
1254 '''yield a single stream from a possibly nested set of iterators'''
1254 '''yield a single stream from a possibly nested set of iterators'''
1255 thing = templatekw.unwraphybrid(thing)
1255 thing = templatekw.unwraphybrid(thing)
1256 if isinstance(thing, bytes):
1256 if isinstance(thing, bytes):
1257 yield thing
1257 yield thing
1258 elif isinstance(thing, str):
1258 elif isinstance(thing, str):
1259 # We can only hit this on Python 3, and it's here to guard
1259 # We can only hit this on Python 3, and it's here to guard
1260 # against infinite recursion.
1260 # against infinite recursion.
1261 raise error.ProgrammingError('Mercurial IO including templates is done'
1261 raise error.ProgrammingError('Mercurial IO including templates is done'
1262 ' with bytes, not strings')
1262 ' with bytes, not strings')
1263 elif thing is None:
1263 elif thing is None:
1264 pass
1264 pass
1265 elif not util.safehasattr(thing, '__iter__'):
1265 elif not util.safehasattr(thing, '__iter__'):
1266 yield pycompat.bytestr(thing)
1266 yield pycompat.bytestr(thing)
1267 else:
1267 else:
1268 for i in thing:
1268 for i in thing:
1269 i = templatekw.unwraphybrid(i)
1269 i = templatekw.unwraphybrid(i)
1270 if isinstance(i, bytes):
1270 if isinstance(i, bytes):
1271 yield i
1271 yield i
1272 elif i is None:
1272 elif i is None:
1273 pass
1273 pass
1274 elif not util.safehasattr(i, '__iter__'):
1274 elif not util.safehasattr(i, '__iter__'):
1275 yield pycompat.bytestr(i)
1275 yield pycompat.bytestr(i)
1276 else:
1276 else:
1277 for j in _flatten(i):
1277 for j in _flatten(i):
1278 yield j
1278 yield j
1279
1279
1280 def unquotestring(s):
1280 def unquotestring(s):
1281 '''unwrap quotes if any; otherwise returns unmodified string'''
1281 '''unwrap quotes if any; otherwise returns unmodified string'''
1282 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1282 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1283 return s
1283 return s
1284 return s[1:-1]
1284 return s[1:-1]
1285
1285
1286 class engine(object):
1286 class engine(object):
1287 '''template expansion engine.
1287 '''template expansion engine.
1288
1288
1289 template expansion works like this. a map file contains key=value
1289 template expansion works like this. a map file contains key=value
1290 pairs. if value is quoted, it is treated as string. otherwise, it
1290 pairs. if value is quoted, it is treated as string. otherwise, it
1291 is treated as name of template file.
1291 is treated as name of template file.
1292
1292
1293 templater is asked to expand a key in map. it looks up key, and
1293 templater is asked to expand a key in map. it looks up key, and
1294 looks for strings like this: {foo}. it expands {foo} by looking up
1294 looks for strings like this: {foo}. it expands {foo} by looking up
1295 foo in map, and substituting it. expansion is recursive: it stops
1295 foo in map, and substituting it. expansion is recursive: it stops
1296 when there is no more {foo} to replace.
1296 when there is no more {foo} to replace.
1297
1297
1298 expansion also allows formatting and filtering.
1298 expansion also allows formatting and filtering.
1299
1299
1300 format uses key to expand each item in list. syntax is
1300 format uses key to expand each item in list. syntax is
1301 {key%format}.
1301 {key%format}.
1302
1302
1303 filter uses function to transform value. syntax is
1303 filter uses function to transform value. syntax is
1304 {key|filter1|filter2|...}.'''
1304 {key|filter1|filter2|...}.'''
1305
1305
1306 def __init__(self, loader, filters=None, defaults=None, resources=None,
1306 def __init__(self, loader, filters=None, defaults=None, resources=None,
1307 aliases=()):
1307 aliases=()):
1308 self._loader = loader
1308 self._loader = loader
1309 if filters is None:
1309 if filters is None:
1310 filters = {}
1310 filters = {}
1311 self._filters = filters
1311 self._filters = filters
1312 if defaults is None:
1312 if defaults is None:
1313 defaults = {}
1313 defaults = {}
1314 if resources is None:
1314 if resources is None:
1315 resources = {}
1315 resources = {}
1316 self._defaults = defaults
1316 self._defaults = defaults
1317 self._resources = resources
1317 self._resources = resources
1318 self._aliasmap = _aliasrules.buildmap(aliases)
1318 self._aliasmap = _aliasrules.buildmap(aliases)
1319 self._cache = {} # key: (func, data)
1319 self._cache = {} # key: (func, data)
1320
1320
1321 def symbol(self, mapping, key):
1321 def symbol(self, mapping, key):
1322 """Resolve symbol to value or function; None if nothing found"""
1322 """Resolve symbol to value or function; None if nothing found"""
1323 v = mapping.get(key)
1323 v = None
1324 if key not in self._resources:
1325 v = mapping.get(key)
1324 if v is None:
1326 if v is None:
1325 v = self._defaults.get(key)
1327 v = self._defaults.get(key)
1326 return v
1328 return v
1327
1329
1328 def resource(self, mapping, key):
1330 def resource(self, mapping, key):
1329 """Return internal data (e.g. cache) used for keyword/function
1331 """Return internal data (e.g. cache) used for keyword/function
1330 evaluation"""
1332 evaluation"""
1331 v = mapping.get(key)
1333 v = None
1334 if key in self._resources:
1335 v = mapping.get(key)
1332 if v is None:
1336 if v is None:
1333 v = self._resources.get(key)
1337 v = self._resources.get(key)
1334 if v is None:
1338 if v is None:
1335 raise KeyError
1339 raise error.Abort(_('template resource not available: %s') % key)
1336 return v
1340 return v
1337
1341
1338 def _load(self, t):
1342 def _load(self, t):
1339 '''load, parse, and cache a template'''
1343 '''load, parse, and cache a template'''
1340 if t not in self._cache:
1344 if t not in self._cache:
1341 # put poison to cut recursion while compiling 't'
1345 # put poison to cut recursion while compiling 't'
1342 self._cache[t] = (_runrecursivesymbol, t)
1346 self._cache[t] = (_runrecursivesymbol, t)
1343 try:
1347 try:
1344 x = parse(self._loader(t))
1348 x = parse(self._loader(t))
1345 if self._aliasmap:
1349 if self._aliasmap:
1346 x = _aliasrules.expand(self._aliasmap, x)
1350 x = _aliasrules.expand(self._aliasmap, x)
1347 self._cache[t] = compileexp(x, self, methods)
1351 self._cache[t] = compileexp(x, self, methods)
1348 except: # re-raises
1352 except: # re-raises
1349 del self._cache[t]
1353 del self._cache[t]
1350 raise
1354 raise
1351 return self._cache[t]
1355 return self._cache[t]
1352
1356
1353 def process(self, t, mapping):
1357 def process(self, t, mapping):
1354 '''Perform expansion. t is name of map element to expand.
1358 '''Perform expansion. t is name of map element to expand.
1355 mapping contains added elements for use during expansion. Is a
1359 mapping contains added elements for use during expansion. Is a
1356 generator.'''
1360 generator.'''
1357 func, data = self._load(t)
1361 func, data = self._load(t)
1358 return _flatten(func(self, mapping, data))
1362 return _flatten(func(self, mapping, data))
1359
1363
1360 engines = {'default': engine}
1364 engines = {'default': engine}
1361
1365
1362 def stylelist():
1366 def stylelist():
1363 paths = templatepaths()
1367 paths = templatepaths()
1364 if not paths:
1368 if not paths:
1365 return _('no templates found, try `hg debuginstall` for more info')
1369 return _('no templates found, try `hg debuginstall` for more info')
1366 dirlist = os.listdir(paths[0])
1370 dirlist = os.listdir(paths[0])
1367 stylelist = []
1371 stylelist = []
1368 for file in dirlist:
1372 for file in dirlist:
1369 split = file.split(".")
1373 split = file.split(".")
1370 if split[-1] in ('orig', 'rej'):
1374 if split[-1] in ('orig', 'rej'):
1371 continue
1375 continue
1372 if split[0] == "map-cmdline":
1376 if split[0] == "map-cmdline":
1373 stylelist.append(split[1])
1377 stylelist.append(split[1])
1374 return ", ".join(sorted(stylelist))
1378 return ", ".join(sorted(stylelist))
1375
1379
1376 def _readmapfile(mapfile):
1380 def _readmapfile(mapfile):
1377 """Load template elements from the given map file"""
1381 """Load template elements from the given map file"""
1378 if not os.path.exists(mapfile):
1382 if not os.path.exists(mapfile):
1379 raise error.Abort(_("style '%s' not found") % mapfile,
1383 raise error.Abort(_("style '%s' not found") % mapfile,
1380 hint=_("available styles: %s") % stylelist())
1384 hint=_("available styles: %s") % stylelist())
1381
1385
1382 base = os.path.dirname(mapfile)
1386 base = os.path.dirname(mapfile)
1383 conf = config.config(includepaths=templatepaths())
1387 conf = config.config(includepaths=templatepaths())
1384 conf.read(mapfile, remap={'': 'templates'})
1388 conf.read(mapfile, remap={'': 'templates'})
1385
1389
1386 cache = {}
1390 cache = {}
1387 tmap = {}
1391 tmap = {}
1388 aliases = []
1392 aliases = []
1389
1393
1390 val = conf.get('templates', '__base__')
1394 val = conf.get('templates', '__base__')
1391 if val and val[0] not in "'\"":
1395 if val and val[0] not in "'\"":
1392 # treat as a pointer to a base class for this style
1396 # treat as a pointer to a base class for this style
1393 path = util.normpath(os.path.join(base, val))
1397 path = util.normpath(os.path.join(base, val))
1394
1398
1395 # fallback check in template paths
1399 # fallback check in template paths
1396 if not os.path.exists(path):
1400 if not os.path.exists(path):
1397 for p in templatepaths():
1401 for p in templatepaths():
1398 p2 = util.normpath(os.path.join(p, val))
1402 p2 = util.normpath(os.path.join(p, val))
1399 if os.path.isfile(p2):
1403 if os.path.isfile(p2):
1400 path = p2
1404 path = p2
1401 break
1405 break
1402 p3 = util.normpath(os.path.join(p2, "map"))
1406 p3 = util.normpath(os.path.join(p2, "map"))
1403 if os.path.isfile(p3):
1407 if os.path.isfile(p3):
1404 path = p3
1408 path = p3
1405 break
1409 break
1406
1410
1407 cache, tmap, aliases = _readmapfile(path)
1411 cache, tmap, aliases = _readmapfile(path)
1408
1412
1409 for key, val in conf['templates'].items():
1413 for key, val in conf['templates'].items():
1410 if not val:
1414 if not val:
1411 raise error.ParseError(_('missing value'),
1415 raise error.ParseError(_('missing value'),
1412 conf.source('templates', key))
1416 conf.source('templates', key))
1413 if val[0] in "'\"":
1417 if val[0] in "'\"":
1414 if val[0] != val[-1]:
1418 if val[0] != val[-1]:
1415 raise error.ParseError(_('unmatched quotes'),
1419 raise error.ParseError(_('unmatched quotes'),
1416 conf.source('templates', key))
1420 conf.source('templates', key))
1417 cache[key] = unquotestring(val)
1421 cache[key] = unquotestring(val)
1418 elif key != '__base__':
1422 elif key != '__base__':
1419 val = 'default', val
1423 val = 'default', val
1420 if ':' in val[1]:
1424 if ':' in val[1]:
1421 val = val[1].split(':', 1)
1425 val = val[1].split(':', 1)
1422 tmap[key] = val[0], os.path.join(base, val[1])
1426 tmap[key] = val[0], os.path.join(base, val[1])
1423 aliases.extend(conf['templatealias'].items())
1427 aliases.extend(conf['templatealias'].items())
1424 return cache, tmap, aliases
1428 return cache, tmap, aliases
1425
1429
1426 class TemplateNotFound(error.Abort):
1430 class TemplateNotFound(error.Abort):
1427 pass
1431 pass
1428
1432
1429 class templater(object):
1433 class templater(object):
1430
1434
1431 def __init__(self, filters=None, defaults=None, resources=None,
1435 def __init__(self, filters=None, defaults=None, resources=None,
1432 cache=None, aliases=(), minchunk=1024, maxchunk=65536):
1436 cache=None, aliases=(), minchunk=1024, maxchunk=65536):
1433 '''set up template engine.
1437 '''set up template engine.
1434 filters is dict of functions. each transforms a value into another.
1438 filters is dict of functions. each transforms a value into another.
1435 defaults is dict of default map definitions.
1439 defaults is dict of default map definitions.
1436 resources is dict of internal data (e.g. cache), which are inaccessible
1440 resources is dict of internal data (e.g. cache), which are inaccessible
1437 from user template.
1441 from user template.
1438 aliases is list of alias (name, replacement) pairs.
1442 aliases is list of alias (name, replacement) pairs.
1439 '''
1443 '''
1440 if filters is None:
1444 if filters is None:
1441 filters = {}
1445 filters = {}
1442 if defaults is None:
1446 if defaults is None:
1443 defaults = {}
1447 defaults = {}
1444 if resources is None:
1448 if resources is None:
1445 resources = {}
1449 resources = {}
1446 if cache is None:
1450 if cache is None:
1447 cache = {}
1451 cache = {}
1448 self.cache = cache.copy()
1452 self.cache = cache.copy()
1449 self.map = {}
1453 self.map = {}
1450 self.filters = templatefilters.filters.copy()
1454 self.filters = templatefilters.filters.copy()
1451 self.filters.update(filters)
1455 self.filters.update(filters)
1452 self.defaults = defaults
1456 self.defaults = defaults
1453 self._resources = {'templ': self}
1457 self._resources = {'templ': self}
1454 self._resources.update(resources)
1458 self._resources.update(resources)
1455 self._aliases = aliases
1459 self._aliases = aliases
1456 self.minchunk, self.maxchunk = minchunk, maxchunk
1460 self.minchunk, self.maxchunk = minchunk, maxchunk
1457 self.ecache = {}
1461 self.ecache = {}
1458
1462
1459 @classmethod
1463 @classmethod
1460 def frommapfile(cls, mapfile, filters=None, defaults=None, resources=None,
1464 def frommapfile(cls, mapfile, filters=None, defaults=None, resources=None,
1461 cache=None, minchunk=1024, maxchunk=65536):
1465 cache=None, minchunk=1024, maxchunk=65536):
1462 """Create templater from the specified map file"""
1466 """Create templater from the specified map file"""
1463 t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk)
1467 t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk)
1464 cache, tmap, aliases = _readmapfile(mapfile)
1468 cache, tmap, aliases = _readmapfile(mapfile)
1465 t.cache.update(cache)
1469 t.cache.update(cache)
1466 t.map = tmap
1470 t.map = tmap
1467 t._aliases = aliases
1471 t._aliases = aliases
1468 return t
1472 return t
1469
1473
1470 def __contains__(self, key):
1474 def __contains__(self, key):
1471 return key in self.cache or key in self.map
1475 return key in self.cache or key in self.map
1472
1476
1473 def load(self, t):
1477 def load(self, t):
1474 '''Get the template for the given template name. Use a local cache.'''
1478 '''Get the template for the given template name. Use a local cache.'''
1475 if t not in self.cache:
1479 if t not in self.cache:
1476 try:
1480 try:
1477 self.cache[t] = util.readfile(self.map[t][1])
1481 self.cache[t] = util.readfile(self.map[t][1])
1478 except KeyError as inst:
1482 except KeyError as inst:
1479 raise TemplateNotFound(_('"%s" not in template map') %
1483 raise TemplateNotFound(_('"%s" not in template map') %
1480 inst.args[0])
1484 inst.args[0])
1481 except IOError as inst:
1485 except IOError as inst:
1482 raise IOError(inst.args[0], _('template file %s: %s') %
1486 raise IOError(inst.args[0], _('template file %s: %s') %
1483 (self.map[t][1], inst.args[1]))
1487 (self.map[t][1], inst.args[1]))
1484 return self.cache[t]
1488 return self.cache[t]
1485
1489
1486 def render(self, mapping):
1490 def render(self, mapping):
1487 """Render the default unnamed template and return result as string"""
1491 """Render the default unnamed template and return result as string"""
1488 mapping = pycompat.strkwargs(mapping)
1492 mapping = pycompat.strkwargs(mapping)
1489 return stringify(self('', **mapping))
1493 return stringify(self('', **mapping))
1490
1494
1491 def __call__(self, t, **mapping):
1495 def __call__(self, t, **mapping):
1492 mapping = pycompat.byteskwargs(mapping)
1496 mapping = pycompat.byteskwargs(mapping)
1493 ttype = t in self.map and self.map[t][0] or 'default'
1497 ttype = t in self.map and self.map[t][0] or 'default'
1494 if ttype not in self.ecache:
1498 if ttype not in self.ecache:
1495 try:
1499 try:
1496 ecls = engines[ttype]
1500 ecls = engines[ttype]
1497 except KeyError:
1501 except KeyError:
1498 raise error.Abort(_('invalid template engine: %s') % ttype)
1502 raise error.Abort(_('invalid template engine: %s') % ttype)
1499 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1503 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1500 self._resources, self._aliases)
1504 self._resources, self._aliases)
1501 proc = self.ecache[ttype]
1505 proc = self.ecache[ttype]
1502
1506
1503 stream = proc.process(t, mapping)
1507 stream = proc.process(t, mapping)
1504 if self.minchunk:
1508 if self.minchunk:
1505 stream = util.increasingchunks(stream, min=self.minchunk,
1509 stream = util.increasingchunks(stream, min=self.minchunk,
1506 max=self.maxchunk)
1510 max=self.maxchunk)
1507 return stream
1511 return stream
1508
1512
1509 def templatepaths():
1513 def templatepaths():
1510 '''return locations used for template files.'''
1514 '''return locations used for template files.'''
1511 pathsrel = ['templates']
1515 pathsrel = ['templates']
1512 paths = [os.path.normpath(os.path.join(util.datapath, f))
1516 paths = [os.path.normpath(os.path.join(util.datapath, f))
1513 for f in pathsrel]
1517 for f in pathsrel]
1514 return [p for p in paths if os.path.isdir(p)]
1518 return [p for p in paths if os.path.isdir(p)]
1515
1519
1516 def templatepath(name):
1520 def templatepath(name):
1517 '''return location of template file. returns None if not found.'''
1521 '''return location of template file. returns None if not found.'''
1518 for p in templatepaths():
1522 for p in templatepaths():
1519 f = os.path.join(p, name)
1523 f = os.path.join(p, name)
1520 if os.path.exists(f):
1524 if os.path.exists(f):
1521 return f
1525 return f
1522 return None
1526 return None
1523
1527
1524 def stylemap(styles, paths=None):
1528 def stylemap(styles, paths=None):
1525 """Return path to mapfile for a given style.
1529 """Return path to mapfile for a given style.
1526
1530
1527 Searches mapfile in the following locations:
1531 Searches mapfile in the following locations:
1528 1. templatepath/style/map
1532 1. templatepath/style/map
1529 2. templatepath/map-style
1533 2. templatepath/map-style
1530 3. templatepath/map
1534 3. templatepath/map
1531 """
1535 """
1532
1536
1533 if paths is None:
1537 if paths is None:
1534 paths = templatepaths()
1538 paths = templatepaths()
1535 elif isinstance(paths, str):
1539 elif isinstance(paths, str):
1536 paths = [paths]
1540 paths = [paths]
1537
1541
1538 if isinstance(styles, str):
1542 if isinstance(styles, str):
1539 styles = [styles]
1543 styles = [styles]
1540
1544
1541 for style in styles:
1545 for style in styles:
1542 # only plain name is allowed to honor template paths
1546 # only plain name is allowed to honor template paths
1543 if (not style
1547 if (not style
1544 or style in (os.curdir, os.pardir)
1548 or style in (os.curdir, os.pardir)
1545 or pycompat.ossep in style
1549 or pycompat.ossep in style
1546 or pycompat.osaltsep and pycompat.osaltsep in style):
1550 or pycompat.osaltsep and pycompat.osaltsep in style):
1547 continue
1551 continue
1548 locations = [os.path.join(style, 'map'), 'map-' + style]
1552 locations = [os.path.join(style, 'map'), 'map-' + style]
1549 locations.append('map')
1553 locations.append('map')
1550
1554
1551 for path in paths:
1555 for path in paths:
1552 for location in locations:
1556 for location in locations:
1553 mapfile = os.path.join(path, location)
1557 mapfile = os.path.join(path, location)
1554 if os.path.isfile(mapfile):
1558 if os.path.isfile(mapfile):
1555 return style, mapfile
1559 return style, mapfile
1556
1560
1557 raise RuntimeError("No hgweb templates found in %r" % paths)
1561 raise RuntimeError("No hgweb templates found in %r" % paths)
1558
1562
1559 def loadfunction(ui, extname, registrarobj):
1563 def loadfunction(ui, extname, registrarobj):
1560 """Load template function from specified registrarobj
1564 """Load template function from specified registrarobj
1561 """
1565 """
1562 for name, func in registrarobj._table.iteritems():
1566 for name, func in registrarobj._table.iteritems():
1563 funcs[name] = func
1567 funcs[name] = func
1564
1568
1565 # tell hggettext to extract docstrings from these functions:
1569 # tell hggettext to extract docstrings from these functions:
1566 i18nfunctions = funcs.values()
1570 i18nfunctions = funcs.values()
@@ -1,4711 +1,4717 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add a
4 $ hg add a
5 $ echo line 1 > b
5 $ echo line 1 > b
6 $ echo line 2 >> b
6 $ echo line 2 >> b
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8
8
9 $ hg add b
9 $ hg add b
10 $ echo other 1 > c
10 $ echo other 1 > c
11 $ echo other 2 >> c
11 $ echo other 2 >> c
12 $ echo >> c
12 $ echo >> c
13 $ echo other 3 >> c
13 $ echo other 3 >> c
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15
15
16 $ hg add c
16 $ hg add c
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 $ echo c >> c
18 $ echo c >> c
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20
20
21 $ echo foo > .hg/branch
21 $ echo foo > .hg/branch
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23
23
24 $ hg co -q 3
24 $ hg co -q 3
25 $ echo other 4 >> d
25 $ echo other 4 >> d
26 $ hg add d
26 $ hg add d
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28
28
29 $ hg merge -q foo
29 $ hg merge -q foo
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31
31
32 Test arithmetic operators have the right precedence:
32 Test arithmetic operators have the right precedence:
33
33
34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
35 2020 1964
35 2020 1964
36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
37 9860 5908
37 9860 5908
38
38
39 Test division:
39 Test division:
40
40
41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
42 (template
42 (template
43 (/
43 (/
44 (integer '5')
44 (integer '5')
45 (integer '2'))
45 (integer '2'))
46 (string ' ')
46 (string ' ')
47 (func
47 (func
48 (symbol 'mod')
48 (symbol 'mod')
49 (list
49 (list
50 (integer '5')
50 (integer '5')
51 (integer '2')))
51 (integer '2')))
52 (string '\n'))
52 (string '\n'))
53 2 1
53 2 1
54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
55 (template
55 (template
56 (/
56 (/
57 (integer '5')
57 (integer '5')
58 (negate
58 (negate
59 (integer '2')))
59 (integer '2')))
60 (string ' ')
60 (string ' ')
61 (func
61 (func
62 (symbol 'mod')
62 (symbol 'mod')
63 (list
63 (list
64 (integer '5')
64 (integer '5')
65 (negate
65 (negate
66 (integer '2'))))
66 (integer '2'))))
67 (string '\n'))
67 (string '\n'))
68 -3 -1
68 -3 -1
69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
70 (template
70 (template
71 (/
71 (/
72 (negate
72 (negate
73 (integer '5'))
73 (integer '5'))
74 (integer '2'))
74 (integer '2'))
75 (string ' ')
75 (string ' ')
76 (func
76 (func
77 (symbol 'mod')
77 (symbol 'mod')
78 (list
78 (list
79 (negate
79 (negate
80 (integer '5'))
80 (integer '5'))
81 (integer '2')))
81 (integer '2')))
82 (string '\n'))
82 (string '\n'))
83 -3 1
83 -3 1
84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
85 (template
85 (template
86 (/
86 (/
87 (negate
87 (negate
88 (integer '5'))
88 (integer '5'))
89 (negate
89 (negate
90 (integer '2')))
90 (integer '2')))
91 (string ' ')
91 (string ' ')
92 (func
92 (func
93 (symbol 'mod')
93 (symbol 'mod')
94 (list
94 (list
95 (negate
95 (negate
96 (integer '5'))
96 (integer '5'))
97 (negate
97 (negate
98 (integer '2'))))
98 (integer '2'))))
99 (string '\n'))
99 (string '\n'))
100 2 -1
100 2 -1
101
101
102 Filters bind closer than arithmetic:
102 Filters bind closer than arithmetic:
103
103
104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
105 (template
105 (template
106 (-
106 (-
107 (|
107 (|
108 (func
108 (func
109 (symbol 'revset')
109 (symbol 'revset')
110 (string '.'))
110 (string '.'))
111 (symbol 'count'))
111 (symbol 'count'))
112 (integer '1'))
112 (integer '1'))
113 (string '\n'))
113 (string '\n'))
114 0
114 0
115
115
116 But negate binds closer still:
116 But negate binds closer still:
117
117
118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
119 (template
119 (template
120 (-
120 (-
121 (integer '1')
121 (integer '1')
122 (|
122 (|
123 (integer '3')
123 (integer '3')
124 (symbol 'stringify')))
124 (symbol 'stringify')))
125 (string '\n'))
125 (string '\n'))
126 hg: parse error: arithmetic only defined on integers
126 hg: parse error: arithmetic only defined on integers
127 [255]
127 [255]
128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
129 (template
129 (template
130 (|
130 (|
131 (negate
131 (negate
132 (integer '3'))
132 (integer '3'))
133 (symbol 'stringify'))
133 (symbol 'stringify'))
134 (string '\n'))
134 (string '\n'))
135 -3
135 -3
136
136
137 Filters bind as close as map operator:
137 Filters bind as close as map operator:
138
138
139 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
139 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
140 (template
140 (template
141 (%
141 (%
142 (|
142 (|
143 (symbol 'desc')
143 (symbol 'desc')
144 (symbol 'splitlines'))
144 (symbol 'splitlines'))
145 (template
145 (template
146 (symbol 'line')
146 (symbol 'line')
147 (string '\n'))))
147 (string '\n'))))
148 line 1
148 line 1
149 line 2
149 line 2
150
150
151 Keyword arguments:
151 Keyword arguments:
152
152
153 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
153 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
154 (template
154 (template
155 (keyvalue
155 (keyvalue
156 (symbol 'foo')
156 (symbol 'foo')
157 (|
157 (|
158 (symbol 'bar')
158 (symbol 'bar')
159 (symbol 'baz'))))
159 (symbol 'baz'))))
160 hg: parse error: can't use a key-value pair in this context
160 hg: parse error: can't use a key-value pair in this context
161 [255]
161 [255]
162
162
163 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
163 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
164 foo
164 foo
165
165
166 Call function which takes named arguments by filter syntax:
166 Call function which takes named arguments by filter syntax:
167
167
168 $ hg debugtemplate '{" "|separate}'
168 $ hg debugtemplate '{" "|separate}'
169 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
169 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
170 hg: parse error: unknown method 'list'
170 hg: parse error: unknown method 'list'
171 [255]
171 [255]
172
172
173 Second branch starting at nullrev:
173 Second branch starting at nullrev:
174
174
175 $ hg update null
175 $ hg update null
176 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
176 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
177 $ echo second > second
177 $ echo second > second
178 $ hg add second
178 $ hg add second
179 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
179 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
180 created new head
180 created new head
181
181
182 $ echo third > third
182 $ echo third > third
183 $ hg add third
183 $ hg add third
184 $ hg mv second fourth
184 $ hg mv second fourth
185 $ hg commit -m third -d "2020-01-01 10:01"
185 $ hg commit -m third -d "2020-01-01 10:01"
186
186
187 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
187 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
188 fourth (second)
188 fourth (second)
189 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
189 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
190 second -> fourth
190 second -> fourth
191 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
191 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
192 8 t
192 8 t
193 7 f
193 7 f
194
194
195 Working-directory revision has special identifiers, though they are still
195 Working-directory revision has special identifiers, though they are still
196 experimental:
196 experimental:
197
197
198 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
198 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
199 2147483647:ffffffffffffffffffffffffffffffffffffffff
199 2147483647:ffffffffffffffffffffffffffffffffffffffff
200
200
201 Some keywords are invalid for working-directory revision, but they should
201 Some keywords are invalid for working-directory revision, but they should
202 never cause crash:
202 never cause crash:
203
203
204 $ hg log -r 'wdir()' -T '{manifest}\n'
204 $ hg log -r 'wdir()' -T '{manifest}\n'
205
205
206
206
207 Internal resources shouldn't be exposed (issue5699):
207 Internal resources shouldn't be exposed (issue5699):
208
208
209 $ hg log -r. -T '{cache}{repo}{templ}{ui}'
209 $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}'
210
211 Never crash on internal resource not available:
212
213 $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n'
214 abort: template resource not available: ctx
215 [255]
210
216
211 Quoting for ui.logtemplate
217 Quoting for ui.logtemplate
212
218
213 $ hg tip --config "ui.logtemplate={rev}\n"
219 $ hg tip --config "ui.logtemplate={rev}\n"
214 8
220 8
215 $ hg tip --config "ui.logtemplate='{rev}\n'"
221 $ hg tip --config "ui.logtemplate='{rev}\n'"
216 8
222 8
217 $ hg tip --config 'ui.logtemplate="{rev}\n"'
223 $ hg tip --config 'ui.logtemplate="{rev}\n"'
218 8
224 8
219 $ hg tip --config 'ui.logtemplate=n{rev}\n'
225 $ hg tip --config 'ui.logtemplate=n{rev}\n'
220 n8
226 n8
221
227
222 Make sure user/global hgrc does not affect tests
228 Make sure user/global hgrc does not affect tests
223
229
224 $ echo '[ui]' > .hg/hgrc
230 $ echo '[ui]' > .hg/hgrc
225 $ echo 'logtemplate =' >> .hg/hgrc
231 $ echo 'logtemplate =' >> .hg/hgrc
226 $ echo 'style =' >> .hg/hgrc
232 $ echo 'style =' >> .hg/hgrc
227
233
228 Add some simple styles to settings
234 Add some simple styles to settings
229
235
230 $ cat <<'EOF' >> .hg/hgrc
236 $ cat <<'EOF' >> .hg/hgrc
231 > [templates]
237 > [templates]
232 > simple = "{rev}\n"
238 > simple = "{rev}\n"
233 > simple2 = {rev}\n
239 > simple2 = {rev}\n
234 > rev = "should not precede {rev} keyword\n"
240 > rev = "should not precede {rev} keyword\n"
235 > EOF
241 > EOF
236
242
237 $ hg log -l1 -Tsimple
243 $ hg log -l1 -Tsimple
238 8
244 8
239 $ hg log -l1 -Tsimple2
245 $ hg log -l1 -Tsimple2
240 8
246 8
241 $ hg log -l1 -Trev
247 $ hg log -l1 -Trev
242 should not precede 8 keyword
248 should not precede 8 keyword
243 $ hg log -l1 -T '{simple}'
249 $ hg log -l1 -T '{simple}'
244 8
250 8
245
251
246 Map file shouldn't see user templates:
252 Map file shouldn't see user templates:
247
253
248 $ cat <<EOF > tmpl
254 $ cat <<EOF > tmpl
249 > changeset = 'nothing expanded:{simple}\n'
255 > changeset = 'nothing expanded:{simple}\n'
250 > EOF
256 > EOF
251 $ hg log -l1 --style ./tmpl
257 $ hg log -l1 --style ./tmpl
252 nothing expanded:
258 nothing expanded:
253
259
254 Test templates and style maps in files:
260 Test templates and style maps in files:
255
261
256 $ echo "{rev}" > tmpl
262 $ echo "{rev}" > tmpl
257 $ hg log -l1 -T./tmpl
263 $ hg log -l1 -T./tmpl
258 8
264 8
259 $ hg log -l1 -Tblah/blah
265 $ hg log -l1 -Tblah/blah
260 blah/blah (no-eol)
266 blah/blah (no-eol)
261
267
262 $ printf 'changeset = "{rev}\\n"\n' > map-simple
268 $ printf 'changeset = "{rev}\\n"\n' > map-simple
263 $ hg log -l1 -T./map-simple
269 $ hg log -l1 -T./map-simple
264 8
270 8
265
271
266 a map file may have [templates] and [templatealias] sections:
272 a map file may have [templates] and [templatealias] sections:
267
273
268 $ cat <<'EOF' > map-simple
274 $ cat <<'EOF' > map-simple
269 > [templates]
275 > [templates]
270 > changeset = "{a}\n"
276 > changeset = "{a}\n"
271 > [templatealias]
277 > [templatealias]
272 > a = rev
278 > a = rev
273 > EOF
279 > EOF
274 $ hg log -l1 -T./map-simple
280 $ hg log -l1 -T./map-simple
275 8
281 8
276
282
277 so it can be included in hgrc
283 so it can be included in hgrc
278
284
279 $ cat <<'EOF' > myhgrc
285 $ cat <<'EOF' > myhgrc
280 > %include map-simple
286 > %include map-simple
281 > [templates]
287 > [templates]
282 > foo = "{changeset}"
288 > foo = "{changeset}"
283 > EOF
289 > EOF
284 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
290 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
285 8
291 8
286 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
292 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
287 8
293 8
288
294
289 Test template map inheritance
295 Test template map inheritance
290
296
291 $ echo "__base__ = map-cmdline.default" > map-simple
297 $ echo "__base__ = map-cmdline.default" > map-simple
292 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
298 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
293 $ hg log -l1 -T./map-simple
299 $ hg log -l1 -T./map-simple
294 changeset: ***8***
300 changeset: ***8***
295 tag: tip
301 tag: tip
296 user: test
302 user: test
297 date: Wed Jan 01 10:01:00 2020 +0000
303 date: Wed Jan 01 10:01:00 2020 +0000
298 summary: third
304 summary: third
299
305
300
306
301 Test docheader, docfooter and separator in template map
307 Test docheader, docfooter and separator in template map
302
308
303 $ cat <<'EOF' > map-myjson
309 $ cat <<'EOF' > map-myjson
304 > docheader = '\{\n'
310 > docheader = '\{\n'
305 > docfooter = '\n}\n'
311 > docfooter = '\n}\n'
306 > separator = ',\n'
312 > separator = ',\n'
307 > changeset = ' {dict(rev, node|short)|json}'
313 > changeset = ' {dict(rev, node|short)|json}'
308 > EOF
314 > EOF
309 $ hg log -l2 -T./map-myjson
315 $ hg log -l2 -T./map-myjson
310 {
316 {
311 {"node": "95c24699272e", "rev": 8},
317 {"node": "95c24699272e", "rev": 8},
312 {"node": "29114dbae42b", "rev": 7}
318 {"node": "29114dbae42b", "rev": 7}
313 }
319 }
314
320
315 Test docheader, docfooter and separator in [templates] section
321 Test docheader, docfooter and separator in [templates] section
316
322
317 $ cat <<'EOF' >> .hg/hgrc
323 $ cat <<'EOF' >> .hg/hgrc
318 > [templates]
324 > [templates]
319 > myjson = ' {dict(rev, node|short)|json}'
325 > myjson = ' {dict(rev, node|short)|json}'
320 > myjson:docheader = '\{\n'
326 > myjson:docheader = '\{\n'
321 > myjson:docfooter = '\n}\n'
327 > myjson:docfooter = '\n}\n'
322 > myjson:separator = ',\n'
328 > myjson:separator = ',\n'
323 > :docheader = 'should not be selected as a docheader for literal templates\n'
329 > :docheader = 'should not be selected as a docheader for literal templates\n'
324 > EOF
330 > EOF
325 $ hg log -l2 -Tmyjson
331 $ hg log -l2 -Tmyjson
326 {
332 {
327 {"node": "95c24699272e", "rev": 8},
333 {"node": "95c24699272e", "rev": 8},
328 {"node": "29114dbae42b", "rev": 7}
334 {"node": "29114dbae42b", "rev": 7}
329 }
335 }
330 $ hg log -l1 -T'{rev}\n'
336 $ hg log -l1 -T'{rev}\n'
331 8
337 8
332
338
333 Template should precede style option
339 Template should precede style option
334
340
335 $ hg log -l1 --style default -T '{rev}\n'
341 $ hg log -l1 --style default -T '{rev}\n'
336 8
342 8
337
343
338 Add a commit with empty description, to ensure that the templates
344 Add a commit with empty description, to ensure that the templates
339 below will omit the description line.
345 below will omit the description line.
340
346
341 $ echo c >> c
347 $ echo c >> c
342 $ hg add c
348 $ hg add c
343 $ hg commit -qm ' '
349 $ hg commit -qm ' '
344
350
345 Default style is like normal output. Phases style should be the same
351 Default style is like normal output. Phases style should be the same
346 as default style, except for extra phase lines.
352 as default style, except for extra phase lines.
347
353
348 $ hg log > log.out
354 $ hg log > log.out
349 $ hg log --style default > style.out
355 $ hg log --style default > style.out
350 $ cmp log.out style.out || diff -u log.out style.out
356 $ cmp log.out style.out || diff -u log.out style.out
351 $ hg log -T phases > phases.out
357 $ hg log -T phases > phases.out
352 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
358 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
353 +phase: draft
359 +phase: draft
354 +phase: draft
360 +phase: draft
355 +phase: draft
361 +phase: draft
356 +phase: draft
362 +phase: draft
357 +phase: draft
363 +phase: draft
358 +phase: draft
364 +phase: draft
359 +phase: draft
365 +phase: draft
360 +phase: draft
366 +phase: draft
361 +phase: draft
367 +phase: draft
362 +phase: draft
368 +phase: draft
363
369
364 $ hg log -v > log.out
370 $ hg log -v > log.out
365 $ hg log -v --style default > style.out
371 $ hg log -v --style default > style.out
366 $ cmp log.out style.out || diff -u log.out style.out
372 $ cmp log.out style.out || diff -u log.out style.out
367 $ hg log -v -T phases > phases.out
373 $ hg log -v -T phases > phases.out
368 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
374 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
369 +phase: draft
375 +phase: draft
370 +phase: draft
376 +phase: draft
371 +phase: draft
377 +phase: draft
372 +phase: draft
378 +phase: draft
373 +phase: draft
379 +phase: draft
374 +phase: draft
380 +phase: draft
375 +phase: draft
381 +phase: draft
376 +phase: draft
382 +phase: draft
377 +phase: draft
383 +phase: draft
378 +phase: draft
384 +phase: draft
379
385
380 $ hg log -q > log.out
386 $ hg log -q > log.out
381 $ hg log -q --style default > style.out
387 $ hg log -q --style default > style.out
382 $ cmp log.out style.out || diff -u log.out style.out
388 $ cmp log.out style.out || diff -u log.out style.out
383 $ hg log -q -T phases > phases.out
389 $ hg log -q -T phases > phases.out
384 $ cmp log.out phases.out || diff -u log.out phases.out
390 $ cmp log.out phases.out || diff -u log.out phases.out
385
391
386 $ hg log --debug > log.out
392 $ hg log --debug > log.out
387 $ hg log --debug --style default > style.out
393 $ hg log --debug --style default > style.out
388 $ cmp log.out style.out || diff -u log.out style.out
394 $ cmp log.out style.out || diff -u log.out style.out
389 $ hg log --debug -T phases > phases.out
395 $ hg log --debug -T phases > phases.out
390 $ cmp log.out phases.out || diff -u log.out phases.out
396 $ cmp log.out phases.out || diff -u log.out phases.out
391
397
392 Default style of working-directory revision should also be the same (but
398 Default style of working-directory revision should also be the same (but
393 date may change while running tests):
399 date may change while running tests):
394
400
395 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
401 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
396 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
402 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
397 $ cmp log.out style.out || diff -u log.out style.out
403 $ cmp log.out style.out || diff -u log.out style.out
398
404
399 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
405 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
400 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
406 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
401 $ cmp log.out style.out || diff -u log.out style.out
407 $ cmp log.out style.out || diff -u log.out style.out
402
408
403 $ hg log -r 'wdir()' -q > log.out
409 $ hg log -r 'wdir()' -q > log.out
404 $ hg log -r 'wdir()' -q --style default > style.out
410 $ hg log -r 'wdir()' -q --style default > style.out
405 $ cmp log.out style.out || diff -u log.out style.out
411 $ cmp log.out style.out || diff -u log.out style.out
406
412
407 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
413 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
408 $ hg log -r 'wdir()' --debug --style default \
414 $ hg log -r 'wdir()' --debug --style default \
409 > | sed 's|^date:.*|date:|' > style.out
415 > | sed 's|^date:.*|date:|' > style.out
410 $ cmp log.out style.out || diff -u log.out style.out
416 $ cmp log.out style.out || diff -u log.out style.out
411
417
412 Default style should also preserve color information (issue2866):
418 Default style should also preserve color information (issue2866):
413
419
414 $ cp $HGRCPATH $HGRCPATH-bak
420 $ cp $HGRCPATH $HGRCPATH-bak
415 $ cat <<EOF >> $HGRCPATH
421 $ cat <<EOF >> $HGRCPATH
416 > [extensions]
422 > [extensions]
417 > color=
423 > color=
418 > EOF
424 > EOF
419
425
420 $ hg --color=debug log > log.out
426 $ hg --color=debug log > log.out
421 $ hg --color=debug log --style default > style.out
427 $ hg --color=debug log --style default > style.out
422 $ cmp log.out style.out || diff -u log.out style.out
428 $ cmp log.out style.out || diff -u log.out style.out
423 $ hg --color=debug log -T phases > phases.out
429 $ hg --color=debug log -T phases > phases.out
424 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
430 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
425 +[log.phase|phase: draft]
431 +[log.phase|phase: draft]
426 +[log.phase|phase: draft]
432 +[log.phase|phase: draft]
427 +[log.phase|phase: draft]
433 +[log.phase|phase: draft]
428 +[log.phase|phase: draft]
434 +[log.phase|phase: draft]
429 +[log.phase|phase: draft]
435 +[log.phase|phase: draft]
430 +[log.phase|phase: draft]
436 +[log.phase|phase: draft]
431 +[log.phase|phase: draft]
437 +[log.phase|phase: draft]
432 +[log.phase|phase: draft]
438 +[log.phase|phase: draft]
433 +[log.phase|phase: draft]
439 +[log.phase|phase: draft]
434 +[log.phase|phase: draft]
440 +[log.phase|phase: draft]
435
441
436 $ hg --color=debug -v log > log.out
442 $ hg --color=debug -v log > log.out
437 $ hg --color=debug -v log --style default > style.out
443 $ hg --color=debug -v log --style default > style.out
438 $ cmp log.out style.out || diff -u log.out style.out
444 $ cmp log.out style.out || diff -u log.out style.out
439 $ hg --color=debug -v log -T phases > phases.out
445 $ hg --color=debug -v log -T phases > phases.out
440 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
446 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
441 +[log.phase|phase: draft]
447 +[log.phase|phase: draft]
442 +[log.phase|phase: draft]
448 +[log.phase|phase: draft]
443 +[log.phase|phase: draft]
449 +[log.phase|phase: draft]
444 +[log.phase|phase: draft]
450 +[log.phase|phase: draft]
445 +[log.phase|phase: draft]
451 +[log.phase|phase: draft]
446 +[log.phase|phase: draft]
452 +[log.phase|phase: draft]
447 +[log.phase|phase: draft]
453 +[log.phase|phase: draft]
448 +[log.phase|phase: draft]
454 +[log.phase|phase: draft]
449 +[log.phase|phase: draft]
455 +[log.phase|phase: draft]
450 +[log.phase|phase: draft]
456 +[log.phase|phase: draft]
451
457
452 $ hg --color=debug -q log > log.out
458 $ hg --color=debug -q log > log.out
453 $ hg --color=debug -q log --style default > style.out
459 $ hg --color=debug -q log --style default > style.out
454 $ cmp log.out style.out || diff -u log.out style.out
460 $ cmp log.out style.out || diff -u log.out style.out
455 $ hg --color=debug -q log -T phases > phases.out
461 $ hg --color=debug -q log -T phases > phases.out
456 $ cmp log.out phases.out || diff -u log.out phases.out
462 $ cmp log.out phases.out || diff -u log.out phases.out
457
463
458 $ hg --color=debug --debug log > log.out
464 $ hg --color=debug --debug log > log.out
459 $ hg --color=debug --debug log --style default > style.out
465 $ hg --color=debug --debug log --style default > style.out
460 $ cmp log.out style.out || diff -u log.out style.out
466 $ cmp log.out style.out || diff -u log.out style.out
461 $ hg --color=debug --debug log -T phases > phases.out
467 $ hg --color=debug --debug log -T phases > phases.out
462 $ cmp log.out phases.out || diff -u log.out phases.out
468 $ cmp log.out phases.out || diff -u log.out phases.out
463
469
464 $ mv $HGRCPATH-bak $HGRCPATH
470 $ mv $HGRCPATH-bak $HGRCPATH
465
471
466 Remove commit with empty commit message, so as to not pollute further
472 Remove commit with empty commit message, so as to not pollute further
467 tests.
473 tests.
468
474
469 $ hg --config extensions.strip= strip -q .
475 $ hg --config extensions.strip= strip -q .
470
476
471 Revision with no copies (used to print a traceback):
477 Revision with no copies (used to print a traceback):
472
478
473 $ hg tip -v --template '\n'
479 $ hg tip -v --template '\n'
474
480
475
481
476 Compact style works:
482 Compact style works:
477
483
478 $ hg log -Tcompact
484 $ hg log -Tcompact
479 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
485 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
480 third
486 third
481
487
482 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
488 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
483 second
489 second
484
490
485 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
491 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
486 merge
492 merge
487
493
488 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
494 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
489 new head
495 new head
490
496
491 4 bbe44766e73d 1970-01-17 04:53 +0000 person
497 4 bbe44766e73d 1970-01-17 04:53 +0000 person
492 new branch
498 new branch
493
499
494 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
500 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
495 no user, no domain
501 no user, no domain
496
502
497 2 97054abb4ab8 1970-01-14 21:20 +0000 other
503 2 97054abb4ab8 1970-01-14 21:20 +0000 other
498 no person
504 no person
499
505
500 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
506 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
501 other 1
507 other 1
502
508
503 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
509 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
504 line 1
510 line 1
505
511
506
512
507 $ hg log -v --style compact
513 $ hg log -v --style compact
508 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
514 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
509 third
515 third
510
516
511 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
517 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
512 second
518 second
513
519
514 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
520 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
515 merge
521 merge
516
522
517 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
523 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
518 new head
524 new head
519
525
520 4 bbe44766e73d 1970-01-17 04:53 +0000 person
526 4 bbe44766e73d 1970-01-17 04:53 +0000 person
521 new branch
527 new branch
522
528
523 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
529 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
524 no user, no domain
530 no user, no domain
525
531
526 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
532 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
527 no person
533 no person
528
534
529 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
535 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
530 other 1
536 other 1
531 other 2
537 other 2
532
538
533 other 3
539 other 3
534
540
535 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
541 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
536 line 1
542 line 1
537 line 2
543 line 2
538
544
539
545
540 $ hg log --debug --style compact
546 $ hg log --debug --style compact
541 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
547 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
542 third
548 third
543
549
544 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
550 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
545 second
551 second
546
552
547 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
553 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
548 merge
554 merge
549
555
550 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
556 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
551 new head
557 new head
552
558
553 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
559 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
554 new branch
560 new branch
555
561
556 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
562 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
557 no user, no domain
563 no user, no domain
558
564
559 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
565 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
560 no person
566 no person
561
567
562 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
568 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
563 other 1
569 other 1
564 other 2
570 other 2
565
571
566 other 3
572 other 3
567
573
568 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
574 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
569 line 1
575 line 1
570 line 2
576 line 2
571
577
572
578
573 Test xml styles:
579 Test xml styles:
574
580
575 $ hg log --style xml -r 'not all()'
581 $ hg log --style xml -r 'not all()'
576 <?xml version="1.0"?>
582 <?xml version="1.0"?>
577 <log>
583 <log>
578 </log>
584 </log>
579
585
580 $ hg log --style xml
586 $ hg log --style xml
581 <?xml version="1.0"?>
587 <?xml version="1.0"?>
582 <log>
588 <log>
583 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
589 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
584 <tag>tip</tag>
590 <tag>tip</tag>
585 <author email="test">test</author>
591 <author email="test">test</author>
586 <date>2020-01-01T10:01:00+00:00</date>
592 <date>2020-01-01T10:01:00+00:00</date>
587 <msg xml:space="preserve">third</msg>
593 <msg xml:space="preserve">third</msg>
588 </logentry>
594 </logentry>
589 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
595 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
590 <parent revision="-1" node="0000000000000000000000000000000000000000" />
596 <parent revision="-1" node="0000000000000000000000000000000000000000" />
591 <author email="user@hostname">User Name</author>
597 <author email="user@hostname">User Name</author>
592 <date>1970-01-12T13:46:40+00:00</date>
598 <date>1970-01-12T13:46:40+00:00</date>
593 <msg xml:space="preserve">second</msg>
599 <msg xml:space="preserve">second</msg>
594 </logentry>
600 </logentry>
595 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
601 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
596 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
602 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
597 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
603 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
598 <author email="person">person</author>
604 <author email="person">person</author>
599 <date>1970-01-18T08:40:01+00:00</date>
605 <date>1970-01-18T08:40:01+00:00</date>
600 <msg xml:space="preserve">merge</msg>
606 <msg xml:space="preserve">merge</msg>
601 </logentry>
607 </logentry>
602 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
608 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
603 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
609 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
604 <author email="person">person</author>
610 <author email="person">person</author>
605 <date>1970-01-18T08:40:00+00:00</date>
611 <date>1970-01-18T08:40:00+00:00</date>
606 <msg xml:space="preserve">new head</msg>
612 <msg xml:space="preserve">new head</msg>
607 </logentry>
613 </logentry>
608 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
614 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
609 <branch>foo</branch>
615 <branch>foo</branch>
610 <author email="person">person</author>
616 <author email="person">person</author>
611 <date>1970-01-17T04:53:20+00:00</date>
617 <date>1970-01-17T04:53:20+00:00</date>
612 <msg xml:space="preserve">new branch</msg>
618 <msg xml:space="preserve">new branch</msg>
613 </logentry>
619 </logentry>
614 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
620 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
615 <author email="person">person</author>
621 <author email="person">person</author>
616 <date>1970-01-16T01:06:40+00:00</date>
622 <date>1970-01-16T01:06:40+00:00</date>
617 <msg xml:space="preserve">no user, no domain</msg>
623 <msg xml:space="preserve">no user, no domain</msg>
618 </logentry>
624 </logentry>
619 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
625 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
620 <author email="other@place">other</author>
626 <author email="other@place">other</author>
621 <date>1970-01-14T21:20:00+00:00</date>
627 <date>1970-01-14T21:20:00+00:00</date>
622 <msg xml:space="preserve">no person</msg>
628 <msg xml:space="preserve">no person</msg>
623 </logentry>
629 </logentry>
624 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
630 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
625 <author email="other@place">A. N. Other</author>
631 <author email="other@place">A. N. Other</author>
626 <date>1970-01-13T17:33:20+00:00</date>
632 <date>1970-01-13T17:33:20+00:00</date>
627 <msg xml:space="preserve">other 1
633 <msg xml:space="preserve">other 1
628 other 2
634 other 2
629
635
630 other 3</msg>
636 other 3</msg>
631 </logentry>
637 </logentry>
632 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
638 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
633 <author email="user@hostname">User Name</author>
639 <author email="user@hostname">User Name</author>
634 <date>1970-01-12T13:46:40+00:00</date>
640 <date>1970-01-12T13:46:40+00:00</date>
635 <msg xml:space="preserve">line 1
641 <msg xml:space="preserve">line 1
636 line 2</msg>
642 line 2</msg>
637 </logentry>
643 </logentry>
638 </log>
644 </log>
639
645
640 $ hg log -v --style xml
646 $ hg log -v --style xml
641 <?xml version="1.0"?>
647 <?xml version="1.0"?>
642 <log>
648 <log>
643 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
649 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
644 <tag>tip</tag>
650 <tag>tip</tag>
645 <author email="test">test</author>
651 <author email="test">test</author>
646 <date>2020-01-01T10:01:00+00:00</date>
652 <date>2020-01-01T10:01:00+00:00</date>
647 <msg xml:space="preserve">third</msg>
653 <msg xml:space="preserve">third</msg>
648 <paths>
654 <paths>
649 <path action="A">fourth</path>
655 <path action="A">fourth</path>
650 <path action="A">third</path>
656 <path action="A">third</path>
651 <path action="R">second</path>
657 <path action="R">second</path>
652 </paths>
658 </paths>
653 <copies>
659 <copies>
654 <copy source="second">fourth</copy>
660 <copy source="second">fourth</copy>
655 </copies>
661 </copies>
656 </logentry>
662 </logentry>
657 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
663 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
658 <parent revision="-1" node="0000000000000000000000000000000000000000" />
664 <parent revision="-1" node="0000000000000000000000000000000000000000" />
659 <author email="user@hostname">User Name</author>
665 <author email="user@hostname">User Name</author>
660 <date>1970-01-12T13:46:40+00:00</date>
666 <date>1970-01-12T13:46:40+00:00</date>
661 <msg xml:space="preserve">second</msg>
667 <msg xml:space="preserve">second</msg>
662 <paths>
668 <paths>
663 <path action="A">second</path>
669 <path action="A">second</path>
664 </paths>
670 </paths>
665 </logentry>
671 </logentry>
666 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
672 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
667 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
673 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
668 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
674 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
669 <author email="person">person</author>
675 <author email="person">person</author>
670 <date>1970-01-18T08:40:01+00:00</date>
676 <date>1970-01-18T08:40:01+00:00</date>
671 <msg xml:space="preserve">merge</msg>
677 <msg xml:space="preserve">merge</msg>
672 <paths>
678 <paths>
673 </paths>
679 </paths>
674 </logentry>
680 </logentry>
675 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
681 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
676 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
682 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
677 <author email="person">person</author>
683 <author email="person">person</author>
678 <date>1970-01-18T08:40:00+00:00</date>
684 <date>1970-01-18T08:40:00+00:00</date>
679 <msg xml:space="preserve">new head</msg>
685 <msg xml:space="preserve">new head</msg>
680 <paths>
686 <paths>
681 <path action="A">d</path>
687 <path action="A">d</path>
682 </paths>
688 </paths>
683 </logentry>
689 </logentry>
684 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
690 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
685 <branch>foo</branch>
691 <branch>foo</branch>
686 <author email="person">person</author>
692 <author email="person">person</author>
687 <date>1970-01-17T04:53:20+00:00</date>
693 <date>1970-01-17T04:53:20+00:00</date>
688 <msg xml:space="preserve">new branch</msg>
694 <msg xml:space="preserve">new branch</msg>
689 <paths>
695 <paths>
690 </paths>
696 </paths>
691 </logentry>
697 </logentry>
692 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
698 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
693 <author email="person">person</author>
699 <author email="person">person</author>
694 <date>1970-01-16T01:06:40+00:00</date>
700 <date>1970-01-16T01:06:40+00:00</date>
695 <msg xml:space="preserve">no user, no domain</msg>
701 <msg xml:space="preserve">no user, no domain</msg>
696 <paths>
702 <paths>
697 <path action="M">c</path>
703 <path action="M">c</path>
698 </paths>
704 </paths>
699 </logentry>
705 </logentry>
700 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
706 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
701 <author email="other@place">other</author>
707 <author email="other@place">other</author>
702 <date>1970-01-14T21:20:00+00:00</date>
708 <date>1970-01-14T21:20:00+00:00</date>
703 <msg xml:space="preserve">no person</msg>
709 <msg xml:space="preserve">no person</msg>
704 <paths>
710 <paths>
705 <path action="A">c</path>
711 <path action="A">c</path>
706 </paths>
712 </paths>
707 </logentry>
713 </logentry>
708 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
714 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
709 <author email="other@place">A. N. Other</author>
715 <author email="other@place">A. N. Other</author>
710 <date>1970-01-13T17:33:20+00:00</date>
716 <date>1970-01-13T17:33:20+00:00</date>
711 <msg xml:space="preserve">other 1
717 <msg xml:space="preserve">other 1
712 other 2
718 other 2
713
719
714 other 3</msg>
720 other 3</msg>
715 <paths>
721 <paths>
716 <path action="A">b</path>
722 <path action="A">b</path>
717 </paths>
723 </paths>
718 </logentry>
724 </logentry>
719 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
725 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
720 <author email="user@hostname">User Name</author>
726 <author email="user@hostname">User Name</author>
721 <date>1970-01-12T13:46:40+00:00</date>
727 <date>1970-01-12T13:46:40+00:00</date>
722 <msg xml:space="preserve">line 1
728 <msg xml:space="preserve">line 1
723 line 2</msg>
729 line 2</msg>
724 <paths>
730 <paths>
725 <path action="A">a</path>
731 <path action="A">a</path>
726 </paths>
732 </paths>
727 </logentry>
733 </logentry>
728 </log>
734 </log>
729
735
730 $ hg log --debug --style xml
736 $ hg log --debug --style xml
731 <?xml version="1.0"?>
737 <?xml version="1.0"?>
732 <log>
738 <log>
733 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
739 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
734 <tag>tip</tag>
740 <tag>tip</tag>
735 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
741 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
736 <parent revision="-1" node="0000000000000000000000000000000000000000" />
742 <parent revision="-1" node="0000000000000000000000000000000000000000" />
737 <author email="test">test</author>
743 <author email="test">test</author>
738 <date>2020-01-01T10:01:00+00:00</date>
744 <date>2020-01-01T10:01:00+00:00</date>
739 <msg xml:space="preserve">third</msg>
745 <msg xml:space="preserve">third</msg>
740 <paths>
746 <paths>
741 <path action="A">fourth</path>
747 <path action="A">fourth</path>
742 <path action="A">third</path>
748 <path action="A">third</path>
743 <path action="R">second</path>
749 <path action="R">second</path>
744 </paths>
750 </paths>
745 <copies>
751 <copies>
746 <copy source="second">fourth</copy>
752 <copy source="second">fourth</copy>
747 </copies>
753 </copies>
748 <extra key="branch">default</extra>
754 <extra key="branch">default</extra>
749 </logentry>
755 </logentry>
750 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
756 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
751 <parent revision="-1" node="0000000000000000000000000000000000000000" />
757 <parent revision="-1" node="0000000000000000000000000000000000000000" />
752 <parent revision="-1" node="0000000000000000000000000000000000000000" />
758 <parent revision="-1" node="0000000000000000000000000000000000000000" />
753 <author email="user@hostname">User Name</author>
759 <author email="user@hostname">User Name</author>
754 <date>1970-01-12T13:46:40+00:00</date>
760 <date>1970-01-12T13:46:40+00:00</date>
755 <msg xml:space="preserve">second</msg>
761 <msg xml:space="preserve">second</msg>
756 <paths>
762 <paths>
757 <path action="A">second</path>
763 <path action="A">second</path>
758 </paths>
764 </paths>
759 <extra key="branch">default</extra>
765 <extra key="branch">default</extra>
760 </logentry>
766 </logentry>
761 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
767 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
762 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
768 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
763 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
769 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
764 <author email="person">person</author>
770 <author email="person">person</author>
765 <date>1970-01-18T08:40:01+00:00</date>
771 <date>1970-01-18T08:40:01+00:00</date>
766 <msg xml:space="preserve">merge</msg>
772 <msg xml:space="preserve">merge</msg>
767 <paths>
773 <paths>
768 </paths>
774 </paths>
769 <extra key="branch">default</extra>
775 <extra key="branch">default</extra>
770 </logentry>
776 </logentry>
771 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
777 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
772 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
778 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
773 <parent revision="-1" node="0000000000000000000000000000000000000000" />
779 <parent revision="-1" node="0000000000000000000000000000000000000000" />
774 <author email="person">person</author>
780 <author email="person">person</author>
775 <date>1970-01-18T08:40:00+00:00</date>
781 <date>1970-01-18T08:40:00+00:00</date>
776 <msg xml:space="preserve">new head</msg>
782 <msg xml:space="preserve">new head</msg>
777 <paths>
783 <paths>
778 <path action="A">d</path>
784 <path action="A">d</path>
779 </paths>
785 </paths>
780 <extra key="branch">default</extra>
786 <extra key="branch">default</extra>
781 </logentry>
787 </logentry>
782 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
788 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
783 <branch>foo</branch>
789 <branch>foo</branch>
784 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
790 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
785 <parent revision="-1" node="0000000000000000000000000000000000000000" />
791 <parent revision="-1" node="0000000000000000000000000000000000000000" />
786 <author email="person">person</author>
792 <author email="person">person</author>
787 <date>1970-01-17T04:53:20+00:00</date>
793 <date>1970-01-17T04:53:20+00:00</date>
788 <msg xml:space="preserve">new branch</msg>
794 <msg xml:space="preserve">new branch</msg>
789 <paths>
795 <paths>
790 </paths>
796 </paths>
791 <extra key="branch">foo</extra>
797 <extra key="branch">foo</extra>
792 </logentry>
798 </logentry>
793 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
799 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
794 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
800 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
795 <parent revision="-1" node="0000000000000000000000000000000000000000" />
801 <parent revision="-1" node="0000000000000000000000000000000000000000" />
796 <author email="person">person</author>
802 <author email="person">person</author>
797 <date>1970-01-16T01:06:40+00:00</date>
803 <date>1970-01-16T01:06:40+00:00</date>
798 <msg xml:space="preserve">no user, no domain</msg>
804 <msg xml:space="preserve">no user, no domain</msg>
799 <paths>
805 <paths>
800 <path action="M">c</path>
806 <path action="M">c</path>
801 </paths>
807 </paths>
802 <extra key="branch">default</extra>
808 <extra key="branch">default</extra>
803 </logentry>
809 </logentry>
804 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
810 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
805 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
811 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
806 <parent revision="-1" node="0000000000000000000000000000000000000000" />
812 <parent revision="-1" node="0000000000000000000000000000000000000000" />
807 <author email="other@place">other</author>
813 <author email="other@place">other</author>
808 <date>1970-01-14T21:20:00+00:00</date>
814 <date>1970-01-14T21:20:00+00:00</date>
809 <msg xml:space="preserve">no person</msg>
815 <msg xml:space="preserve">no person</msg>
810 <paths>
816 <paths>
811 <path action="A">c</path>
817 <path action="A">c</path>
812 </paths>
818 </paths>
813 <extra key="branch">default</extra>
819 <extra key="branch">default</extra>
814 </logentry>
820 </logentry>
815 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
821 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
816 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
822 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
817 <parent revision="-1" node="0000000000000000000000000000000000000000" />
823 <parent revision="-1" node="0000000000000000000000000000000000000000" />
818 <author email="other@place">A. N. Other</author>
824 <author email="other@place">A. N. Other</author>
819 <date>1970-01-13T17:33:20+00:00</date>
825 <date>1970-01-13T17:33:20+00:00</date>
820 <msg xml:space="preserve">other 1
826 <msg xml:space="preserve">other 1
821 other 2
827 other 2
822
828
823 other 3</msg>
829 other 3</msg>
824 <paths>
830 <paths>
825 <path action="A">b</path>
831 <path action="A">b</path>
826 </paths>
832 </paths>
827 <extra key="branch">default</extra>
833 <extra key="branch">default</extra>
828 </logentry>
834 </logentry>
829 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
835 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
830 <parent revision="-1" node="0000000000000000000000000000000000000000" />
836 <parent revision="-1" node="0000000000000000000000000000000000000000" />
831 <parent revision="-1" node="0000000000000000000000000000000000000000" />
837 <parent revision="-1" node="0000000000000000000000000000000000000000" />
832 <author email="user@hostname">User Name</author>
838 <author email="user@hostname">User Name</author>
833 <date>1970-01-12T13:46:40+00:00</date>
839 <date>1970-01-12T13:46:40+00:00</date>
834 <msg xml:space="preserve">line 1
840 <msg xml:space="preserve">line 1
835 line 2</msg>
841 line 2</msg>
836 <paths>
842 <paths>
837 <path action="A">a</path>
843 <path action="A">a</path>
838 </paths>
844 </paths>
839 <extra key="branch">default</extra>
845 <extra key="branch">default</extra>
840 </logentry>
846 </logentry>
841 </log>
847 </log>
842
848
843
849
844 Test JSON style:
850 Test JSON style:
845
851
846 $ hg log -k nosuch -Tjson
852 $ hg log -k nosuch -Tjson
847 []
853 []
848
854
849 $ hg log -qr . -Tjson
855 $ hg log -qr . -Tjson
850 [
856 [
851 {
857 {
852 "rev": 8,
858 "rev": 8,
853 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
859 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
854 }
860 }
855 ]
861 ]
856
862
857 $ hg log -vpr . -Tjson --stat
863 $ hg log -vpr . -Tjson --stat
858 [
864 [
859 {
865 {
860 "rev": 8,
866 "rev": 8,
861 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
867 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
862 "branch": "default",
868 "branch": "default",
863 "phase": "draft",
869 "phase": "draft",
864 "user": "test",
870 "user": "test",
865 "date": [1577872860, 0],
871 "date": [1577872860, 0],
866 "desc": "third",
872 "desc": "third",
867 "bookmarks": [],
873 "bookmarks": [],
868 "tags": ["tip"],
874 "tags": ["tip"],
869 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
875 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
870 "files": ["fourth", "second", "third"],
876 "files": ["fourth", "second", "third"],
871 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
877 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
872 "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"
878 "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"
873 }
879 }
874 ]
880 ]
875
881
876 honor --git but not format-breaking diffopts
882 honor --git but not format-breaking diffopts
877 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
883 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
878 [
884 [
879 {
885 {
880 "rev": 8,
886 "rev": 8,
881 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
887 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
882 "branch": "default",
888 "branch": "default",
883 "phase": "draft",
889 "phase": "draft",
884 "user": "test",
890 "user": "test",
885 "date": [1577872860, 0],
891 "date": [1577872860, 0],
886 "desc": "third",
892 "desc": "third",
887 "bookmarks": [],
893 "bookmarks": [],
888 "tags": ["tip"],
894 "tags": ["tip"],
889 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
895 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
890 "files": ["fourth", "second", "third"],
896 "files": ["fourth", "second", "third"],
891 "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"
897 "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"
892 }
898 }
893 ]
899 ]
894
900
895 $ hg log -T json
901 $ hg log -T json
896 [
902 [
897 {
903 {
898 "rev": 8,
904 "rev": 8,
899 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
905 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
900 "branch": "default",
906 "branch": "default",
901 "phase": "draft",
907 "phase": "draft",
902 "user": "test",
908 "user": "test",
903 "date": [1577872860, 0],
909 "date": [1577872860, 0],
904 "desc": "third",
910 "desc": "third",
905 "bookmarks": [],
911 "bookmarks": [],
906 "tags": ["tip"],
912 "tags": ["tip"],
907 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
913 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
908 },
914 },
909 {
915 {
910 "rev": 7,
916 "rev": 7,
911 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
917 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
912 "branch": "default",
918 "branch": "default",
913 "phase": "draft",
919 "phase": "draft",
914 "user": "User Name <user@hostname>",
920 "user": "User Name <user@hostname>",
915 "date": [1000000, 0],
921 "date": [1000000, 0],
916 "desc": "second",
922 "desc": "second",
917 "bookmarks": [],
923 "bookmarks": [],
918 "tags": [],
924 "tags": [],
919 "parents": ["0000000000000000000000000000000000000000"]
925 "parents": ["0000000000000000000000000000000000000000"]
920 },
926 },
921 {
927 {
922 "rev": 6,
928 "rev": 6,
923 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
929 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
924 "branch": "default",
930 "branch": "default",
925 "phase": "draft",
931 "phase": "draft",
926 "user": "person",
932 "user": "person",
927 "date": [1500001, 0],
933 "date": [1500001, 0],
928 "desc": "merge",
934 "desc": "merge",
929 "bookmarks": [],
935 "bookmarks": [],
930 "tags": [],
936 "tags": [],
931 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
937 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
932 },
938 },
933 {
939 {
934 "rev": 5,
940 "rev": 5,
935 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
941 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
936 "branch": "default",
942 "branch": "default",
937 "phase": "draft",
943 "phase": "draft",
938 "user": "person",
944 "user": "person",
939 "date": [1500000, 0],
945 "date": [1500000, 0],
940 "desc": "new head",
946 "desc": "new head",
941 "bookmarks": [],
947 "bookmarks": [],
942 "tags": [],
948 "tags": [],
943 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
949 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
944 },
950 },
945 {
951 {
946 "rev": 4,
952 "rev": 4,
947 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
953 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
948 "branch": "foo",
954 "branch": "foo",
949 "phase": "draft",
955 "phase": "draft",
950 "user": "person",
956 "user": "person",
951 "date": [1400000, 0],
957 "date": [1400000, 0],
952 "desc": "new branch",
958 "desc": "new branch",
953 "bookmarks": [],
959 "bookmarks": [],
954 "tags": [],
960 "tags": [],
955 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
961 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
956 },
962 },
957 {
963 {
958 "rev": 3,
964 "rev": 3,
959 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
965 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
960 "branch": "default",
966 "branch": "default",
961 "phase": "draft",
967 "phase": "draft",
962 "user": "person",
968 "user": "person",
963 "date": [1300000, 0],
969 "date": [1300000, 0],
964 "desc": "no user, no domain",
970 "desc": "no user, no domain",
965 "bookmarks": [],
971 "bookmarks": [],
966 "tags": [],
972 "tags": [],
967 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
973 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
968 },
974 },
969 {
975 {
970 "rev": 2,
976 "rev": 2,
971 "node": "97054abb4ab824450e9164180baf491ae0078465",
977 "node": "97054abb4ab824450e9164180baf491ae0078465",
972 "branch": "default",
978 "branch": "default",
973 "phase": "draft",
979 "phase": "draft",
974 "user": "other@place",
980 "user": "other@place",
975 "date": [1200000, 0],
981 "date": [1200000, 0],
976 "desc": "no person",
982 "desc": "no person",
977 "bookmarks": [],
983 "bookmarks": [],
978 "tags": [],
984 "tags": [],
979 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
985 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
980 },
986 },
981 {
987 {
982 "rev": 1,
988 "rev": 1,
983 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
989 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
984 "branch": "default",
990 "branch": "default",
985 "phase": "draft",
991 "phase": "draft",
986 "user": "A. N. Other <other@place>",
992 "user": "A. N. Other <other@place>",
987 "date": [1100000, 0],
993 "date": [1100000, 0],
988 "desc": "other 1\nother 2\n\nother 3",
994 "desc": "other 1\nother 2\n\nother 3",
989 "bookmarks": [],
995 "bookmarks": [],
990 "tags": [],
996 "tags": [],
991 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
997 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
992 },
998 },
993 {
999 {
994 "rev": 0,
1000 "rev": 0,
995 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1001 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
996 "branch": "default",
1002 "branch": "default",
997 "phase": "draft",
1003 "phase": "draft",
998 "user": "User Name <user@hostname>",
1004 "user": "User Name <user@hostname>",
999 "date": [1000000, 0],
1005 "date": [1000000, 0],
1000 "desc": "line 1\nline 2",
1006 "desc": "line 1\nline 2",
1001 "bookmarks": [],
1007 "bookmarks": [],
1002 "tags": [],
1008 "tags": [],
1003 "parents": ["0000000000000000000000000000000000000000"]
1009 "parents": ["0000000000000000000000000000000000000000"]
1004 }
1010 }
1005 ]
1011 ]
1006
1012
1007 $ hg heads -v -Tjson
1013 $ hg heads -v -Tjson
1008 [
1014 [
1009 {
1015 {
1010 "rev": 8,
1016 "rev": 8,
1011 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1017 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1012 "branch": "default",
1018 "branch": "default",
1013 "phase": "draft",
1019 "phase": "draft",
1014 "user": "test",
1020 "user": "test",
1015 "date": [1577872860, 0],
1021 "date": [1577872860, 0],
1016 "desc": "third",
1022 "desc": "third",
1017 "bookmarks": [],
1023 "bookmarks": [],
1018 "tags": ["tip"],
1024 "tags": ["tip"],
1019 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1025 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1020 "files": ["fourth", "second", "third"]
1026 "files": ["fourth", "second", "third"]
1021 },
1027 },
1022 {
1028 {
1023 "rev": 6,
1029 "rev": 6,
1024 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1030 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1025 "branch": "default",
1031 "branch": "default",
1026 "phase": "draft",
1032 "phase": "draft",
1027 "user": "person",
1033 "user": "person",
1028 "date": [1500001, 0],
1034 "date": [1500001, 0],
1029 "desc": "merge",
1035 "desc": "merge",
1030 "bookmarks": [],
1036 "bookmarks": [],
1031 "tags": [],
1037 "tags": [],
1032 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1038 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1033 "files": []
1039 "files": []
1034 },
1040 },
1035 {
1041 {
1036 "rev": 4,
1042 "rev": 4,
1037 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1043 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1038 "branch": "foo",
1044 "branch": "foo",
1039 "phase": "draft",
1045 "phase": "draft",
1040 "user": "person",
1046 "user": "person",
1041 "date": [1400000, 0],
1047 "date": [1400000, 0],
1042 "desc": "new branch",
1048 "desc": "new branch",
1043 "bookmarks": [],
1049 "bookmarks": [],
1044 "tags": [],
1050 "tags": [],
1045 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1051 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1046 "files": []
1052 "files": []
1047 }
1053 }
1048 ]
1054 ]
1049
1055
1050 $ hg log --debug -Tjson
1056 $ hg log --debug -Tjson
1051 [
1057 [
1052 {
1058 {
1053 "rev": 8,
1059 "rev": 8,
1054 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1060 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1055 "branch": "default",
1061 "branch": "default",
1056 "phase": "draft",
1062 "phase": "draft",
1057 "user": "test",
1063 "user": "test",
1058 "date": [1577872860, 0],
1064 "date": [1577872860, 0],
1059 "desc": "third",
1065 "desc": "third",
1060 "bookmarks": [],
1066 "bookmarks": [],
1061 "tags": ["tip"],
1067 "tags": ["tip"],
1062 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1068 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1063 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1069 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1064 "extra": {"branch": "default"},
1070 "extra": {"branch": "default"},
1065 "modified": [],
1071 "modified": [],
1066 "added": ["fourth", "third"],
1072 "added": ["fourth", "third"],
1067 "removed": ["second"]
1073 "removed": ["second"]
1068 },
1074 },
1069 {
1075 {
1070 "rev": 7,
1076 "rev": 7,
1071 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1077 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1072 "branch": "default",
1078 "branch": "default",
1073 "phase": "draft",
1079 "phase": "draft",
1074 "user": "User Name <user@hostname>",
1080 "user": "User Name <user@hostname>",
1075 "date": [1000000, 0],
1081 "date": [1000000, 0],
1076 "desc": "second",
1082 "desc": "second",
1077 "bookmarks": [],
1083 "bookmarks": [],
1078 "tags": [],
1084 "tags": [],
1079 "parents": ["0000000000000000000000000000000000000000"],
1085 "parents": ["0000000000000000000000000000000000000000"],
1080 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1086 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1081 "extra": {"branch": "default"},
1087 "extra": {"branch": "default"},
1082 "modified": [],
1088 "modified": [],
1083 "added": ["second"],
1089 "added": ["second"],
1084 "removed": []
1090 "removed": []
1085 },
1091 },
1086 {
1092 {
1087 "rev": 6,
1093 "rev": 6,
1088 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1094 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1089 "branch": "default",
1095 "branch": "default",
1090 "phase": "draft",
1096 "phase": "draft",
1091 "user": "person",
1097 "user": "person",
1092 "date": [1500001, 0],
1098 "date": [1500001, 0],
1093 "desc": "merge",
1099 "desc": "merge",
1094 "bookmarks": [],
1100 "bookmarks": [],
1095 "tags": [],
1101 "tags": [],
1096 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1102 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1097 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1103 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1098 "extra": {"branch": "default"},
1104 "extra": {"branch": "default"},
1099 "modified": [],
1105 "modified": [],
1100 "added": [],
1106 "added": [],
1101 "removed": []
1107 "removed": []
1102 },
1108 },
1103 {
1109 {
1104 "rev": 5,
1110 "rev": 5,
1105 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1111 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1106 "branch": "default",
1112 "branch": "default",
1107 "phase": "draft",
1113 "phase": "draft",
1108 "user": "person",
1114 "user": "person",
1109 "date": [1500000, 0],
1115 "date": [1500000, 0],
1110 "desc": "new head",
1116 "desc": "new head",
1111 "bookmarks": [],
1117 "bookmarks": [],
1112 "tags": [],
1118 "tags": [],
1113 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1119 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1114 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1120 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1115 "extra": {"branch": "default"},
1121 "extra": {"branch": "default"},
1116 "modified": [],
1122 "modified": [],
1117 "added": ["d"],
1123 "added": ["d"],
1118 "removed": []
1124 "removed": []
1119 },
1125 },
1120 {
1126 {
1121 "rev": 4,
1127 "rev": 4,
1122 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1128 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1123 "branch": "foo",
1129 "branch": "foo",
1124 "phase": "draft",
1130 "phase": "draft",
1125 "user": "person",
1131 "user": "person",
1126 "date": [1400000, 0],
1132 "date": [1400000, 0],
1127 "desc": "new branch",
1133 "desc": "new branch",
1128 "bookmarks": [],
1134 "bookmarks": [],
1129 "tags": [],
1135 "tags": [],
1130 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1136 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1131 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1137 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1132 "extra": {"branch": "foo"},
1138 "extra": {"branch": "foo"},
1133 "modified": [],
1139 "modified": [],
1134 "added": [],
1140 "added": [],
1135 "removed": []
1141 "removed": []
1136 },
1142 },
1137 {
1143 {
1138 "rev": 3,
1144 "rev": 3,
1139 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1145 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1140 "branch": "default",
1146 "branch": "default",
1141 "phase": "draft",
1147 "phase": "draft",
1142 "user": "person",
1148 "user": "person",
1143 "date": [1300000, 0],
1149 "date": [1300000, 0],
1144 "desc": "no user, no domain",
1150 "desc": "no user, no domain",
1145 "bookmarks": [],
1151 "bookmarks": [],
1146 "tags": [],
1152 "tags": [],
1147 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1153 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1148 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1154 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1149 "extra": {"branch": "default"},
1155 "extra": {"branch": "default"},
1150 "modified": ["c"],
1156 "modified": ["c"],
1151 "added": [],
1157 "added": [],
1152 "removed": []
1158 "removed": []
1153 },
1159 },
1154 {
1160 {
1155 "rev": 2,
1161 "rev": 2,
1156 "node": "97054abb4ab824450e9164180baf491ae0078465",
1162 "node": "97054abb4ab824450e9164180baf491ae0078465",
1157 "branch": "default",
1163 "branch": "default",
1158 "phase": "draft",
1164 "phase": "draft",
1159 "user": "other@place",
1165 "user": "other@place",
1160 "date": [1200000, 0],
1166 "date": [1200000, 0],
1161 "desc": "no person",
1167 "desc": "no person",
1162 "bookmarks": [],
1168 "bookmarks": [],
1163 "tags": [],
1169 "tags": [],
1164 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1170 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1165 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1171 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1166 "extra": {"branch": "default"},
1172 "extra": {"branch": "default"},
1167 "modified": [],
1173 "modified": [],
1168 "added": ["c"],
1174 "added": ["c"],
1169 "removed": []
1175 "removed": []
1170 },
1176 },
1171 {
1177 {
1172 "rev": 1,
1178 "rev": 1,
1173 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1179 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1174 "branch": "default",
1180 "branch": "default",
1175 "phase": "draft",
1181 "phase": "draft",
1176 "user": "A. N. Other <other@place>",
1182 "user": "A. N. Other <other@place>",
1177 "date": [1100000, 0],
1183 "date": [1100000, 0],
1178 "desc": "other 1\nother 2\n\nother 3",
1184 "desc": "other 1\nother 2\n\nother 3",
1179 "bookmarks": [],
1185 "bookmarks": [],
1180 "tags": [],
1186 "tags": [],
1181 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1187 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1182 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1188 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1183 "extra": {"branch": "default"},
1189 "extra": {"branch": "default"},
1184 "modified": [],
1190 "modified": [],
1185 "added": ["b"],
1191 "added": ["b"],
1186 "removed": []
1192 "removed": []
1187 },
1193 },
1188 {
1194 {
1189 "rev": 0,
1195 "rev": 0,
1190 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1196 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1191 "branch": "default",
1197 "branch": "default",
1192 "phase": "draft",
1198 "phase": "draft",
1193 "user": "User Name <user@hostname>",
1199 "user": "User Name <user@hostname>",
1194 "date": [1000000, 0],
1200 "date": [1000000, 0],
1195 "desc": "line 1\nline 2",
1201 "desc": "line 1\nline 2",
1196 "bookmarks": [],
1202 "bookmarks": [],
1197 "tags": [],
1203 "tags": [],
1198 "parents": ["0000000000000000000000000000000000000000"],
1204 "parents": ["0000000000000000000000000000000000000000"],
1199 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1205 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1200 "extra": {"branch": "default"},
1206 "extra": {"branch": "default"},
1201 "modified": [],
1207 "modified": [],
1202 "added": ["a"],
1208 "added": ["a"],
1203 "removed": []
1209 "removed": []
1204 }
1210 }
1205 ]
1211 ]
1206
1212
1207 Error if style not readable:
1213 Error if style not readable:
1208
1214
1209 #if unix-permissions no-root
1215 #if unix-permissions no-root
1210 $ touch q
1216 $ touch q
1211 $ chmod 0 q
1217 $ chmod 0 q
1212 $ hg log --style ./q
1218 $ hg log --style ./q
1213 abort: Permission denied: ./q
1219 abort: Permission denied: ./q
1214 [255]
1220 [255]
1215 #endif
1221 #endif
1216
1222
1217 Error if no style:
1223 Error if no style:
1218
1224
1219 $ hg log --style notexist
1225 $ hg log --style notexist
1220 abort: style 'notexist' not found
1226 abort: style 'notexist' not found
1221 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1227 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1222 [255]
1228 [255]
1223
1229
1224 $ hg log -T list
1230 $ hg log -T list
1225 available styles: bisect, changelog, compact, default, phases, show, status, xml
1231 available styles: bisect, changelog, compact, default, phases, show, status, xml
1226 abort: specify a template
1232 abort: specify a template
1227 [255]
1233 [255]
1228
1234
1229 Error if style missing key:
1235 Error if style missing key:
1230
1236
1231 $ echo 'q = q' > t
1237 $ echo 'q = q' > t
1232 $ hg log --style ./t
1238 $ hg log --style ./t
1233 abort: "changeset" not in template map
1239 abort: "changeset" not in template map
1234 [255]
1240 [255]
1235
1241
1236 Error if style missing value:
1242 Error if style missing value:
1237
1243
1238 $ echo 'changeset =' > t
1244 $ echo 'changeset =' > t
1239 $ hg log --style t
1245 $ hg log --style t
1240 hg: parse error at t:1: missing value
1246 hg: parse error at t:1: missing value
1241 [255]
1247 [255]
1242
1248
1243 Error if include fails:
1249 Error if include fails:
1244
1250
1245 $ echo 'changeset = q' >> t
1251 $ echo 'changeset = q' >> t
1246 #if unix-permissions no-root
1252 #if unix-permissions no-root
1247 $ hg log --style ./t
1253 $ hg log --style ./t
1248 abort: template file ./q: Permission denied
1254 abort: template file ./q: Permission denied
1249 [255]
1255 [255]
1250 $ rm -f q
1256 $ rm -f q
1251 #endif
1257 #endif
1252
1258
1253 Include works:
1259 Include works:
1254
1260
1255 $ echo '{rev}' > q
1261 $ echo '{rev}' > q
1256 $ hg log --style ./t
1262 $ hg log --style ./t
1257 8
1263 8
1258 7
1264 7
1259 6
1265 6
1260 5
1266 5
1261 4
1267 4
1262 3
1268 3
1263 2
1269 2
1264 1
1270 1
1265 0
1271 0
1266
1272
1267 Check that recursive reference does not fall into RuntimeError (issue4758):
1273 Check that recursive reference does not fall into RuntimeError (issue4758):
1268
1274
1269 common mistake:
1275 common mistake:
1270
1276
1271 $ cat << EOF > issue4758
1277 $ cat << EOF > issue4758
1272 > changeset = '{changeset}\n'
1278 > changeset = '{changeset}\n'
1273 > EOF
1279 > EOF
1274 $ hg log --style ./issue4758
1280 $ hg log --style ./issue4758
1275 abort: recursive reference 'changeset' in template
1281 abort: recursive reference 'changeset' in template
1276 [255]
1282 [255]
1277
1283
1278 circular reference:
1284 circular reference:
1279
1285
1280 $ cat << EOF > issue4758
1286 $ cat << EOF > issue4758
1281 > changeset = '{foo}'
1287 > changeset = '{foo}'
1282 > foo = '{changeset}'
1288 > foo = '{changeset}'
1283 > EOF
1289 > EOF
1284 $ hg log --style ./issue4758
1290 $ hg log --style ./issue4758
1285 abort: recursive reference 'foo' in template
1291 abort: recursive reference 'foo' in template
1286 [255]
1292 [255]
1287
1293
1288 buildmap() -> gettemplate(), where no thunk was made:
1294 buildmap() -> gettemplate(), where no thunk was made:
1289
1295
1290 $ cat << EOF > issue4758
1296 $ cat << EOF > issue4758
1291 > changeset = '{files % changeset}\n'
1297 > changeset = '{files % changeset}\n'
1292 > EOF
1298 > EOF
1293 $ hg log --style ./issue4758
1299 $ hg log --style ./issue4758
1294 abort: recursive reference 'changeset' in template
1300 abort: recursive reference 'changeset' in template
1295 [255]
1301 [255]
1296
1302
1297 not a recursion if a keyword of the same name exists:
1303 not a recursion if a keyword of the same name exists:
1298
1304
1299 $ cat << EOF > issue4758
1305 $ cat << EOF > issue4758
1300 > changeset = '{tags % rev}'
1306 > changeset = '{tags % rev}'
1301 > rev = '{rev} {tag}\n'
1307 > rev = '{rev} {tag}\n'
1302 > EOF
1308 > EOF
1303 $ hg log --style ./issue4758 -r tip
1309 $ hg log --style ./issue4758 -r tip
1304 8 tip
1310 8 tip
1305
1311
1306 Check that {phase} works correctly on parents:
1312 Check that {phase} works correctly on parents:
1307
1313
1308 $ cat << EOF > parentphase
1314 $ cat << EOF > parentphase
1309 > changeset_debug = '{rev} ({phase}):{parents}\n'
1315 > changeset_debug = '{rev} ({phase}):{parents}\n'
1310 > parent = ' {rev} ({phase})'
1316 > parent = ' {rev} ({phase})'
1311 > EOF
1317 > EOF
1312 $ hg phase -r 5 --public
1318 $ hg phase -r 5 --public
1313 $ hg phase -r 7 --secret --force
1319 $ hg phase -r 7 --secret --force
1314 $ hg log --debug -G --style ./parentphase
1320 $ hg log --debug -G --style ./parentphase
1315 @ 8 (secret): 7 (secret) -1 (public)
1321 @ 8 (secret): 7 (secret) -1 (public)
1316 |
1322 |
1317 o 7 (secret): -1 (public) -1 (public)
1323 o 7 (secret): -1 (public) -1 (public)
1318
1324
1319 o 6 (draft): 5 (public) 4 (draft)
1325 o 6 (draft): 5 (public) 4 (draft)
1320 |\
1326 |\
1321 | o 5 (public): 3 (public) -1 (public)
1327 | o 5 (public): 3 (public) -1 (public)
1322 | |
1328 | |
1323 o | 4 (draft): 3 (public) -1 (public)
1329 o | 4 (draft): 3 (public) -1 (public)
1324 |/
1330 |/
1325 o 3 (public): 2 (public) -1 (public)
1331 o 3 (public): 2 (public) -1 (public)
1326 |
1332 |
1327 o 2 (public): 1 (public) -1 (public)
1333 o 2 (public): 1 (public) -1 (public)
1328 |
1334 |
1329 o 1 (public): 0 (public) -1 (public)
1335 o 1 (public): 0 (public) -1 (public)
1330 |
1336 |
1331 o 0 (public): -1 (public) -1 (public)
1337 o 0 (public): -1 (public) -1 (public)
1332
1338
1333
1339
1334 Missing non-standard names give no error (backward compatibility):
1340 Missing non-standard names give no error (backward compatibility):
1335
1341
1336 $ echo "changeset = '{c}'" > t
1342 $ echo "changeset = '{c}'" > t
1337 $ hg log --style ./t
1343 $ hg log --style ./t
1338
1344
1339 Defining non-standard name works:
1345 Defining non-standard name works:
1340
1346
1341 $ cat <<EOF > t
1347 $ cat <<EOF > t
1342 > changeset = '{c}'
1348 > changeset = '{c}'
1343 > c = q
1349 > c = q
1344 > EOF
1350 > EOF
1345 $ hg log --style ./t
1351 $ hg log --style ./t
1346 8
1352 8
1347 7
1353 7
1348 6
1354 6
1349 5
1355 5
1350 4
1356 4
1351 3
1357 3
1352 2
1358 2
1353 1
1359 1
1354 0
1360 0
1355
1361
1356 ui.style works:
1362 ui.style works:
1357
1363
1358 $ echo '[ui]' > .hg/hgrc
1364 $ echo '[ui]' > .hg/hgrc
1359 $ echo 'style = t' >> .hg/hgrc
1365 $ echo 'style = t' >> .hg/hgrc
1360 $ hg log
1366 $ hg log
1361 8
1367 8
1362 7
1368 7
1363 6
1369 6
1364 5
1370 5
1365 4
1371 4
1366 3
1372 3
1367 2
1373 2
1368 1
1374 1
1369 0
1375 0
1370
1376
1371
1377
1372 Issue338:
1378 Issue338:
1373
1379
1374 $ hg log --style=changelog > changelog
1380 $ hg log --style=changelog > changelog
1375
1381
1376 $ cat changelog
1382 $ cat changelog
1377 2020-01-01 test <test>
1383 2020-01-01 test <test>
1378
1384
1379 * fourth, second, third:
1385 * fourth, second, third:
1380 third
1386 third
1381 [95c24699272e] [tip]
1387 [95c24699272e] [tip]
1382
1388
1383 1970-01-12 User Name <user@hostname>
1389 1970-01-12 User Name <user@hostname>
1384
1390
1385 * second:
1391 * second:
1386 second
1392 second
1387 [29114dbae42b]
1393 [29114dbae42b]
1388
1394
1389 1970-01-18 person <person>
1395 1970-01-18 person <person>
1390
1396
1391 * merge
1397 * merge
1392 [d41e714fe50d]
1398 [d41e714fe50d]
1393
1399
1394 * d:
1400 * d:
1395 new head
1401 new head
1396 [13207e5a10d9]
1402 [13207e5a10d9]
1397
1403
1398 1970-01-17 person <person>
1404 1970-01-17 person <person>
1399
1405
1400 * new branch
1406 * new branch
1401 [bbe44766e73d] <foo>
1407 [bbe44766e73d] <foo>
1402
1408
1403 1970-01-16 person <person>
1409 1970-01-16 person <person>
1404
1410
1405 * c:
1411 * c:
1406 no user, no domain
1412 no user, no domain
1407 [10e46f2dcbf4]
1413 [10e46f2dcbf4]
1408
1414
1409 1970-01-14 other <other@place>
1415 1970-01-14 other <other@place>
1410
1416
1411 * c:
1417 * c:
1412 no person
1418 no person
1413 [97054abb4ab8]
1419 [97054abb4ab8]
1414
1420
1415 1970-01-13 A. N. Other <other@place>
1421 1970-01-13 A. N. Other <other@place>
1416
1422
1417 * b:
1423 * b:
1418 other 1 other 2
1424 other 1 other 2
1419
1425
1420 other 3
1426 other 3
1421 [b608e9d1a3f0]
1427 [b608e9d1a3f0]
1422
1428
1423 1970-01-12 User Name <user@hostname>
1429 1970-01-12 User Name <user@hostname>
1424
1430
1425 * a:
1431 * a:
1426 line 1 line 2
1432 line 1 line 2
1427 [1e4e1b8f71e0]
1433 [1e4e1b8f71e0]
1428
1434
1429
1435
1430 Issue2130: xml output for 'hg heads' is malformed
1436 Issue2130: xml output for 'hg heads' is malformed
1431
1437
1432 $ hg heads --style changelog
1438 $ hg heads --style changelog
1433 2020-01-01 test <test>
1439 2020-01-01 test <test>
1434
1440
1435 * fourth, second, third:
1441 * fourth, second, third:
1436 third
1442 third
1437 [95c24699272e] [tip]
1443 [95c24699272e] [tip]
1438
1444
1439 1970-01-18 person <person>
1445 1970-01-18 person <person>
1440
1446
1441 * merge
1447 * merge
1442 [d41e714fe50d]
1448 [d41e714fe50d]
1443
1449
1444 1970-01-17 person <person>
1450 1970-01-17 person <person>
1445
1451
1446 * new branch
1452 * new branch
1447 [bbe44766e73d] <foo>
1453 [bbe44766e73d] <foo>
1448
1454
1449
1455
1450 Keys work:
1456 Keys work:
1451
1457
1452 $ for key in author branch branches date desc file_adds file_dels file_mods \
1458 $ for key in author branch branches date desc file_adds file_dels file_mods \
1453 > file_copies file_copies_switch files \
1459 > file_copies file_copies_switch files \
1454 > manifest node parents rev tags diffstat extras \
1460 > manifest node parents rev tags diffstat extras \
1455 > p1rev p2rev p1node p2node; do
1461 > p1rev p2rev p1node p2node; do
1456 > for mode in '' --verbose --debug; do
1462 > for mode in '' --verbose --debug; do
1457 > hg log $mode --template "$key$mode: {$key}\n"
1463 > hg log $mode --template "$key$mode: {$key}\n"
1458 > done
1464 > done
1459 > done
1465 > done
1460 author: test
1466 author: test
1461 author: User Name <user@hostname>
1467 author: User Name <user@hostname>
1462 author: person
1468 author: person
1463 author: person
1469 author: person
1464 author: person
1470 author: person
1465 author: person
1471 author: person
1466 author: other@place
1472 author: other@place
1467 author: A. N. Other <other@place>
1473 author: A. N. Other <other@place>
1468 author: User Name <user@hostname>
1474 author: User Name <user@hostname>
1469 author--verbose: test
1475 author--verbose: test
1470 author--verbose: User Name <user@hostname>
1476 author--verbose: User Name <user@hostname>
1471 author--verbose: person
1477 author--verbose: person
1472 author--verbose: person
1478 author--verbose: person
1473 author--verbose: person
1479 author--verbose: person
1474 author--verbose: person
1480 author--verbose: person
1475 author--verbose: other@place
1481 author--verbose: other@place
1476 author--verbose: A. N. Other <other@place>
1482 author--verbose: A. N. Other <other@place>
1477 author--verbose: User Name <user@hostname>
1483 author--verbose: User Name <user@hostname>
1478 author--debug: test
1484 author--debug: test
1479 author--debug: User Name <user@hostname>
1485 author--debug: User Name <user@hostname>
1480 author--debug: person
1486 author--debug: person
1481 author--debug: person
1487 author--debug: person
1482 author--debug: person
1488 author--debug: person
1483 author--debug: person
1489 author--debug: person
1484 author--debug: other@place
1490 author--debug: other@place
1485 author--debug: A. N. Other <other@place>
1491 author--debug: A. N. Other <other@place>
1486 author--debug: User Name <user@hostname>
1492 author--debug: User Name <user@hostname>
1487 branch: default
1493 branch: default
1488 branch: default
1494 branch: default
1489 branch: default
1495 branch: default
1490 branch: default
1496 branch: default
1491 branch: foo
1497 branch: foo
1492 branch: default
1498 branch: default
1493 branch: default
1499 branch: default
1494 branch: default
1500 branch: default
1495 branch: default
1501 branch: default
1496 branch--verbose: default
1502 branch--verbose: default
1497 branch--verbose: default
1503 branch--verbose: default
1498 branch--verbose: default
1504 branch--verbose: default
1499 branch--verbose: default
1505 branch--verbose: default
1500 branch--verbose: foo
1506 branch--verbose: foo
1501 branch--verbose: default
1507 branch--verbose: default
1502 branch--verbose: default
1508 branch--verbose: default
1503 branch--verbose: default
1509 branch--verbose: default
1504 branch--verbose: default
1510 branch--verbose: default
1505 branch--debug: default
1511 branch--debug: default
1506 branch--debug: default
1512 branch--debug: default
1507 branch--debug: default
1513 branch--debug: default
1508 branch--debug: default
1514 branch--debug: default
1509 branch--debug: foo
1515 branch--debug: foo
1510 branch--debug: default
1516 branch--debug: default
1511 branch--debug: default
1517 branch--debug: default
1512 branch--debug: default
1518 branch--debug: default
1513 branch--debug: default
1519 branch--debug: default
1514 branches:
1520 branches:
1515 branches:
1521 branches:
1516 branches:
1522 branches:
1517 branches:
1523 branches:
1518 branches: foo
1524 branches: foo
1519 branches:
1525 branches:
1520 branches:
1526 branches:
1521 branches:
1527 branches:
1522 branches:
1528 branches:
1523 branches--verbose:
1529 branches--verbose:
1524 branches--verbose:
1530 branches--verbose:
1525 branches--verbose:
1531 branches--verbose:
1526 branches--verbose:
1532 branches--verbose:
1527 branches--verbose: foo
1533 branches--verbose: foo
1528 branches--verbose:
1534 branches--verbose:
1529 branches--verbose:
1535 branches--verbose:
1530 branches--verbose:
1536 branches--verbose:
1531 branches--verbose:
1537 branches--verbose:
1532 branches--debug:
1538 branches--debug:
1533 branches--debug:
1539 branches--debug:
1534 branches--debug:
1540 branches--debug:
1535 branches--debug:
1541 branches--debug:
1536 branches--debug: foo
1542 branches--debug: foo
1537 branches--debug:
1543 branches--debug:
1538 branches--debug:
1544 branches--debug:
1539 branches--debug:
1545 branches--debug:
1540 branches--debug:
1546 branches--debug:
1541 date: 1577872860.00
1547 date: 1577872860.00
1542 date: 1000000.00
1548 date: 1000000.00
1543 date: 1500001.00
1549 date: 1500001.00
1544 date: 1500000.00
1550 date: 1500000.00
1545 date: 1400000.00
1551 date: 1400000.00
1546 date: 1300000.00
1552 date: 1300000.00
1547 date: 1200000.00
1553 date: 1200000.00
1548 date: 1100000.00
1554 date: 1100000.00
1549 date: 1000000.00
1555 date: 1000000.00
1550 date--verbose: 1577872860.00
1556 date--verbose: 1577872860.00
1551 date--verbose: 1000000.00
1557 date--verbose: 1000000.00
1552 date--verbose: 1500001.00
1558 date--verbose: 1500001.00
1553 date--verbose: 1500000.00
1559 date--verbose: 1500000.00
1554 date--verbose: 1400000.00
1560 date--verbose: 1400000.00
1555 date--verbose: 1300000.00
1561 date--verbose: 1300000.00
1556 date--verbose: 1200000.00
1562 date--verbose: 1200000.00
1557 date--verbose: 1100000.00
1563 date--verbose: 1100000.00
1558 date--verbose: 1000000.00
1564 date--verbose: 1000000.00
1559 date--debug: 1577872860.00
1565 date--debug: 1577872860.00
1560 date--debug: 1000000.00
1566 date--debug: 1000000.00
1561 date--debug: 1500001.00
1567 date--debug: 1500001.00
1562 date--debug: 1500000.00
1568 date--debug: 1500000.00
1563 date--debug: 1400000.00
1569 date--debug: 1400000.00
1564 date--debug: 1300000.00
1570 date--debug: 1300000.00
1565 date--debug: 1200000.00
1571 date--debug: 1200000.00
1566 date--debug: 1100000.00
1572 date--debug: 1100000.00
1567 date--debug: 1000000.00
1573 date--debug: 1000000.00
1568 desc: third
1574 desc: third
1569 desc: second
1575 desc: second
1570 desc: merge
1576 desc: merge
1571 desc: new head
1577 desc: new head
1572 desc: new branch
1578 desc: new branch
1573 desc: no user, no domain
1579 desc: no user, no domain
1574 desc: no person
1580 desc: no person
1575 desc: other 1
1581 desc: other 1
1576 other 2
1582 other 2
1577
1583
1578 other 3
1584 other 3
1579 desc: line 1
1585 desc: line 1
1580 line 2
1586 line 2
1581 desc--verbose: third
1587 desc--verbose: third
1582 desc--verbose: second
1588 desc--verbose: second
1583 desc--verbose: merge
1589 desc--verbose: merge
1584 desc--verbose: new head
1590 desc--verbose: new head
1585 desc--verbose: new branch
1591 desc--verbose: new branch
1586 desc--verbose: no user, no domain
1592 desc--verbose: no user, no domain
1587 desc--verbose: no person
1593 desc--verbose: no person
1588 desc--verbose: other 1
1594 desc--verbose: other 1
1589 other 2
1595 other 2
1590
1596
1591 other 3
1597 other 3
1592 desc--verbose: line 1
1598 desc--verbose: line 1
1593 line 2
1599 line 2
1594 desc--debug: third
1600 desc--debug: third
1595 desc--debug: second
1601 desc--debug: second
1596 desc--debug: merge
1602 desc--debug: merge
1597 desc--debug: new head
1603 desc--debug: new head
1598 desc--debug: new branch
1604 desc--debug: new branch
1599 desc--debug: no user, no domain
1605 desc--debug: no user, no domain
1600 desc--debug: no person
1606 desc--debug: no person
1601 desc--debug: other 1
1607 desc--debug: other 1
1602 other 2
1608 other 2
1603
1609
1604 other 3
1610 other 3
1605 desc--debug: line 1
1611 desc--debug: line 1
1606 line 2
1612 line 2
1607 file_adds: fourth third
1613 file_adds: fourth third
1608 file_adds: second
1614 file_adds: second
1609 file_adds:
1615 file_adds:
1610 file_adds: d
1616 file_adds: d
1611 file_adds:
1617 file_adds:
1612 file_adds:
1618 file_adds:
1613 file_adds: c
1619 file_adds: c
1614 file_adds: b
1620 file_adds: b
1615 file_adds: a
1621 file_adds: a
1616 file_adds--verbose: fourth third
1622 file_adds--verbose: fourth third
1617 file_adds--verbose: second
1623 file_adds--verbose: second
1618 file_adds--verbose:
1624 file_adds--verbose:
1619 file_adds--verbose: d
1625 file_adds--verbose: d
1620 file_adds--verbose:
1626 file_adds--verbose:
1621 file_adds--verbose:
1627 file_adds--verbose:
1622 file_adds--verbose: c
1628 file_adds--verbose: c
1623 file_adds--verbose: b
1629 file_adds--verbose: b
1624 file_adds--verbose: a
1630 file_adds--verbose: a
1625 file_adds--debug: fourth third
1631 file_adds--debug: fourth third
1626 file_adds--debug: second
1632 file_adds--debug: second
1627 file_adds--debug:
1633 file_adds--debug:
1628 file_adds--debug: d
1634 file_adds--debug: d
1629 file_adds--debug:
1635 file_adds--debug:
1630 file_adds--debug:
1636 file_adds--debug:
1631 file_adds--debug: c
1637 file_adds--debug: c
1632 file_adds--debug: b
1638 file_adds--debug: b
1633 file_adds--debug: a
1639 file_adds--debug: a
1634 file_dels: second
1640 file_dels: second
1635 file_dels:
1641 file_dels:
1636 file_dels:
1642 file_dels:
1637 file_dels:
1643 file_dels:
1638 file_dels:
1644 file_dels:
1639 file_dels:
1645 file_dels:
1640 file_dels:
1646 file_dels:
1641 file_dels:
1647 file_dels:
1642 file_dels:
1648 file_dels:
1643 file_dels--verbose: second
1649 file_dels--verbose: second
1644 file_dels--verbose:
1650 file_dels--verbose:
1645 file_dels--verbose:
1651 file_dels--verbose:
1646 file_dels--verbose:
1652 file_dels--verbose:
1647 file_dels--verbose:
1653 file_dels--verbose:
1648 file_dels--verbose:
1654 file_dels--verbose:
1649 file_dels--verbose:
1655 file_dels--verbose:
1650 file_dels--verbose:
1656 file_dels--verbose:
1651 file_dels--verbose:
1657 file_dels--verbose:
1652 file_dels--debug: second
1658 file_dels--debug: second
1653 file_dels--debug:
1659 file_dels--debug:
1654 file_dels--debug:
1660 file_dels--debug:
1655 file_dels--debug:
1661 file_dels--debug:
1656 file_dels--debug:
1662 file_dels--debug:
1657 file_dels--debug:
1663 file_dels--debug:
1658 file_dels--debug:
1664 file_dels--debug:
1659 file_dels--debug:
1665 file_dels--debug:
1660 file_dels--debug:
1666 file_dels--debug:
1661 file_mods:
1667 file_mods:
1662 file_mods:
1668 file_mods:
1663 file_mods:
1669 file_mods:
1664 file_mods:
1670 file_mods:
1665 file_mods:
1671 file_mods:
1666 file_mods: c
1672 file_mods: c
1667 file_mods:
1673 file_mods:
1668 file_mods:
1674 file_mods:
1669 file_mods:
1675 file_mods:
1670 file_mods--verbose:
1676 file_mods--verbose:
1671 file_mods--verbose:
1677 file_mods--verbose:
1672 file_mods--verbose:
1678 file_mods--verbose:
1673 file_mods--verbose:
1679 file_mods--verbose:
1674 file_mods--verbose:
1680 file_mods--verbose:
1675 file_mods--verbose: c
1681 file_mods--verbose: c
1676 file_mods--verbose:
1682 file_mods--verbose:
1677 file_mods--verbose:
1683 file_mods--verbose:
1678 file_mods--verbose:
1684 file_mods--verbose:
1679 file_mods--debug:
1685 file_mods--debug:
1680 file_mods--debug:
1686 file_mods--debug:
1681 file_mods--debug:
1687 file_mods--debug:
1682 file_mods--debug:
1688 file_mods--debug:
1683 file_mods--debug:
1689 file_mods--debug:
1684 file_mods--debug: c
1690 file_mods--debug: c
1685 file_mods--debug:
1691 file_mods--debug:
1686 file_mods--debug:
1692 file_mods--debug:
1687 file_mods--debug:
1693 file_mods--debug:
1688 file_copies: fourth (second)
1694 file_copies: fourth (second)
1689 file_copies:
1695 file_copies:
1690 file_copies:
1696 file_copies:
1691 file_copies:
1697 file_copies:
1692 file_copies:
1698 file_copies:
1693 file_copies:
1699 file_copies:
1694 file_copies:
1700 file_copies:
1695 file_copies:
1701 file_copies:
1696 file_copies:
1702 file_copies:
1697 file_copies--verbose: fourth (second)
1703 file_copies--verbose: fourth (second)
1698 file_copies--verbose:
1704 file_copies--verbose:
1699 file_copies--verbose:
1705 file_copies--verbose:
1700 file_copies--verbose:
1706 file_copies--verbose:
1701 file_copies--verbose:
1707 file_copies--verbose:
1702 file_copies--verbose:
1708 file_copies--verbose:
1703 file_copies--verbose:
1709 file_copies--verbose:
1704 file_copies--verbose:
1710 file_copies--verbose:
1705 file_copies--verbose:
1711 file_copies--verbose:
1706 file_copies--debug: fourth (second)
1712 file_copies--debug: fourth (second)
1707 file_copies--debug:
1713 file_copies--debug:
1708 file_copies--debug:
1714 file_copies--debug:
1709 file_copies--debug:
1715 file_copies--debug:
1710 file_copies--debug:
1716 file_copies--debug:
1711 file_copies--debug:
1717 file_copies--debug:
1712 file_copies--debug:
1718 file_copies--debug:
1713 file_copies--debug:
1719 file_copies--debug:
1714 file_copies--debug:
1720 file_copies--debug:
1715 file_copies_switch:
1721 file_copies_switch:
1716 file_copies_switch:
1722 file_copies_switch:
1717 file_copies_switch:
1723 file_copies_switch:
1718 file_copies_switch:
1724 file_copies_switch:
1719 file_copies_switch:
1725 file_copies_switch:
1720 file_copies_switch:
1726 file_copies_switch:
1721 file_copies_switch:
1727 file_copies_switch:
1722 file_copies_switch:
1728 file_copies_switch:
1723 file_copies_switch:
1729 file_copies_switch:
1724 file_copies_switch--verbose:
1730 file_copies_switch--verbose:
1725 file_copies_switch--verbose:
1731 file_copies_switch--verbose:
1726 file_copies_switch--verbose:
1732 file_copies_switch--verbose:
1727 file_copies_switch--verbose:
1733 file_copies_switch--verbose:
1728 file_copies_switch--verbose:
1734 file_copies_switch--verbose:
1729 file_copies_switch--verbose:
1735 file_copies_switch--verbose:
1730 file_copies_switch--verbose:
1736 file_copies_switch--verbose:
1731 file_copies_switch--verbose:
1737 file_copies_switch--verbose:
1732 file_copies_switch--verbose:
1738 file_copies_switch--verbose:
1733 file_copies_switch--debug:
1739 file_copies_switch--debug:
1734 file_copies_switch--debug:
1740 file_copies_switch--debug:
1735 file_copies_switch--debug:
1741 file_copies_switch--debug:
1736 file_copies_switch--debug:
1742 file_copies_switch--debug:
1737 file_copies_switch--debug:
1743 file_copies_switch--debug:
1738 file_copies_switch--debug:
1744 file_copies_switch--debug:
1739 file_copies_switch--debug:
1745 file_copies_switch--debug:
1740 file_copies_switch--debug:
1746 file_copies_switch--debug:
1741 file_copies_switch--debug:
1747 file_copies_switch--debug:
1742 files: fourth second third
1748 files: fourth second third
1743 files: second
1749 files: second
1744 files:
1750 files:
1745 files: d
1751 files: d
1746 files:
1752 files:
1747 files: c
1753 files: c
1748 files: c
1754 files: c
1749 files: b
1755 files: b
1750 files: a
1756 files: a
1751 files--verbose: fourth second third
1757 files--verbose: fourth second third
1752 files--verbose: second
1758 files--verbose: second
1753 files--verbose:
1759 files--verbose:
1754 files--verbose: d
1760 files--verbose: d
1755 files--verbose:
1761 files--verbose:
1756 files--verbose: c
1762 files--verbose: c
1757 files--verbose: c
1763 files--verbose: c
1758 files--verbose: b
1764 files--verbose: b
1759 files--verbose: a
1765 files--verbose: a
1760 files--debug: fourth second third
1766 files--debug: fourth second third
1761 files--debug: second
1767 files--debug: second
1762 files--debug:
1768 files--debug:
1763 files--debug: d
1769 files--debug: d
1764 files--debug:
1770 files--debug:
1765 files--debug: c
1771 files--debug: c
1766 files--debug: c
1772 files--debug: c
1767 files--debug: b
1773 files--debug: b
1768 files--debug: a
1774 files--debug: a
1769 manifest: 6:94961b75a2da
1775 manifest: 6:94961b75a2da
1770 manifest: 5:f2dbc354b94e
1776 manifest: 5:f2dbc354b94e
1771 manifest: 4:4dc3def4f9b4
1777 manifest: 4:4dc3def4f9b4
1772 manifest: 4:4dc3def4f9b4
1778 manifest: 4:4dc3def4f9b4
1773 manifest: 3:cb5a1327723b
1779 manifest: 3:cb5a1327723b
1774 manifest: 3:cb5a1327723b
1780 manifest: 3:cb5a1327723b
1775 manifest: 2:6e0e82995c35
1781 manifest: 2:6e0e82995c35
1776 manifest: 1:4e8d705b1e53
1782 manifest: 1:4e8d705b1e53
1777 manifest: 0:a0c8bcbbb45c
1783 manifest: 0:a0c8bcbbb45c
1778 manifest--verbose: 6:94961b75a2da
1784 manifest--verbose: 6:94961b75a2da
1779 manifest--verbose: 5:f2dbc354b94e
1785 manifest--verbose: 5:f2dbc354b94e
1780 manifest--verbose: 4:4dc3def4f9b4
1786 manifest--verbose: 4:4dc3def4f9b4
1781 manifest--verbose: 4:4dc3def4f9b4
1787 manifest--verbose: 4:4dc3def4f9b4
1782 manifest--verbose: 3:cb5a1327723b
1788 manifest--verbose: 3:cb5a1327723b
1783 manifest--verbose: 3:cb5a1327723b
1789 manifest--verbose: 3:cb5a1327723b
1784 manifest--verbose: 2:6e0e82995c35
1790 manifest--verbose: 2:6e0e82995c35
1785 manifest--verbose: 1:4e8d705b1e53
1791 manifest--verbose: 1:4e8d705b1e53
1786 manifest--verbose: 0:a0c8bcbbb45c
1792 manifest--verbose: 0:a0c8bcbbb45c
1787 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1793 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1788 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1794 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1789 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1795 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1790 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1796 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1791 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1797 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1792 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1798 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1793 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1799 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1794 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1800 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1795 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1801 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1796 node: 95c24699272ef57d062b8bccc32c878bf841784a
1802 node: 95c24699272ef57d062b8bccc32c878bf841784a
1797 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1803 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1798 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1804 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1799 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1805 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1800 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1806 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1801 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1807 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1802 node: 97054abb4ab824450e9164180baf491ae0078465
1808 node: 97054abb4ab824450e9164180baf491ae0078465
1803 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1809 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1804 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1810 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1805 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1811 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1806 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1812 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1807 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1813 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1808 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1814 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1809 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1815 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1810 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1816 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1811 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1817 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1812 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1818 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1813 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1819 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1814 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1820 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1815 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1821 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1816 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1822 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1817 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1823 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1818 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1824 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1819 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1825 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1820 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1826 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1821 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1827 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1822 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1828 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1823 parents:
1829 parents:
1824 parents: -1:000000000000
1830 parents: -1:000000000000
1825 parents: 5:13207e5a10d9 4:bbe44766e73d
1831 parents: 5:13207e5a10d9 4:bbe44766e73d
1826 parents: 3:10e46f2dcbf4
1832 parents: 3:10e46f2dcbf4
1827 parents:
1833 parents:
1828 parents:
1834 parents:
1829 parents:
1835 parents:
1830 parents:
1836 parents:
1831 parents:
1837 parents:
1832 parents--verbose:
1838 parents--verbose:
1833 parents--verbose: -1:000000000000
1839 parents--verbose: -1:000000000000
1834 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1840 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1835 parents--verbose: 3:10e46f2dcbf4
1841 parents--verbose: 3:10e46f2dcbf4
1836 parents--verbose:
1842 parents--verbose:
1837 parents--verbose:
1843 parents--verbose:
1838 parents--verbose:
1844 parents--verbose:
1839 parents--verbose:
1845 parents--verbose:
1840 parents--verbose:
1846 parents--verbose:
1841 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1847 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1842 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1848 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1843 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1849 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1844 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1850 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1845 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1851 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1846 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1852 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1847 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1853 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1848 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1854 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1849 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1855 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1850 rev: 8
1856 rev: 8
1851 rev: 7
1857 rev: 7
1852 rev: 6
1858 rev: 6
1853 rev: 5
1859 rev: 5
1854 rev: 4
1860 rev: 4
1855 rev: 3
1861 rev: 3
1856 rev: 2
1862 rev: 2
1857 rev: 1
1863 rev: 1
1858 rev: 0
1864 rev: 0
1859 rev--verbose: 8
1865 rev--verbose: 8
1860 rev--verbose: 7
1866 rev--verbose: 7
1861 rev--verbose: 6
1867 rev--verbose: 6
1862 rev--verbose: 5
1868 rev--verbose: 5
1863 rev--verbose: 4
1869 rev--verbose: 4
1864 rev--verbose: 3
1870 rev--verbose: 3
1865 rev--verbose: 2
1871 rev--verbose: 2
1866 rev--verbose: 1
1872 rev--verbose: 1
1867 rev--verbose: 0
1873 rev--verbose: 0
1868 rev--debug: 8
1874 rev--debug: 8
1869 rev--debug: 7
1875 rev--debug: 7
1870 rev--debug: 6
1876 rev--debug: 6
1871 rev--debug: 5
1877 rev--debug: 5
1872 rev--debug: 4
1878 rev--debug: 4
1873 rev--debug: 3
1879 rev--debug: 3
1874 rev--debug: 2
1880 rev--debug: 2
1875 rev--debug: 1
1881 rev--debug: 1
1876 rev--debug: 0
1882 rev--debug: 0
1877 tags: tip
1883 tags: tip
1878 tags:
1884 tags:
1879 tags:
1885 tags:
1880 tags:
1886 tags:
1881 tags:
1887 tags:
1882 tags:
1888 tags:
1883 tags:
1889 tags:
1884 tags:
1890 tags:
1885 tags:
1891 tags:
1886 tags--verbose: tip
1892 tags--verbose: tip
1887 tags--verbose:
1893 tags--verbose:
1888 tags--verbose:
1894 tags--verbose:
1889 tags--verbose:
1895 tags--verbose:
1890 tags--verbose:
1896 tags--verbose:
1891 tags--verbose:
1897 tags--verbose:
1892 tags--verbose:
1898 tags--verbose:
1893 tags--verbose:
1899 tags--verbose:
1894 tags--verbose:
1900 tags--verbose:
1895 tags--debug: tip
1901 tags--debug: tip
1896 tags--debug:
1902 tags--debug:
1897 tags--debug:
1903 tags--debug:
1898 tags--debug:
1904 tags--debug:
1899 tags--debug:
1905 tags--debug:
1900 tags--debug:
1906 tags--debug:
1901 tags--debug:
1907 tags--debug:
1902 tags--debug:
1908 tags--debug:
1903 tags--debug:
1909 tags--debug:
1904 diffstat: 3: +2/-1
1910 diffstat: 3: +2/-1
1905 diffstat: 1: +1/-0
1911 diffstat: 1: +1/-0
1906 diffstat: 0: +0/-0
1912 diffstat: 0: +0/-0
1907 diffstat: 1: +1/-0
1913 diffstat: 1: +1/-0
1908 diffstat: 0: +0/-0
1914 diffstat: 0: +0/-0
1909 diffstat: 1: +1/-0
1915 diffstat: 1: +1/-0
1910 diffstat: 1: +4/-0
1916 diffstat: 1: +4/-0
1911 diffstat: 1: +2/-0
1917 diffstat: 1: +2/-0
1912 diffstat: 1: +1/-0
1918 diffstat: 1: +1/-0
1913 diffstat--verbose: 3: +2/-1
1919 diffstat--verbose: 3: +2/-1
1914 diffstat--verbose: 1: +1/-0
1920 diffstat--verbose: 1: +1/-0
1915 diffstat--verbose: 0: +0/-0
1921 diffstat--verbose: 0: +0/-0
1916 diffstat--verbose: 1: +1/-0
1922 diffstat--verbose: 1: +1/-0
1917 diffstat--verbose: 0: +0/-0
1923 diffstat--verbose: 0: +0/-0
1918 diffstat--verbose: 1: +1/-0
1924 diffstat--verbose: 1: +1/-0
1919 diffstat--verbose: 1: +4/-0
1925 diffstat--verbose: 1: +4/-0
1920 diffstat--verbose: 1: +2/-0
1926 diffstat--verbose: 1: +2/-0
1921 diffstat--verbose: 1: +1/-0
1927 diffstat--verbose: 1: +1/-0
1922 diffstat--debug: 3: +2/-1
1928 diffstat--debug: 3: +2/-1
1923 diffstat--debug: 1: +1/-0
1929 diffstat--debug: 1: +1/-0
1924 diffstat--debug: 0: +0/-0
1930 diffstat--debug: 0: +0/-0
1925 diffstat--debug: 1: +1/-0
1931 diffstat--debug: 1: +1/-0
1926 diffstat--debug: 0: +0/-0
1932 diffstat--debug: 0: +0/-0
1927 diffstat--debug: 1: +1/-0
1933 diffstat--debug: 1: +1/-0
1928 diffstat--debug: 1: +4/-0
1934 diffstat--debug: 1: +4/-0
1929 diffstat--debug: 1: +2/-0
1935 diffstat--debug: 1: +2/-0
1930 diffstat--debug: 1: +1/-0
1936 diffstat--debug: 1: +1/-0
1931 extras: branch=default
1937 extras: branch=default
1932 extras: branch=default
1938 extras: branch=default
1933 extras: branch=default
1939 extras: branch=default
1934 extras: branch=default
1940 extras: branch=default
1935 extras: branch=foo
1941 extras: branch=foo
1936 extras: branch=default
1942 extras: branch=default
1937 extras: branch=default
1943 extras: branch=default
1938 extras: branch=default
1944 extras: branch=default
1939 extras: branch=default
1945 extras: branch=default
1940 extras--verbose: branch=default
1946 extras--verbose: branch=default
1941 extras--verbose: branch=default
1947 extras--verbose: branch=default
1942 extras--verbose: branch=default
1948 extras--verbose: branch=default
1943 extras--verbose: branch=default
1949 extras--verbose: branch=default
1944 extras--verbose: branch=foo
1950 extras--verbose: branch=foo
1945 extras--verbose: branch=default
1951 extras--verbose: branch=default
1946 extras--verbose: branch=default
1952 extras--verbose: branch=default
1947 extras--verbose: branch=default
1953 extras--verbose: branch=default
1948 extras--verbose: branch=default
1954 extras--verbose: branch=default
1949 extras--debug: branch=default
1955 extras--debug: branch=default
1950 extras--debug: branch=default
1956 extras--debug: branch=default
1951 extras--debug: branch=default
1957 extras--debug: branch=default
1952 extras--debug: branch=default
1958 extras--debug: branch=default
1953 extras--debug: branch=foo
1959 extras--debug: branch=foo
1954 extras--debug: branch=default
1960 extras--debug: branch=default
1955 extras--debug: branch=default
1961 extras--debug: branch=default
1956 extras--debug: branch=default
1962 extras--debug: branch=default
1957 extras--debug: branch=default
1963 extras--debug: branch=default
1958 p1rev: 7
1964 p1rev: 7
1959 p1rev: -1
1965 p1rev: -1
1960 p1rev: 5
1966 p1rev: 5
1961 p1rev: 3
1967 p1rev: 3
1962 p1rev: 3
1968 p1rev: 3
1963 p1rev: 2
1969 p1rev: 2
1964 p1rev: 1
1970 p1rev: 1
1965 p1rev: 0
1971 p1rev: 0
1966 p1rev: -1
1972 p1rev: -1
1967 p1rev--verbose: 7
1973 p1rev--verbose: 7
1968 p1rev--verbose: -1
1974 p1rev--verbose: -1
1969 p1rev--verbose: 5
1975 p1rev--verbose: 5
1970 p1rev--verbose: 3
1976 p1rev--verbose: 3
1971 p1rev--verbose: 3
1977 p1rev--verbose: 3
1972 p1rev--verbose: 2
1978 p1rev--verbose: 2
1973 p1rev--verbose: 1
1979 p1rev--verbose: 1
1974 p1rev--verbose: 0
1980 p1rev--verbose: 0
1975 p1rev--verbose: -1
1981 p1rev--verbose: -1
1976 p1rev--debug: 7
1982 p1rev--debug: 7
1977 p1rev--debug: -1
1983 p1rev--debug: -1
1978 p1rev--debug: 5
1984 p1rev--debug: 5
1979 p1rev--debug: 3
1985 p1rev--debug: 3
1980 p1rev--debug: 3
1986 p1rev--debug: 3
1981 p1rev--debug: 2
1987 p1rev--debug: 2
1982 p1rev--debug: 1
1988 p1rev--debug: 1
1983 p1rev--debug: 0
1989 p1rev--debug: 0
1984 p1rev--debug: -1
1990 p1rev--debug: -1
1985 p2rev: -1
1991 p2rev: -1
1986 p2rev: -1
1992 p2rev: -1
1987 p2rev: 4
1993 p2rev: 4
1988 p2rev: -1
1994 p2rev: -1
1989 p2rev: -1
1995 p2rev: -1
1990 p2rev: -1
1996 p2rev: -1
1991 p2rev: -1
1997 p2rev: -1
1992 p2rev: -1
1998 p2rev: -1
1993 p2rev: -1
1999 p2rev: -1
1994 p2rev--verbose: -1
2000 p2rev--verbose: -1
1995 p2rev--verbose: -1
2001 p2rev--verbose: -1
1996 p2rev--verbose: 4
2002 p2rev--verbose: 4
1997 p2rev--verbose: -1
2003 p2rev--verbose: -1
1998 p2rev--verbose: -1
2004 p2rev--verbose: -1
1999 p2rev--verbose: -1
2005 p2rev--verbose: -1
2000 p2rev--verbose: -1
2006 p2rev--verbose: -1
2001 p2rev--verbose: -1
2007 p2rev--verbose: -1
2002 p2rev--verbose: -1
2008 p2rev--verbose: -1
2003 p2rev--debug: -1
2009 p2rev--debug: -1
2004 p2rev--debug: -1
2010 p2rev--debug: -1
2005 p2rev--debug: 4
2011 p2rev--debug: 4
2006 p2rev--debug: -1
2012 p2rev--debug: -1
2007 p2rev--debug: -1
2013 p2rev--debug: -1
2008 p2rev--debug: -1
2014 p2rev--debug: -1
2009 p2rev--debug: -1
2015 p2rev--debug: -1
2010 p2rev--debug: -1
2016 p2rev--debug: -1
2011 p2rev--debug: -1
2017 p2rev--debug: -1
2012 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2018 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2013 p1node: 0000000000000000000000000000000000000000
2019 p1node: 0000000000000000000000000000000000000000
2014 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
2020 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
2015 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2021 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2016 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2022 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2017 p1node: 97054abb4ab824450e9164180baf491ae0078465
2023 p1node: 97054abb4ab824450e9164180baf491ae0078465
2018 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2024 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2019 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
2025 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
2020 p1node: 0000000000000000000000000000000000000000
2026 p1node: 0000000000000000000000000000000000000000
2021 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2027 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2022 p1node--verbose: 0000000000000000000000000000000000000000
2028 p1node--verbose: 0000000000000000000000000000000000000000
2023 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
2029 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
2024 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2030 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2025 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2031 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2026 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2032 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2027 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2033 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2028 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2034 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2029 p1node--verbose: 0000000000000000000000000000000000000000
2035 p1node--verbose: 0000000000000000000000000000000000000000
2030 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2036 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2031 p1node--debug: 0000000000000000000000000000000000000000
2037 p1node--debug: 0000000000000000000000000000000000000000
2032 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2038 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2033 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2039 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2034 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2040 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2035 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2041 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2036 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2042 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2037 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2043 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2038 p1node--debug: 0000000000000000000000000000000000000000
2044 p1node--debug: 0000000000000000000000000000000000000000
2039 p2node: 0000000000000000000000000000000000000000
2045 p2node: 0000000000000000000000000000000000000000
2040 p2node: 0000000000000000000000000000000000000000
2046 p2node: 0000000000000000000000000000000000000000
2041 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2047 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2042 p2node: 0000000000000000000000000000000000000000
2048 p2node: 0000000000000000000000000000000000000000
2043 p2node: 0000000000000000000000000000000000000000
2049 p2node: 0000000000000000000000000000000000000000
2044 p2node: 0000000000000000000000000000000000000000
2050 p2node: 0000000000000000000000000000000000000000
2045 p2node: 0000000000000000000000000000000000000000
2051 p2node: 0000000000000000000000000000000000000000
2046 p2node: 0000000000000000000000000000000000000000
2052 p2node: 0000000000000000000000000000000000000000
2047 p2node: 0000000000000000000000000000000000000000
2053 p2node: 0000000000000000000000000000000000000000
2048 p2node--verbose: 0000000000000000000000000000000000000000
2054 p2node--verbose: 0000000000000000000000000000000000000000
2049 p2node--verbose: 0000000000000000000000000000000000000000
2055 p2node--verbose: 0000000000000000000000000000000000000000
2050 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2056 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2051 p2node--verbose: 0000000000000000000000000000000000000000
2057 p2node--verbose: 0000000000000000000000000000000000000000
2052 p2node--verbose: 0000000000000000000000000000000000000000
2058 p2node--verbose: 0000000000000000000000000000000000000000
2053 p2node--verbose: 0000000000000000000000000000000000000000
2059 p2node--verbose: 0000000000000000000000000000000000000000
2054 p2node--verbose: 0000000000000000000000000000000000000000
2060 p2node--verbose: 0000000000000000000000000000000000000000
2055 p2node--verbose: 0000000000000000000000000000000000000000
2061 p2node--verbose: 0000000000000000000000000000000000000000
2056 p2node--verbose: 0000000000000000000000000000000000000000
2062 p2node--verbose: 0000000000000000000000000000000000000000
2057 p2node--debug: 0000000000000000000000000000000000000000
2063 p2node--debug: 0000000000000000000000000000000000000000
2058 p2node--debug: 0000000000000000000000000000000000000000
2064 p2node--debug: 0000000000000000000000000000000000000000
2059 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2065 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2060 p2node--debug: 0000000000000000000000000000000000000000
2066 p2node--debug: 0000000000000000000000000000000000000000
2061 p2node--debug: 0000000000000000000000000000000000000000
2067 p2node--debug: 0000000000000000000000000000000000000000
2062 p2node--debug: 0000000000000000000000000000000000000000
2068 p2node--debug: 0000000000000000000000000000000000000000
2063 p2node--debug: 0000000000000000000000000000000000000000
2069 p2node--debug: 0000000000000000000000000000000000000000
2064 p2node--debug: 0000000000000000000000000000000000000000
2070 p2node--debug: 0000000000000000000000000000000000000000
2065 p2node--debug: 0000000000000000000000000000000000000000
2071 p2node--debug: 0000000000000000000000000000000000000000
2066
2072
2067 Filters work:
2073 Filters work:
2068
2074
2069 $ hg log --template '{author|domain}\n'
2075 $ hg log --template '{author|domain}\n'
2070
2076
2071 hostname
2077 hostname
2072
2078
2073
2079
2074
2080
2075
2081
2076 place
2082 place
2077 place
2083 place
2078 hostname
2084 hostname
2079
2085
2080 $ hg log --template '{author|person}\n'
2086 $ hg log --template '{author|person}\n'
2081 test
2087 test
2082 User Name
2088 User Name
2083 person
2089 person
2084 person
2090 person
2085 person
2091 person
2086 person
2092 person
2087 other
2093 other
2088 A. N. Other
2094 A. N. Other
2089 User Name
2095 User Name
2090
2096
2091 $ hg log --template '{author|user}\n'
2097 $ hg log --template '{author|user}\n'
2092 test
2098 test
2093 user
2099 user
2094 person
2100 person
2095 person
2101 person
2096 person
2102 person
2097 person
2103 person
2098 other
2104 other
2099 other
2105 other
2100 user
2106 user
2101
2107
2102 $ hg log --template '{date|date}\n'
2108 $ hg log --template '{date|date}\n'
2103 Wed Jan 01 10:01:00 2020 +0000
2109 Wed Jan 01 10:01:00 2020 +0000
2104 Mon Jan 12 13:46:40 1970 +0000
2110 Mon Jan 12 13:46:40 1970 +0000
2105 Sun Jan 18 08:40:01 1970 +0000
2111 Sun Jan 18 08:40:01 1970 +0000
2106 Sun Jan 18 08:40:00 1970 +0000
2112 Sun Jan 18 08:40:00 1970 +0000
2107 Sat Jan 17 04:53:20 1970 +0000
2113 Sat Jan 17 04:53:20 1970 +0000
2108 Fri Jan 16 01:06:40 1970 +0000
2114 Fri Jan 16 01:06:40 1970 +0000
2109 Wed Jan 14 21:20:00 1970 +0000
2115 Wed Jan 14 21:20:00 1970 +0000
2110 Tue Jan 13 17:33:20 1970 +0000
2116 Tue Jan 13 17:33:20 1970 +0000
2111 Mon Jan 12 13:46:40 1970 +0000
2117 Mon Jan 12 13:46:40 1970 +0000
2112
2118
2113 $ hg log --template '{date|isodate}\n'
2119 $ hg log --template '{date|isodate}\n'
2114 2020-01-01 10:01 +0000
2120 2020-01-01 10:01 +0000
2115 1970-01-12 13:46 +0000
2121 1970-01-12 13:46 +0000
2116 1970-01-18 08:40 +0000
2122 1970-01-18 08:40 +0000
2117 1970-01-18 08:40 +0000
2123 1970-01-18 08:40 +0000
2118 1970-01-17 04:53 +0000
2124 1970-01-17 04:53 +0000
2119 1970-01-16 01:06 +0000
2125 1970-01-16 01:06 +0000
2120 1970-01-14 21:20 +0000
2126 1970-01-14 21:20 +0000
2121 1970-01-13 17:33 +0000
2127 1970-01-13 17:33 +0000
2122 1970-01-12 13:46 +0000
2128 1970-01-12 13:46 +0000
2123
2129
2124 $ hg log --template '{date|isodatesec}\n'
2130 $ hg log --template '{date|isodatesec}\n'
2125 2020-01-01 10:01:00 +0000
2131 2020-01-01 10:01:00 +0000
2126 1970-01-12 13:46:40 +0000
2132 1970-01-12 13:46:40 +0000
2127 1970-01-18 08:40:01 +0000
2133 1970-01-18 08:40:01 +0000
2128 1970-01-18 08:40:00 +0000
2134 1970-01-18 08:40:00 +0000
2129 1970-01-17 04:53:20 +0000
2135 1970-01-17 04:53:20 +0000
2130 1970-01-16 01:06:40 +0000
2136 1970-01-16 01:06:40 +0000
2131 1970-01-14 21:20:00 +0000
2137 1970-01-14 21:20:00 +0000
2132 1970-01-13 17:33:20 +0000
2138 1970-01-13 17:33:20 +0000
2133 1970-01-12 13:46:40 +0000
2139 1970-01-12 13:46:40 +0000
2134
2140
2135 $ hg log --template '{date|rfc822date}\n'
2141 $ hg log --template '{date|rfc822date}\n'
2136 Wed, 01 Jan 2020 10:01:00 +0000
2142 Wed, 01 Jan 2020 10:01:00 +0000
2137 Mon, 12 Jan 1970 13:46:40 +0000
2143 Mon, 12 Jan 1970 13:46:40 +0000
2138 Sun, 18 Jan 1970 08:40:01 +0000
2144 Sun, 18 Jan 1970 08:40:01 +0000
2139 Sun, 18 Jan 1970 08:40:00 +0000
2145 Sun, 18 Jan 1970 08:40:00 +0000
2140 Sat, 17 Jan 1970 04:53:20 +0000
2146 Sat, 17 Jan 1970 04:53:20 +0000
2141 Fri, 16 Jan 1970 01:06:40 +0000
2147 Fri, 16 Jan 1970 01:06:40 +0000
2142 Wed, 14 Jan 1970 21:20:00 +0000
2148 Wed, 14 Jan 1970 21:20:00 +0000
2143 Tue, 13 Jan 1970 17:33:20 +0000
2149 Tue, 13 Jan 1970 17:33:20 +0000
2144 Mon, 12 Jan 1970 13:46:40 +0000
2150 Mon, 12 Jan 1970 13:46:40 +0000
2145
2151
2146 $ hg log --template '{desc|firstline}\n'
2152 $ hg log --template '{desc|firstline}\n'
2147 third
2153 third
2148 second
2154 second
2149 merge
2155 merge
2150 new head
2156 new head
2151 new branch
2157 new branch
2152 no user, no domain
2158 no user, no domain
2153 no person
2159 no person
2154 other 1
2160 other 1
2155 line 1
2161 line 1
2156
2162
2157 $ hg log --template '{node|short}\n'
2163 $ hg log --template '{node|short}\n'
2158 95c24699272e
2164 95c24699272e
2159 29114dbae42b
2165 29114dbae42b
2160 d41e714fe50d
2166 d41e714fe50d
2161 13207e5a10d9
2167 13207e5a10d9
2162 bbe44766e73d
2168 bbe44766e73d
2163 10e46f2dcbf4
2169 10e46f2dcbf4
2164 97054abb4ab8
2170 97054abb4ab8
2165 b608e9d1a3f0
2171 b608e9d1a3f0
2166 1e4e1b8f71e0
2172 1e4e1b8f71e0
2167
2173
2168 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2174 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2169 <changeset author="test"/>
2175 <changeset author="test"/>
2170 <changeset author="User Name &lt;user@hostname&gt;"/>
2176 <changeset author="User Name &lt;user@hostname&gt;"/>
2171 <changeset author="person"/>
2177 <changeset author="person"/>
2172 <changeset author="person"/>
2178 <changeset author="person"/>
2173 <changeset author="person"/>
2179 <changeset author="person"/>
2174 <changeset author="person"/>
2180 <changeset author="person"/>
2175 <changeset author="other@place"/>
2181 <changeset author="other@place"/>
2176 <changeset author="A. N. Other &lt;other@place&gt;"/>
2182 <changeset author="A. N. Other &lt;other@place&gt;"/>
2177 <changeset author="User Name &lt;user@hostname&gt;"/>
2183 <changeset author="User Name &lt;user@hostname&gt;"/>
2178
2184
2179 $ hg log --template '{rev}: {children}\n'
2185 $ hg log --template '{rev}: {children}\n'
2180 8:
2186 8:
2181 7: 8:95c24699272e
2187 7: 8:95c24699272e
2182 6:
2188 6:
2183 5: 6:d41e714fe50d
2189 5: 6:d41e714fe50d
2184 4: 6:d41e714fe50d
2190 4: 6:d41e714fe50d
2185 3: 4:bbe44766e73d 5:13207e5a10d9
2191 3: 4:bbe44766e73d 5:13207e5a10d9
2186 2: 3:10e46f2dcbf4
2192 2: 3:10e46f2dcbf4
2187 1: 2:97054abb4ab8
2193 1: 2:97054abb4ab8
2188 0: 1:b608e9d1a3f0
2194 0: 1:b608e9d1a3f0
2189
2195
2190 Formatnode filter works:
2196 Formatnode filter works:
2191
2197
2192 $ hg -q log -r 0 --template '{node|formatnode}\n'
2198 $ hg -q log -r 0 --template '{node|formatnode}\n'
2193 1e4e1b8f71e0
2199 1e4e1b8f71e0
2194
2200
2195 $ hg log -r 0 --template '{node|formatnode}\n'
2201 $ hg log -r 0 --template '{node|formatnode}\n'
2196 1e4e1b8f71e0
2202 1e4e1b8f71e0
2197
2203
2198 $ hg -v log -r 0 --template '{node|formatnode}\n'
2204 $ hg -v log -r 0 --template '{node|formatnode}\n'
2199 1e4e1b8f71e0
2205 1e4e1b8f71e0
2200
2206
2201 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2207 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2202 1e4e1b8f71e05681d422154f5421e385fec3454f
2208 1e4e1b8f71e05681d422154f5421e385fec3454f
2203
2209
2204 Age filter:
2210 Age filter:
2205
2211
2206 $ hg init unstable-hash
2212 $ hg init unstable-hash
2207 $ cd unstable-hash
2213 $ cd unstable-hash
2208 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2214 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2209
2215
2210 >>> from __future__ import absolute_import
2216 >>> from __future__ import absolute_import
2211 >>> import datetime
2217 >>> import datetime
2212 >>> fp = open('a', 'w')
2218 >>> fp = open('a', 'w')
2213 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2219 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2214 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2220 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2215 >>> fp.close()
2221 >>> fp.close()
2216 $ hg add a
2222 $ hg add a
2217 $ hg commit -m future -d "`cat a`"
2223 $ hg commit -m future -d "`cat a`"
2218
2224
2219 $ hg log -l1 --template '{date|age}\n'
2225 $ hg log -l1 --template '{date|age}\n'
2220 7 years from now
2226 7 years from now
2221
2227
2222 $ cd ..
2228 $ cd ..
2223 $ rm -rf unstable-hash
2229 $ rm -rf unstable-hash
2224
2230
2225 Add a dummy commit to make up for the instability of the above:
2231 Add a dummy commit to make up for the instability of the above:
2226
2232
2227 $ echo a > a
2233 $ echo a > a
2228 $ hg add a
2234 $ hg add a
2229 $ hg ci -m future
2235 $ hg ci -m future
2230
2236
2231 Count filter:
2237 Count filter:
2232
2238
2233 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2239 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2234 40 12
2240 40 12
2235
2241
2236 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2242 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2237 0 1 4
2243 0 1 4
2238
2244
2239 $ hg log -G --template '{rev}: children: {children|count}, \
2245 $ hg log -G --template '{rev}: children: {children|count}, \
2240 > tags: {tags|count}, file_adds: {file_adds|count}, \
2246 > tags: {tags|count}, file_adds: {file_adds|count}, \
2241 > ancestors: {revset("ancestors(%s)", rev)|count}'
2247 > ancestors: {revset("ancestors(%s)", rev)|count}'
2242 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2248 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2243 |
2249 |
2244 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2250 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2245 |
2251 |
2246 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2252 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2247
2253
2248 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2254 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2249 |\
2255 |\
2250 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2256 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2251 | |
2257 | |
2252 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2258 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2253 |/
2259 |/
2254 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2260 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2255 |
2261 |
2256 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2262 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2257 |
2263 |
2258 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2264 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2259 |
2265 |
2260 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2266 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2261
2267
2262
2268
2263 Upper/lower filters:
2269 Upper/lower filters:
2264
2270
2265 $ hg log -r0 --template '{branch|upper}\n'
2271 $ hg log -r0 --template '{branch|upper}\n'
2266 DEFAULT
2272 DEFAULT
2267 $ hg log -r0 --template '{author|lower}\n'
2273 $ hg log -r0 --template '{author|lower}\n'
2268 user name <user@hostname>
2274 user name <user@hostname>
2269 $ hg log -r0 --template '{date|upper}\n'
2275 $ hg log -r0 --template '{date|upper}\n'
2270 abort: template filter 'upper' is not compatible with keyword 'date'
2276 abort: template filter 'upper' is not compatible with keyword 'date'
2271 [255]
2277 [255]
2272
2278
2273 Add a commit that does all possible modifications at once
2279 Add a commit that does all possible modifications at once
2274
2280
2275 $ echo modify >> third
2281 $ echo modify >> third
2276 $ touch b
2282 $ touch b
2277 $ hg add b
2283 $ hg add b
2278 $ hg mv fourth fifth
2284 $ hg mv fourth fifth
2279 $ hg rm a
2285 $ hg rm a
2280 $ hg ci -m "Modify, add, remove, rename"
2286 $ hg ci -m "Modify, add, remove, rename"
2281
2287
2282 Check the status template
2288 Check the status template
2283
2289
2284 $ cat <<EOF >> $HGRCPATH
2290 $ cat <<EOF >> $HGRCPATH
2285 > [extensions]
2291 > [extensions]
2286 > color=
2292 > color=
2287 > EOF
2293 > EOF
2288
2294
2289 $ hg log -T status -r 10
2295 $ hg log -T status -r 10
2290 changeset: 10:0f9759ec227a
2296 changeset: 10:0f9759ec227a
2291 tag: tip
2297 tag: tip
2292 user: test
2298 user: test
2293 date: Thu Jan 01 00:00:00 1970 +0000
2299 date: Thu Jan 01 00:00:00 1970 +0000
2294 summary: Modify, add, remove, rename
2300 summary: Modify, add, remove, rename
2295 files:
2301 files:
2296 M third
2302 M third
2297 A b
2303 A b
2298 A fifth
2304 A fifth
2299 R a
2305 R a
2300 R fourth
2306 R fourth
2301
2307
2302 $ hg log -T status -C -r 10
2308 $ hg log -T status -C -r 10
2303 changeset: 10:0f9759ec227a
2309 changeset: 10:0f9759ec227a
2304 tag: tip
2310 tag: tip
2305 user: test
2311 user: test
2306 date: Thu Jan 01 00:00:00 1970 +0000
2312 date: Thu Jan 01 00:00:00 1970 +0000
2307 summary: Modify, add, remove, rename
2313 summary: Modify, add, remove, rename
2308 files:
2314 files:
2309 M third
2315 M third
2310 A b
2316 A b
2311 A fifth
2317 A fifth
2312 fourth
2318 fourth
2313 R a
2319 R a
2314 R fourth
2320 R fourth
2315
2321
2316 $ hg log -T status -C -r 10 -v
2322 $ hg log -T status -C -r 10 -v
2317 changeset: 10:0f9759ec227a
2323 changeset: 10:0f9759ec227a
2318 tag: tip
2324 tag: tip
2319 user: test
2325 user: test
2320 date: Thu Jan 01 00:00:00 1970 +0000
2326 date: Thu Jan 01 00:00:00 1970 +0000
2321 description:
2327 description:
2322 Modify, add, remove, rename
2328 Modify, add, remove, rename
2323
2329
2324 files:
2330 files:
2325 M third
2331 M third
2326 A b
2332 A b
2327 A fifth
2333 A fifth
2328 fourth
2334 fourth
2329 R a
2335 R a
2330 R fourth
2336 R fourth
2331
2337
2332 $ hg log -T status -C -r 10 --debug
2338 $ hg log -T status -C -r 10 --debug
2333 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2339 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2334 tag: tip
2340 tag: tip
2335 phase: secret
2341 phase: secret
2336 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2342 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2337 parent: -1:0000000000000000000000000000000000000000
2343 parent: -1:0000000000000000000000000000000000000000
2338 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2344 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2339 user: test
2345 user: test
2340 date: Thu Jan 01 00:00:00 1970 +0000
2346 date: Thu Jan 01 00:00:00 1970 +0000
2341 extra: branch=default
2347 extra: branch=default
2342 description:
2348 description:
2343 Modify, add, remove, rename
2349 Modify, add, remove, rename
2344
2350
2345 files:
2351 files:
2346 M third
2352 M third
2347 A b
2353 A b
2348 A fifth
2354 A fifth
2349 fourth
2355 fourth
2350 R a
2356 R a
2351 R fourth
2357 R fourth
2352
2358
2353 $ hg log -T status -C -r 10 --quiet
2359 $ hg log -T status -C -r 10 --quiet
2354 10:0f9759ec227a
2360 10:0f9759ec227a
2355 $ hg --color=debug log -T status -r 10
2361 $ hg --color=debug log -T status -r 10
2356 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2362 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2357 [log.tag|tag: tip]
2363 [log.tag|tag: tip]
2358 [log.user|user: test]
2364 [log.user|user: test]
2359 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2365 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2360 [log.summary|summary: Modify, add, remove, rename]
2366 [log.summary|summary: Modify, add, remove, rename]
2361 [ui.note log.files|files:]
2367 [ui.note log.files|files:]
2362 [status.modified|M third]
2368 [status.modified|M third]
2363 [status.added|A b]
2369 [status.added|A b]
2364 [status.added|A fifth]
2370 [status.added|A fifth]
2365 [status.removed|R a]
2371 [status.removed|R a]
2366 [status.removed|R fourth]
2372 [status.removed|R fourth]
2367
2373
2368 $ hg --color=debug log -T status -C -r 10
2374 $ hg --color=debug log -T status -C -r 10
2369 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2375 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2370 [log.tag|tag: tip]
2376 [log.tag|tag: tip]
2371 [log.user|user: test]
2377 [log.user|user: test]
2372 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2378 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2373 [log.summary|summary: Modify, add, remove, rename]
2379 [log.summary|summary: Modify, add, remove, rename]
2374 [ui.note log.files|files:]
2380 [ui.note log.files|files:]
2375 [status.modified|M third]
2381 [status.modified|M third]
2376 [status.added|A b]
2382 [status.added|A b]
2377 [status.added|A fifth]
2383 [status.added|A fifth]
2378 [status.copied| fourth]
2384 [status.copied| fourth]
2379 [status.removed|R a]
2385 [status.removed|R a]
2380 [status.removed|R fourth]
2386 [status.removed|R fourth]
2381
2387
2382 $ hg --color=debug log -T status -C -r 10 -v
2388 $ hg --color=debug log -T status -C -r 10 -v
2383 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2389 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2384 [log.tag|tag: tip]
2390 [log.tag|tag: tip]
2385 [log.user|user: test]
2391 [log.user|user: test]
2386 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2392 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2387 [ui.note log.description|description:]
2393 [ui.note log.description|description:]
2388 [ui.note log.description|Modify, add, remove, rename]
2394 [ui.note log.description|Modify, add, remove, rename]
2389
2395
2390 [ui.note log.files|files:]
2396 [ui.note log.files|files:]
2391 [status.modified|M third]
2397 [status.modified|M third]
2392 [status.added|A b]
2398 [status.added|A b]
2393 [status.added|A fifth]
2399 [status.added|A fifth]
2394 [status.copied| fourth]
2400 [status.copied| fourth]
2395 [status.removed|R a]
2401 [status.removed|R a]
2396 [status.removed|R fourth]
2402 [status.removed|R fourth]
2397
2403
2398 $ hg --color=debug log -T status -C -r 10 --debug
2404 $ hg --color=debug log -T status -C -r 10 --debug
2399 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2405 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2400 [log.tag|tag: tip]
2406 [log.tag|tag: tip]
2401 [log.phase|phase: secret]
2407 [log.phase|phase: secret]
2402 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2408 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2403 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2409 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2404 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2410 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2405 [log.user|user: test]
2411 [log.user|user: test]
2406 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2412 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2407 [ui.debug log.extra|extra: branch=default]
2413 [ui.debug log.extra|extra: branch=default]
2408 [ui.note log.description|description:]
2414 [ui.note log.description|description:]
2409 [ui.note log.description|Modify, add, remove, rename]
2415 [ui.note log.description|Modify, add, remove, rename]
2410
2416
2411 [ui.note log.files|files:]
2417 [ui.note log.files|files:]
2412 [status.modified|M third]
2418 [status.modified|M third]
2413 [status.added|A b]
2419 [status.added|A b]
2414 [status.added|A fifth]
2420 [status.added|A fifth]
2415 [status.copied| fourth]
2421 [status.copied| fourth]
2416 [status.removed|R a]
2422 [status.removed|R a]
2417 [status.removed|R fourth]
2423 [status.removed|R fourth]
2418
2424
2419 $ hg --color=debug log -T status -C -r 10 --quiet
2425 $ hg --color=debug log -T status -C -r 10 --quiet
2420 [log.node|10:0f9759ec227a]
2426 [log.node|10:0f9759ec227a]
2421
2427
2422 Check the bisect template
2428 Check the bisect template
2423
2429
2424 $ hg bisect -g 1
2430 $ hg bisect -g 1
2425 $ hg bisect -b 3 --noupdate
2431 $ hg bisect -b 3 --noupdate
2426 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2432 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2427 $ hg log -T bisect -r 0:4
2433 $ hg log -T bisect -r 0:4
2428 changeset: 0:1e4e1b8f71e0
2434 changeset: 0:1e4e1b8f71e0
2429 bisect: good (implicit)
2435 bisect: good (implicit)
2430 user: User Name <user@hostname>
2436 user: User Name <user@hostname>
2431 date: Mon Jan 12 13:46:40 1970 +0000
2437 date: Mon Jan 12 13:46:40 1970 +0000
2432 summary: line 1
2438 summary: line 1
2433
2439
2434 changeset: 1:b608e9d1a3f0
2440 changeset: 1:b608e9d1a3f0
2435 bisect: good
2441 bisect: good
2436 user: A. N. Other <other@place>
2442 user: A. N. Other <other@place>
2437 date: Tue Jan 13 17:33:20 1970 +0000
2443 date: Tue Jan 13 17:33:20 1970 +0000
2438 summary: other 1
2444 summary: other 1
2439
2445
2440 changeset: 2:97054abb4ab8
2446 changeset: 2:97054abb4ab8
2441 bisect: untested
2447 bisect: untested
2442 user: other@place
2448 user: other@place
2443 date: Wed Jan 14 21:20:00 1970 +0000
2449 date: Wed Jan 14 21:20:00 1970 +0000
2444 summary: no person
2450 summary: no person
2445
2451
2446 changeset: 3:10e46f2dcbf4
2452 changeset: 3:10e46f2dcbf4
2447 bisect: bad
2453 bisect: bad
2448 user: person
2454 user: person
2449 date: Fri Jan 16 01:06:40 1970 +0000
2455 date: Fri Jan 16 01:06:40 1970 +0000
2450 summary: no user, no domain
2456 summary: no user, no domain
2451
2457
2452 changeset: 4:bbe44766e73d
2458 changeset: 4:bbe44766e73d
2453 bisect: bad (implicit)
2459 bisect: bad (implicit)
2454 branch: foo
2460 branch: foo
2455 user: person
2461 user: person
2456 date: Sat Jan 17 04:53:20 1970 +0000
2462 date: Sat Jan 17 04:53:20 1970 +0000
2457 summary: new branch
2463 summary: new branch
2458
2464
2459 $ hg log --debug -T bisect -r 0:4
2465 $ hg log --debug -T bisect -r 0:4
2460 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2466 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2461 bisect: good (implicit)
2467 bisect: good (implicit)
2462 phase: public
2468 phase: public
2463 parent: -1:0000000000000000000000000000000000000000
2469 parent: -1:0000000000000000000000000000000000000000
2464 parent: -1:0000000000000000000000000000000000000000
2470 parent: -1:0000000000000000000000000000000000000000
2465 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2471 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2466 user: User Name <user@hostname>
2472 user: User Name <user@hostname>
2467 date: Mon Jan 12 13:46:40 1970 +0000
2473 date: Mon Jan 12 13:46:40 1970 +0000
2468 files+: a
2474 files+: a
2469 extra: branch=default
2475 extra: branch=default
2470 description:
2476 description:
2471 line 1
2477 line 1
2472 line 2
2478 line 2
2473
2479
2474
2480
2475 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2481 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2476 bisect: good
2482 bisect: good
2477 phase: public
2483 phase: public
2478 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2484 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2479 parent: -1:0000000000000000000000000000000000000000
2485 parent: -1:0000000000000000000000000000000000000000
2480 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2486 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2481 user: A. N. Other <other@place>
2487 user: A. N. Other <other@place>
2482 date: Tue Jan 13 17:33:20 1970 +0000
2488 date: Tue Jan 13 17:33:20 1970 +0000
2483 files+: b
2489 files+: b
2484 extra: branch=default
2490 extra: branch=default
2485 description:
2491 description:
2486 other 1
2492 other 1
2487 other 2
2493 other 2
2488
2494
2489 other 3
2495 other 3
2490
2496
2491
2497
2492 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2498 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2493 bisect: untested
2499 bisect: untested
2494 phase: public
2500 phase: public
2495 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2501 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2496 parent: -1:0000000000000000000000000000000000000000
2502 parent: -1:0000000000000000000000000000000000000000
2497 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2503 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2498 user: other@place
2504 user: other@place
2499 date: Wed Jan 14 21:20:00 1970 +0000
2505 date: Wed Jan 14 21:20:00 1970 +0000
2500 files+: c
2506 files+: c
2501 extra: branch=default
2507 extra: branch=default
2502 description:
2508 description:
2503 no person
2509 no person
2504
2510
2505
2511
2506 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2512 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2507 bisect: bad
2513 bisect: bad
2508 phase: public
2514 phase: public
2509 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2515 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2510 parent: -1:0000000000000000000000000000000000000000
2516 parent: -1:0000000000000000000000000000000000000000
2511 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2517 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2512 user: person
2518 user: person
2513 date: Fri Jan 16 01:06:40 1970 +0000
2519 date: Fri Jan 16 01:06:40 1970 +0000
2514 files: c
2520 files: c
2515 extra: branch=default
2521 extra: branch=default
2516 description:
2522 description:
2517 no user, no domain
2523 no user, no domain
2518
2524
2519
2525
2520 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2526 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2521 bisect: bad (implicit)
2527 bisect: bad (implicit)
2522 branch: foo
2528 branch: foo
2523 phase: draft
2529 phase: draft
2524 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2530 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2525 parent: -1:0000000000000000000000000000000000000000
2531 parent: -1:0000000000000000000000000000000000000000
2526 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2532 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2527 user: person
2533 user: person
2528 date: Sat Jan 17 04:53:20 1970 +0000
2534 date: Sat Jan 17 04:53:20 1970 +0000
2529 extra: branch=foo
2535 extra: branch=foo
2530 description:
2536 description:
2531 new branch
2537 new branch
2532
2538
2533
2539
2534 $ hg log -v -T bisect -r 0:4
2540 $ hg log -v -T bisect -r 0:4
2535 changeset: 0:1e4e1b8f71e0
2541 changeset: 0:1e4e1b8f71e0
2536 bisect: good (implicit)
2542 bisect: good (implicit)
2537 user: User Name <user@hostname>
2543 user: User Name <user@hostname>
2538 date: Mon Jan 12 13:46:40 1970 +0000
2544 date: Mon Jan 12 13:46:40 1970 +0000
2539 files: a
2545 files: a
2540 description:
2546 description:
2541 line 1
2547 line 1
2542 line 2
2548 line 2
2543
2549
2544
2550
2545 changeset: 1:b608e9d1a3f0
2551 changeset: 1:b608e9d1a3f0
2546 bisect: good
2552 bisect: good
2547 user: A. N. Other <other@place>
2553 user: A. N. Other <other@place>
2548 date: Tue Jan 13 17:33:20 1970 +0000
2554 date: Tue Jan 13 17:33:20 1970 +0000
2549 files: b
2555 files: b
2550 description:
2556 description:
2551 other 1
2557 other 1
2552 other 2
2558 other 2
2553
2559
2554 other 3
2560 other 3
2555
2561
2556
2562
2557 changeset: 2:97054abb4ab8
2563 changeset: 2:97054abb4ab8
2558 bisect: untested
2564 bisect: untested
2559 user: other@place
2565 user: other@place
2560 date: Wed Jan 14 21:20:00 1970 +0000
2566 date: Wed Jan 14 21:20:00 1970 +0000
2561 files: c
2567 files: c
2562 description:
2568 description:
2563 no person
2569 no person
2564
2570
2565
2571
2566 changeset: 3:10e46f2dcbf4
2572 changeset: 3:10e46f2dcbf4
2567 bisect: bad
2573 bisect: bad
2568 user: person
2574 user: person
2569 date: Fri Jan 16 01:06:40 1970 +0000
2575 date: Fri Jan 16 01:06:40 1970 +0000
2570 files: c
2576 files: c
2571 description:
2577 description:
2572 no user, no domain
2578 no user, no domain
2573
2579
2574
2580
2575 changeset: 4:bbe44766e73d
2581 changeset: 4:bbe44766e73d
2576 bisect: bad (implicit)
2582 bisect: bad (implicit)
2577 branch: foo
2583 branch: foo
2578 user: person
2584 user: person
2579 date: Sat Jan 17 04:53:20 1970 +0000
2585 date: Sat Jan 17 04:53:20 1970 +0000
2580 description:
2586 description:
2581 new branch
2587 new branch
2582
2588
2583
2589
2584 $ hg --color=debug log -T bisect -r 0:4
2590 $ hg --color=debug log -T bisect -r 0:4
2585 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2591 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2586 [log.bisect bisect.good|bisect: good (implicit)]
2592 [log.bisect bisect.good|bisect: good (implicit)]
2587 [log.user|user: User Name <user@hostname>]
2593 [log.user|user: User Name <user@hostname>]
2588 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2594 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2589 [log.summary|summary: line 1]
2595 [log.summary|summary: line 1]
2590
2596
2591 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2597 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2592 [log.bisect bisect.good|bisect: good]
2598 [log.bisect bisect.good|bisect: good]
2593 [log.user|user: A. N. Other <other@place>]
2599 [log.user|user: A. N. Other <other@place>]
2594 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2600 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2595 [log.summary|summary: other 1]
2601 [log.summary|summary: other 1]
2596
2602
2597 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2603 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2598 [log.bisect bisect.untested|bisect: untested]
2604 [log.bisect bisect.untested|bisect: untested]
2599 [log.user|user: other@place]
2605 [log.user|user: other@place]
2600 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2606 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2601 [log.summary|summary: no person]
2607 [log.summary|summary: no person]
2602
2608
2603 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2609 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2604 [log.bisect bisect.bad|bisect: bad]
2610 [log.bisect bisect.bad|bisect: bad]
2605 [log.user|user: person]
2611 [log.user|user: person]
2606 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2612 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2607 [log.summary|summary: no user, no domain]
2613 [log.summary|summary: no user, no domain]
2608
2614
2609 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2615 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2610 [log.bisect bisect.bad|bisect: bad (implicit)]
2616 [log.bisect bisect.bad|bisect: bad (implicit)]
2611 [log.branch|branch: foo]
2617 [log.branch|branch: foo]
2612 [log.user|user: person]
2618 [log.user|user: person]
2613 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2619 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2614 [log.summary|summary: new branch]
2620 [log.summary|summary: new branch]
2615
2621
2616 $ hg --color=debug log --debug -T bisect -r 0:4
2622 $ hg --color=debug log --debug -T bisect -r 0:4
2617 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2623 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2618 [log.bisect bisect.good|bisect: good (implicit)]
2624 [log.bisect bisect.good|bisect: good (implicit)]
2619 [log.phase|phase: public]
2625 [log.phase|phase: public]
2620 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2626 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2621 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2627 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2622 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2628 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2623 [log.user|user: User Name <user@hostname>]
2629 [log.user|user: User Name <user@hostname>]
2624 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2630 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2625 [ui.debug log.files|files+: a]
2631 [ui.debug log.files|files+: a]
2626 [ui.debug log.extra|extra: branch=default]
2632 [ui.debug log.extra|extra: branch=default]
2627 [ui.note log.description|description:]
2633 [ui.note log.description|description:]
2628 [ui.note log.description|line 1
2634 [ui.note log.description|line 1
2629 line 2]
2635 line 2]
2630
2636
2631
2637
2632 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2638 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2633 [log.bisect bisect.good|bisect: good]
2639 [log.bisect bisect.good|bisect: good]
2634 [log.phase|phase: public]
2640 [log.phase|phase: public]
2635 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2641 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2636 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2642 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2637 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2643 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2638 [log.user|user: A. N. Other <other@place>]
2644 [log.user|user: A. N. Other <other@place>]
2639 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2645 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2640 [ui.debug log.files|files+: b]
2646 [ui.debug log.files|files+: b]
2641 [ui.debug log.extra|extra: branch=default]
2647 [ui.debug log.extra|extra: branch=default]
2642 [ui.note log.description|description:]
2648 [ui.note log.description|description:]
2643 [ui.note log.description|other 1
2649 [ui.note log.description|other 1
2644 other 2
2650 other 2
2645
2651
2646 other 3]
2652 other 3]
2647
2653
2648
2654
2649 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2655 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2650 [log.bisect bisect.untested|bisect: untested]
2656 [log.bisect bisect.untested|bisect: untested]
2651 [log.phase|phase: public]
2657 [log.phase|phase: public]
2652 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2658 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2653 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2659 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2654 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2660 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2655 [log.user|user: other@place]
2661 [log.user|user: other@place]
2656 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2662 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2657 [ui.debug log.files|files+: c]
2663 [ui.debug log.files|files+: c]
2658 [ui.debug log.extra|extra: branch=default]
2664 [ui.debug log.extra|extra: branch=default]
2659 [ui.note log.description|description:]
2665 [ui.note log.description|description:]
2660 [ui.note log.description|no person]
2666 [ui.note log.description|no person]
2661
2667
2662
2668
2663 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2669 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2664 [log.bisect bisect.bad|bisect: bad]
2670 [log.bisect bisect.bad|bisect: bad]
2665 [log.phase|phase: public]
2671 [log.phase|phase: public]
2666 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2672 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2667 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2673 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2668 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2674 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2669 [log.user|user: person]
2675 [log.user|user: person]
2670 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2676 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2671 [ui.debug log.files|files: c]
2677 [ui.debug log.files|files: c]
2672 [ui.debug log.extra|extra: branch=default]
2678 [ui.debug log.extra|extra: branch=default]
2673 [ui.note log.description|description:]
2679 [ui.note log.description|description:]
2674 [ui.note log.description|no user, no domain]
2680 [ui.note log.description|no user, no domain]
2675
2681
2676
2682
2677 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2683 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2678 [log.bisect bisect.bad|bisect: bad (implicit)]
2684 [log.bisect bisect.bad|bisect: bad (implicit)]
2679 [log.branch|branch: foo]
2685 [log.branch|branch: foo]
2680 [log.phase|phase: draft]
2686 [log.phase|phase: draft]
2681 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2687 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2682 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2688 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2683 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2689 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2684 [log.user|user: person]
2690 [log.user|user: person]
2685 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2691 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2686 [ui.debug log.extra|extra: branch=foo]
2692 [ui.debug log.extra|extra: branch=foo]
2687 [ui.note log.description|description:]
2693 [ui.note log.description|description:]
2688 [ui.note log.description|new branch]
2694 [ui.note log.description|new branch]
2689
2695
2690
2696
2691 $ hg --color=debug log -v -T bisect -r 0:4
2697 $ hg --color=debug log -v -T bisect -r 0:4
2692 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2698 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2693 [log.bisect bisect.good|bisect: good (implicit)]
2699 [log.bisect bisect.good|bisect: good (implicit)]
2694 [log.user|user: User Name <user@hostname>]
2700 [log.user|user: User Name <user@hostname>]
2695 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2701 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2696 [ui.note log.files|files: a]
2702 [ui.note log.files|files: a]
2697 [ui.note log.description|description:]
2703 [ui.note log.description|description:]
2698 [ui.note log.description|line 1
2704 [ui.note log.description|line 1
2699 line 2]
2705 line 2]
2700
2706
2701
2707
2702 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2708 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2703 [log.bisect bisect.good|bisect: good]
2709 [log.bisect bisect.good|bisect: good]
2704 [log.user|user: A. N. Other <other@place>]
2710 [log.user|user: A. N. Other <other@place>]
2705 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2711 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2706 [ui.note log.files|files: b]
2712 [ui.note log.files|files: b]
2707 [ui.note log.description|description:]
2713 [ui.note log.description|description:]
2708 [ui.note log.description|other 1
2714 [ui.note log.description|other 1
2709 other 2
2715 other 2
2710
2716
2711 other 3]
2717 other 3]
2712
2718
2713
2719
2714 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2720 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2715 [log.bisect bisect.untested|bisect: untested]
2721 [log.bisect bisect.untested|bisect: untested]
2716 [log.user|user: other@place]
2722 [log.user|user: other@place]
2717 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2723 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2718 [ui.note log.files|files: c]
2724 [ui.note log.files|files: c]
2719 [ui.note log.description|description:]
2725 [ui.note log.description|description:]
2720 [ui.note log.description|no person]
2726 [ui.note log.description|no person]
2721
2727
2722
2728
2723 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2729 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2724 [log.bisect bisect.bad|bisect: bad]
2730 [log.bisect bisect.bad|bisect: bad]
2725 [log.user|user: person]
2731 [log.user|user: person]
2726 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2732 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2727 [ui.note log.files|files: c]
2733 [ui.note log.files|files: c]
2728 [ui.note log.description|description:]
2734 [ui.note log.description|description:]
2729 [ui.note log.description|no user, no domain]
2735 [ui.note log.description|no user, no domain]
2730
2736
2731
2737
2732 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2738 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2733 [log.bisect bisect.bad|bisect: bad (implicit)]
2739 [log.bisect bisect.bad|bisect: bad (implicit)]
2734 [log.branch|branch: foo]
2740 [log.branch|branch: foo]
2735 [log.user|user: person]
2741 [log.user|user: person]
2736 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2742 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2737 [ui.note log.description|description:]
2743 [ui.note log.description|description:]
2738 [ui.note log.description|new branch]
2744 [ui.note log.description|new branch]
2739
2745
2740
2746
2741 $ hg bisect --reset
2747 $ hg bisect --reset
2742
2748
2743 Error on syntax:
2749 Error on syntax:
2744
2750
2745 $ echo 'x = "f' >> t
2751 $ echo 'x = "f' >> t
2746 $ hg log
2752 $ hg log
2747 hg: parse error at t:3: unmatched quotes
2753 hg: parse error at t:3: unmatched quotes
2748 [255]
2754 [255]
2749
2755
2750 $ hg log -T '{date'
2756 $ hg log -T '{date'
2751 hg: parse error at 1: unterminated template expansion
2757 hg: parse error at 1: unterminated template expansion
2752 [255]
2758 [255]
2753
2759
2754 Behind the scenes, this will throw TypeError
2760 Behind the scenes, this will throw TypeError
2755
2761
2756 $ hg log -l 3 --template '{date|obfuscate}\n'
2762 $ hg log -l 3 --template '{date|obfuscate}\n'
2757 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2763 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2758 [255]
2764 [255]
2759
2765
2760 Behind the scenes, this will throw a ValueError
2766 Behind the scenes, this will throw a ValueError
2761
2767
2762 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2768 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2763 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2769 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2764 [255]
2770 [255]
2765
2771
2766 Behind the scenes, this will throw AttributeError
2772 Behind the scenes, this will throw AttributeError
2767
2773
2768 $ hg log -l 3 --template 'line: {date|escape}\n'
2774 $ hg log -l 3 --template 'line: {date|escape}\n'
2769 abort: template filter 'escape' is not compatible with keyword 'date'
2775 abort: template filter 'escape' is not compatible with keyword 'date'
2770 [255]
2776 [255]
2771
2777
2772 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2778 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2773 hg: parse error: localdate expects a date information
2779 hg: parse error: localdate expects a date information
2774 [255]
2780 [255]
2775
2781
2776 Behind the scenes, this will throw ValueError
2782 Behind the scenes, this will throw ValueError
2777
2783
2778 $ hg tip --template '{author|email|date}\n'
2784 $ hg tip --template '{author|email|date}\n'
2779 hg: parse error: date expects a date information
2785 hg: parse error: date expects a date information
2780 [255]
2786 [255]
2781
2787
2782 $ hg tip -T '{author|email|shortdate}\n'
2788 $ hg tip -T '{author|email|shortdate}\n'
2783 abort: template filter 'shortdate' is not compatible with keyword 'author'
2789 abort: template filter 'shortdate' is not compatible with keyword 'author'
2784 [255]
2790 [255]
2785
2791
2786 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2792 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2787 abort: incompatible use of template filter 'shortdate'
2793 abort: incompatible use of template filter 'shortdate'
2788 [255]
2794 [255]
2789
2795
2790 Error in nested template:
2796 Error in nested template:
2791
2797
2792 $ hg log -T '{"date'
2798 $ hg log -T '{"date'
2793 hg: parse error at 2: unterminated string
2799 hg: parse error at 2: unterminated string
2794 [255]
2800 [255]
2795
2801
2796 $ hg log -T '{"foo{date|?}"}'
2802 $ hg log -T '{"foo{date|?}"}'
2797 hg: parse error at 11: syntax error
2803 hg: parse error at 11: syntax error
2798 [255]
2804 [255]
2799
2805
2800 Thrown an error if a template function doesn't exist
2806 Thrown an error if a template function doesn't exist
2801
2807
2802 $ hg tip --template '{foo()}\n'
2808 $ hg tip --template '{foo()}\n'
2803 hg: parse error: unknown function 'foo'
2809 hg: parse error: unknown function 'foo'
2804 [255]
2810 [255]
2805
2811
2806 Pass generator object created by template function to filter
2812 Pass generator object created by template function to filter
2807
2813
2808 $ hg log -l 1 --template '{if(author, author)|user}\n'
2814 $ hg log -l 1 --template '{if(author, author)|user}\n'
2809 test
2815 test
2810
2816
2811 Test index keyword:
2817 Test index keyword:
2812
2818
2813 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2819 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2814 10 0:a 1:b 2:fifth 3:fourth 4:third
2820 10 0:a 1:b 2:fifth 3:fourth 4:third
2815 11 0:a
2821 11 0:a
2816
2822
2817 $ hg branches -T '{index} {branch}\n'
2823 $ hg branches -T '{index} {branch}\n'
2818 0 default
2824 0 default
2819 1 foo
2825 1 foo
2820
2826
2821 Test diff function:
2827 Test diff function:
2822
2828
2823 $ hg diff -c 8
2829 $ hg diff -c 8
2824 diff -r 29114dbae42b -r 95c24699272e fourth
2830 diff -r 29114dbae42b -r 95c24699272e fourth
2825 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2831 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2826 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2832 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2827 @@ -0,0 +1,1 @@
2833 @@ -0,0 +1,1 @@
2828 +second
2834 +second
2829 diff -r 29114dbae42b -r 95c24699272e second
2835 diff -r 29114dbae42b -r 95c24699272e second
2830 --- a/second Mon Jan 12 13:46:40 1970 +0000
2836 --- a/second Mon Jan 12 13:46:40 1970 +0000
2831 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2837 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2832 @@ -1,1 +0,0 @@
2838 @@ -1,1 +0,0 @@
2833 -second
2839 -second
2834 diff -r 29114dbae42b -r 95c24699272e third
2840 diff -r 29114dbae42b -r 95c24699272e third
2835 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2841 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2836 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2842 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2837 @@ -0,0 +1,1 @@
2843 @@ -0,0 +1,1 @@
2838 +third
2844 +third
2839
2845
2840 $ hg log -r 8 -T "{diff()}"
2846 $ hg log -r 8 -T "{diff()}"
2841 diff -r 29114dbae42b -r 95c24699272e fourth
2847 diff -r 29114dbae42b -r 95c24699272e fourth
2842 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2848 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2843 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2849 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2844 @@ -0,0 +1,1 @@
2850 @@ -0,0 +1,1 @@
2845 +second
2851 +second
2846 diff -r 29114dbae42b -r 95c24699272e second
2852 diff -r 29114dbae42b -r 95c24699272e second
2847 --- a/second Mon Jan 12 13:46:40 1970 +0000
2853 --- a/second Mon Jan 12 13:46:40 1970 +0000
2848 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2854 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2849 @@ -1,1 +0,0 @@
2855 @@ -1,1 +0,0 @@
2850 -second
2856 -second
2851 diff -r 29114dbae42b -r 95c24699272e third
2857 diff -r 29114dbae42b -r 95c24699272e third
2852 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2858 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2853 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2859 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2854 @@ -0,0 +1,1 @@
2860 @@ -0,0 +1,1 @@
2855 +third
2861 +third
2856
2862
2857 $ hg log -r 8 -T "{diff('glob:f*')}"
2863 $ hg log -r 8 -T "{diff('glob:f*')}"
2858 diff -r 29114dbae42b -r 95c24699272e fourth
2864 diff -r 29114dbae42b -r 95c24699272e fourth
2859 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2865 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2860 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2866 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2861 @@ -0,0 +1,1 @@
2867 @@ -0,0 +1,1 @@
2862 +second
2868 +second
2863
2869
2864 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2870 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2865 diff -r 29114dbae42b -r 95c24699272e second
2871 diff -r 29114dbae42b -r 95c24699272e second
2866 --- a/second Mon Jan 12 13:46:40 1970 +0000
2872 --- a/second Mon Jan 12 13:46:40 1970 +0000
2867 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2873 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2868 @@ -1,1 +0,0 @@
2874 @@ -1,1 +0,0 @@
2869 -second
2875 -second
2870 diff -r 29114dbae42b -r 95c24699272e third
2876 diff -r 29114dbae42b -r 95c24699272e third
2871 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2877 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2872 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2878 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2873 @@ -0,0 +1,1 @@
2879 @@ -0,0 +1,1 @@
2874 +third
2880 +third
2875
2881
2876 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2882 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2877 diff -r 29114dbae42b -r 95c24699272e fourth
2883 diff -r 29114dbae42b -r 95c24699272e fourth
2878 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2884 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2879 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2885 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2880 @@ -0,0 +1,1 @@
2886 @@ -0,0 +1,1 @@
2881 +second
2887 +second
2882
2888
2883 ui verbosity:
2889 ui verbosity:
2884
2890
2885 $ hg log -l1 -T '{verbosity}\n'
2891 $ hg log -l1 -T '{verbosity}\n'
2886
2892
2887 $ hg log -l1 -T '{verbosity}\n' --debug
2893 $ hg log -l1 -T '{verbosity}\n' --debug
2888 debug
2894 debug
2889 $ hg log -l1 -T '{verbosity}\n' --quiet
2895 $ hg log -l1 -T '{verbosity}\n' --quiet
2890 quiet
2896 quiet
2891 $ hg log -l1 -T '{verbosity}\n' --verbose
2897 $ hg log -l1 -T '{verbosity}\n' --verbose
2892 verbose
2898 verbose
2893
2899
2894 $ cd ..
2900 $ cd ..
2895
2901
2896
2902
2897 latesttag:
2903 latesttag:
2898
2904
2899 $ hg init latesttag
2905 $ hg init latesttag
2900 $ cd latesttag
2906 $ cd latesttag
2901
2907
2902 $ echo a > file
2908 $ echo a > file
2903 $ hg ci -Am a -d '0 0'
2909 $ hg ci -Am a -d '0 0'
2904 adding file
2910 adding file
2905
2911
2906 $ echo b >> file
2912 $ echo b >> file
2907 $ hg ci -m b -d '1 0'
2913 $ hg ci -m b -d '1 0'
2908
2914
2909 $ echo c >> head1
2915 $ echo c >> head1
2910 $ hg ci -Am h1c -d '2 0'
2916 $ hg ci -Am h1c -d '2 0'
2911 adding head1
2917 adding head1
2912
2918
2913 $ hg update -q 1
2919 $ hg update -q 1
2914 $ echo d >> head2
2920 $ echo d >> head2
2915 $ hg ci -Am h2d -d '3 0'
2921 $ hg ci -Am h2d -d '3 0'
2916 adding head2
2922 adding head2
2917 created new head
2923 created new head
2918
2924
2919 $ echo e >> head2
2925 $ echo e >> head2
2920 $ hg ci -m h2e -d '4 0'
2926 $ hg ci -m h2e -d '4 0'
2921
2927
2922 $ hg merge -q
2928 $ hg merge -q
2923 $ hg ci -m merge -d '5 -3600'
2929 $ hg ci -m merge -d '5 -3600'
2924
2930
2925 No tag set:
2931 No tag set:
2926
2932
2927 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2933 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2928 @ 5: null+5
2934 @ 5: null+5
2929 |\
2935 |\
2930 | o 4: null+4
2936 | o 4: null+4
2931 | |
2937 | |
2932 | o 3: null+3
2938 | o 3: null+3
2933 | |
2939 | |
2934 o | 2: null+3
2940 o | 2: null+3
2935 |/
2941 |/
2936 o 1: null+2
2942 o 1: null+2
2937 |
2943 |
2938 o 0: null+1
2944 o 0: null+1
2939
2945
2940
2946
2941 One common tag: longest path wins for {latesttagdistance}:
2947 One common tag: longest path wins for {latesttagdistance}:
2942
2948
2943 $ hg tag -r 1 -m t1 -d '6 0' t1
2949 $ hg tag -r 1 -m t1 -d '6 0' t1
2944 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2950 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2945 @ 6: t1+4
2951 @ 6: t1+4
2946 |
2952 |
2947 o 5: t1+3
2953 o 5: t1+3
2948 |\
2954 |\
2949 | o 4: t1+2
2955 | o 4: t1+2
2950 | |
2956 | |
2951 | o 3: t1+1
2957 | o 3: t1+1
2952 | |
2958 | |
2953 o | 2: t1+1
2959 o | 2: t1+1
2954 |/
2960 |/
2955 o 1: t1+0
2961 o 1: t1+0
2956 |
2962 |
2957 o 0: null+1
2963 o 0: null+1
2958
2964
2959
2965
2960 One ancestor tag: closest wins:
2966 One ancestor tag: closest wins:
2961
2967
2962 $ hg tag -r 2 -m t2 -d '7 0' t2
2968 $ hg tag -r 2 -m t2 -d '7 0' t2
2963 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2969 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2964 @ 7: t2+3
2970 @ 7: t2+3
2965 |
2971 |
2966 o 6: t2+2
2972 o 6: t2+2
2967 |
2973 |
2968 o 5: t2+1
2974 o 5: t2+1
2969 |\
2975 |\
2970 | o 4: t1+2
2976 | o 4: t1+2
2971 | |
2977 | |
2972 | o 3: t1+1
2978 | o 3: t1+1
2973 | |
2979 | |
2974 o | 2: t2+0
2980 o | 2: t2+0
2975 |/
2981 |/
2976 o 1: t1+0
2982 o 1: t1+0
2977 |
2983 |
2978 o 0: null+1
2984 o 0: null+1
2979
2985
2980
2986
2981 Two branch tags: more recent wins if same number of changes:
2987 Two branch tags: more recent wins if same number of changes:
2982
2988
2983 $ hg tag -r 3 -m t3 -d '8 0' t3
2989 $ hg tag -r 3 -m t3 -d '8 0' t3
2984 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2990 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2985 @ 8: t3+5
2991 @ 8: t3+5
2986 |
2992 |
2987 o 7: t3+4
2993 o 7: t3+4
2988 |
2994 |
2989 o 6: t3+3
2995 o 6: t3+3
2990 |
2996 |
2991 o 5: t3+2
2997 o 5: t3+2
2992 |\
2998 |\
2993 | o 4: t3+1
2999 | o 4: t3+1
2994 | |
3000 | |
2995 | o 3: t3+0
3001 | o 3: t3+0
2996 | |
3002 | |
2997 o | 2: t2+0
3003 o | 2: t2+0
2998 |/
3004 |/
2999 o 1: t1+0
3005 o 1: t1+0
3000 |
3006 |
3001 o 0: null+1
3007 o 0: null+1
3002
3008
3003
3009
3004 Two branch tags: fewest changes wins:
3010 Two branch tags: fewest changes wins:
3005
3011
3006 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
3012 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
3007 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3013 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3008 @ 9: t4+5,6
3014 @ 9: t4+5,6
3009 |
3015 |
3010 o 8: t4+4,5
3016 o 8: t4+4,5
3011 |
3017 |
3012 o 7: t4+3,4
3018 o 7: t4+3,4
3013 |
3019 |
3014 o 6: t4+2,3
3020 o 6: t4+2,3
3015 |
3021 |
3016 o 5: t4+1,2
3022 o 5: t4+1,2
3017 |\
3023 |\
3018 | o 4: t4+0,0
3024 | o 4: t4+0,0
3019 | |
3025 | |
3020 | o 3: t3+0,0
3026 | o 3: t3+0,0
3021 | |
3027 | |
3022 o | 2: t2+0,0
3028 o | 2: t2+0,0
3023 |/
3029 |/
3024 o 1: t1+0,0
3030 o 1: t1+0,0
3025 |
3031 |
3026 o 0: null+1,1
3032 o 0: null+1,1
3027
3033
3028
3034
3029 Merged tag overrides:
3035 Merged tag overrides:
3030
3036
3031 $ hg tag -r 5 -m t5 -d '9 0' t5
3037 $ hg tag -r 5 -m t5 -d '9 0' t5
3032 $ hg tag -r 3 -m at3 -d '10 0' at3
3038 $ hg tag -r 3 -m at3 -d '10 0' at3
3033 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3039 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3034 @ 11: t5+6
3040 @ 11: t5+6
3035 |
3041 |
3036 o 10: t5+5
3042 o 10: t5+5
3037 |
3043 |
3038 o 9: t5+4
3044 o 9: t5+4
3039 |
3045 |
3040 o 8: t5+3
3046 o 8: t5+3
3041 |
3047 |
3042 o 7: t5+2
3048 o 7: t5+2
3043 |
3049 |
3044 o 6: t5+1
3050 o 6: t5+1
3045 |
3051 |
3046 o 5: t5+0
3052 o 5: t5+0
3047 |\
3053 |\
3048 | o 4: t4+0
3054 | o 4: t4+0
3049 | |
3055 | |
3050 | o 3: at3:t3+0
3056 | o 3: at3:t3+0
3051 | |
3057 | |
3052 o | 2: t2+0
3058 o | 2: t2+0
3053 |/
3059 |/
3054 o 1: t1+0
3060 o 1: t1+0
3055 |
3061 |
3056 o 0: null+1
3062 o 0: null+1
3057
3063
3058
3064
3059 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3065 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3060 @ 11: t5+6,6
3066 @ 11: t5+6,6
3061 |
3067 |
3062 o 10: t5+5,5
3068 o 10: t5+5,5
3063 |
3069 |
3064 o 9: t5+4,4
3070 o 9: t5+4,4
3065 |
3071 |
3066 o 8: t5+3,3
3072 o 8: t5+3,3
3067 |
3073 |
3068 o 7: t5+2,2
3074 o 7: t5+2,2
3069 |
3075 |
3070 o 6: t5+1,1
3076 o 6: t5+1,1
3071 |
3077 |
3072 o 5: t5+0,0
3078 o 5: t5+0,0
3073 |\
3079 |\
3074 | o 4: t4+0,0
3080 | o 4: t4+0,0
3075 | |
3081 | |
3076 | o 3: at3+0,0 t3+0,0
3082 | o 3: at3+0,0 t3+0,0
3077 | |
3083 | |
3078 o | 2: t2+0,0
3084 o | 2: t2+0,0
3079 |/
3085 |/
3080 o 1: t1+0,0
3086 o 1: t1+0,0
3081 |
3087 |
3082 o 0: null+1,1
3088 o 0: null+1,1
3083
3089
3084
3090
3085 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3091 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3086 @ 11: t3, C: 9, D: 8
3092 @ 11: t3, C: 9, D: 8
3087 |
3093 |
3088 o 10: t3, C: 8, D: 7
3094 o 10: t3, C: 8, D: 7
3089 |
3095 |
3090 o 9: t3, C: 7, D: 6
3096 o 9: t3, C: 7, D: 6
3091 |
3097 |
3092 o 8: t3, C: 6, D: 5
3098 o 8: t3, C: 6, D: 5
3093 |
3099 |
3094 o 7: t3, C: 5, D: 4
3100 o 7: t3, C: 5, D: 4
3095 |
3101 |
3096 o 6: t3, C: 4, D: 3
3102 o 6: t3, C: 4, D: 3
3097 |
3103 |
3098 o 5: t3, C: 3, D: 2
3104 o 5: t3, C: 3, D: 2
3099 |\
3105 |\
3100 | o 4: t3, C: 1, D: 1
3106 | o 4: t3, C: 1, D: 1
3101 | |
3107 | |
3102 | o 3: t3, C: 0, D: 0
3108 | o 3: t3, C: 0, D: 0
3103 | |
3109 | |
3104 o | 2: t1, C: 1, D: 1
3110 o | 2: t1, C: 1, D: 1
3105 |/
3111 |/
3106 o 1: t1, C: 0, D: 0
3112 o 1: t1, C: 0, D: 0
3107 |
3113 |
3108 o 0: null, C: 1, D: 1
3114 o 0: null, C: 1, D: 1
3109
3115
3110
3116
3111 $ cd ..
3117 $ cd ..
3112
3118
3113
3119
3114 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3120 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3115 if it is a relative path
3121 if it is a relative path
3116
3122
3117 $ mkdir -p home/styles
3123 $ mkdir -p home/styles
3118
3124
3119 $ cat > home/styles/teststyle <<EOF
3125 $ cat > home/styles/teststyle <<EOF
3120 > changeset = 'test {rev}:{node|short}\n'
3126 > changeset = 'test {rev}:{node|short}\n'
3121 > EOF
3127 > EOF
3122
3128
3123 $ HOME=`pwd`/home; export HOME
3129 $ HOME=`pwd`/home; export HOME
3124
3130
3125 $ cat > latesttag/.hg/hgrc <<EOF
3131 $ cat > latesttag/.hg/hgrc <<EOF
3126 > [ui]
3132 > [ui]
3127 > style = ~/styles/teststyle
3133 > style = ~/styles/teststyle
3128 > EOF
3134 > EOF
3129
3135
3130 $ hg -R latesttag tip
3136 $ hg -R latesttag tip
3131 test 11:97e5943b523a
3137 test 11:97e5943b523a
3132
3138
3133 Test recursive showlist template (issue1989):
3139 Test recursive showlist template (issue1989):
3134
3140
3135 $ cat > style1989 <<EOF
3141 $ cat > style1989 <<EOF
3136 > changeset = '{file_mods}{manifest}{extras}'
3142 > changeset = '{file_mods}{manifest}{extras}'
3137 > file_mod = 'M|{author|person}\n'
3143 > file_mod = 'M|{author|person}\n'
3138 > manifest = '{rev},{author}\n'
3144 > manifest = '{rev},{author}\n'
3139 > extra = '{key}: {author}\n'
3145 > extra = '{key}: {author}\n'
3140 > EOF
3146 > EOF
3141
3147
3142 $ hg -R latesttag log -r tip --style=style1989
3148 $ hg -R latesttag log -r tip --style=style1989
3143 M|test
3149 M|test
3144 11,test
3150 11,test
3145 branch: test
3151 branch: test
3146
3152
3147 Test new-style inline templating:
3153 Test new-style inline templating:
3148
3154
3149 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3155 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3150 modified files: .hgtags
3156 modified files: .hgtags
3151
3157
3152
3158
3153 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3159 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3154 hg: parse error: keyword 'rev' is not iterable
3160 hg: parse error: keyword 'rev' is not iterable
3155 [255]
3161 [255]
3156 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3162 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3157 hg: parse error: None is not iterable
3163 hg: parse error: None is not iterable
3158 [255]
3164 [255]
3159
3165
3160 Test new-style inline templating of non-list/dict type:
3166 Test new-style inline templating of non-list/dict type:
3161
3167
3162 $ hg log -R latesttag -r tip -T '{manifest}\n'
3168 $ hg log -R latesttag -r tip -T '{manifest}\n'
3163 11:2bc6e9006ce2
3169 11:2bc6e9006ce2
3164 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3170 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3165 string length: 15
3171 string length: 15
3166 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3172 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3167 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3173 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3168
3174
3169 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3175 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3170 branch: default
3176 branch: default
3171 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3177 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3172 hg: parse error: None is not iterable
3178 hg: parse error: None is not iterable
3173 [255]
3179 [255]
3174 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3180 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3175 branch: default
3181 branch: default
3176 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3182 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3177 0:ce3cec86e6c2
3183 0:ce3cec86e6c2
3178 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3184 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3179 9:fbc7cd862e9c
3185 9:fbc7cd862e9c
3180
3186
3181 Test manifest/get() can be join()-ed as before, though it's silly:
3187 Test manifest/get() can be join()-ed as before, though it's silly:
3182
3188
3183 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3189 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3184 11:2bc6e9006ce2
3190 11:2bc6e9006ce2
3185 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
3191 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
3186 default
3192 default
3187
3193
3188 Test min/max of integers
3194 Test min/max of integers
3189
3195
3190 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
3196 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
3191 9
3197 9
3192 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
3198 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
3193 10
3199 10
3194
3200
3195 Test dot operator precedence:
3201 Test dot operator precedence:
3196
3202
3197 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
3203 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
3198 (template
3204 (template
3199 (|
3205 (|
3200 (.
3206 (.
3201 (symbol 'manifest')
3207 (symbol 'manifest')
3202 (symbol 'node'))
3208 (symbol 'node'))
3203 (symbol 'short'))
3209 (symbol 'short'))
3204 (string '\n'))
3210 (string '\n'))
3205 89f4071fec70
3211 89f4071fec70
3206
3212
3207 (the following examples are invalid, but seem natural in parsing POV)
3213 (the following examples are invalid, but seem natural in parsing POV)
3208
3214
3209 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
3215 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
3210 (template
3216 (template
3211 (|
3217 (|
3212 (symbol 'foo')
3218 (symbol 'foo')
3213 (.
3219 (.
3214 (symbol 'bar')
3220 (symbol 'bar')
3215 (symbol 'baz')))
3221 (symbol 'baz')))
3216 (string '\n'))
3222 (string '\n'))
3217 [255]
3223 [255]
3218 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
3224 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
3219 (template
3225 (template
3220 (.
3226 (.
3221 (symbol 'foo')
3227 (symbol 'foo')
3222 (func
3228 (func
3223 (symbol 'bar')
3229 (symbol 'bar')
3224 None))
3230 None))
3225 (string '\n'))
3231 (string '\n'))
3226 [255]
3232 [255]
3227
3233
3228 Test evaluation of dot operator:
3234 Test evaluation of dot operator:
3229
3235
3230 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
3236 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
3231 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
3237 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
3232 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
3238 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
3233 default
3239 default
3234
3240
3235 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
3241 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
3236 hg: parse error: keyword 'author' has no member
3242 hg: parse error: keyword 'author' has no member
3237 [255]
3243 [255]
3238 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
3244 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
3239 hg: parse error: 'a' has no member
3245 hg: parse error: 'a' has no member
3240 [255]
3246 [255]
3241
3247
3242 Test the sub function of templating for expansion:
3248 Test the sub function of templating for expansion:
3243
3249
3244 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3250 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3245 xx
3251 xx
3246
3252
3247 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3253 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3248 hg: parse error: sub got an invalid pattern: [
3254 hg: parse error: sub got an invalid pattern: [
3249 [255]
3255 [255]
3250 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3256 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3251 hg: parse error: sub got an invalid replacement: \1
3257 hg: parse error: sub got an invalid replacement: \1
3252 [255]
3258 [255]
3253
3259
3254 Test the strip function with chars specified:
3260 Test the strip function with chars specified:
3255
3261
3256 $ hg log -R latesttag --template '{desc}\n'
3262 $ hg log -R latesttag --template '{desc}\n'
3257 at3
3263 at3
3258 t5
3264 t5
3259 t4
3265 t4
3260 t3
3266 t3
3261 t2
3267 t2
3262 t1
3268 t1
3263 merge
3269 merge
3264 h2e
3270 h2e
3265 h2d
3271 h2d
3266 h1c
3272 h1c
3267 b
3273 b
3268 a
3274 a
3269
3275
3270 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3276 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3271 at3
3277 at3
3272 5
3278 5
3273 4
3279 4
3274 3
3280 3
3275 2
3281 2
3276 1
3282 1
3277 merg
3283 merg
3278 h2
3284 h2
3279 h2d
3285 h2d
3280 h1c
3286 h1c
3281 b
3287 b
3282 a
3288 a
3283
3289
3284 Test date format:
3290 Test date format:
3285
3291
3286 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3292 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3287 date: 70 01 01 10 +0000
3293 date: 70 01 01 10 +0000
3288 date: 70 01 01 09 +0000
3294 date: 70 01 01 09 +0000
3289 date: 70 01 01 04 +0000
3295 date: 70 01 01 04 +0000
3290 date: 70 01 01 08 +0000
3296 date: 70 01 01 08 +0000
3291 date: 70 01 01 07 +0000
3297 date: 70 01 01 07 +0000
3292 date: 70 01 01 06 +0000
3298 date: 70 01 01 06 +0000
3293 date: 70 01 01 05 +0100
3299 date: 70 01 01 05 +0100
3294 date: 70 01 01 04 +0000
3300 date: 70 01 01 04 +0000
3295 date: 70 01 01 03 +0000
3301 date: 70 01 01 03 +0000
3296 date: 70 01 01 02 +0000
3302 date: 70 01 01 02 +0000
3297 date: 70 01 01 01 +0000
3303 date: 70 01 01 01 +0000
3298 date: 70 01 01 00 +0000
3304 date: 70 01 01 00 +0000
3299
3305
3300 Test invalid date:
3306 Test invalid date:
3301
3307
3302 $ hg log -R latesttag -T '{date(rev)}\n'
3308 $ hg log -R latesttag -T '{date(rev)}\n'
3303 hg: parse error: date expects a date information
3309 hg: parse error: date expects a date information
3304 [255]
3310 [255]
3305
3311
3306 Test integer literal:
3312 Test integer literal:
3307
3313
3308 $ hg debugtemplate -v '{(0)}\n'
3314 $ hg debugtemplate -v '{(0)}\n'
3309 (template
3315 (template
3310 (group
3316 (group
3311 (integer '0'))
3317 (integer '0'))
3312 (string '\n'))
3318 (string '\n'))
3313 0
3319 0
3314 $ hg debugtemplate -v '{(123)}\n'
3320 $ hg debugtemplate -v '{(123)}\n'
3315 (template
3321 (template
3316 (group
3322 (group
3317 (integer '123'))
3323 (integer '123'))
3318 (string '\n'))
3324 (string '\n'))
3319 123
3325 123
3320 $ hg debugtemplate -v '{(-4)}\n'
3326 $ hg debugtemplate -v '{(-4)}\n'
3321 (template
3327 (template
3322 (group
3328 (group
3323 (negate
3329 (negate
3324 (integer '4')))
3330 (integer '4')))
3325 (string '\n'))
3331 (string '\n'))
3326 -4
3332 -4
3327 $ hg debugtemplate '{(-)}\n'
3333 $ hg debugtemplate '{(-)}\n'
3328 hg: parse error at 3: not a prefix: )
3334 hg: parse error at 3: not a prefix: )
3329 [255]
3335 [255]
3330 $ hg debugtemplate '{(-a)}\n'
3336 $ hg debugtemplate '{(-a)}\n'
3331 hg: parse error: negation needs an integer argument
3337 hg: parse error: negation needs an integer argument
3332 [255]
3338 [255]
3333
3339
3334 top-level integer literal is interpreted as symbol (i.e. variable name):
3340 top-level integer literal is interpreted as symbol (i.e. variable name):
3335
3341
3336 $ hg debugtemplate -D 1=one -v '{1}\n'
3342 $ hg debugtemplate -D 1=one -v '{1}\n'
3337 (template
3343 (template
3338 (integer '1')
3344 (integer '1')
3339 (string '\n'))
3345 (string '\n'))
3340 one
3346 one
3341 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3347 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3342 (template
3348 (template
3343 (func
3349 (func
3344 (symbol 'if')
3350 (symbol 'if')
3345 (list
3351 (list
3346 (string 't')
3352 (string 't')
3347 (template
3353 (template
3348 (integer '1'))))
3354 (integer '1'))))
3349 (string '\n'))
3355 (string '\n'))
3350 one
3356 one
3351 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3357 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3352 (template
3358 (template
3353 (|
3359 (|
3354 (integer '1')
3360 (integer '1')
3355 (symbol 'stringify'))
3361 (symbol 'stringify'))
3356 (string '\n'))
3362 (string '\n'))
3357 one
3363 one
3358
3364
3359 unless explicit symbol is expected:
3365 unless explicit symbol is expected:
3360
3366
3361 $ hg log -Ra -r0 -T '{desc|1}\n'
3367 $ hg log -Ra -r0 -T '{desc|1}\n'
3362 hg: parse error: expected a symbol, got 'integer'
3368 hg: parse error: expected a symbol, got 'integer'
3363 [255]
3369 [255]
3364 $ hg log -Ra -r0 -T '{1()}\n'
3370 $ hg log -Ra -r0 -T '{1()}\n'
3365 hg: parse error: expected a symbol, got 'integer'
3371 hg: parse error: expected a symbol, got 'integer'
3366 [255]
3372 [255]
3367
3373
3368 Test string literal:
3374 Test string literal:
3369
3375
3370 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3376 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3371 (template
3377 (template
3372 (string 'string with no template fragment')
3378 (string 'string with no template fragment')
3373 (string '\n'))
3379 (string '\n'))
3374 string with no template fragment
3380 string with no template fragment
3375 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3381 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3376 (template
3382 (template
3377 (template
3383 (template
3378 (string 'template: ')
3384 (string 'template: ')
3379 (symbol 'rev'))
3385 (symbol 'rev'))
3380 (string '\n'))
3386 (string '\n'))
3381 template: 0
3387 template: 0
3382 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3388 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3383 (template
3389 (template
3384 (string 'rawstring: {rev}')
3390 (string 'rawstring: {rev}')
3385 (string '\n'))
3391 (string '\n'))
3386 rawstring: {rev}
3392 rawstring: {rev}
3387 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3393 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3388 (template
3394 (template
3389 (%
3395 (%
3390 (symbol 'files')
3396 (symbol 'files')
3391 (string 'rawstring: {file}'))
3397 (string 'rawstring: {file}'))
3392 (string '\n'))
3398 (string '\n'))
3393 rawstring: {file}
3399 rawstring: {file}
3394
3400
3395 Test string escaping:
3401 Test string escaping:
3396
3402
3397 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3403 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3398 >
3404 >
3399 <>\n<[>
3405 <>\n<[>
3400 <>\n<]>
3406 <>\n<]>
3401 <>\n<
3407 <>\n<
3402
3408
3403 $ hg log -R latesttag -r 0 \
3409 $ hg log -R latesttag -r 0 \
3404 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3410 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3405 >
3411 >
3406 <>\n<[>
3412 <>\n<[>
3407 <>\n<]>
3413 <>\n<]>
3408 <>\n<
3414 <>\n<
3409
3415
3410 $ hg log -R latesttag -r 0 -T esc \
3416 $ hg log -R latesttag -r 0 -T esc \
3411 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3417 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3412 >
3418 >
3413 <>\n<[>
3419 <>\n<[>
3414 <>\n<]>
3420 <>\n<]>
3415 <>\n<
3421 <>\n<
3416
3422
3417 $ cat <<'EOF' > esctmpl
3423 $ cat <<'EOF' > esctmpl
3418 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3424 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3419 > EOF
3425 > EOF
3420 $ hg log -R latesttag -r 0 --style ./esctmpl
3426 $ hg log -R latesttag -r 0 --style ./esctmpl
3421 >
3427 >
3422 <>\n<[>
3428 <>\n<[>
3423 <>\n<]>
3429 <>\n<]>
3424 <>\n<
3430 <>\n<
3425
3431
3426 Test string escaping of quotes:
3432 Test string escaping of quotes:
3427
3433
3428 $ hg log -Ra -r0 -T '{"\""}\n'
3434 $ hg log -Ra -r0 -T '{"\""}\n'
3429 "
3435 "
3430 $ hg log -Ra -r0 -T '{"\\\""}\n'
3436 $ hg log -Ra -r0 -T '{"\\\""}\n'
3431 \"
3437 \"
3432 $ hg log -Ra -r0 -T '{r"\""}\n'
3438 $ hg log -Ra -r0 -T '{r"\""}\n'
3433 \"
3439 \"
3434 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3440 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3435 \\\"
3441 \\\"
3436
3442
3437
3443
3438 $ hg log -Ra -r0 -T '{"\""}\n'
3444 $ hg log -Ra -r0 -T '{"\""}\n'
3439 "
3445 "
3440 $ hg log -Ra -r0 -T '{"\\\""}\n'
3446 $ hg log -Ra -r0 -T '{"\\\""}\n'
3441 \"
3447 \"
3442 $ hg log -Ra -r0 -T '{r"\""}\n'
3448 $ hg log -Ra -r0 -T '{r"\""}\n'
3443 \"
3449 \"
3444 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3450 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3445 \\\"
3451 \\\"
3446
3452
3447 Test exception in quoted template. single backslash before quotation mark is
3453 Test exception in quoted template. single backslash before quotation mark is
3448 stripped before parsing:
3454 stripped before parsing:
3449
3455
3450 $ cat <<'EOF' > escquotetmpl
3456 $ cat <<'EOF' > escquotetmpl
3451 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3457 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3452 > EOF
3458 > EOF
3453 $ cd latesttag
3459 $ cd latesttag
3454 $ hg log -r 2 --style ../escquotetmpl
3460 $ hg log -r 2 --style ../escquotetmpl
3455 " \" \" \\" head1
3461 " \" \" \\" head1
3456
3462
3457 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3463 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3458 valid
3464 valid
3459 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3465 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3460 valid
3466 valid
3461
3467
3462 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3468 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3463 _evalifliteral() templates (issue4733):
3469 _evalifliteral() templates (issue4733):
3464
3470
3465 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3471 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3466 "2
3472 "2
3467 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3473 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3468 "2
3474 "2
3469 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3475 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3470 "2
3476 "2
3471
3477
3472 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3478 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3473 \"
3479 \"
3474 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3480 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3475 \"
3481 \"
3476 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3482 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3477 \"
3483 \"
3478
3484
3479 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3485 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3480 \\\"
3486 \\\"
3481 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3487 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3482 \\\"
3488 \\\"
3483 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3489 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3484 \\\"
3490 \\\"
3485
3491
3486 escaped single quotes and errors:
3492 escaped single quotes and errors:
3487
3493
3488 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3494 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3489 foo
3495 foo
3490 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3496 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3491 foo
3497 foo
3492 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3498 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3493 hg: parse error at 21: unterminated string
3499 hg: parse error at 21: unterminated string
3494 [255]
3500 [255]
3495 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3501 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3496 hg: parse error: trailing \ in string
3502 hg: parse error: trailing \ in string
3497 [255]
3503 [255]
3498 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3504 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3499 hg: parse error: trailing \ in string
3505 hg: parse error: trailing \ in string
3500 [255]
3506 [255]
3501
3507
3502 $ cd ..
3508 $ cd ..
3503
3509
3504 Test leading backslashes:
3510 Test leading backslashes:
3505
3511
3506 $ cd latesttag
3512 $ cd latesttag
3507 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3513 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3508 {rev} {file}
3514 {rev} {file}
3509 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3515 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3510 \2 \head1
3516 \2 \head1
3511 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3517 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3512 \{rev} \{file}
3518 \{rev} \{file}
3513 $ cd ..
3519 $ cd ..
3514
3520
3515 Test leading backslashes in "if" expression (issue4714):
3521 Test leading backslashes in "if" expression (issue4714):
3516
3522
3517 $ cd latesttag
3523 $ cd latesttag
3518 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3524 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3519 {rev} \{rev}
3525 {rev} \{rev}
3520 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3526 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3521 \2 \\{rev}
3527 \2 \\{rev}
3522 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3528 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3523 \{rev} \\\{rev}
3529 \{rev} \\\{rev}
3524 $ cd ..
3530 $ cd ..
3525
3531
3526 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3532 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3527
3533
3528 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3534 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3529 \x6e
3535 \x6e
3530 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3536 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3531 \x5c\x786e
3537 \x5c\x786e
3532 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3538 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3533 \x6e
3539 \x6e
3534 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3540 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3535 \x5c\x786e
3541 \x5c\x786e
3536
3542
3537 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3543 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3538 \x6e
3544 \x6e
3539 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3545 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3540 \x5c\x786e
3546 \x5c\x786e
3541 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3547 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3542 \x6e
3548 \x6e
3543 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3549 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3544 \x5c\x786e
3550 \x5c\x786e
3545
3551
3546 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3552 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3547 fourth
3553 fourth
3548 second
3554 second
3549 third
3555 third
3550 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3556 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3551 fourth\nsecond\nthird
3557 fourth\nsecond\nthird
3552
3558
3553 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3559 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3554 <p>
3560 <p>
3555 1st
3561 1st
3556 </p>
3562 </p>
3557 <p>
3563 <p>
3558 2nd
3564 2nd
3559 </p>
3565 </p>
3560 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3566 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3561 <p>
3567 <p>
3562 1st\n\n2nd
3568 1st\n\n2nd
3563 </p>
3569 </p>
3564 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3570 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3565 1st
3571 1st
3566
3572
3567 2nd
3573 2nd
3568
3574
3569 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3575 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3570 o perso
3576 o perso
3571 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3577 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3572 no person
3578 no person
3573 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3579 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3574 o perso
3580 o perso
3575 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3581 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3576 no perso
3582 no perso
3577
3583
3578 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3584 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3579 -o perso-
3585 -o perso-
3580 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3586 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3581 no person
3587 no person
3582 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3588 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3583 \x2do perso\x2d
3589 \x2do perso\x2d
3584 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3590 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3585 -o perso-
3591 -o perso-
3586 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3592 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3587 \x2do perso\x6e
3593 \x2do perso\x6e
3588
3594
3589 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3595 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3590 fourth
3596 fourth
3591 second
3597 second
3592 third
3598 third
3593
3599
3594 Test string escaping in nested expression:
3600 Test string escaping in nested expression:
3595
3601
3596 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3602 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3597 fourth\x6esecond\x6ethird
3603 fourth\x6esecond\x6ethird
3598 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3604 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3599 fourth\x6esecond\x6ethird
3605 fourth\x6esecond\x6ethird
3600
3606
3601 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3607 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3602 fourth\x6esecond\x6ethird
3608 fourth\x6esecond\x6ethird
3603 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3609 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3604 fourth\x5c\x786esecond\x5c\x786ethird
3610 fourth\x5c\x786esecond\x5c\x786ethird
3605
3611
3606 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3612 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3607 3:\x6eo user, \x6eo domai\x6e
3613 3:\x6eo user, \x6eo domai\x6e
3608 4:\x5c\x786eew bra\x5c\x786ech
3614 4:\x5c\x786eew bra\x5c\x786ech
3609
3615
3610 Test quotes in nested expression are evaluated just like a $(command)
3616 Test quotes in nested expression are evaluated just like a $(command)
3611 substitution in POSIX shells:
3617 substitution in POSIX shells:
3612
3618
3613 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3619 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3614 8:95c24699272e
3620 8:95c24699272e
3615 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3621 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3616 {8} "95c24699272e"
3622 {8} "95c24699272e"
3617
3623
3618 Test recursive evaluation:
3624 Test recursive evaluation:
3619
3625
3620 $ hg init r
3626 $ hg init r
3621 $ cd r
3627 $ cd r
3622 $ echo a > a
3628 $ echo a > a
3623 $ hg ci -Am '{rev}'
3629 $ hg ci -Am '{rev}'
3624 adding a
3630 adding a
3625 $ hg log -r 0 --template '{if(rev, desc)}\n'
3631 $ hg log -r 0 --template '{if(rev, desc)}\n'
3626 {rev}
3632 {rev}
3627 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3633 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3628 test 0
3634 test 0
3629
3635
3630 $ hg branch -q 'text.{rev}'
3636 $ hg branch -q 'text.{rev}'
3631 $ echo aa >> aa
3637 $ echo aa >> aa
3632 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3638 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3633
3639
3634 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3640 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3635 {node|short}desc to
3641 {node|short}desc to
3636 text.{rev}be wrapped
3642 text.{rev}be wrapped
3637 text.{rev}desc to be
3643 text.{rev}desc to be
3638 text.{rev}wrapped (no-eol)
3644 text.{rev}wrapped (no-eol)
3639 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3645 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3640 bcc7ff960b8e:desc to
3646 bcc7ff960b8e:desc to
3641 text.1:be wrapped
3647 text.1:be wrapped
3642 text.1:desc to be
3648 text.1:desc to be
3643 text.1:wrapped (no-eol)
3649 text.1:wrapped (no-eol)
3644 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3650 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3645 hg: parse error: fill expects an integer width
3651 hg: parse error: fill expects an integer width
3646 [255]
3652 [255]
3647
3653
3648 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3654 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3649 bcc7ff960b8e:desc to be
3655 bcc7ff960b8e:desc to be
3650 termwidth.1:wrapped desc
3656 termwidth.1:wrapped desc
3651 termwidth.1:to be wrapped (no-eol)
3657 termwidth.1:to be wrapped (no-eol)
3652
3658
3653 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3659 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3654 {node|short} (no-eol)
3660 {node|short} (no-eol)
3655 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3661 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3656 bcc-ff---b-e (no-eol)
3662 bcc-ff---b-e (no-eol)
3657
3663
3658 $ cat >> .hg/hgrc <<EOF
3664 $ cat >> .hg/hgrc <<EOF
3659 > [extensions]
3665 > [extensions]
3660 > color=
3666 > color=
3661 > [color]
3667 > [color]
3662 > mode=ansi
3668 > mode=ansi
3663 > text.{rev} = red
3669 > text.{rev} = red
3664 > text.1 = green
3670 > text.1 = green
3665 > EOF
3671 > EOF
3666 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3672 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3667 \x1b[0;31mtext\x1b[0m (esc)
3673 \x1b[0;31mtext\x1b[0m (esc)
3668 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3674 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3669 \x1b[0;32mtext\x1b[0m (esc)
3675 \x1b[0;32mtext\x1b[0m (esc)
3670
3676
3671 color effect can be specified without quoting:
3677 color effect can be specified without quoting:
3672
3678
3673 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3679 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3674 \x1b[0;31mtext\x1b[0m (esc)
3680 \x1b[0;31mtext\x1b[0m (esc)
3675
3681
3676 color effects can be nested (issue5413)
3682 color effects can be nested (issue5413)
3677
3683
3678 $ hg debugtemplate --color=always \
3684 $ hg debugtemplate --color=always \
3679 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3685 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3680 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
3686 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
3681
3687
3682 pad() should interact well with color codes (issue5416)
3688 pad() should interact well with color codes (issue5416)
3683
3689
3684 $ hg debugtemplate --color=always \
3690 $ hg debugtemplate --color=always \
3685 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3691 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3686 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3692 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3687
3693
3688 label should be no-op if color is disabled:
3694 label should be no-op if color is disabled:
3689
3695
3690 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3696 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3691 text
3697 text
3692 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3698 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3693 text
3699 text
3694
3700
3695 Test branches inside if statement:
3701 Test branches inside if statement:
3696
3702
3697 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3703 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3698 no
3704 no
3699
3705
3700 Test dict constructor:
3706 Test dict constructor:
3701
3707
3702 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3708 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3703 y=f7769ec2ab97 x=0
3709 y=f7769ec2ab97 x=0
3704 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3710 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3705 x=0
3711 x=0
3706 y=f7769ec2ab97
3712 y=f7769ec2ab97
3707 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3713 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3708 {"x": 0, "y": "f7769ec2ab97"}
3714 {"x": 0, "y": "f7769ec2ab97"}
3709 $ hg log -r 0 -T '{dict()|json}\n'
3715 $ hg log -r 0 -T '{dict()|json}\n'
3710 {}
3716 {}
3711
3717
3712 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3718 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3713 rev=0 node=f7769ec2ab97
3719 rev=0 node=f7769ec2ab97
3714 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3720 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3715 rev=0 node=f7769ec2ab97
3721 rev=0 node=f7769ec2ab97
3716
3722
3717 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3723 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3718 hg: parse error: duplicated dict key 'rev' inferred
3724 hg: parse error: duplicated dict key 'rev' inferred
3719 [255]
3725 [255]
3720 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3726 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3721 hg: parse error: duplicated dict key 'node' inferred
3727 hg: parse error: duplicated dict key 'node' inferred
3722 [255]
3728 [255]
3723 $ hg log -r 0 -T '{dict(1 + 2)}'
3729 $ hg log -r 0 -T '{dict(1 + 2)}'
3724 hg: parse error: dict key cannot be inferred
3730 hg: parse error: dict key cannot be inferred
3725 [255]
3731 [255]
3726
3732
3727 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3733 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3728 hg: parse error: dict got multiple values for keyword argument 'x'
3734 hg: parse error: dict got multiple values for keyword argument 'x'
3729 [255]
3735 [255]
3730
3736
3731 Test get function:
3737 Test get function:
3732
3738
3733 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3739 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3734 default
3740 default
3735 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3741 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3736 default
3742 default
3737 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3743 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3738 hg: parse error: get() expects a dict as first argument
3744 hg: parse error: get() expects a dict as first argument
3739 [255]
3745 [255]
3740
3746
3741 Test json filter applied to hybrid object:
3747 Test json filter applied to hybrid object:
3742
3748
3743 $ hg log -r0 -T '{files|json}\n'
3749 $ hg log -r0 -T '{files|json}\n'
3744 ["a"]
3750 ["a"]
3745 $ hg log -r0 -T '{extras|json}\n'
3751 $ hg log -r0 -T '{extras|json}\n'
3746 {"branch": "default"}
3752 {"branch": "default"}
3747
3753
3748 Test localdate(date, tz) function:
3754 Test localdate(date, tz) function:
3749
3755
3750 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3756 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3751 1970-01-01 09:00 +0900
3757 1970-01-01 09:00 +0900
3752 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3758 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3753 1970-01-01 00:00 +0000
3759 1970-01-01 00:00 +0000
3754 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3760 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3755 hg: parse error: localdate expects a timezone
3761 hg: parse error: localdate expects a timezone
3756 [255]
3762 [255]
3757 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3763 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3758 1970-01-01 02:00 +0200
3764 1970-01-01 02:00 +0200
3759 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3765 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3760 1970-01-01 00:00 +0000
3766 1970-01-01 00:00 +0000
3761 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3767 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3762 1970-01-01 00:00 +0000
3768 1970-01-01 00:00 +0000
3763 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3769 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3764 hg: parse error: localdate expects a timezone
3770 hg: parse error: localdate expects a timezone
3765 [255]
3771 [255]
3766 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3772 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3767 hg: parse error: localdate expects a timezone
3773 hg: parse error: localdate expects a timezone
3768 [255]
3774 [255]
3769
3775
3770 Test shortest(node) function:
3776 Test shortest(node) function:
3771
3777
3772 $ echo b > b
3778 $ echo b > b
3773 $ hg ci -qAm b
3779 $ hg ci -qAm b
3774 $ hg log --template '{shortest(node)}\n'
3780 $ hg log --template '{shortest(node)}\n'
3775 e777
3781 e777
3776 bcc7
3782 bcc7
3777 f776
3783 f776
3778 $ hg log --template '{shortest(node, 10)}\n'
3784 $ hg log --template '{shortest(node, 10)}\n'
3779 e777603221
3785 e777603221
3780 bcc7ff960b
3786 bcc7ff960b
3781 f7769ec2ab
3787 f7769ec2ab
3782 $ hg log --template '{node|shortest}\n' -l1
3788 $ hg log --template '{node|shortest}\n' -l1
3783 e777
3789 e777
3784
3790
3785 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3791 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3786 f7769ec2ab
3792 f7769ec2ab
3787 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3793 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3788 hg: parse error: shortest() expects an integer minlength
3794 hg: parse error: shortest() expects an integer minlength
3789 [255]
3795 [255]
3790
3796
3791 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3797 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3792 ffff
3798 ffff
3793
3799
3794 $ cd ..
3800 $ cd ..
3795
3801
3796 Test shortest(node) with the repo having short hash collision:
3802 Test shortest(node) with the repo having short hash collision:
3797
3803
3798 $ hg init hashcollision
3804 $ hg init hashcollision
3799 $ cd hashcollision
3805 $ cd hashcollision
3800 $ cat <<EOF >> .hg/hgrc
3806 $ cat <<EOF >> .hg/hgrc
3801 > [experimental]
3807 > [experimental]
3802 > evolution.createmarkers=True
3808 > evolution.createmarkers=True
3803 > EOF
3809 > EOF
3804 $ echo 0 > a
3810 $ echo 0 > a
3805 $ hg ci -qAm 0
3811 $ hg ci -qAm 0
3806 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3812 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3807 > hg up -q 0
3813 > hg up -q 0
3808 > echo $i > a
3814 > echo $i > a
3809 > hg ci -qm $i
3815 > hg ci -qm $i
3810 > done
3816 > done
3811 $ hg up -q null
3817 $ hg up -q null
3812 $ hg log -r0: -T '{rev}:{node}\n'
3818 $ hg log -r0: -T '{rev}:{node}\n'
3813 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3819 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3814 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3820 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3815 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3821 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3816 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3822 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3817 4:10776689e627b465361ad5c296a20a487e153ca4
3823 4:10776689e627b465361ad5c296a20a487e153ca4
3818 5:a00be79088084cb3aff086ab799f8790e01a976b
3824 5:a00be79088084cb3aff086ab799f8790e01a976b
3819 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3825 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3820 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3826 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3821 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3827 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3822 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3828 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3823 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3829 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3824 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3830 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3825 obsoleted 1 changesets
3831 obsoleted 1 changesets
3826 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3832 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3827 obsoleted 1 changesets
3833 obsoleted 1 changesets
3828 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3834 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3829 obsoleted 1 changesets
3835 obsoleted 1 changesets
3830
3836
3831 nodes starting with '11' (we don't have the revision number '11' though)
3837 nodes starting with '11' (we don't have the revision number '11' though)
3832
3838
3833 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3839 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3834 1:1142
3840 1:1142
3835 2:1140
3841 2:1140
3836 3:11d
3842 3:11d
3837
3843
3838 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3844 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3839
3845
3840 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3846 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3841 6:a0b
3847 6:a0b
3842 7:a04
3848 7:a04
3843
3849
3844 node '10' conflicts with the revision number '10' even if it is hidden
3850 node '10' conflicts with the revision number '10' even if it is hidden
3845 (we could exclude hidden revision numbers, but currently we don't)
3851 (we could exclude hidden revision numbers, but currently we don't)
3846
3852
3847 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3853 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3848 4:107
3854 4:107
3849 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3855 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3850 4:107
3856 4:107
3851
3857
3852 node 'c562' should be unique if the other 'c562' nodes are hidden
3858 node 'c562' should be unique if the other 'c562' nodes are hidden
3853 (but we don't try the slow path to filter out hidden nodes for now)
3859 (but we don't try the slow path to filter out hidden nodes for now)
3854
3860
3855 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3861 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3856 8:c5625
3862 8:c5625
3857 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3863 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3858 8:c5625
3864 8:c5625
3859 9:c5623
3865 9:c5623
3860 10:c562d
3866 10:c562d
3861
3867
3862 $ cd ..
3868 $ cd ..
3863
3869
3864 Test pad function
3870 Test pad function
3865
3871
3866 $ cd r
3872 $ cd r
3867
3873
3868 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3874 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3869 2 test
3875 2 test
3870 1 {node|short}
3876 1 {node|short}
3871 0 test
3877 0 test
3872
3878
3873 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3879 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3874 2 test
3880 2 test
3875 1 {node|short}
3881 1 {node|short}
3876 0 test
3882 0 test
3877
3883
3878 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3884 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3879 2------------------- test
3885 2------------------- test
3880 1------------------- {node|short}
3886 1------------------- {node|short}
3881 0------------------- test
3887 0------------------- test
3882
3888
3883 Test template string in pad function
3889 Test template string in pad function
3884
3890
3885 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3891 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3886 {0} test
3892 {0} test
3887
3893
3888 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3894 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3889 \{rev} test
3895 \{rev} test
3890
3896
3891 Test width argument passed to pad function
3897 Test width argument passed to pad function
3892
3898
3893 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3899 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3894 0 test
3900 0 test
3895 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3901 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3896 hg: parse error: pad() expects an integer width
3902 hg: parse error: pad() expects an integer width
3897 [255]
3903 [255]
3898
3904
3899 Test invalid fillchar passed to pad function
3905 Test invalid fillchar passed to pad function
3900
3906
3901 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3907 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3902 hg: parse error: pad() expects a single fill character
3908 hg: parse error: pad() expects a single fill character
3903 [255]
3909 [255]
3904 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3910 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3905 hg: parse error: pad() expects a single fill character
3911 hg: parse error: pad() expects a single fill character
3906 [255]
3912 [255]
3907
3913
3908 Test boolean argument passed to pad function
3914 Test boolean argument passed to pad function
3909
3915
3910 no crash
3916 no crash
3911
3917
3912 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3918 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3913 ---------0
3919 ---------0
3914
3920
3915 string/literal
3921 string/literal
3916
3922
3917 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3923 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3918 ---------0
3924 ---------0
3919 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3925 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3920 0---------
3926 0---------
3921 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3927 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3922 0---------
3928 0---------
3923
3929
3924 unknown keyword is evaluated to ''
3930 unknown keyword is evaluated to ''
3925
3931
3926 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3932 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3927 0---------
3933 0---------
3928
3934
3929 Test separate function
3935 Test separate function
3930
3936
3931 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3937 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3932 a-b-c
3938 a-b-c
3933 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3939 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3934 0:f7769ec2ab97 test default
3940 0:f7769ec2ab97 test default
3935 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3941 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3936 a \x1b[0;31mb\x1b[0m c d (esc)
3942 a \x1b[0;31mb\x1b[0m c d (esc)
3937
3943
3938 Test boolean expression/literal passed to if function
3944 Test boolean expression/literal passed to if function
3939
3945
3940 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3946 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3941 rev 0 is True
3947 rev 0 is True
3942 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3948 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3943 literal 0 is True as well
3949 literal 0 is True as well
3944 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3950 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3945 empty string is False
3951 empty string is False
3946 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3952 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3947 empty list is False
3953 empty list is False
3948 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3954 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3949 true is True
3955 true is True
3950 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3956 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3951 false is False
3957 false is False
3952 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3958 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3953 non-empty string is True
3959 non-empty string is True
3954
3960
3955 Test ifcontains function
3961 Test ifcontains function
3956
3962
3957 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3963 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3958 2 is in the string
3964 2 is in the string
3959 1 is not
3965 1 is not
3960 0 is in the string
3966 0 is in the string
3961
3967
3962 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3968 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3963 2 is in the string
3969 2 is in the string
3964 1 is not
3970 1 is not
3965 0 is in the string
3971 0 is in the string
3966
3972
3967 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3973 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3968 2 did not add a
3974 2 did not add a
3969 1 did not add a
3975 1 did not add a
3970 0 added a
3976 0 added a
3971
3977
3972 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3978 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3973 2 is parent of 1
3979 2 is parent of 1
3974 1
3980 1
3975 0
3981 0
3976
3982
3977 Test revset function
3983 Test revset function
3978
3984
3979 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3985 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3980 2 current rev
3986 2 current rev
3981 1 not current rev
3987 1 not current rev
3982 0 not current rev
3988 0 not current rev
3983
3989
3984 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3990 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3985 2 match rev
3991 2 match rev
3986 1 match rev
3992 1 match rev
3987 0 not match rev
3993 0 not match rev
3988
3994
3989 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
3995 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
3990 type not match
3996 type not match
3991
3997
3992 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3998 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3993 2 Parents: 1
3999 2 Parents: 1
3994 1 Parents: 0
4000 1 Parents: 0
3995 0 Parents:
4001 0 Parents:
3996
4002
3997 $ cat >> .hg/hgrc <<EOF
4003 $ cat >> .hg/hgrc <<EOF
3998 > [revsetalias]
4004 > [revsetalias]
3999 > myparents(\$1) = parents(\$1)
4005 > myparents(\$1) = parents(\$1)
4000 > EOF
4006 > EOF
4001 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
4007 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
4002 2 Parents: 1
4008 2 Parents: 1
4003 1 Parents: 0
4009 1 Parents: 0
4004 0 Parents:
4010 0 Parents:
4005
4011
4006 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
4012 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
4007 Rev: 2
4013 Rev: 2
4008 Ancestor: 0
4014 Ancestor: 0
4009 Ancestor: 1
4015 Ancestor: 1
4010 Ancestor: 2
4016 Ancestor: 2
4011
4017
4012 Rev: 1
4018 Rev: 1
4013 Ancestor: 0
4019 Ancestor: 0
4014 Ancestor: 1
4020 Ancestor: 1
4015
4021
4016 Rev: 0
4022 Rev: 0
4017 Ancestor: 0
4023 Ancestor: 0
4018
4024
4019 $ hg log --template '{revset("TIP"|lower)}\n' -l1
4025 $ hg log --template '{revset("TIP"|lower)}\n' -l1
4020 2
4026 2
4021
4027
4022 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
4028 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
4023 2
4029 2
4024
4030
4025 a list template is evaluated for each item of revset/parents
4031 a list template is evaluated for each item of revset/parents
4026
4032
4027 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
4033 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
4028 2 p: 1:bcc7ff960b8e
4034 2 p: 1:bcc7ff960b8e
4029 1 p: 0:f7769ec2ab97
4035 1 p: 0:f7769ec2ab97
4030 0 p:
4036 0 p:
4031
4037
4032 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
4038 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
4033 2 p: 1:bcc7ff960b8e -1:000000000000
4039 2 p: 1:bcc7ff960b8e -1:000000000000
4034 1 p: 0:f7769ec2ab97 -1:000000000000
4040 1 p: 0:f7769ec2ab97 -1:000000000000
4035 0 p: -1:000000000000 -1:000000000000
4041 0 p: -1:000000000000 -1:000000000000
4036
4042
4037 therefore, 'revcache' should be recreated for each rev
4043 therefore, 'revcache' should be recreated for each rev
4038
4044
4039 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
4045 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
4040 2 aa b
4046 2 aa b
4041 p
4047 p
4042 1
4048 1
4043 p a
4049 p a
4044 0 a
4050 0 a
4045 p
4051 p
4046
4052
4047 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
4053 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
4048 2 aa b
4054 2 aa b
4049 p
4055 p
4050 1
4056 1
4051 p a
4057 p a
4052 0 a
4058 0 a
4053 p
4059 p
4054
4060
4055 a revset item must be evaluated as an integer revision, not an offset from tip
4061 a revset item must be evaluated as an integer revision, not an offset from tip
4056
4062
4057 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
4063 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
4058 -1:000000000000
4064 -1:000000000000
4059 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
4065 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
4060 -1:000000000000
4066 -1:000000000000
4061
4067
4062 join() should pick '{rev}' from revset items:
4068 join() should pick '{rev}' from revset items:
4063
4069
4064 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
4070 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
4065 4, 5
4071 4, 5
4066
4072
4067 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
4073 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
4068 default. join() should agree with the default formatting:
4074 default. join() should agree with the default formatting:
4069
4075
4070 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
4076 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
4071 5:13207e5a10d9, 4:bbe44766e73d
4077 5:13207e5a10d9, 4:bbe44766e73d
4072
4078
4073 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
4079 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
4074 5:13207e5a10d9fd28ec424934298e176197f2c67f,
4080 5:13207e5a10d9fd28ec424934298e176197f2c67f,
4075 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
4081 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
4076
4082
4077 Test files function
4083 Test files function
4078
4084
4079 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
4085 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
4080 2
4086 2
4081 a
4087 a
4082 aa
4088 aa
4083 b
4089 b
4084 1
4090 1
4085 a
4091 a
4086 0
4092 0
4087 a
4093 a
4088
4094
4089 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
4095 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
4090 2
4096 2
4091 aa
4097 aa
4092 1
4098 1
4093
4099
4094 0
4100 0
4095
4101
4096
4102
4097 Test relpath function
4103 Test relpath function
4098
4104
4099 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4105 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4100 a
4106 a
4101 $ cd ..
4107 $ cd ..
4102 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4108 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4103 r/a
4109 r/a
4104 $ cd r
4110 $ cd r
4105
4111
4106 Test active bookmark templating
4112 Test active bookmark templating
4107
4113
4108 $ hg book foo
4114 $ hg book foo
4109 $ hg book bar
4115 $ hg book bar
4110 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4116 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4111 2 bar* foo
4117 2 bar* foo
4112 1
4118 1
4113 0
4119 0
4114 $ hg log --template "{rev} {activebookmark}\n"
4120 $ hg log --template "{rev} {activebookmark}\n"
4115 2 bar
4121 2 bar
4116 1
4122 1
4117 0
4123 0
4118 $ hg bookmarks --inactive bar
4124 $ hg bookmarks --inactive bar
4119 $ hg log --template "{rev} {activebookmark}\n"
4125 $ hg log --template "{rev} {activebookmark}\n"
4120 2
4126 2
4121 1
4127 1
4122 0
4128 0
4123 $ hg book -r1 baz
4129 $ hg book -r1 baz
4124 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4130 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4125 2 bar foo
4131 2 bar foo
4126 1 baz
4132 1 baz
4127 0
4133 0
4128 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4134 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4129 2 t
4135 2 t
4130 1 f
4136 1 f
4131 0 f
4137 0 f
4132
4138
4133 Test namespaces dict
4139 Test namespaces dict
4134
4140
4135 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4141 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4136 2
4142 2
4137 bookmarks color=bookmark builtin=True
4143 bookmarks color=bookmark builtin=True
4138 bar,foo
4144 bar,foo
4139 tags color=tag builtin=True
4145 tags color=tag builtin=True
4140 tip
4146 tip
4141 branches color=branch builtin=True
4147 branches color=branch builtin=True
4142 text.{rev}
4148 text.{rev}
4143 revnames color=revname builtin=False
4149 revnames color=revname builtin=False
4144 r2
4150 r2
4145
4151
4146 1
4152 1
4147 bookmarks color=bookmark builtin=True
4153 bookmarks color=bookmark builtin=True
4148 baz
4154 baz
4149 tags color=tag builtin=True
4155 tags color=tag builtin=True
4150
4156
4151 branches color=branch builtin=True
4157 branches color=branch builtin=True
4152 text.{rev}
4158 text.{rev}
4153 revnames color=revname builtin=False
4159 revnames color=revname builtin=False
4154 r1
4160 r1
4155
4161
4156 0
4162 0
4157 bookmarks color=bookmark builtin=True
4163 bookmarks color=bookmark builtin=True
4158
4164
4159 tags color=tag builtin=True
4165 tags color=tag builtin=True
4160
4166
4161 branches color=branch builtin=True
4167 branches color=branch builtin=True
4162 default
4168 default
4163 revnames color=revname builtin=False
4169 revnames color=revname builtin=False
4164 r0
4170 r0
4165
4171
4166 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4172 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4167 bookmarks: bar foo
4173 bookmarks: bar foo
4168 tags: tip
4174 tags: tip
4169 branches: text.{rev}
4175 branches: text.{rev}
4170 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4176 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4171 bookmarks:
4177 bookmarks:
4172 bar
4178 bar
4173 foo
4179 foo
4174 tags:
4180 tags:
4175 tip
4181 tip
4176 branches:
4182 branches:
4177 text.{rev}
4183 text.{rev}
4178 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4184 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4179 bar
4185 bar
4180 foo
4186 foo
4181 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
4187 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
4182 bar
4188 bar
4183 foo
4189 foo
4184
4190
4185 Test stringify on sub expressions
4191 Test stringify on sub expressions
4186
4192
4187 $ cd ..
4193 $ cd ..
4188 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4194 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4189 fourth, second, third
4195 fourth, second, third
4190 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4196 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4191 abc
4197 abc
4192
4198
4193 Test splitlines
4199 Test splitlines
4194
4200
4195 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4201 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4196 @ foo Modify, add, remove, rename
4202 @ foo Modify, add, remove, rename
4197 |
4203 |
4198 o foo future
4204 o foo future
4199 |
4205 |
4200 o foo third
4206 o foo third
4201 |
4207 |
4202 o foo second
4208 o foo second
4203
4209
4204 o foo merge
4210 o foo merge
4205 |\
4211 |\
4206 | o foo new head
4212 | o foo new head
4207 | |
4213 | |
4208 o | foo new branch
4214 o | foo new branch
4209 |/
4215 |/
4210 o foo no user, no domain
4216 o foo no user, no domain
4211 |
4217 |
4212 o foo no person
4218 o foo no person
4213 |
4219 |
4214 o foo other 1
4220 o foo other 1
4215 | foo other 2
4221 | foo other 2
4216 | foo
4222 | foo
4217 | foo other 3
4223 | foo other 3
4218 o foo line 1
4224 o foo line 1
4219 foo line 2
4225 foo line 2
4220
4226
4221 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4227 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4222 line 1 line 2
4228 line 1 line 2
4223 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4229 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4224 line 1|line 2
4230 line 1|line 2
4225
4231
4226 Test startswith
4232 Test startswith
4227 $ hg log -Gv -R a --template "{startswith(desc)}"
4233 $ hg log -Gv -R a --template "{startswith(desc)}"
4228 hg: parse error: startswith expects two arguments
4234 hg: parse error: startswith expects two arguments
4229 [255]
4235 [255]
4230
4236
4231 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4237 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4232 @
4238 @
4233 |
4239 |
4234 o
4240 o
4235 |
4241 |
4236 o
4242 o
4237 |
4243 |
4238 o
4244 o
4239
4245
4240 o
4246 o
4241 |\
4247 |\
4242 | o
4248 | o
4243 | |
4249 | |
4244 o |
4250 o |
4245 |/
4251 |/
4246 o
4252 o
4247 |
4253 |
4248 o
4254 o
4249 |
4255 |
4250 o
4256 o
4251 |
4257 |
4252 o line 1
4258 o line 1
4253 line 2
4259 line 2
4254
4260
4255 Test bad template with better error message
4261 Test bad template with better error message
4256
4262
4257 $ hg log -Gv -R a --template '{desc|user()}'
4263 $ hg log -Gv -R a --template '{desc|user()}'
4258 hg: parse error: expected a symbol, got 'func'
4264 hg: parse error: expected a symbol, got 'func'
4259 [255]
4265 [255]
4260
4266
4261 Test word function (including index out of bounds graceful failure)
4267 Test word function (including index out of bounds graceful failure)
4262
4268
4263 $ hg log -Gv -R a --template "{word('1', desc)}"
4269 $ hg log -Gv -R a --template "{word('1', desc)}"
4264 @ add,
4270 @ add,
4265 |
4271 |
4266 o
4272 o
4267 |
4273 |
4268 o
4274 o
4269 |
4275 |
4270 o
4276 o
4271
4277
4272 o
4278 o
4273 |\
4279 |\
4274 | o head
4280 | o head
4275 | |
4281 | |
4276 o | branch
4282 o | branch
4277 |/
4283 |/
4278 o user,
4284 o user,
4279 |
4285 |
4280 o person
4286 o person
4281 |
4287 |
4282 o 1
4288 o 1
4283 |
4289 |
4284 o 1
4290 o 1
4285
4291
4286
4292
4287 Test word third parameter used as splitter
4293 Test word third parameter used as splitter
4288
4294
4289 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4295 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4290 @ M
4296 @ M
4291 |
4297 |
4292 o future
4298 o future
4293 |
4299 |
4294 o third
4300 o third
4295 |
4301 |
4296 o sec
4302 o sec
4297
4303
4298 o merge
4304 o merge
4299 |\
4305 |\
4300 | o new head
4306 | o new head
4301 | |
4307 | |
4302 o | new branch
4308 o | new branch
4303 |/
4309 |/
4304 o n
4310 o n
4305 |
4311 |
4306 o n
4312 o n
4307 |
4313 |
4308 o
4314 o
4309 |
4315 |
4310 o line 1
4316 o line 1
4311 line 2
4317 line 2
4312
4318
4313 Test word error messages for not enough and too many arguments
4319 Test word error messages for not enough and too many arguments
4314
4320
4315 $ hg log -Gv -R a --template "{word('0')}"
4321 $ hg log -Gv -R a --template "{word('0')}"
4316 hg: parse error: word expects two or three arguments, got 1
4322 hg: parse error: word expects two or three arguments, got 1
4317 [255]
4323 [255]
4318
4324
4319 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4325 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4320 hg: parse error: word expects two or three arguments, got 7
4326 hg: parse error: word expects two or three arguments, got 7
4321 [255]
4327 [255]
4322
4328
4323 Test word for integer literal
4329 Test word for integer literal
4324
4330
4325 $ hg log -R a --template "{word(2, desc)}\n" -r0
4331 $ hg log -R a --template "{word(2, desc)}\n" -r0
4326 line
4332 line
4327
4333
4328 Test word for invalid numbers
4334 Test word for invalid numbers
4329
4335
4330 $ hg log -Gv -R a --template "{word('a', desc)}"
4336 $ hg log -Gv -R a --template "{word('a', desc)}"
4331 hg: parse error: word expects an integer index
4337 hg: parse error: word expects an integer index
4332 [255]
4338 [255]
4333
4339
4334 Test word for out of range
4340 Test word for out of range
4335
4341
4336 $ hg log -R a --template "{word(10000, desc)}"
4342 $ hg log -R a --template "{word(10000, desc)}"
4337 $ hg log -R a --template "{word(-10000, desc)}"
4343 $ hg log -R a --template "{word(-10000, desc)}"
4338
4344
4339 Test indent and not adding to empty lines
4345 Test indent and not adding to empty lines
4340
4346
4341 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4347 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4342 -----
4348 -----
4343 > line 1
4349 > line 1
4344 >> line 2
4350 >> line 2
4345 -----
4351 -----
4346 > other 1
4352 > other 1
4347 >> other 2
4353 >> other 2
4348
4354
4349 >> other 3
4355 >> other 3
4350
4356
4351 Test with non-strings like dates
4357 Test with non-strings like dates
4352
4358
4353 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4359 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4354 1200000.00
4360 1200000.00
4355 1300000.00
4361 1300000.00
4356
4362
4357 Test broken string escapes:
4363 Test broken string escapes:
4358
4364
4359 $ hg log -T "bogus\\" -R a
4365 $ hg log -T "bogus\\" -R a
4360 hg: parse error: trailing \ in string
4366 hg: parse error: trailing \ in string
4361 [255]
4367 [255]
4362 $ hg log -T "\\xy" -R a
4368 $ hg log -T "\\xy" -R a
4363 hg: parse error: invalid \x escape
4369 hg: parse error: invalid \x escape
4364 [255]
4370 [255]
4365
4371
4366 json filter should escape HTML tags so that the output can be embedded in hgweb:
4372 json filter should escape HTML tags so that the output can be embedded in hgweb:
4367
4373
4368 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4374 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4369 "\u003cfoo@example.org\u003e"
4375 "\u003cfoo@example.org\u003e"
4370
4376
4371 Templater supports aliases of symbol and func() styles:
4377 Templater supports aliases of symbol and func() styles:
4372
4378
4373 $ hg clone -q a aliases
4379 $ hg clone -q a aliases
4374 $ cd aliases
4380 $ cd aliases
4375 $ cat <<EOF >> .hg/hgrc
4381 $ cat <<EOF >> .hg/hgrc
4376 > [templatealias]
4382 > [templatealias]
4377 > r = rev
4383 > r = rev
4378 > rn = "{r}:{node|short}"
4384 > rn = "{r}:{node|short}"
4379 > status(c, files) = files % "{c} {file}\n"
4385 > status(c, files) = files % "{c} {file}\n"
4380 > utcdate(d) = localdate(d, "UTC")
4386 > utcdate(d) = localdate(d, "UTC")
4381 > EOF
4387 > EOF
4382
4388
4383 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4389 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4384 (template
4390 (template
4385 (symbol 'rn')
4391 (symbol 'rn')
4386 (string ' ')
4392 (string ' ')
4387 (|
4393 (|
4388 (func
4394 (func
4389 (symbol 'utcdate')
4395 (symbol 'utcdate')
4390 (symbol 'date'))
4396 (symbol 'date'))
4391 (symbol 'isodate'))
4397 (symbol 'isodate'))
4392 (string '\n'))
4398 (string '\n'))
4393 * expanded:
4399 * expanded:
4394 (template
4400 (template
4395 (template
4401 (template
4396 (symbol 'rev')
4402 (symbol 'rev')
4397 (string ':')
4403 (string ':')
4398 (|
4404 (|
4399 (symbol 'node')
4405 (symbol 'node')
4400 (symbol 'short')))
4406 (symbol 'short')))
4401 (string ' ')
4407 (string ' ')
4402 (|
4408 (|
4403 (func
4409 (func
4404 (symbol 'localdate')
4410 (symbol 'localdate')
4405 (list
4411 (list
4406 (symbol 'date')
4412 (symbol 'date')
4407 (string 'UTC')))
4413 (string 'UTC')))
4408 (symbol 'isodate'))
4414 (symbol 'isodate'))
4409 (string '\n'))
4415 (string '\n'))
4410 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4416 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4411
4417
4412 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4418 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4413 (template
4419 (template
4414 (func
4420 (func
4415 (symbol 'status')
4421 (symbol 'status')
4416 (list
4422 (list
4417 (string 'A')
4423 (string 'A')
4418 (symbol 'file_adds'))))
4424 (symbol 'file_adds'))))
4419 * expanded:
4425 * expanded:
4420 (template
4426 (template
4421 (%
4427 (%
4422 (symbol 'file_adds')
4428 (symbol 'file_adds')
4423 (template
4429 (template
4424 (string 'A')
4430 (string 'A')
4425 (string ' ')
4431 (string ' ')
4426 (symbol 'file')
4432 (symbol 'file')
4427 (string '\n'))))
4433 (string '\n'))))
4428 A a
4434 A a
4429
4435
4430 A unary function alias can be called as a filter:
4436 A unary function alias can be called as a filter:
4431
4437
4432 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4438 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4433 (template
4439 (template
4434 (|
4440 (|
4435 (|
4441 (|
4436 (symbol 'date')
4442 (symbol 'date')
4437 (symbol 'utcdate'))
4443 (symbol 'utcdate'))
4438 (symbol 'isodate'))
4444 (symbol 'isodate'))
4439 (string '\n'))
4445 (string '\n'))
4440 * expanded:
4446 * expanded:
4441 (template
4447 (template
4442 (|
4448 (|
4443 (func
4449 (func
4444 (symbol 'localdate')
4450 (symbol 'localdate')
4445 (list
4451 (list
4446 (symbol 'date')
4452 (symbol 'date')
4447 (string 'UTC')))
4453 (string 'UTC')))
4448 (symbol 'isodate'))
4454 (symbol 'isodate'))
4449 (string '\n'))
4455 (string '\n'))
4450 1970-01-12 13:46 +0000
4456 1970-01-12 13:46 +0000
4451
4457
4452 Aliases should be applied only to command arguments and templates in hgrc.
4458 Aliases should be applied only to command arguments and templates in hgrc.
4453 Otherwise, our stock styles and web templates could be corrupted:
4459 Otherwise, our stock styles and web templates could be corrupted:
4454
4460
4455 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4461 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4456 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4462 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4457
4463
4458 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4464 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4459 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4465 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4460
4466
4461 $ cat <<EOF > tmpl
4467 $ cat <<EOF > tmpl
4462 > changeset = 'nothing expanded:{rn}\n'
4468 > changeset = 'nothing expanded:{rn}\n'
4463 > EOF
4469 > EOF
4464 $ hg log -r0 --style ./tmpl
4470 $ hg log -r0 --style ./tmpl
4465 nothing expanded:
4471 nothing expanded:
4466
4472
4467 Aliases in formatter:
4473 Aliases in formatter:
4468
4474
4469 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4475 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4470 default 6:d41e714fe50d
4476 default 6:d41e714fe50d
4471 foo 4:bbe44766e73d
4477 foo 4:bbe44766e73d
4472
4478
4473 Aliases should honor HGPLAIN:
4479 Aliases should honor HGPLAIN:
4474
4480
4475 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4481 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4476 nothing expanded:
4482 nothing expanded:
4477 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4483 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4478 0:1e4e1b8f71e0
4484 0:1e4e1b8f71e0
4479
4485
4480 Unparsable alias:
4486 Unparsable alias:
4481
4487
4482 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4488 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4483 (template
4489 (template
4484 (symbol 'bad'))
4490 (symbol 'bad'))
4485 abort: bad definition of template alias "bad": at 2: not a prefix: end
4491 abort: bad definition of template alias "bad": at 2: not a prefix: end
4486 [255]
4492 [255]
4487 $ hg log --config templatealias.bad='x(' -T '{bad}'
4493 $ hg log --config templatealias.bad='x(' -T '{bad}'
4488 abort: bad definition of template alias "bad": at 2: not a prefix: end
4494 abort: bad definition of template alias "bad": at 2: not a prefix: end
4489 [255]
4495 [255]
4490
4496
4491 $ cd ..
4497 $ cd ..
4492
4498
4493 Set up repository for non-ascii encoding tests:
4499 Set up repository for non-ascii encoding tests:
4494
4500
4495 $ hg init nonascii
4501 $ hg init nonascii
4496 $ cd nonascii
4502 $ cd nonascii
4497 $ $PYTHON <<EOF
4503 $ $PYTHON <<EOF
4498 > open('latin1', 'w').write('\xe9')
4504 > open('latin1', 'w').write('\xe9')
4499 > open('utf-8', 'w').write('\xc3\xa9')
4505 > open('utf-8', 'w').write('\xc3\xa9')
4500 > EOF
4506 > EOF
4501 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4507 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4502 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4508 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4503
4509
4504 json filter should try round-trip conversion to utf-8:
4510 json filter should try round-trip conversion to utf-8:
4505
4511
4506 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4512 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4507 "\u00e9"
4513 "\u00e9"
4508 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4514 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4509 "non-ascii branch: \u00e9"
4515 "non-ascii branch: \u00e9"
4510
4516
4511 json filter takes input as utf-8b:
4517 json filter takes input as utf-8b:
4512
4518
4513 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4519 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4514 "\u00e9"
4520 "\u00e9"
4515 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4521 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4516 "\udce9"
4522 "\udce9"
4517
4523
4518 utf8 filter:
4524 utf8 filter:
4519
4525
4520 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4526 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4521 round-trip: c3a9
4527 round-trip: c3a9
4522 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4528 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4523 decoded: c3a9
4529 decoded: c3a9
4524 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4530 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4525 abort: decoding near * (glob)
4531 abort: decoding near * (glob)
4526 [255]
4532 [255]
4527 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4533 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4528 abort: template filter 'utf8' is not compatible with keyword 'rev'
4534 abort: template filter 'utf8' is not compatible with keyword 'rev'
4529 [255]
4535 [255]
4530
4536
4531 pad width:
4537 pad width:
4532
4538
4533 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4539 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4534 \xc3\xa9- (esc)
4540 \xc3\xa9- (esc)
4535
4541
4536 $ cd ..
4542 $ cd ..
4537
4543
4538 Test that template function in extension is registered as expected
4544 Test that template function in extension is registered as expected
4539
4545
4540 $ cd a
4546 $ cd a
4541
4547
4542 $ cat <<EOF > $TESTTMP/customfunc.py
4548 $ cat <<EOF > $TESTTMP/customfunc.py
4543 > from mercurial import registrar
4549 > from mercurial import registrar
4544 >
4550 >
4545 > templatefunc = registrar.templatefunc()
4551 > templatefunc = registrar.templatefunc()
4546 >
4552 >
4547 > @templatefunc('custom()')
4553 > @templatefunc('custom()')
4548 > def custom(context, mapping, args):
4554 > def custom(context, mapping, args):
4549 > return 'custom'
4555 > return 'custom'
4550 > EOF
4556 > EOF
4551 $ cat <<EOF > .hg/hgrc
4557 $ cat <<EOF > .hg/hgrc
4552 > [extensions]
4558 > [extensions]
4553 > customfunc = $TESTTMP/customfunc.py
4559 > customfunc = $TESTTMP/customfunc.py
4554 > EOF
4560 > EOF
4555
4561
4556 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4562 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4557 custom
4563 custom
4558
4564
4559 $ cd ..
4565 $ cd ..
4560
4566
4561 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4567 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4562 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4568 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4563 columns. We don't care about other aspects of the graph rendering here.
4569 columns. We don't care about other aspects of the graph rendering here.
4564
4570
4565 $ hg init graphwidth
4571 $ hg init graphwidth
4566 $ cd graphwidth
4572 $ cd graphwidth
4567
4573
4568 $ wrappabletext="a a a a a a a a a a a a"
4574 $ wrappabletext="a a a a a a a a a a a a"
4569
4575
4570 $ printf "first\n" > file
4576 $ printf "first\n" > file
4571 $ hg add file
4577 $ hg add file
4572 $ hg commit -m "$wrappabletext"
4578 $ hg commit -m "$wrappabletext"
4573
4579
4574 $ printf "first\nsecond\n" > file
4580 $ printf "first\nsecond\n" > file
4575 $ hg commit -m "$wrappabletext"
4581 $ hg commit -m "$wrappabletext"
4576
4582
4577 $ hg checkout 0
4583 $ hg checkout 0
4578 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4584 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4579 $ printf "third\nfirst\n" > file
4585 $ printf "third\nfirst\n" > file
4580 $ hg commit -m "$wrappabletext"
4586 $ hg commit -m "$wrappabletext"
4581 created new head
4587 created new head
4582
4588
4583 $ hg merge
4589 $ hg merge
4584 merging file
4590 merging file
4585 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4591 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4586 (branch merge, don't forget to commit)
4592 (branch merge, don't forget to commit)
4587
4593
4588 $ hg log --graph -T "{graphwidth}"
4594 $ hg log --graph -T "{graphwidth}"
4589 @ 3
4595 @ 3
4590 |
4596 |
4591 | @ 5
4597 | @ 5
4592 |/
4598 |/
4593 o 3
4599 o 3
4594
4600
4595 $ hg commit -m "$wrappabletext"
4601 $ hg commit -m "$wrappabletext"
4596
4602
4597 $ hg log --graph -T "{graphwidth}"
4603 $ hg log --graph -T "{graphwidth}"
4598 @ 5
4604 @ 5
4599 |\
4605 |\
4600 | o 5
4606 | o 5
4601 | |
4607 | |
4602 o | 5
4608 o | 5
4603 |/
4609 |/
4604 o 3
4610 o 3
4605
4611
4606
4612
4607 $ hg checkout 0
4613 $ hg checkout 0
4608 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4614 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4609 $ printf "third\nfirst\nsecond\n" > file
4615 $ printf "third\nfirst\nsecond\n" > file
4610 $ hg commit -m "$wrappabletext"
4616 $ hg commit -m "$wrappabletext"
4611 created new head
4617 created new head
4612
4618
4613 $ hg log --graph -T "{graphwidth}"
4619 $ hg log --graph -T "{graphwidth}"
4614 @ 3
4620 @ 3
4615 |
4621 |
4616 | o 7
4622 | o 7
4617 | |\
4623 | |\
4618 +---o 7
4624 +---o 7
4619 | |
4625 | |
4620 | o 5
4626 | o 5
4621 |/
4627 |/
4622 o 3
4628 o 3
4623
4629
4624
4630
4625 $ hg log --graph -T "{graphwidth}" -r 3
4631 $ hg log --graph -T "{graphwidth}" -r 3
4626 o 5
4632 o 5
4627 |\
4633 |\
4628 ~ ~
4634 ~ ~
4629
4635
4630 $ hg log --graph -T "{graphwidth}" -r 1
4636 $ hg log --graph -T "{graphwidth}" -r 1
4631 o 3
4637 o 3
4632 |
4638 |
4633 ~
4639 ~
4634
4640
4635 $ hg merge
4641 $ hg merge
4636 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4642 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4637 (branch merge, don't forget to commit)
4643 (branch merge, don't forget to commit)
4638 $ hg commit -m "$wrappabletext"
4644 $ hg commit -m "$wrappabletext"
4639
4645
4640 $ printf "seventh\n" >> file
4646 $ printf "seventh\n" >> file
4641 $ hg commit -m "$wrappabletext"
4647 $ hg commit -m "$wrappabletext"
4642
4648
4643 $ hg log --graph -T "{graphwidth}"
4649 $ hg log --graph -T "{graphwidth}"
4644 @ 3
4650 @ 3
4645 |
4651 |
4646 o 5
4652 o 5
4647 |\
4653 |\
4648 | o 5
4654 | o 5
4649 | |
4655 | |
4650 o | 7
4656 o | 7
4651 |\ \
4657 |\ \
4652 | o | 7
4658 | o | 7
4653 | |/
4659 | |/
4654 o / 5
4660 o / 5
4655 |/
4661 |/
4656 o 3
4662 o 3
4657
4663
4658
4664
4659 The point of graphwidth is to allow wrapping that accounts for the space taken
4665 The point of graphwidth is to allow wrapping that accounts for the space taken
4660 by the graph.
4666 by the graph.
4661
4667
4662 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4668 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4663 @ a a a a
4669 @ a a a a
4664 | a a a a
4670 | a a a a
4665 | a a a a
4671 | a a a a
4666 o a a a
4672 o a a a
4667 |\ a a a
4673 |\ a a a
4668 | | a a a
4674 | | a a a
4669 | | a a a
4675 | | a a a
4670 | o a a a
4676 | o a a a
4671 | | a a a
4677 | | a a a
4672 | | a a a
4678 | | a a a
4673 | | a a a
4679 | | a a a
4674 o | a a
4680 o | a a
4675 |\ \ a a
4681 |\ \ a a
4676 | | | a a
4682 | | | a a
4677 | | | a a
4683 | | | a a
4678 | | | a a
4684 | | | a a
4679 | | | a a
4685 | | | a a
4680 | o | a a
4686 | o | a a
4681 | |/ a a
4687 | |/ a a
4682 | | a a
4688 | | a a
4683 | | a a
4689 | | a a
4684 | | a a
4690 | | a a
4685 | | a a
4691 | | a a
4686 o | a a a
4692 o | a a a
4687 |/ a a a
4693 |/ a a a
4688 | a a a
4694 | a a a
4689 | a a a
4695 | a a a
4690 o a a a a
4696 o a a a a
4691 a a a a
4697 a a a a
4692 a a a a
4698 a a a a
4693
4699
4694 Something tricky happens when there are elided nodes; the next drawn row of
4700 Something tricky happens when there are elided nodes; the next drawn row of
4695 edges can be more than one column wider, but the graph width only increases by
4701 edges can be more than one column wider, but the graph width only increases by
4696 one column. The remaining columns are added in between the nodes.
4702 one column. The remaining columns are added in between the nodes.
4697
4703
4698 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4704 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4699 o 5
4705 o 5
4700 |\
4706 |\
4701 | \
4707 | \
4702 | :\
4708 | :\
4703 o : : 7
4709 o : : 7
4704 :/ /
4710 :/ /
4705 : o 5
4711 : o 5
4706 :/
4712 :/
4707 o 3
4713 o 3
4708
4714
4709
4715
4710 $ cd ..
4716 $ cd ..
4711
4717
General Comments 0
You need to be logged in to leave comments. Login now