##// END OF EJS Templates
formatter: parse name of built-in formatter templates in standard way...
Yuya Nishihara -
r43370:90b9a7e0 default
parent child Browse files
Show More
@@ -1,762 +1,769 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'reponame', b'[%s]\\n', b'baz')
97 ... fm.write(b'reponame', b'[%s]\\n', b'baz')
98 ... files(ui, fm.nested(b'files', tmpl=b'{reponame}'))
98 ... files(ui, fm.nested(b'files', tmpl=b'{reponame}'))
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'{reponame}: {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 contextlib
110 import contextlib
111 import itertools
111 import itertools
112 import os
112 import os
113
113
114 from .i18n import _
114 from .i18n import _
115 from .node import (
115 from .node import (
116 hex,
116 hex,
117 short,
117 short,
118 )
118 )
119 from .thirdparty import attr
119 from .thirdparty import attr
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 templateutil,
127 templateutil,
128 util,
128 util,
129 )
129 )
130 from .utils import (
130 from .utils import (
131 cborutil,
131 cborutil,
132 dateutil,
132 dateutil,
133 stringutil,
133 stringutil,
134 )
134 )
135
135
136 pickle = util.pickle
136 pickle = util.pickle
137
137
138
138
139 class _nullconverter(object):
139 class _nullconverter(object):
140 '''convert non-primitive data types to be processed by formatter'''
140 '''convert non-primitive data types to be processed by formatter'''
141
141
142 # set to True if context object should be stored as item
142 # set to True if context object should be stored as item
143 storecontext = False
143 storecontext = False
144
144
145 @staticmethod
145 @staticmethod
146 def wrapnested(data, tmpl, sep):
146 def wrapnested(data, tmpl, sep):
147 '''wrap nested data by appropriate type'''
147 '''wrap nested data by appropriate type'''
148 return data
148 return data
149
149
150 @staticmethod
150 @staticmethod
151 def formatdate(date, fmt):
151 def formatdate(date, fmt):
152 '''convert date tuple to appropriate format'''
152 '''convert date tuple to appropriate format'''
153 # timestamp can be float, but the canonical form should be int
153 # timestamp can be float, but the canonical form should be int
154 ts, tz = date
154 ts, tz = date
155 return (int(ts), tz)
155 return (int(ts), tz)
156
156
157 @staticmethod
157 @staticmethod
158 def formatdict(data, key, value, fmt, sep):
158 def formatdict(data, key, value, fmt, sep):
159 '''convert dict or key-value pairs to appropriate dict format'''
159 '''convert dict or key-value pairs to appropriate dict format'''
160 # use plain dict instead of util.sortdict so that data can be
160 # use plain dict instead of util.sortdict so that data can be
161 # serialized as a builtin dict in pickle output
161 # serialized as a builtin dict in pickle output
162 return dict(data)
162 return dict(data)
163
163
164 @staticmethod
164 @staticmethod
165 def formatlist(data, name, fmt, sep):
165 def formatlist(data, name, fmt, sep):
166 '''convert iterable to appropriate list format'''
166 '''convert iterable to appropriate list format'''
167 return list(data)
167 return list(data)
168
168
169
169
170 class baseformatter(object):
170 class baseformatter(object):
171 def __init__(self, ui, topic, opts, converter):
171 def __init__(self, ui, topic, opts, converter):
172 self._ui = ui
172 self._ui = ui
173 self._topic = topic
173 self._topic = topic
174 self._opts = opts
174 self._opts = opts
175 self._converter = converter
175 self._converter = converter
176 self._item = None
176 self._item = None
177 # function to convert node to string suitable for this output
177 # function to convert node to string suitable for this output
178 self.hexfunc = hex
178 self.hexfunc = hex
179
179
180 def __enter__(self):
180 def __enter__(self):
181 return self
181 return self
182
182
183 def __exit__(self, exctype, excvalue, traceback):
183 def __exit__(self, exctype, excvalue, traceback):
184 if exctype is None:
184 if exctype is None:
185 self.end()
185 self.end()
186
186
187 def _showitem(self):
187 def _showitem(self):
188 '''show a formatted item once all data is collected'''
188 '''show a formatted item once all data is collected'''
189
189
190 def startitem(self):
190 def startitem(self):
191 '''begin an item in the format list'''
191 '''begin an item in the format list'''
192 if self._item is not None:
192 if self._item is not None:
193 self._showitem()
193 self._showitem()
194 self._item = {}
194 self._item = {}
195
195
196 def formatdate(self, date, fmt=b'%a %b %d %H:%M:%S %Y %1%2'):
196 def formatdate(self, date, fmt=b'%a %b %d %H:%M:%S %Y %1%2'):
197 '''convert date tuple to appropriate format'''
197 '''convert date tuple to appropriate format'''
198 return self._converter.formatdate(date, fmt)
198 return self._converter.formatdate(date, fmt)
199
199
200 def formatdict(self, data, key=b'key', value=b'value', fmt=None, sep=b' '):
200 def formatdict(self, data, key=b'key', value=b'value', fmt=None, sep=b' '):
201 '''convert dict or key-value pairs to appropriate dict format'''
201 '''convert dict or key-value pairs to appropriate dict format'''
202 return self._converter.formatdict(data, key, value, fmt, sep)
202 return self._converter.formatdict(data, key, value, fmt, sep)
203
203
204 def formatlist(self, data, name, fmt=None, sep=b' '):
204 def formatlist(self, data, name, fmt=None, sep=b' '):
205 '''convert iterable to appropriate list format'''
205 '''convert iterable to appropriate list format'''
206 # name is mandatory argument for now, but it could be optional if
206 # name is mandatory argument for now, but it could be optional if
207 # we have default template keyword, e.g. {item}
207 # we have default template keyword, e.g. {item}
208 return self._converter.formatlist(data, name, fmt, sep)
208 return self._converter.formatlist(data, name, fmt, sep)
209
209
210 def context(self, **ctxs):
210 def context(self, **ctxs):
211 '''insert context objects to be used to render template keywords'''
211 '''insert context objects to be used to render template keywords'''
212 ctxs = pycompat.byteskwargs(ctxs)
212 ctxs = pycompat.byteskwargs(ctxs)
213 assert all(k in {b'repo', b'ctx', b'fctx'} for k in ctxs)
213 assert all(k in {b'repo', b'ctx', b'fctx'} for k in ctxs)
214 if self._converter.storecontext:
214 if self._converter.storecontext:
215 # populate missing resources in fctx -> ctx -> repo order
215 # populate missing resources in fctx -> ctx -> repo order
216 if b'fctx' in ctxs and b'ctx' not in ctxs:
216 if b'fctx' in ctxs and b'ctx' not in ctxs:
217 ctxs[b'ctx'] = ctxs[b'fctx'].changectx()
217 ctxs[b'ctx'] = ctxs[b'fctx'].changectx()
218 if b'ctx' in ctxs and b'repo' not in ctxs:
218 if b'ctx' in ctxs and b'repo' not in ctxs:
219 ctxs[b'repo'] = ctxs[b'ctx'].repo()
219 ctxs[b'repo'] = ctxs[b'ctx'].repo()
220 self._item.update(ctxs)
220 self._item.update(ctxs)
221
221
222 def datahint(self):
222 def datahint(self):
223 '''set of field names to be referenced'''
223 '''set of field names to be referenced'''
224 return set()
224 return set()
225
225
226 def data(self, **data):
226 def data(self, **data):
227 '''insert data into item that's not shown in default output'''
227 '''insert data into item that's not shown in default output'''
228 data = pycompat.byteskwargs(data)
228 data = pycompat.byteskwargs(data)
229 self._item.update(data)
229 self._item.update(data)
230
230
231 def write(self, fields, deftext, *fielddata, **opts):
231 def write(self, fields, deftext, *fielddata, **opts):
232 '''do default text output while assigning data to item'''
232 '''do default text output while assigning data to item'''
233 fieldkeys = fields.split()
233 fieldkeys = fields.split()
234 assert len(fieldkeys) == len(fielddata), (fieldkeys, fielddata)
234 assert len(fieldkeys) == len(fielddata), (fieldkeys, fielddata)
235 self._item.update(zip(fieldkeys, fielddata))
235 self._item.update(zip(fieldkeys, fielddata))
236
236
237 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
237 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
238 '''do conditional write (primarily for plain formatter)'''
238 '''do conditional write (primarily for plain formatter)'''
239 fieldkeys = fields.split()
239 fieldkeys = fields.split()
240 assert len(fieldkeys) == len(fielddata)
240 assert len(fieldkeys) == len(fielddata)
241 self._item.update(zip(fieldkeys, fielddata))
241 self._item.update(zip(fieldkeys, fielddata))
242
242
243 def plain(self, text, **opts):
243 def plain(self, text, **opts):
244 '''show raw text for non-templated mode'''
244 '''show raw text for non-templated mode'''
245
245
246 def isplain(self):
246 def isplain(self):
247 '''check for plain formatter usage'''
247 '''check for plain formatter usage'''
248 return False
248 return False
249
249
250 def nested(self, field, tmpl=None, sep=b''):
250 def nested(self, field, tmpl=None, sep=b''):
251 '''sub formatter to store nested data in the specified field'''
251 '''sub formatter to store nested data in the specified field'''
252 data = []
252 data = []
253 self._item[field] = self._converter.wrapnested(data, tmpl, sep)
253 self._item[field] = self._converter.wrapnested(data, tmpl, sep)
254 return _nestedformatter(self._ui, self._converter, data)
254 return _nestedformatter(self._ui, self._converter, data)
255
255
256 def end(self):
256 def end(self):
257 '''end output for the formatter'''
257 '''end output for the formatter'''
258 if self._item is not None:
258 if self._item is not None:
259 self._showitem()
259 self._showitem()
260
260
261
261
262 def nullformatter(ui, topic, opts):
262 def nullformatter(ui, topic, opts):
263 '''formatter that prints nothing'''
263 '''formatter that prints nothing'''
264 return baseformatter(ui, topic, opts, converter=_nullconverter)
264 return baseformatter(ui, topic, opts, converter=_nullconverter)
265
265
266
266
267 class _nestedformatter(baseformatter):
267 class _nestedformatter(baseformatter):
268 '''build sub items and store them in the parent formatter'''
268 '''build sub items and store them in the parent formatter'''
269
269
270 def __init__(self, ui, converter, data):
270 def __init__(self, ui, converter, data):
271 baseformatter.__init__(
271 baseformatter.__init__(
272 self, ui, topic=b'', opts={}, converter=converter
272 self, ui, topic=b'', opts={}, converter=converter
273 )
273 )
274 self._data = data
274 self._data = data
275
275
276 def _showitem(self):
276 def _showitem(self):
277 self._data.append(self._item)
277 self._data.append(self._item)
278
278
279
279
280 def _iteritems(data):
280 def _iteritems(data):
281 '''iterate key-value pairs in stable order'''
281 '''iterate key-value pairs in stable order'''
282 if isinstance(data, dict):
282 if isinstance(data, dict):
283 return sorted(data.iteritems())
283 return sorted(data.iteritems())
284 return data
284 return data
285
285
286
286
287 class _plainconverter(object):
287 class _plainconverter(object):
288 '''convert non-primitive data types to text'''
288 '''convert non-primitive data types to text'''
289
289
290 storecontext = False
290 storecontext = False
291
291
292 @staticmethod
292 @staticmethod
293 def wrapnested(data, tmpl, sep):
293 def wrapnested(data, tmpl, sep):
294 raise error.ProgrammingError(b'plainformatter should never be nested')
294 raise error.ProgrammingError(b'plainformatter should never be nested')
295
295
296 @staticmethod
296 @staticmethod
297 def formatdate(date, fmt):
297 def formatdate(date, fmt):
298 '''stringify date tuple in the given format'''
298 '''stringify date tuple in the given format'''
299 return dateutil.datestr(date, fmt)
299 return dateutil.datestr(date, fmt)
300
300
301 @staticmethod
301 @staticmethod
302 def formatdict(data, key, value, fmt, sep):
302 def formatdict(data, key, value, fmt, sep):
303 '''stringify key-value pairs separated by sep'''
303 '''stringify key-value pairs separated by sep'''
304 prefmt = pycompat.identity
304 prefmt = pycompat.identity
305 if fmt is None:
305 if fmt is None:
306 fmt = b'%s=%s'
306 fmt = b'%s=%s'
307 prefmt = pycompat.bytestr
307 prefmt = pycompat.bytestr
308 return sep.join(
308 return sep.join(
309 fmt % (prefmt(k), prefmt(v)) for k, v in _iteritems(data)
309 fmt % (prefmt(k), prefmt(v)) for k, v in _iteritems(data)
310 )
310 )
311
311
312 @staticmethod
312 @staticmethod
313 def formatlist(data, name, fmt, sep):
313 def formatlist(data, name, fmt, sep):
314 '''stringify iterable separated by sep'''
314 '''stringify iterable separated by sep'''
315 prefmt = pycompat.identity
315 prefmt = pycompat.identity
316 if fmt is None:
316 if fmt is None:
317 fmt = b'%s'
317 fmt = b'%s'
318 prefmt = pycompat.bytestr
318 prefmt = pycompat.bytestr
319 return sep.join(fmt % prefmt(e) for e in data)
319 return sep.join(fmt % prefmt(e) for e in data)
320
320
321
321
322 class plainformatter(baseformatter):
322 class plainformatter(baseformatter):
323 '''the default text output scheme'''
323 '''the default text output scheme'''
324
324
325 def __init__(self, ui, out, topic, opts):
325 def __init__(self, ui, out, topic, opts):
326 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
326 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
327 if ui.debugflag:
327 if ui.debugflag:
328 self.hexfunc = hex
328 self.hexfunc = hex
329 else:
329 else:
330 self.hexfunc = short
330 self.hexfunc = short
331 if ui is out:
331 if ui is out:
332 self._write = ui.write
332 self._write = ui.write
333 else:
333 else:
334 self._write = lambda s, **opts: out.write(s)
334 self._write = lambda s, **opts: out.write(s)
335
335
336 def startitem(self):
336 def startitem(self):
337 pass
337 pass
338
338
339 def data(self, **data):
339 def data(self, **data):
340 pass
340 pass
341
341
342 def write(self, fields, deftext, *fielddata, **opts):
342 def write(self, fields, deftext, *fielddata, **opts):
343 self._write(deftext % fielddata, **opts)
343 self._write(deftext % fielddata, **opts)
344
344
345 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
345 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
346 '''do conditional write'''
346 '''do conditional write'''
347 if cond:
347 if cond:
348 self._write(deftext % fielddata, **opts)
348 self._write(deftext % fielddata, **opts)
349
349
350 def plain(self, text, **opts):
350 def plain(self, text, **opts):
351 self._write(text, **opts)
351 self._write(text, **opts)
352
352
353 def isplain(self):
353 def isplain(self):
354 return True
354 return True
355
355
356 def nested(self, field, tmpl=None, sep=b''):
356 def nested(self, field, tmpl=None, sep=b''):
357 # nested data will be directly written to ui
357 # nested data will be directly written to ui
358 return self
358 return self
359
359
360 def end(self):
360 def end(self):
361 pass
361 pass
362
362
363
363
364 class debugformatter(baseformatter):
364 class debugformatter(baseformatter):
365 def __init__(self, ui, out, topic, opts):
365 def __init__(self, ui, out, topic, opts):
366 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
366 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
367 self._out = out
367 self._out = out
368 self._out.write(b"%s = [\n" % self._topic)
368 self._out.write(b"%s = [\n" % self._topic)
369
369
370 def _showitem(self):
370 def _showitem(self):
371 self._out.write(
371 self._out.write(
372 b' %s,\n' % stringutil.pprint(self._item, indent=4, level=1)
372 b' %s,\n' % stringutil.pprint(self._item, indent=4, level=1)
373 )
373 )
374
374
375 def end(self):
375 def end(self):
376 baseformatter.end(self)
376 baseformatter.end(self)
377 self._out.write(b"]\n")
377 self._out.write(b"]\n")
378
378
379
379
380 class pickleformatter(baseformatter):
380 class pickleformatter(baseformatter):
381 def __init__(self, ui, out, topic, opts):
381 def __init__(self, ui, out, topic, opts):
382 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
382 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
383 self._out = out
383 self._out = out
384 self._data = []
384 self._data = []
385
385
386 def _showitem(self):
386 def _showitem(self):
387 self._data.append(self._item)
387 self._data.append(self._item)
388
388
389 def end(self):
389 def end(self):
390 baseformatter.end(self)
390 baseformatter.end(self)
391 self._out.write(pickle.dumps(self._data))
391 self._out.write(pickle.dumps(self._data))
392
392
393
393
394 class cborformatter(baseformatter):
394 class cborformatter(baseformatter):
395 '''serialize items as an indefinite-length CBOR array'''
395 '''serialize items as an indefinite-length CBOR array'''
396
396
397 def __init__(self, ui, out, topic, opts):
397 def __init__(self, ui, out, topic, opts):
398 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
398 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
399 self._out = out
399 self._out = out
400 self._out.write(cborutil.BEGIN_INDEFINITE_ARRAY)
400 self._out.write(cborutil.BEGIN_INDEFINITE_ARRAY)
401
401
402 def _showitem(self):
402 def _showitem(self):
403 self._out.write(b''.join(cborutil.streamencode(self._item)))
403 self._out.write(b''.join(cborutil.streamencode(self._item)))
404
404
405 def end(self):
405 def end(self):
406 baseformatter.end(self)
406 baseformatter.end(self)
407 self._out.write(cborutil.BREAK)
407 self._out.write(cborutil.BREAK)
408
408
409
409
410 class jsonformatter(baseformatter):
410 class jsonformatter(baseformatter):
411 def __init__(self, ui, out, topic, opts):
411 def __init__(self, ui, out, topic, opts):
412 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
412 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
413 self._out = out
413 self._out = out
414 self._out.write(b"[")
414 self._out.write(b"[")
415 self._first = True
415 self._first = True
416
416
417 def _showitem(self):
417 def _showitem(self):
418 if self._first:
418 if self._first:
419 self._first = False
419 self._first = False
420 else:
420 else:
421 self._out.write(b",")
421 self._out.write(b",")
422
422
423 self._out.write(b"\n {\n")
423 self._out.write(b"\n {\n")
424 first = True
424 first = True
425 for k, v in sorted(self._item.items()):
425 for k, v in sorted(self._item.items()):
426 if first:
426 if first:
427 first = False
427 first = False
428 else:
428 else:
429 self._out.write(b",\n")
429 self._out.write(b",\n")
430 u = templatefilters.json(v, paranoid=False)
430 u = templatefilters.json(v, paranoid=False)
431 self._out.write(b' "%s": %s' % (k, u))
431 self._out.write(b' "%s": %s' % (k, u))
432 self._out.write(b"\n }")
432 self._out.write(b"\n }")
433
433
434 def end(self):
434 def end(self):
435 baseformatter.end(self)
435 baseformatter.end(self)
436 self._out.write(b"\n]\n")
436 self._out.write(b"\n]\n")
437
437
438
438
439 class _templateconverter(object):
439 class _templateconverter(object):
440 '''convert non-primitive data types to be processed by templater'''
440 '''convert non-primitive data types to be processed by templater'''
441
441
442 storecontext = True
442 storecontext = True
443
443
444 @staticmethod
444 @staticmethod
445 def wrapnested(data, tmpl, sep):
445 def wrapnested(data, tmpl, sep):
446 '''wrap nested data by templatable type'''
446 '''wrap nested data by templatable type'''
447 return templateutil.mappinglist(data, tmpl=tmpl, sep=sep)
447 return templateutil.mappinglist(data, tmpl=tmpl, sep=sep)
448
448
449 @staticmethod
449 @staticmethod
450 def formatdate(date, fmt):
450 def formatdate(date, fmt):
451 '''return date tuple'''
451 '''return date tuple'''
452 return templateutil.date(date)
452 return templateutil.date(date)
453
453
454 @staticmethod
454 @staticmethod
455 def formatdict(data, key, value, fmt, sep):
455 def formatdict(data, key, value, fmt, sep):
456 '''build object that can be evaluated as either plain string or dict'''
456 '''build object that can be evaluated as either plain string or dict'''
457 data = util.sortdict(_iteritems(data))
457 data = util.sortdict(_iteritems(data))
458
458
459 def f():
459 def f():
460 yield _plainconverter.formatdict(data, key, value, fmt, sep)
460 yield _plainconverter.formatdict(data, key, value, fmt, sep)
461
461
462 return templateutil.hybriddict(
462 return templateutil.hybriddict(
463 data, key=key, value=value, fmt=fmt, gen=f
463 data, key=key, value=value, fmt=fmt, gen=f
464 )
464 )
465
465
466 @staticmethod
466 @staticmethod
467 def formatlist(data, name, fmt, sep):
467 def formatlist(data, name, fmt, sep):
468 '''build object that can be evaluated as either plain string or list'''
468 '''build object that can be evaluated as either plain string or list'''
469 data = list(data)
469 data = list(data)
470
470
471 def f():
471 def f():
472 yield _plainconverter.formatlist(data, name, fmt, sep)
472 yield _plainconverter.formatlist(data, name, fmt, sep)
473
473
474 return templateutil.hybridlist(data, name=name, fmt=fmt, gen=f)
474 return templateutil.hybridlist(data, name=name, fmt=fmt, gen=f)
475
475
476
476
477 class templateformatter(baseformatter):
477 class templateformatter(baseformatter):
478 def __init__(self, ui, out, topic, opts, spec):
478 def __init__(self, ui, out, topic, opts, spec):
479 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
479 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
480 self._out = out
480 self._out = out
481 self._tref = spec.ref
481 self._tref = spec.ref
482 self._t = loadtemplater(
482 self._t = loadtemplater(
483 ui,
483 ui,
484 spec,
484 spec,
485 defaults=templatekw.keywords,
485 defaults=templatekw.keywords,
486 resources=templateresources(ui),
486 resources=templateresources(ui),
487 cache=templatekw.defaulttempl,
487 cache=templatekw.defaulttempl,
488 )
488 )
489 self._parts = templatepartsmap(
489 self._parts = templatepartsmap(
490 spec, self._t, [b'docheader', b'docfooter', b'separator']
490 spec, self._t, [b'docheader', b'docfooter', b'separator']
491 )
491 )
492 self._counter = itertools.count()
492 self._counter = itertools.count()
493 self._renderitem(b'docheader', {})
493 self._renderitem(b'docheader', {})
494
494
495 def _showitem(self):
495 def _showitem(self):
496 item = self._item.copy()
496 item = self._item.copy()
497 item[b'index'] = index = next(self._counter)
497 item[b'index'] = index = next(self._counter)
498 if index > 0:
498 if index > 0:
499 self._renderitem(b'separator', {})
499 self._renderitem(b'separator', {})
500 self._renderitem(self._tref, item)
500 self._renderitem(self._tref, item)
501
501
502 def _renderitem(self, part, item):
502 def _renderitem(self, part, item):
503 if part not in self._parts:
503 if part not in self._parts:
504 return
504 return
505 ref = self._parts[part]
505 ref = self._parts[part]
506 self._out.write(self._t.render(ref, item))
506 self._out.write(self._t.render(ref, item))
507
507
508 @util.propertycache
508 @util.propertycache
509 def _symbolsused(self):
509 def _symbolsused(self):
510 return self._t.symbolsused(self._tref)
510 return self._t.symbolsused(self._tref)
511
511
512 def datahint(self):
512 def datahint(self):
513 '''set of field names to be referenced from the template'''
513 '''set of field names to be referenced from the template'''
514 return self._symbolsused[0]
514 return self._symbolsused[0]
515
515
516 def end(self):
516 def end(self):
517 baseformatter.end(self)
517 baseformatter.end(self)
518 self._renderitem(b'docfooter', {})
518 self._renderitem(b'docfooter', {})
519
519
520
520
521 @attr.s(frozen=True)
521 @attr.s(frozen=True)
522 class templatespec(object):
522 class templatespec(object):
523 ref = attr.ib()
523 ref = attr.ib()
524 tmpl = attr.ib()
524 tmpl = attr.ib()
525 mapfile = attr.ib()
525 mapfile = attr.ib()
526
526
527
527
528 def lookuptemplate(ui, topic, tmpl):
528 def lookuptemplate(ui, topic, tmpl):
529 """Find the template matching the given -T/--template spec 'tmpl'
529 """Find the template matching the given -T/--template spec 'tmpl'
530
530
531 'tmpl' can be any of the following:
531 'tmpl' can be any of the following:
532
532
533 - a literal template (e.g. '{rev}')
533 - a literal template (e.g. '{rev}')
534 - a reference to built-in template (i.e. formatter)
534 - a map-file name or path (e.g. 'changelog')
535 - a map-file name or path (e.g. 'changelog')
535 - a reference to [templates] in config file
536 - a reference to [templates] in config file
536 - a path to raw template file
537 - a path to raw template file
537
538
538 A map file defines a stand-alone template environment. If a map file
539 A map file defines a stand-alone template environment. If a map file
539 selected, all templates defined in the file will be loaded, and the
540 selected, all templates defined in the file will be loaded, and the
540 template matching the given topic will be rendered. Aliases won't be
541 template matching the given topic will be rendered. Aliases won't be
541 loaded from user config, but from the map file.
542 loaded from user config, but from the map file.
542
543
543 If no map file selected, all templates in [templates] section will be
544 If no map file selected, all templates in [templates] section will be
544 available as well as aliases in [templatealias].
545 available as well as aliases in [templatealias].
545 """
546 """
546
547
548 if not tmpl:
549 return templatespec(None, None, None)
550
547 # looks like a literal template?
551 # looks like a literal template?
548 if b'{' in tmpl:
552 if b'{' in tmpl:
549 return templatespec(b'', tmpl, None)
553 return templatespec(b'', tmpl, None)
550
554
555 # a reference to built-in (formatter) template
556 if tmpl in {b'cbor', b'json', b'pickle', b'debug'}:
557 return templatespec(tmpl, None, None)
558
551 # perhaps a stock style?
559 # perhaps a stock style?
552 if not os.path.split(tmpl)[0]:
560 if not os.path.split(tmpl)[0]:
553 mapname = templater.templatepath(
561 mapname = templater.templatepath(
554 b'map-cmdline.' + tmpl
562 b'map-cmdline.' + tmpl
555 ) or templater.templatepath(tmpl)
563 ) or templater.templatepath(tmpl)
556 if mapname and os.path.isfile(mapname):
564 if mapname and os.path.isfile(mapname):
557 return templatespec(topic, None, mapname)
565 return templatespec(topic, None, mapname)
558
566
559 # perhaps it's a reference to [templates]
567 # perhaps it's a reference to [templates]
560 if ui.config(b'templates', tmpl):
568 if ui.config(b'templates', tmpl):
561 return templatespec(tmpl, None, None)
569 return templatespec(tmpl, None, None)
562
570
563 if tmpl == b'list':
571 if tmpl == b'list':
564 ui.write(_(b"available styles: %s\n") % templater.stylelist())
572 ui.write(_(b"available styles: %s\n") % templater.stylelist())
565 raise error.Abort(_(b"specify a template"))
573 raise error.Abort(_(b"specify a template"))
566
574
567 # perhaps it's a path to a map or a template
575 # perhaps it's a path to a map or a template
568 if (b'/' in tmpl or b'\\' in tmpl) and os.path.isfile(tmpl):
576 if (b'/' in tmpl or b'\\' in tmpl) and os.path.isfile(tmpl):
569 # is it a mapfile for a style?
577 # is it a mapfile for a style?
570 if os.path.basename(tmpl).startswith(b"map-"):
578 if os.path.basename(tmpl).startswith(b"map-"):
571 return templatespec(topic, None, os.path.realpath(tmpl))
579 return templatespec(topic, None, os.path.realpath(tmpl))
572 with util.posixfile(tmpl, b'rb') as f:
580 with util.posixfile(tmpl, b'rb') as f:
573 tmpl = f.read()
581 tmpl = f.read()
574 return templatespec(b'', tmpl, None)
582 return templatespec(b'', tmpl, None)
575
583
576 # constant string?
584 # constant string?
577 return templatespec(b'', tmpl, None)
585 return templatespec(b'', tmpl, None)
578
586
579
587
580 def templatepartsmap(spec, t, partnames):
588 def templatepartsmap(spec, t, partnames):
581 """Create a mapping of {part: ref}"""
589 """Create a mapping of {part: ref}"""
582 partsmap = {spec.ref: spec.ref} # initial ref must exist in t
590 partsmap = {spec.ref: spec.ref} # initial ref must exist in t
583 if spec.mapfile:
591 if spec.mapfile:
584 partsmap.update((p, p) for p in partnames if p in t)
592 partsmap.update((p, p) for p in partnames if p in t)
585 elif spec.ref:
593 elif spec.ref:
586 for part in partnames:
594 for part in partnames:
587 ref = b'%s:%s' % (spec.ref, part) # select config sub-section
595 ref = b'%s:%s' % (spec.ref, part) # select config sub-section
588 if ref in t:
596 if ref in t:
589 partsmap[part] = ref
597 partsmap[part] = ref
590 return partsmap
598 return partsmap
591
599
592
600
593 def loadtemplater(ui, spec, defaults=None, resources=None, cache=None):
601 def loadtemplater(ui, spec, defaults=None, resources=None, cache=None):
594 """Create a templater from either a literal template or loading from
602 """Create a templater from either a literal template or loading from
595 a map file"""
603 a map file"""
596 assert not (spec.tmpl and spec.mapfile)
604 assert not (spec.tmpl and spec.mapfile)
597 if spec.mapfile:
605 if spec.mapfile:
598 frommapfile = templater.templater.frommapfile
606 frommapfile = templater.templater.frommapfile
599 return frommapfile(
607 return frommapfile(
600 spec.mapfile, defaults=defaults, resources=resources, cache=cache
608 spec.mapfile, defaults=defaults, resources=resources, cache=cache
601 )
609 )
602 return maketemplater(
610 return maketemplater(
603 ui, spec.tmpl, defaults=defaults, resources=resources, cache=cache
611 ui, spec.tmpl, defaults=defaults, resources=resources, cache=cache
604 )
612 )
605
613
606
614
607 def maketemplater(ui, tmpl, defaults=None, resources=None, cache=None):
615 def maketemplater(ui, tmpl, defaults=None, resources=None, cache=None):
608 """Create a templater from a string template 'tmpl'"""
616 """Create a templater from a string template 'tmpl'"""
609 aliases = ui.configitems(b'templatealias')
617 aliases = ui.configitems(b'templatealias')
610 t = templater.templater(
618 t = templater.templater(
611 defaults=defaults, resources=resources, cache=cache, aliases=aliases
619 defaults=defaults, resources=resources, cache=cache, aliases=aliases
612 )
620 )
613 t.cache.update(
621 t.cache.update(
614 (k, templater.unquotestring(v)) for k, v in ui.configitems(b'templates')
622 (k, templater.unquotestring(v)) for k, v in ui.configitems(b'templates')
615 )
623 )
616 if tmpl:
624 if tmpl:
617 t.cache[b''] = tmpl
625 t.cache[b''] = tmpl
618 return t
626 return t
619
627
620
628
621 # marker to denote a resource to be loaded on demand based on mapping values
629 # marker to denote a resource to be loaded on demand based on mapping values
622 # (e.g. (ctx, path) -> fctx)
630 # (e.g. (ctx, path) -> fctx)
623 _placeholder = object()
631 _placeholder = object()
624
632
625
633
626 class templateresources(templater.resourcemapper):
634 class templateresources(templater.resourcemapper):
627 """Resource mapper designed for the default templatekw and function"""
635 """Resource mapper designed for the default templatekw and function"""
628
636
629 def __init__(self, ui, repo=None):
637 def __init__(self, ui, repo=None):
630 self._resmap = {
638 self._resmap = {
631 b'cache': {}, # for templatekw/funcs to store reusable data
639 b'cache': {}, # for templatekw/funcs to store reusable data
632 b'repo': repo,
640 b'repo': repo,
633 b'ui': ui,
641 b'ui': ui,
634 }
642 }
635
643
636 def availablekeys(self, mapping):
644 def availablekeys(self, mapping):
637 return {
645 return {
638 k for k in self.knownkeys() if self._getsome(mapping, k) is not None
646 k for k in self.knownkeys() if self._getsome(mapping, k) is not None
639 }
647 }
640
648
641 def knownkeys(self):
649 def knownkeys(self):
642 return {b'cache', b'ctx', b'fctx', b'repo', b'revcache', b'ui'}
650 return {b'cache', b'ctx', b'fctx', b'repo', b'revcache', b'ui'}
643
651
644 def lookup(self, mapping, key):
652 def lookup(self, mapping, key):
645 if key not in self.knownkeys():
653 if key not in self.knownkeys():
646 return None
654 return None
647 v = self._getsome(mapping, key)
655 v = self._getsome(mapping, key)
648 if v is _placeholder:
656 if v is _placeholder:
649 v = mapping[key] = self._loadermap[key](self, mapping)
657 v = mapping[key] = self._loadermap[key](self, mapping)
650 return v
658 return v
651
659
652 def populatemap(self, context, origmapping, newmapping):
660 def populatemap(self, context, origmapping, newmapping):
653 mapping = {}
661 mapping = {}
654 if self._hasnodespec(newmapping):
662 if self._hasnodespec(newmapping):
655 mapping[b'revcache'] = {} # per-ctx cache
663 mapping[b'revcache'] = {} # per-ctx cache
656 if self._hasnodespec(origmapping) and self._hasnodespec(newmapping):
664 if self._hasnodespec(origmapping) and self._hasnodespec(newmapping):
657 orignode = templateutil.runsymbol(context, origmapping, b'node')
665 orignode = templateutil.runsymbol(context, origmapping, b'node')
658 mapping[b'originalnode'] = orignode
666 mapping[b'originalnode'] = orignode
659 # put marker to override 'ctx'/'fctx' in mapping if any, and flag
667 # put marker to override 'ctx'/'fctx' in mapping if any, and flag
660 # its existence to be reported by availablekeys()
668 # its existence to be reported by availablekeys()
661 if b'ctx' not in newmapping and self._hasliteral(newmapping, b'node'):
669 if b'ctx' not in newmapping and self._hasliteral(newmapping, b'node'):
662 mapping[b'ctx'] = _placeholder
670 mapping[b'ctx'] = _placeholder
663 if b'fctx' not in newmapping and self._hasliteral(newmapping, b'path'):
671 if b'fctx' not in newmapping and self._hasliteral(newmapping, b'path'):
664 mapping[b'fctx'] = _placeholder
672 mapping[b'fctx'] = _placeholder
665 return mapping
673 return mapping
666
674
667 def _getsome(self, mapping, key):
675 def _getsome(self, mapping, key):
668 v = mapping.get(key)
676 v = mapping.get(key)
669 if v is not None:
677 if v is not None:
670 return v
678 return v
671 return self._resmap.get(key)
679 return self._resmap.get(key)
672
680
673 def _hasliteral(self, mapping, key):
681 def _hasliteral(self, mapping, key):
674 """Test if a literal value is set or unset in the given mapping"""
682 """Test if a literal value is set or unset in the given mapping"""
675 return key in mapping and not callable(mapping[key])
683 return key in mapping and not callable(mapping[key])
676
684
677 def _getliteral(self, mapping, key):
685 def _getliteral(self, mapping, key):
678 """Return value of the given name if it is a literal"""
686 """Return value of the given name if it is a literal"""
679 v = mapping.get(key)
687 v = mapping.get(key)
680 if callable(v):
688 if callable(v):
681 return None
689 return None
682 return v
690 return v
683
691
684 def _hasnodespec(self, mapping):
692 def _hasnodespec(self, mapping):
685 """Test if context revision is set or unset in the given mapping"""
693 """Test if context revision is set or unset in the given mapping"""
686 return b'node' in mapping or b'ctx' in mapping
694 return b'node' in mapping or b'ctx' in mapping
687
695
688 def _loadctx(self, mapping):
696 def _loadctx(self, mapping):
689 repo = self._getsome(mapping, b'repo')
697 repo = self._getsome(mapping, b'repo')
690 node = self._getliteral(mapping, b'node')
698 node = self._getliteral(mapping, b'node')
691 if repo is None or node is None:
699 if repo is None or node is None:
692 return
700 return
693 try:
701 try:
694 return repo[node]
702 return repo[node]
695 except error.RepoLookupError:
703 except error.RepoLookupError:
696 return None # maybe hidden/non-existent node
704 return None # maybe hidden/non-existent node
697
705
698 def _loadfctx(self, mapping):
706 def _loadfctx(self, mapping):
699 ctx = self._getsome(mapping, b'ctx')
707 ctx = self._getsome(mapping, b'ctx')
700 path = self._getliteral(mapping, b'path')
708 path = self._getliteral(mapping, b'path')
701 if ctx is None or path is None:
709 if ctx is None or path is None:
702 return None
710 return None
703 try:
711 try:
704 return ctx[path]
712 return ctx[path]
705 except error.LookupError:
713 except error.LookupError:
706 return None # maybe removed file?
714 return None # maybe removed file?
707
715
708 _loadermap = {
716 _loadermap = {
709 b'ctx': _loadctx,
717 b'ctx': _loadctx,
710 b'fctx': _loadfctx,
718 b'fctx': _loadfctx,
711 }
719 }
712
720
713
721
714 def formatter(ui, out, topic, opts):
722 def formatter(ui, out, topic, opts):
715 template = opts.get(b"template", b"")
723 spec = lookuptemplate(ui, topic, opts.get(b'template', b''))
716 if template == b"cbor":
724 if spec.ref == b"cbor":
717 return cborformatter(ui, out, topic, opts)
725 return cborformatter(ui, out, topic, opts)
718 elif template == b"json":
726 elif spec.ref == b"json":
719 return jsonformatter(ui, out, topic, opts)
727 return jsonformatter(ui, out, topic, opts)
720 elif template == b"pickle":
728 elif spec.ref == b"pickle":
721 return pickleformatter(ui, out, topic, opts)
729 return pickleformatter(ui, out, topic, opts)
722 elif template == b"debug":
730 elif spec.ref == b"debug":
723 return debugformatter(ui, out, topic, opts)
731 return debugformatter(ui, out, topic, opts)
724 elif template != b"":
732 elif spec.ref or spec.tmpl or spec.mapfile:
725 spec = lookuptemplate(ui, topic, opts.get(b'template', b''))
726 return templateformatter(ui, out, topic, opts, spec)
733 return templateformatter(ui, out, topic, opts, spec)
727 # developer config: ui.formatdebug
734 # developer config: ui.formatdebug
728 elif ui.configbool(b'ui', b'formatdebug'):
735 elif ui.configbool(b'ui', b'formatdebug'):
729 return debugformatter(ui, out, topic, opts)
736 return debugformatter(ui, out, topic, opts)
730 # deprecated config: ui.formatjson
737 # deprecated config: ui.formatjson
731 elif ui.configbool(b'ui', b'formatjson'):
738 elif ui.configbool(b'ui', b'formatjson'):
732 return jsonformatter(ui, out, topic, opts)
739 return jsonformatter(ui, out, topic, opts)
733 return plainformatter(ui, out, topic, opts)
740 return plainformatter(ui, out, topic, opts)
734
741
735
742
736 @contextlib.contextmanager
743 @contextlib.contextmanager
737 def openformatter(ui, filename, topic, opts):
744 def openformatter(ui, filename, topic, opts):
738 """Create a formatter that writes outputs to the specified file
745 """Create a formatter that writes outputs to the specified file
739
746
740 Must be invoked using the 'with' statement.
747 Must be invoked using the 'with' statement.
741 """
748 """
742 with util.posixfile(filename, b'wb') as out:
749 with util.posixfile(filename, b'wb') as out:
743 with formatter(ui, out, topic, opts) as fm:
750 with formatter(ui, out, topic, opts) as fm:
744 yield fm
751 yield fm
745
752
746
753
747 @contextlib.contextmanager
754 @contextlib.contextmanager
748 def _neverending(fm):
755 def _neverending(fm):
749 yield fm
756 yield fm
750
757
751
758
752 def maybereopen(fm, filename):
759 def maybereopen(fm, filename):
753 """Create a formatter backed by file if filename specified, else return
760 """Create a formatter backed by file if filename specified, else return
754 the given formatter
761 the given formatter
755
762
756 Must be invoked using the 'with' statement. This will never call fm.end()
763 Must be invoked using the 'with' statement. This will never call fm.end()
757 of the given formatter.
764 of the given formatter.
758 """
765 """
759 if filename:
766 if filename:
760 return openformatter(fm._ui, filename, fm._topic, fm._opts)
767 return openformatter(fm._ui, filename, fm._topic, fm._opts)
761 else:
768 else:
762 return _neverending(fm)
769 return _neverending(fm)
@@ -1,1064 +1,1064 b''
1 # logcmdutil.py - utility for log-like commands
1 # logcmdutil.py - utility for log-like commands
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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
8 from __future__ import absolute_import
9
9
10 import itertools
10 import itertools
11 import os
11 import os
12 import posixpath
12 import posixpath
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 nullid,
16 nullid,
17 wdirid,
17 wdirid,
18 wdirrev,
18 wdirrev,
19 )
19 )
20
20
21 from . import (
21 from . import (
22 dagop,
22 dagop,
23 error,
23 error,
24 formatter,
24 formatter,
25 graphmod,
25 graphmod,
26 match as matchmod,
26 match as matchmod,
27 mdiff,
27 mdiff,
28 patch,
28 patch,
29 pathutil,
29 pathutil,
30 pycompat,
30 pycompat,
31 revset,
31 revset,
32 revsetlang,
32 revsetlang,
33 scmutil,
33 scmutil,
34 smartset,
34 smartset,
35 templatekw,
35 templatekw,
36 templater,
36 templater,
37 util,
37 util,
38 )
38 )
39 from .utils import (
39 from .utils import (
40 dateutil,
40 dateutil,
41 stringutil,
41 stringutil,
42 )
42 )
43
43
44
44
45 def getlimit(opts):
45 def getlimit(opts):
46 """get the log limit according to option -l/--limit"""
46 """get the log limit according to option -l/--limit"""
47 limit = opts.get(b'limit')
47 limit = opts.get(b'limit')
48 if limit:
48 if limit:
49 try:
49 try:
50 limit = int(limit)
50 limit = int(limit)
51 except ValueError:
51 except ValueError:
52 raise error.Abort(_(b'limit must be a positive integer'))
52 raise error.Abort(_(b'limit must be a positive integer'))
53 if limit <= 0:
53 if limit <= 0:
54 raise error.Abort(_(b'limit must be positive'))
54 raise error.Abort(_(b'limit must be positive'))
55 else:
55 else:
56 limit = None
56 limit = None
57 return limit
57 return limit
58
58
59
59
60 def diffordiffstat(
60 def diffordiffstat(
61 ui,
61 ui,
62 repo,
62 repo,
63 diffopts,
63 diffopts,
64 node1,
64 node1,
65 node2,
65 node2,
66 match,
66 match,
67 changes=None,
67 changes=None,
68 stat=False,
68 stat=False,
69 fp=None,
69 fp=None,
70 graphwidth=0,
70 graphwidth=0,
71 prefix=b'',
71 prefix=b'',
72 root=b'',
72 root=b'',
73 listsubrepos=False,
73 listsubrepos=False,
74 hunksfilterfn=None,
74 hunksfilterfn=None,
75 ):
75 ):
76 '''show diff or diffstat.'''
76 '''show diff or diffstat.'''
77 ctx1 = repo[node1]
77 ctx1 = repo[node1]
78 ctx2 = repo[node2]
78 ctx2 = repo[node2]
79 if root:
79 if root:
80 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
80 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
81 else:
81 else:
82 relroot = b''
82 relroot = b''
83 copysourcematch = None
83 copysourcematch = None
84
84
85 def compose(f, g):
85 def compose(f, g):
86 return lambda x: f(g(x))
86 return lambda x: f(g(x))
87
87
88 def pathfn(f):
88 def pathfn(f):
89 return posixpath.join(prefix, f)
89 return posixpath.join(prefix, f)
90
90
91 if relroot != b'':
91 if relroot != b'':
92 # XXX relative roots currently don't work if the root is within a
92 # XXX relative roots currently don't work if the root is within a
93 # subrepo
93 # subrepo
94 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
94 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
95 uirelroot = uipathfn(pathfn(relroot))
95 uirelroot = uipathfn(pathfn(relroot))
96 relroot += b'/'
96 relroot += b'/'
97 for matchroot in match.files():
97 for matchroot in match.files():
98 if not matchroot.startswith(relroot):
98 if not matchroot.startswith(relroot):
99 ui.warn(
99 ui.warn(
100 _(b'warning: %s not inside relative root %s\n')
100 _(b'warning: %s not inside relative root %s\n')
101 % (uipathfn(pathfn(matchroot)), uirelroot)
101 % (uipathfn(pathfn(matchroot)), uirelroot)
102 )
102 )
103
103
104 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
104 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
105 match = matchmod.intersectmatchers(match, relrootmatch)
105 match = matchmod.intersectmatchers(match, relrootmatch)
106 copysourcematch = relrootmatch
106 copysourcematch = relrootmatch
107
107
108 checkroot = repo.ui.configbool(
108 checkroot = repo.ui.configbool(
109 b'devel', b'all-warnings'
109 b'devel', b'all-warnings'
110 ) or repo.ui.configbool(b'devel', b'check-relroot')
110 ) or repo.ui.configbool(b'devel', b'check-relroot')
111
111
112 def relrootpathfn(f):
112 def relrootpathfn(f):
113 if checkroot and not f.startswith(relroot):
113 if checkroot and not f.startswith(relroot):
114 raise AssertionError(
114 raise AssertionError(
115 b"file %s doesn't start with relroot %s" % (f, relroot)
115 b"file %s doesn't start with relroot %s" % (f, relroot)
116 )
116 )
117 return f[len(relroot) :]
117 return f[len(relroot) :]
118
118
119 pathfn = compose(relrootpathfn, pathfn)
119 pathfn = compose(relrootpathfn, pathfn)
120
120
121 if stat:
121 if stat:
122 diffopts = diffopts.copy(context=0, noprefix=False)
122 diffopts = diffopts.copy(context=0, noprefix=False)
123 width = 80
123 width = 80
124 if not ui.plain():
124 if not ui.plain():
125 width = ui.termwidth() - graphwidth
125 width = ui.termwidth() - graphwidth
126 # If an explicit --root was given, don't respect ui.relative-paths
126 # If an explicit --root was given, don't respect ui.relative-paths
127 if not relroot:
127 if not relroot:
128 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
128 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
129
129
130 chunks = ctx2.diff(
130 chunks = ctx2.diff(
131 ctx1,
131 ctx1,
132 match,
132 match,
133 changes,
133 changes,
134 opts=diffopts,
134 opts=diffopts,
135 pathfn=pathfn,
135 pathfn=pathfn,
136 copysourcematch=copysourcematch,
136 copysourcematch=copysourcematch,
137 hunksfilterfn=hunksfilterfn,
137 hunksfilterfn=hunksfilterfn,
138 )
138 )
139
139
140 if fp is not None or ui.canwritewithoutlabels():
140 if fp is not None or ui.canwritewithoutlabels():
141 out = fp or ui
141 out = fp or ui
142 if stat:
142 if stat:
143 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
143 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
144 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
144 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
145 out.write(chunk)
145 out.write(chunk)
146 else:
146 else:
147 if stat:
147 if stat:
148 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
148 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
149 else:
149 else:
150 chunks = patch.difflabel(
150 chunks = patch.difflabel(
151 lambda chunks, **kwargs: chunks, chunks, opts=diffopts
151 lambda chunks, **kwargs: chunks, chunks, opts=diffopts
152 )
152 )
153 if ui.canbatchlabeledwrites():
153 if ui.canbatchlabeledwrites():
154
154
155 def gen():
155 def gen():
156 for chunk, label in chunks:
156 for chunk, label in chunks:
157 yield ui.label(chunk, label=label)
157 yield ui.label(chunk, label=label)
158
158
159 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
159 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
160 ui.write(chunk)
160 ui.write(chunk)
161 else:
161 else:
162 for chunk, label in chunks:
162 for chunk, label in chunks:
163 ui.write(chunk, label=label)
163 ui.write(chunk, label=label)
164
164
165 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
165 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
166 tempnode2 = node2
166 tempnode2 = node2
167 try:
167 try:
168 if node2 is not None:
168 if node2 is not None:
169 tempnode2 = ctx2.substate[subpath][1]
169 tempnode2 = ctx2.substate[subpath][1]
170 except KeyError:
170 except KeyError:
171 # A subrepo that existed in node1 was deleted between node1 and
171 # A subrepo that existed in node1 was deleted between node1 and
172 # node2 (inclusive). Thus, ctx2's substate won't contain that
172 # node2 (inclusive). Thus, ctx2's substate won't contain that
173 # subpath. The best we can do is to ignore it.
173 # subpath. The best we can do is to ignore it.
174 tempnode2 = None
174 tempnode2 = None
175 submatch = matchmod.subdirmatcher(subpath, match)
175 submatch = matchmod.subdirmatcher(subpath, match)
176 subprefix = repo.wvfs.reljoin(prefix, subpath)
176 subprefix = repo.wvfs.reljoin(prefix, subpath)
177 if listsubrepos or match.exact(subpath) or any(submatch.files()):
177 if listsubrepos or match.exact(subpath) or any(submatch.files()):
178 sub.diff(
178 sub.diff(
179 ui,
179 ui,
180 diffopts,
180 diffopts,
181 tempnode2,
181 tempnode2,
182 submatch,
182 submatch,
183 changes=changes,
183 changes=changes,
184 stat=stat,
184 stat=stat,
185 fp=fp,
185 fp=fp,
186 prefix=subprefix,
186 prefix=subprefix,
187 )
187 )
188
188
189
189
190 class changesetdiffer(object):
190 class changesetdiffer(object):
191 """Generate diff of changeset with pre-configured filtering functions"""
191 """Generate diff of changeset with pre-configured filtering functions"""
192
192
193 def _makefilematcher(self, ctx):
193 def _makefilematcher(self, ctx):
194 return scmutil.matchall(ctx.repo())
194 return scmutil.matchall(ctx.repo())
195
195
196 def _makehunksfilter(self, ctx):
196 def _makehunksfilter(self, ctx):
197 return None
197 return None
198
198
199 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
199 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
200 repo = ctx.repo()
200 repo = ctx.repo()
201 node = ctx.node()
201 node = ctx.node()
202 prev = ctx.p1().node()
202 prev = ctx.p1().node()
203 diffordiffstat(
203 diffordiffstat(
204 ui,
204 ui,
205 repo,
205 repo,
206 diffopts,
206 diffopts,
207 prev,
207 prev,
208 node,
208 node,
209 match=self._makefilematcher(ctx),
209 match=self._makefilematcher(ctx),
210 stat=stat,
210 stat=stat,
211 graphwidth=graphwidth,
211 graphwidth=graphwidth,
212 hunksfilterfn=self._makehunksfilter(ctx),
212 hunksfilterfn=self._makehunksfilter(ctx),
213 )
213 )
214
214
215
215
216 def changesetlabels(ctx):
216 def changesetlabels(ctx):
217 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
217 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
218 if ctx.obsolete():
218 if ctx.obsolete():
219 labels.append(b'changeset.obsolete')
219 labels.append(b'changeset.obsolete')
220 if ctx.isunstable():
220 if ctx.isunstable():
221 labels.append(b'changeset.unstable')
221 labels.append(b'changeset.unstable')
222 for instability in ctx.instabilities():
222 for instability in ctx.instabilities():
223 labels.append(b'instability.%s' % instability)
223 labels.append(b'instability.%s' % instability)
224 return b' '.join(labels)
224 return b' '.join(labels)
225
225
226
226
227 class changesetprinter(object):
227 class changesetprinter(object):
228 '''show changeset information when templating not requested.'''
228 '''show changeset information when templating not requested.'''
229
229
230 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
230 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
231 self.ui = ui
231 self.ui = ui
232 self.repo = repo
232 self.repo = repo
233 self.buffered = buffered
233 self.buffered = buffered
234 self._differ = differ or changesetdiffer()
234 self._differ = differ or changesetdiffer()
235 self._diffopts = patch.diffallopts(ui, diffopts)
235 self._diffopts = patch.diffallopts(ui, diffopts)
236 self._includestat = diffopts and diffopts.get(b'stat')
236 self._includestat = diffopts and diffopts.get(b'stat')
237 self._includediff = diffopts and diffopts.get(b'patch')
237 self._includediff = diffopts and diffopts.get(b'patch')
238 self.header = {}
238 self.header = {}
239 self.hunk = {}
239 self.hunk = {}
240 self.lastheader = None
240 self.lastheader = None
241 self.footer = None
241 self.footer = None
242 self._columns = templatekw.getlogcolumns()
242 self._columns = templatekw.getlogcolumns()
243
243
244 def flush(self, ctx):
244 def flush(self, ctx):
245 rev = ctx.rev()
245 rev = ctx.rev()
246 if rev in self.header:
246 if rev in self.header:
247 h = self.header[rev]
247 h = self.header[rev]
248 if h != self.lastheader:
248 if h != self.lastheader:
249 self.lastheader = h
249 self.lastheader = h
250 self.ui.write(h)
250 self.ui.write(h)
251 del self.header[rev]
251 del self.header[rev]
252 if rev in self.hunk:
252 if rev in self.hunk:
253 self.ui.write(self.hunk[rev])
253 self.ui.write(self.hunk[rev])
254 del self.hunk[rev]
254 del self.hunk[rev]
255
255
256 def close(self):
256 def close(self):
257 if self.footer:
257 if self.footer:
258 self.ui.write(self.footer)
258 self.ui.write(self.footer)
259
259
260 def show(self, ctx, copies=None, **props):
260 def show(self, ctx, copies=None, **props):
261 props = pycompat.byteskwargs(props)
261 props = pycompat.byteskwargs(props)
262 if self.buffered:
262 if self.buffered:
263 self.ui.pushbuffer(labeled=True)
263 self.ui.pushbuffer(labeled=True)
264 self._show(ctx, copies, props)
264 self._show(ctx, copies, props)
265 self.hunk[ctx.rev()] = self.ui.popbuffer()
265 self.hunk[ctx.rev()] = self.ui.popbuffer()
266 else:
266 else:
267 self._show(ctx, copies, props)
267 self._show(ctx, copies, props)
268
268
269 def _show(self, ctx, copies, props):
269 def _show(self, ctx, copies, props):
270 '''show a single changeset or file revision'''
270 '''show a single changeset or file revision'''
271 changenode = ctx.node()
271 changenode = ctx.node()
272 graphwidth = props.get(b'graphwidth', 0)
272 graphwidth = props.get(b'graphwidth', 0)
273
273
274 if self.ui.quiet:
274 if self.ui.quiet:
275 self.ui.write(
275 self.ui.write(
276 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
276 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
277 )
277 )
278 return
278 return
279
279
280 columns = self._columns
280 columns = self._columns
281 self.ui.write(
281 self.ui.write(
282 columns[b'changeset'] % scmutil.formatchangeid(ctx),
282 columns[b'changeset'] % scmutil.formatchangeid(ctx),
283 label=changesetlabels(ctx),
283 label=changesetlabels(ctx),
284 )
284 )
285
285
286 # branches are shown first before any other names due to backwards
286 # branches are shown first before any other names due to backwards
287 # compatibility
287 # compatibility
288 branch = ctx.branch()
288 branch = ctx.branch()
289 # don't show the default branch name
289 # don't show the default branch name
290 if branch != b'default':
290 if branch != b'default':
291 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
291 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
292
292
293 for nsname, ns in self.repo.names.iteritems():
293 for nsname, ns in self.repo.names.iteritems():
294 # branches has special logic already handled above, so here we just
294 # branches has special logic already handled above, so here we just
295 # skip it
295 # skip it
296 if nsname == b'branches':
296 if nsname == b'branches':
297 continue
297 continue
298 # we will use the templatename as the color name since those two
298 # we will use the templatename as the color name since those two
299 # should be the same
299 # should be the same
300 for name in ns.names(self.repo, changenode):
300 for name in ns.names(self.repo, changenode):
301 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
301 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
302 if self.ui.debugflag:
302 if self.ui.debugflag:
303 self.ui.write(
303 self.ui.write(
304 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
304 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
305 )
305 )
306 for pctx in scmutil.meaningfulparents(self.repo, ctx):
306 for pctx in scmutil.meaningfulparents(self.repo, ctx):
307 label = b'log.parent changeset.%s' % pctx.phasestr()
307 label = b'log.parent changeset.%s' % pctx.phasestr()
308 self.ui.write(
308 self.ui.write(
309 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
309 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
310 )
310 )
311
311
312 if self.ui.debugflag:
312 if self.ui.debugflag:
313 mnode = ctx.manifestnode()
313 mnode = ctx.manifestnode()
314 if mnode is None:
314 if mnode is None:
315 mnode = wdirid
315 mnode = wdirid
316 mrev = wdirrev
316 mrev = wdirrev
317 else:
317 else:
318 mrev = self.repo.manifestlog.rev(mnode)
318 mrev = self.repo.manifestlog.rev(mnode)
319 self.ui.write(
319 self.ui.write(
320 columns[b'manifest']
320 columns[b'manifest']
321 % scmutil.formatrevnode(self.ui, mrev, mnode),
321 % scmutil.formatrevnode(self.ui, mrev, mnode),
322 label=b'ui.debug log.manifest',
322 label=b'ui.debug log.manifest',
323 )
323 )
324 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
324 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
325 self.ui.write(
325 self.ui.write(
326 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
326 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
327 )
327 )
328
328
329 if ctx.isunstable():
329 if ctx.isunstable():
330 instabilities = ctx.instabilities()
330 instabilities = ctx.instabilities()
331 self.ui.write(
331 self.ui.write(
332 columns[b'instability'] % b', '.join(instabilities),
332 columns[b'instability'] % b', '.join(instabilities),
333 label=b'log.instability',
333 label=b'log.instability',
334 )
334 )
335
335
336 elif ctx.obsolete():
336 elif ctx.obsolete():
337 self._showobsfate(ctx)
337 self._showobsfate(ctx)
338
338
339 self._exthook(ctx)
339 self._exthook(ctx)
340
340
341 if self.ui.debugflag:
341 if self.ui.debugflag:
342 files = ctx.p1().status(ctx)[:3]
342 files = ctx.p1().status(ctx)[:3]
343 for key, value in zip([b'files', b'files+', b'files-'], files):
343 for key, value in zip([b'files', b'files+', b'files-'], files):
344 if value:
344 if value:
345 self.ui.write(
345 self.ui.write(
346 columns[key] % b" ".join(value),
346 columns[key] % b" ".join(value),
347 label=b'ui.debug log.files',
347 label=b'ui.debug log.files',
348 )
348 )
349 elif ctx.files() and self.ui.verbose:
349 elif ctx.files() and self.ui.verbose:
350 self.ui.write(
350 self.ui.write(
351 columns[b'files'] % b" ".join(ctx.files()),
351 columns[b'files'] % b" ".join(ctx.files()),
352 label=b'ui.note log.files',
352 label=b'ui.note log.files',
353 )
353 )
354 if copies and self.ui.verbose:
354 if copies and self.ui.verbose:
355 copies = [b'%s (%s)' % c for c in copies]
355 copies = [b'%s (%s)' % c for c in copies]
356 self.ui.write(
356 self.ui.write(
357 columns[b'copies'] % b' '.join(copies),
357 columns[b'copies'] % b' '.join(copies),
358 label=b'ui.note log.copies',
358 label=b'ui.note log.copies',
359 )
359 )
360
360
361 extra = ctx.extra()
361 extra = ctx.extra()
362 if extra and self.ui.debugflag:
362 if extra and self.ui.debugflag:
363 for key, value in sorted(extra.items()):
363 for key, value in sorted(extra.items()):
364 self.ui.write(
364 self.ui.write(
365 columns[b'extra'] % (key, stringutil.escapestr(value)),
365 columns[b'extra'] % (key, stringutil.escapestr(value)),
366 label=b'ui.debug log.extra',
366 label=b'ui.debug log.extra',
367 )
367 )
368
368
369 description = ctx.description().strip()
369 description = ctx.description().strip()
370 if description:
370 if description:
371 if self.ui.verbose:
371 if self.ui.verbose:
372 self.ui.write(
372 self.ui.write(
373 _(b"description:\n"), label=b'ui.note log.description'
373 _(b"description:\n"), label=b'ui.note log.description'
374 )
374 )
375 self.ui.write(description, label=b'ui.note log.description')
375 self.ui.write(description, label=b'ui.note log.description')
376 self.ui.write(b"\n\n")
376 self.ui.write(b"\n\n")
377 else:
377 else:
378 self.ui.write(
378 self.ui.write(
379 columns[b'summary'] % description.splitlines()[0],
379 columns[b'summary'] % description.splitlines()[0],
380 label=b'log.summary',
380 label=b'log.summary',
381 )
381 )
382 self.ui.write(b"\n")
382 self.ui.write(b"\n")
383
383
384 self._showpatch(ctx, graphwidth)
384 self._showpatch(ctx, graphwidth)
385
385
386 def _showobsfate(self, ctx):
386 def _showobsfate(self, ctx):
387 # TODO: do not depend on templater
387 # TODO: do not depend on templater
388 tres = formatter.templateresources(self.repo.ui, self.repo)
388 tres = formatter.templateresources(self.repo.ui, self.repo)
389 t = formatter.maketemplater(
389 t = formatter.maketemplater(
390 self.repo.ui,
390 self.repo.ui,
391 b'{join(obsfate, "\n")}',
391 b'{join(obsfate, "\n")}',
392 defaults=templatekw.keywords,
392 defaults=templatekw.keywords,
393 resources=tres,
393 resources=tres,
394 )
394 )
395 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
395 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
396
396
397 if obsfate:
397 if obsfate:
398 for obsfateline in obsfate:
398 for obsfateline in obsfate:
399 self.ui.write(
399 self.ui.write(
400 self._columns[b'obsolete'] % obsfateline,
400 self._columns[b'obsolete'] % obsfateline,
401 label=b'log.obsfate',
401 label=b'log.obsfate',
402 )
402 )
403
403
404 def _exthook(self, ctx):
404 def _exthook(self, ctx):
405 '''empty method used by extension as a hook point
405 '''empty method used by extension as a hook point
406 '''
406 '''
407
407
408 def _showpatch(self, ctx, graphwidth=0):
408 def _showpatch(self, ctx, graphwidth=0):
409 if self._includestat:
409 if self._includestat:
410 self._differ.showdiff(
410 self._differ.showdiff(
411 self.ui, ctx, self._diffopts, graphwidth, stat=True
411 self.ui, ctx, self._diffopts, graphwidth, stat=True
412 )
412 )
413 if self._includestat and self._includediff:
413 if self._includestat and self._includediff:
414 self.ui.write(b"\n")
414 self.ui.write(b"\n")
415 if self._includediff:
415 if self._includediff:
416 self._differ.showdiff(
416 self._differ.showdiff(
417 self.ui, ctx, self._diffopts, graphwidth, stat=False
417 self.ui, ctx, self._diffopts, graphwidth, stat=False
418 )
418 )
419 if self._includestat or self._includediff:
419 if self._includestat or self._includediff:
420 self.ui.write(b"\n")
420 self.ui.write(b"\n")
421
421
422
422
423 class changesetformatter(changesetprinter):
423 class changesetformatter(changesetprinter):
424 """Format changeset information by generic formatter"""
424 """Format changeset information by generic formatter"""
425
425
426 def __init__(
426 def __init__(
427 self, ui, repo, fm, differ=None, diffopts=None, buffered=False
427 self, ui, repo, fm, differ=None, diffopts=None, buffered=False
428 ):
428 ):
429 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
429 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
430 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
430 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
431 self._fm = fm
431 self._fm = fm
432
432
433 def close(self):
433 def close(self):
434 self._fm.end()
434 self._fm.end()
435
435
436 def _show(self, ctx, copies, props):
436 def _show(self, ctx, copies, props):
437 '''show a single changeset or file revision'''
437 '''show a single changeset or file revision'''
438 fm = self._fm
438 fm = self._fm
439 fm.startitem()
439 fm.startitem()
440 fm.context(ctx=ctx)
440 fm.context(ctx=ctx)
441 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx)))
441 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx)))
442
442
443 if self.ui.quiet:
443 if self.ui.quiet:
444 return
444 return
445
445
446 fm.data(
446 fm.data(
447 branch=ctx.branch(),
447 branch=ctx.branch(),
448 phase=ctx.phasestr(),
448 phase=ctx.phasestr(),
449 user=ctx.user(),
449 user=ctx.user(),
450 date=fm.formatdate(ctx.date()),
450 date=fm.formatdate(ctx.date()),
451 desc=ctx.description(),
451 desc=ctx.description(),
452 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
452 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
453 tags=fm.formatlist(ctx.tags(), name=b'tag'),
453 tags=fm.formatlist(ctx.tags(), name=b'tag'),
454 parents=fm.formatlist(
454 parents=fm.formatlist(
455 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
455 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
456 ),
456 ),
457 )
457 )
458
458
459 if self.ui.debugflag:
459 if self.ui.debugflag:
460 fm.data(
460 fm.data(
461 manifest=fm.hexfunc(ctx.manifestnode() or wdirid),
461 manifest=fm.hexfunc(ctx.manifestnode() or wdirid),
462 extra=fm.formatdict(ctx.extra()),
462 extra=fm.formatdict(ctx.extra()),
463 )
463 )
464
464
465 files = ctx.p1().status(ctx)
465 files = ctx.p1().status(ctx)
466 fm.data(
466 fm.data(
467 modified=fm.formatlist(files[0], name=b'file'),
467 modified=fm.formatlist(files[0], name=b'file'),
468 added=fm.formatlist(files[1], name=b'file'),
468 added=fm.formatlist(files[1], name=b'file'),
469 removed=fm.formatlist(files[2], name=b'file'),
469 removed=fm.formatlist(files[2], name=b'file'),
470 )
470 )
471
471
472 elif self.ui.verbose:
472 elif self.ui.verbose:
473 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
473 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
474 if copies:
474 if copies:
475 fm.data(
475 fm.data(
476 copies=fm.formatdict(copies, key=b'name', value=b'source')
476 copies=fm.formatdict(copies, key=b'name', value=b'source')
477 )
477 )
478
478
479 if self._includestat:
479 if self._includestat:
480 self.ui.pushbuffer()
480 self.ui.pushbuffer()
481 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
481 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
482 fm.data(diffstat=self.ui.popbuffer())
482 fm.data(diffstat=self.ui.popbuffer())
483 if self._includediff:
483 if self._includediff:
484 self.ui.pushbuffer()
484 self.ui.pushbuffer()
485 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
485 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
486 fm.data(diff=self.ui.popbuffer())
486 fm.data(diff=self.ui.popbuffer())
487
487
488
488
489 class changesettemplater(changesetprinter):
489 class changesettemplater(changesetprinter):
490 '''format changeset information.
490 '''format changeset information.
491
491
492 Note: there are a variety of convenience functions to build a
492 Note: there are a variety of convenience functions to build a
493 changesettemplater for common cases. See functions such as:
493 changesettemplater for common cases. See functions such as:
494 maketemplater, changesetdisplayer, buildcommittemplate, or other
494 maketemplater, changesetdisplayer, buildcommittemplate, or other
495 functions that use changesest_templater.
495 functions that use changesest_templater.
496 '''
496 '''
497
497
498 # Arguments before "buffered" used to be positional. Consider not
498 # Arguments before "buffered" used to be positional. Consider not
499 # adding/removing arguments before "buffered" to not break callers.
499 # adding/removing arguments before "buffered" to not break callers.
500 def __init__(
500 def __init__(
501 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False
501 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False
502 ):
502 ):
503 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
503 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
504 # tres is shared with _graphnodeformatter()
504 # tres is shared with _graphnodeformatter()
505 self._tresources = tres = formatter.templateresources(ui, repo)
505 self._tresources = tres = formatter.templateresources(ui, repo)
506 self.t = formatter.loadtemplater(
506 self.t = formatter.loadtemplater(
507 ui,
507 ui,
508 tmplspec,
508 tmplspec,
509 defaults=templatekw.keywords,
509 defaults=templatekw.keywords,
510 resources=tres,
510 resources=tres,
511 cache=templatekw.defaulttempl,
511 cache=templatekw.defaulttempl,
512 )
512 )
513 self._counter = itertools.count()
513 self._counter = itertools.count()
514
514
515 self._tref = tmplspec.ref
515 self._tref = tmplspec.ref
516 self._parts = {
516 self._parts = {
517 b'header': b'',
517 b'header': b'',
518 b'footer': b'',
518 b'footer': b'',
519 tmplspec.ref: tmplspec.ref,
519 tmplspec.ref: tmplspec.ref,
520 b'docheader': b'',
520 b'docheader': b'',
521 b'docfooter': b'',
521 b'docfooter': b'',
522 b'separator': b'',
522 b'separator': b'',
523 }
523 }
524 if tmplspec.mapfile:
524 if tmplspec.mapfile:
525 # find correct templates for current mode, for backward
525 # find correct templates for current mode, for backward
526 # compatibility with 'log -v/-q/--debug' using a mapfile
526 # compatibility with 'log -v/-q/--debug' using a mapfile
527 tmplmodes = [
527 tmplmodes = [
528 (True, b''),
528 (True, b''),
529 (self.ui.verbose, b'_verbose'),
529 (self.ui.verbose, b'_verbose'),
530 (self.ui.quiet, b'_quiet'),
530 (self.ui.quiet, b'_quiet'),
531 (self.ui.debugflag, b'_debug'),
531 (self.ui.debugflag, b'_debug'),
532 ]
532 ]
533 for mode, postfix in tmplmodes:
533 for mode, postfix in tmplmodes:
534 for t in self._parts:
534 for t in self._parts:
535 cur = t + postfix
535 cur = t + postfix
536 if mode and cur in self.t:
536 if mode and cur in self.t:
537 self._parts[t] = cur
537 self._parts[t] = cur
538 else:
538 else:
539 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
539 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
540 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
540 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
541 self._parts.update(m)
541 self._parts.update(m)
542
542
543 if self._parts[b'docheader']:
543 if self._parts[b'docheader']:
544 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
544 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
545
545
546 def close(self):
546 def close(self):
547 if self._parts[b'docfooter']:
547 if self._parts[b'docfooter']:
548 if not self.footer:
548 if not self.footer:
549 self.footer = b""
549 self.footer = b""
550 self.footer += self.t.render(self._parts[b'docfooter'], {})
550 self.footer += self.t.render(self._parts[b'docfooter'], {})
551 return super(changesettemplater, self).close()
551 return super(changesettemplater, self).close()
552
552
553 def _show(self, ctx, copies, props):
553 def _show(self, ctx, copies, props):
554 '''show a single changeset or file revision'''
554 '''show a single changeset or file revision'''
555 props = props.copy()
555 props = props.copy()
556 props[b'ctx'] = ctx
556 props[b'ctx'] = ctx
557 props[b'index'] = index = next(self._counter)
557 props[b'index'] = index = next(self._counter)
558 props[b'revcache'] = {b'copies': copies}
558 props[b'revcache'] = {b'copies': copies}
559 graphwidth = props.get(b'graphwidth', 0)
559 graphwidth = props.get(b'graphwidth', 0)
560
560
561 # write separator, which wouldn't work well with the header part below
561 # write separator, which wouldn't work well with the header part below
562 # since there's inherently a conflict between header (across items) and
562 # since there's inherently a conflict between header (across items) and
563 # separator (per item)
563 # separator (per item)
564 if self._parts[b'separator'] and index > 0:
564 if self._parts[b'separator'] and index > 0:
565 self.ui.write(self.t.render(self._parts[b'separator'], {}))
565 self.ui.write(self.t.render(self._parts[b'separator'], {}))
566
566
567 # write header
567 # write header
568 if self._parts[b'header']:
568 if self._parts[b'header']:
569 h = self.t.render(self._parts[b'header'], props)
569 h = self.t.render(self._parts[b'header'], props)
570 if self.buffered:
570 if self.buffered:
571 self.header[ctx.rev()] = h
571 self.header[ctx.rev()] = h
572 else:
572 else:
573 if self.lastheader != h:
573 if self.lastheader != h:
574 self.lastheader = h
574 self.lastheader = h
575 self.ui.write(h)
575 self.ui.write(h)
576
576
577 # write changeset metadata, then patch if requested
577 # write changeset metadata, then patch if requested
578 key = self._parts[self._tref]
578 key = self._parts[self._tref]
579 self.ui.write(self.t.render(key, props))
579 self.ui.write(self.t.render(key, props))
580 self._showpatch(ctx, graphwidth)
580 self._showpatch(ctx, graphwidth)
581
581
582 if self._parts[b'footer']:
582 if self._parts[b'footer']:
583 if not self.footer:
583 if not self.footer:
584 self.footer = self.t.render(self._parts[b'footer'], props)
584 self.footer = self.t.render(self._parts[b'footer'], props)
585
585
586
586
587 def templatespec(tmpl, mapfile):
587 def templatespec(tmpl, mapfile):
588 if pycompat.ispy3:
588 if pycompat.ispy3:
589 assert not isinstance(tmpl, str), b'tmpl must not be a str'
589 assert not isinstance(tmpl, str), b'tmpl must not be a str'
590 if mapfile:
590 if mapfile:
591 return formatter.templatespec(b'changeset', tmpl, mapfile)
591 return formatter.templatespec(b'changeset', tmpl, mapfile)
592 else:
592 else:
593 return formatter.templatespec(b'', tmpl, None)
593 return formatter.templatespec(b'', tmpl, None)
594
594
595
595
596 def _lookuptemplate(ui, tmpl, style):
596 def _lookuptemplate(ui, tmpl, style):
597 """Find the template matching the given template spec or style
597 """Find the template matching the given template spec or style
598
598
599 See formatter.lookuptemplate() for details.
599 See formatter.lookuptemplate() for details.
600 """
600 """
601
601
602 # ui settings
602 # ui settings
603 if not tmpl and not style: # template are stronger than style
603 if not tmpl and not style: # template are stronger than style
604 tmpl = ui.config(b'ui', b'logtemplate')
604 tmpl = ui.config(b'ui', b'logtemplate')
605 if tmpl:
605 if tmpl:
606 return templatespec(templater.unquotestring(tmpl), None)
606 return templatespec(templater.unquotestring(tmpl), None)
607 else:
607 else:
608 style = util.expandpath(ui.config(b'ui', b'style'))
608 style = util.expandpath(ui.config(b'ui', b'style'))
609
609
610 if not tmpl and style:
610 if not tmpl and style:
611 mapfile = style
611 mapfile = style
612 if not os.path.split(mapfile)[0]:
612 if not os.path.split(mapfile)[0]:
613 mapname = templater.templatepath(
613 mapname = templater.templatepath(
614 b'map-cmdline.' + mapfile
614 b'map-cmdline.' + mapfile
615 ) or templater.templatepath(mapfile)
615 ) or templater.templatepath(mapfile)
616 if mapname:
616 if mapname:
617 mapfile = mapname
617 mapfile = mapname
618 return templatespec(None, mapfile)
618 return templatespec(None, mapfile)
619
619
620 if not tmpl:
621 return templatespec(None, None)
622
623 return formatter.lookuptemplate(ui, b'changeset', tmpl)
620 return formatter.lookuptemplate(ui, b'changeset', tmpl)
624
621
625
622
626 def maketemplater(ui, repo, tmpl, buffered=False):
623 def maketemplater(ui, repo, tmpl, buffered=False):
627 """Create a changesettemplater from a literal template 'tmpl'
624 """Create a changesettemplater from a literal template 'tmpl'
628 byte-string."""
625 byte-string."""
629 spec = templatespec(tmpl, None)
626 spec = templatespec(tmpl, None)
630 return changesettemplater(ui, repo, spec, buffered=buffered)
627 return changesettemplater(ui, repo, spec, buffered=buffered)
631
628
632
629
633 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
630 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
634 """show one changeset using template or regular display.
631 """show one changeset using template or regular display.
635
632
636 Display format will be the first non-empty hit of:
633 Display format will be the first non-empty hit of:
637 1. option 'template'
634 1. option 'template'
638 2. option 'style'
635 2. option 'style'
639 3. [ui] setting 'logtemplate'
636 3. [ui] setting 'logtemplate'
640 4. [ui] setting 'style'
637 4. [ui] setting 'style'
641 If all of these values are either the unset or the empty string,
638 If all of these values are either the unset or the empty string,
642 regular display via changesetprinter() is done.
639 regular display via changesetprinter() is done.
643 """
640 """
644 postargs = (differ, opts, buffered)
641 postargs = (differ, opts, buffered)
645 if opts.get(b'template') in {b'cbor', b'json'}:
642 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
643
644 # machine-readable formats have slightly different keyword set than
645 # plain templates, which are handled by changesetformatter.
646 # note that {b'pickle', b'debug'} can also be added to the list if needed.
647 if spec.ref in {b'cbor', b'json'}:
646 fm = ui.formatter(b'log', opts)
648 fm = ui.formatter(b'log', opts)
647 return changesetformatter(ui, repo, fm, *postargs)
649 return changesetformatter(ui, repo, fm, *postargs)
648
650
649 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
650
651 if not spec.ref and not spec.tmpl and not spec.mapfile:
651 if not spec.ref and not spec.tmpl and not spec.mapfile:
652 return changesetprinter(ui, repo, *postargs)
652 return changesetprinter(ui, repo, *postargs)
653
653
654 return changesettemplater(ui, repo, spec, *postargs)
654 return changesettemplater(ui, repo, spec, *postargs)
655
655
656
656
657 def _makematcher(repo, revs, pats, opts):
657 def _makematcher(repo, revs, pats, opts):
658 """Build matcher and expanded patterns from log options
658 """Build matcher and expanded patterns from log options
659
659
660 If --follow, revs are the revisions to follow from.
660 If --follow, revs are the revisions to follow from.
661
661
662 Returns (match, pats, slowpath) where
662 Returns (match, pats, slowpath) where
663 - match: a matcher built from the given pats and -I/-X opts
663 - match: a matcher built from the given pats and -I/-X opts
664 - pats: patterns used (globs are expanded on Windows)
664 - pats: patterns used (globs are expanded on Windows)
665 - slowpath: True if patterns aren't as simple as scanning filelogs
665 - slowpath: True if patterns aren't as simple as scanning filelogs
666 """
666 """
667 # pats/include/exclude are passed to match.match() directly in
667 # pats/include/exclude are passed to match.match() directly in
668 # _matchfiles() revset but walkchangerevs() builds its matcher with
668 # _matchfiles() revset but walkchangerevs() builds its matcher with
669 # scmutil.match(). The difference is input pats are globbed on
669 # scmutil.match(). The difference is input pats are globbed on
670 # platforms without shell expansion (windows).
670 # platforms without shell expansion (windows).
671 wctx = repo[None]
671 wctx = repo[None]
672 match, pats = scmutil.matchandpats(wctx, pats, opts)
672 match, pats = scmutil.matchandpats(wctx, pats, opts)
673 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
673 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
674 if not slowpath:
674 if not slowpath:
675 follow = opts.get(b'follow') or opts.get(b'follow_first')
675 follow = opts.get(b'follow') or opts.get(b'follow_first')
676 startctxs = []
676 startctxs = []
677 if follow and opts.get(b'rev'):
677 if follow and opts.get(b'rev'):
678 startctxs = [repo[r] for r in revs]
678 startctxs = [repo[r] for r in revs]
679 for f in match.files():
679 for f in match.files():
680 if follow and startctxs:
680 if follow and startctxs:
681 # No idea if the path was a directory at that revision, so
681 # No idea if the path was a directory at that revision, so
682 # take the slow path.
682 # take the slow path.
683 if any(f not in c for c in startctxs):
683 if any(f not in c for c in startctxs):
684 slowpath = True
684 slowpath = True
685 continue
685 continue
686 elif follow and f not in wctx:
686 elif follow and f not in wctx:
687 # If the file exists, it may be a directory, so let it
687 # If the file exists, it may be a directory, so let it
688 # take the slow path.
688 # take the slow path.
689 if os.path.exists(repo.wjoin(f)):
689 if os.path.exists(repo.wjoin(f)):
690 slowpath = True
690 slowpath = True
691 continue
691 continue
692 else:
692 else:
693 raise error.Abort(
693 raise error.Abort(
694 _(
694 _(
695 b'cannot follow file not in parent '
695 b'cannot follow file not in parent '
696 b'revision: "%s"'
696 b'revision: "%s"'
697 )
697 )
698 % f
698 % f
699 )
699 )
700 filelog = repo.file(f)
700 filelog = repo.file(f)
701 if not filelog:
701 if not filelog:
702 # A zero count may be a directory or deleted file, so
702 # A zero count may be a directory or deleted file, so
703 # try to find matching entries on the slow path.
703 # try to find matching entries on the slow path.
704 if follow:
704 if follow:
705 raise error.Abort(
705 raise error.Abort(
706 _(b'cannot follow nonexistent file: "%s"') % f
706 _(b'cannot follow nonexistent file: "%s"') % f
707 )
707 )
708 slowpath = True
708 slowpath = True
709
709
710 # We decided to fall back to the slowpath because at least one
710 # We decided to fall back to the slowpath because at least one
711 # of the paths was not a file. Check to see if at least one of them
711 # of the paths was not a file. Check to see if at least one of them
712 # existed in history - in that case, we'll continue down the
712 # existed in history - in that case, we'll continue down the
713 # slowpath; otherwise, we can turn off the slowpath
713 # slowpath; otherwise, we can turn off the slowpath
714 if slowpath:
714 if slowpath:
715 for path in match.files():
715 for path in match.files():
716 if path == b'.' or path in repo.store:
716 if path == b'.' or path in repo.store:
717 break
717 break
718 else:
718 else:
719 slowpath = False
719 slowpath = False
720
720
721 return match, pats, slowpath
721 return match, pats, slowpath
722
722
723
723
724 def _fileancestors(repo, revs, match, followfirst):
724 def _fileancestors(repo, revs, match, followfirst):
725 fctxs = []
725 fctxs = []
726 for r in revs:
726 for r in revs:
727 ctx = repo[r]
727 ctx = repo[r]
728 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
728 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
729
729
730 # When displaying a revision with --patch --follow FILE, we have
730 # When displaying a revision with --patch --follow FILE, we have
731 # to know which file of the revision must be diffed. With
731 # to know which file of the revision must be diffed. With
732 # --follow, we want the names of the ancestors of FILE in the
732 # --follow, we want the names of the ancestors of FILE in the
733 # revision, stored in "fcache". "fcache" is populated as a side effect
733 # revision, stored in "fcache". "fcache" is populated as a side effect
734 # of the graph traversal.
734 # of the graph traversal.
735 fcache = {}
735 fcache = {}
736
736
737 def filematcher(ctx):
737 def filematcher(ctx):
738 return scmutil.matchfiles(repo, fcache.get(ctx.rev(), []))
738 return scmutil.matchfiles(repo, fcache.get(ctx.rev(), []))
739
739
740 def revgen():
740 def revgen():
741 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
741 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
742 fcache[rev] = [c.path() for c in cs]
742 fcache[rev] = [c.path() for c in cs]
743 yield rev
743 yield rev
744
744
745 return smartset.generatorset(revgen(), iterasc=False), filematcher
745 return smartset.generatorset(revgen(), iterasc=False), filematcher
746
746
747
747
748 def _makenofollowfilematcher(repo, pats, opts):
748 def _makenofollowfilematcher(repo, pats, opts):
749 '''hook for extensions to override the filematcher for non-follow cases'''
749 '''hook for extensions to override the filematcher for non-follow cases'''
750 return None
750 return None
751
751
752
752
753 _opt2logrevset = {
753 _opt2logrevset = {
754 b'no_merges': (b'not merge()', None),
754 b'no_merges': (b'not merge()', None),
755 b'only_merges': (b'merge()', None),
755 b'only_merges': (b'merge()', None),
756 b'_matchfiles': (None, b'_matchfiles(%ps)'),
756 b'_matchfiles': (None, b'_matchfiles(%ps)'),
757 b'date': (b'date(%s)', None),
757 b'date': (b'date(%s)', None),
758 b'branch': (b'branch(%s)', b'%lr'),
758 b'branch': (b'branch(%s)', b'%lr'),
759 b'_patslog': (b'filelog(%s)', b'%lr'),
759 b'_patslog': (b'filelog(%s)', b'%lr'),
760 b'keyword': (b'keyword(%s)', b'%lr'),
760 b'keyword': (b'keyword(%s)', b'%lr'),
761 b'prune': (b'ancestors(%s)', b'not %lr'),
761 b'prune': (b'ancestors(%s)', b'not %lr'),
762 b'user': (b'user(%s)', b'%lr'),
762 b'user': (b'user(%s)', b'%lr'),
763 }
763 }
764
764
765
765
766 def _makerevset(repo, match, pats, slowpath, opts):
766 def _makerevset(repo, match, pats, slowpath, opts):
767 """Return a revset string built from log options and file patterns"""
767 """Return a revset string built from log options and file patterns"""
768 opts = dict(opts)
768 opts = dict(opts)
769 # follow or not follow?
769 # follow or not follow?
770 follow = opts.get(b'follow') or opts.get(b'follow_first')
770 follow = opts.get(b'follow') or opts.get(b'follow_first')
771
771
772 # branch and only_branch are really aliases and must be handled at
772 # branch and only_branch are really aliases and must be handled at
773 # the same time
773 # the same time
774 opts[b'branch'] = opts.get(b'branch', []) + opts.get(b'only_branch', [])
774 opts[b'branch'] = opts.get(b'branch', []) + opts.get(b'only_branch', [])
775 opts[b'branch'] = [repo.lookupbranch(b) for b in opts[b'branch']]
775 opts[b'branch'] = [repo.lookupbranch(b) for b in opts[b'branch']]
776
776
777 if slowpath:
777 if slowpath:
778 # See walkchangerevs() slow path.
778 # See walkchangerevs() slow path.
779 #
779 #
780 # pats/include/exclude cannot be represented as separate
780 # pats/include/exclude cannot be represented as separate
781 # revset expressions as their filtering logic applies at file
781 # revset expressions as their filtering logic applies at file
782 # level. For instance "-I a -X b" matches a revision touching
782 # level. For instance "-I a -X b" matches a revision touching
783 # "a" and "b" while "file(a) and not file(b)" does
783 # "a" and "b" while "file(a) and not file(b)" does
784 # not. Besides, filesets are evaluated against the working
784 # not. Besides, filesets are evaluated against the working
785 # directory.
785 # directory.
786 matchargs = [b'r:', b'd:relpath']
786 matchargs = [b'r:', b'd:relpath']
787 for p in pats:
787 for p in pats:
788 matchargs.append(b'p:' + p)
788 matchargs.append(b'p:' + p)
789 for p in opts.get(b'include', []):
789 for p in opts.get(b'include', []):
790 matchargs.append(b'i:' + p)
790 matchargs.append(b'i:' + p)
791 for p in opts.get(b'exclude', []):
791 for p in opts.get(b'exclude', []):
792 matchargs.append(b'x:' + p)
792 matchargs.append(b'x:' + p)
793 opts[b'_matchfiles'] = matchargs
793 opts[b'_matchfiles'] = matchargs
794 elif not follow:
794 elif not follow:
795 opts[b'_patslog'] = list(pats)
795 opts[b'_patslog'] = list(pats)
796
796
797 expr = []
797 expr = []
798 for op, val in sorted(opts.iteritems()):
798 for op, val in sorted(opts.iteritems()):
799 if not val:
799 if not val:
800 continue
800 continue
801 if op not in _opt2logrevset:
801 if op not in _opt2logrevset:
802 continue
802 continue
803 revop, listop = _opt2logrevset[op]
803 revop, listop = _opt2logrevset[op]
804 if revop and b'%' not in revop:
804 if revop and b'%' not in revop:
805 expr.append(revop)
805 expr.append(revop)
806 elif not listop:
806 elif not listop:
807 expr.append(revsetlang.formatspec(revop, val))
807 expr.append(revsetlang.formatspec(revop, val))
808 else:
808 else:
809 if revop:
809 if revop:
810 val = [revsetlang.formatspec(revop, v) for v in val]
810 val = [revsetlang.formatspec(revop, v) for v in val]
811 expr.append(revsetlang.formatspec(listop, val))
811 expr.append(revsetlang.formatspec(listop, val))
812
812
813 if expr:
813 if expr:
814 expr = b'(' + b' and '.join(expr) + b')'
814 expr = b'(' + b' and '.join(expr) + b')'
815 else:
815 else:
816 expr = None
816 expr = None
817 return expr
817 return expr
818
818
819
819
820 def _initialrevs(repo, opts):
820 def _initialrevs(repo, opts):
821 """Return the initial set of revisions to be filtered or followed"""
821 """Return the initial set of revisions to be filtered or followed"""
822 follow = opts.get(b'follow') or opts.get(b'follow_first')
822 follow = opts.get(b'follow') or opts.get(b'follow_first')
823 if opts.get(b'rev'):
823 if opts.get(b'rev'):
824 revs = scmutil.revrange(repo, opts[b'rev'])
824 revs = scmutil.revrange(repo, opts[b'rev'])
825 elif follow and repo.dirstate.p1() == nullid:
825 elif follow and repo.dirstate.p1() == nullid:
826 revs = smartset.baseset()
826 revs = smartset.baseset()
827 elif follow:
827 elif follow:
828 revs = repo.revs(b'.')
828 revs = repo.revs(b'.')
829 else:
829 else:
830 revs = smartset.spanset(repo)
830 revs = smartset.spanset(repo)
831 revs.reverse()
831 revs.reverse()
832 return revs
832 return revs
833
833
834
834
835 def getrevs(repo, pats, opts):
835 def getrevs(repo, pats, opts):
836 """Return (revs, differ) where revs is a smartset
836 """Return (revs, differ) where revs is a smartset
837
837
838 differ is a changesetdiffer with pre-configured file matcher.
838 differ is a changesetdiffer with pre-configured file matcher.
839 """
839 """
840 follow = opts.get(b'follow') or opts.get(b'follow_first')
840 follow = opts.get(b'follow') or opts.get(b'follow_first')
841 followfirst = opts.get(b'follow_first')
841 followfirst = opts.get(b'follow_first')
842 limit = getlimit(opts)
842 limit = getlimit(opts)
843 revs = _initialrevs(repo, opts)
843 revs = _initialrevs(repo, opts)
844 if not revs:
844 if not revs:
845 return smartset.baseset(), None
845 return smartset.baseset(), None
846 match, pats, slowpath = _makematcher(repo, revs, pats, opts)
846 match, pats, slowpath = _makematcher(repo, revs, pats, opts)
847 filematcher = None
847 filematcher = None
848 if follow:
848 if follow:
849 if slowpath or match.always():
849 if slowpath or match.always():
850 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
850 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
851 else:
851 else:
852 revs, filematcher = _fileancestors(repo, revs, match, followfirst)
852 revs, filematcher = _fileancestors(repo, revs, match, followfirst)
853 revs.reverse()
853 revs.reverse()
854 if filematcher is None:
854 if filematcher is None:
855 filematcher = _makenofollowfilematcher(repo, pats, opts)
855 filematcher = _makenofollowfilematcher(repo, pats, opts)
856 if filematcher is None:
856 if filematcher is None:
857
857
858 def filematcher(ctx):
858 def filematcher(ctx):
859 return match
859 return match
860
860
861 expr = _makerevset(repo, match, pats, slowpath, opts)
861 expr = _makerevset(repo, match, pats, slowpath, opts)
862 if opts.get(b'graph'):
862 if opts.get(b'graph'):
863 # User-specified revs might be unsorted, but don't sort before
863 # User-specified revs might be unsorted, but don't sort before
864 # _makerevset because it might depend on the order of revs
864 # _makerevset because it might depend on the order of revs
865 if repo.ui.configbool(b'experimental', b'log.topo'):
865 if repo.ui.configbool(b'experimental', b'log.topo'):
866 if not revs.istopo():
866 if not revs.istopo():
867 revs = dagop.toposort(revs, repo.changelog.parentrevs)
867 revs = dagop.toposort(revs, repo.changelog.parentrevs)
868 # TODO: try to iterate the set lazily
868 # TODO: try to iterate the set lazily
869 revs = revset.baseset(list(revs), istopo=True)
869 revs = revset.baseset(list(revs), istopo=True)
870 elif not (revs.isdescending() or revs.istopo()):
870 elif not (revs.isdescending() or revs.istopo()):
871 revs.sort(reverse=True)
871 revs.sort(reverse=True)
872 if expr:
872 if expr:
873 matcher = revset.match(None, expr)
873 matcher = revset.match(None, expr)
874 revs = matcher(repo, revs)
874 revs = matcher(repo, revs)
875 if limit is not None:
875 if limit is not None:
876 revs = revs.slice(0, limit)
876 revs = revs.slice(0, limit)
877
877
878 differ = changesetdiffer()
878 differ = changesetdiffer()
879 differ._makefilematcher = filematcher
879 differ._makefilematcher = filematcher
880 return revs, differ
880 return revs, differ
881
881
882
882
883 def _parselinerangeopt(repo, opts):
883 def _parselinerangeopt(repo, opts):
884 """Parse --line-range log option and return a list of tuples (filename,
884 """Parse --line-range log option and return a list of tuples (filename,
885 (fromline, toline)).
885 (fromline, toline)).
886 """
886 """
887 linerangebyfname = []
887 linerangebyfname = []
888 for pat in opts.get(b'line_range', []):
888 for pat in opts.get(b'line_range', []):
889 try:
889 try:
890 pat, linerange = pat.rsplit(b',', 1)
890 pat, linerange = pat.rsplit(b',', 1)
891 except ValueError:
891 except ValueError:
892 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
892 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
893 try:
893 try:
894 fromline, toline = map(int, linerange.split(b':'))
894 fromline, toline = map(int, linerange.split(b':'))
895 except ValueError:
895 except ValueError:
896 raise error.Abort(_(b"invalid line range for %s") % pat)
896 raise error.Abort(_(b"invalid line range for %s") % pat)
897 msg = _(b"line range pattern '%s' must match exactly one file") % pat
897 msg = _(b"line range pattern '%s' must match exactly one file") % pat
898 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
898 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
899 linerangebyfname.append(
899 linerangebyfname.append(
900 (fname, util.processlinerange(fromline, toline))
900 (fname, util.processlinerange(fromline, toline))
901 )
901 )
902 return linerangebyfname
902 return linerangebyfname
903
903
904
904
905 def getlinerangerevs(repo, userrevs, opts):
905 def getlinerangerevs(repo, userrevs, opts):
906 """Return (revs, differ).
906 """Return (revs, differ).
907
907
908 "revs" are revisions obtained by processing "line-range" log options and
908 "revs" are revisions obtained by processing "line-range" log options and
909 walking block ancestors of each specified file/line-range.
909 walking block ancestors of each specified file/line-range.
910
910
911 "differ" is a changesetdiffer with pre-configured file matcher and hunks
911 "differ" is a changesetdiffer with pre-configured file matcher and hunks
912 filter.
912 filter.
913 """
913 """
914 wctx = repo[None]
914 wctx = repo[None]
915
915
916 # Two-levels map of "rev -> file ctx -> [line range]".
916 # Two-levels map of "rev -> file ctx -> [line range]".
917 linerangesbyrev = {}
917 linerangesbyrev = {}
918 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
918 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
919 if fname not in wctx:
919 if fname not in wctx:
920 raise error.Abort(
920 raise error.Abort(
921 _(b'cannot follow file not in parent ' b'revision: "%s"')
921 _(b'cannot follow file not in parent ' b'revision: "%s"')
922 % fname
922 % fname
923 )
923 )
924 fctx = wctx.filectx(fname)
924 fctx = wctx.filectx(fname)
925 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
925 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
926 rev = fctx.introrev()
926 rev = fctx.introrev()
927 if rev not in userrevs:
927 if rev not in userrevs:
928 continue
928 continue
929 linerangesbyrev.setdefault(rev, {}).setdefault(
929 linerangesbyrev.setdefault(rev, {}).setdefault(
930 fctx.path(), []
930 fctx.path(), []
931 ).append(linerange)
931 ).append(linerange)
932
932
933 def nofilterhunksfn(fctx, hunks):
933 def nofilterhunksfn(fctx, hunks):
934 return hunks
934 return hunks
935
935
936 def hunksfilter(ctx):
936 def hunksfilter(ctx):
937 fctxlineranges = linerangesbyrev.get(ctx.rev())
937 fctxlineranges = linerangesbyrev.get(ctx.rev())
938 if fctxlineranges is None:
938 if fctxlineranges is None:
939 return nofilterhunksfn
939 return nofilterhunksfn
940
940
941 def filterfn(fctx, hunks):
941 def filterfn(fctx, hunks):
942 lineranges = fctxlineranges.get(fctx.path())
942 lineranges = fctxlineranges.get(fctx.path())
943 if lineranges is not None:
943 if lineranges is not None:
944 for hr, lines in hunks:
944 for hr, lines in hunks:
945 if hr is None: # binary
945 if hr is None: # binary
946 yield hr, lines
946 yield hr, lines
947 continue
947 continue
948 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges):
948 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges):
949 yield hr, lines
949 yield hr, lines
950 else:
950 else:
951 for hunk in hunks:
951 for hunk in hunks:
952 yield hunk
952 yield hunk
953
953
954 return filterfn
954 return filterfn
955
955
956 def filematcher(ctx):
956 def filematcher(ctx):
957 files = list(linerangesbyrev.get(ctx.rev(), []))
957 files = list(linerangesbyrev.get(ctx.rev(), []))
958 return scmutil.matchfiles(repo, files)
958 return scmutil.matchfiles(repo, files)
959
959
960 revs = sorted(linerangesbyrev, reverse=True)
960 revs = sorted(linerangesbyrev, reverse=True)
961
961
962 differ = changesetdiffer()
962 differ = changesetdiffer()
963 differ._makefilematcher = filematcher
963 differ._makefilematcher = filematcher
964 differ._makehunksfilter = hunksfilter
964 differ._makehunksfilter = hunksfilter
965 return revs, differ
965 return revs, differ
966
966
967
967
968 def _graphnodeformatter(ui, displayer):
968 def _graphnodeformatter(ui, displayer):
969 spec = ui.config(b'ui', b'graphnodetemplate')
969 spec = ui.config(b'ui', b'graphnodetemplate')
970 if not spec:
970 if not spec:
971 return templatekw.getgraphnode # fast path for "{graphnode}"
971 return templatekw.getgraphnode # fast path for "{graphnode}"
972
972
973 spec = templater.unquotestring(spec)
973 spec = templater.unquotestring(spec)
974 if isinstance(displayer, changesettemplater):
974 if isinstance(displayer, changesettemplater):
975 # reuse cache of slow templates
975 # reuse cache of slow templates
976 tres = displayer._tresources
976 tres = displayer._tresources
977 else:
977 else:
978 tres = formatter.templateresources(ui)
978 tres = formatter.templateresources(ui)
979 templ = formatter.maketemplater(
979 templ = formatter.maketemplater(
980 ui, spec, defaults=templatekw.keywords, resources=tres
980 ui, spec, defaults=templatekw.keywords, resources=tres
981 )
981 )
982
982
983 def formatnode(repo, ctx):
983 def formatnode(repo, ctx):
984 props = {b'ctx': ctx, b'repo': repo}
984 props = {b'ctx': ctx, b'repo': repo}
985 return templ.renderdefault(props)
985 return templ.renderdefault(props)
986
986
987 return formatnode
987 return formatnode
988
988
989
989
990 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
990 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
991 props = props or {}
991 props = props or {}
992 formatnode = _graphnodeformatter(ui, displayer)
992 formatnode = _graphnodeformatter(ui, displayer)
993 state = graphmod.asciistate()
993 state = graphmod.asciistate()
994 styles = state[b'styles']
994 styles = state[b'styles']
995
995
996 # only set graph styling if HGPLAIN is not set.
996 # only set graph styling if HGPLAIN is not set.
997 if ui.plain(b'graph'):
997 if ui.plain(b'graph'):
998 # set all edge styles to |, the default pre-3.8 behaviour
998 # set all edge styles to |, the default pre-3.8 behaviour
999 styles.update(dict.fromkeys(styles, b'|'))
999 styles.update(dict.fromkeys(styles, b'|'))
1000 else:
1000 else:
1001 edgetypes = {
1001 edgetypes = {
1002 b'parent': graphmod.PARENT,
1002 b'parent': graphmod.PARENT,
1003 b'grandparent': graphmod.GRANDPARENT,
1003 b'grandparent': graphmod.GRANDPARENT,
1004 b'missing': graphmod.MISSINGPARENT,
1004 b'missing': graphmod.MISSINGPARENT,
1005 }
1005 }
1006 for name, key in edgetypes.items():
1006 for name, key in edgetypes.items():
1007 # experimental config: experimental.graphstyle.*
1007 # experimental config: experimental.graphstyle.*
1008 styles[key] = ui.config(
1008 styles[key] = ui.config(
1009 b'experimental', b'graphstyle.%s' % name, styles[key]
1009 b'experimental', b'graphstyle.%s' % name, styles[key]
1010 )
1010 )
1011 if not styles[key]:
1011 if not styles[key]:
1012 styles[key] = None
1012 styles[key] = None
1013
1013
1014 # experimental config: experimental.graphshorten
1014 # experimental config: experimental.graphshorten
1015 state[b'graphshorten'] = ui.configbool(b'experimental', b'graphshorten')
1015 state[b'graphshorten'] = ui.configbool(b'experimental', b'graphshorten')
1016
1016
1017 for rev, type, ctx, parents in dag:
1017 for rev, type, ctx, parents in dag:
1018 char = formatnode(repo, ctx)
1018 char = formatnode(repo, ctx)
1019 copies = getcopies(ctx) if getcopies else None
1019 copies = getcopies(ctx) if getcopies else None
1020 edges = edgefn(type, char, state, rev, parents)
1020 edges = edgefn(type, char, state, rev, parents)
1021 firstedge = next(edges)
1021 firstedge = next(edges)
1022 width = firstedge[2]
1022 width = firstedge[2]
1023 displayer.show(
1023 displayer.show(
1024 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1024 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1025 )
1025 )
1026 lines = displayer.hunk.pop(rev).split(b'\n')
1026 lines = displayer.hunk.pop(rev).split(b'\n')
1027 if not lines[-1]:
1027 if not lines[-1]:
1028 del lines[-1]
1028 del lines[-1]
1029 displayer.flush(ctx)
1029 displayer.flush(ctx)
1030 for type, char, width, coldata in itertools.chain([firstedge], edges):
1030 for type, char, width, coldata in itertools.chain([firstedge], edges):
1031 graphmod.ascii(ui, state, type, char, lines, coldata)
1031 graphmod.ascii(ui, state, type, char, lines, coldata)
1032 lines = []
1032 lines = []
1033 displayer.close()
1033 displayer.close()
1034
1034
1035
1035
1036 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
1036 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
1037 revdag = graphmod.dagwalker(repo, revs)
1037 revdag = graphmod.dagwalker(repo, revs)
1038 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
1038 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
1039
1039
1040
1040
1041 def displayrevs(ui, repo, revs, displayer, getcopies):
1041 def displayrevs(ui, repo, revs, displayer, getcopies):
1042 for rev in revs:
1042 for rev in revs:
1043 ctx = repo[rev]
1043 ctx = repo[rev]
1044 copies = getcopies(ctx) if getcopies else None
1044 copies = getcopies(ctx) if getcopies else None
1045 displayer.show(ctx, copies=copies)
1045 displayer.show(ctx, copies=copies)
1046 displayer.flush(ctx)
1046 displayer.flush(ctx)
1047 displayer.close()
1047 displayer.close()
1048
1048
1049
1049
1050 def checkunsupportedgraphflags(pats, opts):
1050 def checkunsupportedgraphflags(pats, opts):
1051 for op in [b"newest_first"]:
1051 for op in [b"newest_first"]:
1052 if op in opts and opts[op]:
1052 if op in opts and opts[op]:
1053 raise error.Abort(
1053 raise error.Abort(
1054 _(b"-G/--graph option is incompatible with --%s")
1054 _(b"-G/--graph option is incompatible with --%s")
1055 % op.replace(b"_", b"-")
1055 % op.replace(b"_", b"-")
1056 )
1056 )
1057
1057
1058
1058
1059 def graphrevs(repo, nodes, opts):
1059 def graphrevs(repo, nodes, opts):
1060 limit = getlimit(opts)
1060 limit = getlimit(opts)
1061 nodes.reverse()
1061 nodes.reverse()
1062 if limit is not None:
1062 if limit is not None:
1063 nodes = nodes[:limit]
1063 nodes = nodes[:limit]
1064 return graphmod.nodes(repo, nodes)
1064 return graphmod.nodes(repo, nodes)
@@ -1,1828 +1,1837 b''
1 Test template map files and styles
1 Test template map files and styles
2 ==================================
2 ==================================
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo a > a
6 $ echo a > a
7 $ hg add a
7 $ hg add a
8 $ echo line 1 > b
8 $ echo line 1 > b
9 $ echo line 2 >> b
9 $ echo line 2 >> b
10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
11
11
12 $ hg add b
12 $ hg add b
13 $ echo other 1 > c
13 $ echo other 1 > c
14 $ echo other 2 >> c
14 $ echo other 2 >> c
15 $ echo >> c
15 $ echo >> c
16 $ echo other 3 >> c
16 $ echo other 3 >> c
17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
18
18
19 $ hg add c
19 $ hg add c
20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
21 $ echo c >> c
21 $ echo c >> c
22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
23
23
24 $ echo foo > .hg/branch
24 $ echo foo > .hg/branch
25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
26
26
27 $ hg co -q 3
27 $ hg co -q 3
28 $ echo other 4 >> d
28 $ echo other 4 >> d
29 $ hg add d
29 $ hg add d
30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
31
31
32 $ hg merge -q foo
32 $ hg merge -q foo
33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
34
34
35 Second branch starting at nullrev:
35 Second branch starting at nullrev:
36
36
37 $ hg update null
37 $ hg update null
38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
39 $ echo second > second
39 $ echo second > second
40 $ hg add second
40 $ hg add second
41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
42 created new head
42 created new head
43
43
44 $ echo third > third
44 $ echo third > third
45 $ hg add third
45 $ hg add third
46 $ hg mv second fourth
46 $ hg mv second fourth
47 $ hg commit -m third -d "2020-01-01 10:01"
47 $ hg commit -m third -d "2020-01-01 10:01"
48
48
49 Make sure user/global hgrc does not affect tests
49 Make sure user/global hgrc does not affect tests
50
50
51 $ echo '[ui]' > .hg/hgrc
51 $ echo '[ui]' > .hg/hgrc
52 $ echo 'logtemplate =' >> .hg/hgrc
52 $ echo 'logtemplate =' >> .hg/hgrc
53 $ echo 'style =' >> .hg/hgrc
53 $ echo 'style =' >> .hg/hgrc
54
54
55 Add some simple styles to settings
55 Add some simple styles to settings
56
56
57 $ cat <<'EOF' >> .hg/hgrc
57 $ cat <<'EOF' >> .hg/hgrc
58 > [templates]
58 > [templates]
59 > simple = "{rev}\n"
59 > simple = "{rev}\n"
60 > simple2 = {rev}\n
60 > simple2 = {rev}\n
61 > rev = "should not precede {rev} keyword\n"
61 > rev = "should not precede {rev} keyword\n"
62 > EOF
62 > EOF
63
63
64 $ hg log -l1 -Tsimple
64 $ hg log -l1 -Tsimple
65 8
65 8
66 $ hg log -l1 -Tsimple2
66 $ hg log -l1 -Tsimple2
67 8
67 8
68 $ hg log -l1 -Trev
68 $ hg log -l1 -Trev
69 should not precede 8 keyword
69 should not precede 8 keyword
70 $ hg log -l1 -T '{simple}'
70 $ hg log -l1 -T '{simple}'
71 8
71 8
72
72
73 Map file shouldn't see user templates:
73 Map file shouldn't see user templates:
74
74
75 $ cat <<EOF > tmpl
75 $ cat <<EOF > tmpl
76 > changeset = 'nothing expanded:{simple}\n'
76 > changeset = 'nothing expanded:{simple}\n'
77 > EOF
77 > EOF
78 $ hg log -l1 --style ./tmpl
78 $ hg log -l1 --style ./tmpl
79 nothing expanded:
79 nothing expanded:
80
80
81 Test templates and style maps in files:
81 Test templates and style maps in files:
82
82
83 $ echo "{rev}" > tmpl
83 $ echo "{rev}" > tmpl
84 $ hg log -l1 -T./tmpl
84 $ hg log -l1 -T./tmpl
85 8
85 8
86 $ hg log -l1 -Tblah/blah
86 $ hg log -l1 -Tblah/blah
87 blah/blah (no-eol)
87 blah/blah (no-eol)
88
88
89 $ printf 'changeset = "{rev}\\n"\n' > map-simple
89 $ printf 'changeset = "{rev}\\n"\n' > map-simple
90 $ hg log -l1 -T./map-simple
90 $ hg log -l1 -T./map-simple
91 8
91 8
92
92
93 a map file may have [templates] and [templatealias] sections:
93 a map file may have [templates] and [templatealias] sections:
94
94
95 $ cat <<'EOF' > map-simple
95 $ cat <<'EOF' > map-simple
96 > [templates]
96 > [templates]
97 > changeset = "{a}\n"
97 > changeset = "{a}\n"
98 > [templatealias]
98 > [templatealias]
99 > a = rev
99 > a = rev
100 > EOF
100 > EOF
101 $ hg log -l1 -T./map-simple
101 $ hg log -l1 -T./map-simple
102 8
102 8
103
103
104 so it can be included in hgrc
104 so it can be included in hgrc
105
105
106 $ cat <<EOF > myhgrc
106 $ cat <<EOF > myhgrc
107 > %include $HGRCPATH
107 > %include $HGRCPATH
108 > %include map-simple
108 > %include map-simple
109 > [templates]
109 > [templates]
110 > foo = "{changeset}"
110 > foo = "{changeset}"
111 > EOF
111 > EOF
112 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
112 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
113 8
113 8
114 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
114 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
115 8
115 8
116
116
117 Test template map inheritance
117 Test template map inheritance
118
118
119 $ echo "__base__ = map-cmdline.default" > map-simple
119 $ echo "__base__ = map-cmdline.default" > map-simple
120 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
120 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
121 $ hg log -l1 -T./map-simple
121 $ hg log -l1 -T./map-simple
122 changeset: ***8***
122 changeset: ***8***
123 tag: tip
123 tag: tip
124 user: test
124 user: test
125 date: Wed Jan 01 10:01:00 2020 +0000
125 date: Wed Jan 01 10:01:00 2020 +0000
126 summary: third
126 summary: third
127
127
128
128
129 Test docheader, docfooter and separator in template map
129 Test docheader, docfooter and separator in template map
130
130
131 $ cat <<'EOF' > map-myjson
131 $ cat <<'EOF' > map-myjson
132 > docheader = '\{\n'
132 > docheader = '\{\n'
133 > docfooter = '\n}\n'
133 > docfooter = '\n}\n'
134 > separator = ',\n'
134 > separator = ',\n'
135 > changeset = ' {dict(rev, node|short)|json}'
135 > changeset = ' {dict(rev, node|short)|json}'
136 > EOF
136 > EOF
137 $ hg log -l2 -T./map-myjson
137 $ hg log -l2 -T./map-myjson
138 {
138 {
139 {"node": "95c24699272e", "rev": 8},
139 {"node": "95c24699272e", "rev": 8},
140 {"node": "29114dbae42b", "rev": 7}
140 {"node": "29114dbae42b", "rev": 7}
141 }
141 }
142
142
143 Test docheader, docfooter and separator in [templates] section
143 Test docheader, docfooter and separator in [templates] section
144
144
145 $ cat <<'EOF' >> .hg/hgrc
145 $ cat <<'EOF' >> .hg/hgrc
146 > [templates]
146 > [templates]
147 > myjson = ' {dict(rev, node|short)|json}'
147 > myjson = ' {dict(rev, node|short)|json}'
148 > myjson:docheader = '\{\n'
148 > myjson:docheader = '\{\n'
149 > myjson:docfooter = '\n}\n'
149 > myjson:docfooter = '\n}\n'
150 > myjson:separator = ',\n'
150 > myjson:separator = ',\n'
151 > :docheader = 'should not be selected as a docheader for literal templates\n'
151 > :docheader = 'should not be selected as a docheader for literal templates\n'
152 > EOF
152 > EOF
153 $ hg log -l2 -Tmyjson
153 $ hg log -l2 -Tmyjson
154 {
154 {
155 {"node": "95c24699272e", "rev": 8},
155 {"node": "95c24699272e", "rev": 8},
156 {"node": "29114dbae42b", "rev": 7}
156 {"node": "29114dbae42b", "rev": 7}
157 }
157 }
158 $ hg log -l1 -T'{rev}\n'
158 $ hg log -l1 -T'{rev}\n'
159 8
159 8
160
160
161 Template should precede style option
161 Template should precede style option
162
162
163 $ hg log -l1 --style default -T '{rev}\n'
163 $ hg log -l1 --style default -T '{rev}\n'
164 8
164 8
165
165
166 Add a commit with empty description, to ensure that the templates
166 Add a commit with empty description, to ensure that the templates
167 below will omit the description line.
167 below will omit the description line.
168
168
169 $ echo c >> c
169 $ echo c >> c
170 $ hg add c
170 $ hg add c
171 $ hg commit -qm ' '
171 $ hg commit -qm ' '
172
172
173 Default style is like normal output. Phases style should be the same
173 Default style is like normal output. Phases style should be the same
174 as default style, except for extra phase lines.
174 as default style, except for extra phase lines.
175
175
176 $ hg log > log.out
176 $ hg log > log.out
177 $ hg log --style default > style.out
177 $ hg log --style default > style.out
178 $ cmp log.out style.out || diff -u log.out style.out
178 $ cmp log.out style.out || diff -u log.out style.out
179 $ hg log -T phases > phases.out
179 $ hg log -T phases > phases.out
180 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
180 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
181 +phase: draft
181 +phase: draft
182 +phase: draft
182 +phase: draft
183 +phase: draft
183 +phase: draft
184 +phase: draft
184 +phase: draft
185 +phase: draft
185 +phase: draft
186 +phase: draft
186 +phase: draft
187 +phase: draft
187 +phase: draft
188 +phase: draft
188 +phase: draft
189 +phase: draft
189 +phase: draft
190 +phase: draft
190 +phase: draft
191
191
192 $ hg log -v > log.out
192 $ hg log -v > log.out
193 $ hg log -v --style default > style.out
193 $ hg log -v --style default > style.out
194 $ cmp log.out style.out || diff -u log.out style.out
194 $ cmp log.out style.out || diff -u log.out style.out
195 $ hg log -v -T phases > phases.out
195 $ hg log -v -T phases > phases.out
196 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
196 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
197 +phase: draft
197 +phase: draft
198 +phase: draft
198 +phase: draft
199 +phase: draft
199 +phase: draft
200 +phase: draft
200 +phase: draft
201 +phase: draft
201 +phase: draft
202 +phase: draft
202 +phase: draft
203 +phase: draft
203 +phase: draft
204 +phase: draft
204 +phase: draft
205 +phase: draft
205 +phase: draft
206 +phase: draft
206 +phase: draft
207
207
208 $ hg log -q > log.out
208 $ hg log -q > log.out
209 $ hg log -q --style default > style.out
209 $ hg log -q --style default > style.out
210 $ cmp log.out style.out || diff -u log.out style.out
210 $ cmp log.out style.out || diff -u log.out style.out
211 $ hg log -q -T phases > phases.out
211 $ hg log -q -T phases > phases.out
212 $ cmp log.out phases.out || diff -u log.out phases.out
212 $ cmp log.out phases.out || diff -u log.out phases.out
213
213
214 $ hg log --debug > log.out
214 $ hg log --debug > log.out
215 $ hg log --debug --style default > style.out
215 $ hg log --debug --style default > style.out
216 $ cmp log.out style.out || diff -u log.out style.out
216 $ cmp log.out style.out || diff -u log.out style.out
217 $ hg log --debug -T phases > phases.out
217 $ hg log --debug -T phases > phases.out
218 $ cmp log.out phases.out || diff -u log.out phases.out
218 $ cmp log.out phases.out || diff -u log.out phases.out
219
219
220 Default style of working-directory revision should also be the same (but
220 Default style of working-directory revision should also be the same (but
221 date may change while running tests):
221 date may change while running tests):
222
222
223 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
223 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
224 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
224 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
225 $ cmp log.out style.out || diff -u log.out style.out
225 $ cmp log.out style.out || diff -u log.out style.out
226
226
227 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
227 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
228 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
228 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
229 $ cmp log.out style.out || diff -u log.out style.out
229 $ cmp log.out style.out || diff -u log.out style.out
230
230
231 $ hg log -r 'wdir()' -q > log.out
231 $ hg log -r 'wdir()' -q > log.out
232 $ hg log -r 'wdir()' -q --style default > style.out
232 $ hg log -r 'wdir()' -q --style default > style.out
233 $ cmp log.out style.out || diff -u log.out style.out
233 $ cmp log.out style.out || diff -u log.out style.out
234
234
235 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
235 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
236 $ hg log -r 'wdir()' --debug --style default \
236 $ hg log -r 'wdir()' --debug --style default \
237 > | sed 's|^date:.*|date:|' > style.out
237 > | sed 's|^date:.*|date:|' > style.out
238 $ cmp log.out style.out || diff -u log.out style.out
238 $ cmp log.out style.out || diff -u log.out style.out
239
239
240 Default style should also preserve color information (issue2866):
240 Default style should also preserve color information (issue2866):
241
241
242 $ cp $HGRCPATH $HGRCPATH-bak
242 $ cp $HGRCPATH $HGRCPATH-bak
243 $ cat <<EOF >> $HGRCPATH
243 $ cat <<EOF >> $HGRCPATH
244 > [extensions]
244 > [extensions]
245 > color=
245 > color=
246 > EOF
246 > EOF
247
247
248 $ hg --color=debug log > log.out
248 $ hg --color=debug log > log.out
249 $ hg --color=debug log --style default > style.out
249 $ hg --color=debug log --style default > style.out
250 $ cmp log.out style.out || diff -u log.out style.out
250 $ cmp log.out style.out || diff -u log.out style.out
251 $ hg --color=debug log -T phases > phases.out
251 $ hg --color=debug log -T phases > phases.out
252 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
252 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
253 +[log.phase|phase: draft]
253 +[log.phase|phase: draft]
254 +[log.phase|phase: draft]
254 +[log.phase|phase: draft]
255 +[log.phase|phase: draft]
255 +[log.phase|phase: draft]
256 +[log.phase|phase: draft]
256 +[log.phase|phase: draft]
257 +[log.phase|phase: draft]
257 +[log.phase|phase: draft]
258 +[log.phase|phase: draft]
258 +[log.phase|phase: draft]
259 +[log.phase|phase: draft]
259 +[log.phase|phase: draft]
260 +[log.phase|phase: draft]
260 +[log.phase|phase: draft]
261 +[log.phase|phase: draft]
261 +[log.phase|phase: draft]
262 +[log.phase|phase: draft]
262 +[log.phase|phase: draft]
263
263
264 $ hg --color=debug -v log > log.out
264 $ hg --color=debug -v log > log.out
265 $ hg --color=debug -v log --style default > style.out
265 $ hg --color=debug -v log --style default > style.out
266 $ cmp log.out style.out || diff -u log.out style.out
266 $ cmp log.out style.out || diff -u log.out style.out
267 $ hg --color=debug -v log -T phases > phases.out
267 $ hg --color=debug -v log -T phases > phases.out
268 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
268 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
269 +[log.phase|phase: draft]
269 +[log.phase|phase: draft]
270 +[log.phase|phase: draft]
270 +[log.phase|phase: draft]
271 +[log.phase|phase: draft]
271 +[log.phase|phase: draft]
272 +[log.phase|phase: draft]
272 +[log.phase|phase: draft]
273 +[log.phase|phase: draft]
273 +[log.phase|phase: draft]
274 +[log.phase|phase: draft]
274 +[log.phase|phase: draft]
275 +[log.phase|phase: draft]
275 +[log.phase|phase: draft]
276 +[log.phase|phase: draft]
276 +[log.phase|phase: draft]
277 +[log.phase|phase: draft]
277 +[log.phase|phase: draft]
278 +[log.phase|phase: draft]
278 +[log.phase|phase: draft]
279
279
280 $ hg --color=debug -q log > log.out
280 $ hg --color=debug -q log > log.out
281 $ hg --color=debug -q log --style default > style.out
281 $ hg --color=debug -q log --style default > style.out
282 $ cmp log.out style.out || diff -u log.out style.out
282 $ cmp log.out style.out || diff -u log.out style.out
283 $ hg --color=debug -q log -T phases > phases.out
283 $ hg --color=debug -q log -T phases > phases.out
284 $ cmp log.out phases.out || diff -u log.out phases.out
284 $ cmp log.out phases.out || diff -u log.out phases.out
285
285
286 $ hg --color=debug --debug log > log.out
286 $ hg --color=debug --debug log > log.out
287 $ hg --color=debug --debug log --style default > style.out
287 $ hg --color=debug --debug log --style default > style.out
288 $ cmp log.out style.out || diff -u log.out style.out
288 $ cmp log.out style.out || diff -u log.out style.out
289 $ hg --color=debug --debug log -T phases > phases.out
289 $ hg --color=debug --debug log -T phases > phases.out
290 $ cmp log.out phases.out || diff -u log.out phases.out
290 $ cmp log.out phases.out || diff -u log.out phases.out
291
291
292 $ mv $HGRCPATH-bak $HGRCPATH
292 $ mv $HGRCPATH-bak $HGRCPATH
293
293
294 Remove commit with empty commit message, so as to not pollute further
294 Remove commit with empty commit message, so as to not pollute further
295 tests.
295 tests.
296
296
297 $ hg --config extensions.strip= strip -q .
297 $ hg --config extensions.strip= strip -q .
298
298
299 Revision with no copies (used to print a traceback):
299 Revision with no copies (used to print a traceback):
300
300
301 $ hg tip -v --template '\n'
301 $ hg tip -v --template '\n'
302
302
303
303
304 Compact style works:
304 Compact style works:
305
305
306 $ hg log -Tcompact
306 $ hg log -Tcompact
307 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
307 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
308 third
308 third
309
309
310 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
310 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
311 second
311 second
312
312
313 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
313 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
314 merge
314 merge
315
315
316 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
316 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
317 new head
317 new head
318
318
319 4 bbe44766e73d 1970-01-17 04:53 +0000 person
319 4 bbe44766e73d 1970-01-17 04:53 +0000 person
320 new branch
320 new branch
321
321
322 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
322 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
323 no user, no domain
323 no user, no domain
324
324
325 2 97054abb4ab8 1970-01-14 21:20 +0000 other
325 2 97054abb4ab8 1970-01-14 21:20 +0000 other
326 no person
326 no person
327
327
328 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
328 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
329 other 1
329 other 1
330
330
331 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
331 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
332 line 1
332 line 1
333
333
334
334
335 $ hg log -v --style compact
335 $ hg log -v --style compact
336 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
336 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
337 third
337 third
338
338
339 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
339 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
340 second
340 second
341
341
342 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
342 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
343 merge
343 merge
344
344
345 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
345 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
346 new head
346 new head
347
347
348 4 bbe44766e73d 1970-01-17 04:53 +0000 person
348 4 bbe44766e73d 1970-01-17 04:53 +0000 person
349 new branch
349 new branch
350
350
351 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
351 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
352 no user, no domain
352 no user, no domain
353
353
354 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
354 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
355 no person
355 no person
356
356
357 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
357 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
358 other 1
358 other 1
359 other 2
359 other 2
360
360
361 other 3
361 other 3
362
362
363 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
363 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
364 line 1
364 line 1
365 line 2
365 line 2
366
366
367
367
368 $ hg log --debug --style compact
368 $ hg log --debug --style compact
369 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
369 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
370 third
370 third
371
371
372 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
372 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
373 second
373 second
374
374
375 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
375 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
376 merge
376 merge
377
377
378 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
378 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
379 new head
379 new head
380
380
381 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
381 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
382 new branch
382 new branch
383
383
384 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
384 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
385 no user, no domain
385 no user, no domain
386
386
387 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
387 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
388 no person
388 no person
389
389
390 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
390 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
391 other 1
391 other 1
392 other 2
392 other 2
393
393
394 other 3
394 other 3
395
395
396 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
396 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
397 line 1
397 line 1
398 line 2
398 line 2
399
399
400
400
401 Test xml styles:
401 Test xml styles:
402
402
403 $ hg log --style xml -r 'not all()'
403 $ hg log --style xml -r 'not all()'
404 <?xml version="1.0"?>
404 <?xml version="1.0"?>
405 <log>
405 <log>
406 </log>
406 </log>
407
407
408 $ hg log --style xml
408 $ hg log --style xml
409 <?xml version="1.0"?>
409 <?xml version="1.0"?>
410 <log>
410 <log>
411 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
411 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
412 <tag>tip</tag>
412 <tag>tip</tag>
413 <author email="test">test</author>
413 <author email="test">test</author>
414 <date>2020-01-01T10:01:00+00:00</date>
414 <date>2020-01-01T10:01:00+00:00</date>
415 <msg xml:space="preserve">third</msg>
415 <msg xml:space="preserve">third</msg>
416 </logentry>
416 </logentry>
417 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
417 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
418 <parent revision="-1" node="0000000000000000000000000000000000000000" />
418 <parent revision="-1" node="0000000000000000000000000000000000000000" />
419 <author email="user@hostname">User Name</author>
419 <author email="user@hostname">User Name</author>
420 <date>1970-01-12T13:46:40+00:00</date>
420 <date>1970-01-12T13:46:40+00:00</date>
421 <msg xml:space="preserve">second</msg>
421 <msg xml:space="preserve">second</msg>
422 </logentry>
422 </logentry>
423 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
423 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
424 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
424 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
425 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
425 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
426 <author email="person">person</author>
426 <author email="person">person</author>
427 <date>1970-01-18T08:40:01+00:00</date>
427 <date>1970-01-18T08:40:01+00:00</date>
428 <msg xml:space="preserve">merge</msg>
428 <msg xml:space="preserve">merge</msg>
429 </logentry>
429 </logentry>
430 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
430 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
431 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
431 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
432 <author email="person">person</author>
432 <author email="person">person</author>
433 <date>1970-01-18T08:40:00+00:00</date>
433 <date>1970-01-18T08:40:00+00:00</date>
434 <msg xml:space="preserve">new head</msg>
434 <msg xml:space="preserve">new head</msg>
435 </logentry>
435 </logentry>
436 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
436 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
437 <branch>foo</branch>
437 <branch>foo</branch>
438 <author email="person">person</author>
438 <author email="person">person</author>
439 <date>1970-01-17T04:53:20+00:00</date>
439 <date>1970-01-17T04:53:20+00:00</date>
440 <msg xml:space="preserve">new branch</msg>
440 <msg xml:space="preserve">new branch</msg>
441 </logentry>
441 </logentry>
442 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
442 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
443 <author email="person">person</author>
443 <author email="person">person</author>
444 <date>1970-01-16T01:06:40+00:00</date>
444 <date>1970-01-16T01:06:40+00:00</date>
445 <msg xml:space="preserve">no user, no domain</msg>
445 <msg xml:space="preserve">no user, no domain</msg>
446 </logentry>
446 </logentry>
447 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
447 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
448 <author email="other@place">other</author>
448 <author email="other@place">other</author>
449 <date>1970-01-14T21:20:00+00:00</date>
449 <date>1970-01-14T21:20:00+00:00</date>
450 <msg xml:space="preserve">no person</msg>
450 <msg xml:space="preserve">no person</msg>
451 </logentry>
451 </logentry>
452 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
452 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
453 <author email="other@place">A. N. Other</author>
453 <author email="other@place">A. N. Other</author>
454 <date>1970-01-13T17:33:20+00:00</date>
454 <date>1970-01-13T17:33:20+00:00</date>
455 <msg xml:space="preserve">other 1
455 <msg xml:space="preserve">other 1
456 other 2
456 other 2
457
457
458 other 3</msg>
458 other 3</msg>
459 </logentry>
459 </logentry>
460 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
460 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
461 <author email="user@hostname">User Name</author>
461 <author email="user@hostname">User Name</author>
462 <date>1970-01-12T13:46:40+00:00</date>
462 <date>1970-01-12T13:46:40+00:00</date>
463 <msg xml:space="preserve">line 1
463 <msg xml:space="preserve">line 1
464 line 2</msg>
464 line 2</msg>
465 </logentry>
465 </logentry>
466 </log>
466 </log>
467
467
468 $ hg log -v --style xml
468 $ hg log -v --style xml
469 <?xml version="1.0"?>
469 <?xml version="1.0"?>
470 <log>
470 <log>
471 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
471 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
472 <tag>tip</tag>
472 <tag>tip</tag>
473 <author email="test">test</author>
473 <author email="test">test</author>
474 <date>2020-01-01T10:01:00+00:00</date>
474 <date>2020-01-01T10:01:00+00:00</date>
475 <msg xml:space="preserve">third</msg>
475 <msg xml:space="preserve">third</msg>
476 <paths>
476 <paths>
477 <path action="A">fourth</path>
477 <path action="A">fourth</path>
478 <path action="A">third</path>
478 <path action="A">third</path>
479 <path action="R">second</path>
479 <path action="R">second</path>
480 </paths>
480 </paths>
481 <copies>
481 <copies>
482 <copy source="second">fourth</copy>
482 <copy source="second">fourth</copy>
483 </copies>
483 </copies>
484 </logentry>
484 </logentry>
485 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
485 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
486 <parent revision="-1" node="0000000000000000000000000000000000000000" />
486 <parent revision="-1" node="0000000000000000000000000000000000000000" />
487 <author email="user@hostname">User Name</author>
487 <author email="user@hostname">User Name</author>
488 <date>1970-01-12T13:46:40+00:00</date>
488 <date>1970-01-12T13:46:40+00:00</date>
489 <msg xml:space="preserve">second</msg>
489 <msg xml:space="preserve">second</msg>
490 <paths>
490 <paths>
491 <path action="A">second</path>
491 <path action="A">second</path>
492 </paths>
492 </paths>
493 </logentry>
493 </logentry>
494 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
494 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
495 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
495 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
496 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
496 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
497 <author email="person">person</author>
497 <author email="person">person</author>
498 <date>1970-01-18T08:40:01+00:00</date>
498 <date>1970-01-18T08:40:01+00:00</date>
499 <msg xml:space="preserve">merge</msg>
499 <msg xml:space="preserve">merge</msg>
500 <paths>
500 <paths>
501 </paths>
501 </paths>
502 </logentry>
502 </logentry>
503 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
503 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
504 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
504 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
505 <author email="person">person</author>
505 <author email="person">person</author>
506 <date>1970-01-18T08:40:00+00:00</date>
506 <date>1970-01-18T08:40:00+00:00</date>
507 <msg xml:space="preserve">new head</msg>
507 <msg xml:space="preserve">new head</msg>
508 <paths>
508 <paths>
509 <path action="A">d</path>
509 <path action="A">d</path>
510 </paths>
510 </paths>
511 </logentry>
511 </logentry>
512 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
512 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
513 <branch>foo</branch>
513 <branch>foo</branch>
514 <author email="person">person</author>
514 <author email="person">person</author>
515 <date>1970-01-17T04:53:20+00:00</date>
515 <date>1970-01-17T04:53:20+00:00</date>
516 <msg xml:space="preserve">new branch</msg>
516 <msg xml:space="preserve">new branch</msg>
517 <paths>
517 <paths>
518 </paths>
518 </paths>
519 </logentry>
519 </logentry>
520 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
520 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
521 <author email="person">person</author>
521 <author email="person">person</author>
522 <date>1970-01-16T01:06:40+00:00</date>
522 <date>1970-01-16T01:06:40+00:00</date>
523 <msg xml:space="preserve">no user, no domain</msg>
523 <msg xml:space="preserve">no user, no domain</msg>
524 <paths>
524 <paths>
525 <path action="M">c</path>
525 <path action="M">c</path>
526 </paths>
526 </paths>
527 </logentry>
527 </logentry>
528 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
528 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
529 <author email="other@place">other</author>
529 <author email="other@place">other</author>
530 <date>1970-01-14T21:20:00+00:00</date>
530 <date>1970-01-14T21:20:00+00:00</date>
531 <msg xml:space="preserve">no person</msg>
531 <msg xml:space="preserve">no person</msg>
532 <paths>
532 <paths>
533 <path action="A">c</path>
533 <path action="A">c</path>
534 </paths>
534 </paths>
535 </logentry>
535 </logentry>
536 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
536 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
537 <author email="other@place">A. N. Other</author>
537 <author email="other@place">A. N. Other</author>
538 <date>1970-01-13T17:33:20+00:00</date>
538 <date>1970-01-13T17:33:20+00:00</date>
539 <msg xml:space="preserve">other 1
539 <msg xml:space="preserve">other 1
540 other 2
540 other 2
541
541
542 other 3</msg>
542 other 3</msg>
543 <paths>
543 <paths>
544 <path action="A">b</path>
544 <path action="A">b</path>
545 </paths>
545 </paths>
546 </logentry>
546 </logentry>
547 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
547 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
548 <author email="user@hostname">User Name</author>
548 <author email="user@hostname">User Name</author>
549 <date>1970-01-12T13:46:40+00:00</date>
549 <date>1970-01-12T13:46:40+00:00</date>
550 <msg xml:space="preserve">line 1
550 <msg xml:space="preserve">line 1
551 line 2</msg>
551 line 2</msg>
552 <paths>
552 <paths>
553 <path action="A">a</path>
553 <path action="A">a</path>
554 </paths>
554 </paths>
555 </logentry>
555 </logentry>
556 </log>
556 </log>
557
557
558 $ hg log --debug --style xml
558 $ hg log --debug --style xml
559 <?xml version="1.0"?>
559 <?xml version="1.0"?>
560 <log>
560 <log>
561 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
561 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
562 <tag>tip</tag>
562 <tag>tip</tag>
563 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
563 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
564 <parent revision="-1" node="0000000000000000000000000000000000000000" />
564 <parent revision="-1" node="0000000000000000000000000000000000000000" />
565 <author email="test">test</author>
565 <author email="test">test</author>
566 <date>2020-01-01T10:01:00+00:00</date>
566 <date>2020-01-01T10:01:00+00:00</date>
567 <msg xml:space="preserve">third</msg>
567 <msg xml:space="preserve">third</msg>
568 <paths>
568 <paths>
569 <path action="A">fourth</path>
569 <path action="A">fourth</path>
570 <path action="A">third</path>
570 <path action="A">third</path>
571 <path action="R">second</path>
571 <path action="R">second</path>
572 </paths>
572 </paths>
573 <copies>
573 <copies>
574 <copy source="second">fourth</copy>
574 <copy source="second">fourth</copy>
575 </copies>
575 </copies>
576 <extra key="branch">default</extra>
576 <extra key="branch">default</extra>
577 </logentry>
577 </logentry>
578 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
578 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
579 <parent revision="-1" node="0000000000000000000000000000000000000000" />
579 <parent revision="-1" node="0000000000000000000000000000000000000000" />
580 <parent revision="-1" node="0000000000000000000000000000000000000000" />
580 <parent revision="-1" node="0000000000000000000000000000000000000000" />
581 <author email="user@hostname">User Name</author>
581 <author email="user@hostname">User Name</author>
582 <date>1970-01-12T13:46:40+00:00</date>
582 <date>1970-01-12T13:46:40+00:00</date>
583 <msg xml:space="preserve">second</msg>
583 <msg xml:space="preserve">second</msg>
584 <paths>
584 <paths>
585 <path action="A">second</path>
585 <path action="A">second</path>
586 </paths>
586 </paths>
587 <extra key="branch">default</extra>
587 <extra key="branch">default</extra>
588 </logentry>
588 </logentry>
589 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
589 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
590 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
590 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
591 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
591 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
592 <author email="person">person</author>
592 <author email="person">person</author>
593 <date>1970-01-18T08:40:01+00:00</date>
593 <date>1970-01-18T08:40:01+00:00</date>
594 <msg xml:space="preserve">merge</msg>
594 <msg xml:space="preserve">merge</msg>
595 <paths>
595 <paths>
596 </paths>
596 </paths>
597 <extra key="branch">default</extra>
597 <extra key="branch">default</extra>
598 </logentry>
598 </logentry>
599 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
599 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
600 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
600 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
601 <parent revision="-1" node="0000000000000000000000000000000000000000" />
601 <parent revision="-1" node="0000000000000000000000000000000000000000" />
602 <author email="person">person</author>
602 <author email="person">person</author>
603 <date>1970-01-18T08:40:00+00:00</date>
603 <date>1970-01-18T08:40:00+00:00</date>
604 <msg xml:space="preserve">new head</msg>
604 <msg xml:space="preserve">new head</msg>
605 <paths>
605 <paths>
606 <path action="A">d</path>
606 <path action="A">d</path>
607 </paths>
607 </paths>
608 <extra key="branch">default</extra>
608 <extra key="branch">default</extra>
609 </logentry>
609 </logentry>
610 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
610 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
611 <branch>foo</branch>
611 <branch>foo</branch>
612 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
612 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
613 <parent revision="-1" node="0000000000000000000000000000000000000000" />
613 <parent revision="-1" node="0000000000000000000000000000000000000000" />
614 <author email="person">person</author>
614 <author email="person">person</author>
615 <date>1970-01-17T04:53:20+00:00</date>
615 <date>1970-01-17T04:53:20+00:00</date>
616 <msg xml:space="preserve">new branch</msg>
616 <msg xml:space="preserve">new branch</msg>
617 <paths>
617 <paths>
618 </paths>
618 </paths>
619 <extra key="branch">foo</extra>
619 <extra key="branch">foo</extra>
620 </logentry>
620 </logentry>
621 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
621 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
622 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
622 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
623 <parent revision="-1" node="0000000000000000000000000000000000000000" />
623 <parent revision="-1" node="0000000000000000000000000000000000000000" />
624 <author email="person">person</author>
624 <author email="person">person</author>
625 <date>1970-01-16T01:06:40+00:00</date>
625 <date>1970-01-16T01:06:40+00:00</date>
626 <msg xml:space="preserve">no user, no domain</msg>
626 <msg xml:space="preserve">no user, no domain</msg>
627 <paths>
627 <paths>
628 <path action="M">c</path>
628 <path action="M">c</path>
629 </paths>
629 </paths>
630 <extra key="branch">default</extra>
630 <extra key="branch">default</extra>
631 </logentry>
631 </logentry>
632 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
632 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
633 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
633 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
634 <parent revision="-1" node="0000000000000000000000000000000000000000" />
634 <parent revision="-1" node="0000000000000000000000000000000000000000" />
635 <author email="other@place">other</author>
635 <author email="other@place">other</author>
636 <date>1970-01-14T21:20:00+00:00</date>
636 <date>1970-01-14T21:20:00+00:00</date>
637 <msg xml:space="preserve">no person</msg>
637 <msg xml:space="preserve">no person</msg>
638 <paths>
638 <paths>
639 <path action="A">c</path>
639 <path action="A">c</path>
640 </paths>
640 </paths>
641 <extra key="branch">default</extra>
641 <extra key="branch">default</extra>
642 </logentry>
642 </logentry>
643 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
643 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
644 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
644 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
645 <parent revision="-1" node="0000000000000000000000000000000000000000" />
645 <parent revision="-1" node="0000000000000000000000000000000000000000" />
646 <author email="other@place">A. N. Other</author>
646 <author email="other@place">A. N. Other</author>
647 <date>1970-01-13T17:33:20+00:00</date>
647 <date>1970-01-13T17:33:20+00:00</date>
648 <msg xml:space="preserve">other 1
648 <msg xml:space="preserve">other 1
649 other 2
649 other 2
650
650
651 other 3</msg>
651 other 3</msg>
652 <paths>
652 <paths>
653 <path action="A">b</path>
653 <path action="A">b</path>
654 </paths>
654 </paths>
655 <extra key="branch">default</extra>
655 <extra key="branch">default</extra>
656 </logentry>
656 </logentry>
657 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
657 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
658 <parent revision="-1" node="0000000000000000000000000000000000000000" />
658 <parent revision="-1" node="0000000000000000000000000000000000000000" />
659 <parent revision="-1" node="0000000000000000000000000000000000000000" />
659 <parent revision="-1" node="0000000000000000000000000000000000000000" />
660 <author email="user@hostname">User Name</author>
660 <author email="user@hostname">User Name</author>
661 <date>1970-01-12T13:46:40+00:00</date>
661 <date>1970-01-12T13:46:40+00:00</date>
662 <msg xml:space="preserve">line 1
662 <msg xml:space="preserve">line 1
663 line 2</msg>
663 line 2</msg>
664 <paths>
664 <paths>
665 <path action="A">a</path>
665 <path action="A">a</path>
666 </paths>
666 </paths>
667 <extra key="branch">default</extra>
667 <extra key="branch">default</extra>
668 </logentry>
668 </logentry>
669 </log>
669 </log>
670
670
671
671
672 test CBOR style:
672 test CBOR style:
673
673
674 $ cat <<'EOF' > "$TESTTMP/decodecborarray.py"
674 $ cat <<'EOF' > "$TESTTMP/decodecborarray.py"
675 > from __future__ import absolute_import
675 > from __future__ import absolute_import
676 > from mercurial import (
676 > from mercurial import (
677 > dispatch,
677 > dispatch,
678 > pycompat,
678 > pycompat,
679 > )
679 > )
680 > from mercurial.utils import (
680 > from mercurial.utils import (
681 > cborutil,
681 > cborutil,
682 > stringutil,
682 > stringutil,
683 > )
683 > )
684 > dispatch.initstdio()
684 > dispatch.initstdio()
685 > data = pycompat.stdin.read()
685 > data = pycompat.stdin.read()
686 > # our CBOR decoder doesn't support parsing indefinite-length arrays,
686 > # our CBOR decoder doesn't support parsing indefinite-length arrays,
687 > # but the log output is indefinite stream by nature.
687 > # but the log output is indefinite stream by nature.
688 > assert data[:1] == cborutil.BEGIN_INDEFINITE_ARRAY
688 > assert data[:1] == cborutil.BEGIN_INDEFINITE_ARRAY
689 > assert data[-1:] == cborutil.BREAK
689 > assert data[-1:] == cborutil.BREAK
690 > items = cborutil.decodeall(data[1:-1])
690 > items = cborutil.decodeall(data[1:-1])
691 > pycompat.stdout.write(stringutil.pprint(items, indent=1) + b'\n')
691 > pycompat.stdout.write(stringutil.pprint(items, indent=1) + b'\n')
692 > EOF
692 > EOF
693
693
694 $ hg log -k nosuch -Tcbor | "$PYTHON" "$TESTTMP/decodecborarray.py"
694 $ hg log -k nosuch -Tcbor | "$PYTHON" "$TESTTMP/decodecborarray.py"
695 []
695 []
696
696
697 $ hg log -qr0:1 -Tcbor | "$PYTHON" "$TESTTMP/decodecborarray.py"
697 $ hg log -qr0:1 -Tcbor | "$PYTHON" "$TESTTMP/decodecborarray.py"
698 [
698 [
699 {
699 {
700 'node': '1e4e1b8f71e05681d422154f5421e385fec3454f',
700 'node': '1e4e1b8f71e05681d422154f5421e385fec3454f',
701 'rev': 0
701 'rev': 0
702 },
702 },
703 {
703 {
704 'node': 'b608e9d1a3f0273ccf70fb85fd6866b3482bf965',
704 'node': 'b608e9d1a3f0273ccf70fb85fd6866b3482bf965',
705 'rev': 1
705 'rev': 1
706 }
706 }
707 ]
707 ]
708
708
709 $ hg log -vpr . -Tcbor --stat | "$PYTHON" "$TESTTMP/decodecborarray.py"
709 $ hg log -vpr . -Tcbor --stat | "$PYTHON" "$TESTTMP/decodecborarray.py"
710 [
710 [
711 {
711 {
712 'bookmarks': [],
712 'bookmarks': [],
713 'branch': 'default',
713 'branch': 'default',
714 'date': [
714 'date': [
715 1577872860,
715 1577872860,
716 0
716 0
717 ],
717 ],
718 'desc': 'third',
718 'desc': 'third',
719 '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',
719 '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',
720 'diffstat': ' fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n',
720 'diffstat': ' fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n',
721 'files': [
721 'files': [
722 'fourth',
722 'fourth',
723 'second',
723 'second',
724 'third'
724 'third'
725 ],
725 ],
726 'node': '95c24699272ef57d062b8bccc32c878bf841784a',
726 'node': '95c24699272ef57d062b8bccc32c878bf841784a',
727 'parents': [
727 'parents': [
728 '29114dbae42b9f078cf2714dbe3a86bba8ec7453'
728 '29114dbae42b9f078cf2714dbe3a86bba8ec7453'
729 ],
729 ],
730 'phase': 'draft',
730 'phase': 'draft',
731 'rev': 8,
731 'rev': 8,
732 'tags': [
732 'tags': [
733 'tip'
733 'tip'
734 ],
734 ],
735 'user': 'test'
735 'user': 'test'
736 }
736 }
737 ]
737 ]
738
738
739
739
740 Test JSON style:
740 Test JSON style:
741
741
742 $ hg log -k nosuch -Tjson
742 $ hg log -k nosuch -Tjson
743 [
743 [
744 ]
744 ]
745
745
746 $ hg log -qr . -Tjson
746 $ hg log -qr . -Tjson
747 [
747 [
748 {
748 {
749 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
749 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
750 "rev": 8
750 "rev": 8
751 }
751 }
752 ]
752 ]
753
753
754 $ hg log -vpr . -Tjson --stat
754 $ hg log -vpr . -Tjson --stat
755 [
755 [
756 {
756 {
757 "bookmarks": [],
757 "bookmarks": [],
758 "branch": "default",
758 "branch": "default",
759 "date": [1577872860, 0],
759 "date": [1577872860, 0],
760 "desc": "third",
760 "desc": "third",
761 "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",
761 "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",
762 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
762 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
763 "files": ["fourth", "second", "third"],
763 "files": ["fourth", "second", "third"],
764 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
764 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
765 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
765 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
766 "phase": "draft",
766 "phase": "draft",
767 "rev": 8,
767 "rev": 8,
768 "tags": ["tip"],
768 "tags": ["tip"],
769 "user": "test"
769 "user": "test"
770 }
770 }
771 ]
771 ]
772
772
773 honor --git but not format-breaking diffopts
773 honor --git but not format-breaking diffopts
774 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
774 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
775 [
775 [
776 {
776 {
777 "bookmarks": [],
777 "bookmarks": [],
778 "branch": "default",
778 "branch": "default",
779 "date": [1577872860, 0],
779 "date": [1577872860, 0],
780 "desc": "third",
780 "desc": "third",
781 "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",
781 "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",
782 "files": ["fourth", "second", "third"],
782 "files": ["fourth", "second", "third"],
783 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
783 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
784 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
784 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
785 "phase": "draft",
785 "phase": "draft",
786 "rev": 8,
786 "rev": 8,
787 "tags": ["tip"],
787 "tags": ["tip"],
788 "user": "test"
788 "user": "test"
789 }
789 }
790 ]
790 ]
791
791
792 $ hg log -T json
792 $ hg log -T json
793 [
793 [
794 {
794 {
795 "bookmarks": [],
795 "bookmarks": [],
796 "branch": "default",
796 "branch": "default",
797 "date": [1577872860, 0],
797 "date": [1577872860, 0],
798 "desc": "third",
798 "desc": "third",
799 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
799 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
800 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
800 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
801 "phase": "draft",
801 "phase": "draft",
802 "rev": 8,
802 "rev": 8,
803 "tags": ["tip"],
803 "tags": ["tip"],
804 "user": "test"
804 "user": "test"
805 },
805 },
806 {
806 {
807 "bookmarks": [],
807 "bookmarks": [],
808 "branch": "default",
808 "branch": "default",
809 "date": [1000000, 0],
809 "date": [1000000, 0],
810 "desc": "second",
810 "desc": "second",
811 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
811 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
812 "parents": ["0000000000000000000000000000000000000000"],
812 "parents": ["0000000000000000000000000000000000000000"],
813 "phase": "draft",
813 "phase": "draft",
814 "rev": 7,
814 "rev": 7,
815 "tags": [],
815 "tags": [],
816 "user": "User Name <user@hostname>"
816 "user": "User Name <user@hostname>"
817 },
817 },
818 {
818 {
819 "bookmarks": [],
819 "bookmarks": [],
820 "branch": "default",
820 "branch": "default",
821 "date": [1500001, 0],
821 "date": [1500001, 0],
822 "desc": "merge",
822 "desc": "merge",
823 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
823 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
824 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
824 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
825 "phase": "draft",
825 "phase": "draft",
826 "rev": 6,
826 "rev": 6,
827 "tags": [],
827 "tags": [],
828 "user": "person"
828 "user": "person"
829 },
829 },
830 {
830 {
831 "bookmarks": [],
831 "bookmarks": [],
832 "branch": "default",
832 "branch": "default",
833 "date": [1500000, 0],
833 "date": [1500000, 0],
834 "desc": "new head",
834 "desc": "new head",
835 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
835 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
836 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
836 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
837 "phase": "draft",
837 "phase": "draft",
838 "rev": 5,
838 "rev": 5,
839 "tags": [],
839 "tags": [],
840 "user": "person"
840 "user": "person"
841 },
841 },
842 {
842 {
843 "bookmarks": [],
843 "bookmarks": [],
844 "branch": "foo",
844 "branch": "foo",
845 "date": [1400000, 0],
845 "date": [1400000, 0],
846 "desc": "new branch",
846 "desc": "new branch",
847 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
847 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
848 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
848 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
849 "phase": "draft",
849 "phase": "draft",
850 "rev": 4,
850 "rev": 4,
851 "tags": [],
851 "tags": [],
852 "user": "person"
852 "user": "person"
853 },
853 },
854 {
854 {
855 "bookmarks": [],
855 "bookmarks": [],
856 "branch": "default",
856 "branch": "default",
857 "date": [1300000, 0],
857 "date": [1300000, 0],
858 "desc": "no user, no domain",
858 "desc": "no user, no domain",
859 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
859 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
860 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
860 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
861 "phase": "draft",
861 "phase": "draft",
862 "rev": 3,
862 "rev": 3,
863 "tags": [],
863 "tags": [],
864 "user": "person"
864 "user": "person"
865 },
865 },
866 {
866 {
867 "bookmarks": [],
867 "bookmarks": [],
868 "branch": "default",
868 "branch": "default",
869 "date": [1200000, 0],
869 "date": [1200000, 0],
870 "desc": "no person",
870 "desc": "no person",
871 "node": "97054abb4ab824450e9164180baf491ae0078465",
871 "node": "97054abb4ab824450e9164180baf491ae0078465",
872 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
872 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
873 "phase": "draft",
873 "phase": "draft",
874 "rev": 2,
874 "rev": 2,
875 "tags": [],
875 "tags": [],
876 "user": "other@place"
876 "user": "other@place"
877 },
877 },
878 {
878 {
879 "bookmarks": [],
879 "bookmarks": [],
880 "branch": "default",
880 "branch": "default",
881 "date": [1100000, 0],
881 "date": [1100000, 0],
882 "desc": "other 1\nother 2\n\nother 3",
882 "desc": "other 1\nother 2\n\nother 3",
883 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
883 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
884 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
884 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
885 "phase": "draft",
885 "phase": "draft",
886 "rev": 1,
886 "rev": 1,
887 "tags": [],
887 "tags": [],
888 "user": "A. N. Other <other@place>"
888 "user": "A. N. Other <other@place>"
889 },
889 },
890 {
890 {
891 "bookmarks": [],
891 "bookmarks": [],
892 "branch": "default",
892 "branch": "default",
893 "date": [1000000, 0],
893 "date": [1000000, 0],
894 "desc": "line 1\nline 2",
894 "desc": "line 1\nline 2",
895 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
895 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
896 "parents": ["0000000000000000000000000000000000000000"],
896 "parents": ["0000000000000000000000000000000000000000"],
897 "phase": "draft",
897 "phase": "draft",
898 "rev": 0,
898 "rev": 0,
899 "tags": [],
899 "tags": [],
900 "user": "User Name <user@hostname>"
900 "user": "User Name <user@hostname>"
901 }
901 }
902 ]
902 ]
903
903
904 $ hg heads -v -Tjson
904 $ hg heads -v -Tjson
905 [
905 [
906 {
906 {
907 "bookmarks": [],
907 "bookmarks": [],
908 "branch": "default",
908 "branch": "default",
909 "date": [1577872860, 0],
909 "date": [1577872860, 0],
910 "desc": "third",
910 "desc": "third",
911 "files": ["fourth", "second", "third"],
911 "files": ["fourth", "second", "third"],
912 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
912 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
913 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
913 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
914 "phase": "draft",
914 "phase": "draft",
915 "rev": 8,
915 "rev": 8,
916 "tags": ["tip"],
916 "tags": ["tip"],
917 "user": "test"
917 "user": "test"
918 },
918 },
919 {
919 {
920 "bookmarks": [],
920 "bookmarks": [],
921 "branch": "default",
921 "branch": "default",
922 "date": [1500001, 0],
922 "date": [1500001, 0],
923 "desc": "merge",
923 "desc": "merge",
924 "files": [],
924 "files": [],
925 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
925 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
926 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
926 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
927 "phase": "draft",
927 "phase": "draft",
928 "rev": 6,
928 "rev": 6,
929 "tags": [],
929 "tags": [],
930 "user": "person"
930 "user": "person"
931 },
931 },
932 {
932 {
933 "bookmarks": [],
933 "bookmarks": [],
934 "branch": "foo",
934 "branch": "foo",
935 "date": [1400000, 0],
935 "date": [1400000, 0],
936 "desc": "new branch",
936 "desc": "new branch",
937 "files": [],
937 "files": [],
938 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
938 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
939 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
939 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
940 "phase": "draft",
940 "phase": "draft",
941 "rev": 4,
941 "rev": 4,
942 "tags": [],
942 "tags": [],
943 "user": "person"
943 "user": "person"
944 }
944 }
945 ]
945 ]
946
946
947 $ hg log --debug -Tjson
947 $ hg log --debug -Tjson
948 [
948 [
949 {
949 {
950 "added": ["fourth", "third"],
950 "added": ["fourth", "third"],
951 "bookmarks": [],
951 "bookmarks": [],
952 "branch": "default",
952 "branch": "default",
953 "date": [1577872860, 0],
953 "date": [1577872860, 0],
954 "desc": "third",
954 "desc": "third",
955 "extra": {"branch": "default"},
955 "extra": {"branch": "default"},
956 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
956 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
957 "modified": [],
957 "modified": [],
958 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
958 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
959 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
959 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
960 "phase": "draft",
960 "phase": "draft",
961 "removed": ["second"],
961 "removed": ["second"],
962 "rev": 8,
962 "rev": 8,
963 "tags": ["tip"],
963 "tags": ["tip"],
964 "user": "test"
964 "user": "test"
965 },
965 },
966 {
966 {
967 "added": ["second"],
967 "added": ["second"],
968 "bookmarks": [],
968 "bookmarks": [],
969 "branch": "default",
969 "branch": "default",
970 "date": [1000000, 0],
970 "date": [1000000, 0],
971 "desc": "second",
971 "desc": "second",
972 "extra": {"branch": "default"},
972 "extra": {"branch": "default"},
973 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
973 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
974 "modified": [],
974 "modified": [],
975 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
975 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
976 "parents": ["0000000000000000000000000000000000000000"],
976 "parents": ["0000000000000000000000000000000000000000"],
977 "phase": "draft",
977 "phase": "draft",
978 "removed": [],
978 "removed": [],
979 "rev": 7,
979 "rev": 7,
980 "tags": [],
980 "tags": [],
981 "user": "User Name <user@hostname>"
981 "user": "User Name <user@hostname>"
982 },
982 },
983 {
983 {
984 "added": [],
984 "added": [],
985 "bookmarks": [],
985 "bookmarks": [],
986 "branch": "default",
986 "branch": "default",
987 "date": [1500001, 0],
987 "date": [1500001, 0],
988 "desc": "merge",
988 "desc": "merge",
989 "extra": {"branch": "default"},
989 "extra": {"branch": "default"},
990 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
990 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
991 "modified": [],
991 "modified": [],
992 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
992 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
993 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
993 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
994 "phase": "draft",
994 "phase": "draft",
995 "removed": [],
995 "removed": [],
996 "rev": 6,
996 "rev": 6,
997 "tags": [],
997 "tags": [],
998 "user": "person"
998 "user": "person"
999 },
999 },
1000 {
1000 {
1001 "added": ["d"],
1001 "added": ["d"],
1002 "bookmarks": [],
1002 "bookmarks": [],
1003 "branch": "default",
1003 "branch": "default",
1004 "date": [1500000, 0],
1004 "date": [1500000, 0],
1005 "desc": "new head",
1005 "desc": "new head",
1006 "extra": {"branch": "default"},
1006 "extra": {"branch": "default"},
1007 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1007 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1008 "modified": [],
1008 "modified": [],
1009 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1009 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1010 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1010 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1011 "phase": "draft",
1011 "phase": "draft",
1012 "removed": [],
1012 "removed": [],
1013 "rev": 5,
1013 "rev": 5,
1014 "tags": [],
1014 "tags": [],
1015 "user": "person"
1015 "user": "person"
1016 },
1016 },
1017 {
1017 {
1018 "added": [],
1018 "added": [],
1019 "bookmarks": [],
1019 "bookmarks": [],
1020 "branch": "foo",
1020 "branch": "foo",
1021 "date": [1400000, 0],
1021 "date": [1400000, 0],
1022 "desc": "new branch",
1022 "desc": "new branch",
1023 "extra": {"branch": "foo"},
1023 "extra": {"branch": "foo"},
1024 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1024 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1025 "modified": [],
1025 "modified": [],
1026 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1026 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1027 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1027 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1028 "phase": "draft",
1028 "phase": "draft",
1029 "removed": [],
1029 "removed": [],
1030 "rev": 4,
1030 "rev": 4,
1031 "tags": [],
1031 "tags": [],
1032 "user": "person"
1032 "user": "person"
1033 },
1033 },
1034 {
1034 {
1035 "added": [],
1035 "added": [],
1036 "bookmarks": [],
1036 "bookmarks": [],
1037 "branch": "default",
1037 "branch": "default",
1038 "date": [1300000, 0],
1038 "date": [1300000, 0],
1039 "desc": "no user, no domain",
1039 "desc": "no user, no domain",
1040 "extra": {"branch": "default"},
1040 "extra": {"branch": "default"},
1041 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1041 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1042 "modified": ["c"],
1042 "modified": ["c"],
1043 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1043 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1044 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1044 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1045 "phase": "draft",
1045 "phase": "draft",
1046 "removed": [],
1046 "removed": [],
1047 "rev": 3,
1047 "rev": 3,
1048 "tags": [],
1048 "tags": [],
1049 "user": "person"
1049 "user": "person"
1050 },
1050 },
1051 {
1051 {
1052 "added": ["c"],
1052 "added": ["c"],
1053 "bookmarks": [],
1053 "bookmarks": [],
1054 "branch": "default",
1054 "branch": "default",
1055 "date": [1200000, 0],
1055 "date": [1200000, 0],
1056 "desc": "no person",
1056 "desc": "no person",
1057 "extra": {"branch": "default"},
1057 "extra": {"branch": "default"},
1058 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1058 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1059 "modified": [],
1059 "modified": [],
1060 "node": "97054abb4ab824450e9164180baf491ae0078465",
1060 "node": "97054abb4ab824450e9164180baf491ae0078465",
1061 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1061 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1062 "phase": "draft",
1062 "phase": "draft",
1063 "removed": [],
1063 "removed": [],
1064 "rev": 2,
1064 "rev": 2,
1065 "tags": [],
1065 "tags": [],
1066 "user": "other@place"
1066 "user": "other@place"
1067 },
1067 },
1068 {
1068 {
1069 "added": ["b"],
1069 "added": ["b"],
1070 "bookmarks": [],
1070 "bookmarks": [],
1071 "branch": "default",
1071 "branch": "default",
1072 "date": [1100000, 0],
1072 "date": [1100000, 0],
1073 "desc": "other 1\nother 2\n\nother 3",
1073 "desc": "other 1\nother 2\n\nother 3",
1074 "extra": {"branch": "default"},
1074 "extra": {"branch": "default"},
1075 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1075 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1076 "modified": [],
1076 "modified": [],
1077 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1077 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1078 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1078 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1079 "phase": "draft",
1079 "phase": "draft",
1080 "removed": [],
1080 "removed": [],
1081 "rev": 1,
1081 "rev": 1,
1082 "tags": [],
1082 "tags": [],
1083 "user": "A. N. Other <other@place>"
1083 "user": "A. N. Other <other@place>"
1084 },
1084 },
1085 {
1085 {
1086 "added": ["a"],
1086 "added": ["a"],
1087 "bookmarks": [],
1087 "bookmarks": [],
1088 "branch": "default",
1088 "branch": "default",
1089 "date": [1000000, 0],
1089 "date": [1000000, 0],
1090 "desc": "line 1\nline 2",
1090 "desc": "line 1\nline 2",
1091 "extra": {"branch": "default"},
1091 "extra": {"branch": "default"},
1092 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1092 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1093 "modified": [],
1093 "modified": [],
1094 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1094 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1095 "parents": ["0000000000000000000000000000000000000000"],
1095 "parents": ["0000000000000000000000000000000000000000"],
1096 "phase": "draft",
1096 "phase": "draft",
1097 "removed": [],
1097 "removed": [],
1098 "rev": 0,
1098 "rev": 0,
1099 "tags": [],
1099 "tags": [],
1100 "user": "User Name <user@hostname>"
1100 "user": "User Name <user@hostname>"
1101 }
1101 }
1102 ]
1102 ]
1103
1103
1104 Other unsupported formatter styles:
1105
1106 $ hg log -qr . -Tpickle
1107 abort: "pickle" not in template map
1108 [255]
1109 $ hg log -qr . -Tdebug
1110 abort: "debug" not in template map
1111 [255]
1112
1104 Error if style not readable:
1113 Error if style not readable:
1105
1114
1106 #if unix-permissions no-root
1115 #if unix-permissions no-root
1107 $ touch q
1116 $ touch q
1108 $ chmod 0 q
1117 $ chmod 0 q
1109 $ hg log --style ./q
1118 $ hg log --style ./q
1110 abort: Permission denied: './q'
1119 abort: Permission denied: './q'
1111 [255]
1120 [255]
1112 #endif
1121 #endif
1113
1122
1114 Error if no style:
1123 Error if no style:
1115
1124
1116 $ hg log --style notexist
1125 $ hg log --style notexist
1117 abort: style 'notexist' not found
1126 abort: style 'notexist' not found
1118 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1127 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1119 [255]
1128 [255]
1120
1129
1121 $ hg log -T list
1130 $ hg log -T list
1122 available styles: bisect, changelog, compact, default, phases, show, status, xml
1131 available styles: bisect, changelog, compact, default, phases, show, status, xml
1123 abort: specify a template
1132 abort: specify a template
1124 [255]
1133 [255]
1125
1134
1126 Error if style missing key:
1135 Error if style missing key:
1127
1136
1128 $ echo 'q = q' > t
1137 $ echo 'q = q' > t
1129 $ hg log --style ./t
1138 $ hg log --style ./t
1130 abort: "changeset" not in template map
1139 abort: "changeset" not in template map
1131 [255]
1140 [255]
1132
1141
1133 Error if style missing value:
1142 Error if style missing value:
1134
1143
1135 $ echo 'changeset =' > t
1144 $ echo 'changeset =' > t
1136 $ hg log --style t
1145 $ hg log --style t
1137 hg: parse error at t:1: missing value
1146 hg: parse error at t:1: missing value
1138 [255]
1147 [255]
1139
1148
1140 Error if include fails:
1149 Error if include fails:
1141
1150
1142 $ echo 'changeset = q' >> t
1151 $ echo 'changeset = q' >> t
1143 #if unix-permissions no-root
1152 #if unix-permissions no-root
1144 $ hg log --style ./t
1153 $ hg log --style ./t
1145 abort: template file ./q: Permission denied
1154 abort: template file ./q: Permission denied
1146 [255]
1155 [255]
1147 $ rm -f q
1156 $ rm -f q
1148 #endif
1157 #endif
1149
1158
1150 Include works:
1159 Include works:
1151
1160
1152 $ echo '{rev}' > q
1161 $ echo '{rev}' > q
1153 $ hg log --style ./t
1162 $ hg log --style ./t
1154 8
1163 8
1155 7
1164 7
1156 6
1165 6
1157 5
1166 5
1158 4
1167 4
1159 3
1168 3
1160 2
1169 2
1161 1
1170 1
1162 0
1171 0
1163
1172
1164 $ hg phase -r 5 --public
1173 $ hg phase -r 5 --public
1165 $ hg phase -r 7 --secret --force
1174 $ hg phase -r 7 --secret --force
1166
1175
1167 Missing non-standard names give no error (backward compatibility):
1176 Missing non-standard names give no error (backward compatibility):
1168
1177
1169 $ echo "changeset = '{c}'" > t
1178 $ echo "changeset = '{c}'" > t
1170 $ hg log --style ./t
1179 $ hg log --style ./t
1171
1180
1172 Defining non-standard name works:
1181 Defining non-standard name works:
1173
1182
1174 $ cat <<EOF > t
1183 $ cat <<EOF > t
1175 > changeset = '{c}'
1184 > changeset = '{c}'
1176 > c = q
1185 > c = q
1177 > EOF
1186 > EOF
1178 $ hg log --style ./t
1187 $ hg log --style ./t
1179 8
1188 8
1180 7
1189 7
1181 6
1190 6
1182 5
1191 5
1183 4
1192 4
1184 3
1193 3
1185 2
1194 2
1186 1
1195 1
1187 0
1196 0
1188
1197
1189 ui.style works:
1198 ui.style works:
1190
1199
1191 $ echo '[ui]' > .hg/hgrc
1200 $ echo '[ui]' > .hg/hgrc
1192 $ echo 'style = t' >> .hg/hgrc
1201 $ echo 'style = t' >> .hg/hgrc
1193 $ hg log
1202 $ hg log
1194 8
1203 8
1195 7
1204 7
1196 6
1205 6
1197 5
1206 5
1198 4
1207 4
1199 3
1208 3
1200 2
1209 2
1201 1
1210 1
1202 0
1211 0
1203
1212
1204 Issue338:
1213 Issue338:
1205
1214
1206 $ hg log --style=changelog > changelog
1215 $ hg log --style=changelog > changelog
1207
1216
1208 $ cat changelog
1217 $ cat changelog
1209 2020-01-01 test <test>
1218 2020-01-01 test <test>
1210
1219
1211 * fourth, second, third:
1220 * fourth, second, third:
1212 third
1221 third
1213 [95c24699272e] [tip]
1222 [95c24699272e] [tip]
1214
1223
1215 1970-01-12 User Name <user@hostname>
1224 1970-01-12 User Name <user@hostname>
1216
1225
1217 * second:
1226 * second:
1218 second
1227 second
1219 [29114dbae42b]
1228 [29114dbae42b]
1220
1229
1221 1970-01-18 person <person>
1230 1970-01-18 person <person>
1222
1231
1223 * merge
1232 * merge
1224 [d41e714fe50d]
1233 [d41e714fe50d]
1225
1234
1226 * d:
1235 * d:
1227 new head
1236 new head
1228 [13207e5a10d9]
1237 [13207e5a10d9]
1229
1238
1230 1970-01-17 person <person>
1239 1970-01-17 person <person>
1231
1240
1232 * new branch
1241 * new branch
1233 [bbe44766e73d] <foo>
1242 [bbe44766e73d] <foo>
1234
1243
1235 1970-01-16 person <person>
1244 1970-01-16 person <person>
1236
1245
1237 * c:
1246 * c:
1238 no user, no domain
1247 no user, no domain
1239 [10e46f2dcbf4]
1248 [10e46f2dcbf4]
1240
1249
1241 1970-01-14 other <other@place>
1250 1970-01-14 other <other@place>
1242
1251
1243 * c:
1252 * c:
1244 no person
1253 no person
1245 [97054abb4ab8]
1254 [97054abb4ab8]
1246
1255
1247 1970-01-13 A. N. Other <other@place>
1256 1970-01-13 A. N. Other <other@place>
1248
1257
1249 * b:
1258 * b:
1250 other 1 other 2
1259 other 1 other 2
1251
1260
1252 other 3
1261 other 3
1253 [b608e9d1a3f0]
1262 [b608e9d1a3f0]
1254
1263
1255 1970-01-12 User Name <user@hostname>
1264 1970-01-12 User Name <user@hostname>
1256
1265
1257 * a:
1266 * a:
1258 line 1 line 2
1267 line 1 line 2
1259 [1e4e1b8f71e0]
1268 [1e4e1b8f71e0]
1260
1269
1261
1270
1262 Issue2130: xml output for 'hg heads' is malformed
1271 Issue2130: xml output for 'hg heads' is malformed
1263
1272
1264 $ hg heads --style changelog
1273 $ hg heads --style changelog
1265 2020-01-01 test <test>
1274 2020-01-01 test <test>
1266
1275
1267 * fourth, second, third:
1276 * fourth, second, third:
1268 third
1277 third
1269 [95c24699272e] [tip]
1278 [95c24699272e] [tip]
1270
1279
1271 1970-01-18 person <person>
1280 1970-01-18 person <person>
1272
1281
1273 * merge
1282 * merge
1274 [d41e714fe50d]
1283 [d41e714fe50d]
1275
1284
1276 1970-01-17 person <person>
1285 1970-01-17 person <person>
1277
1286
1278 * new branch
1287 * new branch
1279 [bbe44766e73d] <foo>
1288 [bbe44766e73d] <foo>
1280
1289
1281
1290
1282 Add a dummy commit to make up for the instability of the above:
1291 Add a dummy commit to make up for the instability of the above:
1283
1292
1284 $ echo a > a
1293 $ echo a > a
1285 $ hg add a
1294 $ hg add a
1286 $ hg ci -m future
1295 $ hg ci -m future
1287
1296
1288 Add a commit that does all possible modifications at once
1297 Add a commit that does all possible modifications at once
1289
1298
1290 $ echo modify >> third
1299 $ echo modify >> third
1291 $ touch b
1300 $ touch b
1292 $ hg add b
1301 $ hg add b
1293 $ hg mv fourth fifth
1302 $ hg mv fourth fifth
1294 $ hg rm a
1303 $ hg rm a
1295 $ hg ci -m "Modify, add, remove, rename"
1304 $ hg ci -m "Modify, add, remove, rename"
1296
1305
1297 Check the status template
1306 Check the status template
1298
1307
1299 $ cat <<EOF >> $HGRCPATH
1308 $ cat <<EOF >> $HGRCPATH
1300 > [extensions]
1309 > [extensions]
1301 > color=
1310 > color=
1302 > EOF
1311 > EOF
1303
1312
1304 $ hg log -T status -r 10
1313 $ hg log -T status -r 10
1305 changeset: 10:0f9759ec227a
1314 changeset: 10:0f9759ec227a
1306 tag: tip
1315 tag: tip
1307 user: test
1316 user: test
1308 date: Thu Jan 01 00:00:00 1970 +0000
1317 date: Thu Jan 01 00:00:00 1970 +0000
1309 summary: Modify, add, remove, rename
1318 summary: Modify, add, remove, rename
1310 files:
1319 files:
1311 M third
1320 M third
1312 A b
1321 A b
1313 A fifth
1322 A fifth
1314 R a
1323 R a
1315 R fourth
1324 R fourth
1316
1325
1317 $ hg log -T status -C -r 10
1326 $ hg log -T status -C -r 10
1318 changeset: 10:0f9759ec227a
1327 changeset: 10:0f9759ec227a
1319 tag: tip
1328 tag: tip
1320 user: test
1329 user: test
1321 date: Thu Jan 01 00:00:00 1970 +0000
1330 date: Thu Jan 01 00:00:00 1970 +0000
1322 summary: Modify, add, remove, rename
1331 summary: Modify, add, remove, rename
1323 files:
1332 files:
1324 M third
1333 M third
1325 A b
1334 A b
1326 A fifth
1335 A fifth
1327 fourth
1336 fourth
1328 R a
1337 R a
1329 R fourth
1338 R fourth
1330
1339
1331 $ hg log -T status -C -r 10 -v
1340 $ hg log -T status -C -r 10 -v
1332 changeset: 10:0f9759ec227a
1341 changeset: 10:0f9759ec227a
1333 tag: tip
1342 tag: tip
1334 user: test
1343 user: test
1335 date: Thu Jan 01 00:00:00 1970 +0000
1344 date: Thu Jan 01 00:00:00 1970 +0000
1336 description:
1345 description:
1337 Modify, add, remove, rename
1346 Modify, add, remove, rename
1338
1347
1339 files:
1348 files:
1340 M third
1349 M third
1341 A b
1350 A b
1342 A fifth
1351 A fifth
1343 fourth
1352 fourth
1344 R a
1353 R a
1345 R fourth
1354 R fourth
1346
1355
1347 $ hg log -T status -C -r 10 --debug
1356 $ hg log -T status -C -r 10 --debug
1348 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
1357 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
1349 tag: tip
1358 tag: tip
1350 phase: secret
1359 phase: secret
1351 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
1360 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
1352 parent: -1:0000000000000000000000000000000000000000
1361 parent: -1:0000000000000000000000000000000000000000
1353 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
1362 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
1354 user: test
1363 user: test
1355 date: Thu Jan 01 00:00:00 1970 +0000
1364 date: Thu Jan 01 00:00:00 1970 +0000
1356 extra: branch=default
1365 extra: branch=default
1357 description:
1366 description:
1358 Modify, add, remove, rename
1367 Modify, add, remove, rename
1359
1368
1360 files:
1369 files:
1361 M third
1370 M third
1362 A b
1371 A b
1363 A fifth
1372 A fifth
1364 fourth
1373 fourth
1365 R a
1374 R a
1366 R fourth
1375 R fourth
1367
1376
1368 $ hg log -T status -C -r 10 --quiet
1377 $ hg log -T status -C -r 10 --quiet
1369 10:0f9759ec227a
1378 10:0f9759ec227a
1370 $ hg --color=debug log -T status -r 10
1379 $ hg --color=debug log -T status -r 10
1371 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1380 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1372 [log.tag|tag: tip]
1381 [log.tag|tag: tip]
1373 [log.user|user: test]
1382 [log.user|user: test]
1374 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1383 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1375 [log.summary|summary: Modify, add, remove, rename]
1384 [log.summary|summary: Modify, add, remove, rename]
1376 [ui.note log.files|files:]
1385 [ui.note log.files|files:]
1377 [status.modified|M third]
1386 [status.modified|M third]
1378 [status.added|A b]
1387 [status.added|A b]
1379 [status.added|A fifth]
1388 [status.added|A fifth]
1380 [status.removed|R a]
1389 [status.removed|R a]
1381 [status.removed|R fourth]
1390 [status.removed|R fourth]
1382
1391
1383 $ hg --color=debug log -T status -C -r 10
1392 $ hg --color=debug log -T status -C -r 10
1384 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1393 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1385 [log.tag|tag: tip]
1394 [log.tag|tag: tip]
1386 [log.user|user: test]
1395 [log.user|user: test]
1387 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1396 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1388 [log.summary|summary: Modify, add, remove, rename]
1397 [log.summary|summary: Modify, add, remove, rename]
1389 [ui.note log.files|files:]
1398 [ui.note log.files|files:]
1390 [status.modified|M third]
1399 [status.modified|M third]
1391 [status.added|A b]
1400 [status.added|A b]
1392 [status.added|A fifth]
1401 [status.added|A fifth]
1393 [status.copied| fourth]
1402 [status.copied| fourth]
1394 [status.removed|R a]
1403 [status.removed|R a]
1395 [status.removed|R fourth]
1404 [status.removed|R fourth]
1396
1405
1397 $ hg --color=debug log -T status -C -r 10 -v
1406 $ hg --color=debug log -T status -C -r 10 -v
1398 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1407 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
1399 [log.tag|tag: tip]
1408 [log.tag|tag: tip]
1400 [log.user|user: test]
1409 [log.user|user: test]
1401 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1410 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1402 [ui.note log.description|description:]
1411 [ui.note log.description|description:]
1403 [ui.note log.description|Modify, add, remove, rename]
1412 [ui.note log.description|Modify, add, remove, rename]
1404
1413
1405 [ui.note log.files|files:]
1414 [ui.note log.files|files:]
1406 [status.modified|M third]
1415 [status.modified|M third]
1407 [status.added|A b]
1416 [status.added|A b]
1408 [status.added|A fifth]
1417 [status.added|A fifth]
1409 [status.copied| fourth]
1418 [status.copied| fourth]
1410 [status.removed|R a]
1419 [status.removed|R a]
1411 [status.removed|R fourth]
1420 [status.removed|R fourth]
1412
1421
1413 $ hg --color=debug log -T status -C -r 10 --debug
1422 $ hg --color=debug log -T status -C -r 10 --debug
1414 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
1423 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
1415 [log.tag|tag: tip]
1424 [log.tag|tag: tip]
1416 [log.phase|phase: secret]
1425 [log.phase|phase: secret]
1417 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
1426 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
1418 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1427 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1419 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
1428 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
1420 [log.user|user: test]
1429 [log.user|user: test]
1421 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1430 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
1422 [ui.debug log.extra|extra: branch=default]
1431 [ui.debug log.extra|extra: branch=default]
1423 [ui.note log.description|description:]
1432 [ui.note log.description|description:]
1424 [ui.note log.description|Modify, add, remove, rename]
1433 [ui.note log.description|Modify, add, remove, rename]
1425
1434
1426 [ui.note log.files|files:]
1435 [ui.note log.files|files:]
1427 [status.modified|M third]
1436 [status.modified|M third]
1428 [status.added|A b]
1437 [status.added|A b]
1429 [status.added|A fifth]
1438 [status.added|A fifth]
1430 [status.copied| fourth]
1439 [status.copied| fourth]
1431 [status.removed|R a]
1440 [status.removed|R a]
1432 [status.removed|R fourth]
1441 [status.removed|R fourth]
1433
1442
1434 $ hg --color=debug log -T status -C -r 10 --quiet
1443 $ hg --color=debug log -T status -C -r 10 --quiet
1435 [log.node|10:0f9759ec227a]
1444 [log.node|10:0f9759ec227a]
1436
1445
1437 Check the bisect template
1446 Check the bisect template
1438
1447
1439 $ hg bisect -g 1
1448 $ hg bisect -g 1
1440 $ hg bisect -b 3 --noupdate
1449 $ hg bisect -b 3 --noupdate
1441 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
1450 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
1442 $ hg log -T bisect -r 0:4
1451 $ hg log -T bisect -r 0:4
1443 changeset: 0:1e4e1b8f71e0
1452 changeset: 0:1e4e1b8f71e0
1444 bisect: good (implicit)
1453 bisect: good (implicit)
1445 user: User Name <user@hostname>
1454 user: User Name <user@hostname>
1446 date: Mon Jan 12 13:46:40 1970 +0000
1455 date: Mon Jan 12 13:46:40 1970 +0000
1447 summary: line 1
1456 summary: line 1
1448
1457
1449 changeset: 1:b608e9d1a3f0
1458 changeset: 1:b608e9d1a3f0
1450 bisect: good
1459 bisect: good
1451 user: A. N. Other <other@place>
1460 user: A. N. Other <other@place>
1452 date: Tue Jan 13 17:33:20 1970 +0000
1461 date: Tue Jan 13 17:33:20 1970 +0000
1453 summary: other 1
1462 summary: other 1
1454
1463
1455 changeset: 2:97054abb4ab8
1464 changeset: 2:97054abb4ab8
1456 bisect: untested
1465 bisect: untested
1457 user: other@place
1466 user: other@place
1458 date: Wed Jan 14 21:20:00 1970 +0000
1467 date: Wed Jan 14 21:20:00 1970 +0000
1459 summary: no person
1468 summary: no person
1460
1469
1461 changeset: 3:10e46f2dcbf4
1470 changeset: 3:10e46f2dcbf4
1462 bisect: bad
1471 bisect: bad
1463 user: person
1472 user: person
1464 date: Fri Jan 16 01:06:40 1970 +0000
1473 date: Fri Jan 16 01:06:40 1970 +0000
1465 summary: no user, no domain
1474 summary: no user, no domain
1466
1475
1467 changeset: 4:bbe44766e73d
1476 changeset: 4:bbe44766e73d
1468 bisect: bad (implicit)
1477 bisect: bad (implicit)
1469 branch: foo
1478 branch: foo
1470 user: person
1479 user: person
1471 date: Sat Jan 17 04:53:20 1970 +0000
1480 date: Sat Jan 17 04:53:20 1970 +0000
1472 summary: new branch
1481 summary: new branch
1473
1482
1474 $ hg log --debug -T bisect -r 0:4
1483 $ hg log --debug -T bisect -r 0:4
1475 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
1484 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
1476 bisect: good (implicit)
1485 bisect: good (implicit)
1477 phase: public
1486 phase: public
1478 parent: -1:0000000000000000000000000000000000000000
1487 parent: -1:0000000000000000000000000000000000000000
1479 parent: -1:0000000000000000000000000000000000000000
1488 parent: -1:0000000000000000000000000000000000000000
1480 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1489 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1481 user: User Name <user@hostname>
1490 user: User Name <user@hostname>
1482 date: Mon Jan 12 13:46:40 1970 +0000
1491 date: Mon Jan 12 13:46:40 1970 +0000
1483 files+: a
1492 files+: a
1484 extra: branch=default
1493 extra: branch=default
1485 description:
1494 description:
1486 line 1
1495 line 1
1487 line 2
1496 line 2
1488
1497
1489
1498
1490 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1499 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1491 bisect: good
1500 bisect: good
1492 phase: public
1501 phase: public
1493 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
1502 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
1494 parent: -1:0000000000000000000000000000000000000000
1503 parent: -1:0000000000000000000000000000000000000000
1495 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1504 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1496 user: A. N. Other <other@place>
1505 user: A. N. Other <other@place>
1497 date: Tue Jan 13 17:33:20 1970 +0000
1506 date: Tue Jan 13 17:33:20 1970 +0000
1498 files+: b
1507 files+: b
1499 extra: branch=default
1508 extra: branch=default
1500 description:
1509 description:
1501 other 1
1510 other 1
1502 other 2
1511 other 2
1503
1512
1504 other 3
1513 other 3
1505
1514
1506
1515
1507 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
1516 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
1508 bisect: untested
1517 bisect: untested
1509 phase: public
1518 phase: public
1510 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1519 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1511 parent: -1:0000000000000000000000000000000000000000
1520 parent: -1:0000000000000000000000000000000000000000
1512 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1521 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1513 user: other@place
1522 user: other@place
1514 date: Wed Jan 14 21:20:00 1970 +0000
1523 date: Wed Jan 14 21:20:00 1970 +0000
1515 files+: c
1524 files+: c
1516 extra: branch=default
1525 extra: branch=default
1517 description:
1526 description:
1518 no person
1527 no person
1519
1528
1520
1529
1521 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
1530 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
1522 bisect: bad
1531 bisect: bad
1523 phase: public
1532 phase: public
1524 parent: 2:97054abb4ab824450e9164180baf491ae0078465
1533 parent: 2:97054abb4ab824450e9164180baf491ae0078465
1525 parent: -1:0000000000000000000000000000000000000000
1534 parent: -1:0000000000000000000000000000000000000000
1526 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1535 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1527 user: person
1536 user: person
1528 date: Fri Jan 16 01:06:40 1970 +0000
1537 date: Fri Jan 16 01:06:40 1970 +0000
1529 files: c
1538 files: c
1530 extra: branch=default
1539 extra: branch=default
1531 description:
1540 description:
1532 no user, no domain
1541 no user, no domain
1533
1542
1534
1543
1535 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1544 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1536 bisect: bad (implicit)
1545 bisect: bad (implicit)
1537 branch: foo
1546 branch: foo
1538 phase: draft
1547 phase: draft
1539 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
1548 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
1540 parent: -1:0000000000000000000000000000000000000000
1549 parent: -1:0000000000000000000000000000000000000000
1541 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1550 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1542 user: person
1551 user: person
1543 date: Sat Jan 17 04:53:20 1970 +0000
1552 date: Sat Jan 17 04:53:20 1970 +0000
1544 extra: branch=foo
1553 extra: branch=foo
1545 description:
1554 description:
1546 new branch
1555 new branch
1547
1556
1548
1557
1549 $ hg log -v -T bisect -r 0:4
1558 $ hg log -v -T bisect -r 0:4
1550 changeset: 0:1e4e1b8f71e0
1559 changeset: 0:1e4e1b8f71e0
1551 bisect: good (implicit)
1560 bisect: good (implicit)
1552 user: User Name <user@hostname>
1561 user: User Name <user@hostname>
1553 date: Mon Jan 12 13:46:40 1970 +0000
1562 date: Mon Jan 12 13:46:40 1970 +0000
1554 files: a
1563 files: a
1555 description:
1564 description:
1556 line 1
1565 line 1
1557 line 2
1566 line 2
1558
1567
1559
1568
1560 changeset: 1:b608e9d1a3f0
1569 changeset: 1:b608e9d1a3f0
1561 bisect: good
1570 bisect: good
1562 user: A. N. Other <other@place>
1571 user: A. N. Other <other@place>
1563 date: Tue Jan 13 17:33:20 1970 +0000
1572 date: Tue Jan 13 17:33:20 1970 +0000
1564 files: b
1573 files: b
1565 description:
1574 description:
1566 other 1
1575 other 1
1567 other 2
1576 other 2
1568
1577
1569 other 3
1578 other 3
1570
1579
1571
1580
1572 changeset: 2:97054abb4ab8
1581 changeset: 2:97054abb4ab8
1573 bisect: untested
1582 bisect: untested
1574 user: other@place
1583 user: other@place
1575 date: Wed Jan 14 21:20:00 1970 +0000
1584 date: Wed Jan 14 21:20:00 1970 +0000
1576 files: c
1585 files: c
1577 description:
1586 description:
1578 no person
1587 no person
1579
1588
1580
1589
1581 changeset: 3:10e46f2dcbf4
1590 changeset: 3:10e46f2dcbf4
1582 bisect: bad
1591 bisect: bad
1583 user: person
1592 user: person
1584 date: Fri Jan 16 01:06:40 1970 +0000
1593 date: Fri Jan 16 01:06:40 1970 +0000
1585 files: c
1594 files: c
1586 description:
1595 description:
1587 no user, no domain
1596 no user, no domain
1588
1597
1589
1598
1590 changeset: 4:bbe44766e73d
1599 changeset: 4:bbe44766e73d
1591 bisect: bad (implicit)
1600 bisect: bad (implicit)
1592 branch: foo
1601 branch: foo
1593 user: person
1602 user: person
1594 date: Sat Jan 17 04:53:20 1970 +0000
1603 date: Sat Jan 17 04:53:20 1970 +0000
1595 description:
1604 description:
1596 new branch
1605 new branch
1597
1606
1598
1607
1599 $ hg --color=debug log -T bisect -r 0:4
1608 $ hg --color=debug log -T bisect -r 0:4
1600 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
1609 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
1601 [log.bisect bisect.good|bisect: good (implicit)]
1610 [log.bisect bisect.good|bisect: good (implicit)]
1602 [log.user|user: User Name <user@hostname>]
1611 [log.user|user: User Name <user@hostname>]
1603 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1612 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1604 [log.summary|summary: line 1]
1613 [log.summary|summary: line 1]
1605
1614
1606 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
1615 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
1607 [log.bisect bisect.good|bisect: good]
1616 [log.bisect bisect.good|bisect: good]
1608 [log.user|user: A. N. Other <other@place>]
1617 [log.user|user: A. N. Other <other@place>]
1609 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1618 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1610 [log.summary|summary: other 1]
1619 [log.summary|summary: other 1]
1611
1620
1612 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
1621 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
1613 [log.bisect bisect.untested|bisect: untested]
1622 [log.bisect bisect.untested|bisect: untested]
1614 [log.user|user: other@place]
1623 [log.user|user: other@place]
1615 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1624 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1616 [log.summary|summary: no person]
1625 [log.summary|summary: no person]
1617
1626
1618 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
1627 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
1619 [log.bisect bisect.bad|bisect: bad]
1628 [log.bisect bisect.bad|bisect: bad]
1620 [log.user|user: person]
1629 [log.user|user: person]
1621 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1630 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1622 [log.summary|summary: no user, no domain]
1631 [log.summary|summary: no user, no domain]
1623
1632
1624 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
1633 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
1625 [log.bisect bisect.bad|bisect: bad (implicit)]
1634 [log.bisect bisect.bad|bisect: bad (implicit)]
1626 [log.branch|branch: foo]
1635 [log.branch|branch: foo]
1627 [log.user|user: person]
1636 [log.user|user: person]
1628 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1637 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1629 [log.summary|summary: new branch]
1638 [log.summary|summary: new branch]
1630
1639
1631 $ hg --color=debug log --debug -T bisect -r 0:4
1640 $ hg --color=debug log --debug -T bisect -r 0:4
1632 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
1641 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
1633 [log.bisect bisect.good|bisect: good (implicit)]
1642 [log.bisect bisect.good|bisect: good (implicit)]
1634 [log.phase|phase: public]
1643 [log.phase|phase: public]
1635 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1644 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1636 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1645 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1637 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
1646 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
1638 [log.user|user: User Name <user@hostname>]
1647 [log.user|user: User Name <user@hostname>]
1639 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1648 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1640 [ui.debug log.files|files+: a]
1649 [ui.debug log.files|files+: a]
1641 [ui.debug log.extra|extra: branch=default]
1650 [ui.debug log.extra|extra: branch=default]
1642 [ui.note log.description|description:]
1651 [ui.note log.description|description:]
1643 [ui.note log.description|line 1
1652 [ui.note log.description|line 1
1644 line 2]
1653 line 2]
1645
1654
1646
1655
1647 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
1656 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
1648 [log.bisect bisect.good|bisect: good]
1657 [log.bisect bisect.good|bisect: good]
1649 [log.phase|phase: public]
1658 [log.phase|phase: public]
1650 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
1659 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
1651 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1660 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1652 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
1661 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
1653 [log.user|user: A. N. Other <other@place>]
1662 [log.user|user: A. N. Other <other@place>]
1654 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1663 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1655 [ui.debug log.files|files+: b]
1664 [ui.debug log.files|files+: b]
1656 [ui.debug log.extra|extra: branch=default]
1665 [ui.debug log.extra|extra: branch=default]
1657 [ui.note log.description|description:]
1666 [ui.note log.description|description:]
1658 [ui.note log.description|other 1
1667 [ui.note log.description|other 1
1659 other 2
1668 other 2
1660
1669
1661 other 3]
1670 other 3]
1662
1671
1663
1672
1664 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
1673 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
1665 [log.bisect bisect.untested|bisect: untested]
1674 [log.bisect bisect.untested|bisect: untested]
1666 [log.phase|phase: public]
1675 [log.phase|phase: public]
1667 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
1676 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
1668 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1677 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1669 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
1678 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
1670 [log.user|user: other@place]
1679 [log.user|user: other@place]
1671 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1680 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1672 [ui.debug log.files|files+: c]
1681 [ui.debug log.files|files+: c]
1673 [ui.debug log.extra|extra: branch=default]
1682 [ui.debug log.extra|extra: branch=default]
1674 [ui.note log.description|description:]
1683 [ui.note log.description|description:]
1675 [ui.note log.description|no person]
1684 [ui.note log.description|no person]
1676
1685
1677
1686
1678 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
1687 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
1679 [log.bisect bisect.bad|bisect: bad]
1688 [log.bisect bisect.bad|bisect: bad]
1680 [log.phase|phase: public]
1689 [log.phase|phase: public]
1681 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
1690 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
1682 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1691 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1683 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
1692 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
1684 [log.user|user: person]
1693 [log.user|user: person]
1685 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1694 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1686 [ui.debug log.files|files: c]
1695 [ui.debug log.files|files: c]
1687 [ui.debug log.extra|extra: branch=default]
1696 [ui.debug log.extra|extra: branch=default]
1688 [ui.note log.description|description:]
1697 [ui.note log.description|description:]
1689 [ui.note log.description|no user, no domain]
1698 [ui.note log.description|no user, no domain]
1690
1699
1691
1700
1692 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
1701 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
1693 [log.bisect bisect.bad|bisect: bad (implicit)]
1702 [log.bisect bisect.bad|bisect: bad (implicit)]
1694 [log.branch|branch: foo]
1703 [log.branch|branch: foo]
1695 [log.phase|phase: draft]
1704 [log.phase|phase: draft]
1696 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
1705 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
1697 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1706 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
1698 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
1707 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
1699 [log.user|user: person]
1708 [log.user|user: person]
1700 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1709 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1701 [ui.debug log.extra|extra: branch=foo]
1710 [ui.debug log.extra|extra: branch=foo]
1702 [ui.note log.description|description:]
1711 [ui.note log.description|description:]
1703 [ui.note log.description|new branch]
1712 [ui.note log.description|new branch]
1704
1713
1705
1714
1706 $ hg --color=debug log -v -T bisect -r 0:4
1715 $ hg --color=debug log -v -T bisect -r 0:4
1707 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
1716 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
1708 [log.bisect bisect.good|bisect: good (implicit)]
1717 [log.bisect bisect.good|bisect: good (implicit)]
1709 [log.user|user: User Name <user@hostname>]
1718 [log.user|user: User Name <user@hostname>]
1710 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1719 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
1711 [ui.note log.files|files: a]
1720 [ui.note log.files|files: a]
1712 [ui.note log.description|description:]
1721 [ui.note log.description|description:]
1713 [ui.note log.description|line 1
1722 [ui.note log.description|line 1
1714 line 2]
1723 line 2]
1715
1724
1716
1725
1717 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
1726 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
1718 [log.bisect bisect.good|bisect: good]
1727 [log.bisect bisect.good|bisect: good]
1719 [log.user|user: A. N. Other <other@place>]
1728 [log.user|user: A. N. Other <other@place>]
1720 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1729 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
1721 [ui.note log.files|files: b]
1730 [ui.note log.files|files: b]
1722 [ui.note log.description|description:]
1731 [ui.note log.description|description:]
1723 [ui.note log.description|other 1
1732 [ui.note log.description|other 1
1724 other 2
1733 other 2
1725
1734
1726 other 3]
1735 other 3]
1727
1736
1728
1737
1729 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
1738 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
1730 [log.bisect bisect.untested|bisect: untested]
1739 [log.bisect bisect.untested|bisect: untested]
1731 [log.user|user: other@place]
1740 [log.user|user: other@place]
1732 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1741 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
1733 [ui.note log.files|files: c]
1742 [ui.note log.files|files: c]
1734 [ui.note log.description|description:]
1743 [ui.note log.description|description:]
1735 [ui.note log.description|no person]
1744 [ui.note log.description|no person]
1736
1745
1737
1746
1738 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
1747 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
1739 [log.bisect bisect.bad|bisect: bad]
1748 [log.bisect bisect.bad|bisect: bad]
1740 [log.user|user: person]
1749 [log.user|user: person]
1741 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1750 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
1742 [ui.note log.files|files: c]
1751 [ui.note log.files|files: c]
1743 [ui.note log.description|description:]
1752 [ui.note log.description|description:]
1744 [ui.note log.description|no user, no domain]
1753 [ui.note log.description|no user, no domain]
1745
1754
1746
1755
1747 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
1756 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
1748 [log.bisect bisect.bad|bisect: bad (implicit)]
1757 [log.bisect bisect.bad|bisect: bad (implicit)]
1749 [log.branch|branch: foo]
1758 [log.branch|branch: foo]
1750 [log.user|user: person]
1759 [log.user|user: person]
1751 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1760 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
1752 [ui.note log.description|description:]
1761 [ui.note log.description|description:]
1753 [ui.note log.description|new branch]
1762 [ui.note log.description|new branch]
1754
1763
1755
1764
1756 $ hg bisect --reset
1765 $ hg bisect --reset
1757
1766
1758 $ cd ..
1767 $ cd ..
1759
1768
1760 Set up latesttag repository:
1769 Set up latesttag repository:
1761
1770
1762 $ hg init latesttag
1771 $ hg init latesttag
1763 $ cd latesttag
1772 $ cd latesttag
1764
1773
1765 $ echo a > file
1774 $ echo a > file
1766 $ hg ci -Am a -d '0 0'
1775 $ hg ci -Am a -d '0 0'
1767 adding file
1776 adding file
1768
1777
1769 $ echo b >> file
1778 $ echo b >> file
1770 $ hg ci -m b -d '1 0'
1779 $ hg ci -m b -d '1 0'
1771
1780
1772 $ echo c >> head1
1781 $ echo c >> head1
1773 $ hg ci -Am h1c -d '2 0'
1782 $ hg ci -Am h1c -d '2 0'
1774 adding head1
1783 adding head1
1775
1784
1776 $ hg update -q 1
1785 $ hg update -q 1
1777 $ echo d >> head2
1786 $ echo d >> head2
1778 $ hg ci -Am h2d -d '3 0'
1787 $ hg ci -Am h2d -d '3 0'
1779 adding head2
1788 adding head2
1780 created new head
1789 created new head
1781
1790
1782 $ echo e >> head2
1791 $ echo e >> head2
1783 $ hg ci -m h2e -d '4 0'
1792 $ hg ci -m h2e -d '4 0'
1784
1793
1785 $ hg merge -q
1794 $ hg merge -q
1786 $ hg ci -m merge -d '5 -3600'
1795 $ hg ci -m merge -d '5 -3600'
1787
1796
1788 $ hg tag -r 1 -m t1 -d '6 0' t1
1797 $ hg tag -r 1 -m t1 -d '6 0' t1
1789 $ hg tag -r 2 -m t2 -d '7 0' t2
1798 $ hg tag -r 2 -m t2 -d '7 0' t2
1790 $ hg tag -r 3 -m t3 -d '8 0' t3
1799 $ hg tag -r 3 -m t3 -d '8 0' t3
1791 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
1800 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
1792 $ hg tag -r 5 -m t5 -d '9 0' t5
1801 $ hg tag -r 5 -m t5 -d '9 0' t5
1793 $ hg tag -r 3 -m at3 -d '10 0' at3
1802 $ hg tag -r 3 -m at3 -d '10 0' at3
1794
1803
1795 $ cd ..
1804 $ cd ..
1796
1805
1797 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1806 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1798 if it is a relative path
1807 if it is a relative path
1799
1808
1800 $ mkdir -p home/styles
1809 $ mkdir -p home/styles
1801
1810
1802 $ cat > home/styles/teststyle <<EOF
1811 $ cat > home/styles/teststyle <<EOF
1803 > changeset = 'test {rev}:{node|short}\n'
1812 > changeset = 'test {rev}:{node|short}\n'
1804 > EOF
1813 > EOF
1805
1814
1806 $ HOME=`pwd`/home; export HOME
1815 $ HOME=`pwd`/home; export HOME
1807
1816
1808 $ cat > latesttag/.hg/hgrc <<EOF
1817 $ cat > latesttag/.hg/hgrc <<EOF
1809 > [ui]
1818 > [ui]
1810 > style = ~/styles/teststyle
1819 > style = ~/styles/teststyle
1811 > EOF
1820 > EOF
1812
1821
1813 $ hg -R latesttag tip
1822 $ hg -R latesttag tip
1814 test 11:97e5943b523a
1823 test 11:97e5943b523a
1815
1824
1816 Test recursive showlist template (issue1989):
1825 Test recursive showlist template (issue1989):
1817
1826
1818 $ cat > style1989 <<EOF
1827 $ cat > style1989 <<EOF
1819 > changeset = '{file_mods}{manifest}{extras}'
1828 > changeset = '{file_mods}{manifest}{extras}'
1820 > file_mod = 'M|{author|person}\n'
1829 > file_mod = 'M|{author|person}\n'
1821 > manifest = '{rev},{author}\n'
1830 > manifest = '{rev},{author}\n'
1822 > extra = '{key}: {author}\n'
1831 > extra = '{key}: {author}\n'
1823 > EOF
1832 > EOF
1824
1833
1825 $ hg -R latesttag log -r tip --style=style1989
1834 $ hg -R latesttag log -r tip --style=style1989
1826 M|test
1835 M|test
1827 11,
1836 11,
1828 branch: test
1837 branch: test
General Comments 0
You need to be logged in to leave comments. Login now