##// END OF EJS Templates
templater: abstract min/max away...
Yuya Nishihara -
r38284:41ae9b3c default
parent child Browse files
Show More
@@ -1,787 +1,793 b''
1 # hgweb/webutil.py - utility library for the web interface.
1 # hgweb/webutil.py - utility library for the web interface.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import copy
11 import copy
12 import difflib
12 import difflib
13 import os
13 import os
14 import re
14 import re
15
15
16 from ..i18n import _
16 from ..i18n import _
17 from ..node import hex, nullid, short
17 from ..node import hex, nullid, short
18
18
19 from .common import (
19 from .common import (
20 ErrorResponse,
20 ErrorResponse,
21 HTTP_BAD_REQUEST,
21 HTTP_BAD_REQUEST,
22 HTTP_NOT_FOUND,
22 HTTP_NOT_FOUND,
23 paritygen,
23 paritygen,
24 )
24 )
25
25
26 from .. import (
26 from .. import (
27 context,
27 context,
28 error,
28 error,
29 match,
29 match,
30 mdiff,
30 mdiff,
31 obsutil,
31 obsutil,
32 patch,
32 patch,
33 pathutil,
33 pathutil,
34 pycompat,
34 pycompat,
35 scmutil,
35 scmutil,
36 templatefilters,
36 templatefilters,
37 templatekw,
37 templatekw,
38 templateutil,
38 templateutil,
39 ui as uimod,
39 ui as uimod,
40 util,
40 util,
41 )
41 )
42
42
43 from ..utils import (
43 from ..utils import (
44 stringutil,
44 stringutil,
45 )
45 )
46
46
47 archivespecs = util.sortdict((
47 archivespecs = util.sortdict((
48 ('zip', ('application/zip', 'zip', '.zip', None)),
48 ('zip', ('application/zip', 'zip', '.zip', None)),
49 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
49 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
50 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
50 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
51 ))
51 ))
52
52
53 def archivelist(ui, nodeid, url=None):
53 def archivelist(ui, nodeid, url=None):
54 allowed = ui.configlist('web', 'allow-archive', untrusted=True)
54 allowed = ui.configlist('web', 'allow-archive', untrusted=True)
55 archives = []
55 archives = []
56
56
57 for typ, spec in archivespecs.iteritems():
57 for typ, spec in archivespecs.iteritems():
58 if typ in allowed or ui.configbool('web', 'allow' + typ,
58 if typ in allowed or ui.configbool('web', 'allow' + typ,
59 untrusted=True):
59 untrusted=True):
60 archives.append({
60 archives.append({
61 'type': typ,
61 'type': typ,
62 'extension': spec[2],
62 'extension': spec[2],
63 'node': nodeid,
63 'node': nodeid,
64 'url': url,
64 'url': url,
65 })
65 })
66
66
67 return templateutil.mappinglist(archives)
67 return templateutil.mappinglist(archives)
68
68
69 def up(p):
69 def up(p):
70 if p[0:1] != "/":
70 if p[0:1] != "/":
71 p = "/" + p
71 p = "/" + p
72 if p[-1:] == "/":
72 if p[-1:] == "/":
73 p = p[:-1]
73 p = p[:-1]
74 up = os.path.dirname(p)
74 up = os.path.dirname(p)
75 if up == "/":
75 if up == "/":
76 return "/"
76 return "/"
77 return up + "/"
77 return up + "/"
78
78
79 def _navseq(step, firststep=None):
79 def _navseq(step, firststep=None):
80 if firststep:
80 if firststep:
81 yield firststep
81 yield firststep
82 if firststep >= 20 and firststep <= 40:
82 if firststep >= 20 and firststep <= 40:
83 firststep = 50
83 firststep = 50
84 yield firststep
84 yield firststep
85 assert step > 0
85 assert step > 0
86 assert firststep > 0
86 assert firststep > 0
87 while step <= firststep:
87 while step <= firststep:
88 step *= 10
88 step *= 10
89 while True:
89 while True:
90 yield 1 * step
90 yield 1 * step
91 yield 3 * step
91 yield 3 * step
92 step *= 10
92 step *= 10
93
93
94 class revnav(object):
94 class revnav(object):
95
95
96 def __init__(self, repo):
96 def __init__(self, repo):
97 """Navigation generation object
97 """Navigation generation object
98
98
99 :repo: repo object we generate nav for
99 :repo: repo object we generate nav for
100 """
100 """
101 # used for hex generation
101 # used for hex generation
102 self._revlog = repo.changelog
102 self._revlog = repo.changelog
103
103
104 def __nonzero__(self):
104 def __nonzero__(self):
105 """return True if any revision to navigate over"""
105 """return True if any revision to navigate over"""
106 return self._first() is not None
106 return self._first() is not None
107
107
108 __bool__ = __nonzero__
108 __bool__ = __nonzero__
109
109
110 def _first(self):
110 def _first(self):
111 """return the minimum non-filtered changeset or None"""
111 """return the minimum non-filtered changeset or None"""
112 try:
112 try:
113 return next(iter(self._revlog))
113 return next(iter(self._revlog))
114 except StopIteration:
114 except StopIteration:
115 return None
115 return None
116
116
117 def hex(self, rev):
117 def hex(self, rev):
118 return hex(self._revlog.node(rev))
118 return hex(self._revlog.node(rev))
119
119
120 def gen(self, pos, pagelen, limit):
120 def gen(self, pos, pagelen, limit):
121 """computes label and revision id for navigation link
121 """computes label and revision id for navigation link
122
122
123 :pos: is the revision relative to which we generate navigation.
123 :pos: is the revision relative to which we generate navigation.
124 :pagelen: the size of each navigation page
124 :pagelen: the size of each navigation page
125 :limit: how far shall we link
125 :limit: how far shall we link
126
126
127 The return is:
127 The return is:
128 - a single element mappinglist
128 - a single element mappinglist
129 - containing a dictionary with a `before` and `after` key
129 - containing a dictionary with a `before` and `after` key
130 - values are dictionaries with `label` and `node` keys
130 - values are dictionaries with `label` and `node` keys
131 """
131 """
132 if not self:
132 if not self:
133 # empty repo
133 # empty repo
134 return templateutil.mappinglist([
134 return templateutil.mappinglist([
135 {'before': templateutil.mappinglist([]),
135 {'before': templateutil.mappinglist([]),
136 'after': templateutil.mappinglist([])},
136 'after': templateutil.mappinglist([])},
137 ])
137 ])
138
138
139 targets = []
139 targets = []
140 for f in _navseq(1, pagelen):
140 for f in _navseq(1, pagelen):
141 if f > limit:
141 if f > limit:
142 break
142 break
143 targets.append(pos + f)
143 targets.append(pos + f)
144 targets.append(pos - f)
144 targets.append(pos - f)
145 targets.sort()
145 targets.sort()
146
146
147 first = self._first()
147 first = self._first()
148 navbefore = [{'label': '(%i)' % first, 'node': self.hex(first)}]
148 navbefore = [{'label': '(%i)' % first, 'node': self.hex(first)}]
149 navafter = []
149 navafter = []
150 for rev in targets:
150 for rev in targets:
151 if rev not in self._revlog:
151 if rev not in self._revlog:
152 continue
152 continue
153 if pos < rev < limit:
153 if pos < rev < limit:
154 navafter.append({'label': '+%d' % abs(rev - pos),
154 navafter.append({'label': '+%d' % abs(rev - pos),
155 'node': self.hex(rev)})
155 'node': self.hex(rev)})
156 if 0 < rev < pos:
156 if 0 < rev < pos:
157 navbefore.append({'label': '-%d' % abs(rev - pos),
157 navbefore.append({'label': '-%d' % abs(rev - pos),
158 'node': self.hex(rev)})
158 'node': self.hex(rev)})
159
159
160 navafter.append({'label': 'tip', 'node': 'tip'})
160 navafter.append({'label': 'tip', 'node': 'tip'})
161
161
162 # TODO: maybe this can be a scalar object supporting tomap()
162 # TODO: maybe this can be a scalar object supporting tomap()
163 return templateutil.mappinglist([
163 return templateutil.mappinglist([
164 {'before': templateutil.mappinglist(navbefore),
164 {'before': templateutil.mappinglist(navbefore),
165 'after': templateutil.mappinglist(navafter)},
165 'after': templateutil.mappinglist(navafter)},
166 ])
166 ])
167
167
168 class filerevnav(revnav):
168 class filerevnav(revnav):
169
169
170 def __init__(self, repo, path):
170 def __init__(self, repo, path):
171 """Navigation generation object
171 """Navigation generation object
172
172
173 :repo: repo object we generate nav for
173 :repo: repo object we generate nav for
174 :path: path of the file we generate nav for
174 :path: path of the file we generate nav for
175 """
175 """
176 # used for iteration
176 # used for iteration
177 self._changelog = repo.unfiltered().changelog
177 self._changelog = repo.unfiltered().changelog
178 # used for hex generation
178 # used for hex generation
179 self._revlog = repo.file(path)
179 self._revlog = repo.file(path)
180
180
181 def hex(self, rev):
181 def hex(self, rev):
182 return hex(self._changelog.node(self._revlog.linkrev(rev)))
182 return hex(self._changelog.node(self._revlog.linkrev(rev)))
183
183
184 # TODO: maybe this can be a wrapper class for changectx/filectx list, which
184 # TODO: maybe this can be a wrapper class for changectx/filectx list, which
185 # yields {'ctx': ctx}
185 # yields {'ctx': ctx}
186 def _ctxsgen(context, ctxs):
186 def _ctxsgen(context, ctxs):
187 for s in ctxs:
187 for s in ctxs:
188 d = {
188 d = {
189 'node': s.hex(),
189 'node': s.hex(),
190 'rev': s.rev(),
190 'rev': s.rev(),
191 'user': s.user(),
191 'user': s.user(),
192 'date': s.date(),
192 'date': s.date(),
193 'description': s.description(),
193 'description': s.description(),
194 'branch': s.branch(),
194 'branch': s.branch(),
195 }
195 }
196 if util.safehasattr(s, 'path'):
196 if util.safehasattr(s, 'path'):
197 d['file'] = s.path()
197 d['file'] = s.path()
198 yield d
198 yield d
199
199
200 def _siblings(siblings=None, hiderev=None):
200 def _siblings(siblings=None, hiderev=None):
201 if siblings is None:
201 if siblings is None:
202 siblings = []
202 siblings = []
203 siblings = [s for s in siblings if s.node() != nullid]
203 siblings = [s for s in siblings if s.node() != nullid]
204 if len(siblings) == 1 and siblings[0].rev() == hiderev:
204 if len(siblings) == 1 and siblings[0].rev() == hiderev:
205 siblings = []
205 siblings = []
206 return templateutil.mappinggenerator(_ctxsgen, args=(siblings,))
206 return templateutil.mappinggenerator(_ctxsgen, args=(siblings,))
207
207
208 def difffeatureopts(req, ui, section):
208 def difffeatureopts(req, ui, section):
209 diffopts = patch.difffeatureopts(ui, untrusted=True,
209 diffopts = patch.difffeatureopts(ui, untrusted=True,
210 section=section, whitespace=True)
210 section=section, whitespace=True)
211
211
212 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
212 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
213 v = req.qsparams.get(k)
213 v = req.qsparams.get(k)
214 if v is not None:
214 if v is not None:
215 v = stringutil.parsebool(v)
215 v = stringutil.parsebool(v)
216 setattr(diffopts, k, v if v is not None else True)
216 setattr(diffopts, k, v if v is not None else True)
217
217
218 return diffopts
218 return diffopts
219
219
220 def annotate(req, fctx, ui):
220 def annotate(req, fctx, ui):
221 diffopts = difffeatureopts(req, ui, 'annotate')
221 diffopts = difffeatureopts(req, ui, 'annotate')
222 return fctx.annotate(follow=True, diffopts=diffopts)
222 return fctx.annotate(follow=True, diffopts=diffopts)
223
223
224 def parents(ctx, hide=None):
224 def parents(ctx, hide=None):
225 if isinstance(ctx, context.basefilectx):
225 if isinstance(ctx, context.basefilectx):
226 introrev = ctx.introrev()
226 introrev = ctx.introrev()
227 if ctx.changectx().rev() != introrev:
227 if ctx.changectx().rev() != introrev:
228 return _siblings([ctx.repo()[introrev]], hide)
228 return _siblings([ctx.repo()[introrev]], hide)
229 return _siblings(ctx.parents(), hide)
229 return _siblings(ctx.parents(), hide)
230
230
231 def children(ctx, hide=None):
231 def children(ctx, hide=None):
232 return _siblings(ctx.children(), hide)
232 return _siblings(ctx.children(), hide)
233
233
234 def renamelink(fctx):
234 def renamelink(fctx):
235 r = fctx.renamed()
235 r = fctx.renamed()
236 if r:
236 if r:
237 return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}])
237 return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}])
238 return templateutil.mappinglist([])
238 return templateutil.mappinglist([])
239
239
240 def nodetagsdict(repo, node):
240 def nodetagsdict(repo, node):
241 return templateutil.hybridlist(repo.nodetags(node), name='name')
241 return templateutil.hybridlist(repo.nodetags(node), name='name')
242
242
243 def nodebookmarksdict(repo, node):
243 def nodebookmarksdict(repo, node):
244 return templateutil.hybridlist(repo.nodebookmarks(node), name='name')
244 return templateutil.hybridlist(repo.nodebookmarks(node), name='name')
245
245
246 def nodebranchdict(repo, ctx):
246 def nodebranchdict(repo, ctx):
247 branches = []
247 branches = []
248 branch = ctx.branch()
248 branch = ctx.branch()
249 # If this is an empty repo, ctx.node() == nullid,
249 # If this is an empty repo, ctx.node() == nullid,
250 # ctx.branch() == 'default'.
250 # ctx.branch() == 'default'.
251 try:
251 try:
252 branchnode = repo.branchtip(branch)
252 branchnode = repo.branchtip(branch)
253 except error.RepoLookupError:
253 except error.RepoLookupError:
254 branchnode = None
254 branchnode = None
255 if branchnode == ctx.node():
255 if branchnode == ctx.node():
256 branches.append(branch)
256 branches.append(branch)
257 return templateutil.hybridlist(branches, name='name')
257 return templateutil.hybridlist(branches, name='name')
258
258
259 def nodeinbranch(repo, ctx):
259 def nodeinbranch(repo, ctx):
260 branches = []
260 branches = []
261 branch = ctx.branch()
261 branch = ctx.branch()
262 try:
262 try:
263 branchnode = repo.branchtip(branch)
263 branchnode = repo.branchtip(branch)
264 except error.RepoLookupError:
264 except error.RepoLookupError:
265 branchnode = None
265 branchnode = None
266 if branch != 'default' and branchnode != ctx.node():
266 if branch != 'default' and branchnode != ctx.node():
267 branches.append(branch)
267 branches.append(branch)
268 return templateutil.hybridlist(branches, name='name')
268 return templateutil.hybridlist(branches, name='name')
269
269
270 def nodebranchnodefault(ctx):
270 def nodebranchnodefault(ctx):
271 branches = []
271 branches = []
272 branch = ctx.branch()
272 branch = ctx.branch()
273 if branch != 'default':
273 if branch != 'default':
274 branches.append(branch)
274 branches.append(branch)
275 return templateutil.hybridlist(branches, name='name')
275 return templateutil.hybridlist(branches, name='name')
276
276
277 def _nodenamesgen(context, f, node, name):
277 def _nodenamesgen(context, f, node, name):
278 for t in f(node):
278 for t in f(node):
279 yield {name: t}
279 yield {name: t}
280
280
281 def showtag(repo, t1, node=nullid):
281 def showtag(repo, t1, node=nullid):
282 args = (repo.nodetags, node, 'tag')
282 args = (repo.nodetags, node, 'tag')
283 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
283 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
284
284
285 def showbookmark(repo, t1, node=nullid):
285 def showbookmark(repo, t1, node=nullid):
286 args = (repo.nodebookmarks, node, 'bookmark')
286 args = (repo.nodebookmarks, node, 'bookmark')
287 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
287 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
288
288
289 def branchentries(repo, stripecount, limit=0):
289 def branchentries(repo, stripecount, limit=0):
290 tips = []
290 tips = []
291 heads = repo.heads()
291 heads = repo.heads()
292 parity = paritygen(stripecount)
292 parity = paritygen(stripecount)
293 sortkey = lambda item: (not item[1], item[0].rev())
293 sortkey = lambda item: (not item[1], item[0].rev())
294
294
295 def entries(context):
295 def entries(context):
296 count = 0
296 count = 0
297 if not tips:
297 if not tips:
298 for tag, hs, tip, closed in repo.branchmap().iterbranches():
298 for tag, hs, tip, closed in repo.branchmap().iterbranches():
299 tips.append((repo[tip], closed))
299 tips.append((repo[tip], closed))
300 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
300 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
301 if limit > 0 and count >= limit:
301 if limit > 0 and count >= limit:
302 return
302 return
303 count += 1
303 count += 1
304 if closed:
304 if closed:
305 status = 'closed'
305 status = 'closed'
306 elif ctx.node() not in heads:
306 elif ctx.node() not in heads:
307 status = 'inactive'
307 status = 'inactive'
308 else:
308 else:
309 status = 'open'
309 status = 'open'
310 yield {
310 yield {
311 'parity': next(parity),
311 'parity': next(parity),
312 'branch': ctx.branch(),
312 'branch': ctx.branch(),
313 'status': status,
313 'status': status,
314 'node': ctx.hex(),
314 'node': ctx.hex(),
315 'date': ctx.date()
315 'date': ctx.date()
316 }
316 }
317
317
318 return templateutil.mappinggenerator(entries)
318 return templateutil.mappinggenerator(entries)
319
319
320 def cleanpath(repo, path):
320 def cleanpath(repo, path):
321 path = path.lstrip('/')
321 path = path.lstrip('/')
322 return pathutil.canonpath(repo.root, '', path)
322 return pathutil.canonpath(repo.root, '', path)
323
323
324 def changectx(repo, req):
324 def changectx(repo, req):
325 changeid = "tip"
325 changeid = "tip"
326 if 'node' in req.qsparams:
326 if 'node' in req.qsparams:
327 changeid = req.qsparams['node']
327 changeid = req.qsparams['node']
328 ipos = changeid.find(':')
328 ipos = changeid.find(':')
329 if ipos != -1:
329 if ipos != -1:
330 changeid = changeid[(ipos + 1):]
330 changeid = changeid[(ipos + 1):]
331
331
332 return scmutil.revsymbol(repo, changeid)
332 return scmutil.revsymbol(repo, changeid)
333
333
334 def basechangectx(repo, req):
334 def basechangectx(repo, req):
335 if 'node' in req.qsparams:
335 if 'node' in req.qsparams:
336 changeid = req.qsparams['node']
336 changeid = req.qsparams['node']
337 ipos = changeid.find(':')
337 ipos = changeid.find(':')
338 if ipos != -1:
338 if ipos != -1:
339 changeid = changeid[:ipos]
339 changeid = changeid[:ipos]
340 return scmutil.revsymbol(repo, changeid)
340 return scmutil.revsymbol(repo, changeid)
341
341
342 return None
342 return None
343
343
344 def filectx(repo, req):
344 def filectx(repo, req):
345 if 'file' not in req.qsparams:
345 if 'file' not in req.qsparams:
346 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
346 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
347 path = cleanpath(repo, req.qsparams['file'])
347 path = cleanpath(repo, req.qsparams['file'])
348 if 'node' in req.qsparams:
348 if 'node' in req.qsparams:
349 changeid = req.qsparams['node']
349 changeid = req.qsparams['node']
350 elif 'filenode' in req.qsparams:
350 elif 'filenode' in req.qsparams:
351 changeid = req.qsparams['filenode']
351 changeid = req.qsparams['filenode']
352 else:
352 else:
353 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
353 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
354 try:
354 try:
355 fctx = scmutil.revsymbol(repo, changeid)[path]
355 fctx = scmutil.revsymbol(repo, changeid)[path]
356 except error.RepoError:
356 except error.RepoError:
357 fctx = repo.filectx(path, fileid=changeid)
357 fctx = repo.filectx(path, fileid=changeid)
358
358
359 return fctx
359 return fctx
360
360
361 def linerange(req):
361 def linerange(req):
362 linerange = req.qsparams.getall('linerange')
362 linerange = req.qsparams.getall('linerange')
363 if not linerange:
363 if not linerange:
364 return None
364 return None
365 if len(linerange) > 1:
365 if len(linerange) > 1:
366 raise ErrorResponse(HTTP_BAD_REQUEST,
366 raise ErrorResponse(HTTP_BAD_REQUEST,
367 'redundant linerange parameter')
367 'redundant linerange parameter')
368 try:
368 try:
369 fromline, toline = map(int, linerange[0].split(':', 1))
369 fromline, toline = map(int, linerange[0].split(':', 1))
370 except ValueError:
370 except ValueError:
371 raise ErrorResponse(HTTP_BAD_REQUEST,
371 raise ErrorResponse(HTTP_BAD_REQUEST,
372 'invalid linerange parameter')
372 'invalid linerange parameter')
373 try:
373 try:
374 return util.processlinerange(fromline, toline)
374 return util.processlinerange(fromline, toline)
375 except error.ParseError as exc:
375 except error.ParseError as exc:
376 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc))
376 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc))
377
377
378 def formatlinerange(fromline, toline):
378 def formatlinerange(fromline, toline):
379 return '%d:%d' % (fromline + 1, toline)
379 return '%d:%d' % (fromline + 1, toline)
380
380
381 def _succsandmarkersgen(context, mapping):
381 def _succsandmarkersgen(context, mapping):
382 repo = context.resource(mapping, 'repo')
382 repo = context.resource(mapping, 'repo')
383 itemmappings = templatekw.showsuccsandmarkers(context, mapping)
383 itemmappings = templatekw.showsuccsandmarkers(context, mapping)
384 for item in itemmappings.tovalue(context, mapping):
384 for item in itemmappings.tovalue(context, mapping):
385 item['successors'] = _siblings(repo[successor]
385 item['successors'] = _siblings(repo[successor]
386 for successor in item['successors'])
386 for successor in item['successors'])
387 yield item
387 yield item
388
388
389 def succsandmarkers(context, mapping):
389 def succsandmarkers(context, mapping):
390 return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,))
390 return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,))
391
391
392 # teach templater succsandmarkers is switched to (context, mapping) API
392 # teach templater succsandmarkers is switched to (context, mapping) API
393 succsandmarkers._requires = {'repo', 'ctx'}
393 succsandmarkers._requires = {'repo', 'ctx'}
394
394
395 def _whyunstablegen(context, mapping):
395 def _whyunstablegen(context, mapping):
396 repo = context.resource(mapping, 'repo')
396 repo = context.resource(mapping, 'repo')
397 ctx = context.resource(mapping, 'ctx')
397 ctx = context.resource(mapping, 'ctx')
398
398
399 entries = obsutil.whyunstable(repo, ctx)
399 entries = obsutil.whyunstable(repo, ctx)
400 for entry in entries:
400 for entry in entries:
401 if entry.get('divergentnodes'):
401 if entry.get('divergentnodes'):
402 entry['divergentnodes'] = _siblings(entry['divergentnodes'])
402 entry['divergentnodes'] = _siblings(entry['divergentnodes'])
403 yield entry
403 yield entry
404
404
405 def whyunstable(context, mapping):
405 def whyunstable(context, mapping):
406 return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,))
406 return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,))
407
407
408 whyunstable._requires = {'repo', 'ctx'}
408 whyunstable._requires = {'repo', 'ctx'}
409
409
410 def commonentry(repo, ctx):
410 def commonentry(repo, ctx):
411 node = ctx.node()
411 node = ctx.node()
412 return {
412 return {
413 # TODO: perhaps ctx.changectx() should be assigned if ctx is a
413 # TODO: perhaps ctx.changectx() should be assigned if ctx is a
414 # filectx, but I'm not pretty sure if that would always work because
414 # filectx, but I'm not pretty sure if that would always work because
415 # fctx.parents() != fctx.changectx.parents() for example.
415 # fctx.parents() != fctx.changectx.parents() for example.
416 'ctx': ctx,
416 'ctx': ctx,
417 'rev': ctx.rev(),
417 'rev': ctx.rev(),
418 'node': hex(node),
418 'node': hex(node),
419 'author': ctx.user(),
419 'author': ctx.user(),
420 'desc': ctx.description(),
420 'desc': ctx.description(),
421 'date': ctx.date(),
421 'date': ctx.date(),
422 'extra': ctx.extra(),
422 'extra': ctx.extra(),
423 'phase': ctx.phasestr(),
423 'phase': ctx.phasestr(),
424 'obsolete': ctx.obsolete(),
424 'obsolete': ctx.obsolete(),
425 'succsandmarkers': succsandmarkers,
425 'succsandmarkers': succsandmarkers,
426 'instabilities': templateutil.hybridlist(ctx.instabilities(),
426 'instabilities': templateutil.hybridlist(ctx.instabilities(),
427 name='instability'),
427 name='instability'),
428 'whyunstable': whyunstable,
428 'whyunstable': whyunstable,
429 'branch': nodebranchnodefault(ctx),
429 'branch': nodebranchnodefault(ctx),
430 'inbranch': nodeinbranch(repo, ctx),
430 'inbranch': nodeinbranch(repo, ctx),
431 'branches': nodebranchdict(repo, ctx),
431 'branches': nodebranchdict(repo, ctx),
432 'tags': nodetagsdict(repo, node),
432 'tags': nodetagsdict(repo, node),
433 'bookmarks': nodebookmarksdict(repo, node),
433 'bookmarks': nodebookmarksdict(repo, node),
434 'parent': lambda **x: parents(ctx),
434 'parent': lambda **x: parents(ctx),
435 'child': lambda **x: children(ctx),
435 'child': lambda **x: children(ctx),
436 }
436 }
437
437
438 def changelistentry(web, ctx):
438 def changelistentry(web, ctx):
439 '''Obtain a dictionary to be used for entries in a changelist.
439 '''Obtain a dictionary to be used for entries in a changelist.
440
440
441 This function is called when producing items for the "entries" list passed
441 This function is called when producing items for the "entries" list passed
442 to the "shortlog" and "changelog" templates.
442 to the "shortlog" and "changelog" templates.
443 '''
443 '''
444 repo = web.repo
444 repo = web.repo
445 rev = ctx.rev()
445 rev = ctx.rev()
446 n = ctx.node()
446 n = ctx.node()
447 showtags = showtag(repo, 'changelogtag', n)
447 showtags = showtag(repo, 'changelogtag', n)
448 files = listfilediffs(ctx.files(), n, web.maxfiles)
448 files = listfilediffs(ctx.files(), n, web.maxfiles)
449
449
450 entry = commonentry(repo, ctx)
450 entry = commonentry(repo, ctx)
451 entry.update(
451 entry.update(
452 allparents=lambda **x: parents(ctx),
452 allparents=lambda **x: parents(ctx),
453 parent=lambda **x: parents(ctx, rev - 1),
453 parent=lambda **x: parents(ctx, rev - 1),
454 child=lambda **x: children(ctx, rev + 1),
454 child=lambda **x: children(ctx, rev + 1),
455 changelogtag=showtags,
455 changelogtag=showtags,
456 files=files,
456 files=files,
457 )
457 )
458 return entry
458 return entry
459
459
460 def changelistentries(web, revs, maxcount, parityfn):
460 def changelistentries(web, revs, maxcount, parityfn):
461 """Emit up to N records for an iterable of revisions."""
461 """Emit up to N records for an iterable of revisions."""
462 repo = web.repo
462 repo = web.repo
463
463
464 count = 0
464 count = 0
465 for rev in revs:
465 for rev in revs:
466 if count >= maxcount:
466 if count >= maxcount:
467 break
467 break
468
468
469 count += 1
469 count += 1
470
470
471 entry = changelistentry(web, repo[rev])
471 entry = changelistentry(web, repo[rev])
472 entry['parity'] = next(parityfn)
472 entry['parity'] = next(parityfn)
473
473
474 yield entry
474 yield entry
475
475
476 def symrevorshortnode(req, ctx):
476 def symrevorshortnode(req, ctx):
477 if 'node' in req.qsparams:
477 if 'node' in req.qsparams:
478 return templatefilters.revescape(req.qsparams['node'])
478 return templatefilters.revescape(req.qsparams['node'])
479 else:
479 else:
480 return short(ctx.node())
480 return short(ctx.node())
481
481
482 def _listfilesgen(context, ctx, stripecount):
482 def _listfilesgen(context, ctx, stripecount):
483 parity = paritygen(stripecount)
483 parity = paritygen(stripecount)
484 for blockno, f in enumerate(ctx.files()):
484 for blockno, f in enumerate(ctx.files()):
485 template = 'filenodelink' if f in ctx else 'filenolink'
485 template = 'filenodelink' if f in ctx else 'filenolink'
486 yield context.process(template, {
486 yield context.process(template, {
487 'node': ctx.hex(),
487 'node': ctx.hex(),
488 'file': f,
488 'file': f,
489 'blockno': blockno + 1,
489 'blockno': blockno + 1,
490 'parity': next(parity),
490 'parity': next(parity),
491 })
491 })
492
492
493 def changesetentry(web, ctx):
493 def changesetentry(web, ctx):
494 '''Obtain a dictionary to be used to render the "changeset" template.'''
494 '''Obtain a dictionary to be used to render the "changeset" template.'''
495
495
496 showtags = showtag(web.repo, 'changesettag', ctx.node())
496 showtags = showtag(web.repo, 'changesettag', ctx.node())
497 showbookmarks = showbookmark(web.repo, 'changesetbookmark', ctx.node())
497 showbookmarks = showbookmark(web.repo, 'changesetbookmark', ctx.node())
498 showbranch = nodebranchnodefault(ctx)
498 showbranch = nodebranchnodefault(ctx)
499
499
500 basectx = basechangectx(web.repo, web.req)
500 basectx = basechangectx(web.repo, web.req)
501 if basectx is None:
501 if basectx is None:
502 basectx = ctx.p1()
502 basectx = ctx.p1()
503
503
504 style = web.config('web', 'style')
504 style = web.config('web', 'style')
505 if 'style' in web.req.qsparams:
505 if 'style' in web.req.qsparams:
506 style = web.req.qsparams['style']
506 style = web.req.qsparams['style']
507
507
508 diff = diffs(web, ctx, basectx, None, style)
508 diff = diffs(web, ctx, basectx, None, style)
509
509
510 parity = paritygen(web.stripecount)
510 parity = paritygen(web.stripecount)
511 diffstatsgen = diffstatgen(ctx, basectx)
511 diffstatsgen = diffstatgen(ctx, basectx)
512 diffstats = diffstat(ctx, diffstatsgen, parity)
512 diffstats = diffstat(ctx, diffstatsgen, parity)
513
513
514 return dict(
514 return dict(
515 diff=diff,
515 diff=diff,
516 symrev=symrevorshortnode(web.req, ctx),
516 symrev=symrevorshortnode(web.req, ctx),
517 basenode=basectx.hex(),
517 basenode=basectx.hex(),
518 changesettag=showtags,
518 changesettag=showtags,
519 changesetbookmark=showbookmarks,
519 changesetbookmark=showbookmarks,
520 changesetbranch=showbranch,
520 changesetbranch=showbranch,
521 files=templateutil.mappedgenerator(_listfilesgen,
521 files=templateutil.mappedgenerator(_listfilesgen,
522 args=(ctx, web.stripecount)),
522 args=(ctx, web.stripecount)),
523 diffsummary=lambda **x: diffsummary(diffstatsgen),
523 diffsummary=lambda **x: diffsummary(diffstatsgen),
524 diffstat=diffstats,
524 diffstat=diffstats,
525 archives=web.archivelist(ctx.hex()),
525 archives=web.archivelist(ctx.hex()),
526 **pycompat.strkwargs(commonentry(web.repo, ctx)))
526 **pycompat.strkwargs(commonentry(web.repo, ctx)))
527
527
528 def _listfilediffsgen(context, files, node, max):
528 def _listfilediffsgen(context, files, node, max):
529 for f in files[:max]:
529 for f in files[:max]:
530 yield context.process('filedifflink', {'node': hex(node), 'file': f})
530 yield context.process('filedifflink', {'node': hex(node), 'file': f})
531 if len(files) > max:
531 if len(files) > max:
532 yield context.process('fileellipses', {})
532 yield context.process('fileellipses', {})
533
533
534 def listfilediffs(files, node, max):
534 def listfilediffs(files, node, max):
535 return templateutil.mappedgenerator(_listfilediffsgen,
535 return templateutil.mappedgenerator(_listfilediffsgen,
536 args=(files, node, max))
536 args=(files, node, max))
537
537
538 def _prettyprintdifflines(context, lines, blockno, lineidprefix):
538 def _prettyprintdifflines(context, lines, blockno, lineidprefix):
539 for lineno, l in enumerate(lines, 1):
539 for lineno, l in enumerate(lines, 1):
540 difflineno = "%d.%d" % (blockno, lineno)
540 difflineno = "%d.%d" % (blockno, lineno)
541 if l.startswith('+'):
541 if l.startswith('+'):
542 ltype = "difflineplus"
542 ltype = "difflineplus"
543 elif l.startswith('-'):
543 elif l.startswith('-'):
544 ltype = "difflineminus"
544 ltype = "difflineminus"
545 elif l.startswith('@'):
545 elif l.startswith('@'):
546 ltype = "difflineat"
546 ltype = "difflineat"
547 else:
547 else:
548 ltype = "diffline"
548 ltype = "diffline"
549 yield context.process(ltype, {
549 yield context.process(ltype, {
550 'line': l,
550 'line': l,
551 'lineno': lineno,
551 'lineno': lineno,
552 'lineid': lineidprefix + "l%s" % difflineno,
552 'lineid': lineidprefix + "l%s" % difflineno,
553 'linenumber': "% 8s" % difflineno,
553 'linenumber': "% 8s" % difflineno,
554 })
554 })
555
555
556 def _diffsgen(context, repo, ctx, basectx, files, style, stripecount,
556 def _diffsgen(context, repo, ctx, basectx, files, style, stripecount,
557 linerange, lineidprefix):
557 linerange, lineidprefix):
558 if files:
558 if files:
559 m = match.exact(repo.root, repo.getcwd(), files)
559 m = match.exact(repo.root, repo.getcwd(), files)
560 else:
560 else:
561 m = match.always(repo.root, repo.getcwd())
561 m = match.always(repo.root, repo.getcwd())
562
562
563 diffopts = patch.diffopts(repo.ui, untrusted=True)
563 diffopts = patch.diffopts(repo.ui, untrusted=True)
564 node1 = basectx.node()
564 node1 = basectx.node()
565 node2 = ctx.node()
565 node2 = ctx.node()
566 parity = paritygen(stripecount)
566 parity = paritygen(stripecount)
567
567
568 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
568 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
569 for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
569 for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
570 if style != 'raw':
570 if style != 'raw':
571 header = header[1:]
571 header = header[1:]
572 lines = [h + '\n' for h in header]
572 lines = [h + '\n' for h in header]
573 for hunkrange, hunklines in hunks:
573 for hunkrange, hunklines in hunks:
574 if linerange is not None and hunkrange is not None:
574 if linerange is not None and hunkrange is not None:
575 s1, l1, s2, l2 = hunkrange
575 s1, l1, s2, l2 = hunkrange
576 if not mdiff.hunkinrange((s2, l2), linerange):
576 if not mdiff.hunkinrange((s2, l2), linerange):
577 continue
577 continue
578 lines.extend(hunklines)
578 lines.extend(hunklines)
579 if lines:
579 if lines:
580 l = templateutil.mappedgenerator(_prettyprintdifflines,
580 l = templateutil.mappedgenerator(_prettyprintdifflines,
581 args=(lines, blockno,
581 args=(lines, blockno,
582 lineidprefix))
582 lineidprefix))
583 yield {
583 yield {
584 'parity': next(parity),
584 'parity': next(parity),
585 'blockno': blockno,
585 'blockno': blockno,
586 'lines': l,
586 'lines': l,
587 }
587 }
588
588
589 def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''):
589 def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''):
590 args = (web.repo, ctx, basectx, files, style, web.stripecount,
590 args = (web.repo, ctx, basectx, files, style, web.stripecount,
591 linerange, lineidprefix)
591 linerange, lineidprefix)
592 return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock')
592 return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock')
593
593
594 def _compline(type, leftlineno, leftline, rightlineno, rightline):
594 def _compline(type, leftlineno, leftline, rightlineno, rightline):
595 lineid = leftlineno and ("l%d" % leftlineno) or ''
595 lineid = leftlineno and ("l%d" % leftlineno) or ''
596 lineid += rightlineno and ("r%d" % rightlineno) or ''
596 lineid += rightlineno and ("r%d" % rightlineno) or ''
597 llno = '%d' % leftlineno if leftlineno else ''
597 llno = '%d' % leftlineno if leftlineno else ''
598 rlno = '%d' % rightlineno if rightlineno else ''
598 rlno = '%d' % rightlineno if rightlineno else ''
599 return {
599 return {
600 'type': type,
600 'type': type,
601 'lineid': lineid,
601 'lineid': lineid,
602 'leftlineno': leftlineno,
602 'leftlineno': leftlineno,
603 'leftlinenumber': "% 6s" % llno,
603 'leftlinenumber': "% 6s" % llno,
604 'leftline': leftline or '',
604 'leftline': leftline or '',
605 'rightlineno': rightlineno,
605 'rightlineno': rightlineno,
606 'rightlinenumber': "% 6s" % rlno,
606 'rightlinenumber': "% 6s" % rlno,
607 'rightline': rightline or '',
607 'rightline': rightline or '',
608 }
608 }
609
609
610 def _getcompblockgen(context, leftlines, rightlines, opcodes):
610 def _getcompblockgen(context, leftlines, rightlines, opcodes):
611 for type, llo, lhi, rlo, rhi in opcodes:
611 for type, llo, lhi, rlo, rhi in opcodes:
612 len1 = lhi - llo
612 len1 = lhi - llo
613 len2 = rhi - rlo
613 len2 = rhi - rlo
614 count = min(len1, len2)
614 count = min(len1, len2)
615 for i in xrange(count):
615 for i in xrange(count):
616 yield _compline(type=type,
616 yield _compline(type=type,
617 leftlineno=llo + i + 1,
617 leftlineno=llo + i + 1,
618 leftline=leftlines[llo + i],
618 leftline=leftlines[llo + i],
619 rightlineno=rlo + i + 1,
619 rightlineno=rlo + i + 1,
620 rightline=rightlines[rlo + i])
620 rightline=rightlines[rlo + i])
621 if len1 > len2:
621 if len1 > len2:
622 for i in xrange(llo + count, lhi):
622 for i in xrange(llo + count, lhi):
623 yield _compline(type=type,
623 yield _compline(type=type,
624 leftlineno=i + 1,
624 leftlineno=i + 1,
625 leftline=leftlines[i],
625 leftline=leftlines[i],
626 rightlineno=None,
626 rightlineno=None,
627 rightline=None)
627 rightline=None)
628 elif len2 > len1:
628 elif len2 > len1:
629 for i in xrange(rlo + count, rhi):
629 for i in xrange(rlo + count, rhi):
630 yield _compline(type=type,
630 yield _compline(type=type,
631 leftlineno=None,
631 leftlineno=None,
632 leftline=None,
632 leftline=None,
633 rightlineno=i + 1,
633 rightlineno=i + 1,
634 rightline=rightlines[i])
634 rightline=rightlines[i])
635
635
636 def _getcompblock(leftlines, rightlines, opcodes):
636 def _getcompblock(leftlines, rightlines, opcodes):
637 args = (leftlines, rightlines, opcodes)
637 args = (leftlines, rightlines, opcodes)
638 return templateutil.mappinggenerator(_getcompblockgen, args=args,
638 return templateutil.mappinggenerator(_getcompblockgen, args=args,
639 name='comparisonline')
639 name='comparisonline')
640
640
641 def _comparegen(context, contextnum, leftlines, rightlines):
641 def _comparegen(context, contextnum, leftlines, rightlines):
642 '''Generator function that provides side-by-side comparison data.'''
642 '''Generator function that provides side-by-side comparison data.'''
643 s = difflib.SequenceMatcher(None, leftlines, rightlines)
643 s = difflib.SequenceMatcher(None, leftlines, rightlines)
644 if contextnum < 0:
644 if contextnum < 0:
645 l = _getcompblock(leftlines, rightlines, s.get_opcodes())
645 l = _getcompblock(leftlines, rightlines, s.get_opcodes())
646 yield {'lines': l}
646 yield {'lines': l}
647 else:
647 else:
648 for oc in s.get_grouped_opcodes(n=contextnum):
648 for oc in s.get_grouped_opcodes(n=contextnum):
649 l = _getcompblock(leftlines, rightlines, oc)
649 l = _getcompblock(leftlines, rightlines, oc)
650 yield {'lines': l}
650 yield {'lines': l}
651
651
652 def compare(contextnum, leftlines, rightlines):
652 def compare(contextnum, leftlines, rightlines):
653 args = (contextnum, leftlines, rightlines)
653 args = (contextnum, leftlines, rightlines)
654 return templateutil.mappinggenerator(_comparegen, args=args,
654 return templateutil.mappinggenerator(_comparegen, args=args,
655 name='comparisonblock')
655 name='comparisonblock')
656
656
657 def diffstatgen(ctx, basectx):
657 def diffstatgen(ctx, basectx):
658 '''Generator function that provides the diffstat data.'''
658 '''Generator function that provides the diffstat data.'''
659
659
660 stats = patch.diffstatdata(
660 stats = patch.diffstatdata(
661 util.iterlines(ctx.diff(basectx, noprefix=False)))
661 util.iterlines(ctx.diff(basectx, noprefix=False)))
662 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
662 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
663 while True:
663 while True:
664 yield stats, maxname, maxtotal, addtotal, removetotal, binary
664 yield stats, maxname, maxtotal, addtotal, removetotal, binary
665
665
666 def diffsummary(statgen):
666 def diffsummary(statgen):
667 '''Return a short summary of the diff.'''
667 '''Return a short summary of the diff.'''
668
668
669 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
669 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
670 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
670 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
671 len(stats), addtotal, removetotal)
671 len(stats), addtotal, removetotal)
672
672
673 def _diffstattmplgen(context, ctx, statgen, parity):
673 def _diffstattmplgen(context, ctx, statgen, parity):
674 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
674 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
675 files = ctx.files()
675 files = ctx.files()
676
676
677 def pct(i):
677 def pct(i):
678 if maxtotal == 0:
678 if maxtotal == 0:
679 return 0
679 return 0
680 return (float(i) / maxtotal) * 100
680 return (float(i) / maxtotal) * 100
681
681
682 fileno = 0
682 fileno = 0
683 for filename, adds, removes, isbinary in stats:
683 for filename, adds, removes, isbinary in stats:
684 template = 'diffstatlink' if filename in files else 'diffstatnolink'
684 template = 'diffstatlink' if filename in files else 'diffstatnolink'
685 total = adds + removes
685 total = adds + removes
686 fileno += 1
686 fileno += 1
687 yield context.process(template, {
687 yield context.process(template, {
688 'node': ctx.hex(),
688 'node': ctx.hex(),
689 'file': filename,
689 'file': filename,
690 'fileno': fileno,
690 'fileno': fileno,
691 'total': total,
691 'total': total,
692 'addpct': pct(adds),
692 'addpct': pct(adds),
693 'removepct': pct(removes),
693 'removepct': pct(removes),
694 'parity': next(parity),
694 'parity': next(parity),
695 })
695 })
696
696
697 def diffstat(ctx, statgen, parity):
697 def diffstat(ctx, statgen, parity):
698 '''Return a diffstat template for each file in the diff.'''
698 '''Return a diffstat template for each file in the diff.'''
699 args = (ctx, statgen, parity)
699 args = (ctx, statgen, parity)
700 return templateutil.mappedgenerator(_diffstattmplgen, args=args)
700 return templateutil.mappedgenerator(_diffstattmplgen, args=args)
701
701
702 class sessionvars(templateutil.wrapped):
702 class sessionvars(templateutil.wrapped):
703 def __init__(self, vars, start='?'):
703 def __init__(self, vars, start='?'):
704 self._start = start
704 self._start = start
705 self._vars = vars
705 self._vars = vars
706
706
707 def __getitem__(self, key):
707 def __getitem__(self, key):
708 return self._vars[key]
708 return self._vars[key]
709
709
710 def __setitem__(self, key, value):
710 def __setitem__(self, key, value):
711 self._vars[key] = value
711 self._vars[key] = value
712
712
713 def __copy__(self):
713 def __copy__(self):
714 return sessionvars(copy.copy(self._vars), self._start)
714 return sessionvars(copy.copy(self._vars), self._start)
715
715
716 def getmember(self, context, mapping, key):
716 def getmember(self, context, mapping, key):
717 key = templateutil.unwrapvalue(context, mapping, key)
717 key = templateutil.unwrapvalue(context, mapping, key)
718 return self._vars.get(key)
718 return self._vars.get(key)
719
719
720 def getmin(self, context, mapping):
721 raise error.ParseError(_('not comparable'))
722
723 def getmax(self, context, mapping):
724 raise error.ParseError(_('not comparable'))
725
720 def itermaps(self, context):
726 def itermaps(self, context):
721 separator = self._start
727 separator = self._start
722 for key, value in sorted(self._vars.iteritems()):
728 for key, value in sorted(self._vars.iteritems()):
723 yield {'name': key,
729 yield {'name': key,
724 'value': pycompat.bytestr(value),
730 'value': pycompat.bytestr(value),
725 'separator': separator,
731 'separator': separator,
726 }
732 }
727 separator = '&'
733 separator = '&'
728
734
729 def join(self, context, mapping, sep):
735 def join(self, context, mapping, sep):
730 # could be '{separator}{name}={value|urlescape}'
736 # could be '{separator}{name}={value|urlescape}'
731 raise error.ParseError(_('not displayable without template'))
737 raise error.ParseError(_('not displayable without template'))
732
738
733 def show(self, context, mapping):
739 def show(self, context, mapping):
734 return self.join(context, '')
740 return self.join(context, '')
735
741
736 def tovalue(self, context, mapping):
742 def tovalue(self, context, mapping):
737 return self._vars
743 return self._vars
738
744
739 class wsgiui(uimod.ui):
745 class wsgiui(uimod.ui):
740 # default termwidth breaks under mod_wsgi
746 # default termwidth breaks under mod_wsgi
741 def termwidth(self):
747 def termwidth(self):
742 return 80
748 return 80
743
749
744 def getwebsubs(repo):
750 def getwebsubs(repo):
745 websubtable = []
751 websubtable = []
746 websubdefs = repo.ui.configitems('websub')
752 websubdefs = repo.ui.configitems('websub')
747 # we must maintain interhg backwards compatibility
753 # we must maintain interhg backwards compatibility
748 websubdefs += repo.ui.configitems('interhg')
754 websubdefs += repo.ui.configitems('interhg')
749 for key, pattern in websubdefs:
755 for key, pattern in websubdefs:
750 # grab the delimiter from the character after the "s"
756 # grab the delimiter from the character after the "s"
751 unesc = pattern[1:2]
757 unesc = pattern[1:2]
752 delim = re.escape(unesc)
758 delim = re.escape(unesc)
753
759
754 # identify portions of the pattern, taking care to avoid escaped
760 # identify portions of the pattern, taking care to avoid escaped
755 # delimiters. the replace format and flags are optional, but
761 # delimiters. the replace format and flags are optional, but
756 # delimiters are required.
762 # delimiters are required.
757 match = re.match(
763 match = re.match(
758 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
764 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
759 % (delim, delim, delim), pattern)
765 % (delim, delim, delim), pattern)
760 if not match:
766 if not match:
761 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
767 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
762 % (key, pattern))
768 % (key, pattern))
763 continue
769 continue
764
770
765 # we need to unescape the delimiter for regexp and format
771 # we need to unescape the delimiter for regexp and format
766 delim_re = re.compile(br'(?<!\\)\\%s' % delim)
772 delim_re = re.compile(br'(?<!\\)\\%s' % delim)
767 regexp = delim_re.sub(unesc, match.group(1))
773 regexp = delim_re.sub(unesc, match.group(1))
768 format = delim_re.sub(unesc, match.group(2))
774 format = delim_re.sub(unesc, match.group(2))
769
775
770 # the pattern allows for 6 regexp flags, so set them if necessary
776 # the pattern allows for 6 regexp flags, so set them if necessary
771 flagin = match.group(3)
777 flagin = match.group(3)
772 flags = 0
778 flags = 0
773 if flagin:
779 if flagin:
774 for flag in flagin.upper():
780 for flag in flagin.upper():
775 flags |= re.__dict__[flag]
781 flags |= re.__dict__[flag]
776
782
777 try:
783 try:
778 regexp = re.compile(regexp, flags)
784 regexp = re.compile(regexp, flags)
779 websubtable.append((regexp, format))
785 websubtable.append((regexp, format))
780 except re.error:
786 except re.error:
781 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
787 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
782 % (key, regexp))
788 % (key, regexp))
783 return websubtable
789 return websubtable
784
790
785 def getgraphnode(repo, ctx):
791 def getgraphnode(repo, ctx):
786 return (templatekw.getgraphnodecurrent(repo, ctx) +
792 return (templatekw.getgraphnodecurrent(repo, ctx) +
787 templatekw.getgraphnodesymbol(ctx))
793 templatekw.getgraphnodesymbol(ctx))
@@ -1,702 +1,701 b''
1 # templatefuncs.py - common template functions
1 # templatefuncs.py - common template functions
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 re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 bin,
14 bin,
15 wdirid,
15 wdirid,
16 )
16 )
17 from . import (
17 from . import (
18 color,
18 color,
19 encoding,
19 encoding,
20 error,
20 error,
21 minirst,
21 minirst,
22 obsutil,
22 obsutil,
23 pycompat,
24 registrar,
23 registrar,
25 revset as revsetmod,
24 revset as revsetmod,
26 revsetlang,
25 revsetlang,
27 scmutil,
26 scmutil,
28 templatefilters,
27 templatefilters,
29 templatekw,
28 templatekw,
30 templateutil,
29 templateutil,
31 util,
30 util,
32 )
31 )
33 from .utils import (
32 from .utils import (
34 dateutil,
33 dateutil,
35 stringutil,
34 stringutil,
36 )
35 )
37
36
38 evalrawexp = templateutil.evalrawexp
37 evalrawexp = templateutil.evalrawexp
39 evalwrapped = templateutil.evalwrapped
38 evalwrapped = templateutil.evalwrapped
40 evalfuncarg = templateutil.evalfuncarg
39 evalfuncarg = templateutil.evalfuncarg
41 evalboolean = templateutil.evalboolean
40 evalboolean = templateutil.evalboolean
42 evaldate = templateutil.evaldate
41 evaldate = templateutil.evaldate
43 evalinteger = templateutil.evalinteger
42 evalinteger = templateutil.evalinteger
44 evalstring = templateutil.evalstring
43 evalstring = templateutil.evalstring
45 evalstringliteral = templateutil.evalstringliteral
44 evalstringliteral = templateutil.evalstringliteral
46
45
47 # dict of template built-in functions
46 # dict of template built-in functions
48 funcs = {}
47 funcs = {}
49 templatefunc = registrar.templatefunc(funcs)
48 templatefunc = registrar.templatefunc(funcs)
50
49
51 @templatefunc('date(date[, fmt])')
50 @templatefunc('date(date[, fmt])')
52 def date(context, mapping, args):
51 def date(context, mapping, args):
53 """Format a date. See :hg:`help dates` for formatting
52 """Format a date. See :hg:`help dates` for formatting
54 strings. The default is a Unix date format, including the timezone:
53 strings. The default is a Unix date format, including the timezone:
55 "Mon Sep 04 15:13:13 2006 0700"."""
54 "Mon Sep 04 15:13:13 2006 0700"."""
56 if not (1 <= len(args) <= 2):
55 if not (1 <= len(args) <= 2):
57 # i18n: "date" is a keyword
56 # i18n: "date" is a keyword
58 raise error.ParseError(_("date expects one or two arguments"))
57 raise error.ParseError(_("date expects one or two arguments"))
59
58
60 date = evaldate(context, mapping, args[0],
59 date = evaldate(context, mapping, args[0],
61 # i18n: "date" is a keyword
60 # i18n: "date" is a keyword
62 _("date expects a date information"))
61 _("date expects a date information"))
63 fmt = None
62 fmt = None
64 if len(args) == 2:
63 if len(args) == 2:
65 fmt = evalstring(context, mapping, args[1])
64 fmt = evalstring(context, mapping, args[1])
66 if fmt is None:
65 if fmt is None:
67 return dateutil.datestr(date)
66 return dateutil.datestr(date)
68 else:
67 else:
69 return dateutil.datestr(date, fmt)
68 return dateutil.datestr(date, fmt)
70
69
71 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
70 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
72 def dict_(context, mapping, args):
71 def dict_(context, mapping, args):
73 """Construct a dict from key-value pairs. A key may be omitted if
72 """Construct a dict from key-value pairs. A key may be omitted if
74 a value expression can provide an unambiguous name."""
73 a value expression can provide an unambiguous name."""
75 data = util.sortdict()
74 data = util.sortdict()
76
75
77 for v in args['args']:
76 for v in args['args']:
78 k = templateutil.findsymbolicname(v)
77 k = templateutil.findsymbolicname(v)
79 if not k:
78 if not k:
80 raise error.ParseError(_('dict key cannot be inferred'))
79 raise error.ParseError(_('dict key cannot be inferred'))
81 if k in data or k in args['kwargs']:
80 if k in data or k in args['kwargs']:
82 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
81 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
83 data[k] = evalfuncarg(context, mapping, v)
82 data[k] = evalfuncarg(context, mapping, v)
84
83
85 data.update((k, evalfuncarg(context, mapping, v))
84 data.update((k, evalfuncarg(context, mapping, v))
86 for k, v in args['kwargs'].iteritems())
85 for k, v in args['kwargs'].iteritems())
87 return templateutil.hybriddict(data)
86 return templateutil.hybriddict(data)
88
87
89 @templatefunc('diff([includepattern [, excludepattern]])')
88 @templatefunc('diff([includepattern [, excludepattern]])')
90 def diff(context, mapping, args):
89 def diff(context, mapping, args):
91 """Show a diff, optionally
90 """Show a diff, optionally
92 specifying files to include or exclude."""
91 specifying files to include or exclude."""
93 if len(args) > 2:
92 if len(args) > 2:
94 # i18n: "diff" is a keyword
93 # i18n: "diff" is a keyword
95 raise error.ParseError(_("diff expects zero, one, or two arguments"))
94 raise error.ParseError(_("diff expects zero, one, or two arguments"))
96
95
97 def getpatterns(i):
96 def getpatterns(i):
98 if i < len(args):
97 if i < len(args):
99 s = evalstring(context, mapping, args[i]).strip()
98 s = evalstring(context, mapping, args[i]).strip()
100 if s:
99 if s:
101 return [s]
100 return [s]
102 return []
101 return []
103
102
104 ctx = context.resource(mapping, 'ctx')
103 ctx = context.resource(mapping, 'ctx')
105 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
104 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
106
105
107 return ''.join(chunks)
106 return ''.join(chunks)
108
107
109 @templatefunc('extdata(source)', argspec='source')
108 @templatefunc('extdata(source)', argspec='source')
110 def extdata(context, mapping, args):
109 def extdata(context, mapping, args):
111 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
110 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
112 if 'source' not in args:
111 if 'source' not in args:
113 # i18n: "extdata" is a keyword
112 # i18n: "extdata" is a keyword
114 raise error.ParseError(_('extdata expects one argument'))
113 raise error.ParseError(_('extdata expects one argument'))
115
114
116 source = evalstring(context, mapping, args['source'])
115 source = evalstring(context, mapping, args['source'])
117 if not source:
116 if not source:
118 sym = templateutil.findsymbolicname(args['source'])
117 sym = templateutil.findsymbolicname(args['source'])
119 if sym:
118 if sym:
120 raise error.ParseError(_('empty data source specified'),
119 raise error.ParseError(_('empty data source specified'),
121 hint=_("did you mean extdata('%s')?") % sym)
120 hint=_("did you mean extdata('%s')?") % sym)
122 else:
121 else:
123 raise error.ParseError(_('empty data source specified'))
122 raise error.ParseError(_('empty data source specified'))
124 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
123 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
125 ctx = context.resource(mapping, 'ctx')
124 ctx = context.resource(mapping, 'ctx')
126 if source in cache:
125 if source in cache:
127 data = cache[source]
126 data = cache[source]
128 else:
127 else:
129 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
128 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
130 return data.get(ctx.rev(), '')
129 return data.get(ctx.rev(), '')
131
130
132 @templatefunc('files(pattern)')
131 @templatefunc('files(pattern)')
133 def files(context, mapping, args):
132 def files(context, mapping, args):
134 """All files of the current changeset matching the pattern. See
133 """All files of the current changeset matching the pattern. See
135 :hg:`help patterns`."""
134 :hg:`help patterns`."""
136 if not len(args) == 1:
135 if not len(args) == 1:
137 # i18n: "files" is a keyword
136 # i18n: "files" is a keyword
138 raise error.ParseError(_("files expects one argument"))
137 raise error.ParseError(_("files expects one argument"))
139
138
140 raw = evalstring(context, mapping, args[0])
139 raw = evalstring(context, mapping, args[0])
141 ctx = context.resource(mapping, 'ctx')
140 ctx = context.resource(mapping, 'ctx')
142 m = ctx.match([raw])
141 m = ctx.match([raw])
143 files = list(ctx.matches(m))
142 files = list(ctx.matches(m))
144 return templateutil.compatlist(context, mapping, "file", files)
143 return templateutil.compatlist(context, mapping, "file", files)
145
144
146 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
145 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
147 def fill(context, mapping, args):
146 def fill(context, mapping, args):
148 """Fill many
147 """Fill many
149 paragraphs with optional indentation. See the "fill" filter."""
148 paragraphs with optional indentation. See the "fill" filter."""
150 if not (1 <= len(args) <= 4):
149 if not (1 <= len(args) <= 4):
151 # i18n: "fill" is a keyword
150 # i18n: "fill" is a keyword
152 raise error.ParseError(_("fill expects one to four arguments"))
151 raise error.ParseError(_("fill expects one to four arguments"))
153
152
154 text = evalstring(context, mapping, args[0])
153 text = evalstring(context, mapping, args[0])
155 width = 76
154 width = 76
156 initindent = ''
155 initindent = ''
157 hangindent = ''
156 hangindent = ''
158 if 2 <= len(args) <= 4:
157 if 2 <= len(args) <= 4:
159 width = evalinteger(context, mapping, args[1],
158 width = evalinteger(context, mapping, args[1],
160 # i18n: "fill" is a keyword
159 # i18n: "fill" is a keyword
161 _("fill expects an integer width"))
160 _("fill expects an integer width"))
162 try:
161 try:
163 initindent = evalstring(context, mapping, args[2])
162 initindent = evalstring(context, mapping, args[2])
164 hangindent = evalstring(context, mapping, args[3])
163 hangindent = evalstring(context, mapping, args[3])
165 except IndexError:
164 except IndexError:
166 pass
165 pass
167
166
168 return templatefilters.fill(text, width, initindent, hangindent)
167 return templatefilters.fill(text, width, initindent, hangindent)
169
168
170 @templatefunc('formatnode(node)')
169 @templatefunc('formatnode(node)')
171 def formatnode(context, mapping, args):
170 def formatnode(context, mapping, args):
172 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
171 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
173 if len(args) != 1:
172 if len(args) != 1:
174 # i18n: "formatnode" is a keyword
173 # i18n: "formatnode" is a keyword
175 raise error.ParseError(_("formatnode expects one argument"))
174 raise error.ParseError(_("formatnode expects one argument"))
176
175
177 ui = context.resource(mapping, 'ui')
176 ui = context.resource(mapping, 'ui')
178 node = evalstring(context, mapping, args[0])
177 node = evalstring(context, mapping, args[0])
179 if ui.debugflag:
178 if ui.debugflag:
180 return node
179 return node
181 return templatefilters.short(node)
180 return templatefilters.short(node)
182
181
183 @templatefunc('mailmap(author)')
182 @templatefunc('mailmap(author)')
184 def mailmap(context, mapping, args):
183 def mailmap(context, mapping, args):
185 """Return the author, updated according to the value
184 """Return the author, updated according to the value
186 set in the .mailmap file"""
185 set in the .mailmap file"""
187 if len(args) != 1:
186 if len(args) != 1:
188 raise error.ParseError(_("mailmap expects one argument"))
187 raise error.ParseError(_("mailmap expects one argument"))
189
188
190 author = evalstring(context, mapping, args[0])
189 author = evalstring(context, mapping, args[0])
191
190
192 cache = context.resource(mapping, 'cache')
191 cache = context.resource(mapping, 'cache')
193 repo = context.resource(mapping, 'repo')
192 repo = context.resource(mapping, 'repo')
194
193
195 if 'mailmap' not in cache:
194 if 'mailmap' not in cache:
196 data = repo.wvfs.tryread('.mailmap')
195 data = repo.wvfs.tryread('.mailmap')
197 cache['mailmap'] = stringutil.parsemailmap(data)
196 cache['mailmap'] = stringutil.parsemailmap(data)
198
197
199 return stringutil.mapname(cache['mailmap'], author)
198 return stringutil.mapname(cache['mailmap'], author)
200
199
201 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
200 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
202 argspec='text width fillchar left')
201 argspec='text width fillchar left')
203 def pad(context, mapping, args):
202 def pad(context, mapping, args):
204 """Pad text with a
203 """Pad text with a
205 fill character."""
204 fill character."""
206 if 'text' not in args or 'width' not in args:
205 if 'text' not in args or 'width' not in args:
207 # i18n: "pad" is a keyword
206 # i18n: "pad" is a keyword
208 raise error.ParseError(_("pad() expects two to four arguments"))
207 raise error.ParseError(_("pad() expects two to four arguments"))
209
208
210 width = evalinteger(context, mapping, args['width'],
209 width = evalinteger(context, mapping, args['width'],
211 # i18n: "pad" is a keyword
210 # i18n: "pad" is a keyword
212 _("pad() expects an integer width"))
211 _("pad() expects an integer width"))
213
212
214 text = evalstring(context, mapping, args['text'])
213 text = evalstring(context, mapping, args['text'])
215
214
216 left = False
215 left = False
217 fillchar = ' '
216 fillchar = ' '
218 if 'fillchar' in args:
217 if 'fillchar' in args:
219 fillchar = evalstring(context, mapping, args['fillchar'])
218 fillchar = evalstring(context, mapping, args['fillchar'])
220 if len(color.stripeffects(fillchar)) != 1:
219 if len(color.stripeffects(fillchar)) != 1:
221 # i18n: "pad" is a keyword
220 # i18n: "pad" is a keyword
222 raise error.ParseError(_("pad() expects a single fill character"))
221 raise error.ParseError(_("pad() expects a single fill character"))
223 if 'left' in args:
222 if 'left' in args:
224 left = evalboolean(context, mapping, args['left'])
223 left = evalboolean(context, mapping, args['left'])
225
224
226 fillwidth = width - encoding.colwidth(color.stripeffects(text))
225 fillwidth = width - encoding.colwidth(color.stripeffects(text))
227 if fillwidth <= 0:
226 if fillwidth <= 0:
228 return text
227 return text
229 if left:
228 if left:
230 return fillchar * fillwidth + text
229 return fillchar * fillwidth + text
231 else:
230 else:
232 return text + fillchar * fillwidth
231 return text + fillchar * fillwidth
233
232
234 @templatefunc('indent(text, indentchars[, firstline])')
233 @templatefunc('indent(text, indentchars[, firstline])')
235 def indent(context, mapping, args):
234 def indent(context, mapping, args):
236 """Indents all non-empty lines
235 """Indents all non-empty lines
237 with the characters given in the indentchars string. An optional
236 with the characters given in the indentchars string. An optional
238 third parameter will override the indent for the first line only
237 third parameter will override the indent for the first line only
239 if present."""
238 if present."""
240 if not (2 <= len(args) <= 3):
239 if not (2 <= len(args) <= 3):
241 # i18n: "indent" is a keyword
240 # i18n: "indent" is a keyword
242 raise error.ParseError(_("indent() expects two or three arguments"))
241 raise error.ParseError(_("indent() expects two or three arguments"))
243
242
244 text = evalstring(context, mapping, args[0])
243 text = evalstring(context, mapping, args[0])
245 indent = evalstring(context, mapping, args[1])
244 indent = evalstring(context, mapping, args[1])
246
245
247 if len(args) == 3:
246 if len(args) == 3:
248 firstline = evalstring(context, mapping, args[2])
247 firstline = evalstring(context, mapping, args[2])
249 else:
248 else:
250 firstline = indent
249 firstline = indent
251
250
252 # the indent function doesn't indent the first line, so we do it here
251 # the indent function doesn't indent the first line, so we do it here
253 return templatefilters.indent(firstline + text, indent)
252 return templatefilters.indent(firstline + text, indent)
254
253
255 @templatefunc('get(dict, key)')
254 @templatefunc('get(dict, key)')
256 def get(context, mapping, args):
255 def get(context, mapping, args):
257 """Get an attribute/key from an object. Some keywords
256 """Get an attribute/key from an object. Some keywords
258 are complex types. This function allows you to obtain the value of an
257 are complex types. This function allows you to obtain the value of an
259 attribute on these types."""
258 attribute on these types."""
260 if len(args) != 2:
259 if len(args) != 2:
261 # i18n: "get" is a keyword
260 # i18n: "get" is a keyword
262 raise error.ParseError(_("get() expects two arguments"))
261 raise error.ParseError(_("get() expects two arguments"))
263
262
264 dictarg = evalwrapped(context, mapping, args[0])
263 dictarg = evalwrapped(context, mapping, args[0])
265 key = evalrawexp(context, mapping, args[1])
264 key = evalrawexp(context, mapping, args[1])
266 try:
265 try:
267 return dictarg.getmember(context, mapping, key)
266 return dictarg.getmember(context, mapping, key)
268 except error.ParseError as err:
267 except error.ParseError as err:
269 # i18n: "get" is a keyword
268 # i18n: "get" is a keyword
270 hint = _("get() expects a dict as first argument")
269 hint = _("get() expects a dict as first argument")
271 raise error.ParseError(bytes(err), hint=hint)
270 raise error.ParseError(bytes(err), hint=hint)
272
271
273 @templatefunc('if(expr, then[, else])')
272 @templatefunc('if(expr, then[, else])')
274 def if_(context, mapping, args):
273 def if_(context, mapping, args):
275 """Conditionally execute based on the result of
274 """Conditionally execute based on the result of
276 an expression."""
275 an expression."""
277 if not (2 <= len(args) <= 3):
276 if not (2 <= len(args) <= 3):
278 # i18n: "if" is a keyword
277 # i18n: "if" is a keyword
279 raise error.ParseError(_("if expects two or three arguments"))
278 raise error.ParseError(_("if expects two or three arguments"))
280
279
281 test = evalboolean(context, mapping, args[0])
280 test = evalboolean(context, mapping, args[0])
282 if test:
281 if test:
283 return evalrawexp(context, mapping, args[1])
282 return evalrawexp(context, mapping, args[1])
284 elif len(args) == 3:
283 elif len(args) == 3:
285 return evalrawexp(context, mapping, args[2])
284 return evalrawexp(context, mapping, args[2])
286
285
287 @templatefunc('ifcontains(needle, haystack, then[, else])')
286 @templatefunc('ifcontains(needle, haystack, then[, else])')
288 def ifcontains(context, mapping, args):
287 def ifcontains(context, mapping, args):
289 """Conditionally execute based
288 """Conditionally execute based
290 on whether the item "needle" is in "haystack"."""
289 on whether the item "needle" is in "haystack"."""
291 if not (3 <= len(args) <= 4):
290 if not (3 <= len(args) <= 4):
292 # i18n: "ifcontains" is a keyword
291 # i18n: "ifcontains" is a keyword
293 raise error.ParseError(_("ifcontains expects three or four arguments"))
292 raise error.ParseError(_("ifcontains expects three or four arguments"))
294
293
295 haystack = evalfuncarg(context, mapping, args[1])
294 haystack = evalfuncarg(context, mapping, args[1])
296 keytype = getattr(haystack, 'keytype', None)
295 keytype = getattr(haystack, 'keytype', None)
297 try:
296 try:
298 needle = evalrawexp(context, mapping, args[0])
297 needle = evalrawexp(context, mapping, args[0])
299 needle = templateutil.unwrapastype(context, mapping, needle,
298 needle = templateutil.unwrapastype(context, mapping, needle,
300 keytype or bytes)
299 keytype or bytes)
301 found = (needle in haystack)
300 found = (needle in haystack)
302 except error.ParseError:
301 except error.ParseError:
303 found = False
302 found = False
304
303
305 if found:
304 if found:
306 return evalrawexp(context, mapping, args[2])
305 return evalrawexp(context, mapping, args[2])
307 elif len(args) == 4:
306 elif len(args) == 4:
308 return evalrawexp(context, mapping, args[3])
307 return evalrawexp(context, mapping, args[3])
309
308
310 @templatefunc('ifeq(expr1, expr2, then[, else])')
309 @templatefunc('ifeq(expr1, expr2, then[, else])')
311 def ifeq(context, mapping, args):
310 def ifeq(context, mapping, args):
312 """Conditionally execute based on
311 """Conditionally execute based on
313 whether 2 items are equivalent."""
312 whether 2 items are equivalent."""
314 if not (3 <= len(args) <= 4):
313 if not (3 <= len(args) <= 4):
315 # i18n: "ifeq" is a keyword
314 # i18n: "ifeq" is a keyword
316 raise error.ParseError(_("ifeq expects three or four arguments"))
315 raise error.ParseError(_("ifeq expects three or four arguments"))
317
316
318 test = evalstring(context, mapping, args[0])
317 test = evalstring(context, mapping, args[0])
319 match = evalstring(context, mapping, args[1])
318 match = evalstring(context, mapping, args[1])
320 if test == match:
319 if test == match:
321 return evalrawexp(context, mapping, args[2])
320 return evalrawexp(context, mapping, args[2])
322 elif len(args) == 4:
321 elif len(args) == 4:
323 return evalrawexp(context, mapping, args[3])
322 return evalrawexp(context, mapping, args[3])
324
323
325 @templatefunc('join(list, sep)')
324 @templatefunc('join(list, sep)')
326 def join(context, mapping, args):
325 def join(context, mapping, args):
327 """Join items in a list with a delimiter."""
326 """Join items in a list with a delimiter."""
328 if not (1 <= len(args) <= 2):
327 if not (1 <= len(args) <= 2):
329 # i18n: "join" is a keyword
328 # i18n: "join" is a keyword
330 raise error.ParseError(_("join expects one or two arguments"))
329 raise error.ParseError(_("join expects one or two arguments"))
331
330
332 joinset = evalwrapped(context, mapping, args[0])
331 joinset = evalwrapped(context, mapping, args[0])
333 joiner = " "
332 joiner = " "
334 if len(args) > 1:
333 if len(args) > 1:
335 joiner = evalstring(context, mapping, args[1])
334 joiner = evalstring(context, mapping, args[1])
336 return joinset.join(context, mapping, joiner)
335 return joinset.join(context, mapping, joiner)
337
336
338 @templatefunc('label(label, expr)')
337 @templatefunc('label(label, expr)')
339 def label(context, mapping, args):
338 def label(context, mapping, args):
340 """Apply a label to generated content. Content with
339 """Apply a label to generated content. Content with
341 a label applied can result in additional post-processing, such as
340 a label applied can result in additional post-processing, such as
342 automatic colorization."""
341 automatic colorization."""
343 if len(args) != 2:
342 if len(args) != 2:
344 # i18n: "label" is a keyword
343 # i18n: "label" is a keyword
345 raise error.ParseError(_("label expects two arguments"))
344 raise error.ParseError(_("label expects two arguments"))
346
345
347 ui = context.resource(mapping, 'ui')
346 ui = context.resource(mapping, 'ui')
348 thing = evalstring(context, mapping, args[1])
347 thing = evalstring(context, mapping, args[1])
349 # preserve unknown symbol as literal so effects like 'red', 'bold',
348 # preserve unknown symbol as literal so effects like 'red', 'bold',
350 # etc. don't need to be quoted
349 # etc. don't need to be quoted
351 label = evalstringliteral(context, mapping, args[0])
350 label = evalstringliteral(context, mapping, args[0])
352
351
353 return ui.label(thing, label)
352 return ui.label(thing, label)
354
353
355 @templatefunc('latesttag([pattern])')
354 @templatefunc('latesttag([pattern])')
356 def latesttag(context, mapping, args):
355 def latesttag(context, mapping, args):
357 """The global tags matching the given pattern on the
356 """The global tags matching the given pattern on the
358 most recent globally tagged ancestor of this changeset.
357 most recent globally tagged ancestor of this changeset.
359 If no such tags exist, the "{tag}" template resolves to
358 If no such tags exist, the "{tag}" template resolves to
360 the string "null". See :hg:`help revisions.patterns` for the pattern
359 the string "null". See :hg:`help revisions.patterns` for the pattern
361 syntax.
360 syntax.
362 """
361 """
363 if len(args) > 1:
362 if len(args) > 1:
364 # i18n: "latesttag" is a keyword
363 # i18n: "latesttag" is a keyword
365 raise error.ParseError(_("latesttag expects at most one argument"))
364 raise error.ParseError(_("latesttag expects at most one argument"))
366
365
367 pattern = None
366 pattern = None
368 if len(args) == 1:
367 if len(args) == 1:
369 pattern = evalstring(context, mapping, args[0])
368 pattern = evalstring(context, mapping, args[0])
370 return templatekw.showlatesttags(context, mapping, pattern)
369 return templatekw.showlatesttags(context, mapping, pattern)
371
370
372 @templatefunc('localdate(date[, tz])')
371 @templatefunc('localdate(date[, tz])')
373 def localdate(context, mapping, args):
372 def localdate(context, mapping, args):
374 """Converts a date to the specified timezone.
373 """Converts a date to the specified timezone.
375 The default is local date."""
374 The default is local date."""
376 if not (1 <= len(args) <= 2):
375 if not (1 <= len(args) <= 2):
377 # i18n: "localdate" is a keyword
376 # i18n: "localdate" is a keyword
378 raise error.ParseError(_("localdate expects one or two arguments"))
377 raise error.ParseError(_("localdate expects one or two arguments"))
379
378
380 date = evaldate(context, mapping, args[0],
379 date = evaldate(context, mapping, args[0],
381 # i18n: "localdate" is a keyword
380 # i18n: "localdate" is a keyword
382 _("localdate expects a date information"))
381 _("localdate expects a date information"))
383 if len(args) >= 2:
382 if len(args) >= 2:
384 tzoffset = None
383 tzoffset = None
385 tz = evalfuncarg(context, mapping, args[1])
384 tz = evalfuncarg(context, mapping, args[1])
386 if isinstance(tz, bytes):
385 if isinstance(tz, bytes):
387 tzoffset, remainder = dateutil.parsetimezone(tz)
386 tzoffset, remainder = dateutil.parsetimezone(tz)
388 if remainder:
387 if remainder:
389 tzoffset = None
388 tzoffset = None
390 if tzoffset is None:
389 if tzoffset is None:
391 try:
390 try:
392 tzoffset = int(tz)
391 tzoffset = int(tz)
393 except (TypeError, ValueError):
392 except (TypeError, ValueError):
394 # i18n: "localdate" is a keyword
393 # i18n: "localdate" is a keyword
395 raise error.ParseError(_("localdate expects a timezone"))
394 raise error.ParseError(_("localdate expects a timezone"))
396 else:
395 else:
397 tzoffset = dateutil.makedate()[1]
396 tzoffset = dateutil.makedate()[1]
398 return (date[0], tzoffset)
397 return (date[0], tzoffset)
399
398
400 @templatefunc('max(iterable)')
399 @templatefunc('max(iterable)')
401 def max_(context, mapping, args, **kwargs):
400 def max_(context, mapping, args, **kwargs):
402 """Return the max of an iterable"""
401 """Return the max of an iterable"""
403 if len(args) != 1:
402 if len(args) != 1:
404 # i18n: "max" is a keyword
403 # i18n: "max" is a keyword
405 raise error.ParseError(_("max expects one argument"))
404 raise error.ParseError(_("max expects one argument"))
406
405
407 iterable = evalfuncarg(context, mapping, args[0])
406 iterable = evalwrapped(context, mapping, args[0])
408 try:
407 try:
409 x = max(pycompat.maybebytestr(iterable))
408 return iterable.getmax(context, mapping)
410 except (TypeError, ValueError):
409 except error.ParseError as err:
411 # i18n: "max" is a keyword
410 # i18n: "max" is a keyword
412 raise error.ParseError(_("max first argument should be an iterable"))
411 hint = _("max first argument should be an iterable")
413 return templateutil.wraphybridvalue(iterable, x, x)
412 raise error.ParseError(bytes(err), hint=hint)
414
413
415 @templatefunc('min(iterable)')
414 @templatefunc('min(iterable)')
416 def min_(context, mapping, args, **kwargs):
415 def min_(context, mapping, args, **kwargs):
417 """Return the min of an iterable"""
416 """Return the min of an iterable"""
418 if len(args) != 1:
417 if len(args) != 1:
419 # i18n: "min" is a keyword
418 # i18n: "min" is a keyword
420 raise error.ParseError(_("min expects one argument"))
419 raise error.ParseError(_("min expects one argument"))
421
420
422 iterable = evalfuncarg(context, mapping, args[0])
421 iterable = evalwrapped(context, mapping, args[0])
423 try:
422 try:
424 x = min(pycompat.maybebytestr(iterable))
423 return iterable.getmin(context, mapping)
425 except (TypeError, ValueError):
424 except error.ParseError as err:
426 # i18n: "min" is a keyword
425 # i18n: "min" is a keyword
427 raise error.ParseError(_("min first argument should be an iterable"))
426 hint = _("min first argument should be an iterable")
428 return templateutil.wraphybridvalue(iterable, x, x)
427 raise error.ParseError(bytes(err), hint=hint)
429
428
430 @templatefunc('mod(a, b)')
429 @templatefunc('mod(a, b)')
431 def mod(context, mapping, args):
430 def mod(context, mapping, args):
432 """Calculate a mod b such that a / b + a mod b == a"""
431 """Calculate a mod b such that a / b + a mod b == a"""
433 if not len(args) == 2:
432 if not len(args) == 2:
434 # i18n: "mod" is a keyword
433 # i18n: "mod" is a keyword
435 raise error.ParseError(_("mod expects two arguments"))
434 raise error.ParseError(_("mod expects two arguments"))
436
435
437 func = lambda a, b: a % b
436 func = lambda a, b: a % b
438 return templateutil.runarithmetic(context, mapping,
437 return templateutil.runarithmetic(context, mapping,
439 (func, args[0], args[1]))
438 (func, args[0], args[1]))
440
439
441 @templatefunc('obsfateoperations(markers)')
440 @templatefunc('obsfateoperations(markers)')
442 def obsfateoperations(context, mapping, args):
441 def obsfateoperations(context, mapping, args):
443 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
442 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
444 if len(args) != 1:
443 if len(args) != 1:
445 # i18n: "obsfateoperations" is a keyword
444 # i18n: "obsfateoperations" is a keyword
446 raise error.ParseError(_("obsfateoperations expects one argument"))
445 raise error.ParseError(_("obsfateoperations expects one argument"))
447
446
448 markers = evalfuncarg(context, mapping, args[0])
447 markers = evalfuncarg(context, mapping, args[0])
449
448
450 try:
449 try:
451 data = obsutil.markersoperations(markers)
450 data = obsutil.markersoperations(markers)
452 return templateutil.hybridlist(data, name='operation')
451 return templateutil.hybridlist(data, name='operation')
453 except (TypeError, KeyError):
452 except (TypeError, KeyError):
454 # i18n: "obsfateoperations" is a keyword
453 # i18n: "obsfateoperations" is a keyword
455 errmsg = _("obsfateoperations first argument should be an iterable")
454 errmsg = _("obsfateoperations first argument should be an iterable")
456 raise error.ParseError(errmsg)
455 raise error.ParseError(errmsg)
457
456
458 @templatefunc('obsfatedate(markers)')
457 @templatefunc('obsfatedate(markers)')
459 def obsfatedate(context, mapping, args):
458 def obsfatedate(context, mapping, args):
460 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
459 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
461 if len(args) != 1:
460 if len(args) != 1:
462 # i18n: "obsfatedate" is a keyword
461 # i18n: "obsfatedate" is a keyword
463 raise error.ParseError(_("obsfatedate expects one argument"))
462 raise error.ParseError(_("obsfatedate expects one argument"))
464
463
465 markers = evalfuncarg(context, mapping, args[0])
464 markers = evalfuncarg(context, mapping, args[0])
466
465
467 try:
466 try:
468 data = obsutil.markersdates(markers)
467 data = obsutil.markersdates(markers)
469 return templateutil.hybridlist(data, name='date', fmt='%d %d')
468 return templateutil.hybridlist(data, name='date', fmt='%d %d')
470 except (TypeError, KeyError):
469 except (TypeError, KeyError):
471 # i18n: "obsfatedate" is a keyword
470 # i18n: "obsfatedate" is a keyword
472 errmsg = _("obsfatedate first argument should be an iterable")
471 errmsg = _("obsfatedate first argument should be an iterable")
473 raise error.ParseError(errmsg)
472 raise error.ParseError(errmsg)
474
473
475 @templatefunc('obsfateusers(markers)')
474 @templatefunc('obsfateusers(markers)')
476 def obsfateusers(context, mapping, args):
475 def obsfateusers(context, mapping, args):
477 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
476 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
478 if len(args) != 1:
477 if len(args) != 1:
479 # i18n: "obsfateusers" is a keyword
478 # i18n: "obsfateusers" is a keyword
480 raise error.ParseError(_("obsfateusers expects one argument"))
479 raise error.ParseError(_("obsfateusers expects one argument"))
481
480
482 markers = evalfuncarg(context, mapping, args[0])
481 markers = evalfuncarg(context, mapping, args[0])
483
482
484 try:
483 try:
485 data = obsutil.markersusers(markers)
484 data = obsutil.markersusers(markers)
486 return templateutil.hybridlist(data, name='user')
485 return templateutil.hybridlist(data, name='user')
487 except (TypeError, KeyError, ValueError):
486 except (TypeError, KeyError, ValueError):
488 # i18n: "obsfateusers" is a keyword
487 # i18n: "obsfateusers" is a keyword
489 msg = _("obsfateusers first argument should be an iterable of "
488 msg = _("obsfateusers first argument should be an iterable of "
490 "obsmakers")
489 "obsmakers")
491 raise error.ParseError(msg)
490 raise error.ParseError(msg)
492
491
493 @templatefunc('obsfateverb(successors, markers)')
492 @templatefunc('obsfateverb(successors, markers)')
494 def obsfateverb(context, mapping, args):
493 def obsfateverb(context, mapping, args):
495 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
494 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
496 if len(args) != 2:
495 if len(args) != 2:
497 # i18n: "obsfateverb" is a keyword
496 # i18n: "obsfateverb" is a keyword
498 raise error.ParseError(_("obsfateverb expects two arguments"))
497 raise error.ParseError(_("obsfateverb expects two arguments"))
499
498
500 successors = evalfuncarg(context, mapping, args[0])
499 successors = evalfuncarg(context, mapping, args[0])
501 markers = evalfuncarg(context, mapping, args[1])
500 markers = evalfuncarg(context, mapping, args[1])
502
501
503 try:
502 try:
504 return obsutil.obsfateverb(successors, markers)
503 return obsutil.obsfateverb(successors, markers)
505 except TypeError:
504 except TypeError:
506 # i18n: "obsfateverb" is a keyword
505 # i18n: "obsfateverb" is a keyword
507 errmsg = _("obsfateverb first argument should be countable")
506 errmsg = _("obsfateverb first argument should be countable")
508 raise error.ParseError(errmsg)
507 raise error.ParseError(errmsg)
509
508
510 @templatefunc('relpath(path)')
509 @templatefunc('relpath(path)')
511 def relpath(context, mapping, args):
510 def relpath(context, mapping, args):
512 """Convert a repository-absolute path into a filesystem path relative to
511 """Convert a repository-absolute path into a filesystem path relative to
513 the current working directory."""
512 the current working directory."""
514 if len(args) != 1:
513 if len(args) != 1:
515 # i18n: "relpath" is a keyword
514 # i18n: "relpath" is a keyword
516 raise error.ParseError(_("relpath expects one argument"))
515 raise error.ParseError(_("relpath expects one argument"))
517
516
518 repo = context.resource(mapping, 'ctx').repo()
517 repo = context.resource(mapping, 'ctx').repo()
519 path = evalstring(context, mapping, args[0])
518 path = evalstring(context, mapping, args[0])
520 return repo.pathto(path)
519 return repo.pathto(path)
521
520
522 @templatefunc('revset(query[, formatargs...])')
521 @templatefunc('revset(query[, formatargs...])')
523 def revset(context, mapping, args):
522 def revset(context, mapping, args):
524 """Execute a revision set query. See
523 """Execute a revision set query. See
525 :hg:`help revset`."""
524 :hg:`help revset`."""
526 if not len(args) > 0:
525 if not len(args) > 0:
527 # i18n: "revset" is a keyword
526 # i18n: "revset" is a keyword
528 raise error.ParseError(_("revset expects one or more arguments"))
527 raise error.ParseError(_("revset expects one or more arguments"))
529
528
530 raw = evalstring(context, mapping, args[0])
529 raw = evalstring(context, mapping, args[0])
531 ctx = context.resource(mapping, 'ctx')
530 ctx = context.resource(mapping, 'ctx')
532 repo = ctx.repo()
531 repo = ctx.repo()
533
532
534 def query(expr):
533 def query(expr):
535 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
534 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
536 return m(repo)
535 return m(repo)
537
536
538 if len(args) > 1:
537 if len(args) > 1:
539 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
538 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
540 revs = query(revsetlang.formatspec(raw, *formatargs))
539 revs = query(revsetlang.formatspec(raw, *formatargs))
541 revs = list(revs)
540 revs = list(revs)
542 else:
541 else:
543 cache = context.resource(mapping, 'cache')
542 cache = context.resource(mapping, 'cache')
544 revsetcache = cache.setdefault("revsetcache", {})
543 revsetcache = cache.setdefault("revsetcache", {})
545 if raw in revsetcache:
544 if raw in revsetcache:
546 revs = revsetcache[raw]
545 revs = revsetcache[raw]
547 else:
546 else:
548 revs = query(raw)
547 revs = query(raw)
549 revs = list(revs)
548 revs = list(revs)
550 revsetcache[raw] = revs
549 revsetcache[raw] = revs
551 return templatekw.showrevslist(context, mapping, "revision", revs)
550 return templatekw.showrevslist(context, mapping, "revision", revs)
552
551
553 @templatefunc('rstdoc(text, style)')
552 @templatefunc('rstdoc(text, style)')
554 def rstdoc(context, mapping, args):
553 def rstdoc(context, mapping, args):
555 """Format reStructuredText."""
554 """Format reStructuredText."""
556 if len(args) != 2:
555 if len(args) != 2:
557 # i18n: "rstdoc" is a keyword
556 # i18n: "rstdoc" is a keyword
558 raise error.ParseError(_("rstdoc expects two arguments"))
557 raise error.ParseError(_("rstdoc expects two arguments"))
559
558
560 text = evalstring(context, mapping, args[0])
559 text = evalstring(context, mapping, args[0])
561 style = evalstring(context, mapping, args[1])
560 style = evalstring(context, mapping, args[1])
562
561
563 return minirst.format(text, style=style, keep=['verbose'])[0]
562 return minirst.format(text, style=style, keep=['verbose'])[0]
564
563
565 @templatefunc('separate(sep, args...)', argspec='sep *args')
564 @templatefunc('separate(sep, args...)', argspec='sep *args')
566 def separate(context, mapping, args):
565 def separate(context, mapping, args):
567 """Add a separator between non-empty arguments."""
566 """Add a separator between non-empty arguments."""
568 if 'sep' not in args:
567 if 'sep' not in args:
569 # i18n: "separate" is a keyword
568 # i18n: "separate" is a keyword
570 raise error.ParseError(_("separate expects at least one argument"))
569 raise error.ParseError(_("separate expects at least one argument"))
571
570
572 sep = evalstring(context, mapping, args['sep'])
571 sep = evalstring(context, mapping, args['sep'])
573 first = True
572 first = True
574 for arg in args['args']:
573 for arg in args['args']:
575 argstr = evalstring(context, mapping, arg)
574 argstr = evalstring(context, mapping, arg)
576 if not argstr:
575 if not argstr:
577 continue
576 continue
578 if first:
577 if first:
579 first = False
578 first = False
580 else:
579 else:
581 yield sep
580 yield sep
582 yield argstr
581 yield argstr
583
582
584 @templatefunc('shortest(node, minlength=4)')
583 @templatefunc('shortest(node, minlength=4)')
585 def shortest(context, mapping, args):
584 def shortest(context, mapping, args):
586 """Obtain the shortest representation of
585 """Obtain the shortest representation of
587 a node."""
586 a node."""
588 if not (1 <= len(args) <= 2):
587 if not (1 <= len(args) <= 2):
589 # i18n: "shortest" is a keyword
588 # i18n: "shortest" is a keyword
590 raise error.ParseError(_("shortest() expects one or two arguments"))
589 raise error.ParseError(_("shortest() expects one or two arguments"))
591
590
592 hexnode = evalstring(context, mapping, args[0])
591 hexnode = evalstring(context, mapping, args[0])
593
592
594 minlength = 4
593 minlength = 4
595 if len(args) > 1:
594 if len(args) > 1:
596 minlength = evalinteger(context, mapping, args[1],
595 minlength = evalinteger(context, mapping, args[1],
597 # i18n: "shortest" is a keyword
596 # i18n: "shortest" is a keyword
598 _("shortest() expects an integer minlength"))
597 _("shortest() expects an integer minlength"))
599
598
600 repo = context.resource(mapping, 'ctx')._repo
599 repo = context.resource(mapping, 'ctx')._repo
601 if len(hexnode) > 40:
600 if len(hexnode) > 40:
602 return hexnode
601 return hexnode
603 elif len(hexnode) == 40:
602 elif len(hexnode) == 40:
604 try:
603 try:
605 node = bin(hexnode)
604 node = bin(hexnode)
606 except TypeError:
605 except TypeError:
607 return hexnode
606 return hexnode
608 else:
607 else:
609 try:
608 try:
610 node = scmutil.resolvehexnodeidprefix(repo, hexnode)
609 node = scmutil.resolvehexnodeidprefix(repo, hexnode)
611 except error.WdirUnsupported:
610 except error.WdirUnsupported:
612 node = wdirid
611 node = wdirid
613 except error.LookupError:
612 except error.LookupError:
614 return hexnode
613 return hexnode
615 if not node:
614 if not node:
616 return hexnode
615 return hexnode
617 try:
616 try:
618 return scmutil.shortesthexnodeidprefix(repo, node, minlength)
617 return scmutil.shortesthexnodeidprefix(repo, node, minlength)
619 except error.RepoLookupError:
618 except error.RepoLookupError:
620 return hexnode
619 return hexnode
621
620
622 @templatefunc('strip(text[, chars])')
621 @templatefunc('strip(text[, chars])')
623 def strip(context, mapping, args):
622 def strip(context, mapping, args):
624 """Strip characters from a string. By default,
623 """Strip characters from a string. By default,
625 strips all leading and trailing whitespace."""
624 strips all leading and trailing whitespace."""
626 if not (1 <= len(args) <= 2):
625 if not (1 <= len(args) <= 2):
627 # i18n: "strip" is a keyword
626 # i18n: "strip" is a keyword
628 raise error.ParseError(_("strip expects one or two arguments"))
627 raise error.ParseError(_("strip expects one or two arguments"))
629
628
630 text = evalstring(context, mapping, args[0])
629 text = evalstring(context, mapping, args[0])
631 if len(args) == 2:
630 if len(args) == 2:
632 chars = evalstring(context, mapping, args[1])
631 chars = evalstring(context, mapping, args[1])
633 return text.strip(chars)
632 return text.strip(chars)
634 return text.strip()
633 return text.strip()
635
634
636 @templatefunc('sub(pattern, replacement, expression)')
635 @templatefunc('sub(pattern, replacement, expression)')
637 def sub(context, mapping, args):
636 def sub(context, mapping, args):
638 """Perform text substitution
637 """Perform text substitution
639 using regular expressions."""
638 using regular expressions."""
640 if len(args) != 3:
639 if len(args) != 3:
641 # i18n: "sub" is a keyword
640 # i18n: "sub" is a keyword
642 raise error.ParseError(_("sub expects three arguments"))
641 raise error.ParseError(_("sub expects three arguments"))
643
642
644 pat = evalstring(context, mapping, args[0])
643 pat = evalstring(context, mapping, args[0])
645 rpl = evalstring(context, mapping, args[1])
644 rpl = evalstring(context, mapping, args[1])
646 src = evalstring(context, mapping, args[2])
645 src = evalstring(context, mapping, args[2])
647 try:
646 try:
648 patre = re.compile(pat)
647 patre = re.compile(pat)
649 except re.error:
648 except re.error:
650 # i18n: "sub" is a keyword
649 # i18n: "sub" is a keyword
651 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
650 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
652 try:
651 try:
653 yield patre.sub(rpl, src)
652 yield patre.sub(rpl, src)
654 except re.error:
653 except re.error:
655 # i18n: "sub" is a keyword
654 # i18n: "sub" is a keyword
656 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
655 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
657
656
658 @templatefunc('startswith(pattern, text)')
657 @templatefunc('startswith(pattern, text)')
659 def startswith(context, mapping, args):
658 def startswith(context, mapping, args):
660 """Returns the value from the "text" argument
659 """Returns the value from the "text" argument
661 if it begins with the content from the "pattern" argument."""
660 if it begins with the content from the "pattern" argument."""
662 if len(args) != 2:
661 if len(args) != 2:
663 # i18n: "startswith" is a keyword
662 # i18n: "startswith" is a keyword
664 raise error.ParseError(_("startswith expects two arguments"))
663 raise error.ParseError(_("startswith expects two arguments"))
665
664
666 patn = evalstring(context, mapping, args[0])
665 patn = evalstring(context, mapping, args[0])
667 text = evalstring(context, mapping, args[1])
666 text = evalstring(context, mapping, args[1])
668 if text.startswith(patn):
667 if text.startswith(patn):
669 return text
668 return text
670 return ''
669 return ''
671
670
672 @templatefunc('word(number, text[, separator])')
671 @templatefunc('word(number, text[, separator])')
673 def word(context, mapping, args):
672 def word(context, mapping, args):
674 """Return the nth word from a string."""
673 """Return the nth word from a string."""
675 if not (2 <= len(args) <= 3):
674 if not (2 <= len(args) <= 3):
676 # i18n: "word" is a keyword
675 # i18n: "word" is a keyword
677 raise error.ParseError(_("word expects two or three arguments, got %d")
676 raise error.ParseError(_("word expects two or three arguments, got %d")
678 % len(args))
677 % len(args))
679
678
680 num = evalinteger(context, mapping, args[0],
679 num = evalinteger(context, mapping, args[0],
681 # i18n: "word" is a keyword
680 # i18n: "word" is a keyword
682 _("word expects an integer index"))
681 _("word expects an integer index"))
683 text = evalstring(context, mapping, args[1])
682 text = evalstring(context, mapping, args[1])
684 if len(args) == 3:
683 if len(args) == 3:
685 splitter = evalstring(context, mapping, args[2])
684 splitter = evalstring(context, mapping, args[2])
686 else:
685 else:
687 splitter = None
686 splitter = None
688
687
689 tokens = text.split(splitter)
688 tokens = text.split(splitter)
690 if num >= len(tokens) or num < -len(tokens):
689 if num >= len(tokens) or num < -len(tokens):
691 return ''
690 return ''
692 else:
691 else:
693 return tokens[num]
692 return tokens[num]
694
693
695 def loadfunction(ui, extname, registrarobj):
694 def loadfunction(ui, extname, registrarobj):
696 """Load template function from specified registrarobj
695 """Load template function from specified registrarobj
697 """
696 """
698 for name, func in registrarobj._table.iteritems():
697 for name, func in registrarobj._table.iteritems():
699 funcs[name] = func
698 funcs[name] = func
700
699
701 # tell hggettext to extract docstrings from these functions:
700 # tell hggettext to extract docstrings from these functions:
702 i18nfunctions = funcs.values()
701 i18nfunctions = funcs.values()
@@ -1,740 +1,805 b''
1 # templateutil.py - utility for template evaluation
1 # templateutil.py - utility for template evaluation
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 abc
10 import abc
11 import types
11 import types
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 error,
15 error,
16 pycompat,
16 pycompat,
17 util,
17 util,
18 )
18 )
19 from .utils import (
19 from .utils import (
20 dateutil,
20 dateutil,
21 stringutil,
21 stringutil,
22 )
22 )
23
23
24 class ResourceUnavailable(error.Abort):
24 class ResourceUnavailable(error.Abort):
25 pass
25 pass
26
26
27 class TemplateNotFound(error.Abort):
27 class TemplateNotFound(error.Abort):
28 pass
28 pass
29
29
30 class wrapped(object):
30 class wrapped(object):
31 """Object requiring extra conversion prior to displaying or processing
31 """Object requiring extra conversion prior to displaying or processing
32 as value
32 as value
33
33
34 Use unwrapvalue(), unwrapastype(), or unwraphybrid() to obtain the inner
34 Use unwrapvalue(), unwrapastype(), or unwraphybrid() to obtain the inner
35 object.
35 object.
36 """
36 """
37
37
38 __metaclass__ = abc.ABCMeta
38 __metaclass__ = abc.ABCMeta
39
39
40 @abc.abstractmethod
40 @abc.abstractmethod
41 def getmember(self, context, mapping, key):
41 def getmember(self, context, mapping, key):
42 """Return a member item for the specified key
42 """Return a member item for the specified key
43
43
44 The key argument may be a wrapped object.
44 The key argument may be a wrapped object.
45 A returned object may be either a wrapped object or a pure value
45 A returned object may be either a wrapped object or a pure value
46 depending on the self type.
46 depending on the self type.
47 """
47 """
48
48
49 @abc.abstractmethod
49 @abc.abstractmethod
50 def getmin(self, context, mapping):
51 """Return the smallest item, which may be either a wrapped or a pure
52 value depending on the self type"""
53
54 @abc.abstractmethod
55 def getmax(self, context, mapping):
56 """Return the largest item, which may be either a wrapped or a pure
57 value depending on the self type"""
58
59 @abc.abstractmethod
50 def itermaps(self, context):
60 def itermaps(self, context):
51 """Yield each template mapping"""
61 """Yield each template mapping"""
52
62
53 @abc.abstractmethod
63 @abc.abstractmethod
54 def join(self, context, mapping, sep):
64 def join(self, context, mapping, sep):
55 """Join items with the separator; Returns a bytes or (possibly nested)
65 """Join items with the separator; Returns a bytes or (possibly nested)
56 generator of bytes
66 generator of bytes
57
67
58 A pre-configured template may be rendered per item if this container
68 A pre-configured template may be rendered per item if this container
59 holds unprintable items.
69 holds unprintable items.
60 """
70 """
61
71
62 @abc.abstractmethod
72 @abc.abstractmethod
63 def show(self, context, mapping):
73 def show(self, context, mapping):
64 """Return a bytes or (possibly nested) generator of bytes representing
74 """Return a bytes or (possibly nested) generator of bytes representing
65 the underlying object
75 the underlying object
66
76
67 A pre-configured template may be rendered if the underlying object is
77 A pre-configured template may be rendered if the underlying object is
68 not printable.
78 not printable.
69 """
79 """
70
80
71 @abc.abstractmethod
81 @abc.abstractmethod
72 def tovalue(self, context, mapping):
82 def tovalue(self, context, mapping):
73 """Move the inner value object out or create a value representation
83 """Move the inner value object out or create a value representation
74
84
75 A returned value must be serializable by templaterfilters.json().
85 A returned value must be serializable by templaterfilters.json().
76 """
86 """
77
87
78 class wrappedbytes(wrapped):
88 class wrappedbytes(wrapped):
79 """Wrapper for byte string"""
89 """Wrapper for byte string"""
80
90
81 def __init__(self, value):
91 def __init__(self, value):
82 self._value = value
92 self._value = value
83
93
84 def getmember(self, context, mapping, key):
94 def getmember(self, context, mapping, key):
85 raise error.ParseError(_('%r is not a dictionary')
95 raise error.ParseError(_('%r is not a dictionary')
86 % pycompat.bytestr(self._value))
96 % pycompat.bytestr(self._value))
87
97
98 def getmin(self, context, mapping):
99 return self._getby(context, mapping, min)
100
101 def getmax(self, context, mapping):
102 return self._getby(context, mapping, max)
103
104 def _getby(self, context, mapping, func):
105 if not self._value:
106 raise error.ParseError(_('empty string'))
107 return func(pycompat.iterbytestr(self._value))
108
88 def itermaps(self, context):
109 def itermaps(self, context):
89 raise error.ParseError(_('%r is not iterable of mappings')
110 raise error.ParseError(_('%r is not iterable of mappings')
90 % pycompat.bytestr(self._value))
111 % pycompat.bytestr(self._value))
91
112
92 def join(self, context, mapping, sep):
113 def join(self, context, mapping, sep):
93 return joinitems(pycompat.iterbytestr(self._value), sep)
114 return joinitems(pycompat.iterbytestr(self._value), sep)
94
115
95 def show(self, context, mapping):
116 def show(self, context, mapping):
96 return self._value
117 return self._value
97
118
98 def tovalue(self, context, mapping):
119 def tovalue(self, context, mapping):
99 return self._value
120 return self._value
100
121
101 class wrappedvalue(wrapped):
122 class wrappedvalue(wrapped):
102 """Generic wrapper for pure non-list/dict/bytes value"""
123 """Generic wrapper for pure non-list/dict/bytes value"""
103
124
104 def __init__(self, value):
125 def __init__(self, value):
105 self._value = value
126 self._value = value
106
127
107 def getmember(self, context, mapping, key):
128 def getmember(self, context, mapping, key):
108 raise error.ParseError(_('%r is not a dictionary') % self._value)
129 raise error.ParseError(_('%r is not a dictionary') % self._value)
109
130
131 def getmin(self, context, mapping):
132 raise error.ParseError(_("%r is not iterable") % self._value)
133
134 def getmax(self, context, mapping):
135 raise error.ParseError(_("%r is not iterable") % self._value)
136
110 def itermaps(self, context):
137 def itermaps(self, context):
111 raise error.ParseError(_('%r is not iterable of mappings')
138 raise error.ParseError(_('%r is not iterable of mappings')
112 % self._value)
139 % self._value)
113
140
114 def join(self, context, mapping, sep):
141 def join(self, context, mapping, sep):
115 raise error.ParseError(_('%r is not iterable') % self._value)
142 raise error.ParseError(_('%r is not iterable') % self._value)
116
143
117 def show(self, context, mapping):
144 def show(self, context, mapping):
118 return pycompat.bytestr(self._value)
145 return pycompat.bytestr(self._value)
119
146
120 def tovalue(self, context, mapping):
147 def tovalue(self, context, mapping):
121 return self._value
148 return self._value
122
149
123 # stub for representing a date type; may be a real date type that can
150 # stub for representing a date type; may be a real date type that can
124 # provide a readable string value
151 # provide a readable string value
125 class date(object):
152 class date(object):
126 pass
153 pass
127
154
128 class hybrid(wrapped):
155 class hybrid(wrapped):
129 """Wrapper for list or dict to support legacy template
156 """Wrapper for list or dict to support legacy template
130
157
131 This class allows us to handle both:
158 This class allows us to handle both:
132 - "{files}" (legacy command-line-specific list hack) and
159 - "{files}" (legacy command-line-specific list hack) and
133 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
160 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
134 and to access raw values:
161 and to access raw values:
135 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
162 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
136 - "{get(extras, key)}"
163 - "{get(extras, key)}"
137 - "{files|json}"
164 - "{files|json}"
138 """
165 """
139
166
140 def __init__(self, gen, values, makemap, joinfmt, keytype=None):
167 def __init__(self, gen, values, makemap, joinfmt, keytype=None):
141 self._gen = gen # generator or function returning generator
168 self._gen = gen # generator or function returning generator
142 self._values = values
169 self._values = values
143 self._makemap = makemap
170 self._makemap = makemap
144 self._joinfmt = joinfmt
171 self._joinfmt = joinfmt
145 self.keytype = keytype # hint for 'x in y' where type(x) is unresolved
172 self.keytype = keytype # hint for 'x in y' where type(x) is unresolved
146
173
147 def getmember(self, context, mapping, key):
174 def getmember(self, context, mapping, key):
148 # TODO: maybe split hybrid list/dict types?
175 # TODO: maybe split hybrid list/dict types?
149 if not util.safehasattr(self._values, 'get'):
176 if not util.safehasattr(self._values, 'get'):
150 raise error.ParseError(_('not a dictionary'))
177 raise error.ParseError(_('not a dictionary'))
151 key = unwrapastype(context, mapping, key, self.keytype)
178 key = unwrapastype(context, mapping, key, self.keytype)
152 return self._wrapvalue(key, self._values.get(key))
179 return self._wrapvalue(key, self._values.get(key))
153
180
181 def getmin(self, context, mapping):
182 return self._getby(context, mapping, min)
183
184 def getmax(self, context, mapping):
185 return self._getby(context, mapping, max)
186
187 def _getby(self, context, mapping, func):
188 if not self._values:
189 raise error.ParseError(_('empty sequence'))
190 val = func(self._values)
191 return self._wrapvalue(val, val)
192
154 def _wrapvalue(self, key, val):
193 def _wrapvalue(self, key, val):
155 if val is None:
194 if val is None:
156 return
195 return
157 return wraphybridvalue(self, key, val)
196 return wraphybridvalue(self, key, val)
158
197
159 def itermaps(self, context):
198 def itermaps(self, context):
160 makemap = self._makemap
199 makemap = self._makemap
161 for x in self._values:
200 for x in self._values:
162 yield makemap(x)
201 yield makemap(x)
163
202
164 def join(self, context, mapping, sep):
203 def join(self, context, mapping, sep):
165 # TODO: switch gen to (context, mapping) API?
204 # TODO: switch gen to (context, mapping) API?
166 return joinitems((self._joinfmt(x) for x in self._values), sep)
205 return joinitems((self._joinfmt(x) for x in self._values), sep)
167
206
168 def show(self, context, mapping):
207 def show(self, context, mapping):
169 # TODO: switch gen to (context, mapping) API?
208 # TODO: switch gen to (context, mapping) API?
170 gen = self._gen
209 gen = self._gen
171 if gen is None:
210 if gen is None:
172 return self.join(context, mapping, ' ')
211 return self.join(context, mapping, ' ')
173 if callable(gen):
212 if callable(gen):
174 return gen()
213 return gen()
175 return gen
214 return gen
176
215
177 def tovalue(self, context, mapping):
216 def tovalue(self, context, mapping):
178 # TODO: return self._values and get rid of proxy methods
217 # TODO: return self._values and get rid of proxy methods
179 return self
218 return self
180
219
181 def __contains__(self, x):
220 def __contains__(self, x):
182 return x in self._values
221 return x in self._values
183 def __getitem__(self, key):
222 def __getitem__(self, key):
184 return self._values[key]
223 return self._values[key]
185 def __len__(self):
224 def __len__(self):
186 return len(self._values)
225 return len(self._values)
187 def __iter__(self):
226 def __iter__(self):
188 return iter(self._values)
227 return iter(self._values)
189 def __getattr__(self, name):
228 def __getattr__(self, name):
190 if name not in (r'get', r'items', r'iteritems', r'iterkeys',
229 if name not in (r'get', r'items', r'iteritems', r'iterkeys',
191 r'itervalues', r'keys', r'values'):
230 r'itervalues', r'keys', r'values'):
192 raise AttributeError(name)
231 raise AttributeError(name)
193 return getattr(self._values, name)
232 return getattr(self._values, name)
194
233
195 class mappable(wrapped):
234 class mappable(wrapped):
196 """Wrapper for non-list/dict object to support map operation
235 """Wrapper for non-list/dict object to support map operation
197
236
198 This class allows us to handle both:
237 This class allows us to handle both:
199 - "{manifest}"
238 - "{manifest}"
200 - "{manifest % '{rev}:{node}'}"
239 - "{manifest % '{rev}:{node}'}"
201 - "{manifest.rev}"
240 - "{manifest.rev}"
202
241
203 Unlike a hybrid, this does not simulate the behavior of the underling
242 Unlike a hybrid, this does not simulate the behavior of the underling
204 value.
243 value.
205 """
244 """
206
245
207 def __init__(self, gen, key, value, makemap):
246 def __init__(self, gen, key, value, makemap):
208 self._gen = gen # generator or function returning generator
247 self._gen = gen # generator or function returning generator
209 self._key = key
248 self._key = key
210 self._value = value # may be generator of strings
249 self._value = value # may be generator of strings
211 self._makemap = makemap
250 self._makemap = makemap
212
251
213 def tomap(self):
252 def tomap(self):
214 return self._makemap(self._key)
253 return self._makemap(self._key)
215
254
216 def getmember(self, context, mapping, key):
255 def getmember(self, context, mapping, key):
217 w = makewrapped(context, mapping, self._value)
256 w = makewrapped(context, mapping, self._value)
218 return w.getmember(context, mapping, key)
257 return w.getmember(context, mapping, key)
219
258
259 def getmin(self, context, mapping):
260 w = makewrapped(context, mapping, self._value)
261 return w.getmin(context, mapping)
262
263 def getmax(self, context, mapping):
264 w = makewrapped(context, mapping, self._value)
265 return w.getmax(context, mapping)
266
220 def itermaps(self, context):
267 def itermaps(self, context):
221 yield self.tomap()
268 yield self.tomap()
222
269
223 def join(self, context, mapping, sep):
270 def join(self, context, mapping, sep):
224 w = makewrapped(context, mapping, self._value)
271 w = makewrapped(context, mapping, self._value)
225 return w.join(context, mapping, sep)
272 return w.join(context, mapping, sep)
226
273
227 def show(self, context, mapping):
274 def show(self, context, mapping):
228 # TODO: switch gen to (context, mapping) API?
275 # TODO: switch gen to (context, mapping) API?
229 gen = self._gen
276 gen = self._gen
230 if gen is None:
277 if gen is None:
231 return pycompat.bytestr(self._value)
278 return pycompat.bytestr(self._value)
232 if callable(gen):
279 if callable(gen):
233 return gen()
280 return gen()
234 return gen
281 return gen
235
282
236 def tovalue(self, context, mapping):
283 def tovalue(self, context, mapping):
237 return _unthunk(context, mapping, self._value)
284 return _unthunk(context, mapping, self._value)
238
285
239 class _mappingsequence(wrapped):
286 class _mappingsequence(wrapped):
240 """Wrapper for sequence of template mappings
287 """Wrapper for sequence of template mappings
241
288
242 This represents an inner template structure (i.e. a list of dicts),
289 This represents an inner template structure (i.e. a list of dicts),
243 which can also be rendered by the specified named/literal template.
290 which can also be rendered by the specified named/literal template.
244
291
245 Template mappings may be nested.
292 Template mappings may be nested.
246 """
293 """
247
294
248 def __init__(self, name=None, tmpl=None, sep=''):
295 def __init__(self, name=None, tmpl=None, sep=''):
249 if name is not None and tmpl is not None:
296 if name is not None and tmpl is not None:
250 raise error.ProgrammingError('name and tmpl are mutually exclusive')
297 raise error.ProgrammingError('name and tmpl are mutually exclusive')
251 self._name = name
298 self._name = name
252 self._tmpl = tmpl
299 self._tmpl = tmpl
253 self._defaultsep = sep
300 self._defaultsep = sep
254
301
255 def getmember(self, context, mapping, key):
302 def getmember(self, context, mapping, key):
256 raise error.ParseError(_('not a dictionary'))
303 raise error.ParseError(_('not a dictionary'))
257
304
305 def getmin(self, context, mapping):
306 raise error.ParseError(_('not comparable'))
307
308 def getmax(self, context, mapping):
309 raise error.ParseError(_('not comparable'))
310
258 def join(self, context, mapping, sep):
311 def join(self, context, mapping, sep):
259 mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context))
312 mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context))
260 if self._name:
313 if self._name:
261 itemiter = (context.process(self._name, m) for m in mapsiter)
314 itemiter = (context.process(self._name, m) for m in mapsiter)
262 elif self._tmpl:
315 elif self._tmpl:
263 itemiter = (context.expand(self._tmpl, m) for m in mapsiter)
316 itemiter = (context.expand(self._tmpl, m) for m in mapsiter)
264 else:
317 else:
265 raise error.ParseError(_('not displayable without template'))
318 raise error.ParseError(_('not displayable without template'))
266 return joinitems(itemiter, sep)
319 return joinitems(itemiter, sep)
267
320
268 def show(self, context, mapping):
321 def show(self, context, mapping):
269 return self.join(context, mapping, self._defaultsep)
322 return self.join(context, mapping, self._defaultsep)
270
323
271 def tovalue(self, context, mapping):
324 def tovalue(self, context, mapping):
272 knownres = context.knownresourcekeys()
325 knownres = context.knownresourcekeys()
273 items = []
326 items = []
274 for nm in self.itermaps(context):
327 for nm in self.itermaps(context):
275 # drop internal resources (recursively) which shouldn't be displayed
328 # drop internal resources (recursively) which shouldn't be displayed
276 lm = context.overlaymap(mapping, nm)
329 lm = context.overlaymap(mapping, nm)
277 items.append({k: unwrapvalue(context, lm, v)
330 items.append({k: unwrapvalue(context, lm, v)
278 for k, v in nm.iteritems() if k not in knownres})
331 for k, v in nm.iteritems() if k not in knownres})
279 return items
332 return items
280
333
281 class mappinggenerator(_mappingsequence):
334 class mappinggenerator(_mappingsequence):
282 """Wrapper for generator of template mappings
335 """Wrapper for generator of template mappings
283
336
284 The function ``make(context, *args)`` should return a generator of
337 The function ``make(context, *args)`` should return a generator of
285 mapping dicts.
338 mapping dicts.
286 """
339 """
287
340
288 def __init__(self, make, args=(), name=None, tmpl=None, sep=''):
341 def __init__(self, make, args=(), name=None, tmpl=None, sep=''):
289 super(mappinggenerator, self).__init__(name, tmpl, sep)
342 super(mappinggenerator, self).__init__(name, tmpl, sep)
290 self._make = make
343 self._make = make
291 self._args = args
344 self._args = args
292
345
293 def itermaps(self, context):
346 def itermaps(self, context):
294 return self._make(context, *self._args)
347 return self._make(context, *self._args)
295
348
296 class mappinglist(_mappingsequence):
349 class mappinglist(_mappingsequence):
297 """Wrapper for list of template mappings"""
350 """Wrapper for list of template mappings"""
298
351
299 def __init__(self, mappings, name=None, tmpl=None, sep=''):
352 def __init__(self, mappings, name=None, tmpl=None, sep=''):
300 super(mappinglist, self).__init__(name, tmpl, sep)
353 super(mappinglist, self).__init__(name, tmpl, sep)
301 self._mappings = mappings
354 self._mappings = mappings
302
355
303 def itermaps(self, context):
356 def itermaps(self, context):
304 return iter(self._mappings)
357 return iter(self._mappings)
305
358
306 class mappedgenerator(wrapped):
359 class mappedgenerator(wrapped):
307 """Wrapper for generator of strings which acts as a list
360 """Wrapper for generator of strings which acts as a list
308
361
309 The function ``make(context, *args)`` should return a generator of
362 The function ``make(context, *args)`` should return a generator of
310 byte strings, or a generator of (possibly nested) generators of byte
363 byte strings, or a generator of (possibly nested) generators of byte
311 strings (i.e. a generator for a list of byte strings.)
364 strings (i.e. a generator for a list of byte strings.)
312 """
365 """
313
366
314 def __init__(self, make, args=()):
367 def __init__(self, make, args=()):
315 self._make = make
368 self._make = make
316 self._args = args
369 self._args = args
317
370
318 def _gen(self, context):
371 def _gen(self, context):
319 return self._make(context, *self._args)
372 return self._make(context, *self._args)
320
373
321 def getmember(self, context, mapping, key):
374 def getmember(self, context, mapping, key):
322 raise error.ParseError(_('not a dictionary'))
375 raise error.ParseError(_('not a dictionary'))
323
376
377 def getmin(self, context, mapping):
378 return self._getby(context, mapping, min)
379
380 def getmax(self, context, mapping):
381 return self._getby(context, mapping, max)
382
383 def _getby(self, context, mapping, func):
384 xs = self.tovalue(context, mapping)
385 if not xs:
386 raise error.ParseError(_('empty sequence'))
387 return func(xs)
388
324 def itermaps(self, context):
389 def itermaps(self, context):
325 raise error.ParseError(_('list of strings is not mappable'))
390 raise error.ParseError(_('list of strings is not mappable'))
326
391
327 def join(self, context, mapping, sep):
392 def join(self, context, mapping, sep):
328 return joinitems(self._gen(context), sep)
393 return joinitems(self._gen(context), sep)
329
394
330 def show(self, context, mapping):
395 def show(self, context, mapping):
331 return self.join(context, mapping, '')
396 return self.join(context, mapping, '')
332
397
333 def tovalue(self, context, mapping):
398 def tovalue(self, context, mapping):
334 return [stringify(context, mapping, x) for x in self._gen(context)]
399 return [stringify(context, mapping, x) for x in self._gen(context)]
335
400
336 def hybriddict(data, key='key', value='value', fmt=None, gen=None):
401 def hybriddict(data, key='key', value='value', fmt=None, gen=None):
337 """Wrap data to support both dict-like and string-like operations"""
402 """Wrap data to support both dict-like and string-like operations"""
338 prefmt = pycompat.identity
403 prefmt = pycompat.identity
339 if fmt is None:
404 if fmt is None:
340 fmt = '%s=%s'
405 fmt = '%s=%s'
341 prefmt = pycompat.bytestr
406 prefmt = pycompat.bytestr
342 return hybrid(gen, data, lambda k: {key: k, value: data[k]},
407 return hybrid(gen, data, lambda k: {key: k, value: data[k]},
343 lambda k: fmt % (prefmt(k), prefmt(data[k])))
408 lambda k: fmt % (prefmt(k), prefmt(data[k])))
344
409
345 def hybridlist(data, name, fmt=None, gen=None):
410 def hybridlist(data, name, fmt=None, gen=None):
346 """Wrap data to support both list-like and string-like operations"""
411 """Wrap data to support both list-like and string-like operations"""
347 prefmt = pycompat.identity
412 prefmt = pycompat.identity
348 if fmt is None:
413 if fmt is None:
349 fmt = '%s'
414 fmt = '%s'
350 prefmt = pycompat.bytestr
415 prefmt = pycompat.bytestr
351 return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
416 return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
352
417
353 def unwraphybrid(context, mapping, thing):
418 def unwraphybrid(context, mapping, thing):
354 """Return an object which can be stringified possibly by using a legacy
419 """Return an object which can be stringified possibly by using a legacy
355 template"""
420 template"""
356 if not isinstance(thing, wrapped):
421 if not isinstance(thing, wrapped):
357 return thing
422 return thing
358 return thing.show(context, mapping)
423 return thing.show(context, mapping)
359
424
360 def wraphybridvalue(container, key, value):
425 def wraphybridvalue(container, key, value):
361 """Wrap an element of hybrid container to be mappable
426 """Wrap an element of hybrid container to be mappable
362
427
363 The key is passed to the makemap function of the given container, which
428 The key is passed to the makemap function of the given container, which
364 should be an item generated by iter(container).
429 should be an item generated by iter(container).
365 """
430 """
366 makemap = getattr(container, '_makemap', None)
431 makemap = getattr(container, '_makemap', None)
367 if makemap is None:
432 if makemap is None:
368 return value
433 return value
369 if util.safehasattr(value, '_makemap'):
434 if util.safehasattr(value, '_makemap'):
370 # a nested hybrid list/dict, which has its own way of map operation
435 # a nested hybrid list/dict, which has its own way of map operation
371 return value
436 return value
372 return mappable(None, key, value, makemap)
437 return mappable(None, key, value, makemap)
373
438
374 def compatdict(context, mapping, name, data, key='key', value='value',
439 def compatdict(context, mapping, name, data, key='key', value='value',
375 fmt=None, plural=None, separator=' '):
440 fmt=None, plural=None, separator=' '):
376 """Wrap data like hybriddict(), but also supports old-style list template
441 """Wrap data like hybriddict(), but also supports old-style list template
377
442
378 This exists for backward compatibility with the old-style template. Use
443 This exists for backward compatibility with the old-style template. Use
379 hybriddict() for new template keywords.
444 hybriddict() for new template keywords.
380 """
445 """
381 c = [{key: k, value: v} for k, v in data.iteritems()]
446 c = [{key: k, value: v} for k, v in data.iteritems()]
382 f = _showcompatlist(context, mapping, name, c, plural, separator)
447 f = _showcompatlist(context, mapping, name, c, plural, separator)
383 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
448 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
384
449
385 def compatlist(context, mapping, name, data, element=None, fmt=None,
450 def compatlist(context, mapping, name, data, element=None, fmt=None,
386 plural=None, separator=' '):
451 plural=None, separator=' '):
387 """Wrap data like hybridlist(), but also supports old-style list template
452 """Wrap data like hybridlist(), but also supports old-style list template
388
453
389 This exists for backward compatibility with the old-style template. Use
454 This exists for backward compatibility with the old-style template. Use
390 hybridlist() for new template keywords.
455 hybridlist() for new template keywords.
391 """
456 """
392 f = _showcompatlist(context, mapping, name, data, plural, separator)
457 f = _showcompatlist(context, mapping, name, data, plural, separator)
393 return hybridlist(data, name=element or name, fmt=fmt, gen=f)
458 return hybridlist(data, name=element or name, fmt=fmt, gen=f)
394
459
395 def _showcompatlist(context, mapping, name, values, plural=None, separator=' '):
460 def _showcompatlist(context, mapping, name, values, plural=None, separator=' '):
396 """Return a generator that renders old-style list template
461 """Return a generator that renders old-style list template
397
462
398 name is name of key in template map.
463 name is name of key in template map.
399 values is list of strings or dicts.
464 values is list of strings or dicts.
400 plural is plural of name, if not simply name + 's'.
465 plural is plural of name, if not simply name + 's'.
401 separator is used to join values as a string
466 separator is used to join values as a string
402
467
403 expansion works like this, given name 'foo'.
468 expansion works like this, given name 'foo'.
404
469
405 if values is empty, expand 'no_foos'.
470 if values is empty, expand 'no_foos'.
406
471
407 if 'foo' not in template map, return values as a string,
472 if 'foo' not in template map, return values as a string,
408 joined by 'separator'.
473 joined by 'separator'.
409
474
410 expand 'start_foos'.
475 expand 'start_foos'.
411
476
412 for each value, expand 'foo'. if 'last_foo' in template
477 for each value, expand 'foo'. if 'last_foo' in template
413 map, expand it instead of 'foo' for last key.
478 map, expand it instead of 'foo' for last key.
414
479
415 expand 'end_foos'.
480 expand 'end_foos'.
416 """
481 """
417 if not plural:
482 if not plural:
418 plural = name + 's'
483 plural = name + 's'
419 if not values:
484 if not values:
420 noname = 'no_' + plural
485 noname = 'no_' + plural
421 if context.preload(noname):
486 if context.preload(noname):
422 yield context.process(noname, mapping)
487 yield context.process(noname, mapping)
423 return
488 return
424 if not context.preload(name):
489 if not context.preload(name):
425 if isinstance(values[0], bytes):
490 if isinstance(values[0], bytes):
426 yield separator.join(values)
491 yield separator.join(values)
427 else:
492 else:
428 for v in values:
493 for v in values:
429 r = dict(v)
494 r = dict(v)
430 r.update(mapping)
495 r.update(mapping)
431 yield r
496 yield r
432 return
497 return
433 startname = 'start_' + plural
498 startname = 'start_' + plural
434 if context.preload(startname):
499 if context.preload(startname):
435 yield context.process(startname, mapping)
500 yield context.process(startname, mapping)
436 def one(v, tag=name):
501 def one(v, tag=name):
437 vmapping = {}
502 vmapping = {}
438 try:
503 try:
439 vmapping.update(v)
504 vmapping.update(v)
440 # Python 2 raises ValueError if the type of v is wrong. Python
505 # Python 2 raises ValueError if the type of v is wrong. Python
441 # 3 raises TypeError.
506 # 3 raises TypeError.
442 except (AttributeError, TypeError, ValueError):
507 except (AttributeError, TypeError, ValueError):
443 try:
508 try:
444 # Python 2 raises ValueError trying to destructure an e.g.
509 # Python 2 raises ValueError trying to destructure an e.g.
445 # bytes. Python 3 raises TypeError.
510 # bytes. Python 3 raises TypeError.
446 for a, b in v:
511 for a, b in v:
447 vmapping[a] = b
512 vmapping[a] = b
448 except (TypeError, ValueError):
513 except (TypeError, ValueError):
449 vmapping[name] = v
514 vmapping[name] = v
450 vmapping = context.overlaymap(mapping, vmapping)
515 vmapping = context.overlaymap(mapping, vmapping)
451 return context.process(tag, vmapping)
516 return context.process(tag, vmapping)
452 lastname = 'last_' + name
517 lastname = 'last_' + name
453 if context.preload(lastname):
518 if context.preload(lastname):
454 last = values.pop()
519 last = values.pop()
455 else:
520 else:
456 last = None
521 last = None
457 for v in values:
522 for v in values:
458 yield one(v)
523 yield one(v)
459 if last is not None:
524 if last is not None:
460 yield one(last, tag=lastname)
525 yield one(last, tag=lastname)
461 endname = 'end_' + plural
526 endname = 'end_' + plural
462 if context.preload(endname):
527 if context.preload(endname):
463 yield context.process(endname, mapping)
528 yield context.process(endname, mapping)
464
529
465 def flatten(context, mapping, thing):
530 def flatten(context, mapping, thing):
466 """Yield a single stream from a possibly nested set of iterators"""
531 """Yield a single stream from a possibly nested set of iterators"""
467 thing = unwraphybrid(context, mapping, thing)
532 thing = unwraphybrid(context, mapping, thing)
468 if isinstance(thing, bytes):
533 if isinstance(thing, bytes):
469 yield thing
534 yield thing
470 elif isinstance(thing, str):
535 elif isinstance(thing, str):
471 # We can only hit this on Python 3, and it's here to guard
536 # We can only hit this on Python 3, and it's here to guard
472 # against infinite recursion.
537 # against infinite recursion.
473 raise error.ProgrammingError('Mercurial IO including templates is done'
538 raise error.ProgrammingError('Mercurial IO including templates is done'
474 ' with bytes, not strings, got %r' % thing)
539 ' with bytes, not strings, got %r' % thing)
475 elif thing is None:
540 elif thing is None:
476 pass
541 pass
477 elif not util.safehasattr(thing, '__iter__'):
542 elif not util.safehasattr(thing, '__iter__'):
478 yield pycompat.bytestr(thing)
543 yield pycompat.bytestr(thing)
479 else:
544 else:
480 for i in thing:
545 for i in thing:
481 i = unwraphybrid(context, mapping, i)
546 i = unwraphybrid(context, mapping, i)
482 if isinstance(i, bytes):
547 if isinstance(i, bytes):
483 yield i
548 yield i
484 elif i is None:
549 elif i is None:
485 pass
550 pass
486 elif not util.safehasattr(i, '__iter__'):
551 elif not util.safehasattr(i, '__iter__'):
487 yield pycompat.bytestr(i)
552 yield pycompat.bytestr(i)
488 else:
553 else:
489 for j in flatten(context, mapping, i):
554 for j in flatten(context, mapping, i):
490 yield j
555 yield j
491
556
492 def stringify(context, mapping, thing):
557 def stringify(context, mapping, thing):
493 """Turn values into bytes by converting into text and concatenating them"""
558 """Turn values into bytes by converting into text and concatenating them"""
494 if isinstance(thing, bytes):
559 if isinstance(thing, bytes):
495 return thing # retain localstr to be round-tripped
560 return thing # retain localstr to be round-tripped
496 return b''.join(flatten(context, mapping, thing))
561 return b''.join(flatten(context, mapping, thing))
497
562
498 def findsymbolicname(arg):
563 def findsymbolicname(arg):
499 """Find symbolic name for the given compiled expression; returns None
564 """Find symbolic name for the given compiled expression; returns None
500 if nothing found reliably"""
565 if nothing found reliably"""
501 while True:
566 while True:
502 func, data = arg
567 func, data = arg
503 if func is runsymbol:
568 if func is runsymbol:
504 return data
569 return data
505 elif func is runfilter:
570 elif func is runfilter:
506 arg = data[0]
571 arg = data[0]
507 else:
572 else:
508 return None
573 return None
509
574
510 def _unthunk(context, mapping, thing):
575 def _unthunk(context, mapping, thing):
511 """Evaluate a lazy byte string into value"""
576 """Evaluate a lazy byte string into value"""
512 if not isinstance(thing, types.GeneratorType):
577 if not isinstance(thing, types.GeneratorType):
513 return thing
578 return thing
514 return stringify(context, mapping, thing)
579 return stringify(context, mapping, thing)
515
580
516 def evalrawexp(context, mapping, arg):
581 def evalrawexp(context, mapping, arg):
517 """Evaluate given argument as a bare template object which may require
582 """Evaluate given argument as a bare template object which may require
518 further processing (such as folding generator of strings)"""
583 further processing (such as folding generator of strings)"""
519 func, data = arg
584 func, data = arg
520 return func(context, mapping, data)
585 return func(context, mapping, data)
521
586
522 def evalwrapped(context, mapping, arg):
587 def evalwrapped(context, mapping, arg):
523 """Evaluate given argument to wrapped object"""
588 """Evaluate given argument to wrapped object"""
524 thing = evalrawexp(context, mapping, arg)
589 thing = evalrawexp(context, mapping, arg)
525 return makewrapped(context, mapping, thing)
590 return makewrapped(context, mapping, thing)
526
591
527 def makewrapped(context, mapping, thing):
592 def makewrapped(context, mapping, thing):
528 """Lift object to a wrapped type"""
593 """Lift object to a wrapped type"""
529 if isinstance(thing, wrapped):
594 if isinstance(thing, wrapped):
530 return thing
595 return thing
531 thing = _unthunk(context, mapping, thing)
596 thing = _unthunk(context, mapping, thing)
532 if isinstance(thing, bytes):
597 if isinstance(thing, bytes):
533 return wrappedbytes(thing)
598 return wrappedbytes(thing)
534 return wrappedvalue(thing)
599 return wrappedvalue(thing)
535
600
536 def evalfuncarg(context, mapping, arg):
601 def evalfuncarg(context, mapping, arg):
537 """Evaluate given argument as value type"""
602 """Evaluate given argument as value type"""
538 return unwrapvalue(context, mapping, evalrawexp(context, mapping, arg))
603 return unwrapvalue(context, mapping, evalrawexp(context, mapping, arg))
539
604
540 def unwrapvalue(context, mapping, thing):
605 def unwrapvalue(context, mapping, thing):
541 """Move the inner value object out of the wrapper"""
606 """Move the inner value object out of the wrapper"""
542 if isinstance(thing, wrapped):
607 if isinstance(thing, wrapped):
543 return thing.tovalue(context, mapping)
608 return thing.tovalue(context, mapping)
544 # evalrawexp() may return string, generator of strings or arbitrary object
609 # evalrawexp() may return string, generator of strings or arbitrary object
545 # such as date tuple, but filter does not want generator.
610 # such as date tuple, but filter does not want generator.
546 return _unthunk(context, mapping, thing)
611 return _unthunk(context, mapping, thing)
547
612
548 def evalboolean(context, mapping, arg):
613 def evalboolean(context, mapping, arg):
549 """Evaluate given argument as boolean, but also takes boolean literals"""
614 """Evaluate given argument as boolean, but also takes boolean literals"""
550 func, data = arg
615 func, data = arg
551 if func is runsymbol:
616 if func is runsymbol:
552 thing = func(context, mapping, data, default=None)
617 thing = func(context, mapping, data, default=None)
553 if thing is None:
618 if thing is None:
554 # not a template keyword, takes as a boolean literal
619 # not a template keyword, takes as a boolean literal
555 thing = stringutil.parsebool(data)
620 thing = stringutil.parsebool(data)
556 else:
621 else:
557 thing = func(context, mapping, data)
622 thing = func(context, mapping, data)
558 if isinstance(thing, wrapped):
623 if isinstance(thing, wrapped):
559 thing = thing.tovalue(context, mapping)
624 thing = thing.tovalue(context, mapping)
560 if isinstance(thing, bool):
625 if isinstance(thing, bool):
561 return thing
626 return thing
562 # other objects are evaluated as strings, which means 0 is True, but
627 # other objects are evaluated as strings, which means 0 is True, but
563 # empty dict/list should be False as they are expected to be ''
628 # empty dict/list should be False as they are expected to be ''
564 return bool(stringify(context, mapping, thing))
629 return bool(stringify(context, mapping, thing))
565
630
566 def evaldate(context, mapping, arg, err=None):
631 def evaldate(context, mapping, arg, err=None):
567 """Evaluate given argument as a date tuple or a date string; returns
632 """Evaluate given argument as a date tuple or a date string; returns
568 a (unixtime, offset) tuple"""
633 a (unixtime, offset) tuple"""
569 thing = evalrawexp(context, mapping, arg)
634 thing = evalrawexp(context, mapping, arg)
570 return unwrapdate(context, mapping, thing, err)
635 return unwrapdate(context, mapping, thing, err)
571
636
572 def unwrapdate(context, mapping, thing, err=None):
637 def unwrapdate(context, mapping, thing, err=None):
573 thing = unwrapvalue(context, mapping, thing)
638 thing = unwrapvalue(context, mapping, thing)
574 try:
639 try:
575 return dateutil.parsedate(thing)
640 return dateutil.parsedate(thing)
576 except AttributeError:
641 except AttributeError:
577 raise error.ParseError(err or _('not a date tuple nor a string'))
642 raise error.ParseError(err or _('not a date tuple nor a string'))
578 except error.ParseError:
643 except error.ParseError:
579 if not err:
644 if not err:
580 raise
645 raise
581 raise error.ParseError(err)
646 raise error.ParseError(err)
582
647
583 def evalinteger(context, mapping, arg, err=None):
648 def evalinteger(context, mapping, arg, err=None):
584 thing = evalrawexp(context, mapping, arg)
649 thing = evalrawexp(context, mapping, arg)
585 return unwrapinteger(context, mapping, thing, err)
650 return unwrapinteger(context, mapping, thing, err)
586
651
587 def unwrapinteger(context, mapping, thing, err=None):
652 def unwrapinteger(context, mapping, thing, err=None):
588 thing = unwrapvalue(context, mapping, thing)
653 thing = unwrapvalue(context, mapping, thing)
589 try:
654 try:
590 return int(thing)
655 return int(thing)
591 except (TypeError, ValueError):
656 except (TypeError, ValueError):
592 raise error.ParseError(err or _('not an integer'))
657 raise error.ParseError(err or _('not an integer'))
593
658
594 def evalstring(context, mapping, arg):
659 def evalstring(context, mapping, arg):
595 return stringify(context, mapping, evalrawexp(context, mapping, arg))
660 return stringify(context, mapping, evalrawexp(context, mapping, arg))
596
661
597 def evalstringliteral(context, mapping, arg):
662 def evalstringliteral(context, mapping, arg):
598 """Evaluate given argument as string template, but returns symbol name
663 """Evaluate given argument as string template, but returns symbol name
599 if it is unknown"""
664 if it is unknown"""
600 func, data = arg
665 func, data = arg
601 if func is runsymbol:
666 if func is runsymbol:
602 thing = func(context, mapping, data, default=data)
667 thing = func(context, mapping, data, default=data)
603 else:
668 else:
604 thing = func(context, mapping, data)
669 thing = func(context, mapping, data)
605 return stringify(context, mapping, thing)
670 return stringify(context, mapping, thing)
606
671
607 _unwrapfuncbytype = {
672 _unwrapfuncbytype = {
608 None: unwrapvalue,
673 None: unwrapvalue,
609 bytes: stringify,
674 bytes: stringify,
610 date: unwrapdate,
675 date: unwrapdate,
611 int: unwrapinteger,
676 int: unwrapinteger,
612 }
677 }
613
678
614 def unwrapastype(context, mapping, thing, typ):
679 def unwrapastype(context, mapping, thing, typ):
615 """Move the inner value object out of the wrapper and coerce its type"""
680 """Move the inner value object out of the wrapper and coerce its type"""
616 try:
681 try:
617 f = _unwrapfuncbytype[typ]
682 f = _unwrapfuncbytype[typ]
618 except KeyError:
683 except KeyError:
619 raise error.ProgrammingError('invalid type specified: %r' % typ)
684 raise error.ProgrammingError('invalid type specified: %r' % typ)
620 return f(context, mapping, thing)
685 return f(context, mapping, thing)
621
686
622 def runinteger(context, mapping, data):
687 def runinteger(context, mapping, data):
623 return int(data)
688 return int(data)
624
689
625 def runstring(context, mapping, data):
690 def runstring(context, mapping, data):
626 return data
691 return data
627
692
628 def _recursivesymbolblocker(key):
693 def _recursivesymbolblocker(key):
629 def showrecursion(**args):
694 def showrecursion(**args):
630 raise error.Abort(_("recursive reference '%s' in template") % key)
695 raise error.Abort(_("recursive reference '%s' in template") % key)
631 return showrecursion
696 return showrecursion
632
697
633 def runsymbol(context, mapping, key, default=''):
698 def runsymbol(context, mapping, key, default=''):
634 v = context.symbol(mapping, key)
699 v = context.symbol(mapping, key)
635 if v is None:
700 if v is None:
636 # put poison to cut recursion. we can't move this to parsing phase
701 # put poison to cut recursion. we can't move this to parsing phase
637 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
702 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
638 safemapping = mapping.copy()
703 safemapping = mapping.copy()
639 safemapping[key] = _recursivesymbolblocker(key)
704 safemapping[key] = _recursivesymbolblocker(key)
640 try:
705 try:
641 v = context.process(key, safemapping)
706 v = context.process(key, safemapping)
642 except TemplateNotFound:
707 except TemplateNotFound:
643 v = default
708 v = default
644 if callable(v) and getattr(v, '_requires', None) is None:
709 if callable(v) and getattr(v, '_requires', None) is None:
645 # old templatekw: expand all keywords and resources
710 # old templatekw: expand all keywords and resources
646 # (TODO: deprecate this after porting web template keywords to new API)
711 # (TODO: deprecate this after porting web template keywords to new API)
647 props = {k: context._resources.lookup(context, mapping, k)
712 props = {k: context._resources.lookup(context, mapping, k)
648 for k in context._resources.knownkeys()}
713 for k in context._resources.knownkeys()}
649 # pass context to _showcompatlist() through templatekw._showlist()
714 # pass context to _showcompatlist() through templatekw._showlist()
650 props['templ'] = context
715 props['templ'] = context
651 props.update(mapping)
716 props.update(mapping)
652 return v(**pycompat.strkwargs(props))
717 return v(**pycompat.strkwargs(props))
653 if callable(v):
718 if callable(v):
654 # new templatekw
719 # new templatekw
655 try:
720 try:
656 return v(context, mapping)
721 return v(context, mapping)
657 except ResourceUnavailable:
722 except ResourceUnavailable:
658 # unsupported keyword is mapped to empty just like unknown keyword
723 # unsupported keyword is mapped to empty just like unknown keyword
659 return None
724 return None
660 return v
725 return v
661
726
662 def runtemplate(context, mapping, template):
727 def runtemplate(context, mapping, template):
663 for arg in template:
728 for arg in template:
664 yield evalrawexp(context, mapping, arg)
729 yield evalrawexp(context, mapping, arg)
665
730
666 def runfilter(context, mapping, data):
731 def runfilter(context, mapping, data):
667 arg, filt = data
732 arg, filt = data
668 thing = evalrawexp(context, mapping, arg)
733 thing = evalrawexp(context, mapping, arg)
669 intype = getattr(filt, '_intype', None)
734 intype = getattr(filt, '_intype', None)
670 try:
735 try:
671 thing = unwrapastype(context, mapping, thing, intype)
736 thing = unwrapastype(context, mapping, thing, intype)
672 return filt(thing)
737 return filt(thing)
673 except error.ParseError as e:
738 except error.ParseError as e:
674 raise error.ParseError(bytes(e), hint=_formatfiltererror(arg, filt))
739 raise error.ParseError(bytes(e), hint=_formatfiltererror(arg, filt))
675
740
676 def _formatfiltererror(arg, filt):
741 def _formatfiltererror(arg, filt):
677 fn = pycompat.sysbytes(filt.__name__)
742 fn = pycompat.sysbytes(filt.__name__)
678 sym = findsymbolicname(arg)
743 sym = findsymbolicname(arg)
679 if not sym:
744 if not sym:
680 return _("incompatible use of template filter '%s'") % fn
745 return _("incompatible use of template filter '%s'") % fn
681 return (_("template filter '%s' is not compatible with keyword '%s'")
746 return (_("template filter '%s' is not compatible with keyword '%s'")
682 % (fn, sym))
747 % (fn, sym))
683
748
684 def _iteroverlaymaps(context, origmapping, newmappings):
749 def _iteroverlaymaps(context, origmapping, newmappings):
685 """Generate combined mappings from the original mapping and an iterable
750 """Generate combined mappings from the original mapping and an iterable
686 of partial mappings to override the original"""
751 of partial mappings to override the original"""
687 for i, nm in enumerate(newmappings):
752 for i, nm in enumerate(newmappings):
688 lm = context.overlaymap(origmapping, nm)
753 lm = context.overlaymap(origmapping, nm)
689 lm['index'] = i
754 lm['index'] = i
690 yield lm
755 yield lm
691
756
692 def _applymap(context, mapping, d, targ):
757 def _applymap(context, mapping, d, targ):
693 for lm in _iteroverlaymaps(context, mapping, d.itermaps(context)):
758 for lm in _iteroverlaymaps(context, mapping, d.itermaps(context)):
694 yield evalrawexp(context, lm, targ)
759 yield evalrawexp(context, lm, targ)
695
760
696 def runmap(context, mapping, data):
761 def runmap(context, mapping, data):
697 darg, targ = data
762 darg, targ = data
698 d = evalwrapped(context, mapping, darg)
763 d = evalwrapped(context, mapping, darg)
699 return mappedgenerator(_applymap, args=(mapping, d, targ))
764 return mappedgenerator(_applymap, args=(mapping, d, targ))
700
765
701 def runmember(context, mapping, data):
766 def runmember(context, mapping, data):
702 darg, memb = data
767 darg, memb = data
703 d = evalwrapped(context, mapping, darg)
768 d = evalwrapped(context, mapping, darg)
704 if util.safehasattr(d, 'tomap'):
769 if util.safehasattr(d, 'tomap'):
705 lm = context.overlaymap(mapping, d.tomap())
770 lm = context.overlaymap(mapping, d.tomap())
706 return runsymbol(context, lm, memb)
771 return runsymbol(context, lm, memb)
707 try:
772 try:
708 return d.getmember(context, mapping, memb)
773 return d.getmember(context, mapping, memb)
709 except error.ParseError as err:
774 except error.ParseError as err:
710 sym = findsymbolicname(darg)
775 sym = findsymbolicname(darg)
711 if not sym:
776 if not sym:
712 raise
777 raise
713 hint = _("keyword '%s' does not support member operation") % sym
778 hint = _("keyword '%s' does not support member operation") % sym
714 raise error.ParseError(bytes(err), hint=hint)
779 raise error.ParseError(bytes(err), hint=hint)
715
780
716 def runnegate(context, mapping, data):
781 def runnegate(context, mapping, data):
717 data = evalinteger(context, mapping, data,
782 data = evalinteger(context, mapping, data,
718 _('negation needs an integer argument'))
783 _('negation needs an integer argument'))
719 return -data
784 return -data
720
785
721 def runarithmetic(context, mapping, data):
786 def runarithmetic(context, mapping, data):
722 func, left, right = data
787 func, left, right = data
723 left = evalinteger(context, mapping, left,
788 left = evalinteger(context, mapping, left,
724 _('arithmetic only defined on integers'))
789 _('arithmetic only defined on integers'))
725 right = evalinteger(context, mapping, right,
790 right = evalinteger(context, mapping, right,
726 _('arithmetic only defined on integers'))
791 _('arithmetic only defined on integers'))
727 try:
792 try:
728 return func(left, right)
793 return func(left, right)
729 except ZeroDivisionError:
794 except ZeroDivisionError:
730 raise error.Abort(_('division by zero is not defined'))
795 raise error.Abort(_('division by zero is not defined'))
731
796
732 def joinitems(itemiter, sep):
797 def joinitems(itemiter, sep):
733 """Join items with the separator; Returns generator of bytes"""
798 """Join items with the separator; Returns generator of bytes"""
734 first = True
799 first = True
735 for x in itemiter:
800 for x in itemiter:
736 if first:
801 if first:
737 first = False
802 first = False
738 elif sep:
803 elif sep:
739 yield sep
804 yield sep
740 yield x
805 yield x
@@ -1,4913 +1,4958 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add a
4 $ hg add a
5 $ echo line 1 > b
5 $ echo line 1 > b
6 $ echo line 2 >> b
6 $ echo line 2 >> b
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8
8
9 $ hg add b
9 $ hg add b
10 $ echo other 1 > c
10 $ echo other 1 > c
11 $ echo other 2 >> c
11 $ echo other 2 >> c
12 $ echo >> c
12 $ echo >> c
13 $ echo other 3 >> c
13 $ echo other 3 >> c
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15
15
16 $ hg add c
16 $ hg add c
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 $ echo c >> c
18 $ echo c >> c
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20
20
21 $ echo foo > .hg/branch
21 $ echo foo > .hg/branch
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23
23
24 $ hg co -q 3
24 $ hg co -q 3
25 $ echo other 4 >> d
25 $ echo other 4 >> d
26 $ hg add d
26 $ hg add d
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28
28
29 $ hg merge -q foo
29 $ hg merge -q foo
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31
31
32 Test arithmetic operators have the right precedence:
32 Test arithmetic operators have the right precedence:
33
33
34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
35 2020 1964
35 2020 1964
36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
37 9860 5908
37 9860 5908
38
38
39 Test division:
39 Test division:
40
40
41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
42 (template
42 (template
43 (/
43 (/
44 (integer '5')
44 (integer '5')
45 (integer '2'))
45 (integer '2'))
46 (string ' ')
46 (string ' ')
47 (func
47 (func
48 (symbol 'mod')
48 (symbol 'mod')
49 (list
49 (list
50 (integer '5')
50 (integer '5')
51 (integer '2')))
51 (integer '2')))
52 (string '\n'))
52 (string '\n'))
53 2 1
53 2 1
54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
55 (template
55 (template
56 (/
56 (/
57 (integer '5')
57 (integer '5')
58 (negate
58 (negate
59 (integer '2')))
59 (integer '2')))
60 (string ' ')
60 (string ' ')
61 (func
61 (func
62 (symbol 'mod')
62 (symbol 'mod')
63 (list
63 (list
64 (integer '5')
64 (integer '5')
65 (negate
65 (negate
66 (integer '2'))))
66 (integer '2'))))
67 (string '\n'))
67 (string '\n'))
68 -3 -1
68 -3 -1
69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
70 (template
70 (template
71 (/
71 (/
72 (negate
72 (negate
73 (integer '5'))
73 (integer '5'))
74 (integer '2'))
74 (integer '2'))
75 (string ' ')
75 (string ' ')
76 (func
76 (func
77 (symbol 'mod')
77 (symbol 'mod')
78 (list
78 (list
79 (negate
79 (negate
80 (integer '5'))
80 (integer '5'))
81 (integer '2')))
81 (integer '2')))
82 (string '\n'))
82 (string '\n'))
83 -3 1
83 -3 1
84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
85 (template
85 (template
86 (/
86 (/
87 (negate
87 (negate
88 (integer '5'))
88 (integer '5'))
89 (negate
89 (negate
90 (integer '2')))
90 (integer '2')))
91 (string ' ')
91 (string ' ')
92 (func
92 (func
93 (symbol 'mod')
93 (symbol 'mod')
94 (list
94 (list
95 (negate
95 (negate
96 (integer '5'))
96 (integer '5'))
97 (negate
97 (negate
98 (integer '2'))))
98 (integer '2'))))
99 (string '\n'))
99 (string '\n'))
100 2 -1
100 2 -1
101
101
102 Filters bind closer than arithmetic:
102 Filters bind closer than arithmetic:
103
103
104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
105 (template
105 (template
106 (-
106 (-
107 (|
107 (|
108 (func
108 (func
109 (symbol 'revset')
109 (symbol 'revset')
110 (string '.'))
110 (string '.'))
111 (symbol 'count'))
111 (symbol 'count'))
112 (integer '1'))
112 (integer '1'))
113 (string '\n'))
113 (string '\n'))
114 0
114 0
115
115
116 But negate binds closer still:
116 But negate binds closer still:
117
117
118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
119 (template
119 (template
120 (-
120 (-
121 (integer '1')
121 (integer '1')
122 (|
122 (|
123 (integer '3')
123 (integer '3')
124 (symbol 'stringify')))
124 (symbol 'stringify')))
125 (string '\n'))
125 (string '\n'))
126 hg: parse error: arithmetic only defined on integers
126 hg: parse error: arithmetic only defined on integers
127 [255]
127 [255]
128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
129 (template
129 (template
130 (|
130 (|
131 (negate
131 (negate
132 (integer '3'))
132 (integer '3'))
133 (symbol 'stringify'))
133 (symbol 'stringify'))
134 (string '\n'))
134 (string '\n'))
135 -3
135 -3
136
136
137 Filters bind as close as map operator:
137 Filters bind as close as map operator:
138
138
139 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
139 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
140 (template
140 (template
141 (%
141 (%
142 (|
142 (|
143 (symbol 'desc')
143 (symbol 'desc')
144 (symbol 'splitlines'))
144 (symbol 'splitlines'))
145 (template
145 (template
146 (symbol 'line')
146 (symbol 'line')
147 (string '\n'))))
147 (string '\n'))))
148 line 1
148 line 1
149 line 2
149 line 2
150
150
151 Keyword arguments:
151 Keyword arguments:
152
152
153 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
153 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
154 (template
154 (template
155 (keyvalue
155 (keyvalue
156 (symbol 'foo')
156 (symbol 'foo')
157 (|
157 (|
158 (symbol 'bar')
158 (symbol 'bar')
159 (symbol 'baz'))))
159 (symbol 'baz'))))
160 hg: parse error: can't use a key-value pair in this context
160 hg: parse error: can't use a key-value pair in this context
161 [255]
161 [255]
162
162
163 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
163 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
164 foo
164 foo
165
165
166 Call function which takes named arguments by filter syntax:
166 Call function which takes named arguments by filter syntax:
167
167
168 $ hg debugtemplate '{" "|separate}'
168 $ hg debugtemplate '{" "|separate}'
169 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
169 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
170 hg: parse error: unknown method 'list'
170 hg: parse error: unknown method 'list'
171 [255]
171 [255]
172
172
173 Second branch starting at nullrev:
173 Second branch starting at nullrev:
174
174
175 $ hg update null
175 $ hg update null
176 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
176 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
177 $ echo second > second
177 $ echo second > second
178 $ hg add second
178 $ hg add second
179 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
179 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
180 created new head
180 created new head
181
181
182 $ echo third > third
182 $ echo third > third
183 $ hg add third
183 $ hg add third
184 $ hg mv second fourth
184 $ hg mv second fourth
185 $ hg commit -m third -d "2020-01-01 10:01"
185 $ hg commit -m third -d "2020-01-01 10:01"
186
186
187 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
187 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
188 fourth (second)
188 fourth (second)
189 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
189 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
190 second -> fourth
190 second -> fourth
191 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
191 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
192 8 t
192 8 t
193 7 f
193 7 f
194
194
195 Working-directory revision has special identifiers, though they are still
195 Working-directory revision has special identifiers, though they are still
196 experimental:
196 experimental:
197
197
198 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
198 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
199 2147483647:ffffffffffffffffffffffffffffffffffffffff
199 2147483647:ffffffffffffffffffffffffffffffffffffffff
200
200
201 Some keywords are invalid for working-directory revision, but they should
201 Some keywords are invalid for working-directory revision, but they should
202 never cause crash:
202 never cause crash:
203
203
204 $ hg log -r 'wdir()' -T '{manifest}\n'
204 $ hg log -r 'wdir()' -T '{manifest}\n'
205
205
206
206
207 Internal resources shouldn't be exposed (issue5699):
207 Internal resources shouldn't be exposed (issue5699):
208
208
209 $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}'
209 $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}'
210
210
211 Never crash on internal resource not available:
211 Never crash on internal resource not available:
212
212
213 $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n'
213 $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n'
214 abort: template resource not available: ctx
214 abort: template resource not available: ctx
215 [255]
215 [255]
216
216
217 $ hg config -T '{author}'
217 $ hg config -T '{author}'
218
218
219 Quoting for ui.logtemplate
219 Quoting for ui.logtemplate
220
220
221 $ hg tip --config "ui.logtemplate={rev}\n"
221 $ hg tip --config "ui.logtemplate={rev}\n"
222 8
222 8
223 $ hg tip --config "ui.logtemplate='{rev}\n'"
223 $ hg tip --config "ui.logtemplate='{rev}\n'"
224 8
224 8
225 $ hg tip --config 'ui.logtemplate="{rev}\n"'
225 $ hg tip --config 'ui.logtemplate="{rev}\n"'
226 8
226 8
227 $ hg tip --config 'ui.logtemplate=n{rev}\n'
227 $ hg tip --config 'ui.logtemplate=n{rev}\n'
228 n8
228 n8
229
229
230 Make sure user/global hgrc does not affect tests
230 Make sure user/global hgrc does not affect tests
231
231
232 $ echo '[ui]' > .hg/hgrc
232 $ echo '[ui]' > .hg/hgrc
233 $ echo 'logtemplate =' >> .hg/hgrc
233 $ echo 'logtemplate =' >> .hg/hgrc
234 $ echo 'style =' >> .hg/hgrc
234 $ echo 'style =' >> .hg/hgrc
235
235
236 Add some simple styles to settings
236 Add some simple styles to settings
237
237
238 $ cat <<'EOF' >> .hg/hgrc
238 $ cat <<'EOF' >> .hg/hgrc
239 > [templates]
239 > [templates]
240 > simple = "{rev}\n"
240 > simple = "{rev}\n"
241 > simple2 = {rev}\n
241 > simple2 = {rev}\n
242 > rev = "should not precede {rev} keyword\n"
242 > rev = "should not precede {rev} keyword\n"
243 > EOF
243 > EOF
244
244
245 $ hg log -l1 -Tsimple
245 $ hg log -l1 -Tsimple
246 8
246 8
247 $ hg log -l1 -Tsimple2
247 $ hg log -l1 -Tsimple2
248 8
248 8
249 $ hg log -l1 -Trev
249 $ hg log -l1 -Trev
250 should not precede 8 keyword
250 should not precede 8 keyword
251 $ hg log -l1 -T '{simple}'
251 $ hg log -l1 -T '{simple}'
252 8
252 8
253
253
254 Map file shouldn't see user templates:
254 Map file shouldn't see user templates:
255
255
256 $ cat <<EOF > tmpl
256 $ cat <<EOF > tmpl
257 > changeset = 'nothing expanded:{simple}\n'
257 > changeset = 'nothing expanded:{simple}\n'
258 > EOF
258 > EOF
259 $ hg log -l1 --style ./tmpl
259 $ hg log -l1 --style ./tmpl
260 nothing expanded:
260 nothing expanded:
261
261
262 Test templates and style maps in files:
262 Test templates and style maps in files:
263
263
264 $ echo "{rev}" > tmpl
264 $ echo "{rev}" > tmpl
265 $ hg log -l1 -T./tmpl
265 $ hg log -l1 -T./tmpl
266 8
266 8
267 $ hg log -l1 -Tblah/blah
267 $ hg log -l1 -Tblah/blah
268 blah/blah (no-eol)
268 blah/blah (no-eol)
269
269
270 $ printf 'changeset = "{rev}\\n"\n' > map-simple
270 $ printf 'changeset = "{rev}\\n"\n' > map-simple
271 $ hg log -l1 -T./map-simple
271 $ hg log -l1 -T./map-simple
272 8
272 8
273
273
274 a map file may have [templates] and [templatealias] sections:
274 a map file may have [templates] and [templatealias] sections:
275
275
276 $ cat <<'EOF' > map-simple
276 $ cat <<'EOF' > map-simple
277 > [templates]
277 > [templates]
278 > changeset = "{a}\n"
278 > changeset = "{a}\n"
279 > [templatealias]
279 > [templatealias]
280 > a = rev
280 > a = rev
281 > EOF
281 > EOF
282 $ hg log -l1 -T./map-simple
282 $ hg log -l1 -T./map-simple
283 8
283 8
284
284
285 so it can be included in hgrc
285 so it can be included in hgrc
286
286
287 $ cat <<EOF > myhgrc
287 $ cat <<EOF > myhgrc
288 > %include $HGRCPATH
288 > %include $HGRCPATH
289 > %include map-simple
289 > %include map-simple
290 > [templates]
290 > [templates]
291 > foo = "{changeset}"
291 > foo = "{changeset}"
292 > EOF
292 > EOF
293 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
293 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
294 8
294 8
295 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
295 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
296 8
296 8
297
297
298 Test template map inheritance
298 Test template map inheritance
299
299
300 $ echo "__base__ = map-cmdline.default" > map-simple
300 $ echo "__base__ = map-cmdline.default" > map-simple
301 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
301 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
302 $ hg log -l1 -T./map-simple
302 $ hg log -l1 -T./map-simple
303 changeset: ***8***
303 changeset: ***8***
304 tag: tip
304 tag: tip
305 user: test
305 user: test
306 date: Wed Jan 01 10:01:00 2020 +0000
306 date: Wed Jan 01 10:01:00 2020 +0000
307 summary: third
307 summary: third
308
308
309
309
310 Test docheader, docfooter and separator in template map
310 Test docheader, docfooter and separator in template map
311
311
312 $ cat <<'EOF' > map-myjson
312 $ cat <<'EOF' > map-myjson
313 > docheader = '\{\n'
313 > docheader = '\{\n'
314 > docfooter = '\n}\n'
314 > docfooter = '\n}\n'
315 > separator = ',\n'
315 > separator = ',\n'
316 > changeset = ' {dict(rev, node|short)|json}'
316 > changeset = ' {dict(rev, node|short)|json}'
317 > EOF
317 > EOF
318 $ hg log -l2 -T./map-myjson
318 $ hg log -l2 -T./map-myjson
319 {
319 {
320 {"node": "95c24699272e", "rev": 8},
320 {"node": "95c24699272e", "rev": 8},
321 {"node": "29114dbae42b", "rev": 7}
321 {"node": "29114dbae42b", "rev": 7}
322 }
322 }
323
323
324 Test docheader, docfooter and separator in [templates] section
324 Test docheader, docfooter and separator in [templates] section
325
325
326 $ cat <<'EOF' >> .hg/hgrc
326 $ cat <<'EOF' >> .hg/hgrc
327 > [templates]
327 > [templates]
328 > myjson = ' {dict(rev, node|short)|json}'
328 > myjson = ' {dict(rev, node|short)|json}'
329 > myjson:docheader = '\{\n'
329 > myjson:docheader = '\{\n'
330 > myjson:docfooter = '\n}\n'
330 > myjson:docfooter = '\n}\n'
331 > myjson:separator = ',\n'
331 > myjson:separator = ',\n'
332 > :docheader = 'should not be selected as a docheader for literal templates\n'
332 > :docheader = 'should not be selected as a docheader for literal templates\n'
333 > EOF
333 > EOF
334 $ hg log -l2 -Tmyjson
334 $ hg log -l2 -Tmyjson
335 {
335 {
336 {"node": "95c24699272e", "rev": 8},
336 {"node": "95c24699272e", "rev": 8},
337 {"node": "29114dbae42b", "rev": 7}
337 {"node": "29114dbae42b", "rev": 7}
338 }
338 }
339 $ hg log -l1 -T'{rev}\n'
339 $ hg log -l1 -T'{rev}\n'
340 8
340 8
341
341
342 Template should precede style option
342 Template should precede style option
343
343
344 $ hg log -l1 --style default -T '{rev}\n'
344 $ hg log -l1 --style default -T '{rev}\n'
345 8
345 8
346
346
347 Add a commit with empty description, to ensure that the templates
347 Add a commit with empty description, to ensure that the templates
348 below will omit the description line.
348 below will omit the description line.
349
349
350 $ echo c >> c
350 $ echo c >> c
351 $ hg add c
351 $ hg add c
352 $ hg commit -qm ' '
352 $ hg commit -qm ' '
353
353
354 Default style is like normal output. Phases style should be the same
354 Default style is like normal output. Phases style should be the same
355 as default style, except for extra phase lines.
355 as default style, except for extra phase lines.
356
356
357 $ hg log > log.out
357 $ hg log > log.out
358 $ hg log --style default > style.out
358 $ hg log --style default > style.out
359 $ cmp log.out style.out || diff -u log.out style.out
359 $ cmp log.out style.out || diff -u log.out style.out
360 $ hg log -T phases > phases.out
360 $ hg log -T phases > phases.out
361 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
361 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
362 +phase: draft
362 +phase: draft
363 +phase: draft
363 +phase: draft
364 +phase: draft
364 +phase: draft
365 +phase: draft
365 +phase: draft
366 +phase: draft
366 +phase: draft
367 +phase: draft
367 +phase: draft
368 +phase: draft
368 +phase: draft
369 +phase: draft
369 +phase: draft
370 +phase: draft
370 +phase: draft
371 +phase: draft
371 +phase: draft
372
372
373 $ hg log -v > log.out
373 $ hg log -v > log.out
374 $ hg log -v --style default > style.out
374 $ hg log -v --style default > style.out
375 $ cmp log.out style.out || diff -u log.out style.out
375 $ cmp log.out style.out || diff -u log.out style.out
376 $ hg log -v -T phases > phases.out
376 $ hg log -v -T phases > phases.out
377 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
377 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
378 +phase: draft
378 +phase: draft
379 +phase: draft
379 +phase: draft
380 +phase: draft
380 +phase: draft
381 +phase: draft
381 +phase: draft
382 +phase: draft
382 +phase: draft
383 +phase: draft
383 +phase: draft
384 +phase: draft
384 +phase: draft
385 +phase: draft
385 +phase: draft
386 +phase: draft
386 +phase: draft
387 +phase: draft
387 +phase: draft
388
388
389 $ hg log -q > log.out
389 $ hg log -q > log.out
390 $ hg log -q --style default > style.out
390 $ hg log -q --style default > style.out
391 $ cmp log.out style.out || diff -u log.out style.out
391 $ cmp log.out style.out || diff -u log.out style.out
392 $ hg log -q -T phases > phases.out
392 $ hg log -q -T phases > phases.out
393 $ cmp log.out phases.out || diff -u log.out phases.out
393 $ cmp log.out phases.out || diff -u log.out phases.out
394
394
395 $ hg log --debug > log.out
395 $ hg log --debug > log.out
396 $ hg log --debug --style default > style.out
396 $ hg log --debug --style default > style.out
397 $ cmp log.out style.out || diff -u log.out style.out
397 $ cmp log.out style.out || diff -u log.out style.out
398 $ hg log --debug -T phases > phases.out
398 $ hg log --debug -T phases > phases.out
399 $ cmp log.out phases.out || diff -u log.out phases.out
399 $ cmp log.out phases.out || diff -u log.out phases.out
400
400
401 Default style of working-directory revision should also be the same (but
401 Default style of working-directory revision should also be the same (but
402 date may change while running tests):
402 date may change while running tests):
403
403
404 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
404 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
405 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
405 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
406 $ cmp log.out style.out || diff -u log.out style.out
406 $ cmp log.out style.out || diff -u log.out style.out
407
407
408 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
408 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
409 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
409 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
410 $ cmp log.out style.out || diff -u log.out style.out
410 $ cmp log.out style.out || diff -u log.out style.out
411
411
412 $ hg log -r 'wdir()' -q > log.out
412 $ hg log -r 'wdir()' -q > log.out
413 $ hg log -r 'wdir()' -q --style default > style.out
413 $ hg log -r 'wdir()' -q --style default > style.out
414 $ cmp log.out style.out || diff -u log.out style.out
414 $ cmp log.out style.out || diff -u log.out style.out
415
415
416 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
416 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
417 $ hg log -r 'wdir()' --debug --style default \
417 $ hg log -r 'wdir()' --debug --style default \
418 > | sed 's|^date:.*|date:|' > style.out
418 > | sed 's|^date:.*|date:|' > style.out
419 $ cmp log.out style.out || diff -u log.out style.out
419 $ cmp log.out style.out || diff -u log.out style.out
420
420
421 Default style should also preserve color information (issue2866):
421 Default style should also preserve color information (issue2866):
422
422
423 $ cp $HGRCPATH $HGRCPATH-bak
423 $ cp $HGRCPATH $HGRCPATH-bak
424 $ cat <<EOF >> $HGRCPATH
424 $ cat <<EOF >> $HGRCPATH
425 > [extensions]
425 > [extensions]
426 > color=
426 > color=
427 > EOF
427 > EOF
428
428
429 $ hg --color=debug log > log.out
429 $ hg --color=debug log > log.out
430 $ hg --color=debug log --style default > style.out
430 $ hg --color=debug log --style default > style.out
431 $ cmp log.out style.out || diff -u log.out style.out
431 $ cmp log.out style.out || diff -u log.out style.out
432 $ hg --color=debug log -T phases > phases.out
432 $ hg --color=debug log -T phases > phases.out
433 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
433 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
434 +[log.phase|phase: draft]
434 +[log.phase|phase: draft]
435 +[log.phase|phase: draft]
435 +[log.phase|phase: draft]
436 +[log.phase|phase: draft]
436 +[log.phase|phase: draft]
437 +[log.phase|phase: draft]
437 +[log.phase|phase: draft]
438 +[log.phase|phase: draft]
438 +[log.phase|phase: draft]
439 +[log.phase|phase: draft]
439 +[log.phase|phase: draft]
440 +[log.phase|phase: draft]
440 +[log.phase|phase: draft]
441 +[log.phase|phase: draft]
441 +[log.phase|phase: draft]
442 +[log.phase|phase: draft]
442 +[log.phase|phase: draft]
443 +[log.phase|phase: draft]
443 +[log.phase|phase: draft]
444
444
445 $ hg --color=debug -v log > log.out
445 $ hg --color=debug -v log > log.out
446 $ hg --color=debug -v log --style default > style.out
446 $ hg --color=debug -v log --style default > style.out
447 $ cmp log.out style.out || diff -u log.out style.out
447 $ cmp log.out style.out || diff -u log.out style.out
448 $ hg --color=debug -v log -T phases > phases.out
448 $ hg --color=debug -v log -T phases > phases.out
449 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
449 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
450 +[log.phase|phase: draft]
450 +[log.phase|phase: draft]
451 +[log.phase|phase: draft]
451 +[log.phase|phase: draft]
452 +[log.phase|phase: draft]
452 +[log.phase|phase: draft]
453 +[log.phase|phase: draft]
453 +[log.phase|phase: draft]
454 +[log.phase|phase: draft]
454 +[log.phase|phase: draft]
455 +[log.phase|phase: draft]
455 +[log.phase|phase: draft]
456 +[log.phase|phase: draft]
456 +[log.phase|phase: draft]
457 +[log.phase|phase: draft]
457 +[log.phase|phase: draft]
458 +[log.phase|phase: draft]
458 +[log.phase|phase: draft]
459 +[log.phase|phase: draft]
459 +[log.phase|phase: draft]
460
460
461 $ hg --color=debug -q log > log.out
461 $ hg --color=debug -q log > log.out
462 $ hg --color=debug -q log --style default > style.out
462 $ hg --color=debug -q log --style default > style.out
463 $ cmp log.out style.out || diff -u log.out style.out
463 $ cmp log.out style.out || diff -u log.out style.out
464 $ hg --color=debug -q log -T phases > phases.out
464 $ hg --color=debug -q log -T phases > phases.out
465 $ cmp log.out phases.out || diff -u log.out phases.out
465 $ cmp log.out phases.out || diff -u log.out phases.out
466
466
467 $ hg --color=debug --debug log > log.out
467 $ hg --color=debug --debug log > log.out
468 $ hg --color=debug --debug log --style default > style.out
468 $ hg --color=debug --debug log --style default > style.out
469 $ cmp log.out style.out || diff -u log.out style.out
469 $ cmp log.out style.out || diff -u log.out style.out
470 $ hg --color=debug --debug log -T phases > phases.out
470 $ hg --color=debug --debug log -T phases > phases.out
471 $ cmp log.out phases.out || diff -u log.out phases.out
471 $ cmp log.out phases.out || diff -u log.out phases.out
472
472
473 $ mv $HGRCPATH-bak $HGRCPATH
473 $ mv $HGRCPATH-bak $HGRCPATH
474
474
475 Remove commit with empty commit message, so as to not pollute further
475 Remove commit with empty commit message, so as to not pollute further
476 tests.
476 tests.
477
477
478 $ hg --config extensions.strip= strip -q .
478 $ hg --config extensions.strip= strip -q .
479
479
480 Revision with no copies (used to print a traceback):
480 Revision with no copies (used to print a traceback):
481
481
482 $ hg tip -v --template '\n'
482 $ hg tip -v --template '\n'
483
483
484
484
485 Compact style works:
485 Compact style works:
486
486
487 $ hg log -Tcompact
487 $ hg log -Tcompact
488 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
488 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
489 third
489 third
490
490
491 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
491 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
492 second
492 second
493
493
494 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
494 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
495 merge
495 merge
496
496
497 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
497 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
498 new head
498 new head
499
499
500 4 bbe44766e73d 1970-01-17 04:53 +0000 person
500 4 bbe44766e73d 1970-01-17 04:53 +0000 person
501 new branch
501 new branch
502
502
503 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
503 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
504 no user, no domain
504 no user, no domain
505
505
506 2 97054abb4ab8 1970-01-14 21:20 +0000 other
506 2 97054abb4ab8 1970-01-14 21:20 +0000 other
507 no person
507 no person
508
508
509 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
509 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
510 other 1
510 other 1
511
511
512 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
512 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
513 line 1
513 line 1
514
514
515
515
516 $ hg log -v --style compact
516 $ hg log -v --style compact
517 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
517 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
518 third
518 third
519
519
520 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
520 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
521 second
521 second
522
522
523 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
523 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
524 merge
524 merge
525
525
526 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
526 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
527 new head
527 new head
528
528
529 4 bbe44766e73d 1970-01-17 04:53 +0000 person
529 4 bbe44766e73d 1970-01-17 04:53 +0000 person
530 new branch
530 new branch
531
531
532 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
532 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
533 no user, no domain
533 no user, no domain
534
534
535 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
535 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
536 no person
536 no person
537
537
538 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
538 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
539 other 1
539 other 1
540 other 2
540 other 2
541
541
542 other 3
542 other 3
543
543
544 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
544 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
545 line 1
545 line 1
546 line 2
546 line 2
547
547
548
548
549 $ hg log --debug --style compact
549 $ hg log --debug --style compact
550 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
550 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
551 third
551 third
552
552
553 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
553 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
554 second
554 second
555
555
556 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
556 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
557 merge
557 merge
558
558
559 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
559 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
560 new head
560 new head
561
561
562 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
562 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
563 new branch
563 new branch
564
564
565 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
565 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
566 no user, no domain
566 no user, no domain
567
567
568 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
568 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
569 no person
569 no person
570
570
571 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
571 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
572 other 1
572 other 1
573 other 2
573 other 2
574
574
575 other 3
575 other 3
576
576
577 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
577 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
578 line 1
578 line 1
579 line 2
579 line 2
580
580
581
581
582 Test xml styles:
582 Test xml styles:
583
583
584 $ hg log --style xml -r 'not all()'
584 $ hg log --style xml -r 'not all()'
585 <?xml version="1.0"?>
585 <?xml version="1.0"?>
586 <log>
586 <log>
587 </log>
587 </log>
588
588
589 $ hg log --style xml
589 $ hg log --style xml
590 <?xml version="1.0"?>
590 <?xml version="1.0"?>
591 <log>
591 <log>
592 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
592 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
593 <tag>tip</tag>
593 <tag>tip</tag>
594 <author email="test">test</author>
594 <author email="test">test</author>
595 <date>2020-01-01T10:01:00+00:00</date>
595 <date>2020-01-01T10:01:00+00:00</date>
596 <msg xml:space="preserve">third</msg>
596 <msg xml:space="preserve">third</msg>
597 </logentry>
597 </logentry>
598 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
598 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
599 <parent revision="-1" node="0000000000000000000000000000000000000000" />
599 <parent revision="-1" node="0000000000000000000000000000000000000000" />
600 <author email="user@hostname">User Name</author>
600 <author email="user@hostname">User Name</author>
601 <date>1970-01-12T13:46:40+00:00</date>
601 <date>1970-01-12T13:46:40+00:00</date>
602 <msg xml:space="preserve">second</msg>
602 <msg xml:space="preserve">second</msg>
603 </logentry>
603 </logentry>
604 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
604 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
605 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
605 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
606 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
606 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
607 <author email="person">person</author>
607 <author email="person">person</author>
608 <date>1970-01-18T08:40:01+00:00</date>
608 <date>1970-01-18T08:40:01+00:00</date>
609 <msg xml:space="preserve">merge</msg>
609 <msg xml:space="preserve">merge</msg>
610 </logentry>
610 </logentry>
611 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
611 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
612 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
612 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
613 <author email="person">person</author>
613 <author email="person">person</author>
614 <date>1970-01-18T08:40:00+00:00</date>
614 <date>1970-01-18T08:40:00+00:00</date>
615 <msg xml:space="preserve">new head</msg>
615 <msg xml:space="preserve">new head</msg>
616 </logentry>
616 </logentry>
617 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
617 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
618 <branch>foo</branch>
618 <branch>foo</branch>
619 <author email="person">person</author>
619 <author email="person">person</author>
620 <date>1970-01-17T04:53:20+00:00</date>
620 <date>1970-01-17T04:53:20+00:00</date>
621 <msg xml:space="preserve">new branch</msg>
621 <msg xml:space="preserve">new branch</msg>
622 </logentry>
622 </logentry>
623 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
623 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
624 <author email="person">person</author>
624 <author email="person">person</author>
625 <date>1970-01-16T01:06:40+00:00</date>
625 <date>1970-01-16T01:06:40+00:00</date>
626 <msg xml:space="preserve">no user, no domain</msg>
626 <msg xml:space="preserve">no user, no domain</msg>
627 </logentry>
627 </logentry>
628 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
628 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
629 <author email="other@place">other</author>
629 <author email="other@place">other</author>
630 <date>1970-01-14T21:20:00+00:00</date>
630 <date>1970-01-14T21:20:00+00:00</date>
631 <msg xml:space="preserve">no person</msg>
631 <msg xml:space="preserve">no person</msg>
632 </logentry>
632 </logentry>
633 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
633 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
634 <author email="other@place">A. N. Other</author>
634 <author email="other@place">A. N. Other</author>
635 <date>1970-01-13T17:33:20+00:00</date>
635 <date>1970-01-13T17:33:20+00:00</date>
636 <msg xml:space="preserve">other 1
636 <msg xml:space="preserve">other 1
637 other 2
637 other 2
638
638
639 other 3</msg>
639 other 3</msg>
640 </logentry>
640 </logentry>
641 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
641 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
642 <author email="user@hostname">User Name</author>
642 <author email="user@hostname">User Name</author>
643 <date>1970-01-12T13:46:40+00:00</date>
643 <date>1970-01-12T13:46:40+00:00</date>
644 <msg xml:space="preserve">line 1
644 <msg xml:space="preserve">line 1
645 line 2</msg>
645 line 2</msg>
646 </logentry>
646 </logentry>
647 </log>
647 </log>
648
648
649 $ hg log -v --style xml
649 $ hg log -v --style xml
650 <?xml version="1.0"?>
650 <?xml version="1.0"?>
651 <log>
651 <log>
652 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
652 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
653 <tag>tip</tag>
653 <tag>tip</tag>
654 <author email="test">test</author>
654 <author email="test">test</author>
655 <date>2020-01-01T10:01:00+00:00</date>
655 <date>2020-01-01T10:01:00+00:00</date>
656 <msg xml:space="preserve">third</msg>
656 <msg xml:space="preserve">third</msg>
657 <paths>
657 <paths>
658 <path action="A">fourth</path>
658 <path action="A">fourth</path>
659 <path action="A">third</path>
659 <path action="A">third</path>
660 <path action="R">second</path>
660 <path action="R">second</path>
661 </paths>
661 </paths>
662 <copies>
662 <copies>
663 <copy source="second">fourth</copy>
663 <copy source="second">fourth</copy>
664 </copies>
664 </copies>
665 </logentry>
665 </logentry>
666 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
666 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
667 <parent revision="-1" node="0000000000000000000000000000000000000000" />
667 <parent revision="-1" node="0000000000000000000000000000000000000000" />
668 <author email="user@hostname">User Name</author>
668 <author email="user@hostname">User Name</author>
669 <date>1970-01-12T13:46:40+00:00</date>
669 <date>1970-01-12T13:46:40+00:00</date>
670 <msg xml:space="preserve">second</msg>
670 <msg xml:space="preserve">second</msg>
671 <paths>
671 <paths>
672 <path action="A">second</path>
672 <path action="A">second</path>
673 </paths>
673 </paths>
674 </logentry>
674 </logentry>
675 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
675 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
676 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
676 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
677 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
677 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
678 <author email="person">person</author>
678 <author email="person">person</author>
679 <date>1970-01-18T08:40:01+00:00</date>
679 <date>1970-01-18T08:40:01+00:00</date>
680 <msg xml:space="preserve">merge</msg>
680 <msg xml:space="preserve">merge</msg>
681 <paths>
681 <paths>
682 </paths>
682 </paths>
683 </logentry>
683 </logentry>
684 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
684 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
685 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
685 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
686 <author email="person">person</author>
686 <author email="person">person</author>
687 <date>1970-01-18T08:40:00+00:00</date>
687 <date>1970-01-18T08:40:00+00:00</date>
688 <msg xml:space="preserve">new head</msg>
688 <msg xml:space="preserve">new head</msg>
689 <paths>
689 <paths>
690 <path action="A">d</path>
690 <path action="A">d</path>
691 </paths>
691 </paths>
692 </logentry>
692 </logentry>
693 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
693 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
694 <branch>foo</branch>
694 <branch>foo</branch>
695 <author email="person">person</author>
695 <author email="person">person</author>
696 <date>1970-01-17T04:53:20+00:00</date>
696 <date>1970-01-17T04:53:20+00:00</date>
697 <msg xml:space="preserve">new branch</msg>
697 <msg xml:space="preserve">new branch</msg>
698 <paths>
698 <paths>
699 </paths>
699 </paths>
700 </logentry>
700 </logentry>
701 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
701 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
702 <author email="person">person</author>
702 <author email="person">person</author>
703 <date>1970-01-16T01:06:40+00:00</date>
703 <date>1970-01-16T01:06:40+00:00</date>
704 <msg xml:space="preserve">no user, no domain</msg>
704 <msg xml:space="preserve">no user, no domain</msg>
705 <paths>
705 <paths>
706 <path action="M">c</path>
706 <path action="M">c</path>
707 </paths>
707 </paths>
708 </logentry>
708 </logentry>
709 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
709 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
710 <author email="other@place">other</author>
710 <author email="other@place">other</author>
711 <date>1970-01-14T21:20:00+00:00</date>
711 <date>1970-01-14T21:20:00+00:00</date>
712 <msg xml:space="preserve">no person</msg>
712 <msg xml:space="preserve">no person</msg>
713 <paths>
713 <paths>
714 <path action="A">c</path>
714 <path action="A">c</path>
715 </paths>
715 </paths>
716 </logentry>
716 </logentry>
717 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
717 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
718 <author email="other@place">A. N. Other</author>
718 <author email="other@place">A. N. Other</author>
719 <date>1970-01-13T17:33:20+00:00</date>
719 <date>1970-01-13T17:33:20+00:00</date>
720 <msg xml:space="preserve">other 1
720 <msg xml:space="preserve">other 1
721 other 2
721 other 2
722
722
723 other 3</msg>
723 other 3</msg>
724 <paths>
724 <paths>
725 <path action="A">b</path>
725 <path action="A">b</path>
726 </paths>
726 </paths>
727 </logentry>
727 </logentry>
728 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
728 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
729 <author email="user@hostname">User Name</author>
729 <author email="user@hostname">User Name</author>
730 <date>1970-01-12T13:46:40+00:00</date>
730 <date>1970-01-12T13:46:40+00:00</date>
731 <msg xml:space="preserve">line 1
731 <msg xml:space="preserve">line 1
732 line 2</msg>
732 line 2</msg>
733 <paths>
733 <paths>
734 <path action="A">a</path>
734 <path action="A">a</path>
735 </paths>
735 </paths>
736 </logentry>
736 </logentry>
737 </log>
737 </log>
738
738
739 $ hg log --debug --style xml
739 $ hg log --debug --style xml
740 <?xml version="1.0"?>
740 <?xml version="1.0"?>
741 <log>
741 <log>
742 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
742 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
743 <tag>tip</tag>
743 <tag>tip</tag>
744 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
744 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
745 <parent revision="-1" node="0000000000000000000000000000000000000000" />
745 <parent revision="-1" node="0000000000000000000000000000000000000000" />
746 <author email="test">test</author>
746 <author email="test">test</author>
747 <date>2020-01-01T10:01:00+00:00</date>
747 <date>2020-01-01T10:01:00+00:00</date>
748 <msg xml:space="preserve">third</msg>
748 <msg xml:space="preserve">third</msg>
749 <paths>
749 <paths>
750 <path action="A">fourth</path>
750 <path action="A">fourth</path>
751 <path action="A">third</path>
751 <path action="A">third</path>
752 <path action="R">second</path>
752 <path action="R">second</path>
753 </paths>
753 </paths>
754 <copies>
754 <copies>
755 <copy source="second">fourth</copy>
755 <copy source="second">fourth</copy>
756 </copies>
756 </copies>
757 <extra key="branch">default</extra>
757 <extra key="branch">default</extra>
758 </logentry>
758 </logentry>
759 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
759 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
760 <parent revision="-1" node="0000000000000000000000000000000000000000" />
760 <parent revision="-1" node="0000000000000000000000000000000000000000" />
761 <parent revision="-1" node="0000000000000000000000000000000000000000" />
761 <parent revision="-1" node="0000000000000000000000000000000000000000" />
762 <author email="user@hostname">User Name</author>
762 <author email="user@hostname">User Name</author>
763 <date>1970-01-12T13:46:40+00:00</date>
763 <date>1970-01-12T13:46:40+00:00</date>
764 <msg xml:space="preserve">second</msg>
764 <msg xml:space="preserve">second</msg>
765 <paths>
765 <paths>
766 <path action="A">second</path>
766 <path action="A">second</path>
767 </paths>
767 </paths>
768 <extra key="branch">default</extra>
768 <extra key="branch">default</extra>
769 </logentry>
769 </logentry>
770 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
770 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
771 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
771 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
772 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
772 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
773 <author email="person">person</author>
773 <author email="person">person</author>
774 <date>1970-01-18T08:40:01+00:00</date>
774 <date>1970-01-18T08:40:01+00:00</date>
775 <msg xml:space="preserve">merge</msg>
775 <msg xml:space="preserve">merge</msg>
776 <paths>
776 <paths>
777 </paths>
777 </paths>
778 <extra key="branch">default</extra>
778 <extra key="branch">default</extra>
779 </logentry>
779 </logentry>
780 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
780 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
781 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
781 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
782 <parent revision="-1" node="0000000000000000000000000000000000000000" />
782 <parent revision="-1" node="0000000000000000000000000000000000000000" />
783 <author email="person">person</author>
783 <author email="person">person</author>
784 <date>1970-01-18T08:40:00+00:00</date>
784 <date>1970-01-18T08:40:00+00:00</date>
785 <msg xml:space="preserve">new head</msg>
785 <msg xml:space="preserve">new head</msg>
786 <paths>
786 <paths>
787 <path action="A">d</path>
787 <path action="A">d</path>
788 </paths>
788 </paths>
789 <extra key="branch">default</extra>
789 <extra key="branch">default</extra>
790 </logentry>
790 </logentry>
791 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
791 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
792 <branch>foo</branch>
792 <branch>foo</branch>
793 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
793 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
794 <parent revision="-1" node="0000000000000000000000000000000000000000" />
794 <parent revision="-1" node="0000000000000000000000000000000000000000" />
795 <author email="person">person</author>
795 <author email="person">person</author>
796 <date>1970-01-17T04:53:20+00:00</date>
796 <date>1970-01-17T04:53:20+00:00</date>
797 <msg xml:space="preserve">new branch</msg>
797 <msg xml:space="preserve">new branch</msg>
798 <paths>
798 <paths>
799 </paths>
799 </paths>
800 <extra key="branch">foo</extra>
800 <extra key="branch">foo</extra>
801 </logentry>
801 </logentry>
802 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
802 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
803 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
803 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
804 <parent revision="-1" node="0000000000000000000000000000000000000000" />
804 <parent revision="-1" node="0000000000000000000000000000000000000000" />
805 <author email="person">person</author>
805 <author email="person">person</author>
806 <date>1970-01-16T01:06:40+00:00</date>
806 <date>1970-01-16T01:06:40+00:00</date>
807 <msg xml:space="preserve">no user, no domain</msg>
807 <msg xml:space="preserve">no user, no domain</msg>
808 <paths>
808 <paths>
809 <path action="M">c</path>
809 <path action="M">c</path>
810 </paths>
810 </paths>
811 <extra key="branch">default</extra>
811 <extra key="branch">default</extra>
812 </logentry>
812 </logentry>
813 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
813 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
814 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
814 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
815 <parent revision="-1" node="0000000000000000000000000000000000000000" />
815 <parent revision="-1" node="0000000000000000000000000000000000000000" />
816 <author email="other@place">other</author>
816 <author email="other@place">other</author>
817 <date>1970-01-14T21:20:00+00:00</date>
817 <date>1970-01-14T21:20:00+00:00</date>
818 <msg xml:space="preserve">no person</msg>
818 <msg xml:space="preserve">no person</msg>
819 <paths>
819 <paths>
820 <path action="A">c</path>
820 <path action="A">c</path>
821 </paths>
821 </paths>
822 <extra key="branch">default</extra>
822 <extra key="branch">default</extra>
823 </logentry>
823 </logentry>
824 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
824 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
825 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
825 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
826 <parent revision="-1" node="0000000000000000000000000000000000000000" />
826 <parent revision="-1" node="0000000000000000000000000000000000000000" />
827 <author email="other@place">A. N. Other</author>
827 <author email="other@place">A. N. Other</author>
828 <date>1970-01-13T17:33:20+00:00</date>
828 <date>1970-01-13T17:33:20+00:00</date>
829 <msg xml:space="preserve">other 1
829 <msg xml:space="preserve">other 1
830 other 2
830 other 2
831
831
832 other 3</msg>
832 other 3</msg>
833 <paths>
833 <paths>
834 <path action="A">b</path>
834 <path action="A">b</path>
835 </paths>
835 </paths>
836 <extra key="branch">default</extra>
836 <extra key="branch">default</extra>
837 </logentry>
837 </logentry>
838 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
838 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
839 <parent revision="-1" node="0000000000000000000000000000000000000000" />
839 <parent revision="-1" node="0000000000000000000000000000000000000000" />
840 <parent revision="-1" node="0000000000000000000000000000000000000000" />
840 <parent revision="-1" node="0000000000000000000000000000000000000000" />
841 <author email="user@hostname">User Name</author>
841 <author email="user@hostname">User Name</author>
842 <date>1970-01-12T13:46:40+00:00</date>
842 <date>1970-01-12T13:46:40+00:00</date>
843 <msg xml:space="preserve">line 1
843 <msg xml:space="preserve">line 1
844 line 2</msg>
844 line 2</msg>
845 <paths>
845 <paths>
846 <path action="A">a</path>
846 <path action="A">a</path>
847 </paths>
847 </paths>
848 <extra key="branch">default</extra>
848 <extra key="branch">default</extra>
849 </logentry>
849 </logentry>
850 </log>
850 </log>
851
851
852
852
853 Test JSON style:
853 Test JSON style:
854
854
855 $ hg log -k nosuch -Tjson
855 $ hg log -k nosuch -Tjson
856 [
856 [
857 ]
857 ]
858
858
859 $ hg log -qr . -Tjson
859 $ hg log -qr . -Tjson
860 [
860 [
861 {
861 {
862 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
862 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
863 "rev": 8
863 "rev": 8
864 }
864 }
865 ]
865 ]
866
866
867 $ hg log -vpr . -Tjson --stat
867 $ hg log -vpr . -Tjson --stat
868 [
868 [
869 {
869 {
870 "bookmarks": [],
870 "bookmarks": [],
871 "branch": "default",
871 "branch": "default",
872 "date": [1577872860, 0],
872 "date": [1577872860, 0],
873 "desc": "third",
873 "desc": "third",
874 "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",
874 "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",
875 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
875 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
876 "files": ["fourth", "second", "third"],
876 "files": ["fourth", "second", "third"],
877 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
877 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
878 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
878 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
879 "phase": "draft",
879 "phase": "draft",
880 "rev": 8,
880 "rev": 8,
881 "tags": ["tip"],
881 "tags": ["tip"],
882 "user": "test"
882 "user": "test"
883 }
883 }
884 ]
884 ]
885
885
886 honor --git but not format-breaking diffopts
886 honor --git but not format-breaking diffopts
887 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
887 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
888 [
888 [
889 {
889 {
890 "bookmarks": [],
890 "bookmarks": [],
891 "branch": "default",
891 "branch": "default",
892 "date": [1577872860, 0],
892 "date": [1577872860, 0],
893 "desc": "third",
893 "desc": "third",
894 "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",
894 "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",
895 "files": ["fourth", "second", "third"],
895 "files": ["fourth", "second", "third"],
896 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
896 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
897 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
897 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
898 "phase": "draft",
898 "phase": "draft",
899 "rev": 8,
899 "rev": 8,
900 "tags": ["tip"],
900 "tags": ["tip"],
901 "user": "test"
901 "user": "test"
902 }
902 }
903 ]
903 ]
904
904
905 $ hg log -T json
905 $ hg log -T json
906 [
906 [
907 {
907 {
908 "bookmarks": [],
908 "bookmarks": [],
909 "branch": "default",
909 "branch": "default",
910 "date": [1577872860, 0],
910 "date": [1577872860, 0],
911 "desc": "third",
911 "desc": "third",
912 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
912 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
913 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
913 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
914 "phase": "draft",
914 "phase": "draft",
915 "rev": 8,
915 "rev": 8,
916 "tags": ["tip"],
916 "tags": ["tip"],
917 "user": "test"
917 "user": "test"
918 },
918 },
919 {
919 {
920 "bookmarks": [],
920 "bookmarks": [],
921 "branch": "default",
921 "branch": "default",
922 "date": [1000000, 0],
922 "date": [1000000, 0],
923 "desc": "second",
923 "desc": "second",
924 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
924 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
925 "parents": ["0000000000000000000000000000000000000000"],
925 "parents": ["0000000000000000000000000000000000000000"],
926 "phase": "draft",
926 "phase": "draft",
927 "rev": 7,
927 "rev": 7,
928 "tags": [],
928 "tags": [],
929 "user": "User Name <user@hostname>"
929 "user": "User Name <user@hostname>"
930 },
930 },
931 {
931 {
932 "bookmarks": [],
932 "bookmarks": [],
933 "branch": "default",
933 "branch": "default",
934 "date": [1500001, 0],
934 "date": [1500001, 0],
935 "desc": "merge",
935 "desc": "merge",
936 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
936 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
937 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
937 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
938 "phase": "draft",
938 "phase": "draft",
939 "rev": 6,
939 "rev": 6,
940 "tags": [],
940 "tags": [],
941 "user": "person"
941 "user": "person"
942 },
942 },
943 {
943 {
944 "bookmarks": [],
944 "bookmarks": [],
945 "branch": "default",
945 "branch": "default",
946 "date": [1500000, 0],
946 "date": [1500000, 0],
947 "desc": "new head",
947 "desc": "new head",
948 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
948 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
949 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
949 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
950 "phase": "draft",
950 "phase": "draft",
951 "rev": 5,
951 "rev": 5,
952 "tags": [],
952 "tags": [],
953 "user": "person"
953 "user": "person"
954 },
954 },
955 {
955 {
956 "bookmarks": [],
956 "bookmarks": [],
957 "branch": "foo",
957 "branch": "foo",
958 "date": [1400000, 0],
958 "date": [1400000, 0],
959 "desc": "new branch",
959 "desc": "new branch",
960 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
960 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
961 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
961 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
962 "phase": "draft",
962 "phase": "draft",
963 "rev": 4,
963 "rev": 4,
964 "tags": [],
964 "tags": [],
965 "user": "person"
965 "user": "person"
966 },
966 },
967 {
967 {
968 "bookmarks": [],
968 "bookmarks": [],
969 "branch": "default",
969 "branch": "default",
970 "date": [1300000, 0],
970 "date": [1300000, 0],
971 "desc": "no user, no domain",
971 "desc": "no user, no domain",
972 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
972 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
973 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
973 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
974 "phase": "draft",
974 "phase": "draft",
975 "rev": 3,
975 "rev": 3,
976 "tags": [],
976 "tags": [],
977 "user": "person"
977 "user": "person"
978 },
978 },
979 {
979 {
980 "bookmarks": [],
980 "bookmarks": [],
981 "branch": "default",
981 "branch": "default",
982 "date": [1200000, 0],
982 "date": [1200000, 0],
983 "desc": "no person",
983 "desc": "no person",
984 "node": "97054abb4ab824450e9164180baf491ae0078465",
984 "node": "97054abb4ab824450e9164180baf491ae0078465",
985 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
985 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
986 "phase": "draft",
986 "phase": "draft",
987 "rev": 2,
987 "rev": 2,
988 "tags": [],
988 "tags": [],
989 "user": "other@place"
989 "user": "other@place"
990 },
990 },
991 {
991 {
992 "bookmarks": [],
992 "bookmarks": [],
993 "branch": "default",
993 "branch": "default",
994 "date": [1100000, 0],
994 "date": [1100000, 0],
995 "desc": "other 1\nother 2\n\nother 3",
995 "desc": "other 1\nother 2\n\nother 3",
996 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
996 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
997 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
997 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
998 "phase": "draft",
998 "phase": "draft",
999 "rev": 1,
999 "rev": 1,
1000 "tags": [],
1000 "tags": [],
1001 "user": "A. N. Other <other@place>"
1001 "user": "A. N. Other <other@place>"
1002 },
1002 },
1003 {
1003 {
1004 "bookmarks": [],
1004 "bookmarks": [],
1005 "branch": "default",
1005 "branch": "default",
1006 "date": [1000000, 0],
1006 "date": [1000000, 0],
1007 "desc": "line 1\nline 2",
1007 "desc": "line 1\nline 2",
1008 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1008 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1009 "parents": ["0000000000000000000000000000000000000000"],
1009 "parents": ["0000000000000000000000000000000000000000"],
1010 "phase": "draft",
1010 "phase": "draft",
1011 "rev": 0,
1011 "rev": 0,
1012 "tags": [],
1012 "tags": [],
1013 "user": "User Name <user@hostname>"
1013 "user": "User Name <user@hostname>"
1014 }
1014 }
1015 ]
1015 ]
1016
1016
1017 $ hg heads -v -Tjson
1017 $ hg heads -v -Tjson
1018 [
1018 [
1019 {
1019 {
1020 "bookmarks": [],
1020 "bookmarks": [],
1021 "branch": "default",
1021 "branch": "default",
1022 "date": [1577872860, 0],
1022 "date": [1577872860, 0],
1023 "desc": "third",
1023 "desc": "third",
1024 "files": ["fourth", "second", "third"],
1024 "files": ["fourth", "second", "third"],
1025 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1025 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1026 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1026 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1027 "phase": "draft",
1027 "phase": "draft",
1028 "rev": 8,
1028 "rev": 8,
1029 "tags": ["tip"],
1029 "tags": ["tip"],
1030 "user": "test"
1030 "user": "test"
1031 },
1031 },
1032 {
1032 {
1033 "bookmarks": [],
1033 "bookmarks": [],
1034 "branch": "default",
1034 "branch": "default",
1035 "date": [1500001, 0],
1035 "date": [1500001, 0],
1036 "desc": "merge",
1036 "desc": "merge",
1037 "files": [],
1037 "files": [],
1038 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1038 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1039 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1039 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1040 "phase": "draft",
1040 "phase": "draft",
1041 "rev": 6,
1041 "rev": 6,
1042 "tags": [],
1042 "tags": [],
1043 "user": "person"
1043 "user": "person"
1044 },
1044 },
1045 {
1045 {
1046 "bookmarks": [],
1046 "bookmarks": [],
1047 "branch": "foo",
1047 "branch": "foo",
1048 "date": [1400000, 0],
1048 "date": [1400000, 0],
1049 "desc": "new branch",
1049 "desc": "new branch",
1050 "files": [],
1050 "files": [],
1051 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1051 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1052 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1052 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1053 "phase": "draft",
1053 "phase": "draft",
1054 "rev": 4,
1054 "rev": 4,
1055 "tags": [],
1055 "tags": [],
1056 "user": "person"
1056 "user": "person"
1057 }
1057 }
1058 ]
1058 ]
1059
1059
1060 $ hg log --debug -Tjson
1060 $ hg log --debug -Tjson
1061 [
1061 [
1062 {
1062 {
1063 "added": ["fourth", "third"],
1063 "added": ["fourth", "third"],
1064 "bookmarks": [],
1064 "bookmarks": [],
1065 "branch": "default",
1065 "branch": "default",
1066 "date": [1577872860, 0],
1066 "date": [1577872860, 0],
1067 "desc": "third",
1067 "desc": "third",
1068 "extra": {"branch": "default"},
1068 "extra": {"branch": "default"},
1069 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1069 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1070 "modified": [],
1070 "modified": [],
1071 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1071 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1072 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1072 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1073 "phase": "draft",
1073 "phase": "draft",
1074 "removed": ["second"],
1074 "removed": ["second"],
1075 "rev": 8,
1075 "rev": 8,
1076 "tags": ["tip"],
1076 "tags": ["tip"],
1077 "user": "test"
1077 "user": "test"
1078 },
1078 },
1079 {
1079 {
1080 "added": ["second"],
1080 "added": ["second"],
1081 "bookmarks": [],
1081 "bookmarks": [],
1082 "branch": "default",
1082 "branch": "default",
1083 "date": [1000000, 0],
1083 "date": [1000000, 0],
1084 "desc": "second",
1084 "desc": "second",
1085 "extra": {"branch": "default"},
1085 "extra": {"branch": "default"},
1086 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1086 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1087 "modified": [],
1087 "modified": [],
1088 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1088 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1089 "parents": ["0000000000000000000000000000000000000000"],
1089 "parents": ["0000000000000000000000000000000000000000"],
1090 "phase": "draft",
1090 "phase": "draft",
1091 "removed": [],
1091 "removed": [],
1092 "rev": 7,
1092 "rev": 7,
1093 "tags": [],
1093 "tags": [],
1094 "user": "User Name <user@hostname>"
1094 "user": "User Name <user@hostname>"
1095 },
1095 },
1096 {
1096 {
1097 "added": [],
1097 "added": [],
1098 "bookmarks": [],
1098 "bookmarks": [],
1099 "branch": "default",
1099 "branch": "default",
1100 "date": [1500001, 0],
1100 "date": [1500001, 0],
1101 "desc": "merge",
1101 "desc": "merge",
1102 "extra": {"branch": "default"},
1102 "extra": {"branch": "default"},
1103 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1103 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1104 "modified": [],
1104 "modified": [],
1105 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1105 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1106 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1106 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1107 "phase": "draft",
1107 "phase": "draft",
1108 "removed": [],
1108 "removed": [],
1109 "rev": 6,
1109 "rev": 6,
1110 "tags": [],
1110 "tags": [],
1111 "user": "person"
1111 "user": "person"
1112 },
1112 },
1113 {
1113 {
1114 "added": ["d"],
1114 "added": ["d"],
1115 "bookmarks": [],
1115 "bookmarks": [],
1116 "branch": "default",
1116 "branch": "default",
1117 "date": [1500000, 0],
1117 "date": [1500000, 0],
1118 "desc": "new head",
1118 "desc": "new head",
1119 "extra": {"branch": "default"},
1119 "extra": {"branch": "default"},
1120 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1120 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1121 "modified": [],
1121 "modified": [],
1122 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1122 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1123 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1123 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1124 "phase": "draft",
1124 "phase": "draft",
1125 "removed": [],
1125 "removed": [],
1126 "rev": 5,
1126 "rev": 5,
1127 "tags": [],
1127 "tags": [],
1128 "user": "person"
1128 "user": "person"
1129 },
1129 },
1130 {
1130 {
1131 "added": [],
1131 "added": [],
1132 "bookmarks": [],
1132 "bookmarks": [],
1133 "branch": "foo",
1133 "branch": "foo",
1134 "date": [1400000, 0],
1134 "date": [1400000, 0],
1135 "desc": "new branch",
1135 "desc": "new branch",
1136 "extra": {"branch": "foo"},
1136 "extra": {"branch": "foo"},
1137 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1137 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1138 "modified": [],
1138 "modified": [],
1139 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1139 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1140 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1140 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1141 "phase": "draft",
1141 "phase": "draft",
1142 "removed": [],
1142 "removed": [],
1143 "rev": 4,
1143 "rev": 4,
1144 "tags": [],
1144 "tags": [],
1145 "user": "person"
1145 "user": "person"
1146 },
1146 },
1147 {
1147 {
1148 "added": [],
1148 "added": [],
1149 "bookmarks": [],
1149 "bookmarks": [],
1150 "branch": "default",
1150 "branch": "default",
1151 "date": [1300000, 0],
1151 "date": [1300000, 0],
1152 "desc": "no user, no domain",
1152 "desc": "no user, no domain",
1153 "extra": {"branch": "default"},
1153 "extra": {"branch": "default"},
1154 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1154 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1155 "modified": ["c"],
1155 "modified": ["c"],
1156 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1156 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1157 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1157 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1158 "phase": "draft",
1158 "phase": "draft",
1159 "removed": [],
1159 "removed": [],
1160 "rev": 3,
1160 "rev": 3,
1161 "tags": [],
1161 "tags": [],
1162 "user": "person"
1162 "user": "person"
1163 },
1163 },
1164 {
1164 {
1165 "added": ["c"],
1165 "added": ["c"],
1166 "bookmarks": [],
1166 "bookmarks": [],
1167 "branch": "default",
1167 "branch": "default",
1168 "date": [1200000, 0],
1168 "date": [1200000, 0],
1169 "desc": "no person",
1169 "desc": "no person",
1170 "extra": {"branch": "default"},
1170 "extra": {"branch": "default"},
1171 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1171 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1172 "modified": [],
1172 "modified": [],
1173 "node": "97054abb4ab824450e9164180baf491ae0078465",
1173 "node": "97054abb4ab824450e9164180baf491ae0078465",
1174 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1174 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1175 "phase": "draft",
1175 "phase": "draft",
1176 "removed": [],
1176 "removed": [],
1177 "rev": 2,
1177 "rev": 2,
1178 "tags": [],
1178 "tags": [],
1179 "user": "other@place"
1179 "user": "other@place"
1180 },
1180 },
1181 {
1181 {
1182 "added": ["b"],
1182 "added": ["b"],
1183 "bookmarks": [],
1183 "bookmarks": [],
1184 "branch": "default",
1184 "branch": "default",
1185 "date": [1100000, 0],
1185 "date": [1100000, 0],
1186 "desc": "other 1\nother 2\n\nother 3",
1186 "desc": "other 1\nother 2\n\nother 3",
1187 "extra": {"branch": "default"},
1187 "extra": {"branch": "default"},
1188 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1188 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1189 "modified": [],
1189 "modified": [],
1190 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1190 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1191 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1191 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1192 "phase": "draft",
1192 "phase": "draft",
1193 "removed": [],
1193 "removed": [],
1194 "rev": 1,
1194 "rev": 1,
1195 "tags": [],
1195 "tags": [],
1196 "user": "A. N. Other <other@place>"
1196 "user": "A. N. Other <other@place>"
1197 },
1197 },
1198 {
1198 {
1199 "added": ["a"],
1199 "added": ["a"],
1200 "bookmarks": [],
1200 "bookmarks": [],
1201 "branch": "default",
1201 "branch": "default",
1202 "date": [1000000, 0],
1202 "date": [1000000, 0],
1203 "desc": "line 1\nline 2",
1203 "desc": "line 1\nline 2",
1204 "extra": {"branch": "default"},
1204 "extra": {"branch": "default"},
1205 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1205 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1206 "modified": [],
1206 "modified": [],
1207 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1207 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1208 "parents": ["0000000000000000000000000000000000000000"],
1208 "parents": ["0000000000000000000000000000000000000000"],
1209 "phase": "draft",
1209 "phase": "draft",
1210 "removed": [],
1210 "removed": [],
1211 "rev": 0,
1211 "rev": 0,
1212 "tags": [],
1212 "tags": [],
1213 "user": "User Name <user@hostname>"
1213 "user": "User Name <user@hostname>"
1214 }
1214 }
1215 ]
1215 ]
1216
1216
1217 Error if style not readable:
1217 Error if style not readable:
1218
1218
1219 #if unix-permissions no-root
1219 #if unix-permissions no-root
1220 $ touch q
1220 $ touch q
1221 $ chmod 0 q
1221 $ chmod 0 q
1222 $ hg log --style ./q
1222 $ hg log --style ./q
1223 abort: Permission denied: ./q
1223 abort: Permission denied: ./q
1224 [255]
1224 [255]
1225 #endif
1225 #endif
1226
1226
1227 Error if no style:
1227 Error if no style:
1228
1228
1229 $ hg log --style notexist
1229 $ hg log --style notexist
1230 abort: style 'notexist' not found
1230 abort: style 'notexist' not found
1231 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1231 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1232 [255]
1232 [255]
1233
1233
1234 $ hg log -T list
1234 $ hg log -T list
1235 available styles: bisect, changelog, compact, default, phases, show, status, xml
1235 available styles: bisect, changelog, compact, default, phases, show, status, xml
1236 abort: specify a template
1236 abort: specify a template
1237 [255]
1237 [255]
1238
1238
1239 Error if style missing key:
1239 Error if style missing key:
1240
1240
1241 $ echo 'q = q' > t
1241 $ echo 'q = q' > t
1242 $ hg log --style ./t
1242 $ hg log --style ./t
1243 abort: "changeset" not in template map
1243 abort: "changeset" not in template map
1244 [255]
1244 [255]
1245
1245
1246 Error if style missing value:
1246 Error if style missing value:
1247
1247
1248 $ echo 'changeset =' > t
1248 $ echo 'changeset =' > t
1249 $ hg log --style t
1249 $ hg log --style t
1250 hg: parse error at t:1: missing value
1250 hg: parse error at t:1: missing value
1251 [255]
1251 [255]
1252
1252
1253 Error if include fails:
1253 Error if include fails:
1254
1254
1255 $ echo 'changeset = q' >> t
1255 $ echo 'changeset = q' >> t
1256 #if unix-permissions no-root
1256 #if unix-permissions no-root
1257 $ hg log --style ./t
1257 $ hg log --style ./t
1258 abort: template file ./q: Permission denied
1258 abort: template file ./q: Permission denied
1259 [255]
1259 [255]
1260 $ rm -f q
1260 $ rm -f q
1261 #endif
1261 #endif
1262
1262
1263 Include works:
1263 Include works:
1264
1264
1265 $ echo '{rev}' > q
1265 $ echo '{rev}' > q
1266 $ hg log --style ./t
1266 $ hg log --style ./t
1267 8
1267 8
1268 7
1268 7
1269 6
1269 6
1270 5
1270 5
1271 4
1271 4
1272 3
1272 3
1273 2
1273 2
1274 1
1274 1
1275 0
1275 0
1276
1276
1277 Check that recursive reference does not fall into RuntimeError (issue4758):
1277 Check that recursive reference does not fall into RuntimeError (issue4758):
1278
1278
1279 common mistake:
1279 common mistake:
1280
1280
1281 $ cat << EOF > issue4758
1281 $ cat << EOF > issue4758
1282 > changeset = '{changeset}\n'
1282 > changeset = '{changeset}\n'
1283 > EOF
1283 > EOF
1284 $ hg log --style ./issue4758
1284 $ hg log --style ./issue4758
1285 abort: recursive reference 'changeset' in template
1285 abort: recursive reference 'changeset' in template
1286 [255]
1286 [255]
1287
1287
1288 circular reference:
1288 circular reference:
1289
1289
1290 $ cat << EOF > issue4758
1290 $ cat << EOF > issue4758
1291 > changeset = '{foo}'
1291 > changeset = '{foo}'
1292 > foo = '{changeset}'
1292 > foo = '{changeset}'
1293 > EOF
1293 > EOF
1294 $ hg log --style ./issue4758
1294 $ hg log --style ./issue4758
1295 abort: recursive reference 'foo' in template
1295 abort: recursive reference 'foo' in template
1296 [255]
1296 [255]
1297
1297
1298 buildmap() -> gettemplate(), where no thunk was made:
1298 buildmap() -> gettemplate(), where no thunk was made:
1299
1299
1300 $ cat << EOF > issue4758
1300 $ cat << EOF > issue4758
1301 > changeset = '{files % changeset}\n'
1301 > changeset = '{files % changeset}\n'
1302 > EOF
1302 > EOF
1303 $ hg log --style ./issue4758
1303 $ hg log --style ./issue4758
1304 abort: recursive reference 'changeset' in template
1304 abort: recursive reference 'changeset' in template
1305 [255]
1305 [255]
1306
1306
1307 not a recursion if a keyword of the same name exists:
1307 not a recursion if a keyword of the same name exists:
1308
1308
1309 $ cat << EOF > issue4758
1309 $ cat << EOF > issue4758
1310 > changeset = '{tags % rev}'
1310 > changeset = '{tags % rev}'
1311 > rev = '{rev} {tag}\n'
1311 > rev = '{rev} {tag}\n'
1312 > EOF
1312 > EOF
1313 $ hg log --style ./issue4758 -r tip
1313 $ hg log --style ./issue4758 -r tip
1314 8 tip
1314 8 tip
1315
1315
1316 Check that {phase} works correctly on parents:
1316 Check that {phase} works correctly on parents:
1317
1317
1318 $ cat << EOF > parentphase
1318 $ cat << EOF > parentphase
1319 > changeset_debug = '{rev} ({phase}):{parents}\n'
1319 > changeset_debug = '{rev} ({phase}):{parents}\n'
1320 > parent = ' {rev} ({phase})'
1320 > parent = ' {rev} ({phase})'
1321 > EOF
1321 > EOF
1322 $ hg phase -r 5 --public
1322 $ hg phase -r 5 --public
1323 $ hg phase -r 7 --secret --force
1323 $ hg phase -r 7 --secret --force
1324 $ hg log --debug -G --style ./parentphase
1324 $ hg log --debug -G --style ./parentphase
1325 @ 8 (secret): 7 (secret) -1 (public)
1325 @ 8 (secret): 7 (secret) -1 (public)
1326 |
1326 |
1327 o 7 (secret): -1 (public) -1 (public)
1327 o 7 (secret): -1 (public) -1 (public)
1328
1328
1329 o 6 (draft): 5 (public) 4 (draft)
1329 o 6 (draft): 5 (public) 4 (draft)
1330 |\
1330 |\
1331 | o 5 (public): 3 (public) -1 (public)
1331 | o 5 (public): 3 (public) -1 (public)
1332 | |
1332 | |
1333 o | 4 (draft): 3 (public) -1 (public)
1333 o | 4 (draft): 3 (public) -1 (public)
1334 |/
1334 |/
1335 o 3 (public): 2 (public) -1 (public)
1335 o 3 (public): 2 (public) -1 (public)
1336 |
1336 |
1337 o 2 (public): 1 (public) -1 (public)
1337 o 2 (public): 1 (public) -1 (public)
1338 |
1338 |
1339 o 1 (public): 0 (public) -1 (public)
1339 o 1 (public): 0 (public) -1 (public)
1340 |
1340 |
1341 o 0 (public): -1 (public) -1 (public)
1341 o 0 (public): -1 (public) -1 (public)
1342
1342
1343
1343
1344 Missing non-standard names give no error (backward compatibility):
1344 Missing non-standard names give no error (backward compatibility):
1345
1345
1346 $ echo "changeset = '{c}'" > t
1346 $ echo "changeset = '{c}'" > t
1347 $ hg log --style ./t
1347 $ hg log --style ./t
1348
1348
1349 Defining non-standard name works:
1349 Defining non-standard name works:
1350
1350
1351 $ cat <<EOF > t
1351 $ cat <<EOF > t
1352 > changeset = '{c}'
1352 > changeset = '{c}'
1353 > c = q
1353 > c = q
1354 > EOF
1354 > EOF
1355 $ hg log --style ./t
1355 $ hg log --style ./t
1356 8
1356 8
1357 7
1357 7
1358 6
1358 6
1359 5
1359 5
1360 4
1360 4
1361 3
1361 3
1362 2
1362 2
1363 1
1363 1
1364 0
1364 0
1365
1365
1366 ui.style works:
1366 ui.style works:
1367
1367
1368 $ echo '[ui]' > .hg/hgrc
1368 $ echo '[ui]' > .hg/hgrc
1369 $ echo 'style = t' >> .hg/hgrc
1369 $ echo 'style = t' >> .hg/hgrc
1370 $ hg log
1370 $ hg log
1371 8
1371 8
1372 7
1372 7
1373 6
1373 6
1374 5
1374 5
1375 4
1375 4
1376 3
1376 3
1377 2
1377 2
1378 1
1378 1
1379 0
1379 0
1380
1380
1381
1381
1382 Issue338:
1382 Issue338:
1383
1383
1384 $ hg log --style=changelog > changelog
1384 $ hg log --style=changelog > changelog
1385
1385
1386 $ cat changelog
1386 $ cat changelog
1387 2020-01-01 test <test>
1387 2020-01-01 test <test>
1388
1388
1389 * fourth, second, third:
1389 * fourth, second, third:
1390 third
1390 third
1391 [95c24699272e] [tip]
1391 [95c24699272e] [tip]
1392
1392
1393 1970-01-12 User Name <user@hostname>
1393 1970-01-12 User Name <user@hostname>
1394
1394
1395 * second:
1395 * second:
1396 second
1396 second
1397 [29114dbae42b]
1397 [29114dbae42b]
1398
1398
1399 1970-01-18 person <person>
1399 1970-01-18 person <person>
1400
1400
1401 * merge
1401 * merge
1402 [d41e714fe50d]
1402 [d41e714fe50d]
1403
1403
1404 * d:
1404 * d:
1405 new head
1405 new head
1406 [13207e5a10d9]
1406 [13207e5a10d9]
1407
1407
1408 1970-01-17 person <person>
1408 1970-01-17 person <person>
1409
1409
1410 * new branch
1410 * new branch
1411 [bbe44766e73d] <foo>
1411 [bbe44766e73d] <foo>
1412
1412
1413 1970-01-16 person <person>
1413 1970-01-16 person <person>
1414
1414
1415 * c:
1415 * c:
1416 no user, no domain
1416 no user, no domain
1417 [10e46f2dcbf4]
1417 [10e46f2dcbf4]
1418
1418
1419 1970-01-14 other <other@place>
1419 1970-01-14 other <other@place>
1420
1420
1421 * c:
1421 * c:
1422 no person
1422 no person
1423 [97054abb4ab8]
1423 [97054abb4ab8]
1424
1424
1425 1970-01-13 A. N. Other <other@place>
1425 1970-01-13 A. N. Other <other@place>
1426
1426
1427 * b:
1427 * b:
1428 other 1 other 2
1428 other 1 other 2
1429
1429
1430 other 3
1430 other 3
1431 [b608e9d1a3f0]
1431 [b608e9d1a3f0]
1432
1432
1433 1970-01-12 User Name <user@hostname>
1433 1970-01-12 User Name <user@hostname>
1434
1434
1435 * a:
1435 * a:
1436 line 1 line 2
1436 line 1 line 2
1437 [1e4e1b8f71e0]
1437 [1e4e1b8f71e0]
1438
1438
1439
1439
1440 Issue2130: xml output for 'hg heads' is malformed
1440 Issue2130: xml output for 'hg heads' is malformed
1441
1441
1442 $ hg heads --style changelog
1442 $ hg heads --style changelog
1443 2020-01-01 test <test>
1443 2020-01-01 test <test>
1444
1444
1445 * fourth, second, third:
1445 * fourth, second, third:
1446 third
1446 third
1447 [95c24699272e] [tip]
1447 [95c24699272e] [tip]
1448
1448
1449 1970-01-18 person <person>
1449 1970-01-18 person <person>
1450
1450
1451 * merge
1451 * merge
1452 [d41e714fe50d]
1452 [d41e714fe50d]
1453
1453
1454 1970-01-17 person <person>
1454 1970-01-17 person <person>
1455
1455
1456 * new branch
1456 * new branch
1457 [bbe44766e73d] <foo>
1457 [bbe44766e73d] <foo>
1458
1458
1459
1459
1460 Keys work:
1460 Keys work:
1461
1461
1462 $ for key in author branch branches date desc file_adds file_dels file_mods \
1462 $ for key in author branch branches date desc file_adds file_dels file_mods \
1463 > file_copies file_copies_switch files \
1463 > file_copies file_copies_switch files \
1464 > manifest node parents rev tags diffstat extras \
1464 > manifest node parents rev tags diffstat extras \
1465 > p1rev p2rev p1node p2node; do
1465 > p1rev p2rev p1node p2node; do
1466 > for mode in '' --verbose --debug; do
1466 > for mode in '' --verbose --debug; do
1467 > hg log $mode --template "$key$mode: {$key}\n"
1467 > hg log $mode --template "$key$mode: {$key}\n"
1468 > done
1468 > done
1469 > done
1469 > done
1470 author: test
1470 author: test
1471 author: User Name <user@hostname>
1471 author: User Name <user@hostname>
1472 author: person
1472 author: person
1473 author: person
1473 author: person
1474 author: person
1474 author: person
1475 author: person
1475 author: person
1476 author: other@place
1476 author: other@place
1477 author: A. N. Other <other@place>
1477 author: A. N. Other <other@place>
1478 author: User Name <user@hostname>
1478 author: User Name <user@hostname>
1479 author--verbose: test
1479 author--verbose: test
1480 author--verbose: User Name <user@hostname>
1480 author--verbose: User Name <user@hostname>
1481 author--verbose: person
1481 author--verbose: person
1482 author--verbose: person
1482 author--verbose: person
1483 author--verbose: person
1483 author--verbose: person
1484 author--verbose: person
1484 author--verbose: person
1485 author--verbose: other@place
1485 author--verbose: other@place
1486 author--verbose: A. N. Other <other@place>
1486 author--verbose: A. N. Other <other@place>
1487 author--verbose: User Name <user@hostname>
1487 author--verbose: User Name <user@hostname>
1488 author--debug: test
1488 author--debug: test
1489 author--debug: User Name <user@hostname>
1489 author--debug: User Name <user@hostname>
1490 author--debug: person
1490 author--debug: person
1491 author--debug: person
1491 author--debug: person
1492 author--debug: person
1492 author--debug: person
1493 author--debug: person
1493 author--debug: person
1494 author--debug: other@place
1494 author--debug: other@place
1495 author--debug: A. N. Other <other@place>
1495 author--debug: A. N. Other <other@place>
1496 author--debug: User Name <user@hostname>
1496 author--debug: User Name <user@hostname>
1497 branch: default
1497 branch: default
1498 branch: default
1498 branch: default
1499 branch: default
1499 branch: default
1500 branch: default
1500 branch: default
1501 branch: foo
1501 branch: foo
1502 branch: default
1502 branch: default
1503 branch: default
1503 branch: default
1504 branch: default
1504 branch: default
1505 branch: default
1505 branch: default
1506 branch--verbose: default
1506 branch--verbose: default
1507 branch--verbose: default
1507 branch--verbose: default
1508 branch--verbose: default
1508 branch--verbose: default
1509 branch--verbose: default
1509 branch--verbose: default
1510 branch--verbose: foo
1510 branch--verbose: foo
1511 branch--verbose: default
1511 branch--verbose: default
1512 branch--verbose: default
1512 branch--verbose: default
1513 branch--verbose: default
1513 branch--verbose: default
1514 branch--verbose: default
1514 branch--verbose: default
1515 branch--debug: default
1515 branch--debug: default
1516 branch--debug: default
1516 branch--debug: default
1517 branch--debug: default
1517 branch--debug: default
1518 branch--debug: default
1518 branch--debug: default
1519 branch--debug: foo
1519 branch--debug: foo
1520 branch--debug: default
1520 branch--debug: default
1521 branch--debug: default
1521 branch--debug: default
1522 branch--debug: default
1522 branch--debug: default
1523 branch--debug: default
1523 branch--debug: default
1524 branches:
1524 branches:
1525 branches:
1525 branches:
1526 branches:
1526 branches:
1527 branches:
1527 branches:
1528 branches: foo
1528 branches: foo
1529 branches:
1529 branches:
1530 branches:
1530 branches:
1531 branches:
1531 branches:
1532 branches:
1532 branches:
1533 branches--verbose:
1533 branches--verbose:
1534 branches--verbose:
1534 branches--verbose:
1535 branches--verbose:
1535 branches--verbose:
1536 branches--verbose:
1536 branches--verbose:
1537 branches--verbose: foo
1537 branches--verbose: foo
1538 branches--verbose:
1538 branches--verbose:
1539 branches--verbose:
1539 branches--verbose:
1540 branches--verbose:
1540 branches--verbose:
1541 branches--verbose:
1541 branches--verbose:
1542 branches--debug:
1542 branches--debug:
1543 branches--debug:
1543 branches--debug:
1544 branches--debug:
1544 branches--debug:
1545 branches--debug:
1545 branches--debug:
1546 branches--debug: foo
1546 branches--debug: foo
1547 branches--debug:
1547 branches--debug:
1548 branches--debug:
1548 branches--debug:
1549 branches--debug:
1549 branches--debug:
1550 branches--debug:
1550 branches--debug:
1551 date: 1577872860.00
1551 date: 1577872860.00
1552 date: 1000000.00
1552 date: 1000000.00
1553 date: 1500001.00
1553 date: 1500001.00
1554 date: 1500000.00
1554 date: 1500000.00
1555 date: 1400000.00
1555 date: 1400000.00
1556 date: 1300000.00
1556 date: 1300000.00
1557 date: 1200000.00
1557 date: 1200000.00
1558 date: 1100000.00
1558 date: 1100000.00
1559 date: 1000000.00
1559 date: 1000000.00
1560 date--verbose: 1577872860.00
1560 date--verbose: 1577872860.00
1561 date--verbose: 1000000.00
1561 date--verbose: 1000000.00
1562 date--verbose: 1500001.00
1562 date--verbose: 1500001.00
1563 date--verbose: 1500000.00
1563 date--verbose: 1500000.00
1564 date--verbose: 1400000.00
1564 date--verbose: 1400000.00
1565 date--verbose: 1300000.00
1565 date--verbose: 1300000.00
1566 date--verbose: 1200000.00
1566 date--verbose: 1200000.00
1567 date--verbose: 1100000.00
1567 date--verbose: 1100000.00
1568 date--verbose: 1000000.00
1568 date--verbose: 1000000.00
1569 date--debug: 1577872860.00
1569 date--debug: 1577872860.00
1570 date--debug: 1000000.00
1570 date--debug: 1000000.00
1571 date--debug: 1500001.00
1571 date--debug: 1500001.00
1572 date--debug: 1500000.00
1572 date--debug: 1500000.00
1573 date--debug: 1400000.00
1573 date--debug: 1400000.00
1574 date--debug: 1300000.00
1574 date--debug: 1300000.00
1575 date--debug: 1200000.00
1575 date--debug: 1200000.00
1576 date--debug: 1100000.00
1576 date--debug: 1100000.00
1577 date--debug: 1000000.00
1577 date--debug: 1000000.00
1578 desc: third
1578 desc: third
1579 desc: second
1579 desc: second
1580 desc: merge
1580 desc: merge
1581 desc: new head
1581 desc: new head
1582 desc: new branch
1582 desc: new branch
1583 desc: no user, no domain
1583 desc: no user, no domain
1584 desc: no person
1584 desc: no person
1585 desc: other 1
1585 desc: other 1
1586 other 2
1586 other 2
1587
1587
1588 other 3
1588 other 3
1589 desc: line 1
1589 desc: line 1
1590 line 2
1590 line 2
1591 desc--verbose: third
1591 desc--verbose: third
1592 desc--verbose: second
1592 desc--verbose: second
1593 desc--verbose: merge
1593 desc--verbose: merge
1594 desc--verbose: new head
1594 desc--verbose: new head
1595 desc--verbose: new branch
1595 desc--verbose: new branch
1596 desc--verbose: no user, no domain
1596 desc--verbose: no user, no domain
1597 desc--verbose: no person
1597 desc--verbose: no person
1598 desc--verbose: other 1
1598 desc--verbose: other 1
1599 other 2
1599 other 2
1600
1600
1601 other 3
1601 other 3
1602 desc--verbose: line 1
1602 desc--verbose: line 1
1603 line 2
1603 line 2
1604 desc--debug: third
1604 desc--debug: third
1605 desc--debug: second
1605 desc--debug: second
1606 desc--debug: merge
1606 desc--debug: merge
1607 desc--debug: new head
1607 desc--debug: new head
1608 desc--debug: new branch
1608 desc--debug: new branch
1609 desc--debug: no user, no domain
1609 desc--debug: no user, no domain
1610 desc--debug: no person
1610 desc--debug: no person
1611 desc--debug: other 1
1611 desc--debug: other 1
1612 other 2
1612 other 2
1613
1613
1614 other 3
1614 other 3
1615 desc--debug: line 1
1615 desc--debug: line 1
1616 line 2
1616 line 2
1617 file_adds: fourth third
1617 file_adds: fourth third
1618 file_adds: second
1618 file_adds: second
1619 file_adds:
1619 file_adds:
1620 file_adds: d
1620 file_adds: d
1621 file_adds:
1621 file_adds:
1622 file_adds:
1622 file_adds:
1623 file_adds: c
1623 file_adds: c
1624 file_adds: b
1624 file_adds: b
1625 file_adds: a
1625 file_adds: a
1626 file_adds--verbose: fourth third
1626 file_adds--verbose: fourth third
1627 file_adds--verbose: second
1627 file_adds--verbose: second
1628 file_adds--verbose:
1628 file_adds--verbose:
1629 file_adds--verbose: d
1629 file_adds--verbose: d
1630 file_adds--verbose:
1630 file_adds--verbose:
1631 file_adds--verbose:
1631 file_adds--verbose:
1632 file_adds--verbose: c
1632 file_adds--verbose: c
1633 file_adds--verbose: b
1633 file_adds--verbose: b
1634 file_adds--verbose: a
1634 file_adds--verbose: a
1635 file_adds--debug: fourth third
1635 file_adds--debug: fourth third
1636 file_adds--debug: second
1636 file_adds--debug: second
1637 file_adds--debug:
1637 file_adds--debug:
1638 file_adds--debug: d
1638 file_adds--debug: d
1639 file_adds--debug:
1639 file_adds--debug:
1640 file_adds--debug:
1640 file_adds--debug:
1641 file_adds--debug: c
1641 file_adds--debug: c
1642 file_adds--debug: b
1642 file_adds--debug: b
1643 file_adds--debug: a
1643 file_adds--debug: a
1644 file_dels: second
1644 file_dels: second
1645 file_dels:
1645 file_dels:
1646 file_dels:
1646 file_dels:
1647 file_dels:
1647 file_dels:
1648 file_dels:
1648 file_dels:
1649 file_dels:
1649 file_dels:
1650 file_dels:
1650 file_dels:
1651 file_dels:
1651 file_dels:
1652 file_dels:
1652 file_dels:
1653 file_dels--verbose: second
1653 file_dels--verbose: second
1654 file_dels--verbose:
1654 file_dels--verbose:
1655 file_dels--verbose:
1655 file_dels--verbose:
1656 file_dels--verbose:
1656 file_dels--verbose:
1657 file_dels--verbose:
1657 file_dels--verbose:
1658 file_dels--verbose:
1658 file_dels--verbose:
1659 file_dels--verbose:
1659 file_dels--verbose:
1660 file_dels--verbose:
1660 file_dels--verbose:
1661 file_dels--verbose:
1661 file_dels--verbose:
1662 file_dels--debug: second
1662 file_dels--debug: second
1663 file_dels--debug:
1663 file_dels--debug:
1664 file_dels--debug:
1664 file_dels--debug:
1665 file_dels--debug:
1665 file_dels--debug:
1666 file_dels--debug:
1666 file_dels--debug:
1667 file_dels--debug:
1667 file_dels--debug:
1668 file_dels--debug:
1668 file_dels--debug:
1669 file_dels--debug:
1669 file_dels--debug:
1670 file_dels--debug:
1670 file_dels--debug:
1671 file_mods:
1671 file_mods:
1672 file_mods:
1672 file_mods:
1673 file_mods:
1673 file_mods:
1674 file_mods:
1674 file_mods:
1675 file_mods:
1675 file_mods:
1676 file_mods: c
1676 file_mods: c
1677 file_mods:
1677 file_mods:
1678 file_mods:
1678 file_mods:
1679 file_mods:
1679 file_mods:
1680 file_mods--verbose:
1680 file_mods--verbose:
1681 file_mods--verbose:
1681 file_mods--verbose:
1682 file_mods--verbose:
1682 file_mods--verbose:
1683 file_mods--verbose:
1683 file_mods--verbose:
1684 file_mods--verbose:
1684 file_mods--verbose:
1685 file_mods--verbose: c
1685 file_mods--verbose: c
1686 file_mods--verbose:
1686 file_mods--verbose:
1687 file_mods--verbose:
1687 file_mods--verbose:
1688 file_mods--verbose:
1688 file_mods--verbose:
1689 file_mods--debug:
1689 file_mods--debug:
1690 file_mods--debug:
1690 file_mods--debug:
1691 file_mods--debug:
1691 file_mods--debug:
1692 file_mods--debug:
1692 file_mods--debug:
1693 file_mods--debug:
1693 file_mods--debug:
1694 file_mods--debug: c
1694 file_mods--debug: c
1695 file_mods--debug:
1695 file_mods--debug:
1696 file_mods--debug:
1696 file_mods--debug:
1697 file_mods--debug:
1697 file_mods--debug:
1698 file_copies: fourth (second)
1698 file_copies: fourth (second)
1699 file_copies:
1699 file_copies:
1700 file_copies:
1700 file_copies:
1701 file_copies:
1701 file_copies:
1702 file_copies:
1702 file_copies:
1703 file_copies:
1703 file_copies:
1704 file_copies:
1704 file_copies:
1705 file_copies:
1705 file_copies:
1706 file_copies:
1706 file_copies:
1707 file_copies--verbose: fourth (second)
1707 file_copies--verbose: fourth (second)
1708 file_copies--verbose:
1708 file_copies--verbose:
1709 file_copies--verbose:
1709 file_copies--verbose:
1710 file_copies--verbose:
1710 file_copies--verbose:
1711 file_copies--verbose:
1711 file_copies--verbose:
1712 file_copies--verbose:
1712 file_copies--verbose:
1713 file_copies--verbose:
1713 file_copies--verbose:
1714 file_copies--verbose:
1714 file_copies--verbose:
1715 file_copies--verbose:
1715 file_copies--verbose:
1716 file_copies--debug: fourth (second)
1716 file_copies--debug: fourth (second)
1717 file_copies--debug:
1717 file_copies--debug:
1718 file_copies--debug:
1718 file_copies--debug:
1719 file_copies--debug:
1719 file_copies--debug:
1720 file_copies--debug:
1720 file_copies--debug:
1721 file_copies--debug:
1721 file_copies--debug:
1722 file_copies--debug:
1722 file_copies--debug:
1723 file_copies--debug:
1723 file_copies--debug:
1724 file_copies--debug:
1724 file_copies--debug:
1725 file_copies_switch:
1725 file_copies_switch:
1726 file_copies_switch:
1726 file_copies_switch:
1727 file_copies_switch:
1727 file_copies_switch:
1728 file_copies_switch:
1728 file_copies_switch:
1729 file_copies_switch:
1729 file_copies_switch:
1730 file_copies_switch:
1730 file_copies_switch:
1731 file_copies_switch:
1731 file_copies_switch:
1732 file_copies_switch:
1732 file_copies_switch:
1733 file_copies_switch:
1733 file_copies_switch:
1734 file_copies_switch--verbose:
1734 file_copies_switch--verbose:
1735 file_copies_switch--verbose:
1735 file_copies_switch--verbose:
1736 file_copies_switch--verbose:
1736 file_copies_switch--verbose:
1737 file_copies_switch--verbose:
1737 file_copies_switch--verbose:
1738 file_copies_switch--verbose:
1738 file_copies_switch--verbose:
1739 file_copies_switch--verbose:
1739 file_copies_switch--verbose:
1740 file_copies_switch--verbose:
1740 file_copies_switch--verbose:
1741 file_copies_switch--verbose:
1741 file_copies_switch--verbose:
1742 file_copies_switch--verbose:
1742 file_copies_switch--verbose:
1743 file_copies_switch--debug:
1743 file_copies_switch--debug:
1744 file_copies_switch--debug:
1744 file_copies_switch--debug:
1745 file_copies_switch--debug:
1745 file_copies_switch--debug:
1746 file_copies_switch--debug:
1746 file_copies_switch--debug:
1747 file_copies_switch--debug:
1747 file_copies_switch--debug:
1748 file_copies_switch--debug:
1748 file_copies_switch--debug:
1749 file_copies_switch--debug:
1749 file_copies_switch--debug:
1750 file_copies_switch--debug:
1750 file_copies_switch--debug:
1751 file_copies_switch--debug:
1751 file_copies_switch--debug:
1752 files: fourth second third
1752 files: fourth second third
1753 files: second
1753 files: second
1754 files:
1754 files:
1755 files: d
1755 files: d
1756 files:
1756 files:
1757 files: c
1757 files: c
1758 files: c
1758 files: c
1759 files: b
1759 files: b
1760 files: a
1760 files: a
1761 files--verbose: fourth second third
1761 files--verbose: fourth second third
1762 files--verbose: second
1762 files--verbose: second
1763 files--verbose:
1763 files--verbose:
1764 files--verbose: d
1764 files--verbose: d
1765 files--verbose:
1765 files--verbose:
1766 files--verbose: c
1766 files--verbose: c
1767 files--verbose: c
1767 files--verbose: c
1768 files--verbose: b
1768 files--verbose: b
1769 files--verbose: a
1769 files--verbose: a
1770 files--debug: fourth second third
1770 files--debug: fourth second third
1771 files--debug: second
1771 files--debug: second
1772 files--debug:
1772 files--debug:
1773 files--debug: d
1773 files--debug: d
1774 files--debug:
1774 files--debug:
1775 files--debug: c
1775 files--debug: c
1776 files--debug: c
1776 files--debug: c
1777 files--debug: b
1777 files--debug: b
1778 files--debug: a
1778 files--debug: a
1779 manifest: 6:94961b75a2da
1779 manifest: 6:94961b75a2da
1780 manifest: 5:f2dbc354b94e
1780 manifest: 5:f2dbc354b94e
1781 manifest: 4:4dc3def4f9b4
1781 manifest: 4:4dc3def4f9b4
1782 manifest: 4:4dc3def4f9b4
1782 manifest: 4:4dc3def4f9b4
1783 manifest: 3:cb5a1327723b
1783 manifest: 3:cb5a1327723b
1784 manifest: 3:cb5a1327723b
1784 manifest: 3:cb5a1327723b
1785 manifest: 2:6e0e82995c35
1785 manifest: 2:6e0e82995c35
1786 manifest: 1:4e8d705b1e53
1786 manifest: 1:4e8d705b1e53
1787 manifest: 0:a0c8bcbbb45c
1787 manifest: 0:a0c8bcbbb45c
1788 manifest--verbose: 6:94961b75a2da
1788 manifest--verbose: 6:94961b75a2da
1789 manifest--verbose: 5:f2dbc354b94e
1789 manifest--verbose: 5:f2dbc354b94e
1790 manifest--verbose: 4:4dc3def4f9b4
1790 manifest--verbose: 4:4dc3def4f9b4
1791 manifest--verbose: 4:4dc3def4f9b4
1791 manifest--verbose: 4:4dc3def4f9b4
1792 manifest--verbose: 3:cb5a1327723b
1792 manifest--verbose: 3:cb5a1327723b
1793 manifest--verbose: 3:cb5a1327723b
1793 manifest--verbose: 3:cb5a1327723b
1794 manifest--verbose: 2:6e0e82995c35
1794 manifest--verbose: 2:6e0e82995c35
1795 manifest--verbose: 1:4e8d705b1e53
1795 manifest--verbose: 1:4e8d705b1e53
1796 manifest--verbose: 0:a0c8bcbbb45c
1796 manifest--verbose: 0:a0c8bcbbb45c
1797 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1797 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1798 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1798 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1799 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1799 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1800 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1800 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1801 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1801 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1802 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1802 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1803 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1803 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1804 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1804 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1805 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1805 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1806 node: 95c24699272ef57d062b8bccc32c878bf841784a
1806 node: 95c24699272ef57d062b8bccc32c878bf841784a
1807 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1807 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1808 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1808 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1809 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1809 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1810 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1810 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1811 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1811 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1812 node: 97054abb4ab824450e9164180baf491ae0078465
1812 node: 97054abb4ab824450e9164180baf491ae0078465
1813 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1813 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1814 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1814 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1815 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1815 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1816 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1816 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1817 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1817 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1818 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1818 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1819 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1819 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1820 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1820 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1821 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1821 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1822 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1822 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1823 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1823 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1824 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1824 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1825 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1825 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1826 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1826 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1827 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1827 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1828 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1828 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1829 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1829 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1830 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1830 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1831 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1831 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1832 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1832 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1833 parents:
1833 parents:
1834 parents: -1:000000000000
1834 parents: -1:000000000000
1835 parents: 5:13207e5a10d9 4:bbe44766e73d
1835 parents: 5:13207e5a10d9 4:bbe44766e73d
1836 parents: 3:10e46f2dcbf4
1836 parents: 3:10e46f2dcbf4
1837 parents:
1837 parents:
1838 parents:
1838 parents:
1839 parents:
1839 parents:
1840 parents:
1840 parents:
1841 parents:
1841 parents:
1842 parents--verbose:
1842 parents--verbose:
1843 parents--verbose: -1:000000000000
1843 parents--verbose: -1:000000000000
1844 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1844 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1845 parents--verbose: 3:10e46f2dcbf4
1845 parents--verbose: 3:10e46f2dcbf4
1846 parents--verbose:
1846 parents--verbose:
1847 parents--verbose:
1847 parents--verbose:
1848 parents--verbose:
1848 parents--verbose:
1849 parents--verbose:
1849 parents--verbose:
1850 parents--verbose:
1850 parents--verbose:
1851 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1851 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1852 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1852 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1853 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1853 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1854 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1854 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1855 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1855 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1856 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1856 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1857 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1857 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1858 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1858 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1859 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1859 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1860 rev: 8
1860 rev: 8
1861 rev: 7
1861 rev: 7
1862 rev: 6
1862 rev: 6
1863 rev: 5
1863 rev: 5
1864 rev: 4
1864 rev: 4
1865 rev: 3
1865 rev: 3
1866 rev: 2
1866 rev: 2
1867 rev: 1
1867 rev: 1
1868 rev: 0
1868 rev: 0
1869 rev--verbose: 8
1869 rev--verbose: 8
1870 rev--verbose: 7
1870 rev--verbose: 7
1871 rev--verbose: 6
1871 rev--verbose: 6
1872 rev--verbose: 5
1872 rev--verbose: 5
1873 rev--verbose: 4
1873 rev--verbose: 4
1874 rev--verbose: 3
1874 rev--verbose: 3
1875 rev--verbose: 2
1875 rev--verbose: 2
1876 rev--verbose: 1
1876 rev--verbose: 1
1877 rev--verbose: 0
1877 rev--verbose: 0
1878 rev--debug: 8
1878 rev--debug: 8
1879 rev--debug: 7
1879 rev--debug: 7
1880 rev--debug: 6
1880 rev--debug: 6
1881 rev--debug: 5
1881 rev--debug: 5
1882 rev--debug: 4
1882 rev--debug: 4
1883 rev--debug: 3
1883 rev--debug: 3
1884 rev--debug: 2
1884 rev--debug: 2
1885 rev--debug: 1
1885 rev--debug: 1
1886 rev--debug: 0
1886 rev--debug: 0
1887 tags: tip
1887 tags: tip
1888 tags:
1888 tags:
1889 tags:
1889 tags:
1890 tags:
1890 tags:
1891 tags:
1891 tags:
1892 tags:
1892 tags:
1893 tags:
1893 tags:
1894 tags:
1894 tags:
1895 tags:
1895 tags:
1896 tags--verbose: tip
1896 tags--verbose: tip
1897 tags--verbose:
1897 tags--verbose:
1898 tags--verbose:
1898 tags--verbose:
1899 tags--verbose:
1899 tags--verbose:
1900 tags--verbose:
1900 tags--verbose:
1901 tags--verbose:
1901 tags--verbose:
1902 tags--verbose:
1902 tags--verbose:
1903 tags--verbose:
1903 tags--verbose:
1904 tags--verbose:
1904 tags--verbose:
1905 tags--debug: tip
1905 tags--debug: tip
1906 tags--debug:
1906 tags--debug:
1907 tags--debug:
1907 tags--debug:
1908 tags--debug:
1908 tags--debug:
1909 tags--debug:
1909 tags--debug:
1910 tags--debug:
1910 tags--debug:
1911 tags--debug:
1911 tags--debug:
1912 tags--debug:
1912 tags--debug:
1913 tags--debug:
1913 tags--debug:
1914 diffstat: 3: +2/-1
1914 diffstat: 3: +2/-1
1915 diffstat: 1: +1/-0
1915 diffstat: 1: +1/-0
1916 diffstat: 0: +0/-0
1916 diffstat: 0: +0/-0
1917 diffstat: 1: +1/-0
1917 diffstat: 1: +1/-0
1918 diffstat: 0: +0/-0
1918 diffstat: 0: +0/-0
1919 diffstat: 1: +1/-0
1919 diffstat: 1: +1/-0
1920 diffstat: 1: +4/-0
1920 diffstat: 1: +4/-0
1921 diffstat: 1: +2/-0
1921 diffstat: 1: +2/-0
1922 diffstat: 1: +1/-0
1922 diffstat: 1: +1/-0
1923 diffstat--verbose: 3: +2/-1
1923 diffstat--verbose: 3: +2/-1
1924 diffstat--verbose: 1: +1/-0
1924 diffstat--verbose: 1: +1/-0
1925 diffstat--verbose: 0: +0/-0
1925 diffstat--verbose: 0: +0/-0
1926 diffstat--verbose: 1: +1/-0
1926 diffstat--verbose: 1: +1/-0
1927 diffstat--verbose: 0: +0/-0
1927 diffstat--verbose: 0: +0/-0
1928 diffstat--verbose: 1: +1/-0
1928 diffstat--verbose: 1: +1/-0
1929 diffstat--verbose: 1: +4/-0
1929 diffstat--verbose: 1: +4/-0
1930 diffstat--verbose: 1: +2/-0
1930 diffstat--verbose: 1: +2/-0
1931 diffstat--verbose: 1: +1/-0
1931 diffstat--verbose: 1: +1/-0
1932 diffstat--debug: 3: +2/-1
1932 diffstat--debug: 3: +2/-1
1933 diffstat--debug: 1: +1/-0
1933 diffstat--debug: 1: +1/-0
1934 diffstat--debug: 0: +0/-0
1934 diffstat--debug: 0: +0/-0
1935 diffstat--debug: 1: +1/-0
1935 diffstat--debug: 1: +1/-0
1936 diffstat--debug: 0: +0/-0
1936 diffstat--debug: 0: +0/-0
1937 diffstat--debug: 1: +1/-0
1937 diffstat--debug: 1: +1/-0
1938 diffstat--debug: 1: +4/-0
1938 diffstat--debug: 1: +4/-0
1939 diffstat--debug: 1: +2/-0
1939 diffstat--debug: 1: +2/-0
1940 diffstat--debug: 1: +1/-0
1940 diffstat--debug: 1: +1/-0
1941 extras: branch=default
1941 extras: branch=default
1942 extras: branch=default
1942 extras: branch=default
1943 extras: branch=default
1943 extras: branch=default
1944 extras: branch=default
1944 extras: branch=default
1945 extras: branch=foo
1945 extras: branch=foo
1946 extras: branch=default
1946 extras: branch=default
1947 extras: branch=default
1947 extras: branch=default
1948 extras: branch=default
1948 extras: branch=default
1949 extras: branch=default
1949 extras: branch=default
1950 extras--verbose: branch=default
1950 extras--verbose: branch=default
1951 extras--verbose: branch=default
1951 extras--verbose: branch=default
1952 extras--verbose: branch=default
1952 extras--verbose: branch=default
1953 extras--verbose: branch=default
1953 extras--verbose: branch=default
1954 extras--verbose: branch=foo
1954 extras--verbose: branch=foo
1955 extras--verbose: branch=default
1955 extras--verbose: branch=default
1956 extras--verbose: branch=default
1956 extras--verbose: branch=default
1957 extras--verbose: branch=default
1957 extras--verbose: branch=default
1958 extras--verbose: branch=default
1958 extras--verbose: branch=default
1959 extras--debug: branch=default
1959 extras--debug: branch=default
1960 extras--debug: branch=default
1960 extras--debug: branch=default
1961 extras--debug: branch=default
1961 extras--debug: branch=default
1962 extras--debug: branch=default
1962 extras--debug: branch=default
1963 extras--debug: branch=foo
1963 extras--debug: branch=foo
1964 extras--debug: branch=default
1964 extras--debug: branch=default
1965 extras--debug: branch=default
1965 extras--debug: branch=default
1966 extras--debug: branch=default
1966 extras--debug: branch=default
1967 extras--debug: branch=default
1967 extras--debug: branch=default
1968 p1rev: 7
1968 p1rev: 7
1969 p1rev: -1
1969 p1rev: -1
1970 p1rev: 5
1970 p1rev: 5
1971 p1rev: 3
1971 p1rev: 3
1972 p1rev: 3
1972 p1rev: 3
1973 p1rev: 2
1973 p1rev: 2
1974 p1rev: 1
1974 p1rev: 1
1975 p1rev: 0
1975 p1rev: 0
1976 p1rev: -1
1976 p1rev: -1
1977 p1rev--verbose: 7
1977 p1rev--verbose: 7
1978 p1rev--verbose: -1
1978 p1rev--verbose: -1
1979 p1rev--verbose: 5
1979 p1rev--verbose: 5
1980 p1rev--verbose: 3
1980 p1rev--verbose: 3
1981 p1rev--verbose: 3
1981 p1rev--verbose: 3
1982 p1rev--verbose: 2
1982 p1rev--verbose: 2
1983 p1rev--verbose: 1
1983 p1rev--verbose: 1
1984 p1rev--verbose: 0
1984 p1rev--verbose: 0
1985 p1rev--verbose: -1
1985 p1rev--verbose: -1
1986 p1rev--debug: 7
1986 p1rev--debug: 7
1987 p1rev--debug: -1
1987 p1rev--debug: -1
1988 p1rev--debug: 5
1988 p1rev--debug: 5
1989 p1rev--debug: 3
1989 p1rev--debug: 3
1990 p1rev--debug: 3
1990 p1rev--debug: 3
1991 p1rev--debug: 2
1991 p1rev--debug: 2
1992 p1rev--debug: 1
1992 p1rev--debug: 1
1993 p1rev--debug: 0
1993 p1rev--debug: 0
1994 p1rev--debug: -1
1994 p1rev--debug: -1
1995 p2rev: -1
1995 p2rev: -1
1996 p2rev: -1
1996 p2rev: -1
1997 p2rev: 4
1997 p2rev: 4
1998 p2rev: -1
1998 p2rev: -1
1999 p2rev: -1
1999 p2rev: -1
2000 p2rev: -1
2000 p2rev: -1
2001 p2rev: -1
2001 p2rev: -1
2002 p2rev: -1
2002 p2rev: -1
2003 p2rev: -1
2003 p2rev: -1
2004 p2rev--verbose: -1
2004 p2rev--verbose: -1
2005 p2rev--verbose: -1
2005 p2rev--verbose: -1
2006 p2rev--verbose: 4
2006 p2rev--verbose: 4
2007 p2rev--verbose: -1
2007 p2rev--verbose: -1
2008 p2rev--verbose: -1
2008 p2rev--verbose: -1
2009 p2rev--verbose: -1
2009 p2rev--verbose: -1
2010 p2rev--verbose: -1
2010 p2rev--verbose: -1
2011 p2rev--verbose: -1
2011 p2rev--verbose: -1
2012 p2rev--verbose: -1
2012 p2rev--verbose: -1
2013 p2rev--debug: -1
2013 p2rev--debug: -1
2014 p2rev--debug: -1
2014 p2rev--debug: -1
2015 p2rev--debug: 4
2015 p2rev--debug: 4
2016 p2rev--debug: -1
2016 p2rev--debug: -1
2017 p2rev--debug: -1
2017 p2rev--debug: -1
2018 p2rev--debug: -1
2018 p2rev--debug: -1
2019 p2rev--debug: -1
2019 p2rev--debug: -1
2020 p2rev--debug: -1
2020 p2rev--debug: -1
2021 p2rev--debug: -1
2021 p2rev--debug: -1
2022 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2022 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2023 p1node: 0000000000000000000000000000000000000000
2023 p1node: 0000000000000000000000000000000000000000
2024 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
2024 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
2025 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2025 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2026 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2026 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2027 p1node: 97054abb4ab824450e9164180baf491ae0078465
2027 p1node: 97054abb4ab824450e9164180baf491ae0078465
2028 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2028 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2029 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
2029 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
2030 p1node: 0000000000000000000000000000000000000000
2030 p1node: 0000000000000000000000000000000000000000
2031 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2031 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2032 p1node--verbose: 0000000000000000000000000000000000000000
2032 p1node--verbose: 0000000000000000000000000000000000000000
2033 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
2033 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
2034 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2034 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2035 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2035 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2036 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2036 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2037 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2037 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2038 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2038 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2039 p1node--verbose: 0000000000000000000000000000000000000000
2039 p1node--verbose: 0000000000000000000000000000000000000000
2040 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2040 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2041 p1node--debug: 0000000000000000000000000000000000000000
2041 p1node--debug: 0000000000000000000000000000000000000000
2042 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2042 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2043 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2043 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2044 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2044 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2045 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2045 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2046 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2046 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2047 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2047 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2048 p1node--debug: 0000000000000000000000000000000000000000
2048 p1node--debug: 0000000000000000000000000000000000000000
2049 p2node: 0000000000000000000000000000000000000000
2049 p2node: 0000000000000000000000000000000000000000
2050 p2node: 0000000000000000000000000000000000000000
2050 p2node: 0000000000000000000000000000000000000000
2051 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2051 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2052 p2node: 0000000000000000000000000000000000000000
2052 p2node: 0000000000000000000000000000000000000000
2053 p2node: 0000000000000000000000000000000000000000
2053 p2node: 0000000000000000000000000000000000000000
2054 p2node: 0000000000000000000000000000000000000000
2054 p2node: 0000000000000000000000000000000000000000
2055 p2node: 0000000000000000000000000000000000000000
2055 p2node: 0000000000000000000000000000000000000000
2056 p2node: 0000000000000000000000000000000000000000
2056 p2node: 0000000000000000000000000000000000000000
2057 p2node: 0000000000000000000000000000000000000000
2057 p2node: 0000000000000000000000000000000000000000
2058 p2node--verbose: 0000000000000000000000000000000000000000
2058 p2node--verbose: 0000000000000000000000000000000000000000
2059 p2node--verbose: 0000000000000000000000000000000000000000
2059 p2node--verbose: 0000000000000000000000000000000000000000
2060 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2060 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2061 p2node--verbose: 0000000000000000000000000000000000000000
2061 p2node--verbose: 0000000000000000000000000000000000000000
2062 p2node--verbose: 0000000000000000000000000000000000000000
2062 p2node--verbose: 0000000000000000000000000000000000000000
2063 p2node--verbose: 0000000000000000000000000000000000000000
2063 p2node--verbose: 0000000000000000000000000000000000000000
2064 p2node--verbose: 0000000000000000000000000000000000000000
2064 p2node--verbose: 0000000000000000000000000000000000000000
2065 p2node--verbose: 0000000000000000000000000000000000000000
2065 p2node--verbose: 0000000000000000000000000000000000000000
2066 p2node--verbose: 0000000000000000000000000000000000000000
2066 p2node--verbose: 0000000000000000000000000000000000000000
2067 p2node--debug: 0000000000000000000000000000000000000000
2067 p2node--debug: 0000000000000000000000000000000000000000
2068 p2node--debug: 0000000000000000000000000000000000000000
2068 p2node--debug: 0000000000000000000000000000000000000000
2069 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2069 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2070 p2node--debug: 0000000000000000000000000000000000000000
2070 p2node--debug: 0000000000000000000000000000000000000000
2071 p2node--debug: 0000000000000000000000000000000000000000
2071 p2node--debug: 0000000000000000000000000000000000000000
2072 p2node--debug: 0000000000000000000000000000000000000000
2072 p2node--debug: 0000000000000000000000000000000000000000
2073 p2node--debug: 0000000000000000000000000000000000000000
2073 p2node--debug: 0000000000000000000000000000000000000000
2074 p2node--debug: 0000000000000000000000000000000000000000
2074 p2node--debug: 0000000000000000000000000000000000000000
2075 p2node--debug: 0000000000000000000000000000000000000000
2075 p2node--debug: 0000000000000000000000000000000000000000
2076
2076
2077 Filters work:
2077 Filters work:
2078
2078
2079 $ hg log --template '{author|domain}\n'
2079 $ hg log --template '{author|domain}\n'
2080
2080
2081 hostname
2081 hostname
2082
2082
2083
2083
2084
2084
2085
2085
2086 place
2086 place
2087 place
2087 place
2088 hostname
2088 hostname
2089
2089
2090 $ hg log --template '{author|person}\n'
2090 $ hg log --template '{author|person}\n'
2091 test
2091 test
2092 User Name
2092 User Name
2093 person
2093 person
2094 person
2094 person
2095 person
2095 person
2096 person
2096 person
2097 other
2097 other
2098 A. N. Other
2098 A. N. Other
2099 User Name
2099 User Name
2100
2100
2101 $ hg log --template '{author|user}\n'
2101 $ hg log --template '{author|user}\n'
2102 test
2102 test
2103 user
2103 user
2104 person
2104 person
2105 person
2105 person
2106 person
2106 person
2107 person
2107 person
2108 other
2108 other
2109 other
2109 other
2110 user
2110 user
2111
2111
2112 $ hg log --template '{date|date}\n'
2112 $ hg log --template '{date|date}\n'
2113 Wed Jan 01 10:01:00 2020 +0000
2113 Wed Jan 01 10:01:00 2020 +0000
2114 Mon Jan 12 13:46:40 1970 +0000
2114 Mon Jan 12 13:46:40 1970 +0000
2115 Sun Jan 18 08:40:01 1970 +0000
2115 Sun Jan 18 08:40:01 1970 +0000
2116 Sun Jan 18 08:40:00 1970 +0000
2116 Sun Jan 18 08:40:00 1970 +0000
2117 Sat Jan 17 04:53:20 1970 +0000
2117 Sat Jan 17 04:53:20 1970 +0000
2118 Fri Jan 16 01:06:40 1970 +0000
2118 Fri Jan 16 01:06:40 1970 +0000
2119 Wed Jan 14 21:20:00 1970 +0000
2119 Wed Jan 14 21:20:00 1970 +0000
2120 Tue Jan 13 17:33:20 1970 +0000
2120 Tue Jan 13 17:33:20 1970 +0000
2121 Mon Jan 12 13:46:40 1970 +0000
2121 Mon Jan 12 13:46:40 1970 +0000
2122
2122
2123 $ hg log --template '{date|isodate}\n'
2123 $ hg log --template '{date|isodate}\n'
2124 2020-01-01 10:01 +0000
2124 2020-01-01 10:01 +0000
2125 1970-01-12 13:46 +0000
2125 1970-01-12 13:46 +0000
2126 1970-01-18 08:40 +0000
2126 1970-01-18 08:40 +0000
2127 1970-01-18 08:40 +0000
2127 1970-01-18 08:40 +0000
2128 1970-01-17 04:53 +0000
2128 1970-01-17 04:53 +0000
2129 1970-01-16 01:06 +0000
2129 1970-01-16 01:06 +0000
2130 1970-01-14 21:20 +0000
2130 1970-01-14 21:20 +0000
2131 1970-01-13 17:33 +0000
2131 1970-01-13 17:33 +0000
2132 1970-01-12 13:46 +0000
2132 1970-01-12 13:46 +0000
2133
2133
2134 $ hg log --template '{date|isodatesec}\n'
2134 $ hg log --template '{date|isodatesec}\n'
2135 2020-01-01 10:01:00 +0000
2135 2020-01-01 10:01:00 +0000
2136 1970-01-12 13:46:40 +0000
2136 1970-01-12 13:46:40 +0000
2137 1970-01-18 08:40:01 +0000
2137 1970-01-18 08:40:01 +0000
2138 1970-01-18 08:40:00 +0000
2138 1970-01-18 08:40:00 +0000
2139 1970-01-17 04:53:20 +0000
2139 1970-01-17 04:53:20 +0000
2140 1970-01-16 01:06:40 +0000
2140 1970-01-16 01:06:40 +0000
2141 1970-01-14 21:20:00 +0000
2141 1970-01-14 21:20:00 +0000
2142 1970-01-13 17:33:20 +0000
2142 1970-01-13 17:33:20 +0000
2143 1970-01-12 13:46:40 +0000
2143 1970-01-12 13:46:40 +0000
2144
2144
2145 $ hg log --template '{date|rfc822date}\n'
2145 $ hg log --template '{date|rfc822date}\n'
2146 Wed, 01 Jan 2020 10:01:00 +0000
2146 Wed, 01 Jan 2020 10:01:00 +0000
2147 Mon, 12 Jan 1970 13:46:40 +0000
2147 Mon, 12 Jan 1970 13:46:40 +0000
2148 Sun, 18 Jan 1970 08:40:01 +0000
2148 Sun, 18 Jan 1970 08:40:01 +0000
2149 Sun, 18 Jan 1970 08:40:00 +0000
2149 Sun, 18 Jan 1970 08:40:00 +0000
2150 Sat, 17 Jan 1970 04:53:20 +0000
2150 Sat, 17 Jan 1970 04:53:20 +0000
2151 Fri, 16 Jan 1970 01:06:40 +0000
2151 Fri, 16 Jan 1970 01:06:40 +0000
2152 Wed, 14 Jan 1970 21:20:00 +0000
2152 Wed, 14 Jan 1970 21:20:00 +0000
2153 Tue, 13 Jan 1970 17:33:20 +0000
2153 Tue, 13 Jan 1970 17:33:20 +0000
2154 Mon, 12 Jan 1970 13:46:40 +0000
2154 Mon, 12 Jan 1970 13:46:40 +0000
2155
2155
2156 $ hg log --template '{desc|firstline}\n'
2156 $ hg log --template '{desc|firstline}\n'
2157 third
2157 third
2158 second
2158 second
2159 merge
2159 merge
2160 new head
2160 new head
2161 new branch
2161 new branch
2162 no user, no domain
2162 no user, no domain
2163 no person
2163 no person
2164 other 1
2164 other 1
2165 line 1
2165 line 1
2166
2166
2167 $ hg log --template '{node|short}\n'
2167 $ hg log --template '{node|short}\n'
2168 95c24699272e
2168 95c24699272e
2169 29114dbae42b
2169 29114dbae42b
2170 d41e714fe50d
2170 d41e714fe50d
2171 13207e5a10d9
2171 13207e5a10d9
2172 bbe44766e73d
2172 bbe44766e73d
2173 10e46f2dcbf4
2173 10e46f2dcbf4
2174 97054abb4ab8
2174 97054abb4ab8
2175 b608e9d1a3f0
2175 b608e9d1a3f0
2176 1e4e1b8f71e0
2176 1e4e1b8f71e0
2177
2177
2178 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2178 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2179 <changeset author="test"/>
2179 <changeset author="test"/>
2180 <changeset author="User Name &lt;user@hostname&gt;"/>
2180 <changeset author="User Name &lt;user@hostname&gt;"/>
2181 <changeset author="person"/>
2181 <changeset author="person"/>
2182 <changeset author="person"/>
2182 <changeset author="person"/>
2183 <changeset author="person"/>
2183 <changeset author="person"/>
2184 <changeset author="person"/>
2184 <changeset author="person"/>
2185 <changeset author="other@place"/>
2185 <changeset author="other@place"/>
2186 <changeset author="A. N. Other &lt;other@place&gt;"/>
2186 <changeset author="A. N. Other &lt;other@place&gt;"/>
2187 <changeset author="User Name &lt;user@hostname&gt;"/>
2187 <changeset author="User Name &lt;user@hostname&gt;"/>
2188
2188
2189 $ hg log --template '{rev}: {children}\n'
2189 $ hg log --template '{rev}: {children}\n'
2190 8:
2190 8:
2191 7: 8:95c24699272e
2191 7: 8:95c24699272e
2192 6:
2192 6:
2193 5: 6:d41e714fe50d
2193 5: 6:d41e714fe50d
2194 4: 6:d41e714fe50d
2194 4: 6:d41e714fe50d
2195 3: 4:bbe44766e73d 5:13207e5a10d9
2195 3: 4:bbe44766e73d 5:13207e5a10d9
2196 2: 3:10e46f2dcbf4
2196 2: 3:10e46f2dcbf4
2197 1: 2:97054abb4ab8
2197 1: 2:97054abb4ab8
2198 0: 1:b608e9d1a3f0
2198 0: 1:b608e9d1a3f0
2199
2199
2200 Formatnode filter works:
2200 Formatnode filter works:
2201
2201
2202 $ hg -q log -r 0 --template '{node|formatnode}\n'
2202 $ hg -q log -r 0 --template '{node|formatnode}\n'
2203 1e4e1b8f71e0
2203 1e4e1b8f71e0
2204
2204
2205 $ hg log -r 0 --template '{node|formatnode}\n'
2205 $ hg log -r 0 --template '{node|formatnode}\n'
2206 1e4e1b8f71e0
2206 1e4e1b8f71e0
2207
2207
2208 $ hg -v log -r 0 --template '{node|formatnode}\n'
2208 $ hg -v log -r 0 --template '{node|formatnode}\n'
2209 1e4e1b8f71e0
2209 1e4e1b8f71e0
2210
2210
2211 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2211 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2212 1e4e1b8f71e05681d422154f5421e385fec3454f
2212 1e4e1b8f71e05681d422154f5421e385fec3454f
2213
2213
2214 Age filter:
2214 Age filter:
2215
2215
2216 $ hg init unstable-hash
2216 $ hg init unstable-hash
2217 $ cd unstable-hash
2217 $ cd unstable-hash
2218 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2218 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2219
2219
2220 >>> from __future__ import absolute_import
2220 >>> from __future__ import absolute_import
2221 >>> import datetime
2221 >>> import datetime
2222 >>> fp = open('a', 'wb')
2222 >>> fp = open('a', 'wb')
2223 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2223 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2224 >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
2224 >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
2225 >>> fp.close()
2225 >>> fp.close()
2226 $ hg add a
2226 $ hg add a
2227 $ hg commit -m future -d "`cat a`"
2227 $ hg commit -m future -d "`cat a`"
2228
2228
2229 $ hg log -l1 --template '{date|age}\n'
2229 $ hg log -l1 --template '{date|age}\n'
2230 7 years from now
2230 7 years from now
2231
2231
2232 $ cd ..
2232 $ cd ..
2233 $ rm -rf unstable-hash
2233 $ rm -rf unstable-hash
2234
2234
2235 Filename filters:
2235 Filename filters:
2236
2236
2237 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
2237 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
2238 bar||foo|
2238 bar||foo|
2239 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
2239 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
2240 foo|foo||
2240 foo|foo||
2241 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
2241 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
2242 foo|foo|foo|
2242 foo|foo|foo|
2243
2243
2244 Add a dummy commit to make up for the instability of the above:
2244 Add a dummy commit to make up for the instability of the above:
2245
2245
2246 $ echo a > a
2246 $ echo a > a
2247 $ hg add a
2247 $ hg add a
2248 $ hg ci -m future
2248 $ hg ci -m future
2249
2249
2250 Count filter:
2250 Count filter:
2251
2251
2252 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2252 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2253 40 12
2253 40 12
2254
2254
2255 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2255 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2256 0 1 4
2256 0 1 4
2257
2257
2258 $ hg log -G --template '{rev}: children: {children|count}, \
2258 $ hg log -G --template '{rev}: children: {children|count}, \
2259 > tags: {tags|count}, file_adds: {file_adds|count}, \
2259 > tags: {tags|count}, file_adds: {file_adds|count}, \
2260 > ancestors: {revset("ancestors(%s)", rev)|count}'
2260 > ancestors: {revset("ancestors(%s)", rev)|count}'
2261 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2261 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2262 |
2262 |
2263 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2263 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2264 |
2264 |
2265 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2265 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2266
2266
2267 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2267 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2268 |\
2268 |\
2269 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2269 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2270 | |
2270 | |
2271 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2271 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2272 |/
2272 |/
2273 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2273 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2274 |
2274 |
2275 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2275 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2276 |
2276 |
2277 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2277 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2278 |
2278 |
2279 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2279 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2280
2280
2281
2281
2282 $ hg log -l1 -T '{termwidth|count}\n'
2282 $ hg log -l1 -T '{termwidth|count}\n'
2283 hg: parse error: not countable
2283 hg: parse error: not countable
2284 (template filter 'count' is not compatible with keyword 'termwidth')
2284 (template filter 'count' is not compatible with keyword 'termwidth')
2285 [255]
2285 [255]
2286
2286
2287 Upper/lower filters:
2287 Upper/lower filters:
2288
2288
2289 $ hg log -r0 --template '{branch|upper}\n'
2289 $ hg log -r0 --template '{branch|upper}\n'
2290 DEFAULT
2290 DEFAULT
2291 $ hg log -r0 --template '{author|lower}\n'
2291 $ hg log -r0 --template '{author|lower}\n'
2292 user name <user@hostname>
2292 user name <user@hostname>
2293 $ hg log -r0 --template '{date|upper}\n'
2293 $ hg log -r0 --template '{date|upper}\n'
2294 1000000.00
2294 1000000.00
2295
2295
2296 Add a commit that does all possible modifications at once
2296 Add a commit that does all possible modifications at once
2297
2297
2298 $ echo modify >> third
2298 $ echo modify >> third
2299 $ touch b
2299 $ touch b
2300 $ hg add b
2300 $ hg add b
2301 $ hg mv fourth fifth
2301 $ hg mv fourth fifth
2302 $ hg rm a
2302 $ hg rm a
2303 $ hg ci -m "Modify, add, remove, rename"
2303 $ hg ci -m "Modify, add, remove, rename"
2304
2304
2305 Check the status template
2305 Check the status template
2306
2306
2307 $ cat <<EOF >> $HGRCPATH
2307 $ cat <<EOF >> $HGRCPATH
2308 > [extensions]
2308 > [extensions]
2309 > color=
2309 > color=
2310 > EOF
2310 > EOF
2311
2311
2312 $ hg log -T status -r 10
2312 $ hg log -T status -r 10
2313 changeset: 10:0f9759ec227a
2313 changeset: 10:0f9759ec227a
2314 tag: tip
2314 tag: tip
2315 user: test
2315 user: test
2316 date: Thu Jan 01 00:00:00 1970 +0000
2316 date: Thu Jan 01 00:00:00 1970 +0000
2317 summary: Modify, add, remove, rename
2317 summary: Modify, add, remove, rename
2318 files:
2318 files:
2319 M third
2319 M third
2320 A b
2320 A b
2321 A fifth
2321 A fifth
2322 R a
2322 R a
2323 R fourth
2323 R fourth
2324
2324
2325 $ hg log -T status -C -r 10
2325 $ hg log -T status -C -r 10
2326 changeset: 10:0f9759ec227a
2326 changeset: 10:0f9759ec227a
2327 tag: tip
2327 tag: tip
2328 user: test
2328 user: test
2329 date: Thu Jan 01 00:00:00 1970 +0000
2329 date: Thu Jan 01 00:00:00 1970 +0000
2330 summary: Modify, add, remove, rename
2330 summary: Modify, add, remove, rename
2331 files:
2331 files:
2332 M third
2332 M third
2333 A b
2333 A b
2334 A fifth
2334 A fifth
2335 fourth
2335 fourth
2336 R a
2336 R a
2337 R fourth
2337 R fourth
2338
2338
2339 $ hg log -T status -C -r 10 -v
2339 $ hg log -T status -C -r 10 -v
2340 changeset: 10:0f9759ec227a
2340 changeset: 10:0f9759ec227a
2341 tag: tip
2341 tag: tip
2342 user: test
2342 user: test
2343 date: Thu Jan 01 00:00:00 1970 +0000
2343 date: Thu Jan 01 00:00:00 1970 +0000
2344 description:
2344 description:
2345 Modify, add, remove, rename
2345 Modify, add, remove, rename
2346
2346
2347 files:
2347 files:
2348 M third
2348 M third
2349 A b
2349 A b
2350 A fifth
2350 A fifth
2351 fourth
2351 fourth
2352 R a
2352 R a
2353 R fourth
2353 R fourth
2354
2354
2355 $ hg log -T status -C -r 10 --debug
2355 $ hg log -T status -C -r 10 --debug
2356 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2356 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2357 tag: tip
2357 tag: tip
2358 phase: secret
2358 phase: secret
2359 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2359 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2360 parent: -1:0000000000000000000000000000000000000000
2360 parent: -1:0000000000000000000000000000000000000000
2361 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2361 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2362 user: test
2362 user: test
2363 date: Thu Jan 01 00:00:00 1970 +0000
2363 date: Thu Jan 01 00:00:00 1970 +0000
2364 extra: branch=default
2364 extra: branch=default
2365 description:
2365 description:
2366 Modify, add, remove, rename
2366 Modify, add, remove, rename
2367
2367
2368 files:
2368 files:
2369 M third
2369 M third
2370 A b
2370 A b
2371 A fifth
2371 A fifth
2372 fourth
2372 fourth
2373 R a
2373 R a
2374 R fourth
2374 R fourth
2375
2375
2376 $ hg log -T status -C -r 10 --quiet
2376 $ hg log -T status -C -r 10 --quiet
2377 10:0f9759ec227a
2377 10:0f9759ec227a
2378 $ hg --color=debug log -T status -r 10
2378 $ hg --color=debug log -T status -r 10
2379 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2379 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2380 [log.tag|tag: tip]
2380 [log.tag|tag: tip]
2381 [log.user|user: test]
2381 [log.user|user: test]
2382 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2382 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2383 [log.summary|summary: Modify, add, remove, rename]
2383 [log.summary|summary: Modify, add, remove, rename]
2384 [ui.note log.files|files:]
2384 [ui.note log.files|files:]
2385 [status.modified|M third]
2385 [status.modified|M third]
2386 [status.added|A b]
2386 [status.added|A b]
2387 [status.added|A fifth]
2387 [status.added|A fifth]
2388 [status.removed|R a]
2388 [status.removed|R a]
2389 [status.removed|R fourth]
2389 [status.removed|R fourth]
2390
2390
2391 $ hg --color=debug log -T status -C -r 10
2391 $ hg --color=debug log -T status -C -r 10
2392 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2392 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2393 [log.tag|tag: tip]
2393 [log.tag|tag: tip]
2394 [log.user|user: test]
2394 [log.user|user: test]
2395 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2395 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2396 [log.summary|summary: Modify, add, remove, rename]
2396 [log.summary|summary: Modify, add, remove, rename]
2397 [ui.note log.files|files:]
2397 [ui.note log.files|files:]
2398 [status.modified|M third]
2398 [status.modified|M third]
2399 [status.added|A b]
2399 [status.added|A b]
2400 [status.added|A fifth]
2400 [status.added|A fifth]
2401 [status.copied| fourth]
2401 [status.copied| fourth]
2402 [status.removed|R a]
2402 [status.removed|R a]
2403 [status.removed|R fourth]
2403 [status.removed|R fourth]
2404
2404
2405 $ hg --color=debug log -T status -C -r 10 -v
2405 $ hg --color=debug log -T status -C -r 10 -v
2406 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2406 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2407 [log.tag|tag: tip]
2407 [log.tag|tag: tip]
2408 [log.user|user: test]
2408 [log.user|user: test]
2409 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2409 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2410 [ui.note log.description|description:]
2410 [ui.note log.description|description:]
2411 [ui.note log.description|Modify, add, remove, rename]
2411 [ui.note log.description|Modify, add, remove, rename]
2412
2412
2413 [ui.note log.files|files:]
2413 [ui.note log.files|files:]
2414 [status.modified|M third]
2414 [status.modified|M third]
2415 [status.added|A b]
2415 [status.added|A b]
2416 [status.added|A fifth]
2416 [status.added|A fifth]
2417 [status.copied| fourth]
2417 [status.copied| fourth]
2418 [status.removed|R a]
2418 [status.removed|R a]
2419 [status.removed|R fourth]
2419 [status.removed|R fourth]
2420
2420
2421 $ hg --color=debug log -T status -C -r 10 --debug
2421 $ hg --color=debug log -T status -C -r 10 --debug
2422 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2422 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2423 [log.tag|tag: tip]
2423 [log.tag|tag: tip]
2424 [log.phase|phase: secret]
2424 [log.phase|phase: secret]
2425 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2425 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2426 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2426 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2427 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2427 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2428 [log.user|user: test]
2428 [log.user|user: test]
2429 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2429 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2430 [ui.debug log.extra|extra: branch=default]
2430 [ui.debug log.extra|extra: branch=default]
2431 [ui.note log.description|description:]
2431 [ui.note log.description|description:]
2432 [ui.note log.description|Modify, add, remove, rename]
2432 [ui.note log.description|Modify, add, remove, rename]
2433
2433
2434 [ui.note log.files|files:]
2434 [ui.note log.files|files:]
2435 [status.modified|M third]
2435 [status.modified|M third]
2436 [status.added|A b]
2436 [status.added|A b]
2437 [status.added|A fifth]
2437 [status.added|A fifth]
2438 [status.copied| fourth]
2438 [status.copied| fourth]
2439 [status.removed|R a]
2439 [status.removed|R a]
2440 [status.removed|R fourth]
2440 [status.removed|R fourth]
2441
2441
2442 $ hg --color=debug log -T status -C -r 10 --quiet
2442 $ hg --color=debug log -T status -C -r 10 --quiet
2443 [log.node|10:0f9759ec227a]
2443 [log.node|10:0f9759ec227a]
2444
2444
2445 Check the bisect template
2445 Check the bisect template
2446
2446
2447 $ hg bisect -g 1
2447 $ hg bisect -g 1
2448 $ hg bisect -b 3 --noupdate
2448 $ hg bisect -b 3 --noupdate
2449 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2449 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2450 $ hg log -T bisect -r 0:4
2450 $ hg log -T bisect -r 0:4
2451 changeset: 0:1e4e1b8f71e0
2451 changeset: 0:1e4e1b8f71e0
2452 bisect: good (implicit)
2452 bisect: good (implicit)
2453 user: User Name <user@hostname>
2453 user: User Name <user@hostname>
2454 date: Mon Jan 12 13:46:40 1970 +0000
2454 date: Mon Jan 12 13:46:40 1970 +0000
2455 summary: line 1
2455 summary: line 1
2456
2456
2457 changeset: 1:b608e9d1a3f0
2457 changeset: 1:b608e9d1a3f0
2458 bisect: good
2458 bisect: good
2459 user: A. N. Other <other@place>
2459 user: A. N. Other <other@place>
2460 date: Tue Jan 13 17:33:20 1970 +0000
2460 date: Tue Jan 13 17:33:20 1970 +0000
2461 summary: other 1
2461 summary: other 1
2462
2462
2463 changeset: 2:97054abb4ab8
2463 changeset: 2:97054abb4ab8
2464 bisect: untested
2464 bisect: untested
2465 user: other@place
2465 user: other@place
2466 date: Wed Jan 14 21:20:00 1970 +0000
2466 date: Wed Jan 14 21:20:00 1970 +0000
2467 summary: no person
2467 summary: no person
2468
2468
2469 changeset: 3:10e46f2dcbf4
2469 changeset: 3:10e46f2dcbf4
2470 bisect: bad
2470 bisect: bad
2471 user: person
2471 user: person
2472 date: Fri Jan 16 01:06:40 1970 +0000
2472 date: Fri Jan 16 01:06:40 1970 +0000
2473 summary: no user, no domain
2473 summary: no user, no domain
2474
2474
2475 changeset: 4:bbe44766e73d
2475 changeset: 4:bbe44766e73d
2476 bisect: bad (implicit)
2476 bisect: bad (implicit)
2477 branch: foo
2477 branch: foo
2478 user: person
2478 user: person
2479 date: Sat Jan 17 04:53:20 1970 +0000
2479 date: Sat Jan 17 04:53:20 1970 +0000
2480 summary: new branch
2480 summary: new branch
2481
2481
2482 $ hg log --debug -T bisect -r 0:4
2482 $ hg log --debug -T bisect -r 0:4
2483 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2483 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2484 bisect: good (implicit)
2484 bisect: good (implicit)
2485 phase: public
2485 phase: public
2486 parent: -1:0000000000000000000000000000000000000000
2486 parent: -1:0000000000000000000000000000000000000000
2487 parent: -1:0000000000000000000000000000000000000000
2487 parent: -1:0000000000000000000000000000000000000000
2488 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2488 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2489 user: User Name <user@hostname>
2489 user: User Name <user@hostname>
2490 date: Mon Jan 12 13:46:40 1970 +0000
2490 date: Mon Jan 12 13:46:40 1970 +0000
2491 files+: a
2491 files+: a
2492 extra: branch=default
2492 extra: branch=default
2493 description:
2493 description:
2494 line 1
2494 line 1
2495 line 2
2495 line 2
2496
2496
2497
2497
2498 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2498 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2499 bisect: good
2499 bisect: good
2500 phase: public
2500 phase: public
2501 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2501 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2502 parent: -1:0000000000000000000000000000000000000000
2502 parent: -1:0000000000000000000000000000000000000000
2503 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2503 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2504 user: A. N. Other <other@place>
2504 user: A. N. Other <other@place>
2505 date: Tue Jan 13 17:33:20 1970 +0000
2505 date: Tue Jan 13 17:33:20 1970 +0000
2506 files+: b
2506 files+: b
2507 extra: branch=default
2507 extra: branch=default
2508 description:
2508 description:
2509 other 1
2509 other 1
2510 other 2
2510 other 2
2511
2511
2512 other 3
2512 other 3
2513
2513
2514
2514
2515 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2515 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2516 bisect: untested
2516 bisect: untested
2517 phase: public
2517 phase: public
2518 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2518 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2519 parent: -1:0000000000000000000000000000000000000000
2519 parent: -1:0000000000000000000000000000000000000000
2520 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2520 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2521 user: other@place
2521 user: other@place
2522 date: Wed Jan 14 21:20:00 1970 +0000
2522 date: Wed Jan 14 21:20:00 1970 +0000
2523 files+: c
2523 files+: c
2524 extra: branch=default
2524 extra: branch=default
2525 description:
2525 description:
2526 no person
2526 no person
2527
2527
2528
2528
2529 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2529 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2530 bisect: bad
2530 bisect: bad
2531 phase: public
2531 phase: public
2532 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2532 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2533 parent: -1:0000000000000000000000000000000000000000
2533 parent: -1:0000000000000000000000000000000000000000
2534 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2534 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2535 user: person
2535 user: person
2536 date: Fri Jan 16 01:06:40 1970 +0000
2536 date: Fri Jan 16 01:06:40 1970 +0000
2537 files: c
2537 files: c
2538 extra: branch=default
2538 extra: branch=default
2539 description:
2539 description:
2540 no user, no domain
2540 no user, no domain
2541
2541
2542
2542
2543 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2543 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2544 bisect: bad (implicit)
2544 bisect: bad (implicit)
2545 branch: foo
2545 branch: foo
2546 phase: draft
2546 phase: draft
2547 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2547 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2548 parent: -1:0000000000000000000000000000000000000000
2548 parent: -1:0000000000000000000000000000000000000000
2549 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2549 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2550 user: person
2550 user: person
2551 date: Sat Jan 17 04:53:20 1970 +0000
2551 date: Sat Jan 17 04:53:20 1970 +0000
2552 extra: branch=foo
2552 extra: branch=foo
2553 description:
2553 description:
2554 new branch
2554 new branch
2555
2555
2556
2556
2557 $ hg log -v -T bisect -r 0:4
2557 $ hg log -v -T bisect -r 0:4
2558 changeset: 0:1e4e1b8f71e0
2558 changeset: 0:1e4e1b8f71e0
2559 bisect: good (implicit)
2559 bisect: good (implicit)
2560 user: User Name <user@hostname>
2560 user: User Name <user@hostname>
2561 date: Mon Jan 12 13:46:40 1970 +0000
2561 date: Mon Jan 12 13:46:40 1970 +0000
2562 files: a
2562 files: a
2563 description:
2563 description:
2564 line 1
2564 line 1
2565 line 2
2565 line 2
2566
2566
2567
2567
2568 changeset: 1:b608e9d1a3f0
2568 changeset: 1:b608e9d1a3f0
2569 bisect: good
2569 bisect: good
2570 user: A. N. Other <other@place>
2570 user: A. N. Other <other@place>
2571 date: Tue Jan 13 17:33:20 1970 +0000
2571 date: Tue Jan 13 17:33:20 1970 +0000
2572 files: b
2572 files: b
2573 description:
2573 description:
2574 other 1
2574 other 1
2575 other 2
2575 other 2
2576
2576
2577 other 3
2577 other 3
2578
2578
2579
2579
2580 changeset: 2:97054abb4ab8
2580 changeset: 2:97054abb4ab8
2581 bisect: untested
2581 bisect: untested
2582 user: other@place
2582 user: other@place
2583 date: Wed Jan 14 21:20:00 1970 +0000
2583 date: Wed Jan 14 21:20:00 1970 +0000
2584 files: c
2584 files: c
2585 description:
2585 description:
2586 no person
2586 no person
2587
2587
2588
2588
2589 changeset: 3:10e46f2dcbf4
2589 changeset: 3:10e46f2dcbf4
2590 bisect: bad
2590 bisect: bad
2591 user: person
2591 user: person
2592 date: Fri Jan 16 01:06:40 1970 +0000
2592 date: Fri Jan 16 01:06:40 1970 +0000
2593 files: c
2593 files: c
2594 description:
2594 description:
2595 no user, no domain
2595 no user, no domain
2596
2596
2597
2597
2598 changeset: 4:bbe44766e73d
2598 changeset: 4:bbe44766e73d
2599 bisect: bad (implicit)
2599 bisect: bad (implicit)
2600 branch: foo
2600 branch: foo
2601 user: person
2601 user: person
2602 date: Sat Jan 17 04:53:20 1970 +0000
2602 date: Sat Jan 17 04:53:20 1970 +0000
2603 description:
2603 description:
2604 new branch
2604 new branch
2605
2605
2606
2606
2607 $ hg --color=debug log -T bisect -r 0:4
2607 $ hg --color=debug log -T bisect -r 0:4
2608 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2608 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2609 [log.bisect bisect.good|bisect: good (implicit)]
2609 [log.bisect bisect.good|bisect: good (implicit)]
2610 [log.user|user: User Name <user@hostname>]
2610 [log.user|user: User Name <user@hostname>]
2611 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2611 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2612 [log.summary|summary: line 1]
2612 [log.summary|summary: line 1]
2613
2613
2614 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2614 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2615 [log.bisect bisect.good|bisect: good]
2615 [log.bisect bisect.good|bisect: good]
2616 [log.user|user: A. N. Other <other@place>]
2616 [log.user|user: A. N. Other <other@place>]
2617 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2617 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2618 [log.summary|summary: other 1]
2618 [log.summary|summary: other 1]
2619
2619
2620 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2620 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2621 [log.bisect bisect.untested|bisect: untested]
2621 [log.bisect bisect.untested|bisect: untested]
2622 [log.user|user: other@place]
2622 [log.user|user: other@place]
2623 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2623 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2624 [log.summary|summary: no person]
2624 [log.summary|summary: no person]
2625
2625
2626 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2626 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2627 [log.bisect bisect.bad|bisect: bad]
2627 [log.bisect bisect.bad|bisect: bad]
2628 [log.user|user: person]
2628 [log.user|user: person]
2629 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2629 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2630 [log.summary|summary: no user, no domain]
2630 [log.summary|summary: no user, no domain]
2631
2631
2632 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2632 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2633 [log.bisect bisect.bad|bisect: bad (implicit)]
2633 [log.bisect bisect.bad|bisect: bad (implicit)]
2634 [log.branch|branch: foo]
2634 [log.branch|branch: foo]
2635 [log.user|user: person]
2635 [log.user|user: person]
2636 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2636 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2637 [log.summary|summary: new branch]
2637 [log.summary|summary: new branch]
2638
2638
2639 $ hg --color=debug log --debug -T bisect -r 0:4
2639 $ hg --color=debug log --debug -T bisect -r 0:4
2640 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2640 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2641 [log.bisect bisect.good|bisect: good (implicit)]
2641 [log.bisect bisect.good|bisect: good (implicit)]
2642 [log.phase|phase: public]
2642 [log.phase|phase: public]
2643 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2643 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2644 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2644 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2645 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2645 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2646 [log.user|user: User Name <user@hostname>]
2646 [log.user|user: User Name <user@hostname>]
2647 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2647 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2648 [ui.debug log.files|files+: a]
2648 [ui.debug log.files|files+: a]
2649 [ui.debug log.extra|extra: branch=default]
2649 [ui.debug log.extra|extra: branch=default]
2650 [ui.note log.description|description:]
2650 [ui.note log.description|description:]
2651 [ui.note log.description|line 1
2651 [ui.note log.description|line 1
2652 line 2]
2652 line 2]
2653
2653
2654
2654
2655 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2655 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2656 [log.bisect bisect.good|bisect: good]
2656 [log.bisect bisect.good|bisect: good]
2657 [log.phase|phase: public]
2657 [log.phase|phase: public]
2658 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2658 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2659 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2659 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2660 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2660 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2661 [log.user|user: A. N. Other <other@place>]
2661 [log.user|user: A. N. Other <other@place>]
2662 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2662 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2663 [ui.debug log.files|files+: b]
2663 [ui.debug log.files|files+: b]
2664 [ui.debug log.extra|extra: branch=default]
2664 [ui.debug log.extra|extra: branch=default]
2665 [ui.note log.description|description:]
2665 [ui.note log.description|description:]
2666 [ui.note log.description|other 1
2666 [ui.note log.description|other 1
2667 other 2
2667 other 2
2668
2668
2669 other 3]
2669 other 3]
2670
2670
2671
2671
2672 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2672 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2673 [log.bisect bisect.untested|bisect: untested]
2673 [log.bisect bisect.untested|bisect: untested]
2674 [log.phase|phase: public]
2674 [log.phase|phase: public]
2675 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2675 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2676 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2676 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2677 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2677 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2678 [log.user|user: other@place]
2678 [log.user|user: other@place]
2679 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2679 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2680 [ui.debug log.files|files+: c]
2680 [ui.debug log.files|files+: c]
2681 [ui.debug log.extra|extra: branch=default]
2681 [ui.debug log.extra|extra: branch=default]
2682 [ui.note log.description|description:]
2682 [ui.note log.description|description:]
2683 [ui.note log.description|no person]
2683 [ui.note log.description|no person]
2684
2684
2685
2685
2686 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2686 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2687 [log.bisect bisect.bad|bisect: bad]
2687 [log.bisect bisect.bad|bisect: bad]
2688 [log.phase|phase: public]
2688 [log.phase|phase: public]
2689 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2689 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2690 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2690 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2691 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2691 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2692 [log.user|user: person]
2692 [log.user|user: person]
2693 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2693 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2694 [ui.debug log.files|files: c]
2694 [ui.debug log.files|files: c]
2695 [ui.debug log.extra|extra: branch=default]
2695 [ui.debug log.extra|extra: branch=default]
2696 [ui.note log.description|description:]
2696 [ui.note log.description|description:]
2697 [ui.note log.description|no user, no domain]
2697 [ui.note log.description|no user, no domain]
2698
2698
2699
2699
2700 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2700 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2701 [log.bisect bisect.bad|bisect: bad (implicit)]
2701 [log.bisect bisect.bad|bisect: bad (implicit)]
2702 [log.branch|branch: foo]
2702 [log.branch|branch: foo]
2703 [log.phase|phase: draft]
2703 [log.phase|phase: draft]
2704 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2704 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2705 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2705 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2706 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2706 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2707 [log.user|user: person]
2707 [log.user|user: person]
2708 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2708 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2709 [ui.debug log.extra|extra: branch=foo]
2709 [ui.debug log.extra|extra: branch=foo]
2710 [ui.note log.description|description:]
2710 [ui.note log.description|description:]
2711 [ui.note log.description|new branch]
2711 [ui.note log.description|new branch]
2712
2712
2713
2713
2714 $ hg --color=debug log -v -T bisect -r 0:4
2714 $ hg --color=debug log -v -T bisect -r 0:4
2715 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2715 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2716 [log.bisect bisect.good|bisect: good (implicit)]
2716 [log.bisect bisect.good|bisect: good (implicit)]
2717 [log.user|user: User Name <user@hostname>]
2717 [log.user|user: User Name <user@hostname>]
2718 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2718 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2719 [ui.note log.files|files: a]
2719 [ui.note log.files|files: a]
2720 [ui.note log.description|description:]
2720 [ui.note log.description|description:]
2721 [ui.note log.description|line 1
2721 [ui.note log.description|line 1
2722 line 2]
2722 line 2]
2723
2723
2724
2724
2725 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2725 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2726 [log.bisect bisect.good|bisect: good]
2726 [log.bisect bisect.good|bisect: good]
2727 [log.user|user: A. N. Other <other@place>]
2727 [log.user|user: A. N. Other <other@place>]
2728 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2728 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2729 [ui.note log.files|files: b]
2729 [ui.note log.files|files: b]
2730 [ui.note log.description|description:]
2730 [ui.note log.description|description:]
2731 [ui.note log.description|other 1
2731 [ui.note log.description|other 1
2732 other 2
2732 other 2
2733
2733
2734 other 3]
2734 other 3]
2735
2735
2736
2736
2737 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2737 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2738 [log.bisect bisect.untested|bisect: untested]
2738 [log.bisect bisect.untested|bisect: untested]
2739 [log.user|user: other@place]
2739 [log.user|user: other@place]
2740 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2740 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2741 [ui.note log.files|files: c]
2741 [ui.note log.files|files: c]
2742 [ui.note log.description|description:]
2742 [ui.note log.description|description:]
2743 [ui.note log.description|no person]
2743 [ui.note log.description|no person]
2744
2744
2745
2745
2746 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2746 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2747 [log.bisect bisect.bad|bisect: bad]
2747 [log.bisect bisect.bad|bisect: bad]
2748 [log.user|user: person]
2748 [log.user|user: person]
2749 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2749 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2750 [ui.note log.files|files: c]
2750 [ui.note log.files|files: c]
2751 [ui.note log.description|description:]
2751 [ui.note log.description|description:]
2752 [ui.note log.description|no user, no domain]
2752 [ui.note log.description|no user, no domain]
2753
2753
2754
2754
2755 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2755 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2756 [log.bisect bisect.bad|bisect: bad (implicit)]
2756 [log.bisect bisect.bad|bisect: bad (implicit)]
2757 [log.branch|branch: foo]
2757 [log.branch|branch: foo]
2758 [log.user|user: person]
2758 [log.user|user: person]
2759 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2759 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2760 [ui.note log.description|description:]
2760 [ui.note log.description|description:]
2761 [ui.note log.description|new branch]
2761 [ui.note log.description|new branch]
2762
2762
2763
2763
2764 $ hg bisect --reset
2764 $ hg bisect --reset
2765
2765
2766 Error on syntax:
2766 Error on syntax:
2767
2767
2768 $ echo 'x = "f' >> t
2768 $ echo 'x = "f' >> t
2769 $ hg log
2769 $ hg log
2770 hg: parse error at t:3: unmatched quotes
2770 hg: parse error at t:3: unmatched quotes
2771 [255]
2771 [255]
2772
2772
2773 $ hg log -T '{date'
2773 $ hg log -T '{date'
2774 hg: parse error at 1: unterminated template expansion
2774 hg: parse error at 1: unterminated template expansion
2775 ({date
2775 ({date
2776 ^ here)
2776 ^ here)
2777 [255]
2777 [255]
2778 $ hg log -T '{date(}'
2778 $ hg log -T '{date(}'
2779 hg: parse error at 6: not a prefix: end
2779 hg: parse error at 6: not a prefix: end
2780 ({date(}
2780 ({date(}
2781 ^ here)
2781 ^ here)
2782 [255]
2782 [255]
2783 $ hg log -T '{date)}'
2783 $ hg log -T '{date)}'
2784 hg: parse error at 5: invalid token
2784 hg: parse error at 5: invalid token
2785 ({date)}
2785 ({date)}
2786 ^ here)
2786 ^ here)
2787 [255]
2787 [255]
2788 $ hg log -T '{date date}'
2788 $ hg log -T '{date date}'
2789 hg: parse error at 6: invalid token
2789 hg: parse error at 6: invalid token
2790 ({date date}
2790 ({date date}
2791 ^ here)
2791 ^ here)
2792 [255]
2792 [255]
2793
2793
2794 $ hg log -T '{}'
2794 $ hg log -T '{}'
2795 hg: parse error at 1: not a prefix: end
2795 hg: parse error at 1: not a prefix: end
2796 ({}
2796 ({}
2797 ^ here)
2797 ^ here)
2798 [255]
2798 [255]
2799 $ hg debugtemplate -v '{()}'
2799 $ hg debugtemplate -v '{()}'
2800 (template
2800 (template
2801 (group
2801 (group
2802 None))
2802 None))
2803 hg: parse error: missing argument
2803 hg: parse error: missing argument
2804 [255]
2804 [255]
2805
2805
2806 Behind the scenes, this would throw TypeError without intype=bytes
2806 Behind the scenes, this would throw TypeError without intype=bytes
2807
2807
2808 $ hg log -l 3 --template '{date|obfuscate}\n'
2808 $ hg log -l 3 --template '{date|obfuscate}\n'
2809 &#48;&#46;&#48;&#48;
2809 &#48;&#46;&#48;&#48;
2810 &#48;&#46;&#48;&#48;
2810 &#48;&#46;&#48;&#48;
2811 &#49;&#53;&#55;&#55;&#56;&#55;&#50;&#56;&#54;&#48;&#46;&#48;&#48;
2811 &#49;&#53;&#55;&#55;&#56;&#55;&#50;&#56;&#54;&#48;&#46;&#48;&#48;
2812
2812
2813 Behind the scenes, this will throw a ValueError
2813 Behind the scenes, this will throw a ValueError
2814
2814
2815 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2815 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2816 hg: parse error: invalid date: 'Modify, add, remove, rename'
2816 hg: parse error: invalid date: 'Modify, add, remove, rename'
2817 (template filter 'shortdate' is not compatible with keyword 'desc')
2817 (template filter 'shortdate' is not compatible with keyword 'desc')
2818 [255]
2818 [255]
2819
2819
2820 Behind the scenes, this would throw AttributeError without intype=bytes
2820 Behind the scenes, this would throw AttributeError without intype=bytes
2821
2821
2822 $ hg log -l 3 --template 'line: {date|escape}\n'
2822 $ hg log -l 3 --template 'line: {date|escape}\n'
2823 line: 0.00
2823 line: 0.00
2824 line: 0.00
2824 line: 0.00
2825 line: 1577872860.00
2825 line: 1577872860.00
2826
2826
2827 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2827 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2828 hg: parse error: localdate expects a date information
2828 hg: parse error: localdate expects a date information
2829 [255]
2829 [255]
2830
2830
2831 Behind the scenes, this will throw ValueError
2831 Behind the scenes, this will throw ValueError
2832
2832
2833 $ hg tip --template '{author|email|date}\n'
2833 $ hg tip --template '{author|email|date}\n'
2834 hg: parse error: date expects a date information
2834 hg: parse error: date expects a date information
2835 [255]
2835 [255]
2836
2836
2837 $ hg tip -T '{author|email|shortdate}\n'
2837 $ hg tip -T '{author|email|shortdate}\n'
2838 hg: parse error: invalid date: 'test'
2838 hg: parse error: invalid date: 'test'
2839 (template filter 'shortdate' is not compatible with keyword 'author')
2839 (template filter 'shortdate' is not compatible with keyword 'author')
2840 [255]
2840 [255]
2841
2841
2842 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2842 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2843 hg: parse error: invalid date: 'default'
2843 hg: parse error: invalid date: 'default'
2844 (incompatible use of template filter 'shortdate')
2844 (incompatible use of template filter 'shortdate')
2845 [255]
2845 [255]
2846
2846
2847 Error in nested template:
2847 Error in nested template:
2848
2848
2849 $ hg log -T '{"date'
2849 $ hg log -T '{"date'
2850 hg: parse error at 2: unterminated string
2850 hg: parse error at 2: unterminated string
2851 ({"date
2851 ({"date
2852 ^ here)
2852 ^ here)
2853 [255]
2853 [255]
2854
2854
2855 $ hg log -T '{"foo{date|?}"}'
2855 $ hg log -T '{"foo{date|?}"}'
2856 hg: parse error at 11: syntax error
2856 hg: parse error at 11: syntax error
2857 ({"foo{date|?}"}
2857 ({"foo{date|?}"}
2858 ^ here)
2858 ^ here)
2859 [255]
2859 [255]
2860
2860
2861 Thrown an error if a template function doesn't exist
2861 Thrown an error if a template function doesn't exist
2862
2862
2863 $ hg tip --template '{foo()}\n'
2863 $ hg tip --template '{foo()}\n'
2864 hg: parse error: unknown function 'foo'
2864 hg: parse error: unknown function 'foo'
2865 [255]
2865 [255]
2866
2866
2867 Pass generator object created by template function to filter
2867 Pass generator object created by template function to filter
2868
2868
2869 $ hg log -l 1 --template '{if(author, author)|user}\n'
2869 $ hg log -l 1 --template '{if(author, author)|user}\n'
2870 test
2870 test
2871
2871
2872 Test index keyword:
2872 Test index keyword:
2873
2873
2874 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2874 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2875 10 0:a 1:b 2:fifth 3:fourth 4:third
2875 10 0:a 1:b 2:fifth 3:fourth 4:third
2876 11 0:a
2876 11 0:a
2877
2877
2878 $ hg branches -T '{index} {branch}\n'
2878 $ hg branches -T '{index} {branch}\n'
2879 0 default
2879 0 default
2880 1 foo
2880 1 foo
2881
2881
2882 Test diff function:
2882 Test diff function:
2883
2883
2884 $ hg diff -c 8
2884 $ hg diff -c 8
2885 diff -r 29114dbae42b -r 95c24699272e fourth
2885 diff -r 29114dbae42b -r 95c24699272e fourth
2886 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2886 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2887 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2887 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2888 @@ -0,0 +1,1 @@
2888 @@ -0,0 +1,1 @@
2889 +second
2889 +second
2890 diff -r 29114dbae42b -r 95c24699272e second
2890 diff -r 29114dbae42b -r 95c24699272e second
2891 --- a/second Mon Jan 12 13:46:40 1970 +0000
2891 --- a/second Mon Jan 12 13:46:40 1970 +0000
2892 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2892 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2893 @@ -1,1 +0,0 @@
2893 @@ -1,1 +0,0 @@
2894 -second
2894 -second
2895 diff -r 29114dbae42b -r 95c24699272e third
2895 diff -r 29114dbae42b -r 95c24699272e third
2896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2897 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2897 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2898 @@ -0,0 +1,1 @@
2898 @@ -0,0 +1,1 @@
2899 +third
2899 +third
2900
2900
2901 $ hg log -r 8 -T "{diff()}"
2901 $ hg log -r 8 -T "{diff()}"
2902 diff -r 29114dbae42b -r 95c24699272e fourth
2902 diff -r 29114dbae42b -r 95c24699272e fourth
2903 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2903 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2904 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2904 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2905 @@ -0,0 +1,1 @@
2905 @@ -0,0 +1,1 @@
2906 +second
2906 +second
2907 diff -r 29114dbae42b -r 95c24699272e second
2907 diff -r 29114dbae42b -r 95c24699272e second
2908 --- a/second Mon Jan 12 13:46:40 1970 +0000
2908 --- a/second Mon Jan 12 13:46:40 1970 +0000
2909 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2909 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2910 @@ -1,1 +0,0 @@
2910 @@ -1,1 +0,0 @@
2911 -second
2911 -second
2912 diff -r 29114dbae42b -r 95c24699272e third
2912 diff -r 29114dbae42b -r 95c24699272e third
2913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2914 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2914 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2915 @@ -0,0 +1,1 @@
2915 @@ -0,0 +1,1 @@
2916 +third
2916 +third
2917
2917
2918 $ hg log -r 8 -T "{diff('glob:f*')}"
2918 $ hg log -r 8 -T "{diff('glob:f*')}"
2919 diff -r 29114dbae42b -r 95c24699272e fourth
2919 diff -r 29114dbae42b -r 95c24699272e fourth
2920 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2920 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2921 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2921 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2922 @@ -0,0 +1,1 @@
2922 @@ -0,0 +1,1 @@
2923 +second
2923 +second
2924
2924
2925 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2925 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2926 diff -r 29114dbae42b -r 95c24699272e second
2926 diff -r 29114dbae42b -r 95c24699272e second
2927 --- a/second Mon Jan 12 13:46:40 1970 +0000
2927 --- a/second Mon Jan 12 13:46:40 1970 +0000
2928 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2928 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2929 @@ -1,1 +0,0 @@
2929 @@ -1,1 +0,0 @@
2930 -second
2930 -second
2931 diff -r 29114dbae42b -r 95c24699272e third
2931 diff -r 29114dbae42b -r 95c24699272e third
2932 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2932 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2933 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2933 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2934 @@ -0,0 +1,1 @@
2934 @@ -0,0 +1,1 @@
2935 +third
2935 +third
2936
2936
2937 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2937 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2938 diff -r 29114dbae42b -r 95c24699272e fourth
2938 diff -r 29114dbae42b -r 95c24699272e fourth
2939 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2939 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2940 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2940 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2941 @@ -0,0 +1,1 @@
2941 @@ -0,0 +1,1 @@
2942 +second
2942 +second
2943
2943
2944 ui verbosity:
2944 ui verbosity:
2945
2945
2946 $ hg log -l1 -T '{verbosity}\n'
2946 $ hg log -l1 -T '{verbosity}\n'
2947
2947
2948 $ hg log -l1 -T '{verbosity}\n' --debug
2948 $ hg log -l1 -T '{verbosity}\n' --debug
2949 debug
2949 debug
2950 $ hg log -l1 -T '{verbosity}\n' --quiet
2950 $ hg log -l1 -T '{verbosity}\n' --quiet
2951 quiet
2951 quiet
2952 $ hg log -l1 -T '{verbosity}\n' --verbose
2952 $ hg log -l1 -T '{verbosity}\n' --verbose
2953 verbose
2953 verbose
2954
2954
2955 $ cd ..
2955 $ cd ..
2956
2956
2957
2957
2958 latesttag:
2958 latesttag:
2959
2959
2960 $ hg init latesttag
2960 $ hg init latesttag
2961 $ cd latesttag
2961 $ cd latesttag
2962
2962
2963 $ echo a > file
2963 $ echo a > file
2964 $ hg ci -Am a -d '0 0'
2964 $ hg ci -Am a -d '0 0'
2965 adding file
2965 adding file
2966
2966
2967 $ echo b >> file
2967 $ echo b >> file
2968 $ hg ci -m b -d '1 0'
2968 $ hg ci -m b -d '1 0'
2969
2969
2970 $ echo c >> head1
2970 $ echo c >> head1
2971 $ hg ci -Am h1c -d '2 0'
2971 $ hg ci -Am h1c -d '2 0'
2972 adding head1
2972 adding head1
2973
2973
2974 $ hg update -q 1
2974 $ hg update -q 1
2975 $ echo d >> head2
2975 $ echo d >> head2
2976 $ hg ci -Am h2d -d '3 0'
2976 $ hg ci -Am h2d -d '3 0'
2977 adding head2
2977 adding head2
2978 created new head
2978 created new head
2979
2979
2980 $ echo e >> head2
2980 $ echo e >> head2
2981 $ hg ci -m h2e -d '4 0'
2981 $ hg ci -m h2e -d '4 0'
2982
2982
2983 $ hg merge -q
2983 $ hg merge -q
2984 $ hg ci -m merge -d '5 -3600'
2984 $ hg ci -m merge -d '5 -3600'
2985
2985
2986 No tag set:
2986 No tag set:
2987
2987
2988 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2988 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2989 @ 5: null+5
2989 @ 5: null+5
2990 |\
2990 |\
2991 | o 4: null+4
2991 | o 4: null+4
2992 | |
2992 | |
2993 | o 3: null+3
2993 | o 3: null+3
2994 | |
2994 | |
2995 o | 2: null+3
2995 o | 2: null+3
2996 |/
2996 |/
2997 o 1: null+2
2997 o 1: null+2
2998 |
2998 |
2999 o 0: null+1
2999 o 0: null+1
3000
3000
3001
3001
3002 One common tag: longest path wins for {latesttagdistance}:
3002 One common tag: longest path wins for {latesttagdistance}:
3003
3003
3004 $ hg tag -r 1 -m t1 -d '6 0' t1
3004 $ hg tag -r 1 -m t1 -d '6 0' t1
3005 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3005 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3006 @ 6: t1+4
3006 @ 6: t1+4
3007 |
3007 |
3008 o 5: t1+3
3008 o 5: t1+3
3009 |\
3009 |\
3010 | o 4: t1+2
3010 | o 4: t1+2
3011 | |
3011 | |
3012 | o 3: t1+1
3012 | o 3: t1+1
3013 | |
3013 | |
3014 o | 2: t1+1
3014 o | 2: t1+1
3015 |/
3015 |/
3016 o 1: t1+0
3016 o 1: t1+0
3017 |
3017 |
3018 o 0: null+1
3018 o 0: null+1
3019
3019
3020
3020
3021 One ancestor tag: closest wins:
3021 One ancestor tag: closest wins:
3022
3022
3023 $ hg tag -r 2 -m t2 -d '7 0' t2
3023 $ hg tag -r 2 -m t2 -d '7 0' t2
3024 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3024 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3025 @ 7: t2+3
3025 @ 7: t2+3
3026 |
3026 |
3027 o 6: t2+2
3027 o 6: t2+2
3028 |
3028 |
3029 o 5: t2+1
3029 o 5: t2+1
3030 |\
3030 |\
3031 | o 4: t1+2
3031 | o 4: t1+2
3032 | |
3032 | |
3033 | o 3: t1+1
3033 | o 3: t1+1
3034 | |
3034 | |
3035 o | 2: t2+0
3035 o | 2: t2+0
3036 |/
3036 |/
3037 o 1: t1+0
3037 o 1: t1+0
3038 |
3038 |
3039 o 0: null+1
3039 o 0: null+1
3040
3040
3041
3041
3042 Two branch tags: more recent wins if same number of changes:
3042 Two branch tags: more recent wins if same number of changes:
3043
3043
3044 $ hg tag -r 3 -m t3 -d '8 0' t3
3044 $ hg tag -r 3 -m t3 -d '8 0' t3
3045 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3045 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3046 @ 8: t3+5
3046 @ 8: t3+5
3047 |
3047 |
3048 o 7: t3+4
3048 o 7: t3+4
3049 |
3049 |
3050 o 6: t3+3
3050 o 6: t3+3
3051 |
3051 |
3052 o 5: t3+2
3052 o 5: t3+2
3053 |\
3053 |\
3054 | o 4: t3+1
3054 | o 4: t3+1
3055 | |
3055 | |
3056 | o 3: t3+0
3056 | o 3: t3+0
3057 | |
3057 | |
3058 o | 2: t2+0
3058 o | 2: t2+0
3059 |/
3059 |/
3060 o 1: t1+0
3060 o 1: t1+0
3061 |
3061 |
3062 o 0: null+1
3062 o 0: null+1
3063
3063
3064
3064
3065 Two branch tags: fewest changes wins:
3065 Two branch tags: fewest changes wins:
3066
3066
3067 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
3067 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
3068 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3068 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3069 @ 9: t4+5,6
3069 @ 9: t4+5,6
3070 |
3070 |
3071 o 8: t4+4,5
3071 o 8: t4+4,5
3072 |
3072 |
3073 o 7: t4+3,4
3073 o 7: t4+3,4
3074 |
3074 |
3075 o 6: t4+2,3
3075 o 6: t4+2,3
3076 |
3076 |
3077 o 5: t4+1,2
3077 o 5: t4+1,2
3078 |\
3078 |\
3079 | o 4: t4+0,0
3079 | o 4: t4+0,0
3080 | |
3080 | |
3081 | o 3: t3+0,0
3081 | o 3: t3+0,0
3082 | |
3082 | |
3083 o | 2: t2+0,0
3083 o | 2: t2+0,0
3084 |/
3084 |/
3085 o 1: t1+0,0
3085 o 1: t1+0,0
3086 |
3086 |
3087 o 0: null+1,1
3087 o 0: null+1,1
3088
3088
3089
3089
3090 Merged tag overrides:
3090 Merged tag overrides:
3091
3091
3092 $ hg tag -r 5 -m t5 -d '9 0' t5
3092 $ hg tag -r 5 -m t5 -d '9 0' t5
3093 $ hg tag -r 3 -m at3 -d '10 0' at3
3093 $ hg tag -r 3 -m at3 -d '10 0' at3
3094 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3094 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3095 @ 11: t5+6
3095 @ 11: t5+6
3096 |
3096 |
3097 o 10: t5+5
3097 o 10: t5+5
3098 |
3098 |
3099 o 9: t5+4
3099 o 9: t5+4
3100 |
3100 |
3101 o 8: t5+3
3101 o 8: t5+3
3102 |
3102 |
3103 o 7: t5+2
3103 o 7: t5+2
3104 |
3104 |
3105 o 6: t5+1
3105 o 6: t5+1
3106 |
3106 |
3107 o 5: t5+0
3107 o 5: t5+0
3108 |\
3108 |\
3109 | o 4: t4+0
3109 | o 4: t4+0
3110 | |
3110 | |
3111 | o 3: at3:t3+0
3111 | o 3: at3:t3+0
3112 | |
3112 | |
3113 o | 2: t2+0
3113 o | 2: t2+0
3114 |/
3114 |/
3115 o 1: t1+0
3115 o 1: t1+0
3116 |
3116 |
3117 o 0: null+1
3117 o 0: null+1
3118
3118
3119
3119
3120 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3120 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3121 @ 11: t5+6,6
3121 @ 11: t5+6,6
3122 |
3122 |
3123 o 10: t5+5,5
3123 o 10: t5+5,5
3124 |
3124 |
3125 o 9: t5+4,4
3125 o 9: t5+4,4
3126 |
3126 |
3127 o 8: t5+3,3
3127 o 8: t5+3,3
3128 |
3128 |
3129 o 7: t5+2,2
3129 o 7: t5+2,2
3130 |
3130 |
3131 o 6: t5+1,1
3131 o 6: t5+1,1
3132 |
3132 |
3133 o 5: t5+0,0
3133 o 5: t5+0,0
3134 |\
3134 |\
3135 | o 4: t4+0,0
3135 | o 4: t4+0,0
3136 | |
3136 | |
3137 | o 3: at3+0,0 t3+0,0
3137 | o 3: at3+0,0 t3+0,0
3138 | |
3138 | |
3139 o | 2: t2+0,0
3139 o | 2: t2+0,0
3140 |/
3140 |/
3141 o 1: t1+0,0
3141 o 1: t1+0,0
3142 |
3142 |
3143 o 0: null+1,1
3143 o 0: null+1,1
3144
3144
3145
3145
3146 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3146 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3147 @ 11: t3, C: 9, D: 8
3147 @ 11: t3, C: 9, D: 8
3148 |
3148 |
3149 o 10: t3, C: 8, D: 7
3149 o 10: t3, C: 8, D: 7
3150 |
3150 |
3151 o 9: t3, C: 7, D: 6
3151 o 9: t3, C: 7, D: 6
3152 |
3152 |
3153 o 8: t3, C: 6, D: 5
3153 o 8: t3, C: 6, D: 5
3154 |
3154 |
3155 o 7: t3, C: 5, D: 4
3155 o 7: t3, C: 5, D: 4
3156 |
3156 |
3157 o 6: t3, C: 4, D: 3
3157 o 6: t3, C: 4, D: 3
3158 |
3158 |
3159 o 5: t3, C: 3, D: 2
3159 o 5: t3, C: 3, D: 2
3160 |\
3160 |\
3161 | o 4: t3, C: 1, D: 1
3161 | o 4: t3, C: 1, D: 1
3162 | |
3162 | |
3163 | o 3: t3, C: 0, D: 0
3163 | o 3: t3, C: 0, D: 0
3164 | |
3164 | |
3165 o | 2: t1, C: 1, D: 1
3165 o | 2: t1, C: 1, D: 1
3166 |/
3166 |/
3167 o 1: t1, C: 0, D: 0
3167 o 1: t1, C: 0, D: 0
3168 |
3168 |
3169 o 0: null, C: 1, D: 1
3169 o 0: null, C: 1, D: 1
3170
3170
3171
3171
3172 $ cd ..
3172 $ cd ..
3173
3173
3174
3174
3175 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3175 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3176 if it is a relative path
3176 if it is a relative path
3177
3177
3178 $ mkdir -p home/styles
3178 $ mkdir -p home/styles
3179
3179
3180 $ cat > home/styles/teststyle <<EOF
3180 $ cat > home/styles/teststyle <<EOF
3181 > changeset = 'test {rev}:{node|short}\n'
3181 > changeset = 'test {rev}:{node|short}\n'
3182 > EOF
3182 > EOF
3183
3183
3184 $ HOME=`pwd`/home; export HOME
3184 $ HOME=`pwd`/home; export HOME
3185
3185
3186 $ cat > latesttag/.hg/hgrc <<EOF
3186 $ cat > latesttag/.hg/hgrc <<EOF
3187 > [ui]
3187 > [ui]
3188 > style = ~/styles/teststyle
3188 > style = ~/styles/teststyle
3189 > EOF
3189 > EOF
3190
3190
3191 $ hg -R latesttag tip
3191 $ hg -R latesttag tip
3192 test 11:97e5943b523a
3192 test 11:97e5943b523a
3193
3193
3194 Test recursive showlist template (issue1989):
3194 Test recursive showlist template (issue1989):
3195
3195
3196 $ cat > style1989 <<EOF
3196 $ cat > style1989 <<EOF
3197 > changeset = '{file_mods}{manifest}{extras}'
3197 > changeset = '{file_mods}{manifest}{extras}'
3198 > file_mod = 'M|{author|person}\n'
3198 > file_mod = 'M|{author|person}\n'
3199 > manifest = '{rev},{author}\n'
3199 > manifest = '{rev},{author}\n'
3200 > extra = '{key}: {author}\n'
3200 > extra = '{key}: {author}\n'
3201 > EOF
3201 > EOF
3202
3202
3203 $ hg -R latesttag log -r tip --style=style1989
3203 $ hg -R latesttag log -r tip --style=style1989
3204 M|test
3204 M|test
3205 11,test
3205 11,test
3206 branch: test
3206 branch: test
3207
3207
3208 Test new-style inline templating:
3208 Test new-style inline templating:
3209
3209
3210 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3210 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3211 modified files: .hgtags
3211 modified files: .hgtags
3212
3212
3213
3213
3214 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3214 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3215 hg: parse error: 11 is not iterable of mappings
3215 hg: parse error: 11 is not iterable of mappings
3216 [255]
3216 [255]
3217 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3217 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3218 hg: parse error: None is not iterable of mappings
3218 hg: parse error: None is not iterable of mappings
3219 [255]
3219 [255]
3220 $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}'
3220 $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}'
3221 hg: parse error: list of strings is not mappable
3221 hg: parse error: list of strings is not mappable
3222 [255]
3222 [255]
3223
3223
3224 Test new-style inline templating of non-list/dict type:
3224 Test new-style inline templating of non-list/dict type:
3225
3225
3226 $ hg log -R latesttag -r tip -T '{manifest}\n'
3226 $ hg log -R latesttag -r tip -T '{manifest}\n'
3227 11:2bc6e9006ce2
3227 11:2bc6e9006ce2
3228 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3228 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3229 string length: 15
3229 string length: 15
3230 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3230 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3231 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3231 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3232
3232
3233 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3233 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3234 branch: default
3234 branch: default
3235 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3235 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3236 hg: parse error: None is not iterable of mappings
3236 hg: parse error: None is not iterable of mappings
3237 [255]
3237 [255]
3238 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3238 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3239 branch: default
3239 branch: default
3240 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3240 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3241 0:ce3cec86e6c2
3241 0:ce3cec86e6c2
3242 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3242 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3243 9:fbc7cd862e9c
3243 9:fbc7cd862e9c
3244
3244
3245 Test manifest/get() can be join()-ed as string, though it's silly:
3245 Test manifest/get() can be join()-ed as string, though it's silly:
3246
3246
3247 $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
3247 $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
3248 1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
3248 1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
3249 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
3249 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
3250 d.e.f.a.u.l.t
3250 d.e.f.a.u.l.t
3251
3251
3252 Test join() over string
3252 Test join() over string
3253
3253
3254 $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
3254 $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
3255 1.1
3255 1.1
3256
3256
3257 Test join() over uniterable
3257 Test join() over uniterable
3258
3258
3259 $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
3259 $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
3260 hg: parse error: 11 is not iterable
3260 hg: parse error: 11 is not iterable
3261 [255]
3261 [255]
3262
3262
3263 Test min/max of integers
3263 Test min/max of integers
3264
3264
3265 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
3265 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
3266 9
3266 9
3267 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
3267 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
3268 10
3268 10
3269
3269
3270 Test min/max over map operation:
3270 Test min/max over map operation:
3271
3271
3272 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
3272 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
3273 at3
3273 at3
3274 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
3274 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
3275 t3
3275 t3
3276
3276
3277 Test min/max of strings:
3278
3279 $ hg log -R latesttag -l1 -T '{min(desc)}\n'
3280 3
3281 $ hg log -R latesttag -l1 -T '{max(desc)}\n'
3282 t
3283
3284 Test min/max of non-iterable:
3285
3286 $ hg debugtemplate '{min(1)}'
3287 hg: parse error: 1 is not iterable
3288 (min first argument should be an iterable)
3289 [255]
3290 $ hg debugtemplate '{max(2)}'
3291 hg: parse error: 2 is not iterable
3292 (max first argument should be an iterable)
3293 [255]
3294
3295 Test min/max of empty sequence:
3296
3297 $ hg debugtemplate '{min("")}'
3298 hg: parse error: empty string
3299 (min first argument should be an iterable)
3300 [255]
3301 $ hg debugtemplate '{max("")}'
3302 hg: parse error: empty string
3303 (max first argument should be an iterable)
3304 [255]
3305 $ hg debugtemplate '{min(dict())}'
3306 hg: parse error: empty sequence
3307 (min first argument should be an iterable)
3308 [255]
3309 $ hg debugtemplate '{max(dict())}'
3310 hg: parse error: empty sequence
3311 (max first argument should be an iterable)
3312 [255]
3313 $ hg debugtemplate '{min(dict() % "")}'
3314 hg: parse error: empty sequence
3315 (min first argument should be an iterable)
3316 [255]
3317 $ hg debugtemplate '{max(dict() % "")}'
3318 hg: parse error: empty sequence
3319 (max first argument should be an iterable)
3320 [255]
3321
3277 Test min/max of if() result
3322 Test min/max of if() result
3278
3323
3279 $ cd latesttag
3324 $ cd latesttag
3280 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
3325 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
3281 9
3326 9
3282 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
3327 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
3283 10
3328 10
3284 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
3329 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
3285 9
3330 9
3286 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
3331 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
3287 10
3332 10
3288 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
3333 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
3289 9
3334 9
3290 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
3335 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
3291 10
3336 10
3292 $ cd ..
3337 $ cd ..
3293
3338
3294 Test laziness of if() then/else clause
3339 Test laziness of if() then/else clause
3295
3340
3296 $ hg debugtemplate '{count(0)}'
3341 $ hg debugtemplate '{count(0)}'
3297 hg: parse error: not countable
3342 hg: parse error: not countable
3298 (incompatible use of template filter 'count')
3343 (incompatible use of template filter 'count')
3299 [255]
3344 [255]
3300 $ hg debugtemplate '{if(true, "", count(0))}'
3345 $ hg debugtemplate '{if(true, "", count(0))}'
3301 $ hg debugtemplate '{if(false, count(0), "")}'
3346 $ hg debugtemplate '{if(false, count(0), "")}'
3302 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
3347 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
3303 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
3348 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
3304 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
3349 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
3305 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
3350 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
3306
3351
3307 Test dot operator precedence:
3352 Test dot operator precedence:
3308
3353
3309 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
3354 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
3310 (template
3355 (template
3311 (|
3356 (|
3312 (.
3357 (.
3313 (symbol 'manifest')
3358 (symbol 'manifest')
3314 (symbol 'node'))
3359 (symbol 'node'))
3315 (symbol 'short'))
3360 (symbol 'short'))
3316 (string '\n'))
3361 (string '\n'))
3317 89f4071fec70
3362 89f4071fec70
3318
3363
3319 (the following examples are invalid, but seem natural in parsing POV)
3364 (the following examples are invalid, but seem natural in parsing POV)
3320
3365
3321 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
3366 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
3322 (template
3367 (template
3323 (|
3368 (|
3324 (symbol 'foo')
3369 (symbol 'foo')
3325 (.
3370 (.
3326 (symbol 'bar')
3371 (symbol 'bar')
3327 (symbol 'baz')))
3372 (symbol 'baz')))
3328 (string '\n'))
3373 (string '\n'))
3329 [255]
3374 [255]
3330 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
3375 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
3331 (template
3376 (template
3332 (.
3377 (.
3333 (symbol 'foo')
3378 (symbol 'foo')
3334 (func
3379 (func
3335 (symbol 'bar')
3380 (symbol 'bar')
3336 None))
3381 None))
3337 (string '\n'))
3382 (string '\n'))
3338 [255]
3383 [255]
3339
3384
3340 Test evaluation of dot operator:
3385 Test evaluation of dot operator:
3341
3386
3342 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
3387 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
3343 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
3388 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
3344 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
3389 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
3345 default
3390 default
3346
3391
3347 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
3392 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
3348 hg: parse error: 'test' is not a dictionary
3393 hg: parse error: 'test' is not a dictionary
3349 (keyword 'author' does not support member operation)
3394 (keyword 'author' does not support member operation)
3350 [255]
3395 [255]
3351 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
3396 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
3352 hg: parse error: 'a' is not a dictionary
3397 hg: parse error: 'a' is not a dictionary
3353 [255]
3398 [255]
3354
3399
3355 Test the sub function of templating for expansion:
3400 Test the sub function of templating for expansion:
3356
3401
3357 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3402 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3358 xx
3403 xx
3359
3404
3360 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3405 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3361 hg: parse error: sub got an invalid pattern: [
3406 hg: parse error: sub got an invalid pattern: [
3362 [255]
3407 [255]
3363 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3408 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3364 hg: parse error: sub got an invalid replacement: \1
3409 hg: parse error: sub got an invalid replacement: \1
3365 [255]
3410 [255]
3366
3411
3367 Test the strip function with chars specified:
3412 Test the strip function with chars specified:
3368
3413
3369 $ hg log -R latesttag --template '{desc}\n'
3414 $ hg log -R latesttag --template '{desc}\n'
3370 at3
3415 at3
3371 t5
3416 t5
3372 t4
3417 t4
3373 t3
3418 t3
3374 t2
3419 t2
3375 t1
3420 t1
3376 merge
3421 merge
3377 h2e
3422 h2e
3378 h2d
3423 h2d
3379 h1c
3424 h1c
3380 b
3425 b
3381 a
3426 a
3382
3427
3383 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3428 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3384 at3
3429 at3
3385 5
3430 5
3386 4
3431 4
3387 3
3432 3
3388 2
3433 2
3389 1
3434 1
3390 merg
3435 merg
3391 h2
3436 h2
3392 h2d
3437 h2d
3393 h1c
3438 h1c
3394 b
3439 b
3395 a
3440 a
3396
3441
3397 Test date format:
3442 Test date format:
3398
3443
3399 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3444 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3400 date: 70 01 01 10 +0000
3445 date: 70 01 01 10 +0000
3401 date: 70 01 01 09 +0000
3446 date: 70 01 01 09 +0000
3402 date: 70 01 01 04 +0000
3447 date: 70 01 01 04 +0000
3403 date: 70 01 01 08 +0000
3448 date: 70 01 01 08 +0000
3404 date: 70 01 01 07 +0000
3449 date: 70 01 01 07 +0000
3405 date: 70 01 01 06 +0000
3450 date: 70 01 01 06 +0000
3406 date: 70 01 01 05 +0100
3451 date: 70 01 01 05 +0100
3407 date: 70 01 01 04 +0000
3452 date: 70 01 01 04 +0000
3408 date: 70 01 01 03 +0000
3453 date: 70 01 01 03 +0000
3409 date: 70 01 01 02 +0000
3454 date: 70 01 01 02 +0000
3410 date: 70 01 01 01 +0000
3455 date: 70 01 01 01 +0000
3411 date: 70 01 01 00 +0000
3456 date: 70 01 01 00 +0000
3412
3457
3413 Test invalid date:
3458 Test invalid date:
3414
3459
3415 $ hg log -R latesttag -T '{date(rev)}\n'
3460 $ hg log -R latesttag -T '{date(rev)}\n'
3416 hg: parse error: date expects a date information
3461 hg: parse error: date expects a date information
3417 [255]
3462 [255]
3418
3463
3419 Test integer literal:
3464 Test integer literal:
3420
3465
3421 $ hg debugtemplate -v '{(0)}\n'
3466 $ hg debugtemplate -v '{(0)}\n'
3422 (template
3467 (template
3423 (group
3468 (group
3424 (integer '0'))
3469 (integer '0'))
3425 (string '\n'))
3470 (string '\n'))
3426 0
3471 0
3427 $ hg debugtemplate -v '{(123)}\n'
3472 $ hg debugtemplate -v '{(123)}\n'
3428 (template
3473 (template
3429 (group
3474 (group
3430 (integer '123'))
3475 (integer '123'))
3431 (string '\n'))
3476 (string '\n'))
3432 123
3477 123
3433 $ hg debugtemplate -v '{(-4)}\n'
3478 $ hg debugtemplate -v '{(-4)}\n'
3434 (template
3479 (template
3435 (group
3480 (group
3436 (negate
3481 (negate
3437 (integer '4')))
3482 (integer '4')))
3438 (string '\n'))
3483 (string '\n'))
3439 -4
3484 -4
3440 $ hg debugtemplate '{(-)}\n'
3485 $ hg debugtemplate '{(-)}\n'
3441 hg: parse error at 3: not a prefix: )
3486 hg: parse error at 3: not a prefix: )
3442 ({(-)}\n
3487 ({(-)}\n
3443 ^ here)
3488 ^ here)
3444 [255]
3489 [255]
3445 $ hg debugtemplate '{(-a)}\n'
3490 $ hg debugtemplate '{(-a)}\n'
3446 hg: parse error: negation needs an integer argument
3491 hg: parse error: negation needs an integer argument
3447 [255]
3492 [255]
3448
3493
3449 top-level integer literal is interpreted as symbol (i.e. variable name):
3494 top-level integer literal is interpreted as symbol (i.e. variable name):
3450
3495
3451 $ hg debugtemplate -D 1=one -v '{1}\n'
3496 $ hg debugtemplate -D 1=one -v '{1}\n'
3452 (template
3497 (template
3453 (integer '1')
3498 (integer '1')
3454 (string '\n'))
3499 (string '\n'))
3455 one
3500 one
3456 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3501 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3457 (template
3502 (template
3458 (func
3503 (func
3459 (symbol 'if')
3504 (symbol 'if')
3460 (list
3505 (list
3461 (string 't')
3506 (string 't')
3462 (template
3507 (template
3463 (integer '1'))))
3508 (integer '1'))))
3464 (string '\n'))
3509 (string '\n'))
3465 one
3510 one
3466 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3511 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3467 (template
3512 (template
3468 (|
3513 (|
3469 (integer '1')
3514 (integer '1')
3470 (symbol 'stringify'))
3515 (symbol 'stringify'))
3471 (string '\n'))
3516 (string '\n'))
3472 one
3517 one
3473
3518
3474 unless explicit symbol is expected:
3519 unless explicit symbol is expected:
3475
3520
3476 $ hg log -Ra -r0 -T '{desc|1}\n'
3521 $ hg log -Ra -r0 -T '{desc|1}\n'
3477 hg: parse error: expected a symbol, got 'integer'
3522 hg: parse error: expected a symbol, got 'integer'
3478 [255]
3523 [255]
3479 $ hg log -Ra -r0 -T '{1()}\n'
3524 $ hg log -Ra -r0 -T '{1()}\n'
3480 hg: parse error: expected a symbol, got 'integer'
3525 hg: parse error: expected a symbol, got 'integer'
3481 [255]
3526 [255]
3482
3527
3483 Test string literal:
3528 Test string literal:
3484
3529
3485 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3530 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3486 (template
3531 (template
3487 (string 'string with no template fragment')
3532 (string 'string with no template fragment')
3488 (string '\n'))
3533 (string '\n'))
3489 string with no template fragment
3534 string with no template fragment
3490 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3535 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3491 (template
3536 (template
3492 (template
3537 (template
3493 (string 'template: ')
3538 (string 'template: ')
3494 (symbol 'rev'))
3539 (symbol 'rev'))
3495 (string '\n'))
3540 (string '\n'))
3496 template: 0
3541 template: 0
3497 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3542 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3498 (template
3543 (template
3499 (string 'rawstring: {rev}')
3544 (string 'rawstring: {rev}')
3500 (string '\n'))
3545 (string '\n'))
3501 rawstring: {rev}
3546 rawstring: {rev}
3502 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3547 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3503 (template
3548 (template
3504 (%
3549 (%
3505 (symbol 'files')
3550 (symbol 'files')
3506 (string 'rawstring: {file}'))
3551 (string 'rawstring: {file}'))
3507 (string '\n'))
3552 (string '\n'))
3508 rawstring: {file}
3553 rawstring: {file}
3509
3554
3510 Test string escaping:
3555 Test string escaping:
3511
3556
3512 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3557 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3513 >
3558 >
3514 <>\n<[>
3559 <>\n<[>
3515 <>\n<]>
3560 <>\n<]>
3516 <>\n<
3561 <>\n<
3517
3562
3518 $ hg log -R latesttag -r 0 \
3563 $ hg log -R latesttag -r 0 \
3519 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3564 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3520 >
3565 >
3521 <>\n<[>
3566 <>\n<[>
3522 <>\n<]>
3567 <>\n<]>
3523 <>\n<
3568 <>\n<
3524
3569
3525 $ hg log -R latesttag -r 0 -T esc \
3570 $ hg log -R latesttag -r 0 -T esc \
3526 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3571 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3527 >
3572 >
3528 <>\n<[>
3573 <>\n<[>
3529 <>\n<]>
3574 <>\n<]>
3530 <>\n<
3575 <>\n<
3531
3576
3532 $ cat <<'EOF' > esctmpl
3577 $ cat <<'EOF' > esctmpl
3533 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3578 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3534 > EOF
3579 > EOF
3535 $ hg log -R latesttag -r 0 --style ./esctmpl
3580 $ hg log -R latesttag -r 0 --style ./esctmpl
3536 >
3581 >
3537 <>\n<[>
3582 <>\n<[>
3538 <>\n<]>
3583 <>\n<]>
3539 <>\n<
3584 <>\n<
3540
3585
3541 Test string escaping of quotes:
3586 Test string escaping of quotes:
3542
3587
3543 $ hg log -Ra -r0 -T '{"\""}\n'
3588 $ hg log -Ra -r0 -T '{"\""}\n'
3544 "
3589 "
3545 $ hg log -Ra -r0 -T '{"\\\""}\n'
3590 $ hg log -Ra -r0 -T '{"\\\""}\n'
3546 \"
3591 \"
3547 $ hg log -Ra -r0 -T '{r"\""}\n'
3592 $ hg log -Ra -r0 -T '{r"\""}\n'
3548 \"
3593 \"
3549 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3594 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3550 \\\"
3595 \\\"
3551
3596
3552
3597
3553 $ hg log -Ra -r0 -T '{"\""}\n'
3598 $ hg log -Ra -r0 -T '{"\""}\n'
3554 "
3599 "
3555 $ hg log -Ra -r0 -T '{"\\\""}\n'
3600 $ hg log -Ra -r0 -T '{"\\\""}\n'
3556 \"
3601 \"
3557 $ hg log -Ra -r0 -T '{r"\""}\n'
3602 $ hg log -Ra -r0 -T '{r"\""}\n'
3558 \"
3603 \"
3559 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3604 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3560 \\\"
3605 \\\"
3561
3606
3562 Test exception in quoted template. single backslash before quotation mark is
3607 Test exception in quoted template. single backslash before quotation mark is
3563 stripped before parsing:
3608 stripped before parsing:
3564
3609
3565 $ cat <<'EOF' > escquotetmpl
3610 $ cat <<'EOF' > escquotetmpl
3566 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3611 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3567 > EOF
3612 > EOF
3568 $ cd latesttag
3613 $ cd latesttag
3569 $ hg log -r 2 --style ../escquotetmpl
3614 $ hg log -r 2 --style ../escquotetmpl
3570 " \" \" \\" head1
3615 " \" \" \\" head1
3571
3616
3572 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3617 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3573 valid
3618 valid
3574 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3619 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3575 valid
3620 valid
3576
3621
3577 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3622 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3578 _evalifliteral() templates (issue4733):
3623 _evalifliteral() templates (issue4733):
3579
3624
3580 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3625 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3581 "2
3626 "2
3582 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3627 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3583 "2
3628 "2
3584 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3629 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3585 "2
3630 "2
3586
3631
3587 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3632 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3588 \"
3633 \"
3589 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3634 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3590 \"
3635 \"
3591 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3636 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3592 \"
3637 \"
3593
3638
3594 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3639 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3595 \\\"
3640 \\\"
3596 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3641 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3597 \\\"
3642 \\\"
3598 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3643 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3599 \\\"
3644 \\\"
3600
3645
3601 escaped single quotes and errors:
3646 escaped single quotes and errors:
3602
3647
3603 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3648 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3604 foo
3649 foo
3605 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3650 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3606 foo
3651 foo
3607 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3652 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3608 hg: parse error at 21: unterminated string
3653 hg: parse error at 21: unterminated string
3609 ({if(rev, "{if(rev, \")}")}\n
3654 ({if(rev, "{if(rev, \")}")}\n
3610 ^ here)
3655 ^ here)
3611 [255]
3656 [255]
3612 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3657 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3613 hg: parse error: trailing \ in string
3658 hg: parse error: trailing \ in string
3614 [255]
3659 [255]
3615 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3660 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3616 hg: parse error: trailing \ in string
3661 hg: parse error: trailing \ in string
3617 [255]
3662 [255]
3618
3663
3619 $ cd ..
3664 $ cd ..
3620
3665
3621 Test leading backslashes:
3666 Test leading backslashes:
3622
3667
3623 $ cd latesttag
3668 $ cd latesttag
3624 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3669 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3625 {rev} {file}
3670 {rev} {file}
3626 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3671 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3627 \2 \head1
3672 \2 \head1
3628 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3673 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3629 \{rev} \{file}
3674 \{rev} \{file}
3630 $ cd ..
3675 $ cd ..
3631
3676
3632 Test leading backslashes in "if" expression (issue4714):
3677 Test leading backslashes in "if" expression (issue4714):
3633
3678
3634 $ cd latesttag
3679 $ cd latesttag
3635 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3680 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3636 {rev} \{rev}
3681 {rev} \{rev}
3637 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3682 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3638 \2 \\{rev}
3683 \2 \\{rev}
3639 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3684 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3640 \{rev} \\\{rev}
3685 \{rev} \\\{rev}
3641 $ cd ..
3686 $ cd ..
3642
3687
3643 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3688 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3644
3689
3645 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3690 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3646 \x6e
3691 \x6e
3647 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3692 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3648 \x5c\x786e
3693 \x5c\x786e
3649 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3694 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3650 \x6e
3695 \x6e
3651 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3696 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3652 \x5c\x786e
3697 \x5c\x786e
3653
3698
3654 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3699 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3655 \x6e
3700 \x6e
3656 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3701 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3657 \x5c\x786e
3702 \x5c\x786e
3658 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3703 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3659 \x6e
3704 \x6e
3660 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3705 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3661 \x5c\x786e
3706 \x5c\x786e
3662
3707
3663 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3708 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3664 fourth
3709 fourth
3665 second
3710 second
3666 third
3711 third
3667 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3712 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3668 fourth\nsecond\nthird
3713 fourth\nsecond\nthird
3669
3714
3670 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3715 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3671 <p>
3716 <p>
3672 1st
3717 1st
3673 </p>
3718 </p>
3674 <p>
3719 <p>
3675 2nd
3720 2nd
3676 </p>
3721 </p>
3677 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3722 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3678 <p>
3723 <p>
3679 1st\n\n2nd
3724 1st\n\n2nd
3680 </p>
3725 </p>
3681 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3726 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3682 1st
3727 1st
3683
3728
3684 2nd
3729 2nd
3685
3730
3686 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3731 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3687 o perso
3732 o perso
3688 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3733 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3689 no person
3734 no person
3690 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3735 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3691 o perso
3736 o perso
3692 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3737 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3693 no perso
3738 no perso
3694
3739
3695 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3740 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3696 -o perso-
3741 -o perso-
3697 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3742 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3698 no person
3743 no person
3699 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3744 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3700 \x2do perso\x2d
3745 \x2do perso\x2d
3701 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3746 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3702 -o perso-
3747 -o perso-
3703 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3748 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3704 \x2do perso\x6e
3749 \x2do perso\x6e
3705
3750
3706 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3751 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3707 fourth
3752 fourth
3708 second
3753 second
3709 third
3754 third
3710
3755
3711 Test string escaping in nested expression:
3756 Test string escaping in nested expression:
3712
3757
3713 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3758 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3714 fourth\x6esecond\x6ethird
3759 fourth\x6esecond\x6ethird
3715 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3760 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3716 fourth\x6esecond\x6ethird
3761 fourth\x6esecond\x6ethird
3717
3762
3718 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3763 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3719 fourth\x6esecond\x6ethird
3764 fourth\x6esecond\x6ethird
3720 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3765 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3721 fourth\x5c\x786esecond\x5c\x786ethird
3766 fourth\x5c\x786esecond\x5c\x786ethird
3722
3767
3723 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3768 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3724 3:\x6eo user, \x6eo domai\x6e
3769 3:\x6eo user, \x6eo domai\x6e
3725 4:\x5c\x786eew bra\x5c\x786ech
3770 4:\x5c\x786eew bra\x5c\x786ech
3726
3771
3727 Test quotes in nested expression are evaluated just like a $(command)
3772 Test quotes in nested expression are evaluated just like a $(command)
3728 substitution in POSIX shells:
3773 substitution in POSIX shells:
3729
3774
3730 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3775 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3731 8:95c24699272e
3776 8:95c24699272e
3732 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3777 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3733 {8} "95c24699272e"
3778 {8} "95c24699272e"
3734
3779
3735 Test recursive evaluation:
3780 Test recursive evaluation:
3736
3781
3737 $ hg init r
3782 $ hg init r
3738 $ cd r
3783 $ cd r
3739 $ echo a > a
3784 $ echo a > a
3740 $ hg ci -Am '{rev}'
3785 $ hg ci -Am '{rev}'
3741 adding a
3786 adding a
3742 $ hg log -r 0 --template '{if(rev, desc)}\n'
3787 $ hg log -r 0 --template '{if(rev, desc)}\n'
3743 {rev}
3788 {rev}
3744 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3789 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3745 test 0
3790 test 0
3746
3791
3747 $ hg branch -q 'text.{rev}'
3792 $ hg branch -q 'text.{rev}'
3748 $ echo aa >> aa
3793 $ echo aa >> aa
3749 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3794 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3750
3795
3751 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3796 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3752 {node|short}desc to
3797 {node|short}desc to
3753 text.{rev}be wrapped
3798 text.{rev}be wrapped
3754 text.{rev}desc to be
3799 text.{rev}desc to be
3755 text.{rev}wrapped (no-eol)
3800 text.{rev}wrapped (no-eol)
3756 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3801 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3757 bcc7ff960b8e:desc to
3802 bcc7ff960b8e:desc to
3758 text.1:be wrapped
3803 text.1:be wrapped
3759 text.1:desc to be
3804 text.1:desc to be
3760 text.1:wrapped (no-eol)
3805 text.1:wrapped (no-eol)
3761 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3806 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3762 hg: parse error: fill expects an integer width
3807 hg: parse error: fill expects an integer width
3763 [255]
3808 [255]
3764
3809
3765 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3810 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3766 bcc7ff960b8e:desc to be
3811 bcc7ff960b8e:desc to be
3767 termwidth.1:wrapped desc
3812 termwidth.1:wrapped desc
3768 termwidth.1:to be wrapped (no-eol)
3813 termwidth.1:to be wrapped (no-eol)
3769
3814
3770 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3815 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3771 {node|short} (no-eol)
3816 {node|short} (no-eol)
3772 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3817 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3773 bcc-ff---b-e (no-eol)
3818 bcc-ff---b-e (no-eol)
3774
3819
3775 $ cat >> .hg/hgrc <<EOF
3820 $ cat >> .hg/hgrc <<EOF
3776 > [extensions]
3821 > [extensions]
3777 > color=
3822 > color=
3778 > [color]
3823 > [color]
3779 > mode=ansi
3824 > mode=ansi
3780 > text.{rev} = red
3825 > text.{rev} = red
3781 > text.1 = green
3826 > text.1 = green
3782 > EOF
3827 > EOF
3783 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3828 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3784 \x1b[0;31mtext\x1b[0m (esc)
3829 \x1b[0;31mtext\x1b[0m (esc)
3785 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3830 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3786 \x1b[0;32mtext\x1b[0m (esc)
3831 \x1b[0;32mtext\x1b[0m (esc)
3787
3832
3788 color effect can be specified without quoting:
3833 color effect can be specified without quoting:
3789
3834
3790 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3835 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3791 \x1b[0;31mtext\x1b[0m (esc)
3836 \x1b[0;31mtext\x1b[0m (esc)
3792
3837
3793 color effects can be nested (issue5413)
3838 color effects can be nested (issue5413)
3794
3839
3795 $ hg debugtemplate --color=always \
3840 $ hg debugtemplate --color=always \
3796 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3841 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3797 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
3842 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
3798
3843
3799 pad() should interact well with color codes (issue5416)
3844 pad() should interact well with color codes (issue5416)
3800
3845
3801 $ hg debugtemplate --color=always \
3846 $ hg debugtemplate --color=always \
3802 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3847 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3803 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3848 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3804
3849
3805 label should be no-op if color is disabled:
3850 label should be no-op if color is disabled:
3806
3851
3807 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3852 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3808 text
3853 text
3809 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3854 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3810 text
3855 text
3811
3856
3812 Test branches inside if statement:
3857 Test branches inside if statement:
3813
3858
3814 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3859 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3815 no
3860 no
3816
3861
3817 Test dict constructor:
3862 Test dict constructor:
3818
3863
3819 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3864 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3820 y=f7769ec2ab97 x=0
3865 y=f7769ec2ab97 x=0
3821 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3866 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3822 x=0
3867 x=0
3823 y=f7769ec2ab97
3868 y=f7769ec2ab97
3824 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3869 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3825 {"x": 0, "y": "f7769ec2ab97"}
3870 {"x": 0, "y": "f7769ec2ab97"}
3826 $ hg log -r 0 -T '{dict()|json}\n'
3871 $ hg log -r 0 -T '{dict()|json}\n'
3827 {}
3872 {}
3828
3873
3829 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3874 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3830 rev=0 node=f7769ec2ab97
3875 rev=0 node=f7769ec2ab97
3831 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3876 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3832 rev=0 node=f7769ec2ab97
3877 rev=0 node=f7769ec2ab97
3833
3878
3834 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3879 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3835 hg: parse error: duplicated dict key 'rev' inferred
3880 hg: parse error: duplicated dict key 'rev' inferred
3836 [255]
3881 [255]
3837 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3882 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3838 hg: parse error: duplicated dict key 'node' inferred
3883 hg: parse error: duplicated dict key 'node' inferred
3839 [255]
3884 [255]
3840 $ hg log -r 0 -T '{dict(1 + 2)}'
3885 $ hg log -r 0 -T '{dict(1 + 2)}'
3841 hg: parse error: dict key cannot be inferred
3886 hg: parse error: dict key cannot be inferred
3842 [255]
3887 [255]
3843
3888
3844 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3889 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3845 hg: parse error: dict got multiple values for keyword argument 'x'
3890 hg: parse error: dict got multiple values for keyword argument 'x'
3846 [255]
3891 [255]
3847
3892
3848 Test get function:
3893 Test get function:
3849
3894
3850 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3895 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3851 default
3896 default
3852 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3897 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3853 default
3898 default
3854 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3899 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3855 hg: parse error: not a dictionary
3900 hg: parse error: not a dictionary
3856 (get() expects a dict as first argument)
3901 (get() expects a dict as first argument)
3857 [255]
3902 [255]
3858
3903
3859 Test json filter applied to hybrid object:
3904 Test json filter applied to hybrid object:
3860
3905
3861 $ hg log -r0 -T '{files|json}\n'
3906 $ hg log -r0 -T '{files|json}\n'
3862 ["a"]
3907 ["a"]
3863 $ hg log -r0 -T '{extras|json}\n'
3908 $ hg log -r0 -T '{extras|json}\n'
3864 {"branch": "default"}
3909 {"branch": "default"}
3865
3910
3866 Test json filter applied to map result:
3911 Test json filter applied to map result:
3867
3912
3868 $ hg log -r0 -T '{json(extras % "{key}")}\n'
3913 $ hg log -r0 -T '{json(extras % "{key}")}\n'
3869 ["branch"]
3914 ["branch"]
3870
3915
3871 Test localdate(date, tz) function:
3916 Test localdate(date, tz) function:
3872
3917
3873 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3918 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3874 1970-01-01 09:00 +0900
3919 1970-01-01 09:00 +0900
3875 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3920 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3876 1970-01-01 00:00 +0000
3921 1970-01-01 00:00 +0000
3877 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3922 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3878 hg: parse error: localdate expects a timezone
3923 hg: parse error: localdate expects a timezone
3879 [255]
3924 [255]
3880 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3925 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3881 1970-01-01 02:00 +0200
3926 1970-01-01 02:00 +0200
3882 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3927 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3883 1970-01-01 00:00 +0000
3928 1970-01-01 00:00 +0000
3884 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3929 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3885 1970-01-01 00:00 +0000
3930 1970-01-01 00:00 +0000
3886 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3931 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3887 hg: parse error: localdate expects a timezone
3932 hg: parse error: localdate expects a timezone
3888 [255]
3933 [255]
3889 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3934 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3890 hg: parse error: localdate expects a timezone
3935 hg: parse error: localdate expects a timezone
3891 [255]
3936 [255]
3892
3937
3893 Test shortest(node) function:
3938 Test shortest(node) function:
3894
3939
3895 $ echo b > b
3940 $ echo b > b
3896 $ hg ci -qAm b
3941 $ hg ci -qAm b
3897 $ hg log --template '{shortest(node)}\n'
3942 $ hg log --template '{shortest(node)}\n'
3898 e777
3943 e777
3899 bcc7
3944 bcc7
3900 f776
3945 f776
3901 $ hg log --template '{shortest(node, 10)}\n'
3946 $ hg log --template '{shortest(node, 10)}\n'
3902 e777603221
3947 e777603221
3903 bcc7ff960b
3948 bcc7ff960b
3904 f7769ec2ab
3949 f7769ec2ab
3905 $ hg log --template '{node|shortest}\n' -l1
3950 $ hg log --template '{node|shortest}\n' -l1
3906 e777
3951 e777
3907
3952
3908 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3953 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3909 f7769ec2ab
3954 f7769ec2ab
3910 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3955 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3911 hg: parse error: shortest() expects an integer minlength
3956 hg: parse error: shortest() expects an integer minlength
3912 [255]
3957 [255]
3913
3958
3914 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3959 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3915 ffff
3960 ffff
3916
3961
3917 $ hg log --template '{shortest("f")}\n' -l1
3962 $ hg log --template '{shortest("f")}\n' -l1
3918 f
3963 f
3919
3964
3920 $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
3965 $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
3921 0123456789012345678901234567890123456789
3966 0123456789012345678901234567890123456789
3922
3967
3923 $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
3968 $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
3924 01234567890123456789012345678901234567890123456789
3969 01234567890123456789012345678901234567890123456789
3925
3970
3926 $ hg log --template '{shortest("not a hex string")}\n' -l1
3971 $ hg log --template '{shortest("not a hex string")}\n' -l1
3927 not a hex string
3972 not a hex string
3928
3973
3929 $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
3974 $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
3930 not a hex string, but it's 40 bytes long
3975 not a hex string, but it's 40 bytes long
3931
3976
3932 $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
3977 $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
3933 ffff
3978 ffff
3934
3979
3935 $ hg log --template '{shortest("fffffff")}\n' -l1
3980 $ hg log --template '{shortest("fffffff")}\n' -l1
3936 ffff
3981 ffff
3937
3982
3938 $ hg log --template '{shortest("ff")}\n' -l1
3983 $ hg log --template '{shortest("ff")}\n' -l1
3939 ffff
3984 ffff
3940
3985
3941 $ cd ..
3986 $ cd ..
3942
3987
3943 Test shortest(node) with the repo having short hash collision:
3988 Test shortest(node) with the repo having short hash collision:
3944
3989
3945 $ hg init hashcollision
3990 $ hg init hashcollision
3946 $ cd hashcollision
3991 $ cd hashcollision
3947 $ cat <<EOF >> .hg/hgrc
3992 $ cat <<EOF >> .hg/hgrc
3948 > [experimental]
3993 > [experimental]
3949 > evolution.createmarkers=True
3994 > evolution.createmarkers=True
3950 > EOF
3995 > EOF
3951 $ echo 0 > a
3996 $ echo 0 > a
3952 $ hg ci -qAm 0
3997 $ hg ci -qAm 0
3953 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3998 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3954 > hg up -q 0
3999 > hg up -q 0
3955 > echo $i > a
4000 > echo $i > a
3956 > hg ci -qm $i
4001 > hg ci -qm $i
3957 > done
4002 > done
3958 $ hg up -q null
4003 $ hg up -q null
3959 $ hg log -r0: -T '{rev}:{node}\n'
4004 $ hg log -r0: -T '{rev}:{node}\n'
3960 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
4005 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3961 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
4006 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3962 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
4007 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3963 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
4008 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3964 4:10776689e627b465361ad5c296a20a487e153ca4
4009 4:10776689e627b465361ad5c296a20a487e153ca4
3965 5:a00be79088084cb3aff086ab799f8790e01a976b
4010 5:a00be79088084cb3aff086ab799f8790e01a976b
3966 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
4011 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3967 7:a0457b3450b8e1b778f1163b31a435802987fe5d
4012 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3968 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
4013 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3969 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
4014 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3970 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
4015 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3971 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
4016 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3972 obsoleted 1 changesets
4017 obsoleted 1 changesets
3973 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
4018 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3974 obsoleted 1 changesets
4019 obsoleted 1 changesets
3975 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
4020 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3976 obsoleted 1 changesets
4021 obsoleted 1 changesets
3977
4022
3978 nodes starting with '11' (we don't have the revision number '11' though)
4023 nodes starting with '11' (we don't have the revision number '11' though)
3979
4024
3980 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
4025 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3981 1:1142
4026 1:1142
3982 2:1140
4027 2:1140
3983 3:11d
4028 3:11d
3984
4029
3985 '5:a00' is hidden, but still we have two nodes starting with 'a0'
4030 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3986
4031
3987 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
4032 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3988 6:a0b
4033 6:a0b
3989 7:a04
4034 7:a04
3990
4035
3991 node '10' conflicts with the revision number '10' even if it is hidden
4036 node '10' conflicts with the revision number '10' even if it is hidden
3992 (we could exclude hidden revision numbers, but currently we don't)
4037 (we could exclude hidden revision numbers, but currently we don't)
3993
4038
3994 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
4039 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3995 4:107
4040 4:107
3996 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
4041 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3997 4:107
4042 4:107
3998
4043
3999 node 'c562' should be unique if the other 'c562' nodes are hidden
4044 node 'c562' should be unique if the other 'c562' nodes are hidden
4000 (but we don't try the slow path to filter out hidden nodes for now)
4045 (but we don't try the slow path to filter out hidden nodes for now)
4001
4046
4002 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
4047 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
4003 8:c5625
4048 8:c5625
4004 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
4049 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
4005 8:c5625
4050 8:c5625
4006 9:c5623
4051 9:c5623
4007 10:c562d
4052 10:c562d
4008
4053
4009 $ cd ..
4054 $ cd ..
4010
4055
4011 Test pad function
4056 Test pad function
4012
4057
4013 $ cd r
4058 $ cd r
4014
4059
4015 $ hg log --template '{pad(rev, 20)} {author|user}\n'
4060 $ hg log --template '{pad(rev, 20)} {author|user}\n'
4016 2 test
4061 2 test
4017 1 {node|short}
4062 1 {node|short}
4018 0 test
4063 0 test
4019
4064
4020 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
4065 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
4021 2 test
4066 2 test
4022 1 {node|short}
4067 1 {node|short}
4023 0 test
4068 0 test
4024
4069
4025 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
4070 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
4026 2------------------- test
4071 2------------------- test
4027 1------------------- {node|short}
4072 1------------------- {node|short}
4028 0------------------- test
4073 0------------------- test
4029
4074
4030 Test template string in pad function
4075 Test template string in pad function
4031
4076
4032 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
4077 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
4033 {0} test
4078 {0} test
4034
4079
4035 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
4080 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
4036 \{rev} test
4081 \{rev} test
4037
4082
4038 Test width argument passed to pad function
4083 Test width argument passed to pad function
4039
4084
4040 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
4085 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
4041 0 test
4086 0 test
4042 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
4087 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
4043 hg: parse error: pad() expects an integer width
4088 hg: parse error: pad() expects an integer width
4044 [255]
4089 [255]
4045
4090
4046 Test invalid fillchar passed to pad function
4091 Test invalid fillchar passed to pad function
4047
4092
4048 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
4093 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
4049 hg: parse error: pad() expects a single fill character
4094 hg: parse error: pad() expects a single fill character
4050 [255]
4095 [255]
4051 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
4096 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
4052 hg: parse error: pad() expects a single fill character
4097 hg: parse error: pad() expects a single fill character
4053 [255]
4098 [255]
4054
4099
4055 Test boolean argument passed to pad function
4100 Test boolean argument passed to pad function
4056
4101
4057 no crash
4102 no crash
4058
4103
4059 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
4104 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
4060 ---------0
4105 ---------0
4061
4106
4062 string/literal
4107 string/literal
4063
4108
4064 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
4109 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
4065 ---------0
4110 ---------0
4066 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
4111 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
4067 0---------
4112 0---------
4068 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
4113 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
4069 0---------
4114 0---------
4070
4115
4071 unknown keyword is evaluated to ''
4116 unknown keyword is evaluated to ''
4072
4117
4073 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
4118 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
4074 0---------
4119 0---------
4075
4120
4076 Test separate function
4121 Test separate function
4077
4122
4078 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
4123 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
4079 a-b-c
4124 a-b-c
4080 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
4125 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
4081 0:f7769ec2ab97 test default
4126 0:f7769ec2ab97 test default
4082 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
4127 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
4083 a \x1b[0;31mb\x1b[0m c d (esc)
4128 a \x1b[0;31mb\x1b[0m c d (esc)
4084
4129
4085 Test boolean expression/literal passed to if function
4130 Test boolean expression/literal passed to if function
4086
4131
4087 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
4132 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
4088 rev 0 is True
4133 rev 0 is True
4089 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
4134 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
4090 literal 0 is True as well
4135 literal 0 is True as well
4091 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
4136 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
4092 empty string is False
4137 empty string is False
4093 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
4138 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
4094 empty list is False
4139 empty list is False
4095 $ hg log -r 0 -T '{if(true, "true is True")}\n'
4140 $ hg log -r 0 -T '{if(true, "true is True")}\n'
4096 true is True
4141 true is True
4097 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
4142 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
4098 false is False
4143 false is False
4099 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
4144 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
4100 non-empty string is True
4145 non-empty string is True
4101
4146
4102 Test ifcontains function
4147 Test ifcontains function
4103
4148
4104 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
4149 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
4105 2 is in the string
4150 2 is in the string
4106 1 is not
4151 1 is not
4107 0 is in the string
4152 0 is in the string
4108
4153
4109 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
4154 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
4110 2 is in the string
4155 2 is in the string
4111 1 is not
4156 1 is not
4112 0 is in the string
4157 0 is in the string
4113
4158
4114 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
4159 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
4115 2 did not add a
4160 2 did not add a
4116 1 did not add a
4161 1 did not add a
4117 0 added a
4162 0 added a
4118
4163
4119 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
4164 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
4120 2 is parent of 1
4165 2 is parent of 1
4121 1
4166 1
4122 0
4167 0
4123
4168
4124 Test revset function
4169 Test revset function
4125
4170
4126 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
4171 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
4127 2 current rev
4172 2 current rev
4128 1 not current rev
4173 1 not current rev
4129 0 not current rev
4174 0 not current rev
4130
4175
4131 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
4176 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
4132 2 match rev
4177 2 match rev
4133 1 match rev
4178 1 match rev
4134 0 not match rev
4179 0 not match rev
4135
4180
4136 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
4181 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
4137 type not match
4182 type not match
4138
4183
4139 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
4184 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
4140 2 Parents: 1
4185 2 Parents: 1
4141 1 Parents: 0
4186 1 Parents: 0
4142 0 Parents:
4187 0 Parents:
4143
4188
4144 $ cat >> .hg/hgrc <<EOF
4189 $ cat >> .hg/hgrc <<EOF
4145 > [revsetalias]
4190 > [revsetalias]
4146 > myparents(\$1) = parents(\$1)
4191 > myparents(\$1) = parents(\$1)
4147 > EOF
4192 > EOF
4148 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
4193 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
4149 2 Parents: 1
4194 2 Parents: 1
4150 1 Parents: 0
4195 1 Parents: 0
4151 0 Parents:
4196 0 Parents:
4152
4197
4153 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
4198 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
4154 Rev: 2
4199 Rev: 2
4155 Ancestor: 0
4200 Ancestor: 0
4156 Ancestor: 1
4201 Ancestor: 1
4157 Ancestor: 2
4202 Ancestor: 2
4158
4203
4159 Rev: 1
4204 Rev: 1
4160 Ancestor: 0
4205 Ancestor: 0
4161 Ancestor: 1
4206 Ancestor: 1
4162
4207
4163 Rev: 0
4208 Rev: 0
4164 Ancestor: 0
4209 Ancestor: 0
4165
4210
4166 $ hg log --template '{revset("TIP"|lower)}\n' -l1
4211 $ hg log --template '{revset("TIP"|lower)}\n' -l1
4167 2
4212 2
4168
4213
4169 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
4214 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
4170 2
4215 2
4171
4216
4172 a list template is evaluated for each item of revset/parents
4217 a list template is evaluated for each item of revset/parents
4173
4218
4174 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
4219 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
4175 2 p: 1:bcc7ff960b8e
4220 2 p: 1:bcc7ff960b8e
4176 1 p: 0:f7769ec2ab97
4221 1 p: 0:f7769ec2ab97
4177 0 p:
4222 0 p:
4178
4223
4179 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
4224 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
4180 2 p: 1:bcc7ff960b8e -1:000000000000
4225 2 p: 1:bcc7ff960b8e -1:000000000000
4181 1 p: 0:f7769ec2ab97 -1:000000000000
4226 1 p: 0:f7769ec2ab97 -1:000000000000
4182 0 p: -1:000000000000 -1:000000000000
4227 0 p: -1:000000000000 -1:000000000000
4183
4228
4184 therefore, 'revcache' should be recreated for each rev
4229 therefore, 'revcache' should be recreated for each rev
4185
4230
4186 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
4231 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
4187 2 aa b
4232 2 aa b
4188 p
4233 p
4189 1
4234 1
4190 p a
4235 p a
4191 0 a
4236 0 a
4192 p
4237 p
4193
4238
4194 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
4239 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
4195 2 aa b
4240 2 aa b
4196 p
4241 p
4197 1
4242 1
4198 p a
4243 p a
4199 0 a
4244 0 a
4200 p
4245 p
4201
4246
4202 a revset item must be evaluated as an integer revision, not an offset from tip
4247 a revset item must be evaluated as an integer revision, not an offset from tip
4203
4248
4204 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
4249 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
4205 -1:000000000000
4250 -1:000000000000
4206 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
4251 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
4207 -1:000000000000
4252 -1:000000000000
4208
4253
4209 join() should pick '{rev}' from revset items:
4254 join() should pick '{rev}' from revset items:
4210
4255
4211 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
4256 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
4212 4, 5
4257 4, 5
4213
4258
4214 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
4259 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
4215 default. join() should agree with the default formatting:
4260 default. join() should agree with the default formatting:
4216
4261
4217 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
4262 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
4218 5:13207e5a10d9, 4:bbe44766e73d
4263 5:13207e5a10d9, 4:bbe44766e73d
4219
4264
4220 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
4265 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
4221 5:13207e5a10d9fd28ec424934298e176197f2c67f,
4266 5:13207e5a10d9fd28ec424934298e176197f2c67f,
4222 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
4267 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
4223
4268
4224 Invalid arguments passed to revset()
4269 Invalid arguments passed to revset()
4225
4270
4226 $ hg log -T '{revset("%whatever", 0)}\n'
4271 $ hg log -T '{revset("%whatever", 0)}\n'
4227 hg: parse error: unexpected revspec format character w
4272 hg: parse error: unexpected revspec format character w
4228 [255]
4273 [255]
4229 $ hg log -T '{revset("%lwhatever", files)}\n'
4274 $ hg log -T '{revset("%lwhatever", files)}\n'
4230 hg: parse error: unexpected revspec format character w
4275 hg: parse error: unexpected revspec format character w
4231 [255]
4276 [255]
4232 $ hg log -T '{revset("%s %s", 0)}\n'
4277 $ hg log -T '{revset("%s %s", 0)}\n'
4233 hg: parse error: missing argument for revspec
4278 hg: parse error: missing argument for revspec
4234 [255]
4279 [255]
4235 $ hg log -T '{revset("", 0)}\n'
4280 $ hg log -T '{revset("", 0)}\n'
4236 hg: parse error: too many revspec arguments specified
4281 hg: parse error: too many revspec arguments specified
4237 [255]
4282 [255]
4238 $ hg log -T '{revset("%s", 0, 1)}\n'
4283 $ hg log -T '{revset("%s", 0, 1)}\n'
4239 hg: parse error: too many revspec arguments specified
4284 hg: parse error: too many revspec arguments specified
4240 [255]
4285 [255]
4241 $ hg log -T '{revset("%", 0)}\n'
4286 $ hg log -T '{revset("%", 0)}\n'
4242 hg: parse error: incomplete revspec format character
4287 hg: parse error: incomplete revspec format character
4243 [255]
4288 [255]
4244 $ hg log -T '{revset("%l", 0)}\n'
4289 $ hg log -T '{revset("%l", 0)}\n'
4245 hg: parse error: incomplete revspec format character
4290 hg: parse error: incomplete revspec format character
4246 [255]
4291 [255]
4247 $ hg log -T '{revset("%d", 'foo')}\n'
4292 $ hg log -T '{revset("%d", 'foo')}\n'
4248 hg: parse error: invalid argument for revspec
4293 hg: parse error: invalid argument for revspec
4249 [255]
4294 [255]
4250 $ hg log -T '{revset("%ld", files)}\n'
4295 $ hg log -T '{revset("%ld", files)}\n'
4251 hg: parse error: invalid argument for revspec
4296 hg: parse error: invalid argument for revspec
4252 [255]
4297 [255]
4253 $ hg log -T '{revset("%ls", 0)}\n'
4298 $ hg log -T '{revset("%ls", 0)}\n'
4254 hg: parse error: invalid argument for revspec
4299 hg: parse error: invalid argument for revspec
4255 [255]
4300 [255]
4256 $ hg log -T '{revset("%b", 'foo')}\n'
4301 $ hg log -T '{revset("%b", 'foo')}\n'
4257 hg: parse error: invalid argument for revspec
4302 hg: parse error: invalid argument for revspec
4258 [255]
4303 [255]
4259 $ hg log -T '{revset("%lb", files)}\n'
4304 $ hg log -T '{revset("%lb", files)}\n'
4260 hg: parse error: invalid argument for revspec
4305 hg: parse error: invalid argument for revspec
4261 [255]
4306 [255]
4262 $ hg log -T '{revset("%r", 0)}\n'
4307 $ hg log -T '{revset("%r", 0)}\n'
4263 hg: parse error: invalid argument for revspec
4308 hg: parse error: invalid argument for revspec
4264 [255]
4309 [255]
4265
4310
4266 Test 'originalnode'
4311 Test 'originalnode'
4267
4312
4268 $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n'
4313 $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n'
4269 000000000000 bcc7ff960b8e
4314 000000000000 bcc7ff960b8e
4270 $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n'
4315 $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n'
4271 a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1
4316 a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1
4272
4317
4273 Test files function
4318 Test files function
4274
4319
4275 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
4320 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
4276 2
4321 2
4277 a
4322 a
4278 aa
4323 aa
4279 b
4324 b
4280 1
4325 1
4281 a
4326 a
4282 0
4327 0
4283 a
4328 a
4284
4329
4285 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
4330 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
4286 2
4331 2
4287 aa
4332 aa
4288 1
4333 1
4289
4334
4290 0
4335 0
4291
4336
4292
4337
4293 Test relpath function
4338 Test relpath function
4294
4339
4295 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4340 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4296 a
4341 a
4297 $ cd ..
4342 $ cd ..
4298 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4343 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4299 r/a
4344 r/a
4300 $ cd r
4345 $ cd r
4301
4346
4302 Test active bookmark templating
4347 Test active bookmark templating
4303
4348
4304 $ hg book foo
4349 $ hg book foo
4305 $ hg book bar
4350 $ hg book bar
4306 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4351 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4307 2 bar* foo
4352 2 bar* foo
4308 1
4353 1
4309 0
4354 0
4310 $ hg log --template "{rev} {activebookmark}\n"
4355 $ hg log --template "{rev} {activebookmark}\n"
4311 2 bar
4356 2 bar
4312 1
4357 1
4313 0
4358 0
4314 $ hg bookmarks --inactive bar
4359 $ hg bookmarks --inactive bar
4315 $ hg log --template "{rev} {activebookmark}\n"
4360 $ hg log --template "{rev} {activebookmark}\n"
4316 2
4361 2
4317 1
4362 1
4318 0
4363 0
4319 $ hg book -r1 baz
4364 $ hg book -r1 baz
4320 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4365 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4321 2 bar foo
4366 2 bar foo
4322 1 baz
4367 1 baz
4323 0
4368 0
4324 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4369 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4325 2 t
4370 2 t
4326 1 f
4371 1 f
4327 0 f
4372 0 f
4328
4373
4329 Test namespaces dict
4374 Test namespaces dict
4330
4375
4331 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4376 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4332 2
4377 2
4333 bookmarks color=bookmark builtin=True
4378 bookmarks color=bookmark builtin=True
4334 bar,foo
4379 bar,foo
4335 tags color=tag builtin=True
4380 tags color=tag builtin=True
4336 tip
4381 tip
4337 branches color=branch builtin=True
4382 branches color=branch builtin=True
4338 text.{rev}
4383 text.{rev}
4339 revnames color=revname builtin=False
4384 revnames color=revname builtin=False
4340 r2
4385 r2
4341
4386
4342 1
4387 1
4343 bookmarks color=bookmark builtin=True
4388 bookmarks color=bookmark builtin=True
4344 baz
4389 baz
4345 tags color=tag builtin=True
4390 tags color=tag builtin=True
4346
4391
4347 branches color=branch builtin=True
4392 branches color=branch builtin=True
4348 text.{rev}
4393 text.{rev}
4349 revnames color=revname builtin=False
4394 revnames color=revname builtin=False
4350 r1
4395 r1
4351
4396
4352 0
4397 0
4353 bookmarks color=bookmark builtin=True
4398 bookmarks color=bookmark builtin=True
4354
4399
4355 tags color=tag builtin=True
4400 tags color=tag builtin=True
4356
4401
4357 branches color=branch builtin=True
4402 branches color=branch builtin=True
4358 default
4403 default
4359 revnames color=revname builtin=False
4404 revnames color=revname builtin=False
4360 r0
4405 r0
4361
4406
4362 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4407 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4363 bookmarks: bar foo
4408 bookmarks: bar foo
4364 tags: tip
4409 tags: tip
4365 branches: text.{rev}
4410 branches: text.{rev}
4366 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4411 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4367 bookmarks:
4412 bookmarks:
4368 bar
4413 bar
4369 foo
4414 foo
4370 tags:
4415 tags:
4371 tip
4416 tip
4372 branches:
4417 branches:
4373 text.{rev}
4418 text.{rev}
4374 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4419 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4375 bar
4420 bar
4376 foo
4421 foo
4377 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
4422 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
4378 bar
4423 bar
4379 foo
4424 foo
4380
4425
4381 Test stringify on sub expressions
4426 Test stringify on sub expressions
4382
4427
4383 $ cd ..
4428 $ cd ..
4384 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4429 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4385 fourth, second, third
4430 fourth, second, third
4386 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4431 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4387 abc
4432 abc
4388
4433
4389 Test splitlines
4434 Test splitlines
4390
4435
4391 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4436 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4392 @ foo Modify, add, remove, rename
4437 @ foo Modify, add, remove, rename
4393 |
4438 |
4394 o foo future
4439 o foo future
4395 |
4440 |
4396 o foo third
4441 o foo third
4397 |
4442 |
4398 o foo second
4443 o foo second
4399
4444
4400 o foo merge
4445 o foo merge
4401 |\
4446 |\
4402 | o foo new head
4447 | o foo new head
4403 | |
4448 | |
4404 o | foo new branch
4449 o | foo new branch
4405 |/
4450 |/
4406 o foo no user, no domain
4451 o foo no user, no domain
4407 |
4452 |
4408 o foo no person
4453 o foo no person
4409 |
4454 |
4410 o foo other 1
4455 o foo other 1
4411 | foo other 2
4456 | foo other 2
4412 | foo
4457 | foo
4413 | foo other 3
4458 | foo other 3
4414 o foo line 1
4459 o foo line 1
4415 foo line 2
4460 foo line 2
4416
4461
4417 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4462 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4418 line 1 line 2
4463 line 1 line 2
4419 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4464 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4420 line 1|line 2
4465 line 1|line 2
4421
4466
4422 Test startswith
4467 Test startswith
4423 $ hg log -Gv -R a --template "{startswith(desc)}"
4468 $ hg log -Gv -R a --template "{startswith(desc)}"
4424 hg: parse error: startswith expects two arguments
4469 hg: parse error: startswith expects two arguments
4425 [255]
4470 [255]
4426
4471
4427 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4472 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4428 @
4473 @
4429 |
4474 |
4430 o
4475 o
4431 |
4476 |
4432 o
4477 o
4433 |
4478 |
4434 o
4479 o
4435
4480
4436 o
4481 o
4437 |\
4482 |\
4438 | o
4483 | o
4439 | |
4484 | |
4440 o |
4485 o |
4441 |/
4486 |/
4442 o
4487 o
4443 |
4488 |
4444 o
4489 o
4445 |
4490 |
4446 o
4491 o
4447 |
4492 |
4448 o line 1
4493 o line 1
4449 line 2
4494 line 2
4450
4495
4451 Test bad template with better error message
4496 Test bad template with better error message
4452
4497
4453 $ hg log -Gv -R a --template '{desc|user()}'
4498 $ hg log -Gv -R a --template '{desc|user()}'
4454 hg: parse error: expected a symbol, got 'func'
4499 hg: parse error: expected a symbol, got 'func'
4455 [255]
4500 [255]
4456
4501
4457 Test word function (including index out of bounds graceful failure)
4502 Test word function (including index out of bounds graceful failure)
4458
4503
4459 $ hg log -Gv -R a --template "{word('1', desc)}"
4504 $ hg log -Gv -R a --template "{word('1', desc)}"
4460 @ add,
4505 @ add,
4461 |
4506 |
4462 o
4507 o
4463 |
4508 |
4464 o
4509 o
4465 |
4510 |
4466 o
4511 o
4467
4512
4468 o
4513 o
4469 |\
4514 |\
4470 | o head
4515 | o head
4471 | |
4516 | |
4472 o | branch
4517 o | branch
4473 |/
4518 |/
4474 o user,
4519 o user,
4475 |
4520 |
4476 o person
4521 o person
4477 |
4522 |
4478 o 1
4523 o 1
4479 |
4524 |
4480 o 1
4525 o 1
4481
4526
4482
4527
4483 Test word third parameter used as splitter
4528 Test word third parameter used as splitter
4484
4529
4485 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4530 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4486 @ M
4531 @ M
4487 |
4532 |
4488 o future
4533 o future
4489 |
4534 |
4490 o third
4535 o third
4491 |
4536 |
4492 o sec
4537 o sec
4493
4538
4494 o merge
4539 o merge
4495 |\
4540 |\
4496 | o new head
4541 | o new head
4497 | |
4542 | |
4498 o | new branch
4543 o | new branch
4499 |/
4544 |/
4500 o n
4545 o n
4501 |
4546 |
4502 o n
4547 o n
4503 |
4548 |
4504 o
4549 o
4505 |
4550 |
4506 o line 1
4551 o line 1
4507 line 2
4552 line 2
4508
4553
4509 Test word error messages for not enough and too many arguments
4554 Test word error messages for not enough and too many arguments
4510
4555
4511 $ hg log -Gv -R a --template "{word('0')}"
4556 $ hg log -Gv -R a --template "{word('0')}"
4512 hg: parse error: word expects two or three arguments, got 1
4557 hg: parse error: word expects two or three arguments, got 1
4513 [255]
4558 [255]
4514
4559
4515 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4560 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4516 hg: parse error: word expects two or three arguments, got 7
4561 hg: parse error: word expects two or three arguments, got 7
4517 [255]
4562 [255]
4518
4563
4519 Test word for integer literal
4564 Test word for integer literal
4520
4565
4521 $ hg log -R a --template "{word(2, desc)}\n" -r0
4566 $ hg log -R a --template "{word(2, desc)}\n" -r0
4522 line
4567 line
4523
4568
4524 Test word for invalid numbers
4569 Test word for invalid numbers
4525
4570
4526 $ hg log -Gv -R a --template "{word('a', desc)}"
4571 $ hg log -Gv -R a --template "{word('a', desc)}"
4527 hg: parse error: word expects an integer index
4572 hg: parse error: word expects an integer index
4528 [255]
4573 [255]
4529
4574
4530 Test word for out of range
4575 Test word for out of range
4531
4576
4532 $ hg log -R a --template "{word(10000, desc)}"
4577 $ hg log -R a --template "{word(10000, desc)}"
4533 $ hg log -R a --template "{word(-10000, desc)}"
4578 $ hg log -R a --template "{word(-10000, desc)}"
4534
4579
4535 Test indent and not adding to empty lines
4580 Test indent and not adding to empty lines
4536
4581
4537 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4582 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4538 -----
4583 -----
4539 > line 1
4584 > line 1
4540 >> line 2
4585 >> line 2
4541 -----
4586 -----
4542 > other 1
4587 > other 1
4543 >> other 2
4588 >> other 2
4544
4589
4545 >> other 3
4590 >> other 3
4546
4591
4547 Test with non-strings like dates
4592 Test with non-strings like dates
4548
4593
4549 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4594 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4550 1200000.00
4595 1200000.00
4551 1300000.00
4596 1300000.00
4552
4597
4553 Test broken string escapes:
4598 Test broken string escapes:
4554
4599
4555 $ hg log -T "bogus\\" -R a
4600 $ hg log -T "bogus\\" -R a
4556 hg: parse error: trailing \ in string
4601 hg: parse error: trailing \ in string
4557 [255]
4602 [255]
4558 $ hg log -T "\\xy" -R a
4603 $ hg log -T "\\xy" -R a
4559 hg: parse error: invalid \x escape* (glob)
4604 hg: parse error: invalid \x escape* (glob)
4560 [255]
4605 [255]
4561
4606
4562 json filter should escape HTML tags so that the output can be embedded in hgweb:
4607 json filter should escape HTML tags so that the output can be embedded in hgweb:
4563
4608
4564 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4609 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4565 "\u003cfoo@example.org\u003e"
4610 "\u003cfoo@example.org\u003e"
4566
4611
4567 Templater supports aliases of symbol and func() styles:
4612 Templater supports aliases of symbol and func() styles:
4568
4613
4569 $ hg clone -q a aliases
4614 $ hg clone -q a aliases
4570 $ cd aliases
4615 $ cd aliases
4571 $ cat <<EOF >> .hg/hgrc
4616 $ cat <<EOF >> .hg/hgrc
4572 > [templatealias]
4617 > [templatealias]
4573 > r = rev
4618 > r = rev
4574 > rn = "{r}:{node|short}"
4619 > rn = "{r}:{node|short}"
4575 > status(c, files) = files % "{c} {file}\n"
4620 > status(c, files) = files % "{c} {file}\n"
4576 > utcdate(d) = localdate(d, "UTC")
4621 > utcdate(d) = localdate(d, "UTC")
4577 > EOF
4622 > EOF
4578
4623
4579 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4624 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4580 (template
4625 (template
4581 (symbol 'rn')
4626 (symbol 'rn')
4582 (string ' ')
4627 (string ' ')
4583 (|
4628 (|
4584 (func
4629 (func
4585 (symbol 'utcdate')
4630 (symbol 'utcdate')
4586 (symbol 'date'))
4631 (symbol 'date'))
4587 (symbol 'isodate'))
4632 (symbol 'isodate'))
4588 (string '\n'))
4633 (string '\n'))
4589 * expanded:
4634 * expanded:
4590 (template
4635 (template
4591 (template
4636 (template
4592 (symbol 'rev')
4637 (symbol 'rev')
4593 (string ':')
4638 (string ':')
4594 (|
4639 (|
4595 (symbol 'node')
4640 (symbol 'node')
4596 (symbol 'short')))
4641 (symbol 'short')))
4597 (string ' ')
4642 (string ' ')
4598 (|
4643 (|
4599 (func
4644 (func
4600 (symbol 'localdate')
4645 (symbol 'localdate')
4601 (list
4646 (list
4602 (symbol 'date')
4647 (symbol 'date')
4603 (string 'UTC')))
4648 (string 'UTC')))
4604 (symbol 'isodate'))
4649 (symbol 'isodate'))
4605 (string '\n'))
4650 (string '\n'))
4606 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4651 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4607
4652
4608 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4653 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4609 (template
4654 (template
4610 (func
4655 (func
4611 (symbol 'status')
4656 (symbol 'status')
4612 (list
4657 (list
4613 (string 'A')
4658 (string 'A')
4614 (symbol 'file_adds'))))
4659 (symbol 'file_adds'))))
4615 * expanded:
4660 * expanded:
4616 (template
4661 (template
4617 (%
4662 (%
4618 (symbol 'file_adds')
4663 (symbol 'file_adds')
4619 (template
4664 (template
4620 (string 'A')
4665 (string 'A')
4621 (string ' ')
4666 (string ' ')
4622 (symbol 'file')
4667 (symbol 'file')
4623 (string '\n'))))
4668 (string '\n'))))
4624 A a
4669 A a
4625
4670
4626 A unary function alias can be called as a filter:
4671 A unary function alias can be called as a filter:
4627
4672
4628 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4673 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4629 (template
4674 (template
4630 (|
4675 (|
4631 (|
4676 (|
4632 (symbol 'date')
4677 (symbol 'date')
4633 (symbol 'utcdate'))
4678 (symbol 'utcdate'))
4634 (symbol 'isodate'))
4679 (symbol 'isodate'))
4635 (string '\n'))
4680 (string '\n'))
4636 * expanded:
4681 * expanded:
4637 (template
4682 (template
4638 (|
4683 (|
4639 (func
4684 (func
4640 (symbol 'localdate')
4685 (symbol 'localdate')
4641 (list
4686 (list
4642 (symbol 'date')
4687 (symbol 'date')
4643 (string 'UTC')))
4688 (string 'UTC')))
4644 (symbol 'isodate'))
4689 (symbol 'isodate'))
4645 (string '\n'))
4690 (string '\n'))
4646 1970-01-12 13:46 +0000
4691 1970-01-12 13:46 +0000
4647
4692
4648 Aliases should be applied only to command arguments and templates in hgrc.
4693 Aliases should be applied only to command arguments and templates in hgrc.
4649 Otherwise, our stock styles and web templates could be corrupted:
4694 Otherwise, our stock styles and web templates could be corrupted:
4650
4695
4651 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4696 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4652 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4697 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4653
4698
4654 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4699 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4655 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4700 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4656
4701
4657 $ cat <<EOF > tmpl
4702 $ cat <<EOF > tmpl
4658 > changeset = 'nothing expanded:{rn}\n'
4703 > changeset = 'nothing expanded:{rn}\n'
4659 > EOF
4704 > EOF
4660 $ hg log -r0 --style ./tmpl
4705 $ hg log -r0 --style ./tmpl
4661 nothing expanded:
4706 nothing expanded:
4662
4707
4663 Aliases in formatter:
4708 Aliases in formatter:
4664
4709
4665 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4710 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4666 default 6:d41e714fe50d
4711 default 6:d41e714fe50d
4667 foo 4:bbe44766e73d
4712 foo 4:bbe44766e73d
4668
4713
4669 Aliases should honor HGPLAIN:
4714 Aliases should honor HGPLAIN:
4670
4715
4671 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4716 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4672 nothing expanded:
4717 nothing expanded:
4673 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4718 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4674 0:1e4e1b8f71e0
4719 0:1e4e1b8f71e0
4675
4720
4676 Unparsable alias:
4721 Unparsable alias:
4677
4722
4678 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4723 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4679 (template
4724 (template
4680 (symbol 'bad'))
4725 (symbol 'bad'))
4681 abort: bad definition of template alias "bad": at 2: not a prefix: end
4726 abort: bad definition of template alias "bad": at 2: not a prefix: end
4682 [255]
4727 [255]
4683 $ hg log --config templatealias.bad='x(' -T '{bad}'
4728 $ hg log --config templatealias.bad='x(' -T '{bad}'
4684 abort: bad definition of template alias "bad": at 2: not a prefix: end
4729 abort: bad definition of template alias "bad": at 2: not a prefix: end
4685 [255]
4730 [255]
4686
4731
4687 $ cd ..
4732 $ cd ..
4688
4733
4689 Set up repository for non-ascii encoding tests:
4734 Set up repository for non-ascii encoding tests:
4690
4735
4691 $ hg init nonascii
4736 $ hg init nonascii
4692 $ cd nonascii
4737 $ cd nonascii
4693 $ $PYTHON <<EOF
4738 $ $PYTHON <<EOF
4694 > open('latin1', 'wb').write(b'\xe9')
4739 > open('latin1', 'wb').write(b'\xe9')
4695 > open('utf-8', 'wb').write(b'\xc3\xa9')
4740 > open('utf-8', 'wb').write(b'\xc3\xa9')
4696 > EOF
4741 > EOF
4697 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4742 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4698 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4743 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4699
4744
4700 json filter should try round-trip conversion to utf-8:
4745 json filter should try round-trip conversion to utf-8:
4701
4746
4702 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4747 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4703 "\u00e9"
4748 "\u00e9"
4704 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4749 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4705 "non-ascii branch: \u00e9"
4750 "non-ascii branch: \u00e9"
4706
4751
4707 json filter should take input as utf-8 if it was converted from utf-8:
4752 json filter should take input as utf-8 if it was converted from utf-8:
4708
4753
4709 $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
4754 $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
4710 "\u00e9"
4755 "\u00e9"
4711 $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
4756 $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
4712 "non-ascii branch: \u00e9"
4757 "non-ascii branch: \u00e9"
4713
4758
4714 json filter takes input as utf-8b:
4759 json filter takes input as utf-8b:
4715
4760
4716 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4761 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4717 "\u00e9"
4762 "\u00e9"
4718 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4763 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4719 "\udce9"
4764 "\udce9"
4720
4765
4721 utf8 filter:
4766 utf8 filter:
4722
4767
4723 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4768 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4724 round-trip: c3a9
4769 round-trip: c3a9
4725 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4770 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4726 decoded: c3a9
4771 decoded: c3a9
4727 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4772 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4728 abort: decoding near * (glob)
4773 abort: decoding near * (glob)
4729 [255]
4774 [255]
4730 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
4775 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
4731 coerced to string: 0
4776 coerced to string: 0
4732
4777
4733 pad width:
4778 pad width:
4734
4779
4735 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4780 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4736 \xc3\xa9- (esc)
4781 \xc3\xa9- (esc)
4737
4782
4738 $ cd ..
4783 $ cd ..
4739
4784
4740 Test that template function in extension is registered as expected
4785 Test that template function in extension is registered as expected
4741
4786
4742 $ cd a
4787 $ cd a
4743
4788
4744 $ cat <<EOF > $TESTTMP/customfunc.py
4789 $ cat <<EOF > $TESTTMP/customfunc.py
4745 > from mercurial import registrar
4790 > from mercurial import registrar
4746 >
4791 >
4747 > templatefunc = registrar.templatefunc()
4792 > templatefunc = registrar.templatefunc()
4748 >
4793 >
4749 > @templatefunc(b'custom()')
4794 > @templatefunc(b'custom()')
4750 > def custom(context, mapping, args):
4795 > def custom(context, mapping, args):
4751 > return b'custom'
4796 > return b'custom'
4752 > EOF
4797 > EOF
4753 $ cat <<EOF > .hg/hgrc
4798 $ cat <<EOF > .hg/hgrc
4754 > [extensions]
4799 > [extensions]
4755 > customfunc = $TESTTMP/customfunc.py
4800 > customfunc = $TESTTMP/customfunc.py
4756 > EOF
4801 > EOF
4757
4802
4758 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4803 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4759 custom
4804 custom
4760
4805
4761 $ cd ..
4806 $ cd ..
4762
4807
4763 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4808 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4764 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4809 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4765 columns. We don't care about other aspects of the graph rendering here.
4810 columns. We don't care about other aspects of the graph rendering here.
4766
4811
4767 $ hg init graphwidth
4812 $ hg init graphwidth
4768 $ cd graphwidth
4813 $ cd graphwidth
4769
4814
4770 $ wrappabletext="a a a a a a a a a a a a"
4815 $ wrappabletext="a a a a a a a a a a a a"
4771
4816
4772 $ printf "first\n" > file
4817 $ printf "first\n" > file
4773 $ hg add file
4818 $ hg add file
4774 $ hg commit -m "$wrappabletext"
4819 $ hg commit -m "$wrappabletext"
4775
4820
4776 $ printf "first\nsecond\n" > file
4821 $ printf "first\nsecond\n" > file
4777 $ hg commit -m "$wrappabletext"
4822 $ hg commit -m "$wrappabletext"
4778
4823
4779 $ hg checkout 0
4824 $ hg checkout 0
4780 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4825 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4781 $ printf "third\nfirst\n" > file
4826 $ printf "third\nfirst\n" > file
4782 $ hg commit -m "$wrappabletext"
4827 $ hg commit -m "$wrappabletext"
4783 created new head
4828 created new head
4784
4829
4785 $ hg merge
4830 $ hg merge
4786 merging file
4831 merging file
4787 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4832 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4788 (branch merge, don't forget to commit)
4833 (branch merge, don't forget to commit)
4789
4834
4790 $ hg log --graph -T "{graphwidth}"
4835 $ hg log --graph -T "{graphwidth}"
4791 @ 3
4836 @ 3
4792 |
4837 |
4793 | @ 5
4838 | @ 5
4794 |/
4839 |/
4795 o 3
4840 o 3
4796
4841
4797 $ hg commit -m "$wrappabletext"
4842 $ hg commit -m "$wrappabletext"
4798
4843
4799 $ hg log --graph -T "{graphwidth}"
4844 $ hg log --graph -T "{graphwidth}"
4800 @ 5
4845 @ 5
4801 |\
4846 |\
4802 | o 5
4847 | o 5
4803 | |
4848 | |
4804 o | 5
4849 o | 5
4805 |/
4850 |/
4806 o 3
4851 o 3
4807
4852
4808
4853
4809 $ hg checkout 0
4854 $ hg checkout 0
4810 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4855 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4811 $ printf "third\nfirst\nsecond\n" > file
4856 $ printf "third\nfirst\nsecond\n" > file
4812 $ hg commit -m "$wrappabletext"
4857 $ hg commit -m "$wrappabletext"
4813 created new head
4858 created new head
4814
4859
4815 $ hg log --graph -T "{graphwidth}"
4860 $ hg log --graph -T "{graphwidth}"
4816 @ 3
4861 @ 3
4817 |
4862 |
4818 | o 7
4863 | o 7
4819 | |\
4864 | |\
4820 +---o 7
4865 +---o 7
4821 | |
4866 | |
4822 | o 5
4867 | o 5
4823 |/
4868 |/
4824 o 3
4869 o 3
4825
4870
4826
4871
4827 $ hg log --graph -T "{graphwidth}" -r 3
4872 $ hg log --graph -T "{graphwidth}" -r 3
4828 o 5
4873 o 5
4829 |\
4874 |\
4830 ~ ~
4875 ~ ~
4831
4876
4832 $ hg log --graph -T "{graphwidth}" -r 1
4877 $ hg log --graph -T "{graphwidth}" -r 1
4833 o 3
4878 o 3
4834 |
4879 |
4835 ~
4880 ~
4836
4881
4837 $ hg merge
4882 $ hg merge
4838 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4883 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4839 (branch merge, don't forget to commit)
4884 (branch merge, don't forget to commit)
4840 $ hg commit -m "$wrappabletext"
4885 $ hg commit -m "$wrappabletext"
4841
4886
4842 $ printf "seventh\n" >> file
4887 $ printf "seventh\n" >> file
4843 $ hg commit -m "$wrappabletext"
4888 $ hg commit -m "$wrappabletext"
4844
4889
4845 $ hg log --graph -T "{graphwidth}"
4890 $ hg log --graph -T "{graphwidth}"
4846 @ 3
4891 @ 3
4847 |
4892 |
4848 o 5
4893 o 5
4849 |\
4894 |\
4850 | o 5
4895 | o 5
4851 | |
4896 | |
4852 o | 7
4897 o | 7
4853 |\ \
4898 |\ \
4854 | o | 7
4899 | o | 7
4855 | |/
4900 | |/
4856 o / 5
4901 o / 5
4857 |/
4902 |/
4858 o 3
4903 o 3
4859
4904
4860
4905
4861 The point of graphwidth is to allow wrapping that accounts for the space taken
4906 The point of graphwidth is to allow wrapping that accounts for the space taken
4862 by the graph.
4907 by the graph.
4863
4908
4864 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4909 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4865 @ a a a a
4910 @ a a a a
4866 | a a a a
4911 | a a a a
4867 | a a a a
4912 | a a a a
4868 o a a a
4913 o a a a
4869 |\ a a a
4914 |\ a a a
4870 | | a a a
4915 | | a a a
4871 | | a a a
4916 | | a a a
4872 | o a a a
4917 | o a a a
4873 | | a a a
4918 | | a a a
4874 | | a a a
4919 | | a a a
4875 | | a a a
4920 | | a a a
4876 o | a a
4921 o | a a
4877 |\ \ a a
4922 |\ \ a a
4878 | | | a a
4923 | | | a a
4879 | | | a a
4924 | | | a a
4880 | | | a a
4925 | | | a a
4881 | | | a a
4926 | | | a a
4882 | o | a a
4927 | o | a a
4883 | |/ a a
4928 | |/ a a
4884 | | a a
4929 | | a a
4885 | | a a
4930 | | a a
4886 | | a a
4931 | | a a
4887 | | a a
4932 | | a a
4888 o | a a a
4933 o | a a a
4889 |/ a a a
4934 |/ a a a
4890 | a a a
4935 | a a a
4891 | a a a
4936 | a a a
4892 o a a a a
4937 o a a a a
4893 a a a a
4938 a a a a
4894 a a a a
4939 a a a a
4895
4940
4896 Something tricky happens when there are elided nodes; the next drawn row of
4941 Something tricky happens when there are elided nodes; the next drawn row of
4897 edges can be more than one column wider, but the graph width only increases by
4942 edges can be more than one column wider, but the graph width only increases by
4898 one column. The remaining columns are added in between the nodes.
4943 one column. The remaining columns are added in between the nodes.
4899
4944
4900 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4945 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4901 o 5
4946 o 5
4902 |\
4947 |\
4903 | \
4948 | \
4904 | :\
4949 | :\
4905 o : : 7
4950 o : : 7
4906 :/ /
4951 :/ /
4907 : o 5
4952 : o 5
4908 :/
4953 :/
4909 o 3
4954 o 3
4910
4955
4911
4956
4912 $ cd ..
4957 $ cd ..
4913
4958
General Comments 0
You need to be logged in to leave comments. Login now