##// END OF EJS Templates
templater: wrap get/min/max result so map operation can apply to element...
Yuya Nishihara -
r34535:b3073e17 default
parent child Browse files
Show More
@@ -1,872 +1,891 b''
1 # templatekw.py - common changeset template keywords
1 # templatekw.py - common changeset template keywords
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2009 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 from .i18n import _
10 from .i18n import _
11 from .node import (
11 from .node import (
12 hex,
12 hex,
13 nullid,
13 nullid,
14 )
14 )
15
15
16 from . import (
16 from . import (
17 encoding,
17 encoding,
18 error,
18 error,
19 hbisect,
19 hbisect,
20 obsutil,
20 obsutil,
21 patch,
21 patch,
22 pycompat,
22 pycompat,
23 registrar,
23 registrar,
24 scmutil,
24 scmutil,
25 util,
25 util,
26 )
26 )
27
27
28 class _hybrid(object):
28 class _hybrid(object):
29 """Wrapper for list or dict to support legacy template
29 """Wrapper for list or dict to support legacy template
30
30
31 This class allows us to handle both:
31 This class allows us to handle both:
32 - "{files}" (legacy command-line-specific list hack) and
32 - "{files}" (legacy command-line-specific list hack) and
33 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
33 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
34 and to access raw values:
34 and to access raw values:
35 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
35 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
36 - "{get(extras, key)}"
36 - "{get(extras, key)}"
37 - "{files|json}"
37 - "{files|json}"
38 """
38 """
39
39
40 def __init__(self, gen, values, makemap, joinfmt):
40 def __init__(self, gen, values, makemap, joinfmt):
41 if gen is not None:
41 if gen is not None:
42 self.gen = gen # generator or function returning generator
42 self.gen = gen # generator or function returning generator
43 self._values = values
43 self._values = values
44 self._makemap = makemap
44 self._makemap = makemap
45 self.joinfmt = joinfmt
45 self.joinfmt = joinfmt
46 def gen(self):
46 def gen(self):
47 """Default generator to stringify this as {join(self, ' ')}"""
47 """Default generator to stringify this as {join(self, ' ')}"""
48 for i, x in enumerate(self._values):
48 for i, x in enumerate(self._values):
49 if i > 0:
49 if i > 0:
50 yield ' '
50 yield ' '
51 yield self.joinfmt(x)
51 yield self.joinfmt(x)
52 def itermaps(self):
52 def itermaps(self):
53 makemap = self._makemap
53 makemap = self._makemap
54 for x in self._values:
54 for x in self._values:
55 yield makemap(x)
55 yield makemap(x)
56 def __contains__(self, x):
56 def __contains__(self, x):
57 return x in self._values
57 return x in self._values
58 def __getitem__(self, key):
58 def __getitem__(self, key):
59 return self._values[key]
59 return self._values[key]
60 def __len__(self):
60 def __len__(self):
61 return len(self._values)
61 return len(self._values)
62 def __iter__(self):
62 def __iter__(self):
63 return iter(self._values)
63 return iter(self._values)
64 def __getattr__(self, name):
64 def __getattr__(self, name):
65 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
65 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
66 'keys', 'values'):
66 'keys', 'values'):
67 raise AttributeError(name)
67 raise AttributeError(name)
68 return getattr(self._values, name)
68 return getattr(self._values, name)
69
69
70 class _mappable(object):
70 class _mappable(object):
71 """Wrapper for non-list/dict object to support map operation
71 """Wrapper for non-list/dict object to support map operation
72
72
73 This class allows us to handle both:
73 This class allows us to handle both:
74 - "{manifest}"
74 - "{manifest}"
75 - "{manifest % '{rev}:{node}'}"
75 - "{manifest % '{rev}:{node}'}"
76
76
77 Unlike a _hybrid, this does not simulate the behavior of the underling
77 Unlike a _hybrid, this does not simulate the behavior of the underling
78 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
78 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
79 """
79 """
80
80
81 def __init__(self, gen, value, makemap):
81 def __init__(self, gen, key, value, makemap):
82 self.gen = gen
82 if gen is not None:
83 self.gen = gen # generator or function returning generator
84 self._key = key
83 self._value = value # may be generator of strings
85 self._value = value # may be generator of strings
84 self._makemap = makemap
86 self._makemap = makemap
85
87
88 def gen(self):
89 yield pycompat.bytestr(self._value)
90
86 def tomap(self):
91 def tomap(self):
87 return self._makemap()
92 return self._makemap(self._key)
88
93
89 def itermaps(self):
94 def itermaps(self):
90 yield self.tomap()
95 yield self.tomap()
91
96
92 def hybriddict(data, key='key', value='value', fmt='%s=%s', gen=None):
97 def hybriddict(data, key='key', value='value', fmt='%s=%s', gen=None):
93 """Wrap data to support both dict-like and string-like operations"""
98 """Wrap data to support both dict-like and string-like operations"""
94 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
99 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
95 lambda k: fmt % (k, data[k]))
100 lambda k: fmt % (k, data[k]))
96
101
97 def hybridlist(data, name, fmt='%s', gen=None):
102 def hybridlist(data, name, fmt='%s', gen=None):
98 """Wrap data to support both list-like and string-like operations"""
103 """Wrap data to support both list-like and string-like operations"""
99 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
104 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
100
105
101 def unwraphybrid(thing):
106 def unwraphybrid(thing):
102 """Return an object which can be stringified possibly by using a legacy
107 """Return an object which can be stringified possibly by using a legacy
103 template"""
108 template"""
104 gen = getattr(thing, 'gen', None)
109 gen = getattr(thing, 'gen', None)
105 if gen is None:
110 if gen is None:
106 return thing
111 return thing
107 if callable(gen):
112 if callable(gen):
108 return gen()
113 return gen()
109 return gen
114 return gen
110
115
111 def unwrapvalue(thing):
116 def unwrapvalue(thing):
112 """Move the inner value object out of the wrapper"""
117 """Move the inner value object out of the wrapper"""
113 if not util.safehasattr(thing, '_value'):
118 if not util.safehasattr(thing, '_value'):
114 return thing
119 return thing
115 return thing._value
120 return thing._value
116
121
122 def wraphybridvalue(container, key, value):
123 """Wrap an element of hybrid container to be mappable
124
125 The key is passed to the makemap function of the given container, which
126 should be an item generated by iter(container).
127 """
128 makemap = getattr(container, '_makemap', None)
129 if makemap is None:
130 return value
131 if util.safehasattr(value, '_makemap'):
132 # a nested hybrid list/dict, which has its own way of map operation
133 return value
134 return _mappable(None, key, value, makemap)
135
117 def showdict(name, data, mapping, plural=None, key='key', value='value',
136 def showdict(name, data, mapping, plural=None, key='key', value='value',
118 fmt='%s=%s', separator=' '):
137 fmt='%s=%s', separator=' '):
119 c = [{key: k, value: v} for k, v in data.iteritems()]
138 c = [{key: k, value: v} for k, v in data.iteritems()]
120 f = _showlist(name, c, mapping, plural, separator)
139 f = _showlist(name, c, mapping, plural, separator)
121 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
140 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
122
141
123 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
142 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
124 if not element:
143 if not element:
125 element = name
144 element = name
126 f = _showlist(name, values, mapping, plural, separator)
145 f = _showlist(name, values, mapping, plural, separator)
127 return hybridlist(values, name=element, gen=f)
146 return hybridlist(values, name=element, gen=f)
128
147
129 def _showlist(name, values, mapping, plural=None, separator=' '):
148 def _showlist(name, values, mapping, plural=None, separator=' '):
130 '''expand set of values.
149 '''expand set of values.
131 name is name of key in template map.
150 name is name of key in template map.
132 values is list of strings or dicts.
151 values is list of strings or dicts.
133 plural is plural of name, if not simply name + 's'.
152 plural is plural of name, if not simply name + 's'.
134 separator is used to join values as a string
153 separator is used to join values as a string
135
154
136 expansion works like this, given name 'foo'.
155 expansion works like this, given name 'foo'.
137
156
138 if values is empty, expand 'no_foos'.
157 if values is empty, expand 'no_foos'.
139
158
140 if 'foo' not in template map, return values as a string,
159 if 'foo' not in template map, return values as a string,
141 joined by 'separator'.
160 joined by 'separator'.
142
161
143 expand 'start_foos'.
162 expand 'start_foos'.
144
163
145 for each value, expand 'foo'. if 'last_foo' in template
164 for each value, expand 'foo'. if 'last_foo' in template
146 map, expand it instead of 'foo' for last key.
165 map, expand it instead of 'foo' for last key.
147
166
148 expand 'end_foos'.
167 expand 'end_foos'.
149 '''
168 '''
150 templ = mapping['templ']
169 templ = mapping['templ']
151 strmapping = pycompat.strkwargs(mapping)
170 strmapping = pycompat.strkwargs(mapping)
152 if not plural:
171 if not plural:
153 plural = name + 's'
172 plural = name + 's'
154 if not values:
173 if not values:
155 noname = 'no_' + plural
174 noname = 'no_' + plural
156 if noname in templ:
175 if noname in templ:
157 yield templ(noname, **strmapping)
176 yield templ(noname, **strmapping)
158 return
177 return
159 if name not in templ:
178 if name not in templ:
160 if isinstance(values[0], bytes):
179 if isinstance(values[0], bytes):
161 yield separator.join(values)
180 yield separator.join(values)
162 else:
181 else:
163 for v in values:
182 for v in values:
164 yield dict(v, **strmapping)
183 yield dict(v, **strmapping)
165 return
184 return
166 startname = 'start_' + plural
185 startname = 'start_' + plural
167 if startname in templ:
186 if startname in templ:
168 yield templ(startname, **strmapping)
187 yield templ(startname, **strmapping)
169 vmapping = mapping.copy()
188 vmapping = mapping.copy()
170 def one(v, tag=name):
189 def one(v, tag=name):
171 try:
190 try:
172 vmapping.update(v)
191 vmapping.update(v)
173 except (AttributeError, ValueError):
192 except (AttributeError, ValueError):
174 try:
193 try:
175 for a, b in v:
194 for a, b in v:
176 vmapping[a] = b
195 vmapping[a] = b
177 except ValueError:
196 except ValueError:
178 vmapping[name] = v
197 vmapping[name] = v
179 return templ(tag, **pycompat.strkwargs(vmapping))
198 return templ(tag, **pycompat.strkwargs(vmapping))
180 lastname = 'last_' + name
199 lastname = 'last_' + name
181 if lastname in templ:
200 if lastname in templ:
182 last = values.pop()
201 last = values.pop()
183 else:
202 else:
184 last = None
203 last = None
185 for v in values:
204 for v in values:
186 yield one(v)
205 yield one(v)
187 if last is not None:
206 if last is not None:
188 yield one(last, tag=lastname)
207 yield one(last, tag=lastname)
189 endname = 'end_' + plural
208 endname = 'end_' + plural
190 if endname in templ:
209 if endname in templ:
191 yield templ(endname, **strmapping)
210 yield templ(endname, **strmapping)
192
211
193 def getfiles(repo, ctx, revcache):
212 def getfiles(repo, ctx, revcache):
194 if 'files' not in revcache:
213 if 'files' not in revcache:
195 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
214 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
196 return revcache['files']
215 return revcache['files']
197
216
198 def getlatesttags(repo, ctx, cache, pattern=None):
217 def getlatesttags(repo, ctx, cache, pattern=None):
199 '''return date, distance and name for the latest tag of rev'''
218 '''return date, distance and name for the latest tag of rev'''
200
219
201 cachename = 'latesttags'
220 cachename = 'latesttags'
202 if pattern is not None:
221 if pattern is not None:
203 cachename += '-' + pattern
222 cachename += '-' + pattern
204 match = util.stringmatcher(pattern)[2]
223 match = util.stringmatcher(pattern)[2]
205 else:
224 else:
206 match = util.always
225 match = util.always
207
226
208 if cachename not in cache:
227 if cachename not in cache:
209 # Cache mapping from rev to a tuple with tag date, tag
228 # Cache mapping from rev to a tuple with tag date, tag
210 # distance and tag name
229 # distance and tag name
211 cache[cachename] = {-1: (0, 0, ['null'])}
230 cache[cachename] = {-1: (0, 0, ['null'])}
212 latesttags = cache[cachename]
231 latesttags = cache[cachename]
213
232
214 rev = ctx.rev()
233 rev = ctx.rev()
215 todo = [rev]
234 todo = [rev]
216 while todo:
235 while todo:
217 rev = todo.pop()
236 rev = todo.pop()
218 if rev in latesttags:
237 if rev in latesttags:
219 continue
238 continue
220 ctx = repo[rev]
239 ctx = repo[rev]
221 tags = [t for t in ctx.tags()
240 tags = [t for t in ctx.tags()
222 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
241 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
223 and match(t))]
242 and match(t))]
224 if tags:
243 if tags:
225 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
244 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
226 continue
245 continue
227 try:
246 try:
228 ptags = [latesttags[p.rev()] for p in ctx.parents()]
247 ptags = [latesttags[p.rev()] for p in ctx.parents()]
229 if len(ptags) > 1:
248 if len(ptags) > 1:
230 if ptags[0][2] == ptags[1][2]:
249 if ptags[0][2] == ptags[1][2]:
231 # The tuples are laid out so the right one can be found by
250 # The tuples are laid out so the right one can be found by
232 # comparison in this case.
251 # comparison in this case.
233 pdate, pdist, ptag = max(ptags)
252 pdate, pdist, ptag = max(ptags)
234 else:
253 else:
235 def key(x):
254 def key(x):
236 changessincetag = len(repo.revs('only(%d, %s)',
255 changessincetag = len(repo.revs('only(%d, %s)',
237 ctx.rev(), x[2][0]))
256 ctx.rev(), x[2][0]))
238 # Smallest number of changes since tag wins. Date is
257 # Smallest number of changes since tag wins. Date is
239 # used as tiebreaker.
258 # used as tiebreaker.
240 return [-changessincetag, x[0]]
259 return [-changessincetag, x[0]]
241 pdate, pdist, ptag = max(ptags, key=key)
260 pdate, pdist, ptag = max(ptags, key=key)
242 else:
261 else:
243 pdate, pdist, ptag = ptags[0]
262 pdate, pdist, ptag = ptags[0]
244 except KeyError:
263 except KeyError:
245 # Cache miss - recurse
264 # Cache miss - recurse
246 todo.append(rev)
265 todo.append(rev)
247 todo.extend(p.rev() for p in ctx.parents())
266 todo.extend(p.rev() for p in ctx.parents())
248 continue
267 continue
249 latesttags[rev] = pdate, pdist + 1, ptag
268 latesttags[rev] = pdate, pdist + 1, ptag
250 return latesttags[rev]
269 return latesttags[rev]
251
270
252 def getrenamedfn(repo, endrev=None):
271 def getrenamedfn(repo, endrev=None):
253 rcache = {}
272 rcache = {}
254 if endrev is None:
273 if endrev is None:
255 endrev = len(repo)
274 endrev = len(repo)
256
275
257 def getrenamed(fn, rev):
276 def getrenamed(fn, rev):
258 '''looks up all renames for a file (up to endrev) the first
277 '''looks up all renames for a file (up to endrev) the first
259 time the file is given. It indexes on the changerev and only
278 time the file is given. It indexes on the changerev and only
260 parses the manifest if linkrev != changerev.
279 parses the manifest if linkrev != changerev.
261 Returns rename info for fn at changerev rev.'''
280 Returns rename info for fn at changerev rev.'''
262 if fn not in rcache:
281 if fn not in rcache:
263 rcache[fn] = {}
282 rcache[fn] = {}
264 fl = repo.file(fn)
283 fl = repo.file(fn)
265 for i in fl:
284 for i in fl:
266 lr = fl.linkrev(i)
285 lr = fl.linkrev(i)
267 renamed = fl.renamed(fl.node(i))
286 renamed = fl.renamed(fl.node(i))
268 rcache[fn][lr] = renamed
287 rcache[fn][lr] = renamed
269 if lr >= endrev:
288 if lr >= endrev:
270 break
289 break
271 if rev in rcache[fn]:
290 if rev in rcache[fn]:
272 return rcache[fn][rev]
291 return rcache[fn][rev]
273
292
274 # If linkrev != rev (i.e. rev not found in rcache) fallback to
293 # If linkrev != rev (i.e. rev not found in rcache) fallback to
275 # filectx logic.
294 # filectx logic.
276 try:
295 try:
277 return repo[rev][fn].renamed()
296 return repo[rev][fn].renamed()
278 except error.LookupError:
297 except error.LookupError:
279 return None
298 return None
280
299
281 return getrenamed
300 return getrenamed
282
301
283 # default templates internally used for rendering of lists
302 # default templates internally used for rendering of lists
284 defaulttempl = {
303 defaulttempl = {
285 'parent': '{rev}:{node|formatnode} ',
304 'parent': '{rev}:{node|formatnode} ',
286 'manifest': '{rev}:{node|formatnode}',
305 'manifest': '{rev}:{node|formatnode}',
287 'file_copy': '{name} ({source})',
306 'file_copy': '{name} ({source})',
288 'envvar': '{key}={value}',
307 'envvar': '{key}={value}',
289 'extra': '{key}={value|stringescape}'
308 'extra': '{key}={value|stringescape}'
290 }
309 }
291 # filecopy is preserved for compatibility reasons
310 # filecopy is preserved for compatibility reasons
292 defaulttempl['filecopy'] = defaulttempl['file_copy']
311 defaulttempl['filecopy'] = defaulttempl['file_copy']
293
312
294 # keywords are callables like:
313 # keywords are callables like:
295 # fn(repo, ctx, templ, cache, revcache, **args)
314 # fn(repo, ctx, templ, cache, revcache, **args)
296 # with:
315 # with:
297 # repo - current repository instance
316 # repo - current repository instance
298 # ctx - the changectx being displayed
317 # ctx - the changectx being displayed
299 # templ - the templater instance
318 # templ - the templater instance
300 # cache - a cache dictionary for the whole templater run
319 # cache - a cache dictionary for the whole templater run
301 # revcache - a cache dictionary for the current revision
320 # revcache - a cache dictionary for the current revision
302 keywords = {}
321 keywords = {}
303
322
304 templatekeyword = registrar.templatekeyword(keywords)
323 templatekeyword = registrar.templatekeyword(keywords)
305
324
306 @templatekeyword('author')
325 @templatekeyword('author')
307 def showauthor(repo, ctx, templ, **args):
326 def showauthor(repo, ctx, templ, **args):
308 """String. The unmodified author of the changeset."""
327 """String. The unmodified author of the changeset."""
309 return ctx.user()
328 return ctx.user()
310
329
311 @templatekeyword('bisect')
330 @templatekeyword('bisect')
312 def showbisect(repo, ctx, templ, **args):
331 def showbisect(repo, ctx, templ, **args):
313 """String. The changeset bisection status."""
332 """String. The changeset bisection status."""
314 return hbisect.label(repo, ctx.node())
333 return hbisect.label(repo, ctx.node())
315
334
316 @templatekeyword('branch')
335 @templatekeyword('branch')
317 def showbranch(**args):
336 def showbranch(**args):
318 """String. The name of the branch on which the changeset was
337 """String. The name of the branch on which the changeset was
319 committed.
338 committed.
320 """
339 """
321 return args[r'ctx'].branch()
340 return args[r'ctx'].branch()
322
341
323 @templatekeyword('branches')
342 @templatekeyword('branches')
324 def showbranches(**args):
343 def showbranches(**args):
325 """List of strings. The name of the branch on which the
344 """List of strings. The name of the branch on which the
326 changeset was committed. Will be empty if the branch name was
345 changeset was committed. Will be empty if the branch name was
327 default. (DEPRECATED)
346 default. (DEPRECATED)
328 """
347 """
329 args = pycompat.byteskwargs(args)
348 args = pycompat.byteskwargs(args)
330 branch = args['ctx'].branch()
349 branch = args['ctx'].branch()
331 if branch != 'default':
350 if branch != 'default':
332 return showlist('branch', [branch], args, plural='branches')
351 return showlist('branch', [branch], args, plural='branches')
333 return showlist('branch', [], args, plural='branches')
352 return showlist('branch', [], args, plural='branches')
334
353
335 @templatekeyword('bookmarks')
354 @templatekeyword('bookmarks')
336 def showbookmarks(**args):
355 def showbookmarks(**args):
337 """List of strings. Any bookmarks associated with the
356 """List of strings. Any bookmarks associated with the
338 changeset. Also sets 'active', the name of the active bookmark.
357 changeset. Also sets 'active', the name of the active bookmark.
339 """
358 """
340 args = pycompat.byteskwargs(args)
359 args = pycompat.byteskwargs(args)
341 repo = args['ctx']._repo
360 repo = args['ctx']._repo
342 bookmarks = args['ctx'].bookmarks()
361 bookmarks = args['ctx'].bookmarks()
343 active = repo._activebookmark
362 active = repo._activebookmark
344 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
363 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
345 f = _showlist('bookmark', bookmarks, args)
364 f = _showlist('bookmark', bookmarks, args)
346 return _hybrid(f, bookmarks, makemap, pycompat.identity)
365 return _hybrid(f, bookmarks, makemap, pycompat.identity)
347
366
348 @templatekeyword('children')
367 @templatekeyword('children')
349 def showchildren(**args):
368 def showchildren(**args):
350 """List of strings. The children of the changeset."""
369 """List of strings. The children of the changeset."""
351 args = pycompat.byteskwargs(args)
370 args = pycompat.byteskwargs(args)
352 ctx = args['ctx']
371 ctx = args['ctx']
353 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
372 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
354 return showlist('children', childrevs, args, element='child')
373 return showlist('children', childrevs, args, element='child')
355
374
356 # Deprecated, but kept alive for help generation a purpose.
375 # Deprecated, but kept alive for help generation a purpose.
357 @templatekeyword('currentbookmark')
376 @templatekeyword('currentbookmark')
358 def showcurrentbookmark(**args):
377 def showcurrentbookmark(**args):
359 """String. The active bookmark, if it is
378 """String. The active bookmark, if it is
360 associated with the changeset (DEPRECATED)"""
379 associated with the changeset (DEPRECATED)"""
361 return showactivebookmark(**args)
380 return showactivebookmark(**args)
362
381
363 @templatekeyword('activebookmark')
382 @templatekeyword('activebookmark')
364 def showactivebookmark(**args):
383 def showactivebookmark(**args):
365 """String. The active bookmark, if it is
384 """String. The active bookmark, if it is
366 associated with the changeset"""
385 associated with the changeset"""
367 active = args[r'repo']._activebookmark
386 active = args[r'repo']._activebookmark
368 if active and active in args[r'ctx'].bookmarks():
387 if active and active in args[r'ctx'].bookmarks():
369 return active
388 return active
370 return ''
389 return ''
371
390
372 @templatekeyword('date')
391 @templatekeyword('date')
373 def showdate(repo, ctx, templ, **args):
392 def showdate(repo, ctx, templ, **args):
374 """Date information. The date when the changeset was committed."""
393 """Date information. The date when the changeset was committed."""
375 return ctx.date()
394 return ctx.date()
376
395
377 @templatekeyword('desc')
396 @templatekeyword('desc')
378 def showdescription(repo, ctx, templ, **args):
397 def showdescription(repo, ctx, templ, **args):
379 """String. The text of the changeset description."""
398 """String. The text of the changeset description."""
380 s = ctx.description()
399 s = ctx.description()
381 if isinstance(s, encoding.localstr):
400 if isinstance(s, encoding.localstr):
382 # try hard to preserve utf-8 bytes
401 # try hard to preserve utf-8 bytes
383 return encoding.tolocal(encoding.fromlocal(s).strip())
402 return encoding.tolocal(encoding.fromlocal(s).strip())
384 else:
403 else:
385 return s.strip()
404 return s.strip()
386
405
387 @templatekeyword('diffstat')
406 @templatekeyword('diffstat')
388 def showdiffstat(repo, ctx, templ, **args):
407 def showdiffstat(repo, ctx, templ, **args):
389 """String. Statistics of changes with the following format:
408 """String. Statistics of changes with the following format:
390 "modified files: +added/-removed lines"
409 "modified files: +added/-removed lines"
391 """
410 """
392 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
411 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
393 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
412 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
394 return '%s: +%s/-%s' % (len(stats), adds, removes)
413 return '%s: +%s/-%s' % (len(stats), adds, removes)
395
414
396 @templatekeyword('envvars')
415 @templatekeyword('envvars')
397 def showenvvars(repo, **args):
416 def showenvvars(repo, **args):
398 """A dictionary of environment variables. (EXPERIMENTAL)"""
417 """A dictionary of environment variables. (EXPERIMENTAL)"""
399 args = pycompat.byteskwargs(args)
418 args = pycompat.byteskwargs(args)
400 env = repo.ui.exportableenviron()
419 env = repo.ui.exportableenviron()
401 env = util.sortdict((k, env[k]) for k in sorted(env))
420 env = util.sortdict((k, env[k]) for k in sorted(env))
402 return showdict('envvar', env, args, plural='envvars')
421 return showdict('envvar', env, args, plural='envvars')
403
422
404 @templatekeyword('extras')
423 @templatekeyword('extras')
405 def showextras(**args):
424 def showextras(**args):
406 """List of dicts with key, value entries of the 'extras'
425 """List of dicts with key, value entries of the 'extras'
407 field of this changeset."""
426 field of this changeset."""
408 args = pycompat.byteskwargs(args)
427 args = pycompat.byteskwargs(args)
409 extras = args['ctx'].extra()
428 extras = args['ctx'].extra()
410 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
429 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
411 makemap = lambda k: {'key': k, 'value': extras[k]}
430 makemap = lambda k: {'key': k, 'value': extras[k]}
412 c = [makemap(k) for k in extras]
431 c = [makemap(k) for k in extras]
413 f = _showlist('extra', c, args, plural='extras')
432 f = _showlist('extra', c, args, plural='extras')
414 return _hybrid(f, extras, makemap,
433 return _hybrid(f, extras, makemap,
415 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
434 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
416
435
417 @templatekeyword('file_adds')
436 @templatekeyword('file_adds')
418 def showfileadds(**args):
437 def showfileadds(**args):
419 """List of strings. Files added by this changeset."""
438 """List of strings. Files added by this changeset."""
420 args = pycompat.byteskwargs(args)
439 args = pycompat.byteskwargs(args)
421 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
440 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
422 return showlist('file_add', getfiles(repo, ctx, revcache)[1], args,
441 return showlist('file_add', getfiles(repo, ctx, revcache)[1], args,
423 element='file')
442 element='file')
424
443
425 @templatekeyword('file_copies')
444 @templatekeyword('file_copies')
426 def showfilecopies(**args):
445 def showfilecopies(**args):
427 """List of strings. Files copied in this changeset with
446 """List of strings. Files copied in this changeset with
428 their sources.
447 their sources.
429 """
448 """
430 args = pycompat.byteskwargs(args)
449 args = pycompat.byteskwargs(args)
431 cache, ctx = args['cache'], args['ctx']
450 cache, ctx = args['cache'], args['ctx']
432 copies = args['revcache'].get('copies')
451 copies = args['revcache'].get('copies')
433 if copies is None:
452 if copies is None:
434 if 'getrenamed' not in cache:
453 if 'getrenamed' not in cache:
435 cache['getrenamed'] = getrenamedfn(args['repo'])
454 cache['getrenamed'] = getrenamedfn(args['repo'])
436 copies = []
455 copies = []
437 getrenamed = cache['getrenamed']
456 getrenamed = cache['getrenamed']
438 for fn in ctx.files():
457 for fn in ctx.files():
439 rename = getrenamed(fn, ctx.rev())
458 rename = getrenamed(fn, ctx.rev())
440 if rename:
459 if rename:
441 copies.append((fn, rename[0]))
460 copies.append((fn, rename[0]))
442
461
443 copies = util.sortdict(copies)
462 copies = util.sortdict(copies)
444 return showdict('file_copy', copies, args, plural='file_copies',
463 return showdict('file_copy', copies, args, plural='file_copies',
445 key='name', value='source', fmt='%s (%s)')
464 key='name', value='source', fmt='%s (%s)')
446
465
447 # showfilecopiesswitch() displays file copies only if copy records are
466 # showfilecopiesswitch() displays file copies only if copy records are
448 # provided before calling the templater, usually with a --copies
467 # provided before calling the templater, usually with a --copies
449 # command line switch.
468 # command line switch.
450 @templatekeyword('file_copies_switch')
469 @templatekeyword('file_copies_switch')
451 def showfilecopiesswitch(**args):
470 def showfilecopiesswitch(**args):
452 """List of strings. Like "file_copies" but displayed
471 """List of strings. Like "file_copies" but displayed
453 only if the --copied switch is set.
472 only if the --copied switch is set.
454 """
473 """
455 args = pycompat.byteskwargs(args)
474 args = pycompat.byteskwargs(args)
456 copies = args['revcache'].get('copies') or []
475 copies = args['revcache'].get('copies') or []
457 copies = util.sortdict(copies)
476 copies = util.sortdict(copies)
458 return showdict('file_copy', copies, args, plural='file_copies',
477 return showdict('file_copy', copies, args, plural='file_copies',
459 key='name', value='source', fmt='%s (%s)')
478 key='name', value='source', fmt='%s (%s)')
460
479
461 @templatekeyword('file_dels')
480 @templatekeyword('file_dels')
462 def showfiledels(**args):
481 def showfiledels(**args):
463 """List of strings. Files removed by this changeset."""
482 """List of strings. Files removed by this changeset."""
464 args = pycompat.byteskwargs(args)
483 args = pycompat.byteskwargs(args)
465 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
484 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
466 return showlist('file_del', getfiles(repo, ctx, revcache)[2], args,
485 return showlist('file_del', getfiles(repo, ctx, revcache)[2], args,
467 element='file')
486 element='file')
468
487
469 @templatekeyword('file_mods')
488 @templatekeyword('file_mods')
470 def showfilemods(**args):
489 def showfilemods(**args):
471 """List of strings. Files modified by this changeset."""
490 """List of strings. Files modified by this changeset."""
472 args = pycompat.byteskwargs(args)
491 args = pycompat.byteskwargs(args)
473 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
492 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
474 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], args,
493 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], args,
475 element='file')
494 element='file')
476
495
477 @templatekeyword('files')
496 @templatekeyword('files')
478 def showfiles(**args):
497 def showfiles(**args):
479 """List of strings. All files modified, added, or removed by this
498 """List of strings. All files modified, added, or removed by this
480 changeset.
499 changeset.
481 """
500 """
482 args = pycompat.byteskwargs(args)
501 args = pycompat.byteskwargs(args)
483 return showlist('file', args['ctx'].files(), args)
502 return showlist('file', args['ctx'].files(), args)
484
503
485 @templatekeyword('graphnode')
504 @templatekeyword('graphnode')
486 def showgraphnode(repo, ctx, **args):
505 def showgraphnode(repo, ctx, **args):
487 """String. The character representing the changeset node in
506 """String. The character representing the changeset node in
488 an ASCII revision graph"""
507 an ASCII revision graph"""
489 wpnodes = repo.dirstate.parents()
508 wpnodes = repo.dirstate.parents()
490 if wpnodes[1] == nullid:
509 if wpnodes[1] == nullid:
491 wpnodes = wpnodes[:1]
510 wpnodes = wpnodes[:1]
492 if ctx.node() in wpnodes:
511 if ctx.node() in wpnodes:
493 return '@'
512 return '@'
494 elif ctx.obsolete():
513 elif ctx.obsolete():
495 return 'x'
514 return 'x'
496 elif ctx.closesbranch():
515 elif ctx.closesbranch():
497 return '_'
516 return '_'
498 else:
517 else:
499 return 'o'
518 return 'o'
500
519
501 @templatekeyword('graphwidth')
520 @templatekeyword('graphwidth')
502 def showgraphwidth(repo, ctx, templ, **args):
521 def showgraphwidth(repo, ctx, templ, **args):
503 """Integer. The width of the graph drawn by 'log --graph' or zero."""
522 """Integer. The width of the graph drawn by 'log --graph' or zero."""
504 # The value args['graphwidth'] will be this function, so we use an internal
523 # The value args['graphwidth'] will be this function, so we use an internal
505 # name to pass the value through props into this function.
524 # name to pass the value through props into this function.
506 return args.get('_graphwidth', 0)
525 return args.get('_graphwidth', 0)
507
526
508 @templatekeyword('index')
527 @templatekeyword('index')
509 def showindex(**args):
528 def showindex(**args):
510 """Integer. The current iteration of the loop. (0 indexed)"""
529 """Integer. The current iteration of the loop. (0 indexed)"""
511 # just hosts documentation; should be overridden by template mapping
530 # just hosts documentation; should be overridden by template mapping
512 raise error.Abort(_("can't use index in this context"))
531 raise error.Abort(_("can't use index in this context"))
513
532
514 @templatekeyword('latesttag')
533 @templatekeyword('latesttag')
515 def showlatesttag(**args):
534 def showlatesttag(**args):
516 """List of strings. The global tags on the most recent globally
535 """List of strings. The global tags on the most recent globally
517 tagged ancestor of this changeset. If no such tags exist, the list
536 tagged ancestor of this changeset. If no such tags exist, the list
518 consists of the single string "null".
537 consists of the single string "null".
519 """
538 """
520 return showlatesttags(None, **args)
539 return showlatesttags(None, **args)
521
540
522 def showlatesttags(pattern, **args):
541 def showlatesttags(pattern, **args):
523 """helper method for the latesttag keyword and function"""
542 """helper method for the latesttag keyword and function"""
524 args = pycompat.byteskwargs(args)
543 args = pycompat.byteskwargs(args)
525 repo, ctx = args['repo'], args['ctx']
544 repo, ctx = args['repo'], args['ctx']
526 cache = args['cache']
545 cache = args['cache']
527 latesttags = getlatesttags(repo, ctx, cache, pattern)
546 latesttags = getlatesttags(repo, ctx, cache, pattern)
528
547
529 # latesttag[0] is an implementation detail for sorting csets on different
548 # latesttag[0] is an implementation detail for sorting csets on different
530 # branches in a stable manner- it is the date the tagged cset was created,
549 # branches in a stable manner- it is the date the tagged cset was created,
531 # not the date the tag was created. Therefore it isn't made visible here.
550 # not the date the tag was created. Therefore it isn't made visible here.
532 makemap = lambda v: {
551 makemap = lambda v: {
533 'changes': _showchangessincetag,
552 'changes': _showchangessincetag,
534 'distance': latesttags[1],
553 'distance': latesttags[1],
535 'latesttag': v, # BC with {latesttag % '{latesttag}'}
554 'latesttag': v, # BC with {latesttag % '{latesttag}'}
536 'tag': v
555 'tag': v
537 }
556 }
538
557
539 tags = latesttags[2]
558 tags = latesttags[2]
540 f = _showlist('latesttag', tags, args, separator=':')
559 f = _showlist('latesttag', tags, args, separator=':')
541 return _hybrid(f, tags, makemap, pycompat.identity)
560 return _hybrid(f, tags, makemap, pycompat.identity)
542
561
543 @templatekeyword('latesttagdistance')
562 @templatekeyword('latesttagdistance')
544 def showlatesttagdistance(repo, ctx, templ, cache, **args):
563 def showlatesttagdistance(repo, ctx, templ, cache, **args):
545 """Integer. Longest path to the latest tag."""
564 """Integer. Longest path to the latest tag."""
546 return getlatesttags(repo, ctx, cache)[1]
565 return getlatesttags(repo, ctx, cache)[1]
547
566
548 @templatekeyword('changessincelatesttag')
567 @templatekeyword('changessincelatesttag')
549 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
568 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
550 """Integer. All ancestors not in the latest tag."""
569 """Integer. All ancestors not in the latest tag."""
551 latesttag = getlatesttags(repo, ctx, cache)[2][0]
570 latesttag = getlatesttags(repo, ctx, cache)[2][0]
552
571
553 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
572 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
554
573
555 def _showchangessincetag(repo, ctx, **args):
574 def _showchangessincetag(repo, ctx, **args):
556 offset = 0
575 offset = 0
557 revs = [ctx.rev()]
576 revs = [ctx.rev()]
558 tag = args[r'tag']
577 tag = args[r'tag']
559
578
560 # The only() revset doesn't currently support wdir()
579 # The only() revset doesn't currently support wdir()
561 if ctx.rev() is None:
580 if ctx.rev() is None:
562 offset = 1
581 offset = 1
563 revs = [p.rev() for p in ctx.parents()]
582 revs = [p.rev() for p in ctx.parents()]
564
583
565 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
584 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
566
585
567 @templatekeyword('manifest')
586 @templatekeyword('manifest')
568 def showmanifest(**args):
587 def showmanifest(**args):
569 repo, ctx, templ = args[r'repo'], args[r'ctx'], args[r'templ']
588 repo, ctx, templ = args[r'repo'], args[r'ctx'], args[r'templ']
570 mnode = ctx.manifestnode()
589 mnode = ctx.manifestnode()
571 if mnode is None:
590 if mnode is None:
572 # just avoid crash, we might want to use the 'ff...' hash in future
591 # just avoid crash, we might want to use the 'ff...' hash in future
573 return
592 return
574 mrev = repo.manifestlog._revlog.rev(mnode)
593 mrev = repo.manifestlog._revlog.rev(mnode)
575 mhex = hex(mnode)
594 mhex = hex(mnode)
576 args = args.copy()
595 args = args.copy()
577 args.update({r'rev': mrev, r'node': mhex})
596 args.update({r'rev': mrev, r'node': mhex})
578 f = templ('manifest', **args)
597 f = templ('manifest', **args)
579 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
598 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
580 # rev and node are completely different from changeset's.
599 # rev and node are completely different from changeset's.
581 return _mappable(f, f, lambda: {'rev': mrev, 'node': mhex})
600 return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
582
601
583 def shownames(namespace, **args):
602 def shownames(namespace, **args):
584 """helper method to generate a template keyword for a namespace"""
603 """helper method to generate a template keyword for a namespace"""
585 args = pycompat.byteskwargs(args)
604 args = pycompat.byteskwargs(args)
586 ctx = args['ctx']
605 ctx = args['ctx']
587 repo = ctx.repo()
606 repo = ctx.repo()
588 ns = repo.names[namespace]
607 ns = repo.names[namespace]
589 names = ns.names(repo, ctx.node())
608 names = ns.names(repo, ctx.node())
590 return showlist(ns.templatename, names, args, plural=namespace)
609 return showlist(ns.templatename, names, args, plural=namespace)
591
610
592 @templatekeyword('namespaces')
611 @templatekeyword('namespaces')
593 def shownamespaces(**args):
612 def shownamespaces(**args):
594 """Dict of lists. Names attached to this changeset per
613 """Dict of lists. Names attached to this changeset per
595 namespace."""
614 namespace."""
596 args = pycompat.byteskwargs(args)
615 args = pycompat.byteskwargs(args)
597 ctx = args['ctx']
616 ctx = args['ctx']
598 repo = ctx.repo()
617 repo = ctx.repo()
599
618
600 namespaces = util.sortdict()
619 namespaces = util.sortdict()
601 colornames = {}
620 colornames = {}
602 builtins = {}
621 builtins = {}
603
622
604 for k, ns in repo.names.iteritems():
623 for k, ns in repo.names.iteritems():
605 namespaces[k] = showlist('name', ns.names(repo, ctx.node()), args)
624 namespaces[k] = showlist('name', ns.names(repo, ctx.node()), args)
606 colornames[k] = ns.colorname
625 colornames[k] = ns.colorname
607 builtins[k] = ns.builtin
626 builtins[k] = ns.builtin
608
627
609 f = _showlist('namespace', list(namespaces), args)
628 f = _showlist('namespace', list(namespaces), args)
610
629
611 def makemap(ns):
630 def makemap(ns):
612 return {
631 return {
613 'namespace': ns,
632 'namespace': ns,
614 'names': namespaces[ns],
633 'names': namespaces[ns],
615 'builtin': builtins[ns],
634 'builtin': builtins[ns],
616 'colorname': colornames[ns],
635 'colorname': colornames[ns],
617 }
636 }
618
637
619 return _hybrid(f, namespaces, makemap, pycompat.identity)
638 return _hybrid(f, namespaces, makemap, pycompat.identity)
620
639
621 @templatekeyword('node')
640 @templatekeyword('node')
622 def shownode(repo, ctx, templ, **args):
641 def shownode(repo, ctx, templ, **args):
623 """String. The changeset identification hash, as a 40 hexadecimal
642 """String. The changeset identification hash, as a 40 hexadecimal
624 digit string.
643 digit string.
625 """
644 """
626 return ctx.hex()
645 return ctx.hex()
627
646
628 @templatekeyword('obsolete')
647 @templatekeyword('obsolete')
629 def showobsolete(repo, ctx, templ, **args):
648 def showobsolete(repo, ctx, templ, **args):
630 """String. Whether the changeset is obsolete.
649 """String. Whether the changeset is obsolete.
631 """
650 """
632 if ctx.obsolete():
651 if ctx.obsolete():
633 return 'obsolete'
652 return 'obsolete'
634 return ''
653 return ''
635
654
636 @templatekeyword('peerpaths')
655 @templatekeyword('peerpaths')
637 def showpeerpaths(repo, **args):
656 def showpeerpaths(repo, **args):
638 """A dictionary of repository locations defined in the [paths] section
657 """A dictionary of repository locations defined in the [paths] section
639 of your configuration file. (EXPERIMENTAL)"""
658 of your configuration file. (EXPERIMENTAL)"""
640 # see commands.paths() for naming of dictionary keys
659 # see commands.paths() for naming of dictionary keys
641 paths = util.sortdict()
660 paths = util.sortdict()
642 for k, p in sorted(repo.ui.paths.iteritems()):
661 for k, p in sorted(repo.ui.paths.iteritems()):
643 d = util.sortdict()
662 d = util.sortdict()
644 d['url'] = p.rawloc
663 d['url'] = p.rawloc
645 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
664 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
646 def f():
665 def f():
647 yield d['url']
666 yield d['url']
648 paths[k] = hybriddict(d, gen=f())
667 paths[k] = hybriddict(d, gen=f())
649
668
650 # no hybriddict() since d['path'] can't be formatted as a string. perhaps
669 # no hybriddict() since d['path'] can't be formatted as a string. perhaps
651 # hybriddict() should call templatefilters.stringify(d[value]).
670 # hybriddict() should call templatefilters.stringify(d[value]).
652 return _hybrid(None, paths, lambda k: {'name': k, 'path': paths[k]},
671 return _hybrid(None, paths, lambda k: {'name': k, 'path': paths[k]},
653 lambda k: '%s=%s' % (k, paths[k]['url']))
672 lambda k: '%s=%s' % (k, paths[k]['url']))
654
673
655 @templatekeyword("predecessors")
674 @templatekeyword("predecessors")
656 def showpredecessors(repo, ctx, **args):
675 def showpredecessors(repo, ctx, **args):
657 """Returns the list if the closest visible successors
676 """Returns the list if the closest visible successors
658 """
677 """
659 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
678 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
660 predecessors = map(hex, predecessors)
679 predecessors = map(hex, predecessors)
661
680
662 return _hybrid(None, predecessors,
681 return _hybrid(None, predecessors,
663 lambda x: {'ctx': repo[x], 'revcache': {}},
682 lambda x: {'ctx': repo[x], 'revcache': {}},
664 lambda x: scmutil.formatchangeid(repo[x]))
683 lambda x: scmutil.formatchangeid(repo[x]))
665
684
666 @templatekeyword("successorssets")
685 @templatekeyword("successorssets")
667 def showsuccessorssets(repo, ctx, **args):
686 def showsuccessorssets(repo, ctx, **args):
668 """Returns a string of sets of successors for a changectx
687 """Returns a string of sets of successors for a changectx
669
688
670 Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and
689 Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and
671 ctx2 while also diverged into ctx3"""
690 ctx2 while also diverged into ctx3"""
672 if not ctx.obsolete():
691 if not ctx.obsolete():
673 return ''
692 return ''
674 args = pycompat.byteskwargs(args)
693 args = pycompat.byteskwargs(args)
675
694
676 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
695 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
677 ssets = [[hex(n) for n in ss] for ss in ssets]
696 ssets = [[hex(n) for n in ss] for ss in ssets]
678
697
679 data = []
698 data = []
680 for ss in ssets:
699 for ss in ssets:
681 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
700 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
682 lambda x: scmutil.formatchangeid(repo[x]))
701 lambda x: scmutil.formatchangeid(repo[x]))
683 data.append(h)
702 data.append(h)
684
703
685 # Format the successorssets
704 # Format the successorssets
686 def render(d):
705 def render(d):
687 t = []
706 t = []
688 for i in d.gen():
707 for i in d.gen():
689 t.append(i)
708 t.append(i)
690 return "".join(t)
709 return "".join(t)
691
710
692 def gen(data):
711 def gen(data):
693 yield "; ".join(render(d) for d in data)
712 yield "; ".join(render(d) for d in data)
694
713
695 return _hybrid(gen(data), data, lambda x: {'successorset': x},
714 return _hybrid(gen(data), data, lambda x: {'successorset': x},
696 pycompat.identity)
715 pycompat.identity)
697
716
698 @templatekeyword("succsandmarkers")
717 @templatekeyword("succsandmarkers")
699 def showsuccsandmarkers(repo, ctx, **args):
718 def showsuccsandmarkers(repo, ctx, **args):
700 """Returns a list of dict for each final successor of ctx.
719 """Returns a list of dict for each final successor of ctx.
701
720
702 The dict contains successors node id in "successors" keys and the list of
721 The dict contains successors node id in "successors" keys and the list of
703 obs-markers from ctx to the set of successors in "markers"
722 obs-markers from ctx to the set of successors in "markers"
704
723
705 (EXPERIMENTAL)
724 (EXPERIMENTAL)
706 """
725 """
707
726
708 values = obsutil.successorsandmarkers(repo, ctx)
727 values = obsutil.successorsandmarkers(repo, ctx)
709
728
710 if values is None:
729 if values is None:
711 values = []
730 values = []
712
731
713 # Format successors and markers to avoid exposing binary to templates
732 # Format successors and markers to avoid exposing binary to templates
714 data = []
733 data = []
715 for i in values:
734 for i in values:
716 # Format successors
735 # Format successors
717 successors = i['successors']
736 successors = i['successors']
718
737
719 successors = [hex(n) for n in successors]
738 successors = [hex(n) for n in successors]
720 successors = _hybrid(None, successors,
739 successors = _hybrid(None, successors,
721 lambda x: {'ctx': repo[x], 'revcache': {}},
740 lambda x: {'ctx': repo[x], 'revcache': {}},
722 lambda x: scmutil.formatchangeid(repo[x]))
741 lambda x: scmutil.formatchangeid(repo[x]))
723
742
724 # Format markers
743 # Format markers
725 finalmarkers = []
744 finalmarkers = []
726 for m in i['markers']:
745 for m in i['markers']:
727 hexprec = hex(m[0])
746 hexprec = hex(m[0])
728 hexsucs = tuple(hex(n) for n in m[1])
747 hexsucs = tuple(hex(n) for n in m[1])
729 hexparents = None
748 hexparents = None
730 if m[5] is not None:
749 if m[5] is not None:
731 hexparents = tuple(hex(n) for n in m[5])
750 hexparents = tuple(hex(n) for n in m[5])
732 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
751 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
733 finalmarkers.append(newmarker)
752 finalmarkers.append(newmarker)
734
753
735 data.append({'successors': successors, 'markers': finalmarkers})
754 data.append({'successors': successors, 'markers': finalmarkers})
736
755
737 f = _showlist('succsandmarkers', data, args)
756 f = _showlist('succsandmarkers', data, args)
738 return _hybrid(f, data, lambda x: x, pycompat.identity)
757 return _hybrid(f, data, lambda x: x, pycompat.identity)
739
758
740 @templatekeyword('p1rev')
759 @templatekeyword('p1rev')
741 def showp1rev(repo, ctx, templ, **args):
760 def showp1rev(repo, ctx, templ, **args):
742 """Integer. The repository-local revision number of the changeset's
761 """Integer. The repository-local revision number of the changeset's
743 first parent, or -1 if the changeset has no parents."""
762 first parent, or -1 if the changeset has no parents."""
744 return ctx.p1().rev()
763 return ctx.p1().rev()
745
764
746 @templatekeyword('p2rev')
765 @templatekeyword('p2rev')
747 def showp2rev(repo, ctx, templ, **args):
766 def showp2rev(repo, ctx, templ, **args):
748 """Integer. The repository-local revision number of the changeset's
767 """Integer. The repository-local revision number of the changeset's
749 second parent, or -1 if the changeset has no second parent."""
768 second parent, or -1 if the changeset has no second parent."""
750 return ctx.p2().rev()
769 return ctx.p2().rev()
751
770
752 @templatekeyword('p1node')
771 @templatekeyword('p1node')
753 def showp1node(repo, ctx, templ, **args):
772 def showp1node(repo, ctx, templ, **args):
754 """String. The identification hash of the changeset's first parent,
773 """String. The identification hash of the changeset's first parent,
755 as a 40 digit hexadecimal string. If the changeset has no parents, all
774 as a 40 digit hexadecimal string. If the changeset has no parents, all
756 digits are 0."""
775 digits are 0."""
757 return ctx.p1().hex()
776 return ctx.p1().hex()
758
777
759 @templatekeyword('p2node')
778 @templatekeyword('p2node')
760 def showp2node(repo, ctx, templ, **args):
779 def showp2node(repo, ctx, templ, **args):
761 """String. The identification hash of the changeset's second
780 """String. The identification hash of the changeset's second
762 parent, as a 40 digit hexadecimal string. If the changeset has no second
781 parent, as a 40 digit hexadecimal string. If the changeset has no second
763 parent, all digits are 0."""
782 parent, all digits are 0."""
764 return ctx.p2().hex()
783 return ctx.p2().hex()
765
784
766 @templatekeyword('parents')
785 @templatekeyword('parents')
767 def showparents(**args):
786 def showparents(**args):
768 """List of strings. The parents of the changeset in "rev:node"
787 """List of strings. The parents of the changeset in "rev:node"
769 format. If the changeset has only one "natural" parent (the predecessor
788 format. If the changeset has only one "natural" parent (the predecessor
770 revision) nothing is shown."""
789 revision) nothing is shown."""
771 args = pycompat.byteskwargs(args)
790 args = pycompat.byteskwargs(args)
772 repo = args['repo']
791 repo = args['repo']
773 ctx = args['ctx']
792 ctx = args['ctx']
774 pctxs = scmutil.meaningfulparents(repo, ctx)
793 pctxs = scmutil.meaningfulparents(repo, ctx)
775 # ifcontains() needs a list of str
794 # ifcontains() needs a list of str
776 prevs = ["%d" % p.rev() for p in pctxs]
795 prevs = ["%d" % p.rev() for p in pctxs]
777 parents = [[('rev', p.rev()),
796 parents = [[('rev', p.rev()),
778 ('node', p.hex()),
797 ('node', p.hex()),
779 ('phase', p.phasestr())]
798 ('phase', p.phasestr())]
780 for p in pctxs]
799 for p in pctxs]
781 f = _showlist('parent', parents, args)
800 f = _showlist('parent', parents, args)
782 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
801 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
783 lambda x: scmutil.formatchangeid(repo[int(x)]))
802 lambda x: scmutil.formatchangeid(repo[int(x)]))
784
803
785 @templatekeyword('phase')
804 @templatekeyword('phase')
786 def showphase(repo, ctx, templ, **args):
805 def showphase(repo, ctx, templ, **args):
787 """String. The changeset phase name."""
806 """String. The changeset phase name."""
788 return ctx.phasestr()
807 return ctx.phasestr()
789
808
790 @templatekeyword('phaseidx')
809 @templatekeyword('phaseidx')
791 def showphaseidx(repo, ctx, templ, **args):
810 def showphaseidx(repo, ctx, templ, **args):
792 """Integer. The changeset phase index."""
811 """Integer. The changeset phase index."""
793 return ctx.phase()
812 return ctx.phase()
794
813
795 @templatekeyword('rev')
814 @templatekeyword('rev')
796 def showrev(repo, ctx, templ, **args):
815 def showrev(repo, ctx, templ, **args):
797 """Integer. The repository-local changeset revision number."""
816 """Integer. The repository-local changeset revision number."""
798 return scmutil.intrev(ctx)
817 return scmutil.intrev(ctx)
799
818
800 def showrevslist(name, revs, **args):
819 def showrevslist(name, revs, **args):
801 """helper to generate a list of revisions in which a mapped template will
820 """helper to generate a list of revisions in which a mapped template will
802 be evaluated"""
821 be evaluated"""
803 args = pycompat.byteskwargs(args)
822 args = pycompat.byteskwargs(args)
804 repo = args['ctx'].repo()
823 repo = args['ctx'].repo()
805 # ifcontains() needs a list of str
824 # ifcontains() needs a list of str
806 revs = ["%d" % r for r in revs]
825 revs = ["%d" % r for r in revs]
807 f = _showlist(name, revs, args)
826 f = _showlist(name, revs, args)
808 return _hybrid(f, revs,
827 return _hybrid(f, revs,
809 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
828 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
810 pycompat.identity)
829 pycompat.identity)
811
830
812 @templatekeyword('subrepos')
831 @templatekeyword('subrepos')
813 def showsubrepos(**args):
832 def showsubrepos(**args):
814 """List of strings. Updated subrepositories in the changeset."""
833 """List of strings. Updated subrepositories in the changeset."""
815 args = pycompat.byteskwargs(args)
834 args = pycompat.byteskwargs(args)
816 ctx = args['ctx']
835 ctx = args['ctx']
817 substate = ctx.substate
836 substate = ctx.substate
818 if not substate:
837 if not substate:
819 return showlist('subrepo', [], args)
838 return showlist('subrepo', [], args)
820 psubstate = ctx.parents()[0].substate or {}
839 psubstate = ctx.parents()[0].substate or {}
821 subrepos = []
840 subrepos = []
822 for sub in substate:
841 for sub in substate:
823 if sub not in psubstate or substate[sub] != psubstate[sub]:
842 if sub not in psubstate or substate[sub] != psubstate[sub]:
824 subrepos.append(sub) # modified or newly added in ctx
843 subrepos.append(sub) # modified or newly added in ctx
825 for sub in psubstate:
844 for sub in psubstate:
826 if sub not in substate:
845 if sub not in substate:
827 subrepos.append(sub) # removed in ctx
846 subrepos.append(sub) # removed in ctx
828 return showlist('subrepo', sorted(subrepos), args)
847 return showlist('subrepo', sorted(subrepos), args)
829
848
830 # don't remove "showtags" definition, even though namespaces will put
849 # don't remove "showtags" definition, even though namespaces will put
831 # a helper function for "tags" keyword into "keywords" map automatically,
850 # a helper function for "tags" keyword into "keywords" map automatically,
832 # because online help text is built without namespaces initialization
851 # because online help text is built without namespaces initialization
833 @templatekeyword('tags')
852 @templatekeyword('tags')
834 def showtags(**args):
853 def showtags(**args):
835 """List of strings. Any tags associated with the changeset."""
854 """List of strings. Any tags associated with the changeset."""
836 return shownames('tags', **args)
855 return shownames('tags', **args)
837
856
838 def loadkeyword(ui, extname, registrarobj):
857 def loadkeyword(ui, extname, registrarobj):
839 """Load template keyword from specified registrarobj
858 """Load template keyword from specified registrarobj
840 """
859 """
841 for name, func in registrarobj._table.iteritems():
860 for name, func in registrarobj._table.iteritems():
842 keywords[name] = func
861 keywords[name] = func
843
862
844 @templatekeyword('termwidth')
863 @templatekeyword('termwidth')
845 def showtermwidth(repo, ctx, templ, **args):
864 def showtermwidth(repo, ctx, templ, **args):
846 """Integer. The width of the current terminal."""
865 """Integer. The width of the current terminal."""
847 return repo.ui.termwidth()
866 return repo.ui.termwidth()
848
867
849 @templatekeyword('troubles')
868 @templatekeyword('troubles')
850 def showtroubles(repo, **args):
869 def showtroubles(repo, **args):
851 """List of strings. Evolution troubles affecting the changeset.
870 """List of strings. Evolution troubles affecting the changeset.
852
871
853 (DEPRECATED)
872 (DEPRECATED)
854 """
873 """
855 msg = ("'troubles' is deprecated, "
874 msg = ("'troubles' is deprecated, "
856 "use 'instabilities'")
875 "use 'instabilities'")
857 repo.ui.deprecwarn(msg, '4.4')
876 repo.ui.deprecwarn(msg, '4.4')
858
877
859 return showinstabilities(repo=repo, **args)
878 return showinstabilities(repo=repo, **args)
860
879
861 @templatekeyword('instabilities')
880 @templatekeyword('instabilities')
862 def showinstabilities(**args):
881 def showinstabilities(**args):
863 """List of strings. Evolution instabilities affecting the changeset.
882 """List of strings. Evolution instabilities affecting the changeset.
864
883
865 (EXPERIMENTAL)
884 (EXPERIMENTAL)
866 """
885 """
867 args = pycompat.byteskwargs(args)
886 args = pycompat.byteskwargs(args)
868 return showlist('instability', args['ctx'].instabilities(), args,
887 return showlist('instability', args['ctx'].instabilities(), args,
869 plural='instabilities')
888 plural='instabilities')
870
889
871 # tell hggettext to extract docstrings from these functions:
890 # tell hggettext to extract docstrings from these functions:
872 i18nfunctions = keywords.values()
891 i18nfunctions = keywords.values()
@@ -1,1469 +1,1474 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import os
10 import os
11 import re
11 import re
12 import types
12 import types
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 color,
16 color,
17 config,
17 config,
18 encoding,
18 encoding,
19 error,
19 error,
20 minirst,
20 minirst,
21 obsutil,
21 obsutil,
22 parser,
22 parser,
23 pycompat,
23 pycompat,
24 registrar,
24 registrar,
25 revset as revsetmod,
25 revset as revsetmod,
26 revsetlang,
26 revsetlang,
27 scmutil,
27 scmutil,
28 templatefilters,
28 templatefilters,
29 templatekw,
29 templatekw,
30 util,
30 util,
31 )
31 )
32
32
33 # template parsing
33 # template parsing
34
34
35 elements = {
35 elements = {
36 # token-type: binding-strength, primary, prefix, infix, suffix
36 # token-type: binding-strength, primary, prefix, infix, suffix
37 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
37 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
38 "%": (15, None, None, ("%", 15), None),
38 "%": (15, None, None, ("%", 15), None),
39 "|": (15, None, None, ("|", 15), None),
39 "|": (15, None, None, ("|", 15), None),
40 "*": (5, None, None, ("*", 5), None),
40 "*": (5, None, None, ("*", 5), None),
41 "/": (5, None, None, ("/", 5), None),
41 "/": (5, None, None, ("/", 5), None),
42 "+": (4, None, None, ("+", 4), None),
42 "+": (4, None, None, ("+", 4), None),
43 "-": (4, None, ("negate", 19), ("-", 4), None),
43 "-": (4, None, ("negate", 19), ("-", 4), None),
44 "=": (3, None, None, ("keyvalue", 3), None),
44 "=": (3, None, None, ("keyvalue", 3), None),
45 ",": (2, None, None, ("list", 2), None),
45 ",": (2, None, None, ("list", 2), None),
46 ")": (0, None, None, None, None),
46 ")": (0, None, None, None, None),
47 "integer": (0, "integer", None, None, None),
47 "integer": (0, "integer", None, None, None),
48 "symbol": (0, "symbol", None, None, None),
48 "symbol": (0, "symbol", None, None, None),
49 "string": (0, "string", None, None, None),
49 "string": (0, "string", None, None, None),
50 "template": (0, "template", None, None, None),
50 "template": (0, "template", None, None, None),
51 "end": (0, None, None, None, None),
51 "end": (0, None, None, None, None),
52 }
52 }
53
53
54 def tokenize(program, start, end, term=None):
54 def tokenize(program, start, end, term=None):
55 """Parse a template expression into a stream of tokens, which must end
55 """Parse a template expression into a stream of tokens, which must end
56 with term if specified"""
56 with term if specified"""
57 pos = start
57 pos = start
58 program = pycompat.bytestr(program)
58 program = pycompat.bytestr(program)
59 while pos < end:
59 while pos < end:
60 c = program[pos]
60 c = program[pos]
61 if c.isspace(): # skip inter-token whitespace
61 if c.isspace(): # skip inter-token whitespace
62 pass
62 pass
63 elif c in "(=,)%|+-*/": # handle simple operators
63 elif c in "(=,)%|+-*/": # handle simple operators
64 yield (c, None, pos)
64 yield (c, None, pos)
65 elif c in '"\'': # handle quoted templates
65 elif c in '"\'': # handle quoted templates
66 s = pos + 1
66 s = pos + 1
67 data, pos = _parsetemplate(program, s, end, c)
67 data, pos = _parsetemplate(program, s, end, c)
68 yield ('template', data, s)
68 yield ('template', data, s)
69 pos -= 1
69 pos -= 1
70 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
70 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
71 # handle quoted strings
71 # handle quoted strings
72 c = program[pos + 1]
72 c = program[pos + 1]
73 s = pos = pos + 2
73 s = pos = pos + 2
74 while pos < end: # find closing quote
74 while pos < end: # find closing quote
75 d = program[pos]
75 d = program[pos]
76 if d == '\\': # skip over escaped characters
76 if d == '\\': # skip over escaped characters
77 pos += 2
77 pos += 2
78 continue
78 continue
79 if d == c:
79 if d == c:
80 yield ('string', program[s:pos], s)
80 yield ('string', program[s:pos], s)
81 break
81 break
82 pos += 1
82 pos += 1
83 else:
83 else:
84 raise error.ParseError(_("unterminated string"), s)
84 raise error.ParseError(_("unterminated string"), s)
85 elif c.isdigit():
85 elif c.isdigit():
86 s = pos
86 s = pos
87 while pos < end:
87 while pos < end:
88 d = program[pos]
88 d = program[pos]
89 if not d.isdigit():
89 if not d.isdigit():
90 break
90 break
91 pos += 1
91 pos += 1
92 yield ('integer', program[s:pos], s)
92 yield ('integer', program[s:pos], s)
93 pos -= 1
93 pos -= 1
94 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
94 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
95 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
95 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
96 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
96 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
97 # where some of nested templates were preprocessed as strings and
97 # where some of nested templates were preprocessed as strings and
98 # then compiled. therefore, \"...\" was allowed. (issue4733)
98 # then compiled. therefore, \"...\" was allowed. (issue4733)
99 #
99 #
100 # processing flow of _evalifliteral() at 5ab28a2e9962:
100 # processing flow of _evalifliteral() at 5ab28a2e9962:
101 # outer template string -> stringify() -> compiletemplate()
101 # outer template string -> stringify() -> compiletemplate()
102 # ------------------------ ------------ ------------------
102 # ------------------------ ------------ ------------------
103 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
103 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
104 # ~~~~~~~~
104 # ~~~~~~~~
105 # escaped quoted string
105 # escaped quoted string
106 if c == 'r':
106 if c == 'r':
107 pos += 1
107 pos += 1
108 token = 'string'
108 token = 'string'
109 else:
109 else:
110 token = 'template'
110 token = 'template'
111 quote = program[pos:pos + 2]
111 quote = program[pos:pos + 2]
112 s = pos = pos + 2
112 s = pos = pos + 2
113 while pos < end: # find closing escaped quote
113 while pos < end: # find closing escaped quote
114 if program.startswith('\\\\\\', pos, end):
114 if program.startswith('\\\\\\', pos, end):
115 pos += 4 # skip over double escaped characters
115 pos += 4 # skip over double escaped characters
116 continue
116 continue
117 if program.startswith(quote, pos, end):
117 if program.startswith(quote, pos, end):
118 # interpret as if it were a part of an outer string
118 # interpret as if it were a part of an outer string
119 data = parser.unescapestr(program[s:pos])
119 data = parser.unescapestr(program[s:pos])
120 if token == 'template':
120 if token == 'template':
121 data = _parsetemplate(data, 0, len(data))[0]
121 data = _parsetemplate(data, 0, len(data))[0]
122 yield (token, data, s)
122 yield (token, data, s)
123 pos += 1
123 pos += 1
124 break
124 break
125 pos += 1
125 pos += 1
126 else:
126 else:
127 raise error.ParseError(_("unterminated string"), s)
127 raise error.ParseError(_("unterminated string"), s)
128 elif c.isalnum() or c in '_':
128 elif c.isalnum() or c in '_':
129 s = pos
129 s = pos
130 pos += 1
130 pos += 1
131 while pos < end: # find end of symbol
131 while pos < end: # find end of symbol
132 d = program[pos]
132 d = program[pos]
133 if not (d.isalnum() or d == "_"):
133 if not (d.isalnum() or d == "_"):
134 break
134 break
135 pos += 1
135 pos += 1
136 sym = program[s:pos]
136 sym = program[s:pos]
137 yield ('symbol', sym, s)
137 yield ('symbol', sym, s)
138 pos -= 1
138 pos -= 1
139 elif c == term:
139 elif c == term:
140 yield ('end', None, pos + 1)
140 yield ('end', None, pos + 1)
141 return
141 return
142 else:
142 else:
143 raise error.ParseError(_("syntax error"), pos)
143 raise error.ParseError(_("syntax error"), pos)
144 pos += 1
144 pos += 1
145 if term:
145 if term:
146 raise error.ParseError(_("unterminated template expansion"), start)
146 raise error.ParseError(_("unterminated template expansion"), start)
147 yield ('end', None, pos)
147 yield ('end', None, pos)
148
148
149 def _parsetemplate(tmpl, start, stop, quote=''):
149 def _parsetemplate(tmpl, start, stop, quote=''):
150 r"""
150 r"""
151 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
151 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
152 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
152 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
153 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
153 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
154 ([('string', 'foo'), ('symbol', 'bar')], 9)
154 ([('string', 'foo'), ('symbol', 'bar')], 9)
155 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
155 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
156 ([('string', 'foo')], 4)
156 ([('string', 'foo')], 4)
157 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
157 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
158 ([('string', 'foo"'), ('string', 'bar')], 9)
158 ([('string', 'foo"'), ('string', 'bar')], 9)
159 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
159 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
160 ([('string', 'foo\\')], 6)
160 ([('string', 'foo\\')], 6)
161 """
161 """
162 parsed = []
162 parsed = []
163 sepchars = '{' + quote
163 sepchars = '{' + quote
164 pos = start
164 pos = start
165 p = parser.parser(elements)
165 p = parser.parser(elements)
166 while pos < stop:
166 while pos < stop:
167 n = min((tmpl.find(c, pos, stop) for c in sepchars),
167 n = min((tmpl.find(c, pos, stop) for c in sepchars),
168 key=lambda n: (n < 0, n))
168 key=lambda n: (n < 0, n))
169 if n < 0:
169 if n < 0:
170 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
170 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
171 pos = stop
171 pos = stop
172 break
172 break
173 c = tmpl[n:n + 1]
173 c = tmpl[n:n + 1]
174 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
174 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
175 if bs % 2 == 1:
175 if bs % 2 == 1:
176 # escaped (e.g. '\{', '\\\{', but not '\\{')
176 # escaped (e.g. '\{', '\\\{', but not '\\{')
177 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
177 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
178 pos = n + 1
178 pos = n + 1
179 continue
179 continue
180 if n > pos:
180 if n > pos:
181 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
181 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
182 if c == quote:
182 if c == quote:
183 return parsed, n + 1
183 return parsed, n + 1
184
184
185 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
185 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
186 parsed.append(parseres)
186 parsed.append(parseres)
187
187
188 if quote:
188 if quote:
189 raise error.ParseError(_("unterminated string"), start)
189 raise error.ParseError(_("unterminated string"), start)
190 return parsed, pos
190 return parsed, pos
191
191
192 def _unnesttemplatelist(tree):
192 def _unnesttemplatelist(tree):
193 """Expand list of templates to node tuple
193 """Expand list of templates to node tuple
194
194
195 >>> def f(tree):
195 >>> def f(tree):
196 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
196 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
197 >>> f((b'template', []))
197 >>> f((b'template', []))
198 (string '')
198 (string '')
199 >>> f((b'template', [(b'string', b'foo')]))
199 >>> f((b'template', [(b'string', b'foo')]))
200 (string 'foo')
200 (string 'foo')
201 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
201 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
202 (template
202 (template
203 (string 'foo')
203 (string 'foo')
204 (symbol 'rev'))
204 (symbol 'rev'))
205 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
205 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
206 (template
206 (template
207 (symbol 'rev'))
207 (symbol 'rev'))
208 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
208 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
209 (string 'foo')
209 (string 'foo')
210 """
210 """
211 if not isinstance(tree, tuple):
211 if not isinstance(tree, tuple):
212 return tree
212 return tree
213 op = tree[0]
213 op = tree[0]
214 if op != 'template':
214 if op != 'template':
215 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
215 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
216
216
217 assert len(tree) == 2
217 assert len(tree) == 2
218 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
218 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
219 if not xs:
219 if not xs:
220 return ('string', '') # empty template ""
220 return ('string', '') # empty template ""
221 elif len(xs) == 1 and xs[0][0] == 'string':
221 elif len(xs) == 1 and xs[0][0] == 'string':
222 return xs[0] # fast path for string with no template fragment "x"
222 return xs[0] # fast path for string with no template fragment "x"
223 else:
223 else:
224 return (op,) + xs
224 return (op,) + xs
225
225
226 def parse(tmpl):
226 def parse(tmpl):
227 """Parse template string into tree"""
227 """Parse template string into tree"""
228 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
228 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
229 assert pos == len(tmpl), 'unquoted template should be consumed'
229 assert pos == len(tmpl), 'unquoted template should be consumed'
230 return _unnesttemplatelist(('template', parsed))
230 return _unnesttemplatelist(('template', parsed))
231
231
232 def _parseexpr(expr):
232 def _parseexpr(expr):
233 """Parse a template expression into tree
233 """Parse a template expression into tree
234
234
235 >>> _parseexpr(b'"foo"')
235 >>> _parseexpr(b'"foo"')
236 ('string', 'foo')
236 ('string', 'foo')
237 >>> _parseexpr(b'foo(bar)')
237 >>> _parseexpr(b'foo(bar)')
238 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
238 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
239 >>> _parseexpr(b'foo(')
239 >>> _parseexpr(b'foo(')
240 Traceback (most recent call last):
240 Traceback (most recent call last):
241 ...
241 ...
242 ParseError: ('not a prefix: end', 4)
242 ParseError: ('not a prefix: end', 4)
243 >>> _parseexpr(b'"foo" "bar"')
243 >>> _parseexpr(b'"foo" "bar"')
244 Traceback (most recent call last):
244 Traceback (most recent call last):
245 ...
245 ...
246 ParseError: ('invalid token', 7)
246 ParseError: ('invalid token', 7)
247 """
247 """
248 p = parser.parser(elements)
248 p = parser.parser(elements)
249 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
249 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
250 if pos != len(expr):
250 if pos != len(expr):
251 raise error.ParseError(_('invalid token'), pos)
251 raise error.ParseError(_('invalid token'), pos)
252 return _unnesttemplatelist(tree)
252 return _unnesttemplatelist(tree)
253
253
254 def prettyformat(tree):
254 def prettyformat(tree):
255 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
255 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
256
256
257 def compileexp(exp, context, curmethods):
257 def compileexp(exp, context, curmethods):
258 """Compile parsed template tree to (func, data) pair"""
258 """Compile parsed template tree to (func, data) pair"""
259 t = exp[0]
259 t = exp[0]
260 if t in curmethods:
260 if t in curmethods:
261 return curmethods[t](exp, context)
261 return curmethods[t](exp, context)
262 raise error.ParseError(_("unknown method '%s'") % t)
262 raise error.ParseError(_("unknown method '%s'") % t)
263
263
264 # template evaluation
264 # template evaluation
265
265
266 def getsymbol(exp):
266 def getsymbol(exp):
267 if exp[0] == 'symbol':
267 if exp[0] == 'symbol':
268 return exp[1]
268 return exp[1]
269 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
269 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
270
270
271 def getlist(x):
271 def getlist(x):
272 if not x:
272 if not x:
273 return []
273 return []
274 if x[0] == 'list':
274 if x[0] == 'list':
275 return getlist(x[1]) + [x[2]]
275 return getlist(x[1]) + [x[2]]
276 return [x]
276 return [x]
277
277
278 def gettemplate(exp, context):
278 def gettemplate(exp, context):
279 """Compile given template tree or load named template from map file;
279 """Compile given template tree or load named template from map file;
280 returns (func, data) pair"""
280 returns (func, data) pair"""
281 if exp[0] in ('template', 'string'):
281 if exp[0] in ('template', 'string'):
282 return compileexp(exp, context, methods)
282 return compileexp(exp, context, methods)
283 if exp[0] == 'symbol':
283 if exp[0] == 'symbol':
284 # unlike runsymbol(), here 'symbol' is always taken as template name
284 # unlike runsymbol(), here 'symbol' is always taken as template name
285 # even if it exists in mapping. this allows us to override mapping
285 # even if it exists in mapping. this allows us to override mapping
286 # by web templates, e.g. 'changelogtag' is redefined in map file.
286 # by web templates, e.g. 'changelogtag' is redefined in map file.
287 return context._load(exp[1])
287 return context._load(exp[1])
288 raise error.ParseError(_("expected template specifier"))
288 raise error.ParseError(_("expected template specifier"))
289
289
290 def findsymbolicname(arg):
290 def findsymbolicname(arg):
291 """Find symbolic name for the given compiled expression; returns None
291 """Find symbolic name for the given compiled expression; returns None
292 if nothing found reliably"""
292 if nothing found reliably"""
293 while True:
293 while True:
294 func, data = arg
294 func, data = arg
295 if func is runsymbol:
295 if func is runsymbol:
296 return data
296 return data
297 elif func is runfilter:
297 elif func is runfilter:
298 arg = data[0]
298 arg = data[0]
299 else:
299 else:
300 return None
300 return None
301
301
302 def evalrawexp(context, mapping, arg):
302 def evalrawexp(context, mapping, arg):
303 """Evaluate given argument as a bare template object which may require
303 """Evaluate given argument as a bare template object which may require
304 further processing (such as folding generator of strings)"""
304 further processing (such as folding generator of strings)"""
305 func, data = arg
305 func, data = arg
306 return func(context, mapping, data)
306 return func(context, mapping, data)
307
307
308 def evalfuncarg(context, mapping, arg):
308 def evalfuncarg(context, mapping, arg):
309 """Evaluate given argument as value type"""
309 """Evaluate given argument as value type"""
310 thing = evalrawexp(context, mapping, arg)
310 thing = evalrawexp(context, mapping, arg)
311 thing = templatekw.unwrapvalue(thing)
311 thing = templatekw.unwrapvalue(thing)
312 # evalrawexp() may return string, generator of strings or arbitrary object
312 # evalrawexp() may return string, generator of strings or arbitrary object
313 # such as date tuple, but filter does not want generator.
313 # such as date tuple, but filter does not want generator.
314 if isinstance(thing, types.GeneratorType):
314 if isinstance(thing, types.GeneratorType):
315 thing = stringify(thing)
315 thing = stringify(thing)
316 return thing
316 return thing
317
317
318 def evalboolean(context, mapping, arg):
318 def evalboolean(context, mapping, arg):
319 """Evaluate given argument as boolean, but also takes boolean literals"""
319 """Evaluate given argument as boolean, but also takes boolean literals"""
320 func, data = arg
320 func, data = arg
321 if func is runsymbol:
321 if func is runsymbol:
322 thing = func(context, mapping, data, default=None)
322 thing = func(context, mapping, data, default=None)
323 if thing is None:
323 if thing is None:
324 # not a template keyword, takes as a boolean literal
324 # not a template keyword, takes as a boolean literal
325 thing = util.parsebool(data)
325 thing = util.parsebool(data)
326 else:
326 else:
327 thing = func(context, mapping, data)
327 thing = func(context, mapping, data)
328 thing = templatekw.unwrapvalue(thing)
328 thing = templatekw.unwrapvalue(thing)
329 if isinstance(thing, bool):
329 if isinstance(thing, bool):
330 return thing
330 return thing
331 # other objects are evaluated as strings, which means 0 is True, but
331 # other objects are evaluated as strings, which means 0 is True, but
332 # empty dict/list should be False as they are expected to be ''
332 # empty dict/list should be False as they are expected to be ''
333 return bool(stringify(thing))
333 return bool(stringify(thing))
334
334
335 def evalinteger(context, mapping, arg, err):
335 def evalinteger(context, mapping, arg, err):
336 v = evalfuncarg(context, mapping, arg)
336 v = evalfuncarg(context, mapping, arg)
337 try:
337 try:
338 return int(v)
338 return int(v)
339 except (TypeError, ValueError):
339 except (TypeError, ValueError):
340 raise error.ParseError(err)
340 raise error.ParseError(err)
341
341
342 def evalstring(context, mapping, arg):
342 def evalstring(context, mapping, arg):
343 return stringify(evalrawexp(context, mapping, arg))
343 return stringify(evalrawexp(context, mapping, arg))
344
344
345 def evalstringliteral(context, mapping, arg):
345 def evalstringliteral(context, mapping, arg):
346 """Evaluate given argument as string template, but returns symbol name
346 """Evaluate given argument as string template, but returns symbol name
347 if it is unknown"""
347 if it is unknown"""
348 func, data = arg
348 func, data = arg
349 if func is runsymbol:
349 if func is runsymbol:
350 thing = func(context, mapping, data, default=data)
350 thing = func(context, mapping, data, default=data)
351 else:
351 else:
352 thing = func(context, mapping, data)
352 thing = func(context, mapping, data)
353 return stringify(thing)
353 return stringify(thing)
354
354
355 def runinteger(context, mapping, data):
355 def runinteger(context, mapping, data):
356 return int(data)
356 return int(data)
357
357
358 def runstring(context, mapping, data):
358 def runstring(context, mapping, data):
359 return data
359 return data
360
360
361 def _recursivesymbolblocker(key):
361 def _recursivesymbolblocker(key):
362 def showrecursion(**args):
362 def showrecursion(**args):
363 raise error.Abort(_("recursive reference '%s' in template") % key)
363 raise error.Abort(_("recursive reference '%s' in template") % key)
364 return showrecursion
364 return showrecursion
365
365
366 def _runrecursivesymbol(context, mapping, key):
366 def _runrecursivesymbol(context, mapping, key):
367 raise error.Abort(_("recursive reference '%s' in template") % key)
367 raise error.Abort(_("recursive reference '%s' in template") % key)
368
368
369 def runsymbol(context, mapping, key, default=''):
369 def runsymbol(context, mapping, key, default=''):
370 v = mapping.get(key)
370 v = mapping.get(key)
371 if v is None:
371 if v is None:
372 v = context._defaults.get(key)
372 v = context._defaults.get(key)
373 if v is None:
373 if v is None:
374 # put poison to cut recursion. we can't move this to parsing phase
374 # put poison to cut recursion. we can't move this to parsing phase
375 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
375 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
376 safemapping = mapping.copy()
376 safemapping = mapping.copy()
377 safemapping[key] = _recursivesymbolblocker(key)
377 safemapping[key] = _recursivesymbolblocker(key)
378 try:
378 try:
379 v = context.process(key, safemapping)
379 v = context.process(key, safemapping)
380 except TemplateNotFound:
380 except TemplateNotFound:
381 v = default
381 v = default
382 if callable(v):
382 if callable(v):
383 return v(**pycompat.strkwargs(mapping))
383 return v(**pycompat.strkwargs(mapping))
384 return v
384 return v
385
385
386 def buildtemplate(exp, context):
386 def buildtemplate(exp, context):
387 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
387 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
388 return (runtemplate, ctmpl)
388 return (runtemplate, ctmpl)
389
389
390 def runtemplate(context, mapping, template):
390 def runtemplate(context, mapping, template):
391 for arg in template:
391 for arg in template:
392 yield evalrawexp(context, mapping, arg)
392 yield evalrawexp(context, mapping, arg)
393
393
394 def buildfilter(exp, context):
394 def buildfilter(exp, context):
395 n = getsymbol(exp[2])
395 n = getsymbol(exp[2])
396 if n in context._filters:
396 if n in context._filters:
397 filt = context._filters[n]
397 filt = context._filters[n]
398 arg = compileexp(exp[1], context, methods)
398 arg = compileexp(exp[1], context, methods)
399 return (runfilter, (arg, filt))
399 return (runfilter, (arg, filt))
400 if n in funcs:
400 if n in funcs:
401 f = funcs[n]
401 f = funcs[n]
402 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
402 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
403 return (f, args)
403 return (f, args)
404 raise error.ParseError(_("unknown function '%s'") % n)
404 raise error.ParseError(_("unknown function '%s'") % n)
405
405
406 def runfilter(context, mapping, data):
406 def runfilter(context, mapping, data):
407 arg, filt = data
407 arg, filt = data
408 thing = evalfuncarg(context, mapping, arg)
408 thing = evalfuncarg(context, mapping, arg)
409 try:
409 try:
410 return filt(thing)
410 return filt(thing)
411 except (ValueError, AttributeError, TypeError):
411 except (ValueError, AttributeError, TypeError):
412 sym = findsymbolicname(arg)
412 sym = findsymbolicname(arg)
413 if sym:
413 if sym:
414 msg = (_("template filter '%s' is not compatible with keyword '%s'")
414 msg = (_("template filter '%s' is not compatible with keyword '%s'")
415 % (filt.func_name, sym))
415 % (filt.func_name, sym))
416 else:
416 else:
417 msg = _("incompatible use of template filter '%s'") % filt.func_name
417 msg = _("incompatible use of template filter '%s'") % filt.func_name
418 raise error.Abort(msg)
418 raise error.Abort(msg)
419
419
420 def buildmap(exp, context):
420 def buildmap(exp, context):
421 darg = compileexp(exp[1], context, methods)
421 darg = compileexp(exp[1], context, methods)
422 targ = gettemplate(exp[2], context)
422 targ = gettemplate(exp[2], context)
423 return (runmap, (darg, targ))
423 return (runmap, (darg, targ))
424
424
425 def runmap(context, mapping, data):
425 def runmap(context, mapping, data):
426 darg, targ = data
426 darg, targ = data
427 d = evalrawexp(context, mapping, darg)
427 d = evalrawexp(context, mapping, darg)
428 if util.safehasattr(d, 'itermaps'):
428 if util.safehasattr(d, 'itermaps'):
429 diter = d.itermaps()
429 diter = d.itermaps()
430 else:
430 else:
431 try:
431 try:
432 diter = iter(d)
432 diter = iter(d)
433 except TypeError:
433 except TypeError:
434 sym = findsymbolicname(darg)
434 sym = findsymbolicname(darg)
435 if sym:
435 if sym:
436 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
436 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
437 else:
437 else:
438 raise error.ParseError(_("%r is not iterable") % d)
438 raise error.ParseError(_("%r is not iterable") % d)
439
439
440 for i, v in enumerate(diter):
440 for i, v in enumerate(diter):
441 lm = mapping.copy()
441 lm = mapping.copy()
442 lm['index'] = i
442 lm['index'] = i
443 if isinstance(v, dict):
443 if isinstance(v, dict):
444 lm.update(v)
444 lm.update(v)
445 lm['originalnode'] = mapping.get('node')
445 lm['originalnode'] = mapping.get('node')
446 yield evalrawexp(context, lm, targ)
446 yield evalrawexp(context, lm, targ)
447 else:
447 else:
448 # v is not an iterable of dicts, this happen when 'key'
448 # v is not an iterable of dicts, this happen when 'key'
449 # has been fully expanded already and format is useless.
449 # has been fully expanded already and format is useless.
450 # If so, return the expanded value.
450 # If so, return the expanded value.
451 yield v
451 yield v
452
452
453 def buildnegate(exp, context):
453 def buildnegate(exp, context):
454 arg = compileexp(exp[1], context, exprmethods)
454 arg = compileexp(exp[1], context, exprmethods)
455 return (runnegate, arg)
455 return (runnegate, arg)
456
456
457 def runnegate(context, mapping, data):
457 def runnegate(context, mapping, data):
458 data = evalinteger(context, mapping, data,
458 data = evalinteger(context, mapping, data,
459 _('negation needs an integer argument'))
459 _('negation needs an integer argument'))
460 return -data
460 return -data
461
461
462 def buildarithmetic(exp, context, func):
462 def buildarithmetic(exp, context, func):
463 left = compileexp(exp[1], context, exprmethods)
463 left = compileexp(exp[1], context, exprmethods)
464 right = compileexp(exp[2], context, exprmethods)
464 right = compileexp(exp[2], context, exprmethods)
465 return (runarithmetic, (func, left, right))
465 return (runarithmetic, (func, left, right))
466
466
467 def runarithmetic(context, mapping, data):
467 def runarithmetic(context, mapping, data):
468 func, left, right = data
468 func, left, right = data
469 left = evalinteger(context, mapping, left,
469 left = evalinteger(context, mapping, left,
470 _('arithmetic only defined on integers'))
470 _('arithmetic only defined on integers'))
471 right = evalinteger(context, mapping, right,
471 right = evalinteger(context, mapping, right,
472 _('arithmetic only defined on integers'))
472 _('arithmetic only defined on integers'))
473 try:
473 try:
474 return func(left, right)
474 return func(left, right)
475 except ZeroDivisionError:
475 except ZeroDivisionError:
476 raise error.Abort(_('division by zero is not defined'))
476 raise error.Abort(_('division by zero is not defined'))
477
477
478 def buildfunc(exp, context):
478 def buildfunc(exp, context):
479 n = getsymbol(exp[1])
479 n = getsymbol(exp[1])
480 if n in funcs:
480 if n in funcs:
481 f = funcs[n]
481 f = funcs[n]
482 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
482 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
483 return (f, args)
483 return (f, args)
484 if n in context._filters:
484 if n in context._filters:
485 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
485 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
486 if len(args) != 1:
486 if len(args) != 1:
487 raise error.ParseError(_("filter %s expects one argument") % n)
487 raise error.ParseError(_("filter %s expects one argument") % n)
488 f = context._filters[n]
488 f = context._filters[n]
489 return (runfilter, (args[0], f))
489 return (runfilter, (args[0], f))
490 raise error.ParseError(_("unknown function '%s'") % n)
490 raise error.ParseError(_("unknown function '%s'") % n)
491
491
492 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
492 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
493 """Compile parsed tree of function arguments into list or dict of
493 """Compile parsed tree of function arguments into list or dict of
494 (func, data) pairs
494 (func, data) pairs
495
495
496 >>> context = engine(lambda t: (runsymbol, t))
496 >>> context = engine(lambda t: (runsymbol, t))
497 >>> def fargs(expr, argspec):
497 >>> def fargs(expr, argspec):
498 ... x = _parseexpr(expr)
498 ... x = _parseexpr(expr)
499 ... n = getsymbol(x[1])
499 ... n = getsymbol(x[1])
500 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
500 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
501 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
501 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
502 ['l', 'k']
502 ['l', 'k']
503 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
503 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
504 >>> list(args.keys()), list(args[b'opts'].keys())
504 >>> list(args.keys()), list(args[b'opts'].keys())
505 (['opts'], ['opts', 'k'])
505 (['opts'], ['opts', 'k'])
506 """
506 """
507 def compiledict(xs):
507 def compiledict(xs):
508 return util.sortdict((k, compileexp(x, context, curmethods))
508 return util.sortdict((k, compileexp(x, context, curmethods))
509 for k, x in xs.iteritems())
509 for k, x in xs.iteritems())
510 def compilelist(xs):
510 def compilelist(xs):
511 return [compileexp(x, context, curmethods) for x in xs]
511 return [compileexp(x, context, curmethods) for x in xs]
512
512
513 if not argspec:
513 if not argspec:
514 # filter or function with no argspec: return list of positional args
514 # filter or function with no argspec: return list of positional args
515 return compilelist(getlist(exp))
515 return compilelist(getlist(exp))
516
516
517 # function with argspec: return dict of named args
517 # function with argspec: return dict of named args
518 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
518 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
519 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
519 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
520 keyvaluenode='keyvalue', keynode='symbol')
520 keyvaluenode='keyvalue', keynode='symbol')
521 compargs = util.sortdict()
521 compargs = util.sortdict()
522 if varkey:
522 if varkey:
523 compargs[varkey] = compilelist(treeargs.pop(varkey))
523 compargs[varkey] = compilelist(treeargs.pop(varkey))
524 if optkey:
524 if optkey:
525 compargs[optkey] = compiledict(treeargs.pop(optkey))
525 compargs[optkey] = compiledict(treeargs.pop(optkey))
526 compargs.update(compiledict(treeargs))
526 compargs.update(compiledict(treeargs))
527 return compargs
527 return compargs
528
528
529 def buildkeyvaluepair(exp, content):
529 def buildkeyvaluepair(exp, content):
530 raise error.ParseError(_("can't use a key-value pair in this context"))
530 raise error.ParseError(_("can't use a key-value pair in this context"))
531
531
532 # dict of template built-in functions
532 # dict of template built-in functions
533 funcs = {}
533 funcs = {}
534
534
535 templatefunc = registrar.templatefunc(funcs)
535 templatefunc = registrar.templatefunc(funcs)
536
536
537 @templatefunc('date(date[, fmt])')
537 @templatefunc('date(date[, fmt])')
538 def date(context, mapping, args):
538 def date(context, mapping, args):
539 """Format a date. See :hg:`help dates` for formatting
539 """Format a date. See :hg:`help dates` for formatting
540 strings. The default is a Unix date format, including the timezone:
540 strings. The default is a Unix date format, including the timezone:
541 "Mon Sep 04 15:13:13 2006 0700"."""
541 "Mon Sep 04 15:13:13 2006 0700"."""
542 if not (1 <= len(args) <= 2):
542 if not (1 <= len(args) <= 2):
543 # i18n: "date" is a keyword
543 # i18n: "date" is a keyword
544 raise error.ParseError(_("date expects one or two arguments"))
544 raise error.ParseError(_("date expects one or two arguments"))
545
545
546 date = evalfuncarg(context, mapping, args[0])
546 date = evalfuncarg(context, mapping, args[0])
547 fmt = None
547 fmt = None
548 if len(args) == 2:
548 if len(args) == 2:
549 fmt = evalstring(context, mapping, args[1])
549 fmt = evalstring(context, mapping, args[1])
550 try:
550 try:
551 if fmt is None:
551 if fmt is None:
552 return util.datestr(date)
552 return util.datestr(date)
553 else:
553 else:
554 return util.datestr(date, fmt)
554 return util.datestr(date, fmt)
555 except (TypeError, ValueError):
555 except (TypeError, ValueError):
556 # i18n: "date" is a keyword
556 # i18n: "date" is a keyword
557 raise error.ParseError(_("date expects a date information"))
557 raise error.ParseError(_("date expects a date information"))
558
558
559 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
559 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
560 def dict_(context, mapping, args):
560 def dict_(context, mapping, args):
561 """Construct a dict from key-value pairs. A key may be omitted if
561 """Construct a dict from key-value pairs. A key may be omitted if
562 a value expression can provide an unambiguous name."""
562 a value expression can provide an unambiguous name."""
563 data = util.sortdict()
563 data = util.sortdict()
564
564
565 for v in args['args']:
565 for v in args['args']:
566 k = findsymbolicname(v)
566 k = findsymbolicname(v)
567 if not k:
567 if not k:
568 raise error.ParseError(_('dict key cannot be inferred'))
568 raise error.ParseError(_('dict key cannot be inferred'))
569 if k in data or k in args['kwargs']:
569 if k in data or k in args['kwargs']:
570 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
570 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
571 data[k] = evalfuncarg(context, mapping, v)
571 data[k] = evalfuncarg(context, mapping, v)
572
572
573 data.update((k, evalfuncarg(context, mapping, v))
573 data.update((k, evalfuncarg(context, mapping, v))
574 for k, v in args['kwargs'].iteritems())
574 for k, v in args['kwargs'].iteritems())
575 return templatekw.hybriddict(data)
575 return templatekw.hybriddict(data)
576
576
577 @templatefunc('diff([includepattern [, excludepattern]])')
577 @templatefunc('diff([includepattern [, excludepattern]])')
578 def diff(context, mapping, args):
578 def diff(context, mapping, args):
579 """Show a diff, optionally
579 """Show a diff, optionally
580 specifying files to include or exclude."""
580 specifying files to include or exclude."""
581 if len(args) > 2:
581 if len(args) > 2:
582 # i18n: "diff" is a keyword
582 # i18n: "diff" is a keyword
583 raise error.ParseError(_("diff expects zero, one, or two arguments"))
583 raise error.ParseError(_("diff expects zero, one, or two arguments"))
584
584
585 def getpatterns(i):
585 def getpatterns(i):
586 if i < len(args):
586 if i < len(args):
587 s = evalstring(context, mapping, args[i]).strip()
587 s = evalstring(context, mapping, args[i]).strip()
588 if s:
588 if s:
589 return [s]
589 return [s]
590 return []
590 return []
591
591
592 ctx = mapping['ctx']
592 ctx = mapping['ctx']
593 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
593 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
594
594
595 return ''.join(chunks)
595 return ''.join(chunks)
596
596
597 @templatefunc('extdata(source)', argspec='source')
597 @templatefunc('extdata(source)', argspec='source')
598 def extdata(context, mapping, args):
598 def extdata(context, mapping, args):
599 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
599 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
600 if 'source' not in args:
600 if 'source' not in args:
601 # i18n: "extdata" is a keyword
601 # i18n: "extdata" is a keyword
602 raise error.ParseError(_('extdata expects one argument'))
602 raise error.ParseError(_('extdata expects one argument'))
603
603
604 source = evalstring(context, mapping, args['source'])
604 source = evalstring(context, mapping, args['source'])
605 cache = mapping['cache'].setdefault('extdata', {})
605 cache = mapping['cache'].setdefault('extdata', {})
606 ctx = mapping['ctx']
606 ctx = mapping['ctx']
607 if source in cache:
607 if source in cache:
608 data = cache[source]
608 data = cache[source]
609 else:
609 else:
610 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
610 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
611 return data.get(ctx.rev(), '')
611 return data.get(ctx.rev(), '')
612
612
613 @templatefunc('files(pattern)')
613 @templatefunc('files(pattern)')
614 def files(context, mapping, args):
614 def files(context, mapping, args):
615 """All files of the current changeset matching the pattern. See
615 """All files of the current changeset matching the pattern. See
616 :hg:`help patterns`."""
616 :hg:`help patterns`."""
617 if not len(args) == 1:
617 if not len(args) == 1:
618 # i18n: "files" is a keyword
618 # i18n: "files" is a keyword
619 raise error.ParseError(_("files expects one argument"))
619 raise error.ParseError(_("files expects one argument"))
620
620
621 raw = evalstring(context, mapping, args[0])
621 raw = evalstring(context, mapping, args[0])
622 ctx = mapping['ctx']
622 ctx = mapping['ctx']
623 m = ctx.match([raw])
623 m = ctx.match([raw])
624 files = list(ctx.matches(m))
624 files = list(ctx.matches(m))
625 return templatekw.showlist("file", files, mapping)
625 return templatekw.showlist("file", files, mapping)
626
626
627 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
627 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
628 def fill(context, mapping, args):
628 def fill(context, mapping, args):
629 """Fill many
629 """Fill many
630 paragraphs with optional indentation. See the "fill" filter."""
630 paragraphs with optional indentation. See the "fill" filter."""
631 if not (1 <= len(args) <= 4):
631 if not (1 <= len(args) <= 4):
632 # i18n: "fill" is a keyword
632 # i18n: "fill" is a keyword
633 raise error.ParseError(_("fill expects one to four arguments"))
633 raise error.ParseError(_("fill expects one to four arguments"))
634
634
635 text = evalstring(context, mapping, args[0])
635 text = evalstring(context, mapping, args[0])
636 width = 76
636 width = 76
637 initindent = ''
637 initindent = ''
638 hangindent = ''
638 hangindent = ''
639 if 2 <= len(args) <= 4:
639 if 2 <= len(args) <= 4:
640 width = evalinteger(context, mapping, args[1],
640 width = evalinteger(context, mapping, args[1],
641 # i18n: "fill" is a keyword
641 # i18n: "fill" is a keyword
642 _("fill expects an integer width"))
642 _("fill expects an integer width"))
643 try:
643 try:
644 initindent = evalstring(context, mapping, args[2])
644 initindent = evalstring(context, mapping, args[2])
645 hangindent = evalstring(context, mapping, args[3])
645 hangindent = evalstring(context, mapping, args[3])
646 except IndexError:
646 except IndexError:
647 pass
647 pass
648
648
649 return templatefilters.fill(text, width, initindent, hangindent)
649 return templatefilters.fill(text, width, initindent, hangindent)
650
650
651 @templatefunc('formatnode(node)')
651 @templatefunc('formatnode(node)')
652 def formatnode(context, mapping, args):
652 def formatnode(context, mapping, args):
653 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
653 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
654 if len(args) != 1:
654 if len(args) != 1:
655 # i18n: "formatnode" is a keyword
655 # i18n: "formatnode" is a keyword
656 raise error.ParseError(_("formatnode expects one argument"))
656 raise error.ParseError(_("formatnode expects one argument"))
657
657
658 ui = mapping['ui']
658 ui = mapping['ui']
659 node = evalstring(context, mapping, args[0])
659 node = evalstring(context, mapping, args[0])
660 if ui.debugflag:
660 if ui.debugflag:
661 return node
661 return node
662 return templatefilters.short(node)
662 return templatefilters.short(node)
663
663
664 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
664 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
665 argspec='text width fillchar left')
665 argspec='text width fillchar left')
666 def pad(context, mapping, args):
666 def pad(context, mapping, args):
667 """Pad text with a
667 """Pad text with a
668 fill character."""
668 fill character."""
669 if 'text' not in args or 'width' not in args:
669 if 'text' not in args or 'width' not in args:
670 # i18n: "pad" is a keyword
670 # i18n: "pad" is a keyword
671 raise error.ParseError(_("pad() expects two to four arguments"))
671 raise error.ParseError(_("pad() expects two to four arguments"))
672
672
673 width = evalinteger(context, mapping, args['width'],
673 width = evalinteger(context, mapping, args['width'],
674 # i18n: "pad" is a keyword
674 # i18n: "pad" is a keyword
675 _("pad() expects an integer width"))
675 _("pad() expects an integer width"))
676
676
677 text = evalstring(context, mapping, args['text'])
677 text = evalstring(context, mapping, args['text'])
678
678
679 left = False
679 left = False
680 fillchar = ' '
680 fillchar = ' '
681 if 'fillchar' in args:
681 if 'fillchar' in args:
682 fillchar = evalstring(context, mapping, args['fillchar'])
682 fillchar = evalstring(context, mapping, args['fillchar'])
683 if len(color.stripeffects(fillchar)) != 1:
683 if len(color.stripeffects(fillchar)) != 1:
684 # i18n: "pad" is a keyword
684 # i18n: "pad" is a keyword
685 raise error.ParseError(_("pad() expects a single fill character"))
685 raise error.ParseError(_("pad() expects a single fill character"))
686 if 'left' in args:
686 if 'left' in args:
687 left = evalboolean(context, mapping, args['left'])
687 left = evalboolean(context, mapping, args['left'])
688
688
689 fillwidth = width - encoding.colwidth(color.stripeffects(text))
689 fillwidth = width - encoding.colwidth(color.stripeffects(text))
690 if fillwidth <= 0:
690 if fillwidth <= 0:
691 return text
691 return text
692 if left:
692 if left:
693 return fillchar * fillwidth + text
693 return fillchar * fillwidth + text
694 else:
694 else:
695 return text + fillchar * fillwidth
695 return text + fillchar * fillwidth
696
696
697 @templatefunc('indent(text, indentchars[, firstline])')
697 @templatefunc('indent(text, indentchars[, firstline])')
698 def indent(context, mapping, args):
698 def indent(context, mapping, args):
699 """Indents all non-empty lines
699 """Indents all non-empty lines
700 with the characters given in the indentchars string. An optional
700 with the characters given in the indentchars string. An optional
701 third parameter will override the indent for the first line only
701 third parameter will override the indent for the first line only
702 if present."""
702 if present."""
703 if not (2 <= len(args) <= 3):
703 if not (2 <= len(args) <= 3):
704 # i18n: "indent" is a keyword
704 # i18n: "indent" is a keyword
705 raise error.ParseError(_("indent() expects two or three arguments"))
705 raise error.ParseError(_("indent() expects two or three arguments"))
706
706
707 text = evalstring(context, mapping, args[0])
707 text = evalstring(context, mapping, args[0])
708 indent = evalstring(context, mapping, args[1])
708 indent = evalstring(context, mapping, args[1])
709
709
710 if len(args) == 3:
710 if len(args) == 3:
711 firstline = evalstring(context, mapping, args[2])
711 firstline = evalstring(context, mapping, args[2])
712 else:
712 else:
713 firstline = indent
713 firstline = indent
714
714
715 # the indent function doesn't indent the first line, so we do it here
715 # the indent function doesn't indent the first line, so we do it here
716 return templatefilters.indent(firstline + text, indent)
716 return templatefilters.indent(firstline + text, indent)
717
717
718 @templatefunc('get(dict, key)')
718 @templatefunc('get(dict, key)')
719 def get(context, mapping, args):
719 def get(context, mapping, args):
720 """Get an attribute/key from an object. Some keywords
720 """Get an attribute/key from an object. Some keywords
721 are complex types. This function allows you to obtain the value of an
721 are complex types. This function allows you to obtain the value of an
722 attribute on these types."""
722 attribute on these types."""
723 if len(args) != 2:
723 if len(args) != 2:
724 # i18n: "get" is a keyword
724 # i18n: "get" is a keyword
725 raise error.ParseError(_("get() expects two arguments"))
725 raise error.ParseError(_("get() expects two arguments"))
726
726
727 dictarg = evalfuncarg(context, mapping, args[0])
727 dictarg = evalfuncarg(context, mapping, args[0])
728 if not util.safehasattr(dictarg, 'get'):
728 if not util.safehasattr(dictarg, 'get'):
729 # i18n: "get" is a keyword
729 # i18n: "get" is a keyword
730 raise error.ParseError(_("get() expects a dict as first argument"))
730 raise error.ParseError(_("get() expects a dict as first argument"))
731
731
732 key = evalfuncarg(context, mapping, args[1])
732 key = evalfuncarg(context, mapping, args[1])
733 return dictarg.get(key)
733 val = dictarg.get(key)
734 if val is None:
735 return
736 return templatekw.wraphybridvalue(dictarg, key, val)
734
737
735 @templatefunc('if(expr, then[, else])')
738 @templatefunc('if(expr, then[, else])')
736 def if_(context, mapping, args):
739 def if_(context, mapping, args):
737 """Conditionally execute based on the result of
740 """Conditionally execute based on the result of
738 an expression."""
741 an expression."""
739 if not (2 <= len(args) <= 3):
742 if not (2 <= len(args) <= 3):
740 # i18n: "if" is a keyword
743 # i18n: "if" is a keyword
741 raise error.ParseError(_("if expects two or three arguments"))
744 raise error.ParseError(_("if expects two or three arguments"))
742
745
743 test = evalboolean(context, mapping, args[0])
746 test = evalboolean(context, mapping, args[0])
744 if test:
747 if test:
745 yield evalrawexp(context, mapping, args[1])
748 yield evalrawexp(context, mapping, args[1])
746 elif len(args) == 3:
749 elif len(args) == 3:
747 yield evalrawexp(context, mapping, args[2])
750 yield evalrawexp(context, mapping, args[2])
748
751
749 @templatefunc('ifcontains(needle, haystack, then[, else])')
752 @templatefunc('ifcontains(needle, haystack, then[, else])')
750 def ifcontains(context, mapping, args):
753 def ifcontains(context, mapping, args):
751 """Conditionally execute based
754 """Conditionally execute based
752 on whether the item "needle" is in "haystack"."""
755 on whether the item "needle" is in "haystack"."""
753 if not (3 <= len(args) <= 4):
756 if not (3 <= len(args) <= 4):
754 # i18n: "ifcontains" is a keyword
757 # i18n: "ifcontains" is a keyword
755 raise error.ParseError(_("ifcontains expects three or four arguments"))
758 raise error.ParseError(_("ifcontains expects three or four arguments"))
756
759
757 needle = evalstring(context, mapping, args[0])
760 needle = evalstring(context, mapping, args[0])
758 haystack = evalfuncarg(context, mapping, args[1])
761 haystack = evalfuncarg(context, mapping, args[1])
759
762
760 if needle in haystack:
763 if needle in haystack:
761 yield evalrawexp(context, mapping, args[2])
764 yield evalrawexp(context, mapping, args[2])
762 elif len(args) == 4:
765 elif len(args) == 4:
763 yield evalrawexp(context, mapping, args[3])
766 yield evalrawexp(context, mapping, args[3])
764
767
765 @templatefunc('ifeq(expr1, expr2, then[, else])')
768 @templatefunc('ifeq(expr1, expr2, then[, else])')
766 def ifeq(context, mapping, args):
769 def ifeq(context, mapping, args):
767 """Conditionally execute based on
770 """Conditionally execute based on
768 whether 2 items are equivalent."""
771 whether 2 items are equivalent."""
769 if not (3 <= len(args) <= 4):
772 if not (3 <= len(args) <= 4):
770 # i18n: "ifeq" is a keyword
773 # i18n: "ifeq" is a keyword
771 raise error.ParseError(_("ifeq expects three or four arguments"))
774 raise error.ParseError(_("ifeq expects three or four arguments"))
772
775
773 test = evalstring(context, mapping, args[0])
776 test = evalstring(context, mapping, args[0])
774 match = evalstring(context, mapping, args[1])
777 match = evalstring(context, mapping, args[1])
775 if test == match:
778 if test == match:
776 yield evalrawexp(context, mapping, args[2])
779 yield evalrawexp(context, mapping, args[2])
777 elif len(args) == 4:
780 elif len(args) == 4:
778 yield evalrawexp(context, mapping, args[3])
781 yield evalrawexp(context, mapping, args[3])
779
782
780 @templatefunc('join(list, sep)')
783 @templatefunc('join(list, sep)')
781 def join(context, mapping, args):
784 def join(context, mapping, args):
782 """Join items in a list with a delimiter."""
785 """Join items in a list with a delimiter."""
783 if not (1 <= len(args) <= 2):
786 if not (1 <= len(args) <= 2):
784 # i18n: "join" is a keyword
787 # i18n: "join" is a keyword
785 raise error.ParseError(_("join expects one or two arguments"))
788 raise error.ParseError(_("join expects one or two arguments"))
786
789
787 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
790 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
788 # abuses generator as a keyword that returns a list of dicts.
791 # abuses generator as a keyword that returns a list of dicts.
789 joinset = evalrawexp(context, mapping, args[0])
792 joinset = evalrawexp(context, mapping, args[0])
790 joinset = templatekw.unwrapvalue(joinset)
793 joinset = templatekw.unwrapvalue(joinset)
791 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
794 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
792 joiner = " "
795 joiner = " "
793 if len(args) > 1:
796 if len(args) > 1:
794 joiner = evalstring(context, mapping, args[1])
797 joiner = evalstring(context, mapping, args[1])
795
798
796 first = True
799 first = True
797 for x in joinset:
800 for x in joinset:
798 if first:
801 if first:
799 first = False
802 first = False
800 else:
803 else:
801 yield joiner
804 yield joiner
802 yield joinfmt(x)
805 yield joinfmt(x)
803
806
804 @templatefunc('label(label, expr)')
807 @templatefunc('label(label, expr)')
805 def label(context, mapping, args):
808 def label(context, mapping, args):
806 """Apply a label to generated content. Content with
809 """Apply a label to generated content. Content with
807 a label applied can result in additional post-processing, such as
810 a label applied can result in additional post-processing, such as
808 automatic colorization."""
811 automatic colorization."""
809 if len(args) != 2:
812 if len(args) != 2:
810 # i18n: "label" is a keyword
813 # i18n: "label" is a keyword
811 raise error.ParseError(_("label expects two arguments"))
814 raise error.ParseError(_("label expects two arguments"))
812
815
813 ui = mapping['ui']
816 ui = mapping['ui']
814 thing = evalstring(context, mapping, args[1])
817 thing = evalstring(context, mapping, args[1])
815 # preserve unknown symbol as literal so effects like 'red', 'bold',
818 # preserve unknown symbol as literal so effects like 'red', 'bold',
816 # etc. don't need to be quoted
819 # etc. don't need to be quoted
817 label = evalstringliteral(context, mapping, args[0])
820 label = evalstringliteral(context, mapping, args[0])
818
821
819 return ui.label(thing, label)
822 return ui.label(thing, label)
820
823
821 @templatefunc('latesttag([pattern])')
824 @templatefunc('latesttag([pattern])')
822 def latesttag(context, mapping, args):
825 def latesttag(context, mapping, args):
823 """The global tags matching the given pattern on the
826 """The global tags matching the given pattern on the
824 most recent globally tagged ancestor of this changeset.
827 most recent globally tagged ancestor of this changeset.
825 If no such tags exist, the "{tag}" template resolves to
828 If no such tags exist, the "{tag}" template resolves to
826 the string "null"."""
829 the string "null"."""
827 if len(args) > 1:
830 if len(args) > 1:
828 # i18n: "latesttag" is a keyword
831 # i18n: "latesttag" is a keyword
829 raise error.ParseError(_("latesttag expects at most one argument"))
832 raise error.ParseError(_("latesttag expects at most one argument"))
830
833
831 pattern = None
834 pattern = None
832 if len(args) == 1:
835 if len(args) == 1:
833 pattern = evalstring(context, mapping, args[0])
836 pattern = evalstring(context, mapping, args[0])
834
837
835 return templatekw.showlatesttags(pattern, **mapping)
838 return templatekw.showlatesttags(pattern, **mapping)
836
839
837 @templatefunc('localdate(date[, tz])')
840 @templatefunc('localdate(date[, tz])')
838 def localdate(context, mapping, args):
841 def localdate(context, mapping, args):
839 """Converts a date to the specified timezone.
842 """Converts a date to the specified timezone.
840 The default is local date."""
843 The default is local date."""
841 if not (1 <= len(args) <= 2):
844 if not (1 <= len(args) <= 2):
842 # i18n: "localdate" is a keyword
845 # i18n: "localdate" is a keyword
843 raise error.ParseError(_("localdate expects one or two arguments"))
846 raise error.ParseError(_("localdate expects one or two arguments"))
844
847
845 date = evalfuncarg(context, mapping, args[0])
848 date = evalfuncarg(context, mapping, args[0])
846 try:
849 try:
847 date = util.parsedate(date)
850 date = util.parsedate(date)
848 except AttributeError: # not str nor date tuple
851 except AttributeError: # not str nor date tuple
849 # i18n: "localdate" is a keyword
852 # i18n: "localdate" is a keyword
850 raise error.ParseError(_("localdate expects a date information"))
853 raise error.ParseError(_("localdate expects a date information"))
851 if len(args) >= 2:
854 if len(args) >= 2:
852 tzoffset = None
855 tzoffset = None
853 tz = evalfuncarg(context, mapping, args[1])
856 tz = evalfuncarg(context, mapping, args[1])
854 if isinstance(tz, str):
857 if isinstance(tz, str):
855 tzoffset, remainder = util.parsetimezone(tz)
858 tzoffset, remainder = util.parsetimezone(tz)
856 if remainder:
859 if remainder:
857 tzoffset = None
860 tzoffset = None
858 if tzoffset is None:
861 if tzoffset is None:
859 try:
862 try:
860 tzoffset = int(tz)
863 tzoffset = int(tz)
861 except (TypeError, ValueError):
864 except (TypeError, ValueError):
862 # i18n: "localdate" is a keyword
865 # i18n: "localdate" is a keyword
863 raise error.ParseError(_("localdate expects a timezone"))
866 raise error.ParseError(_("localdate expects a timezone"))
864 else:
867 else:
865 tzoffset = util.makedate()[1]
868 tzoffset = util.makedate()[1]
866 return (date[0], tzoffset)
869 return (date[0], tzoffset)
867
870
868 @templatefunc('max(iterable)')
871 @templatefunc('max(iterable)')
869 def max_(context, mapping, args, **kwargs):
872 def max_(context, mapping, args, **kwargs):
870 """Return the max of an iterable"""
873 """Return the max of an iterable"""
871 if len(args) != 1:
874 if len(args) != 1:
872 # i18n: "max" is a keyword
875 # i18n: "max" is a keyword
873 raise error.ParseError(_("max expects one arguments"))
876 raise error.ParseError(_("max expects one arguments"))
874
877
875 iterable = evalfuncarg(context, mapping, args[0])
878 iterable = evalfuncarg(context, mapping, args[0])
876 try:
879 try:
877 return max(iterable)
880 x = max(iterable)
878 except (TypeError, ValueError):
881 except (TypeError, ValueError):
879 # i18n: "max" is a keyword
882 # i18n: "max" is a keyword
880 raise error.ParseError(_("max first argument should be an iterable"))
883 raise error.ParseError(_("max first argument should be an iterable"))
884 return templatekw.wraphybridvalue(iterable, x, x)
881
885
882 @templatefunc('min(iterable)')
886 @templatefunc('min(iterable)')
883 def min_(context, mapping, args, **kwargs):
887 def min_(context, mapping, args, **kwargs):
884 """Return the min of an iterable"""
888 """Return the min of an iterable"""
885 if len(args) != 1:
889 if len(args) != 1:
886 # i18n: "min" is a keyword
890 # i18n: "min" is a keyword
887 raise error.ParseError(_("min expects one arguments"))
891 raise error.ParseError(_("min expects one arguments"))
888
892
889 iterable = evalfuncarg(context, mapping, args[0])
893 iterable = evalfuncarg(context, mapping, args[0])
890 try:
894 try:
891 return min(iterable)
895 x = min(iterable)
892 except (TypeError, ValueError):
896 except (TypeError, ValueError):
893 # i18n: "min" is a keyword
897 # i18n: "min" is a keyword
894 raise error.ParseError(_("min first argument should be an iterable"))
898 raise error.ParseError(_("min first argument should be an iterable"))
899 return templatekw.wraphybridvalue(iterable, x, x)
895
900
896 @templatefunc('mod(a, b)')
901 @templatefunc('mod(a, b)')
897 def mod(context, mapping, args):
902 def mod(context, mapping, args):
898 """Calculate a mod b such that a / b + a mod b == a"""
903 """Calculate a mod b such that a / b + a mod b == a"""
899 if not len(args) == 2:
904 if not len(args) == 2:
900 # i18n: "mod" is a keyword
905 # i18n: "mod" is a keyword
901 raise error.ParseError(_("mod expects two arguments"))
906 raise error.ParseError(_("mod expects two arguments"))
902
907
903 func = lambda a, b: a % b
908 func = lambda a, b: a % b
904 return runarithmetic(context, mapping, (func, args[0], args[1]))
909 return runarithmetic(context, mapping, (func, args[0], args[1]))
905
910
906 @templatefunc('obsfateoperations(markers)')
911 @templatefunc('obsfateoperations(markers)')
907 def obsfateoperations(context, mapping, args):
912 def obsfateoperations(context, mapping, args):
908 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
913 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
909 if len(args) != 1:
914 if len(args) != 1:
910 # i18n: "obsfateoperations" is a keyword
915 # i18n: "obsfateoperations" is a keyword
911 raise error.ParseError(_("obsfateoperations expects one arguments"))
916 raise error.ParseError(_("obsfateoperations expects one arguments"))
912
917
913 markers = evalfuncarg(context, mapping, args[0])
918 markers = evalfuncarg(context, mapping, args[0])
914
919
915 try:
920 try:
916 data = obsutil.markersoperations(markers)
921 data = obsutil.markersoperations(markers)
917 return templatekw.hybridlist(data, name='operation')
922 return templatekw.hybridlist(data, name='operation')
918 except (TypeError, KeyError):
923 except (TypeError, KeyError):
919 # i18n: "obsfateoperations" is a keyword
924 # i18n: "obsfateoperations" is a keyword
920 errmsg = _("obsfateoperations first argument should be an iterable")
925 errmsg = _("obsfateoperations first argument should be an iterable")
921 raise error.ParseError(errmsg)
926 raise error.ParseError(errmsg)
922
927
923 @templatefunc('obsfatedate(markers)')
928 @templatefunc('obsfatedate(markers)')
924 def obsfatedate(context, mapping, args):
929 def obsfatedate(context, mapping, args):
925 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
930 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
926 if len(args) != 1:
931 if len(args) != 1:
927 # i18n: "obsfatedate" is a keyword
932 # i18n: "obsfatedate" is a keyword
928 raise error.ParseError(_("obsfatedate expects one arguments"))
933 raise error.ParseError(_("obsfatedate expects one arguments"))
929
934
930 markers = evalfuncarg(context, mapping, args[0])
935 markers = evalfuncarg(context, mapping, args[0])
931
936
932 try:
937 try:
933 data = obsutil.markersdates(markers)
938 data = obsutil.markersdates(markers)
934 return templatekw.hybridlist(data, name='date', fmt='%d %d')
939 return templatekw.hybridlist(data, name='date', fmt='%d %d')
935 except (TypeError, KeyError):
940 except (TypeError, KeyError):
936 # i18n: "obsfatedate" is a keyword
941 # i18n: "obsfatedate" is a keyword
937 errmsg = _("obsfatedate first argument should be an iterable")
942 errmsg = _("obsfatedate first argument should be an iterable")
938 raise error.ParseError(errmsg)
943 raise error.ParseError(errmsg)
939
944
940 @templatefunc('obsfateusers(markers)')
945 @templatefunc('obsfateusers(markers)')
941 def obsfateusers(context, mapping, args):
946 def obsfateusers(context, mapping, args):
942 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
947 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
943 if len(args) != 1:
948 if len(args) != 1:
944 # i18n: "obsfateusers" is a keyword
949 # i18n: "obsfateusers" is a keyword
945 raise error.ParseError(_("obsfateusers expects one arguments"))
950 raise error.ParseError(_("obsfateusers expects one arguments"))
946
951
947 markers = evalfuncarg(context, mapping, args[0])
952 markers = evalfuncarg(context, mapping, args[0])
948
953
949 try:
954 try:
950 data = obsutil.markersusers(markers)
955 data = obsutil.markersusers(markers)
951 return templatekw.hybridlist(data, name='user')
956 return templatekw.hybridlist(data, name='user')
952 except (TypeError, KeyError, ValueError):
957 except (TypeError, KeyError, ValueError):
953 # i18n: "obsfateusers" is a keyword
958 # i18n: "obsfateusers" is a keyword
954 msg = _("obsfateusers first argument should be an iterable of "
959 msg = _("obsfateusers first argument should be an iterable of "
955 "obsmakers")
960 "obsmakers")
956 raise error.ParseError(msg)
961 raise error.ParseError(msg)
957
962
958 @templatefunc('obsfateverb(successors)')
963 @templatefunc('obsfateverb(successors)')
959 def obsfateverb(context, mapping, args):
964 def obsfateverb(context, mapping, args):
960 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
965 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
961 if len(args) != 1:
966 if len(args) != 1:
962 # i18n: "obsfateverb" is a keyword
967 # i18n: "obsfateverb" is a keyword
963 raise error.ParseError(_("obsfateverb expects one arguments"))
968 raise error.ParseError(_("obsfateverb expects one arguments"))
964
969
965 successors = evalfuncarg(context, mapping, args[0])
970 successors = evalfuncarg(context, mapping, args[0])
966
971
967 try:
972 try:
968 return obsutil.successorsetverb(successors)
973 return obsutil.successorsetverb(successors)
969 except TypeError:
974 except TypeError:
970 # i18n: "obsfateverb" is a keyword
975 # i18n: "obsfateverb" is a keyword
971 errmsg = _("obsfateverb first argument should be countable")
976 errmsg = _("obsfateverb first argument should be countable")
972 raise error.ParseError(errmsg)
977 raise error.ParseError(errmsg)
973
978
974 @templatefunc('relpath(path)')
979 @templatefunc('relpath(path)')
975 def relpath(context, mapping, args):
980 def relpath(context, mapping, args):
976 """Convert a repository-absolute path into a filesystem path relative to
981 """Convert a repository-absolute path into a filesystem path relative to
977 the current working directory."""
982 the current working directory."""
978 if len(args) != 1:
983 if len(args) != 1:
979 # i18n: "relpath" is a keyword
984 # i18n: "relpath" is a keyword
980 raise error.ParseError(_("relpath expects one argument"))
985 raise error.ParseError(_("relpath expects one argument"))
981
986
982 repo = mapping['ctx'].repo()
987 repo = mapping['ctx'].repo()
983 path = evalstring(context, mapping, args[0])
988 path = evalstring(context, mapping, args[0])
984 return repo.pathto(path)
989 return repo.pathto(path)
985
990
986 @templatefunc('revset(query[, formatargs...])')
991 @templatefunc('revset(query[, formatargs...])')
987 def revset(context, mapping, args):
992 def revset(context, mapping, args):
988 """Execute a revision set query. See
993 """Execute a revision set query. See
989 :hg:`help revset`."""
994 :hg:`help revset`."""
990 if not len(args) > 0:
995 if not len(args) > 0:
991 # i18n: "revset" is a keyword
996 # i18n: "revset" is a keyword
992 raise error.ParseError(_("revset expects one or more arguments"))
997 raise error.ParseError(_("revset expects one or more arguments"))
993
998
994 raw = evalstring(context, mapping, args[0])
999 raw = evalstring(context, mapping, args[0])
995 ctx = mapping['ctx']
1000 ctx = mapping['ctx']
996 repo = ctx.repo()
1001 repo = ctx.repo()
997
1002
998 def query(expr):
1003 def query(expr):
999 m = revsetmod.match(repo.ui, expr, repo=repo)
1004 m = revsetmod.match(repo.ui, expr, repo=repo)
1000 return m(repo)
1005 return m(repo)
1001
1006
1002 if len(args) > 1:
1007 if len(args) > 1:
1003 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
1008 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
1004 revs = query(revsetlang.formatspec(raw, *formatargs))
1009 revs = query(revsetlang.formatspec(raw, *formatargs))
1005 revs = list(revs)
1010 revs = list(revs)
1006 else:
1011 else:
1007 revsetcache = mapping['cache'].setdefault("revsetcache", {})
1012 revsetcache = mapping['cache'].setdefault("revsetcache", {})
1008 if raw in revsetcache:
1013 if raw in revsetcache:
1009 revs = revsetcache[raw]
1014 revs = revsetcache[raw]
1010 else:
1015 else:
1011 revs = query(raw)
1016 revs = query(raw)
1012 revs = list(revs)
1017 revs = list(revs)
1013 revsetcache[raw] = revs
1018 revsetcache[raw] = revs
1014
1019
1015 return templatekw.showrevslist("revision", revs, **mapping)
1020 return templatekw.showrevslist("revision", revs, **mapping)
1016
1021
1017 @templatefunc('rstdoc(text, style)')
1022 @templatefunc('rstdoc(text, style)')
1018 def rstdoc(context, mapping, args):
1023 def rstdoc(context, mapping, args):
1019 """Format reStructuredText."""
1024 """Format reStructuredText."""
1020 if len(args) != 2:
1025 if len(args) != 2:
1021 # i18n: "rstdoc" is a keyword
1026 # i18n: "rstdoc" is a keyword
1022 raise error.ParseError(_("rstdoc expects two arguments"))
1027 raise error.ParseError(_("rstdoc expects two arguments"))
1023
1028
1024 text = evalstring(context, mapping, args[0])
1029 text = evalstring(context, mapping, args[0])
1025 style = evalstring(context, mapping, args[1])
1030 style = evalstring(context, mapping, args[1])
1026
1031
1027 return minirst.format(text, style=style, keep=['verbose'])
1032 return minirst.format(text, style=style, keep=['verbose'])
1028
1033
1029 @templatefunc('separate(sep, args)', argspec='sep *args')
1034 @templatefunc('separate(sep, args)', argspec='sep *args')
1030 def separate(context, mapping, args):
1035 def separate(context, mapping, args):
1031 """Add a separator between non-empty arguments."""
1036 """Add a separator between non-empty arguments."""
1032 if 'sep' not in args:
1037 if 'sep' not in args:
1033 # i18n: "separate" is a keyword
1038 # i18n: "separate" is a keyword
1034 raise error.ParseError(_("separate expects at least one argument"))
1039 raise error.ParseError(_("separate expects at least one argument"))
1035
1040
1036 sep = evalstring(context, mapping, args['sep'])
1041 sep = evalstring(context, mapping, args['sep'])
1037 first = True
1042 first = True
1038 for arg in args['args']:
1043 for arg in args['args']:
1039 argstr = evalstring(context, mapping, arg)
1044 argstr = evalstring(context, mapping, arg)
1040 if not argstr:
1045 if not argstr:
1041 continue
1046 continue
1042 if first:
1047 if first:
1043 first = False
1048 first = False
1044 else:
1049 else:
1045 yield sep
1050 yield sep
1046 yield argstr
1051 yield argstr
1047
1052
1048 @templatefunc('shortest(node, minlength=4)')
1053 @templatefunc('shortest(node, minlength=4)')
1049 def shortest(context, mapping, args):
1054 def shortest(context, mapping, args):
1050 """Obtain the shortest representation of
1055 """Obtain the shortest representation of
1051 a node."""
1056 a node."""
1052 if not (1 <= len(args) <= 2):
1057 if not (1 <= len(args) <= 2):
1053 # i18n: "shortest" is a keyword
1058 # i18n: "shortest" is a keyword
1054 raise error.ParseError(_("shortest() expects one or two arguments"))
1059 raise error.ParseError(_("shortest() expects one or two arguments"))
1055
1060
1056 node = evalstring(context, mapping, args[0])
1061 node = evalstring(context, mapping, args[0])
1057
1062
1058 minlength = 4
1063 minlength = 4
1059 if len(args) > 1:
1064 if len(args) > 1:
1060 minlength = evalinteger(context, mapping, args[1],
1065 minlength = evalinteger(context, mapping, args[1],
1061 # i18n: "shortest" is a keyword
1066 # i18n: "shortest" is a keyword
1062 _("shortest() expects an integer minlength"))
1067 _("shortest() expects an integer minlength"))
1063
1068
1064 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1069 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1065 # which would be unacceptably slow. so we look for hash collision in
1070 # which would be unacceptably slow. so we look for hash collision in
1066 # unfiltered space, which means some hashes may be slightly longer.
1071 # unfiltered space, which means some hashes may be slightly longer.
1067 cl = mapping['ctx']._repo.unfiltered().changelog
1072 cl = mapping['ctx']._repo.unfiltered().changelog
1068 return cl.shortest(node, minlength)
1073 return cl.shortest(node, minlength)
1069
1074
1070 @templatefunc('strip(text[, chars])')
1075 @templatefunc('strip(text[, chars])')
1071 def strip(context, mapping, args):
1076 def strip(context, mapping, args):
1072 """Strip characters from a string. By default,
1077 """Strip characters from a string. By default,
1073 strips all leading and trailing whitespace."""
1078 strips all leading and trailing whitespace."""
1074 if not (1 <= len(args) <= 2):
1079 if not (1 <= len(args) <= 2):
1075 # i18n: "strip" is a keyword
1080 # i18n: "strip" is a keyword
1076 raise error.ParseError(_("strip expects one or two arguments"))
1081 raise error.ParseError(_("strip expects one or two arguments"))
1077
1082
1078 text = evalstring(context, mapping, args[0])
1083 text = evalstring(context, mapping, args[0])
1079 if len(args) == 2:
1084 if len(args) == 2:
1080 chars = evalstring(context, mapping, args[1])
1085 chars = evalstring(context, mapping, args[1])
1081 return text.strip(chars)
1086 return text.strip(chars)
1082 return text.strip()
1087 return text.strip()
1083
1088
1084 @templatefunc('sub(pattern, replacement, expression)')
1089 @templatefunc('sub(pattern, replacement, expression)')
1085 def sub(context, mapping, args):
1090 def sub(context, mapping, args):
1086 """Perform text substitution
1091 """Perform text substitution
1087 using regular expressions."""
1092 using regular expressions."""
1088 if len(args) != 3:
1093 if len(args) != 3:
1089 # i18n: "sub" is a keyword
1094 # i18n: "sub" is a keyword
1090 raise error.ParseError(_("sub expects three arguments"))
1095 raise error.ParseError(_("sub expects three arguments"))
1091
1096
1092 pat = evalstring(context, mapping, args[0])
1097 pat = evalstring(context, mapping, args[0])
1093 rpl = evalstring(context, mapping, args[1])
1098 rpl = evalstring(context, mapping, args[1])
1094 src = evalstring(context, mapping, args[2])
1099 src = evalstring(context, mapping, args[2])
1095 try:
1100 try:
1096 patre = re.compile(pat)
1101 patre = re.compile(pat)
1097 except re.error:
1102 except re.error:
1098 # i18n: "sub" is a keyword
1103 # i18n: "sub" is a keyword
1099 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1104 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1100 try:
1105 try:
1101 yield patre.sub(rpl, src)
1106 yield patre.sub(rpl, src)
1102 except re.error:
1107 except re.error:
1103 # i18n: "sub" is a keyword
1108 # i18n: "sub" is a keyword
1104 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1109 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1105
1110
1106 @templatefunc('startswith(pattern, text)')
1111 @templatefunc('startswith(pattern, text)')
1107 def startswith(context, mapping, args):
1112 def startswith(context, mapping, args):
1108 """Returns the value from the "text" argument
1113 """Returns the value from the "text" argument
1109 if it begins with the content from the "pattern" argument."""
1114 if it begins with the content from the "pattern" argument."""
1110 if len(args) != 2:
1115 if len(args) != 2:
1111 # i18n: "startswith" is a keyword
1116 # i18n: "startswith" is a keyword
1112 raise error.ParseError(_("startswith expects two arguments"))
1117 raise error.ParseError(_("startswith expects two arguments"))
1113
1118
1114 patn = evalstring(context, mapping, args[0])
1119 patn = evalstring(context, mapping, args[0])
1115 text = evalstring(context, mapping, args[1])
1120 text = evalstring(context, mapping, args[1])
1116 if text.startswith(patn):
1121 if text.startswith(patn):
1117 return text
1122 return text
1118 return ''
1123 return ''
1119
1124
1120 @templatefunc('word(number, text[, separator])')
1125 @templatefunc('word(number, text[, separator])')
1121 def word(context, mapping, args):
1126 def word(context, mapping, args):
1122 """Return the nth word from a string."""
1127 """Return the nth word from a string."""
1123 if not (2 <= len(args) <= 3):
1128 if not (2 <= len(args) <= 3):
1124 # i18n: "word" is a keyword
1129 # i18n: "word" is a keyword
1125 raise error.ParseError(_("word expects two or three arguments, got %d")
1130 raise error.ParseError(_("word expects two or three arguments, got %d")
1126 % len(args))
1131 % len(args))
1127
1132
1128 num = evalinteger(context, mapping, args[0],
1133 num = evalinteger(context, mapping, args[0],
1129 # i18n: "word" is a keyword
1134 # i18n: "word" is a keyword
1130 _("word expects an integer index"))
1135 _("word expects an integer index"))
1131 text = evalstring(context, mapping, args[1])
1136 text = evalstring(context, mapping, args[1])
1132 if len(args) == 3:
1137 if len(args) == 3:
1133 splitter = evalstring(context, mapping, args[2])
1138 splitter = evalstring(context, mapping, args[2])
1134 else:
1139 else:
1135 splitter = None
1140 splitter = None
1136
1141
1137 tokens = text.split(splitter)
1142 tokens = text.split(splitter)
1138 if num >= len(tokens) or num < -len(tokens):
1143 if num >= len(tokens) or num < -len(tokens):
1139 return ''
1144 return ''
1140 else:
1145 else:
1141 return tokens[num]
1146 return tokens[num]
1142
1147
1143 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1148 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1144 exprmethods = {
1149 exprmethods = {
1145 "integer": lambda e, c: (runinteger, e[1]),
1150 "integer": lambda e, c: (runinteger, e[1]),
1146 "string": lambda e, c: (runstring, e[1]),
1151 "string": lambda e, c: (runstring, e[1]),
1147 "symbol": lambda e, c: (runsymbol, e[1]),
1152 "symbol": lambda e, c: (runsymbol, e[1]),
1148 "template": buildtemplate,
1153 "template": buildtemplate,
1149 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1154 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1150 # ".": buildmember,
1155 # ".": buildmember,
1151 "|": buildfilter,
1156 "|": buildfilter,
1152 "%": buildmap,
1157 "%": buildmap,
1153 "func": buildfunc,
1158 "func": buildfunc,
1154 "keyvalue": buildkeyvaluepair,
1159 "keyvalue": buildkeyvaluepair,
1155 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1160 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1156 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1161 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1157 "negate": buildnegate,
1162 "negate": buildnegate,
1158 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1163 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1159 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1164 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1160 }
1165 }
1161
1166
1162 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1167 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1163 methods = exprmethods.copy()
1168 methods = exprmethods.copy()
1164 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1169 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1165
1170
1166 class _aliasrules(parser.basealiasrules):
1171 class _aliasrules(parser.basealiasrules):
1167 """Parsing and expansion rule set of template aliases"""
1172 """Parsing and expansion rule set of template aliases"""
1168 _section = _('template alias')
1173 _section = _('template alias')
1169 _parse = staticmethod(_parseexpr)
1174 _parse = staticmethod(_parseexpr)
1170
1175
1171 @staticmethod
1176 @staticmethod
1172 def _trygetfunc(tree):
1177 def _trygetfunc(tree):
1173 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1178 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1174 None"""
1179 None"""
1175 if tree[0] == 'func' and tree[1][0] == 'symbol':
1180 if tree[0] == 'func' and tree[1][0] == 'symbol':
1176 return tree[1][1], getlist(tree[2])
1181 return tree[1][1], getlist(tree[2])
1177 if tree[0] == '|' and tree[2][0] == 'symbol':
1182 if tree[0] == '|' and tree[2][0] == 'symbol':
1178 return tree[2][1], [tree[1]]
1183 return tree[2][1], [tree[1]]
1179
1184
1180 def expandaliases(tree, aliases):
1185 def expandaliases(tree, aliases):
1181 """Return new tree of aliases are expanded"""
1186 """Return new tree of aliases are expanded"""
1182 aliasmap = _aliasrules.buildmap(aliases)
1187 aliasmap = _aliasrules.buildmap(aliases)
1183 return _aliasrules.expand(aliasmap, tree)
1188 return _aliasrules.expand(aliasmap, tree)
1184
1189
1185 # template engine
1190 # template engine
1186
1191
1187 stringify = templatefilters.stringify
1192 stringify = templatefilters.stringify
1188
1193
1189 def _flatten(thing):
1194 def _flatten(thing):
1190 '''yield a single stream from a possibly nested set of iterators'''
1195 '''yield a single stream from a possibly nested set of iterators'''
1191 thing = templatekw.unwraphybrid(thing)
1196 thing = templatekw.unwraphybrid(thing)
1192 if isinstance(thing, bytes):
1197 if isinstance(thing, bytes):
1193 yield thing
1198 yield thing
1194 elif thing is None:
1199 elif thing is None:
1195 pass
1200 pass
1196 elif not util.safehasattr(thing, '__iter__'):
1201 elif not util.safehasattr(thing, '__iter__'):
1197 yield pycompat.bytestr(thing)
1202 yield pycompat.bytestr(thing)
1198 else:
1203 else:
1199 for i in thing:
1204 for i in thing:
1200 i = templatekw.unwraphybrid(i)
1205 i = templatekw.unwraphybrid(i)
1201 if isinstance(i, bytes):
1206 if isinstance(i, bytes):
1202 yield i
1207 yield i
1203 elif i is None:
1208 elif i is None:
1204 pass
1209 pass
1205 elif not util.safehasattr(i, '__iter__'):
1210 elif not util.safehasattr(i, '__iter__'):
1206 yield pycompat.bytestr(i)
1211 yield pycompat.bytestr(i)
1207 else:
1212 else:
1208 for j in _flatten(i):
1213 for j in _flatten(i):
1209 yield j
1214 yield j
1210
1215
1211 def unquotestring(s):
1216 def unquotestring(s):
1212 '''unwrap quotes if any; otherwise returns unmodified string'''
1217 '''unwrap quotes if any; otherwise returns unmodified string'''
1213 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1218 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1214 return s
1219 return s
1215 return s[1:-1]
1220 return s[1:-1]
1216
1221
1217 class engine(object):
1222 class engine(object):
1218 '''template expansion engine.
1223 '''template expansion engine.
1219
1224
1220 template expansion works like this. a map file contains key=value
1225 template expansion works like this. a map file contains key=value
1221 pairs. if value is quoted, it is treated as string. otherwise, it
1226 pairs. if value is quoted, it is treated as string. otherwise, it
1222 is treated as name of template file.
1227 is treated as name of template file.
1223
1228
1224 templater is asked to expand a key in map. it looks up key, and
1229 templater is asked to expand a key in map. it looks up key, and
1225 looks for strings like this: {foo}. it expands {foo} by looking up
1230 looks for strings like this: {foo}. it expands {foo} by looking up
1226 foo in map, and substituting it. expansion is recursive: it stops
1231 foo in map, and substituting it. expansion is recursive: it stops
1227 when there is no more {foo} to replace.
1232 when there is no more {foo} to replace.
1228
1233
1229 expansion also allows formatting and filtering.
1234 expansion also allows formatting and filtering.
1230
1235
1231 format uses key to expand each item in list. syntax is
1236 format uses key to expand each item in list. syntax is
1232 {key%format}.
1237 {key%format}.
1233
1238
1234 filter uses function to transform value. syntax is
1239 filter uses function to transform value. syntax is
1235 {key|filter1|filter2|...}.'''
1240 {key|filter1|filter2|...}.'''
1236
1241
1237 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1242 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1238 self._loader = loader
1243 self._loader = loader
1239 if filters is None:
1244 if filters is None:
1240 filters = {}
1245 filters = {}
1241 self._filters = filters
1246 self._filters = filters
1242 if defaults is None:
1247 if defaults is None:
1243 defaults = {}
1248 defaults = {}
1244 self._defaults = defaults
1249 self._defaults = defaults
1245 self._aliasmap = _aliasrules.buildmap(aliases)
1250 self._aliasmap = _aliasrules.buildmap(aliases)
1246 self._cache = {} # key: (func, data)
1251 self._cache = {} # key: (func, data)
1247
1252
1248 def _load(self, t):
1253 def _load(self, t):
1249 '''load, parse, and cache a template'''
1254 '''load, parse, and cache a template'''
1250 if t not in self._cache:
1255 if t not in self._cache:
1251 # put poison to cut recursion while compiling 't'
1256 # put poison to cut recursion while compiling 't'
1252 self._cache[t] = (_runrecursivesymbol, t)
1257 self._cache[t] = (_runrecursivesymbol, t)
1253 try:
1258 try:
1254 x = parse(self._loader(t))
1259 x = parse(self._loader(t))
1255 if self._aliasmap:
1260 if self._aliasmap:
1256 x = _aliasrules.expand(self._aliasmap, x)
1261 x = _aliasrules.expand(self._aliasmap, x)
1257 self._cache[t] = compileexp(x, self, methods)
1262 self._cache[t] = compileexp(x, self, methods)
1258 except: # re-raises
1263 except: # re-raises
1259 del self._cache[t]
1264 del self._cache[t]
1260 raise
1265 raise
1261 return self._cache[t]
1266 return self._cache[t]
1262
1267
1263 def process(self, t, mapping):
1268 def process(self, t, mapping):
1264 '''Perform expansion. t is name of map element to expand.
1269 '''Perform expansion. t is name of map element to expand.
1265 mapping contains added elements for use during expansion. Is a
1270 mapping contains added elements for use during expansion. Is a
1266 generator.'''
1271 generator.'''
1267 func, data = self._load(t)
1272 func, data = self._load(t)
1268 return _flatten(func(self, mapping, data))
1273 return _flatten(func(self, mapping, data))
1269
1274
1270 engines = {'default': engine}
1275 engines = {'default': engine}
1271
1276
1272 def stylelist():
1277 def stylelist():
1273 paths = templatepaths()
1278 paths = templatepaths()
1274 if not paths:
1279 if not paths:
1275 return _('no templates found, try `hg debuginstall` for more info')
1280 return _('no templates found, try `hg debuginstall` for more info')
1276 dirlist = os.listdir(paths[0])
1281 dirlist = os.listdir(paths[0])
1277 stylelist = []
1282 stylelist = []
1278 for file in dirlist:
1283 for file in dirlist:
1279 split = file.split(".")
1284 split = file.split(".")
1280 if split[-1] in ('orig', 'rej'):
1285 if split[-1] in ('orig', 'rej'):
1281 continue
1286 continue
1282 if split[0] == "map-cmdline":
1287 if split[0] == "map-cmdline":
1283 stylelist.append(split[1])
1288 stylelist.append(split[1])
1284 return ", ".join(sorted(stylelist))
1289 return ", ".join(sorted(stylelist))
1285
1290
1286 def _readmapfile(mapfile):
1291 def _readmapfile(mapfile):
1287 """Load template elements from the given map file"""
1292 """Load template elements from the given map file"""
1288 if not os.path.exists(mapfile):
1293 if not os.path.exists(mapfile):
1289 raise error.Abort(_("style '%s' not found") % mapfile,
1294 raise error.Abort(_("style '%s' not found") % mapfile,
1290 hint=_("available styles: %s") % stylelist())
1295 hint=_("available styles: %s") % stylelist())
1291
1296
1292 base = os.path.dirname(mapfile)
1297 base = os.path.dirname(mapfile)
1293 conf = config.config(includepaths=templatepaths())
1298 conf = config.config(includepaths=templatepaths())
1294 conf.read(mapfile)
1299 conf.read(mapfile)
1295
1300
1296 cache = {}
1301 cache = {}
1297 tmap = {}
1302 tmap = {}
1298 for key, val in conf[''].items():
1303 for key, val in conf[''].items():
1299 if not val:
1304 if not val:
1300 raise error.ParseError(_('missing value'), conf.source('', key))
1305 raise error.ParseError(_('missing value'), conf.source('', key))
1301 if val[0] in "'\"":
1306 if val[0] in "'\"":
1302 if val[0] != val[-1]:
1307 if val[0] != val[-1]:
1303 raise error.ParseError(_('unmatched quotes'),
1308 raise error.ParseError(_('unmatched quotes'),
1304 conf.source('', key))
1309 conf.source('', key))
1305 cache[key] = unquotestring(val)
1310 cache[key] = unquotestring(val)
1306 elif key == "__base__":
1311 elif key == "__base__":
1307 # treat as a pointer to a base class for this style
1312 # treat as a pointer to a base class for this style
1308 path = util.normpath(os.path.join(base, val))
1313 path = util.normpath(os.path.join(base, val))
1309
1314
1310 # fallback check in template paths
1315 # fallback check in template paths
1311 if not os.path.exists(path):
1316 if not os.path.exists(path):
1312 for p in templatepaths():
1317 for p in templatepaths():
1313 p2 = util.normpath(os.path.join(p, val))
1318 p2 = util.normpath(os.path.join(p, val))
1314 if os.path.isfile(p2):
1319 if os.path.isfile(p2):
1315 path = p2
1320 path = p2
1316 break
1321 break
1317 p3 = util.normpath(os.path.join(p2, "map"))
1322 p3 = util.normpath(os.path.join(p2, "map"))
1318 if os.path.isfile(p3):
1323 if os.path.isfile(p3):
1319 path = p3
1324 path = p3
1320 break
1325 break
1321
1326
1322 bcache, btmap = _readmapfile(path)
1327 bcache, btmap = _readmapfile(path)
1323 for k in bcache:
1328 for k in bcache:
1324 if k not in cache:
1329 if k not in cache:
1325 cache[k] = bcache[k]
1330 cache[k] = bcache[k]
1326 for k in btmap:
1331 for k in btmap:
1327 if k not in tmap:
1332 if k not in tmap:
1328 tmap[k] = btmap[k]
1333 tmap[k] = btmap[k]
1329 else:
1334 else:
1330 val = 'default', val
1335 val = 'default', val
1331 if ':' in val[1]:
1336 if ':' in val[1]:
1332 val = val[1].split(':', 1)
1337 val = val[1].split(':', 1)
1333 tmap[key] = val[0], os.path.join(base, val[1])
1338 tmap[key] = val[0], os.path.join(base, val[1])
1334 return cache, tmap
1339 return cache, tmap
1335
1340
1336 class TemplateNotFound(error.Abort):
1341 class TemplateNotFound(error.Abort):
1337 pass
1342 pass
1338
1343
1339 class templater(object):
1344 class templater(object):
1340
1345
1341 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1346 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1342 minchunk=1024, maxchunk=65536):
1347 minchunk=1024, maxchunk=65536):
1343 '''set up template engine.
1348 '''set up template engine.
1344 filters is dict of functions. each transforms a value into another.
1349 filters is dict of functions. each transforms a value into another.
1345 defaults is dict of default map definitions.
1350 defaults is dict of default map definitions.
1346 aliases is list of alias (name, replacement) pairs.
1351 aliases is list of alias (name, replacement) pairs.
1347 '''
1352 '''
1348 if filters is None:
1353 if filters is None:
1349 filters = {}
1354 filters = {}
1350 if defaults is None:
1355 if defaults is None:
1351 defaults = {}
1356 defaults = {}
1352 if cache is None:
1357 if cache is None:
1353 cache = {}
1358 cache = {}
1354 self.cache = cache.copy()
1359 self.cache = cache.copy()
1355 self.map = {}
1360 self.map = {}
1356 self.filters = templatefilters.filters.copy()
1361 self.filters = templatefilters.filters.copy()
1357 self.filters.update(filters)
1362 self.filters.update(filters)
1358 self.defaults = defaults
1363 self.defaults = defaults
1359 self._aliases = aliases
1364 self._aliases = aliases
1360 self.minchunk, self.maxchunk = minchunk, maxchunk
1365 self.minchunk, self.maxchunk = minchunk, maxchunk
1361 self.ecache = {}
1366 self.ecache = {}
1362
1367
1363 @classmethod
1368 @classmethod
1364 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1369 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1365 minchunk=1024, maxchunk=65536):
1370 minchunk=1024, maxchunk=65536):
1366 """Create templater from the specified map file"""
1371 """Create templater from the specified map file"""
1367 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1372 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1368 cache, tmap = _readmapfile(mapfile)
1373 cache, tmap = _readmapfile(mapfile)
1369 t.cache.update(cache)
1374 t.cache.update(cache)
1370 t.map = tmap
1375 t.map = tmap
1371 return t
1376 return t
1372
1377
1373 def __contains__(self, key):
1378 def __contains__(self, key):
1374 return key in self.cache or key in self.map
1379 return key in self.cache or key in self.map
1375
1380
1376 def load(self, t):
1381 def load(self, t):
1377 '''Get the template for the given template name. Use a local cache.'''
1382 '''Get the template for the given template name. Use a local cache.'''
1378 if t not in self.cache:
1383 if t not in self.cache:
1379 try:
1384 try:
1380 self.cache[t] = util.readfile(self.map[t][1])
1385 self.cache[t] = util.readfile(self.map[t][1])
1381 except KeyError as inst:
1386 except KeyError as inst:
1382 raise TemplateNotFound(_('"%s" not in template map') %
1387 raise TemplateNotFound(_('"%s" not in template map') %
1383 inst.args[0])
1388 inst.args[0])
1384 except IOError as inst:
1389 except IOError as inst:
1385 raise IOError(inst.args[0], _('template file %s: %s') %
1390 raise IOError(inst.args[0], _('template file %s: %s') %
1386 (self.map[t][1], inst.args[1]))
1391 (self.map[t][1], inst.args[1]))
1387 return self.cache[t]
1392 return self.cache[t]
1388
1393
1389 def render(self, mapping):
1394 def render(self, mapping):
1390 """Render the default unnamed template and return result as string"""
1395 """Render the default unnamed template and return result as string"""
1391 mapping = pycompat.strkwargs(mapping)
1396 mapping = pycompat.strkwargs(mapping)
1392 return stringify(self('', **mapping))
1397 return stringify(self('', **mapping))
1393
1398
1394 def __call__(self, t, **mapping):
1399 def __call__(self, t, **mapping):
1395 mapping = pycompat.byteskwargs(mapping)
1400 mapping = pycompat.byteskwargs(mapping)
1396 ttype = t in self.map and self.map[t][0] or 'default'
1401 ttype = t in self.map and self.map[t][0] or 'default'
1397 if ttype not in self.ecache:
1402 if ttype not in self.ecache:
1398 try:
1403 try:
1399 ecls = engines[ttype]
1404 ecls = engines[ttype]
1400 except KeyError:
1405 except KeyError:
1401 raise error.Abort(_('invalid template engine: %s') % ttype)
1406 raise error.Abort(_('invalid template engine: %s') % ttype)
1402 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1407 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1403 self._aliases)
1408 self._aliases)
1404 proc = self.ecache[ttype]
1409 proc = self.ecache[ttype]
1405
1410
1406 stream = proc.process(t, mapping)
1411 stream = proc.process(t, mapping)
1407 if self.minchunk:
1412 if self.minchunk:
1408 stream = util.increasingchunks(stream, min=self.minchunk,
1413 stream = util.increasingchunks(stream, min=self.minchunk,
1409 max=self.maxchunk)
1414 max=self.maxchunk)
1410 return stream
1415 return stream
1411
1416
1412 def templatepaths():
1417 def templatepaths():
1413 '''return locations used for template files.'''
1418 '''return locations used for template files.'''
1414 pathsrel = ['templates']
1419 pathsrel = ['templates']
1415 paths = [os.path.normpath(os.path.join(util.datapath, f))
1420 paths = [os.path.normpath(os.path.join(util.datapath, f))
1416 for f in pathsrel]
1421 for f in pathsrel]
1417 return [p for p in paths if os.path.isdir(p)]
1422 return [p for p in paths if os.path.isdir(p)]
1418
1423
1419 def templatepath(name):
1424 def templatepath(name):
1420 '''return location of template file. returns None if not found.'''
1425 '''return location of template file. returns None if not found.'''
1421 for p in templatepaths():
1426 for p in templatepaths():
1422 f = os.path.join(p, name)
1427 f = os.path.join(p, name)
1423 if os.path.exists(f):
1428 if os.path.exists(f):
1424 return f
1429 return f
1425 return None
1430 return None
1426
1431
1427 def stylemap(styles, paths=None):
1432 def stylemap(styles, paths=None):
1428 """Return path to mapfile for a given style.
1433 """Return path to mapfile for a given style.
1429
1434
1430 Searches mapfile in the following locations:
1435 Searches mapfile in the following locations:
1431 1. templatepath/style/map
1436 1. templatepath/style/map
1432 2. templatepath/map-style
1437 2. templatepath/map-style
1433 3. templatepath/map
1438 3. templatepath/map
1434 """
1439 """
1435
1440
1436 if paths is None:
1441 if paths is None:
1437 paths = templatepaths()
1442 paths = templatepaths()
1438 elif isinstance(paths, str):
1443 elif isinstance(paths, str):
1439 paths = [paths]
1444 paths = [paths]
1440
1445
1441 if isinstance(styles, str):
1446 if isinstance(styles, str):
1442 styles = [styles]
1447 styles = [styles]
1443
1448
1444 for style in styles:
1449 for style in styles:
1445 # only plain name is allowed to honor template paths
1450 # only plain name is allowed to honor template paths
1446 if (not style
1451 if (not style
1447 or style in (os.curdir, os.pardir)
1452 or style in (os.curdir, os.pardir)
1448 or pycompat.ossep in style
1453 or pycompat.ossep in style
1449 or pycompat.osaltsep and pycompat.osaltsep in style):
1454 or pycompat.osaltsep and pycompat.osaltsep in style):
1450 continue
1455 continue
1451 locations = [os.path.join(style, 'map'), 'map-' + style]
1456 locations = [os.path.join(style, 'map'), 'map-' + style]
1452 locations.append('map')
1457 locations.append('map')
1453
1458
1454 for path in paths:
1459 for path in paths:
1455 for location in locations:
1460 for location in locations:
1456 mapfile = os.path.join(path, location)
1461 mapfile = os.path.join(path, location)
1457 if os.path.isfile(mapfile):
1462 if os.path.isfile(mapfile):
1458 return style, mapfile
1463 return style, mapfile
1459
1464
1460 raise RuntimeError("No hgweb templates found in %r" % paths)
1465 raise RuntimeError("No hgweb templates found in %r" % paths)
1461
1466
1462 def loadfunction(ui, extname, registrarobj):
1467 def loadfunction(ui, extname, registrarobj):
1463 """Load template function from specified registrarobj
1468 """Load template function from specified registrarobj
1464 """
1469 """
1465 for name, func in registrarobj._table.iteritems():
1470 for name, func in registrarobj._table.iteritems():
1466 funcs[name] = func
1471 funcs[name] = func
1467
1472
1468 # tell hggettext to extract docstrings from these functions:
1473 # tell hggettext to extract docstrings from these functions:
1469 i18nfunctions = funcs.values()
1474 i18nfunctions = funcs.values()
@@ -1,4599 +1,4613 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 Quoting for ui.logtemplate
207 Quoting for ui.logtemplate
208
208
209 $ hg tip --config "ui.logtemplate={rev}\n"
209 $ hg tip --config "ui.logtemplate={rev}\n"
210 8
210 8
211 $ hg tip --config "ui.logtemplate='{rev}\n'"
211 $ hg tip --config "ui.logtemplate='{rev}\n'"
212 8
212 8
213 $ hg tip --config 'ui.logtemplate="{rev}\n"'
213 $ hg tip --config 'ui.logtemplate="{rev}\n"'
214 8
214 8
215 $ hg tip --config 'ui.logtemplate=n{rev}\n'
215 $ hg tip --config 'ui.logtemplate=n{rev}\n'
216 n8
216 n8
217
217
218 Make sure user/global hgrc does not affect tests
218 Make sure user/global hgrc does not affect tests
219
219
220 $ echo '[ui]' > .hg/hgrc
220 $ echo '[ui]' > .hg/hgrc
221 $ echo 'logtemplate =' >> .hg/hgrc
221 $ echo 'logtemplate =' >> .hg/hgrc
222 $ echo 'style =' >> .hg/hgrc
222 $ echo 'style =' >> .hg/hgrc
223
223
224 Add some simple styles to settings
224 Add some simple styles to settings
225
225
226 $ cat <<'EOF' >> .hg/hgrc
226 $ cat <<'EOF' >> .hg/hgrc
227 > [templates]
227 > [templates]
228 > simple = "{rev}\n"
228 > simple = "{rev}\n"
229 > simple2 = {rev}\n
229 > simple2 = {rev}\n
230 > rev = "should not precede {rev} keyword\n"
230 > rev = "should not precede {rev} keyword\n"
231 > EOF
231 > EOF
232
232
233 $ hg log -l1 -Tsimple
233 $ hg log -l1 -Tsimple
234 8
234 8
235 $ hg log -l1 -Tsimple2
235 $ hg log -l1 -Tsimple2
236 8
236 8
237 $ hg log -l1 -Trev
237 $ hg log -l1 -Trev
238 should not precede 8 keyword
238 should not precede 8 keyword
239 $ hg log -l1 -T '{simple}'
239 $ hg log -l1 -T '{simple}'
240 8
240 8
241
241
242 Map file shouldn't see user templates:
242 Map file shouldn't see user templates:
243
243
244 $ cat <<EOF > tmpl
244 $ cat <<EOF > tmpl
245 > changeset = 'nothing expanded:{simple}\n'
245 > changeset = 'nothing expanded:{simple}\n'
246 > EOF
246 > EOF
247 $ hg log -l1 --style ./tmpl
247 $ hg log -l1 --style ./tmpl
248 nothing expanded:
248 nothing expanded:
249
249
250 Test templates and style maps in files:
250 Test templates and style maps in files:
251
251
252 $ echo "{rev}" > tmpl
252 $ echo "{rev}" > tmpl
253 $ hg log -l1 -T./tmpl
253 $ hg log -l1 -T./tmpl
254 8
254 8
255 $ hg log -l1 -Tblah/blah
255 $ hg log -l1 -Tblah/blah
256 blah/blah (no-eol)
256 blah/blah (no-eol)
257
257
258 $ printf 'changeset = "{rev}\\n"\n' > map-simple
258 $ printf 'changeset = "{rev}\\n"\n' > map-simple
259 $ hg log -l1 -T./map-simple
259 $ hg log -l1 -T./map-simple
260 8
260 8
261
261
262 Test template map inheritance
262 Test template map inheritance
263
263
264 $ echo "__base__ = map-cmdline.default" > map-simple
264 $ echo "__base__ = map-cmdline.default" > map-simple
265 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
265 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
266 $ hg log -l1 -T./map-simple
266 $ hg log -l1 -T./map-simple
267 changeset: ***8***
267 changeset: ***8***
268 tag: tip
268 tag: tip
269 user: test
269 user: test
270 date: Wed Jan 01 10:01:00 2020 +0000
270 date: Wed Jan 01 10:01:00 2020 +0000
271 summary: third
271 summary: third
272
272
273
273
274 Test docheader, docfooter and separator in template map
274 Test docheader, docfooter and separator in template map
275
275
276 $ cat <<'EOF' > map-myjson
276 $ cat <<'EOF' > map-myjson
277 > docheader = '\{\n'
277 > docheader = '\{\n'
278 > docfooter = '\n}\n'
278 > docfooter = '\n}\n'
279 > separator = ',\n'
279 > separator = ',\n'
280 > changeset = ' {dict(rev, node|short)|json}'
280 > changeset = ' {dict(rev, node|short)|json}'
281 > EOF
281 > EOF
282 $ hg log -l2 -T./map-myjson
282 $ hg log -l2 -T./map-myjson
283 {
283 {
284 {"node": "95c24699272e", "rev": 8},
284 {"node": "95c24699272e", "rev": 8},
285 {"node": "29114dbae42b", "rev": 7}
285 {"node": "29114dbae42b", "rev": 7}
286 }
286 }
287
287
288 Test docheader, docfooter and separator in [templates] section
288 Test docheader, docfooter and separator in [templates] section
289
289
290 $ cat <<'EOF' >> .hg/hgrc
290 $ cat <<'EOF' >> .hg/hgrc
291 > [templates]
291 > [templates]
292 > myjson = ' {dict(rev, node|short)|json}'
292 > myjson = ' {dict(rev, node|short)|json}'
293 > myjson:docheader = '\{\n'
293 > myjson:docheader = '\{\n'
294 > myjson:docfooter = '\n}\n'
294 > myjson:docfooter = '\n}\n'
295 > myjson:separator = ',\n'
295 > myjson:separator = ',\n'
296 > :docheader = 'should not be selected as a docheader for literal templates\n'
296 > :docheader = 'should not be selected as a docheader for literal templates\n'
297 > EOF
297 > EOF
298 $ hg log -l2 -Tmyjson
298 $ hg log -l2 -Tmyjson
299 {
299 {
300 {"node": "95c24699272e", "rev": 8},
300 {"node": "95c24699272e", "rev": 8},
301 {"node": "29114dbae42b", "rev": 7}
301 {"node": "29114dbae42b", "rev": 7}
302 }
302 }
303 $ hg log -l1 -T'{rev}\n'
303 $ hg log -l1 -T'{rev}\n'
304 8
304 8
305
305
306 Template should precede style option
306 Template should precede style option
307
307
308 $ hg log -l1 --style default -T '{rev}\n'
308 $ hg log -l1 --style default -T '{rev}\n'
309 8
309 8
310
310
311 Add a commit with empty description, to ensure that the templates
311 Add a commit with empty description, to ensure that the templates
312 below will omit the description line.
312 below will omit the description line.
313
313
314 $ echo c >> c
314 $ echo c >> c
315 $ hg add c
315 $ hg add c
316 $ hg commit -qm ' '
316 $ hg commit -qm ' '
317
317
318 Default style is like normal output. Phases style should be the same
318 Default style is like normal output. Phases style should be the same
319 as default style, except for extra phase lines.
319 as default style, except for extra phase lines.
320
320
321 $ hg log > log.out
321 $ hg log > log.out
322 $ hg log --style default > style.out
322 $ hg log --style default > style.out
323 $ cmp log.out style.out || diff -u log.out style.out
323 $ cmp log.out style.out || diff -u log.out style.out
324 $ hg log -T phases > phases.out
324 $ hg log -T phases > phases.out
325 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
325 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
326 +phase: draft
326 +phase: draft
327 +phase: draft
327 +phase: draft
328 +phase: draft
328 +phase: draft
329 +phase: draft
329 +phase: draft
330 +phase: draft
330 +phase: draft
331 +phase: draft
331 +phase: draft
332 +phase: draft
332 +phase: draft
333 +phase: draft
333 +phase: draft
334 +phase: draft
334 +phase: draft
335 +phase: draft
335 +phase: draft
336
336
337 $ hg log -v > log.out
337 $ hg log -v > log.out
338 $ hg log -v --style default > style.out
338 $ hg log -v --style default > style.out
339 $ cmp log.out style.out || diff -u log.out style.out
339 $ cmp log.out style.out || diff -u log.out style.out
340 $ hg log -v -T phases > phases.out
340 $ hg log -v -T phases > phases.out
341 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
341 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
342 +phase: draft
342 +phase: draft
343 +phase: draft
343 +phase: draft
344 +phase: draft
344 +phase: draft
345 +phase: draft
345 +phase: draft
346 +phase: draft
346 +phase: draft
347 +phase: draft
347 +phase: draft
348 +phase: draft
348 +phase: draft
349 +phase: draft
349 +phase: draft
350 +phase: draft
350 +phase: draft
351 +phase: draft
351 +phase: draft
352
352
353 $ hg log -q > log.out
353 $ hg log -q > log.out
354 $ hg log -q --style default > style.out
354 $ hg log -q --style default > style.out
355 $ cmp log.out style.out || diff -u log.out style.out
355 $ cmp log.out style.out || diff -u log.out style.out
356 $ hg log -q -T phases > phases.out
356 $ hg log -q -T phases > phases.out
357 $ cmp log.out phases.out || diff -u log.out phases.out
357 $ cmp log.out phases.out || diff -u log.out phases.out
358
358
359 $ hg log --debug > log.out
359 $ hg log --debug > log.out
360 $ hg log --debug --style default > style.out
360 $ hg log --debug --style default > style.out
361 $ cmp log.out style.out || diff -u log.out style.out
361 $ cmp log.out style.out || diff -u log.out style.out
362 $ hg log --debug -T phases > phases.out
362 $ hg log --debug -T phases > phases.out
363 $ cmp log.out phases.out || diff -u log.out phases.out
363 $ cmp log.out phases.out || diff -u log.out phases.out
364
364
365 Default style of working-directory revision should also be the same (but
365 Default style of working-directory revision should also be the same (but
366 date may change while running tests):
366 date may change while running tests):
367
367
368 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
368 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
369 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
369 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
370 $ cmp log.out style.out || diff -u log.out style.out
370 $ cmp log.out style.out || diff -u log.out style.out
371
371
372 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
372 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
373 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
373 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
374 $ cmp log.out style.out || diff -u log.out style.out
374 $ cmp log.out style.out || diff -u log.out style.out
375
375
376 $ hg log -r 'wdir()' -q > log.out
376 $ hg log -r 'wdir()' -q > log.out
377 $ hg log -r 'wdir()' -q --style default > style.out
377 $ hg log -r 'wdir()' -q --style default > style.out
378 $ cmp log.out style.out || diff -u log.out style.out
378 $ cmp log.out style.out || diff -u log.out style.out
379
379
380 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
380 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
381 $ hg log -r 'wdir()' --debug --style default \
381 $ hg log -r 'wdir()' --debug --style default \
382 > | sed 's|^date:.*|date:|' > style.out
382 > | sed 's|^date:.*|date:|' > style.out
383 $ cmp log.out style.out || diff -u log.out style.out
383 $ cmp log.out style.out || diff -u log.out style.out
384
384
385 Default style should also preserve color information (issue2866):
385 Default style should also preserve color information (issue2866):
386
386
387 $ cp $HGRCPATH $HGRCPATH-bak
387 $ cp $HGRCPATH $HGRCPATH-bak
388 $ cat <<EOF >> $HGRCPATH
388 $ cat <<EOF >> $HGRCPATH
389 > [extensions]
389 > [extensions]
390 > color=
390 > color=
391 > EOF
391 > EOF
392
392
393 $ hg --color=debug log > log.out
393 $ hg --color=debug log > log.out
394 $ hg --color=debug log --style default > style.out
394 $ hg --color=debug log --style default > style.out
395 $ cmp log.out style.out || diff -u log.out style.out
395 $ cmp log.out style.out || diff -u log.out style.out
396 $ hg --color=debug log -T phases > phases.out
396 $ hg --color=debug log -T phases > phases.out
397 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
397 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
398 +[log.phase|phase: draft]
398 +[log.phase|phase: draft]
399 +[log.phase|phase: draft]
399 +[log.phase|phase: draft]
400 +[log.phase|phase: draft]
400 +[log.phase|phase: draft]
401 +[log.phase|phase: draft]
401 +[log.phase|phase: draft]
402 +[log.phase|phase: draft]
402 +[log.phase|phase: draft]
403 +[log.phase|phase: draft]
403 +[log.phase|phase: draft]
404 +[log.phase|phase: draft]
404 +[log.phase|phase: draft]
405 +[log.phase|phase: draft]
405 +[log.phase|phase: draft]
406 +[log.phase|phase: draft]
406 +[log.phase|phase: draft]
407 +[log.phase|phase: draft]
407 +[log.phase|phase: draft]
408
408
409 $ hg --color=debug -v log > log.out
409 $ hg --color=debug -v log > log.out
410 $ hg --color=debug -v log --style default > style.out
410 $ hg --color=debug -v log --style default > style.out
411 $ cmp log.out style.out || diff -u log.out style.out
411 $ cmp log.out style.out || diff -u log.out style.out
412 $ hg --color=debug -v log -T phases > phases.out
412 $ hg --color=debug -v log -T phases > phases.out
413 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
413 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
414 +[log.phase|phase: draft]
414 +[log.phase|phase: draft]
415 +[log.phase|phase: draft]
415 +[log.phase|phase: draft]
416 +[log.phase|phase: draft]
416 +[log.phase|phase: draft]
417 +[log.phase|phase: draft]
417 +[log.phase|phase: draft]
418 +[log.phase|phase: draft]
418 +[log.phase|phase: draft]
419 +[log.phase|phase: draft]
419 +[log.phase|phase: draft]
420 +[log.phase|phase: draft]
420 +[log.phase|phase: draft]
421 +[log.phase|phase: draft]
421 +[log.phase|phase: draft]
422 +[log.phase|phase: draft]
422 +[log.phase|phase: draft]
423 +[log.phase|phase: draft]
423 +[log.phase|phase: draft]
424
424
425 $ hg --color=debug -q log > log.out
425 $ hg --color=debug -q log > log.out
426 $ hg --color=debug -q log --style default > style.out
426 $ hg --color=debug -q log --style default > style.out
427 $ cmp log.out style.out || diff -u log.out style.out
427 $ cmp log.out style.out || diff -u log.out style.out
428 $ hg --color=debug -q log -T phases > phases.out
428 $ hg --color=debug -q log -T phases > phases.out
429 $ cmp log.out phases.out || diff -u log.out phases.out
429 $ cmp log.out phases.out || diff -u log.out phases.out
430
430
431 $ hg --color=debug --debug log > log.out
431 $ hg --color=debug --debug log > log.out
432 $ hg --color=debug --debug log --style default > style.out
432 $ hg --color=debug --debug log --style default > style.out
433 $ cmp log.out style.out || diff -u log.out style.out
433 $ cmp log.out style.out || diff -u log.out style.out
434 $ hg --color=debug --debug log -T phases > phases.out
434 $ hg --color=debug --debug log -T phases > phases.out
435 $ cmp log.out phases.out || diff -u log.out phases.out
435 $ cmp log.out phases.out || diff -u log.out phases.out
436
436
437 $ mv $HGRCPATH-bak $HGRCPATH
437 $ mv $HGRCPATH-bak $HGRCPATH
438
438
439 Remove commit with empty commit message, so as to not pollute further
439 Remove commit with empty commit message, so as to not pollute further
440 tests.
440 tests.
441
441
442 $ hg --config extensions.strip= strip -q .
442 $ hg --config extensions.strip= strip -q .
443
443
444 Revision with no copies (used to print a traceback):
444 Revision with no copies (used to print a traceback):
445
445
446 $ hg tip -v --template '\n'
446 $ hg tip -v --template '\n'
447
447
448
448
449 Compact style works:
449 Compact style works:
450
450
451 $ hg log -Tcompact
451 $ hg log -Tcompact
452 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
452 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
453 third
453 third
454
454
455 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
455 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
456 second
456 second
457
457
458 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
458 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
459 merge
459 merge
460
460
461 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
461 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
462 new head
462 new head
463
463
464 4 bbe44766e73d 1970-01-17 04:53 +0000 person
464 4 bbe44766e73d 1970-01-17 04:53 +0000 person
465 new branch
465 new branch
466
466
467 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
467 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
468 no user, no domain
468 no user, no domain
469
469
470 2 97054abb4ab8 1970-01-14 21:20 +0000 other
470 2 97054abb4ab8 1970-01-14 21:20 +0000 other
471 no person
471 no person
472
472
473 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
473 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
474 other 1
474 other 1
475
475
476 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
476 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
477 line 1
477 line 1
478
478
479
479
480 $ hg log -v --style compact
480 $ hg log -v --style compact
481 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
481 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
482 third
482 third
483
483
484 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
484 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
485 second
485 second
486
486
487 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
487 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
488 merge
488 merge
489
489
490 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
490 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
491 new head
491 new head
492
492
493 4 bbe44766e73d 1970-01-17 04:53 +0000 person
493 4 bbe44766e73d 1970-01-17 04:53 +0000 person
494 new branch
494 new branch
495
495
496 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
496 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
497 no user, no domain
497 no user, no domain
498
498
499 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
499 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
500 no person
500 no person
501
501
502 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
502 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
503 other 1
503 other 1
504 other 2
504 other 2
505
505
506 other 3
506 other 3
507
507
508 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
508 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
509 line 1
509 line 1
510 line 2
510 line 2
511
511
512
512
513 $ hg log --debug --style compact
513 $ hg log --debug --style compact
514 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
514 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
515 third
515 third
516
516
517 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
517 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
518 second
518 second
519
519
520 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
520 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
521 merge
521 merge
522
522
523 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
523 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
524 new head
524 new head
525
525
526 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
526 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
527 new branch
527 new branch
528
528
529 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
529 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
530 no user, no domain
530 no user, no domain
531
531
532 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
532 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
533 no person
533 no person
534
534
535 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
535 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
536 other 1
536 other 1
537 other 2
537 other 2
538
538
539 other 3
539 other 3
540
540
541 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
541 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
542 line 1
542 line 1
543 line 2
543 line 2
544
544
545
545
546 Test xml styles:
546 Test xml styles:
547
547
548 $ hg log --style xml -r 'not all()'
548 $ hg log --style xml -r 'not all()'
549 <?xml version="1.0"?>
549 <?xml version="1.0"?>
550 <log>
550 <log>
551 </log>
551 </log>
552
552
553 $ hg log --style xml
553 $ hg log --style xml
554 <?xml version="1.0"?>
554 <?xml version="1.0"?>
555 <log>
555 <log>
556 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
556 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
557 <tag>tip</tag>
557 <tag>tip</tag>
558 <author email="test">test</author>
558 <author email="test">test</author>
559 <date>2020-01-01T10:01:00+00:00</date>
559 <date>2020-01-01T10:01:00+00:00</date>
560 <msg xml:space="preserve">third</msg>
560 <msg xml:space="preserve">third</msg>
561 </logentry>
561 </logentry>
562 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
562 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
563 <parent revision="-1" node="0000000000000000000000000000000000000000" />
563 <parent revision="-1" node="0000000000000000000000000000000000000000" />
564 <author email="user@hostname">User Name</author>
564 <author email="user@hostname">User Name</author>
565 <date>1970-01-12T13:46:40+00:00</date>
565 <date>1970-01-12T13:46:40+00:00</date>
566 <msg xml:space="preserve">second</msg>
566 <msg xml:space="preserve">second</msg>
567 </logentry>
567 </logentry>
568 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
568 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
569 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
569 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
570 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
570 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
571 <author email="person">person</author>
571 <author email="person">person</author>
572 <date>1970-01-18T08:40:01+00:00</date>
572 <date>1970-01-18T08:40:01+00:00</date>
573 <msg xml:space="preserve">merge</msg>
573 <msg xml:space="preserve">merge</msg>
574 </logentry>
574 </logentry>
575 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
575 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
576 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
576 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
577 <author email="person">person</author>
577 <author email="person">person</author>
578 <date>1970-01-18T08:40:00+00:00</date>
578 <date>1970-01-18T08:40:00+00:00</date>
579 <msg xml:space="preserve">new head</msg>
579 <msg xml:space="preserve">new head</msg>
580 </logentry>
580 </logentry>
581 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
581 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
582 <branch>foo</branch>
582 <branch>foo</branch>
583 <author email="person">person</author>
583 <author email="person">person</author>
584 <date>1970-01-17T04:53:20+00:00</date>
584 <date>1970-01-17T04:53:20+00:00</date>
585 <msg xml:space="preserve">new branch</msg>
585 <msg xml:space="preserve">new branch</msg>
586 </logentry>
586 </logentry>
587 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
587 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
588 <author email="person">person</author>
588 <author email="person">person</author>
589 <date>1970-01-16T01:06:40+00:00</date>
589 <date>1970-01-16T01:06:40+00:00</date>
590 <msg xml:space="preserve">no user, no domain</msg>
590 <msg xml:space="preserve">no user, no domain</msg>
591 </logentry>
591 </logentry>
592 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
592 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
593 <author email="other@place">other</author>
593 <author email="other@place">other</author>
594 <date>1970-01-14T21:20:00+00:00</date>
594 <date>1970-01-14T21:20:00+00:00</date>
595 <msg xml:space="preserve">no person</msg>
595 <msg xml:space="preserve">no person</msg>
596 </logentry>
596 </logentry>
597 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
597 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
598 <author email="other@place">A. N. Other</author>
598 <author email="other@place">A. N. Other</author>
599 <date>1970-01-13T17:33:20+00:00</date>
599 <date>1970-01-13T17:33:20+00:00</date>
600 <msg xml:space="preserve">other 1
600 <msg xml:space="preserve">other 1
601 other 2
601 other 2
602
602
603 other 3</msg>
603 other 3</msg>
604 </logentry>
604 </logentry>
605 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
605 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
606 <author email="user@hostname">User Name</author>
606 <author email="user@hostname">User Name</author>
607 <date>1970-01-12T13:46:40+00:00</date>
607 <date>1970-01-12T13:46:40+00:00</date>
608 <msg xml:space="preserve">line 1
608 <msg xml:space="preserve">line 1
609 line 2</msg>
609 line 2</msg>
610 </logentry>
610 </logentry>
611 </log>
611 </log>
612
612
613 $ hg log -v --style xml
613 $ hg log -v --style xml
614 <?xml version="1.0"?>
614 <?xml version="1.0"?>
615 <log>
615 <log>
616 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
616 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
617 <tag>tip</tag>
617 <tag>tip</tag>
618 <author email="test">test</author>
618 <author email="test">test</author>
619 <date>2020-01-01T10:01:00+00:00</date>
619 <date>2020-01-01T10:01:00+00:00</date>
620 <msg xml:space="preserve">third</msg>
620 <msg xml:space="preserve">third</msg>
621 <paths>
621 <paths>
622 <path action="A">fourth</path>
622 <path action="A">fourth</path>
623 <path action="A">third</path>
623 <path action="A">third</path>
624 <path action="R">second</path>
624 <path action="R">second</path>
625 </paths>
625 </paths>
626 <copies>
626 <copies>
627 <copy source="second">fourth</copy>
627 <copy source="second">fourth</copy>
628 </copies>
628 </copies>
629 </logentry>
629 </logentry>
630 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
630 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
631 <parent revision="-1" node="0000000000000000000000000000000000000000" />
631 <parent revision="-1" node="0000000000000000000000000000000000000000" />
632 <author email="user@hostname">User Name</author>
632 <author email="user@hostname">User Name</author>
633 <date>1970-01-12T13:46:40+00:00</date>
633 <date>1970-01-12T13:46:40+00:00</date>
634 <msg xml:space="preserve">second</msg>
634 <msg xml:space="preserve">second</msg>
635 <paths>
635 <paths>
636 <path action="A">second</path>
636 <path action="A">second</path>
637 </paths>
637 </paths>
638 </logentry>
638 </logentry>
639 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
639 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
640 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
640 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
641 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
641 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
642 <author email="person">person</author>
642 <author email="person">person</author>
643 <date>1970-01-18T08:40:01+00:00</date>
643 <date>1970-01-18T08:40:01+00:00</date>
644 <msg xml:space="preserve">merge</msg>
644 <msg xml:space="preserve">merge</msg>
645 <paths>
645 <paths>
646 </paths>
646 </paths>
647 </logentry>
647 </logentry>
648 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
648 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
649 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
649 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
650 <author email="person">person</author>
650 <author email="person">person</author>
651 <date>1970-01-18T08:40:00+00:00</date>
651 <date>1970-01-18T08:40:00+00:00</date>
652 <msg xml:space="preserve">new head</msg>
652 <msg xml:space="preserve">new head</msg>
653 <paths>
653 <paths>
654 <path action="A">d</path>
654 <path action="A">d</path>
655 </paths>
655 </paths>
656 </logentry>
656 </logentry>
657 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
657 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
658 <branch>foo</branch>
658 <branch>foo</branch>
659 <author email="person">person</author>
659 <author email="person">person</author>
660 <date>1970-01-17T04:53:20+00:00</date>
660 <date>1970-01-17T04:53:20+00:00</date>
661 <msg xml:space="preserve">new branch</msg>
661 <msg xml:space="preserve">new branch</msg>
662 <paths>
662 <paths>
663 </paths>
663 </paths>
664 </logentry>
664 </logentry>
665 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
665 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
666 <author email="person">person</author>
666 <author email="person">person</author>
667 <date>1970-01-16T01:06:40+00:00</date>
667 <date>1970-01-16T01:06:40+00:00</date>
668 <msg xml:space="preserve">no user, no domain</msg>
668 <msg xml:space="preserve">no user, no domain</msg>
669 <paths>
669 <paths>
670 <path action="M">c</path>
670 <path action="M">c</path>
671 </paths>
671 </paths>
672 </logentry>
672 </logentry>
673 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
673 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
674 <author email="other@place">other</author>
674 <author email="other@place">other</author>
675 <date>1970-01-14T21:20:00+00:00</date>
675 <date>1970-01-14T21:20:00+00:00</date>
676 <msg xml:space="preserve">no person</msg>
676 <msg xml:space="preserve">no person</msg>
677 <paths>
677 <paths>
678 <path action="A">c</path>
678 <path action="A">c</path>
679 </paths>
679 </paths>
680 </logentry>
680 </logentry>
681 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
681 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
682 <author email="other@place">A. N. Other</author>
682 <author email="other@place">A. N. Other</author>
683 <date>1970-01-13T17:33:20+00:00</date>
683 <date>1970-01-13T17:33:20+00:00</date>
684 <msg xml:space="preserve">other 1
684 <msg xml:space="preserve">other 1
685 other 2
685 other 2
686
686
687 other 3</msg>
687 other 3</msg>
688 <paths>
688 <paths>
689 <path action="A">b</path>
689 <path action="A">b</path>
690 </paths>
690 </paths>
691 </logentry>
691 </logentry>
692 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
692 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
693 <author email="user@hostname">User Name</author>
693 <author email="user@hostname">User Name</author>
694 <date>1970-01-12T13:46:40+00:00</date>
694 <date>1970-01-12T13:46:40+00:00</date>
695 <msg xml:space="preserve">line 1
695 <msg xml:space="preserve">line 1
696 line 2</msg>
696 line 2</msg>
697 <paths>
697 <paths>
698 <path action="A">a</path>
698 <path action="A">a</path>
699 </paths>
699 </paths>
700 </logentry>
700 </logentry>
701 </log>
701 </log>
702
702
703 $ hg log --debug --style xml
703 $ hg log --debug --style xml
704 <?xml version="1.0"?>
704 <?xml version="1.0"?>
705 <log>
705 <log>
706 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
706 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
707 <tag>tip</tag>
707 <tag>tip</tag>
708 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
708 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
709 <parent revision="-1" node="0000000000000000000000000000000000000000" />
709 <parent revision="-1" node="0000000000000000000000000000000000000000" />
710 <author email="test">test</author>
710 <author email="test">test</author>
711 <date>2020-01-01T10:01:00+00:00</date>
711 <date>2020-01-01T10:01:00+00:00</date>
712 <msg xml:space="preserve">third</msg>
712 <msg xml:space="preserve">third</msg>
713 <paths>
713 <paths>
714 <path action="A">fourth</path>
714 <path action="A">fourth</path>
715 <path action="A">third</path>
715 <path action="A">third</path>
716 <path action="R">second</path>
716 <path action="R">second</path>
717 </paths>
717 </paths>
718 <copies>
718 <copies>
719 <copy source="second">fourth</copy>
719 <copy source="second">fourth</copy>
720 </copies>
720 </copies>
721 <extra key="branch">default</extra>
721 <extra key="branch">default</extra>
722 </logentry>
722 </logentry>
723 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
723 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
724 <parent revision="-1" node="0000000000000000000000000000000000000000" />
724 <parent revision="-1" node="0000000000000000000000000000000000000000" />
725 <parent revision="-1" node="0000000000000000000000000000000000000000" />
725 <parent revision="-1" node="0000000000000000000000000000000000000000" />
726 <author email="user@hostname">User Name</author>
726 <author email="user@hostname">User Name</author>
727 <date>1970-01-12T13:46:40+00:00</date>
727 <date>1970-01-12T13:46:40+00:00</date>
728 <msg xml:space="preserve">second</msg>
728 <msg xml:space="preserve">second</msg>
729 <paths>
729 <paths>
730 <path action="A">second</path>
730 <path action="A">second</path>
731 </paths>
731 </paths>
732 <extra key="branch">default</extra>
732 <extra key="branch">default</extra>
733 </logentry>
733 </logentry>
734 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
734 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
735 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
735 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
736 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
736 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
737 <author email="person">person</author>
737 <author email="person">person</author>
738 <date>1970-01-18T08:40:01+00:00</date>
738 <date>1970-01-18T08:40:01+00:00</date>
739 <msg xml:space="preserve">merge</msg>
739 <msg xml:space="preserve">merge</msg>
740 <paths>
740 <paths>
741 </paths>
741 </paths>
742 <extra key="branch">default</extra>
742 <extra key="branch">default</extra>
743 </logentry>
743 </logentry>
744 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
744 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
745 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
745 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
746 <parent revision="-1" node="0000000000000000000000000000000000000000" />
746 <parent revision="-1" node="0000000000000000000000000000000000000000" />
747 <author email="person">person</author>
747 <author email="person">person</author>
748 <date>1970-01-18T08:40:00+00:00</date>
748 <date>1970-01-18T08:40:00+00:00</date>
749 <msg xml:space="preserve">new head</msg>
749 <msg xml:space="preserve">new head</msg>
750 <paths>
750 <paths>
751 <path action="A">d</path>
751 <path action="A">d</path>
752 </paths>
752 </paths>
753 <extra key="branch">default</extra>
753 <extra key="branch">default</extra>
754 </logentry>
754 </logentry>
755 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
755 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
756 <branch>foo</branch>
756 <branch>foo</branch>
757 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
757 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
758 <parent revision="-1" node="0000000000000000000000000000000000000000" />
758 <parent revision="-1" node="0000000000000000000000000000000000000000" />
759 <author email="person">person</author>
759 <author email="person">person</author>
760 <date>1970-01-17T04:53:20+00:00</date>
760 <date>1970-01-17T04:53:20+00:00</date>
761 <msg xml:space="preserve">new branch</msg>
761 <msg xml:space="preserve">new branch</msg>
762 <paths>
762 <paths>
763 </paths>
763 </paths>
764 <extra key="branch">foo</extra>
764 <extra key="branch">foo</extra>
765 </logentry>
765 </logentry>
766 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
766 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
767 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
767 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
768 <parent revision="-1" node="0000000000000000000000000000000000000000" />
768 <parent revision="-1" node="0000000000000000000000000000000000000000" />
769 <author email="person">person</author>
769 <author email="person">person</author>
770 <date>1970-01-16T01:06:40+00:00</date>
770 <date>1970-01-16T01:06:40+00:00</date>
771 <msg xml:space="preserve">no user, no domain</msg>
771 <msg xml:space="preserve">no user, no domain</msg>
772 <paths>
772 <paths>
773 <path action="M">c</path>
773 <path action="M">c</path>
774 </paths>
774 </paths>
775 <extra key="branch">default</extra>
775 <extra key="branch">default</extra>
776 </logentry>
776 </logentry>
777 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
777 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
778 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
778 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
779 <parent revision="-1" node="0000000000000000000000000000000000000000" />
779 <parent revision="-1" node="0000000000000000000000000000000000000000" />
780 <author email="other@place">other</author>
780 <author email="other@place">other</author>
781 <date>1970-01-14T21:20:00+00:00</date>
781 <date>1970-01-14T21:20:00+00:00</date>
782 <msg xml:space="preserve">no person</msg>
782 <msg xml:space="preserve">no person</msg>
783 <paths>
783 <paths>
784 <path action="A">c</path>
784 <path action="A">c</path>
785 </paths>
785 </paths>
786 <extra key="branch">default</extra>
786 <extra key="branch">default</extra>
787 </logentry>
787 </logentry>
788 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
788 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
789 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
789 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
790 <parent revision="-1" node="0000000000000000000000000000000000000000" />
790 <parent revision="-1" node="0000000000000000000000000000000000000000" />
791 <author email="other@place">A. N. Other</author>
791 <author email="other@place">A. N. Other</author>
792 <date>1970-01-13T17:33:20+00:00</date>
792 <date>1970-01-13T17:33:20+00:00</date>
793 <msg xml:space="preserve">other 1
793 <msg xml:space="preserve">other 1
794 other 2
794 other 2
795
795
796 other 3</msg>
796 other 3</msg>
797 <paths>
797 <paths>
798 <path action="A">b</path>
798 <path action="A">b</path>
799 </paths>
799 </paths>
800 <extra key="branch">default</extra>
800 <extra key="branch">default</extra>
801 </logentry>
801 </logentry>
802 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
802 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
803 <parent revision="-1" node="0000000000000000000000000000000000000000" />
803 <parent revision="-1" node="0000000000000000000000000000000000000000" />
804 <parent revision="-1" node="0000000000000000000000000000000000000000" />
804 <parent revision="-1" node="0000000000000000000000000000000000000000" />
805 <author email="user@hostname">User Name</author>
805 <author email="user@hostname">User Name</author>
806 <date>1970-01-12T13:46:40+00:00</date>
806 <date>1970-01-12T13:46:40+00:00</date>
807 <msg xml:space="preserve">line 1
807 <msg xml:space="preserve">line 1
808 line 2</msg>
808 line 2</msg>
809 <paths>
809 <paths>
810 <path action="A">a</path>
810 <path action="A">a</path>
811 </paths>
811 </paths>
812 <extra key="branch">default</extra>
812 <extra key="branch">default</extra>
813 </logentry>
813 </logentry>
814 </log>
814 </log>
815
815
816
816
817 Test JSON style:
817 Test JSON style:
818
818
819 $ hg log -k nosuch -Tjson
819 $ hg log -k nosuch -Tjson
820 []
820 []
821
821
822 $ hg log -qr . -Tjson
822 $ hg log -qr . -Tjson
823 [
823 [
824 {
824 {
825 "rev": 8,
825 "rev": 8,
826 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
826 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
827 }
827 }
828 ]
828 ]
829
829
830 $ hg log -vpr . -Tjson --stat
830 $ hg log -vpr . -Tjson --stat
831 [
831 [
832 {
832 {
833 "rev": 8,
833 "rev": 8,
834 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
834 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
835 "branch": "default",
835 "branch": "default",
836 "phase": "draft",
836 "phase": "draft",
837 "user": "test",
837 "user": "test",
838 "date": [1577872860, 0],
838 "date": [1577872860, 0],
839 "desc": "third",
839 "desc": "third",
840 "bookmarks": [],
840 "bookmarks": [],
841 "tags": ["tip"],
841 "tags": ["tip"],
842 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
842 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
843 "files": ["fourth", "second", "third"],
843 "files": ["fourth", "second", "third"],
844 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
844 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
845 "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"
845 "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"
846 }
846 }
847 ]
847 ]
848
848
849 honor --git but not format-breaking diffopts
849 honor --git but not format-breaking diffopts
850 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
850 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
851 [
851 [
852 {
852 {
853 "rev": 8,
853 "rev": 8,
854 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
854 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
855 "branch": "default",
855 "branch": "default",
856 "phase": "draft",
856 "phase": "draft",
857 "user": "test",
857 "user": "test",
858 "date": [1577872860, 0],
858 "date": [1577872860, 0],
859 "desc": "third",
859 "desc": "third",
860 "bookmarks": [],
860 "bookmarks": [],
861 "tags": ["tip"],
861 "tags": ["tip"],
862 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
862 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
863 "files": ["fourth", "second", "third"],
863 "files": ["fourth", "second", "third"],
864 "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"
864 "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"
865 }
865 }
866 ]
866 ]
867
867
868 $ hg log -T json
868 $ hg log -T json
869 [
869 [
870 {
870 {
871 "rev": 8,
871 "rev": 8,
872 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
872 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
873 "branch": "default",
873 "branch": "default",
874 "phase": "draft",
874 "phase": "draft",
875 "user": "test",
875 "user": "test",
876 "date": [1577872860, 0],
876 "date": [1577872860, 0],
877 "desc": "third",
877 "desc": "third",
878 "bookmarks": [],
878 "bookmarks": [],
879 "tags": ["tip"],
879 "tags": ["tip"],
880 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
880 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
881 },
881 },
882 {
882 {
883 "rev": 7,
883 "rev": 7,
884 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
884 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
885 "branch": "default",
885 "branch": "default",
886 "phase": "draft",
886 "phase": "draft",
887 "user": "User Name <user@hostname>",
887 "user": "User Name <user@hostname>",
888 "date": [1000000, 0],
888 "date": [1000000, 0],
889 "desc": "second",
889 "desc": "second",
890 "bookmarks": [],
890 "bookmarks": [],
891 "tags": [],
891 "tags": [],
892 "parents": ["0000000000000000000000000000000000000000"]
892 "parents": ["0000000000000000000000000000000000000000"]
893 },
893 },
894 {
894 {
895 "rev": 6,
895 "rev": 6,
896 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
896 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
897 "branch": "default",
897 "branch": "default",
898 "phase": "draft",
898 "phase": "draft",
899 "user": "person",
899 "user": "person",
900 "date": [1500001, 0],
900 "date": [1500001, 0],
901 "desc": "merge",
901 "desc": "merge",
902 "bookmarks": [],
902 "bookmarks": [],
903 "tags": [],
903 "tags": [],
904 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
904 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
905 },
905 },
906 {
906 {
907 "rev": 5,
907 "rev": 5,
908 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
908 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
909 "branch": "default",
909 "branch": "default",
910 "phase": "draft",
910 "phase": "draft",
911 "user": "person",
911 "user": "person",
912 "date": [1500000, 0],
912 "date": [1500000, 0],
913 "desc": "new head",
913 "desc": "new head",
914 "bookmarks": [],
914 "bookmarks": [],
915 "tags": [],
915 "tags": [],
916 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
916 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
917 },
917 },
918 {
918 {
919 "rev": 4,
919 "rev": 4,
920 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
920 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
921 "branch": "foo",
921 "branch": "foo",
922 "phase": "draft",
922 "phase": "draft",
923 "user": "person",
923 "user": "person",
924 "date": [1400000, 0],
924 "date": [1400000, 0],
925 "desc": "new branch",
925 "desc": "new branch",
926 "bookmarks": [],
926 "bookmarks": [],
927 "tags": [],
927 "tags": [],
928 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
928 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
929 },
929 },
930 {
930 {
931 "rev": 3,
931 "rev": 3,
932 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
932 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
933 "branch": "default",
933 "branch": "default",
934 "phase": "draft",
934 "phase": "draft",
935 "user": "person",
935 "user": "person",
936 "date": [1300000, 0],
936 "date": [1300000, 0],
937 "desc": "no user, no domain",
937 "desc": "no user, no domain",
938 "bookmarks": [],
938 "bookmarks": [],
939 "tags": [],
939 "tags": [],
940 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
940 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
941 },
941 },
942 {
942 {
943 "rev": 2,
943 "rev": 2,
944 "node": "97054abb4ab824450e9164180baf491ae0078465",
944 "node": "97054abb4ab824450e9164180baf491ae0078465",
945 "branch": "default",
945 "branch": "default",
946 "phase": "draft",
946 "phase": "draft",
947 "user": "other@place",
947 "user": "other@place",
948 "date": [1200000, 0],
948 "date": [1200000, 0],
949 "desc": "no person",
949 "desc": "no person",
950 "bookmarks": [],
950 "bookmarks": [],
951 "tags": [],
951 "tags": [],
952 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
952 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
953 },
953 },
954 {
954 {
955 "rev": 1,
955 "rev": 1,
956 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
956 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
957 "branch": "default",
957 "branch": "default",
958 "phase": "draft",
958 "phase": "draft",
959 "user": "A. N. Other <other@place>",
959 "user": "A. N. Other <other@place>",
960 "date": [1100000, 0],
960 "date": [1100000, 0],
961 "desc": "other 1\nother 2\n\nother 3",
961 "desc": "other 1\nother 2\n\nother 3",
962 "bookmarks": [],
962 "bookmarks": [],
963 "tags": [],
963 "tags": [],
964 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
964 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
965 },
965 },
966 {
966 {
967 "rev": 0,
967 "rev": 0,
968 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
968 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
969 "branch": "default",
969 "branch": "default",
970 "phase": "draft",
970 "phase": "draft",
971 "user": "User Name <user@hostname>",
971 "user": "User Name <user@hostname>",
972 "date": [1000000, 0],
972 "date": [1000000, 0],
973 "desc": "line 1\nline 2",
973 "desc": "line 1\nline 2",
974 "bookmarks": [],
974 "bookmarks": [],
975 "tags": [],
975 "tags": [],
976 "parents": ["0000000000000000000000000000000000000000"]
976 "parents": ["0000000000000000000000000000000000000000"]
977 }
977 }
978 ]
978 ]
979
979
980 $ hg heads -v -Tjson
980 $ hg heads -v -Tjson
981 [
981 [
982 {
982 {
983 "rev": 8,
983 "rev": 8,
984 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
984 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
985 "branch": "default",
985 "branch": "default",
986 "phase": "draft",
986 "phase": "draft",
987 "user": "test",
987 "user": "test",
988 "date": [1577872860, 0],
988 "date": [1577872860, 0],
989 "desc": "third",
989 "desc": "third",
990 "bookmarks": [],
990 "bookmarks": [],
991 "tags": ["tip"],
991 "tags": ["tip"],
992 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
992 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
993 "files": ["fourth", "second", "third"]
993 "files": ["fourth", "second", "third"]
994 },
994 },
995 {
995 {
996 "rev": 6,
996 "rev": 6,
997 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
997 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
998 "branch": "default",
998 "branch": "default",
999 "phase": "draft",
999 "phase": "draft",
1000 "user": "person",
1000 "user": "person",
1001 "date": [1500001, 0],
1001 "date": [1500001, 0],
1002 "desc": "merge",
1002 "desc": "merge",
1003 "bookmarks": [],
1003 "bookmarks": [],
1004 "tags": [],
1004 "tags": [],
1005 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1005 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1006 "files": []
1006 "files": []
1007 },
1007 },
1008 {
1008 {
1009 "rev": 4,
1009 "rev": 4,
1010 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1010 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1011 "branch": "foo",
1011 "branch": "foo",
1012 "phase": "draft",
1012 "phase": "draft",
1013 "user": "person",
1013 "user": "person",
1014 "date": [1400000, 0],
1014 "date": [1400000, 0],
1015 "desc": "new branch",
1015 "desc": "new branch",
1016 "bookmarks": [],
1016 "bookmarks": [],
1017 "tags": [],
1017 "tags": [],
1018 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1018 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1019 "files": []
1019 "files": []
1020 }
1020 }
1021 ]
1021 ]
1022
1022
1023 $ hg log --debug -Tjson
1023 $ hg log --debug -Tjson
1024 [
1024 [
1025 {
1025 {
1026 "rev": 8,
1026 "rev": 8,
1027 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1027 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1028 "branch": "default",
1028 "branch": "default",
1029 "phase": "draft",
1029 "phase": "draft",
1030 "user": "test",
1030 "user": "test",
1031 "date": [1577872860, 0],
1031 "date": [1577872860, 0],
1032 "desc": "third",
1032 "desc": "third",
1033 "bookmarks": [],
1033 "bookmarks": [],
1034 "tags": ["tip"],
1034 "tags": ["tip"],
1035 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1035 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1036 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1036 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1037 "extra": {"branch": "default"},
1037 "extra": {"branch": "default"},
1038 "modified": [],
1038 "modified": [],
1039 "added": ["fourth", "third"],
1039 "added": ["fourth", "third"],
1040 "removed": ["second"]
1040 "removed": ["second"]
1041 },
1041 },
1042 {
1042 {
1043 "rev": 7,
1043 "rev": 7,
1044 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1044 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1045 "branch": "default",
1045 "branch": "default",
1046 "phase": "draft",
1046 "phase": "draft",
1047 "user": "User Name <user@hostname>",
1047 "user": "User Name <user@hostname>",
1048 "date": [1000000, 0],
1048 "date": [1000000, 0],
1049 "desc": "second",
1049 "desc": "second",
1050 "bookmarks": [],
1050 "bookmarks": [],
1051 "tags": [],
1051 "tags": [],
1052 "parents": ["0000000000000000000000000000000000000000"],
1052 "parents": ["0000000000000000000000000000000000000000"],
1053 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1053 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1054 "extra": {"branch": "default"},
1054 "extra": {"branch": "default"},
1055 "modified": [],
1055 "modified": [],
1056 "added": ["second"],
1056 "added": ["second"],
1057 "removed": []
1057 "removed": []
1058 },
1058 },
1059 {
1059 {
1060 "rev": 6,
1060 "rev": 6,
1061 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1061 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1062 "branch": "default",
1062 "branch": "default",
1063 "phase": "draft",
1063 "phase": "draft",
1064 "user": "person",
1064 "user": "person",
1065 "date": [1500001, 0],
1065 "date": [1500001, 0],
1066 "desc": "merge",
1066 "desc": "merge",
1067 "bookmarks": [],
1067 "bookmarks": [],
1068 "tags": [],
1068 "tags": [],
1069 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1069 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1070 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1070 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1071 "extra": {"branch": "default"},
1071 "extra": {"branch": "default"},
1072 "modified": [],
1072 "modified": [],
1073 "added": [],
1073 "added": [],
1074 "removed": []
1074 "removed": []
1075 },
1075 },
1076 {
1076 {
1077 "rev": 5,
1077 "rev": 5,
1078 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1078 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1079 "branch": "default",
1079 "branch": "default",
1080 "phase": "draft",
1080 "phase": "draft",
1081 "user": "person",
1081 "user": "person",
1082 "date": [1500000, 0],
1082 "date": [1500000, 0],
1083 "desc": "new head",
1083 "desc": "new head",
1084 "bookmarks": [],
1084 "bookmarks": [],
1085 "tags": [],
1085 "tags": [],
1086 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1086 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1087 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1087 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1088 "extra": {"branch": "default"},
1088 "extra": {"branch": "default"},
1089 "modified": [],
1089 "modified": [],
1090 "added": ["d"],
1090 "added": ["d"],
1091 "removed": []
1091 "removed": []
1092 },
1092 },
1093 {
1093 {
1094 "rev": 4,
1094 "rev": 4,
1095 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1095 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1096 "branch": "foo",
1096 "branch": "foo",
1097 "phase": "draft",
1097 "phase": "draft",
1098 "user": "person",
1098 "user": "person",
1099 "date": [1400000, 0],
1099 "date": [1400000, 0],
1100 "desc": "new branch",
1100 "desc": "new branch",
1101 "bookmarks": [],
1101 "bookmarks": [],
1102 "tags": [],
1102 "tags": [],
1103 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1103 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1104 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1104 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1105 "extra": {"branch": "foo"},
1105 "extra": {"branch": "foo"},
1106 "modified": [],
1106 "modified": [],
1107 "added": [],
1107 "added": [],
1108 "removed": []
1108 "removed": []
1109 },
1109 },
1110 {
1110 {
1111 "rev": 3,
1111 "rev": 3,
1112 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1112 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1113 "branch": "default",
1113 "branch": "default",
1114 "phase": "draft",
1114 "phase": "draft",
1115 "user": "person",
1115 "user": "person",
1116 "date": [1300000, 0],
1116 "date": [1300000, 0],
1117 "desc": "no user, no domain",
1117 "desc": "no user, no domain",
1118 "bookmarks": [],
1118 "bookmarks": [],
1119 "tags": [],
1119 "tags": [],
1120 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1120 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1121 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1121 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1122 "extra": {"branch": "default"},
1122 "extra": {"branch": "default"},
1123 "modified": ["c"],
1123 "modified": ["c"],
1124 "added": [],
1124 "added": [],
1125 "removed": []
1125 "removed": []
1126 },
1126 },
1127 {
1127 {
1128 "rev": 2,
1128 "rev": 2,
1129 "node": "97054abb4ab824450e9164180baf491ae0078465",
1129 "node": "97054abb4ab824450e9164180baf491ae0078465",
1130 "branch": "default",
1130 "branch": "default",
1131 "phase": "draft",
1131 "phase": "draft",
1132 "user": "other@place",
1132 "user": "other@place",
1133 "date": [1200000, 0],
1133 "date": [1200000, 0],
1134 "desc": "no person",
1134 "desc": "no person",
1135 "bookmarks": [],
1135 "bookmarks": [],
1136 "tags": [],
1136 "tags": [],
1137 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1137 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1138 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1138 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1139 "extra": {"branch": "default"},
1139 "extra": {"branch": "default"},
1140 "modified": [],
1140 "modified": [],
1141 "added": ["c"],
1141 "added": ["c"],
1142 "removed": []
1142 "removed": []
1143 },
1143 },
1144 {
1144 {
1145 "rev": 1,
1145 "rev": 1,
1146 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1146 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1147 "branch": "default",
1147 "branch": "default",
1148 "phase": "draft",
1148 "phase": "draft",
1149 "user": "A. N. Other <other@place>",
1149 "user": "A. N. Other <other@place>",
1150 "date": [1100000, 0],
1150 "date": [1100000, 0],
1151 "desc": "other 1\nother 2\n\nother 3",
1151 "desc": "other 1\nother 2\n\nother 3",
1152 "bookmarks": [],
1152 "bookmarks": [],
1153 "tags": [],
1153 "tags": [],
1154 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1154 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1155 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1155 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1156 "extra": {"branch": "default"},
1156 "extra": {"branch": "default"},
1157 "modified": [],
1157 "modified": [],
1158 "added": ["b"],
1158 "added": ["b"],
1159 "removed": []
1159 "removed": []
1160 },
1160 },
1161 {
1161 {
1162 "rev": 0,
1162 "rev": 0,
1163 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1163 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1164 "branch": "default",
1164 "branch": "default",
1165 "phase": "draft",
1165 "phase": "draft",
1166 "user": "User Name <user@hostname>",
1166 "user": "User Name <user@hostname>",
1167 "date": [1000000, 0],
1167 "date": [1000000, 0],
1168 "desc": "line 1\nline 2",
1168 "desc": "line 1\nline 2",
1169 "bookmarks": [],
1169 "bookmarks": [],
1170 "tags": [],
1170 "tags": [],
1171 "parents": ["0000000000000000000000000000000000000000"],
1171 "parents": ["0000000000000000000000000000000000000000"],
1172 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1172 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1173 "extra": {"branch": "default"},
1173 "extra": {"branch": "default"},
1174 "modified": [],
1174 "modified": [],
1175 "added": ["a"],
1175 "added": ["a"],
1176 "removed": []
1176 "removed": []
1177 }
1177 }
1178 ]
1178 ]
1179
1179
1180 Error if style not readable:
1180 Error if style not readable:
1181
1181
1182 #if unix-permissions no-root
1182 #if unix-permissions no-root
1183 $ touch q
1183 $ touch q
1184 $ chmod 0 q
1184 $ chmod 0 q
1185 $ hg log --style ./q
1185 $ hg log --style ./q
1186 abort: Permission denied: ./q
1186 abort: Permission denied: ./q
1187 [255]
1187 [255]
1188 #endif
1188 #endif
1189
1189
1190 Error if no style:
1190 Error if no style:
1191
1191
1192 $ hg log --style notexist
1192 $ hg log --style notexist
1193 abort: style 'notexist' not found
1193 abort: style 'notexist' not found
1194 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1194 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1195 [255]
1195 [255]
1196
1196
1197 $ hg log -T list
1197 $ hg log -T list
1198 available styles: bisect, changelog, compact, default, phases, show, status, xml
1198 available styles: bisect, changelog, compact, default, phases, show, status, xml
1199 abort: specify a template
1199 abort: specify a template
1200 [255]
1200 [255]
1201
1201
1202 Error if style missing key:
1202 Error if style missing key:
1203
1203
1204 $ echo 'q = q' > t
1204 $ echo 'q = q' > t
1205 $ hg log --style ./t
1205 $ hg log --style ./t
1206 abort: "changeset" not in template map
1206 abort: "changeset" not in template map
1207 [255]
1207 [255]
1208
1208
1209 Error if style missing value:
1209 Error if style missing value:
1210
1210
1211 $ echo 'changeset =' > t
1211 $ echo 'changeset =' > t
1212 $ hg log --style t
1212 $ hg log --style t
1213 hg: parse error at t:1: missing value
1213 hg: parse error at t:1: missing value
1214 [255]
1214 [255]
1215
1215
1216 Error if include fails:
1216 Error if include fails:
1217
1217
1218 $ echo 'changeset = q' >> t
1218 $ echo 'changeset = q' >> t
1219 #if unix-permissions no-root
1219 #if unix-permissions no-root
1220 $ hg log --style ./t
1220 $ hg log --style ./t
1221 abort: template file ./q: Permission denied
1221 abort: template file ./q: Permission denied
1222 [255]
1222 [255]
1223 $ rm -f q
1223 $ rm -f q
1224 #endif
1224 #endif
1225
1225
1226 Include works:
1226 Include works:
1227
1227
1228 $ echo '{rev}' > q
1228 $ echo '{rev}' > q
1229 $ hg log --style ./t
1229 $ hg log --style ./t
1230 8
1230 8
1231 7
1231 7
1232 6
1232 6
1233 5
1233 5
1234 4
1234 4
1235 3
1235 3
1236 2
1236 2
1237 1
1237 1
1238 0
1238 0
1239
1239
1240 Check that recursive reference does not fall into RuntimeError (issue4758):
1240 Check that recursive reference does not fall into RuntimeError (issue4758):
1241
1241
1242 common mistake:
1242 common mistake:
1243
1243
1244 $ cat << EOF > issue4758
1244 $ cat << EOF > issue4758
1245 > changeset = '{changeset}\n'
1245 > changeset = '{changeset}\n'
1246 > EOF
1246 > EOF
1247 $ hg log --style ./issue4758
1247 $ hg log --style ./issue4758
1248 abort: recursive reference 'changeset' in template
1248 abort: recursive reference 'changeset' in template
1249 [255]
1249 [255]
1250
1250
1251 circular reference:
1251 circular reference:
1252
1252
1253 $ cat << EOF > issue4758
1253 $ cat << EOF > issue4758
1254 > changeset = '{foo}'
1254 > changeset = '{foo}'
1255 > foo = '{changeset}'
1255 > foo = '{changeset}'
1256 > EOF
1256 > EOF
1257 $ hg log --style ./issue4758
1257 $ hg log --style ./issue4758
1258 abort: recursive reference 'foo' in template
1258 abort: recursive reference 'foo' in template
1259 [255]
1259 [255]
1260
1260
1261 buildmap() -> gettemplate(), where no thunk was made:
1261 buildmap() -> gettemplate(), where no thunk was made:
1262
1262
1263 $ cat << EOF > issue4758
1263 $ cat << EOF > issue4758
1264 > changeset = '{files % changeset}\n'
1264 > changeset = '{files % changeset}\n'
1265 > EOF
1265 > EOF
1266 $ hg log --style ./issue4758
1266 $ hg log --style ./issue4758
1267 abort: recursive reference 'changeset' in template
1267 abort: recursive reference 'changeset' in template
1268 [255]
1268 [255]
1269
1269
1270 not a recursion if a keyword of the same name exists:
1270 not a recursion if a keyword of the same name exists:
1271
1271
1272 $ cat << EOF > issue4758
1272 $ cat << EOF > issue4758
1273 > changeset = '{tags % rev}'
1273 > changeset = '{tags % rev}'
1274 > rev = '{rev} {tag}\n'
1274 > rev = '{rev} {tag}\n'
1275 > EOF
1275 > EOF
1276 $ hg log --style ./issue4758 -r tip
1276 $ hg log --style ./issue4758 -r tip
1277 8 tip
1277 8 tip
1278
1278
1279 Check that {phase} works correctly on parents:
1279 Check that {phase} works correctly on parents:
1280
1280
1281 $ cat << EOF > parentphase
1281 $ cat << EOF > parentphase
1282 > changeset_debug = '{rev} ({phase}):{parents}\n'
1282 > changeset_debug = '{rev} ({phase}):{parents}\n'
1283 > parent = ' {rev} ({phase})'
1283 > parent = ' {rev} ({phase})'
1284 > EOF
1284 > EOF
1285 $ hg phase -r 5 --public
1285 $ hg phase -r 5 --public
1286 $ hg phase -r 7 --secret --force
1286 $ hg phase -r 7 --secret --force
1287 $ hg log --debug -G --style ./parentphase
1287 $ hg log --debug -G --style ./parentphase
1288 @ 8 (secret): 7 (secret) -1 (public)
1288 @ 8 (secret): 7 (secret) -1 (public)
1289 |
1289 |
1290 o 7 (secret): -1 (public) -1 (public)
1290 o 7 (secret): -1 (public) -1 (public)
1291
1291
1292 o 6 (draft): 5 (public) 4 (draft)
1292 o 6 (draft): 5 (public) 4 (draft)
1293 |\
1293 |\
1294 | o 5 (public): 3 (public) -1 (public)
1294 | o 5 (public): 3 (public) -1 (public)
1295 | |
1295 | |
1296 o | 4 (draft): 3 (public) -1 (public)
1296 o | 4 (draft): 3 (public) -1 (public)
1297 |/
1297 |/
1298 o 3 (public): 2 (public) -1 (public)
1298 o 3 (public): 2 (public) -1 (public)
1299 |
1299 |
1300 o 2 (public): 1 (public) -1 (public)
1300 o 2 (public): 1 (public) -1 (public)
1301 |
1301 |
1302 o 1 (public): 0 (public) -1 (public)
1302 o 1 (public): 0 (public) -1 (public)
1303 |
1303 |
1304 o 0 (public): -1 (public) -1 (public)
1304 o 0 (public): -1 (public) -1 (public)
1305
1305
1306
1306
1307 Missing non-standard names give no error (backward compatibility):
1307 Missing non-standard names give no error (backward compatibility):
1308
1308
1309 $ echo "changeset = '{c}'" > t
1309 $ echo "changeset = '{c}'" > t
1310 $ hg log --style ./t
1310 $ hg log --style ./t
1311
1311
1312 Defining non-standard name works:
1312 Defining non-standard name works:
1313
1313
1314 $ cat <<EOF > t
1314 $ cat <<EOF > t
1315 > changeset = '{c}'
1315 > changeset = '{c}'
1316 > c = q
1316 > c = q
1317 > EOF
1317 > EOF
1318 $ hg log --style ./t
1318 $ hg log --style ./t
1319 8
1319 8
1320 7
1320 7
1321 6
1321 6
1322 5
1322 5
1323 4
1323 4
1324 3
1324 3
1325 2
1325 2
1326 1
1326 1
1327 0
1327 0
1328
1328
1329 ui.style works:
1329 ui.style works:
1330
1330
1331 $ echo '[ui]' > .hg/hgrc
1331 $ echo '[ui]' > .hg/hgrc
1332 $ echo 'style = t' >> .hg/hgrc
1332 $ echo 'style = t' >> .hg/hgrc
1333 $ hg log
1333 $ hg log
1334 8
1334 8
1335 7
1335 7
1336 6
1336 6
1337 5
1337 5
1338 4
1338 4
1339 3
1339 3
1340 2
1340 2
1341 1
1341 1
1342 0
1342 0
1343
1343
1344
1344
1345 Issue338:
1345 Issue338:
1346
1346
1347 $ hg log --style=changelog > changelog
1347 $ hg log --style=changelog > changelog
1348
1348
1349 $ cat changelog
1349 $ cat changelog
1350 2020-01-01 test <test>
1350 2020-01-01 test <test>
1351
1351
1352 * fourth, second, third:
1352 * fourth, second, third:
1353 third
1353 third
1354 [95c24699272e] [tip]
1354 [95c24699272e] [tip]
1355
1355
1356 1970-01-12 User Name <user@hostname>
1356 1970-01-12 User Name <user@hostname>
1357
1357
1358 * second:
1358 * second:
1359 second
1359 second
1360 [29114dbae42b]
1360 [29114dbae42b]
1361
1361
1362 1970-01-18 person <person>
1362 1970-01-18 person <person>
1363
1363
1364 * merge
1364 * merge
1365 [d41e714fe50d]
1365 [d41e714fe50d]
1366
1366
1367 * d:
1367 * d:
1368 new head
1368 new head
1369 [13207e5a10d9]
1369 [13207e5a10d9]
1370
1370
1371 1970-01-17 person <person>
1371 1970-01-17 person <person>
1372
1372
1373 * new branch
1373 * new branch
1374 [bbe44766e73d] <foo>
1374 [bbe44766e73d] <foo>
1375
1375
1376 1970-01-16 person <person>
1376 1970-01-16 person <person>
1377
1377
1378 * c:
1378 * c:
1379 no user, no domain
1379 no user, no domain
1380 [10e46f2dcbf4]
1380 [10e46f2dcbf4]
1381
1381
1382 1970-01-14 other <other@place>
1382 1970-01-14 other <other@place>
1383
1383
1384 * c:
1384 * c:
1385 no person
1385 no person
1386 [97054abb4ab8]
1386 [97054abb4ab8]
1387
1387
1388 1970-01-13 A. N. Other <other@place>
1388 1970-01-13 A. N. Other <other@place>
1389
1389
1390 * b:
1390 * b:
1391 other 1 other 2
1391 other 1 other 2
1392
1392
1393 other 3
1393 other 3
1394 [b608e9d1a3f0]
1394 [b608e9d1a3f0]
1395
1395
1396 1970-01-12 User Name <user@hostname>
1396 1970-01-12 User Name <user@hostname>
1397
1397
1398 * a:
1398 * a:
1399 line 1 line 2
1399 line 1 line 2
1400 [1e4e1b8f71e0]
1400 [1e4e1b8f71e0]
1401
1401
1402
1402
1403 Issue2130: xml output for 'hg heads' is malformed
1403 Issue2130: xml output for 'hg heads' is malformed
1404
1404
1405 $ hg heads --style changelog
1405 $ hg heads --style changelog
1406 2020-01-01 test <test>
1406 2020-01-01 test <test>
1407
1407
1408 * fourth, second, third:
1408 * fourth, second, third:
1409 third
1409 third
1410 [95c24699272e] [tip]
1410 [95c24699272e] [tip]
1411
1411
1412 1970-01-18 person <person>
1412 1970-01-18 person <person>
1413
1413
1414 * merge
1414 * merge
1415 [d41e714fe50d]
1415 [d41e714fe50d]
1416
1416
1417 1970-01-17 person <person>
1417 1970-01-17 person <person>
1418
1418
1419 * new branch
1419 * new branch
1420 [bbe44766e73d] <foo>
1420 [bbe44766e73d] <foo>
1421
1421
1422
1422
1423 Keys work:
1423 Keys work:
1424
1424
1425 $ for key in author branch branches date desc file_adds file_dels file_mods \
1425 $ for key in author branch branches date desc file_adds file_dels file_mods \
1426 > file_copies file_copies_switch files \
1426 > file_copies file_copies_switch files \
1427 > manifest node parents rev tags diffstat extras \
1427 > manifest node parents rev tags diffstat extras \
1428 > p1rev p2rev p1node p2node; do
1428 > p1rev p2rev p1node p2node; do
1429 > for mode in '' --verbose --debug; do
1429 > for mode in '' --verbose --debug; do
1430 > hg log $mode --template "$key$mode: {$key}\n"
1430 > hg log $mode --template "$key$mode: {$key}\n"
1431 > done
1431 > done
1432 > done
1432 > done
1433 author: test
1433 author: test
1434 author: User Name <user@hostname>
1434 author: User Name <user@hostname>
1435 author: person
1435 author: person
1436 author: person
1436 author: person
1437 author: person
1437 author: person
1438 author: person
1438 author: person
1439 author: other@place
1439 author: other@place
1440 author: A. N. Other <other@place>
1440 author: A. N. Other <other@place>
1441 author: User Name <user@hostname>
1441 author: User Name <user@hostname>
1442 author--verbose: test
1442 author--verbose: test
1443 author--verbose: User Name <user@hostname>
1443 author--verbose: User Name <user@hostname>
1444 author--verbose: person
1444 author--verbose: person
1445 author--verbose: person
1445 author--verbose: person
1446 author--verbose: person
1446 author--verbose: person
1447 author--verbose: person
1447 author--verbose: person
1448 author--verbose: other@place
1448 author--verbose: other@place
1449 author--verbose: A. N. Other <other@place>
1449 author--verbose: A. N. Other <other@place>
1450 author--verbose: User Name <user@hostname>
1450 author--verbose: User Name <user@hostname>
1451 author--debug: test
1451 author--debug: test
1452 author--debug: User Name <user@hostname>
1452 author--debug: User Name <user@hostname>
1453 author--debug: person
1453 author--debug: person
1454 author--debug: person
1454 author--debug: person
1455 author--debug: person
1455 author--debug: person
1456 author--debug: person
1456 author--debug: person
1457 author--debug: other@place
1457 author--debug: other@place
1458 author--debug: A. N. Other <other@place>
1458 author--debug: A. N. Other <other@place>
1459 author--debug: User Name <user@hostname>
1459 author--debug: User Name <user@hostname>
1460 branch: default
1460 branch: default
1461 branch: default
1461 branch: default
1462 branch: default
1462 branch: default
1463 branch: default
1463 branch: default
1464 branch: foo
1464 branch: foo
1465 branch: default
1465 branch: default
1466 branch: default
1466 branch: default
1467 branch: default
1467 branch: default
1468 branch: default
1468 branch: default
1469 branch--verbose: default
1469 branch--verbose: default
1470 branch--verbose: default
1470 branch--verbose: default
1471 branch--verbose: default
1471 branch--verbose: default
1472 branch--verbose: default
1472 branch--verbose: default
1473 branch--verbose: foo
1473 branch--verbose: foo
1474 branch--verbose: default
1474 branch--verbose: default
1475 branch--verbose: default
1475 branch--verbose: default
1476 branch--verbose: default
1476 branch--verbose: default
1477 branch--verbose: default
1477 branch--verbose: default
1478 branch--debug: default
1478 branch--debug: default
1479 branch--debug: default
1479 branch--debug: default
1480 branch--debug: default
1480 branch--debug: default
1481 branch--debug: default
1481 branch--debug: default
1482 branch--debug: foo
1482 branch--debug: foo
1483 branch--debug: default
1483 branch--debug: default
1484 branch--debug: default
1484 branch--debug: default
1485 branch--debug: default
1485 branch--debug: default
1486 branch--debug: default
1486 branch--debug: default
1487 branches:
1487 branches:
1488 branches:
1488 branches:
1489 branches:
1489 branches:
1490 branches:
1490 branches:
1491 branches: foo
1491 branches: foo
1492 branches:
1492 branches:
1493 branches:
1493 branches:
1494 branches:
1494 branches:
1495 branches:
1495 branches:
1496 branches--verbose:
1496 branches--verbose:
1497 branches--verbose:
1497 branches--verbose:
1498 branches--verbose:
1498 branches--verbose:
1499 branches--verbose:
1499 branches--verbose:
1500 branches--verbose: foo
1500 branches--verbose: foo
1501 branches--verbose:
1501 branches--verbose:
1502 branches--verbose:
1502 branches--verbose:
1503 branches--verbose:
1503 branches--verbose:
1504 branches--verbose:
1504 branches--verbose:
1505 branches--debug:
1505 branches--debug:
1506 branches--debug:
1506 branches--debug:
1507 branches--debug:
1507 branches--debug:
1508 branches--debug:
1508 branches--debug:
1509 branches--debug: foo
1509 branches--debug: foo
1510 branches--debug:
1510 branches--debug:
1511 branches--debug:
1511 branches--debug:
1512 branches--debug:
1512 branches--debug:
1513 branches--debug:
1513 branches--debug:
1514 date: 1577872860.00
1514 date: 1577872860.00
1515 date: 1000000.00
1515 date: 1000000.00
1516 date: 1500001.00
1516 date: 1500001.00
1517 date: 1500000.00
1517 date: 1500000.00
1518 date: 1400000.00
1518 date: 1400000.00
1519 date: 1300000.00
1519 date: 1300000.00
1520 date: 1200000.00
1520 date: 1200000.00
1521 date: 1100000.00
1521 date: 1100000.00
1522 date: 1000000.00
1522 date: 1000000.00
1523 date--verbose: 1577872860.00
1523 date--verbose: 1577872860.00
1524 date--verbose: 1000000.00
1524 date--verbose: 1000000.00
1525 date--verbose: 1500001.00
1525 date--verbose: 1500001.00
1526 date--verbose: 1500000.00
1526 date--verbose: 1500000.00
1527 date--verbose: 1400000.00
1527 date--verbose: 1400000.00
1528 date--verbose: 1300000.00
1528 date--verbose: 1300000.00
1529 date--verbose: 1200000.00
1529 date--verbose: 1200000.00
1530 date--verbose: 1100000.00
1530 date--verbose: 1100000.00
1531 date--verbose: 1000000.00
1531 date--verbose: 1000000.00
1532 date--debug: 1577872860.00
1532 date--debug: 1577872860.00
1533 date--debug: 1000000.00
1533 date--debug: 1000000.00
1534 date--debug: 1500001.00
1534 date--debug: 1500001.00
1535 date--debug: 1500000.00
1535 date--debug: 1500000.00
1536 date--debug: 1400000.00
1536 date--debug: 1400000.00
1537 date--debug: 1300000.00
1537 date--debug: 1300000.00
1538 date--debug: 1200000.00
1538 date--debug: 1200000.00
1539 date--debug: 1100000.00
1539 date--debug: 1100000.00
1540 date--debug: 1000000.00
1540 date--debug: 1000000.00
1541 desc: third
1541 desc: third
1542 desc: second
1542 desc: second
1543 desc: merge
1543 desc: merge
1544 desc: new head
1544 desc: new head
1545 desc: new branch
1545 desc: new branch
1546 desc: no user, no domain
1546 desc: no user, no domain
1547 desc: no person
1547 desc: no person
1548 desc: other 1
1548 desc: other 1
1549 other 2
1549 other 2
1550
1550
1551 other 3
1551 other 3
1552 desc: line 1
1552 desc: line 1
1553 line 2
1553 line 2
1554 desc--verbose: third
1554 desc--verbose: third
1555 desc--verbose: second
1555 desc--verbose: second
1556 desc--verbose: merge
1556 desc--verbose: merge
1557 desc--verbose: new head
1557 desc--verbose: new head
1558 desc--verbose: new branch
1558 desc--verbose: new branch
1559 desc--verbose: no user, no domain
1559 desc--verbose: no user, no domain
1560 desc--verbose: no person
1560 desc--verbose: no person
1561 desc--verbose: other 1
1561 desc--verbose: other 1
1562 other 2
1562 other 2
1563
1563
1564 other 3
1564 other 3
1565 desc--verbose: line 1
1565 desc--verbose: line 1
1566 line 2
1566 line 2
1567 desc--debug: third
1567 desc--debug: third
1568 desc--debug: second
1568 desc--debug: second
1569 desc--debug: merge
1569 desc--debug: merge
1570 desc--debug: new head
1570 desc--debug: new head
1571 desc--debug: new branch
1571 desc--debug: new branch
1572 desc--debug: no user, no domain
1572 desc--debug: no user, no domain
1573 desc--debug: no person
1573 desc--debug: no person
1574 desc--debug: other 1
1574 desc--debug: other 1
1575 other 2
1575 other 2
1576
1576
1577 other 3
1577 other 3
1578 desc--debug: line 1
1578 desc--debug: line 1
1579 line 2
1579 line 2
1580 file_adds: fourth third
1580 file_adds: fourth third
1581 file_adds: second
1581 file_adds: second
1582 file_adds:
1582 file_adds:
1583 file_adds: d
1583 file_adds: d
1584 file_adds:
1584 file_adds:
1585 file_adds:
1585 file_adds:
1586 file_adds: c
1586 file_adds: c
1587 file_adds: b
1587 file_adds: b
1588 file_adds: a
1588 file_adds: a
1589 file_adds--verbose: fourth third
1589 file_adds--verbose: fourth third
1590 file_adds--verbose: second
1590 file_adds--verbose: second
1591 file_adds--verbose:
1591 file_adds--verbose:
1592 file_adds--verbose: d
1592 file_adds--verbose: d
1593 file_adds--verbose:
1593 file_adds--verbose:
1594 file_adds--verbose:
1594 file_adds--verbose:
1595 file_adds--verbose: c
1595 file_adds--verbose: c
1596 file_adds--verbose: b
1596 file_adds--verbose: b
1597 file_adds--verbose: a
1597 file_adds--verbose: a
1598 file_adds--debug: fourth third
1598 file_adds--debug: fourth third
1599 file_adds--debug: second
1599 file_adds--debug: second
1600 file_adds--debug:
1600 file_adds--debug:
1601 file_adds--debug: d
1601 file_adds--debug: d
1602 file_adds--debug:
1602 file_adds--debug:
1603 file_adds--debug:
1603 file_adds--debug:
1604 file_adds--debug: c
1604 file_adds--debug: c
1605 file_adds--debug: b
1605 file_adds--debug: b
1606 file_adds--debug: a
1606 file_adds--debug: a
1607 file_dels: second
1607 file_dels: second
1608 file_dels:
1608 file_dels:
1609 file_dels:
1609 file_dels:
1610 file_dels:
1610 file_dels:
1611 file_dels:
1611 file_dels:
1612 file_dels:
1612 file_dels:
1613 file_dels:
1613 file_dels:
1614 file_dels:
1614 file_dels:
1615 file_dels:
1615 file_dels:
1616 file_dels--verbose: second
1616 file_dels--verbose: second
1617 file_dels--verbose:
1617 file_dels--verbose:
1618 file_dels--verbose:
1618 file_dels--verbose:
1619 file_dels--verbose:
1619 file_dels--verbose:
1620 file_dels--verbose:
1620 file_dels--verbose:
1621 file_dels--verbose:
1621 file_dels--verbose:
1622 file_dels--verbose:
1622 file_dels--verbose:
1623 file_dels--verbose:
1623 file_dels--verbose:
1624 file_dels--verbose:
1624 file_dels--verbose:
1625 file_dels--debug: second
1625 file_dels--debug: second
1626 file_dels--debug:
1626 file_dels--debug:
1627 file_dels--debug:
1627 file_dels--debug:
1628 file_dels--debug:
1628 file_dels--debug:
1629 file_dels--debug:
1629 file_dels--debug:
1630 file_dels--debug:
1630 file_dels--debug:
1631 file_dels--debug:
1631 file_dels--debug:
1632 file_dels--debug:
1632 file_dels--debug:
1633 file_dels--debug:
1633 file_dels--debug:
1634 file_mods:
1634 file_mods:
1635 file_mods:
1635 file_mods:
1636 file_mods:
1636 file_mods:
1637 file_mods:
1637 file_mods:
1638 file_mods:
1638 file_mods:
1639 file_mods: c
1639 file_mods: c
1640 file_mods:
1640 file_mods:
1641 file_mods:
1641 file_mods:
1642 file_mods:
1642 file_mods:
1643 file_mods--verbose:
1643 file_mods--verbose:
1644 file_mods--verbose:
1644 file_mods--verbose:
1645 file_mods--verbose:
1645 file_mods--verbose:
1646 file_mods--verbose:
1646 file_mods--verbose:
1647 file_mods--verbose:
1647 file_mods--verbose:
1648 file_mods--verbose: c
1648 file_mods--verbose: c
1649 file_mods--verbose:
1649 file_mods--verbose:
1650 file_mods--verbose:
1650 file_mods--verbose:
1651 file_mods--verbose:
1651 file_mods--verbose:
1652 file_mods--debug:
1652 file_mods--debug:
1653 file_mods--debug:
1653 file_mods--debug:
1654 file_mods--debug:
1654 file_mods--debug:
1655 file_mods--debug:
1655 file_mods--debug:
1656 file_mods--debug:
1656 file_mods--debug:
1657 file_mods--debug: c
1657 file_mods--debug: c
1658 file_mods--debug:
1658 file_mods--debug:
1659 file_mods--debug:
1659 file_mods--debug:
1660 file_mods--debug:
1660 file_mods--debug:
1661 file_copies: fourth (second)
1661 file_copies: fourth (second)
1662 file_copies:
1662 file_copies:
1663 file_copies:
1663 file_copies:
1664 file_copies:
1664 file_copies:
1665 file_copies:
1665 file_copies:
1666 file_copies:
1666 file_copies:
1667 file_copies:
1667 file_copies:
1668 file_copies:
1668 file_copies:
1669 file_copies:
1669 file_copies:
1670 file_copies--verbose: fourth (second)
1670 file_copies--verbose: fourth (second)
1671 file_copies--verbose:
1671 file_copies--verbose:
1672 file_copies--verbose:
1672 file_copies--verbose:
1673 file_copies--verbose:
1673 file_copies--verbose:
1674 file_copies--verbose:
1674 file_copies--verbose:
1675 file_copies--verbose:
1675 file_copies--verbose:
1676 file_copies--verbose:
1676 file_copies--verbose:
1677 file_copies--verbose:
1677 file_copies--verbose:
1678 file_copies--verbose:
1678 file_copies--verbose:
1679 file_copies--debug: fourth (second)
1679 file_copies--debug: fourth (second)
1680 file_copies--debug:
1680 file_copies--debug:
1681 file_copies--debug:
1681 file_copies--debug:
1682 file_copies--debug:
1682 file_copies--debug:
1683 file_copies--debug:
1683 file_copies--debug:
1684 file_copies--debug:
1684 file_copies--debug:
1685 file_copies--debug:
1685 file_copies--debug:
1686 file_copies--debug:
1686 file_copies--debug:
1687 file_copies--debug:
1687 file_copies--debug:
1688 file_copies_switch:
1688 file_copies_switch:
1689 file_copies_switch:
1689 file_copies_switch:
1690 file_copies_switch:
1690 file_copies_switch:
1691 file_copies_switch:
1691 file_copies_switch:
1692 file_copies_switch:
1692 file_copies_switch:
1693 file_copies_switch:
1693 file_copies_switch:
1694 file_copies_switch:
1694 file_copies_switch:
1695 file_copies_switch:
1695 file_copies_switch:
1696 file_copies_switch:
1696 file_copies_switch:
1697 file_copies_switch--verbose:
1697 file_copies_switch--verbose:
1698 file_copies_switch--verbose:
1698 file_copies_switch--verbose:
1699 file_copies_switch--verbose:
1699 file_copies_switch--verbose:
1700 file_copies_switch--verbose:
1700 file_copies_switch--verbose:
1701 file_copies_switch--verbose:
1701 file_copies_switch--verbose:
1702 file_copies_switch--verbose:
1702 file_copies_switch--verbose:
1703 file_copies_switch--verbose:
1703 file_copies_switch--verbose:
1704 file_copies_switch--verbose:
1704 file_copies_switch--verbose:
1705 file_copies_switch--verbose:
1705 file_copies_switch--verbose:
1706 file_copies_switch--debug:
1706 file_copies_switch--debug:
1707 file_copies_switch--debug:
1707 file_copies_switch--debug:
1708 file_copies_switch--debug:
1708 file_copies_switch--debug:
1709 file_copies_switch--debug:
1709 file_copies_switch--debug:
1710 file_copies_switch--debug:
1710 file_copies_switch--debug:
1711 file_copies_switch--debug:
1711 file_copies_switch--debug:
1712 file_copies_switch--debug:
1712 file_copies_switch--debug:
1713 file_copies_switch--debug:
1713 file_copies_switch--debug:
1714 file_copies_switch--debug:
1714 file_copies_switch--debug:
1715 files: fourth second third
1715 files: fourth second third
1716 files: second
1716 files: second
1717 files:
1717 files:
1718 files: d
1718 files: d
1719 files:
1719 files:
1720 files: c
1720 files: c
1721 files: c
1721 files: c
1722 files: b
1722 files: b
1723 files: a
1723 files: a
1724 files--verbose: fourth second third
1724 files--verbose: fourth second third
1725 files--verbose: second
1725 files--verbose: second
1726 files--verbose:
1726 files--verbose:
1727 files--verbose: d
1727 files--verbose: d
1728 files--verbose:
1728 files--verbose:
1729 files--verbose: c
1729 files--verbose: c
1730 files--verbose: c
1730 files--verbose: c
1731 files--verbose: b
1731 files--verbose: b
1732 files--verbose: a
1732 files--verbose: a
1733 files--debug: fourth second third
1733 files--debug: fourth second third
1734 files--debug: second
1734 files--debug: second
1735 files--debug:
1735 files--debug:
1736 files--debug: d
1736 files--debug: d
1737 files--debug:
1737 files--debug:
1738 files--debug: c
1738 files--debug: c
1739 files--debug: c
1739 files--debug: c
1740 files--debug: b
1740 files--debug: b
1741 files--debug: a
1741 files--debug: a
1742 manifest: 6:94961b75a2da
1742 manifest: 6:94961b75a2da
1743 manifest: 5:f2dbc354b94e
1743 manifest: 5:f2dbc354b94e
1744 manifest: 4:4dc3def4f9b4
1744 manifest: 4:4dc3def4f9b4
1745 manifest: 4:4dc3def4f9b4
1745 manifest: 4:4dc3def4f9b4
1746 manifest: 3:cb5a1327723b
1746 manifest: 3:cb5a1327723b
1747 manifest: 3:cb5a1327723b
1747 manifest: 3:cb5a1327723b
1748 manifest: 2:6e0e82995c35
1748 manifest: 2:6e0e82995c35
1749 manifest: 1:4e8d705b1e53
1749 manifest: 1:4e8d705b1e53
1750 manifest: 0:a0c8bcbbb45c
1750 manifest: 0:a0c8bcbbb45c
1751 manifest--verbose: 6:94961b75a2da
1751 manifest--verbose: 6:94961b75a2da
1752 manifest--verbose: 5:f2dbc354b94e
1752 manifest--verbose: 5:f2dbc354b94e
1753 manifest--verbose: 4:4dc3def4f9b4
1753 manifest--verbose: 4:4dc3def4f9b4
1754 manifest--verbose: 4:4dc3def4f9b4
1754 manifest--verbose: 4:4dc3def4f9b4
1755 manifest--verbose: 3:cb5a1327723b
1755 manifest--verbose: 3:cb5a1327723b
1756 manifest--verbose: 3:cb5a1327723b
1756 manifest--verbose: 3:cb5a1327723b
1757 manifest--verbose: 2:6e0e82995c35
1757 manifest--verbose: 2:6e0e82995c35
1758 manifest--verbose: 1:4e8d705b1e53
1758 manifest--verbose: 1:4e8d705b1e53
1759 manifest--verbose: 0:a0c8bcbbb45c
1759 manifest--verbose: 0:a0c8bcbbb45c
1760 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1760 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1761 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1761 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1762 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1762 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1763 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1763 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1764 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1764 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1765 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1765 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1766 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1766 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1767 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1767 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1768 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1768 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1769 node: 95c24699272ef57d062b8bccc32c878bf841784a
1769 node: 95c24699272ef57d062b8bccc32c878bf841784a
1770 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1770 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1771 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1771 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1772 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1772 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1773 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1773 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1774 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1774 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1775 node: 97054abb4ab824450e9164180baf491ae0078465
1775 node: 97054abb4ab824450e9164180baf491ae0078465
1776 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1776 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1777 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1777 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1778 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1778 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1779 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1779 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1780 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1780 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1781 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1781 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1782 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1782 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1783 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1783 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1784 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1784 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1785 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1785 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1786 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1786 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1787 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1787 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1788 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1788 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1789 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1789 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1790 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1790 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1791 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1791 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1792 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1792 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1793 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1793 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1794 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1794 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1795 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1795 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1796 parents:
1796 parents:
1797 parents: -1:000000000000
1797 parents: -1:000000000000
1798 parents: 5:13207e5a10d9 4:bbe44766e73d
1798 parents: 5:13207e5a10d9 4:bbe44766e73d
1799 parents: 3:10e46f2dcbf4
1799 parents: 3:10e46f2dcbf4
1800 parents:
1800 parents:
1801 parents:
1801 parents:
1802 parents:
1802 parents:
1803 parents:
1803 parents:
1804 parents:
1804 parents:
1805 parents--verbose:
1805 parents--verbose:
1806 parents--verbose: -1:000000000000
1806 parents--verbose: -1:000000000000
1807 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1807 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1808 parents--verbose: 3:10e46f2dcbf4
1808 parents--verbose: 3:10e46f2dcbf4
1809 parents--verbose:
1809 parents--verbose:
1810 parents--verbose:
1810 parents--verbose:
1811 parents--verbose:
1811 parents--verbose:
1812 parents--verbose:
1812 parents--verbose:
1813 parents--verbose:
1813 parents--verbose:
1814 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1814 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1815 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1815 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1816 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1816 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1817 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1817 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1818 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1818 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1819 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1819 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1820 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1820 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1821 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1821 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1822 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1822 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1823 rev: 8
1823 rev: 8
1824 rev: 7
1824 rev: 7
1825 rev: 6
1825 rev: 6
1826 rev: 5
1826 rev: 5
1827 rev: 4
1827 rev: 4
1828 rev: 3
1828 rev: 3
1829 rev: 2
1829 rev: 2
1830 rev: 1
1830 rev: 1
1831 rev: 0
1831 rev: 0
1832 rev--verbose: 8
1832 rev--verbose: 8
1833 rev--verbose: 7
1833 rev--verbose: 7
1834 rev--verbose: 6
1834 rev--verbose: 6
1835 rev--verbose: 5
1835 rev--verbose: 5
1836 rev--verbose: 4
1836 rev--verbose: 4
1837 rev--verbose: 3
1837 rev--verbose: 3
1838 rev--verbose: 2
1838 rev--verbose: 2
1839 rev--verbose: 1
1839 rev--verbose: 1
1840 rev--verbose: 0
1840 rev--verbose: 0
1841 rev--debug: 8
1841 rev--debug: 8
1842 rev--debug: 7
1842 rev--debug: 7
1843 rev--debug: 6
1843 rev--debug: 6
1844 rev--debug: 5
1844 rev--debug: 5
1845 rev--debug: 4
1845 rev--debug: 4
1846 rev--debug: 3
1846 rev--debug: 3
1847 rev--debug: 2
1847 rev--debug: 2
1848 rev--debug: 1
1848 rev--debug: 1
1849 rev--debug: 0
1849 rev--debug: 0
1850 tags: tip
1850 tags: tip
1851 tags:
1851 tags:
1852 tags:
1852 tags:
1853 tags:
1853 tags:
1854 tags:
1854 tags:
1855 tags:
1855 tags:
1856 tags:
1856 tags:
1857 tags:
1857 tags:
1858 tags:
1858 tags:
1859 tags--verbose: tip
1859 tags--verbose: tip
1860 tags--verbose:
1860 tags--verbose:
1861 tags--verbose:
1861 tags--verbose:
1862 tags--verbose:
1862 tags--verbose:
1863 tags--verbose:
1863 tags--verbose:
1864 tags--verbose:
1864 tags--verbose:
1865 tags--verbose:
1865 tags--verbose:
1866 tags--verbose:
1866 tags--verbose:
1867 tags--verbose:
1867 tags--verbose:
1868 tags--debug: tip
1868 tags--debug: tip
1869 tags--debug:
1869 tags--debug:
1870 tags--debug:
1870 tags--debug:
1871 tags--debug:
1871 tags--debug:
1872 tags--debug:
1872 tags--debug:
1873 tags--debug:
1873 tags--debug:
1874 tags--debug:
1874 tags--debug:
1875 tags--debug:
1875 tags--debug:
1876 tags--debug:
1876 tags--debug:
1877 diffstat: 3: +2/-1
1877 diffstat: 3: +2/-1
1878 diffstat: 1: +1/-0
1878 diffstat: 1: +1/-0
1879 diffstat: 0: +0/-0
1879 diffstat: 0: +0/-0
1880 diffstat: 1: +1/-0
1880 diffstat: 1: +1/-0
1881 diffstat: 0: +0/-0
1881 diffstat: 0: +0/-0
1882 diffstat: 1: +1/-0
1882 diffstat: 1: +1/-0
1883 diffstat: 1: +4/-0
1883 diffstat: 1: +4/-0
1884 diffstat: 1: +2/-0
1884 diffstat: 1: +2/-0
1885 diffstat: 1: +1/-0
1885 diffstat: 1: +1/-0
1886 diffstat--verbose: 3: +2/-1
1886 diffstat--verbose: 3: +2/-1
1887 diffstat--verbose: 1: +1/-0
1887 diffstat--verbose: 1: +1/-0
1888 diffstat--verbose: 0: +0/-0
1888 diffstat--verbose: 0: +0/-0
1889 diffstat--verbose: 1: +1/-0
1889 diffstat--verbose: 1: +1/-0
1890 diffstat--verbose: 0: +0/-0
1890 diffstat--verbose: 0: +0/-0
1891 diffstat--verbose: 1: +1/-0
1891 diffstat--verbose: 1: +1/-0
1892 diffstat--verbose: 1: +4/-0
1892 diffstat--verbose: 1: +4/-0
1893 diffstat--verbose: 1: +2/-0
1893 diffstat--verbose: 1: +2/-0
1894 diffstat--verbose: 1: +1/-0
1894 diffstat--verbose: 1: +1/-0
1895 diffstat--debug: 3: +2/-1
1895 diffstat--debug: 3: +2/-1
1896 diffstat--debug: 1: +1/-0
1896 diffstat--debug: 1: +1/-0
1897 diffstat--debug: 0: +0/-0
1897 diffstat--debug: 0: +0/-0
1898 diffstat--debug: 1: +1/-0
1898 diffstat--debug: 1: +1/-0
1899 diffstat--debug: 0: +0/-0
1899 diffstat--debug: 0: +0/-0
1900 diffstat--debug: 1: +1/-0
1900 diffstat--debug: 1: +1/-0
1901 diffstat--debug: 1: +4/-0
1901 diffstat--debug: 1: +4/-0
1902 diffstat--debug: 1: +2/-0
1902 diffstat--debug: 1: +2/-0
1903 diffstat--debug: 1: +1/-0
1903 diffstat--debug: 1: +1/-0
1904 extras: branch=default
1904 extras: branch=default
1905 extras: branch=default
1905 extras: branch=default
1906 extras: branch=default
1906 extras: branch=default
1907 extras: branch=default
1907 extras: branch=default
1908 extras: branch=foo
1908 extras: branch=foo
1909 extras: branch=default
1909 extras: branch=default
1910 extras: branch=default
1910 extras: branch=default
1911 extras: branch=default
1911 extras: branch=default
1912 extras: branch=default
1912 extras: branch=default
1913 extras--verbose: branch=default
1913 extras--verbose: branch=default
1914 extras--verbose: branch=default
1914 extras--verbose: branch=default
1915 extras--verbose: branch=default
1915 extras--verbose: branch=default
1916 extras--verbose: branch=default
1916 extras--verbose: branch=default
1917 extras--verbose: branch=foo
1917 extras--verbose: branch=foo
1918 extras--verbose: branch=default
1918 extras--verbose: branch=default
1919 extras--verbose: branch=default
1919 extras--verbose: branch=default
1920 extras--verbose: branch=default
1920 extras--verbose: branch=default
1921 extras--verbose: branch=default
1921 extras--verbose: branch=default
1922 extras--debug: branch=default
1922 extras--debug: branch=default
1923 extras--debug: branch=default
1923 extras--debug: branch=default
1924 extras--debug: branch=default
1924 extras--debug: branch=default
1925 extras--debug: branch=default
1925 extras--debug: branch=default
1926 extras--debug: branch=foo
1926 extras--debug: branch=foo
1927 extras--debug: branch=default
1927 extras--debug: branch=default
1928 extras--debug: branch=default
1928 extras--debug: branch=default
1929 extras--debug: branch=default
1929 extras--debug: branch=default
1930 extras--debug: branch=default
1930 extras--debug: branch=default
1931 p1rev: 7
1931 p1rev: 7
1932 p1rev: -1
1932 p1rev: -1
1933 p1rev: 5
1933 p1rev: 5
1934 p1rev: 3
1934 p1rev: 3
1935 p1rev: 3
1935 p1rev: 3
1936 p1rev: 2
1936 p1rev: 2
1937 p1rev: 1
1937 p1rev: 1
1938 p1rev: 0
1938 p1rev: 0
1939 p1rev: -1
1939 p1rev: -1
1940 p1rev--verbose: 7
1940 p1rev--verbose: 7
1941 p1rev--verbose: -1
1941 p1rev--verbose: -1
1942 p1rev--verbose: 5
1942 p1rev--verbose: 5
1943 p1rev--verbose: 3
1943 p1rev--verbose: 3
1944 p1rev--verbose: 3
1944 p1rev--verbose: 3
1945 p1rev--verbose: 2
1945 p1rev--verbose: 2
1946 p1rev--verbose: 1
1946 p1rev--verbose: 1
1947 p1rev--verbose: 0
1947 p1rev--verbose: 0
1948 p1rev--verbose: -1
1948 p1rev--verbose: -1
1949 p1rev--debug: 7
1949 p1rev--debug: 7
1950 p1rev--debug: -1
1950 p1rev--debug: -1
1951 p1rev--debug: 5
1951 p1rev--debug: 5
1952 p1rev--debug: 3
1952 p1rev--debug: 3
1953 p1rev--debug: 3
1953 p1rev--debug: 3
1954 p1rev--debug: 2
1954 p1rev--debug: 2
1955 p1rev--debug: 1
1955 p1rev--debug: 1
1956 p1rev--debug: 0
1956 p1rev--debug: 0
1957 p1rev--debug: -1
1957 p1rev--debug: -1
1958 p2rev: -1
1958 p2rev: -1
1959 p2rev: -1
1959 p2rev: -1
1960 p2rev: 4
1960 p2rev: 4
1961 p2rev: -1
1961 p2rev: -1
1962 p2rev: -1
1962 p2rev: -1
1963 p2rev: -1
1963 p2rev: -1
1964 p2rev: -1
1964 p2rev: -1
1965 p2rev: -1
1965 p2rev: -1
1966 p2rev: -1
1966 p2rev: -1
1967 p2rev--verbose: -1
1967 p2rev--verbose: -1
1968 p2rev--verbose: -1
1968 p2rev--verbose: -1
1969 p2rev--verbose: 4
1969 p2rev--verbose: 4
1970 p2rev--verbose: -1
1970 p2rev--verbose: -1
1971 p2rev--verbose: -1
1971 p2rev--verbose: -1
1972 p2rev--verbose: -1
1972 p2rev--verbose: -1
1973 p2rev--verbose: -1
1973 p2rev--verbose: -1
1974 p2rev--verbose: -1
1974 p2rev--verbose: -1
1975 p2rev--verbose: -1
1975 p2rev--verbose: -1
1976 p2rev--debug: -1
1976 p2rev--debug: -1
1977 p2rev--debug: -1
1977 p2rev--debug: -1
1978 p2rev--debug: 4
1978 p2rev--debug: 4
1979 p2rev--debug: -1
1979 p2rev--debug: -1
1980 p2rev--debug: -1
1980 p2rev--debug: -1
1981 p2rev--debug: -1
1981 p2rev--debug: -1
1982 p2rev--debug: -1
1982 p2rev--debug: -1
1983 p2rev--debug: -1
1983 p2rev--debug: -1
1984 p2rev--debug: -1
1984 p2rev--debug: -1
1985 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1985 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1986 p1node: 0000000000000000000000000000000000000000
1986 p1node: 0000000000000000000000000000000000000000
1987 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1987 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1988 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1988 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1989 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1989 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1990 p1node: 97054abb4ab824450e9164180baf491ae0078465
1990 p1node: 97054abb4ab824450e9164180baf491ae0078465
1991 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1991 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1992 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1992 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1993 p1node: 0000000000000000000000000000000000000000
1993 p1node: 0000000000000000000000000000000000000000
1994 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1994 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1995 p1node--verbose: 0000000000000000000000000000000000000000
1995 p1node--verbose: 0000000000000000000000000000000000000000
1996 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1996 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1997 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1997 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1998 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1998 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1999 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1999 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2000 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2000 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2001 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2001 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2002 p1node--verbose: 0000000000000000000000000000000000000000
2002 p1node--verbose: 0000000000000000000000000000000000000000
2003 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2003 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2004 p1node--debug: 0000000000000000000000000000000000000000
2004 p1node--debug: 0000000000000000000000000000000000000000
2005 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2005 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2006 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2006 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2007 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2007 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2008 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2008 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2009 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2009 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2010 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2010 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2011 p1node--debug: 0000000000000000000000000000000000000000
2011 p1node--debug: 0000000000000000000000000000000000000000
2012 p2node: 0000000000000000000000000000000000000000
2012 p2node: 0000000000000000000000000000000000000000
2013 p2node: 0000000000000000000000000000000000000000
2013 p2node: 0000000000000000000000000000000000000000
2014 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2014 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2015 p2node: 0000000000000000000000000000000000000000
2015 p2node: 0000000000000000000000000000000000000000
2016 p2node: 0000000000000000000000000000000000000000
2016 p2node: 0000000000000000000000000000000000000000
2017 p2node: 0000000000000000000000000000000000000000
2017 p2node: 0000000000000000000000000000000000000000
2018 p2node: 0000000000000000000000000000000000000000
2018 p2node: 0000000000000000000000000000000000000000
2019 p2node: 0000000000000000000000000000000000000000
2019 p2node: 0000000000000000000000000000000000000000
2020 p2node: 0000000000000000000000000000000000000000
2020 p2node: 0000000000000000000000000000000000000000
2021 p2node--verbose: 0000000000000000000000000000000000000000
2021 p2node--verbose: 0000000000000000000000000000000000000000
2022 p2node--verbose: 0000000000000000000000000000000000000000
2022 p2node--verbose: 0000000000000000000000000000000000000000
2023 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2023 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2024 p2node--verbose: 0000000000000000000000000000000000000000
2024 p2node--verbose: 0000000000000000000000000000000000000000
2025 p2node--verbose: 0000000000000000000000000000000000000000
2025 p2node--verbose: 0000000000000000000000000000000000000000
2026 p2node--verbose: 0000000000000000000000000000000000000000
2026 p2node--verbose: 0000000000000000000000000000000000000000
2027 p2node--verbose: 0000000000000000000000000000000000000000
2027 p2node--verbose: 0000000000000000000000000000000000000000
2028 p2node--verbose: 0000000000000000000000000000000000000000
2028 p2node--verbose: 0000000000000000000000000000000000000000
2029 p2node--verbose: 0000000000000000000000000000000000000000
2029 p2node--verbose: 0000000000000000000000000000000000000000
2030 p2node--debug: 0000000000000000000000000000000000000000
2030 p2node--debug: 0000000000000000000000000000000000000000
2031 p2node--debug: 0000000000000000000000000000000000000000
2031 p2node--debug: 0000000000000000000000000000000000000000
2032 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2032 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2033 p2node--debug: 0000000000000000000000000000000000000000
2033 p2node--debug: 0000000000000000000000000000000000000000
2034 p2node--debug: 0000000000000000000000000000000000000000
2034 p2node--debug: 0000000000000000000000000000000000000000
2035 p2node--debug: 0000000000000000000000000000000000000000
2035 p2node--debug: 0000000000000000000000000000000000000000
2036 p2node--debug: 0000000000000000000000000000000000000000
2036 p2node--debug: 0000000000000000000000000000000000000000
2037 p2node--debug: 0000000000000000000000000000000000000000
2037 p2node--debug: 0000000000000000000000000000000000000000
2038 p2node--debug: 0000000000000000000000000000000000000000
2038 p2node--debug: 0000000000000000000000000000000000000000
2039
2039
2040 Filters work:
2040 Filters work:
2041
2041
2042 $ hg log --template '{author|domain}\n'
2042 $ hg log --template '{author|domain}\n'
2043
2043
2044 hostname
2044 hostname
2045
2045
2046
2046
2047
2047
2048
2048
2049 place
2049 place
2050 place
2050 place
2051 hostname
2051 hostname
2052
2052
2053 $ hg log --template '{author|person}\n'
2053 $ hg log --template '{author|person}\n'
2054 test
2054 test
2055 User Name
2055 User Name
2056 person
2056 person
2057 person
2057 person
2058 person
2058 person
2059 person
2059 person
2060 other
2060 other
2061 A. N. Other
2061 A. N. Other
2062 User Name
2062 User Name
2063
2063
2064 $ hg log --template '{author|user}\n'
2064 $ hg log --template '{author|user}\n'
2065 test
2065 test
2066 user
2066 user
2067 person
2067 person
2068 person
2068 person
2069 person
2069 person
2070 person
2070 person
2071 other
2071 other
2072 other
2072 other
2073 user
2073 user
2074
2074
2075 $ hg log --template '{date|date}\n'
2075 $ hg log --template '{date|date}\n'
2076 Wed Jan 01 10:01:00 2020 +0000
2076 Wed Jan 01 10:01:00 2020 +0000
2077 Mon Jan 12 13:46:40 1970 +0000
2077 Mon Jan 12 13:46:40 1970 +0000
2078 Sun Jan 18 08:40:01 1970 +0000
2078 Sun Jan 18 08:40:01 1970 +0000
2079 Sun Jan 18 08:40:00 1970 +0000
2079 Sun Jan 18 08:40:00 1970 +0000
2080 Sat Jan 17 04:53:20 1970 +0000
2080 Sat Jan 17 04:53:20 1970 +0000
2081 Fri Jan 16 01:06:40 1970 +0000
2081 Fri Jan 16 01:06:40 1970 +0000
2082 Wed Jan 14 21:20:00 1970 +0000
2082 Wed Jan 14 21:20:00 1970 +0000
2083 Tue Jan 13 17:33:20 1970 +0000
2083 Tue Jan 13 17:33:20 1970 +0000
2084 Mon Jan 12 13:46:40 1970 +0000
2084 Mon Jan 12 13:46:40 1970 +0000
2085
2085
2086 $ hg log --template '{date|isodate}\n'
2086 $ hg log --template '{date|isodate}\n'
2087 2020-01-01 10:01 +0000
2087 2020-01-01 10:01 +0000
2088 1970-01-12 13:46 +0000
2088 1970-01-12 13:46 +0000
2089 1970-01-18 08:40 +0000
2089 1970-01-18 08:40 +0000
2090 1970-01-18 08:40 +0000
2090 1970-01-18 08:40 +0000
2091 1970-01-17 04:53 +0000
2091 1970-01-17 04:53 +0000
2092 1970-01-16 01:06 +0000
2092 1970-01-16 01:06 +0000
2093 1970-01-14 21:20 +0000
2093 1970-01-14 21:20 +0000
2094 1970-01-13 17:33 +0000
2094 1970-01-13 17:33 +0000
2095 1970-01-12 13:46 +0000
2095 1970-01-12 13:46 +0000
2096
2096
2097 $ hg log --template '{date|isodatesec}\n'
2097 $ hg log --template '{date|isodatesec}\n'
2098 2020-01-01 10:01:00 +0000
2098 2020-01-01 10:01:00 +0000
2099 1970-01-12 13:46:40 +0000
2099 1970-01-12 13:46:40 +0000
2100 1970-01-18 08:40:01 +0000
2100 1970-01-18 08:40:01 +0000
2101 1970-01-18 08:40:00 +0000
2101 1970-01-18 08:40:00 +0000
2102 1970-01-17 04:53:20 +0000
2102 1970-01-17 04:53:20 +0000
2103 1970-01-16 01:06:40 +0000
2103 1970-01-16 01:06:40 +0000
2104 1970-01-14 21:20:00 +0000
2104 1970-01-14 21:20:00 +0000
2105 1970-01-13 17:33:20 +0000
2105 1970-01-13 17:33:20 +0000
2106 1970-01-12 13:46:40 +0000
2106 1970-01-12 13:46:40 +0000
2107
2107
2108 $ hg log --template '{date|rfc822date}\n'
2108 $ hg log --template '{date|rfc822date}\n'
2109 Wed, 01 Jan 2020 10:01:00 +0000
2109 Wed, 01 Jan 2020 10:01:00 +0000
2110 Mon, 12 Jan 1970 13:46:40 +0000
2110 Mon, 12 Jan 1970 13:46:40 +0000
2111 Sun, 18 Jan 1970 08:40:01 +0000
2111 Sun, 18 Jan 1970 08:40:01 +0000
2112 Sun, 18 Jan 1970 08:40:00 +0000
2112 Sun, 18 Jan 1970 08:40:00 +0000
2113 Sat, 17 Jan 1970 04:53:20 +0000
2113 Sat, 17 Jan 1970 04:53:20 +0000
2114 Fri, 16 Jan 1970 01:06:40 +0000
2114 Fri, 16 Jan 1970 01:06:40 +0000
2115 Wed, 14 Jan 1970 21:20:00 +0000
2115 Wed, 14 Jan 1970 21:20:00 +0000
2116 Tue, 13 Jan 1970 17:33:20 +0000
2116 Tue, 13 Jan 1970 17:33:20 +0000
2117 Mon, 12 Jan 1970 13:46:40 +0000
2117 Mon, 12 Jan 1970 13:46:40 +0000
2118
2118
2119 $ hg log --template '{desc|firstline}\n'
2119 $ hg log --template '{desc|firstline}\n'
2120 third
2120 third
2121 second
2121 second
2122 merge
2122 merge
2123 new head
2123 new head
2124 new branch
2124 new branch
2125 no user, no domain
2125 no user, no domain
2126 no person
2126 no person
2127 other 1
2127 other 1
2128 line 1
2128 line 1
2129
2129
2130 $ hg log --template '{node|short}\n'
2130 $ hg log --template '{node|short}\n'
2131 95c24699272e
2131 95c24699272e
2132 29114dbae42b
2132 29114dbae42b
2133 d41e714fe50d
2133 d41e714fe50d
2134 13207e5a10d9
2134 13207e5a10d9
2135 bbe44766e73d
2135 bbe44766e73d
2136 10e46f2dcbf4
2136 10e46f2dcbf4
2137 97054abb4ab8
2137 97054abb4ab8
2138 b608e9d1a3f0
2138 b608e9d1a3f0
2139 1e4e1b8f71e0
2139 1e4e1b8f71e0
2140
2140
2141 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2141 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2142 <changeset author="test"/>
2142 <changeset author="test"/>
2143 <changeset author="User Name &lt;user@hostname&gt;"/>
2143 <changeset author="User Name &lt;user@hostname&gt;"/>
2144 <changeset author="person"/>
2144 <changeset author="person"/>
2145 <changeset author="person"/>
2145 <changeset author="person"/>
2146 <changeset author="person"/>
2146 <changeset author="person"/>
2147 <changeset author="person"/>
2147 <changeset author="person"/>
2148 <changeset author="other@place"/>
2148 <changeset author="other@place"/>
2149 <changeset author="A. N. Other &lt;other@place&gt;"/>
2149 <changeset author="A. N. Other &lt;other@place&gt;"/>
2150 <changeset author="User Name &lt;user@hostname&gt;"/>
2150 <changeset author="User Name &lt;user@hostname&gt;"/>
2151
2151
2152 $ hg log --template '{rev}: {children}\n'
2152 $ hg log --template '{rev}: {children}\n'
2153 8:
2153 8:
2154 7: 8:95c24699272e
2154 7: 8:95c24699272e
2155 6:
2155 6:
2156 5: 6:d41e714fe50d
2156 5: 6:d41e714fe50d
2157 4: 6:d41e714fe50d
2157 4: 6:d41e714fe50d
2158 3: 4:bbe44766e73d 5:13207e5a10d9
2158 3: 4:bbe44766e73d 5:13207e5a10d9
2159 2: 3:10e46f2dcbf4
2159 2: 3:10e46f2dcbf4
2160 1: 2:97054abb4ab8
2160 1: 2:97054abb4ab8
2161 0: 1:b608e9d1a3f0
2161 0: 1:b608e9d1a3f0
2162
2162
2163 Formatnode filter works:
2163 Formatnode filter works:
2164
2164
2165 $ hg -q log -r 0 --template '{node|formatnode}\n'
2165 $ hg -q log -r 0 --template '{node|formatnode}\n'
2166 1e4e1b8f71e0
2166 1e4e1b8f71e0
2167
2167
2168 $ hg log -r 0 --template '{node|formatnode}\n'
2168 $ hg log -r 0 --template '{node|formatnode}\n'
2169 1e4e1b8f71e0
2169 1e4e1b8f71e0
2170
2170
2171 $ hg -v log -r 0 --template '{node|formatnode}\n'
2171 $ hg -v log -r 0 --template '{node|formatnode}\n'
2172 1e4e1b8f71e0
2172 1e4e1b8f71e0
2173
2173
2174 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2174 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2175 1e4e1b8f71e05681d422154f5421e385fec3454f
2175 1e4e1b8f71e05681d422154f5421e385fec3454f
2176
2176
2177 Age filter:
2177 Age filter:
2178
2178
2179 $ hg init unstable-hash
2179 $ hg init unstable-hash
2180 $ cd unstable-hash
2180 $ cd unstable-hash
2181 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2181 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2182
2182
2183 >>> from __future__ import absolute_import
2183 >>> from __future__ import absolute_import
2184 >>> import datetime
2184 >>> import datetime
2185 >>> fp = open('a', 'w')
2185 >>> fp = open('a', 'w')
2186 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2186 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2187 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2187 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2188 >>> fp.close()
2188 >>> fp.close()
2189 $ hg add a
2189 $ hg add a
2190 $ hg commit -m future -d "`cat a`"
2190 $ hg commit -m future -d "`cat a`"
2191
2191
2192 $ hg log -l1 --template '{date|age}\n'
2192 $ hg log -l1 --template '{date|age}\n'
2193 7 years from now
2193 7 years from now
2194
2194
2195 $ cd ..
2195 $ cd ..
2196 $ rm -rf unstable-hash
2196 $ rm -rf unstable-hash
2197
2197
2198 Add a dummy commit to make up for the instability of the above:
2198 Add a dummy commit to make up for the instability of the above:
2199
2199
2200 $ echo a > a
2200 $ echo a > a
2201 $ hg add a
2201 $ hg add a
2202 $ hg ci -m future
2202 $ hg ci -m future
2203
2203
2204 Count filter:
2204 Count filter:
2205
2205
2206 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2206 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2207 40 12
2207 40 12
2208
2208
2209 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2209 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2210 0 1 4
2210 0 1 4
2211
2211
2212 $ hg log -G --template '{rev}: children: {children|count}, \
2212 $ hg log -G --template '{rev}: children: {children|count}, \
2213 > tags: {tags|count}, file_adds: {file_adds|count}, \
2213 > tags: {tags|count}, file_adds: {file_adds|count}, \
2214 > ancestors: {revset("ancestors(%s)", rev)|count}'
2214 > ancestors: {revset("ancestors(%s)", rev)|count}'
2215 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2215 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2216 |
2216 |
2217 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2217 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2218 |
2218 |
2219 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2219 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2220
2220
2221 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2221 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2222 |\
2222 |\
2223 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2223 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2224 | |
2224 | |
2225 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2225 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2226 |/
2226 |/
2227 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2227 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2228 |
2228 |
2229 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2229 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2230 |
2230 |
2231 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2231 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2232 |
2232 |
2233 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2233 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2234
2234
2235
2235
2236 Upper/lower filters:
2236 Upper/lower filters:
2237
2237
2238 $ hg log -r0 --template '{branch|upper}\n'
2238 $ hg log -r0 --template '{branch|upper}\n'
2239 DEFAULT
2239 DEFAULT
2240 $ hg log -r0 --template '{author|lower}\n'
2240 $ hg log -r0 --template '{author|lower}\n'
2241 user name <user@hostname>
2241 user name <user@hostname>
2242 $ hg log -r0 --template '{date|upper}\n'
2242 $ hg log -r0 --template '{date|upper}\n'
2243 abort: template filter 'upper' is not compatible with keyword 'date'
2243 abort: template filter 'upper' is not compatible with keyword 'date'
2244 [255]
2244 [255]
2245
2245
2246 Add a commit that does all possible modifications at once
2246 Add a commit that does all possible modifications at once
2247
2247
2248 $ echo modify >> third
2248 $ echo modify >> third
2249 $ touch b
2249 $ touch b
2250 $ hg add b
2250 $ hg add b
2251 $ hg mv fourth fifth
2251 $ hg mv fourth fifth
2252 $ hg rm a
2252 $ hg rm a
2253 $ hg ci -m "Modify, add, remove, rename"
2253 $ hg ci -m "Modify, add, remove, rename"
2254
2254
2255 Check the status template
2255 Check the status template
2256
2256
2257 $ cat <<EOF >> $HGRCPATH
2257 $ cat <<EOF >> $HGRCPATH
2258 > [extensions]
2258 > [extensions]
2259 > color=
2259 > color=
2260 > EOF
2260 > EOF
2261
2261
2262 $ hg log -T status -r 10
2262 $ hg log -T status -r 10
2263 changeset: 10:0f9759ec227a
2263 changeset: 10:0f9759ec227a
2264 tag: tip
2264 tag: tip
2265 user: test
2265 user: test
2266 date: Thu Jan 01 00:00:00 1970 +0000
2266 date: Thu Jan 01 00:00:00 1970 +0000
2267 summary: Modify, add, remove, rename
2267 summary: Modify, add, remove, rename
2268 files:
2268 files:
2269 M third
2269 M third
2270 A b
2270 A b
2271 A fifth
2271 A fifth
2272 R a
2272 R a
2273 R fourth
2273 R fourth
2274
2274
2275 $ hg log -T status -C -r 10
2275 $ hg log -T status -C -r 10
2276 changeset: 10:0f9759ec227a
2276 changeset: 10:0f9759ec227a
2277 tag: tip
2277 tag: tip
2278 user: test
2278 user: test
2279 date: Thu Jan 01 00:00:00 1970 +0000
2279 date: Thu Jan 01 00:00:00 1970 +0000
2280 summary: Modify, add, remove, rename
2280 summary: Modify, add, remove, rename
2281 files:
2281 files:
2282 M third
2282 M third
2283 A b
2283 A b
2284 A fifth
2284 A fifth
2285 fourth
2285 fourth
2286 R a
2286 R a
2287 R fourth
2287 R fourth
2288
2288
2289 $ hg log -T status -C -r 10 -v
2289 $ hg log -T status -C -r 10 -v
2290 changeset: 10:0f9759ec227a
2290 changeset: 10:0f9759ec227a
2291 tag: tip
2291 tag: tip
2292 user: test
2292 user: test
2293 date: Thu Jan 01 00:00:00 1970 +0000
2293 date: Thu Jan 01 00:00:00 1970 +0000
2294 description:
2294 description:
2295 Modify, add, remove, rename
2295 Modify, add, remove, rename
2296
2296
2297 files:
2297 files:
2298 M third
2298 M third
2299 A b
2299 A b
2300 A fifth
2300 A fifth
2301 fourth
2301 fourth
2302 R a
2302 R a
2303 R fourth
2303 R fourth
2304
2304
2305 $ hg log -T status -C -r 10 --debug
2305 $ hg log -T status -C -r 10 --debug
2306 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2306 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2307 tag: tip
2307 tag: tip
2308 phase: secret
2308 phase: secret
2309 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2309 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2310 parent: -1:0000000000000000000000000000000000000000
2310 parent: -1:0000000000000000000000000000000000000000
2311 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2311 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2312 user: test
2312 user: test
2313 date: Thu Jan 01 00:00:00 1970 +0000
2313 date: Thu Jan 01 00:00:00 1970 +0000
2314 extra: branch=default
2314 extra: branch=default
2315 description:
2315 description:
2316 Modify, add, remove, rename
2316 Modify, add, remove, rename
2317
2317
2318 files:
2318 files:
2319 M third
2319 M third
2320 A b
2320 A b
2321 A fifth
2321 A fifth
2322 fourth
2322 fourth
2323 R a
2323 R a
2324 R fourth
2324 R fourth
2325
2325
2326 $ hg log -T status -C -r 10 --quiet
2326 $ hg log -T status -C -r 10 --quiet
2327 10:0f9759ec227a
2327 10:0f9759ec227a
2328 $ hg --color=debug log -T status -r 10
2328 $ hg --color=debug log -T status -r 10
2329 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2329 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2330 [log.tag|tag: tip]
2330 [log.tag|tag: tip]
2331 [log.user|user: test]
2331 [log.user|user: test]
2332 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2332 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2333 [log.summary|summary: Modify, add, remove, rename]
2333 [log.summary|summary: Modify, add, remove, rename]
2334 [ui.note log.files|files:]
2334 [ui.note log.files|files:]
2335 [status.modified|M third]
2335 [status.modified|M third]
2336 [status.added|A b]
2336 [status.added|A b]
2337 [status.added|A fifth]
2337 [status.added|A fifth]
2338 [status.removed|R a]
2338 [status.removed|R a]
2339 [status.removed|R fourth]
2339 [status.removed|R fourth]
2340
2340
2341 $ hg --color=debug log -T status -C -r 10
2341 $ hg --color=debug log -T status -C -r 10
2342 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2342 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2343 [log.tag|tag: tip]
2343 [log.tag|tag: tip]
2344 [log.user|user: test]
2344 [log.user|user: test]
2345 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2345 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2346 [log.summary|summary: Modify, add, remove, rename]
2346 [log.summary|summary: Modify, add, remove, rename]
2347 [ui.note log.files|files:]
2347 [ui.note log.files|files:]
2348 [status.modified|M third]
2348 [status.modified|M third]
2349 [status.added|A b]
2349 [status.added|A b]
2350 [status.added|A fifth]
2350 [status.added|A fifth]
2351 [status.copied| fourth]
2351 [status.copied| fourth]
2352 [status.removed|R a]
2352 [status.removed|R a]
2353 [status.removed|R fourth]
2353 [status.removed|R fourth]
2354
2354
2355 $ hg --color=debug log -T status -C -r 10 -v
2355 $ hg --color=debug log -T status -C -r 10 -v
2356 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2356 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2357 [log.tag|tag: tip]
2357 [log.tag|tag: tip]
2358 [log.user|user: test]
2358 [log.user|user: test]
2359 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2359 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2360 [ui.note log.description|description:]
2360 [ui.note log.description|description:]
2361 [ui.note log.description|Modify, add, remove, rename]
2361 [ui.note log.description|Modify, add, remove, rename]
2362
2362
2363 [ui.note log.files|files:]
2363 [ui.note log.files|files:]
2364 [status.modified|M third]
2364 [status.modified|M third]
2365 [status.added|A b]
2365 [status.added|A b]
2366 [status.added|A fifth]
2366 [status.added|A fifth]
2367 [status.copied| fourth]
2367 [status.copied| fourth]
2368 [status.removed|R a]
2368 [status.removed|R a]
2369 [status.removed|R fourth]
2369 [status.removed|R fourth]
2370
2370
2371 $ hg --color=debug log -T status -C -r 10 --debug
2371 $ hg --color=debug log -T status -C -r 10 --debug
2372 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2372 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2373 [log.tag|tag: tip]
2373 [log.tag|tag: tip]
2374 [log.phase|phase: secret]
2374 [log.phase|phase: secret]
2375 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2375 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2376 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2376 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2377 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2377 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2378 [log.user|user: test]
2378 [log.user|user: test]
2379 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2379 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2380 [ui.debug log.extra|extra: branch=default]
2380 [ui.debug log.extra|extra: branch=default]
2381 [ui.note log.description|description:]
2381 [ui.note log.description|description:]
2382 [ui.note log.description|Modify, add, remove, rename]
2382 [ui.note log.description|Modify, add, remove, rename]
2383
2383
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.copied| fourth]
2388 [status.copied| fourth]
2389 [status.removed|R a]
2389 [status.removed|R a]
2390 [status.removed|R fourth]
2390 [status.removed|R fourth]
2391
2391
2392 $ hg --color=debug log -T status -C -r 10 --quiet
2392 $ hg --color=debug log -T status -C -r 10 --quiet
2393 [log.node|10:0f9759ec227a]
2393 [log.node|10:0f9759ec227a]
2394
2394
2395 Check the bisect template
2395 Check the bisect template
2396
2396
2397 $ hg bisect -g 1
2397 $ hg bisect -g 1
2398 $ hg bisect -b 3 --noupdate
2398 $ hg bisect -b 3 --noupdate
2399 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2399 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2400 $ hg log -T bisect -r 0:4
2400 $ hg log -T bisect -r 0:4
2401 changeset: 0:1e4e1b8f71e0
2401 changeset: 0:1e4e1b8f71e0
2402 bisect: good (implicit)
2402 bisect: good (implicit)
2403 user: User Name <user@hostname>
2403 user: User Name <user@hostname>
2404 date: Mon Jan 12 13:46:40 1970 +0000
2404 date: Mon Jan 12 13:46:40 1970 +0000
2405 summary: line 1
2405 summary: line 1
2406
2406
2407 changeset: 1:b608e9d1a3f0
2407 changeset: 1:b608e9d1a3f0
2408 bisect: good
2408 bisect: good
2409 user: A. N. Other <other@place>
2409 user: A. N. Other <other@place>
2410 date: Tue Jan 13 17:33:20 1970 +0000
2410 date: Tue Jan 13 17:33:20 1970 +0000
2411 summary: other 1
2411 summary: other 1
2412
2412
2413 changeset: 2:97054abb4ab8
2413 changeset: 2:97054abb4ab8
2414 bisect: untested
2414 bisect: untested
2415 user: other@place
2415 user: other@place
2416 date: Wed Jan 14 21:20:00 1970 +0000
2416 date: Wed Jan 14 21:20:00 1970 +0000
2417 summary: no person
2417 summary: no person
2418
2418
2419 changeset: 3:10e46f2dcbf4
2419 changeset: 3:10e46f2dcbf4
2420 bisect: bad
2420 bisect: bad
2421 user: person
2421 user: person
2422 date: Fri Jan 16 01:06:40 1970 +0000
2422 date: Fri Jan 16 01:06:40 1970 +0000
2423 summary: no user, no domain
2423 summary: no user, no domain
2424
2424
2425 changeset: 4:bbe44766e73d
2425 changeset: 4:bbe44766e73d
2426 bisect: bad (implicit)
2426 bisect: bad (implicit)
2427 branch: foo
2427 branch: foo
2428 user: person
2428 user: person
2429 date: Sat Jan 17 04:53:20 1970 +0000
2429 date: Sat Jan 17 04:53:20 1970 +0000
2430 summary: new branch
2430 summary: new branch
2431
2431
2432 $ hg log --debug -T bisect -r 0:4
2432 $ hg log --debug -T bisect -r 0:4
2433 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2433 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2434 bisect: good (implicit)
2434 bisect: good (implicit)
2435 phase: public
2435 phase: public
2436 parent: -1:0000000000000000000000000000000000000000
2436 parent: -1:0000000000000000000000000000000000000000
2437 parent: -1:0000000000000000000000000000000000000000
2437 parent: -1:0000000000000000000000000000000000000000
2438 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2438 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2439 user: User Name <user@hostname>
2439 user: User Name <user@hostname>
2440 date: Mon Jan 12 13:46:40 1970 +0000
2440 date: Mon Jan 12 13:46:40 1970 +0000
2441 files+: a
2441 files+: a
2442 extra: branch=default
2442 extra: branch=default
2443 description:
2443 description:
2444 line 1
2444 line 1
2445 line 2
2445 line 2
2446
2446
2447
2447
2448 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2448 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2449 bisect: good
2449 bisect: good
2450 phase: public
2450 phase: public
2451 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2451 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2452 parent: -1:0000000000000000000000000000000000000000
2452 parent: -1:0000000000000000000000000000000000000000
2453 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2453 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2454 user: A. N. Other <other@place>
2454 user: A. N. Other <other@place>
2455 date: Tue Jan 13 17:33:20 1970 +0000
2455 date: Tue Jan 13 17:33:20 1970 +0000
2456 files+: b
2456 files+: b
2457 extra: branch=default
2457 extra: branch=default
2458 description:
2458 description:
2459 other 1
2459 other 1
2460 other 2
2460 other 2
2461
2461
2462 other 3
2462 other 3
2463
2463
2464
2464
2465 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2465 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2466 bisect: untested
2466 bisect: untested
2467 phase: public
2467 phase: public
2468 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2468 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2469 parent: -1:0000000000000000000000000000000000000000
2469 parent: -1:0000000000000000000000000000000000000000
2470 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2470 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2471 user: other@place
2471 user: other@place
2472 date: Wed Jan 14 21:20:00 1970 +0000
2472 date: Wed Jan 14 21:20:00 1970 +0000
2473 files+: c
2473 files+: c
2474 extra: branch=default
2474 extra: branch=default
2475 description:
2475 description:
2476 no person
2476 no person
2477
2477
2478
2478
2479 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2479 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2480 bisect: bad
2480 bisect: bad
2481 phase: public
2481 phase: public
2482 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2482 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2483 parent: -1:0000000000000000000000000000000000000000
2483 parent: -1:0000000000000000000000000000000000000000
2484 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2484 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2485 user: person
2485 user: person
2486 date: Fri Jan 16 01:06:40 1970 +0000
2486 date: Fri Jan 16 01:06:40 1970 +0000
2487 files: c
2487 files: c
2488 extra: branch=default
2488 extra: branch=default
2489 description:
2489 description:
2490 no user, no domain
2490 no user, no domain
2491
2491
2492
2492
2493 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2493 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2494 bisect: bad (implicit)
2494 bisect: bad (implicit)
2495 branch: foo
2495 branch: foo
2496 phase: draft
2496 phase: draft
2497 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2497 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2498 parent: -1:0000000000000000000000000000000000000000
2498 parent: -1:0000000000000000000000000000000000000000
2499 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2499 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2500 user: person
2500 user: person
2501 date: Sat Jan 17 04:53:20 1970 +0000
2501 date: Sat Jan 17 04:53:20 1970 +0000
2502 extra: branch=foo
2502 extra: branch=foo
2503 description:
2503 description:
2504 new branch
2504 new branch
2505
2505
2506
2506
2507 $ hg log -v -T bisect -r 0:4
2507 $ hg log -v -T bisect -r 0:4
2508 changeset: 0:1e4e1b8f71e0
2508 changeset: 0:1e4e1b8f71e0
2509 bisect: good (implicit)
2509 bisect: good (implicit)
2510 user: User Name <user@hostname>
2510 user: User Name <user@hostname>
2511 date: Mon Jan 12 13:46:40 1970 +0000
2511 date: Mon Jan 12 13:46:40 1970 +0000
2512 files: a
2512 files: a
2513 description:
2513 description:
2514 line 1
2514 line 1
2515 line 2
2515 line 2
2516
2516
2517
2517
2518 changeset: 1:b608e9d1a3f0
2518 changeset: 1:b608e9d1a3f0
2519 bisect: good
2519 bisect: good
2520 user: A. N. Other <other@place>
2520 user: A. N. Other <other@place>
2521 date: Tue Jan 13 17:33:20 1970 +0000
2521 date: Tue Jan 13 17:33:20 1970 +0000
2522 files: b
2522 files: b
2523 description:
2523 description:
2524 other 1
2524 other 1
2525 other 2
2525 other 2
2526
2526
2527 other 3
2527 other 3
2528
2528
2529
2529
2530 changeset: 2:97054abb4ab8
2530 changeset: 2:97054abb4ab8
2531 bisect: untested
2531 bisect: untested
2532 user: other@place
2532 user: other@place
2533 date: Wed Jan 14 21:20:00 1970 +0000
2533 date: Wed Jan 14 21:20:00 1970 +0000
2534 files: c
2534 files: c
2535 description:
2535 description:
2536 no person
2536 no person
2537
2537
2538
2538
2539 changeset: 3:10e46f2dcbf4
2539 changeset: 3:10e46f2dcbf4
2540 bisect: bad
2540 bisect: bad
2541 user: person
2541 user: person
2542 date: Fri Jan 16 01:06:40 1970 +0000
2542 date: Fri Jan 16 01:06:40 1970 +0000
2543 files: c
2543 files: c
2544 description:
2544 description:
2545 no user, no domain
2545 no user, no domain
2546
2546
2547
2547
2548 changeset: 4:bbe44766e73d
2548 changeset: 4:bbe44766e73d
2549 bisect: bad (implicit)
2549 bisect: bad (implicit)
2550 branch: foo
2550 branch: foo
2551 user: person
2551 user: person
2552 date: Sat Jan 17 04:53:20 1970 +0000
2552 date: Sat Jan 17 04:53:20 1970 +0000
2553 description:
2553 description:
2554 new branch
2554 new branch
2555
2555
2556
2556
2557 $ hg --color=debug log -T bisect -r 0:4
2557 $ hg --color=debug log -T bisect -r 0:4
2558 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2558 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2559 [log.bisect bisect.good|bisect: good (implicit)]
2559 [log.bisect bisect.good|bisect: good (implicit)]
2560 [log.user|user: User Name <user@hostname>]
2560 [log.user|user: User Name <user@hostname>]
2561 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2561 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2562 [log.summary|summary: line 1]
2562 [log.summary|summary: line 1]
2563
2563
2564 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2564 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2565 [log.bisect bisect.good|bisect: good]
2565 [log.bisect bisect.good|bisect: good]
2566 [log.user|user: A. N. Other <other@place>]
2566 [log.user|user: A. N. Other <other@place>]
2567 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2567 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2568 [log.summary|summary: other 1]
2568 [log.summary|summary: other 1]
2569
2569
2570 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2570 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2571 [log.bisect bisect.untested|bisect: untested]
2571 [log.bisect bisect.untested|bisect: untested]
2572 [log.user|user: other@place]
2572 [log.user|user: other@place]
2573 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2573 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2574 [log.summary|summary: no person]
2574 [log.summary|summary: no person]
2575
2575
2576 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2576 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2577 [log.bisect bisect.bad|bisect: bad]
2577 [log.bisect bisect.bad|bisect: bad]
2578 [log.user|user: person]
2578 [log.user|user: person]
2579 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2579 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2580 [log.summary|summary: no user, no domain]
2580 [log.summary|summary: no user, no domain]
2581
2581
2582 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2582 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2583 [log.bisect bisect.bad|bisect: bad (implicit)]
2583 [log.bisect bisect.bad|bisect: bad (implicit)]
2584 [log.branch|branch: foo]
2584 [log.branch|branch: foo]
2585 [log.user|user: person]
2585 [log.user|user: person]
2586 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2586 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2587 [log.summary|summary: new branch]
2587 [log.summary|summary: new branch]
2588
2588
2589 $ hg --color=debug log --debug -T bisect -r 0:4
2589 $ hg --color=debug log --debug -T bisect -r 0:4
2590 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2590 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2591 [log.bisect bisect.good|bisect: good (implicit)]
2591 [log.bisect bisect.good|bisect: good (implicit)]
2592 [log.phase|phase: public]
2592 [log.phase|phase: public]
2593 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2593 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2594 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2594 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2595 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2595 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2596 [log.user|user: User Name <user@hostname>]
2596 [log.user|user: User Name <user@hostname>]
2597 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2597 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2598 [ui.debug log.files|files+: a]
2598 [ui.debug log.files|files+: a]
2599 [ui.debug log.extra|extra: branch=default]
2599 [ui.debug log.extra|extra: branch=default]
2600 [ui.note log.description|description:]
2600 [ui.note log.description|description:]
2601 [ui.note log.description|line 1
2601 [ui.note log.description|line 1
2602 line 2]
2602 line 2]
2603
2603
2604
2604
2605 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2605 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2606 [log.bisect bisect.good|bisect: good]
2606 [log.bisect bisect.good|bisect: good]
2607 [log.phase|phase: public]
2607 [log.phase|phase: public]
2608 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2608 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2609 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2609 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2610 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2610 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2611 [log.user|user: A. N. Other <other@place>]
2611 [log.user|user: A. N. Other <other@place>]
2612 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2612 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2613 [ui.debug log.files|files+: b]
2613 [ui.debug log.files|files+: b]
2614 [ui.debug log.extra|extra: branch=default]
2614 [ui.debug log.extra|extra: branch=default]
2615 [ui.note log.description|description:]
2615 [ui.note log.description|description:]
2616 [ui.note log.description|other 1
2616 [ui.note log.description|other 1
2617 other 2
2617 other 2
2618
2618
2619 other 3]
2619 other 3]
2620
2620
2621
2621
2622 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2622 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2623 [log.bisect bisect.untested|bisect: untested]
2623 [log.bisect bisect.untested|bisect: untested]
2624 [log.phase|phase: public]
2624 [log.phase|phase: public]
2625 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2625 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2626 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2626 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2627 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2627 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2628 [log.user|user: other@place]
2628 [log.user|user: other@place]
2629 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2629 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2630 [ui.debug log.files|files+: c]
2630 [ui.debug log.files|files+: c]
2631 [ui.debug log.extra|extra: branch=default]
2631 [ui.debug log.extra|extra: branch=default]
2632 [ui.note log.description|description:]
2632 [ui.note log.description|description:]
2633 [ui.note log.description|no person]
2633 [ui.note log.description|no person]
2634
2634
2635
2635
2636 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2636 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2637 [log.bisect bisect.bad|bisect: bad]
2637 [log.bisect bisect.bad|bisect: bad]
2638 [log.phase|phase: public]
2638 [log.phase|phase: public]
2639 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2639 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2640 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2640 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2641 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2641 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2642 [log.user|user: person]
2642 [log.user|user: person]
2643 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2643 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2644 [ui.debug log.files|files: c]
2644 [ui.debug log.files|files: c]
2645 [ui.debug log.extra|extra: branch=default]
2645 [ui.debug log.extra|extra: branch=default]
2646 [ui.note log.description|description:]
2646 [ui.note log.description|description:]
2647 [ui.note log.description|no user, no domain]
2647 [ui.note log.description|no user, no domain]
2648
2648
2649
2649
2650 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2650 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2651 [log.bisect bisect.bad|bisect: bad (implicit)]
2651 [log.bisect bisect.bad|bisect: bad (implicit)]
2652 [log.branch|branch: foo]
2652 [log.branch|branch: foo]
2653 [log.phase|phase: draft]
2653 [log.phase|phase: draft]
2654 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2654 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2655 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2655 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2656 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2656 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2657 [log.user|user: person]
2657 [log.user|user: person]
2658 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2658 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2659 [ui.debug log.extra|extra: branch=foo]
2659 [ui.debug log.extra|extra: branch=foo]
2660 [ui.note log.description|description:]
2660 [ui.note log.description|description:]
2661 [ui.note log.description|new branch]
2661 [ui.note log.description|new branch]
2662
2662
2663
2663
2664 $ hg --color=debug log -v -T bisect -r 0:4
2664 $ hg --color=debug log -v -T bisect -r 0:4
2665 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2665 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2666 [log.bisect bisect.good|bisect: good (implicit)]
2666 [log.bisect bisect.good|bisect: good (implicit)]
2667 [log.user|user: User Name <user@hostname>]
2667 [log.user|user: User Name <user@hostname>]
2668 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2668 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2669 [ui.note log.files|files: a]
2669 [ui.note log.files|files: a]
2670 [ui.note log.description|description:]
2670 [ui.note log.description|description:]
2671 [ui.note log.description|line 1
2671 [ui.note log.description|line 1
2672 line 2]
2672 line 2]
2673
2673
2674
2674
2675 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2675 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2676 [log.bisect bisect.good|bisect: good]
2676 [log.bisect bisect.good|bisect: good]
2677 [log.user|user: A. N. Other <other@place>]
2677 [log.user|user: A. N. Other <other@place>]
2678 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2678 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2679 [ui.note log.files|files: b]
2679 [ui.note log.files|files: b]
2680 [ui.note log.description|description:]
2680 [ui.note log.description|description:]
2681 [ui.note log.description|other 1
2681 [ui.note log.description|other 1
2682 other 2
2682 other 2
2683
2683
2684 other 3]
2684 other 3]
2685
2685
2686
2686
2687 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2687 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2688 [log.bisect bisect.untested|bisect: untested]
2688 [log.bisect bisect.untested|bisect: untested]
2689 [log.user|user: other@place]
2689 [log.user|user: other@place]
2690 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2690 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2691 [ui.note log.files|files: c]
2691 [ui.note log.files|files: c]
2692 [ui.note log.description|description:]
2692 [ui.note log.description|description:]
2693 [ui.note log.description|no person]
2693 [ui.note log.description|no person]
2694
2694
2695
2695
2696 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2696 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2697 [log.bisect bisect.bad|bisect: bad]
2697 [log.bisect bisect.bad|bisect: bad]
2698 [log.user|user: person]
2698 [log.user|user: person]
2699 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2699 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2700 [ui.note log.files|files: c]
2700 [ui.note log.files|files: c]
2701 [ui.note log.description|description:]
2701 [ui.note log.description|description:]
2702 [ui.note log.description|no user, no domain]
2702 [ui.note log.description|no user, no domain]
2703
2703
2704
2704
2705 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2705 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2706 [log.bisect bisect.bad|bisect: bad (implicit)]
2706 [log.bisect bisect.bad|bisect: bad (implicit)]
2707 [log.branch|branch: foo]
2707 [log.branch|branch: foo]
2708 [log.user|user: person]
2708 [log.user|user: person]
2709 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2709 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
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 bisect --reset
2714 $ hg bisect --reset
2715
2715
2716 Error on syntax:
2716 Error on syntax:
2717
2717
2718 $ echo 'x = "f' >> t
2718 $ echo 'x = "f' >> t
2719 $ hg log
2719 $ hg log
2720 hg: parse error at t:3: unmatched quotes
2720 hg: parse error at t:3: unmatched quotes
2721 [255]
2721 [255]
2722
2722
2723 $ hg log -T '{date'
2723 $ hg log -T '{date'
2724 hg: parse error at 1: unterminated template expansion
2724 hg: parse error at 1: unterminated template expansion
2725 [255]
2725 [255]
2726
2726
2727 Behind the scenes, this will throw TypeError
2727 Behind the scenes, this will throw TypeError
2728
2728
2729 $ hg log -l 3 --template '{date|obfuscate}\n'
2729 $ hg log -l 3 --template '{date|obfuscate}\n'
2730 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2730 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2731 [255]
2731 [255]
2732
2732
2733 Behind the scenes, this will throw a ValueError
2733 Behind the scenes, this will throw a ValueError
2734
2734
2735 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2735 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2736 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2736 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2737 [255]
2737 [255]
2738
2738
2739 Behind the scenes, this will throw AttributeError
2739 Behind the scenes, this will throw AttributeError
2740
2740
2741 $ hg log -l 3 --template 'line: {date|escape}\n'
2741 $ hg log -l 3 --template 'line: {date|escape}\n'
2742 abort: template filter 'escape' is not compatible with keyword 'date'
2742 abort: template filter 'escape' is not compatible with keyword 'date'
2743 [255]
2743 [255]
2744
2744
2745 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2745 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2746 hg: parse error: localdate expects a date information
2746 hg: parse error: localdate expects a date information
2747 [255]
2747 [255]
2748
2748
2749 Behind the scenes, this will throw ValueError
2749 Behind the scenes, this will throw ValueError
2750
2750
2751 $ hg tip --template '{author|email|date}\n'
2751 $ hg tip --template '{author|email|date}\n'
2752 hg: parse error: date expects a date information
2752 hg: parse error: date expects a date information
2753 [255]
2753 [255]
2754
2754
2755 $ hg tip -T '{author|email|shortdate}\n'
2755 $ hg tip -T '{author|email|shortdate}\n'
2756 abort: template filter 'shortdate' is not compatible with keyword 'author'
2756 abort: template filter 'shortdate' is not compatible with keyword 'author'
2757 [255]
2757 [255]
2758
2758
2759 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2759 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2760 abort: incompatible use of template filter 'shortdate'
2760 abort: incompatible use of template filter 'shortdate'
2761 [255]
2761 [255]
2762
2762
2763 Error in nested template:
2763 Error in nested template:
2764
2764
2765 $ hg log -T '{"date'
2765 $ hg log -T '{"date'
2766 hg: parse error at 2: unterminated string
2766 hg: parse error at 2: unterminated string
2767 [255]
2767 [255]
2768
2768
2769 $ hg log -T '{"foo{date|?}"}'
2769 $ hg log -T '{"foo{date|?}"}'
2770 hg: parse error at 11: syntax error
2770 hg: parse error at 11: syntax error
2771 [255]
2771 [255]
2772
2772
2773 Thrown an error if a template function doesn't exist
2773 Thrown an error if a template function doesn't exist
2774
2774
2775 $ hg tip --template '{foo()}\n'
2775 $ hg tip --template '{foo()}\n'
2776 hg: parse error: unknown function 'foo'
2776 hg: parse error: unknown function 'foo'
2777 [255]
2777 [255]
2778
2778
2779 Pass generator object created by template function to filter
2779 Pass generator object created by template function to filter
2780
2780
2781 $ hg log -l 1 --template '{if(author, author)|user}\n'
2781 $ hg log -l 1 --template '{if(author, author)|user}\n'
2782 test
2782 test
2783
2783
2784 Test index keyword:
2784 Test index keyword:
2785
2785
2786 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2786 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2787 10 0:a 1:b 2:fifth 3:fourth 4:third
2787 10 0:a 1:b 2:fifth 3:fourth 4:third
2788 11 0:a
2788 11 0:a
2789
2789
2790 $ hg branches -T '{index} {branch}\n'
2790 $ hg branches -T '{index} {branch}\n'
2791 0 default
2791 0 default
2792 1 foo
2792 1 foo
2793
2793
2794 Test diff function:
2794 Test diff function:
2795
2795
2796 $ hg diff -c 8
2796 $ hg diff -c 8
2797 diff -r 29114dbae42b -r 95c24699272e fourth
2797 diff -r 29114dbae42b -r 95c24699272e fourth
2798 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2798 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2799 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2799 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2800 @@ -0,0 +1,1 @@
2800 @@ -0,0 +1,1 @@
2801 +second
2801 +second
2802 diff -r 29114dbae42b -r 95c24699272e second
2802 diff -r 29114dbae42b -r 95c24699272e second
2803 --- a/second Mon Jan 12 13:46:40 1970 +0000
2803 --- a/second Mon Jan 12 13:46:40 1970 +0000
2804 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2804 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2805 @@ -1,1 +0,0 @@
2805 @@ -1,1 +0,0 @@
2806 -second
2806 -second
2807 diff -r 29114dbae42b -r 95c24699272e third
2807 diff -r 29114dbae42b -r 95c24699272e third
2808 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2808 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2809 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2809 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2810 @@ -0,0 +1,1 @@
2810 @@ -0,0 +1,1 @@
2811 +third
2811 +third
2812
2812
2813 $ hg log -r 8 -T "{diff()}"
2813 $ hg log -r 8 -T "{diff()}"
2814 diff -r 29114dbae42b -r 95c24699272e fourth
2814 diff -r 29114dbae42b -r 95c24699272e fourth
2815 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2815 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2816 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2816 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2817 @@ -0,0 +1,1 @@
2817 @@ -0,0 +1,1 @@
2818 +second
2818 +second
2819 diff -r 29114dbae42b -r 95c24699272e second
2819 diff -r 29114dbae42b -r 95c24699272e second
2820 --- a/second Mon Jan 12 13:46:40 1970 +0000
2820 --- a/second Mon Jan 12 13:46:40 1970 +0000
2821 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2821 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2822 @@ -1,1 +0,0 @@
2822 @@ -1,1 +0,0 @@
2823 -second
2823 -second
2824 diff -r 29114dbae42b -r 95c24699272e third
2824 diff -r 29114dbae42b -r 95c24699272e third
2825 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2825 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2826 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2826 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2827 @@ -0,0 +1,1 @@
2827 @@ -0,0 +1,1 @@
2828 +third
2828 +third
2829
2829
2830 $ hg log -r 8 -T "{diff('glob:f*')}"
2830 $ hg log -r 8 -T "{diff('glob:f*')}"
2831 diff -r 29114dbae42b -r 95c24699272e fourth
2831 diff -r 29114dbae42b -r 95c24699272e fourth
2832 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2832 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2833 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2833 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2834 @@ -0,0 +1,1 @@
2834 @@ -0,0 +1,1 @@
2835 +second
2835 +second
2836
2836
2837 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2837 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2838 diff -r 29114dbae42b -r 95c24699272e second
2838 diff -r 29114dbae42b -r 95c24699272e second
2839 --- a/second Mon Jan 12 13:46:40 1970 +0000
2839 --- a/second Mon Jan 12 13:46:40 1970 +0000
2840 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2840 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2841 @@ -1,1 +0,0 @@
2841 @@ -1,1 +0,0 @@
2842 -second
2842 -second
2843 diff -r 29114dbae42b -r 95c24699272e third
2843 diff -r 29114dbae42b -r 95c24699272e third
2844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2845 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2845 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2846 @@ -0,0 +1,1 @@
2846 @@ -0,0 +1,1 @@
2847 +third
2847 +third
2848
2848
2849 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2849 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2850 diff -r 29114dbae42b -r 95c24699272e fourth
2850 diff -r 29114dbae42b -r 95c24699272e fourth
2851 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2851 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2852 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2852 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2853 @@ -0,0 +1,1 @@
2853 @@ -0,0 +1,1 @@
2854 +second
2854 +second
2855
2855
2856 $ cd ..
2856 $ cd ..
2857
2857
2858
2858
2859 latesttag:
2859 latesttag:
2860
2860
2861 $ hg init latesttag
2861 $ hg init latesttag
2862 $ cd latesttag
2862 $ cd latesttag
2863
2863
2864 $ echo a > file
2864 $ echo a > file
2865 $ hg ci -Am a -d '0 0'
2865 $ hg ci -Am a -d '0 0'
2866 adding file
2866 adding file
2867
2867
2868 $ echo b >> file
2868 $ echo b >> file
2869 $ hg ci -m b -d '1 0'
2869 $ hg ci -m b -d '1 0'
2870
2870
2871 $ echo c >> head1
2871 $ echo c >> head1
2872 $ hg ci -Am h1c -d '2 0'
2872 $ hg ci -Am h1c -d '2 0'
2873 adding head1
2873 adding head1
2874
2874
2875 $ hg update -q 1
2875 $ hg update -q 1
2876 $ echo d >> head2
2876 $ echo d >> head2
2877 $ hg ci -Am h2d -d '3 0'
2877 $ hg ci -Am h2d -d '3 0'
2878 adding head2
2878 adding head2
2879 created new head
2879 created new head
2880
2880
2881 $ echo e >> head2
2881 $ echo e >> head2
2882 $ hg ci -m h2e -d '4 0'
2882 $ hg ci -m h2e -d '4 0'
2883
2883
2884 $ hg merge -q
2884 $ hg merge -q
2885 $ hg ci -m merge -d '5 -3600'
2885 $ hg ci -m merge -d '5 -3600'
2886
2886
2887 No tag set:
2887 No tag set:
2888
2888
2889 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2889 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2890 @ 5: null+5
2890 @ 5: null+5
2891 |\
2891 |\
2892 | o 4: null+4
2892 | o 4: null+4
2893 | |
2893 | |
2894 | o 3: null+3
2894 | o 3: null+3
2895 | |
2895 | |
2896 o | 2: null+3
2896 o | 2: null+3
2897 |/
2897 |/
2898 o 1: null+2
2898 o 1: null+2
2899 |
2899 |
2900 o 0: null+1
2900 o 0: null+1
2901
2901
2902
2902
2903 One common tag: longest path wins for {latesttagdistance}:
2903 One common tag: longest path wins for {latesttagdistance}:
2904
2904
2905 $ hg tag -r 1 -m t1 -d '6 0' t1
2905 $ hg tag -r 1 -m t1 -d '6 0' t1
2906 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2906 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2907 @ 6: t1+4
2907 @ 6: t1+4
2908 |
2908 |
2909 o 5: t1+3
2909 o 5: t1+3
2910 |\
2910 |\
2911 | o 4: t1+2
2911 | o 4: t1+2
2912 | |
2912 | |
2913 | o 3: t1+1
2913 | o 3: t1+1
2914 | |
2914 | |
2915 o | 2: t1+1
2915 o | 2: t1+1
2916 |/
2916 |/
2917 o 1: t1+0
2917 o 1: t1+0
2918 |
2918 |
2919 o 0: null+1
2919 o 0: null+1
2920
2920
2921
2921
2922 One ancestor tag: closest wins:
2922 One ancestor tag: closest wins:
2923
2923
2924 $ hg tag -r 2 -m t2 -d '7 0' t2
2924 $ hg tag -r 2 -m t2 -d '7 0' t2
2925 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2925 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2926 @ 7: t2+3
2926 @ 7: t2+3
2927 |
2927 |
2928 o 6: t2+2
2928 o 6: t2+2
2929 |
2929 |
2930 o 5: t2+1
2930 o 5: t2+1
2931 |\
2931 |\
2932 | o 4: t1+2
2932 | o 4: t1+2
2933 | |
2933 | |
2934 | o 3: t1+1
2934 | o 3: t1+1
2935 | |
2935 | |
2936 o | 2: t2+0
2936 o | 2: t2+0
2937 |/
2937 |/
2938 o 1: t1+0
2938 o 1: t1+0
2939 |
2939 |
2940 o 0: null+1
2940 o 0: null+1
2941
2941
2942
2942
2943 Two branch tags: more recent wins if same number of changes:
2943 Two branch tags: more recent wins if same number of changes:
2944
2944
2945 $ hg tag -r 3 -m t3 -d '8 0' t3
2945 $ hg tag -r 3 -m t3 -d '8 0' t3
2946 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2946 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2947 @ 8: t3+5
2947 @ 8: t3+5
2948 |
2948 |
2949 o 7: t3+4
2949 o 7: t3+4
2950 |
2950 |
2951 o 6: t3+3
2951 o 6: t3+3
2952 |
2952 |
2953 o 5: t3+2
2953 o 5: t3+2
2954 |\
2954 |\
2955 | o 4: t3+1
2955 | o 4: t3+1
2956 | |
2956 | |
2957 | o 3: t3+0
2957 | o 3: t3+0
2958 | |
2958 | |
2959 o | 2: t2+0
2959 o | 2: t2+0
2960 |/
2960 |/
2961 o 1: t1+0
2961 o 1: t1+0
2962 |
2962 |
2963 o 0: null+1
2963 o 0: null+1
2964
2964
2965
2965
2966 Two branch tags: fewest changes wins:
2966 Two branch tags: fewest changes wins:
2967
2967
2968 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
2968 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
2969 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2969 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2970 @ 9: t4+5,6
2970 @ 9: t4+5,6
2971 |
2971 |
2972 o 8: t4+4,5
2972 o 8: t4+4,5
2973 |
2973 |
2974 o 7: t4+3,4
2974 o 7: t4+3,4
2975 |
2975 |
2976 o 6: t4+2,3
2976 o 6: t4+2,3
2977 |
2977 |
2978 o 5: t4+1,2
2978 o 5: t4+1,2
2979 |\
2979 |\
2980 | o 4: t4+0,0
2980 | o 4: t4+0,0
2981 | |
2981 | |
2982 | o 3: t3+0,0
2982 | o 3: t3+0,0
2983 | |
2983 | |
2984 o | 2: t2+0,0
2984 o | 2: t2+0,0
2985 |/
2985 |/
2986 o 1: t1+0,0
2986 o 1: t1+0,0
2987 |
2987 |
2988 o 0: null+1,1
2988 o 0: null+1,1
2989
2989
2990
2990
2991 Merged tag overrides:
2991 Merged tag overrides:
2992
2992
2993 $ hg tag -r 5 -m t5 -d '9 0' t5
2993 $ hg tag -r 5 -m t5 -d '9 0' t5
2994 $ hg tag -r 3 -m at3 -d '10 0' at3
2994 $ hg tag -r 3 -m at3 -d '10 0' at3
2995 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2995 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2996 @ 11: t5+6
2996 @ 11: t5+6
2997 |
2997 |
2998 o 10: t5+5
2998 o 10: t5+5
2999 |
2999 |
3000 o 9: t5+4
3000 o 9: t5+4
3001 |
3001 |
3002 o 8: t5+3
3002 o 8: t5+3
3003 |
3003 |
3004 o 7: t5+2
3004 o 7: t5+2
3005 |
3005 |
3006 o 6: t5+1
3006 o 6: t5+1
3007 |
3007 |
3008 o 5: t5+0
3008 o 5: t5+0
3009 |\
3009 |\
3010 | o 4: t4+0
3010 | o 4: t4+0
3011 | |
3011 | |
3012 | o 3: at3:t3+0
3012 | o 3: at3:t3+0
3013 | |
3013 | |
3014 o | 2: t2+0
3014 o | 2: t2+0
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 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3021 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3022 @ 11: t5+6,6
3022 @ 11: t5+6,6
3023 |
3023 |
3024 o 10: t5+5,5
3024 o 10: t5+5,5
3025 |
3025 |
3026 o 9: t5+4,4
3026 o 9: t5+4,4
3027 |
3027 |
3028 o 8: t5+3,3
3028 o 8: t5+3,3
3029 |
3029 |
3030 o 7: t5+2,2
3030 o 7: t5+2,2
3031 |
3031 |
3032 o 6: t5+1,1
3032 o 6: t5+1,1
3033 |
3033 |
3034 o 5: t5+0,0
3034 o 5: t5+0,0
3035 |\
3035 |\
3036 | o 4: t4+0,0
3036 | o 4: t4+0,0
3037 | |
3037 | |
3038 | o 3: at3+0,0 t3+0,0
3038 | o 3: at3+0,0 t3+0,0
3039 | |
3039 | |
3040 o | 2: t2+0,0
3040 o | 2: t2+0,0
3041 |/
3041 |/
3042 o 1: t1+0,0
3042 o 1: t1+0,0
3043 |
3043 |
3044 o 0: null+1,1
3044 o 0: null+1,1
3045
3045
3046
3046
3047 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3047 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3048 @ 11: t3, C: 9, D: 8
3048 @ 11: t3, C: 9, D: 8
3049 |
3049 |
3050 o 10: t3, C: 8, D: 7
3050 o 10: t3, C: 8, D: 7
3051 |
3051 |
3052 o 9: t3, C: 7, D: 6
3052 o 9: t3, C: 7, D: 6
3053 |
3053 |
3054 o 8: t3, C: 6, D: 5
3054 o 8: t3, C: 6, D: 5
3055 |
3055 |
3056 o 7: t3, C: 5, D: 4
3056 o 7: t3, C: 5, D: 4
3057 |
3057 |
3058 o 6: t3, C: 4, D: 3
3058 o 6: t3, C: 4, D: 3
3059 |
3059 |
3060 o 5: t3, C: 3, D: 2
3060 o 5: t3, C: 3, D: 2
3061 |\
3061 |\
3062 | o 4: t3, C: 1, D: 1
3062 | o 4: t3, C: 1, D: 1
3063 | |
3063 | |
3064 | o 3: t3, C: 0, D: 0
3064 | o 3: t3, C: 0, D: 0
3065 | |
3065 | |
3066 o | 2: t1, C: 1, D: 1
3066 o | 2: t1, C: 1, D: 1
3067 |/
3067 |/
3068 o 1: t1, C: 0, D: 0
3068 o 1: t1, C: 0, D: 0
3069 |
3069 |
3070 o 0: null, C: 1, D: 1
3070 o 0: null, C: 1, D: 1
3071
3071
3072
3072
3073 $ cd ..
3073 $ cd ..
3074
3074
3075
3075
3076 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3076 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3077 if it is a relative path
3077 if it is a relative path
3078
3078
3079 $ mkdir -p home/styles
3079 $ mkdir -p home/styles
3080
3080
3081 $ cat > home/styles/teststyle <<EOF
3081 $ cat > home/styles/teststyle <<EOF
3082 > changeset = 'test {rev}:{node|short}\n'
3082 > changeset = 'test {rev}:{node|short}\n'
3083 > EOF
3083 > EOF
3084
3084
3085 $ HOME=`pwd`/home; export HOME
3085 $ HOME=`pwd`/home; export HOME
3086
3086
3087 $ cat > latesttag/.hg/hgrc <<EOF
3087 $ cat > latesttag/.hg/hgrc <<EOF
3088 > [ui]
3088 > [ui]
3089 > style = ~/styles/teststyle
3089 > style = ~/styles/teststyle
3090 > EOF
3090 > EOF
3091
3091
3092 $ hg -R latesttag tip
3092 $ hg -R latesttag tip
3093 test 11:97e5943b523a
3093 test 11:97e5943b523a
3094
3094
3095 Test recursive showlist template (issue1989):
3095 Test recursive showlist template (issue1989):
3096
3096
3097 $ cat > style1989 <<EOF
3097 $ cat > style1989 <<EOF
3098 > changeset = '{file_mods}{manifest}{extras}'
3098 > changeset = '{file_mods}{manifest}{extras}'
3099 > file_mod = 'M|{author|person}\n'
3099 > file_mod = 'M|{author|person}\n'
3100 > manifest = '{rev},{author}\n'
3100 > manifest = '{rev},{author}\n'
3101 > extra = '{key}: {author}\n'
3101 > extra = '{key}: {author}\n'
3102 > EOF
3102 > EOF
3103
3103
3104 $ hg -R latesttag log -r tip --style=style1989
3104 $ hg -R latesttag log -r tip --style=style1989
3105 M|test
3105 M|test
3106 11,test
3106 11,test
3107 branch: test
3107 branch: test
3108
3108
3109 Test new-style inline templating:
3109 Test new-style inline templating:
3110
3110
3111 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3111 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3112 modified files: .hgtags
3112 modified files: .hgtags
3113
3113
3114
3114
3115 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3115 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3116 hg: parse error: keyword 'rev' is not iterable
3116 hg: parse error: keyword 'rev' is not iterable
3117 [255]
3117 [255]
3118 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3118 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3119 hg: parse error: None is not iterable
3119 hg: parse error: None is not iterable
3120 [255]
3120 [255]
3121
3121
3122 Test new-style inline templating of non-list/dict type:
3122 Test new-style inline templating of non-list/dict type:
3123
3123
3124 $ hg log -R latesttag -r tip -T '{manifest}\n'
3124 $ hg log -R latesttag -r tip -T '{manifest}\n'
3125 11:2bc6e9006ce2
3125 11:2bc6e9006ce2
3126 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3126 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3127 string length: 15
3127 string length: 15
3128 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3128 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3129 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3129 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3130
3130
3131 Test manifest can be join()-ed as before, though it's silly:
3131 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3132 branch: default
3133 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3134 hg: parse error: None is not iterable
3135 [255]
3136 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3137 branch: default
3138 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3139 0:ce3cec86e6c2
3140 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3141 9:fbc7cd862e9c
3142
3143 Test manifest/get() can be join()-ed as before, though it's silly:
3132
3144
3133 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3145 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3134 11:2bc6e9006ce2
3146 11:2bc6e9006ce2
3147 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
3148 default
3135
3149
3136 Test the sub function of templating for expansion:
3150 Test the sub function of templating for expansion:
3137
3151
3138 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3152 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3139 xx
3153 xx
3140
3154
3141 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3155 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3142 hg: parse error: sub got an invalid pattern: [
3156 hg: parse error: sub got an invalid pattern: [
3143 [255]
3157 [255]
3144 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3158 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3145 hg: parse error: sub got an invalid replacement: \1
3159 hg: parse error: sub got an invalid replacement: \1
3146 [255]
3160 [255]
3147
3161
3148 Test the strip function with chars specified:
3162 Test the strip function with chars specified:
3149
3163
3150 $ hg log -R latesttag --template '{desc}\n'
3164 $ hg log -R latesttag --template '{desc}\n'
3151 at3
3165 at3
3152 t5
3166 t5
3153 t4
3167 t4
3154 t3
3168 t3
3155 t2
3169 t2
3156 t1
3170 t1
3157 merge
3171 merge
3158 h2e
3172 h2e
3159 h2d
3173 h2d
3160 h1c
3174 h1c
3161 b
3175 b
3162 a
3176 a
3163
3177
3164 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3178 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3165 at3
3179 at3
3166 5
3180 5
3167 4
3181 4
3168 3
3182 3
3169 2
3183 2
3170 1
3184 1
3171 merg
3185 merg
3172 h2
3186 h2
3173 h2d
3187 h2d
3174 h1c
3188 h1c
3175 b
3189 b
3176 a
3190 a
3177
3191
3178 Test date format:
3192 Test date format:
3179
3193
3180 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3194 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3181 date: 70 01 01 10 +0000
3195 date: 70 01 01 10 +0000
3182 date: 70 01 01 09 +0000
3196 date: 70 01 01 09 +0000
3183 date: 70 01 01 04 +0000
3197 date: 70 01 01 04 +0000
3184 date: 70 01 01 08 +0000
3198 date: 70 01 01 08 +0000
3185 date: 70 01 01 07 +0000
3199 date: 70 01 01 07 +0000
3186 date: 70 01 01 06 +0000
3200 date: 70 01 01 06 +0000
3187 date: 70 01 01 05 +0100
3201 date: 70 01 01 05 +0100
3188 date: 70 01 01 04 +0000
3202 date: 70 01 01 04 +0000
3189 date: 70 01 01 03 +0000
3203 date: 70 01 01 03 +0000
3190 date: 70 01 01 02 +0000
3204 date: 70 01 01 02 +0000
3191 date: 70 01 01 01 +0000
3205 date: 70 01 01 01 +0000
3192 date: 70 01 01 00 +0000
3206 date: 70 01 01 00 +0000
3193
3207
3194 Test invalid date:
3208 Test invalid date:
3195
3209
3196 $ hg log -R latesttag -T '{date(rev)}\n'
3210 $ hg log -R latesttag -T '{date(rev)}\n'
3197 hg: parse error: date expects a date information
3211 hg: parse error: date expects a date information
3198 [255]
3212 [255]
3199
3213
3200 Test integer literal:
3214 Test integer literal:
3201
3215
3202 $ hg debugtemplate -v '{(0)}\n'
3216 $ hg debugtemplate -v '{(0)}\n'
3203 (template
3217 (template
3204 (group
3218 (group
3205 (integer '0'))
3219 (integer '0'))
3206 (string '\n'))
3220 (string '\n'))
3207 0
3221 0
3208 $ hg debugtemplate -v '{(123)}\n'
3222 $ hg debugtemplate -v '{(123)}\n'
3209 (template
3223 (template
3210 (group
3224 (group
3211 (integer '123'))
3225 (integer '123'))
3212 (string '\n'))
3226 (string '\n'))
3213 123
3227 123
3214 $ hg debugtemplate -v '{(-4)}\n'
3228 $ hg debugtemplate -v '{(-4)}\n'
3215 (template
3229 (template
3216 (group
3230 (group
3217 (negate
3231 (negate
3218 (integer '4')))
3232 (integer '4')))
3219 (string '\n'))
3233 (string '\n'))
3220 -4
3234 -4
3221 $ hg debugtemplate '{(-)}\n'
3235 $ hg debugtemplate '{(-)}\n'
3222 hg: parse error at 3: not a prefix: )
3236 hg: parse error at 3: not a prefix: )
3223 [255]
3237 [255]
3224 $ hg debugtemplate '{(-a)}\n'
3238 $ hg debugtemplate '{(-a)}\n'
3225 hg: parse error: negation needs an integer argument
3239 hg: parse error: negation needs an integer argument
3226 [255]
3240 [255]
3227
3241
3228 top-level integer literal is interpreted as symbol (i.e. variable name):
3242 top-level integer literal is interpreted as symbol (i.e. variable name):
3229
3243
3230 $ hg debugtemplate -D 1=one -v '{1}\n'
3244 $ hg debugtemplate -D 1=one -v '{1}\n'
3231 (template
3245 (template
3232 (integer '1')
3246 (integer '1')
3233 (string '\n'))
3247 (string '\n'))
3234 one
3248 one
3235 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3249 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3236 (template
3250 (template
3237 (func
3251 (func
3238 (symbol 'if')
3252 (symbol 'if')
3239 (list
3253 (list
3240 (string 't')
3254 (string 't')
3241 (template
3255 (template
3242 (integer '1'))))
3256 (integer '1'))))
3243 (string '\n'))
3257 (string '\n'))
3244 one
3258 one
3245 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3259 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3246 (template
3260 (template
3247 (|
3261 (|
3248 (integer '1')
3262 (integer '1')
3249 (symbol 'stringify'))
3263 (symbol 'stringify'))
3250 (string '\n'))
3264 (string '\n'))
3251 one
3265 one
3252
3266
3253 unless explicit symbol is expected:
3267 unless explicit symbol is expected:
3254
3268
3255 $ hg log -Ra -r0 -T '{desc|1}\n'
3269 $ hg log -Ra -r0 -T '{desc|1}\n'
3256 hg: parse error: expected a symbol, got 'integer'
3270 hg: parse error: expected a symbol, got 'integer'
3257 [255]
3271 [255]
3258 $ hg log -Ra -r0 -T '{1()}\n'
3272 $ hg log -Ra -r0 -T '{1()}\n'
3259 hg: parse error: expected a symbol, got 'integer'
3273 hg: parse error: expected a symbol, got 'integer'
3260 [255]
3274 [255]
3261
3275
3262 Test string literal:
3276 Test string literal:
3263
3277
3264 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3278 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3265 (template
3279 (template
3266 (string 'string with no template fragment')
3280 (string 'string with no template fragment')
3267 (string '\n'))
3281 (string '\n'))
3268 string with no template fragment
3282 string with no template fragment
3269 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3283 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3270 (template
3284 (template
3271 (template
3285 (template
3272 (string 'template: ')
3286 (string 'template: ')
3273 (symbol 'rev'))
3287 (symbol 'rev'))
3274 (string '\n'))
3288 (string '\n'))
3275 template: 0
3289 template: 0
3276 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3290 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3277 (template
3291 (template
3278 (string 'rawstring: {rev}')
3292 (string 'rawstring: {rev}')
3279 (string '\n'))
3293 (string '\n'))
3280 rawstring: {rev}
3294 rawstring: {rev}
3281 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3295 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3282 (template
3296 (template
3283 (%
3297 (%
3284 (symbol 'files')
3298 (symbol 'files')
3285 (string 'rawstring: {file}'))
3299 (string 'rawstring: {file}'))
3286 (string '\n'))
3300 (string '\n'))
3287 rawstring: {file}
3301 rawstring: {file}
3288
3302
3289 Test string escaping:
3303 Test string escaping:
3290
3304
3291 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3305 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3292 >
3306 >
3293 <>\n<[>
3307 <>\n<[>
3294 <>\n<]>
3308 <>\n<]>
3295 <>\n<
3309 <>\n<
3296
3310
3297 $ hg log -R latesttag -r 0 \
3311 $ hg log -R latesttag -r 0 \
3298 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3312 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3299 >
3313 >
3300 <>\n<[>
3314 <>\n<[>
3301 <>\n<]>
3315 <>\n<]>
3302 <>\n<
3316 <>\n<
3303
3317
3304 $ hg log -R latesttag -r 0 -T esc \
3318 $ hg log -R latesttag -r 0 -T esc \
3305 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3319 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3306 >
3320 >
3307 <>\n<[>
3321 <>\n<[>
3308 <>\n<]>
3322 <>\n<]>
3309 <>\n<
3323 <>\n<
3310
3324
3311 $ cat <<'EOF' > esctmpl
3325 $ cat <<'EOF' > esctmpl
3312 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3326 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3313 > EOF
3327 > EOF
3314 $ hg log -R latesttag -r 0 --style ./esctmpl
3328 $ hg log -R latesttag -r 0 --style ./esctmpl
3315 >
3329 >
3316 <>\n<[>
3330 <>\n<[>
3317 <>\n<]>
3331 <>\n<]>
3318 <>\n<
3332 <>\n<
3319
3333
3320 Test string escaping of quotes:
3334 Test string escaping of quotes:
3321
3335
3322 $ hg log -Ra -r0 -T '{"\""}\n'
3336 $ hg log -Ra -r0 -T '{"\""}\n'
3323 "
3337 "
3324 $ hg log -Ra -r0 -T '{"\\\""}\n'
3338 $ hg log -Ra -r0 -T '{"\\\""}\n'
3325 \"
3339 \"
3326 $ hg log -Ra -r0 -T '{r"\""}\n'
3340 $ hg log -Ra -r0 -T '{r"\""}\n'
3327 \"
3341 \"
3328 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3342 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3329 \\\"
3343 \\\"
3330
3344
3331
3345
3332 $ hg log -Ra -r0 -T '{"\""}\n'
3346 $ hg log -Ra -r0 -T '{"\""}\n'
3333 "
3347 "
3334 $ hg log -Ra -r0 -T '{"\\\""}\n'
3348 $ hg log -Ra -r0 -T '{"\\\""}\n'
3335 \"
3349 \"
3336 $ hg log -Ra -r0 -T '{r"\""}\n'
3350 $ hg log -Ra -r0 -T '{r"\""}\n'
3337 \"
3351 \"
3338 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3352 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3339 \\\"
3353 \\\"
3340
3354
3341 Test exception in quoted template. single backslash before quotation mark is
3355 Test exception in quoted template. single backslash before quotation mark is
3342 stripped before parsing:
3356 stripped before parsing:
3343
3357
3344 $ cat <<'EOF' > escquotetmpl
3358 $ cat <<'EOF' > escquotetmpl
3345 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3359 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3346 > EOF
3360 > EOF
3347 $ cd latesttag
3361 $ cd latesttag
3348 $ hg log -r 2 --style ../escquotetmpl
3362 $ hg log -r 2 --style ../escquotetmpl
3349 " \" \" \\" head1
3363 " \" \" \\" head1
3350
3364
3351 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3365 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3352 valid
3366 valid
3353 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3367 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3354 valid
3368 valid
3355
3369
3356 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3370 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3357 _evalifliteral() templates (issue4733):
3371 _evalifliteral() templates (issue4733):
3358
3372
3359 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3373 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3360 "2
3374 "2
3361 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3375 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3362 "2
3376 "2
3363 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3377 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3364 "2
3378 "2
3365
3379
3366 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3380 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3367 \"
3381 \"
3368 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3382 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3369 \"
3383 \"
3370 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3384 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3371 \"
3385 \"
3372
3386
3373 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3387 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3374 \\\"
3388 \\\"
3375 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3389 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3376 \\\"
3390 \\\"
3377 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3391 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3378 \\\"
3392 \\\"
3379
3393
3380 escaped single quotes and errors:
3394 escaped single quotes and errors:
3381
3395
3382 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3396 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3383 foo
3397 foo
3384 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3398 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3385 foo
3399 foo
3386 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3400 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3387 hg: parse error at 21: unterminated string
3401 hg: parse error at 21: unterminated string
3388 [255]
3402 [255]
3389 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3403 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3390 hg: parse error: trailing \ in string
3404 hg: parse error: trailing \ in string
3391 [255]
3405 [255]
3392 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3406 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3393 hg: parse error: trailing \ in string
3407 hg: parse error: trailing \ in string
3394 [255]
3408 [255]
3395
3409
3396 $ cd ..
3410 $ cd ..
3397
3411
3398 Test leading backslashes:
3412 Test leading backslashes:
3399
3413
3400 $ cd latesttag
3414 $ cd latesttag
3401 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3415 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3402 {rev} {file}
3416 {rev} {file}
3403 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3417 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3404 \2 \head1
3418 \2 \head1
3405 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3419 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3406 \{rev} \{file}
3420 \{rev} \{file}
3407 $ cd ..
3421 $ cd ..
3408
3422
3409 Test leading backslashes in "if" expression (issue4714):
3423 Test leading backslashes in "if" expression (issue4714):
3410
3424
3411 $ cd latesttag
3425 $ cd latesttag
3412 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3426 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3413 {rev} \{rev}
3427 {rev} \{rev}
3414 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3428 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3415 \2 \\{rev}
3429 \2 \\{rev}
3416 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3430 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3417 \{rev} \\\{rev}
3431 \{rev} \\\{rev}
3418 $ cd ..
3432 $ cd ..
3419
3433
3420 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3434 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3421
3435
3422 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3436 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3423 \x6e
3437 \x6e
3424 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3438 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3425 \x5c\x786e
3439 \x5c\x786e
3426 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3440 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3427 \x6e
3441 \x6e
3428 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3442 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3429 \x5c\x786e
3443 \x5c\x786e
3430
3444
3431 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3445 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3432 \x6e
3446 \x6e
3433 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3447 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3434 \x5c\x786e
3448 \x5c\x786e
3435 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3449 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3436 \x6e
3450 \x6e
3437 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3451 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3438 \x5c\x786e
3452 \x5c\x786e
3439
3453
3440 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3454 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3441 fourth
3455 fourth
3442 second
3456 second
3443 third
3457 third
3444 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3458 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3445 fourth\nsecond\nthird
3459 fourth\nsecond\nthird
3446
3460
3447 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3461 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3448 <p>
3462 <p>
3449 1st
3463 1st
3450 </p>
3464 </p>
3451 <p>
3465 <p>
3452 2nd
3466 2nd
3453 </p>
3467 </p>
3454 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3468 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3455 <p>
3469 <p>
3456 1st\n\n2nd
3470 1st\n\n2nd
3457 </p>
3471 </p>
3458 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3472 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3459 1st
3473 1st
3460
3474
3461 2nd
3475 2nd
3462
3476
3463 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3477 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3464 o perso
3478 o perso
3465 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3479 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3466 no person
3480 no person
3467 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3481 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3468 o perso
3482 o perso
3469 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3483 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3470 no perso
3484 no perso
3471
3485
3472 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3486 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3473 -o perso-
3487 -o perso-
3474 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3488 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3475 no person
3489 no person
3476 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3490 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3477 \x2do perso\x2d
3491 \x2do perso\x2d
3478 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3492 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3479 -o perso-
3493 -o perso-
3480 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3494 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3481 \x2do perso\x6e
3495 \x2do perso\x6e
3482
3496
3483 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3497 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3484 fourth
3498 fourth
3485 second
3499 second
3486 third
3500 third
3487
3501
3488 Test string escaping in nested expression:
3502 Test string escaping in nested expression:
3489
3503
3490 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3504 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3491 fourth\x6esecond\x6ethird
3505 fourth\x6esecond\x6ethird
3492 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3506 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3493 fourth\x6esecond\x6ethird
3507 fourth\x6esecond\x6ethird
3494
3508
3495 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3509 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3496 fourth\x6esecond\x6ethird
3510 fourth\x6esecond\x6ethird
3497 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3511 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3498 fourth\x5c\x786esecond\x5c\x786ethird
3512 fourth\x5c\x786esecond\x5c\x786ethird
3499
3513
3500 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3514 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3501 3:\x6eo user, \x6eo domai\x6e
3515 3:\x6eo user, \x6eo domai\x6e
3502 4:\x5c\x786eew bra\x5c\x786ech
3516 4:\x5c\x786eew bra\x5c\x786ech
3503
3517
3504 Test quotes in nested expression are evaluated just like a $(command)
3518 Test quotes in nested expression are evaluated just like a $(command)
3505 substitution in POSIX shells:
3519 substitution in POSIX shells:
3506
3520
3507 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3521 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3508 8:95c24699272e
3522 8:95c24699272e
3509 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3523 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3510 {8} "95c24699272e"
3524 {8} "95c24699272e"
3511
3525
3512 Test recursive evaluation:
3526 Test recursive evaluation:
3513
3527
3514 $ hg init r
3528 $ hg init r
3515 $ cd r
3529 $ cd r
3516 $ echo a > a
3530 $ echo a > a
3517 $ hg ci -Am '{rev}'
3531 $ hg ci -Am '{rev}'
3518 adding a
3532 adding a
3519 $ hg log -r 0 --template '{if(rev, desc)}\n'
3533 $ hg log -r 0 --template '{if(rev, desc)}\n'
3520 {rev}
3534 {rev}
3521 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3535 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3522 test 0
3536 test 0
3523
3537
3524 $ hg branch -q 'text.{rev}'
3538 $ hg branch -q 'text.{rev}'
3525 $ echo aa >> aa
3539 $ echo aa >> aa
3526 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3540 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3527
3541
3528 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3542 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3529 {node|short}desc to
3543 {node|short}desc to
3530 text.{rev}be wrapped
3544 text.{rev}be wrapped
3531 text.{rev}desc to be
3545 text.{rev}desc to be
3532 text.{rev}wrapped (no-eol)
3546 text.{rev}wrapped (no-eol)
3533 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3547 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3534 bcc7ff960b8e:desc to
3548 bcc7ff960b8e:desc to
3535 text.1:be wrapped
3549 text.1:be wrapped
3536 text.1:desc to be
3550 text.1:desc to be
3537 text.1:wrapped (no-eol)
3551 text.1:wrapped (no-eol)
3538 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3552 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3539 hg: parse error: fill expects an integer width
3553 hg: parse error: fill expects an integer width
3540 [255]
3554 [255]
3541
3555
3542 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3556 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3543 bcc7ff960b8e:desc to be
3557 bcc7ff960b8e:desc to be
3544 termwidth.1:wrapped desc
3558 termwidth.1:wrapped desc
3545 termwidth.1:to be wrapped (no-eol)
3559 termwidth.1:to be wrapped (no-eol)
3546
3560
3547 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3561 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3548 {node|short} (no-eol)
3562 {node|short} (no-eol)
3549 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3563 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3550 bcc-ff---b-e (no-eol)
3564 bcc-ff---b-e (no-eol)
3551
3565
3552 $ cat >> .hg/hgrc <<EOF
3566 $ cat >> .hg/hgrc <<EOF
3553 > [extensions]
3567 > [extensions]
3554 > color=
3568 > color=
3555 > [color]
3569 > [color]
3556 > mode=ansi
3570 > mode=ansi
3557 > text.{rev} = red
3571 > text.{rev} = red
3558 > text.1 = green
3572 > text.1 = green
3559 > EOF
3573 > EOF
3560 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3574 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3561 \x1b[0;31mtext\x1b[0m (esc)
3575 \x1b[0;31mtext\x1b[0m (esc)
3562 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3576 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3563 \x1b[0;32mtext\x1b[0m (esc)
3577 \x1b[0;32mtext\x1b[0m (esc)
3564
3578
3565 color effect can be specified without quoting:
3579 color effect can be specified without quoting:
3566
3580
3567 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3581 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3568 \x1b[0;31mtext\x1b[0m (esc)
3582 \x1b[0;31mtext\x1b[0m (esc)
3569
3583
3570 color effects can be nested (issue5413)
3584 color effects can be nested (issue5413)
3571
3585
3572 $ hg debugtemplate --color=always \
3586 $ hg debugtemplate --color=always \
3573 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3587 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3574 \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)
3588 \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)
3575
3589
3576 pad() should interact well with color codes (issue5416)
3590 pad() should interact well with color codes (issue5416)
3577
3591
3578 $ hg debugtemplate --color=always \
3592 $ hg debugtemplate --color=always \
3579 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3593 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3580 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3594 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3581
3595
3582 label should be no-op if color is disabled:
3596 label should be no-op if color is disabled:
3583
3597
3584 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3598 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3585 text
3599 text
3586 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3600 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3587 text
3601 text
3588
3602
3589 Test branches inside if statement:
3603 Test branches inside if statement:
3590
3604
3591 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3605 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3592 no
3606 no
3593
3607
3594 Test dict constructor:
3608 Test dict constructor:
3595
3609
3596 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3610 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3597 y=f7769ec2ab97 x=0
3611 y=f7769ec2ab97 x=0
3598 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3612 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3599 x=0
3613 x=0
3600 y=f7769ec2ab97
3614 y=f7769ec2ab97
3601 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3615 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3602 {"x": 0, "y": "f7769ec2ab97"}
3616 {"x": 0, "y": "f7769ec2ab97"}
3603 $ hg log -r 0 -T '{dict()|json}\n'
3617 $ hg log -r 0 -T '{dict()|json}\n'
3604 {}
3618 {}
3605
3619
3606 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3620 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3607 rev=0 node=f7769ec2ab97
3621 rev=0 node=f7769ec2ab97
3608 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3622 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3609 rev=0 node=f7769ec2ab97
3623 rev=0 node=f7769ec2ab97
3610
3624
3611 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3625 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3612 hg: parse error: duplicated dict key 'rev' inferred
3626 hg: parse error: duplicated dict key 'rev' inferred
3613 [255]
3627 [255]
3614 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3628 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3615 hg: parse error: duplicated dict key 'node' inferred
3629 hg: parse error: duplicated dict key 'node' inferred
3616 [255]
3630 [255]
3617 $ hg log -r 0 -T '{dict(1 + 2)}'
3631 $ hg log -r 0 -T '{dict(1 + 2)}'
3618 hg: parse error: dict key cannot be inferred
3632 hg: parse error: dict key cannot be inferred
3619 [255]
3633 [255]
3620
3634
3621 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3635 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3622 hg: parse error: dict got multiple values for keyword argument 'x'
3636 hg: parse error: dict got multiple values for keyword argument 'x'
3623 [255]
3637 [255]
3624
3638
3625 Test get function:
3639 Test get function:
3626
3640
3627 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3641 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3628 default
3642 default
3629 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3643 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3630 default
3644 default
3631 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3645 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3632 hg: parse error: get() expects a dict as first argument
3646 hg: parse error: get() expects a dict as first argument
3633 [255]
3647 [255]
3634
3648
3635 Test json filter applied to hybrid object:
3649 Test json filter applied to hybrid object:
3636
3650
3637 $ hg log -r0 -T '{files|json}\n'
3651 $ hg log -r0 -T '{files|json}\n'
3638 ["a"]
3652 ["a"]
3639 $ hg log -r0 -T '{extras|json}\n'
3653 $ hg log -r0 -T '{extras|json}\n'
3640 {"branch": "default"}
3654 {"branch": "default"}
3641
3655
3642 Test localdate(date, tz) function:
3656 Test localdate(date, tz) function:
3643
3657
3644 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3658 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3645 1970-01-01 09:00 +0900
3659 1970-01-01 09:00 +0900
3646 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3660 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3647 1970-01-01 00:00 +0000
3661 1970-01-01 00:00 +0000
3648 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3662 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3649 hg: parse error: localdate expects a timezone
3663 hg: parse error: localdate expects a timezone
3650 [255]
3664 [255]
3651 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3665 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3652 1970-01-01 02:00 +0200
3666 1970-01-01 02:00 +0200
3653 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3667 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3654 1970-01-01 00:00 +0000
3668 1970-01-01 00:00 +0000
3655 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3669 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3656 1970-01-01 00:00 +0000
3670 1970-01-01 00:00 +0000
3657 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3671 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3658 hg: parse error: localdate expects a timezone
3672 hg: parse error: localdate expects a timezone
3659 [255]
3673 [255]
3660 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3674 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3661 hg: parse error: localdate expects a timezone
3675 hg: parse error: localdate expects a timezone
3662 [255]
3676 [255]
3663
3677
3664 Test shortest(node) function:
3678 Test shortest(node) function:
3665
3679
3666 $ echo b > b
3680 $ echo b > b
3667 $ hg ci -qAm b
3681 $ hg ci -qAm b
3668 $ hg log --template '{shortest(node)}\n'
3682 $ hg log --template '{shortest(node)}\n'
3669 e777
3683 e777
3670 bcc7
3684 bcc7
3671 f776
3685 f776
3672 $ hg log --template '{shortest(node, 10)}\n'
3686 $ hg log --template '{shortest(node, 10)}\n'
3673 e777603221
3687 e777603221
3674 bcc7ff960b
3688 bcc7ff960b
3675 f7769ec2ab
3689 f7769ec2ab
3676 $ hg log --template '{node|shortest}\n' -l1
3690 $ hg log --template '{node|shortest}\n' -l1
3677 e777
3691 e777
3678
3692
3679 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3693 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3680 f7769ec2ab
3694 f7769ec2ab
3681 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3695 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3682 hg: parse error: shortest() expects an integer minlength
3696 hg: parse error: shortest() expects an integer minlength
3683 [255]
3697 [255]
3684
3698
3685 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3699 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3686 ffff
3700 ffff
3687
3701
3688 $ cd ..
3702 $ cd ..
3689
3703
3690 Test shortest(node) with the repo having short hash collision:
3704 Test shortest(node) with the repo having short hash collision:
3691
3705
3692 $ hg init hashcollision
3706 $ hg init hashcollision
3693 $ cd hashcollision
3707 $ cd hashcollision
3694 $ cat <<EOF >> .hg/hgrc
3708 $ cat <<EOF >> .hg/hgrc
3695 > [experimental]
3709 > [experimental]
3696 > stabilization = createmarkers
3710 > stabilization = createmarkers
3697 > EOF
3711 > EOF
3698 $ echo 0 > a
3712 $ echo 0 > a
3699 $ hg ci -qAm 0
3713 $ hg ci -qAm 0
3700 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3714 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3701 > hg up -q 0
3715 > hg up -q 0
3702 > echo $i > a
3716 > echo $i > a
3703 > hg ci -qm $i
3717 > hg ci -qm $i
3704 > done
3718 > done
3705 $ hg up -q null
3719 $ hg up -q null
3706 $ hg log -r0: -T '{rev}:{node}\n'
3720 $ hg log -r0: -T '{rev}:{node}\n'
3707 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3721 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3708 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3722 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3709 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3723 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3710 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3724 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3711 4:10776689e627b465361ad5c296a20a487e153ca4
3725 4:10776689e627b465361ad5c296a20a487e153ca4
3712 5:a00be79088084cb3aff086ab799f8790e01a976b
3726 5:a00be79088084cb3aff086ab799f8790e01a976b
3713 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3727 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3714 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3728 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3715 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3729 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3716 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3730 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3717 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3731 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3718 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3732 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3719 obsoleted 1 changesets
3733 obsoleted 1 changesets
3720 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3734 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3721 obsoleted 1 changesets
3735 obsoleted 1 changesets
3722 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3736 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3723 obsoleted 1 changesets
3737 obsoleted 1 changesets
3724
3738
3725 nodes starting with '11' (we don't have the revision number '11' though)
3739 nodes starting with '11' (we don't have the revision number '11' though)
3726
3740
3727 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3741 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3728 1:1142
3742 1:1142
3729 2:1140
3743 2:1140
3730 3:11d
3744 3:11d
3731
3745
3732 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3746 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3733
3747
3734 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3748 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3735 6:a0b
3749 6:a0b
3736 7:a04
3750 7:a04
3737
3751
3738 node '10' conflicts with the revision number '10' even if it is hidden
3752 node '10' conflicts with the revision number '10' even if it is hidden
3739 (we could exclude hidden revision numbers, but currently we don't)
3753 (we could exclude hidden revision numbers, but currently we don't)
3740
3754
3741 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3755 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3742 4:107
3756 4:107
3743 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3757 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3744 4:107
3758 4:107
3745
3759
3746 node 'c562' should be unique if the other 'c562' nodes are hidden
3760 node 'c562' should be unique if the other 'c562' nodes are hidden
3747 (but we don't try the slow path to filter out hidden nodes for now)
3761 (but we don't try the slow path to filter out hidden nodes for now)
3748
3762
3749 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3763 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3750 8:c5625
3764 8:c5625
3751 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3765 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3752 8:c5625
3766 8:c5625
3753 9:c5623
3767 9:c5623
3754 10:c562d
3768 10:c562d
3755
3769
3756 $ cd ..
3770 $ cd ..
3757
3771
3758 Test pad function
3772 Test pad function
3759
3773
3760 $ cd r
3774 $ cd r
3761
3775
3762 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3776 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3763 2 test
3777 2 test
3764 1 {node|short}
3778 1 {node|short}
3765 0 test
3779 0 test
3766
3780
3767 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3781 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3768 2 test
3782 2 test
3769 1 {node|short}
3783 1 {node|short}
3770 0 test
3784 0 test
3771
3785
3772 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3786 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3773 2------------------- test
3787 2------------------- test
3774 1------------------- {node|short}
3788 1------------------- {node|short}
3775 0------------------- test
3789 0------------------- test
3776
3790
3777 Test template string in pad function
3791 Test template string in pad function
3778
3792
3779 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3793 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3780 {0} test
3794 {0} test
3781
3795
3782 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3796 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3783 \{rev} test
3797 \{rev} test
3784
3798
3785 Test width argument passed to pad function
3799 Test width argument passed to pad function
3786
3800
3787 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3801 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3788 0 test
3802 0 test
3789 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3803 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3790 hg: parse error: pad() expects an integer width
3804 hg: parse error: pad() expects an integer width
3791 [255]
3805 [255]
3792
3806
3793 Test invalid fillchar passed to pad function
3807 Test invalid fillchar passed to pad function
3794
3808
3795 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3809 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3796 hg: parse error: pad() expects a single fill character
3810 hg: parse error: pad() expects a single fill character
3797 [255]
3811 [255]
3798 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3812 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3799 hg: parse error: pad() expects a single fill character
3813 hg: parse error: pad() expects a single fill character
3800 [255]
3814 [255]
3801
3815
3802 Test boolean argument passed to pad function
3816 Test boolean argument passed to pad function
3803
3817
3804 no crash
3818 no crash
3805
3819
3806 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3820 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3807 ---------0
3821 ---------0
3808
3822
3809 string/literal
3823 string/literal
3810
3824
3811 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3825 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3812 ---------0
3826 ---------0
3813 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3827 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3814 0---------
3828 0---------
3815 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3829 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3816 0---------
3830 0---------
3817
3831
3818 unknown keyword is evaluated to ''
3832 unknown keyword is evaluated to ''
3819
3833
3820 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3834 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3821 0---------
3835 0---------
3822
3836
3823 Test separate function
3837 Test separate function
3824
3838
3825 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3839 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3826 a-b-c
3840 a-b-c
3827 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3841 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3828 0:f7769ec2ab97 test default
3842 0:f7769ec2ab97 test default
3829 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3843 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3830 a \x1b[0;31mb\x1b[0m c d (esc)
3844 a \x1b[0;31mb\x1b[0m c d (esc)
3831
3845
3832 Test boolean expression/literal passed to if function
3846 Test boolean expression/literal passed to if function
3833
3847
3834 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3848 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3835 rev 0 is True
3849 rev 0 is True
3836 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3850 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3837 literal 0 is True as well
3851 literal 0 is True as well
3838 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3852 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3839 empty string is False
3853 empty string is False
3840 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3854 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3841 empty list is False
3855 empty list is False
3842 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3856 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3843 true is True
3857 true is True
3844 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3858 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3845 false is False
3859 false is False
3846 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3860 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3847 non-empty string is True
3861 non-empty string is True
3848
3862
3849 Test ifcontains function
3863 Test ifcontains function
3850
3864
3851 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3865 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3852 2 is in the string
3866 2 is in the string
3853 1 is not
3867 1 is not
3854 0 is in the string
3868 0 is in the string
3855
3869
3856 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3870 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3857 2 is in the string
3871 2 is in the string
3858 1 is not
3872 1 is not
3859 0 is in the string
3873 0 is in the string
3860
3874
3861 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3875 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3862 2 did not add a
3876 2 did not add a
3863 1 did not add a
3877 1 did not add a
3864 0 added a
3878 0 added a
3865
3879
3866 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3880 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3867 2 is parent of 1
3881 2 is parent of 1
3868 1
3882 1
3869 0
3883 0
3870
3884
3871 Test revset function
3885 Test revset function
3872
3886
3873 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3887 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3874 2 current rev
3888 2 current rev
3875 1 not current rev
3889 1 not current rev
3876 0 not current rev
3890 0 not current rev
3877
3891
3878 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3892 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3879 2 match rev
3893 2 match rev
3880 1 match rev
3894 1 match rev
3881 0 not match rev
3895 0 not match rev
3882
3896
3883 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3897 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3884 2 Parents: 1
3898 2 Parents: 1
3885 1 Parents: 0
3899 1 Parents: 0
3886 0 Parents:
3900 0 Parents:
3887
3901
3888 $ cat >> .hg/hgrc <<EOF
3902 $ cat >> .hg/hgrc <<EOF
3889 > [revsetalias]
3903 > [revsetalias]
3890 > myparents(\$1) = parents(\$1)
3904 > myparents(\$1) = parents(\$1)
3891 > EOF
3905 > EOF
3892 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3906 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3893 2 Parents: 1
3907 2 Parents: 1
3894 1 Parents: 0
3908 1 Parents: 0
3895 0 Parents:
3909 0 Parents:
3896
3910
3897 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3911 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3898 Rev: 2
3912 Rev: 2
3899 Ancestor: 0
3913 Ancestor: 0
3900 Ancestor: 1
3914 Ancestor: 1
3901 Ancestor: 2
3915 Ancestor: 2
3902
3916
3903 Rev: 1
3917 Rev: 1
3904 Ancestor: 0
3918 Ancestor: 0
3905 Ancestor: 1
3919 Ancestor: 1
3906
3920
3907 Rev: 0
3921 Rev: 0
3908 Ancestor: 0
3922 Ancestor: 0
3909
3923
3910 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3924 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3911 2
3925 2
3912
3926
3913 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3927 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3914 2
3928 2
3915
3929
3916 a list template is evaluated for each item of revset/parents
3930 a list template is evaluated for each item of revset/parents
3917
3931
3918 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3932 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3919 2 p: 1:bcc7ff960b8e
3933 2 p: 1:bcc7ff960b8e
3920 1 p: 0:f7769ec2ab97
3934 1 p: 0:f7769ec2ab97
3921 0 p:
3935 0 p:
3922
3936
3923 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3937 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3924 2 p: 1:bcc7ff960b8e -1:000000000000
3938 2 p: 1:bcc7ff960b8e -1:000000000000
3925 1 p: 0:f7769ec2ab97 -1:000000000000
3939 1 p: 0:f7769ec2ab97 -1:000000000000
3926 0 p: -1:000000000000 -1:000000000000
3940 0 p: -1:000000000000 -1:000000000000
3927
3941
3928 therefore, 'revcache' should be recreated for each rev
3942 therefore, 'revcache' should be recreated for each rev
3929
3943
3930 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3944 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3931 2 aa b
3945 2 aa b
3932 p
3946 p
3933 1
3947 1
3934 p a
3948 p a
3935 0 a
3949 0 a
3936 p
3950 p
3937
3951
3938 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3952 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3939 2 aa b
3953 2 aa b
3940 p
3954 p
3941 1
3955 1
3942 p a
3956 p a
3943 0 a
3957 0 a
3944 p
3958 p
3945
3959
3946 a revset item must be evaluated as an integer revision, not an offset from tip
3960 a revset item must be evaluated as an integer revision, not an offset from tip
3947
3961
3948 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3962 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3949 -1:000000000000
3963 -1:000000000000
3950 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3964 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3951 -1:000000000000
3965 -1:000000000000
3952
3966
3953 join() should pick '{rev}' from revset items:
3967 join() should pick '{rev}' from revset items:
3954
3968
3955 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3969 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3956 4, 5
3970 4, 5
3957
3971
3958 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3972 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3959 default. join() should agree with the default formatting:
3973 default. join() should agree with the default formatting:
3960
3974
3961 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3975 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3962 5:13207e5a10d9, 4:bbe44766e73d
3976 5:13207e5a10d9, 4:bbe44766e73d
3963
3977
3964 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3978 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3965 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3979 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3966 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3980 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3967
3981
3968 Test files function
3982 Test files function
3969
3983
3970 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3984 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3971 2
3985 2
3972 a
3986 a
3973 aa
3987 aa
3974 b
3988 b
3975 1
3989 1
3976 a
3990 a
3977 0
3991 0
3978 a
3992 a
3979
3993
3980 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3994 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3981 2
3995 2
3982 aa
3996 aa
3983 1
3997 1
3984
3998
3985 0
3999 0
3986
4000
3987
4001
3988 Test relpath function
4002 Test relpath function
3989
4003
3990 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4004 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
3991 a
4005 a
3992 $ cd ..
4006 $ cd ..
3993 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4007 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
3994 r/a
4008 r/a
3995 $ cd r
4009 $ cd r
3996
4010
3997 Test active bookmark templating
4011 Test active bookmark templating
3998
4012
3999 $ hg book foo
4013 $ hg book foo
4000 $ hg book bar
4014 $ hg book bar
4001 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4015 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4002 2 bar* foo
4016 2 bar* foo
4003 1
4017 1
4004 0
4018 0
4005 $ hg log --template "{rev} {activebookmark}\n"
4019 $ hg log --template "{rev} {activebookmark}\n"
4006 2 bar
4020 2 bar
4007 1
4021 1
4008 0
4022 0
4009 $ hg bookmarks --inactive bar
4023 $ hg bookmarks --inactive bar
4010 $ hg log --template "{rev} {activebookmark}\n"
4024 $ hg log --template "{rev} {activebookmark}\n"
4011 2
4025 2
4012 1
4026 1
4013 0
4027 0
4014 $ hg book -r1 baz
4028 $ hg book -r1 baz
4015 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4029 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4016 2 bar foo
4030 2 bar foo
4017 1 baz
4031 1 baz
4018 0
4032 0
4019 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4033 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4020 2 t
4034 2 t
4021 1 f
4035 1 f
4022 0 f
4036 0 f
4023
4037
4024 Test namespaces dict
4038 Test namespaces dict
4025
4039
4026 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4040 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4027 2
4041 2
4028 bookmarks color=bookmark builtin=True
4042 bookmarks color=bookmark builtin=True
4029 bar,foo
4043 bar,foo
4030 tags color=tag builtin=True
4044 tags color=tag builtin=True
4031 tip
4045 tip
4032 branches color=branch builtin=True
4046 branches color=branch builtin=True
4033 text.{rev}
4047 text.{rev}
4034 revnames color=revname builtin=False
4048 revnames color=revname builtin=False
4035 r2
4049 r2
4036
4050
4037 1
4051 1
4038 bookmarks color=bookmark builtin=True
4052 bookmarks color=bookmark builtin=True
4039 baz
4053 baz
4040 tags color=tag builtin=True
4054 tags color=tag builtin=True
4041
4055
4042 branches color=branch builtin=True
4056 branches color=branch builtin=True
4043 text.{rev}
4057 text.{rev}
4044 revnames color=revname builtin=False
4058 revnames color=revname builtin=False
4045 r1
4059 r1
4046
4060
4047 0
4061 0
4048 bookmarks color=bookmark builtin=True
4062 bookmarks color=bookmark builtin=True
4049
4063
4050 tags color=tag builtin=True
4064 tags color=tag builtin=True
4051
4065
4052 branches color=branch builtin=True
4066 branches color=branch builtin=True
4053 default
4067 default
4054 revnames color=revname builtin=False
4068 revnames color=revname builtin=False
4055 r0
4069 r0
4056
4070
4057 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4071 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4058 bookmarks: bar foo
4072 bookmarks: bar foo
4059 tags: tip
4073 tags: tip
4060 branches: text.{rev}
4074 branches: text.{rev}
4061 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4075 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4062 bookmarks:
4076 bookmarks:
4063 bar
4077 bar
4064 foo
4078 foo
4065 tags:
4079 tags:
4066 tip
4080 tip
4067 branches:
4081 branches:
4068 text.{rev}
4082 text.{rev}
4069 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4083 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4070 bar
4084 bar
4071 foo
4085 foo
4072
4086
4073 Test stringify on sub expressions
4087 Test stringify on sub expressions
4074
4088
4075 $ cd ..
4089 $ cd ..
4076 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4090 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4077 fourth, second, third
4091 fourth, second, third
4078 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4092 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4079 abc
4093 abc
4080
4094
4081 Test splitlines
4095 Test splitlines
4082
4096
4083 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4097 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4084 @ foo Modify, add, remove, rename
4098 @ foo Modify, add, remove, rename
4085 |
4099 |
4086 o foo future
4100 o foo future
4087 |
4101 |
4088 o foo third
4102 o foo third
4089 |
4103 |
4090 o foo second
4104 o foo second
4091
4105
4092 o foo merge
4106 o foo merge
4093 |\
4107 |\
4094 | o foo new head
4108 | o foo new head
4095 | |
4109 | |
4096 o | foo new branch
4110 o | foo new branch
4097 |/
4111 |/
4098 o foo no user, no domain
4112 o foo no user, no domain
4099 |
4113 |
4100 o foo no person
4114 o foo no person
4101 |
4115 |
4102 o foo other 1
4116 o foo other 1
4103 | foo other 2
4117 | foo other 2
4104 | foo
4118 | foo
4105 | foo other 3
4119 | foo other 3
4106 o foo line 1
4120 o foo line 1
4107 foo line 2
4121 foo line 2
4108
4122
4109 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4123 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4110 line 1 line 2
4124 line 1 line 2
4111 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4125 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4112 line 1|line 2
4126 line 1|line 2
4113
4127
4114 Test startswith
4128 Test startswith
4115 $ hg log -Gv -R a --template "{startswith(desc)}"
4129 $ hg log -Gv -R a --template "{startswith(desc)}"
4116 hg: parse error: startswith expects two arguments
4130 hg: parse error: startswith expects two arguments
4117 [255]
4131 [255]
4118
4132
4119 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4133 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4120 @
4134 @
4121 |
4135 |
4122 o
4136 o
4123 |
4137 |
4124 o
4138 o
4125 |
4139 |
4126 o
4140 o
4127
4141
4128 o
4142 o
4129 |\
4143 |\
4130 | o
4144 | o
4131 | |
4145 | |
4132 o |
4146 o |
4133 |/
4147 |/
4134 o
4148 o
4135 |
4149 |
4136 o
4150 o
4137 |
4151 |
4138 o
4152 o
4139 |
4153 |
4140 o line 1
4154 o line 1
4141 line 2
4155 line 2
4142
4156
4143 Test bad template with better error message
4157 Test bad template with better error message
4144
4158
4145 $ hg log -Gv -R a --template '{desc|user()}'
4159 $ hg log -Gv -R a --template '{desc|user()}'
4146 hg: parse error: expected a symbol, got 'func'
4160 hg: parse error: expected a symbol, got 'func'
4147 [255]
4161 [255]
4148
4162
4149 Test word function (including index out of bounds graceful failure)
4163 Test word function (including index out of bounds graceful failure)
4150
4164
4151 $ hg log -Gv -R a --template "{word('1', desc)}"
4165 $ hg log -Gv -R a --template "{word('1', desc)}"
4152 @ add,
4166 @ add,
4153 |
4167 |
4154 o
4168 o
4155 |
4169 |
4156 o
4170 o
4157 |
4171 |
4158 o
4172 o
4159
4173
4160 o
4174 o
4161 |\
4175 |\
4162 | o head
4176 | o head
4163 | |
4177 | |
4164 o | branch
4178 o | branch
4165 |/
4179 |/
4166 o user,
4180 o user,
4167 |
4181 |
4168 o person
4182 o person
4169 |
4183 |
4170 o 1
4184 o 1
4171 |
4185 |
4172 o 1
4186 o 1
4173
4187
4174
4188
4175 Test word third parameter used as splitter
4189 Test word third parameter used as splitter
4176
4190
4177 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4191 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4178 @ M
4192 @ M
4179 |
4193 |
4180 o future
4194 o future
4181 |
4195 |
4182 o third
4196 o third
4183 |
4197 |
4184 o sec
4198 o sec
4185
4199
4186 o merge
4200 o merge
4187 |\
4201 |\
4188 | o new head
4202 | o new head
4189 | |
4203 | |
4190 o | new branch
4204 o | new branch
4191 |/
4205 |/
4192 o n
4206 o n
4193 |
4207 |
4194 o n
4208 o n
4195 |
4209 |
4196 o
4210 o
4197 |
4211 |
4198 o line 1
4212 o line 1
4199 line 2
4213 line 2
4200
4214
4201 Test word error messages for not enough and too many arguments
4215 Test word error messages for not enough and too many arguments
4202
4216
4203 $ hg log -Gv -R a --template "{word('0')}"
4217 $ hg log -Gv -R a --template "{word('0')}"
4204 hg: parse error: word expects two or three arguments, got 1
4218 hg: parse error: word expects two or three arguments, got 1
4205 [255]
4219 [255]
4206
4220
4207 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4221 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4208 hg: parse error: word expects two or three arguments, got 7
4222 hg: parse error: word expects two or three arguments, got 7
4209 [255]
4223 [255]
4210
4224
4211 Test word for integer literal
4225 Test word for integer literal
4212
4226
4213 $ hg log -R a --template "{word(2, desc)}\n" -r0
4227 $ hg log -R a --template "{word(2, desc)}\n" -r0
4214 line
4228 line
4215
4229
4216 Test word for invalid numbers
4230 Test word for invalid numbers
4217
4231
4218 $ hg log -Gv -R a --template "{word('a', desc)}"
4232 $ hg log -Gv -R a --template "{word('a', desc)}"
4219 hg: parse error: word expects an integer index
4233 hg: parse error: word expects an integer index
4220 [255]
4234 [255]
4221
4235
4222 Test word for out of range
4236 Test word for out of range
4223
4237
4224 $ hg log -R a --template "{word(10000, desc)}"
4238 $ hg log -R a --template "{word(10000, desc)}"
4225 $ hg log -R a --template "{word(-10000, desc)}"
4239 $ hg log -R a --template "{word(-10000, desc)}"
4226
4240
4227 Test indent and not adding to empty lines
4241 Test indent and not adding to empty lines
4228
4242
4229 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4243 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4230 -----
4244 -----
4231 > line 1
4245 > line 1
4232 >> line 2
4246 >> line 2
4233 -----
4247 -----
4234 > other 1
4248 > other 1
4235 >> other 2
4249 >> other 2
4236
4250
4237 >> other 3
4251 >> other 3
4238
4252
4239 Test with non-strings like dates
4253 Test with non-strings like dates
4240
4254
4241 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4255 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4242 1200000.00
4256 1200000.00
4243 1300000.00
4257 1300000.00
4244
4258
4245 Test broken string escapes:
4259 Test broken string escapes:
4246
4260
4247 $ hg log -T "bogus\\" -R a
4261 $ hg log -T "bogus\\" -R a
4248 hg: parse error: trailing \ in string
4262 hg: parse error: trailing \ in string
4249 [255]
4263 [255]
4250 $ hg log -T "\\xy" -R a
4264 $ hg log -T "\\xy" -R a
4251 hg: parse error: invalid \x escape
4265 hg: parse error: invalid \x escape
4252 [255]
4266 [255]
4253
4267
4254 json filter should escape HTML tags so that the output can be embedded in hgweb:
4268 json filter should escape HTML tags so that the output can be embedded in hgweb:
4255
4269
4256 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4270 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4257 "\u003cfoo@example.org\u003e"
4271 "\u003cfoo@example.org\u003e"
4258
4272
4259 Templater supports aliases of symbol and func() styles:
4273 Templater supports aliases of symbol and func() styles:
4260
4274
4261 $ hg clone -q a aliases
4275 $ hg clone -q a aliases
4262 $ cd aliases
4276 $ cd aliases
4263 $ cat <<EOF >> .hg/hgrc
4277 $ cat <<EOF >> .hg/hgrc
4264 > [templatealias]
4278 > [templatealias]
4265 > r = rev
4279 > r = rev
4266 > rn = "{r}:{node|short}"
4280 > rn = "{r}:{node|short}"
4267 > status(c, files) = files % "{c} {file}\n"
4281 > status(c, files) = files % "{c} {file}\n"
4268 > utcdate(d) = localdate(d, "UTC")
4282 > utcdate(d) = localdate(d, "UTC")
4269 > EOF
4283 > EOF
4270
4284
4271 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4285 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4272 (template
4286 (template
4273 (symbol 'rn')
4287 (symbol 'rn')
4274 (string ' ')
4288 (string ' ')
4275 (|
4289 (|
4276 (func
4290 (func
4277 (symbol 'utcdate')
4291 (symbol 'utcdate')
4278 (symbol 'date'))
4292 (symbol 'date'))
4279 (symbol 'isodate'))
4293 (symbol 'isodate'))
4280 (string '\n'))
4294 (string '\n'))
4281 * expanded:
4295 * expanded:
4282 (template
4296 (template
4283 (template
4297 (template
4284 (symbol 'rev')
4298 (symbol 'rev')
4285 (string ':')
4299 (string ':')
4286 (|
4300 (|
4287 (symbol 'node')
4301 (symbol 'node')
4288 (symbol 'short')))
4302 (symbol 'short')))
4289 (string ' ')
4303 (string ' ')
4290 (|
4304 (|
4291 (func
4305 (func
4292 (symbol 'localdate')
4306 (symbol 'localdate')
4293 (list
4307 (list
4294 (symbol 'date')
4308 (symbol 'date')
4295 (string 'UTC')))
4309 (string 'UTC')))
4296 (symbol 'isodate'))
4310 (symbol 'isodate'))
4297 (string '\n'))
4311 (string '\n'))
4298 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4312 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4299
4313
4300 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4314 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4301 (template
4315 (template
4302 (func
4316 (func
4303 (symbol 'status')
4317 (symbol 'status')
4304 (list
4318 (list
4305 (string 'A')
4319 (string 'A')
4306 (symbol 'file_adds'))))
4320 (symbol 'file_adds'))))
4307 * expanded:
4321 * expanded:
4308 (template
4322 (template
4309 (%
4323 (%
4310 (symbol 'file_adds')
4324 (symbol 'file_adds')
4311 (template
4325 (template
4312 (string 'A')
4326 (string 'A')
4313 (string ' ')
4327 (string ' ')
4314 (symbol 'file')
4328 (symbol 'file')
4315 (string '\n'))))
4329 (string '\n'))))
4316 A a
4330 A a
4317
4331
4318 A unary function alias can be called as a filter:
4332 A unary function alias can be called as a filter:
4319
4333
4320 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4334 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4321 (template
4335 (template
4322 (|
4336 (|
4323 (|
4337 (|
4324 (symbol 'date')
4338 (symbol 'date')
4325 (symbol 'utcdate'))
4339 (symbol 'utcdate'))
4326 (symbol 'isodate'))
4340 (symbol 'isodate'))
4327 (string '\n'))
4341 (string '\n'))
4328 * expanded:
4342 * expanded:
4329 (template
4343 (template
4330 (|
4344 (|
4331 (func
4345 (func
4332 (symbol 'localdate')
4346 (symbol 'localdate')
4333 (list
4347 (list
4334 (symbol 'date')
4348 (symbol 'date')
4335 (string 'UTC')))
4349 (string 'UTC')))
4336 (symbol 'isodate'))
4350 (symbol 'isodate'))
4337 (string '\n'))
4351 (string '\n'))
4338 1970-01-12 13:46 +0000
4352 1970-01-12 13:46 +0000
4339
4353
4340 Aliases should be applied only to command arguments and templates in hgrc.
4354 Aliases should be applied only to command arguments and templates in hgrc.
4341 Otherwise, our stock styles and web templates could be corrupted:
4355 Otherwise, our stock styles and web templates could be corrupted:
4342
4356
4343 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4357 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4344 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4358 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4345
4359
4346 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4360 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4347 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4361 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4348
4362
4349 $ cat <<EOF > tmpl
4363 $ cat <<EOF > tmpl
4350 > changeset = 'nothing expanded:{rn}\n'
4364 > changeset = 'nothing expanded:{rn}\n'
4351 > EOF
4365 > EOF
4352 $ hg log -r0 --style ./tmpl
4366 $ hg log -r0 --style ./tmpl
4353 nothing expanded:
4367 nothing expanded:
4354
4368
4355 Aliases in formatter:
4369 Aliases in formatter:
4356
4370
4357 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4371 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4358 default 6:d41e714fe50d
4372 default 6:d41e714fe50d
4359 foo 4:bbe44766e73d
4373 foo 4:bbe44766e73d
4360
4374
4361 Aliases should honor HGPLAIN:
4375 Aliases should honor HGPLAIN:
4362
4376
4363 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4377 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4364 nothing expanded:
4378 nothing expanded:
4365 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4379 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4366 0:1e4e1b8f71e0
4380 0:1e4e1b8f71e0
4367
4381
4368 Unparsable alias:
4382 Unparsable alias:
4369
4383
4370 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4384 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4371 (template
4385 (template
4372 (symbol 'bad'))
4386 (symbol 'bad'))
4373 abort: bad definition of template alias "bad": at 2: not a prefix: end
4387 abort: bad definition of template alias "bad": at 2: not a prefix: end
4374 [255]
4388 [255]
4375 $ hg log --config templatealias.bad='x(' -T '{bad}'
4389 $ hg log --config templatealias.bad='x(' -T '{bad}'
4376 abort: bad definition of template alias "bad": at 2: not a prefix: end
4390 abort: bad definition of template alias "bad": at 2: not a prefix: end
4377 [255]
4391 [255]
4378
4392
4379 $ cd ..
4393 $ cd ..
4380
4394
4381 Set up repository for non-ascii encoding tests:
4395 Set up repository for non-ascii encoding tests:
4382
4396
4383 $ hg init nonascii
4397 $ hg init nonascii
4384 $ cd nonascii
4398 $ cd nonascii
4385 $ $PYTHON <<EOF
4399 $ $PYTHON <<EOF
4386 > open('latin1', 'w').write('\xe9')
4400 > open('latin1', 'w').write('\xe9')
4387 > open('utf-8', 'w').write('\xc3\xa9')
4401 > open('utf-8', 'w').write('\xc3\xa9')
4388 > EOF
4402 > EOF
4389 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4403 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4390 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4404 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4391
4405
4392 json filter should try round-trip conversion to utf-8:
4406 json filter should try round-trip conversion to utf-8:
4393
4407
4394 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4408 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4395 "\u00e9"
4409 "\u00e9"
4396 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4410 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4397 "non-ascii branch: \u00e9"
4411 "non-ascii branch: \u00e9"
4398
4412
4399 json filter takes input as utf-8b:
4413 json filter takes input as utf-8b:
4400
4414
4401 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4415 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4402 "\u00e9"
4416 "\u00e9"
4403 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4417 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4404 "\udce9"
4418 "\udce9"
4405
4419
4406 utf8 filter:
4420 utf8 filter:
4407
4421
4408 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4422 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4409 round-trip: c3a9
4423 round-trip: c3a9
4410 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4424 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4411 decoded: c3a9
4425 decoded: c3a9
4412 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4426 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4413 abort: decoding near * (glob)
4427 abort: decoding near * (glob)
4414 [255]
4428 [255]
4415 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4429 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4416 abort: template filter 'utf8' is not compatible with keyword 'rev'
4430 abort: template filter 'utf8' is not compatible with keyword 'rev'
4417 [255]
4431 [255]
4418
4432
4419 pad width:
4433 pad width:
4420
4434
4421 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4435 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4422 \xc3\xa9- (esc)
4436 \xc3\xa9- (esc)
4423
4437
4424 $ cd ..
4438 $ cd ..
4425
4439
4426 Test that template function in extension is registered as expected
4440 Test that template function in extension is registered as expected
4427
4441
4428 $ cd a
4442 $ cd a
4429
4443
4430 $ cat <<EOF > $TESTTMP/customfunc.py
4444 $ cat <<EOF > $TESTTMP/customfunc.py
4431 > from mercurial import registrar
4445 > from mercurial import registrar
4432 >
4446 >
4433 > templatefunc = registrar.templatefunc()
4447 > templatefunc = registrar.templatefunc()
4434 >
4448 >
4435 > @templatefunc('custom()')
4449 > @templatefunc('custom()')
4436 > def custom(context, mapping, args):
4450 > def custom(context, mapping, args):
4437 > return 'custom'
4451 > return 'custom'
4438 > EOF
4452 > EOF
4439 $ cat <<EOF > .hg/hgrc
4453 $ cat <<EOF > .hg/hgrc
4440 > [extensions]
4454 > [extensions]
4441 > customfunc = $TESTTMP/customfunc.py
4455 > customfunc = $TESTTMP/customfunc.py
4442 > EOF
4456 > EOF
4443
4457
4444 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4458 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4445 custom
4459 custom
4446
4460
4447 $ cd ..
4461 $ cd ..
4448
4462
4449 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4463 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4450 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4464 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4451 columns. We don't care about other aspects of the graph rendering here.
4465 columns. We don't care about other aspects of the graph rendering here.
4452
4466
4453 $ hg init graphwidth
4467 $ hg init graphwidth
4454 $ cd graphwidth
4468 $ cd graphwidth
4455
4469
4456 $ wrappabletext="a a a a a a a a a a a a"
4470 $ wrappabletext="a a a a a a a a a a a a"
4457
4471
4458 $ printf "first\n" > file
4472 $ printf "first\n" > file
4459 $ hg add file
4473 $ hg add file
4460 $ hg commit -m "$wrappabletext"
4474 $ hg commit -m "$wrappabletext"
4461
4475
4462 $ printf "first\nsecond\n" > file
4476 $ printf "first\nsecond\n" > file
4463 $ hg commit -m "$wrappabletext"
4477 $ hg commit -m "$wrappabletext"
4464
4478
4465 $ hg checkout 0
4479 $ hg checkout 0
4466 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4480 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4467 $ printf "third\nfirst\n" > file
4481 $ printf "third\nfirst\n" > file
4468 $ hg commit -m "$wrappabletext"
4482 $ hg commit -m "$wrappabletext"
4469 created new head
4483 created new head
4470
4484
4471 $ hg merge
4485 $ hg merge
4472 merging file
4486 merging file
4473 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4487 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4474 (branch merge, don't forget to commit)
4488 (branch merge, don't forget to commit)
4475
4489
4476 $ hg log --graph -T "{graphwidth}"
4490 $ hg log --graph -T "{graphwidth}"
4477 @ 3
4491 @ 3
4478 |
4492 |
4479 | @ 5
4493 | @ 5
4480 |/
4494 |/
4481 o 3
4495 o 3
4482
4496
4483 $ hg commit -m "$wrappabletext"
4497 $ hg commit -m "$wrappabletext"
4484
4498
4485 $ hg log --graph -T "{graphwidth}"
4499 $ hg log --graph -T "{graphwidth}"
4486 @ 5
4500 @ 5
4487 |\
4501 |\
4488 | o 5
4502 | o 5
4489 | |
4503 | |
4490 o | 5
4504 o | 5
4491 |/
4505 |/
4492 o 3
4506 o 3
4493
4507
4494
4508
4495 $ hg checkout 0
4509 $ hg checkout 0
4496 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4510 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4497 $ printf "third\nfirst\nsecond\n" > file
4511 $ printf "third\nfirst\nsecond\n" > file
4498 $ hg commit -m "$wrappabletext"
4512 $ hg commit -m "$wrappabletext"
4499 created new head
4513 created new head
4500
4514
4501 $ hg log --graph -T "{graphwidth}"
4515 $ hg log --graph -T "{graphwidth}"
4502 @ 3
4516 @ 3
4503 |
4517 |
4504 | o 7
4518 | o 7
4505 | |\
4519 | |\
4506 +---o 7
4520 +---o 7
4507 | |
4521 | |
4508 | o 5
4522 | o 5
4509 |/
4523 |/
4510 o 3
4524 o 3
4511
4525
4512
4526
4513 $ hg log --graph -T "{graphwidth}" -r 3
4527 $ hg log --graph -T "{graphwidth}" -r 3
4514 o 5
4528 o 5
4515 |\
4529 |\
4516 ~ ~
4530 ~ ~
4517
4531
4518 $ hg log --graph -T "{graphwidth}" -r 1
4532 $ hg log --graph -T "{graphwidth}" -r 1
4519 o 3
4533 o 3
4520 |
4534 |
4521 ~
4535 ~
4522
4536
4523 $ hg merge
4537 $ hg merge
4524 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4538 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4525 (branch merge, don't forget to commit)
4539 (branch merge, don't forget to commit)
4526 $ hg commit -m "$wrappabletext"
4540 $ hg commit -m "$wrappabletext"
4527
4541
4528 $ printf "seventh\n" >> file
4542 $ printf "seventh\n" >> file
4529 $ hg commit -m "$wrappabletext"
4543 $ hg commit -m "$wrappabletext"
4530
4544
4531 $ hg log --graph -T "{graphwidth}"
4545 $ hg log --graph -T "{graphwidth}"
4532 @ 3
4546 @ 3
4533 |
4547 |
4534 o 5
4548 o 5
4535 |\
4549 |\
4536 | o 5
4550 | o 5
4537 | |
4551 | |
4538 o | 7
4552 o | 7
4539 |\ \
4553 |\ \
4540 | o | 7
4554 | o | 7
4541 | |/
4555 | |/
4542 o / 5
4556 o / 5
4543 |/
4557 |/
4544 o 3
4558 o 3
4545
4559
4546
4560
4547 The point of graphwidth is to allow wrapping that accounts for the space taken
4561 The point of graphwidth is to allow wrapping that accounts for the space taken
4548 by the graph.
4562 by the graph.
4549
4563
4550 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4564 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4551 @ a a a a
4565 @ a a a a
4552 | a a a a
4566 | a a a a
4553 | a a a a
4567 | a a a a
4554 o a a a
4568 o a a a
4555 |\ a a a
4569 |\ a a a
4556 | | a a a
4570 | | a a a
4557 | | a a a
4571 | | a a a
4558 | o a a a
4572 | o a a a
4559 | | a a a
4573 | | a a a
4560 | | a a a
4574 | | a a a
4561 | | a a a
4575 | | a a a
4562 o | a a
4576 o | a a
4563 |\ \ a a
4577 |\ \ a a
4564 | | | a a
4578 | | | a a
4565 | | | a a
4579 | | | a a
4566 | | | a a
4580 | | | a a
4567 | | | a a
4581 | | | a a
4568 | o | a a
4582 | o | a a
4569 | |/ a a
4583 | |/ a a
4570 | | a a
4584 | | a a
4571 | | a a
4585 | | a a
4572 | | a a
4586 | | a a
4573 | | a a
4587 | | a a
4574 o | a a a
4588 o | a a a
4575 |/ a a a
4589 |/ a a a
4576 | a a a
4590 | a a a
4577 | a a a
4591 | a a a
4578 o a a a a
4592 o a a a a
4579 a a a a
4593 a a a a
4580 a a a a
4594 a a a a
4581
4595
4582 Something tricky happens when there are elided nodes; the next drawn row of
4596 Something tricky happens when there are elided nodes; the next drawn row of
4583 edges can be more than one column wider, but the graph width only increases by
4597 edges can be more than one column wider, but the graph width only increases by
4584 one column. The remaining columns are added in between the nodes.
4598 one column. The remaining columns are added in between the nodes.
4585
4599
4586 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4600 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4587 o 5
4601 o 5
4588 |\
4602 |\
4589 | \
4603 | \
4590 | :\
4604 | :\
4591 o : : 7
4605 o : : 7
4592 :/ /
4606 :/ /
4593 : o 5
4607 : o 5
4594 :/
4608 :/
4595 o 3
4609 o 3
4596
4610
4597
4611
4598 $ cd ..
4612 $ cd ..
4599
4613
General Comments 0
You need to be logged in to leave comments. Login now