##// END OF EJS Templates
registrar: add templatefunc to mark a function as template function (API)...
FUJIWARA Katsunori -
r28695:cc103bd0 default
parent child Browse files
Show More
@@ -1,1066 +1,1068 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
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, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import atexit
10 import atexit
11 import difflib
11 import difflib
12 import errno
12 import errno
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import shlex
16 import shlex
17 import signal
17 import signal
18 import socket
18 import socket
19 import sys
19 import sys
20 import time
20 import time
21 import traceback
21 import traceback
22
22
23
23
24 from .i18n import _
24 from .i18n import _
25
25
26 from . import (
26 from . import (
27 cmdutil,
27 cmdutil,
28 commands,
28 commands,
29 demandimport,
29 demandimport,
30 encoding,
30 encoding,
31 error,
31 error,
32 extensions,
32 extensions,
33 fancyopts,
33 fancyopts,
34 fileset,
34 fileset,
35 hg,
35 hg,
36 hook,
36 hook,
37 revset,
37 revset,
38 templatefilters,
38 templatefilters,
39 templatekw,
39 templatekw,
40 templater,
40 ui as uimod,
41 ui as uimod,
41 util,
42 util,
42 )
43 )
43
44
44 class request(object):
45 class request(object):
45 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
46 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
46 ferr=None):
47 ferr=None):
47 self.args = args
48 self.args = args
48 self.ui = ui
49 self.ui = ui
49 self.repo = repo
50 self.repo = repo
50
51
51 # input/output/error streams
52 # input/output/error streams
52 self.fin = fin
53 self.fin = fin
53 self.fout = fout
54 self.fout = fout
54 self.ferr = ferr
55 self.ferr = ferr
55
56
56 def run():
57 def run():
57 "run the command in sys.argv"
58 "run the command in sys.argv"
58 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
59 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
59
60
60 def _getsimilar(symbols, value):
61 def _getsimilar(symbols, value):
61 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
62 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
62 # The cutoff for similarity here is pretty arbitrary. It should
63 # The cutoff for similarity here is pretty arbitrary. It should
63 # probably be investigated and tweaked.
64 # probably be investigated and tweaked.
64 return [s for s in symbols if sim(s) > 0.6]
65 return [s for s in symbols if sim(s) > 0.6]
65
66
66 def _reportsimilar(write, similar):
67 def _reportsimilar(write, similar):
67 if len(similar) == 1:
68 if len(similar) == 1:
68 write(_("(did you mean %s?)\n") % similar[0])
69 write(_("(did you mean %s?)\n") % similar[0])
69 elif similar:
70 elif similar:
70 ss = ", ".join(sorted(similar))
71 ss = ", ".join(sorted(similar))
71 write(_("(did you mean one of %s?)\n") % ss)
72 write(_("(did you mean one of %s?)\n") % ss)
72
73
73 def _formatparse(write, inst):
74 def _formatparse(write, inst):
74 similar = []
75 similar = []
75 if isinstance(inst, error.UnknownIdentifier):
76 if isinstance(inst, error.UnknownIdentifier):
76 # make sure to check fileset first, as revset can invoke fileset
77 # make sure to check fileset first, as revset can invoke fileset
77 similar = _getsimilar(inst.symbols, inst.function)
78 similar = _getsimilar(inst.symbols, inst.function)
78 if len(inst.args) > 1:
79 if len(inst.args) > 1:
79 write(_("hg: parse error at %s: %s\n") %
80 write(_("hg: parse error at %s: %s\n") %
80 (inst.args[1], inst.args[0]))
81 (inst.args[1], inst.args[0]))
81 if (inst.args[0][0] == ' '):
82 if (inst.args[0][0] == ' '):
82 write(_("unexpected leading whitespace\n"))
83 write(_("unexpected leading whitespace\n"))
83 else:
84 else:
84 write(_("hg: parse error: %s\n") % inst.args[0])
85 write(_("hg: parse error: %s\n") % inst.args[0])
85 _reportsimilar(write, similar)
86 _reportsimilar(write, similar)
86 if inst.hint:
87 if inst.hint:
87 write(_("(%s)\n") % inst.hint)
88 write(_("(%s)\n") % inst.hint)
88
89
89 def dispatch(req):
90 def dispatch(req):
90 "run the command specified in req.args"
91 "run the command specified in req.args"
91 if req.ferr:
92 if req.ferr:
92 ferr = req.ferr
93 ferr = req.ferr
93 elif req.ui:
94 elif req.ui:
94 ferr = req.ui.ferr
95 ferr = req.ui.ferr
95 else:
96 else:
96 ferr = sys.stderr
97 ferr = sys.stderr
97
98
98 try:
99 try:
99 if not req.ui:
100 if not req.ui:
100 req.ui = uimod.ui()
101 req.ui = uimod.ui()
101 if '--traceback' in req.args:
102 if '--traceback' in req.args:
102 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
103 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
103
104
104 # set ui streams from the request
105 # set ui streams from the request
105 if req.fin:
106 if req.fin:
106 req.ui.fin = req.fin
107 req.ui.fin = req.fin
107 if req.fout:
108 if req.fout:
108 req.ui.fout = req.fout
109 req.ui.fout = req.fout
109 if req.ferr:
110 if req.ferr:
110 req.ui.ferr = req.ferr
111 req.ui.ferr = req.ferr
111 except error.Abort as inst:
112 except error.Abort as inst:
112 ferr.write(_("abort: %s\n") % inst)
113 ferr.write(_("abort: %s\n") % inst)
113 if inst.hint:
114 if inst.hint:
114 ferr.write(_("(%s)\n") % inst.hint)
115 ferr.write(_("(%s)\n") % inst.hint)
115 return -1
116 return -1
116 except error.ParseError as inst:
117 except error.ParseError as inst:
117 _formatparse(ferr.write, inst)
118 _formatparse(ferr.write, inst)
118 return -1
119 return -1
119
120
120 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
121 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
121 starttime = time.time()
122 starttime = time.time()
122 ret = None
123 ret = None
123 try:
124 try:
124 ret = _runcatch(req)
125 ret = _runcatch(req)
125 except KeyboardInterrupt:
126 except KeyboardInterrupt:
126 try:
127 try:
127 req.ui.warn(_("interrupted!\n"))
128 req.ui.warn(_("interrupted!\n"))
128 except IOError as inst:
129 except IOError as inst:
129 if inst.errno != errno.EPIPE:
130 if inst.errno != errno.EPIPE:
130 raise
131 raise
131 ret = -1
132 ret = -1
132 finally:
133 finally:
133 duration = time.time() - starttime
134 duration = time.time() - starttime
134 req.ui.flush()
135 req.ui.flush()
135 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
136 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
136 msg, ret or 0, duration)
137 msg, ret or 0, duration)
137 return ret
138 return ret
138
139
139 def _runcatch(req):
140 def _runcatch(req):
140 def catchterm(*args):
141 def catchterm(*args):
141 raise error.SignalInterrupt
142 raise error.SignalInterrupt
142
143
143 ui = req.ui
144 ui = req.ui
144 try:
145 try:
145 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
146 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
146 num = getattr(signal, name, None)
147 num = getattr(signal, name, None)
147 if num:
148 if num:
148 signal.signal(num, catchterm)
149 signal.signal(num, catchterm)
149 except ValueError:
150 except ValueError:
150 pass # happens if called in a thread
151 pass # happens if called in a thread
151
152
152 try:
153 try:
153 try:
154 try:
154 debugger = 'pdb'
155 debugger = 'pdb'
155 debugtrace = {
156 debugtrace = {
156 'pdb' : pdb.set_trace
157 'pdb' : pdb.set_trace
157 }
158 }
158 debugmortem = {
159 debugmortem = {
159 'pdb' : pdb.post_mortem
160 'pdb' : pdb.post_mortem
160 }
161 }
161
162
162 # read --config before doing anything else
163 # read --config before doing anything else
163 # (e.g. to change trust settings for reading .hg/hgrc)
164 # (e.g. to change trust settings for reading .hg/hgrc)
164 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
165 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
165
166
166 if req.repo:
167 if req.repo:
167 # copy configs that were passed on the cmdline (--config) to
168 # copy configs that were passed on the cmdline (--config) to
168 # the repo ui
169 # the repo ui
169 for sec, name, val in cfgs:
170 for sec, name, val in cfgs:
170 req.repo.ui.setconfig(sec, name, val, source='--config')
171 req.repo.ui.setconfig(sec, name, val, source='--config')
171
172
172 # developer config: ui.debugger
173 # developer config: ui.debugger
173 debugger = ui.config("ui", "debugger")
174 debugger = ui.config("ui", "debugger")
174 debugmod = pdb
175 debugmod = pdb
175 if not debugger or ui.plain():
176 if not debugger or ui.plain():
176 # if we are in HGPLAIN mode, then disable custom debugging
177 # if we are in HGPLAIN mode, then disable custom debugging
177 debugger = 'pdb'
178 debugger = 'pdb'
178 elif '--debugger' in req.args:
179 elif '--debugger' in req.args:
179 # This import can be slow for fancy debuggers, so only
180 # This import can be slow for fancy debuggers, so only
180 # do it when absolutely necessary, i.e. when actual
181 # do it when absolutely necessary, i.e. when actual
181 # debugging has been requested
182 # debugging has been requested
182 with demandimport.deactivated():
183 with demandimport.deactivated():
183 try:
184 try:
184 debugmod = __import__(debugger)
185 debugmod = __import__(debugger)
185 except ImportError:
186 except ImportError:
186 pass # Leave debugmod = pdb
187 pass # Leave debugmod = pdb
187
188
188 debugtrace[debugger] = debugmod.set_trace
189 debugtrace[debugger] = debugmod.set_trace
189 debugmortem[debugger] = debugmod.post_mortem
190 debugmortem[debugger] = debugmod.post_mortem
190
191
191 # enter the debugger before command execution
192 # enter the debugger before command execution
192 if '--debugger' in req.args:
193 if '--debugger' in req.args:
193 ui.warn(_("entering debugger - "
194 ui.warn(_("entering debugger - "
194 "type c to continue starting hg or h for help\n"))
195 "type c to continue starting hg or h for help\n"))
195
196
196 if (debugger != 'pdb' and
197 if (debugger != 'pdb' and
197 debugtrace[debugger] == debugtrace['pdb']):
198 debugtrace[debugger] == debugtrace['pdb']):
198 ui.warn(_("%s debugger specified "
199 ui.warn(_("%s debugger specified "
199 "but its module was not found\n") % debugger)
200 "but its module was not found\n") % debugger)
200 with demandimport.deactivated():
201 with demandimport.deactivated():
201 debugtrace[debugger]()
202 debugtrace[debugger]()
202 try:
203 try:
203 return _dispatch(req)
204 return _dispatch(req)
204 finally:
205 finally:
205 ui.flush()
206 ui.flush()
206 except: # re-raises
207 except: # re-raises
207 # enter the debugger when we hit an exception
208 # enter the debugger when we hit an exception
208 if '--debugger' in req.args:
209 if '--debugger' in req.args:
209 traceback.print_exc()
210 traceback.print_exc()
210 debugmortem[debugger](sys.exc_info()[2])
211 debugmortem[debugger](sys.exc_info()[2])
211 ui.traceback()
212 ui.traceback()
212 raise
213 raise
213
214
214 # Global exception handling, alphabetically
215 # Global exception handling, alphabetically
215 # Mercurial-specific first, followed by built-in and library exceptions
216 # Mercurial-specific first, followed by built-in and library exceptions
216 except error.AmbiguousCommand as inst:
217 except error.AmbiguousCommand as inst:
217 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
218 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
218 (inst.args[0], " ".join(inst.args[1])))
219 (inst.args[0], " ".join(inst.args[1])))
219 except error.ParseError as inst:
220 except error.ParseError as inst:
220 _formatparse(ui.warn, inst)
221 _formatparse(ui.warn, inst)
221 return -1
222 return -1
222 except error.LockHeld as inst:
223 except error.LockHeld as inst:
223 if inst.errno == errno.ETIMEDOUT:
224 if inst.errno == errno.ETIMEDOUT:
224 reason = _('timed out waiting for lock held by %s') % inst.locker
225 reason = _('timed out waiting for lock held by %s') % inst.locker
225 else:
226 else:
226 reason = _('lock held by %s') % inst.locker
227 reason = _('lock held by %s') % inst.locker
227 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
228 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
228 except error.LockUnavailable as inst:
229 except error.LockUnavailable as inst:
229 ui.warn(_("abort: could not lock %s: %s\n") %
230 ui.warn(_("abort: could not lock %s: %s\n") %
230 (inst.desc or inst.filename, inst.strerror))
231 (inst.desc or inst.filename, inst.strerror))
231 except error.CommandError as inst:
232 except error.CommandError as inst:
232 if inst.args[0]:
233 if inst.args[0]:
233 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
234 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
234 commands.help_(ui, inst.args[0], full=False, command=True)
235 commands.help_(ui, inst.args[0], full=False, command=True)
235 else:
236 else:
236 ui.warn(_("hg: %s\n") % inst.args[1])
237 ui.warn(_("hg: %s\n") % inst.args[1])
237 commands.help_(ui, 'shortlist')
238 commands.help_(ui, 'shortlist')
238 except error.OutOfBandError as inst:
239 except error.OutOfBandError as inst:
239 if inst.args:
240 if inst.args:
240 msg = _("abort: remote error:\n")
241 msg = _("abort: remote error:\n")
241 else:
242 else:
242 msg = _("abort: remote error\n")
243 msg = _("abort: remote error\n")
243 ui.warn(msg)
244 ui.warn(msg)
244 if inst.args:
245 if inst.args:
245 ui.warn(''.join(inst.args))
246 ui.warn(''.join(inst.args))
246 if inst.hint:
247 if inst.hint:
247 ui.warn('(%s)\n' % inst.hint)
248 ui.warn('(%s)\n' % inst.hint)
248 except error.RepoError as inst:
249 except error.RepoError as inst:
249 ui.warn(_("abort: %s!\n") % inst)
250 ui.warn(_("abort: %s!\n") % inst)
250 if inst.hint:
251 if inst.hint:
251 ui.warn(_("(%s)\n") % inst.hint)
252 ui.warn(_("(%s)\n") % inst.hint)
252 except error.ResponseError as inst:
253 except error.ResponseError as inst:
253 ui.warn(_("abort: %s") % inst.args[0])
254 ui.warn(_("abort: %s") % inst.args[0])
254 if not isinstance(inst.args[1], basestring):
255 if not isinstance(inst.args[1], basestring):
255 ui.warn(" %r\n" % (inst.args[1],))
256 ui.warn(" %r\n" % (inst.args[1],))
256 elif not inst.args[1]:
257 elif not inst.args[1]:
257 ui.warn(_(" empty string\n"))
258 ui.warn(_(" empty string\n"))
258 else:
259 else:
259 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
260 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
260 except error.CensoredNodeError as inst:
261 except error.CensoredNodeError as inst:
261 ui.warn(_("abort: file censored %s!\n") % inst)
262 ui.warn(_("abort: file censored %s!\n") % inst)
262 except error.RevlogError as inst:
263 except error.RevlogError as inst:
263 ui.warn(_("abort: %s!\n") % inst)
264 ui.warn(_("abort: %s!\n") % inst)
264 except error.SignalInterrupt:
265 except error.SignalInterrupt:
265 ui.warn(_("killed!\n"))
266 ui.warn(_("killed!\n"))
266 except error.UnknownCommand as inst:
267 except error.UnknownCommand as inst:
267 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
268 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
268 try:
269 try:
269 # check if the command is in a disabled extension
270 # check if the command is in a disabled extension
270 # (but don't check for extensions themselves)
271 # (but don't check for extensions themselves)
271 commands.help_(ui, inst.args[0], unknowncmd=True)
272 commands.help_(ui, inst.args[0], unknowncmd=True)
272 except (error.UnknownCommand, error.Abort):
273 except (error.UnknownCommand, error.Abort):
273 suggested = False
274 suggested = False
274 if len(inst.args) == 2:
275 if len(inst.args) == 2:
275 sim = _getsimilar(inst.args[1], inst.args[0])
276 sim = _getsimilar(inst.args[1], inst.args[0])
276 if sim:
277 if sim:
277 _reportsimilar(ui.warn, sim)
278 _reportsimilar(ui.warn, sim)
278 suggested = True
279 suggested = True
279 if not suggested:
280 if not suggested:
280 commands.help_(ui, 'shortlist')
281 commands.help_(ui, 'shortlist')
281 except error.InterventionRequired as inst:
282 except error.InterventionRequired as inst:
282 ui.warn("%s\n" % inst)
283 ui.warn("%s\n" % inst)
283 if inst.hint:
284 if inst.hint:
284 ui.warn(_("(%s)\n") % inst.hint)
285 ui.warn(_("(%s)\n") % inst.hint)
285 return 1
286 return 1
286 except error.Abort as inst:
287 except error.Abort as inst:
287 ui.warn(_("abort: %s\n") % inst)
288 ui.warn(_("abort: %s\n") % inst)
288 if inst.hint:
289 if inst.hint:
289 ui.warn(_("(%s)\n") % inst.hint)
290 ui.warn(_("(%s)\n") % inst.hint)
290 except ImportError as inst:
291 except ImportError as inst:
291 ui.warn(_("abort: %s!\n") % inst)
292 ui.warn(_("abort: %s!\n") % inst)
292 m = str(inst).split()[-1]
293 m = str(inst).split()[-1]
293 if m in "mpatch bdiff".split():
294 if m in "mpatch bdiff".split():
294 ui.warn(_("(did you forget to compile extensions?)\n"))
295 ui.warn(_("(did you forget to compile extensions?)\n"))
295 elif m in "zlib".split():
296 elif m in "zlib".split():
296 ui.warn(_("(is your Python install correct?)\n"))
297 ui.warn(_("(is your Python install correct?)\n"))
297 except IOError as inst:
298 except IOError as inst:
298 if util.safehasattr(inst, "code"):
299 if util.safehasattr(inst, "code"):
299 ui.warn(_("abort: %s\n") % inst)
300 ui.warn(_("abort: %s\n") % inst)
300 elif util.safehasattr(inst, "reason"):
301 elif util.safehasattr(inst, "reason"):
301 try: # usually it is in the form (errno, strerror)
302 try: # usually it is in the form (errno, strerror)
302 reason = inst.reason.args[1]
303 reason = inst.reason.args[1]
303 except (AttributeError, IndexError):
304 except (AttributeError, IndexError):
304 # it might be anything, for example a string
305 # it might be anything, for example a string
305 reason = inst.reason
306 reason = inst.reason
306 if isinstance(reason, unicode):
307 if isinstance(reason, unicode):
307 # SSLError of Python 2.7.9 contains a unicode
308 # SSLError of Python 2.7.9 contains a unicode
308 reason = reason.encode(encoding.encoding, 'replace')
309 reason = reason.encode(encoding.encoding, 'replace')
309 ui.warn(_("abort: error: %s\n") % reason)
310 ui.warn(_("abort: error: %s\n") % reason)
310 elif (util.safehasattr(inst, "args")
311 elif (util.safehasattr(inst, "args")
311 and inst.args and inst.args[0] == errno.EPIPE):
312 and inst.args and inst.args[0] == errno.EPIPE):
312 pass
313 pass
313 elif getattr(inst, "strerror", None):
314 elif getattr(inst, "strerror", None):
314 if getattr(inst, "filename", None):
315 if getattr(inst, "filename", None):
315 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
316 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
316 else:
317 else:
317 ui.warn(_("abort: %s\n") % inst.strerror)
318 ui.warn(_("abort: %s\n") % inst.strerror)
318 else:
319 else:
319 raise
320 raise
320 except OSError as inst:
321 except OSError as inst:
321 if getattr(inst, "filename", None) is not None:
322 if getattr(inst, "filename", None) is not None:
322 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
323 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
323 else:
324 else:
324 ui.warn(_("abort: %s\n") % inst.strerror)
325 ui.warn(_("abort: %s\n") % inst.strerror)
325 except KeyboardInterrupt:
326 except KeyboardInterrupt:
326 raise
327 raise
327 except MemoryError:
328 except MemoryError:
328 ui.warn(_("abort: out of memory\n"))
329 ui.warn(_("abort: out of memory\n"))
329 except SystemExit as inst:
330 except SystemExit as inst:
330 # Commands shouldn't sys.exit directly, but give a return code.
331 # Commands shouldn't sys.exit directly, but give a return code.
331 # Just in case catch this and and pass exit code to caller.
332 # Just in case catch this and and pass exit code to caller.
332 return inst.code
333 return inst.code
333 except socket.error as inst:
334 except socket.error as inst:
334 ui.warn(_("abort: %s\n") % inst.args[-1])
335 ui.warn(_("abort: %s\n") % inst.args[-1])
335 except: # re-raises
336 except: # re-raises
336 # For compatibility checking, we discard the portion of the hg
337 # For compatibility checking, we discard the portion of the hg
337 # version after the + on the assumption that if a "normal
338 # version after the + on the assumption that if a "normal
338 # user" is running a build with a + in it the packager
339 # user" is running a build with a + in it the packager
339 # probably built from fairly close to a tag and anyone with a
340 # probably built from fairly close to a tag and anyone with a
340 # 'make local' copy of hg (where the version number can be out
341 # 'make local' copy of hg (where the version number can be out
341 # of date) will be clueful enough to notice the implausible
342 # of date) will be clueful enough to notice the implausible
342 # version number and try updating.
343 # version number and try updating.
343 ct = util.versiontuple(n=2)
344 ct = util.versiontuple(n=2)
344 worst = None, ct, ''
345 worst = None, ct, ''
345 if ui.config('ui', 'supportcontact', None) is None:
346 if ui.config('ui', 'supportcontact', None) is None:
346 for name, mod in extensions.extensions():
347 for name, mod in extensions.extensions():
347 testedwith = getattr(mod, 'testedwith', '')
348 testedwith = getattr(mod, 'testedwith', '')
348 report = getattr(mod, 'buglink', _('the extension author.'))
349 report = getattr(mod, 'buglink', _('the extension author.'))
349 if not testedwith.strip():
350 if not testedwith.strip():
350 # We found an untested extension. It's likely the culprit.
351 # We found an untested extension. It's likely the culprit.
351 worst = name, 'unknown', report
352 worst = name, 'unknown', report
352 break
353 break
353
354
354 # Never blame on extensions bundled with Mercurial.
355 # Never blame on extensions bundled with Mercurial.
355 if testedwith == 'internal':
356 if testedwith == 'internal':
356 continue
357 continue
357
358
358 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
359 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
359 if ct in tested:
360 if ct in tested:
360 continue
361 continue
361
362
362 lower = [t for t in tested if t < ct]
363 lower = [t for t in tested if t < ct]
363 nearest = max(lower or tested)
364 nearest = max(lower or tested)
364 if worst[0] is None or nearest < worst[1]:
365 if worst[0] is None or nearest < worst[1]:
365 worst = name, nearest, report
366 worst = name, nearest, report
366 if worst[0] is not None:
367 if worst[0] is not None:
367 name, testedwith, report = worst
368 name, testedwith, report = worst
368 if not isinstance(testedwith, str):
369 if not isinstance(testedwith, str):
369 testedwith = '.'.join([str(c) for c in testedwith])
370 testedwith = '.'.join([str(c) for c in testedwith])
370 warning = (_('** Unknown exception encountered with '
371 warning = (_('** Unknown exception encountered with '
371 'possibly-broken third-party extension %s\n'
372 'possibly-broken third-party extension %s\n'
372 '** which supports versions %s of Mercurial.\n'
373 '** which supports versions %s of Mercurial.\n'
373 '** Please disable %s and try your action again.\n'
374 '** Please disable %s and try your action again.\n'
374 '** If that fixes the bug please report it to %s\n')
375 '** If that fixes the bug please report it to %s\n')
375 % (name, testedwith, name, report))
376 % (name, testedwith, name, report))
376 else:
377 else:
377 bugtracker = ui.config('ui', 'supportcontact', None)
378 bugtracker = ui.config('ui', 'supportcontact', None)
378 if bugtracker is None:
379 if bugtracker is None:
379 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
380 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
380 warning = (_("** unknown exception encountered, "
381 warning = (_("** unknown exception encountered, "
381 "please report by visiting\n** ") + bugtracker + '\n')
382 "please report by visiting\n** ") + bugtracker + '\n')
382 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
383 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
383 (_("** Mercurial Distributed SCM (version %s)\n") %
384 (_("** Mercurial Distributed SCM (version %s)\n") %
384 util.version()) +
385 util.version()) +
385 (_("** Extensions loaded: %s\n") %
386 (_("** Extensions loaded: %s\n") %
386 ", ".join([x[0] for x in extensions.extensions()])))
387 ", ".join([x[0] for x in extensions.extensions()])))
387 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
388 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
388 ui.warn(warning)
389 ui.warn(warning)
389 raise
390 raise
390
391
391 return -1
392 return -1
392
393
393 def aliasargs(fn, givenargs):
394 def aliasargs(fn, givenargs):
394 args = getattr(fn, 'args', [])
395 args = getattr(fn, 'args', [])
395 if args:
396 if args:
396 cmd = ' '.join(map(util.shellquote, args))
397 cmd = ' '.join(map(util.shellquote, args))
397
398
398 nums = []
399 nums = []
399 def replacer(m):
400 def replacer(m):
400 num = int(m.group(1)) - 1
401 num = int(m.group(1)) - 1
401 nums.append(num)
402 nums.append(num)
402 if num < len(givenargs):
403 if num < len(givenargs):
403 return givenargs[num]
404 return givenargs[num]
404 raise error.Abort(_('too few arguments for command alias'))
405 raise error.Abort(_('too few arguments for command alias'))
405 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
406 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
406 givenargs = [x for i, x in enumerate(givenargs)
407 givenargs = [x for i, x in enumerate(givenargs)
407 if i not in nums]
408 if i not in nums]
408 args = shlex.split(cmd)
409 args = shlex.split(cmd)
409 return args + givenargs
410 return args + givenargs
410
411
411 def aliasinterpolate(name, args, cmd):
412 def aliasinterpolate(name, args, cmd):
412 '''interpolate args into cmd for shell aliases
413 '''interpolate args into cmd for shell aliases
413
414
414 This also handles $0, $@ and "$@".
415 This also handles $0, $@ and "$@".
415 '''
416 '''
416 # util.interpolate can't deal with "$@" (with quotes) because it's only
417 # util.interpolate can't deal with "$@" (with quotes) because it's only
417 # built to match prefix + patterns.
418 # built to match prefix + patterns.
418 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
419 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
419 replacemap['$0'] = name
420 replacemap['$0'] = name
420 replacemap['$$'] = '$'
421 replacemap['$$'] = '$'
421 replacemap['$@'] = ' '.join(args)
422 replacemap['$@'] = ' '.join(args)
422 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
423 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
423 # parameters, separated out into words. Emulate the same behavior here by
424 # parameters, separated out into words. Emulate the same behavior here by
424 # quoting the arguments individually. POSIX shells will then typically
425 # quoting the arguments individually. POSIX shells will then typically
425 # tokenize each argument into exactly one word.
426 # tokenize each argument into exactly one word.
426 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
427 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
427 # escape '\$' for regex
428 # escape '\$' for regex
428 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
429 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
429 r = re.compile(regex)
430 r = re.compile(regex)
430 return r.sub(lambda x: replacemap[x.group()], cmd)
431 return r.sub(lambda x: replacemap[x.group()], cmd)
431
432
432 class cmdalias(object):
433 class cmdalias(object):
433 def __init__(self, name, definition, cmdtable):
434 def __init__(self, name, definition, cmdtable):
434 self.name = self.cmd = name
435 self.name = self.cmd = name
435 self.cmdname = ''
436 self.cmdname = ''
436 self.definition = definition
437 self.definition = definition
437 self.fn = None
438 self.fn = None
438 self.args = []
439 self.args = []
439 self.opts = []
440 self.opts = []
440 self.help = ''
441 self.help = ''
441 self.badalias = None
442 self.badalias = None
442 self.unknowncmd = False
443 self.unknowncmd = False
443
444
444 try:
445 try:
445 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
446 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
446 for alias, e in cmdtable.iteritems():
447 for alias, e in cmdtable.iteritems():
447 if e is entry:
448 if e is entry:
448 self.cmd = alias
449 self.cmd = alias
449 break
450 break
450 self.shadows = True
451 self.shadows = True
451 except error.UnknownCommand:
452 except error.UnknownCommand:
452 self.shadows = False
453 self.shadows = False
453
454
454 if not self.definition:
455 if not self.definition:
455 self.badalias = _("no definition for alias '%s'") % self.name
456 self.badalias = _("no definition for alias '%s'") % self.name
456 return
457 return
457
458
458 if self.definition.startswith('!'):
459 if self.definition.startswith('!'):
459 self.shell = True
460 self.shell = True
460 def fn(ui, *args):
461 def fn(ui, *args):
461 env = {'HG_ARGS': ' '.join((self.name,) + args)}
462 env = {'HG_ARGS': ' '.join((self.name,) + args)}
462 def _checkvar(m):
463 def _checkvar(m):
463 if m.groups()[0] == '$':
464 if m.groups()[0] == '$':
464 return m.group()
465 return m.group()
465 elif int(m.groups()[0]) <= len(args):
466 elif int(m.groups()[0]) <= len(args):
466 return m.group()
467 return m.group()
467 else:
468 else:
468 ui.debug("No argument found for substitution "
469 ui.debug("No argument found for substitution "
469 "of %i variable in alias '%s' definition."
470 "of %i variable in alias '%s' definition."
470 % (int(m.groups()[0]), self.name))
471 % (int(m.groups()[0]), self.name))
471 return ''
472 return ''
472 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
473 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
473 cmd = aliasinterpolate(self.name, args, cmd)
474 cmd = aliasinterpolate(self.name, args, cmd)
474 return ui.system(cmd, environ=env)
475 return ui.system(cmd, environ=env)
475 self.fn = fn
476 self.fn = fn
476 return
477 return
477
478
478 try:
479 try:
479 args = shlex.split(self.definition)
480 args = shlex.split(self.definition)
480 except ValueError as inst:
481 except ValueError as inst:
481 self.badalias = (_("error in definition for alias '%s': %s")
482 self.badalias = (_("error in definition for alias '%s': %s")
482 % (self.name, inst))
483 % (self.name, inst))
483 return
484 return
484 self.cmdname = cmd = args.pop(0)
485 self.cmdname = cmd = args.pop(0)
485 args = map(util.expandpath, args)
486 args = map(util.expandpath, args)
486
487
487 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
488 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
488 if _earlygetopt([invalidarg], args):
489 if _earlygetopt([invalidarg], args):
489 self.badalias = (_("error in definition for alias '%s': %s may "
490 self.badalias = (_("error in definition for alias '%s': %s may "
490 "only be given on the command line")
491 "only be given on the command line")
491 % (self.name, invalidarg))
492 % (self.name, invalidarg))
492 return
493 return
493
494
494 try:
495 try:
495 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
496 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
496 if len(tableentry) > 2:
497 if len(tableentry) > 2:
497 self.fn, self.opts, self.help = tableentry
498 self.fn, self.opts, self.help = tableentry
498 else:
499 else:
499 self.fn, self.opts = tableentry
500 self.fn, self.opts = tableentry
500
501
501 self.args = aliasargs(self.fn, args)
502 self.args = aliasargs(self.fn, args)
502 if self.help.startswith("hg " + cmd):
503 if self.help.startswith("hg " + cmd):
503 # drop prefix in old-style help lines so hg shows the alias
504 # drop prefix in old-style help lines so hg shows the alias
504 self.help = self.help[4 + len(cmd):]
505 self.help = self.help[4 + len(cmd):]
505 self.__doc__ = self.fn.__doc__
506 self.__doc__ = self.fn.__doc__
506
507
507 except error.UnknownCommand:
508 except error.UnknownCommand:
508 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
509 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
509 % (self.name, cmd))
510 % (self.name, cmd))
510 self.unknowncmd = True
511 self.unknowncmd = True
511 except error.AmbiguousCommand:
512 except error.AmbiguousCommand:
512 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
513 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
513 % (self.name, cmd))
514 % (self.name, cmd))
514
515
515 def __getattr__(self, name):
516 def __getattr__(self, name):
516 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
517 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
517 if name not in adefaults:
518 if name not in adefaults:
518 raise AttributeError(name)
519 raise AttributeError(name)
519 if self.badalias or util.safehasattr(self, 'shell'):
520 if self.badalias or util.safehasattr(self, 'shell'):
520 return adefaults[name]
521 return adefaults[name]
521 return getattr(self.fn, name)
522 return getattr(self.fn, name)
522
523
523 def __call__(self, ui, *args, **opts):
524 def __call__(self, ui, *args, **opts):
524 if self.badalias:
525 if self.badalias:
525 hint = None
526 hint = None
526 if self.unknowncmd:
527 if self.unknowncmd:
527 try:
528 try:
528 # check if the command is in a disabled extension
529 # check if the command is in a disabled extension
529 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
530 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
530 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
531 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
531 except error.UnknownCommand:
532 except error.UnknownCommand:
532 pass
533 pass
533 raise error.Abort(self.badalias, hint=hint)
534 raise error.Abort(self.badalias, hint=hint)
534 if self.shadows:
535 if self.shadows:
535 ui.debug("alias '%s' shadows command '%s'\n" %
536 ui.debug("alias '%s' shadows command '%s'\n" %
536 (self.name, self.cmdname))
537 (self.name, self.cmdname))
537
538
538 if util.safehasattr(self, 'shell'):
539 if util.safehasattr(self, 'shell'):
539 return self.fn(ui, *args, **opts)
540 return self.fn(ui, *args, **opts)
540 else:
541 else:
541 try:
542 try:
542 return util.checksignature(self.fn)(ui, *args, **opts)
543 return util.checksignature(self.fn)(ui, *args, **opts)
543 except error.SignatureError:
544 except error.SignatureError:
544 args = ' '.join([self.cmdname] + self.args)
545 args = ' '.join([self.cmdname] + self.args)
545 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
546 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
546 raise
547 raise
547
548
548 def addaliases(ui, cmdtable):
549 def addaliases(ui, cmdtable):
549 # aliases are processed after extensions have been loaded, so they
550 # aliases are processed after extensions have been loaded, so they
550 # may use extension commands. Aliases can also use other alias definitions,
551 # may use extension commands. Aliases can also use other alias definitions,
551 # but only if they have been defined prior to the current definition.
552 # but only if they have been defined prior to the current definition.
552 for alias, definition in ui.configitems('alias'):
553 for alias, definition in ui.configitems('alias'):
553 aliasdef = cmdalias(alias, definition, cmdtable)
554 aliasdef = cmdalias(alias, definition, cmdtable)
554
555
555 try:
556 try:
556 olddef = cmdtable[aliasdef.cmd][0]
557 olddef = cmdtable[aliasdef.cmd][0]
557 if olddef.definition == aliasdef.definition:
558 if olddef.definition == aliasdef.definition:
558 continue
559 continue
559 except (KeyError, AttributeError):
560 except (KeyError, AttributeError):
560 # definition might not exist or it might not be a cmdalias
561 # definition might not exist or it might not be a cmdalias
561 pass
562 pass
562
563
563 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
564 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
564
565
565 def _parse(ui, args):
566 def _parse(ui, args):
566 options = {}
567 options = {}
567 cmdoptions = {}
568 cmdoptions = {}
568
569
569 try:
570 try:
570 args = fancyopts.fancyopts(args, commands.globalopts, options)
571 args = fancyopts.fancyopts(args, commands.globalopts, options)
571 except fancyopts.getopt.GetoptError as inst:
572 except fancyopts.getopt.GetoptError as inst:
572 raise error.CommandError(None, inst)
573 raise error.CommandError(None, inst)
573
574
574 if args:
575 if args:
575 cmd, args = args[0], args[1:]
576 cmd, args = args[0], args[1:]
576 aliases, entry = cmdutil.findcmd(cmd, commands.table,
577 aliases, entry = cmdutil.findcmd(cmd, commands.table,
577 ui.configbool("ui", "strict"))
578 ui.configbool("ui", "strict"))
578 cmd = aliases[0]
579 cmd = aliases[0]
579 args = aliasargs(entry[0], args)
580 args = aliasargs(entry[0], args)
580 defaults = ui.config("defaults", cmd)
581 defaults = ui.config("defaults", cmd)
581 if defaults:
582 if defaults:
582 args = map(util.expandpath, shlex.split(defaults)) + args
583 args = map(util.expandpath, shlex.split(defaults)) + args
583 c = list(entry[1])
584 c = list(entry[1])
584 else:
585 else:
585 cmd = None
586 cmd = None
586 c = []
587 c = []
587
588
588 # combine global options into local
589 # combine global options into local
589 for o in commands.globalopts:
590 for o in commands.globalopts:
590 c.append((o[0], o[1], options[o[1]], o[3]))
591 c.append((o[0], o[1], options[o[1]], o[3]))
591
592
592 try:
593 try:
593 args = fancyopts.fancyopts(args, c, cmdoptions, True)
594 args = fancyopts.fancyopts(args, c, cmdoptions, True)
594 except fancyopts.getopt.GetoptError as inst:
595 except fancyopts.getopt.GetoptError as inst:
595 raise error.CommandError(cmd, inst)
596 raise error.CommandError(cmd, inst)
596
597
597 # separate global options back out
598 # separate global options back out
598 for o in commands.globalopts:
599 for o in commands.globalopts:
599 n = o[1]
600 n = o[1]
600 options[n] = cmdoptions[n]
601 options[n] = cmdoptions[n]
601 del cmdoptions[n]
602 del cmdoptions[n]
602
603
603 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
604 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
604
605
605 def _parseconfig(ui, config):
606 def _parseconfig(ui, config):
606 """parse the --config options from the command line"""
607 """parse the --config options from the command line"""
607 configs = []
608 configs = []
608
609
609 for cfg in config:
610 for cfg in config:
610 try:
611 try:
611 name, value = [cfgelem.strip()
612 name, value = [cfgelem.strip()
612 for cfgelem in cfg.split('=', 1)]
613 for cfgelem in cfg.split('=', 1)]
613 section, name = name.split('.', 1)
614 section, name = name.split('.', 1)
614 if not section or not name:
615 if not section or not name:
615 raise IndexError
616 raise IndexError
616 ui.setconfig(section, name, value, '--config')
617 ui.setconfig(section, name, value, '--config')
617 configs.append((section, name, value))
618 configs.append((section, name, value))
618 except (IndexError, ValueError):
619 except (IndexError, ValueError):
619 raise error.Abort(_('malformed --config option: %r '
620 raise error.Abort(_('malformed --config option: %r '
620 '(use --config section.name=value)') % cfg)
621 '(use --config section.name=value)') % cfg)
621
622
622 return configs
623 return configs
623
624
624 def _earlygetopt(aliases, args):
625 def _earlygetopt(aliases, args):
625 """Return list of values for an option (or aliases).
626 """Return list of values for an option (or aliases).
626
627
627 The values are listed in the order they appear in args.
628 The values are listed in the order they appear in args.
628 The options and values are removed from args.
629 The options and values are removed from args.
629
630
630 >>> args = ['x', '--cwd', 'foo', 'y']
631 >>> args = ['x', '--cwd', 'foo', 'y']
631 >>> _earlygetopt(['--cwd'], args), args
632 >>> _earlygetopt(['--cwd'], args), args
632 (['foo'], ['x', 'y'])
633 (['foo'], ['x', 'y'])
633
634
634 >>> args = ['x', '--cwd=bar', 'y']
635 >>> args = ['x', '--cwd=bar', 'y']
635 >>> _earlygetopt(['--cwd'], args), args
636 >>> _earlygetopt(['--cwd'], args), args
636 (['bar'], ['x', 'y'])
637 (['bar'], ['x', 'y'])
637
638
638 >>> args = ['x', '-R', 'foo', 'y']
639 >>> args = ['x', '-R', 'foo', 'y']
639 >>> _earlygetopt(['-R'], args), args
640 >>> _earlygetopt(['-R'], args), args
640 (['foo'], ['x', 'y'])
641 (['foo'], ['x', 'y'])
641
642
642 >>> args = ['x', '-Rbar', 'y']
643 >>> args = ['x', '-Rbar', 'y']
643 >>> _earlygetopt(['-R'], args), args
644 >>> _earlygetopt(['-R'], args), args
644 (['bar'], ['x', 'y'])
645 (['bar'], ['x', 'y'])
645 """
646 """
646 try:
647 try:
647 argcount = args.index("--")
648 argcount = args.index("--")
648 except ValueError:
649 except ValueError:
649 argcount = len(args)
650 argcount = len(args)
650 shortopts = [opt for opt in aliases if len(opt) == 2]
651 shortopts = [opt for opt in aliases if len(opt) == 2]
651 values = []
652 values = []
652 pos = 0
653 pos = 0
653 while pos < argcount:
654 while pos < argcount:
654 fullarg = arg = args[pos]
655 fullarg = arg = args[pos]
655 equals = arg.find('=')
656 equals = arg.find('=')
656 if equals > -1:
657 if equals > -1:
657 arg = arg[:equals]
658 arg = arg[:equals]
658 if arg in aliases:
659 if arg in aliases:
659 del args[pos]
660 del args[pos]
660 if equals > -1:
661 if equals > -1:
661 values.append(fullarg[equals + 1:])
662 values.append(fullarg[equals + 1:])
662 argcount -= 1
663 argcount -= 1
663 else:
664 else:
664 if pos + 1 >= argcount:
665 if pos + 1 >= argcount:
665 # ignore and let getopt report an error if there is no value
666 # ignore and let getopt report an error if there is no value
666 break
667 break
667 values.append(args.pop(pos))
668 values.append(args.pop(pos))
668 argcount -= 2
669 argcount -= 2
669 elif arg[:2] in shortopts:
670 elif arg[:2] in shortopts:
670 # short option can have no following space, e.g. hg log -Rfoo
671 # short option can have no following space, e.g. hg log -Rfoo
671 values.append(args.pop(pos)[2:])
672 values.append(args.pop(pos)[2:])
672 argcount -= 1
673 argcount -= 1
673 else:
674 else:
674 pos += 1
675 pos += 1
675 return values
676 return values
676
677
677 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
678 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
678 # run pre-hook, and abort if it fails
679 # run pre-hook, and abort if it fails
679 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
680 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
680 pats=cmdpats, opts=cmdoptions)
681 pats=cmdpats, opts=cmdoptions)
681 ret = _runcommand(ui, options, cmd, d)
682 ret = _runcommand(ui, options, cmd, d)
682 # run post-hook, passing command result
683 # run post-hook, passing command result
683 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
684 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
684 result=ret, pats=cmdpats, opts=cmdoptions)
685 result=ret, pats=cmdpats, opts=cmdoptions)
685 return ret
686 return ret
686
687
687 def _getlocal(ui, rpath, wd=None):
688 def _getlocal(ui, rpath, wd=None):
688 """Return (path, local ui object) for the given target path.
689 """Return (path, local ui object) for the given target path.
689
690
690 Takes paths in [cwd]/.hg/hgrc into account."
691 Takes paths in [cwd]/.hg/hgrc into account."
691 """
692 """
692 if wd is None:
693 if wd is None:
693 try:
694 try:
694 wd = os.getcwd()
695 wd = os.getcwd()
695 except OSError as e:
696 except OSError as e:
696 raise error.Abort(_("error getting current working directory: %s") %
697 raise error.Abort(_("error getting current working directory: %s") %
697 e.strerror)
698 e.strerror)
698 path = cmdutil.findrepo(wd) or ""
699 path = cmdutil.findrepo(wd) or ""
699 if not path:
700 if not path:
700 lui = ui
701 lui = ui
701 else:
702 else:
702 lui = ui.copy()
703 lui = ui.copy()
703 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
704 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
704
705
705 if rpath and rpath[-1]:
706 if rpath and rpath[-1]:
706 path = lui.expandpath(rpath[-1])
707 path = lui.expandpath(rpath[-1])
707 lui = ui.copy()
708 lui = ui.copy()
708 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
709 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
709
710
710 return path, lui
711 return path, lui
711
712
712 def _checkshellalias(lui, ui, args, precheck=True):
713 def _checkshellalias(lui, ui, args, precheck=True):
713 """Return the function to run the shell alias, if it is required
714 """Return the function to run the shell alias, if it is required
714
715
715 'precheck' is whether this function is invoked before adding
716 'precheck' is whether this function is invoked before adding
716 aliases or not.
717 aliases or not.
717 """
718 """
718 options = {}
719 options = {}
719
720
720 try:
721 try:
721 args = fancyopts.fancyopts(args, commands.globalopts, options)
722 args = fancyopts.fancyopts(args, commands.globalopts, options)
722 except fancyopts.getopt.GetoptError:
723 except fancyopts.getopt.GetoptError:
723 return
724 return
724
725
725 if not args:
726 if not args:
726 return
727 return
727
728
728 if precheck:
729 if precheck:
729 strict = True
730 strict = True
730 cmdtable = commands.table.copy()
731 cmdtable = commands.table.copy()
731 addaliases(lui, cmdtable)
732 addaliases(lui, cmdtable)
732 else:
733 else:
733 strict = False
734 strict = False
734 cmdtable = commands.table
735 cmdtable = commands.table
735
736
736 cmd = args[0]
737 cmd = args[0]
737 try:
738 try:
738 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
739 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
739 except (error.AmbiguousCommand, error.UnknownCommand):
740 except (error.AmbiguousCommand, error.UnknownCommand):
740 return
741 return
741
742
742 cmd = aliases[0]
743 cmd = aliases[0]
743 fn = entry[0]
744 fn = entry[0]
744
745
745 if cmd and util.safehasattr(fn, 'shell'):
746 if cmd and util.safehasattr(fn, 'shell'):
746 d = lambda: fn(ui, *args[1:])
747 d = lambda: fn(ui, *args[1:])
747 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
748 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
748 [], {})
749 [], {})
749
750
750 def _cmdattr(ui, cmd, func, attr):
751 def _cmdattr(ui, cmd, func, attr):
751 try:
752 try:
752 return getattr(func, attr)
753 return getattr(func, attr)
753 except AttributeError:
754 except AttributeError:
754 ui.deprecwarn("missing attribute '%s', use @command decorator "
755 ui.deprecwarn("missing attribute '%s', use @command decorator "
755 "to register '%s'" % (attr, cmd), '3.8')
756 "to register '%s'" % (attr, cmd), '3.8')
756 return False
757 return False
757
758
758 _loaded = set()
759 _loaded = set()
759
760
760 # list of (objname, loadermod, loadername) tuple:
761 # list of (objname, loadermod, loadername) tuple:
761 # - objname is the name of an object in extension module, from which
762 # - objname is the name of an object in extension module, from which
762 # extra information is loaded
763 # extra information is loaded
763 # - loadermod is the module where loader is placed
764 # - loadermod is the module where loader is placed
764 # - loadername is the name of the function, which takes (ui, extensionname,
765 # - loadername is the name of the function, which takes (ui, extensionname,
765 # extraobj) arguments
766 # extraobj) arguments
766 extraloaders = [
767 extraloaders = [
767 ('cmdtable', commands, 'loadcmdtable'),
768 ('cmdtable', commands, 'loadcmdtable'),
768 ('filesetpredicate', fileset, 'loadpredicate'),
769 ('filesetpredicate', fileset, 'loadpredicate'),
769 ('revsetpredicate', revset, 'loadpredicate'),
770 ('revsetpredicate', revset, 'loadpredicate'),
770 ('templatefilter', templatefilters, 'loadfilter'),
771 ('templatefilter', templatefilters, 'loadfilter'),
772 ('templatefunc', templater, 'loadfunction'),
771 ('templatekeyword', templatekw, 'loadkeyword'),
773 ('templatekeyword', templatekw, 'loadkeyword'),
772 ]
774 ]
773
775
774 def _dispatch(req):
776 def _dispatch(req):
775 args = req.args
777 args = req.args
776 ui = req.ui
778 ui = req.ui
777
779
778 # check for cwd
780 # check for cwd
779 cwd = _earlygetopt(['--cwd'], args)
781 cwd = _earlygetopt(['--cwd'], args)
780 if cwd:
782 if cwd:
781 os.chdir(cwd[-1])
783 os.chdir(cwd[-1])
782
784
783 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
785 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
784 path, lui = _getlocal(ui, rpath)
786 path, lui = _getlocal(ui, rpath)
785
787
786 # Now that we're operating in the right directory/repository with
788 # Now that we're operating in the right directory/repository with
787 # the right config settings, check for shell aliases
789 # the right config settings, check for shell aliases
788 shellaliasfn = _checkshellalias(lui, ui, args)
790 shellaliasfn = _checkshellalias(lui, ui, args)
789 if shellaliasfn:
791 if shellaliasfn:
790 return shellaliasfn()
792 return shellaliasfn()
791
793
792 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
794 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
793 # reposetup. Programs like TortoiseHg will call _dispatch several
795 # reposetup. Programs like TortoiseHg will call _dispatch several
794 # times so we keep track of configured extensions in _loaded.
796 # times so we keep track of configured extensions in _loaded.
795 extensions.loadall(lui)
797 extensions.loadall(lui)
796 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
798 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
797 # Propagate any changes to lui.__class__ by extensions
799 # Propagate any changes to lui.__class__ by extensions
798 ui.__class__ = lui.__class__
800 ui.__class__ = lui.__class__
799
801
800 # (uisetup and extsetup are handled in extensions.loadall)
802 # (uisetup and extsetup are handled in extensions.loadall)
801
803
802 for name, module in exts:
804 for name, module in exts:
803 for objname, loadermod, loadername in extraloaders:
805 for objname, loadermod, loadername in extraloaders:
804 extraobj = getattr(module, objname, None)
806 extraobj = getattr(module, objname, None)
805 if extraobj is not None:
807 if extraobj is not None:
806 getattr(loadermod, loadername)(ui, name, extraobj)
808 getattr(loadermod, loadername)(ui, name, extraobj)
807 _loaded.add(name)
809 _loaded.add(name)
808
810
809 # (reposetup is handled in hg.repository)
811 # (reposetup is handled in hg.repository)
810
812
811 addaliases(lui, commands.table)
813 addaliases(lui, commands.table)
812
814
813 if not lui.configbool("ui", "strict"):
815 if not lui.configbool("ui", "strict"):
814 # All aliases and commands are completely defined, now.
816 # All aliases and commands are completely defined, now.
815 # Check abbreviation/ambiguity of shell alias again, because shell
817 # Check abbreviation/ambiguity of shell alias again, because shell
816 # alias may cause failure of "_parse" (see issue4355)
818 # alias may cause failure of "_parse" (see issue4355)
817 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
819 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
818 if shellaliasfn:
820 if shellaliasfn:
819 return shellaliasfn()
821 return shellaliasfn()
820
822
821 # check for fallback encoding
823 # check for fallback encoding
822 fallback = lui.config('ui', 'fallbackencoding')
824 fallback = lui.config('ui', 'fallbackencoding')
823 if fallback:
825 if fallback:
824 encoding.fallbackencoding = fallback
826 encoding.fallbackencoding = fallback
825
827
826 fullargs = args
828 fullargs = args
827 cmd, func, args, options, cmdoptions = _parse(lui, args)
829 cmd, func, args, options, cmdoptions = _parse(lui, args)
828
830
829 if options["config"]:
831 if options["config"]:
830 raise error.Abort(_("option --config may not be abbreviated!"))
832 raise error.Abort(_("option --config may not be abbreviated!"))
831 if options["cwd"]:
833 if options["cwd"]:
832 raise error.Abort(_("option --cwd may not be abbreviated!"))
834 raise error.Abort(_("option --cwd may not be abbreviated!"))
833 if options["repository"]:
835 if options["repository"]:
834 raise error.Abort(_(
836 raise error.Abort(_(
835 "option -R has to be separated from other options (e.g. not -qR) "
837 "option -R has to be separated from other options (e.g. not -qR) "
836 "and --repository may only be abbreviated as --repo!"))
838 "and --repository may only be abbreviated as --repo!"))
837
839
838 if options["encoding"]:
840 if options["encoding"]:
839 encoding.encoding = options["encoding"]
841 encoding.encoding = options["encoding"]
840 if options["encodingmode"]:
842 if options["encodingmode"]:
841 encoding.encodingmode = options["encodingmode"]
843 encoding.encodingmode = options["encodingmode"]
842 if options["time"]:
844 if options["time"]:
843 def get_times():
845 def get_times():
844 t = os.times()
846 t = os.times()
845 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
847 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
846 t = (t[0], t[1], t[2], t[3], time.clock())
848 t = (t[0], t[1], t[2], t[3], time.clock())
847 return t
849 return t
848 s = get_times()
850 s = get_times()
849 def print_time():
851 def print_time():
850 t = get_times()
852 t = get_times()
851 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
853 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
852 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
854 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
853 atexit.register(print_time)
855 atexit.register(print_time)
854
856
855 uis = set([ui, lui])
857 uis = set([ui, lui])
856
858
857 if req.repo:
859 if req.repo:
858 uis.add(req.repo.ui)
860 uis.add(req.repo.ui)
859
861
860 if options['verbose'] or options['debug'] or options['quiet']:
862 if options['verbose'] or options['debug'] or options['quiet']:
861 for opt in ('verbose', 'debug', 'quiet'):
863 for opt in ('verbose', 'debug', 'quiet'):
862 val = str(bool(options[opt]))
864 val = str(bool(options[opt]))
863 for ui_ in uis:
865 for ui_ in uis:
864 ui_.setconfig('ui', opt, val, '--' + opt)
866 ui_.setconfig('ui', opt, val, '--' + opt)
865
867
866 if options['traceback']:
868 if options['traceback']:
867 for ui_ in uis:
869 for ui_ in uis:
868 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
870 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
869
871
870 if options['noninteractive']:
872 if options['noninteractive']:
871 for ui_ in uis:
873 for ui_ in uis:
872 ui_.setconfig('ui', 'interactive', 'off', '-y')
874 ui_.setconfig('ui', 'interactive', 'off', '-y')
873
875
874 if cmdoptions.get('insecure', False):
876 if cmdoptions.get('insecure', False):
875 for ui_ in uis:
877 for ui_ in uis:
876 ui_.setconfig('web', 'cacerts', '!', '--insecure')
878 ui_.setconfig('web', 'cacerts', '!', '--insecure')
877
879
878 if options['version']:
880 if options['version']:
879 return commands.version_(ui)
881 return commands.version_(ui)
880 if options['help']:
882 if options['help']:
881 return commands.help_(ui, cmd, command=cmd is not None)
883 return commands.help_(ui, cmd, command=cmd is not None)
882 elif not cmd:
884 elif not cmd:
883 return commands.help_(ui, 'shortlist')
885 return commands.help_(ui, 'shortlist')
884
886
885 repo = None
887 repo = None
886 cmdpats = args[:]
888 cmdpats = args[:]
887 if not _cmdattr(ui, cmd, func, 'norepo'):
889 if not _cmdattr(ui, cmd, func, 'norepo'):
888 # use the repo from the request only if we don't have -R
890 # use the repo from the request only if we don't have -R
889 if not rpath and not cwd:
891 if not rpath and not cwd:
890 repo = req.repo
892 repo = req.repo
891
893
892 if repo:
894 if repo:
893 # set the descriptors of the repo ui to those of ui
895 # set the descriptors of the repo ui to those of ui
894 repo.ui.fin = ui.fin
896 repo.ui.fin = ui.fin
895 repo.ui.fout = ui.fout
897 repo.ui.fout = ui.fout
896 repo.ui.ferr = ui.ferr
898 repo.ui.ferr = ui.ferr
897 else:
899 else:
898 try:
900 try:
899 repo = hg.repository(ui, path=path)
901 repo = hg.repository(ui, path=path)
900 if not repo.local():
902 if not repo.local():
901 raise error.Abort(_("repository '%s' is not local") % path)
903 raise error.Abort(_("repository '%s' is not local") % path)
902 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
904 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
903 except error.RequirementError:
905 except error.RequirementError:
904 raise
906 raise
905 except error.RepoError:
907 except error.RepoError:
906 if rpath and rpath[-1]: # invalid -R path
908 if rpath and rpath[-1]: # invalid -R path
907 raise
909 raise
908 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
910 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
909 if (_cmdattr(ui, cmd, func, 'inferrepo') and
911 if (_cmdattr(ui, cmd, func, 'inferrepo') and
910 args and not path):
912 args and not path):
911 # try to infer -R from command args
913 # try to infer -R from command args
912 repos = map(cmdutil.findrepo, args)
914 repos = map(cmdutil.findrepo, args)
913 guess = repos[0]
915 guess = repos[0]
914 if guess and repos.count(guess) == len(repos):
916 if guess and repos.count(guess) == len(repos):
915 req.args = ['--repository', guess] + fullargs
917 req.args = ['--repository', guess] + fullargs
916 return _dispatch(req)
918 return _dispatch(req)
917 if not path:
919 if not path:
918 raise error.RepoError(_("no repository found in '%s'"
920 raise error.RepoError(_("no repository found in '%s'"
919 " (.hg not found)")
921 " (.hg not found)")
920 % os.getcwd())
922 % os.getcwd())
921 raise
923 raise
922 if repo:
924 if repo:
923 ui = repo.ui
925 ui = repo.ui
924 if options['hidden']:
926 if options['hidden']:
925 repo = repo.unfiltered()
927 repo = repo.unfiltered()
926 args.insert(0, repo)
928 args.insert(0, repo)
927 elif rpath:
929 elif rpath:
928 ui.warn(_("warning: --repository ignored\n"))
930 ui.warn(_("warning: --repository ignored\n"))
929
931
930 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
932 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
931 ui.log("command", '%s\n', msg)
933 ui.log("command", '%s\n', msg)
932 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
934 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
933 try:
935 try:
934 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
936 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
935 cmdpats, cmdoptions)
937 cmdpats, cmdoptions)
936 finally:
938 finally:
937 if repo and repo != req.repo:
939 if repo and repo != req.repo:
938 repo.close()
940 repo.close()
939
941
940 def lsprofile(ui, func, fp):
942 def lsprofile(ui, func, fp):
941 format = ui.config('profiling', 'format', default='text')
943 format = ui.config('profiling', 'format', default='text')
942 field = ui.config('profiling', 'sort', default='inlinetime')
944 field = ui.config('profiling', 'sort', default='inlinetime')
943 limit = ui.configint('profiling', 'limit', default=30)
945 limit = ui.configint('profiling', 'limit', default=30)
944 climit = ui.configint('profiling', 'nested', default=0)
946 climit = ui.configint('profiling', 'nested', default=0)
945
947
946 if format not in ['text', 'kcachegrind']:
948 if format not in ['text', 'kcachegrind']:
947 ui.warn(_("unrecognized profiling format '%s'"
949 ui.warn(_("unrecognized profiling format '%s'"
948 " - Ignored\n") % format)
950 " - Ignored\n") % format)
949 format = 'text'
951 format = 'text'
950
952
951 try:
953 try:
952 from . import lsprof
954 from . import lsprof
953 except ImportError:
955 except ImportError:
954 raise error.Abort(_(
956 raise error.Abort(_(
955 'lsprof not available - install from '
957 'lsprof not available - install from '
956 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
958 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
957 p = lsprof.Profiler()
959 p = lsprof.Profiler()
958 p.enable(subcalls=True)
960 p.enable(subcalls=True)
959 try:
961 try:
960 return func()
962 return func()
961 finally:
963 finally:
962 p.disable()
964 p.disable()
963
965
964 if format == 'kcachegrind':
966 if format == 'kcachegrind':
965 from . import lsprofcalltree
967 from . import lsprofcalltree
966 calltree = lsprofcalltree.KCacheGrind(p)
968 calltree = lsprofcalltree.KCacheGrind(p)
967 calltree.output(fp)
969 calltree.output(fp)
968 else:
970 else:
969 # format == 'text'
971 # format == 'text'
970 stats = lsprof.Stats(p.getstats())
972 stats = lsprof.Stats(p.getstats())
971 stats.sort(field)
973 stats.sort(field)
972 stats.pprint(limit=limit, file=fp, climit=climit)
974 stats.pprint(limit=limit, file=fp, climit=climit)
973
975
974 def flameprofile(ui, func, fp):
976 def flameprofile(ui, func, fp):
975 try:
977 try:
976 from flamegraph import flamegraph
978 from flamegraph import flamegraph
977 except ImportError:
979 except ImportError:
978 raise error.Abort(_(
980 raise error.Abort(_(
979 'flamegraph not available - install from '
981 'flamegraph not available - install from '
980 'https://github.com/evanhempel/python-flamegraph'))
982 'https://github.com/evanhempel/python-flamegraph'))
981 # developer config: profiling.freq
983 # developer config: profiling.freq
982 freq = ui.configint('profiling', 'freq', default=1000)
984 freq = ui.configint('profiling', 'freq', default=1000)
983 filter_ = None
985 filter_ = None
984 collapse_recursion = True
986 collapse_recursion = True
985 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
987 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
986 filter_, collapse_recursion)
988 filter_, collapse_recursion)
987 start_time = time.clock()
989 start_time = time.clock()
988 try:
990 try:
989 thread.start()
991 thread.start()
990 func()
992 func()
991 finally:
993 finally:
992 thread.stop()
994 thread.stop()
993 thread.join()
995 thread.join()
994 print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
996 print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
995 time.clock() - start_time, thread.num_frames(),
997 time.clock() - start_time, thread.num_frames(),
996 thread.num_frames(unique=True)))
998 thread.num_frames(unique=True)))
997
999
998
1000
999 def statprofile(ui, func, fp):
1001 def statprofile(ui, func, fp):
1000 try:
1002 try:
1001 import statprof
1003 import statprof
1002 except ImportError:
1004 except ImportError:
1003 raise error.Abort(_(
1005 raise error.Abort(_(
1004 'statprof not available - install using "easy_install statprof"'))
1006 'statprof not available - install using "easy_install statprof"'))
1005
1007
1006 freq = ui.configint('profiling', 'freq', default=1000)
1008 freq = ui.configint('profiling', 'freq', default=1000)
1007 if freq > 0:
1009 if freq > 0:
1008 statprof.reset(freq)
1010 statprof.reset(freq)
1009 else:
1011 else:
1010 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
1012 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
1011
1013
1012 statprof.start()
1014 statprof.start()
1013 try:
1015 try:
1014 return func()
1016 return func()
1015 finally:
1017 finally:
1016 statprof.stop()
1018 statprof.stop()
1017 statprof.display(fp)
1019 statprof.display(fp)
1018
1020
1019 def _runcommand(ui, options, cmd, cmdfunc):
1021 def _runcommand(ui, options, cmd, cmdfunc):
1020 """Enables the profiler if applicable.
1022 """Enables the profiler if applicable.
1021
1023
1022 ``profiling.enabled`` - boolean config that enables or disables profiling
1024 ``profiling.enabled`` - boolean config that enables or disables profiling
1023 """
1025 """
1024 def checkargs():
1026 def checkargs():
1025 try:
1027 try:
1026 return cmdfunc()
1028 return cmdfunc()
1027 except error.SignatureError:
1029 except error.SignatureError:
1028 raise error.CommandError(cmd, _("invalid arguments"))
1030 raise error.CommandError(cmd, _("invalid arguments"))
1029
1031
1030 if options['profile'] or ui.configbool('profiling', 'enabled'):
1032 if options['profile'] or ui.configbool('profiling', 'enabled'):
1031 profiler = os.getenv('HGPROF')
1033 profiler = os.getenv('HGPROF')
1032 if profiler is None:
1034 if profiler is None:
1033 profiler = ui.config('profiling', 'type', default='ls')
1035 profiler = ui.config('profiling', 'type', default='ls')
1034 if profiler not in ('ls', 'stat', 'flame'):
1036 if profiler not in ('ls', 'stat', 'flame'):
1035 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
1037 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
1036 profiler = 'ls'
1038 profiler = 'ls'
1037
1039
1038 output = ui.config('profiling', 'output')
1040 output = ui.config('profiling', 'output')
1039
1041
1040 if output == 'blackbox':
1042 if output == 'blackbox':
1041 import StringIO
1043 import StringIO
1042 fp = StringIO.StringIO()
1044 fp = StringIO.StringIO()
1043 elif output:
1045 elif output:
1044 path = ui.expandpath(output)
1046 path = ui.expandpath(output)
1045 fp = open(path, 'wb')
1047 fp = open(path, 'wb')
1046 else:
1048 else:
1047 fp = sys.stderr
1049 fp = sys.stderr
1048
1050
1049 try:
1051 try:
1050 if profiler == 'ls':
1052 if profiler == 'ls':
1051 return lsprofile(ui, checkargs, fp)
1053 return lsprofile(ui, checkargs, fp)
1052 elif profiler == 'flame':
1054 elif profiler == 'flame':
1053 return flameprofile(ui, checkargs, fp)
1055 return flameprofile(ui, checkargs, fp)
1054 else:
1056 else:
1055 return statprofile(ui, checkargs, fp)
1057 return statprofile(ui, checkargs, fp)
1056 finally:
1058 finally:
1057 if output:
1059 if output:
1058 if output == 'blackbox':
1060 if output == 'blackbox':
1059 val = "Profile:\n%s" % fp.getvalue()
1061 val = "Profile:\n%s" % fp.getvalue()
1060 # ui.log treats the input as a format string,
1062 # ui.log treats the input as a format string,
1061 # so we need to escape any % signs.
1063 # so we need to escape any % signs.
1062 val = val.replace('%', '%%')
1064 val = val.replace('%', '%%')
1063 ui.log('profile', val)
1065 ui.log('profile', val)
1064 fp.close()
1066 fp.close()
1065 else:
1067 else:
1066 return checkargs()
1068 return checkargs()
@@ -1,218 +1,244 b''
1 # registrar.py - utilities to register function for specific purpose
1 # registrar.py - utilities to register function for specific purpose
2 #
2 #
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
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 from . import (
10 from . import (
11 util,
11 util,
12 )
12 )
13
13
14 class _funcregistrarbase(object):
14 class _funcregistrarbase(object):
15 """Base of decorator to register a fuction for specific purpose
15 """Base of decorator to register a fuction for specific purpose
16
16
17 This decorator stores decorated functions into own dict 'table'.
17 This decorator stores decorated functions into own dict 'table'.
18
18
19 The least derived class can be defined by overriding 'formatdoc',
19 The least derived class can be defined by overriding 'formatdoc',
20 for example::
20 for example::
21
21
22 class keyword(_funcregistrarbase):
22 class keyword(_funcregistrarbase):
23 _docformat = ":%s: %s"
23 _docformat = ":%s: %s"
24
24
25 This should be used as below:
25 This should be used as below:
26
26
27 keyword = registrar.keyword()
27 keyword = registrar.keyword()
28
28
29 @keyword('bar')
29 @keyword('bar')
30 def barfunc(*args, **kwargs):
30 def barfunc(*args, **kwargs):
31 '''Explanation of bar keyword ....
31 '''Explanation of bar keyword ....
32 '''
32 '''
33 pass
33 pass
34
34
35 In this case:
35 In this case:
36
36
37 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
37 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
38 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
38 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
39 """
39 """
40 def __init__(self, table=None):
40 def __init__(self, table=None):
41 if table is None:
41 if table is None:
42 self._table = {}
42 self._table = {}
43 else:
43 else:
44 self._table = table
44 self._table = table
45
45
46 def __call__(self, decl, *args, **kwargs):
46 def __call__(self, decl, *args, **kwargs):
47 return lambda func: self._doregister(func, decl, *args, **kwargs)
47 return lambda func: self._doregister(func, decl, *args, **kwargs)
48
48
49 def _doregister(self, func, decl, *args, **kwargs):
49 def _doregister(self, func, decl, *args, **kwargs):
50 name = self._getname(decl)
50 name = self._getname(decl)
51
51
52 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
52 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
53 doc = func.__doc__.strip()
53 doc = func.__doc__.strip()
54 func._origdoc = doc
54 func._origdoc = doc
55 func.__doc__ = self._formatdoc(decl, doc)
55 func.__doc__ = self._formatdoc(decl, doc)
56
56
57 self._table[name] = func
57 self._table[name] = func
58 self._extrasetup(name, func, *args, **kwargs)
58 self._extrasetup(name, func, *args, **kwargs)
59
59
60 return func
60 return func
61
61
62 def _parsefuncdecl(self, decl):
62 def _parsefuncdecl(self, decl):
63 """Parse function declaration and return the name of function in it
63 """Parse function declaration and return the name of function in it
64 """
64 """
65 i = decl.find('(')
65 i = decl.find('(')
66 if i >= 0:
66 if i >= 0:
67 return decl[:i]
67 return decl[:i]
68 else:
68 else:
69 return decl
69 return decl
70
70
71 def _getname(self, decl):
71 def _getname(self, decl):
72 """Return the name of the registered function from decl
72 """Return the name of the registered function from decl
73
73
74 Derived class should override this, if it allows more
74 Derived class should override this, if it allows more
75 descriptive 'decl' string than just a name.
75 descriptive 'decl' string than just a name.
76 """
76 """
77 return decl
77 return decl
78
78
79 _docformat = None
79 _docformat = None
80
80
81 def _formatdoc(self, decl, doc):
81 def _formatdoc(self, decl, doc):
82 """Return formatted document of the registered function for help
82 """Return formatted document of the registered function for help
83
83
84 'doc' is '__doc__.strip()' of the registered function.
84 'doc' is '__doc__.strip()' of the registered function.
85 """
85 """
86 return self._docformat % (decl, doc)
86 return self._docformat % (decl, doc)
87
87
88 def _extrasetup(self, name, func):
88 def _extrasetup(self, name, func):
89 """Execute exra setup for registered function, if needed
89 """Execute exra setup for registered function, if needed
90 """
90 """
91 pass
91 pass
92
92
93 class revsetpredicate(_funcregistrarbase):
93 class revsetpredicate(_funcregistrarbase):
94 """Decorator to register revset predicate
94 """Decorator to register revset predicate
95
95
96 Usage::
96 Usage::
97
97
98 revsetpredicate = registrar.revsetpredicate()
98 revsetpredicate = registrar.revsetpredicate()
99
99
100 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
100 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
101 def mypredicatefunc(repo, subset, x):
101 def mypredicatefunc(repo, subset, x):
102 '''Explanation of this revset predicate ....
102 '''Explanation of this revset predicate ....
103 '''
103 '''
104 pass
104 pass
105
105
106 The first string argument is used also in online help.
106 The first string argument is used also in online help.
107
107
108 Optional argument 'safe' indicates whether a predicate is safe for
108 Optional argument 'safe' indicates whether a predicate is safe for
109 DoS attack (False by default).
109 DoS attack (False by default).
110
110
111 'revsetpredicate' instance in example above can be used to
111 'revsetpredicate' instance in example above can be used to
112 decorate multiple functions.
112 decorate multiple functions.
113
113
114 Decorated functions are registered automatically at loading
114 Decorated functions are registered automatically at loading
115 extension, if an instance named as 'revsetpredicate' is used for
115 extension, if an instance named as 'revsetpredicate' is used for
116 decorating in extension.
116 decorating in extension.
117
117
118 Otherwise, explicit 'revset.loadpredicate()' is needed.
118 Otherwise, explicit 'revset.loadpredicate()' is needed.
119 """
119 """
120 _getname = _funcregistrarbase._parsefuncdecl
120 _getname = _funcregistrarbase._parsefuncdecl
121 _docformat = "``%s``\n %s"
121 _docformat = "``%s``\n %s"
122
122
123 def _extrasetup(self, name, func, safe=False):
123 def _extrasetup(self, name, func, safe=False):
124 func._safe = safe
124 func._safe = safe
125
125
126 class filesetpredicate(_funcregistrarbase):
126 class filesetpredicate(_funcregistrarbase):
127 """Decorator to register fileset predicate
127 """Decorator to register fileset predicate
128
128
129 Usage::
129 Usage::
130
130
131 filesetpredicate = registrar.filesetpredicate()
131 filesetpredicate = registrar.filesetpredicate()
132
132
133 @filesetpredicate('mypredicate()')
133 @filesetpredicate('mypredicate()')
134 def mypredicatefunc(mctx, x):
134 def mypredicatefunc(mctx, x):
135 '''Explanation of this fileset predicate ....
135 '''Explanation of this fileset predicate ....
136 '''
136 '''
137 pass
137 pass
138
138
139 The first string argument is used also in online help.
139 The first string argument is used also in online help.
140
140
141 Optional argument 'callstatus' indicates whether a predicate
141 Optional argument 'callstatus' indicates whether a predicate
142 implies 'matchctx.status()' at runtime or not (False, by
142 implies 'matchctx.status()' at runtime or not (False, by
143 default).
143 default).
144
144
145 Optional argument 'callexisting' indicates whether a predicate
145 Optional argument 'callexisting' indicates whether a predicate
146 implies 'matchctx.existing()' at runtime or not (False, by
146 implies 'matchctx.existing()' at runtime or not (False, by
147 default).
147 default).
148
148
149 'filesetpredicate' instance in example above can be used to
149 'filesetpredicate' instance in example above can be used to
150 decorate multiple functions.
150 decorate multiple functions.
151
151
152 Decorated functions are registered automatically at loading
152 Decorated functions are registered automatically at loading
153 extension, if an instance named as 'filesetpredicate' is used for
153 extension, if an instance named as 'filesetpredicate' is used for
154 decorating in extension.
154 decorating in extension.
155
155
156 Otherwise, explicit 'fileset.loadpredicate()' is needed.
156 Otherwise, explicit 'fileset.loadpredicate()' is needed.
157 """
157 """
158 _getname = _funcregistrarbase._parsefuncdecl
158 _getname = _funcregistrarbase._parsefuncdecl
159 _docformat = "``%s``\n %s"
159 _docformat = "``%s``\n %s"
160
160
161 def _extrasetup(self, name, func, callstatus=False, callexisting=False):
161 def _extrasetup(self, name, func, callstatus=False, callexisting=False):
162 func._callstatus = callstatus
162 func._callstatus = callstatus
163 func._callexisting = callexisting
163 func._callexisting = callexisting
164
164
165 class _templateregistrarbase(_funcregistrarbase):
165 class _templateregistrarbase(_funcregistrarbase):
166 """Base of decorator to register functions as template specific one
166 """Base of decorator to register functions as template specific one
167 """
167 """
168 _docformat = ":%s: %s"
168 _docformat = ":%s: %s"
169
169
170 class templatekeyword(_templateregistrarbase):
170 class templatekeyword(_templateregistrarbase):
171 """Decorator to register template keyword
171 """Decorator to register template keyword
172
172
173 Usage::
173 Usage::
174
174
175 templaetkeyword = registrar.templatekeyword()
175 templaetkeyword = registrar.templatekeyword()
176
176
177 @templatekeyword('mykeyword')
177 @templatekeyword('mykeyword')
178 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
178 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
179 '''Explanation of this template keyword ....
179 '''Explanation of this template keyword ....
180 '''
180 '''
181 pass
181 pass
182
182
183 The first string argument is used also in online help.
183 The first string argument is used also in online help.
184
184
185 'templatekeyword' instance in example above can be used to
185 'templatekeyword' instance in example above can be used to
186 decorate multiple functions.
186 decorate multiple functions.
187
187
188 Decorated functions are registered automatically at loading
188 Decorated functions are registered automatically at loading
189 extension, if an instance named as 'templatekeyword' is used for
189 extension, if an instance named as 'templatekeyword' is used for
190 decorating in extension.
190 decorating in extension.
191
191
192 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
192 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
193 """
193 """
194
194
195 class templatefilter(_templateregistrarbase):
195 class templatefilter(_templateregistrarbase):
196 """Decorator to register template filer
196 """Decorator to register template filer
197
197
198 Usage::
198 Usage::
199
199
200 templatefilter = registrar.templatefilter()
200 templatefilter = registrar.templatefilter()
201
201
202 @templatefilter('myfilter')
202 @templatefilter('myfilter')
203 def myfilterfunc(text):
203 def myfilterfunc(text):
204 '''Explanation of this template filter ....
204 '''Explanation of this template filter ....
205 '''
205 '''
206 pass
206 pass
207
207
208 The first string argument is used also in online help.
208 The first string argument is used also in online help.
209
209
210 'templatefilter' instance in example above can be used to
210 'templatefilter' instance in example above can be used to
211 decorate multiple functions.
211 decorate multiple functions.
212
212
213 Decorated functions are registered automatically at loading
213 Decorated functions are registered automatically at loading
214 extension, if an instance named as 'templatefilter' is used for
214 extension, if an instance named as 'templatefilter' is used for
215 decorating in extension.
215 decorating in extension.
216
216
217 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
217 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
218 """
218 """
219
220 class templatefunc(_templateregistrarbase):
221 """Decorator to register template function
222
223 Usage::
224
225 templatefunc = registrar.templatefunc()
226
227 @templatefunc('myfunc(arg1, arg2[, arg3])')
228 def myfuncfunc(context, mapping, args):
229 '''Explanation of this template function ....
230 '''
231 pass
232
233 The first string argument is used also in online help.
234
235 'templatefunc' instance in example above can be used to
236 decorate multiple functions.
237
238 Decorated functions are registered automatically at loading
239 extension, if an instance named as 'templatefunc' is used for
240 decorating in extension.
241
242 Otherwise, explicit 'templater.loadfunction()' is needed.
243 """
244 _getname = _funcregistrarbase._parsefuncdecl
@@ -1,1073 +1,1079 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import re
11 import re
12 import types
12 import types
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 config,
16 config,
17 error,
17 error,
18 minirst,
18 minirst,
19 parser,
19 parser,
20 revset as revsetmod,
20 revset as revsetmod,
21 templatefilters,
21 templatefilters,
22 templatekw,
22 templatekw,
23 util,
23 util,
24 )
24 )
25
25
26 # template parsing
26 # template parsing
27
27
28 elements = {
28 elements = {
29 # token-type: binding-strength, primary, prefix, infix, suffix
29 # token-type: binding-strength, primary, prefix, infix, suffix
30 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
30 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
31 ",": (2, None, None, ("list", 2), None),
31 ",": (2, None, None, ("list", 2), None),
32 "|": (5, None, None, ("|", 5), None),
32 "|": (5, None, None, ("|", 5), None),
33 "%": (6, None, None, ("%", 6), None),
33 "%": (6, None, None, ("%", 6), None),
34 ")": (0, None, None, None, None),
34 ")": (0, None, None, None, None),
35 "integer": (0, "integer", None, None, None),
35 "integer": (0, "integer", None, None, None),
36 "symbol": (0, "symbol", None, None, None),
36 "symbol": (0, "symbol", None, None, None),
37 "string": (0, "string", None, None, None),
37 "string": (0, "string", None, None, None),
38 "template": (0, "template", None, None, None),
38 "template": (0, "template", None, None, None),
39 "end": (0, None, None, None, None),
39 "end": (0, None, None, None, None),
40 }
40 }
41
41
42 def tokenize(program, start, end):
42 def tokenize(program, start, end):
43 pos = start
43 pos = start
44 while pos < end:
44 while pos < end:
45 c = program[pos]
45 c = program[pos]
46 if c.isspace(): # skip inter-token whitespace
46 if c.isspace(): # skip inter-token whitespace
47 pass
47 pass
48 elif c in "(,)%|": # handle simple operators
48 elif c in "(,)%|": # handle simple operators
49 yield (c, None, pos)
49 yield (c, None, pos)
50 elif c in '"\'': # handle quoted templates
50 elif c in '"\'': # handle quoted templates
51 s = pos + 1
51 s = pos + 1
52 data, pos = _parsetemplate(program, s, end, c)
52 data, pos = _parsetemplate(program, s, end, c)
53 yield ('template', data, s)
53 yield ('template', data, s)
54 pos -= 1
54 pos -= 1
55 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
55 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
56 # handle quoted strings
56 # handle quoted strings
57 c = program[pos + 1]
57 c = program[pos + 1]
58 s = pos = pos + 2
58 s = pos = pos + 2
59 while pos < end: # find closing quote
59 while pos < end: # find closing quote
60 d = program[pos]
60 d = program[pos]
61 if d == '\\': # skip over escaped characters
61 if d == '\\': # skip over escaped characters
62 pos += 2
62 pos += 2
63 continue
63 continue
64 if d == c:
64 if d == c:
65 yield ('string', program[s:pos], s)
65 yield ('string', program[s:pos], s)
66 break
66 break
67 pos += 1
67 pos += 1
68 else:
68 else:
69 raise error.ParseError(_("unterminated string"), s)
69 raise error.ParseError(_("unterminated string"), s)
70 elif c.isdigit() or c == '-':
70 elif c.isdigit() or c == '-':
71 s = pos
71 s = pos
72 if c == '-': # simply take negate operator as part of integer
72 if c == '-': # simply take negate operator as part of integer
73 pos += 1
73 pos += 1
74 if pos >= end or not program[pos].isdigit():
74 if pos >= end or not program[pos].isdigit():
75 raise error.ParseError(_("integer literal without digits"), s)
75 raise error.ParseError(_("integer literal without digits"), s)
76 pos += 1
76 pos += 1
77 while pos < end:
77 while pos < end:
78 d = program[pos]
78 d = program[pos]
79 if not d.isdigit():
79 if not d.isdigit():
80 break
80 break
81 pos += 1
81 pos += 1
82 yield ('integer', program[s:pos], s)
82 yield ('integer', program[s:pos], s)
83 pos -= 1
83 pos -= 1
84 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
84 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
85 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
85 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
86 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
86 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
87 # where some of nested templates were preprocessed as strings and
87 # where some of nested templates were preprocessed as strings and
88 # then compiled. therefore, \"...\" was allowed. (issue4733)
88 # then compiled. therefore, \"...\" was allowed. (issue4733)
89 #
89 #
90 # processing flow of _evalifliteral() at 5ab28a2e9962:
90 # processing flow of _evalifliteral() at 5ab28a2e9962:
91 # outer template string -> stringify() -> compiletemplate()
91 # outer template string -> stringify() -> compiletemplate()
92 # ------------------------ ------------ ------------------
92 # ------------------------ ------------ ------------------
93 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
93 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
94 # ~~~~~~~~
94 # ~~~~~~~~
95 # escaped quoted string
95 # escaped quoted string
96 if c == 'r':
96 if c == 'r':
97 pos += 1
97 pos += 1
98 token = 'string'
98 token = 'string'
99 else:
99 else:
100 token = 'template'
100 token = 'template'
101 quote = program[pos:pos + 2]
101 quote = program[pos:pos + 2]
102 s = pos = pos + 2
102 s = pos = pos + 2
103 while pos < end: # find closing escaped quote
103 while pos < end: # find closing escaped quote
104 if program.startswith('\\\\\\', pos, end):
104 if program.startswith('\\\\\\', pos, end):
105 pos += 4 # skip over double escaped characters
105 pos += 4 # skip over double escaped characters
106 continue
106 continue
107 if program.startswith(quote, pos, end):
107 if program.startswith(quote, pos, end):
108 # interpret as if it were a part of an outer string
108 # interpret as if it were a part of an outer string
109 data = parser.unescapestr(program[s:pos])
109 data = parser.unescapestr(program[s:pos])
110 if token == 'template':
110 if token == 'template':
111 data = _parsetemplate(data, 0, len(data))[0]
111 data = _parsetemplate(data, 0, len(data))[0]
112 yield (token, data, s)
112 yield (token, data, s)
113 pos += 1
113 pos += 1
114 break
114 break
115 pos += 1
115 pos += 1
116 else:
116 else:
117 raise error.ParseError(_("unterminated string"), s)
117 raise error.ParseError(_("unterminated string"), s)
118 elif c.isalnum() or c in '_':
118 elif c.isalnum() or c in '_':
119 s = pos
119 s = pos
120 pos += 1
120 pos += 1
121 while pos < end: # find end of symbol
121 while pos < end: # find end of symbol
122 d = program[pos]
122 d = program[pos]
123 if not (d.isalnum() or d == "_"):
123 if not (d.isalnum() or d == "_"):
124 break
124 break
125 pos += 1
125 pos += 1
126 sym = program[s:pos]
126 sym = program[s:pos]
127 yield ('symbol', sym, s)
127 yield ('symbol', sym, s)
128 pos -= 1
128 pos -= 1
129 elif c == '}':
129 elif c == '}':
130 yield ('end', None, pos + 1)
130 yield ('end', None, pos + 1)
131 return
131 return
132 else:
132 else:
133 raise error.ParseError(_("syntax error"), pos)
133 raise error.ParseError(_("syntax error"), pos)
134 pos += 1
134 pos += 1
135 raise error.ParseError(_("unterminated template expansion"), start)
135 raise error.ParseError(_("unterminated template expansion"), start)
136
136
137 def _parsetemplate(tmpl, start, stop, quote=''):
137 def _parsetemplate(tmpl, start, stop, quote=''):
138 r"""
138 r"""
139 >>> _parsetemplate('foo{bar}"baz', 0, 12)
139 >>> _parsetemplate('foo{bar}"baz', 0, 12)
140 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
140 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
141 >>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"')
141 >>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"')
142 ([('string', 'foo'), ('symbol', 'bar')], 9)
142 ([('string', 'foo'), ('symbol', 'bar')], 9)
143 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
143 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
144 ([('string', 'foo')], 4)
144 ([('string', 'foo')], 4)
145 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
145 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
146 ([('string', 'foo"'), ('string', 'bar')], 9)
146 ([('string', 'foo"'), ('string', 'bar')], 9)
147 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
147 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
148 ([('string', 'foo\\')], 6)
148 ([('string', 'foo\\')], 6)
149 """
149 """
150 parsed = []
150 parsed = []
151 sepchars = '{' + quote
151 sepchars = '{' + quote
152 pos = start
152 pos = start
153 p = parser.parser(elements)
153 p = parser.parser(elements)
154 while pos < stop:
154 while pos < stop:
155 n = min((tmpl.find(c, pos, stop) for c in sepchars),
155 n = min((tmpl.find(c, pos, stop) for c in sepchars),
156 key=lambda n: (n < 0, n))
156 key=lambda n: (n < 0, n))
157 if n < 0:
157 if n < 0:
158 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
158 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
159 pos = stop
159 pos = stop
160 break
160 break
161 c = tmpl[n]
161 c = tmpl[n]
162 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
162 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
163 if bs % 2 == 1:
163 if bs % 2 == 1:
164 # escaped (e.g. '\{', '\\\{', but not '\\{')
164 # escaped (e.g. '\{', '\\\{', but not '\\{')
165 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
165 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
166 pos = n + 1
166 pos = n + 1
167 continue
167 continue
168 if n > pos:
168 if n > pos:
169 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
169 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
170 if c == quote:
170 if c == quote:
171 return parsed, n + 1
171 return parsed, n + 1
172
172
173 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop))
173 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop))
174 parsed.append(parseres)
174 parsed.append(parseres)
175
175
176 if quote:
176 if quote:
177 raise error.ParseError(_("unterminated string"), start)
177 raise error.ParseError(_("unterminated string"), start)
178 return parsed, pos
178 return parsed, pos
179
179
180 def _unnesttemplatelist(tree):
180 def _unnesttemplatelist(tree):
181 """Expand list of templates to node tuple
181 """Expand list of templates to node tuple
182
182
183 >>> def f(tree):
183 >>> def f(tree):
184 ... print prettyformat(_unnesttemplatelist(tree))
184 ... print prettyformat(_unnesttemplatelist(tree))
185 >>> f(('template', []))
185 >>> f(('template', []))
186 ('string', '')
186 ('string', '')
187 >>> f(('template', [('string', 'foo')]))
187 >>> f(('template', [('string', 'foo')]))
188 ('string', 'foo')
188 ('string', 'foo')
189 >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
189 >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
190 (template
190 (template
191 ('string', 'foo')
191 ('string', 'foo')
192 ('symbol', 'rev'))
192 ('symbol', 'rev'))
193 >>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
193 >>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
194 (template
194 (template
195 ('symbol', 'rev'))
195 ('symbol', 'rev'))
196 >>> f(('template', [('template', [('string', 'foo')])]))
196 >>> f(('template', [('template', [('string', 'foo')])]))
197 ('string', 'foo')
197 ('string', 'foo')
198 """
198 """
199 if not isinstance(tree, tuple):
199 if not isinstance(tree, tuple):
200 return tree
200 return tree
201 op = tree[0]
201 op = tree[0]
202 if op != 'template':
202 if op != 'template':
203 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
203 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
204
204
205 assert len(tree) == 2
205 assert len(tree) == 2
206 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
206 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
207 if not xs:
207 if not xs:
208 return ('string', '') # empty template ""
208 return ('string', '') # empty template ""
209 elif len(xs) == 1 and xs[0][0] == 'string':
209 elif len(xs) == 1 and xs[0][0] == 'string':
210 return xs[0] # fast path for string with no template fragment "x"
210 return xs[0] # fast path for string with no template fragment "x"
211 else:
211 else:
212 return (op,) + xs
212 return (op,) + xs
213
213
214 def parse(tmpl):
214 def parse(tmpl):
215 """Parse template string into tree"""
215 """Parse template string into tree"""
216 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
216 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
217 assert pos == len(tmpl), 'unquoted template should be consumed'
217 assert pos == len(tmpl), 'unquoted template should be consumed'
218 return _unnesttemplatelist(('template', parsed))
218 return _unnesttemplatelist(('template', parsed))
219
219
220 def prettyformat(tree):
220 def prettyformat(tree):
221 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
221 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
222
222
223 def compiletemplate(tmpl, context):
223 def compiletemplate(tmpl, context):
224 """Parse and compile template string to (func, data) pair"""
224 """Parse and compile template string to (func, data) pair"""
225 return compileexp(parse(tmpl), context, methods)
225 return compileexp(parse(tmpl), context, methods)
226
226
227 def compileexp(exp, context, curmethods):
227 def compileexp(exp, context, curmethods):
228 t = exp[0]
228 t = exp[0]
229 if t in curmethods:
229 if t in curmethods:
230 return curmethods[t](exp, context)
230 return curmethods[t](exp, context)
231 raise error.ParseError(_("unknown method '%s'") % t)
231 raise error.ParseError(_("unknown method '%s'") % t)
232
232
233 # template evaluation
233 # template evaluation
234
234
235 def getsymbol(exp):
235 def getsymbol(exp):
236 if exp[0] == 'symbol':
236 if exp[0] == 'symbol':
237 return exp[1]
237 return exp[1]
238 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
238 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
239
239
240 def getlist(x):
240 def getlist(x):
241 if not x:
241 if not x:
242 return []
242 return []
243 if x[0] == 'list':
243 if x[0] == 'list':
244 return getlist(x[1]) + [x[2]]
244 return getlist(x[1]) + [x[2]]
245 return [x]
245 return [x]
246
246
247 def gettemplate(exp, context):
247 def gettemplate(exp, context):
248 """Compile given template tree or load named template from map file;
248 """Compile given template tree or load named template from map file;
249 returns (func, data) pair"""
249 returns (func, data) pair"""
250 if exp[0] in ('template', 'string'):
250 if exp[0] in ('template', 'string'):
251 return compileexp(exp, context, methods)
251 return compileexp(exp, context, methods)
252 if exp[0] == 'symbol':
252 if exp[0] == 'symbol':
253 # unlike runsymbol(), here 'symbol' is always taken as template name
253 # unlike runsymbol(), here 'symbol' is always taken as template name
254 # even if it exists in mapping. this allows us to override mapping
254 # even if it exists in mapping. this allows us to override mapping
255 # by web templates, e.g. 'changelogtag' is redefined in map file.
255 # by web templates, e.g. 'changelogtag' is redefined in map file.
256 return context._load(exp[1])
256 return context._load(exp[1])
257 raise error.ParseError(_("expected template specifier"))
257 raise error.ParseError(_("expected template specifier"))
258
258
259 def evalfuncarg(context, mapping, arg):
259 def evalfuncarg(context, mapping, arg):
260 func, data = arg
260 func, data = arg
261 # func() may return string, generator of strings or arbitrary object such
261 # func() may return string, generator of strings or arbitrary object such
262 # as date tuple, but filter does not want generator.
262 # as date tuple, but filter does not want generator.
263 thing = func(context, mapping, data)
263 thing = func(context, mapping, data)
264 if isinstance(thing, types.GeneratorType):
264 if isinstance(thing, types.GeneratorType):
265 thing = stringify(thing)
265 thing = stringify(thing)
266 return thing
266 return thing
267
267
268 def evalinteger(context, mapping, arg, err):
268 def evalinteger(context, mapping, arg, err):
269 v = evalfuncarg(context, mapping, arg)
269 v = evalfuncarg(context, mapping, arg)
270 try:
270 try:
271 return int(v)
271 return int(v)
272 except (TypeError, ValueError):
272 except (TypeError, ValueError):
273 raise error.ParseError(err)
273 raise error.ParseError(err)
274
274
275 def evalstring(context, mapping, arg):
275 def evalstring(context, mapping, arg):
276 func, data = arg
276 func, data = arg
277 return stringify(func(context, mapping, data))
277 return stringify(func(context, mapping, data))
278
278
279 def evalstringliteral(context, mapping, arg):
279 def evalstringliteral(context, mapping, arg):
280 """Evaluate given argument as string template, but returns symbol name
280 """Evaluate given argument as string template, but returns symbol name
281 if it is unknown"""
281 if it is unknown"""
282 func, data = arg
282 func, data = arg
283 if func is runsymbol:
283 if func is runsymbol:
284 thing = func(context, mapping, data, default=data)
284 thing = func(context, mapping, data, default=data)
285 else:
285 else:
286 thing = func(context, mapping, data)
286 thing = func(context, mapping, data)
287 return stringify(thing)
287 return stringify(thing)
288
288
289 def runinteger(context, mapping, data):
289 def runinteger(context, mapping, data):
290 return int(data)
290 return int(data)
291
291
292 def runstring(context, mapping, data):
292 def runstring(context, mapping, data):
293 return data
293 return data
294
294
295 def _recursivesymbolblocker(key):
295 def _recursivesymbolblocker(key):
296 def showrecursion(**args):
296 def showrecursion(**args):
297 raise error.Abort(_("recursive reference '%s' in template") % key)
297 raise error.Abort(_("recursive reference '%s' in template") % key)
298 return showrecursion
298 return showrecursion
299
299
300 def _runrecursivesymbol(context, mapping, key):
300 def _runrecursivesymbol(context, mapping, key):
301 raise error.Abort(_("recursive reference '%s' in template") % key)
301 raise error.Abort(_("recursive reference '%s' in template") % key)
302
302
303 def runsymbol(context, mapping, key, default=''):
303 def runsymbol(context, mapping, key, default=''):
304 v = mapping.get(key)
304 v = mapping.get(key)
305 if v is None:
305 if v is None:
306 v = context._defaults.get(key)
306 v = context._defaults.get(key)
307 if v is None:
307 if v is None:
308 # put poison to cut recursion. we can't move this to parsing phase
308 # put poison to cut recursion. we can't move this to parsing phase
309 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
309 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
310 safemapping = mapping.copy()
310 safemapping = mapping.copy()
311 safemapping[key] = _recursivesymbolblocker(key)
311 safemapping[key] = _recursivesymbolblocker(key)
312 try:
312 try:
313 v = context.process(key, safemapping)
313 v = context.process(key, safemapping)
314 except TemplateNotFound:
314 except TemplateNotFound:
315 v = default
315 v = default
316 if callable(v):
316 if callable(v):
317 return v(**mapping)
317 return v(**mapping)
318 return v
318 return v
319
319
320 def buildtemplate(exp, context):
320 def buildtemplate(exp, context):
321 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
321 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
322 return (runtemplate, ctmpl)
322 return (runtemplate, ctmpl)
323
323
324 def runtemplate(context, mapping, template):
324 def runtemplate(context, mapping, template):
325 for func, data in template:
325 for func, data in template:
326 yield func(context, mapping, data)
326 yield func(context, mapping, data)
327
327
328 def buildfilter(exp, context):
328 def buildfilter(exp, context):
329 arg = compileexp(exp[1], context, methods)
329 arg = compileexp(exp[1], context, methods)
330 n = getsymbol(exp[2])
330 n = getsymbol(exp[2])
331 if n in context._filters:
331 if n in context._filters:
332 filt = context._filters[n]
332 filt = context._filters[n]
333 return (runfilter, (arg, filt))
333 return (runfilter, (arg, filt))
334 if n in funcs:
334 if n in funcs:
335 f = funcs[n]
335 f = funcs[n]
336 return (f, [arg])
336 return (f, [arg])
337 raise error.ParseError(_("unknown function '%s'") % n)
337 raise error.ParseError(_("unknown function '%s'") % n)
338
338
339 def runfilter(context, mapping, data):
339 def runfilter(context, mapping, data):
340 arg, filt = data
340 arg, filt = data
341 thing = evalfuncarg(context, mapping, arg)
341 thing = evalfuncarg(context, mapping, arg)
342 try:
342 try:
343 return filt(thing)
343 return filt(thing)
344 except (ValueError, AttributeError, TypeError):
344 except (ValueError, AttributeError, TypeError):
345 if isinstance(arg[1], tuple):
345 if isinstance(arg[1], tuple):
346 dt = arg[1][1]
346 dt = arg[1][1]
347 else:
347 else:
348 dt = arg[1]
348 dt = arg[1]
349 raise error.Abort(_("template filter '%s' is not compatible with "
349 raise error.Abort(_("template filter '%s' is not compatible with "
350 "keyword '%s'") % (filt.func_name, dt))
350 "keyword '%s'") % (filt.func_name, dt))
351
351
352 def buildmap(exp, context):
352 def buildmap(exp, context):
353 func, data = compileexp(exp[1], context, methods)
353 func, data = compileexp(exp[1], context, methods)
354 tfunc, tdata = gettemplate(exp[2], context)
354 tfunc, tdata = gettemplate(exp[2], context)
355 return (runmap, (func, data, tfunc, tdata))
355 return (runmap, (func, data, tfunc, tdata))
356
356
357 def runmap(context, mapping, data):
357 def runmap(context, mapping, data):
358 func, data, tfunc, tdata = data
358 func, data, tfunc, tdata = data
359 d = func(context, mapping, data)
359 d = func(context, mapping, data)
360 if util.safehasattr(d, 'itermaps'):
360 if util.safehasattr(d, 'itermaps'):
361 diter = d.itermaps()
361 diter = d.itermaps()
362 else:
362 else:
363 try:
363 try:
364 diter = iter(d)
364 diter = iter(d)
365 except TypeError:
365 except TypeError:
366 if func is runsymbol:
366 if func is runsymbol:
367 raise error.ParseError(_("keyword '%s' is not iterable") % data)
367 raise error.ParseError(_("keyword '%s' is not iterable") % data)
368 else:
368 else:
369 raise error.ParseError(_("%r is not iterable") % d)
369 raise error.ParseError(_("%r is not iterable") % d)
370
370
371 for i in diter:
371 for i in diter:
372 lm = mapping.copy()
372 lm = mapping.copy()
373 if isinstance(i, dict):
373 if isinstance(i, dict):
374 lm.update(i)
374 lm.update(i)
375 lm['originalnode'] = mapping.get('node')
375 lm['originalnode'] = mapping.get('node')
376 yield tfunc(context, lm, tdata)
376 yield tfunc(context, lm, tdata)
377 else:
377 else:
378 # v is not an iterable of dicts, this happen when 'key'
378 # v is not an iterable of dicts, this happen when 'key'
379 # has been fully expanded already and format is useless.
379 # has been fully expanded already and format is useless.
380 # If so, return the expanded value.
380 # If so, return the expanded value.
381 yield i
381 yield i
382
382
383 def buildfunc(exp, context):
383 def buildfunc(exp, context):
384 n = getsymbol(exp[1])
384 n = getsymbol(exp[1])
385 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
385 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
386 if n in funcs:
386 if n in funcs:
387 f = funcs[n]
387 f = funcs[n]
388 return (f, args)
388 return (f, args)
389 if n in context._filters:
389 if n in context._filters:
390 if len(args) != 1:
390 if len(args) != 1:
391 raise error.ParseError(_("filter %s expects one argument") % n)
391 raise error.ParseError(_("filter %s expects one argument") % n)
392 f = context._filters[n]
392 f = context._filters[n]
393 return (runfilter, (args[0], f))
393 return (runfilter, (args[0], f))
394 raise error.ParseError(_("unknown function '%s'") % n)
394 raise error.ParseError(_("unknown function '%s'") % n)
395
395
396 def date(context, mapping, args):
396 def date(context, mapping, args):
397 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
397 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
398 strings. The default is a Unix date format, including the timezone:
398 strings. The default is a Unix date format, including the timezone:
399 "Mon Sep 04 15:13:13 2006 0700"."""
399 "Mon Sep 04 15:13:13 2006 0700"."""
400 if not (1 <= len(args) <= 2):
400 if not (1 <= len(args) <= 2):
401 # i18n: "date" is a keyword
401 # i18n: "date" is a keyword
402 raise error.ParseError(_("date expects one or two arguments"))
402 raise error.ParseError(_("date expects one or two arguments"))
403
403
404 date = evalfuncarg(context, mapping, args[0])
404 date = evalfuncarg(context, mapping, args[0])
405 fmt = None
405 fmt = None
406 if len(args) == 2:
406 if len(args) == 2:
407 fmt = evalstring(context, mapping, args[1])
407 fmt = evalstring(context, mapping, args[1])
408 try:
408 try:
409 if fmt is None:
409 if fmt is None:
410 return util.datestr(date)
410 return util.datestr(date)
411 else:
411 else:
412 return util.datestr(date, fmt)
412 return util.datestr(date, fmt)
413 except (TypeError, ValueError):
413 except (TypeError, ValueError):
414 # i18n: "date" is a keyword
414 # i18n: "date" is a keyword
415 raise error.ParseError(_("date expects a date information"))
415 raise error.ParseError(_("date expects a date information"))
416
416
417 def diff(context, mapping, args):
417 def diff(context, mapping, args):
418 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
418 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
419 specifying files to include or exclude."""
419 specifying files to include or exclude."""
420 if len(args) > 2:
420 if len(args) > 2:
421 # i18n: "diff" is a keyword
421 # i18n: "diff" is a keyword
422 raise error.ParseError(_("diff expects zero, one, or two arguments"))
422 raise error.ParseError(_("diff expects zero, one, or two arguments"))
423
423
424 def getpatterns(i):
424 def getpatterns(i):
425 if i < len(args):
425 if i < len(args):
426 s = evalstring(context, mapping, args[i]).strip()
426 s = evalstring(context, mapping, args[i]).strip()
427 if s:
427 if s:
428 return [s]
428 return [s]
429 return []
429 return []
430
430
431 ctx = mapping['ctx']
431 ctx = mapping['ctx']
432 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
432 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
433
433
434 return ''.join(chunks)
434 return ''.join(chunks)
435
435
436 def fill(context, mapping, args):
436 def fill(context, mapping, args):
437 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
437 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
438 paragraphs with optional indentation. See the "fill" filter."""
438 paragraphs with optional indentation. See the "fill" filter."""
439 if not (1 <= len(args) <= 4):
439 if not (1 <= len(args) <= 4):
440 # i18n: "fill" is a keyword
440 # i18n: "fill" is a keyword
441 raise error.ParseError(_("fill expects one to four arguments"))
441 raise error.ParseError(_("fill expects one to four arguments"))
442
442
443 text = evalstring(context, mapping, args[0])
443 text = evalstring(context, mapping, args[0])
444 width = 76
444 width = 76
445 initindent = ''
445 initindent = ''
446 hangindent = ''
446 hangindent = ''
447 if 2 <= len(args) <= 4:
447 if 2 <= len(args) <= 4:
448 width = evalinteger(context, mapping, args[1],
448 width = evalinteger(context, mapping, args[1],
449 # i18n: "fill" is a keyword
449 # i18n: "fill" is a keyword
450 _("fill expects an integer width"))
450 _("fill expects an integer width"))
451 try:
451 try:
452 initindent = evalstring(context, mapping, args[2])
452 initindent = evalstring(context, mapping, args[2])
453 hangindent = evalstring(context, mapping, args[3])
453 hangindent = evalstring(context, mapping, args[3])
454 except IndexError:
454 except IndexError:
455 pass
455 pass
456
456
457 return templatefilters.fill(text, width, initindent, hangindent)
457 return templatefilters.fill(text, width, initindent, hangindent)
458
458
459 def pad(context, mapping, args):
459 def pad(context, mapping, args):
460 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
460 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
461 fill character."""
461 fill character."""
462 if not (2 <= len(args) <= 4):
462 if not (2 <= len(args) <= 4):
463 # i18n: "pad" is a keyword
463 # i18n: "pad" is a keyword
464 raise error.ParseError(_("pad() expects two to four arguments"))
464 raise error.ParseError(_("pad() expects two to four arguments"))
465
465
466 width = evalinteger(context, mapping, args[1],
466 width = evalinteger(context, mapping, args[1],
467 # i18n: "pad" is a keyword
467 # i18n: "pad" is a keyword
468 _("pad() expects an integer width"))
468 _("pad() expects an integer width"))
469
469
470 text = evalstring(context, mapping, args[0])
470 text = evalstring(context, mapping, args[0])
471
471
472 right = False
472 right = False
473 fillchar = ' '
473 fillchar = ' '
474 if len(args) > 2:
474 if len(args) > 2:
475 fillchar = evalstring(context, mapping, args[2])
475 fillchar = evalstring(context, mapping, args[2])
476 if len(args) > 3:
476 if len(args) > 3:
477 right = util.parsebool(args[3][1])
477 right = util.parsebool(args[3][1])
478
478
479 if right:
479 if right:
480 return text.rjust(width, fillchar)
480 return text.rjust(width, fillchar)
481 else:
481 else:
482 return text.ljust(width, fillchar)
482 return text.ljust(width, fillchar)
483
483
484 def indent(context, mapping, args):
484 def indent(context, mapping, args):
485 """:indent(text, indentchars[, firstline]): Indents all non-empty lines
485 """:indent(text, indentchars[, firstline]): Indents all non-empty lines
486 with the characters given in the indentchars string. An optional
486 with the characters given in the indentchars string. An optional
487 third parameter will override the indent for the first line only
487 third parameter will override the indent for the first line only
488 if present."""
488 if present."""
489 if not (2 <= len(args) <= 3):
489 if not (2 <= len(args) <= 3):
490 # i18n: "indent" is a keyword
490 # i18n: "indent" is a keyword
491 raise error.ParseError(_("indent() expects two or three arguments"))
491 raise error.ParseError(_("indent() expects two or three arguments"))
492
492
493 text = evalstring(context, mapping, args[0])
493 text = evalstring(context, mapping, args[0])
494 indent = evalstring(context, mapping, args[1])
494 indent = evalstring(context, mapping, args[1])
495
495
496 if len(args) == 3:
496 if len(args) == 3:
497 firstline = evalstring(context, mapping, args[2])
497 firstline = evalstring(context, mapping, args[2])
498 else:
498 else:
499 firstline = indent
499 firstline = indent
500
500
501 # the indent function doesn't indent the first line, so we do it here
501 # the indent function doesn't indent the first line, so we do it here
502 return templatefilters.indent(firstline + text, indent)
502 return templatefilters.indent(firstline + text, indent)
503
503
504 def get(context, mapping, args):
504 def get(context, mapping, args):
505 """:get(dict, key): Get an attribute/key from an object. Some keywords
505 """:get(dict, key): Get an attribute/key from an object. Some keywords
506 are complex types. This function allows you to obtain the value of an
506 are complex types. This function allows you to obtain the value of an
507 attribute on these types."""
507 attribute on these types."""
508 if len(args) != 2:
508 if len(args) != 2:
509 # i18n: "get" is a keyword
509 # i18n: "get" is a keyword
510 raise error.ParseError(_("get() expects two arguments"))
510 raise error.ParseError(_("get() expects two arguments"))
511
511
512 dictarg = evalfuncarg(context, mapping, args[0])
512 dictarg = evalfuncarg(context, mapping, args[0])
513 if not util.safehasattr(dictarg, 'get'):
513 if not util.safehasattr(dictarg, 'get'):
514 # i18n: "get" is a keyword
514 # i18n: "get" is a keyword
515 raise error.ParseError(_("get() expects a dict as first argument"))
515 raise error.ParseError(_("get() expects a dict as first argument"))
516
516
517 key = evalfuncarg(context, mapping, args[1])
517 key = evalfuncarg(context, mapping, args[1])
518 return dictarg.get(key)
518 return dictarg.get(key)
519
519
520 def if_(context, mapping, args):
520 def if_(context, mapping, args):
521 """:if(expr, then[, else]): Conditionally execute based on the result of
521 """:if(expr, then[, else]): Conditionally execute based on the result of
522 an expression."""
522 an expression."""
523 if not (2 <= len(args) <= 3):
523 if not (2 <= len(args) <= 3):
524 # i18n: "if" is a keyword
524 # i18n: "if" is a keyword
525 raise error.ParseError(_("if expects two or three arguments"))
525 raise error.ParseError(_("if expects two or three arguments"))
526
526
527 test = evalstring(context, mapping, args[0])
527 test = evalstring(context, mapping, args[0])
528 if test:
528 if test:
529 yield args[1][0](context, mapping, args[1][1])
529 yield args[1][0](context, mapping, args[1][1])
530 elif len(args) == 3:
530 elif len(args) == 3:
531 yield args[2][0](context, mapping, args[2][1])
531 yield args[2][0](context, mapping, args[2][1])
532
532
533 def ifcontains(context, mapping, args):
533 def ifcontains(context, mapping, args):
534 """:ifcontains(search, thing, then[, else]): Conditionally execute based
534 """:ifcontains(search, thing, then[, else]): Conditionally execute based
535 on whether the item "search" is in "thing"."""
535 on whether the item "search" is in "thing"."""
536 if not (3 <= len(args) <= 4):
536 if not (3 <= len(args) <= 4):
537 # i18n: "ifcontains" is a keyword
537 # i18n: "ifcontains" is a keyword
538 raise error.ParseError(_("ifcontains expects three or four arguments"))
538 raise error.ParseError(_("ifcontains expects three or four arguments"))
539
539
540 item = evalstring(context, mapping, args[0])
540 item = evalstring(context, mapping, args[0])
541 items = evalfuncarg(context, mapping, args[1])
541 items = evalfuncarg(context, mapping, args[1])
542
542
543 if item in items:
543 if item in items:
544 yield args[2][0](context, mapping, args[2][1])
544 yield args[2][0](context, mapping, args[2][1])
545 elif len(args) == 4:
545 elif len(args) == 4:
546 yield args[3][0](context, mapping, args[3][1])
546 yield args[3][0](context, mapping, args[3][1])
547
547
548 def ifeq(context, mapping, args):
548 def ifeq(context, mapping, args):
549 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
549 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
550 whether 2 items are equivalent."""
550 whether 2 items are equivalent."""
551 if not (3 <= len(args) <= 4):
551 if not (3 <= len(args) <= 4):
552 # i18n: "ifeq" is a keyword
552 # i18n: "ifeq" is a keyword
553 raise error.ParseError(_("ifeq expects three or four arguments"))
553 raise error.ParseError(_("ifeq expects three or four arguments"))
554
554
555 test = evalstring(context, mapping, args[0])
555 test = evalstring(context, mapping, args[0])
556 match = evalstring(context, mapping, args[1])
556 match = evalstring(context, mapping, args[1])
557 if test == match:
557 if test == match:
558 yield args[2][0](context, mapping, args[2][1])
558 yield args[2][0](context, mapping, args[2][1])
559 elif len(args) == 4:
559 elif len(args) == 4:
560 yield args[3][0](context, mapping, args[3][1])
560 yield args[3][0](context, mapping, args[3][1])
561
561
562 def join(context, mapping, args):
562 def join(context, mapping, args):
563 """:join(list, sep): Join items in a list with a delimiter."""
563 """:join(list, sep): Join items in a list with a delimiter."""
564 if not (1 <= len(args) <= 2):
564 if not (1 <= len(args) <= 2):
565 # i18n: "join" is a keyword
565 # i18n: "join" is a keyword
566 raise error.ParseError(_("join expects one or two arguments"))
566 raise error.ParseError(_("join expects one or two arguments"))
567
567
568 joinset = args[0][0](context, mapping, args[0][1])
568 joinset = args[0][0](context, mapping, args[0][1])
569 if util.safehasattr(joinset, 'itermaps'):
569 if util.safehasattr(joinset, 'itermaps'):
570 jf = joinset.joinfmt
570 jf = joinset.joinfmt
571 joinset = [jf(x) for x in joinset.itermaps()]
571 joinset = [jf(x) for x in joinset.itermaps()]
572
572
573 joiner = " "
573 joiner = " "
574 if len(args) > 1:
574 if len(args) > 1:
575 joiner = evalstring(context, mapping, args[1])
575 joiner = evalstring(context, mapping, args[1])
576
576
577 first = True
577 first = True
578 for x in joinset:
578 for x in joinset:
579 if first:
579 if first:
580 first = False
580 first = False
581 else:
581 else:
582 yield joiner
582 yield joiner
583 yield x
583 yield x
584
584
585 def label(context, mapping, args):
585 def label(context, mapping, args):
586 """:label(label, expr): Apply a label to generated content. Content with
586 """:label(label, expr): Apply a label to generated content. Content with
587 a label applied can result in additional post-processing, such as
587 a label applied can result in additional post-processing, such as
588 automatic colorization."""
588 automatic colorization."""
589 if len(args) != 2:
589 if len(args) != 2:
590 # i18n: "label" is a keyword
590 # i18n: "label" is a keyword
591 raise error.ParseError(_("label expects two arguments"))
591 raise error.ParseError(_("label expects two arguments"))
592
592
593 ui = mapping['ui']
593 ui = mapping['ui']
594 thing = evalstring(context, mapping, args[1])
594 thing = evalstring(context, mapping, args[1])
595 # preserve unknown symbol as literal so effects like 'red', 'bold',
595 # preserve unknown symbol as literal so effects like 'red', 'bold',
596 # etc. don't need to be quoted
596 # etc. don't need to be quoted
597 label = evalstringliteral(context, mapping, args[0])
597 label = evalstringliteral(context, mapping, args[0])
598
598
599 return ui.label(thing, label)
599 return ui.label(thing, label)
600
600
601 def latesttag(context, mapping, args):
601 def latesttag(context, mapping, args):
602 """:latesttag([pattern]): The global tags matching the given pattern on the
602 """:latesttag([pattern]): The global tags matching the given pattern on the
603 most recent globally tagged ancestor of this changeset."""
603 most recent globally tagged ancestor of this changeset."""
604 if len(args) > 1:
604 if len(args) > 1:
605 # i18n: "latesttag" is a keyword
605 # i18n: "latesttag" is a keyword
606 raise error.ParseError(_("latesttag expects at most one argument"))
606 raise error.ParseError(_("latesttag expects at most one argument"))
607
607
608 pattern = None
608 pattern = None
609 if len(args) == 1:
609 if len(args) == 1:
610 pattern = evalstring(context, mapping, args[0])
610 pattern = evalstring(context, mapping, args[0])
611
611
612 return templatekw.showlatesttags(pattern, **mapping)
612 return templatekw.showlatesttags(pattern, **mapping)
613
613
614 def localdate(context, mapping, args):
614 def localdate(context, mapping, args):
615 """:localdate(date[, tz]): Converts a date to the specified timezone.
615 """:localdate(date[, tz]): Converts a date to the specified timezone.
616 The default is local date."""
616 The default is local date."""
617 if not (1 <= len(args) <= 2):
617 if not (1 <= len(args) <= 2):
618 # i18n: "localdate" is a keyword
618 # i18n: "localdate" is a keyword
619 raise error.ParseError(_("localdate expects one or two arguments"))
619 raise error.ParseError(_("localdate expects one or two arguments"))
620
620
621 date = evalfuncarg(context, mapping, args[0])
621 date = evalfuncarg(context, mapping, args[0])
622 try:
622 try:
623 date = util.parsedate(date)
623 date = util.parsedate(date)
624 except AttributeError: # not str nor date tuple
624 except AttributeError: # not str nor date tuple
625 # i18n: "localdate" is a keyword
625 # i18n: "localdate" is a keyword
626 raise error.ParseError(_("localdate expects a date information"))
626 raise error.ParseError(_("localdate expects a date information"))
627 if len(args) >= 2:
627 if len(args) >= 2:
628 tzoffset = None
628 tzoffset = None
629 tz = evalfuncarg(context, mapping, args[1])
629 tz = evalfuncarg(context, mapping, args[1])
630 if isinstance(tz, str):
630 if isinstance(tz, str):
631 tzoffset = util.parsetimezone(tz)
631 tzoffset = util.parsetimezone(tz)
632 if tzoffset is None:
632 if tzoffset is None:
633 try:
633 try:
634 tzoffset = int(tz)
634 tzoffset = int(tz)
635 except (TypeError, ValueError):
635 except (TypeError, ValueError):
636 # i18n: "localdate" is a keyword
636 # i18n: "localdate" is a keyword
637 raise error.ParseError(_("localdate expects a timezone"))
637 raise error.ParseError(_("localdate expects a timezone"))
638 else:
638 else:
639 tzoffset = util.makedate()[1]
639 tzoffset = util.makedate()[1]
640 return (date[0], tzoffset)
640 return (date[0], tzoffset)
641
641
642 def revset(context, mapping, args):
642 def revset(context, mapping, args):
643 """:revset(query[, formatargs...]): Execute a revision set query. See
643 """:revset(query[, formatargs...]): Execute a revision set query. See
644 :hg:`help revset`."""
644 :hg:`help revset`."""
645 if not len(args) > 0:
645 if not len(args) > 0:
646 # i18n: "revset" is a keyword
646 # i18n: "revset" is a keyword
647 raise error.ParseError(_("revset expects one or more arguments"))
647 raise error.ParseError(_("revset expects one or more arguments"))
648
648
649 raw = evalstring(context, mapping, args[0])
649 raw = evalstring(context, mapping, args[0])
650 ctx = mapping['ctx']
650 ctx = mapping['ctx']
651 repo = ctx.repo()
651 repo = ctx.repo()
652
652
653 def query(expr):
653 def query(expr):
654 m = revsetmod.match(repo.ui, expr)
654 m = revsetmod.match(repo.ui, expr)
655 return m(repo)
655 return m(repo)
656
656
657 if len(args) > 1:
657 if len(args) > 1:
658 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
658 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
659 revs = query(revsetmod.formatspec(raw, *formatargs))
659 revs = query(revsetmod.formatspec(raw, *formatargs))
660 revs = list(revs)
660 revs = list(revs)
661 else:
661 else:
662 revsetcache = mapping['cache'].setdefault("revsetcache", {})
662 revsetcache = mapping['cache'].setdefault("revsetcache", {})
663 if raw in revsetcache:
663 if raw in revsetcache:
664 revs = revsetcache[raw]
664 revs = revsetcache[raw]
665 else:
665 else:
666 revs = query(raw)
666 revs = query(raw)
667 revs = list(revs)
667 revs = list(revs)
668 revsetcache[raw] = revs
668 revsetcache[raw] = revs
669
669
670 return templatekw.showrevslist("revision", revs, **mapping)
670 return templatekw.showrevslist("revision", revs, **mapping)
671
671
672 def rstdoc(context, mapping, args):
672 def rstdoc(context, mapping, args):
673 """:rstdoc(text, style): Format ReStructuredText."""
673 """:rstdoc(text, style): Format ReStructuredText."""
674 if len(args) != 2:
674 if len(args) != 2:
675 # i18n: "rstdoc" is a keyword
675 # i18n: "rstdoc" is a keyword
676 raise error.ParseError(_("rstdoc expects two arguments"))
676 raise error.ParseError(_("rstdoc expects two arguments"))
677
677
678 text = evalstring(context, mapping, args[0])
678 text = evalstring(context, mapping, args[0])
679 style = evalstring(context, mapping, args[1])
679 style = evalstring(context, mapping, args[1])
680
680
681 return minirst.format(text, style=style, keep=['verbose'])
681 return minirst.format(text, style=style, keep=['verbose'])
682
682
683 def shortest(context, mapping, args):
683 def shortest(context, mapping, args):
684 """:shortest(node, minlength=4): Obtain the shortest representation of
684 """:shortest(node, minlength=4): Obtain the shortest representation of
685 a node."""
685 a node."""
686 if not (1 <= len(args) <= 2):
686 if not (1 <= len(args) <= 2):
687 # i18n: "shortest" is a keyword
687 # i18n: "shortest" is a keyword
688 raise error.ParseError(_("shortest() expects one or two arguments"))
688 raise error.ParseError(_("shortest() expects one or two arguments"))
689
689
690 node = evalstring(context, mapping, args[0])
690 node = evalstring(context, mapping, args[0])
691
691
692 minlength = 4
692 minlength = 4
693 if len(args) > 1:
693 if len(args) > 1:
694 minlength = evalinteger(context, mapping, args[1],
694 minlength = evalinteger(context, mapping, args[1],
695 # i18n: "shortest" is a keyword
695 # i18n: "shortest" is a keyword
696 _("shortest() expects an integer minlength"))
696 _("shortest() expects an integer minlength"))
697
697
698 cl = mapping['ctx']._repo.changelog
698 cl = mapping['ctx']._repo.changelog
699 def isvalid(test):
699 def isvalid(test):
700 try:
700 try:
701 try:
701 try:
702 cl.index.partialmatch(test)
702 cl.index.partialmatch(test)
703 except AttributeError:
703 except AttributeError:
704 # Pure mercurial doesn't support partialmatch on the index.
704 # Pure mercurial doesn't support partialmatch on the index.
705 # Fallback to the slow way.
705 # Fallback to the slow way.
706 if cl._partialmatch(test) is None:
706 if cl._partialmatch(test) is None:
707 return False
707 return False
708
708
709 try:
709 try:
710 i = int(test)
710 i = int(test)
711 # if we are a pure int, then starting with zero will not be
711 # if we are a pure int, then starting with zero will not be
712 # confused as a rev; or, obviously, if the int is larger than
712 # confused as a rev; or, obviously, if the int is larger than
713 # the value of the tip rev
713 # the value of the tip rev
714 if test[0] == '0' or i > len(cl):
714 if test[0] == '0' or i > len(cl):
715 return True
715 return True
716 return False
716 return False
717 except ValueError:
717 except ValueError:
718 return True
718 return True
719 except error.RevlogError:
719 except error.RevlogError:
720 return False
720 return False
721
721
722 shortest = node
722 shortest = node
723 startlength = max(6, minlength)
723 startlength = max(6, minlength)
724 length = startlength
724 length = startlength
725 while True:
725 while True:
726 test = node[:length]
726 test = node[:length]
727 if isvalid(test):
727 if isvalid(test):
728 shortest = test
728 shortest = test
729 if length == minlength or length > startlength:
729 if length == minlength or length > startlength:
730 return shortest
730 return shortest
731 length -= 1
731 length -= 1
732 else:
732 else:
733 length += 1
733 length += 1
734 if len(shortest) <= length:
734 if len(shortest) <= length:
735 return shortest
735 return shortest
736
736
737 def strip(context, mapping, args):
737 def strip(context, mapping, args):
738 """:strip(text[, chars]): Strip characters from a string. By default,
738 """:strip(text[, chars]): Strip characters from a string. By default,
739 strips all leading and trailing whitespace."""
739 strips all leading and trailing whitespace."""
740 if not (1 <= len(args) <= 2):
740 if not (1 <= len(args) <= 2):
741 # i18n: "strip" is a keyword
741 # i18n: "strip" is a keyword
742 raise error.ParseError(_("strip expects one or two arguments"))
742 raise error.ParseError(_("strip expects one or two arguments"))
743
743
744 text = evalstring(context, mapping, args[0])
744 text = evalstring(context, mapping, args[0])
745 if len(args) == 2:
745 if len(args) == 2:
746 chars = evalstring(context, mapping, args[1])
746 chars = evalstring(context, mapping, args[1])
747 return text.strip(chars)
747 return text.strip(chars)
748 return text.strip()
748 return text.strip()
749
749
750 def sub(context, mapping, args):
750 def sub(context, mapping, args):
751 """:sub(pattern, replacement, expression): Perform text substitution
751 """:sub(pattern, replacement, expression): Perform text substitution
752 using regular expressions."""
752 using regular expressions."""
753 if len(args) != 3:
753 if len(args) != 3:
754 # i18n: "sub" is a keyword
754 # i18n: "sub" is a keyword
755 raise error.ParseError(_("sub expects three arguments"))
755 raise error.ParseError(_("sub expects three arguments"))
756
756
757 pat = evalstring(context, mapping, args[0])
757 pat = evalstring(context, mapping, args[0])
758 rpl = evalstring(context, mapping, args[1])
758 rpl = evalstring(context, mapping, args[1])
759 src = evalstring(context, mapping, args[2])
759 src = evalstring(context, mapping, args[2])
760 try:
760 try:
761 patre = re.compile(pat)
761 patre = re.compile(pat)
762 except re.error:
762 except re.error:
763 # i18n: "sub" is a keyword
763 # i18n: "sub" is a keyword
764 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
764 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
765 try:
765 try:
766 yield patre.sub(rpl, src)
766 yield patre.sub(rpl, src)
767 except re.error:
767 except re.error:
768 # i18n: "sub" is a keyword
768 # i18n: "sub" is a keyword
769 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
769 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
770
770
771 def startswith(context, mapping, args):
771 def startswith(context, mapping, args):
772 """:startswith(pattern, text): Returns the value from the "text" argument
772 """:startswith(pattern, text): Returns the value from the "text" argument
773 if it begins with the content from the "pattern" argument."""
773 if it begins with the content from the "pattern" argument."""
774 if len(args) != 2:
774 if len(args) != 2:
775 # i18n: "startswith" is a keyword
775 # i18n: "startswith" is a keyword
776 raise error.ParseError(_("startswith expects two arguments"))
776 raise error.ParseError(_("startswith expects two arguments"))
777
777
778 patn = evalstring(context, mapping, args[0])
778 patn = evalstring(context, mapping, args[0])
779 text = evalstring(context, mapping, args[1])
779 text = evalstring(context, mapping, args[1])
780 if text.startswith(patn):
780 if text.startswith(patn):
781 return text
781 return text
782 return ''
782 return ''
783
783
784
784
785 def word(context, mapping, args):
785 def word(context, mapping, args):
786 """:word(number, text[, separator]): Return the nth word from a string."""
786 """:word(number, text[, separator]): Return the nth word from a string."""
787 if not (2 <= len(args) <= 3):
787 if not (2 <= len(args) <= 3):
788 # i18n: "word" is a keyword
788 # i18n: "word" is a keyword
789 raise error.ParseError(_("word expects two or three arguments, got %d")
789 raise error.ParseError(_("word expects two or three arguments, got %d")
790 % len(args))
790 % len(args))
791
791
792 num = evalinteger(context, mapping, args[0],
792 num = evalinteger(context, mapping, args[0],
793 # i18n: "word" is a keyword
793 # i18n: "word" is a keyword
794 _("word expects an integer index"))
794 _("word expects an integer index"))
795 text = evalstring(context, mapping, args[1])
795 text = evalstring(context, mapping, args[1])
796 if len(args) == 3:
796 if len(args) == 3:
797 splitter = evalstring(context, mapping, args[2])
797 splitter = evalstring(context, mapping, args[2])
798 else:
798 else:
799 splitter = None
799 splitter = None
800
800
801 tokens = text.split(splitter)
801 tokens = text.split(splitter)
802 if num >= len(tokens) or num < -len(tokens):
802 if num >= len(tokens) or num < -len(tokens):
803 return ''
803 return ''
804 else:
804 else:
805 return tokens[num]
805 return tokens[num]
806
806
807 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
807 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
808 exprmethods = {
808 exprmethods = {
809 "integer": lambda e, c: (runinteger, e[1]),
809 "integer": lambda e, c: (runinteger, e[1]),
810 "string": lambda e, c: (runstring, e[1]),
810 "string": lambda e, c: (runstring, e[1]),
811 "symbol": lambda e, c: (runsymbol, e[1]),
811 "symbol": lambda e, c: (runsymbol, e[1]),
812 "template": buildtemplate,
812 "template": buildtemplate,
813 "group": lambda e, c: compileexp(e[1], c, exprmethods),
813 "group": lambda e, c: compileexp(e[1], c, exprmethods),
814 # ".": buildmember,
814 # ".": buildmember,
815 "|": buildfilter,
815 "|": buildfilter,
816 "%": buildmap,
816 "%": buildmap,
817 "func": buildfunc,
817 "func": buildfunc,
818 }
818 }
819
819
820 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
820 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
821 methods = exprmethods.copy()
821 methods = exprmethods.copy()
822 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
822 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
823
823
824 funcs = {
824 funcs = {
825 "date": date,
825 "date": date,
826 "diff": diff,
826 "diff": diff,
827 "fill": fill,
827 "fill": fill,
828 "get": get,
828 "get": get,
829 "if": if_,
829 "if": if_,
830 "ifcontains": ifcontains,
830 "ifcontains": ifcontains,
831 "ifeq": ifeq,
831 "ifeq": ifeq,
832 "indent": indent,
832 "indent": indent,
833 "join": join,
833 "join": join,
834 "label": label,
834 "label": label,
835 "latesttag": latesttag,
835 "latesttag": latesttag,
836 "localdate": localdate,
836 "localdate": localdate,
837 "pad": pad,
837 "pad": pad,
838 "revset": revset,
838 "revset": revset,
839 "rstdoc": rstdoc,
839 "rstdoc": rstdoc,
840 "shortest": shortest,
840 "shortest": shortest,
841 "startswith": startswith,
841 "startswith": startswith,
842 "strip": strip,
842 "strip": strip,
843 "sub": sub,
843 "sub": sub,
844 "word": word,
844 "word": word,
845 }
845 }
846
846
847 # template engine
847 # template engine
848
848
849 stringify = templatefilters.stringify
849 stringify = templatefilters.stringify
850
850
851 def _flatten(thing):
851 def _flatten(thing):
852 '''yield a single stream from a possibly nested set of iterators'''
852 '''yield a single stream from a possibly nested set of iterators'''
853 if isinstance(thing, str):
853 if isinstance(thing, str):
854 yield thing
854 yield thing
855 elif not util.safehasattr(thing, '__iter__'):
855 elif not util.safehasattr(thing, '__iter__'):
856 if thing is not None:
856 if thing is not None:
857 yield str(thing)
857 yield str(thing)
858 else:
858 else:
859 for i in thing:
859 for i in thing:
860 if isinstance(i, str):
860 if isinstance(i, str):
861 yield i
861 yield i
862 elif not util.safehasattr(i, '__iter__'):
862 elif not util.safehasattr(i, '__iter__'):
863 if i is not None:
863 if i is not None:
864 yield str(i)
864 yield str(i)
865 elif i is not None:
865 elif i is not None:
866 for j in _flatten(i):
866 for j in _flatten(i):
867 yield j
867 yield j
868
868
869 def unquotestring(s):
869 def unquotestring(s):
870 '''unwrap quotes if any; otherwise returns unmodified string'''
870 '''unwrap quotes if any; otherwise returns unmodified string'''
871 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
871 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
872 return s
872 return s
873 return s[1:-1]
873 return s[1:-1]
874
874
875 class engine(object):
875 class engine(object):
876 '''template expansion engine.
876 '''template expansion engine.
877
877
878 template expansion works like this. a map file contains key=value
878 template expansion works like this. a map file contains key=value
879 pairs. if value is quoted, it is treated as string. otherwise, it
879 pairs. if value is quoted, it is treated as string. otherwise, it
880 is treated as name of template file.
880 is treated as name of template file.
881
881
882 templater is asked to expand a key in map. it looks up key, and
882 templater is asked to expand a key in map. it looks up key, and
883 looks for strings like this: {foo}. it expands {foo} by looking up
883 looks for strings like this: {foo}. it expands {foo} by looking up
884 foo in map, and substituting it. expansion is recursive: it stops
884 foo in map, and substituting it. expansion is recursive: it stops
885 when there is no more {foo} to replace.
885 when there is no more {foo} to replace.
886
886
887 expansion also allows formatting and filtering.
887 expansion also allows formatting and filtering.
888
888
889 format uses key to expand each item in list. syntax is
889 format uses key to expand each item in list. syntax is
890 {key%format}.
890 {key%format}.
891
891
892 filter uses function to transform value. syntax is
892 filter uses function to transform value. syntax is
893 {key|filter1|filter2|...}.'''
893 {key|filter1|filter2|...}.'''
894
894
895 def __init__(self, loader, filters=None, defaults=None):
895 def __init__(self, loader, filters=None, defaults=None):
896 self._loader = loader
896 self._loader = loader
897 if filters is None:
897 if filters is None:
898 filters = {}
898 filters = {}
899 self._filters = filters
899 self._filters = filters
900 if defaults is None:
900 if defaults is None:
901 defaults = {}
901 defaults = {}
902 self._defaults = defaults
902 self._defaults = defaults
903 self._cache = {} # key: (func, data)
903 self._cache = {} # key: (func, data)
904
904
905 def _load(self, t):
905 def _load(self, t):
906 '''load, parse, and cache a template'''
906 '''load, parse, and cache a template'''
907 if t not in self._cache:
907 if t not in self._cache:
908 # put poison to cut recursion while compiling 't'
908 # put poison to cut recursion while compiling 't'
909 self._cache[t] = (_runrecursivesymbol, t)
909 self._cache[t] = (_runrecursivesymbol, t)
910 try:
910 try:
911 self._cache[t] = compiletemplate(self._loader(t), self)
911 self._cache[t] = compiletemplate(self._loader(t), self)
912 except: # re-raises
912 except: # re-raises
913 del self._cache[t]
913 del self._cache[t]
914 raise
914 raise
915 return self._cache[t]
915 return self._cache[t]
916
916
917 def process(self, t, mapping):
917 def process(self, t, mapping):
918 '''Perform expansion. t is name of map element to expand.
918 '''Perform expansion. t is name of map element to expand.
919 mapping contains added elements for use during expansion. Is a
919 mapping contains added elements for use during expansion. Is a
920 generator.'''
920 generator.'''
921 func, data = self._load(t)
921 func, data = self._load(t)
922 return _flatten(func(self, mapping, data))
922 return _flatten(func(self, mapping, data))
923
923
924 engines = {'default': engine}
924 engines = {'default': engine}
925
925
926 def stylelist():
926 def stylelist():
927 paths = templatepaths()
927 paths = templatepaths()
928 if not paths:
928 if not paths:
929 return _('no templates found, try `hg debuginstall` for more info')
929 return _('no templates found, try `hg debuginstall` for more info')
930 dirlist = os.listdir(paths[0])
930 dirlist = os.listdir(paths[0])
931 stylelist = []
931 stylelist = []
932 for file in dirlist:
932 for file in dirlist:
933 split = file.split(".")
933 split = file.split(".")
934 if split[-1] in ('orig', 'rej'):
934 if split[-1] in ('orig', 'rej'):
935 continue
935 continue
936 if split[0] == "map-cmdline":
936 if split[0] == "map-cmdline":
937 stylelist.append(split[1])
937 stylelist.append(split[1])
938 return ", ".join(sorted(stylelist))
938 return ", ".join(sorted(stylelist))
939
939
940 class TemplateNotFound(error.Abort):
940 class TemplateNotFound(error.Abort):
941 pass
941 pass
942
942
943 class templater(object):
943 class templater(object):
944
944
945 def __init__(self, mapfile, filters=None, defaults=None, cache=None,
945 def __init__(self, mapfile, filters=None, defaults=None, cache=None,
946 minchunk=1024, maxchunk=65536):
946 minchunk=1024, maxchunk=65536):
947 '''set up template engine.
947 '''set up template engine.
948 mapfile is name of file to read map definitions from.
948 mapfile is name of file to read map definitions from.
949 filters is dict of functions. each transforms a value into another.
949 filters is dict of functions. each transforms a value into another.
950 defaults is dict of default map definitions.'''
950 defaults is dict of default map definitions.'''
951 if filters is None:
951 if filters is None:
952 filters = {}
952 filters = {}
953 if defaults is None:
953 if defaults is None:
954 defaults = {}
954 defaults = {}
955 if cache is None:
955 if cache is None:
956 cache = {}
956 cache = {}
957 self.mapfile = mapfile or 'template'
957 self.mapfile = mapfile or 'template'
958 self.cache = cache.copy()
958 self.cache = cache.copy()
959 self.map = {}
959 self.map = {}
960 if mapfile:
960 if mapfile:
961 self.base = os.path.dirname(mapfile)
961 self.base = os.path.dirname(mapfile)
962 else:
962 else:
963 self.base = ''
963 self.base = ''
964 self.filters = templatefilters.filters.copy()
964 self.filters = templatefilters.filters.copy()
965 self.filters.update(filters)
965 self.filters.update(filters)
966 self.defaults = defaults
966 self.defaults = defaults
967 self.minchunk, self.maxchunk = minchunk, maxchunk
967 self.minchunk, self.maxchunk = minchunk, maxchunk
968 self.ecache = {}
968 self.ecache = {}
969
969
970 if not mapfile:
970 if not mapfile:
971 return
971 return
972 if not os.path.exists(mapfile):
972 if not os.path.exists(mapfile):
973 raise error.Abort(_("style '%s' not found") % mapfile,
973 raise error.Abort(_("style '%s' not found") % mapfile,
974 hint=_("available styles: %s") % stylelist())
974 hint=_("available styles: %s") % stylelist())
975
975
976 conf = config.config(includepaths=templatepaths())
976 conf = config.config(includepaths=templatepaths())
977 conf.read(mapfile)
977 conf.read(mapfile)
978
978
979 for key, val in conf[''].items():
979 for key, val in conf[''].items():
980 if not val:
980 if not val:
981 raise error.ParseError(_('missing value'), conf.source('', key))
981 raise error.ParseError(_('missing value'), conf.source('', key))
982 if val[0] in "'\"":
982 if val[0] in "'\"":
983 if val[0] != val[-1]:
983 if val[0] != val[-1]:
984 raise error.ParseError(_('unmatched quotes'),
984 raise error.ParseError(_('unmatched quotes'),
985 conf.source('', key))
985 conf.source('', key))
986 self.cache[key] = unquotestring(val)
986 self.cache[key] = unquotestring(val)
987 else:
987 else:
988 val = 'default', val
988 val = 'default', val
989 if ':' in val[1]:
989 if ':' in val[1]:
990 val = val[1].split(':', 1)
990 val = val[1].split(':', 1)
991 self.map[key] = val[0], os.path.join(self.base, val[1])
991 self.map[key] = val[0], os.path.join(self.base, val[1])
992
992
993 def __contains__(self, key):
993 def __contains__(self, key):
994 return key in self.cache or key in self.map
994 return key in self.cache or key in self.map
995
995
996 def load(self, t):
996 def load(self, t):
997 '''Get the template for the given template name. Use a local cache.'''
997 '''Get the template for the given template name. Use a local cache.'''
998 if t not in self.cache:
998 if t not in self.cache:
999 try:
999 try:
1000 self.cache[t] = util.readfile(self.map[t][1])
1000 self.cache[t] = util.readfile(self.map[t][1])
1001 except KeyError as inst:
1001 except KeyError as inst:
1002 raise TemplateNotFound(_('"%s" not in template map') %
1002 raise TemplateNotFound(_('"%s" not in template map') %
1003 inst.args[0])
1003 inst.args[0])
1004 except IOError as inst:
1004 except IOError as inst:
1005 raise IOError(inst.args[0], _('template file %s: %s') %
1005 raise IOError(inst.args[0], _('template file %s: %s') %
1006 (self.map[t][1], inst.args[1]))
1006 (self.map[t][1], inst.args[1]))
1007 return self.cache[t]
1007 return self.cache[t]
1008
1008
1009 def __call__(self, t, **mapping):
1009 def __call__(self, t, **mapping):
1010 ttype = t in self.map and self.map[t][0] or 'default'
1010 ttype = t in self.map and self.map[t][0] or 'default'
1011 if ttype not in self.ecache:
1011 if ttype not in self.ecache:
1012 self.ecache[ttype] = engines[ttype](self.load,
1012 self.ecache[ttype] = engines[ttype](self.load,
1013 self.filters, self.defaults)
1013 self.filters, self.defaults)
1014 proc = self.ecache[ttype]
1014 proc = self.ecache[ttype]
1015
1015
1016 stream = proc.process(t, mapping)
1016 stream = proc.process(t, mapping)
1017 if self.minchunk:
1017 if self.minchunk:
1018 stream = util.increasingchunks(stream, min=self.minchunk,
1018 stream = util.increasingchunks(stream, min=self.minchunk,
1019 max=self.maxchunk)
1019 max=self.maxchunk)
1020 return stream
1020 return stream
1021
1021
1022 def templatepaths():
1022 def templatepaths():
1023 '''return locations used for template files.'''
1023 '''return locations used for template files.'''
1024 pathsrel = ['templates']
1024 pathsrel = ['templates']
1025 paths = [os.path.normpath(os.path.join(util.datapath, f))
1025 paths = [os.path.normpath(os.path.join(util.datapath, f))
1026 for f in pathsrel]
1026 for f in pathsrel]
1027 return [p for p in paths if os.path.isdir(p)]
1027 return [p for p in paths if os.path.isdir(p)]
1028
1028
1029 def templatepath(name):
1029 def templatepath(name):
1030 '''return location of template file. returns None if not found.'''
1030 '''return location of template file. returns None if not found.'''
1031 for p in templatepaths():
1031 for p in templatepaths():
1032 f = os.path.join(p, name)
1032 f = os.path.join(p, name)
1033 if os.path.exists(f):
1033 if os.path.exists(f):
1034 return f
1034 return f
1035 return None
1035 return None
1036
1036
1037 def stylemap(styles, paths=None):
1037 def stylemap(styles, paths=None):
1038 """Return path to mapfile for a given style.
1038 """Return path to mapfile for a given style.
1039
1039
1040 Searches mapfile in the following locations:
1040 Searches mapfile in the following locations:
1041 1. templatepath/style/map
1041 1. templatepath/style/map
1042 2. templatepath/map-style
1042 2. templatepath/map-style
1043 3. templatepath/map
1043 3. templatepath/map
1044 """
1044 """
1045
1045
1046 if paths is None:
1046 if paths is None:
1047 paths = templatepaths()
1047 paths = templatepaths()
1048 elif isinstance(paths, str):
1048 elif isinstance(paths, str):
1049 paths = [paths]
1049 paths = [paths]
1050
1050
1051 if isinstance(styles, str):
1051 if isinstance(styles, str):
1052 styles = [styles]
1052 styles = [styles]
1053
1053
1054 for style in styles:
1054 for style in styles:
1055 # only plain name is allowed to honor template paths
1055 # only plain name is allowed to honor template paths
1056 if (not style
1056 if (not style
1057 or style in (os.curdir, os.pardir)
1057 or style in (os.curdir, os.pardir)
1058 or os.sep in style
1058 or os.sep in style
1059 or os.altsep and os.altsep in style):
1059 or os.altsep and os.altsep in style):
1060 continue
1060 continue
1061 locations = [os.path.join(style, 'map'), 'map-' + style]
1061 locations = [os.path.join(style, 'map'), 'map-' + style]
1062 locations.append('map')
1062 locations.append('map')
1063
1063
1064 for path in paths:
1064 for path in paths:
1065 for location in locations:
1065 for location in locations:
1066 mapfile = os.path.join(path, location)
1066 mapfile = os.path.join(path, location)
1067 if os.path.isfile(mapfile):
1067 if os.path.isfile(mapfile):
1068 return style, mapfile
1068 return style, mapfile
1069
1069
1070 raise RuntimeError("No hgweb templates found in %r" % paths)
1070 raise RuntimeError("No hgweb templates found in %r" % paths)
1071
1071
1072 def loadfunction(ui, extname, registrarobj):
1073 """Load template function from specified registrarobj
1074 """
1075 for name, func in registrarobj._table.iteritems():
1076 funcs[name] = func
1077
1072 # tell hggettext to extract docstrings from these functions:
1078 # tell hggettext to extract docstrings from these functions:
1073 i18nfunctions = funcs.values()
1079 i18nfunctions = funcs.values()
@@ -1,3695 +1,3718 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add a
4 $ hg add a
5 $ echo line 1 > b
5 $ echo line 1 > b
6 $ echo line 2 >> b
6 $ echo line 2 >> b
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8
8
9 $ hg add b
9 $ hg add b
10 $ echo other 1 > c
10 $ echo other 1 > c
11 $ echo other 2 >> c
11 $ echo other 2 >> c
12 $ echo >> c
12 $ echo >> c
13 $ echo other 3 >> c
13 $ echo other 3 >> c
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15
15
16 $ hg add c
16 $ hg add c
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 $ echo c >> c
18 $ echo c >> c
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20
20
21 $ echo foo > .hg/branch
21 $ echo foo > .hg/branch
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23
23
24 $ hg co -q 3
24 $ hg co -q 3
25 $ echo other 4 >> d
25 $ echo other 4 >> d
26 $ hg add d
26 $ hg add d
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28
28
29 $ hg merge -q foo
29 $ hg merge -q foo
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31
31
32 Second branch starting at nullrev:
32 Second branch starting at nullrev:
33
33
34 $ hg update null
34 $ hg update null
35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
36 $ echo second > second
36 $ echo second > second
37 $ hg add second
37 $ hg add second
38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
39 created new head
39 created new head
40
40
41 $ echo third > third
41 $ echo third > third
42 $ hg add third
42 $ hg add third
43 $ hg mv second fourth
43 $ hg mv second fourth
44 $ hg commit -m third -d "2020-01-01 10:01"
44 $ hg commit -m third -d "2020-01-01 10:01"
45
45
46 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
46 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
47 fourth (second)
47 fourth (second)
48 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
48 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
49 second -> fourth
49 second -> fourth
50 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
50 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
51 8 t
51 8 t
52 7 f
52 7 f
53
53
54 Working-directory revision has special identifiers, though they are still
54 Working-directory revision has special identifiers, though they are still
55 experimental:
55 experimental:
56
56
57 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
57 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
58 2147483647:ffffffffffffffffffffffffffffffffffffffff
58 2147483647:ffffffffffffffffffffffffffffffffffffffff
59
59
60 Some keywords are invalid for working-directory revision, but they should
60 Some keywords are invalid for working-directory revision, but they should
61 never cause crash:
61 never cause crash:
62
62
63 $ hg log -r 'wdir()' -T '{manifest}\n'
63 $ hg log -r 'wdir()' -T '{manifest}\n'
64
64
65
65
66 Quoting for ui.logtemplate
66 Quoting for ui.logtemplate
67
67
68 $ hg tip --config "ui.logtemplate={rev}\n"
68 $ hg tip --config "ui.logtemplate={rev}\n"
69 8
69 8
70 $ hg tip --config "ui.logtemplate='{rev}\n'"
70 $ hg tip --config "ui.logtemplate='{rev}\n'"
71 8
71 8
72 $ hg tip --config 'ui.logtemplate="{rev}\n"'
72 $ hg tip --config 'ui.logtemplate="{rev}\n"'
73 8
73 8
74 $ hg tip --config 'ui.logtemplate=n{rev}\n'
74 $ hg tip --config 'ui.logtemplate=n{rev}\n'
75 n8
75 n8
76
76
77 Make sure user/global hgrc does not affect tests
77 Make sure user/global hgrc does not affect tests
78
78
79 $ echo '[ui]' > .hg/hgrc
79 $ echo '[ui]' > .hg/hgrc
80 $ echo 'logtemplate =' >> .hg/hgrc
80 $ echo 'logtemplate =' >> .hg/hgrc
81 $ echo 'style =' >> .hg/hgrc
81 $ echo 'style =' >> .hg/hgrc
82
82
83 Add some simple styles to settings
83 Add some simple styles to settings
84
84
85 $ echo '[templates]' >> .hg/hgrc
85 $ echo '[templates]' >> .hg/hgrc
86 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
86 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
87 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
87 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
88
88
89 $ hg log -l1 -Tsimple
89 $ hg log -l1 -Tsimple
90 8
90 8
91 $ hg log -l1 -Tsimple2
91 $ hg log -l1 -Tsimple2
92 8
92 8
93
93
94 Test templates and style maps in files:
94 Test templates and style maps in files:
95
95
96 $ echo "{rev}" > tmpl
96 $ echo "{rev}" > tmpl
97 $ hg log -l1 -T./tmpl
97 $ hg log -l1 -T./tmpl
98 8
98 8
99 $ hg log -l1 -Tblah/blah
99 $ hg log -l1 -Tblah/blah
100 blah/blah (no-eol)
100 blah/blah (no-eol)
101
101
102 $ printf 'changeset = "{rev}\\n"\n' > map-simple
102 $ printf 'changeset = "{rev}\\n"\n' > map-simple
103 $ hg log -l1 -T./map-simple
103 $ hg log -l1 -T./map-simple
104 8
104 8
105
105
106 Template should precede style option
106 Template should precede style option
107
107
108 $ hg log -l1 --style default -T '{rev}\n'
108 $ hg log -l1 --style default -T '{rev}\n'
109 8
109 8
110
110
111 Add a commit with empty description, to ensure that the templates
111 Add a commit with empty description, to ensure that the templates
112 below will omit the description line.
112 below will omit the description line.
113
113
114 $ echo c >> c
114 $ echo c >> c
115 $ hg add c
115 $ hg add c
116 $ hg commit -qm ' '
116 $ hg commit -qm ' '
117
117
118 Default style is like normal output. Phases style should be the same
118 Default style is like normal output. Phases style should be the same
119 as default style, except for extra phase lines.
119 as default style, except for extra phase lines.
120
120
121 $ hg log > log.out
121 $ hg log > log.out
122 $ hg log --style default > style.out
122 $ hg log --style default > style.out
123 $ cmp log.out style.out || diff -u log.out style.out
123 $ cmp log.out style.out || diff -u log.out style.out
124 $ hg log -T phases > phases.out
124 $ hg log -T phases > phases.out
125 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
125 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
126 +phase: draft
126 +phase: draft
127 +phase: draft
127 +phase: draft
128 +phase: draft
128 +phase: draft
129 +phase: draft
129 +phase: draft
130 +phase: draft
130 +phase: draft
131 +phase: draft
131 +phase: draft
132 +phase: draft
132 +phase: draft
133 +phase: draft
133 +phase: draft
134 +phase: draft
134 +phase: draft
135 +phase: draft
135 +phase: draft
136
136
137 $ hg log -v > log.out
137 $ hg log -v > log.out
138 $ hg log -v --style default > style.out
138 $ hg log -v --style default > style.out
139 $ cmp log.out style.out || diff -u log.out style.out
139 $ cmp log.out style.out || diff -u log.out style.out
140 $ hg log -v -T phases > phases.out
140 $ hg log -v -T phases > phases.out
141 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
141 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
142 +phase: draft
142 +phase: draft
143 +phase: draft
143 +phase: draft
144 +phase: draft
144 +phase: draft
145 +phase: draft
145 +phase: draft
146 +phase: draft
146 +phase: draft
147 +phase: draft
147 +phase: draft
148 +phase: draft
148 +phase: draft
149 +phase: draft
149 +phase: draft
150 +phase: draft
150 +phase: draft
151 +phase: draft
151 +phase: draft
152
152
153 $ hg log -q > log.out
153 $ hg log -q > log.out
154 $ hg log -q --style default > style.out
154 $ hg log -q --style default > style.out
155 $ cmp log.out style.out || diff -u log.out style.out
155 $ cmp log.out style.out || diff -u log.out style.out
156 $ hg log -q -T phases > phases.out
156 $ hg log -q -T phases > phases.out
157 $ cmp log.out phases.out || diff -u log.out phases.out
157 $ cmp log.out phases.out || diff -u log.out phases.out
158
158
159 $ hg log --debug > log.out
159 $ hg log --debug > log.out
160 $ hg log --debug --style default > style.out
160 $ hg log --debug --style default > style.out
161 $ cmp log.out style.out || diff -u log.out style.out
161 $ cmp log.out style.out || diff -u log.out style.out
162 $ hg log --debug -T phases > phases.out
162 $ hg log --debug -T phases > phases.out
163 $ cmp log.out phases.out || diff -u log.out phases.out
163 $ cmp log.out phases.out || diff -u log.out phases.out
164
164
165 Default style of working-directory revision should also be the same (but
165 Default style of working-directory revision should also be the same (but
166 date may change while running tests):
166 date may change while running tests):
167
167
168 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
168 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
169 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
169 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
170 $ cmp log.out style.out || diff -u log.out style.out
170 $ cmp log.out style.out || diff -u log.out style.out
171
171
172 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
172 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
173 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
173 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
174 $ cmp log.out style.out || diff -u log.out style.out
174 $ cmp log.out style.out || diff -u log.out style.out
175
175
176 $ hg log -r 'wdir()' -q > log.out
176 $ hg log -r 'wdir()' -q > log.out
177 $ hg log -r 'wdir()' -q --style default > style.out
177 $ hg log -r 'wdir()' -q --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
179
180 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
180 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
181 $ hg log -r 'wdir()' --debug --style default \
181 $ hg log -r 'wdir()' --debug --style default \
182 > | sed 's|^date:.*|date:|' > style.out
182 > | sed 's|^date:.*|date:|' > style.out
183 $ cmp log.out style.out || diff -u log.out style.out
183 $ cmp log.out style.out || diff -u log.out style.out
184
184
185 Default style should also preserve color information (issue2866):
185 Default style should also preserve color information (issue2866):
186
186
187 $ cp $HGRCPATH $HGRCPATH-bak
187 $ cp $HGRCPATH $HGRCPATH-bak
188 $ cat <<EOF >> $HGRCPATH
188 $ cat <<EOF >> $HGRCPATH
189 > [extensions]
189 > [extensions]
190 > color=
190 > color=
191 > EOF
191 > EOF
192
192
193 $ hg --color=debug log > log.out
193 $ hg --color=debug log > log.out
194 $ hg --color=debug log --style default > style.out
194 $ hg --color=debug log --style default > style.out
195 $ cmp log.out style.out || diff -u log.out style.out
195 $ cmp log.out style.out || diff -u log.out style.out
196 $ hg --color=debug log -T phases > phases.out
196 $ hg --color=debug log -T phases > phases.out
197 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
197 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
198 +[log.phase|phase: draft]
198 +[log.phase|phase: draft]
199 +[log.phase|phase: draft]
199 +[log.phase|phase: draft]
200 +[log.phase|phase: draft]
200 +[log.phase|phase: draft]
201 +[log.phase|phase: draft]
201 +[log.phase|phase: draft]
202 +[log.phase|phase: draft]
202 +[log.phase|phase: draft]
203 +[log.phase|phase: draft]
203 +[log.phase|phase: draft]
204 +[log.phase|phase: draft]
204 +[log.phase|phase: draft]
205 +[log.phase|phase: draft]
205 +[log.phase|phase: draft]
206 +[log.phase|phase: draft]
206 +[log.phase|phase: draft]
207 +[log.phase|phase: draft]
207 +[log.phase|phase: draft]
208
208
209 $ hg --color=debug -v log > log.out
209 $ hg --color=debug -v log > log.out
210 $ hg --color=debug -v log --style default > style.out
210 $ hg --color=debug -v log --style default > style.out
211 $ cmp log.out style.out || diff -u log.out style.out
211 $ cmp log.out style.out || diff -u log.out style.out
212 $ hg --color=debug -v log -T phases > phases.out
212 $ hg --color=debug -v log -T phases > phases.out
213 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
213 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
214 +[log.phase|phase: draft]
214 +[log.phase|phase: draft]
215 +[log.phase|phase: draft]
215 +[log.phase|phase: draft]
216 +[log.phase|phase: draft]
216 +[log.phase|phase: draft]
217 +[log.phase|phase: draft]
217 +[log.phase|phase: draft]
218 +[log.phase|phase: draft]
218 +[log.phase|phase: draft]
219 +[log.phase|phase: draft]
219 +[log.phase|phase: draft]
220 +[log.phase|phase: draft]
220 +[log.phase|phase: draft]
221 +[log.phase|phase: draft]
221 +[log.phase|phase: draft]
222 +[log.phase|phase: draft]
222 +[log.phase|phase: draft]
223 +[log.phase|phase: draft]
223 +[log.phase|phase: draft]
224
224
225 $ hg --color=debug -q log > log.out
225 $ hg --color=debug -q log > log.out
226 $ hg --color=debug -q log --style default > style.out
226 $ hg --color=debug -q log --style default > style.out
227 $ cmp log.out style.out || diff -u log.out style.out
227 $ cmp log.out style.out || diff -u log.out style.out
228 $ hg --color=debug -q log -T phases > phases.out
228 $ hg --color=debug -q log -T phases > phases.out
229 $ cmp log.out phases.out || diff -u log.out phases.out
229 $ cmp log.out phases.out || diff -u log.out phases.out
230
230
231 $ hg --color=debug --debug log > log.out
231 $ hg --color=debug --debug log > log.out
232 $ hg --color=debug --debug log --style default > style.out
232 $ hg --color=debug --debug log --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 $ hg --color=debug --debug log -T phases > phases.out
234 $ hg --color=debug --debug log -T phases > phases.out
235 $ cmp log.out phases.out || diff -u log.out phases.out
235 $ cmp log.out phases.out || diff -u log.out phases.out
236
236
237 $ mv $HGRCPATH-bak $HGRCPATH
237 $ mv $HGRCPATH-bak $HGRCPATH
238
238
239 Remove commit with empty commit message, so as to not pollute further
239 Remove commit with empty commit message, so as to not pollute further
240 tests.
240 tests.
241
241
242 $ hg --config extensions.strip= strip -q .
242 $ hg --config extensions.strip= strip -q .
243
243
244 Revision with no copies (used to print a traceback):
244 Revision with no copies (used to print a traceback):
245
245
246 $ hg tip -v --template '\n'
246 $ hg tip -v --template '\n'
247
247
248
248
249 Compact style works:
249 Compact style works:
250
250
251 $ hg log -Tcompact
251 $ hg log -Tcompact
252 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
252 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
253 third
253 third
254
254
255 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
255 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
256 second
256 second
257
257
258 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
258 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
259 merge
259 merge
260
260
261 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
261 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
262 new head
262 new head
263
263
264 4 bbe44766e73d 1970-01-17 04:53 +0000 person
264 4 bbe44766e73d 1970-01-17 04:53 +0000 person
265 new branch
265 new branch
266
266
267 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
267 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
268 no user, no domain
268 no user, no domain
269
269
270 2 97054abb4ab8 1970-01-14 21:20 +0000 other
270 2 97054abb4ab8 1970-01-14 21:20 +0000 other
271 no person
271 no person
272
272
273 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
273 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
274 other 1
274 other 1
275
275
276 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
276 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
277 line 1
277 line 1
278
278
279
279
280 $ hg log -v --style compact
280 $ hg log -v --style compact
281 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
281 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
282 third
282 third
283
283
284 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
284 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
285 second
285 second
286
286
287 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
287 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
288 merge
288 merge
289
289
290 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
290 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
291 new head
291 new head
292
292
293 4 bbe44766e73d 1970-01-17 04:53 +0000 person
293 4 bbe44766e73d 1970-01-17 04:53 +0000 person
294 new branch
294 new branch
295
295
296 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
296 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
297 no user, no domain
297 no user, no domain
298
298
299 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
299 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
300 no person
300 no person
301
301
302 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
302 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
303 other 1
303 other 1
304 other 2
304 other 2
305
305
306 other 3
306 other 3
307
307
308 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
308 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
309 line 1
309 line 1
310 line 2
310 line 2
311
311
312
312
313 $ hg log --debug --style compact
313 $ hg log --debug --style compact
314 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
314 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
315 third
315 third
316
316
317 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
317 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
318 second
318 second
319
319
320 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
320 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
321 merge
321 merge
322
322
323 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
323 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
324 new head
324 new head
325
325
326 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
326 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
327 new branch
327 new branch
328
328
329 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
329 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
330 no user, no domain
330 no user, no domain
331
331
332 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
332 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
333 no person
333 no person
334
334
335 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
335 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
336 other 1
336 other 1
337 other 2
337 other 2
338
338
339 other 3
339 other 3
340
340
341 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
341 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
342 line 1
342 line 1
343 line 2
343 line 2
344
344
345
345
346 Test xml styles:
346 Test xml styles:
347
347
348 $ hg log --style xml -r 'not all()'
348 $ hg log --style xml -r 'not all()'
349 <?xml version="1.0"?>
349 <?xml version="1.0"?>
350 <log>
350 <log>
351 </log>
351 </log>
352
352
353 $ hg log --style xml
353 $ hg log --style xml
354 <?xml version="1.0"?>
354 <?xml version="1.0"?>
355 <log>
355 <log>
356 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
356 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
357 <tag>tip</tag>
357 <tag>tip</tag>
358 <author email="test">test</author>
358 <author email="test">test</author>
359 <date>2020-01-01T10:01:00+00:00</date>
359 <date>2020-01-01T10:01:00+00:00</date>
360 <msg xml:space="preserve">third</msg>
360 <msg xml:space="preserve">third</msg>
361 </logentry>
361 </logentry>
362 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
362 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
363 <parent revision="-1" node="0000000000000000000000000000000000000000" />
363 <parent revision="-1" node="0000000000000000000000000000000000000000" />
364 <author email="user@hostname">User Name</author>
364 <author email="user@hostname">User Name</author>
365 <date>1970-01-12T13:46:40+00:00</date>
365 <date>1970-01-12T13:46:40+00:00</date>
366 <msg xml:space="preserve">second</msg>
366 <msg xml:space="preserve">second</msg>
367 </logentry>
367 </logentry>
368 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
368 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
369 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
369 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
370 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
370 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
371 <author email="person">person</author>
371 <author email="person">person</author>
372 <date>1970-01-18T08:40:01+00:00</date>
372 <date>1970-01-18T08:40:01+00:00</date>
373 <msg xml:space="preserve">merge</msg>
373 <msg xml:space="preserve">merge</msg>
374 </logentry>
374 </logentry>
375 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
375 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
376 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
376 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
377 <author email="person">person</author>
377 <author email="person">person</author>
378 <date>1970-01-18T08:40:00+00:00</date>
378 <date>1970-01-18T08:40:00+00:00</date>
379 <msg xml:space="preserve">new head</msg>
379 <msg xml:space="preserve">new head</msg>
380 </logentry>
380 </logentry>
381 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
381 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
382 <branch>foo</branch>
382 <branch>foo</branch>
383 <author email="person">person</author>
383 <author email="person">person</author>
384 <date>1970-01-17T04:53:20+00:00</date>
384 <date>1970-01-17T04:53:20+00:00</date>
385 <msg xml:space="preserve">new branch</msg>
385 <msg xml:space="preserve">new branch</msg>
386 </logentry>
386 </logentry>
387 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
387 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
388 <author email="person">person</author>
388 <author email="person">person</author>
389 <date>1970-01-16T01:06:40+00:00</date>
389 <date>1970-01-16T01:06:40+00:00</date>
390 <msg xml:space="preserve">no user, no domain</msg>
390 <msg xml:space="preserve">no user, no domain</msg>
391 </logentry>
391 </logentry>
392 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
392 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
393 <author email="other@place">other</author>
393 <author email="other@place">other</author>
394 <date>1970-01-14T21:20:00+00:00</date>
394 <date>1970-01-14T21:20:00+00:00</date>
395 <msg xml:space="preserve">no person</msg>
395 <msg xml:space="preserve">no person</msg>
396 </logentry>
396 </logentry>
397 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
397 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
398 <author email="other@place">A. N. Other</author>
398 <author email="other@place">A. N. Other</author>
399 <date>1970-01-13T17:33:20+00:00</date>
399 <date>1970-01-13T17:33:20+00:00</date>
400 <msg xml:space="preserve">other 1
400 <msg xml:space="preserve">other 1
401 other 2
401 other 2
402
402
403 other 3</msg>
403 other 3</msg>
404 </logentry>
404 </logentry>
405 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
405 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
406 <author email="user@hostname">User Name</author>
406 <author email="user@hostname">User Name</author>
407 <date>1970-01-12T13:46:40+00:00</date>
407 <date>1970-01-12T13:46:40+00:00</date>
408 <msg xml:space="preserve">line 1
408 <msg xml:space="preserve">line 1
409 line 2</msg>
409 line 2</msg>
410 </logentry>
410 </logentry>
411 </log>
411 </log>
412
412
413 $ hg log -v --style xml
413 $ hg log -v --style xml
414 <?xml version="1.0"?>
414 <?xml version="1.0"?>
415 <log>
415 <log>
416 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
416 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
417 <tag>tip</tag>
417 <tag>tip</tag>
418 <author email="test">test</author>
418 <author email="test">test</author>
419 <date>2020-01-01T10:01:00+00:00</date>
419 <date>2020-01-01T10:01:00+00:00</date>
420 <msg xml:space="preserve">third</msg>
420 <msg xml:space="preserve">third</msg>
421 <paths>
421 <paths>
422 <path action="A">fourth</path>
422 <path action="A">fourth</path>
423 <path action="A">third</path>
423 <path action="A">third</path>
424 <path action="R">second</path>
424 <path action="R">second</path>
425 </paths>
425 </paths>
426 <copies>
426 <copies>
427 <copy source="second">fourth</copy>
427 <copy source="second">fourth</copy>
428 </copies>
428 </copies>
429 </logentry>
429 </logentry>
430 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
430 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
431 <parent revision="-1" node="0000000000000000000000000000000000000000" />
431 <parent revision="-1" node="0000000000000000000000000000000000000000" />
432 <author email="user@hostname">User Name</author>
432 <author email="user@hostname">User Name</author>
433 <date>1970-01-12T13:46:40+00:00</date>
433 <date>1970-01-12T13:46:40+00:00</date>
434 <msg xml:space="preserve">second</msg>
434 <msg xml:space="preserve">second</msg>
435 <paths>
435 <paths>
436 <path action="A">second</path>
436 <path action="A">second</path>
437 </paths>
437 </paths>
438 </logentry>
438 </logentry>
439 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
439 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
440 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
440 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
441 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
441 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
442 <author email="person">person</author>
442 <author email="person">person</author>
443 <date>1970-01-18T08:40:01+00:00</date>
443 <date>1970-01-18T08:40:01+00:00</date>
444 <msg xml:space="preserve">merge</msg>
444 <msg xml:space="preserve">merge</msg>
445 <paths>
445 <paths>
446 </paths>
446 </paths>
447 </logentry>
447 </logentry>
448 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
448 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
449 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
449 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
450 <author email="person">person</author>
450 <author email="person">person</author>
451 <date>1970-01-18T08:40:00+00:00</date>
451 <date>1970-01-18T08:40:00+00:00</date>
452 <msg xml:space="preserve">new head</msg>
452 <msg xml:space="preserve">new head</msg>
453 <paths>
453 <paths>
454 <path action="A">d</path>
454 <path action="A">d</path>
455 </paths>
455 </paths>
456 </logentry>
456 </logentry>
457 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
457 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
458 <branch>foo</branch>
458 <branch>foo</branch>
459 <author email="person">person</author>
459 <author email="person">person</author>
460 <date>1970-01-17T04:53:20+00:00</date>
460 <date>1970-01-17T04:53:20+00:00</date>
461 <msg xml:space="preserve">new branch</msg>
461 <msg xml:space="preserve">new branch</msg>
462 <paths>
462 <paths>
463 </paths>
463 </paths>
464 </logentry>
464 </logentry>
465 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
465 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
466 <author email="person">person</author>
466 <author email="person">person</author>
467 <date>1970-01-16T01:06:40+00:00</date>
467 <date>1970-01-16T01:06:40+00:00</date>
468 <msg xml:space="preserve">no user, no domain</msg>
468 <msg xml:space="preserve">no user, no domain</msg>
469 <paths>
469 <paths>
470 <path action="M">c</path>
470 <path action="M">c</path>
471 </paths>
471 </paths>
472 </logentry>
472 </logentry>
473 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
473 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
474 <author email="other@place">other</author>
474 <author email="other@place">other</author>
475 <date>1970-01-14T21:20:00+00:00</date>
475 <date>1970-01-14T21:20:00+00:00</date>
476 <msg xml:space="preserve">no person</msg>
476 <msg xml:space="preserve">no person</msg>
477 <paths>
477 <paths>
478 <path action="A">c</path>
478 <path action="A">c</path>
479 </paths>
479 </paths>
480 </logentry>
480 </logentry>
481 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
481 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
482 <author email="other@place">A. N. Other</author>
482 <author email="other@place">A. N. Other</author>
483 <date>1970-01-13T17:33:20+00:00</date>
483 <date>1970-01-13T17:33:20+00:00</date>
484 <msg xml:space="preserve">other 1
484 <msg xml:space="preserve">other 1
485 other 2
485 other 2
486
486
487 other 3</msg>
487 other 3</msg>
488 <paths>
488 <paths>
489 <path action="A">b</path>
489 <path action="A">b</path>
490 </paths>
490 </paths>
491 </logentry>
491 </logentry>
492 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
492 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
493 <author email="user@hostname">User Name</author>
493 <author email="user@hostname">User Name</author>
494 <date>1970-01-12T13:46:40+00:00</date>
494 <date>1970-01-12T13:46:40+00:00</date>
495 <msg xml:space="preserve">line 1
495 <msg xml:space="preserve">line 1
496 line 2</msg>
496 line 2</msg>
497 <paths>
497 <paths>
498 <path action="A">a</path>
498 <path action="A">a</path>
499 </paths>
499 </paths>
500 </logentry>
500 </logentry>
501 </log>
501 </log>
502
502
503 $ hg log --debug --style xml
503 $ hg log --debug --style xml
504 <?xml version="1.0"?>
504 <?xml version="1.0"?>
505 <log>
505 <log>
506 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
506 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
507 <tag>tip</tag>
507 <tag>tip</tag>
508 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
508 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
509 <parent revision="-1" node="0000000000000000000000000000000000000000" />
509 <parent revision="-1" node="0000000000000000000000000000000000000000" />
510 <author email="test">test</author>
510 <author email="test">test</author>
511 <date>2020-01-01T10:01:00+00:00</date>
511 <date>2020-01-01T10:01:00+00:00</date>
512 <msg xml:space="preserve">third</msg>
512 <msg xml:space="preserve">third</msg>
513 <paths>
513 <paths>
514 <path action="A">fourth</path>
514 <path action="A">fourth</path>
515 <path action="A">third</path>
515 <path action="A">third</path>
516 <path action="R">second</path>
516 <path action="R">second</path>
517 </paths>
517 </paths>
518 <copies>
518 <copies>
519 <copy source="second">fourth</copy>
519 <copy source="second">fourth</copy>
520 </copies>
520 </copies>
521 <extra key="branch">default</extra>
521 <extra key="branch">default</extra>
522 </logentry>
522 </logentry>
523 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
523 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
524 <parent revision="-1" node="0000000000000000000000000000000000000000" />
524 <parent revision="-1" node="0000000000000000000000000000000000000000" />
525 <parent revision="-1" node="0000000000000000000000000000000000000000" />
525 <parent revision="-1" node="0000000000000000000000000000000000000000" />
526 <author email="user@hostname">User Name</author>
526 <author email="user@hostname">User Name</author>
527 <date>1970-01-12T13:46:40+00:00</date>
527 <date>1970-01-12T13:46:40+00:00</date>
528 <msg xml:space="preserve">second</msg>
528 <msg xml:space="preserve">second</msg>
529 <paths>
529 <paths>
530 <path action="A">second</path>
530 <path action="A">second</path>
531 </paths>
531 </paths>
532 <extra key="branch">default</extra>
532 <extra key="branch">default</extra>
533 </logentry>
533 </logentry>
534 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
534 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
535 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
535 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
536 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
536 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
537 <author email="person">person</author>
537 <author email="person">person</author>
538 <date>1970-01-18T08:40:01+00:00</date>
538 <date>1970-01-18T08:40:01+00:00</date>
539 <msg xml:space="preserve">merge</msg>
539 <msg xml:space="preserve">merge</msg>
540 <paths>
540 <paths>
541 </paths>
541 </paths>
542 <extra key="branch">default</extra>
542 <extra key="branch">default</extra>
543 </logentry>
543 </logentry>
544 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
544 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
545 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
545 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
546 <parent revision="-1" node="0000000000000000000000000000000000000000" />
546 <parent revision="-1" node="0000000000000000000000000000000000000000" />
547 <author email="person">person</author>
547 <author email="person">person</author>
548 <date>1970-01-18T08:40:00+00:00</date>
548 <date>1970-01-18T08:40:00+00:00</date>
549 <msg xml:space="preserve">new head</msg>
549 <msg xml:space="preserve">new head</msg>
550 <paths>
550 <paths>
551 <path action="A">d</path>
551 <path action="A">d</path>
552 </paths>
552 </paths>
553 <extra key="branch">default</extra>
553 <extra key="branch">default</extra>
554 </logentry>
554 </logentry>
555 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
555 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
556 <branch>foo</branch>
556 <branch>foo</branch>
557 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
557 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
558 <parent revision="-1" node="0000000000000000000000000000000000000000" />
558 <parent revision="-1" node="0000000000000000000000000000000000000000" />
559 <author email="person">person</author>
559 <author email="person">person</author>
560 <date>1970-01-17T04:53:20+00:00</date>
560 <date>1970-01-17T04:53:20+00:00</date>
561 <msg xml:space="preserve">new branch</msg>
561 <msg xml:space="preserve">new branch</msg>
562 <paths>
562 <paths>
563 </paths>
563 </paths>
564 <extra key="branch">foo</extra>
564 <extra key="branch">foo</extra>
565 </logentry>
565 </logentry>
566 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
566 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
567 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
567 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
568 <parent revision="-1" node="0000000000000000000000000000000000000000" />
568 <parent revision="-1" node="0000000000000000000000000000000000000000" />
569 <author email="person">person</author>
569 <author email="person">person</author>
570 <date>1970-01-16T01:06:40+00:00</date>
570 <date>1970-01-16T01:06:40+00:00</date>
571 <msg xml:space="preserve">no user, no domain</msg>
571 <msg xml:space="preserve">no user, no domain</msg>
572 <paths>
572 <paths>
573 <path action="M">c</path>
573 <path action="M">c</path>
574 </paths>
574 </paths>
575 <extra key="branch">default</extra>
575 <extra key="branch">default</extra>
576 </logentry>
576 </logentry>
577 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
577 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
578 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
578 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
579 <parent revision="-1" node="0000000000000000000000000000000000000000" />
579 <parent revision="-1" node="0000000000000000000000000000000000000000" />
580 <author email="other@place">other</author>
580 <author email="other@place">other</author>
581 <date>1970-01-14T21:20:00+00:00</date>
581 <date>1970-01-14T21:20:00+00:00</date>
582 <msg xml:space="preserve">no person</msg>
582 <msg xml:space="preserve">no person</msg>
583 <paths>
583 <paths>
584 <path action="A">c</path>
584 <path action="A">c</path>
585 </paths>
585 </paths>
586 <extra key="branch">default</extra>
586 <extra key="branch">default</extra>
587 </logentry>
587 </logentry>
588 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
588 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
589 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
589 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
590 <parent revision="-1" node="0000000000000000000000000000000000000000" />
590 <parent revision="-1" node="0000000000000000000000000000000000000000" />
591 <author email="other@place">A. N. Other</author>
591 <author email="other@place">A. N. Other</author>
592 <date>1970-01-13T17:33:20+00:00</date>
592 <date>1970-01-13T17:33:20+00:00</date>
593 <msg xml:space="preserve">other 1
593 <msg xml:space="preserve">other 1
594 other 2
594 other 2
595
595
596 other 3</msg>
596 other 3</msg>
597 <paths>
597 <paths>
598 <path action="A">b</path>
598 <path action="A">b</path>
599 </paths>
599 </paths>
600 <extra key="branch">default</extra>
600 <extra key="branch">default</extra>
601 </logentry>
601 </logentry>
602 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
602 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
603 <parent revision="-1" node="0000000000000000000000000000000000000000" />
603 <parent revision="-1" node="0000000000000000000000000000000000000000" />
604 <parent revision="-1" node="0000000000000000000000000000000000000000" />
604 <parent revision="-1" node="0000000000000000000000000000000000000000" />
605 <author email="user@hostname">User Name</author>
605 <author email="user@hostname">User Name</author>
606 <date>1970-01-12T13:46:40+00:00</date>
606 <date>1970-01-12T13:46:40+00:00</date>
607 <msg xml:space="preserve">line 1
607 <msg xml:space="preserve">line 1
608 line 2</msg>
608 line 2</msg>
609 <paths>
609 <paths>
610 <path action="A">a</path>
610 <path action="A">a</path>
611 </paths>
611 </paths>
612 <extra key="branch">default</extra>
612 <extra key="branch">default</extra>
613 </logentry>
613 </logentry>
614 </log>
614 </log>
615
615
616
616
617 Test JSON style:
617 Test JSON style:
618
618
619 $ hg log -k nosuch -Tjson
619 $ hg log -k nosuch -Tjson
620 []
620 []
621
621
622 $ hg log -qr . -Tjson
622 $ hg log -qr . -Tjson
623 [
623 [
624 {
624 {
625 "rev": 8,
625 "rev": 8,
626 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
626 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
627 }
627 }
628 ]
628 ]
629
629
630 $ hg log -vpr . -Tjson --stat
630 $ hg log -vpr . -Tjson --stat
631 [
631 [
632 {
632 {
633 "rev": 8,
633 "rev": 8,
634 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
634 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
635 "branch": "default",
635 "branch": "default",
636 "phase": "draft",
636 "phase": "draft",
637 "user": "test",
637 "user": "test",
638 "date": [1577872860, 0],
638 "date": [1577872860, 0],
639 "desc": "third",
639 "desc": "third",
640 "bookmarks": [],
640 "bookmarks": [],
641 "tags": ["tip"],
641 "tags": ["tip"],
642 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
642 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
643 "files": ["fourth", "second", "third"],
643 "files": ["fourth", "second", "third"],
644 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
644 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
645 "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"
645 "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"
646 }
646 }
647 ]
647 ]
648
648
649 honor --git but not format-breaking diffopts
649 honor --git but not format-breaking diffopts
650 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
650 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
651 [
651 [
652 {
652 {
653 "rev": 8,
653 "rev": 8,
654 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
654 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
655 "branch": "default",
655 "branch": "default",
656 "phase": "draft",
656 "phase": "draft",
657 "user": "test",
657 "user": "test",
658 "date": [1577872860, 0],
658 "date": [1577872860, 0],
659 "desc": "third",
659 "desc": "third",
660 "bookmarks": [],
660 "bookmarks": [],
661 "tags": ["tip"],
661 "tags": ["tip"],
662 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
662 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
663 "files": ["fourth", "second", "third"],
663 "files": ["fourth", "second", "third"],
664 "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"
664 "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"
665 }
665 }
666 ]
666 ]
667
667
668 $ hg log -T json
668 $ hg log -T json
669 [
669 [
670 {
670 {
671 "rev": 8,
671 "rev": 8,
672 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
672 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
673 "branch": "default",
673 "branch": "default",
674 "phase": "draft",
674 "phase": "draft",
675 "user": "test",
675 "user": "test",
676 "date": [1577872860, 0],
676 "date": [1577872860, 0],
677 "desc": "third",
677 "desc": "third",
678 "bookmarks": [],
678 "bookmarks": [],
679 "tags": ["tip"],
679 "tags": ["tip"],
680 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
680 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
681 },
681 },
682 {
682 {
683 "rev": 7,
683 "rev": 7,
684 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
684 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
685 "branch": "default",
685 "branch": "default",
686 "phase": "draft",
686 "phase": "draft",
687 "user": "User Name <user@hostname>",
687 "user": "User Name <user@hostname>",
688 "date": [1000000, 0],
688 "date": [1000000, 0],
689 "desc": "second",
689 "desc": "second",
690 "bookmarks": [],
690 "bookmarks": [],
691 "tags": [],
691 "tags": [],
692 "parents": ["0000000000000000000000000000000000000000"]
692 "parents": ["0000000000000000000000000000000000000000"]
693 },
693 },
694 {
694 {
695 "rev": 6,
695 "rev": 6,
696 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
696 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
697 "branch": "default",
697 "branch": "default",
698 "phase": "draft",
698 "phase": "draft",
699 "user": "person",
699 "user": "person",
700 "date": [1500001, 0],
700 "date": [1500001, 0],
701 "desc": "merge",
701 "desc": "merge",
702 "bookmarks": [],
702 "bookmarks": [],
703 "tags": [],
703 "tags": [],
704 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
704 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
705 },
705 },
706 {
706 {
707 "rev": 5,
707 "rev": 5,
708 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
708 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
709 "branch": "default",
709 "branch": "default",
710 "phase": "draft",
710 "phase": "draft",
711 "user": "person",
711 "user": "person",
712 "date": [1500000, 0],
712 "date": [1500000, 0],
713 "desc": "new head",
713 "desc": "new head",
714 "bookmarks": [],
714 "bookmarks": [],
715 "tags": [],
715 "tags": [],
716 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
716 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
717 },
717 },
718 {
718 {
719 "rev": 4,
719 "rev": 4,
720 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
720 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
721 "branch": "foo",
721 "branch": "foo",
722 "phase": "draft",
722 "phase": "draft",
723 "user": "person",
723 "user": "person",
724 "date": [1400000, 0],
724 "date": [1400000, 0],
725 "desc": "new branch",
725 "desc": "new branch",
726 "bookmarks": [],
726 "bookmarks": [],
727 "tags": [],
727 "tags": [],
728 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
728 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
729 },
729 },
730 {
730 {
731 "rev": 3,
731 "rev": 3,
732 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
732 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
733 "branch": "default",
733 "branch": "default",
734 "phase": "draft",
734 "phase": "draft",
735 "user": "person",
735 "user": "person",
736 "date": [1300000, 0],
736 "date": [1300000, 0],
737 "desc": "no user, no domain",
737 "desc": "no user, no domain",
738 "bookmarks": [],
738 "bookmarks": [],
739 "tags": [],
739 "tags": [],
740 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
740 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
741 },
741 },
742 {
742 {
743 "rev": 2,
743 "rev": 2,
744 "node": "97054abb4ab824450e9164180baf491ae0078465",
744 "node": "97054abb4ab824450e9164180baf491ae0078465",
745 "branch": "default",
745 "branch": "default",
746 "phase": "draft",
746 "phase": "draft",
747 "user": "other@place",
747 "user": "other@place",
748 "date": [1200000, 0],
748 "date": [1200000, 0],
749 "desc": "no person",
749 "desc": "no person",
750 "bookmarks": [],
750 "bookmarks": [],
751 "tags": [],
751 "tags": [],
752 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
752 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
753 },
753 },
754 {
754 {
755 "rev": 1,
755 "rev": 1,
756 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
756 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
757 "branch": "default",
757 "branch": "default",
758 "phase": "draft",
758 "phase": "draft",
759 "user": "A. N. Other <other@place>",
759 "user": "A. N. Other <other@place>",
760 "date": [1100000, 0],
760 "date": [1100000, 0],
761 "desc": "other 1\nother 2\n\nother 3",
761 "desc": "other 1\nother 2\n\nother 3",
762 "bookmarks": [],
762 "bookmarks": [],
763 "tags": [],
763 "tags": [],
764 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
764 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
765 },
765 },
766 {
766 {
767 "rev": 0,
767 "rev": 0,
768 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
768 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
769 "branch": "default",
769 "branch": "default",
770 "phase": "draft",
770 "phase": "draft",
771 "user": "User Name <user@hostname>",
771 "user": "User Name <user@hostname>",
772 "date": [1000000, 0],
772 "date": [1000000, 0],
773 "desc": "line 1\nline 2",
773 "desc": "line 1\nline 2",
774 "bookmarks": [],
774 "bookmarks": [],
775 "tags": [],
775 "tags": [],
776 "parents": ["0000000000000000000000000000000000000000"]
776 "parents": ["0000000000000000000000000000000000000000"]
777 }
777 }
778 ]
778 ]
779
779
780 $ hg heads -v -Tjson
780 $ hg heads -v -Tjson
781 [
781 [
782 {
782 {
783 "rev": 8,
783 "rev": 8,
784 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
784 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
785 "branch": "default",
785 "branch": "default",
786 "phase": "draft",
786 "phase": "draft",
787 "user": "test",
787 "user": "test",
788 "date": [1577872860, 0],
788 "date": [1577872860, 0],
789 "desc": "third",
789 "desc": "third",
790 "bookmarks": [],
790 "bookmarks": [],
791 "tags": ["tip"],
791 "tags": ["tip"],
792 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
792 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
793 "files": ["fourth", "second", "third"]
793 "files": ["fourth", "second", "third"]
794 },
794 },
795 {
795 {
796 "rev": 6,
796 "rev": 6,
797 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
797 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
798 "branch": "default",
798 "branch": "default",
799 "phase": "draft",
799 "phase": "draft",
800 "user": "person",
800 "user": "person",
801 "date": [1500001, 0],
801 "date": [1500001, 0],
802 "desc": "merge",
802 "desc": "merge",
803 "bookmarks": [],
803 "bookmarks": [],
804 "tags": [],
804 "tags": [],
805 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
805 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
806 "files": []
806 "files": []
807 },
807 },
808 {
808 {
809 "rev": 4,
809 "rev": 4,
810 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
810 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
811 "branch": "foo",
811 "branch": "foo",
812 "phase": "draft",
812 "phase": "draft",
813 "user": "person",
813 "user": "person",
814 "date": [1400000, 0],
814 "date": [1400000, 0],
815 "desc": "new branch",
815 "desc": "new branch",
816 "bookmarks": [],
816 "bookmarks": [],
817 "tags": [],
817 "tags": [],
818 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
818 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
819 "files": []
819 "files": []
820 }
820 }
821 ]
821 ]
822
822
823 $ hg log --debug -Tjson
823 $ hg log --debug -Tjson
824 [
824 [
825 {
825 {
826 "rev": 8,
826 "rev": 8,
827 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
827 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
828 "branch": "default",
828 "branch": "default",
829 "phase": "draft",
829 "phase": "draft",
830 "user": "test",
830 "user": "test",
831 "date": [1577872860, 0],
831 "date": [1577872860, 0],
832 "desc": "third",
832 "desc": "third",
833 "bookmarks": [],
833 "bookmarks": [],
834 "tags": ["tip"],
834 "tags": ["tip"],
835 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
835 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
836 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
836 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
837 "extra": {"branch": "default"},
837 "extra": {"branch": "default"},
838 "modified": [],
838 "modified": [],
839 "added": ["fourth", "third"],
839 "added": ["fourth", "third"],
840 "removed": ["second"]
840 "removed": ["second"]
841 },
841 },
842 {
842 {
843 "rev": 7,
843 "rev": 7,
844 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
844 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
845 "branch": "default",
845 "branch": "default",
846 "phase": "draft",
846 "phase": "draft",
847 "user": "User Name <user@hostname>",
847 "user": "User Name <user@hostname>",
848 "date": [1000000, 0],
848 "date": [1000000, 0],
849 "desc": "second",
849 "desc": "second",
850 "bookmarks": [],
850 "bookmarks": [],
851 "tags": [],
851 "tags": [],
852 "parents": ["0000000000000000000000000000000000000000"],
852 "parents": ["0000000000000000000000000000000000000000"],
853 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
853 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
854 "extra": {"branch": "default"},
854 "extra": {"branch": "default"},
855 "modified": [],
855 "modified": [],
856 "added": ["second"],
856 "added": ["second"],
857 "removed": []
857 "removed": []
858 },
858 },
859 {
859 {
860 "rev": 6,
860 "rev": 6,
861 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
861 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
862 "branch": "default",
862 "branch": "default",
863 "phase": "draft",
863 "phase": "draft",
864 "user": "person",
864 "user": "person",
865 "date": [1500001, 0],
865 "date": [1500001, 0],
866 "desc": "merge",
866 "desc": "merge",
867 "bookmarks": [],
867 "bookmarks": [],
868 "tags": [],
868 "tags": [],
869 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
869 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
870 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
870 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
871 "extra": {"branch": "default"},
871 "extra": {"branch": "default"},
872 "modified": [],
872 "modified": [],
873 "added": [],
873 "added": [],
874 "removed": []
874 "removed": []
875 },
875 },
876 {
876 {
877 "rev": 5,
877 "rev": 5,
878 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
878 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
879 "branch": "default",
879 "branch": "default",
880 "phase": "draft",
880 "phase": "draft",
881 "user": "person",
881 "user": "person",
882 "date": [1500000, 0],
882 "date": [1500000, 0],
883 "desc": "new head",
883 "desc": "new head",
884 "bookmarks": [],
884 "bookmarks": [],
885 "tags": [],
885 "tags": [],
886 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
886 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
887 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
887 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
888 "extra": {"branch": "default"},
888 "extra": {"branch": "default"},
889 "modified": [],
889 "modified": [],
890 "added": ["d"],
890 "added": ["d"],
891 "removed": []
891 "removed": []
892 },
892 },
893 {
893 {
894 "rev": 4,
894 "rev": 4,
895 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
895 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
896 "branch": "foo",
896 "branch": "foo",
897 "phase": "draft",
897 "phase": "draft",
898 "user": "person",
898 "user": "person",
899 "date": [1400000, 0],
899 "date": [1400000, 0],
900 "desc": "new branch",
900 "desc": "new branch",
901 "bookmarks": [],
901 "bookmarks": [],
902 "tags": [],
902 "tags": [],
903 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
903 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
904 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
904 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
905 "extra": {"branch": "foo"},
905 "extra": {"branch": "foo"},
906 "modified": [],
906 "modified": [],
907 "added": [],
907 "added": [],
908 "removed": []
908 "removed": []
909 },
909 },
910 {
910 {
911 "rev": 3,
911 "rev": 3,
912 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
912 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
913 "branch": "default",
913 "branch": "default",
914 "phase": "draft",
914 "phase": "draft",
915 "user": "person",
915 "user": "person",
916 "date": [1300000, 0],
916 "date": [1300000, 0],
917 "desc": "no user, no domain",
917 "desc": "no user, no domain",
918 "bookmarks": [],
918 "bookmarks": [],
919 "tags": [],
919 "tags": [],
920 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
920 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
921 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
921 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
922 "extra": {"branch": "default"},
922 "extra": {"branch": "default"},
923 "modified": ["c"],
923 "modified": ["c"],
924 "added": [],
924 "added": [],
925 "removed": []
925 "removed": []
926 },
926 },
927 {
927 {
928 "rev": 2,
928 "rev": 2,
929 "node": "97054abb4ab824450e9164180baf491ae0078465",
929 "node": "97054abb4ab824450e9164180baf491ae0078465",
930 "branch": "default",
930 "branch": "default",
931 "phase": "draft",
931 "phase": "draft",
932 "user": "other@place",
932 "user": "other@place",
933 "date": [1200000, 0],
933 "date": [1200000, 0],
934 "desc": "no person",
934 "desc": "no person",
935 "bookmarks": [],
935 "bookmarks": [],
936 "tags": [],
936 "tags": [],
937 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
937 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
938 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
938 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
939 "extra": {"branch": "default"},
939 "extra": {"branch": "default"},
940 "modified": [],
940 "modified": [],
941 "added": ["c"],
941 "added": ["c"],
942 "removed": []
942 "removed": []
943 },
943 },
944 {
944 {
945 "rev": 1,
945 "rev": 1,
946 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
946 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
947 "branch": "default",
947 "branch": "default",
948 "phase": "draft",
948 "phase": "draft",
949 "user": "A. N. Other <other@place>",
949 "user": "A. N. Other <other@place>",
950 "date": [1100000, 0],
950 "date": [1100000, 0],
951 "desc": "other 1\nother 2\n\nother 3",
951 "desc": "other 1\nother 2\n\nother 3",
952 "bookmarks": [],
952 "bookmarks": [],
953 "tags": [],
953 "tags": [],
954 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
954 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
955 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
955 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
956 "extra": {"branch": "default"},
956 "extra": {"branch": "default"},
957 "modified": [],
957 "modified": [],
958 "added": ["b"],
958 "added": ["b"],
959 "removed": []
959 "removed": []
960 },
960 },
961 {
961 {
962 "rev": 0,
962 "rev": 0,
963 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
963 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
964 "branch": "default",
964 "branch": "default",
965 "phase": "draft",
965 "phase": "draft",
966 "user": "User Name <user@hostname>",
966 "user": "User Name <user@hostname>",
967 "date": [1000000, 0],
967 "date": [1000000, 0],
968 "desc": "line 1\nline 2",
968 "desc": "line 1\nline 2",
969 "bookmarks": [],
969 "bookmarks": [],
970 "tags": [],
970 "tags": [],
971 "parents": ["0000000000000000000000000000000000000000"],
971 "parents": ["0000000000000000000000000000000000000000"],
972 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
972 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
973 "extra": {"branch": "default"},
973 "extra": {"branch": "default"},
974 "modified": [],
974 "modified": [],
975 "added": ["a"],
975 "added": ["a"],
976 "removed": []
976 "removed": []
977 }
977 }
978 ]
978 ]
979
979
980 Error if style not readable:
980 Error if style not readable:
981
981
982 #if unix-permissions no-root
982 #if unix-permissions no-root
983 $ touch q
983 $ touch q
984 $ chmod 0 q
984 $ chmod 0 q
985 $ hg log --style ./q
985 $ hg log --style ./q
986 abort: Permission denied: ./q
986 abort: Permission denied: ./q
987 [255]
987 [255]
988 #endif
988 #endif
989
989
990 Error if no style:
990 Error if no style:
991
991
992 $ hg log --style notexist
992 $ hg log --style notexist
993 abort: style 'notexist' not found
993 abort: style 'notexist' not found
994 (available styles: bisect, changelog, compact, default, phases, status, xml)
994 (available styles: bisect, changelog, compact, default, phases, status, xml)
995 [255]
995 [255]
996
996
997 $ hg log -T list
997 $ hg log -T list
998 available styles: bisect, changelog, compact, default, phases, status, xml
998 available styles: bisect, changelog, compact, default, phases, status, xml
999 abort: specify a template
999 abort: specify a template
1000 [255]
1000 [255]
1001
1001
1002 Error if style missing key:
1002 Error if style missing key:
1003
1003
1004 $ echo 'q = q' > t
1004 $ echo 'q = q' > t
1005 $ hg log --style ./t
1005 $ hg log --style ./t
1006 abort: "changeset" not in template map
1006 abort: "changeset" not in template map
1007 [255]
1007 [255]
1008
1008
1009 Error if style missing value:
1009 Error if style missing value:
1010
1010
1011 $ echo 'changeset =' > t
1011 $ echo 'changeset =' > t
1012 $ hg log --style t
1012 $ hg log --style t
1013 hg: parse error at t:1: missing value
1013 hg: parse error at t:1: missing value
1014 [255]
1014 [255]
1015
1015
1016 Error if include fails:
1016 Error if include fails:
1017
1017
1018 $ echo 'changeset = q' >> t
1018 $ echo 'changeset = q' >> t
1019 #if unix-permissions no-root
1019 #if unix-permissions no-root
1020 $ hg log --style ./t
1020 $ hg log --style ./t
1021 abort: template file ./q: Permission denied
1021 abort: template file ./q: Permission denied
1022 [255]
1022 [255]
1023 $ rm -f q
1023 $ rm -f q
1024 #endif
1024 #endif
1025
1025
1026 Include works:
1026 Include works:
1027
1027
1028 $ echo '{rev}' > q
1028 $ echo '{rev}' > q
1029 $ hg log --style ./t
1029 $ hg log --style ./t
1030 8
1030 8
1031 7
1031 7
1032 6
1032 6
1033 5
1033 5
1034 4
1034 4
1035 3
1035 3
1036 2
1036 2
1037 1
1037 1
1038 0
1038 0
1039
1039
1040 Check that recursive reference does not fall into RuntimeError (issue4758):
1040 Check that recursive reference does not fall into RuntimeError (issue4758):
1041
1041
1042 common mistake:
1042 common mistake:
1043
1043
1044 $ hg log -T '{changeset}\n'
1044 $ hg log -T '{changeset}\n'
1045 abort: recursive reference 'changeset' in template
1045 abort: recursive reference 'changeset' in template
1046 [255]
1046 [255]
1047
1047
1048 circular reference:
1048 circular reference:
1049
1049
1050 $ cat << EOF > issue4758
1050 $ cat << EOF > issue4758
1051 > changeset = '{foo}'
1051 > changeset = '{foo}'
1052 > foo = '{changeset}'
1052 > foo = '{changeset}'
1053 > EOF
1053 > EOF
1054 $ hg log --style ./issue4758
1054 $ hg log --style ./issue4758
1055 abort: recursive reference 'foo' in template
1055 abort: recursive reference 'foo' in template
1056 [255]
1056 [255]
1057
1057
1058 buildmap() -> gettemplate(), where no thunk was made:
1058 buildmap() -> gettemplate(), where no thunk was made:
1059
1059
1060 $ hg log -T '{files % changeset}\n'
1060 $ hg log -T '{files % changeset}\n'
1061 abort: recursive reference 'changeset' in template
1061 abort: recursive reference 'changeset' in template
1062 [255]
1062 [255]
1063
1063
1064 not a recursion if a keyword of the same name exists:
1064 not a recursion if a keyword of the same name exists:
1065
1065
1066 $ cat << EOF > issue4758
1066 $ cat << EOF > issue4758
1067 > changeset = '{tags % rev}'
1067 > changeset = '{tags % rev}'
1068 > rev = '{rev} {tag}\n'
1068 > rev = '{rev} {tag}\n'
1069 > EOF
1069 > EOF
1070 $ hg log --style ./issue4758 -r tip
1070 $ hg log --style ./issue4758 -r tip
1071 8 tip
1071 8 tip
1072
1072
1073 Check that {phase} works correctly on parents:
1073 Check that {phase} works correctly on parents:
1074
1074
1075 $ cat << EOF > parentphase
1075 $ cat << EOF > parentphase
1076 > changeset_debug = '{rev} ({phase}):{parents}\n'
1076 > changeset_debug = '{rev} ({phase}):{parents}\n'
1077 > parent = ' {rev} ({phase})'
1077 > parent = ' {rev} ({phase})'
1078 > EOF
1078 > EOF
1079 $ hg phase -r 5 --public
1079 $ hg phase -r 5 --public
1080 $ hg phase -r 7 --secret --force
1080 $ hg phase -r 7 --secret --force
1081 $ hg log --debug -G --style ./parentphase
1081 $ hg log --debug -G --style ./parentphase
1082 @ 8 (secret): 7 (secret) -1 (public)
1082 @ 8 (secret): 7 (secret) -1 (public)
1083 |
1083 |
1084 o 7 (secret): -1 (public) -1 (public)
1084 o 7 (secret): -1 (public) -1 (public)
1085
1085
1086 o 6 (draft): 5 (public) 4 (draft)
1086 o 6 (draft): 5 (public) 4 (draft)
1087 |\
1087 |\
1088 | o 5 (public): 3 (public) -1 (public)
1088 | o 5 (public): 3 (public) -1 (public)
1089 | |
1089 | |
1090 o | 4 (draft): 3 (public) -1 (public)
1090 o | 4 (draft): 3 (public) -1 (public)
1091 |/
1091 |/
1092 o 3 (public): 2 (public) -1 (public)
1092 o 3 (public): 2 (public) -1 (public)
1093 |
1093 |
1094 o 2 (public): 1 (public) -1 (public)
1094 o 2 (public): 1 (public) -1 (public)
1095 |
1095 |
1096 o 1 (public): 0 (public) -1 (public)
1096 o 1 (public): 0 (public) -1 (public)
1097 |
1097 |
1098 o 0 (public): -1 (public) -1 (public)
1098 o 0 (public): -1 (public) -1 (public)
1099
1099
1100
1100
1101 Missing non-standard names give no error (backward compatibility):
1101 Missing non-standard names give no error (backward compatibility):
1102
1102
1103 $ echo "changeset = '{c}'" > t
1103 $ echo "changeset = '{c}'" > t
1104 $ hg log --style ./t
1104 $ hg log --style ./t
1105
1105
1106 Defining non-standard name works:
1106 Defining non-standard name works:
1107
1107
1108 $ cat <<EOF > t
1108 $ cat <<EOF > t
1109 > changeset = '{c}'
1109 > changeset = '{c}'
1110 > c = q
1110 > c = q
1111 > EOF
1111 > EOF
1112 $ hg log --style ./t
1112 $ hg log --style ./t
1113 8
1113 8
1114 7
1114 7
1115 6
1115 6
1116 5
1116 5
1117 4
1117 4
1118 3
1118 3
1119 2
1119 2
1120 1
1120 1
1121 0
1121 0
1122
1122
1123 ui.style works:
1123 ui.style works:
1124
1124
1125 $ echo '[ui]' > .hg/hgrc
1125 $ echo '[ui]' > .hg/hgrc
1126 $ echo 'style = t' >> .hg/hgrc
1126 $ echo 'style = t' >> .hg/hgrc
1127 $ hg log
1127 $ hg log
1128 8
1128 8
1129 7
1129 7
1130 6
1130 6
1131 5
1131 5
1132 4
1132 4
1133 3
1133 3
1134 2
1134 2
1135 1
1135 1
1136 0
1136 0
1137
1137
1138
1138
1139 Issue338:
1139 Issue338:
1140
1140
1141 $ hg log --style=changelog > changelog
1141 $ hg log --style=changelog > changelog
1142
1142
1143 $ cat changelog
1143 $ cat changelog
1144 2020-01-01 test <test>
1144 2020-01-01 test <test>
1145
1145
1146 * fourth, second, third:
1146 * fourth, second, third:
1147 third
1147 third
1148 [95c24699272e] [tip]
1148 [95c24699272e] [tip]
1149
1149
1150 1970-01-12 User Name <user@hostname>
1150 1970-01-12 User Name <user@hostname>
1151
1151
1152 * second:
1152 * second:
1153 second
1153 second
1154 [29114dbae42b]
1154 [29114dbae42b]
1155
1155
1156 1970-01-18 person <person>
1156 1970-01-18 person <person>
1157
1157
1158 * merge
1158 * merge
1159 [d41e714fe50d]
1159 [d41e714fe50d]
1160
1160
1161 * d:
1161 * d:
1162 new head
1162 new head
1163 [13207e5a10d9]
1163 [13207e5a10d9]
1164
1164
1165 1970-01-17 person <person>
1165 1970-01-17 person <person>
1166
1166
1167 * new branch
1167 * new branch
1168 [bbe44766e73d] <foo>
1168 [bbe44766e73d] <foo>
1169
1169
1170 1970-01-16 person <person>
1170 1970-01-16 person <person>
1171
1171
1172 * c:
1172 * c:
1173 no user, no domain
1173 no user, no domain
1174 [10e46f2dcbf4]
1174 [10e46f2dcbf4]
1175
1175
1176 1970-01-14 other <other@place>
1176 1970-01-14 other <other@place>
1177
1177
1178 * c:
1178 * c:
1179 no person
1179 no person
1180 [97054abb4ab8]
1180 [97054abb4ab8]
1181
1181
1182 1970-01-13 A. N. Other <other@place>
1182 1970-01-13 A. N. Other <other@place>
1183
1183
1184 * b:
1184 * b:
1185 other 1 other 2
1185 other 1 other 2
1186
1186
1187 other 3
1187 other 3
1188 [b608e9d1a3f0]
1188 [b608e9d1a3f0]
1189
1189
1190 1970-01-12 User Name <user@hostname>
1190 1970-01-12 User Name <user@hostname>
1191
1191
1192 * a:
1192 * a:
1193 line 1 line 2
1193 line 1 line 2
1194 [1e4e1b8f71e0]
1194 [1e4e1b8f71e0]
1195
1195
1196
1196
1197 Issue2130: xml output for 'hg heads' is malformed
1197 Issue2130: xml output for 'hg heads' is malformed
1198
1198
1199 $ hg heads --style changelog
1199 $ hg heads --style changelog
1200 2020-01-01 test <test>
1200 2020-01-01 test <test>
1201
1201
1202 * fourth, second, third:
1202 * fourth, second, third:
1203 third
1203 third
1204 [95c24699272e] [tip]
1204 [95c24699272e] [tip]
1205
1205
1206 1970-01-18 person <person>
1206 1970-01-18 person <person>
1207
1207
1208 * merge
1208 * merge
1209 [d41e714fe50d]
1209 [d41e714fe50d]
1210
1210
1211 1970-01-17 person <person>
1211 1970-01-17 person <person>
1212
1212
1213 * new branch
1213 * new branch
1214 [bbe44766e73d] <foo>
1214 [bbe44766e73d] <foo>
1215
1215
1216
1216
1217 Keys work:
1217 Keys work:
1218
1218
1219 $ for key in author branch branches date desc file_adds file_dels file_mods \
1219 $ for key in author branch branches date desc file_adds file_dels file_mods \
1220 > file_copies file_copies_switch files \
1220 > file_copies file_copies_switch files \
1221 > manifest node parents rev tags diffstat extras \
1221 > manifest node parents rev tags diffstat extras \
1222 > p1rev p2rev p1node p2node; do
1222 > p1rev p2rev p1node p2node; do
1223 > for mode in '' --verbose --debug; do
1223 > for mode in '' --verbose --debug; do
1224 > hg log $mode --template "$key$mode: {$key}\n"
1224 > hg log $mode --template "$key$mode: {$key}\n"
1225 > done
1225 > done
1226 > done
1226 > done
1227 author: test
1227 author: test
1228 author: User Name <user@hostname>
1228 author: User Name <user@hostname>
1229 author: person
1229 author: person
1230 author: person
1230 author: person
1231 author: person
1231 author: person
1232 author: person
1232 author: person
1233 author: other@place
1233 author: other@place
1234 author: A. N. Other <other@place>
1234 author: A. N. Other <other@place>
1235 author: User Name <user@hostname>
1235 author: User Name <user@hostname>
1236 author--verbose: test
1236 author--verbose: test
1237 author--verbose: User Name <user@hostname>
1237 author--verbose: User Name <user@hostname>
1238 author--verbose: person
1238 author--verbose: person
1239 author--verbose: person
1239 author--verbose: person
1240 author--verbose: person
1240 author--verbose: person
1241 author--verbose: person
1241 author--verbose: person
1242 author--verbose: other@place
1242 author--verbose: other@place
1243 author--verbose: A. N. Other <other@place>
1243 author--verbose: A. N. Other <other@place>
1244 author--verbose: User Name <user@hostname>
1244 author--verbose: User Name <user@hostname>
1245 author--debug: test
1245 author--debug: test
1246 author--debug: User Name <user@hostname>
1246 author--debug: User Name <user@hostname>
1247 author--debug: person
1247 author--debug: person
1248 author--debug: person
1248 author--debug: person
1249 author--debug: person
1249 author--debug: person
1250 author--debug: person
1250 author--debug: person
1251 author--debug: other@place
1251 author--debug: other@place
1252 author--debug: A. N. Other <other@place>
1252 author--debug: A. N. Other <other@place>
1253 author--debug: User Name <user@hostname>
1253 author--debug: User Name <user@hostname>
1254 branch: default
1254 branch: default
1255 branch: default
1255 branch: default
1256 branch: default
1256 branch: default
1257 branch: default
1257 branch: default
1258 branch: foo
1258 branch: foo
1259 branch: default
1259 branch: default
1260 branch: default
1260 branch: default
1261 branch: default
1261 branch: default
1262 branch: default
1262 branch: default
1263 branch--verbose: default
1263 branch--verbose: default
1264 branch--verbose: default
1264 branch--verbose: default
1265 branch--verbose: default
1265 branch--verbose: default
1266 branch--verbose: default
1266 branch--verbose: default
1267 branch--verbose: foo
1267 branch--verbose: foo
1268 branch--verbose: default
1268 branch--verbose: default
1269 branch--verbose: default
1269 branch--verbose: default
1270 branch--verbose: default
1270 branch--verbose: default
1271 branch--verbose: default
1271 branch--verbose: default
1272 branch--debug: default
1272 branch--debug: default
1273 branch--debug: default
1273 branch--debug: default
1274 branch--debug: default
1274 branch--debug: default
1275 branch--debug: default
1275 branch--debug: default
1276 branch--debug: foo
1276 branch--debug: foo
1277 branch--debug: default
1277 branch--debug: default
1278 branch--debug: default
1278 branch--debug: default
1279 branch--debug: default
1279 branch--debug: default
1280 branch--debug: default
1280 branch--debug: default
1281 branches:
1281 branches:
1282 branches:
1282 branches:
1283 branches:
1283 branches:
1284 branches:
1284 branches:
1285 branches: foo
1285 branches: foo
1286 branches:
1286 branches:
1287 branches:
1287 branches:
1288 branches:
1288 branches:
1289 branches:
1289 branches:
1290 branches--verbose:
1290 branches--verbose:
1291 branches--verbose:
1291 branches--verbose:
1292 branches--verbose:
1292 branches--verbose:
1293 branches--verbose:
1293 branches--verbose:
1294 branches--verbose: foo
1294 branches--verbose: foo
1295 branches--verbose:
1295 branches--verbose:
1296 branches--verbose:
1296 branches--verbose:
1297 branches--verbose:
1297 branches--verbose:
1298 branches--verbose:
1298 branches--verbose:
1299 branches--debug:
1299 branches--debug:
1300 branches--debug:
1300 branches--debug:
1301 branches--debug:
1301 branches--debug:
1302 branches--debug:
1302 branches--debug:
1303 branches--debug: foo
1303 branches--debug: foo
1304 branches--debug:
1304 branches--debug:
1305 branches--debug:
1305 branches--debug:
1306 branches--debug:
1306 branches--debug:
1307 branches--debug:
1307 branches--debug:
1308 date: 1577872860.00
1308 date: 1577872860.00
1309 date: 1000000.00
1309 date: 1000000.00
1310 date: 1500001.00
1310 date: 1500001.00
1311 date: 1500000.00
1311 date: 1500000.00
1312 date: 1400000.00
1312 date: 1400000.00
1313 date: 1300000.00
1313 date: 1300000.00
1314 date: 1200000.00
1314 date: 1200000.00
1315 date: 1100000.00
1315 date: 1100000.00
1316 date: 1000000.00
1316 date: 1000000.00
1317 date--verbose: 1577872860.00
1317 date--verbose: 1577872860.00
1318 date--verbose: 1000000.00
1318 date--verbose: 1000000.00
1319 date--verbose: 1500001.00
1319 date--verbose: 1500001.00
1320 date--verbose: 1500000.00
1320 date--verbose: 1500000.00
1321 date--verbose: 1400000.00
1321 date--verbose: 1400000.00
1322 date--verbose: 1300000.00
1322 date--verbose: 1300000.00
1323 date--verbose: 1200000.00
1323 date--verbose: 1200000.00
1324 date--verbose: 1100000.00
1324 date--verbose: 1100000.00
1325 date--verbose: 1000000.00
1325 date--verbose: 1000000.00
1326 date--debug: 1577872860.00
1326 date--debug: 1577872860.00
1327 date--debug: 1000000.00
1327 date--debug: 1000000.00
1328 date--debug: 1500001.00
1328 date--debug: 1500001.00
1329 date--debug: 1500000.00
1329 date--debug: 1500000.00
1330 date--debug: 1400000.00
1330 date--debug: 1400000.00
1331 date--debug: 1300000.00
1331 date--debug: 1300000.00
1332 date--debug: 1200000.00
1332 date--debug: 1200000.00
1333 date--debug: 1100000.00
1333 date--debug: 1100000.00
1334 date--debug: 1000000.00
1334 date--debug: 1000000.00
1335 desc: third
1335 desc: third
1336 desc: second
1336 desc: second
1337 desc: merge
1337 desc: merge
1338 desc: new head
1338 desc: new head
1339 desc: new branch
1339 desc: new branch
1340 desc: no user, no domain
1340 desc: no user, no domain
1341 desc: no person
1341 desc: no person
1342 desc: other 1
1342 desc: other 1
1343 other 2
1343 other 2
1344
1344
1345 other 3
1345 other 3
1346 desc: line 1
1346 desc: line 1
1347 line 2
1347 line 2
1348 desc--verbose: third
1348 desc--verbose: third
1349 desc--verbose: second
1349 desc--verbose: second
1350 desc--verbose: merge
1350 desc--verbose: merge
1351 desc--verbose: new head
1351 desc--verbose: new head
1352 desc--verbose: new branch
1352 desc--verbose: new branch
1353 desc--verbose: no user, no domain
1353 desc--verbose: no user, no domain
1354 desc--verbose: no person
1354 desc--verbose: no person
1355 desc--verbose: other 1
1355 desc--verbose: other 1
1356 other 2
1356 other 2
1357
1357
1358 other 3
1358 other 3
1359 desc--verbose: line 1
1359 desc--verbose: line 1
1360 line 2
1360 line 2
1361 desc--debug: third
1361 desc--debug: third
1362 desc--debug: second
1362 desc--debug: second
1363 desc--debug: merge
1363 desc--debug: merge
1364 desc--debug: new head
1364 desc--debug: new head
1365 desc--debug: new branch
1365 desc--debug: new branch
1366 desc--debug: no user, no domain
1366 desc--debug: no user, no domain
1367 desc--debug: no person
1367 desc--debug: no person
1368 desc--debug: other 1
1368 desc--debug: other 1
1369 other 2
1369 other 2
1370
1370
1371 other 3
1371 other 3
1372 desc--debug: line 1
1372 desc--debug: line 1
1373 line 2
1373 line 2
1374 file_adds: fourth third
1374 file_adds: fourth third
1375 file_adds: second
1375 file_adds: second
1376 file_adds:
1376 file_adds:
1377 file_adds: d
1377 file_adds: d
1378 file_adds:
1378 file_adds:
1379 file_adds:
1379 file_adds:
1380 file_adds: c
1380 file_adds: c
1381 file_adds: b
1381 file_adds: b
1382 file_adds: a
1382 file_adds: a
1383 file_adds--verbose: fourth third
1383 file_adds--verbose: fourth third
1384 file_adds--verbose: second
1384 file_adds--verbose: second
1385 file_adds--verbose:
1385 file_adds--verbose:
1386 file_adds--verbose: d
1386 file_adds--verbose: d
1387 file_adds--verbose:
1387 file_adds--verbose:
1388 file_adds--verbose:
1388 file_adds--verbose:
1389 file_adds--verbose: c
1389 file_adds--verbose: c
1390 file_adds--verbose: b
1390 file_adds--verbose: b
1391 file_adds--verbose: a
1391 file_adds--verbose: a
1392 file_adds--debug: fourth third
1392 file_adds--debug: fourth third
1393 file_adds--debug: second
1393 file_adds--debug: second
1394 file_adds--debug:
1394 file_adds--debug:
1395 file_adds--debug: d
1395 file_adds--debug: d
1396 file_adds--debug:
1396 file_adds--debug:
1397 file_adds--debug:
1397 file_adds--debug:
1398 file_adds--debug: c
1398 file_adds--debug: c
1399 file_adds--debug: b
1399 file_adds--debug: b
1400 file_adds--debug: a
1400 file_adds--debug: a
1401 file_dels: second
1401 file_dels: second
1402 file_dels:
1402 file_dels:
1403 file_dels:
1403 file_dels:
1404 file_dels:
1404 file_dels:
1405 file_dels:
1405 file_dels:
1406 file_dels:
1406 file_dels:
1407 file_dels:
1407 file_dels:
1408 file_dels:
1408 file_dels:
1409 file_dels:
1409 file_dels:
1410 file_dels--verbose: second
1410 file_dels--verbose: second
1411 file_dels--verbose:
1411 file_dels--verbose:
1412 file_dels--verbose:
1412 file_dels--verbose:
1413 file_dels--verbose:
1413 file_dels--verbose:
1414 file_dels--verbose:
1414 file_dels--verbose:
1415 file_dels--verbose:
1415 file_dels--verbose:
1416 file_dels--verbose:
1416 file_dels--verbose:
1417 file_dels--verbose:
1417 file_dels--verbose:
1418 file_dels--verbose:
1418 file_dels--verbose:
1419 file_dels--debug: second
1419 file_dels--debug: second
1420 file_dels--debug:
1420 file_dels--debug:
1421 file_dels--debug:
1421 file_dels--debug:
1422 file_dels--debug:
1422 file_dels--debug:
1423 file_dels--debug:
1423 file_dels--debug:
1424 file_dels--debug:
1424 file_dels--debug:
1425 file_dels--debug:
1425 file_dels--debug:
1426 file_dels--debug:
1426 file_dels--debug:
1427 file_dels--debug:
1427 file_dels--debug:
1428 file_mods:
1428 file_mods:
1429 file_mods:
1429 file_mods:
1430 file_mods:
1430 file_mods:
1431 file_mods:
1431 file_mods:
1432 file_mods:
1432 file_mods:
1433 file_mods: c
1433 file_mods: c
1434 file_mods:
1434 file_mods:
1435 file_mods:
1435 file_mods:
1436 file_mods:
1436 file_mods:
1437 file_mods--verbose:
1437 file_mods--verbose:
1438 file_mods--verbose:
1438 file_mods--verbose:
1439 file_mods--verbose:
1439 file_mods--verbose:
1440 file_mods--verbose:
1440 file_mods--verbose:
1441 file_mods--verbose:
1441 file_mods--verbose:
1442 file_mods--verbose: c
1442 file_mods--verbose: c
1443 file_mods--verbose:
1443 file_mods--verbose:
1444 file_mods--verbose:
1444 file_mods--verbose:
1445 file_mods--verbose:
1445 file_mods--verbose:
1446 file_mods--debug:
1446 file_mods--debug:
1447 file_mods--debug:
1447 file_mods--debug:
1448 file_mods--debug:
1448 file_mods--debug:
1449 file_mods--debug:
1449 file_mods--debug:
1450 file_mods--debug:
1450 file_mods--debug:
1451 file_mods--debug: c
1451 file_mods--debug: c
1452 file_mods--debug:
1452 file_mods--debug:
1453 file_mods--debug:
1453 file_mods--debug:
1454 file_mods--debug:
1454 file_mods--debug:
1455 file_copies: fourth (second)
1455 file_copies: fourth (second)
1456 file_copies:
1456 file_copies:
1457 file_copies:
1457 file_copies:
1458 file_copies:
1458 file_copies:
1459 file_copies:
1459 file_copies:
1460 file_copies:
1460 file_copies:
1461 file_copies:
1461 file_copies:
1462 file_copies:
1462 file_copies:
1463 file_copies:
1463 file_copies:
1464 file_copies--verbose: fourth (second)
1464 file_copies--verbose: fourth (second)
1465 file_copies--verbose:
1465 file_copies--verbose:
1466 file_copies--verbose:
1466 file_copies--verbose:
1467 file_copies--verbose:
1467 file_copies--verbose:
1468 file_copies--verbose:
1468 file_copies--verbose:
1469 file_copies--verbose:
1469 file_copies--verbose:
1470 file_copies--verbose:
1470 file_copies--verbose:
1471 file_copies--verbose:
1471 file_copies--verbose:
1472 file_copies--verbose:
1472 file_copies--verbose:
1473 file_copies--debug: fourth (second)
1473 file_copies--debug: fourth (second)
1474 file_copies--debug:
1474 file_copies--debug:
1475 file_copies--debug:
1475 file_copies--debug:
1476 file_copies--debug:
1476 file_copies--debug:
1477 file_copies--debug:
1477 file_copies--debug:
1478 file_copies--debug:
1478 file_copies--debug:
1479 file_copies--debug:
1479 file_copies--debug:
1480 file_copies--debug:
1480 file_copies--debug:
1481 file_copies--debug:
1481 file_copies--debug:
1482 file_copies_switch:
1482 file_copies_switch:
1483 file_copies_switch:
1483 file_copies_switch:
1484 file_copies_switch:
1484 file_copies_switch:
1485 file_copies_switch:
1485 file_copies_switch:
1486 file_copies_switch:
1486 file_copies_switch:
1487 file_copies_switch:
1487 file_copies_switch:
1488 file_copies_switch:
1488 file_copies_switch:
1489 file_copies_switch:
1489 file_copies_switch:
1490 file_copies_switch:
1490 file_copies_switch:
1491 file_copies_switch--verbose:
1491 file_copies_switch--verbose:
1492 file_copies_switch--verbose:
1492 file_copies_switch--verbose:
1493 file_copies_switch--verbose:
1493 file_copies_switch--verbose:
1494 file_copies_switch--verbose:
1494 file_copies_switch--verbose:
1495 file_copies_switch--verbose:
1495 file_copies_switch--verbose:
1496 file_copies_switch--verbose:
1496 file_copies_switch--verbose:
1497 file_copies_switch--verbose:
1497 file_copies_switch--verbose:
1498 file_copies_switch--verbose:
1498 file_copies_switch--verbose:
1499 file_copies_switch--verbose:
1499 file_copies_switch--verbose:
1500 file_copies_switch--debug:
1500 file_copies_switch--debug:
1501 file_copies_switch--debug:
1501 file_copies_switch--debug:
1502 file_copies_switch--debug:
1502 file_copies_switch--debug:
1503 file_copies_switch--debug:
1503 file_copies_switch--debug:
1504 file_copies_switch--debug:
1504 file_copies_switch--debug:
1505 file_copies_switch--debug:
1505 file_copies_switch--debug:
1506 file_copies_switch--debug:
1506 file_copies_switch--debug:
1507 file_copies_switch--debug:
1507 file_copies_switch--debug:
1508 file_copies_switch--debug:
1508 file_copies_switch--debug:
1509 files: fourth second third
1509 files: fourth second third
1510 files: second
1510 files: second
1511 files:
1511 files:
1512 files: d
1512 files: d
1513 files:
1513 files:
1514 files: c
1514 files: c
1515 files: c
1515 files: c
1516 files: b
1516 files: b
1517 files: a
1517 files: a
1518 files--verbose: fourth second third
1518 files--verbose: fourth second third
1519 files--verbose: second
1519 files--verbose: second
1520 files--verbose:
1520 files--verbose:
1521 files--verbose: d
1521 files--verbose: d
1522 files--verbose:
1522 files--verbose:
1523 files--verbose: c
1523 files--verbose: c
1524 files--verbose: c
1524 files--verbose: c
1525 files--verbose: b
1525 files--verbose: b
1526 files--verbose: a
1526 files--verbose: a
1527 files--debug: fourth second third
1527 files--debug: fourth second third
1528 files--debug: second
1528 files--debug: second
1529 files--debug:
1529 files--debug:
1530 files--debug: d
1530 files--debug: d
1531 files--debug:
1531 files--debug:
1532 files--debug: c
1532 files--debug: c
1533 files--debug: c
1533 files--debug: c
1534 files--debug: b
1534 files--debug: b
1535 files--debug: a
1535 files--debug: a
1536 manifest: 6:94961b75a2da
1536 manifest: 6:94961b75a2da
1537 manifest: 5:f2dbc354b94e
1537 manifest: 5:f2dbc354b94e
1538 manifest: 4:4dc3def4f9b4
1538 manifest: 4:4dc3def4f9b4
1539 manifest: 4:4dc3def4f9b4
1539 manifest: 4:4dc3def4f9b4
1540 manifest: 3:cb5a1327723b
1540 manifest: 3:cb5a1327723b
1541 manifest: 3:cb5a1327723b
1541 manifest: 3:cb5a1327723b
1542 manifest: 2:6e0e82995c35
1542 manifest: 2:6e0e82995c35
1543 manifest: 1:4e8d705b1e53
1543 manifest: 1:4e8d705b1e53
1544 manifest: 0:a0c8bcbbb45c
1544 manifest: 0:a0c8bcbbb45c
1545 manifest--verbose: 6:94961b75a2da
1545 manifest--verbose: 6:94961b75a2da
1546 manifest--verbose: 5:f2dbc354b94e
1546 manifest--verbose: 5:f2dbc354b94e
1547 manifest--verbose: 4:4dc3def4f9b4
1547 manifest--verbose: 4:4dc3def4f9b4
1548 manifest--verbose: 4:4dc3def4f9b4
1548 manifest--verbose: 4:4dc3def4f9b4
1549 manifest--verbose: 3:cb5a1327723b
1549 manifest--verbose: 3:cb5a1327723b
1550 manifest--verbose: 3:cb5a1327723b
1550 manifest--verbose: 3:cb5a1327723b
1551 manifest--verbose: 2:6e0e82995c35
1551 manifest--verbose: 2:6e0e82995c35
1552 manifest--verbose: 1:4e8d705b1e53
1552 manifest--verbose: 1:4e8d705b1e53
1553 manifest--verbose: 0:a0c8bcbbb45c
1553 manifest--verbose: 0:a0c8bcbbb45c
1554 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1554 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1555 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1555 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1556 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1556 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1557 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1557 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1558 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1558 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1559 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1559 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1560 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1560 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1561 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1561 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1562 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1562 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1563 node: 95c24699272ef57d062b8bccc32c878bf841784a
1563 node: 95c24699272ef57d062b8bccc32c878bf841784a
1564 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1564 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1565 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1565 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1566 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1566 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1567 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1567 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1568 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1568 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1569 node: 97054abb4ab824450e9164180baf491ae0078465
1569 node: 97054abb4ab824450e9164180baf491ae0078465
1570 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1570 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1571 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1571 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1572 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1572 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1573 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1573 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1574 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1574 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1575 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1575 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1576 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1576 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1577 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1577 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1578 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1578 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1579 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1579 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1580 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1580 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1581 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1581 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1582 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1582 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1583 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1583 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1584 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1584 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1585 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1585 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1586 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1586 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1587 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1587 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1588 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1588 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1589 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1589 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1590 parents:
1590 parents:
1591 parents: -1:000000000000
1591 parents: -1:000000000000
1592 parents: 5:13207e5a10d9 4:bbe44766e73d
1592 parents: 5:13207e5a10d9 4:bbe44766e73d
1593 parents: 3:10e46f2dcbf4
1593 parents: 3:10e46f2dcbf4
1594 parents:
1594 parents:
1595 parents:
1595 parents:
1596 parents:
1596 parents:
1597 parents:
1597 parents:
1598 parents:
1598 parents:
1599 parents--verbose:
1599 parents--verbose:
1600 parents--verbose: -1:000000000000
1600 parents--verbose: -1:000000000000
1601 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1601 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1602 parents--verbose: 3:10e46f2dcbf4
1602 parents--verbose: 3:10e46f2dcbf4
1603 parents--verbose:
1603 parents--verbose:
1604 parents--verbose:
1604 parents--verbose:
1605 parents--verbose:
1605 parents--verbose:
1606 parents--verbose:
1606 parents--verbose:
1607 parents--verbose:
1607 parents--verbose:
1608 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1608 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1609 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1609 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1610 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1610 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1611 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1611 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1612 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1612 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1613 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1613 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1614 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1614 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1615 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1615 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1616 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1616 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1617 rev: 8
1617 rev: 8
1618 rev: 7
1618 rev: 7
1619 rev: 6
1619 rev: 6
1620 rev: 5
1620 rev: 5
1621 rev: 4
1621 rev: 4
1622 rev: 3
1622 rev: 3
1623 rev: 2
1623 rev: 2
1624 rev: 1
1624 rev: 1
1625 rev: 0
1625 rev: 0
1626 rev--verbose: 8
1626 rev--verbose: 8
1627 rev--verbose: 7
1627 rev--verbose: 7
1628 rev--verbose: 6
1628 rev--verbose: 6
1629 rev--verbose: 5
1629 rev--verbose: 5
1630 rev--verbose: 4
1630 rev--verbose: 4
1631 rev--verbose: 3
1631 rev--verbose: 3
1632 rev--verbose: 2
1632 rev--verbose: 2
1633 rev--verbose: 1
1633 rev--verbose: 1
1634 rev--verbose: 0
1634 rev--verbose: 0
1635 rev--debug: 8
1635 rev--debug: 8
1636 rev--debug: 7
1636 rev--debug: 7
1637 rev--debug: 6
1637 rev--debug: 6
1638 rev--debug: 5
1638 rev--debug: 5
1639 rev--debug: 4
1639 rev--debug: 4
1640 rev--debug: 3
1640 rev--debug: 3
1641 rev--debug: 2
1641 rev--debug: 2
1642 rev--debug: 1
1642 rev--debug: 1
1643 rev--debug: 0
1643 rev--debug: 0
1644 tags: tip
1644 tags: tip
1645 tags:
1645 tags:
1646 tags:
1646 tags:
1647 tags:
1647 tags:
1648 tags:
1648 tags:
1649 tags:
1649 tags:
1650 tags:
1650 tags:
1651 tags:
1651 tags:
1652 tags:
1652 tags:
1653 tags--verbose: tip
1653 tags--verbose: tip
1654 tags--verbose:
1654 tags--verbose:
1655 tags--verbose:
1655 tags--verbose:
1656 tags--verbose:
1656 tags--verbose:
1657 tags--verbose:
1657 tags--verbose:
1658 tags--verbose:
1658 tags--verbose:
1659 tags--verbose:
1659 tags--verbose:
1660 tags--verbose:
1660 tags--verbose:
1661 tags--verbose:
1661 tags--verbose:
1662 tags--debug: tip
1662 tags--debug: tip
1663 tags--debug:
1663 tags--debug:
1664 tags--debug:
1664 tags--debug:
1665 tags--debug:
1665 tags--debug:
1666 tags--debug:
1666 tags--debug:
1667 tags--debug:
1667 tags--debug:
1668 tags--debug:
1668 tags--debug:
1669 tags--debug:
1669 tags--debug:
1670 tags--debug:
1670 tags--debug:
1671 diffstat: 3: +2/-1
1671 diffstat: 3: +2/-1
1672 diffstat: 1: +1/-0
1672 diffstat: 1: +1/-0
1673 diffstat: 0: +0/-0
1673 diffstat: 0: +0/-0
1674 diffstat: 1: +1/-0
1674 diffstat: 1: +1/-0
1675 diffstat: 0: +0/-0
1675 diffstat: 0: +0/-0
1676 diffstat: 1: +1/-0
1676 diffstat: 1: +1/-0
1677 diffstat: 1: +4/-0
1677 diffstat: 1: +4/-0
1678 diffstat: 1: +2/-0
1678 diffstat: 1: +2/-0
1679 diffstat: 1: +1/-0
1679 diffstat: 1: +1/-0
1680 diffstat--verbose: 3: +2/-1
1680 diffstat--verbose: 3: +2/-1
1681 diffstat--verbose: 1: +1/-0
1681 diffstat--verbose: 1: +1/-0
1682 diffstat--verbose: 0: +0/-0
1682 diffstat--verbose: 0: +0/-0
1683 diffstat--verbose: 1: +1/-0
1683 diffstat--verbose: 1: +1/-0
1684 diffstat--verbose: 0: +0/-0
1684 diffstat--verbose: 0: +0/-0
1685 diffstat--verbose: 1: +1/-0
1685 diffstat--verbose: 1: +1/-0
1686 diffstat--verbose: 1: +4/-0
1686 diffstat--verbose: 1: +4/-0
1687 diffstat--verbose: 1: +2/-0
1687 diffstat--verbose: 1: +2/-0
1688 diffstat--verbose: 1: +1/-0
1688 diffstat--verbose: 1: +1/-0
1689 diffstat--debug: 3: +2/-1
1689 diffstat--debug: 3: +2/-1
1690 diffstat--debug: 1: +1/-0
1690 diffstat--debug: 1: +1/-0
1691 diffstat--debug: 0: +0/-0
1691 diffstat--debug: 0: +0/-0
1692 diffstat--debug: 1: +1/-0
1692 diffstat--debug: 1: +1/-0
1693 diffstat--debug: 0: +0/-0
1693 diffstat--debug: 0: +0/-0
1694 diffstat--debug: 1: +1/-0
1694 diffstat--debug: 1: +1/-0
1695 diffstat--debug: 1: +4/-0
1695 diffstat--debug: 1: +4/-0
1696 diffstat--debug: 1: +2/-0
1696 diffstat--debug: 1: +2/-0
1697 diffstat--debug: 1: +1/-0
1697 diffstat--debug: 1: +1/-0
1698 extras: branch=default
1698 extras: branch=default
1699 extras: branch=default
1699 extras: branch=default
1700 extras: branch=default
1700 extras: branch=default
1701 extras: branch=default
1701 extras: branch=default
1702 extras: branch=foo
1702 extras: branch=foo
1703 extras: branch=default
1703 extras: branch=default
1704 extras: branch=default
1704 extras: branch=default
1705 extras: branch=default
1705 extras: branch=default
1706 extras: branch=default
1706 extras: branch=default
1707 extras--verbose: branch=default
1707 extras--verbose: branch=default
1708 extras--verbose: branch=default
1708 extras--verbose: branch=default
1709 extras--verbose: branch=default
1709 extras--verbose: branch=default
1710 extras--verbose: branch=default
1710 extras--verbose: branch=default
1711 extras--verbose: branch=foo
1711 extras--verbose: branch=foo
1712 extras--verbose: branch=default
1712 extras--verbose: branch=default
1713 extras--verbose: branch=default
1713 extras--verbose: branch=default
1714 extras--verbose: branch=default
1714 extras--verbose: branch=default
1715 extras--verbose: branch=default
1715 extras--verbose: branch=default
1716 extras--debug: branch=default
1716 extras--debug: branch=default
1717 extras--debug: branch=default
1717 extras--debug: branch=default
1718 extras--debug: branch=default
1718 extras--debug: branch=default
1719 extras--debug: branch=default
1719 extras--debug: branch=default
1720 extras--debug: branch=foo
1720 extras--debug: branch=foo
1721 extras--debug: branch=default
1721 extras--debug: branch=default
1722 extras--debug: branch=default
1722 extras--debug: branch=default
1723 extras--debug: branch=default
1723 extras--debug: branch=default
1724 extras--debug: branch=default
1724 extras--debug: branch=default
1725 p1rev: 7
1725 p1rev: 7
1726 p1rev: -1
1726 p1rev: -1
1727 p1rev: 5
1727 p1rev: 5
1728 p1rev: 3
1728 p1rev: 3
1729 p1rev: 3
1729 p1rev: 3
1730 p1rev: 2
1730 p1rev: 2
1731 p1rev: 1
1731 p1rev: 1
1732 p1rev: 0
1732 p1rev: 0
1733 p1rev: -1
1733 p1rev: -1
1734 p1rev--verbose: 7
1734 p1rev--verbose: 7
1735 p1rev--verbose: -1
1735 p1rev--verbose: -1
1736 p1rev--verbose: 5
1736 p1rev--verbose: 5
1737 p1rev--verbose: 3
1737 p1rev--verbose: 3
1738 p1rev--verbose: 3
1738 p1rev--verbose: 3
1739 p1rev--verbose: 2
1739 p1rev--verbose: 2
1740 p1rev--verbose: 1
1740 p1rev--verbose: 1
1741 p1rev--verbose: 0
1741 p1rev--verbose: 0
1742 p1rev--verbose: -1
1742 p1rev--verbose: -1
1743 p1rev--debug: 7
1743 p1rev--debug: 7
1744 p1rev--debug: -1
1744 p1rev--debug: -1
1745 p1rev--debug: 5
1745 p1rev--debug: 5
1746 p1rev--debug: 3
1746 p1rev--debug: 3
1747 p1rev--debug: 3
1747 p1rev--debug: 3
1748 p1rev--debug: 2
1748 p1rev--debug: 2
1749 p1rev--debug: 1
1749 p1rev--debug: 1
1750 p1rev--debug: 0
1750 p1rev--debug: 0
1751 p1rev--debug: -1
1751 p1rev--debug: -1
1752 p2rev: -1
1752 p2rev: -1
1753 p2rev: -1
1753 p2rev: -1
1754 p2rev: 4
1754 p2rev: 4
1755 p2rev: -1
1755 p2rev: -1
1756 p2rev: -1
1756 p2rev: -1
1757 p2rev: -1
1757 p2rev: -1
1758 p2rev: -1
1758 p2rev: -1
1759 p2rev: -1
1759 p2rev: -1
1760 p2rev: -1
1760 p2rev: -1
1761 p2rev--verbose: -1
1761 p2rev--verbose: -1
1762 p2rev--verbose: -1
1762 p2rev--verbose: -1
1763 p2rev--verbose: 4
1763 p2rev--verbose: 4
1764 p2rev--verbose: -1
1764 p2rev--verbose: -1
1765 p2rev--verbose: -1
1765 p2rev--verbose: -1
1766 p2rev--verbose: -1
1766 p2rev--verbose: -1
1767 p2rev--verbose: -1
1767 p2rev--verbose: -1
1768 p2rev--verbose: -1
1768 p2rev--verbose: -1
1769 p2rev--verbose: -1
1769 p2rev--verbose: -1
1770 p2rev--debug: -1
1770 p2rev--debug: -1
1771 p2rev--debug: -1
1771 p2rev--debug: -1
1772 p2rev--debug: 4
1772 p2rev--debug: 4
1773 p2rev--debug: -1
1773 p2rev--debug: -1
1774 p2rev--debug: -1
1774 p2rev--debug: -1
1775 p2rev--debug: -1
1775 p2rev--debug: -1
1776 p2rev--debug: -1
1776 p2rev--debug: -1
1777 p2rev--debug: -1
1777 p2rev--debug: -1
1778 p2rev--debug: -1
1778 p2rev--debug: -1
1779 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1779 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1780 p1node: 0000000000000000000000000000000000000000
1780 p1node: 0000000000000000000000000000000000000000
1781 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1781 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1782 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1782 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1783 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1783 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1784 p1node: 97054abb4ab824450e9164180baf491ae0078465
1784 p1node: 97054abb4ab824450e9164180baf491ae0078465
1785 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1785 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1786 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1786 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1787 p1node: 0000000000000000000000000000000000000000
1787 p1node: 0000000000000000000000000000000000000000
1788 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1788 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1789 p1node--verbose: 0000000000000000000000000000000000000000
1789 p1node--verbose: 0000000000000000000000000000000000000000
1790 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1790 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1791 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1791 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1792 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1792 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1793 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1793 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1794 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1794 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1795 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1795 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1796 p1node--verbose: 0000000000000000000000000000000000000000
1796 p1node--verbose: 0000000000000000000000000000000000000000
1797 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1797 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1798 p1node--debug: 0000000000000000000000000000000000000000
1798 p1node--debug: 0000000000000000000000000000000000000000
1799 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1799 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1800 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1800 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1801 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1801 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1802 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1802 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1803 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1803 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1804 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1804 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1805 p1node--debug: 0000000000000000000000000000000000000000
1805 p1node--debug: 0000000000000000000000000000000000000000
1806 p2node: 0000000000000000000000000000000000000000
1806 p2node: 0000000000000000000000000000000000000000
1807 p2node: 0000000000000000000000000000000000000000
1807 p2node: 0000000000000000000000000000000000000000
1808 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1808 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1809 p2node: 0000000000000000000000000000000000000000
1809 p2node: 0000000000000000000000000000000000000000
1810 p2node: 0000000000000000000000000000000000000000
1810 p2node: 0000000000000000000000000000000000000000
1811 p2node: 0000000000000000000000000000000000000000
1811 p2node: 0000000000000000000000000000000000000000
1812 p2node: 0000000000000000000000000000000000000000
1812 p2node: 0000000000000000000000000000000000000000
1813 p2node: 0000000000000000000000000000000000000000
1813 p2node: 0000000000000000000000000000000000000000
1814 p2node: 0000000000000000000000000000000000000000
1814 p2node: 0000000000000000000000000000000000000000
1815 p2node--verbose: 0000000000000000000000000000000000000000
1815 p2node--verbose: 0000000000000000000000000000000000000000
1816 p2node--verbose: 0000000000000000000000000000000000000000
1816 p2node--verbose: 0000000000000000000000000000000000000000
1817 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1817 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1818 p2node--verbose: 0000000000000000000000000000000000000000
1818 p2node--verbose: 0000000000000000000000000000000000000000
1819 p2node--verbose: 0000000000000000000000000000000000000000
1819 p2node--verbose: 0000000000000000000000000000000000000000
1820 p2node--verbose: 0000000000000000000000000000000000000000
1820 p2node--verbose: 0000000000000000000000000000000000000000
1821 p2node--verbose: 0000000000000000000000000000000000000000
1821 p2node--verbose: 0000000000000000000000000000000000000000
1822 p2node--verbose: 0000000000000000000000000000000000000000
1822 p2node--verbose: 0000000000000000000000000000000000000000
1823 p2node--verbose: 0000000000000000000000000000000000000000
1823 p2node--verbose: 0000000000000000000000000000000000000000
1824 p2node--debug: 0000000000000000000000000000000000000000
1824 p2node--debug: 0000000000000000000000000000000000000000
1825 p2node--debug: 0000000000000000000000000000000000000000
1825 p2node--debug: 0000000000000000000000000000000000000000
1826 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1826 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1827 p2node--debug: 0000000000000000000000000000000000000000
1827 p2node--debug: 0000000000000000000000000000000000000000
1828 p2node--debug: 0000000000000000000000000000000000000000
1828 p2node--debug: 0000000000000000000000000000000000000000
1829 p2node--debug: 0000000000000000000000000000000000000000
1829 p2node--debug: 0000000000000000000000000000000000000000
1830 p2node--debug: 0000000000000000000000000000000000000000
1830 p2node--debug: 0000000000000000000000000000000000000000
1831 p2node--debug: 0000000000000000000000000000000000000000
1831 p2node--debug: 0000000000000000000000000000000000000000
1832 p2node--debug: 0000000000000000000000000000000000000000
1832 p2node--debug: 0000000000000000000000000000000000000000
1833
1833
1834 Filters work:
1834 Filters work:
1835
1835
1836 $ hg log --template '{author|domain}\n'
1836 $ hg log --template '{author|domain}\n'
1837
1837
1838 hostname
1838 hostname
1839
1839
1840
1840
1841
1841
1842
1842
1843 place
1843 place
1844 place
1844 place
1845 hostname
1845 hostname
1846
1846
1847 $ hg log --template '{author|person}\n'
1847 $ hg log --template '{author|person}\n'
1848 test
1848 test
1849 User Name
1849 User Name
1850 person
1850 person
1851 person
1851 person
1852 person
1852 person
1853 person
1853 person
1854 other
1854 other
1855 A. N. Other
1855 A. N. Other
1856 User Name
1856 User Name
1857
1857
1858 $ hg log --template '{author|user}\n'
1858 $ hg log --template '{author|user}\n'
1859 test
1859 test
1860 user
1860 user
1861 person
1861 person
1862 person
1862 person
1863 person
1863 person
1864 person
1864 person
1865 other
1865 other
1866 other
1866 other
1867 user
1867 user
1868
1868
1869 $ hg log --template '{date|date}\n'
1869 $ hg log --template '{date|date}\n'
1870 Wed Jan 01 10:01:00 2020 +0000
1870 Wed Jan 01 10:01:00 2020 +0000
1871 Mon Jan 12 13:46:40 1970 +0000
1871 Mon Jan 12 13:46:40 1970 +0000
1872 Sun Jan 18 08:40:01 1970 +0000
1872 Sun Jan 18 08:40:01 1970 +0000
1873 Sun Jan 18 08:40:00 1970 +0000
1873 Sun Jan 18 08:40:00 1970 +0000
1874 Sat Jan 17 04:53:20 1970 +0000
1874 Sat Jan 17 04:53:20 1970 +0000
1875 Fri Jan 16 01:06:40 1970 +0000
1875 Fri Jan 16 01:06:40 1970 +0000
1876 Wed Jan 14 21:20:00 1970 +0000
1876 Wed Jan 14 21:20:00 1970 +0000
1877 Tue Jan 13 17:33:20 1970 +0000
1877 Tue Jan 13 17:33:20 1970 +0000
1878 Mon Jan 12 13:46:40 1970 +0000
1878 Mon Jan 12 13:46:40 1970 +0000
1879
1879
1880 $ hg log --template '{date|isodate}\n'
1880 $ hg log --template '{date|isodate}\n'
1881 2020-01-01 10:01 +0000
1881 2020-01-01 10:01 +0000
1882 1970-01-12 13:46 +0000
1882 1970-01-12 13:46 +0000
1883 1970-01-18 08:40 +0000
1883 1970-01-18 08:40 +0000
1884 1970-01-18 08:40 +0000
1884 1970-01-18 08:40 +0000
1885 1970-01-17 04:53 +0000
1885 1970-01-17 04:53 +0000
1886 1970-01-16 01:06 +0000
1886 1970-01-16 01:06 +0000
1887 1970-01-14 21:20 +0000
1887 1970-01-14 21:20 +0000
1888 1970-01-13 17:33 +0000
1888 1970-01-13 17:33 +0000
1889 1970-01-12 13:46 +0000
1889 1970-01-12 13:46 +0000
1890
1890
1891 $ hg log --template '{date|isodatesec}\n'
1891 $ hg log --template '{date|isodatesec}\n'
1892 2020-01-01 10:01:00 +0000
1892 2020-01-01 10:01:00 +0000
1893 1970-01-12 13:46:40 +0000
1893 1970-01-12 13:46:40 +0000
1894 1970-01-18 08:40:01 +0000
1894 1970-01-18 08:40:01 +0000
1895 1970-01-18 08:40:00 +0000
1895 1970-01-18 08:40:00 +0000
1896 1970-01-17 04:53:20 +0000
1896 1970-01-17 04:53:20 +0000
1897 1970-01-16 01:06:40 +0000
1897 1970-01-16 01:06:40 +0000
1898 1970-01-14 21:20:00 +0000
1898 1970-01-14 21:20:00 +0000
1899 1970-01-13 17:33:20 +0000
1899 1970-01-13 17:33:20 +0000
1900 1970-01-12 13:46:40 +0000
1900 1970-01-12 13:46:40 +0000
1901
1901
1902 $ hg log --template '{date|rfc822date}\n'
1902 $ hg log --template '{date|rfc822date}\n'
1903 Wed, 01 Jan 2020 10:01:00 +0000
1903 Wed, 01 Jan 2020 10:01:00 +0000
1904 Mon, 12 Jan 1970 13:46:40 +0000
1904 Mon, 12 Jan 1970 13:46:40 +0000
1905 Sun, 18 Jan 1970 08:40:01 +0000
1905 Sun, 18 Jan 1970 08:40:01 +0000
1906 Sun, 18 Jan 1970 08:40:00 +0000
1906 Sun, 18 Jan 1970 08:40:00 +0000
1907 Sat, 17 Jan 1970 04:53:20 +0000
1907 Sat, 17 Jan 1970 04:53:20 +0000
1908 Fri, 16 Jan 1970 01:06:40 +0000
1908 Fri, 16 Jan 1970 01:06:40 +0000
1909 Wed, 14 Jan 1970 21:20:00 +0000
1909 Wed, 14 Jan 1970 21:20:00 +0000
1910 Tue, 13 Jan 1970 17:33:20 +0000
1910 Tue, 13 Jan 1970 17:33:20 +0000
1911 Mon, 12 Jan 1970 13:46:40 +0000
1911 Mon, 12 Jan 1970 13:46:40 +0000
1912
1912
1913 $ hg log --template '{desc|firstline}\n'
1913 $ hg log --template '{desc|firstline}\n'
1914 third
1914 third
1915 second
1915 second
1916 merge
1916 merge
1917 new head
1917 new head
1918 new branch
1918 new branch
1919 no user, no domain
1919 no user, no domain
1920 no person
1920 no person
1921 other 1
1921 other 1
1922 line 1
1922 line 1
1923
1923
1924 $ hg log --template '{node|short}\n'
1924 $ hg log --template '{node|short}\n'
1925 95c24699272e
1925 95c24699272e
1926 29114dbae42b
1926 29114dbae42b
1927 d41e714fe50d
1927 d41e714fe50d
1928 13207e5a10d9
1928 13207e5a10d9
1929 bbe44766e73d
1929 bbe44766e73d
1930 10e46f2dcbf4
1930 10e46f2dcbf4
1931 97054abb4ab8
1931 97054abb4ab8
1932 b608e9d1a3f0
1932 b608e9d1a3f0
1933 1e4e1b8f71e0
1933 1e4e1b8f71e0
1934
1934
1935 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1935 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1936 <changeset author="test"/>
1936 <changeset author="test"/>
1937 <changeset author="User Name &lt;user@hostname&gt;"/>
1937 <changeset author="User Name &lt;user@hostname&gt;"/>
1938 <changeset author="person"/>
1938 <changeset author="person"/>
1939 <changeset author="person"/>
1939 <changeset author="person"/>
1940 <changeset author="person"/>
1940 <changeset author="person"/>
1941 <changeset author="person"/>
1941 <changeset author="person"/>
1942 <changeset author="other@place"/>
1942 <changeset author="other@place"/>
1943 <changeset author="A. N. Other &lt;other@place&gt;"/>
1943 <changeset author="A. N. Other &lt;other@place&gt;"/>
1944 <changeset author="User Name &lt;user@hostname&gt;"/>
1944 <changeset author="User Name &lt;user@hostname&gt;"/>
1945
1945
1946 $ hg log --template '{rev}: {children}\n'
1946 $ hg log --template '{rev}: {children}\n'
1947 8:
1947 8:
1948 7: 8:95c24699272e
1948 7: 8:95c24699272e
1949 6:
1949 6:
1950 5: 6:d41e714fe50d
1950 5: 6:d41e714fe50d
1951 4: 6:d41e714fe50d
1951 4: 6:d41e714fe50d
1952 3: 4:bbe44766e73d 5:13207e5a10d9
1952 3: 4:bbe44766e73d 5:13207e5a10d9
1953 2: 3:10e46f2dcbf4
1953 2: 3:10e46f2dcbf4
1954 1: 2:97054abb4ab8
1954 1: 2:97054abb4ab8
1955 0: 1:b608e9d1a3f0
1955 0: 1:b608e9d1a3f0
1956
1956
1957 Formatnode filter works:
1957 Formatnode filter works:
1958
1958
1959 $ hg -q log -r 0 --template '{node|formatnode}\n'
1959 $ hg -q log -r 0 --template '{node|formatnode}\n'
1960 1e4e1b8f71e0
1960 1e4e1b8f71e0
1961
1961
1962 $ hg log -r 0 --template '{node|formatnode}\n'
1962 $ hg log -r 0 --template '{node|formatnode}\n'
1963 1e4e1b8f71e0
1963 1e4e1b8f71e0
1964
1964
1965 $ hg -v log -r 0 --template '{node|formatnode}\n'
1965 $ hg -v log -r 0 --template '{node|formatnode}\n'
1966 1e4e1b8f71e0
1966 1e4e1b8f71e0
1967
1967
1968 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1968 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1969 1e4e1b8f71e05681d422154f5421e385fec3454f
1969 1e4e1b8f71e05681d422154f5421e385fec3454f
1970
1970
1971 Age filter:
1971 Age filter:
1972
1972
1973 $ hg init unstable-hash
1973 $ hg init unstable-hash
1974 $ cd unstable-hash
1974 $ cd unstable-hash
1975 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1975 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1976
1976
1977 >>> from datetime import datetime, timedelta
1977 >>> from datetime import datetime, timedelta
1978 >>> fp = open('a', 'w')
1978 >>> fp = open('a', 'w')
1979 >>> n = datetime.now() + timedelta(366 * 7)
1979 >>> n = datetime.now() + timedelta(366 * 7)
1980 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1980 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1981 >>> fp.close()
1981 >>> fp.close()
1982 $ hg add a
1982 $ hg add a
1983 $ hg commit -m future -d "`cat a`"
1983 $ hg commit -m future -d "`cat a`"
1984
1984
1985 $ hg log -l1 --template '{date|age}\n'
1985 $ hg log -l1 --template '{date|age}\n'
1986 7 years from now
1986 7 years from now
1987
1987
1988 $ cd ..
1988 $ cd ..
1989 $ rm -rf unstable-hash
1989 $ rm -rf unstable-hash
1990
1990
1991 Add a dummy commit to make up for the instability of the above:
1991 Add a dummy commit to make up for the instability of the above:
1992
1992
1993 $ echo a > a
1993 $ echo a > a
1994 $ hg add a
1994 $ hg add a
1995 $ hg ci -m future
1995 $ hg ci -m future
1996
1996
1997 Count filter:
1997 Count filter:
1998
1998
1999 $ hg log -l1 --template '{node|count} {node|short|count}\n'
1999 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2000 40 12
2000 40 12
2001
2001
2002 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2002 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2003 0 1 4
2003 0 1 4
2004
2004
2005 $ hg log -G --template '{rev}: children: {children|count}, \
2005 $ hg log -G --template '{rev}: children: {children|count}, \
2006 > tags: {tags|count}, file_adds: {file_adds|count}, \
2006 > tags: {tags|count}, file_adds: {file_adds|count}, \
2007 > ancestors: {revset("ancestors(%s)", rev)|count}'
2007 > ancestors: {revset("ancestors(%s)", rev)|count}'
2008 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2008 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2009 |
2009 |
2010 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2010 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2011 |
2011 |
2012 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2012 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2013
2013
2014 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2014 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2015 |\
2015 |\
2016 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2016 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2017 | |
2017 | |
2018 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2018 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2019 |/
2019 |/
2020 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2020 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2021 |
2021 |
2022 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2022 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2023 |
2023 |
2024 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2024 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2025 |
2025 |
2026 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2026 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2027
2027
2028
2028
2029 Upper/lower filters:
2029 Upper/lower filters:
2030
2030
2031 $ hg log -r0 --template '{branch|upper}\n'
2031 $ hg log -r0 --template '{branch|upper}\n'
2032 DEFAULT
2032 DEFAULT
2033 $ hg log -r0 --template '{author|lower}\n'
2033 $ hg log -r0 --template '{author|lower}\n'
2034 user name <user@hostname>
2034 user name <user@hostname>
2035 $ hg log -r0 --template '{date|upper}\n'
2035 $ hg log -r0 --template '{date|upper}\n'
2036 abort: template filter 'upper' is not compatible with keyword 'date'
2036 abort: template filter 'upper' is not compatible with keyword 'date'
2037 [255]
2037 [255]
2038
2038
2039 Add a commit that does all possible modifications at once
2039 Add a commit that does all possible modifications at once
2040
2040
2041 $ echo modify >> third
2041 $ echo modify >> third
2042 $ touch b
2042 $ touch b
2043 $ hg add b
2043 $ hg add b
2044 $ hg mv fourth fifth
2044 $ hg mv fourth fifth
2045 $ hg rm a
2045 $ hg rm a
2046 $ hg ci -m "Modify, add, remove, rename"
2046 $ hg ci -m "Modify, add, remove, rename"
2047
2047
2048 Check the status template
2048 Check the status template
2049
2049
2050 $ cat <<EOF >> $HGRCPATH
2050 $ cat <<EOF >> $HGRCPATH
2051 > [extensions]
2051 > [extensions]
2052 > color=
2052 > color=
2053 > EOF
2053 > EOF
2054
2054
2055 $ hg log -T status -r 10
2055 $ hg log -T status -r 10
2056 changeset: 10:0f9759ec227a
2056 changeset: 10:0f9759ec227a
2057 tag: tip
2057 tag: tip
2058 user: test
2058 user: test
2059 date: Thu Jan 01 00:00:00 1970 +0000
2059 date: Thu Jan 01 00:00:00 1970 +0000
2060 summary: Modify, add, remove, rename
2060 summary: Modify, add, remove, rename
2061 files:
2061 files:
2062 M third
2062 M third
2063 A b
2063 A b
2064 A fifth
2064 A fifth
2065 R a
2065 R a
2066 R fourth
2066 R fourth
2067
2067
2068 $ hg log -T status -C -r 10
2068 $ hg log -T status -C -r 10
2069 changeset: 10:0f9759ec227a
2069 changeset: 10:0f9759ec227a
2070 tag: tip
2070 tag: tip
2071 user: test
2071 user: test
2072 date: Thu Jan 01 00:00:00 1970 +0000
2072 date: Thu Jan 01 00:00:00 1970 +0000
2073 summary: Modify, add, remove, rename
2073 summary: Modify, add, remove, rename
2074 files:
2074 files:
2075 M third
2075 M third
2076 A b
2076 A b
2077 A fifth
2077 A fifth
2078 fourth
2078 fourth
2079 R a
2079 R a
2080 R fourth
2080 R fourth
2081
2081
2082 $ hg log -T status -C -r 10 -v
2082 $ hg log -T status -C -r 10 -v
2083 changeset: 10:0f9759ec227a
2083 changeset: 10:0f9759ec227a
2084 tag: tip
2084 tag: tip
2085 user: test
2085 user: test
2086 date: Thu Jan 01 00:00:00 1970 +0000
2086 date: Thu Jan 01 00:00:00 1970 +0000
2087 description:
2087 description:
2088 Modify, add, remove, rename
2088 Modify, add, remove, rename
2089
2089
2090 files:
2090 files:
2091 M third
2091 M third
2092 A b
2092 A b
2093 A fifth
2093 A fifth
2094 fourth
2094 fourth
2095 R a
2095 R a
2096 R fourth
2096 R fourth
2097
2097
2098 $ hg log -T status -C -r 10 --debug
2098 $ hg log -T status -C -r 10 --debug
2099 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2099 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2100 tag: tip
2100 tag: tip
2101 phase: secret
2101 phase: secret
2102 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2102 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2103 parent: -1:0000000000000000000000000000000000000000
2103 parent: -1:0000000000000000000000000000000000000000
2104 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2104 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2105 user: test
2105 user: test
2106 date: Thu Jan 01 00:00:00 1970 +0000
2106 date: Thu Jan 01 00:00:00 1970 +0000
2107 extra: branch=default
2107 extra: branch=default
2108 description:
2108 description:
2109 Modify, add, remove, rename
2109 Modify, add, remove, rename
2110
2110
2111 files:
2111 files:
2112 M third
2112 M third
2113 A b
2113 A b
2114 A fifth
2114 A fifth
2115 fourth
2115 fourth
2116 R a
2116 R a
2117 R fourth
2117 R fourth
2118
2118
2119 $ hg log -T status -C -r 10 --quiet
2119 $ hg log -T status -C -r 10 --quiet
2120 10:0f9759ec227a
2120 10:0f9759ec227a
2121 $ hg --color=debug log -T status -r 10
2121 $ hg --color=debug log -T status -r 10
2122 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2122 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2123 [log.tag|tag: tip]
2123 [log.tag|tag: tip]
2124 [log.user|user: test]
2124 [log.user|user: test]
2125 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2125 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2126 [log.summary|summary: Modify, add, remove, rename]
2126 [log.summary|summary: Modify, add, remove, rename]
2127 [ui.note log.files|files:]
2127 [ui.note log.files|files:]
2128 [status.modified|M third]
2128 [status.modified|M third]
2129 [status.added|A b]
2129 [status.added|A b]
2130 [status.added|A fifth]
2130 [status.added|A fifth]
2131 [status.removed|R a]
2131 [status.removed|R a]
2132 [status.removed|R fourth]
2132 [status.removed|R fourth]
2133
2133
2134 $ hg --color=debug log -T status -C -r 10
2134 $ hg --color=debug log -T status -C -r 10
2135 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2135 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2136 [log.tag|tag: tip]
2136 [log.tag|tag: tip]
2137 [log.user|user: test]
2137 [log.user|user: test]
2138 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2138 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2139 [log.summary|summary: Modify, add, remove, rename]
2139 [log.summary|summary: Modify, add, remove, rename]
2140 [ui.note log.files|files:]
2140 [ui.note log.files|files:]
2141 [status.modified|M third]
2141 [status.modified|M third]
2142 [status.added|A b]
2142 [status.added|A b]
2143 [status.added|A fifth]
2143 [status.added|A fifth]
2144 [status.copied| fourth]
2144 [status.copied| fourth]
2145 [status.removed|R a]
2145 [status.removed|R a]
2146 [status.removed|R fourth]
2146 [status.removed|R fourth]
2147
2147
2148 $ hg --color=debug log -T status -C -r 10 -v
2148 $ hg --color=debug log -T status -C -r 10 -v
2149 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2149 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2150 [log.tag|tag: tip]
2150 [log.tag|tag: tip]
2151 [log.user|user: test]
2151 [log.user|user: test]
2152 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2152 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2153 [ui.note log.description|description:]
2153 [ui.note log.description|description:]
2154 [ui.note log.description|Modify, add, remove, rename]
2154 [ui.note log.description|Modify, add, remove, rename]
2155
2155
2156 [ui.note log.files|files:]
2156 [ui.note log.files|files:]
2157 [status.modified|M third]
2157 [status.modified|M third]
2158 [status.added|A b]
2158 [status.added|A b]
2159 [status.added|A fifth]
2159 [status.added|A fifth]
2160 [status.copied| fourth]
2160 [status.copied| fourth]
2161 [status.removed|R a]
2161 [status.removed|R a]
2162 [status.removed|R fourth]
2162 [status.removed|R fourth]
2163
2163
2164 $ hg --color=debug log -T status -C -r 10 --debug
2164 $ hg --color=debug log -T status -C -r 10 --debug
2165 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2165 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2166 [log.tag|tag: tip]
2166 [log.tag|tag: tip]
2167 [log.phase|phase: secret]
2167 [log.phase|phase: secret]
2168 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2168 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2169 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2169 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2170 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2170 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2171 [log.user|user: test]
2171 [log.user|user: test]
2172 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2172 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2173 [ui.debug log.extra|extra: branch=default]
2173 [ui.debug log.extra|extra: branch=default]
2174 [ui.note log.description|description:]
2174 [ui.note log.description|description:]
2175 [ui.note log.description|Modify, add, remove, rename]
2175 [ui.note log.description|Modify, add, remove, rename]
2176
2176
2177 [ui.note log.files|files:]
2177 [ui.note log.files|files:]
2178 [status.modified|M third]
2178 [status.modified|M third]
2179 [status.added|A b]
2179 [status.added|A b]
2180 [status.added|A fifth]
2180 [status.added|A fifth]
2181 [status.copied| fourth]
2181 [status.copied| fourth]
2182 [status.removed|R a]
2182 [status.removed|R a]
2183 [status.removed|R fourth]
2183 [status.removed|R fourth]
2184
2184
2185 $ hg --color=debug log -T status -C -r 10 --quiet
2185 $ hg --color=debug log -T status -C -r 10 --quiet
2186 [log.node|10:0f9759ec227a]
2186 [log.node|10:0f9759ec227a]
2187
2187
2188 Check the bisect template
2188 Check the bisect template
2189
2189
2190 $ hg bisect -g 1
2190 $ hg bisect -g 1
2191 $ hg bisect -b 3 --noupdate
2191 $ hg bisect -b 3 --noupdate
2192 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2192 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2193 $ hg log -T bisect -r 0:4
2193 $ hg log -T bisect -r 0:4
2194 changeset: 0:1e4e1b8f71e0
2194 changeset: 0:1e4e1b8f71e0
2195 bisect: good (implicit)
2195 bisect: good (implicit)
2196 user: User Name <user@hostname>
2196 user: User Name <user@hostname>
2197 date: Mon Jan 12 13:46:40 1970 +0000
2197 date: Mon Jan 12 13:46:40 1970 +0000
2198 summary: line 1
2198 summary: line 1
2199
2199
2200 changeset: 1:b608e9d1a3f0
2200 changeset: 1:b608e9d1a3f0
2201 bisect: good
2201 bisect: good
2202 user: A. N. Other <other@place>
2202 user: A. N. Other <other@place>
2203 date: Tue Jan 13 17:33:20 1970 +0000
2203 date: Tue Jan 13 17:33:20 1970 +0000
2204 summary: other 1
2204 summary: other 1
2205
2205
2206 changeset: 2:97054abb4ab8
2206 changeset: 2:97054abb4ab8
2207 bisect: untested
2207 bisect: untested
2208 user: other@place
2208 user: other@place
2209 date: Wed Jan 14 21:20:00 1970 +0000
2209 date: Wed Jan 14 21:20:00 1970 +0000
2210 summary: no person
2210 summary: no person
2211
2211
2212 changeset: 3:10e46f2dcbf4
2212 changeset: 3:10e46f2dcbf4
2213 bisect: bad
2213 bisect: bad
2214 user: person
2214 user: person
2215 date: Fri Jan 16 01:06:40 1970 +0000
2215 date: Fri Jan 16 01:06:40 1970 +0000
2216 summary: no user, no domain
2216 summary: no user, no domain
2217
2217
2218 changeset: 4:bbe44766e73d
2218 changeset: 4:bbe44766e73d
2219 bisect: bad (implicit)
2219 bisect: bad (implicit)
2220 branch: foo
2220 branch: foo
2221 user: person
2221 user: person
2222 date: Sat Jan 17 04:53:20 1970 +0000
2222 date: Sat Jan 17 04:53:20 1970 +0000
2223 summary: new branch
2223 summary: new branch
2224
2224
2225 $ hg log --debug -T bisect -r 0:4
2225 $ hg log --debug -T bisect -r 0:4
2226 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2226 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2227 bisect: good (implicit)
2227 bisect: good (implicit)
2228 phase: public
2228 phase: public
2229 parent: -1:0000000000000000000000000000000000000000
2229 parent: -1:0000000000000000000000000000000000000000
2230 parent: -1:0000000000000000000000000000000000000000
2230 parent: -1:0000000000000000000000000000000000000000
2231 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2231 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2232 user: User Name <user@hostname>
2232 user: User Name <user@hostname>
2233 date: Mon Jan 12 13:46:40 1970 +0000
2233 date: Mon Jan 12 13:46:40 1970 +0000
2234 files+: a
2234 files+: a
2235 extra: branch=default
2235 extra: branch=default
2236 description:
2236 description:
2237 line 1
2237 line 1
2238 line 2
2238 line 2
2239
2239
2240
2240
2241 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2241 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2242 bisect: good
2242 bisect: good
2243 phase: public
2243 phase: public
2244 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2244 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2245 parent: -1:0000000000000000000000000000000000000000
2245 parent: -1:0000000000000000000000000000000000000000
2246 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2246 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2247 user: A. N. Other <other@place>
2247 user: A. N. Other <other@place>
2248 date: Tue Jan 13 17:33:20 1970 +0000
2248 date: Tue Jan 13 17:33:20 1970 +0000
2249 files+: b
2249 files+: b
2250 extra: branch=default
2250 extra: branch=default
2251 description:
2251 description:
2252 other 1
2252 other 1
2253 other 2
2253 other 2
2254
2254
2255 other 3
2255 other 3
2256
2256
2257
2257
2258 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2258 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2259 bisect: untested
2259 bisect: untested
2260 phase: public
2260 phase: public
2261 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2261 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2262 parent: -1:0000000000000000000000000000000000000000
2262 parent: -1:0000000000000000000000000000000000000000
2263 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2263 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2264 user: other@place
2264 user: other@place
2265 date: Wed Jan 14 21:20:00 1970 +0000
2265 date: Wed Jan 14 21:20:00 1970 +0000
2266 files+: c
2266 files+: c
2267 extra: branch=default
2267 extra: branch=default
2268 description:
2268 description:
2269 no person
2269 no person
2270
2270
2271
2271
2272 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2272 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2273 bisect: bad
2273 bisect: bad
2274 phase: public
2274 phase: public
2275 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2275 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2276 parent: -1:0000000000000000000000000000000000000000
2276 parent: -1:0000000000000000000000000000000000000000
2277 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2277 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2278 user: person
2278 user: person
2279 date: Fri Jan 16 01:06:40 1970 +0000
2279 date: Fri Jan 16 01:06:40 1970 +0000
2280 files: c
2280 files: c
2281 extra: branch=default
2281 extra: branch=default
2282 description:
2282 description:
2283 no user, no domain
2283 no user, no domain
2284
2284
2285
2285
2286 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2286 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2287 bisect: bad (implicit)
2287 bisect: bad (implicit)
2288 branch: foo
2288 branch: foo
2289 phase: draft
2289 phase: draft
2290 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2290 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2291 parent: -1:0000000000000000000000000000000000000000
2291 parent: -1:0000000000000000000000000000000000000000
2292 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2292 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2293 user: person
2293 user: person
2294 date: Sat Jan 17 04:53:20 1970 +0000
2294 date: Sat Jan 17 04:53:20 1970 +0000
2295 extra: branch=foo
2295 extra: branch=foo
2296 description:
2296 description:
2297 new branch
2297 new branch
2298
2298
2299
2299
2300 $ hg log -v -T bisect -r 0:4
2300 $ hg log -v -T bisect -r 0:4
2301 changeset: 0:1e4e1b8f71e0
2301 changeset: 0:1e4e1b8f71e0
2302 bisect: good (implicit)
2302 bisect: good (implicit)
2303 user: User Name <user@hostname>
2303 user: User Name <user@hostname>
2304 date: Mon Jan 12 13:46:40 1970 +0000
2304 date: Mon Jan 12 13:46:40 1970 +0000
2305 files: a
2305 files: a
2306 description:
2306 description:
2307 line 1
2307 line 1
2308 line 2
2308 line 2
2309
2309
2310
2310
2311 changeset: 1:b608e9d1a3f0
2311 changeset: 1:b608e9d1a3f0
2312 bisect: good
2312 bisect: good
2313 user: A. N. Other <other@place>
2313 user: A. N. Other <other@place>
2314 date: Tue Jan 13 17:33:20 1970 +0000
2314 date: Tue Jan 13 17:33:20 1970 +0000
2315 files: b
2315 files: b
2316 description:
2316 description:
2317 other 1
2317 other 1
2318 other 2
2318 other 2
2319
2319
2320 other 3
2320 other 3
2321
2321
2322
2322
2323 changeset: 2:97054abb4ab8
2323 changeset: 2:97054abb4ab8
2324 bisect: untested
2324 bisect: untested
2325 user: other@place
2325 user: other@place
2326 date: Wed Jan 14 21:20:00 1970 +0000
2326 date: Wed Jan 14 21:20:00 1970 +0000
2327 files: c
2327 files: c
2328 description:
2328 description:
2329 no person
2329 no person
2330
2330
2331
2331
2332 changeset: 3:10e46f2dcbf4
2332 changeset: 3:10e46f2dcbf4
2333 bisect: bad
2333 bisect: bad
2334 user: person
2334 user: person
2335 date: Fri Jan 16 01:06:40 1970 +0000
2335 date: Fri Jan 16 01:06:40 1970 +0000
2336 files: c
2336 files: c
2337 description:
2337 description:
2338 no user, no domain
2338 no user, no domain
2339
2339
2340
2340
2341 changeset: 4:bbe44766e73d
2341 changeset: 4:bbe44766e73d
2342 bisect: bad (implicit)
2342 bisect: bad (implicit)
2343 branch: foo
2343 branch: foo
2344 user: person
2344 user: person
2345 date: Sat Jan 17 04:53:20 1970 +0000
2345 date: Sat Jan 17 04:53:20 1970 +0000
2346 description:
2346 description:
2347 new branch
2347 new branch
2348
2348
2349
2349
2350 $ hg --color=debug log -T bisect -r 0:4
2350 $ hg --color=debug log -T bisect -r 0:4
2351 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2351 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2352 [log.bisect bisect.good|bisect: good (implicit)]
2352 [log.bisect bisect.good|bisect: good (implicit)]
2353 [log.user|user: User Name <user@hostname>]
2353 [log.user|user: User Name <user@hostname>]
2354 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2354 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2355 [log.summary|summary: line 1]
2355 [log.summary|summary: line 1]
2356
2356
2357 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2357 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2358 [log.bisect bisect.good|bisect: good]
2358 [log.bisect bisect.good|bisect: good]
2359 [log.user|user: A. N. Other <other@place>]
2359 [log.user|user: A. N. Other <other@place>]
2360 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2360 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2361 [log.summary|summary: other 1]
2361 [log.summary|summary: other 1]
2362
2362
2363 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2363 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2364 [log.bisect bisect.untested|bisect: untested]
2364 [log.bisect bisect.untested|bisect: untested]
2365 [log.user|user: other@place]
2365 [log.user|user: other@place]
2366 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2366 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2367 [log.summary|summary: no person]
2367 [log.summary|summary: no person]
2368
2368
2369 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2369 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2370 [log.bisect bisect.bad|bisect: bad]
2370 [log.bisect bisect.bad|bisect: bad]
2371 [log.user|user: person]
2371 [log.user|user: person]
2372 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2372 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2373 [log.summary|summary: no user, no domain]
2373 [log.summary|summary: no user, no domain]
2374
2374
2375 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2375 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2376 [log.bisect bisect.bad|bisect: bad (implicit)]
2376 [log.bisect bisect.bad|bisect: bad (implicit)]
2377 [log.branch|branch: foo]
2377 [log.branch|branch: foo]
2378 [log.user|user: person]
2378 [log.user|user: person]
2379 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2379 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2380 [log.summary|summary: new branch]
2380 [log.summary|summary: new branch]
2381
2381
2382 $ hg --color=debug log --debug -T bisect -r 0:4
2382 $ hg --color=debug log --debug -T bisect -r 0:4
2383 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2383 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2384 [log.bisect bisect.good|bisect: good (implicit)]
2384 [log.bisect bisect.good|bisect: good (implicit)]
2385 [log.phase|phase: public]
2385 [log.phase|phase: public]
2386 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2386 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2387 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2387 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2388 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2388 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2389 [log.user|user: User Name <user@hostname>]
2389 [log.user|user: User Name <user@hostname>]
2390 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2390 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2391 [ui.debug log.files|files+: a]
2391 [ui.debug log.files|files+: a]
2392 [ui.debug log.extra|extra: branch=default]
2392 [ui.debug log.extra|extra: branch=default]
2393 [ui.note log.description|description:]
2393 [ui.note log.description|description:]
2394 [ui.note log.description|line 1
2394 [ui.note log.description|line 1
2395 line 2]
2395 line 2]
2396
2396
2397
2397
2398 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2398 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2399 [log.bisect bisect.good|bisect: good]
2399 [log.bisect bisect.good|bisect: good]
2400 [log.phase|phase: public]
2400 [log.phase|phase: public]
2401 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2401 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2402 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2402 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2403 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2403 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2404 [log.user|user: A. N. Other <other@place>]
2404 [log.user|user: A. N. Other <other@place>]
2405 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2405 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2406 [ui.debug log.files|files+: b]
2406 [ui.debug log.files|files+: b]
2407 [ui.debug log.extra|extra: branch=default]
2407 [ui.debug log.extra|extra: branch=default]
2408 [ui.note log.description|description:]
2408 [ui.note log.description|description:]
2409 [ui.note log.description|other 1
2409 [ui.note log.description|other 1
2410 other 2
2410 other 2
2411
2411
2412 other 3]
2412 other 3]
2413
2413
2414
2414
2415 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2415 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2416 [log.bisect bisect.untested|bisect: untested]
2416 [log.bisect bisect.untested|bisect: untested]
2417 [log.phase|phase: public]
2417 [log.phase|phase: public]
2418 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2418 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2419 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2419 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2420 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2420 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2421 [log.user|user: other@place]
2421 [log.user|user: other@place]
2422 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2422 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2423 [ui.debug log.files|files+: c]
2423 [ui.debug log.files|files+: c]
2424 [ui.debug log.extra|extra: branch=default]
2424 [ui.debug log.extra|extra: branch=default]
2425 [ui.note log.description|description:]
2425 [ui.note log.description|description:]
2426 [ui.note log.description|no person]
2426 [ui.note log.description|no person]
2427
2427
2428
2428
2429 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2429 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2430 [log.bisect bisect.bad|bisect: bad]
2430 [log.bisect bisect.bad|bisect: bad]
2431 [log.phase|phase: public]
2431 [log.phase|phase: public]
2432 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2432 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2433 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2433 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2434 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2434 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2435 [log.user|user: person]
2435 [log.user|user: person]
2436 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2436 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2437 [ui.debug log.files|files: c]
2437 [ui.debug log.files|files: c]
2438 [ui.debug log.extra|extra: branch=default]
2438 [ui.debug log.extra|extra: branch=default]
2439 [ui.note log.description|description:]
2439 [ui.note log.description|description:]
2440 [ui.note log.description|no user, no domain]
2440 [ui.note log.description|no user, no domain]
2441
2441
2442
2442
2443 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2443 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2444 [log.bisect bisect.bad|bisect: bad (implicit)]
2444 [log.bisect bisect.bad|bisect: bad (implicit)]
2445 [log.branch|branch: foo]
2445 [log.branch|branch: foo]
2446 [log.phase|phase: draft]
2446 [log.phase|phase: draft]
2447 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2447 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2448 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2448 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2449 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2449 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2450 [log.user|user: person]
2450 [log.user|user: person]
2451 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2451 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2452 [ui.debug log.extra|extra: branch=foo]
2452 [ui.debug log.extra|extra: branch=foo]
2453 [ui.note log.description|description:]
2453 [ui.note log.description|description:]
2454 [ui.note log.description|new branch]
2454 [ui.note log.description|new branch]
2455
2455
2456
2456
2457 $ hg --color=debug log -v -T bisect -r 0:4
2457 $ hg --color=debug log -v -T bisect -r 0:4
2458 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2458 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2459 [log.bisect bisect.good|bisect: good (implicit)]
2459 [log.bisect bisect.good|bisect: good (implicit)]
2460 [log.user|user: User Name <user@hostname>]
2460 [log.user|user: User Name <user@hostname>]
2461 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2461 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2462 [ui.note log.files|files: a]
2462 [ui.note log.files|files: a]
2463 [ui.note log.description|description:]
2463 [ui.note log.description|description:]
2464 [ui.note log.description|line 1
2464 [ui.note log.description|line 1
2465 line 2]
2465 line 2]
2466
2466
2467
2467
2468 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2468 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2469 [log.bisect bisect.good|bisect: good]
2469 [log.bisect bisect.good|bisect: good]
2470 [log.user|user: A. N. Other <other@place>]
2470 [log.user|user: A. N. Other <other@place>]
2471 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2471 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2472 [ui.note log.files|files: b]
2472 [ui.note log.files|files: b]
2473 [ui.note log.description|description:]
2473 [ui.note log.description|description:]
2474 [ui.note log.description|other 1
2474 [ui.note log.description|other 1
2475 other 2
2475 other 2
2476
2476
2477 other 3]
2477 other 3]
2478
2478
2479
2479
2480 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2480 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2481 [log.bisect bisect.untested|bisect: untested]
2481 [log.bisect bisect.untested|bisect: untested]
2482 [log.user|user: other@place]
2482 [log.user|user: other@place]
2483 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2483 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2484 [ui.note log.files|files: c]
2484 [ui.note log.files|files: c]
2485 [ui.note log.description|description:]
2485 [ui.note log.description|description:]
2486 [ui.note log.description|no person]
2486 [ui.note log.description|no person]
2487
2487
2488
2488
2489 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2489 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2490 [log.bisect bisect.bad|bisect: bad]
2490 [log.bisect bisect.bad|bisect: bad]
2491 [log.user|user: person]
2491 [log.user|user: person]
2492 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2492 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2493 [ui.note log.files|files: c]
2493 [ui.note log.files|files: c]
2494 [ui.note log.description|description:]
2494 [ui.note log.description|description:]
2495 [ui.note log.description|no user, no domain]
2495 [ui.note log.description|no user, no domain]
2496
2496
2497
2497
2498 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2498 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2499 [log.bisect bisect.bad|bisect: bad (implicit)]
2499 [log.bisect bisect.bad|bisect: bad (implicit)]
2500 [log.branch|branch: foo]
2500 [log.branch|branch: foo]
2501 [log.user|user: person]
2501 [log.user|user: person]
2502 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2502 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2503 [ui.note log.description|description:]
2503 [ui.note log.description|description:]
2504 [ui.note log.description|new branch]
2504 [ui.note log.description|new branch]
2505
2505
2506
2506
2507 $ hg bisect --reset
2507 $ hg bisect --reset
2508
2508
2509 Error on syntax:
2509 Error on syntax:
2510
2510
2511 $ echo 'x = "f' >> t
2511 $ echo 'x = "f' >> t
2512 $ hg log
2512 $ hg log
2513 hg: parse error at t:3: unmatched quotes
2513 hg: parse error at t:3: unmatched quotes
2514 [255]
2514 [255]
2515
2515
2516 $ hg log -T '{date'
2516 $ hg log -T '{date'
2517 hg: parse error at 1: unterminated template expansion
2517 hg: parse error at 1: unterminated template expansion
2518 [255]
2518 [255]
2519
2519
2520 Behind the scenes, this will throw TypeError
2520 Behind the scenes, this will throw TypeError
2521
2521
2522 $ hg log -l 3 --template '{date|obfuscate}\n'
2522 $ hg log -l 3 --template '{date|obfuscate}\n'
2523 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2523 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2524 [255]
2524 [255]
2525
2525
2526 Behind the scenes, this will throw a ValueError
2526 Behind the scenes, this will throw a ValueError
2527
2527
2528 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2528 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2529 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2529 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2530 [255]
2530 [255]
2531
2531
2532 Behind the scenes, this will throw AttributeError
2532 Behind the scenes, this will throw AttributeError
2533
2533
2534 $ hg log -l 3 --template 'line: {date|escape}\n'
2534 $ hg log -l 3 --template 'line: {date|escape}\n'
2535 abort: template filter 'escape' is not compatible with keyword 'date'
2535 abort: template filter 'escape' is not compatible with keyword 'date'
2536 [255]
2536 [255]
2537
2537
2538 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2538 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2539 hg: parse error: localdate expects a date information
2539 hg: parse error: localdate expects a date information
2540 [255]
2540 [255]
2541
2541
2542 Behind the scenes, this will throw ValueError
2542 Behind the scenes, this will throw ValueError
2543
2543
2544 $ hg tip --template '{author|email|date}\n'
2544 $ hg tip --template '{author|email|date}\n'
2545 hg: parse error: date expects a date information
2545 hg: parse error: date expects a date information
2546 [255]
2546 [255]
2547
2547
2548 Error in nested template:
2548 Error in nested template:
2549
2549
2550 $ hg log -T '{"date'
2550 $ hg log -T '{"date'
2551 hg: parse error at 2: unterminated string
2551 hg: parse error at 2: unterminated string
2552 [255]
2552 [255]
2553
2553
2554 $ hg log -T '{"foo{date|=}"}'
2554 $ hg log -T '{"foo{date|=}"}'
2555 hg: parse error at 11: syntax error
2555 hg: parse error at 11: syntax error
2556 [255]
2556 [255]
2557
2557
2558 Thrown an error if a template function doesn't exist
2558 Thrown an error if a template function doesn't exist
2559
2559
2560 $ hg tip --template '{foo()}\n'
2560 $ hg tip --template '{foo()}\n'
2561 hg: parse error: unknown function 'foo'
2561 hg: parse error: unknown function 'foo'
2562 [255]
2562 [255]
2563
2563
2564 Pass generator object created by template function to filter
2564 Pass generator object created by template function to filter
2565
2565
2566 $ hg log -l 1 --template '{if(author, author)|user}\n'
2566 $ hg log -l 1 --template '{if(author, author)|user}\n'
2567 test
2567 test
2568
2568
2569 Test diff function:
2569 Test diff function:
2570
2570
2571 $ hg diff -c 8
2571 $ hg diff -c 8
2572 diff -r 29114dbae42b -r 95c24699272e fourth
2572 diff -r 29114dbae42b -r 95c24699272e fourth
2573 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2573 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2574 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2574 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2575 @@ -0,0 +1,1 @@
2575 @@ -0,0 +1,1 @@
2576 +second
2576 +second
2577 diff -r 29114dbae42b -r 95c24699272e second
2577 diff -r 29114dbae42b -r 95c24699272e second
2578 --- a/second Mon Jan 12 13:46:40 1970 +0000
2578 --- a/second Mon Jan 12 13:46:40 1970 +0000
2579 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2579 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2580 @@ -1,1 +0,0 @@
2580 @@ -1,1 +0,0 @@
2581 -second
2581 -second
2582 diff -r 29114dbae42b -r 95c24699272e third
2582 diff -r 29114dbae42b -r 95c24699272e third
2583 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2583 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2584 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2584 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2585 @@ -0,0 +1,1 @@
2585 @@ -0,0 +1,1 @@
2586 +third
2586 +third
2587
2587
2588 $ hg log -r 8 -T "{diff()}"
2588 $ hg log -r 8 -T "{diff()}"
2589 diff -r 29114dbae42b -r 95c24699272e fourth
2589 diff -r 29114dbae42b -r 95c24699272e fourth
2590 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2590 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2591 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2591 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2592 @@ -0,0 +1,1 @@
2592 @@ -0,0 +1,1 @@
2593 +second
2593 +second
2594 diff -r 29114dbae42b -r 95c24699272e second
2594 diff -r 29114dbae42b -r 95c24699272e second
2595 --- a/second Mon Jan 12 13:46:40 1970 +0000
2595 --- a/second Mon Jan 12 13:46:40 1970 +0000
2596 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2596 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2597 @@ -1,1 +0,0 @@
2597 @@ -1,1 +0,0 @@
2598 -second
2598 -second
2599 diff -r 29114dbae42b -r 95c24699272e third
2599 diff -r 29114dbae42b -r 95c24699272e third
2600 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2600 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2601 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2601 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2602 @@ -0,0 +1,1 @@
2602 @@ -0,0 +1,1 @@
2603 +third
2603 +third
2604
2604
2605 $ hg log -r 8 -T "{diff('glob:f*')}"
2605 $ hg log -r 8 -T "{diff('glob:f*')}"
2606 diff -r 29114dbae42b -r 95c24699272e fourth
2606 diff -r 29114dbae42b -r 95c24699272e fourth
2607 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2607 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2608 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2608 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2609 @@ -0,0 +1,1 @@
2609 @@ -0,0 +1,1 @@
2610 +second
2610 +second
2611
2611
2612 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2612 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2613 diff -r 29114dbae42b -r 95c24699272e second
2613 diff -r 29114dbae42b -r 95c24699272e second
2614 --- a/second Mon Jan 12 13:46:40 1970 +0000
2614 --- a/second Mon Jan 12 13:46:40 1970 +0000
2615 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2615 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2616 @@ -1,1 +0,0 @@
2616 @@ -1,1 +0,0 @@
2617 -second
2617 -second
2618 diff -r 29114dbae42b -r 95c24699272e third
2618 diff -r 29114dbae42b -r 95c24699272e third
2619 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2619 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2620 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2620 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2621 @@ -0,0 +1,1 @@
2621 @@ -0,0 +1,1 @@
2622 +third
2622 +third
2623
2623
2624 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2624 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2625 diff -r 29114dbae42b -r 95c24699272e fourth
2625 diff -r 29114dbae42b -r 95c24699272e fourth
2626 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2626 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2627 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2627 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2628 @@ -0,0 +1,1 @@
2628 @@ -0,0 +1,1 @@
2629 +second
2629 +second
2630
2630
2631 $ cd ..
2631 $ cd ..
2632
2632
2633
2633
2634 latesttag:
2634 latesttag:
2635
2635
2636 $ hg init latesttag
2636 $ hg init latesttag
2637 $ cd latesttag
2637 $ cd latesttag
2638
2638
2639 $ echo a > file
2639 $ echo a > file
2640 $ hg ci -Am a -d '0 0'
2640 $ hg ci -Am a -d '0 0'
2641 adding file
2641 adding file
2642
2642
2643 $ echo b >> file
2643 $ echo b >> file
2644 $ hg ci -m b -d '1 0'
2644 $ hg ci -m b -d '1 0'
2645
2645
2646 $ echo c >> head1
2646 $ echo c >> head1
2647 $ hg ci -Am h1c -d '2 0'
2647 $ hg ci -Am h1c -d '2 0'
2648 adding head1
2648 adding head1
2649
2649
2650 $ hg update -q 1
2650 $ hg update -q 1
2651 $ echo d >> head2
2651 $ echo d >> head2
2652 $ hg ci -Am h2d -d '3 0'
2652 $ hg ci -Am h2d -d '3 0'
2653 adding head2
2653 adding head2
2654 created new head
2654 created new head
2655
2655
2656 $ echo e >> head2
2656 $ echo e >> head2
2657 $ hg ci -m h2e -d '4 0'
2657 $ hg ci -m h2e -d '4 0'
2658
2658
2659 $ hg merge -q
2659 $ hg merge -q
2660 $ hg ci -m merge -d '5 -3600'
2660 $ hg ci -m merge -d '5 -3600'
2661
2661
2662 No tag set:
2662 No tag set:
2663
2663
2664 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2664 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2665 5: null+5
2665 5: null+5
2666 4: null+4
2666 4: null+4
2667 3: null+3
2667 3: null+3
2668 2: null+3
2668 2: null+3
2669 1: null+2
2669 1: null+2
2670 0: null+1
2670 0: null+1
2671
2671
2672 One common tag: longest path wins:
2672 One common tag: longest path wins:
2673
2673
2674 $ hg tag -r 1 -m t1 -d '6 0' t1
2674 $ hg tag -r 1 -m t1 -d '6 0' t1
2675 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2675 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2676 6: t1+4
2676 6: t1+4
2677 5: t1+3
2677 5: t1+3
2678 4: t1+2
2678 4: t1+2
2679 3: t1+1
2679 3: t1+1
2680 2: t1+1
2680 2: t1+1
2681 1: t1+0
2681 1: t1+0
2682 0: null+1
2682 0: null+1
2683
2683
2684 One ancestor tag: more recent wins:
2684 One ancestor tag: more recent wins:
2685
2685
2686 $ hg tag -r 2 -m t2 -d '7 0' t2
2686 $ hg tag -r 2 -m t2 -d '7 0' t2
2687 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2687 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2688 7: t2+3
2688 7: t2+3
2689 6: t2+2
2689 6: t2+2
2690 5: t2+1
2690 5: t2+1
2691 4: t1+2
2691 4: t1+2
2692 3: t1+1
2692 3: t1+1
2693 2: t2+0
2693 2: t2+0
2694 1: t1+0
2694 1: t1+0
2695 0: null+1
2695 0: null+1
2696
2696
2697 Two branch tags: more recent wins:
2697 Two branch tags: more recent wins:
2698
2698
2699 $ hg tag -r 3 -m t3 -d '8 0' t3
2699 $ hg tag -r 3 -m t3 -d '8 0' t3
2700 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2700 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2701 8: t3+5
2701 8: t3+5
2702 7: t3+4
2702 7: t3+4
2703 6: t3+3
2703 6: t3+3
2704 5: t3+2
2704 5: t3+2
2705 4: t3+1
2705 4: t3+1
2706 3: t3+0
2706 3: t3+0
2707 2: t2+0
2707 2: t2+0
2708 1: t1+0
2708 1: t1+0
2709 0: null+1
2709 0: null+1
2710
2710
2711 Merged tag overrides:
2711 Merged tag overrides:
2712
2712
2713 $ hg tag -r 5 -m t5 -d '9 0' t5
2713 $ hg tag -r 5 -m t5 -d '9 0' t5
2714 $ hg tag -r 3 -m at3 -d '10 0' at3
2714 $ hg tag -r 3 -m at3 -d '10 0' at3
2715 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2715 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2716 10: t5+5
2716 10: t5+5
2717 9: t5+4
2717 9: t5+4
2718 8: t5+3
2718 8: t5+3
2719 7: t5+2
2719 7: t5+2
2720 6: t5+1
2720 6: t5+1
2721 5: t5+0
2721 5: t5+0
2722 4: at3:t3+1
2722 4: at3:t3+1
2723 3: at3:t3+0
2723 3: at3:t3+0
2724 2: t2+0
2724 2: t2+0
2725 1: t1+0
2725 1: t1+0
2726 0: null+1
2726 0: null+1
2727
2727
2728 $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2728 $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2729 10: t5+5,5
2729 10: t5+5,5
2730 9: t5+4,4
2730 9: t5+4,4
2731 8: t5+3,3
2731 8: t5+3,3
2732 7: t5+2,2
2732 7: t5+2,2
2733 6: t5+1,1
2733 6: t5+1,1
2734 5: t5+0,0
2734 5: t5+0,0
2735 4: at3+1,1 t3+1,1
2735 4: at3+1,1 t3+1,1
2736 3: at3+0,0 t3+0,0
2736 3: at3+0,0 t3+0,0
2737 2: t2+0,0
2737 2: t2+0,0
2738 1: t1+0,0
2738 1: t1+0,0
2739 0: null+1,1
2739 0: null+1,1
2740
2740
2741 $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
2741 $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
2742 10: t3, C: 8, D: 7
2742 10: t3, C: 8, D: 7
2743 9: t3, C: 7, D: 6
2743 9: t3, C: 7, D: 6
2744 8: t3, C: 6, D: 5
2744 8: t3, C: 6, D: 5
2745 7: t3, C: 5, D: 4
2745 7: t3, C: 5, D: 4
2746 6: t3, C: 4, D: 3
2746 6: t3, C: 4, D: 3
2747 5: t3, C: 3, D: 2
2747 5: t3, C: 3, D: 2
2748 4: t3, C: 1, D: 1
2748 4: t3, C: 1, D: 1
2749 3: t3, C: 0, D: 0
2749 3: t3, C: 0, D: 0
2750 2: t1, C: 1, D: 1
2750 2: t1, C: 1, D: 1
2751 1: t1, C: 0, D: 0
2751 1: t1, C: 0, D: 0
2752 0: null, C: 1, D: 1
2752 0: null, C: 1, D: 1
2753
2753
2754 $ cd ..
2754 $ cd ..
2755
2755
2756
2756
2757 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2757 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2758 if it is a relative path
2758 if it is a relative path
2759
2759
2760 $ mkdir -p home/styles
2760 $ mkdir -p home/styles
2761
2761
2762 $ cat > home/styles/teststyle <<EOF
2762 $ cat > home/styles/teststyle <<EOF
2763 > changeset = 'test {rev}:{node|short}\n'
2763 > changeset = 'test {rev}:{node|short}\n'
2764 > EOF
2764 > EOF
2765
2765
2766 $ HOME=`pwd`/home; export HOME
2766 $ HOME=`pwd`/home; export HOME
2767
2767
2768 $ cat > latesttag/.hg/hgrc <<EOF
2768 $ cat > latesttag/.hg/hgrc <<EOF
2769 > [ui]
2769 > [ui]
2770 > style = ~/styles/teststyle
2770 > style = ~/styles/teststyle
2771 > EOF
2771 > EOF
2772
2772
2773 $ hg -R latesttag tip
2773 $ hg -R latesttag tip
2774 test 10:9b4a630e5f5f
2774 test 10:9b4a630e5f5f
2775
2775
2776 Test recursive showlist template (issue1989):
2776 Test recursive showlist template (issue1989):
2777
2777
2778 $ cat > style1989 <<EOF
2778 $ cat > style1989 <<EOF
2779 > changeset = '{file_mods}{manifest}{extras}'
2779 > changeset = '{file_mods}{manifest}{extras}'
2780 > file_mod = 'M|{author|person}\n'
2780 > file_mod = 'M|{author|person}\n'
2781 > manifest = '{rev},{author}\n'
2781 > manifest = '{rev},{author}\n'
2782 > extra = '{key}: {author}\n'
2782 > extra = '{key}: {author}\n'
2783 > EOF
2783 > EOF
2784
2784
2785 $ hg -R latesttag log -r tip --style=style1989
2785 $ hg -R latesttag log -r tip --style=style1989
2786 M|test
2786 M|test
2787 10,test
2787 10,test
2788 branch: test
2788 branch: test
2789
2789
2790 Test new-style inline templating:
2790 Test new-style inline templating:
2791
2791
2792 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2792 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2793 modified files: .hgtags
2793 modified files: .hgtags
2794
2794
2795
2795
2796 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
2796 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
2797 hg: parse error: keyword 'rev' is not iterable
2797 hg: parse error: keyword 'rev' is not iterable
2798 [255]
2798 [255]
2799 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
2799 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
2800 hg: parse error: None is not iterable
2800 hg: parse error: None is not iterable
2801 [255]
2801 [255]
2802
2802
2803 Test the sub function of templating for expansion:
2803 Test the sub function of templating for expansion:
2804
2804
2805 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2805 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2806 xx
2806 xx
2807
2807
2808 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
2808 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
2809 hg: parse error: sub got an invalid pattern: [
2809 hg: parse error: sub got an invalid pattern: [
2810 [255]
2810 [255]
2811 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
2811 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
2812 hg: parse error: sub got an invalid replacement: \1
2812 hg: parse error: sub got an invalid replacement: \1
2813 [255]
2813 [255]
2814
2814
2815 Test the strip function with chars specified:
2815 Test the strip function with chars specified:
2816
2816
2817 $ hg log -R latesttag --template '{desc}\n'
2817 $ hg log -R latesttag --template '{desc}\n'
2818 at3
2818 at3
2819 t5
2819 t5
2820 t3
2820 t3
2821 t2
2821 t2
2822 t1
2822 t1
2823 merge
2823 merge
2824 h2e
2824 h2e
2825 h2d
2825 h2d
2826 h1c
2826 h1c
2827 b
2827 b
2828 a
2828 a
2829
2829
2830 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2830 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2831 at3
2831 at3
2832 5
2832 5
2833 3
2833 3
2834 2
2834 2
2835 1
2835 1
2836 merg
2836 merg
2837 h2
2837 h2
2838 h2d
2838 h2d
2839 h1c
2839 h1c
2840 b
2840 b
2841 a
2841 a
2842
2842
2843 Test date format:
2843 Test date format:
2844
2844
2845 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2845 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2846 date: 70 01 01 10 +0000
2846 date: 70 01 01 10 +0000
2847 date: 70 01 01 09 +0000
2847 date: 70 01 01 09 +0000
2848 date: 70 01 01 08 +0000
2848 date: 70 01 01 08 +0000
2849 date: 70 01 01 07 +0000
2849 date: 70 01 01 07 +0000
2850 date: 70 01 01 06 +0000
2850 date: 70 01 01 06 +0000
2851 date: 70 01 01 05 +0100
2851 date: 70 01 01 05 +0100
2852 date: 70 01 01 04 +0000
2852 date: 70 01 01 04 +0000
2853 date: 70 01 01 03 +0000
2853 date: 70 01 01 03 +0000
2854 date: 70 01 01 02 +0000
2854 date: 70 01 01 02 +0000
2855 date: 70 01 01 01 +0000
2855 date: 70 01 01 01 +0000
2856 date: 70 01 01 00 +0000
2856 date: 70 01 01 00 +0000
2857
2857
2858 Test invalid date:
2858 Test invalid date:
2859
2859
2860 $ hg log -R latesttag -T '{date(rev)}\n'
2860 $ hg log -R latesttag -T '{date(rev)}\n'
2861 hg: parse error: date expects a date information
2861 hg: parse error: date expects a date information
2862 [255]
2862 [255]
2863
2863
2864 Test integer literal:
2864 Test integer literal:
2865
2865
2866 $ hg debugtemplate -v '{(0)}\n'
2866 $ hg debugtemplate -v '{(0)}\n'
2867 (template
2867 (template
2868 (group
2868 (group
2869 ('integer', '0'))
2869 ('integer', '0'))
2870 ('string', '\n'))
2870 ('string', '\n'))
2871 0
2871 0
2872 $ hg debugtemplate -v '{(123)}\n'
2872 $ hg debugtemplate -v '{(123)}\n'
2873 (template
2873 (template
2874 (group
2874 (group
2875 ('integer', '123'))
2875 ('integer', '123'))
2876 ('string', '\n'))
2876 ('string', '\n'))
2877 123
2877 123
2878 $ hg debugtemplate -v '{(-4)}\n'
2878 $ hg debugtemplate -v '{(-4)}\n'
2879 (template
2879 (template
2880 (group
2880 (group
2881 ('integer', '-4'))
2881 ('integer', '-4'))
2882 ('string', '\n'))
2882 ('string', '\n'))
2883 -4
2883 -4
2884 $ hg debugtemplate '{(-)}\n'
2884 $ hg debugtemplate '{(-)}\n'
2885 hg: parse error at 2: integer literal without digits
2885 hg: parse error at 2: integer literal without digits
2886 [255]
2886 [255]
2887 $ hg debugtemplate '{(-a)}\n'
2887 $ hg debugtemplate '{(-a)}\n'
2888 hg: parse error at 2: integer literal without digits
2888 hg: parse error at 2: integer literal without digits
2889 [255]
2889 [255]
2890
2890
2891 top-level integer literal is interpreted as symbol (i.e. variable name):
2891 top-level integer literal is interpreted as symbol (i.e. variable name):
2892
2892
2893 $ hg debugtemplate -D 1=one -v '{1}\n'
2893 $ hg debugtemplate -D 1=one -v '{1}\n'
2894 (template
2894 (template
2895 ('integer', '1')
2895 ('integer', '1')
2896 ('string', '\n'))
2896 ('string', '\n'))
2897 one
2897 one
2898 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
2898 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
2899 (template
2899 (template
2900 (func
2900 (func
2901 ('symbol', 'if')
2901 ('symbol', 'if')
2902 (list
2902 (list
2903 ('string', 't')
2903 ('string', 't')
2904 (template
2904 (template
2905 ('integer', '1'))))
2905 ('integer', '1'))))
2906 ('string', '\n'))
2906 ('string', '\n'))
2907 one
2907 one
2908 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
2908 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
2909 (template
2909 (template
2910 (|
2910 (|
2911 ('integer', '1')
2911 ('integer', '1')
2912 ('symbol', 'stringify'))
2912 ('symbol', 'stringify'))
2913 ('string', '\n'))
2913 ('string', '\n'))
2914 one
2914 one
2915
2915
2916 unless explicit symbol is expected:
2916 unless explicit symbol is expected:
2917
2917
2918 $ hg log -Ra -r0 -T '{desc|1}\n'
2918 $ hg log -Ra -r0 -T '{desc|1}\n'
2919 hg: parse error: expected a symbol, got 'integer'
2919 hg: parse error: expected a symbol, got 'integer'
2920 [255]
2920 [255]
2921 $ hg log -Ra -r0 -T '{1()}\n'
2921 $ hg log -Ra -r0 -T '{1()}\n'
2922 hg: parse error: expected a symbol, got 'integer'
2922 hg: parse error: expected a symbol, got 'integer'
2923 [255]
2923 [255]
2924
2924
2925 Test string literal:
2925 Test string literal:
2926
2926
2927 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
2927 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
2928 (template
2928 (template
2929 ('string', 'string with no template fragment')
2929 ('string', 'string with no template fragment')
2930 ('string', '\n'))
2930 ('string', '\n'))
2931 string with no template fragment
2931 string with no template fragment
2932 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
2932 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
2933 (template
2933 (template
2934 (template
2934 (template
2935 ('string', 'template: ')
2935 ('string', 'template: ')
2936 ('symbol', 'rev'))
2936 ('symbol', 'rev'))
2937 ('string', '\n'))
2937 ('string', '\n'))
2938 template: 0
2938 template: 0
2939 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
2939 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
2940 (template
2940 (template
2941 ('string', 'rawstring: {rev}')
2941 ('string', 'rawstring: {rev}')
2942 ('string', '\n'))
2942 ('string', '\n'))
2943 rawstring: {rev}
2943 rawstring: {rev}
2944 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
2944 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
2945 (template
2945 (template
2946 (%
2946 (%
2947 ('symbol', 'files')
2947 ('symbol', 'files')
2948 ('string', 'rawstring: {file}'))
2948 ('string', 'rawstring: {file}'))
2949 ('string', '\n'))
2949 ('string', '\n'))
2950 rawstring: {file}
2950 rawstring: {file}
2951
2951
2952 Test string escaping:
2952 Test string escaping:
2953
2953
2954 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2954 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2955 >
2955 >
2956 <>\n<[>
2956 <>\n<[>
2957 <>\n<]>
2957 <>\n<]>
2958 <>\n<
2958 <>\n<
2959
2959
2960 $ hg log -R latesttag -r 0 \
2960 $ hg log -R latesttag -r 0 \
2961 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2961 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2962 >
2962 >
2963 <>\n<[>
2963 <>\n<[>
2964 <>\n<]>
2964 <>\n<]>
2965 <>\n<
2965 <>\n<
2966
2966
2967 $ hg log -R latesttag -r 0 -T esc \
2967 $ hg log -R latesttag -r 0 -T esc \
2968 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2968 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2969 >
2969 >
2970 <>\n<[>
2970 <>\n<[>
2971 <>\n<]>
2971 <>\n<]>
2972 <>\n<
2972 <>\n<
2973
2973
2974 $ cat <<'EOF' > esctmpl
2974 $ cat <<'EOF' > esctmpl
2975 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2975 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2976 > EOF
2976 > EOF
2977 $ hg log -R latesttag -r 0 --style ./esctmpl
2977 $ hg log -R latesttag -r 0 --style ./esctmpl
2978 >
2978 >
2979 <>\n<[>
2979 <>\n<[>
2980 <>\n<]>
2980 <>\n<]>
2981 <>\n<
2981 <>\n<
2982
2982
2983 Test string escaping of quotes:
2983 Test string escaping of quotes:
2984
2984
2985 $ hg log -Ra -r0 -T '{"\""}\n'
2985 $ hg log -Ra -r0 -T '{"\""}\n'
2986 "
2986 "
2987 $ hg log -Ra -r0 -T '{"\\\""}\n'
2987 $ hg log -Ra -r0 -T '{"\\\""}\n'
2988 \"
2988 \"
2989 $ hg log -Ra -r0 -T '{r"\""}\n'
2989 $ hg log -Ra -r0 -T '{r"\""}\n'
2990 \"
2990 \"
2991 $ hg log -Ra -r0 -T '{r"\\\""}\n'
2991 $ hg log -Ra -r0 -T '{r"\\\""}\n'
2992 \\\"
2992 \\\"
2993
2993
2994
2994
2995 $ hg log -Ra -r0 -T '{"\""}\n'
2995 $ hg log -Ra -r0 -T '{"\""}\n'
2996 "
2996 "
2997 $ hg log -Ra -r0 -T '{"\\\""}\n'
2997 $ hg log -Ra -r0 -T '{"\\\""}\n'
2998 \"
2998 \"
2999 $ hg log -Ra -r0 -T '{r"\""}\n'
2999 $ hg log -Ra -r0 -T '{r"\""}\n'
3000 \"
3000 \"
3001 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3001 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3002 \\\"
3002 \\\"
3003
3003
3004 Test exception in quoted template. single backslash before quotation mark is
3004 Test exception in quoted template. single backslash before quotation mark is
3005 stripped before parsing:
3005 stripped before parsing:
3006
3006
3007 $ cat <<'EOF' > escquotetmpl
3007 $ cat <<'EOF' > escquotetmpl
3008 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3008 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3009 > EOF
3009 > EOF
3010 $ cd latesttag
3010 $ cd latesttag
3011 $ hg log -r 2 --style ../escquotetmpl
3011 $ hg log -r 2 --style ../escquotetmpl
3012 " \" \" \\" head1
3012 " \" \" \\" head1
3013
3013
3014 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3014 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3015 valid
3015 valid
3016 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3016 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3017 valid
3017 valid
3018
3018
3019 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3019 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3020 _evalifliteral() templates (issue4733):
3020 _evalifliteral() templates (issue4733):
3021
3021
3022 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3022 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3023 "2
3023 "2
3024 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3024 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3025 "2
3025 "2
3026 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3026 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3027 "2
3027 "2
3028
3028
3029 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3029 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3030 \"
3030 \"
3031 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3031 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3032 \"
3032 \"
3033 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3033 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3034 \"
3034 \"
3035
3035
3036 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3036 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3037 \\\"
3037 \\\"
3038 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3038 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3039 \\\"
3039 \\\"
3040 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3040 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3041 \\\"
3041 \\\"
3042
3042
3043 escaped single quotes and errors:
3043 escaped single quotes and errors:
3044
3044
3045 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3045 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3046 foo
3046 foo
3047 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3047 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3048 foo
3048 foo
3049 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3049 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3050 hg: parse error at 21: unterminated string
3050 hg: parse error at 21: unterminated string
3051 [255]
3051 [255]
3052 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3052 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3053 hg: parse error: trailing \ in string
3053 hg: parse error: trailing \ in string
3054 [255]
3054 [255]
3055 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3055 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3056 hg: parse error: trailing \ in string
3056 hg: parse error: trailing \ in string
3057 [255]
3057 [255]
3058
3058
3059 $ cd ..
3059 $ cd ..
3060
3060
3061 Test leading backslashes:
3061 Test leading backslashes:
3062
3062
3063 $ cd latesttag
3063 $ cd latesttag
3064 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3064 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3065 {rev} {file}
3065 {rev} {file}
3066 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3066 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3067 \2 \head1
3067 \2 \head1
3068 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3068 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3069 \{rev} \{file}
3069 \{rev} \{file}
3070 $ cd ..
3070 $ cd ..
3071
3071
3072 Test leading backslashes in "if" expression (issue4714):
3072 Test leading backslashes in "if" expression (issue4714):
3073
3073
3074 $ cd latesttag
3074 $ cd latesttag
3075 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3075 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3076 {rev} \{rev}
3076 {rev} \{rev}
3077 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3077 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3078 \2 \\{rev}
3078 \2 \\{rev}
3079 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3079 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3080 \{rev} \\\{rev}
3080 \{rev} \\\{rev}
3081 $ cd ..
3081 $ cd ..
3082
3082
3083 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3083 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3084
3084
3085 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3085 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3086 \x6e
3086 \x6e
3087 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3087 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3088 \x5c\x786e
3088 \x5c\x786e
3089 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3089 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3090 \x6e
3090 \x6e
3091 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3091 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3092 \x5c\x786e
3092 \x5c\x786e
3093
3093
3094 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3094 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3095 \x6e
3095 \x6e
3096 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3096 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3097 \x5c\x786e
3097 \x5c\x786e
3098 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3098 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3099 \x6e
3099 \x6e
3100 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3100 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3101 \x5c\x786e
3101 \x5c\x786e
3102
3102
3103 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3103 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3104 fourth
3104 fourth
3105 second
3105 second
3106 third
3106 third
3107 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3107 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3108 fourth\nsecond\nthird
3108 fourth\nsecond\nthird
3109
3109
3110 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3110 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3111 <p>
3111 <p>
3112 1st
3112 1st
3113 </p>
3113 </p>
3114 <p>
3114 <p>
3115 2nd
3115 2nd
3116 </p>
3116 </p>
3117 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3117 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3118 <p>
3118 <p>
3119 1st\n\n2nd
3119 1st\n\n2nd
3120 </p>
3120 </p>
3121 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3121 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3122 1st
3122 1st
3123
3123
3124 2nd
3124 2nd
3125
3125
3126 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3126 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3127 o perso
3127 o perso
3128 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3128 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3129 no person
3129 no person
3130 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3130 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3131 o perso
3131 o perso
3132 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3132 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3133 no perso
3133 no perso
3134
3134
3135 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3135 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3136 -o perso-
3136 -o perso-
3137 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3137 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3138 no person
3138 no person
3139 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3139 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3140 \x2do perso\x2d
3140 \x2do perso\x2d
3141 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3141 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3142 -o perso-
3142 -o perso-
3143 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3143 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3144 \x2do perso\x6e
3144 \x2do perso\x6e
3145
3145
3146 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3146 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3147 fourth
3147 fourth
3148 second
3148 second
3149 third
3149 third
3150
3150
3151 Test string escaping in nested expression:
3151 Test string escaping in nested expression:
3152
3152
3153 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3153 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3154 fourth\x6esecond\x6ethird
3154 fourth\x6esecond\x6ethird
3155 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3155 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3156 fourth\x6esecond\x6ethird
3156 fourth\x6esecond\x6ethird
3157
3157
3158 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3158 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3159 fourth\x6esecond\x6ethird
3159 fourth\x6esecond\x6ethird
3160 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3160 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3161 fourth\x5c\x786esecond\x5c\x786ethird
3161 fourth\x5c\x786esecond\x5c\x786ethird
3162
3162
3163 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3163 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3164 3:\x6eo user, \x6eo domai\x6e
3164 3:\x6eo user, \x6eo domai\x6e
3165 4:\x5c\x786eew bra\x5c\x786ech
3165 4:\x5c\x786eew bra\x5c\x786ech
3166
3166
3167 Test quotes in nested expression are evaluated just like a $(command)
3167 Test quotes in nested expression are evaluated just like a $(command)
3168 substitution in POSIX shells:
3168 substitution in POSIX shells:
3169
3169
3170 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3170 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3171 8:95c24699272e
3171 8:95c24699272e
3172 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3172 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3173 {8} "95c24699272e"
3173 {8} "95c24699272e"
3174
3174
3175 Test recursive evaluation:
3175 Test recursive evaluation:
3176
3176
3177 $ hg init r
3177 $ hg init r
3178 $ cd r
3178 $ cd r
3179 $ echo a > a
3179 $ echo a > a
3180 $ hg ci -Am '{rev}'
3180 $ hg ci -Am '{rev}'
3181 adding a
3181 adding a
3182 $ hg log -r 0 --template '{if(rev, desc)}\n'
3182 $ hg log -r 0 --template '{if(rev, desc)}\n'
3183 {rev}
3183 {rev}
3184 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3184 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3185 test 0
3185 test 0
3186
3186
3187 $ hg branch -q 'text.{rev}'
3187 $ hg branch -q 'text.{rev}'
3188 $ echo aa >> aa
3188 $ echo aa >> aa
3189 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3189 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3190
3190
3191 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3191 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3192 {node|short}desc to
3192 {node|short}desc to
3193 text.{rev}be wrapped
3193 text.{rev}be wrapped
3194 text.{rev}desc to be
3194 text.{rev}desc to be
3195 text.{rev}wrapped (no-eol)
3195 text.{rev}wrapped (no-eol)
3196 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3196 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3197 bcc7ff960b8e:desc to
3197 bcc7ff960b8e:desc to
3198 text.1:be wrapped
3198 text.1:be wrapped
3199 text.1:desc to be
3199 text.1:desc to be
3200 text.1:wrapped (no-eol)
3200 text.1:wrapped (no-eol)
3201 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3201 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3202 hg: parse error: fill expects an integer width
3202 hg: parse error: fill expects an integer width
3203 [255]
3203 [255]
3204
3204
3205 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3205 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3206 {node|short} (no-eol)
3206 {node|short} (no-eol)
3207 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3207 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3208 bcc-ff---b-e (no-eol)
3208 bcc-ff---b-e (no-eol)
3209
3209
3210 $ cat >> .hg/hgrc <<EOF
3210 $ cat >> .hg/hgrc <<EOF
3211 > [extensions]
3211 > [extensions]
3212 > color=
3212 > color=
3213 > [color]
3213 > [color]
3214 > mode=ansi
3214 > mode=ansi
3215 > text.{rev} = red
3215 > text.{rev} = red
3216 > text.1 = green
3216 > text.1 = green
3217 > EOF
3217 > EOF
3218 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3218 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3219 \x1b[0;31mtext\x1b[0m (esc)
3219 \x1b[0;31mtext\x1b[0m (esc)
3220 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3220 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3221 \x1b[0;32mtext\x1b[0m (esc)
3221 \x1b[0;32mtext\x1b[0m (esc)
3222
3222
3223 color effect can be specified without quoting:
3223 color effect can be specified without quoting:
3224
3224
3225 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3225 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3226 \x1b[0;31mtext\x1b[0m (esc)
3226 \x1b[0;31mtext\x1b[0m (esc)
3227
3227
3228 label should be no-op if color is disabled:
3228 label should be no-op if color is disabled:
3229
3229
3230 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3230 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3231 text
3231 text
3232 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3232 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3233 text
3233 text
3234
3234
3235 Test branches inside if statement:
3235 Test branches inside if statement:
3236
3236
3237 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3237 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3238 no
3238 no
3239
3239
3240 Test get function:
3240 Test get function:
3241
3241
3242 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3242 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3243 default
3243 default
3244 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3244 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3245 default
3245 default
3246 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3246 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3247 hg: parse error: get() expects a dict as first argument
3247 hg: parse error: get() expects a dict as first argument
3248 [255]
3248 [255]
3249
3249
3250 Test localdate(date, tz) function:
3250 Test localdate(date, tz) function:
3251
3251
3252 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3252 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3253 1970-01-01 09:00 +0900
3253 1970-01-01 09:00 +0900
3254 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3254 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3255 1970-01-01 00:00 +0000
3255 1970-01-01 00:00 +0000
3256 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3256 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3257 1970-01-01 02:00 +0200
3257 1970-01-01 02:00 +0200
3258 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3258 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3259 1970-01-01 00:00 +0000
3259 1970-01-01 00:00 +0000
3260 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3260 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3261 1970-01-01 00:00 +0000
3261 1970-01-01 00:00 +0000
3262 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3262 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3263 hg: parse error: localdate expects a timezone
3263 hg: parse error: localdate expects a timezone
3264 [255]
3264 [255]
3265 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3265 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3266 hg: parse error: localdate expects a timezone
3266 hg: parse error: localdate expects a timezone
3267 [255]
3267 [255]
3268
3268
3269 Test shortest(node) function:
3269 Test shortest(node) function:
3270
3270
3271 $ echo b > b
3271 $ echo b > b
3272 $ hg ci -qAm b
3272 $ hg ci -qAm b
3273 $ hg log --template '{shortest(node)}\n'
3273 $ hg log --template '{shortest(node)}\n'
3274 e777
3274 e777
3275 bcc7
3275 bcc7
3276 f776
3276 f776
3277 $ hg log --template '{shortest(node, 10)}\n'
3277 $ hg log --template '{shortest(node, 10)}\n'
3278 e777603221
3278 e777603221
3279 bcc7ff960b
3279 bcc7ff960b
3280 f7769ec2ab
3280 f7769ec2ab
3281 $ hg log --template '{node|shortest}\n' -l1
3281 $ hg log --template '{node|shortest}\n' -l1
3282 e777
3282 e777
3283
3283
3284 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3284 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3285 f7769ec2ab
3285 f7769ec2ab
3286 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3286 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3287 hg: parse error: shortest() expects an integer minlength
3287 hg: parse error: shortest() expects an integer minlength
3288 [255]
3288 [255]
3289
3289
3290 Test pad function
3290 Test pad function
3291
3291
3292 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3292 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3293 2 test
3293 2 test
3294 1 {node|short}
3294 1 {node|short}
3295 0 test
3295 0 test
3296
3296
3297 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3297 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3298 2 test
3298 2 test
3299 1 {node|short}
3299 1 {node|short}
3300 0 test
3300 0 test
3301
3301
3302 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3302 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3303 2------------------- test
3303 2------------------- test
3304 1------------------- {node|short}
3304 1------------------- {node|short}
3305 0------------------- test
3305 0------------------- test
3306
3306
3307 Test template string in pad function
3307 Test template string in pad function
3308
3308
3309 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3309 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3310 {0} test
3310 {0} test
3311
3311
3312 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3312 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3313 \{rev} test
3313 \{rev} test
3314
3314
3315 Test width argument passed to pad function
3315 Test width argument passed to pad function
3316
3316
3317 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3317 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3318 0 test
3318 0 test
3319 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3319 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3320 hg: parse error: pad() expects an integer width
3320 hg: parse error: pad() expects an integer width
3321 [255]
3321 [255]
3322
3322
3323 Test ifcontains function
3323 Test ifcontains function
3324
3324
3325 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3325 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3326 2 is in the string
3326 2 is in the string
3327 1 is not
3327 1 is not
3328 0 is in the string
3328 0 is in the string
3329
3329
3330 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3330 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3331 2 is in the string
3331 2 is in the string
3332 1 is not
3332 1 is not
3333 0 is in the string
3333 0 is in the string
3334
3334
3335 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3335 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3336 2 did not add a
3336 2 did not add a
3337 1 did not add a
3337 1 did not add a
3338 0 added a
3338 0 added a
3339
3339
3340 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3340 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3341 2 is parent of 1
3341 2 is parent of 1
3342 1
3342 1
3343 0
3343 0
3344
3344
3345 Test revset function
3345 Test revset function
3346
3346
3347 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3347 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3348 2 current rev
3348 2 current rev
3349 1 not current rev
3349 1 not current rev
3350 0 not current rev
3350 0 not current rev
3351
3351
3352 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3352 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3353 2 match rev
3353 2 match rev
3354 1 match rev
3354 1 match rev
3355 0 not match rev
3355 0 not match rev
3356
3356
3357 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3357 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3358 2 Parents: 1
3358 2 Parents: 1
3359 1 Parents: 0
3359 1 Parents: 0
3360 0 Parents:
3360 0 Parents:
3361
3361
3362 $ cat >> .hg/hgrc <<EOF
3362 $ cat >> .hg/hgrc <<EOF
3363 > [revsetalias]
3363 > [revsetalias]
3364 > myparents(\$1) = parents(\$1)
3364 > myparents(\$1) = parents(\$1)
3365 > EOF
3365 > EOF
3366 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3366 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3367 2 Parents: 1
3367 2 Parents: 1
3368 1 Parents: 0
3368 1 Parents: 0
3369 0 Parents:
3369 0 Parents:
3370
3370
3371 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3371 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3372 Rev: 2
3372 Rev: 2
3373 Ancestor: 0
3373 Ancestor: 0
3374 Ancestor: 1
3374 Ancestor: 1
3375 Ancestor: 2
3375 Ancestor: 2
3376
3376
3377 Rev: 1
3377 Rev: 1
3378 Ancestor: 0
3378 Ancestor: 0
3379 Ancestor: 1
3379 Ancestor: 1
3380
3380
3381 Rev: 0
3381 Rev: 0
3382 Ancestor: 0
3382 Ancestor: 0
3383
3383
3384 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3384 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3385 2
3385 2
3386
3386
3387 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3387 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3388 2
3388 2
3389
3389
3390 a list template is evaluated for each item of revset/parents
3390 a list template is evaluated for each item of revset/parents
3391
3391
3392 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3392 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3393 2 p: 1:bcc7ff960b8e
3393 2 p: 1:bcc7ff960b8e
3394 1 p: 0:f7769ec2ab97
3394 1 p: 0:f7769ec2ab97
3395 0 p:
3395 0 p:
3396
3396
3397 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3397 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3398 2 p: 1:bcc7ff960b8e -1:000000000000
3398 2 p: 1:bcc7ff960b8e -1:000000000000
3399 1 p: 0:f7769ec2ab97 -1:000000000000
3399 1 p: 0:f7769ec2ab97 -1:000000000000
3400 0 p: -1:000000000000 -1:000000000000
3400 0 p: -1:000000000000 -1:000000000000
3401
3401
3402 therefore, 'revcache' should be recreated for each rev
3402 therefore, 'revcache' should be recreated for each rev
3403
3403
3404 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3404 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3405 2 aa b
3405 2 aa b
3406 p
3406 p
3407 1
3407 1
3408 p a
3408 p a
3409 0 a
3409 0 a
3410 p
3410 p
3411
3411
3412 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3412 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3413 2 aa b
3413 2 aa b
3414 p
3414 p
3415 1
3415 1
3416 p a
3416 p a
3417 0 a
3417 0 a
3418 p
3418 p
3419
3419
3420 a revset item must be evaluated as an integer revision, not an offset from tip
3420 a revset item must be evaluated as an integer revision, not an offset from tip
3421
3421
3422 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3422 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3423 -1:000000000000
3423 -1:000000000000
3424 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3424 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3425 -1:000000000000
3425 -1:000000000000
3426
3426
3427 Test active bookmark templating
3427 Test active bookmark templating
3428
3428
3429 $ hg book foo
3429 $ hg book foo
3430 $ hg book bar
3430 $ hg book bar
3431 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3431 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3432 2 bar* foo
3432 2 bar* foo
3433 1
3433 1
3434 0
3434 0
3435 $ hg log --template "{rev} {activebookmark}\n"
3435 $ hg log --template "{rev} {activebookmark}\n"
3436 2 bar
3436 2 bar
3437 1
3437 1
3438 0
3438 0
3439 $ hg bookmarks --inactive bar
3439 $ hg bookmarks --inactive bar
3440 $ hg log --template "{rev} {activebookmark}\n"
3440 $ hg log --template "{rev} {activebookmark}\n"
3441 2
3441 2
3442 1
3442 1
3443 0
3443 0
3444 $ hg book -r1 baz
3444 $ hg book -r1 baz
3445 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
3445 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
3446 2 bar foo
3446 2 bar foo
3447 1 baz
3447 1 baz
3448 0
3448 0
3449 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
3449 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
3450 2 t
3450 2 t
3451 1 f
3451 1 f
3452 0 f
3452 0 f
3453
3453
3454 Test namespaces dict
3454 Test namespaces dict
3455
3455
3456 $ hg log -T '{rev}{namespaces % " {namespace}={join(names, ",")}"}\n'
3456 $ hg log -T '{rev}{namespaces % " {namespace}={join(names, ",")}"}\n'
3457 2 bookmarks=bar,foo tags=tip branches=text.{rev}
3457 2 bookmarks=bar,foo tags=tip branches=text.{rev}
3458 1 bookmarks=baz tags= branches=text.{rev}
3458 1 bookmarks=baz tags= branches=text.{rev}
3459 0 bookmarks= tags= branches=default
3459 0 bookmarks= tags= branches=default
3460 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
3460 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
3461 bookmarks: bar foo
3461 bookmarks: bar foo
3462 tags: tip
3462 tags: tip
3463 branches: text.{rev}
3463 branches: text.{rev}
3464 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
3464 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
3465 bookmarks:
3465 bookmarks:
3466 bar
3466 bar
3467 foo
3467 foo
3468 tags:
3468 tags:
3469 tip
3469 tip
3470 branches:
3470 branches:
3471 text.{rev}
3471 text.{rev}
3472 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
3472 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
3473 bar
3473 bar
3474 foo
3474 foo
3475
3475
3476 Test stringify on sub expressions
3476 Test stringify on sub expressions
3477
3477
3478 $ cd ..
3478 $ cd ..
3479 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
3479 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
3480 fourth, second, third
3480 fourth, second, third
3481 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
3481 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
3482 abc
3482 abc
3483
3483
3484 Test splitlines
3484 Test splitlines
3485
3485
3486 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
3486 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
3487 @ foo Modify, add, remove, rename
3487 @ foo Modify, add, remove, rename
3488 |
3488 |
3489 o foo future
3489 o foo future
3490 |
3490 |
3491 o foo third
3491 o foo third
3492 |
3492 |
3493 o foo second
3493 o foo second
3494
3494
3495 o foo merge
3495 o foo merge
3496 |\
3496 |\
3497 | o foo new head
3497 | o foo new head
3498 | |
3498 | |
3499 o | foo new branch
3499 o | foo new branch
3500 |/
3500 |/
3501 o foo no user, no domain
3501 o foo no user, no domain
3502 |
3502 |
3503 o foo no person
3503 o foo no person
3504 |
3504 |
3505 o foo other 1
3505 o foo other 1
3506 | foo other 2
3506 | foo other 2
3507 | foo
3507 | foo
3508 | foo other 3
3508 | foo other 3
3509 o foo line 1
3509 o foo line 1
3510 foo line 2
3510 foo line 2
3511
3511
3512 Test startswith
3512 Test startswith
3513 $ hg log -Gv -R a --template "{startswith(desc)}"
3513 $ hg log -Gv -R a --template "{startswith(desc)}"
3514 hg: parse error: startswith expects two arguments
3514 hg: parse error: startswith expects two arguments
3515 [255]
3515 [255]
3516
3516
3517 $ hg log -Gv -R a --template "{startswith('line', desc)}"
3517 $ hg log -Gv -R a --template "{startswith('line', desc)}"
3518 @
3518 @
3519 |
3519 |
3520 o
3520 o
3521 |
3521 |
3522 o
3522 o
3523 |
3523 |
3524 o
3524 o
3525
3525
3526 o
3526 o
3527 |\
3527 |\
3528 | o
3528 | o
3529 | |
3529 | |
3530 o |
3530 o |
3531 |/
3531 |/
3532 o
3532 o
3533 |
3533 |
3534 o
3534 o
3535 |
3535 |
3536 o
3536 o
3537 |
3537 |
3538 o line 1
3538 o line 1
3539 line 2
3539 line 2
3540
3540
3541 Test bad template with better error message
3541 Test bad template with better error message
3542
3542
3543 $ hg log -Gv -R a --template '{desc|user()}'
3543 $ hg log -Gv -R a --template '{desc|user()}'
3544 hg: parse error: expected a symbol, got 'func'
3544 hg: parse error: expected a symbol, got 'func'
3545 [255]
3545 [255]
3546
3546
3547 Test word function (including index out of bounds graceful failure)
3547 Test word function (including index out of bounds graceful failure)
3548
3548
3549 $ hg log -Gv -R a --template "{word('1', desc)}"
3549 $ hg log -Gv -R a --template "{word('1', desc)}"
3550 @ add,
3550 @ add,
3551 |
3551 |
3552 o
3552 o
3553 |
3553 |
3554 o
3554 o
3555 |
3555 |
3556 o
3556 o
3557
3557
3558 o
3558 o
3559 |\
3559 |\
3560 | o head
3560 | o head
3561 | |
3561 | |
3562 o | branch
3562 o | branch
3563 |/
3563 |/
3564 o user,
3564 o user,
3565 |
3565 |
3566 o person
3566 o person
3567 |
3567 |
3568 o 1
3568 o 1
3569 |
3569 |
3570 o 1
3570 o 1
3571
3571
3572
3572
3573 Test word third parameter used as splitter
3573 Test word third parameter used as splitter
3574
3574
3575 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
3575 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
3576 @ M
3576 @ M
3577 |
3577 |
3578 o future
3578 o future
3579 |
3579 |
3580 o third
3580 o third
3581 |
3581 |
3582 o sec
3582 o sec
3583
3583
3584 o merge
3584 o merge
3585 |\
3585 |\
3586 | o new head
3586 | o new head
3587 | |
3587 | |
3588 o | new branch
3588 o | new branch
3589 |/
3589 |/
3590 o n
3590 o n
3591 |
3591 |
3592 o n
3592 o n
3593 |
3593 |
3594 o
3594 o
3595 |
3595 |
3596 o line 1
3596 o line 1
3597 line 2
3597 line 2
3598
3598
3599 Test word error messages for not enough and too many arguments
3599 Test word error messages for not enough and too many arguments
3600
3600
3601 $ hg log -Gv -R a --template "{word('0')}"
3601 $ hg log -Gv -R a --template "{word('0')}"
3602 hg: parse error: word expects two or three arguments, got 1
3602 hg: parse error: word expects two or three arguments, got 1
3603 [255]
3603 [255]
3604
3604
3605 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
3605 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
3606 hg: parse error: word expects two or three arguments, got 7
3606 hg: parse error: word expects two or three arguments, got 7
3607 [255]
3607 [255]
3608
3608
3609 Test word for integer literal
3609 Test word for integer literal
3610
3610
3611 $ hg log -R a --template "{word(2, desc)}\n" -r0
3611 $ hg log -R a --template "{word(2, desc)}\n" -r0
3612 line
3612 line
3613
3613
3614 Test word for invalid numbers
3614 Test word for invalid numbers
3615
3615
3616 $ hg log -Gv -R a --template "{word('a', desc)}"
3616 $ hg log -Gv -R a --template "{word('a', desc)}"
3617 hg: parse error: word expects an integer index
3617 hg: parse error: word expects an integer index
3618 [255]
3618 [255]
3619
3619
3620 Test word for out of range
3620 Test word for out of range
3621
3621
3622 $ hg log -R a --template "{word(10000, desc)}"
3622 $ hg log -R a --template "{word(10000, desc)}"
3623 $ hg log -R a --template "{word(-10000, desc)}"
3623 $ hg log -R a --template "{word(-10000, desc)}"
3624
3624
3625 Test indent and not adding to empty lines
3625 Test indent and not adding to empty lines
3626
3626
3627 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
3627 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
3628 -----
3628 -----
3629 > line 1
3629 > line 1
3630 >> line 2
3630 >> line 2
3631 -----
3631 -----
3632 > other 1
3632 > other 1
3633 >> other 2
3633 >> other 2
3634
3634
3635 >> other 3
3635 >> other 3
3636
3636
3637 Test with non-strings like dates
3637 Test with non-strings like dates
3638
3638
3639 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
3639 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
3640 1200000.00
3640 1200000.00
3641 1300000.00
3641 1300000.00
3642
3642
3643 Test broken string escapes:
3643 Test broken string escapes:
3644
3644
3645 $ hg log -T "bogus\\" -R a
3645 $ hg log -T "bogus\\" -R a
3646 hg: parse error: trailing \ in string
3646 hg: parse error: trailing \ in string
3647 [255]
3647 [255]
3648 $ hg log -T "\\xy" -R a
3648 $ hg log -T "\\xy" -R a
3649 hg: parse error: invalid \x escape
3649 hg: parse error: invalid \x escape
3650 [255]
3650 [255]
3651
3651
3652 json filter should escape HTML tags so that the output can be embedded in hgweb:
3652 json filter should escape HTML tags so that the output can be embedded in hgweb:
3653
3653
3654 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
3654 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
3655 "\u003cfoo@example.org\u003e"
3655 "\u003cfoo@example.org\u003e"
3656
3656
3657 Set up repository for non-ascii encoding tests:
3657 Set up repository for non-ascii encoding tests:
3658
3658
3659 $ hg init nonascii
3659 $ hg init nonascii
3660 $ cd nonascii
3660 $ cd nonascii
3661 $ python <<EOF
3661 $ python <<EOF
3662 > open('latin1', 'w').write('\xe9')
3662 > open('latin1', 'w').write('\xe9')
3663 > open('utf-8', 'w').write('\xc3\xa9')
3663 > open('utf-8', 'w').write('\xc3\xa9')
3664 > EOF
3664 > EOF
3665 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
3665 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
3666 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
3666 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
3667
3667
3668 json filter should try round-trip conversion to utf-8:
3668 json filter should try round-trip conversion to utf-8:
3669
3669
3670 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
3670 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
3671 "\u00e9"
3671 "\u00e9"
3672 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
3672 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
3673 "non-ascii branch: \u00e9"
3673 "non-ascii branch: \u00e9"
3674
3674
3675 json filter takes input as utf-8b:
3675 json filter takes input as utf-8b:
3676
3676
3677 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
3677 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
3678 "\u00e9"
3678 "\u00e9"
3679 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
3679 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
3680 "\udce9"
3680 "\udce9"
3681
3681
3682 utf8 filter:
3682 utf8 filter:
3683
3683
3684 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
3684 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
3685 round-trip: c3a9
3685 round-trip: c3a9
3686 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
3686 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
3687 decoded: c3a9
3687 decoded: c3a9
3688 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
3688 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
3689 abort: decoding near * (glob)
3689 abort: decoding near * (glob)
3690 [255]
3690 [255]
3691 $ hg log -T "invalid type: {rev|utf8}\n" -r0
3691 $ hg log -T "invalid type: {rev|utf8}\n" -r0
3692 abort: template filter 'utf8' is not compatible with keyword 'rev'
3692 abort: template filter 'utf8' is not compatible with keyword 'rev'
3693 [255]
3693 [255]
3694
3694
3695 $ cd ..
3695 $ cd ..
3696
3697 Test that template function in extension is registered as expected
3698
3699 $ cd a
3700
3701 $ cat <<EOF > $TESTTMP/customfunc.py
3702 > from mercurial import registrar
3703 >
3704 > templatefunc = registrar.templatefunc()
3705 >
3706 > @templatefunc('custom()')
3707 > def custom(context, mapping, args):
3708 > return 'custom'
3709 > EOF
3710 $ cat <<EOF > .hg/hgrc
3711 > [extensions]
3712 > customfunc = $TESTTMP/customfunc.py
3713 > EOF
3714
3715 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
3716 custom
3717
3718 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now