##// END OF EJS Templates
templatekw: add new-style template expansion to {manifest}...
Yuya Nishihara -
r34331:89aec183 default
parent child Browse files
Show More
@@ -1,840 +1,872 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
42 self.gen = gen
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 @util.propertycache
46 @util.propertycache
47 def gen(self):
47 def gen(self):
48 return self._defaultgen()
48 return self._defaultgen()
49 def _defaultgen(self):
49 def _defaultgen(self):
50 """Generator to stringify this as {join(self, ' ')}"""
50 """Generator to stringify this as {join(self, ' ')}"""
51 for i, x in enumerate(self._values):
51 for i, x in enumerate(self._values):
52 if i > 0:
52 if i > 0:
53 yield ' '
53 yield ' '
54 yield self.joinfmt(x)
54 yield self.joinfmt(x)
55 def itermaps(self):
55 def itermaps(self):
56 makemap = self._makemap
56 makemap = self._makemap
57 for x in self._values:
57 for x in self._values:
58 yield makemap(x)
58 yield makemap(x)
59 def __contains__(self, x):
59 def __contains__(self, x):
60 return x in self._values
60 return x in self._values
61 def __getitem__(self, key):
61 def __getitem__(self, key):
62 return self._values[key]
62 return self._values[key]
63 def __len__(self):
63 def __len__(self):
64 return len(self._values)
64 return len(self._values)
65 def __iter__(self):
65 def __iter__(self):
66 return iter(self._values)
66 return iter(self._values)
67 def __getattr__(self, name):
67 def __getattr__(self, name):
68 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
68 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
69 'keys', 'values'):
69 'keys', 'values'):
70 raise AttributeError(name)
70 raise AttributeError(name)
71 return getattr(self._values, name)
71 return getattr(self._values, name)
72
72
73 class _mappable(object):
74 """Wrapper for non-list/dict object to support map operation
75
76 This class allows us to handle both:
77 - "{manifest}"
78 - "{manifest % '{rev}:{node}'}"
79
80 Unlike a _hybrid, this does not simulate the behavior of the underling
81 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
82 """
83
84 def __init__(self, gen, value, makemap):
85 self.gen = gen
86 self._value = value # may be generator of strings
87 self._makemap = makemap
88
89 def tomap(self):
90 return self._makemap()
91
92 def itermaps(self):
93 yield self.tomap()
94
73 def hybriddict(data, key='key', value='value', fmt='%s=%s', gen=None):
95 def hybriddict(data, key='key', value='value', fmt='%s=%s', gen=None):
74 """Wrap data to support both dict-like and string-like operations"""
96 """Wrap data to support both dict-like and string-like operations"""
75 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
97 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
76 lambda k: fmt % (k, data[k]))
98 lambda k: fmt % (k, data[k]))
77
99
78 def hybridlist(data, name, fmt='%s', gen=None):
100 def hybridlist(data, name, fmt='%s', gen=None):
79 """Wrap data to support both list-like and string-like operations"""
101 """Wrap data to support both list-like and string-like operations"""
80 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
102 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
81
103
82 def unwraphybrid(thing):
104 def unwraphybrid(thing):
83 """Return an object which can be stringified possibly by using a legacy
105 """Return an object which can be stringified possibly by using a legacy
84 template"""
106 template"""
85 if not util.safehasattr(thing, 'gen'):
107 if not util.safehasattr(thing, 'gen'):
86 return thing
108 return thing
87 return thing.gen
109 return thing.gen
88
110
111 def unwrapvalue(thing):
112 """Move the inner value object out of the wrapper"""
113 if not util.safehasattr(thing, '_value'):
114 return thing
115 return thing._value
116
89 def showdict(name, data, mapping, plural=None, key='key', value='value',
117 def showdict(name, data, mapping, plural=None, key='key', value='value',
90 fmt='%s=%s', separator=' '):
118 fmt='%s=%s', separator=' '):
91 c = [{key: k, value: v} for k, v in data.iteritems()]
119 c = [{key: k, value: v} for k, v in data.iteritems()]
92 f = _showlist(name, c, mapping, plural, separator)
120 f = _showlist(name, c, mapping, plural, separator)
93 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
121 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
94
122
95 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
123 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
96 if not element:
124 if not element:
97 element = name
125 element = name
98 f = _showlist(name, values, mapping, plural, separator)
126 f = _showlist(name, values, mapping, plural, separator)
99 return hybridlist(values, name=element, gen=f)
127 return hybridlist(values, name=element, gen=f)
100
128
101 def _showlist(name, values, mapping, plural=None, separator=' '):
129 def _showlist(name, values, mapping, plural=None, separator=' '):
102 '''expand set of values.
130 '''expand set of values.
103 name is name of key in template map.
131 name is name of key in template map.
104 values is list of strings or dicts.
132 values is list of strings or dicts.
105 plural is plural of name, if not simply name + 's'.
133 plural is plural of name, if not simply name + 's'.
106 separator is used to join values as a string
134 separator is used to join values as a string
107
135
108 expansion works like this, given name 'foo'.
136 expansion works like this, given name 'foo'.
109
137
110 if values is empty, expand 'no_foos'.
138 if values is empty, expand 'no_foos'.
111
139
112 if 'foo' not in template map, return values as a string,
140 if 'foo' not in template map, return values as a string,
113 joined by 'separator'.
141 joined by 'separator'.
114
142
115 expand 'start_foos'.
143 expand 'start_foos'.
116
144
117 for each value, expand 'foo'. if 'last_foo' in template
145 for each value, expand 'foo'. if 'last_foo' in template
118 map, expand it instead of 'foo' for last key.
146 map, expand it instead of 'foo' for last key.
119
147
120 expand 'end_foos'.
148 expand 'end_foos'.
121 '''
149 '''
122 templ = mapping['templ']
150 templ = mapping['templ']
123 strmapping = pycompat.strkwargs(mapping)
151 strmapping = pycompat.strkwargs(mapping)
124 if not plural:
152 if not plural:
125 plural = name + 's'
153 plural = name + 's'
126 if not values:
154 if not values:
127 noname = 'no_' + plural
155 noname = 'no_' + plural
128 if noname in templ:
156 if noname in templ:
129 yield templ(noname, **strmapping)
157 yield templ(noname, **strmapping)
130 return
158 return
131 if name not in templ:
159 if name not in templ:
132 if isinstance(values[0], bytes):
160 if isinstance(values[0], bytes):
133 yield separator.join(values)
161 yield separator.join(values)
134 else:
162 else:
135 for v in values:
163 for v in values:
136 yield dict(v, **strmapping)
164 yield dict(v, **strmapping)
137 return
165 return
138 startname = 'start_' + plural
166 startname = 'start_' + plural
139 if startname in templ:
167 if startname in templ:
140 yield templ(startname, **strmapping)
168 yield templ(startname, **strmapping)
141 vmapping = mapping.copy()
169 vmapping = mapping.copy()
142 def one(v, tag=name):
170 def one(v, tag=name):
143 try:
171 try:
144 vmapping.update(v)
172 vmapping.update(v)
145 except (AttributeError, ValueError):
173 except (AttributeError, ValueError):
146 try:
174 try:
147 for a, b in v:
175 for a, b in v:
148 vmapping[a] = b
176 vmapping[a] = b
149 except ValueError:
177 except ValueError:
150 vmapping[name] = v
178 vmapping[name] = v
151 return templ(tag, **pycompat.strkwargs(vmapping))
179 return templ(tag, **pycompat.strkwargs(vmapping))
152 lastname = 'last_' + name
180 lastname = 'last_' + name
153 if lastname in templ:
181 if lastname in templ:
154 last = values.pop()
182 last = values.pop()
155 else:
183 else:
156 last = None
184 last = None
157 for v in values:
185 for v in values:
158 yield one(v)
186 yield one(v)
159 if last is not None:
187 if last is not None:
160 yield one(last, tag=lastname)
188 yield one(last, tag=lastname)
161 endname = 'end_' + plural
189 endname = 'end_' + plural
162 if endname in templ:
190 if endname in templ:
163 yield templ(endname, **strmapping)
191 yield templ(endname, **strmapping)
164
192
165 def getfiles(repo, ctx, revcache):
193 def getfiles(repo, ctx, revcache):
166 if 'files' not in revcache:
194 if 'files' not in revcache:
167 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
195 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
168 return revcache['files']
196 return revcache['files']
169
197
170 def getlatesttags(repo, ctx, cache, pattern=None):
198 def getlatesttags(repo, ctx, cache, pattern=None):
171 '''return date, distance and name for the latest tag of rev'''
199 '''return date, distance and name for the latest tag of rev'''
172
200
173 cachename = 'latesttags'
201 cachename = 'latesttags'
174 if pattern is not None:
202 if pattern is not None:
175 cachename += '-' + pattern
203 cachename += '-' + pattern
176 match = util.stringmatcher(pattern)[2]
204 match = util.stringmatcher(pattern)[2]
177 else:
205 else:
178 match = util.always
206 match = util.always
179
207
180 if cachename not in cache:
208 if cachename not in cache:
181 # Cache mapping from rev to a tuple with tag date, tag
209 # Cache mapping from rev to a tuple with tag date, tag
182 # distance and tag name
210 # distance and tag name
183 cache[cachename] = {-1: (0, 0, ['null'])}
211 cache[cachename] = {-1: (0, 0, ['null'])}
184 latesttags = cache[cachename]
212 latesttags = cache[cachename]
185
213
186 rev = ctx.rev()
214 rev = ctx.rev()
187 todo = [rev]
215 todo = [rev]
188 while todo:
216 while todo:
189 rev = todo.pop()
217 rev = todo.pop()
190 if rev in latesttags:
218 if rev in latesttags:
191 continue
219 continue
192 ctx = repo[rev]
220 ctx = repo[rev]
193 tags = [t for t in ctx.tags()
221 tags = [t for t in ctx.tags()
194 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
222 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
195 and match(t))]
223 and match(t))]
196 if tags:
224 if tags:
197 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
225 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
198 continue
226 continue
199 try:
227 try:
200 ptags = [latesttags[p.rev()] for p in ctx.parents()]
228 ptags = [latesttags[p.rev()] for p in ctx.parents()]
201 if len(ptags) > 1:
229 if len(ptags) > 1:
202 if ptags[0][2] == ptags[1][2]:
230 if ptags[0][2] == ptags[1][2]:
203 # The tuples are laid out so the right one can be found by
231 # The tuples are laid out so the right one can be found by
204 # comparison in this case.
232 # comparison in this case.
205 pdate, pdist, ptag = max(ptags)
233 pdate, pdist, ptag = max(ptags)
206 else:
234 else:
207 def key(x):
235 def key(x):
208 changessincetag = len(repo.revs('only(%d, %s)',
236 changessincetag = len(repo.revs('only(%d, %s)',
209 ctx.rev(), x[2][0]))
237 ctx.rev(), x[2][0]))
210 # Smallest number of changes since tag wins. Date is
238 # Smallest number of changes since tag wins. Date is
211 # used as tiebreaker.
239 # used as tiebreaker.
212 return [-changessincetag, x[0]]
240 return [-changessincetag, x[0]]
213 pdate, pdist, ptag = max(ptags, key=key)
241 pdate, pdist, ptag = max(ptags, key=key)
214 else:
242 else:
215 pdate, pdist, ptag = ptags[0]
243 pdate, pdist, ptag = ptags[0]
216 except KeyError:
244 except KeyError:
217 # Cache miss - recurse
245 # Cache miss - recurse
218 todo.append(rev)
246 todo.append(rev)
219 todo.extend(p.rev() for p in ctx.parents())
247 todo.extend(p.rev() for p in ctx.parents())
220 continue
248 continue
221 latesttags[rev] = pdate, pdist + 1, ptag
249 latesttags[rev] = pdate, pdist + 1, ptag
222 return latesttags[rev]
250 return latesttags[rev]
223
251
224 def getrenamedfn(repo, endrev=None):
252 def getrenamedfn(repo, endrev=None):
225 rcache = {}
253 rcache = {}
226 if endrev is None:
254 if endrev is None:
227 endrev = len(repo)
255 endrev = len(repo)
228
256
229 def getrenamed(fn, rev):
257 def getrenamed(fn, rev):
230 '''looks up all renames for a file (up to endrev) the first
258 '''looks up all renames for a file (up to endrev) the first
231 time the file is given. It indexes on the changerev and only
259 time the file is given. It indexes on the changerev and only
232 parses the manifest if linkrev != changerev.
260 parses the manifest if linkrev != changerev.
233 Returns rename info for fn at changerev rev.'''
261 Returns rename info for fn at changerev rev.'''
234 if fn not in rcache:
262 if fn not in rcache:
235 rcache[fn] = {}
263 rcache[fn] = {}
236 fl = repo.file(fn)
264 fl = repo.file(fn)
237 for i in fl:
265 for i in fl:
238 lr = fl.linkrev(i)
266 lr = fl.linkrev(i)
239 renamed = fl.renamed(fl.node(i))
267 renamed = fl.renamed(fl.node(i))
240 rcache[fn][lr] = renamed
268 rcache[fn][lr] = renamed
241 if lr >= endrev:
269 if lr >= endrev:
242 break
270 break
243 if rev in rcache[fn]:
271 if rev in rcache[fn]:
244 return rcache[fn][rev]
272 return rcache[fn][rev]
245
273
246 # If linkrev != rev (i.e. rev not found in rcache) fallback to
274 # If linkrev != rev (i.e. rev not found in rcache) fallback to
247 # filectx logic.
275 # filectx logic.
248 try:
276 try:
249 return repo[rev][fn].renamed()
277 return repo[rev][fn].renamed()
250 except error.LookupError:
278 except error.LookupError:
251 return None
279 return None
252
280
253 return getrenamed
281 return getrenamed
254
282
255 # default templates internally used for rendering of lists
283 # default templates internally used for rendering of lists
256 defaulttempl = {
284 defaulttempl = {
257 'parent': '{rev}:{node|formatnode} ',
285 'parent': '{rev}:{node|formatnode} ',
258 'manifest': '{rev}:{node|formatnode}',
286 'manifest': '{rev}:{node|formatnode}',
259 'file_copy': '{name} ({source})',
287 'file_copy': '{name} ({source})',
260 'envvar': '{key}={value}',
288 'envvar': '{key}={value}',
261 'extra': '{key}={value|stringescape}'
289 'extra': '{key}={value|stringescape}'
262 }
290 }
263 # filecopy is preserved for compatibility reasons
291 # filecopy is preserved for compatibility reasons
264 defaulttempl['filecopy'] = defaulttempl['file_copy']
292 defaulttempl['filecopy'] = defaulttempl['file_copy']
265
293
266 # keywords are callables like:
294 # keywords are callables like:
267 # fn(repo, ctx, templ, cache, revcache, **args)
295 # fn(repo, ctx, templ, cache, revcache, **args)
268 # with:
296 # with:
269 # repo - current repository instance
297 # repo - current repository instance
270 # ctx - the changectx being displayed
298 # ctx - the changectx being displayed
271 # templ - the templater instance
299 # templ - the templater instance
272 # cache - a cache dictionary for the whole templater run
300 # cache - a cache dictionary for the whole templater run
273 # revcache - a cache dictionary for the current revision
301 # revcache - a cache dictionary for the current revision
274 keywords = {}
302 keywords = {}
275
303
276 templatekeyword = registrar.templatekeyword(keywords)
304 templatekeyword = registrar.templatekeyword(keywords)
277
305
278 @templatekeyword('author')
306 @templatekeyword('author')
279 def showauthor(repo, ctx, templ, **args):
307 def showauthor(repo, ctx, templ, **args):
280 """String. The unmodified author of the changeset."""
308 """String. The unmodified author of the changeset."""
281 return ctx.user()
309 return ctx.user()
282
310
283 @templatekeyword('bisect')
311 @templatekeyword('bisect')
284 def showbisect(repo, ctx, templ, **args):
312 def showbisect(repo, ctx, templ, **args):
285 """String. The changeset bisection status."""
313 """String. The changeset bisection status."""
286 return hbisect.label(repo, ctx.node())
314 return hbisect.label(repo, ctx.node())
287
315
288 @templatekeyword('branch')
316 @templatekeyword('branch')
289 def showbranch(**args):
317 def showbranch(**args):
290 """String. The name of the branch on which the changeset was
318 """String. The name of the branch on which the changeset was
291 committed.
319 committed.
292 """
320 """
293 return args[r'ctx'].branch()
321 return args[r'ctx'].branch()
294
322
295 @templatekeyword('branches')
323 @templatekeyword('branches')
296 def showbranches(**args):
324 def showbranches(**args):
297 """List of strings. The name of the branch on which the
325 """List of strings. The name of the branch on which the
298 changeset was committed. Will be empty if the branch name was
326 changeset was committed. Will be empty if the branch name was
299 default. (DEPRECATED)
327 default. (DEPRECATED)
300 """
328 """
301 args = pycompat.byteskwargs(args)
329 args = pycompat.byteskwargs(args)
302 branch = args['ctx'].branch()
330 branch = args['ctx'].branch()
303 if branch != 'default':
331 if branch != 'default':
304 return showlist('branch', [branch], args, plural='branches')
332 return showlist('branch', [branch], args, plural='branches')
305 return showlist('branch', [], args, plural='branches')
333 return showlist('branch', [], args, plural='branches')
306
334
307 @templatekeyword('bookmarks')
335 @templatekeyword('bookmarks')
308 def showbookmarks(**args):
336 def showbookmarks(**args):
309 """List of strings. Any bookmarks associated with the
337 """List of strings. Any bookmarks associated with the
310 changeset. Also sets 'active', the name of the active bookmark.
338 changeset. Also sets 'active', the name of the active bookmark.
311 """
339 """
312 args = pycompat.byteskwargs(args)
340 args = pycompat.byteskwargs(args)
313 repo = args['ctx']._repo
341 repo = args['ctx']._repo
314 bookmarks = args['ctx'].bookmarks()
342 bookmarks = args['ctx'].bookmarks()
315 active = repo._activebookmark
343 active = repo._activebookmark
316 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
344 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
317 f = _showlist('bookmark', bookmarks, args)
345 f = _showlist('bookmark', bookmarks, args)
318 return _hybrid(f, bookmarks, makemap, pycompat.identity)
346 return _hybrid(f, bookmarks, makemap, pycompat.identity)
319
347
320 @templatekeyword('children')
348 @templatekeyword('children')
321 def showchildren(**args):
349 def showchildren(**args):
322 """List of strings. The children of the changeset."""
350 """List of strings. The children of the changeset."""
323 args = pycompat.byteskwargs(args)
351 args = pycompat.byteskwargs(args)
324 ctx = args['ctx']
352 ctx = args['ctx']
325 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
353 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
326 return showlist('children', childrevs, args, element='child')
354 return showlist('children', childrevs, args, element='child')
327
355
328 # Deprecated, but kept alive for help generation a purpose.
356 # Deprecated, but kept alive for help generation a purpose.
329 @templatekeyword('currentbookmark')
357 @templatekeyword('currentbookmark')
330 def showcurrentbookmark(**args):
358 def showcurrentbookmark(**args):
331 """String. The active bookmark, if it is
359 """String. The active bookmark, if it is
332 associated with the changeset (DEPRECATED)"""
360 associated with the changeset (DEPRECATED)"""
333 return showactivebookmark(**args)
361 return showactivebookmark(**args)
334
362
335 @templatekeyword('activebookmark')
363 @templatekeyword('activebookmark')
336 def showactivebookmark(**args):
364 def showactivebookmark(**args):
337 """String. The active bookmark, if it is
365 """String. The active bookmark, if it is
338 associated with the changeset"""
366 associated with the changeset"""
339 active = args[r'repo']._activebookmark
367 active = args[r'repo']._activebookmark
340 if active and active in args[r'ctx'].bookmarks():
368 if active and active in args[r'ctx'].bookmarks():
341 return active
369 return active
342 return ''
370 return ''
343
371
344 @templatekeyword('date')
372 @templatekeyword('date')
345 def showdate(repo, ctx, templ, **args):
373 def showdate(repo, ctx, templ, **args):
346 """Date information. The date when the changeset was committed."""
374 """Date information. The date when the changeset was committed."""
347 return ctx.date()
375 return ctx.date()
348
376
349 @templatekeyword('desc')
377 @templatekeyword('desc')
350 def showdescription(repo, ctx, templ, **args):
378 def showdescription(repo, ctx, templ, **args):
351 """String. The text of the changeset description."""
379 """String. The text of the changeset description."""
352 s = ctx.description()
380 s = ctx.description()
353 if isinstance(s, encoding.localstr):
381 if isinstance(s, encoding.localstr):
354 # try hard to preserve utf-8 bytes
382 # try hard to preserve utf-8 bytes
355 return encoding.tolocal(encoding.fromlocal(s).strip())
383 return encoding.tolocal(encoding.fromlocal(s).strip())
356 else:
384 else:
357 return s.strip()
385 return s.strip()
358
386
359 @templatekeyword('diffstat')
387 @templatekeyword('diffstat')
360 def showdiffstat(repo, ctx, templ, **args):
388 def showdiffstat(repo, ctx, templ, **args):
361 """String. Statistics of changes with the following format:
389 """String. Statistics of changes with the following format:
362 "modified files: +added/-removed lines"
390 "modified files: +added/-removed lines"
363 """
391 """
364 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
392 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
365 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
393 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
366 return '%s: +%s/-%s' % (len(stats), adds, removes)
394 return '%s: +%s/-%s' % (len(stats), adds, removes)
367
395
368 @templatekeyword('envvars')
396 @templatekeyword('envvars')
369 def showenvvars(repo, **args):
397 def showenvvars(repo, **args):
370 """A dictionary of environment variables. (EXPERIMENTAL)"""
398 """A dictionary of environment variables. (EXPERIMENTAL)"""
371 args = pycompat.byteskwargs(args)
399 args = pycompat.byteskwargs(args)
372 env = repo.ui.exportableenviron()
400 env = repo.ui.exportableenviron()
373 env = util.sortdict((k, env[k]) for k in sorted(env))
401 env = util.sortdict((k, env[k]) for k in sorted(env))
374 return showdict('envvar', env, args, plural='envvars')
402 return showdict('envvar', env, args, plural='envvars')
375
403
376 @templatekeyword('extras')
404 @templatekeyword('extras')
377 def showextras(**args):
405 def showextras(**args):
378 """List of dicts with key, value entries of the 'extras'
406 """List of dicts with key, value entries of the 'extras'
379 field of this changeset."""
407 field of this changeset."""
380 args = pycompat.byteskwargs(args)
408 args = pycompat.byteskwargs(args)
381 extras = args['ctx'].extra()
409 extras = args['ctx'].extra()
382 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
410 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
383 makemap = lambda k: {'key': k, 'value': extras[k]}
411 makemap = lambda k: {'key': k, 'value': extras[k]}
384 c = [makemap(k) for k in extras]
412 c = [makemap(k) for k in extras]
385 f = _showlist('extra', c, args, plural='extras')
413 f = _showlist('extra', c, args, plural='extras')
386 return _hybrid(f, extras, makemap,
414 return _hybrid(f, extras, makemap,
387 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
415 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
388
416
389 @templatekeyword('file_adds')
417 @templatekeyword('file_adds')
390 def showfileadds(**args):
418 def showfileadds(**args):
391 """List of strings. Files added by this changeset."""
419 """List of strings. Files added by this changeset."""
392 args = pycompat.byteskwargs(args)
420 args = pycompat.byteskwargs(args)
393 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
421 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
394 return showlist('file_add', getfiles(repo, ctx, revcache)[1], args,
422 return showlist('file_add', getfiles(repo, ctx, revcache)[1], args,
395 element='file')
423 element='file')
396
424
397 @templatekeyword('file_copies')
425 @templatekeyword('file_copies')
398 def showfilecopies(**args):
426 def showfilecopies(**args):
399 """List of strings. Files copied in this changeset with
427 """List of strings. Files copied in this changeset with
400 their sources.
428 their sources.
401 """
429 """
402 args = pycompat.byteskwargs(args)
430 args = pycompat.byteskwargs(args)
403 cache, ctx = args['cache'], args['ctx']
431 cache, ctx = args['cache'], args['ctx']
404 copies = args['revcache'].get('copies')
432 copies = args['revcache'].get('copies')
405 if copies is None:
433 if copies is None:
406 if 'getrenamed' not in cache:
434 if 'getrenamed' not in cache:
407 cache['getrenamed'] = getrenamedfn(args['repo'])
435 cache['getrenamed'] = getrenamedfn(args['repo'])
408 copies = []
436 copies = []
409 getrenamed = cache['getrenamed']
437 getrenamed = cache['getrenamed']
410 for fn in ctx.files():
438 for fn in ctx.files():
411 rename = getrenamed(fn, ctx.rev())
439 rename = getrenamed(fn, ctx.rev())
412 if rename:
440 if rename:
413 copies.append((fn, rename[0]))
441 copies.append((fn, rename[0]))
414
442
415 copies = util.sortdict(copies)
443 copies = util.sortdict(copies)
416 return showdict('file_copy', copies, args, plural='file_copies',
444 return showdict('file_copy', copies, args, plural='file_copies',
417 key='name', value='source', fmt='%s (%s)')
445 key='name', value='source', fmt='%s (%s)')
418
446
419 # showfilecopiesswitch() displays file copies only if copy records are
447 # showfilecopiesswitch() displays file copies only if copy records are
420 # provided before calling the templater, usually with a --copies
448 # provided before calling the templater, usually with a --copies
421 # command line switch.
449 # command line switch.
422 @templatekeyword('file_copies_switch')
450 @templatekeyword('file_copies_switch')
423 def showfilecopiesswitch(**args):
451 def showfilecopiesswitch(**args):
424 """List of strings. Like "file_copies" but displayed
452 """List of strings. Like "file_copies" but displayed
425 only if the --copied switch is set.
453 only if the --copied switch is set.
426 """
454 """
427 args = pycompat.byteskwargs(args)
455 args = pycompat.byteskwargs(args)
428 copies = args['revcache'].get('copies') or []
456 copies = args['revcache'].get('copies') or []
429 copies = util.sortdict(copies)
457 copies = util.sortdict(copies)
430 return showdict('file_copy', copies, args, plural='file_copies',
458 return showdict('file_copy', copies, args, plural='file_copies',
431 key='name', value='source', fmt='%s (%s)')
459 key='name', value='source', fmt='%s (%s)')
432
460
433 @templatekeyword('file_dels')
461 @templatekeyword('file_dels')
434 def showfiledels(**args):
462 def showfiledels(**args):
435 """List of strings. Files removed by this changeset."""
463 """List of strings. Files removed by this changeset."""
436 args = pycompat.byteskwargs(args)
464 args = pycompat.byteskwargs(args)
437 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
465 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
438 return showlist('file_del', getfiles(repo, ctx, revcache)[2], args,
466 return showlist('file_del', getfiles(repo, ctx, revcache)[2], args,
439 element='file')
467 element='file')
440
468
441 @templatekeyword('file_mods')
469 @templatekeyword('file_mods')
442 def showfilemods(**args):
470 def showfilemods(**args):
443 """List of strings. Files modified by this changeset."""
471 """List of strings. Files modified by this changeset."""
444 args = pycompat.byteskwargs(args)
472 args = pycompat.byteskwargs(args)
445 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
473 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
446 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], args,
474 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], args,
447 element='file')
475 element='file')
448
476
449 @templatekeyword('files')
477 @templatekeyword('files')
450 def showfiles(**args):
478 def showfiles(**args):
451 """List of strings. All files modified, added, or removed by this
479 """List of strings. All files modified, added, or removed by this
452 changeset.
480 changeset.
453 """
481 """
454 args = pycompat.byteskwargs(args)
482 args = pycompat.byteskwargs(args)
455 return showlist('file', args['ctx'].files(), args)
483 return showlist('file', args['ctx'].files(), args)
456
484
457 @templatekeyword('graphnode')
485 @templatekeyword('graphnode')
458 def showgraphnode(repo, ctx, **args):
486 def showgraphnode(repo, ctx, **args):
459 """String. The character representing the changeset node in
487 """String. The character representing the changeset node in
460 an ASCII revision graph"""
488 an ASCII revision graph"""
461 wpnodes = repo.dirstate.parents()
489 wpnodes = repo.dirstate.parents()
462 if wpnodes[1] == nullid:
490 if wpnodes[1] == nullid:
463 wpnodes = wpnodes[:1]
491 wpnodes = wpnodes[:1]
464 if ctx.node() in wpnodes:
492 if ctx.node() in wpnodes:
465 return '@'
493 return '@'
466 elif ctx.obsolete():
494 elif ctx.obsolete():
467 return 'x'
495 return 'x'
468 elif ctx.closesbranch():
496 elif ctx.closesbranch():
469 return '_'
497 return '_'
470 else:
498 else:
471 return 'o'
499 return 'o'
472
500
473 @templatekeyword('graphwidth')
501 @templatekeyword('graphwidth')
474 def showgraphwidth(repo, ctx, templ, **args):
502 def showgraphwidth(repo, ctx, templ, **args):
475 """Integer. The width of the graph drawn by 'log --graph' or zero."""
503 """Integer. The width of the graph drawn by 'log --graph' or zero."""
476 # The value args['graphwidth'] will be this function, so we use an internal
504 # The value args['graphwidth'] will be this function, so we use an internal
477 # name to pass the value through props into this function.
505 # name to pass the value through props into this function.
478 return args.get('_graphwidth', 0)
506 return args.get('_graphwidth', 0)
479
507
480 @templatekeyword('index')
508 @templatekeyword('index')
481 def showindex(**args):
509 def showindex(**args):
482 """Integer. The current iteration of the loop. (0 indexed)"""
510 """Integer. The current iteration of the loop. (0 indexed)"""
483 # just hosts documentation; should be overridden by template mapping
511 # just hosts documentation; should be overridden by template mapping
484 raise error.Abort(_("can't use index in this context"))
512 raise error.Abort(_("can't use index in this context"))
485
513
486 @templatekeyword('latesttag')
514 @templatekeyword('latesttag')
487 def showlatesttag(**args):
515 def showlatesttag(**args):
488 """List of strings. The global tags on the most recent globally
516 """List of strings. The global tags on the most recent globally
489 tagged ancestor of this changeset. If no such tags exist, the list
517 tagged ancestor of this changeset. If no such tags exist, the list
490 consists of the single string "null".
518 consists of the single string "null".
491 """
519 """
492 return showlatesttags(None, **args)
520 return showlatesttags(None, **args)
493
521
494 def showlatesttags(pattern, **args):
522 def showlatesttags(pattern, **args):
495 """helper method for the latesttag keyword and function"""
523 """helper method for the latesttag keyword and function"""
496 args = pycompat.byteskwargs(args)
524 args = pycompat.byteskwargs(args)
497 repo, ctx = args['repo'], args['ctx']
525 repo, ctx = args['repo'], args['ctx']
498 cache = args['cache']
526 cache = args['cache']
499 latesttags = getlatesttags(repo, ctx, cache, pattern)
527 latesttags = getlatesttags(repo, ctx, cache, pattern)
500
528
501 # latesttag[0] is an implementation detail for sorting csets on different
529 # latesttag[0] is an implementation detail for sorting csets on different
502 # branches in a stable manner- it is the date the tagged cset was created,
530 # branches in a stable manner- it is the date the tagged cset was created,
503 # not the date the tag was created. Therefore it isn't made visible here.
531 # not the date the tag was created. Therefore it isn't made visible here.
504 makemap = lambda v: {
532 makemap = lambda v: {
505 'changes': _showchangessincetag,
533 'changes': _showchangessincetag,
506 'distance': latesttags[1],
534 'distance': latesttags[1],
507 'latesttag': v, # BC with {latesttag % '{latesttag}'}
535 'latesttag': v, # BC with {latesttag % '{latesttag}'}
508 'tag': v
536 'tag': v
509 }
537 }
510
538
511 tags = latesttags[2]
539 tags = latesttags[2]
512 f = _showlist('latesttag', tags, args, separator=':')
540 f = _showlist('latesttag', tags, args, separator=':')
513 return _hybrid(f, tags, makemap, pycompat.identity)
541 return _hybrid(f, tags, makemap, pycompat.identity)
514
542
515 @templatekeyword('latesttagdistance')
543 @templatekeyword('latesttagdistance')
516 def showlatesttagdistance(repo, ctx, templ, cache, **args):
544 def showlatesttagdistance(repo, ctx, templ, cache, **args):
517 """Integer. Longest path to the latest tag."""
545 """Integer. Longest path to the latest tag."""
518 return getlatesttags(repo, ctx, cache)[1]
546 return getlatesttags(repo, ctx, cache)[1]
519
547
520 @templatekeyword('changessincelatesttag')
548 @templatekeyword('changessincelatesttag')
521 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
549 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
522 """Integer. All ancestors not in the latest tag."""
550 """Integer. All ancestors not in the latest tag."""
523 latesttag = getlatesttags(repo, ctx, cache)[2][0]
551 latesttag = getlatesttags(repo, ctx, cache)[2][0]
524
552
525 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
553 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
526
554
527 def _showchangessincetag(repo, ctx, **args):
555 def _showchangessincetag(repo, ctx, **args):
528 offset = 0
556 offset = 0
529 revs = [ctx.rev()]
557 revs = [ctx.rev()]
530 tag = args[r'tag']
558 tag = args[r'tag']
531
559
532 # The only() revset doesn't currently support wdir()
560 # The only() revset doesn't currently support wdir()
533 if ctx.rev() is None:
561 if ctx.rev() is None:
534 offset = 1
562 offset = 1
535 revs = [p.rev() for p in ctx.parents()]
563 revs = [p.rev() for p in ctx.parents()]
536
564
537 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
565 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
538
566
539 @templatekeyword('manifest')
567 @templatekeyword('manifest')
540 def showmanifest(**args):
568 def showmanifest(**args):
541 repo, ctx, templ = args[r'repo'], args[r'ctx'], args[r'templ']
569 repo, ctx, templ = args[r'repo'], args[r'ctx'], args[r'templ']
542 mnode = ctx.manifestnode()
570 mnode = ctx.manifestnode()
543 if mnode is None:
571 if mnode is None:
544 # just avoid crash, we might want to use the 'ff...' hash in future
572 # just avoid crash, we might want to use the 'ff...' hash in future
545 return
573 return
574 mrev = repo.manifestlog._revlog.rev(mnode)
575 mhex = hex(mnode)
546 args = args.copy()
576 args = args.copy()
547 args.update({r'rev': repo.manifestlog._revlog.rev(mnode),
577 args.update({r'rev': mrev, r'node': mhex})
548 r'node': hex(mnode)})
578 f = templ('manifest', **args)
549 return templ('manifest', **args)
579 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
580 # rev and node are completely different from changeset's.
581 return _mappable(f, f, lambda: {'rev': mrev, 'node': mhex})
550
582
551 def shownames(namespace, **args):
583 def shownames(namespace, **args):
552 """helper method to generate a template keyword for a namespace"""
584 """helper method to generate a template keyword for a namespace"""
553 args = pycompat.byteskwargs(args)
585 args = pycompat.byteskwargs(args)
554 ctx = args['ctx']
586 ctx = args['ctx']
555 repo = ctx.repo()
587 repo = ctx.repo()
556 ns = repo.names[namespace]
588 ns = repo.names[namespace]
557 names = ns.names(repo, ctx.node())
589 names = ns.names(repo, ctx.node())
558 return showlist(ns.templatename, names, args, plural=namespace)
590 return showlist(ns.templatename, names, args, plural=namespace)
559
591
560 @templatekeyword('namespaces')
592 @templatekeyword('namespaces')
561 def shownamespaces(**args):
593 def shownamespaces(**args):
562 """Dict of lists. Names attached to this changeset per
594 """Dict of lists. Names attached to this changeset per
563 namespace."""
595 namespace."""
564 args = pycompat.byteskwargs(args)
596 args = pycompat.byteskwargs(args)
565 ctx = args['ctx']
597 ctx = args['ctx']
566 repo = ctx.repo()
598 repo = ctx.repo()
567
599
568 namespaces = util.sortdict()
600 namespaces = util.sortdict()
569 colornames = {}
601 colornames = {}
570 builtins = {}
602 builtins = {}
571
603
572 for k, ns in repo.names.iteritems():
604 for k, ns in repo.names.iteritems():
573 namespaces[k] = showlist('name', ns.names(repo, ctx.node()), args)
605 namespaces[k] = showlist('name', ns.names(repo, ctx.node()), args)
574 colornames[k] = ns.colorname
606 colornames[k] = ns.colorname
575 builtins[k] = ns.builtin
607 builtins[k] = ns.builtin
576
608
577 f = _showlist('namespace', list(namespaces), args)
609 f = _showlist('namespace', list(namespaces), args)
578
610
579 def makemap(ns):
611 def makemap(ns):
580 return {
612 return {
581 'namespace': ns,
613 'namespace': ns,
582 'names': namespaces[ns],
614 'names': namespaces[ns],
583 'builtin': builtins[ns],
615 'builtin': builtins[ns],
584 'colorname': colornames[ns],
616 'colorname': colornames[ns],
585 }
617 }
586
618
587 return _hybrid(f, namespaces, makemap, pycompat.identity)
619 return _hybrid(f, namespaces, makemap, pycompat.identity)
588
620
589 @templatekeyword('node')
621 @templatekeyword('node')
590 def shownode(repo, ctx, templ, **args):
622 def shownode(repo, ctx, templ, **args):
591 """String. The changeset identification hash, as a 40 hexadecimal
623 """String. The changeset identification hash, as a 40 hexadecimal
592 digit string.
624 digit string.
593 """
625 """
594 return ctx.hex()
626 return ctx.hex()
595
627
596 @templatekeyword('obsolete')
628 @templatekeyword('obsolete')
597 def showobsolete(repo, ctx, templ, **args):
629 def showobsolete(repo, ctx, templ, **args):
598 """String. Whether the changeset is obsolete.
630 """String. Whether the changeset is obsolete.
599 """
631 """
600 if ctx.obsolete():
632 if ctx.obsolete():
601 return 'obsolete'
633 return 'obsolete'
602 return ''
634 return ''
603
635
604 @templatekeyword('peerpaths')
636 @templatekeyword('peerpaths')
605 def showpeerpaths(repo, **args):
637 def showpeerpaths(repo, **args):
606 """A dictionary of repository locations defined in the [paths] section
638 """A dictionary of repository locations defined in the [paths] section
607 of your configuration file. (EXPERIMENTAL)"""
639 of your configuration file. (EXPERIMENTAL)"""
608 # see commands.paths() for naming of dictionary keys
640 # see commands.paths() for naming of dictionary keys
609 paths = util.sortdict()
641 paths = util.sortdict()
610 for k, p in sorted(repo.ui.paths.iteritems()):
642 for k, p in sorted(repo.ui.paths.iteritems()):
611 d = util.sortdict()
643 d = util.sortdict()
612 d['url'] = p.rawloc
644 d['url'] = p.rawloc
613 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
645 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
614 def f():
646 def f():
615 yield d['url']
647 yield d['url']
616 paths[k] = hybriddict(d, gen=f())
648 paths[k] = hybriddict(d, gen=f())
617
649
618 # no hybriddict() since d['path'] can't be formatted as a string. perhaps
650 # no hybriddict() since d['path'] can't be formatted as a string. perhaps
619 # hybriddict() should call templatefilters.stringify(d[value]).
651 # hybriddict() should call templatefilters.stringify(d[value]).
620 return _hybrid(None, paths, lambda k: {'name': k, 'path': paths[k]},
652 return _hybrid(None, paths, lambda k: {'name': k, 'path': paths[k]},
621 lambda k: '%s=%s' % (k, paths[k]['url']))
653 lambda k: '%s=%s' % (k, paths[k]['url']))
622
654
623 @templatekeyword("predecessors")
655 @templatekeyword("predecessors")
624 def showpredecessors(repo, ctx, **args):
656 def showpredecessors(repo, ctx, **args):
625 """Returns the list if the closest visible successors
657 """Returns the list if the closest visible successors
626 """
658 """
627 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
659 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
628 predecessors = map(hex, predecessors)
660 predecessors = map(hex, predecessors)
629
661
630 return _hybrid(None, predecessors,
662 return _hybrid(None, predecessors,
631 lambda x: {'ctx': repo[x], 'revcache': {}},
663 lambda x: {'ctx': repo[x], 'revcache': {}},
632 lambda x: scmutil.formatchangeid(repo[x]))
664 lambda x: scmutil.formatchangeid(repo[x]))
633
665
634 @templatekeyword("successorssets")
666 @templatekeyword("successorssets")
635 def showsuccessorssets(repo, ctx, **args):
667 def showsuccessorssets(repo, ctx, **args):
636 """Returns a string of sets of successors for a changectx
668 """Returns a string of sets of successors for a changectx
637
669
638 Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and
670 Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and
639 ctx2 while also diverged into ctx3"""
671 ctx2 while also diverged into ctx3"""
640 if not ctx.obsolete():
672 if not ctx.obsolete():
641 return ''
673 return ''
642 args = pycompat.byteskwargs(args)
674 args = pycompat.byteskwargs(args)
643
675
644 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
676 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
645 ssets = [[hex(n) for n in ss] for ss in ssets]
677 ssets = [[hex(n) for n in ss] for ss in ssets]
646
678
647 data = []
679 data = []
648 for ss in ssets:
680 for ss in ssets:
649 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
681 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
650 lambda x: scmutil.formatchangeid(repo[x]))
682 lambda x: scmutil.formatchangeid(repo[x]))
651 data.append(h)
683 data.append(h)
652
684
653 # Format the successorssets
685 # Format the successorssets
654 def render(d):
686 def render(d):
655 t = []
687 t = []
656 for i in d.gen:
688 for i in d.gen:
657 t.append(i)
689 t.append(i)
658 return "".join(t)
690 return "".join(t)
659
691
660 def gen(data):
692 def gen(data):
661 yield "; ".join(render(d) for d in data)
693 yield "; ".join(render(d) for d in data)
662
694
663 return _hybrid(gen(data), data, lambda x: {'successorset': x},
695 return _hybrid(gen(data), data, lambda x: {'successorset': x},
664 pycompat.identity)
696 pycompat.identity)
665
697
666 @templatekeyword("succsandmarkers")
698 @templatekeyword("succsandmarkers")
667 def showsuccsandmarkers(repo, ctx, **args):
699 def showsuccsandmarkers(repo, ctx, **args):
668 """Returns a list of dict for each final successor of ctx.
700 """Returns a list of dict for each final successor of ctx.
669
701
670 The dict contains successors node id in "successors" keys and the list of
702 The dict contains successors node id in "successors" keys and the list of
671 obs-markers from ctx to the set of successors in "markers"
703 obs-markers from ctx to the set of successors in "markers"
672
704
673 (EXPERIMENTAL)
705 (EXPERIMENTAL)
674 """
706 """
675
707
676 values = obsutil.successorsandmarkers(repo, ctx)
708 values = obsutil.successorsandmarkers(repo, ctx)
677
709
678 if values is None:
710 if values is None:
679 values = []
711 values = []
680
712
681 # Format successors and markers to avoid exposing binary to templates
713 # Format successors and markers to avoid exposing binary to templates
682 data = []
714 data = []
683 for i in values:
715 for i in values:
684 # Format successors
716 # Format successors
685 successors = i['successors']
717 successors = i['successors']
686
718
687 successors = [hex(n) for n in successors]
719 successors = [hex(n) for n in successors]
688 successors = _hybrid(None, successors,
720 successors = _hybrid(None, successors,
689 lambda x: {'ctx': repo[x], 'revcache': {}},
721 lambda x: {'ctx': repo[x], 'revcache': {}},
690 lambda x: scmutil.formatchangeid(repo[x]))
722 lambda x: scmutil.formatchangeid(repo[x]))
691
723
692 # Format markers
724 # Format markers
693 finalmarkers = []
725 finalmarkers = []
694 for m in i['markers']:
726 for m in i['markers']:
695 hexprec = hex(m[0])
727 hexprec = hex(m[0])
696 hexsucs = tuple(hex(n) for n in m[1])
728 hexsucs = tuple(hex(n) for n in m[1])
697 hexparents = None
729 hexparents = None
698 if m[5] is not None:
730 if m[5] is not None:
699 hexparents = tuple(hex(n) for n in m[5])
731 hexparents = tuple(hex(n) for n in m[5])
700 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
732 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
701 finalmarkers.append(newmarker)
733 finalmarkers.append(newmarker)
702
734
703 data.append({'successors': successors, 'markers': finalmarkers})
735 data.append({'successors': successors, 'markers': finalmarkers})
704
736
705 f = _showlist('succsandmarkers', data, args)
737 f = _showlist('succsandmarkers', data, args)
706 return _hybrid(f, data, lambda x: x, pycompat.identity)
738 return _hybrid(f, data, lambda x: x, pycompat.identity)
707
739
708 @templatekeyword('p1rev')
740 @templatekeyword('p1rev')
709 def showp1rev(repo, ctx, templ, **args):
741 def showp1rev(repo, ctx, templ, **args):
710 """Integer. The repository-local revision number of the changeset's
742 """Integer. The repository-local revision number of the changeset's
711 first parent, or -1 if the changeset has no parents."""
743 first parent, or -1 if the changeset has no parents."""
712 return ctx.p1().rev()
744 return ctx.p1().rev()
713
745
714 @templatekeyword('p2rev')
746 @templatekeyword('p2rev')
715 def showp2rev(repo, ctx, templ, **args):
747 def showp2rev(repo, ctx, templ, **args):
716 """Integer. The repository-local revision number of the changeset's
748 """Integer. The repository-local revision number of the changeset's
717 second parent, or -1 if the changeset has no second parent."""
749 second parent, or -1 if the changeset has no second parent."""
718 return ctx.p2().rev()
750 return ctx.p2().rev()
719
751
720 @templatekeyword('p1node')
752 @templatekeyword('p1node')
721 def showp1node(repo, ctx, templ, **args):
753 def showp1node(repo, ctx, templ, **args):
722 """String. The identification hash of the changeset's first parent,
754 """String. The identification hash of the changeset's first parent,
723 as a 40 digit hexadecimal string. If the changeset has no parents, all
755 as a 40 digit hexadecimal string. If the changeset has no parents, all
724 digits are 0."""
756 digits are 0."""
725 return ctx.p1().hex()
757 return ctx.p1().hex()
726
758
727 @templatekeyword('p2node')
759 @templatekeyword('p2node')
728 def showp2node(repo, ctx, templ, **args):
760 def showp2node(repo, ctx, templ, **args):
729 """String. The identification hash of the changeset's second
761 """String. The identification hash of the changeset's second
730 parent, as a 40 digit hexadecimal string. If the changeset has no second
762 parent, as a 40 digit hexadecimal string. If the changeset has no second
731 parent, all digits are 0."""
763 parent, all digits are 0."""
732 return ctx.p2().hex()
764 return ctx.p2().hex()
733
765
734 @templatekeyword('parents')
766 @templatekeyword('parents')
735 def showparents(**args):
767 def showparents(**args):
736 """List of strings. The parents of the changeset in "rev:node"
768 """List of strings. The parents of the changeset in "rev:node"
737 format. If the changeset has only one "natural" parent (the predecessor
769 format. If the changeset has only one "natural" parent (the predecessor
738 revision) nothing is shown."""
770 revision) nothing is shown."""
739 args = pycompat.byteskwargs(args)
771 args = pycompat.byteskwargs(args)
740 repo = args['repo']
772 repo = args['repo']
741 ctx = args['ctx']
773 ctx = args['ctx']
742 pctxs = scmutil.meaningfulparents(repo, ctx)
774 pctxs = scmutil.meaningfulparents(repo, ctx)
743 # ifcontains() needs a list of str
775 # ifcontains() needs a list of str
744 prevs = ["%d" % p.rev() for p in pctxs]
776 prevs = ["%d" % p.rev() for p in pctxs]
745 parents = [[('rev', p.rev()),
777 parents = [[('rev', p.rev()),
746 ('node', p.hex()),
778 ('node', p.hex()),
747 ('phase', p.phasestr())]
779 ('phase', p.phasestr())]
748 for p in pctxs]
780 for p in pctxs]
749 f = _showlist('parent', parents, args)
781 f = _showlist('parent', parents, args)
750 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
782 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
751 lambda x: scmutil.formatchangeid(repo[int(x)]))
783 lambda x: scmutil.formatchangeid(repo[int(x)]))
752
784
753 @templatekeyword('phase')
785 @templatekeyword('phase')
754 def showphase(repo, ctx, templ, **args):
786 def showphase(repo, ctx, templ, **args):
755 """String. The changeset phase name."""
787 """String. The changeset phase name."""
756 return ctx.phasestr()
788 return ctx.phasestr()
757
789
758 @templatekeyword('phaseidx')
790 @templatekeyword('phaseidx')
759 def showphaseidx(repo, ctx, templ, **args):
791 def showphaseidx(repo, ctx, templ, **args):
760 """Integer. The changeset phase index."""
792 """Integer. The changeset phase index."""
761 return ctx.phase()
793 return ctx.phase()
762
794
763 @templatekeyword('rev')
795 @templatekeyword('rev')
764 def showrev(repo, ctx, templ, **args):
796 def showrev(repo, ctx, templ, **args):
765 """Integer. The repository-local changeset revision number."""
797 """Integer. The repository-local changeset revision number."""
766 return scmutil.intrev(ctx)
798 return scmutil.intrev(ctx)
767
799
768 def showrevslist(name, revs, **args):
800 def showrevslist(name, revs, **args):
769 """helper to generate a list of revisions in which a mapped template will
801 """helper to generate a list of revisions in which a mapped template will
770 be evaluated"""
802 be evaluated"""
771 args = pycompat.byteskwargs(args)
803 args = pycompat.byteskwargs(args)
772 repo = args['ctx'].repo()
804 repo = args['ctx'].repo()
773 # ifcontains() needs a list of str
805 # ifcontains() needs a list of str
774 revs = ["%d" % r for r in revs]
806 revs = ["%d" % r for r in revs]
775 f = _showlist(name, revs, args)
807 f = _showlist(name, revs, args)
776 return _hybrid(f, revs,
808 return _hybrid(f, revs,
777 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
809 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
778 pycompat.identity)
810 pycompat.identity)
779
811
780 @templatekeyword('subrepos')
812 @templatekeyword('subrepos')
781 def showsubrepos(**args):
813 def showsubrepos(**args):
782 """List of strings. Updated subrepositories in the changeset."""
814 """List of strings. Updated subrepositories in the changeset."""
783 args = pycompat.byteskwargs(args)
815 args = pycompat.byteskwargs(args)
784 ctx = args['ctx']
816 ctx = args['ctx']
785 substate = ctx.substate
817 substate = ctx.substate
786 if not substate:
818 if not substate:
787 return showlist('subrepo', [], args)
819 return showlist('subrepo', [], args)
788 psubstate = ctx.parents()[0].substate or {}
820 psubstate = ctx.parents()[0].substate or {}
789 subrepos = []
821 subrepos = []
790 for sub in substate:
822 for sub in substate:
791 if sub not in psubstate or substate[sub] != psubstate[sub]:
823 if sub not in psubstate or substate[sub] != psubstate[sub]:
792 subrepos.append(sub) # modified or newly added in ctx
824 subrepos.append(sub) # modified or newly added in ctx
793 for sub in psubstate:
825 for sub in psubstate:
794 if sub not in substate:
826 if sub not in substate:
795 subrepos.append(sub) # removed in ctx
827 subrepos.append(sub) # removed in ctx
796 return showlist('subrepo', sorted(subrepos), args)
828 return showlist('subrepo', sorted(subrepos), args)
797
829
798 # don't remove "showtags" definition, even though namespaces will put
830 # don't remove "showtags" definition, even though namespaces will put
799 # a helper function for "tags" keyword into "keywords" map automatically,
831 # a helper function for "tags" keyword into "keywords" map automatically,
800 # because online help text is built without namespaces initialization
832 # because online help text is built without namespaces initialization
801 @templatekeyword('tags')
833 @templatekeyword('tags')
802 def showtags(**args):
834 def showtags(**args):
803 """List of strings. Any tags associated with the changeset."""
835 """List of strings. Any tags associated with the changeset."""
804 return shownames('tags', **args)
836 return shownames('tags', **args)
805
837
806 def loadkeyword(ui, extname, registrarobj):
838 def loadkeyword(ui, extname, registrarobj):
807 """Load template keyword from specified registrarobj
839 """Load template keyword from specified registrarobj
808 """
840 """
809 for name, func in registrarobj._table.iteritems():
841 for name, func in registrarobj._table.iteritems():
810 keywords[name] = func
842 keywords[name] = func
811
843
812 @templatekeyword('termwidth')
844 @templatekeyword('termwidth')
813 def showtermwidth(repo, ctx, templ, **args):
845 def showtermwidth(repo, ctx, templ, **args):
814 """Integer. The width of the current terminal."""
846 """Integer. The width of the current terminal."""
815 return repo.ui.termwidth()
847 return repo.ui.termwidth()
816
848
817 @templatekeyword('troubles')
849 @templatekeyword('troubles')
818 def showtroubles(repo, **args):
850 def showtroubles(repo, **args):
819 """List of strings. Evolution troubles affecting the changeset.
851 """List of strings. Evolution troubles affecting the changeset.
820
852
821 (DEPRECATED)
853 (DEPRECATED)
822 """
854 """
823 msg = ("'troubles' is deprecated, "
855 msg = ("'troubles' is deprecated, "
824 "use 'instabilities'")
856 "use 'instabilities'")
825 repo.ui.deprecwarn(msg, '4.4')
857 repo.ui.deprecwarn(msg, '4.4')
826
858
827 return showinstabilities(repo=repo, **args)
859 return showinstabilities(repo=repo, **args)
828
860
829 @templatekeyword('instabilities')
861 @templatekeyword('instabilities')
830 def showinstabilities(**args):
862 def showinstabilities(**args):
831 """List of strings. Evolution instabilities affecting the changeset.
863 """List of strings. Evolution instabilities affecting the changeset.
832
864
833 (EXPERIMENTAL)
865 (EXPERIMENTAL)
834 """
866 """
835 args = pycompat.byteskwargs(args)
867 args = pycompat.byteskwargs(args)
836 return showlist('instability', args['ctx'].instabilities(), args,
868 return showlist('instability', args['ctx'].instabilities(), args,
837 plural='instabilities')
869 plural='instabilities')
838
870
839 # tell hggettext to extract docstrings from these functions:
871 # tell hggettext to extract docstrings from these functions:
840 i18nfunctions = keywords.values()
872 i18nfunctions = keywords.values()
@@ -1,1448 +1,1451 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 templatefilters,
27 templatefilters,
28 templatekw,
28 templatekw,
29 util,
29 util,
30 )
30 )
31
31
32 # template parsing
32 # template parsing
33
33
34 elements = {
34 elements = {
35 # token-type: binding-strength, primary, prefix, infix, suffix
35 # token-type: binding-strength, primary, prefix, infix, suffix
36 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
36 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
37 "%": (15, None, None, ("%", 15), None),
37 "%": (15, None, None, ("%", 15), None),
38 "|": (15, None, None, ("|", 15), None),
38 "|": (15, None, None, ("|", 15), None),
39 "*": (5, None, None, ("*", 5), None),
39 "*": (5, None, None, ("*", 5), None),
40 "/": (5, None, None, ("/", 5), None),
40 "/": (5, None, None, ("/", 5), None),
41 "+": (4, None, None, ("+", 4), None),
41 "+": (4, None, None, ("+", 4), None),
42 "-": (4, None, ("negate", 19), ("-", 4), None),
42 "-": (4, None, ("negate", 19), ("-", 4), None),
43 "=": (3, None, None, ("keyvalue", 3), None),
43 "=": (3, None, None, ("keyvalue", 3), None),
44 ",": (2, None, None, ("list", 2), None),
44 ",": (2, None, None, ("list", 2), None),
45 ")": (0, None, None, None, None),
45 ")": (0, None, None, None, None),
46 "integer": (0, "integer", None, None, None),
46 "integer": (0, "integer", None, None, None),
47 "symbol": (0, "symbol", None, None, None),
47 "symbol": (0, "symbol", None, None, None),
48 "string": (0, "string", None, None, None),
48 "string": (0, "string", None, None, None),
49 "template": (0, "template", None, None, None),
49 "template": (0, "template", None, None, None),
50 "end": (0, None, None, None, None),
50 "end": (0, None, None, None, None),
51 }
51 }
52
52
53 def tokenize(program, start, end, term=None):
53 def tokenize(program, start, end, term=None):
54 """Parse a template expression into a stream of tokens, which must end
54 """Parse a template expression into a stream of tokens, which must end
55 with term if specified"""
55 with term if specified"""
56 pos = start
56 pos = start
57 program = pycompat.bytestr(program)
57 program = pycompat.bytestr(program)
58 while pos < end:
58 while pos < end:
59 c = program[pos]
59 c = program[pos]
60 if c.isspace(): # skip inter-token whitespace
60 if c.isspace(): # skip inter-token whitespace
61 pass
61 pass
62 elif c in "(=,)%|+-*/": # handle simple operators
62 elif c in "(=,)%|+-*/": # handle simple operators
63 yield (c, None, pos)
63 yield (c, None, pos)
64 elif c in '"\'': # handle quoted templates
64 elif c in '"\'': # handle quoted templates
65 s = pos + 1
65 s = pos + 1
66 data, pos = _parsetemplate(program, s, end, c)
66 data, pos = _parsetemplate(program, s, end, c)
67 yield ('template', data, s)
67 yield ('template', data, s)
68 pos -= 1
68 pos -= 1
69 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
69 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
70 # handle quoted strings
70 # handle quoted strings
71 c = program[pos + 1]
71 c = program[pos + 1]
72 s = pos = pos + 2
72 s = pos = pos + 2
73 while pos < end: # find closing quote
73 while pos < end: # find closing quote
74 d = program[pos]
74 d = program[pos]
75 if d == '\\': # skip over escaped characters
75 if d == '\\': # skip over escaped characters
76 pos += 2
76 pos += 2
77 continue
77 continue
78 if d == c:
78 if d == c:
79 yield ('string', program[s:pos], s)
79 yield ('string', program[s:pos], s)
80 break
80 break
81 pos += 1
81 pos += 1
82 else:
82 else:
83 raise error.ParseError(_("unterminated string"), s)
83 raise error.ParseError(_("unterminated string"), s)
84 elif c.isdigit():
84 elif c.isdigit():
85 s = pos
85 s = pos
86 while pos < end:
86 while pos < end:
87 d = program[pos]
87 d = program[pos]
88 if not d.isdigit():
88 if not d.isdigit():
89 break
89 break
90 pos += 1
90 pos += 1
91 yield ('integer', program[s:pos], s)
91 yield ('integer', program[s:pos], s)
92 pos -= 1
92 pos -= 1
93 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
93 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
94 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
94 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
95 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
95 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
96 # where some of nested templates were preprocessed as strings and
96 # where some of nested templates were preprocessed as strings and
97 # then compiled. therefore, \"...\" was allowed. (issue4733)
97 # then compiled. therefore, \"...\" was allowed. (issue4733)
98 #
98 #
99 # processing flow of _evalifliteral() at 5ab28a2e9962:
99 # processing flow of _evalifliteral() at 5ab28a2e9962:
100 # outer template string -> stringify() -> compiletemplate()
100 # outer template string -> stringify() -> compiletemplate()
101 # ------------------------ ------------ ------------------
101 # ------------------------ ------------ ------------------
102 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
102 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
103 # ~~~~~~~~
103 # ~~~~~~~~
104 # escaped quoted string
104 # escaped quoted string
105 if c == 'r':
105 if c == 'r':
106 pos += 1
106 pos += 1
107 token = 'string'
107 token = 'string'
108 else:
108 else:
109 token = 'template'
109 token = 'template'
110 quote = program[pos:pos + 2]
110 quote = program[pos:pos + 2]
111 s = pos = pos + 2
111 s = pos = pos + 2
112 while pos < end: # find closing escaped quote
112 while pos < end: # find closing escaped quote
113 if program.startswith('\\\\\\', pos, end):
113 if program.startswith('\\\\\\', pos, end):
114 pos += 4 # skip over double escaped characters
114 pos += 4 # skip over double escaped characters
115 continue
115 continue
116 if program.startswith(quote, pos, end):
116 if program.startswith(quote, pos, end):
117 # interpret as if it were a part of an outer string
117 # interpret as if it were a part of an outer string
118 data = parser.unescapestr(program[s:pos])
118 data = parser.unescapestr(program[s:pos])
119 if token == 'template':
119 if token == 'template':
120 data = _parsetemplate(data, 0, len(data))[0]
120 data = _parsetemplate(data, 0, len(data))[0]
121 yield (token, data, s)
121 yield (token, data, s)
122 pos += 1
122 pos += 1
123 break
123 break
124 pos += 1
124 pos += 1
125 else:
125 else:
126 raise error.ParseError(_("unterminated string"), s)
126 raise error.ParseError(_("unterminated string"), s)
127 elif c.isalnum() or c in '_':
127 elif c.isalnum() or c in '_':
128 s = pos
128 s = pos
129 pos += 1
129 pos += 1
130 while pos < end: # find end of symbol
130 while pos < end: # find end of symbol
131 d = program[pos]
131 d = program[pos]
132 if not (d.isalnum() or d == "_"):
132 if not (d.isalnum() or d == "_"):
133 break
133 break
134 pos += 1
134 pos += 1
135 sym = program[s:pos]
135 sym = program[s:pos]
136 yield ('symbol', sym, s)
136 yield ('symbol', sym, s)
137 pos -= 1
137 pos -= 1
138 elif c == term:
138 elif c == term:
139 yield ('end', None, pos + 1)
139 yield ('end', None, pos + 1)
140 return
140 return
141 else:
141 else:
142 raise error.ParseError(_("syntax error"), pos)
142 raise error.ParseError(_("syntax error"), pos)
143 pos += 1
143 pos += 1
144 if term:
144 if term:
145 raise error.ParseError(_("unterminated template expansion"), start)
145 raise error.ParseError(_("unterminated template expansion"), start)
146 yield ('end', None, pos)
146 yield ('end', None, pos)
147
147
148 def _parsetemplate(tmpl, start, stop, quote=''):
148 def _parsetemplate(tmpl, start, stop, quote=''):
149 r"""
149 r"""
150 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
150 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
151 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
151 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
152 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
152 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
153 ([('string', 'foo'), ('symbol', 'bar')], 9)
153 ([('string', 'foo'), ('symbol', 'bar')], 9)
154 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
154 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
155 ([('string', 'foo')], 4)
155 ([('string', 'foo')], 4)
156 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
156 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
157 ([('string', 'foo"'), ('string', 'bar')], 9)
157 ([('string', 'foo"'), ('string', 'bar')], 9)
158 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
158 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
159 ([('string', 'foo\\')], 6)
159 ([('string', 'foo\\')], 6)
160 """
160 """
161 parsed = []
161 parsed = []
162 sepchars = '{' + quote
162 sepchars = '{' + quote
163 pos = start
163 pos = start
164 p = parser.parser(elements)
164 p = parser.parser(elements)
165 while pos < stop:
165 while pos < stop:
166 n = min((tmpl.find(c, pos, stop) for c in sepchars),
166 n = min((tmpl.find(c, pos, stop) for c in sepchars),
167 key=lambda n: (n < 0, n))
167 key=lambda n: (n < 0, n))
168 if n < 0:
168 if n < 0:
169 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
169 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
170 pos = stop
170 pos = stop
171 break
171 break
172 c = tmpl[n:n + 1]
172 c = tmpl[n:n + 1]
173 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
173 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
174 if bs % 2 == 1:
174 if bs % 2 == 1:
175 # escaped (e.g. '\{', '\\\{', but not '\\{')
175 # escaped (e.g. '\{', '\\\{', but not '\\{')
176 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
176 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
177 pos = n + 1
177 pos = n + 1
178 continue
178 continue
179 if n > pos:
179 if n > pos:
180 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
180 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
181 if c == quote:
181 if c == quote:
182 return parsed, n + 1
182 return parsed, n + 1
183
183
184 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
184 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
185 parsed.append(parseres)
185 parsed.append(parseres)
186
186
187 if quote:
187 if quote:
188 raise error.ParseError(_("unterminated string"), start)
188 raise error.ParseError(_("unterminated string"), start)
189 return parsed, pos
189 return parsed, pos
190
190
191 def _unnesttemplatelist(tree):
191 def _unnesttemplatelist(tree):
192 """Expand list of templates to node tuple
192 """Expand list of templates to node tuple
193
193
194 >>> def f(tree):
194 >>> def f(tree):
195 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
195 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
196 >>> f((b'template', []))
196 >>> f((b'template', []))
197 (string '')
197 (string '')
198 >>> f((b'template', [(b'string', b'foo')]))
198 >>> f((b'template', [(b'string', b'foo')]))
199 (string 'foo')
199 (string 'foo')
200 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
200 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
201 (template
201 (template
202 (string 'foo')
202 (string 'foo')
203 (symbol 'rev'))
203 (symbol 'rev'))
204 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
204 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
205 (template
205 (template
206 (symbol 'rev'))
206 (symbol 'rev'))
207 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
207 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
208 (string 'foo')
208 (string 'foo')
209 """
209 """
210 if not isinstance(tree, tuple):
210 if not isinstance(tree, tuple):
211 return tree
211 return tree
212 op = tree[0]
212 op = tree[0]
213 if op != 'template':
213 if op != 'template':
214 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
214 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
215
215
216 assert len(tree) == 2
216 assert len(tree) == 2
217 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
217 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
218 if not xs:
218 if not xs:
219 return ('string', '') # empty template ""
219 return ('string', '') # empty template ""
220 elif len(xs) == 1 and xs[0][0] == 'string':
220 elif len(xs) == 1 and xs[0][0] == 'string':
221 return xs[0] # fast path for string with no template fragment "x"
221 return xs[0] # fast path for string with no template fragment "x"
222 else:
222 else:
223 return (op,) + xs
223 return (op,) + xs
224
224
225 def parse(tmpl):
225 def parse(tmpl):
226 """Parse template string into tree"""
226 """Parse template string into tree"""
227 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
227 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
228 assert pos == len(tmpl), 'unquoted template should be consumed'
228 assert pos == len(tmpl), 'unquoted template should be consumed'
229 return _unnesttemplatelist(('template', parsed))
229 return _unnesttemplatelist(('template', parsed))
230
230
231 def _parseexpr(expr):
231 def _parseexpr(expr):
232 """Parse a template expression into tree
232 """Parse a template expression into tree
233
233
234 >>> _parseexpr(b'"foo"')
234 >>> _parseexpr(b'"foo"')
235 ('string', 'foo')
235 ('string', 'foo')
236 >>> _parseexpr(b'foo(bar)')
236 >>> _parseexpr(b'foo(bar)')
237 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
237 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
238 >>> _parseexpr(b'foo(')
238 >>> _parseexpr(b'foo(')
239 Traceback (most recent call last):
239 Traceback (most recent call last):
240 ...
240 ...
241 ParseError: ('not a prefix: end', 4)
241 ParseError: ('not a prefix: end', 4)
242 >>> _parseexpr(b'"foo" "bar"')
242 >>> _parseexpr(b'"foo" "bar"')
243 Traceback (most recent call last):
243 Traceback (most recent call last):
244 ...
244 ...
245 ParseError: ('invalid token', 7)
245 ParseError: ('invalid token', 7)
246 """
246 """
247 p = parser.parser(elements)
247 p = parser.parser(elements)
248 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
248 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
249 if pos != len(expr):
249 if pos != len(expr):
250 raise error.ParseError(_('invalid token'), pos)
250 raise error.ParseError(_('invalid token'), pos)
251 return _unnesttemplatelist(tree)
251 return _unnesttemplatelist(tree)
252
252
253 def prettyformat(tree):
253 def prettyformat(tree):
254 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
254 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
255
255
256 def compileexp(exp, context, curmethods):
256 def compileexp(exp, context, curmethods):
257 """Compile parsed template tree to (func, data) pair"""
257 """Compile parsed template tree to (func, data) pair"""
258 t = exp[0]
258 t = exp[0]
259 if t in curmethods:
259 if t in curmethods:
260 return curmethods[t](exp, context)
260 return curmethods[t](exp, context)
261 raise error.ParseError(_("unknown method '%s'") % t)
261 raise error.ParseError(_("unknown method '%s'") % t)
262
262
263 # template evaluation
263 # template evaluation
264
264
265 def getsymbol(exp):
265 def getsymbol(exp):
266 if exp[0] == 'symbol':
266 if exp[0] == 'symbol':
267 return exp[1]
267 return exp[1]
268 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
268 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
269
269
270 def getlist(x):
270 def getlist(x):
271 if not x:
271 if not x:
272 return []
272 return []
273 if x[0] == 'list':
273 if x[0] == 'list':
274 return getlist(x[1]) + [x[2]]
274 return getlist(x[1]) + [x[2]]
275 return [x]
275 return [x]
276
276
277 def gettemplate(exp, context):
277 def gettemplate(exp, context):
278 """Compile given template tree or load named template from map file;
278 """Compile given template tree or load named template from map file;
279 returns (func, data) pair"""
279 returns (func, data) pair"""
280 if exp[0] in ('template', 'string'):
280 if exp[0] in ('template', 'string'):
281 return compileexp(exp, context, methods)
281 return compileexp(exp, context, methods)
282 if exp[0] == 'symbol':
282 if exp[0] == 'symbol':
283 # unlike runsymbol(), here 'symbol' is always taken as template name
283 # unlike runsymbol(), here 'symbol' is always taken as template name
284 # even if it exists in mapping. this allows us to override mapping
284 # even if it exists in mapping. this allows us to override mapping
285 # by web templates, e.g. 'changelogtag' is redefined in map file.
285 # by web templates, e.g. 'changelogtag' is redefined in map file.
286 return context._load(exp[1])
286 return context._load(exp[1])
287 raise error.ParseError(_("expected template specifier"))
287 raise error.ParseError(_("expected template specifier"))
288
288
289 def findsymbolicname(arg):
289 def findsymbolicname(arg):
290 """Find symbolic name for the given compiled expression; returns None
290 """Find symbolic name for the given compiled expression; returns None
291 if nothing found reliably"""
291 if nothing found reliably"""
292 while True:
292 while True:
293 func, data = arg
293 func, data = arg
294 if func is runsymbol:
294 if func is runsymbol:
295 return data
295 return data
296 elif func is runfilter:
296 elif func is runfilter:
297 arg = data[0]
297 arg = data[0]
298 else:
298 else:
299 return None
299 return None
300
300
301 def evalrawexp(context, mapping, arg):
301 def evalrawexp(context, mapping, arg):
302 """Evaluate given argument as a bare template object which may require
302 """Evaluate given argument as a bare template object which may require
303 further processing (such as folding generator of strings)"""
303 further processing (such as folding generator of strings)"""
304 func, data = arg
304 func, data = arg
305 return func(context, mapping, data)
305 return func(context, mapping, data)
306
306
307 def evalfuncarg(context, mapping, arg):
307 def evalfuncarg(context, mapping, arg):
308 """Evaluate given argument as value type"""
308 """Evaluate given argument as value type"""
309 thing = evalrawexp(context, mapping, arg)
309 thing = evalrawexp(context, mapping, arg)
310 thing = templatekw.unwrapvalue(thing)
310 # evalrawexp() may return string, generator of strings or arbitrary object
311 # evalrawexp() may return string, generator of strings or arbitrary object
311 # such as date tuple, but filter does not want generator.
312 # such as date tuple, but filter does not want generator.
312 if isinstance(thing, types.GeneratorType):
313 if isinstance(thing, types.GeneratorType):
313 thing = stringify(thing)
314 thing = stringify(thing)
314 return thing
315 return thing
315
316
316 def evalboolean(context, mapping, arg):
317 def evalboolean(context, mapping, arg):
317 """Evaluate given argument as boolean, but also takes boolean literals"""
318 """Evaluate given argument as boolean, but also takes boolean literals"""
318 func, data = arg
319 func, data = arg
319 if func is runsymbol:
320 if func is runsymbol:
320 thing = func(context, mapping, data, default=None)
321 thing = func(context, mapping, data, default=None)
321 if thing is None:
322 if thing is None:
322 # not a template keyword, takes as a boolean literal
323 # not a template keyword, takes as a boolean literal
323 thing = util.parsebool(data)
324 thing = util.parsebool(data)
324 else:
325 else:
325 thing = func(context, mapping, data)
326 thing = func(context, mapping, data)
327 thing = templatekw.unwrapvalue(thing)
326 if isinstance(thing, bool):
328 if isinstance(thing, bool):
327 return thing
329 return thing
328 # other objects are evaluated as strings, which means 0 is True, but
330 # other objects are evaluated as strings, which means 0 is True, but
329 # empty dict/list should be False as they are expected to be ''
331 # empty dict/list should be False as they are expected to be ''
330 return bool(stringify(thing))
332 return bool(stringify(thing))
331
333
332 def evalinteger(context, mapping, arg, err):
334 def evalinteger(context, mapping, arg, err):
333 v = evalfuncarg(context, mapping, arg)
335 v = evalfuncarg(context, mapping, arg)
334 try:
336 try:
335 return int(v)
337 return int(v)
336 except (TypeError, ValueError):
338 except (TypeError, ValueError):
337 raise error.ParseError(err)
339 raise error.ParseError(err)
338
340
339 def evalstring(context, mapping, arg):
341 def evalstring(context, mapping, arg):
340 return stringify(evalrawexp(context, mapping, arg))
342 return stringify(evalrawexp(context, mapping, arg))
341
343
342 def evalstringliteral(context, mapping, arg):
344 def evalstringliteral(context, mapping, arg):
343 """Evaluate given argument as string template, but returns symbol name
345 """Evaluate given argument as string template, but returns symbol name
344 if it is unknown"""
346 if it is unknown"""
345 func, data = arg
347 func, data = arg
346 if func is runsymbol:
348 if func is runsymbol:
347 thing = func(context, mapping, data, default=data)
349 thing = func(context, mapping, data, default=data)
348 else:
350 else:
349 thing = func(context, mapping, data)
351 thing = func(context, mapping, data)
350 return stringify(thing)
352 return stringify(thing)
351
353
352 def runinteger(context, mapping, data):
354 def runinteger(context, mapping, data):
353 return int(data)
355 return int(data)
354
356
355 def runstring(context, mapping, data):
357 def runstring(context, mapping, data):
356 return data
358 return data
357
359
358 def _recursivesymbolblocker(key):
360 def _recursivesymbolblocker(key):
359 def showrecursion(**args):
361 def showrecursion(**args):
360 raise error.Abort(_("recursive reference '%s' in template") % key)
362 raise error.Abort(_("recursive reference '%s' in template") % key)
361 return showrecursion
363 return showrecursion
362
364
363 def _runrecursivesymbol(context, mapping, key):
365 def _runrecursivesymbol(context, mapping, key):
364 raise error.Abort(_("recursive reference '%s' in template") % key)
366 raise error.Abort(_("recursive reference '%s' in template") % key)
365
367
366 def runsymbol(context, mapping, key, default=''):
368 def runsymbol(context, mapping, key, default=''):
367 v = mapping.get(key)
369 v = mapping.get(key)
368 if v is None:
370 if v is None:
369 v = context._defaults.get(key)
371 v = context._defaults.get(key)
370 if v is None:
372 if v is None:
371 # put poison to cut recursion. we can't move this to parsing phase
373 # put poison to cut recursion. we can't move this to parsing phase
372 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
374 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
373 safemapping = mapping.copy()
375 safemapping = mapping.copy()
374 safemapping[key] = _recursivesymbolblocker(key)
376 safemapping[key] = _recursivesymbolblocker(key)
375 try:
377 try:
376 v = context.process(key, safemapping)
378 v = context.process(key, safemapping)
377 except TemplateNotFound:
379 except TemplateNotFound:
378 v = default
380 v = default
379 if callable(v):
381 if callable(v):
380 return v(**pycompat.strkwargs(mapping))
382 return v(**pycompat.strkwargs(mapping))
381 return v
383 return v
382
384
383 def buildtemplate(exp, context):
385 def buildtemplate(exp, context):
384 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
386 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
385 return (runtemplate, ctmpl)
387 return (runtemplate, ctmpl)
386
388
387 def runtemplate(context, mapping, template):
389 def runtemplate(context, mapping, template):
388 for arg in template:
390 for arg in template:
389 yield evalrawexp(context, mapping, arg)
391 yield evalrawexp(context, mapping, arg)
390
392
391 def buildfilter(exp, context):
393 def buildfilter(exp, context):
392 n = getsymbol(exp[2])
394 n = getsymbol(exp[2])
393 if n in context._filters:
395 if n in context._filters:
394 filt = context._filters[n]
396 filt = context._filters[n]
395 arg = compileexp(exp[1], context, methods)
397 arg = compileexp(exp[1], context, methods)
396 return (runfilter, (arg, filt))
398 return (runfilter, (arg, filt))
397 if n in funcs:
399 if n in funcs:
398 f = funcs[n]
400 f = funcs[n]
399 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
401 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
400 return (f, args)
402 return (f, args)
401 raise error.ParseError(_("unknown function '%s'") % n)
403 raise error.ParseError(_("unknown function '%s'") % n)
402
404
403 def runfilter(context, mapping, data):
405 def runfilter(context, mapping, data):
404 arg, filt = data
406 arg, filt = data
405 thing = evalfuncarg(context, mapping, arg)
407 thing = evalfuncarg(context, mapping, arg)
406 try:
408 try:
407 return filt(thing)
409 return filt(thing)
408 except (ValueError, AttributeError, TypeError):
410 except (ValueError, AttributeError, TypeError):
409 sym = findsymbolicname(arg)
411 sym = findsymbolicname(arg)
410 if sym:
412 if sym:
411 msg = (_("template filter '%s' is not compatible with keyword '%s'")
413 msg = (_("template filter '%s' is not compatible with keyword '%s'")
412 % (filt.func_name, sym))
414 % (filt.func_name, sym))
413 else:
415 else:
414 msg = _("incompatible use of template filter '%s'") % filt.func_name
416 msg = _("incompatible use of template filter '%s'") % filt.func_name
415 raise error.Abort(msg)
417 raise error.Abort(msg)
416
418
417 def buildmap(exp, context):
419 def buildmap(exp, context):
418 darg = compileexp(exp[1], context, methods)
420 darg = compileexp(exp[1], context, methods)
419 targ = gettemplate(exp[2], context)
421 targ = gettemplate(exp[2], context)
420 return (runmap, (darg, targ))
422 return (runmap, (darg, targ))
421
423
422 def runmap(context, mapping, data):
424 def runmap(context, mapping, data):
423 darg, targ = data
425 darg, targ = data
424 d = evalrawexp(context, mapping, darg)
426 d = evalrawexp(context, mapping, darg)
425 if util.safehasattr(d, 'itermaps'):
427 if util.safehasattr(d, 'itermaps'):
426 diter = d.itermaps()
428 diter = d.itermaps()
427 else:
429 else:
428 try:
430 try:
429 diter = iter(d)
431 diter = iter(d)
430 except TypeError:
432 except TypeError:
431 sym = findsymbolicname(darg)
433 sym = findsymbolicname(darg)
432 if sym:
434 if sym:
433 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
435 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
434 else:
436 else:
435 raise error.ParseError(_("%r is not iterable") % d)
437 raise error.ParseError(_("%r is not iterable") % d)
436
438
437 for i, v in enumerate(diter):
439 for i, v in enumerate(diter):
438 lm = mapping.copy()
440 lm = mapping.copy()
439 lm['index'] = i
441 lm['index'] = i
440 if isinstance(v, dict):
442 if isinstance(v, dict):
441 lm.update(v)
443 lm.update(v)
442 lm['originalnode'] = mapping.get('node')
444 lm['originalnode'] = mapping.get('node')
443 yield evalrawexp(context, lm, targ)
445 yield evalrawexp(context, lm, targ)
444 else:
446 else:
445 # v is not an iterable of dicts, this happen when 'key'
447 # v is not an iterable of dicts, this happen when 'key'
446 # has been fully expanded already and format is useless.
448 # has been fully expanded already and format is useless.
447 # If so, return the expanded value.
449 # If so, return the expanded value.
448 yield v
450 yield v
449
451
450 def buildnegate(exp, context):
452 def buildnegate(exp, context):
451 arg = compileexp(exp[1], context, exprmethods)
453 arg = compileexp(exp[1], context, exprmethods)
452 return (runnegate, arg)
454 return (runnegate, arg)
453
455
454 def runnegate(context, mapping, data):
456 def runnegate(context, mapping, data):
455 data = evalinteger(context, mapping, data,
457 data = evalinteger(context, mapping, data,
456 _('negation needs an integer argument'))
458 _('negation needs an integer argument'))
457 return -data
459 return -data
458
460
459 def buildarithmetic(exp, context, func):
461 def buildarithmetic(exp, context, func):
460 left = compileexp(exp[1], context, exprmethods)
462 left = compileexp(exp[1], context, exprmethods)
461 right = compileexp(exp[2], context, exprmethods)
463 right = compileexp(exp[2], context, exprmethods)
462 return (runarithmetic, (func, left, right))
464 return (runarithmetic, (func, left, right))
463
465
464 def runarithmetic(context, mapping, data):
466 def runarithmetic(context, mapping, data):
465 func, left, right = data
467 func, left, right = data
466 left = evalinteger(context, mapping, left,
468 left = evalinteger(context, mapping, left,
467 _('arithmetic only defined on integers'))
469 _('arithmetic only defined on integers'))
468 right = evalinteger(context, mapping, right,
470 right = evalinteger(context, mapping, right,
469 _('arithmetic only defined on integers'))
471 _('arithmetic only defined on integers'))
470 try:
472 try:
471 return func(left, right)
473 return func(left, right)
472 except ZeroDivisionError:
474 except ZeroDivisionError:
473 raise error.Abort(_('division by zero is not defined'))
475 raise error.Abort(_('division by zero is not defined'))
474
476
475 def buildfunc(exp, context):
477 def buildfunc(exp, context):
476 n = getsymbol(exp[1])
478 n = getsymbol(exp[1])
477 if n in funcs:
479 if n in funcs:
478 f = funcs[n]
480 f = funcs[n]
479 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
481 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
480 return (f, args)
482 return (f, args)
481 if n in context._filters:
483 if n in context._filters:
482 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
484 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
483 if len(args) != 1:
485 if len(args) != 1:
484 raise error.ParseError(_("filter %s expects one argument") % n)
486 raise error.ParseError(_("filter %s expects one argument") % n)
485 f = context._filters[n]
487 f = context._filters[n]
486 return (runfilter, (args[0], f))
488 return (runfilter, (args[0], f))
487 raise error.ParseError(_("unknown function '%s'") % n)
489 raise error.ParseError(_("unknown function '%s'") % n)
488
490
489 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
491 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
490 """Compile parsed tree of function arguments into list or dict of
492 """Compile parsed tree of function arguments into list or dict of
491 (func, data) pairs
493 (func, data) pairs
492
494
493 >>> context = engine(lambda t: (runsymbol, t))
495 >>> context = engine(lambda t: (runsymbol, t))
494 >>> def fargs(expr, argspec):
496 >>> def fargs(expr, argspec):
495 ... x = _parseexpr(expr)
497 ... x = _parseexpr(expr)
496 ... n = getsymbol(x[1])
498 ... n = getsymbol(x[1])
497 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
499 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
498 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
500 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
499 ['l', 'k']
501 ['l', 'k']
500 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
502 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
501 >>> list(args.keys()), list(args[b'opts'].keys())
503 >>> list(args.keys()), list(args[b'opts'].keys())
502 (['opts'], ['opts', 'k'])
504 (['opts'], ['opts', 'k'])
503 """
505 """
504 def compiledict(xs):
506 def compiledict(xs):
505 return util.sortdict((k, compileexp(x, context, curmethods))
507 return util.sortdict((k, compileexp(x, context, curmethods))
506 for k, x in xs.iteritems())
508 for k, x in xs.iteritems())
507 def compilelist(xs):
509 def compilelist(xs):
508 return [compileexp(x, context, curmethods) for x in xs]
510 return [compileexp(x, context, curmethods) for x in xs]
509
511
510 if not argspec:
512 if not argspec:
511 # filter or function with no argspec: return list of positional args
513 # filter or function with no argspec: return list of positional args
512 return compilelist(getlist(exp))
514 return compilelist(getlist(exp))
513
515
514 # function with argspec: return dict of named args
516 # function with argspec: return dict of named args
515 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
517 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
516 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
518 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
517 keyvaluenode='keyvalue', keynode='symbol')
519 keyvaluenode='keyvalue', keynode='symbol')
518 compargs = util.sortdict()
520 compargs = util.sortdict()
519 if varkey:
521 if varkey:
520 compargs[varkey] = compilelist(treeargs.pop(varkey))
522 compargs[varkey] = compilelist(treeargs.pop(varkey))
521 if optkey:
523 if optkey:
522 compargs[optkey] = compiledict(treeargs.pop(optkey))
524 compargs[optkey] = compiledict(treeargs.pop(optkey))
523 compargs.update(compiledict(treeargs))
525 compargs.update(compiledict(treeargs))
524 return compargs
526 return compargs
525
527
526 def buildkeyvaluepair(exp, content):
528 def buildkeyvaluepair(exp, content):
527 raise error.ParseError(_("can't use a key-value pair in this context"))
529 raise error.ParseError(_("can't use a key-value pair in this context"))
528
530
529 # dict of template built-in functions
531 # dict of template built-in functions
530 funcs = {}
532 funcs = {}
531
533
532 templatefunc = registrar.templatefunc(funcs)
534 templatefunc = registrar.templatefunc(funcs)
533
535
534 @templatefunc('date(date[, fmt])')
536 @templatefunc('date(date[, fmt])')
535 def date(context, mapping, args):
537 def date(context, mapping, args):
536 """Format a date. See :hg:`help dates` for formatting
538 """Format a date. See :hg:`help dates` for formatting
537 strings. The default is a Unix date format, including the timezone:
539 strings. The default is a Unix date format, including the timezone:
538 "Mon Sep 04 15:13:13 2006 0700"."""
540 "Mon Sep 04 15:13:13 2006 0700"."""
539 if not (1 <= len(args) <= 2):
541 if not (1 <= len(args) <= 2):
540 # i18n: "date" is a keyword
542 # i18n: "date" is a keyword
541 raise error.ParseError(_("date expects one or two arguments"))
543 raise error.ParseError(_("date expects one or two arguments"))
542
544
543 date = evalfuncarg(context, mapping, args[0])
545 date = evalfuncarg(context, mapping, args[0])
544 fmt = None
546 fmt = None
545 if len(args) == 2:
547 if len(args) == 2:
546 fmt = evalstring(context, mapping, args[1])
548 fmt = evalstring(context, mapping, args[1])
547 try:
549 try:
548 if fmt is None:
550 if fmt is None:
549 return util.datestr(date)
551 return util.datestr(date)
550 else:
552 else:
551 return util.datestr(date, fmt)
553 return util.datestr(date, fmt)
552 except (TypeError, ValueError):
554 except (TypeError, ValueError):
553 # i18n: "date" is a keyword
555 # i18n: "date" is a keyword
554 raise error.ParseError(_("date expects a date information"))
556 raise error.ParseError(_("date expects a date information"))
555
557
556 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
558 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
557 def dict_(context, mapping, args):
559 def dict_(context, mapping, args):
558 """Construct a dict from key-value pairs. A key may be omitted if
560 """Construct a dict from key-value pairs. A key may be omitted if
559 a value expression can provide an unambiguous name."""
561 a value expression can provide an unambiguous name."""
560 data = util.sortdict()
562 data = util.sortdict()
561
563
562 for v in args['args']:
564 for v in args['args']:
563 k = findsymbolicname(v)
565 k = findsymbolicname(v)
564 if not k:
566 if not k:
565 raise error.ParseError(_('dict key cannot be inferred'))
567 raise error.ParseError(_('dict key cannot be inferred'))
566 if k in data or k in args['kwargs']:
568 if k in data or k in args['kwargs']:
567 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
569 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
568 data[k] = evalfuncarg(context, mapping, v)
570 data[k] = evalfuncarg(context, mapping, v)
569
571
570 data.update((k, evalfuncarg(context, mapping, v))
572 data.update((k, evalfuncarg(context, mapping, v))
571 for k, v in args['kwargs'].iteritems())
573 for k, v in args['kwargs'].iteritems())
572 return templatekw.hybriddict(data)
574 return templatekw.hybriddict(data)
573
575
574 @templatefunc('diff([includepattern [, excludepattern]])')
576 @templatefunc('diff([includepattern [, excludepattern]])')
575 def diff(context, mapping, args):
577 def diff(context, mapping, args):
576 """Show a diff, optionally
578 """Show a diff, optionally
577 specifying files to include or exclude."""
579 specifying files to include or exclude."""
578 if len(args) > 2:
580 if len(args) > 2:
579 # i18n: "diff" is a keyword
581 # i18n: "diff" is a keyword
580 raise error.ParseError(_("diff expects zero, one, or two arguments"))
582 raise error.ParseError(_("diff expects zero, one, or two arguments"))
581
583
582 def getpatterns(i):
584 def getpatterns(i):
583 if i < len(args):
585 if i < len(args):
584 s = evalstring(context, mapping, args[i]).strip()
586 s = evalstring(context, mapping, args[i]).strip()
585 if s:
587 if s:
586 return [s]
588 return [s]
587 return []
589 return []
588
590
589 ctx = mapping['ctx']
591 ctx = mapping['ctx']
590 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
592 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
591
593
592 return ''.join(chunks)
594 return ''.join(chunks)
593
595
594 @templatefunc('files(pattern)')
596 @templatefunc('files(pattern)')
595 def files(context, mapping, args):
597 def files(context, mapping, args):
596 """All files of the current changeset matching the pattern. See
598 """All files of the current changeset matching the pattern. See
597 :hg:`help patterns`."""
599 :hg:`help patterns`."""
598 if not len(args) == 1:
600 if not len(args) == 1:
599 # i18n: "files" is a keyword
601 # i18n: "files" is a keyword
600 raise error.ParseError(_("files expects one argument"))
602 raise error.ParseError(_("files expects one argument"))
601
603
602 raw = evalstring(context, mapping, args[0])
604 raw = evalstring(context, mapping, args[0])
603 ctx = mapping['ctx']
605 ctx = mapping['ctx']
604 m = ctx.match([raw])
606 m = ctx.match([raw])
605 files = list(ctx.matches(m))
607 files = list(ctx.matches(m))
606 return templatekw.showlist("file", files, mapping)
608 return templatekw.showlist("file", files, mapping)
607
609
608 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
610 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
609 def fill(context, mapping, args):
611 def fill(context, mapping, args):
610 """Fill many
612 """Fill many
611 paragraphs with optional indentation. See the "fill" filter."""
613 paragraphs with optional indentation. See the "fill" filter."""
612 if not (1 <= len(args) <= 4):
614 if not (1 <= len(args) <= 4):
613 # i18n: "fill" is a keyword
615 # i18n: "fill" is a keyword
614 raise error.ParseError(_("fill expects one to four arguments"))
616 raise error.ParseError(_("fill expects one to four arguments"))
615
617
616 text = evalstring(context, mapping, args[0])
618 text = evalstring(context, mapping, args[0])
617 width = 76
619 width = 76
618 initindent = ''
620 initindent = ''
619 hangindent = ''
621 hangindent = ''
620 if 2 <= len(args) <= 4:
622 if 2 <= len(args) <= 4:
621 width = evalinteger(context, mapping, args[1],
623 width = evalinteger(context, mapping, args[1],
622 # i18n: "fill" is a keyword
624 # i18n: "fill" is a keyword
623 _("fill expects an integer width"))
625 _("fill expects an integer width"))
624 try:
626 try:
625 initindent = evalstring(context, mapping, args[2])
627 initindent = evalstring(context, mapping, args[2])
626 hangindent = evalstring(context, mapping, args[3])
628 hangindent = evalstring(context, mapping, args[3])
627 except IndexError:
629 except IndexError:
628 pass
630 pass
629
631
630 return templatefilters.fill(text, width, initindent, hangindent)
632 return templatefilters.fill(text, width, initindent, hangindent)
631
633
632 @templatefunc('formatnode(node)')
634 @templatefunc('formatnode(node)')
633 def formatnode(context, mapping, args):
635 def formatnode(context, mapping, args):
634 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
636 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
635 if len(args) != 1:
637 if len(args) != 1:
636 # i18n: "formatnode" is a keyword
638 # i18n: "formatnode" is a keyword
637 raise error.ParseError(_("formatnode expects one argument"))
639 raise error.ParseError(_("formatnode expects one argument"))
638
640
639 ui = mapping['ui']
641 ui = mapping['ui']
640 node = evalstring(context, mapping, args[0])
642 node = evalstring(context, mapping, args[0])
641 if ui.debugflag:
643 if ui.debugflag:
642 return node
644 return node
643 return templatefilters.short(node)
645 return templatefilters.short(node)
644
646
645 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
647 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
646 argspec='text width fillchar left')
648 argspec='text width fillchar left')
647 def pad(context, mapping, args):
649 def pad(context, mapping, args):
648 """Pad text with a
650 """Pad text with a
649 fill character."""
651 fill character."""
650 if 'text' not in args or 'width' not in args:
652 if 'text' not in args or 'width' not in args:
651 # i18n: "pad" is a keyword
653 # i18n: "pad" is a keyword
652 raise error.ParseError(_("pad() expects two to four arguments"))
654 raise error.ParseError(_("pad() expects two to four arguments"))
653
655
654 width = evalinteger(context, mapping, args['width'],
656 width = evalinteger(context, mapping, args['width'],
655 # i18n: "pad" is a keyword
657 # i18n: "pad" is a keyword
656 _("pad() expects an integer width"))
658 _("pad() expects an integer width"))
657
659
658 text = evalstring(context, mapping, args['text'])
660 text = evalstring(context, mapping, args['text'])
659
661
660 left = False
662 left = False
661 fillchar = ' '
663 fillchar = ' '
662 if 'fillchar' in args:
664 if 'fillchar' in args:
663 fillchar = evalstring(context, mapping, args['fillchar'])
665 fillchar = evalstring(context, mapping, args['fillchar'])
664 if len(color.stripeffects(fillchar)) != 1:
666 if len(color.stripeffects(fillchar)) != 1:
665 # i18n: "pad" is a keyword
667 # i18n: "pad" is a keyword
666 raise error.ParseError(_("pad() expects a single fill character"))
668 raise error.ParseError(_("pad() expects a single fill character"))
667 if 'left' in args:
669 if 'left' in args:
668 left = evalboolean(context, mapping, args['left'])
670 left = evalboolean(context, mapping, args['left'])
669
671
670 fillwidth = width - encoding.colwidth(color.stripeffects(text))
672 fillwidth = width - encoding.colwidth(color.stripeffects(text))
671 if fillwidth <= 0:
673 if fillwidth <= 0:
672 return text
674 return text
673 if left:
675 if left:
674 return fillchar * fillwidth + text
676 return fillchar * fillwidth + text
675 else:
677 else:
676 return text + fillchar * fillwidth
678 return text + fillchar * fillwidth
677
679
678 @templatefunc('indent(text, indentchars[, firstline])')
680 @templatefunc('indent(text, indentchars[, firstline])')
679 def indent(context, mapping, args):
681 def indent(context, mapping, args):
680 """Indents all non-empty lines
682 """Indents all non-empty lines
681 with the characters given in the indentchars string. An optional
683 with the characters given in the indentchars string. An optional
682 third parameter will override the indent for the first line only
684 third parameter will override the indent for the first line only
683 if present."""
685 if present."""
684 if not (2 <= len(args) <= 3):
686 if not (2 <= len(args) <= 3):
685 # i18n: "indent" is a keyword
687 # i18n: "indent" is a keyword
686 raise error.ParseError(_("indent() expects two or three arguments"))
688 raise error.ParseError(_("indent() expects two or three arguments"))
687
689
688 text = evalstring(context, mapping, args[0])
690 text = evalstring(context, mapping, args[0])
689 indent = evalstring(context, mapping, args[1])
691 indent = evalstring(context, mapping, args[1])
690
692
691 if len(args) == 3:
693 if len(args) == 3:
692 firstline = evalstring(context, mapping, args[2])
694 firstline = evalstring(context, mapping, args[2])
693 else:
695 else:
694 firstline = indent
696 firstline = indent
695
697
696 # the indent function doesn't indent the first line, so we do it here
698 # the indent function doesn't indent the first line, so we do it here
697 return templatefilters.indent(firstline + text, indent)
699 return templatefilters.indent(firstline + text, indent)
698
700
699 @templatefunc('get(dict, key)')
701 @templatefunc('get(dict, key)')
700 def get(context, mapping, args):
702 def get(context, mapping, args):
701 """Get an attribute/key from an object. Some keywords
703 """Get an attribute/key from an object. Some keywords
702 are complex types. This function allows you to obtain the value of an
704 are complex types. This function allows you to obtain the value of an
703 attribute on these types."""
705 attribute on these types."""
704 if len(args) != 2:
706 if len(args) != 2:
705 # i18n: "get" is a keyword
707 # i18n: "get" is a keyword
706 raise error.ParseError(_("get() expects two arguments"))
708 raise error.ParseError(_("get() expects two arguments"))
707
709
708 dictarg = evalfuncarg(context, mapping, args[0])
710 dictarg = evalfuncarg(context, mapping, args[0])
709 if not util.safehasattr(dictarg, 'get'):
711 if not util.safehasattr(dictarg, 'get'):
710 # i18n: "get" is a keyword
712 # i18n: "get" is a keyword
711 raise error.ParseError(_("get() expects a dict as first argument"))
713 raise error.ParseError(_("get() expects a dict as first argument"))
712
714
713 key = evalfuncarg(context, mapping, args[1])
715 key = evalfuncarg(context, mapping, args[1])
714 return dictarg.get(key)
716 return dictarg.get(key)
715
717
716 @templatefunc('if(expr, then[, else])')
718 @templatefunc('if(expr, then[, else])')
717 def if_(context, mapping, args):
719 def if_(context, mapping, args):
718 """Conditionally execute based on the result of
720 """Conditionally execute based on the result of
719 an expression."""
721 an expression."""
720 if not (2 <= len(args) <= 3):
722 if not (2 <= len(args) <= 3):
721 # i18n: "if" is a keyword
723 # i18n: "if" is a keyword
722 raise error.ParseError(_("if expects two or three arguments"))
724 raise error.ParseError(_("if expects two or three arguments"))
723
725
724 test = evalboolean(context, mapping, args[0])
726 test = evalboolean(context, mapping, args[0])
725 if test:
727 if test:
726 yield evalrawexp(context, mapping, args[1])
728 yield evalrawexp(context, mapping, args[1])
727 elif len(args) == 3:
729 elif len(args) == 3:
728 yield evalrawexp(context, mapping, args[2])
730 yield evalrawexp(context, mapping, args[2])
729
731
730 @templatefunc('ifcontains(needle, haystack, then[, else])')
732 @templatefunc('ifcontains(needle, haystack, then[, else])')
731 def ifcontains(context, mapping, args):
733 def ifcontains(context, mapping, args):
732 """Conditionally execute based
734 """Conditionally execute based
733 on whether the item "needle" is in "haystack"."""
735 on whether the item "needle" is in "haystack"."""
734 if not (3 <= len(args) <= 4):
736 if not (3 <= len(args) <= 4):
735 # i18n: "ifcontains" is a keyword
737 # i18n: "ifcontains" is a keyword
736 raise error.ParseError(_("ifcontains expects three or four arguments"))
738 raise error.ParseError(_("ifcontains expects three or four arguments"))
737
739
738 needle = evalstring(context, mapping, args[0])
740 needle = evalstring(context, mapping, args[0])
739 haystack = evalfuncarg(context, mapping, args[1])
741 haystack = evalfuncarg(context, mapping, args[1])
740
742
741 if needle in haystack:
743 if needle in haystack:
742 yield evalrawexp(context, mapping, args[2])
744 yield evalrawexp(context, mapping, args[2])
743 elif len(args) == 4:
745 elif len(args) == 4:
744 yield evalrawexp(context, mapping, args[3])
746 yield evalrawexp(context, mapping, args[3])
745
747
746 @templatefunc('ifeq(expr1, expr2, then[, else])')
748 @templatefunc('ifeq(expr1, expr2, then[, else])')
747 def ifeq(context, mapping, args):
749 def ifeq(context, mapping, args):
748 """Conditionally execute based on
750 """Conditionally execute based on
749 whether 2 items are equivalent."""
751 whether 2 items are equivalent."""
750 if not (3 <= len(args) <= 4):
752 if not (3 <= len(args) <= 4):
751 # i18n: "ifeq" is a keyword
753 # i18n: "ifeq" is a keyword
752 raise error.ParseError(_("ifeq expects three or four arguments"))
754 raise error.ParseError(_("ifeq expects three or four arguments"))
753
755
754 test = evalstring(context, mapping, args[0])
756 test = evalstring(context, mapping, args[0])
755 match = evalstring(context, mapping, args[1])
757 match = evalstring(context, mapping, args[1])
756 if test == match:
758 if test == match:
757 yield evalrawexp(context, mapping, args[2])
759 yield evalrawexp(context, mapping, args[2])
758 elif len(args) == 4:
760 elif len(args) == 4:
759 yield evalrawexp(context, mapping, args[3])
761 yield evalrawexp(context, mapping, args[3])
760
762
761 @templatefunc('join(list, sep)')
763 @templatefunc('join(list, sep)')
762 def join(context, mapping, args):
764 def join(context, mapping, args):
763 """Join items in a list with a delimiter."""
765 """Join items in a list with a delimiter."""
764 if not (1 <= len(args) <= 2):
766 if not (1 <= len(args) <= 2):
765 # i18n: "join" is a keyword
767 # i18n: "join" is a keyword
766 raise error.ParseError(_("join expects one or two arguments"))
768 raise error.ParseError(_("join expects one or two arguments"))
767
769
768 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
770 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
769 # abuses generator as a keyword that returns a list of dicts.
771 # abuses generator as a keyword that returns a list of dicts.
770 joinset = evalrawexp(context, mapping, args[0])
772 joinset = evalrawexp(context, mapping, args[0])
773 joinset = templatekw.unwrapvalue(joinset)
771 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
774 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
772 joiner = " "
775 joiner = " "
773 if len(args) > 1:
776 if len(args) > 1:
774 joiner = evalstring(context, mapping, args[1])
777 joiner = evalstring(context, mapping, args[1])
775
778
776 first = True
779 first = True
777 for x in joinset:
780 for x in joinset:
778 if first:
781 if first:
779 first = False
782 first = False
780 else:
783 else:
781 yield joiner
784 yield joiner
782 yield joinfmt(x)
785 yield joinfmt(x)
783
786
784 @templatefunc('label(label, expr)')
787 @templatefunc('label(label, expr)')
785 def label(context, mapping, args):
788 def label(context, mapping, args):
786 """Apply a label to generated content. Content with
789 """Apply a label to generated content. Content with
787 a label applied can result in additional post-processing, such as
790 a label applied can result in additional post-processing, such as
788 automatic colorization."""
791 automatic colorization."""
789 if len(args) != 2:
792 if len(args) != 2:
790 # i18n: "label" is a keyword
793 # i18n: "label" is a keyword
791 raise error.ParseError(_("label expects two arguments"))
794 raise error.ParseError(_("label expects two arguments"))
792
795
793 ui = mapping['ui']
796 ui = mapping['ui']
794 thing = evalstring(context, mapping, args[1])
797 thing = evalstring(context, mapping, args[1])
795 # preserve unknown symbol as literal so effects like 'red', 'bold',
798 # preserve unknown symbol as literal so effects like 'red', 'bold',
796 # etc. don't need to be quoted
799 # etc. don't need to be quoted
797 label = evalstringliteral(context, mapping, args[0])
800 label = evalstringliteral(context, mapping, args[0])
798
801
799 return ui.label(thing, label)
802 return ui.label(thing, label)
800
803
801 @templatefunc('latesttag([pattern])')
804 @templatefunc('latesttag([pattern])')
802 def latesttag(context, mapping, args):
805 def latesttag(context, mapping, args):
803 """The global tags matching the given pattern on the
806 """The global tags matching the given pattern on the
804 most recent globally tagged ancestor of this changeset.
807 most recent globally tagged ancestor of this changeset.
805 If no such tags exist, the "{tag}" template resolves to
808 If no such tags exist, the "{tag}" template resolves to
806 the string "null"."""
809 the string "null"."""
807 if len(args) > 1:
810 if len(args) > 1:
808 # i18n: "latesttag" is a keyword
811 # i18n: "latesttag" is a keyword
809 raise error.ParseError(_("latesttag expects at most one argument"))
812 raise error.ParseError(_("latesttag expects at most one argument"))
810
813
811 pattern = None
814 pattern = None
812 if len(args) == 1:
815 if len(args) == 1:
813 pattern = evalstring(context, mapping, args[0])
816 pattern = evalstring(context, mapping, args[0])
814
817
815 return templatekw.showlatesttags(pattern, **mapping)
818 return templatekw.showlatesttags(pattern, **mapping)
816
819
817 @templatefunc('localdate(date[, tz])')
820 @templatefunc('localdate(date[, tz])')
818 def localdate(context, mapping, args):
821 def localdate(context, mapping, args):
819 """Converts a date to the specified timezone.
822 """Converts a date to the specified timezone.
820 The default is local date."""
823 The default is local date."""
821 if not (1 <= len(args) <= 2):
824 if not (1 <= len(args) <= 2):
822 # i18n: "localdate" is a keyword
825 # i18n: "localdate" is a keyword
823 raise error.ParseError(_("localdate expects one or two arguments"))
826 raise error.ParseError(_("localdate expects one or two arguments"))
824
827
825 date = evalfuncarg(context, mapping, args[0])
828 date = evalfuncarg(context, mapping, args[0])
826 try:
829 try:
827 date = util.parsedate(date)
830 date = util.parsedate(date)
828 except AttributeError: # not str nor date tuple
831 except AttributeError: # not str nor date tuple
829 # i18n: "localdate" is a keyword
832 # i18n: "localdate" is a keyword
830 raise error.ParseError(_("localdate expects a date information"))
833 raise error.ParseError(_("localdate expects a date information"))
831 if len(args) >= 2:
834 if len(args) >= 2:
832 tzoffset = None
835 tzoffset = None
833 tz = evalfuncarg(context, mapping, args[1])
836 tz = evalfuncarg(context, mapping, args[1])
834 if isinstance(tz, str):
837 if isinstance(tz, str):
835 tzoffset, remainder = util.parsetimezone(tz)
838 tzoffset, remainder = util.parsetimezone(tz)
836 if remainder:
839 if remainder:
837 tzoffset = None
840 tzoffset = None
838 if tzoffset is None:
841 if tzoffset is None:
839 try:
842 try:
840 tzoffset = int(tz)
843 tzoffset = int(tz)
841 except (TypeError, ValueError):
844 except (TypeError, ValueError):
842 # i18n: "localdate" is a keyword
845 # i18n: "localdate" is a keyword
843 raise error.ParseError(_("localdate expects a timezone"))
846 raise error.ParseError(_("localdate expects a timezone"))
844 else:
847 else:
845 tzoffset = util.makedate()[1]
848 tzoffset = util.makedate()[1]
846 return (date[0], tzoffset)
849 return (date[0], tzoffset)
847
850
848 @templatefunc('max(iterable)')
851 @templatefunc('max(iterable)')
849 def max_(context, mapping, args, **kwargs):
852 def max_(context, mapping, args, **kwargs):
850 """Return the max of an iterable"""
853 """Return the max of an iterable"""
851 if len(args) != 1:
854 if len(args) != 1:
852 # i18n: "max" is a keyword
855 # i18n: "max" is a keyword
853 raise error.ParseError(_("max expects one arguments"))
856 raise error.ParseError(_("max expects one arguments"))
854
857
855 iterable = evalfuncarg(context, mapping, args[0])
858 iterable = evalfuncarg(context, mapping, args[0])
856 try:
859 try:
857 return max(iterable)
860 return max(iterable)
858 except (TypeError, ValueError):
861 except (TypeError, ValueError):
859 # i18n: "max" is a keyword
862 # i18n: "max" is a keyword
860 raise error.ParseError(_("max first argument should be an iterable"))
863 raise error.ParseError(_("max first argument should be an iterable"))
861
864
862 @templatefunc('min(iterable)')
865 @templatefunc('min(iterable)')
863 def min_(context, mapping, args, **kwargs):
866 def min_(context, mapping, args, **kwargs):
864 """Return the min of an iterable"""
867 """Return the min of an iterable"""
865 if len(args) != 1:
868 if len(args) != 1:
866 # i18n: "min" is a keyword
869 # i18n: "min" is a keyword
867 raise error.ParseError(_("min expects one arguments"))
870 raise error.ParseError(_("min expects one arguments"))
868
871
869 iterable = evalfuncarg(context, mapping, args[0])
872 iterable = evalfuncarg(context, mapping, args[0])
870 try:
873 try:
871 return min(iterable)
874 return min(iterable)
872 except (TypeError, ValueError):
875 except (TypeError, ValueError):
873 # i18n: "min" is a keyword
876 # i18n: "min" is a keyword
874 raise error.ParseError(_("min first argument should be an iterable"))
877 raise error.ParseError(_("min first argument should be an iterable"))
875
878
876 @templatefunc('mod(a, b)')
879 @templatefunc('mod(a, b)')
877 def mod(context, mapping, args):
880 def mod(context, mapping, args):
878 """Calculate a mod b such that a / b + a mod b == a"""
881 """Calculate a mod b such that a / b + a mod b == a"""
879 if not len(args) == 2:
882 if not len(args) == 2:
880 # i18n: "mod" is a keyword
883 # i18n: "mod" is a keyword
881 raise error.ParseError(_("mod expects two arguments"))
884 raise error.ParseError(_("mod expects two arguments"))
882
885
883 func = lambda a, b: a % b
886 func = lambda a, b: a % b
884 return runarithmetic(context, mapping, (func, args[0], args[1]))
887 return runarithmetic(context, mapping, (func, args[0], args[1]))
885
888
886 @templatefunc('obsfateoperations(markers)')
889 @templatefunc('obsfateoperations(markers)')
887 def obsfateoperations(context, mapping, args):
890 def obsfateoperations(context, mapping, args):
888 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
891 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
889 if len(args) != 1:
892 if len(args) != 1:
890 # i18n: "obsfateoperations" is a keyword
893 # i18n: "obsfateoperations" is a keyword
891 raise error.ParseError(_("obsfateoperations expects one arguments"))
894 raise error.ParseError(_("obsfateoperations expects one arguments"))
892
895
893 markers = evalfuncarg(context, mapping, args[0])
896 markers = evalfuncarg(context, mapping, args[0])
894
897
895 try:
898 try:
896 data = obsutil.markersoperations(markers)
899 data = obsutil.markersoperations(markers)
897 return templatekw.hybridlist(data, name='operation')
900 return templatekw.hybridlist(data, name='operation')
898 except (TypeError, KeyError):
901 except (TypeError, KeyError):
899 # i18n: "obsfateoperations" is a keyword
902 # i18n: "obsfateoperations" is a keyword
900 errmsg = _("obsfateoperations first argument should be an iterable")
903 errmsg = _("obsfateoperations first argument should be an iterable")
901 raise error.ParseError(errmsg)
904 raise error.ParseError(errmsg)
902
905
903 @templatefunc('obsfatedate(markers)')
906 @templatefunc('obsfatedate(markers)')
904 def obsfatedate(context, mapping, args):
907 def obsfatedate(context, mapping, args):
905 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
908 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
906 if len(args) != 1:
909 if len(args) != 1:
907 # i18n: "obsfatedate" is a keyword
910 # i18n: "obsfatedate" is a keyword
908 raise error.ParseError(_("obsfatedate expects one arguments"))
911 raise error.ParseError(_("obsfatedate expects one arguments"))
909
912
910 markers = evalfuncarg(context, mapping, args[0])
913 markers = evalfuncarg(context, mapping, args[0])
911
914
912 try:
915 try:
913 data = obsutil.markersdates(markers)
916 data = obsutil.markersdates(markers)
914 return templatekw.hybridlist(data, name='date', fmt='%d %d')
917 return templatekw.hybridlist(data, name='date', fmt='%d %d')
915 except (TypeError, KeyError):
918 except (TypeError, KeyError):
916 # i18n: "obsfatedate" is a keyword
919 # i18n: "obsfatedate" is a keyword
917 errmsg = _("obsfatedate first argument should be an iterable")
920 errmsg = _("obsfatedate first argument should be an iterable")
918 raise error.ParseError(errmsg)
921 raise error.ParseError(errmsg)
919
922
920 @templatefunc('obsfateusers(markers)')
923 @templatefunc('obsfateusers(markers)')
921 def obsfateusers(context, mapping, args):
924 def obsfateusers(context, mapping, args):
922 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
925 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
923 if len(args) != 1:
926 if len(args) != 1:
924 # i18n: "obsfateusers" is a keyword
927 # i18n: "obsfateusers" is a keyword
925 raise error.ParseError(_("obsfateusers expects one arguments"))
928 raise error.ParseError(_("obsfateusers expects one arguments"))
926
929
927 markers = evalfuncarg(context, mapping, args[0])
930 markers = evalfuncarg(context, mapping, args[0])
928
931
929 try:
932 try:
930 data = obsutil.markersusers(markers)
933 data = obsutil.markersusers(markers)
931 return templatekw.hybridlist(data, name='user')
934 return templatekw.hybridlist(data, name='user')
932 except (TypeError, KeyError, ValueError):
935 except (TypeError, KeyError, ValueError):
933 # i18n: "obsfateusers" is a keyword
936 # i18n: "obsfateusers" is a keyword
934 msg = _("obsfateusers first argument should be an iterable of "
937 msg = _("obsfateusers first argument should be an iterable of "
935 "obsmakers")
938 "obsmakers")
936 raise error.ParseError(msg)
939 raise error.ParseError(msg)
937
940
938 @templatefunc('obsfateverb(successors)')
941 @templatefunc('obsfateverb(successors)')
939 def obsfateverb(context, mapping, args):
942 def obsfateverb(context, mapping, args):
940 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
943 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
941 if len(args) != 1:
944 if len(args) != 1:
942 # i18n: "obsfateverb" is a keyword
945 # i18n: "obsfateverb" is a keyword
943 raise error.ParseError(_("obsfateverb expects one arguments"))
946 raise error.ParseError(_("obsfateverb expects one arguments"))
944
947
945 successors = evalfuncarg(context, mapping, args[0])
948 successors = evalfuncarg(context, mapping, args[0])
946
949
947 try:
950 try:
948 return obsutil.successorsetverb(successors)
951 return obsutil.successorsetverb(successors)
949 except TypeError:
952 except TypeError:
950 # i18n: "obsfateverb" is a keyword
953 # i18n: "obsfateverb" is a keyword
951 errmsg = _("obsfateverb first argument should be countable")
954 errmsg = _("obsfateverb first argument should be countable")
952 raise error.ParseError(errmsg)
955 raise error.ParseError(errmsg)
953
956
954 @templatefunc('relpath(path)')
957 @templatefunc('relpath(path)')
955 def relpath(context, mapping, args):
958 def relpath(context, mapping, args):
956 """Convert a repository-absolute path into a filesystem path relative to
959 """Convert a repository-absolute path into a filesystem path relative to
957 the current working directory."""
960 the current working directory."""
958 if len(args) != 1:
961 if len(args) != 1:
959 # i18n: "relpath" is a keyword
962 # i18n: "relpath" is a keyword
960 raise error.ParseError(_("relpath expects one argument"))
963 raise error.ParseError(_("relpath expects one argument"))
961
964
962 repo = mapping['ctx'].repo()
965 repo = mapping['ctx'].repo()
963 path = evalstring(context, mapping, args[0])
966 path = evalstring(context, mapping, args[0])
964 return repo.pathto(path)
967 return repo.pathto(path)
965
968
966 @templatefunc('revset(query[, formatargs...])')
969 @templatefunc('revset(query[, formatargs...])')
967 def revset(context, mapping, args):
970 def revset(context, mapping, args):
968 """Execute a revision set query. See
971 """Execute a revision set query. See
969 :hg:`help revset`."""
972 :hg:`help revset`."""
970 if not len(args) > 0:
973 if not len(args) > 0:
971 # i18n: "revset" is a keyword
974 # i18n: "revset" is a keyword
972 raise error.ParseError(_("revset expects one or more arguments"))
975 raise error.ParseError(_("revset expects one or more arguments"))
973
976
974 raw = evalstring(context, mapping, args[0])
977 raw = evalstring(context, mapping, args[0])
975 ctx = mapping['ctx']
978 ctx = mapping['ctx']
976 repo = ctx.repo()
979 repo = ctx.repo()
977
980
978 def query(expr):
981 def query(expr):
979 m = revsetmod.match(repo.ui, expr, repo=repo)
982 m = revsetmod.match(repo.ui, expr, repo=repo)
980 return m(repo)
983 return m(repo)
981
984
982 if len(args) > 1:
985 if len(args) > 1:
983 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
986 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
984 revs = query(revsetlang.formatspec(raw, *formatargs))
987 revs = query(revsetlang.formatspec(raw, *formatargs))
985 revs = list(revs)
988 revs = list(revs)
986 else:
989 else:
987 revsetcache = mapping['cache'].setdefault("revsetcache", {})
990 revsetcache = mapping['cache'].setdefault("revsetcache", {})
988 if raw in revsetcache:
991 if raw in revsetcache:
989 revs = revsetcache[raw]
992 revs = revsetcache[raw]
990 else:
993 else:
991 revs = query(raw)
994 revs = query(raw)
992 revs = list(revs)
995 revs = list(revs)
993 revsetcache[raw] = revs
996 revsetcache[raw] = revs
994
997
995 return templatekw.showrevslist("revision", revs, **mapping)
998 return templatekw.showrevslist("revision", revs, **mapping)
996
999
997 @templatefunc('rstdoc(text, style)')
1000 @templatefunc('rstdoc(text, style)')
998 def rstdoc(context, mapping, args):
1001 def rstdoc(context, mapping, args):
999 """Format reStructuredText."""
1002 """Format reStructuredText."""
1000 if len(args) != 2:
1003 if len(args) != 2:
1001 # i18n: "rstdoc" is a keyword
1004 # i18n: "rstdoc" is a keyword
1002 raise error.ParseError(_("rstdoc expects two arguments"))
1005 raise error.ParseError(_("rstdoc expects two arguments"))
1003
1006
1004 text = evalstring(context, mapping, args[0])
1007 text = evalstring(context, mapping, args[0])
1005 style = evalstring(context, mapping, args[1])
1008 style = evalstring(context, mapping, args[1])
1006
1009
1007 return minirst.format(text, style=style, keep=['verbose'])
1010 return minirst.format(text, style=style, keep=['verbose'])
1008
1011
1009 @templatefunc('separate(sep, args)', argspec='sep *args')
1012 @templatefunc('separate(sep, args)', argspec='sep *args')
1010 def separate(context, mapping, args):
1013 def separate(context, mapping, args):
1011 """Add a separator between non-empty arguments."""
1014 """Add a separator between non-empty arguments."""
1012 if 'sep' not in args:
1015 if 'sep' not in args:
1013 # i18n: "separate" is a keyword
1016 # i18n: "separate" is a keyword
1014 raise error.ParseError(_("separate expects at least one argument"))
1017 raise error.ParseError(_("separate expects at least one argument"))
1015
1018
1016 sep = evalstring(context, mapping, args['sep'])
1019 sep = evalstring(context, mapping, args['sep'])
1017 first = True
1020 first = True
1018 for arg in args['args']:
1021 for arg in args['args']:
1019 argstr = evalstring(context, mapping, arg)
1022 argstr = evalstring(context, mapping, arg)
1020 if not argstr:
1023 if not argstr:
1021 continue
1024 continue
1022 if first:
1025 if first:
1023 first = False
1026 first = False
1024 else:
1027 else:
1025 yield sep
1028 yield sep
1026 yield argstr
1029 yield argstr
1027
1030
1028 @templatefunc('shortest(node, minlength=4)')
1031 @templatefunc('shortest(node, minlength=4)')
1029 def shortest(context, mapping, args):
1032 def shortest(context, mapping, args):
1030 """Obtain the shortest representation of
1033 """Obtain the shortest representation of
1031 a node."""
1034 a node."""
1032 if not (1 <= len(args) <= 2):
1035 if not (1 <= len(args) <= 2):
1033 # i18n: "shortest" is a keyword
1036 # i18n: "shortest" is a keyword
1034 raise error.ParseError(_("shortest() expects one or two arguments"))
1037 raise error.ParseError(_("shortest() expects one or two arguments"))
1035
1038
1036 node = evalstring(context, mapping, args[0])
1039 node = evalstring(context, mapping, args[0])
1037
1040
1038 minlength = 4
1041 minlength = 4
1039 if len(args) > 1:
1042 if len(args) > 1:
1040 minlength = evalinteger(context, mapping, args[1],
1043 minlength = evalinteger(context, mapping, args[1],
1041 # i18n: "shortest" is a keyword
1044 # i18n: "shortest" is a keyword
1042 _("shortest() expects an integer minlength"))
1045 _("shortest() expects an integer minlength"))
1043
1046
1044 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1047 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1045 # which would be unacceptably slow. so we look for hash collision in
1048 # which would be unacceptably slow. so we look for hash collision in
1046 # unfiltered space, which means some hashes may be slightly longer.
1049 # unfiltered space, which means some hashes may be slightly longer.
1047 cl = mapping['ctx']._repo.unfiltered().changelog
1050 cl = mapping['ctx']._repo.unfiltered().changelog
1048 return cl.shortest(node, minlength)
1051 return cl.shortest(node, minlength)
1049
1052
1050 @templatefunc('strip(text[, chars])')
1053 @templatefunc('strip(text[, chars])')
1051 def strip(context, mapping, args):
1054 def strip(context, mapping, args):
1052 """Strip characters from a string. By default,
1055 """Strip characters from a string. By default,
1053 strips all leading and trailing whitespace."""
1056 strips all leading and trailing whitespace."""
1054 if not (1 <= len(args) <= 2):
1057 if not (1 <= len(args) <= 2):
1055 # i18n: "strip" is a keyword
1058 # i18n: "strip" is a keyword
1056 raise error.ParseError(_("strip expects one or two arguments"))
1059 raise error.ParseError(_("strip expects one or two arguments"))
1057
1060
1058 text = evalstring(context, mapping, args[0])
1061 text = evalstring(context, mapping, args[0])
1059 if len(args) == 2:
1062 if len(args) == 2:
1060 chars = evalstring(context, mapping, args[1])
1063 chars = evalstring(context, mapping, args[1])
1061 return text.strip(chars)
1064 return text.strip(chars)
1062 return text.strip()
1065 return text.strip()
1063
1066
1064 @templatefunc('sub(pattern, replacement, expression)')
1067 @templatefunc('sub(pattern, replacement, expression)')
1065 def sub(context, mapping, args):
1068 def sub(context, mapping, args):
1066 """Perform text substitution
1069 """Perform text substitution
1067 using regular expressions."""
1070 using regular expressions."""
1068 if len(args) != 3:
1071 if len(args) != 3:
1069 # i18n: "sub" is a keyword
1072 # i18n: "sub" is a keyword
1070 raise error.ParseError(_("sub expects three arguments"))
1073 raise error.ParseError(_("sub expects three arguments"))
1071
1074
1072 pat = evalstring(context, mapping, args[0])
1075 pat = evalstring(context, mapping, args[0])
1073 rpl = evalstring(context, mapping, args[1])
1076 rpl = evalstring(context, mapping, args[1])
1074 src = evalstring(context, mapping, args[2])
1077 src = evalstring(context, mapping, args[2])
1075 try:
1078 try:
1076 patre = re.compile(pat)
1079 patre = re.compile(pat)
1077 except re.error:
1080 except re.error:
1078 # i18n: "sub" is a keyword
1081 # i18n: "sub" is a keyword
1079 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1082 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1080 try:
1083 try:
1081 yield patre.sub(rpl, src)
1084 yield patre.sub(rpl, src)
1082 except re.error:
1085 except re.error:
1083 # i18n: "sub" is a keyword
1086 # i18n: "sub" is a keyword
1084 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1087 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1085
1088
1086 @templatefunc('startswith(pattern, text)')
1089 @templatefunc('startswith(pattern, text)')
1087 def startswith(context, mapping, args):
1090 def startswith(context, mapping, args):
1088 """Returns the value from the "text" argument
1091 """Returns the value from the "text" argument
1089 if it begins with the content from the "pattern" argument."""
1092 if it begins with the content from the "pattern" argument."""
1090 if len(args) != 2:
1093 if len(args) != 2:
1091 # i18n: "startswith" is a keyword
1094 # i18n: "startswith" is a keyword
1092 raise error.ParseError(_("startswith expects two arguments"))
1095 raise error.ParseError(_("startswith expects two arguments"))
1093
1096
1094 patn = evalstring(context, mapping, args[0])
1097 patn = evalstring(context, mapping, args[0])
1095 text = evalstring(context, mapping, args[1])
1098 text = evalstring(context, mapping, args[1])
1096 if text.startswith(patn):
1099 if text.startswith(patn):
1097 return text
1100 return text
1098 return ''
1101 return ''
1099
1102
1100 @templatefunc('word(number, text[, separator])')
1103 @templatefunc('word(number, text[, separator])')
1101 def word(context, mapping, args):
1104 def word(context, mapping, args):
1102 """Return the nth word from a string."""
1105 """Return the nth word from a string."""
1103 if not (2 <= len(args) <= 3):
1106 if not (2 <= len(args) <= 3):
1104 # i18n: "word" is a keyword
1107 # i18n: "word" is a keyword
1105 raise error.ParseError(_("word expects two or three arguments, got %d")
1108 raise error.ParseError(_("word expects two or three arguments, got %d")
1106 % len(args))
1109 % len(args))
1107
1110
1108 num = evalinteger(context, mapping, args[0],
1111 num = evalinteger(context, mapping, args[0],
1109 # i18n: "word" is a keyword
1112 # i18n: "word" is a keyword
1110 _("word expects an integer index"))
1113 _("word expects an integer index"))
1111 text = evalstring(context, mapping, args[1])
1114 text = evalstring(context, mapping, args[1])
1112 if len(args) == 3:
1115 if len(args) == 3:
1113 splitter = evalstring(context, mapping, args[2])
1116 splitter = evalstring(context, mapping, args[2])
1114 else:
1117 else:
1115 splitter = None
1118 splitter = None
1116
1119
1117 tokens = text.split(splitter)
1120 tokens = text.split(splitter)
1118 if num >= len(tokens) or num < -len(tokens):
1121 if num >= len(tokens) or num < -len(tokens):
1119 return ''
1122 return ''
1120 else:
1123 else:
1121 return tokens[num]
1124 return tokens[num]
1122
1125
1123 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1126 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1124 exprmethods = {
1127 exprmethods = {
1125 "integer": lambda e, c: (runinteger, e[1]),
1128 "integer": lambda e, c: (runinteger, e[1]),
1126 "string": lambda e, c: (runstring, e[1]),
1129 "string": lambda e, c: (runstring, e[1]),
1127 "symbol": lambda e, c: (runsymbol, e[1]),
1130 "symbol": lambda e, c: (runsymbol, e[1]),
1128 "template": buildtemplate,
1131 "template": buildtemplate,
1129 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1132 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1130 # ".": buildmember,
1133 # ".": buildmember,
1131 "|": buildfilter,
1134 "|": buildfilter,
1132 "%": buildmap,
1135 "%": buildmap,
1133 "func": buildfunc,
1136 "func": buildfunc,
1134 "keyvalue": buildkeyvaluepair,
1137 "keyvalue": buildkeyvaluepair,
1135 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1138 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1136 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1139 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1137 "negate": buildnegate,
1140 "negate": buildnegate,
1138 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1141 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1139 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1142 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1140 }
1143 }
1141
1144
1142 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1145 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1143 methods = exprmethods.copy()
1146 methods = exprmethods.copy()
1144 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1147 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1145
1148
1146 class _aliasrules(parser.basealiasrules):
1149 class _aliasrules(parser.basealiasrules):
1147 """Parsing and expansion rule set of template aliases"""
1150 """Parsing and expansion rule set of template aliases"""
1148 _section = _('template alias')
1151 _section = _('template alias')
1149 _parse = staticmethod(_parseexpr)
1152 _parse = staticmethod(_parseexpr)
1150
1153
1151 @staticmethod
1154 @staticmethod
1152 def _trygetfunc(tree):
1155 def _trygetfunc(tree):
1153 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1156 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1154 None"""
1157 None"""
1155 if tree[0] == 'func' and tree[1][0] == 'symbol':
1158 if tree[0] == 'func' and tree[1][0] == 'symbol':
1156 return tree[1][1], getlist(tree[2])
1159 return tree[1][1], getlist(tree[2])
1157 if tree[0] == '|' and tree[2][0] == 'symbol':
1160 if tree[0] == '|' and tree[2][0] == 'symbol':
1158 return tree[2][1], [tree[1]]
1161 return tree[2][1], [tree[1]]
1159
1162
1160 def expandaliases(tree, aliases):
1163 def expandaliases(tree, aliases):
1161 """Return new tree of aliases are expanded"""
1164 """Return new tree of aliases are expanded"""
1162 aliasmap = _aliasrules.buildmap(aliases)
1165 aliasmap = _aliasrules.buildmap(aliases)
1163 return _aliasrules.expand(aliasmap, tree)
1166 return _aliasrules.expand(aliasmap, tree)
1164
1167
1165 # template engine
1168 # template engine
1166
1169
1167 stringify = templatefilters.stringify
1170 stringify = templatefilters.stringify
1168
1171
1169 def _flatten(thing):
1172 def _flatten(thing):
1170 '''yield a single stream from a possibly nested set of iterators'''
1173 '''yield a single stream from a possibly nested set of iterators'''
1171 thing = templatekw.unwraphybrid(thing)
1174 thing = templatekw.unwraphybrid(thing)
1172 if isinstance(thing, bytes):
1175 if isinstance(thing, bytes):
1173 yield thing
1176 yield thing
1174 elif thing is None:
1177 elif thing is None:
1175 pass
1178 pass
1176 elif not util.safehasattr(thing, '__iter__'):
1179 elif not util.safehasattr(thing, '__iter__'):
1177 yield pycompat.bytestr(thing)
1180 yield pycompat.bytestr(thing)
1178 else:
1181 else:
1179 for i in thing:
1182 for i in thing:
1180 i = templatekw.unwraphybrid(i)
1183 i = templatekw.unwraphybrid(i)
1181 if isinstance(i, bytes):
1184 if isinstance(i, bytes):
1182 yield i
1185 yield i
1183 elif i is None:
1186 elif i is None:
1184 pass
1187 pass
1185 elif not util.safehasattr(i, '__iter__'):
1188 elif not util.safehasattr(i, '__iter__'):
1186 yield pycompat.bytestr(i)
1189 yield pycompat.bytestr(i)
1187 else:
1190 else:
1188 for j in _flatten(i):
1191 for j in _flatten(i):
1189 yield j
1192 yield j
1190
1193
1191 def unquotestring(s):
1194 def unquotestring(s):
1192 '''unwrap quotes if any; otherwise returns unmodified string'''
1195 '''unwrap quotes if any; otherwise returns unmodified string'''
1193 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1196 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1194 return s
1197 return s
1195 return s[1:-1]
1198 return s[1:-1]
1196
1199
1197 class engine(object):
1200 class engine(object):
1198 '''template expansion engine.
1201 '''template expansion engine.
1199
1202
1200 template expansion works like this. a map file contains key=value
1203 template expansion works like this. a map file contains key=value
1201 pairs. if value is quoted, it is treated as string. otherwise, it
1204 pairs. if value is quoted, it is treated as string. otherwise, it
1202 is treated as name of template file.
1205 is treated as name of template file.
1203
1206
1204 templater is asked to expand a key in map. it looks up key, and
1207 templater is asked to expand a key in map. it looks up key, and
1205 looks for strings like this: {foo}. it expands {foo} by looking up
1208 looks for strings like this: {foo}. it expands {foo} by looking up
1206 foo in map, and substituting it. expansion is recursive: it stops
1209 foo in map, and substituting it. expansion is recursive: it stops
1207 when there is no more {foo} to replace.
1210 when there is no more {foo} to replace.
1208
1211
1209 expansion also allows formatting and filtering.
1212 expansion also allows formatting and filtering.
1210
1213
1211 format uses key to expand each item in list. syntax is
1214 format uses key to expand each item in list. syntax is
1212 {key%format}.
1215 {key%format}.
1213
1216
1214 filter uses function to transform value. syntax is
1217 filter uses function to transform value. syntax is
1215 {key|filter1|filter2|...}.'''
1218 {key|filter1|filter2|...}.'''
1216
1219
1217 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1220 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1218 self._loader = loader
1221 self._loader = loader
1219 if filters is None:
1222 if filters is None:
1220 filters = {}
1223 filters = {}
1221 self._filters = filters
1224 self._filters = filters
1222 if defaults is None:
1225 if defaults is None:
1223 defaults = {}
1226 defaults = {}
1224 self._defaults = defaults
1227 self._defaults = defaults
1225 self._aliasmap = _aliasrules.buildmap(aliases)
1228 self._aliasmap = _aliasrules.buildmap(aliases)
1226 self._cache = {} # key: (func, data)
1229 self._cache = {} # key: (func, data)
1227
1230
1228 def _load(self, t):
1231 def _load(self, t):
1229 '''load, parse, and cache a template'''
1232 '''load, parse, and cache a template'''
1230 if t not in self._cache:
1233 if t not in self._cache:
1231 # put poison to cut recursion while compiling 't'
1234 # put poison to cut recursion while compiling 't'
1232 self._cache[t] = (_runrecursivesymbol, t)
1235 self._cache[t] = (_runrecursivesymbol, t)
1233 try:
1236 try:
1234 x = parse(self._loader(t))
1237 x = parse(self._loader(t))
1235 if self._aliasmap:
1238 if self._aliasmap:
1236 x = _aliasrules.expand(self._aliasmap, x)
1239 x = _aliasrules.expand(self._aliasmap, x)
1237 self._cache[t] = compileexp(x, self, methods)
1240 self._cache[t] = compileexp(x, self, methods)
1238 except: # re-raises
1241 except: # re-raises
1239 del self._cache[t]
1242 del self._cache[t]
1240 raise
1243 raise
1241 return self._cache[t]
1244 return self._cache[t]
1242
1245
1243 def process(self, t, mapping):
1246 def process(self, t, mapping):
1244 '''Perform expansion. t is name of map element to expand.
1247 '''Perform expansion. t is name of map element to expand.
1245 mapping contains added elements for use during expansion. Is a
1248 mapping contains added elements for use during expansion. Is a
1246 generator.'''
1249 generator.'''
1247 func, data = self._load(t)
1250 func, data = self._load(t)
1248 return _flatten(func(self, mapping, data))
1251 return _flatten(func(self, mapping, data))
1249
1252
1250 engines = {'default': engine}
1253 engines = {'default': engine}
1251
1254
1252 def stylelist():
1255 def stylelist():
1253 paths = templatepaths()
1256 paths = templatepaths()
1254 if not paths:
1257 if not paths:
1255 return _('no templates found, try `hg debuginstall` for more info')
1258 return _('no templates found, try `hg debuginstall` for more info')
1256 dirlist = os.listdir(paths[0])
1259 dirlist = os.listdir(paths[0])
1257 stylelist = []
1260 stylelist = []
1258 for file in dirlist:
1261 for file in dirlist:
1259 split = file.split(".")
1262 split = file.split(".")
1260 if split[-1] in ('orig', 'rej'):
1263 if split[-1] in ('orig', 'rej'):
1261 continue
1264 continue
1262 if split[0] == "map-cmdline":
1265 if split[0] == "map-cmdline":
1263 stylelist.append(split[1])
1266 stylelist.append(split[1])
1264 return ", ".join(sorted(stylelist))
1267 return ", ".join(sorted(stylelist))
1265
1268
1266 def _readmapfile(mapfile):
1269 def _readmapfile(mapfile):
1267 """Load template elements from the given map file"""
1270 """Load template elements from the given map file"""
1268 if not os.path.exists(mapfile):
1271 if not os.path.exists(mapfile):
1269 raise error.Abort(_("style '%s' not found") % mapfile,
1272 raise error.Abort(_("style '%s' not found") % mapfile,
1270 hint=_("available styles: %s") % stylelist())
1273 hint=_("available styles: %s") % stylelist())
1271
1274
1272 base = os.path.dirname(mapfile)
1275 base = os.path.dirname(mapfile)
1273 conf = config.config(includepaths=templatepaths())
1276 conf = config.config(includepaths=templatepaths())
1274 conf.read(mapfile)
1277 conf.read(mapfile)
1275
1278
1276 cache = {}
1279 cache = {}
1277 tmap = {}
1280 tmap = {}
1278 for key, val in conf[''].items():
1281 for key, val in conf[''].items():
1279 if not val:
1282 if not val:
1280 raise error.ParseError(_('missing value'), conf.source('', key))
1283 raise error.ParseError(_('missing value'), conf.source('', key))
1281 if val[0] in "'\"":
1284 if val[0] in "'\"":
1282 if val[0] != val[-1]:
1285 if val[0] != val[-1]:
1283 raise error.ParseError(_('unmatched quotes'),
1286 raise error.ParseError(_('unmatched quotes'),
1284 conf.source('', key))
1287 conf.source('', key))
1285 cache[key] = unquotestring(val)
1288 cache[key] = unquotestring(val)
1286 elif key == "__base__":
1289 elif key == "__base__":
1287 # treat as a pointer to a base class for this style
1290 # treat as a pointer to a base class for this style
1288 path = util.normpath(os.path.join(base, val))
1291 path = util.normpath(os.path.join(base, val))
1289
1292
1290 # fallback check in template paths
1293 # fallback check in template paths
1291 if not os.path.exists(path):
1294 if not os.path.exists(path):
1292 for p in templatepaths():
1295 for p in templatepaths():
1293 p2 = util.normpath(os.path.join(p, val))
1296 p2 = util.normpath(os.path.join(p, val))
1294 if os.path.isfile(p2):
1297 if os.path.isfile(p2):
1295 path = p2
1298 path = p2
1296 break
1299 break
1297 p3 = util.normpath(os.path.join(p2, "map"))
1300 p3 = util.normpath(os.path.join(p2, "map"))
1298 if os.path.isfile(p3):
1301 if os.path.isfile(p3):
1299 path = p3
1302 path = p3
1300 break
1303 break
1301
1304
1302 bcache, btmap = _readmapfile(path)
1305 bcache, btmap = _readmapfile(path)
1303 for k in bcache:
1306 for k in bcache:
1304 if k not in cache:
1307 if k not in cache:
1305 cache[k] = bcache[k]
1308 cache[k] = bcache[k]
1306 for k in btmap:
1309 for k in btmap:
1307 if k not in tmap:
1310 if k not in tmap:
1308 tmap[k] = btmap[k]
1311 tmap[k] = btmap[k]
1309 else:
1312 else:
1310 val = 'default', val
1313 val = 'default', val
1311 if ':' in val[1]:
1314 if ':' in val[1]:
1312 val = val[1].split(':', 1)
1315 val = val[1].split(':', 1)
1313 tmap[key] = val[0], os.path.join(base, val[1])
1316 tmap[key] = val[0], os.path.join(base, val[1])
1314 return cache, tmap
1317 return cache, tmap
1315
1318
1316 class TemplateNotFound(error.Abort):
1319 class TemplateNotFound(error.Abort):
1317 pass
1320 pass
1318
1321
1319 class templater(object):
1322 class templater(object):
1320
1323
1321 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1324 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1322 minchunk=1024, maxchunk=65536):
1325 minchunk=1024, maxchunk=65536):
1323 '''set up template engine.
1326 '''set up template engine.
1324 filters is dict of functions. each transforms a value into another.
1327 filters is dict of functions. each transforms a value into another.
1325 defaults is dict of default map definitions.
1328 defaults is dict of default map definitions.
1326 aliases is list of alias (name, replacement) pairs.
1329 aliases is list of alias (name, replacement) pairs.
1327 '''
1330 '''
1328 if filters is None:
1331 if filters is None:
1329 filters = {}
1332 filters = {}
1330 if defaults is None:
1333 if defaults is None:
1331 defaults = {}
1334 defaults = {}
1332 if cache is None:
1335 if cache is None:
1333 cache = {}
1336 cache = {}
1334 self.cache = cache.copy()
1337 self.cache = cache.copy()
1335 self.map = {}
1338 self.map = {}
1336 self.filters = templatefilters.filters.copy()
1339 self.filters = templatefilters.filters.copy()
1337 self.filters.update(filters)
1340 self.filters.update(filters)
1338 self.defaults = defaults
1341 self.defaults = defaults
1339 self._aliases = aliases
1342 self._aliases = aliases
1340 self.minchunk, self.maxchunk = minchunk, maxchunk
1343 self.minchunk, self.maxchunk = minchunk, maxchunk
1341 self.ecache = {}
1344 self.ecache = {}
1342
1345
1343 @classmethod
1346 @classmethod
1344 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1347 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1345 minchunk=1024, maxchunk=65536):
1348 minchunk=1024, maxchunk=65536):
1346 """Create templater from the specified map file"""
1349 """Create templater from the specified map file"""
1347 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1350 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1348 cache, tmap = _readmapfile(mapfile)
1351 cache, tmap = _readmapfile(mapfile)
1349 t.cache.update(cache)
1352 t.cache.update(cache)
1350 t.map = tmap
1353 t.map = tmap
1351 return t
1354 return t
1352
1355
1353 def __contains__(self, key):
1356 def __contains__(self, key):
1354 return key in self.cache or key in self.map
1357 return key in self.cache or key in self.map
1355
1358
1356 def load(self, t):
1359 def load(self, t):
1357 '''Get the template for the given template name. Use a local cache.'''
1360 '''Get the template for the given template name. Use a local cache.'''
1358 if t not in self.cache:
1361 if t not in self.cache:
1359 try:
1362 try:
1360 self.cache[t] = util.readfile(self.map[t][1])
1363 self.cache[t] = util.readfile(self.map[t][1])
1361 except KeyError as inst:
1364 except KeyError as inst:
1362 raise TemplateNotFound(_('"%s" not in template map') %
1365 raise TemplateNotFound(_('"%s" not in template map') %
1363 inst.args[0])
1366 inst.args[0])
1364 except IOError as inst:
1367 except IOError as inst:
1365 raise IOError(inst.args[0], _('template file %s: %s') %
1368 raise IOError(inst.args[0], _('template file %s: %s') %
1366 (self.map[t][1], inst.args[1]))
1369 (self.map[t][1], inst.args[1]))
1367 return self.cache[t]
1370 return self.cache[t]
1368
1371
1369 def render(self, mapping):
1372 def render(self, mapping):
1370 """Render the default unnamed template and return result as string"""
1373 """Render the default unnamed template and return result as string"""
1371 return stringify(self('', **mapping))
1374 return stringify(self('', **mapping))
1372
1375
1373 def __call__(self, t, **mapping):
1376 def __call__(self, t, **mapping):
1374 mapping = pycompat.byteskwargs(mapping)
1377 mapping = pycompat.byteskwargs(mapping)
1375 ttype = t in self.map and self.map[t][0] or 'default'
1378 ttype = t in self.map and self.map[t][0] or 'default'
1376 if ttype not in self.ecache:
1379 if ttype not in self.ecache:
1377 try:
1380 try:
1378 ecls = engines[ttype]
1381 ecls = engines[ttype]
1379 except KeyError:
1382 except KeyError:
1380 raise error.Abort(_('invalid template engine: %s') % ttype)
1383 raise error.Abort(_('invalid template engine: %s') % ttype)
1381 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1384 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1382 self._aliases)
1385 self._aliases)
1383 proc = self.ecache[ttype]
1386 proc = self.ecache[ttype]
1384
1387
1385 stream = proc.process(t, mapping)
1388 stream = proc.process(t, mapping)
1386 if self.minchunk:
1389 if self.minchunk:
1387 stream = util.increasingchunks(stream, min=self.minchunk,
1390 stream = util.increasingchunks(stream, min=self.minchunk,
1388 max=self.maxchunk)
1391 max=self.maxchunk)
1389 return stream
1392 return stream
1390
1393
1391 def templatepaths():
1394 def templatepaths():
1392 '''return locations used for template files.'''
1395 '''return locations used for template files.'''
1393 pathsrel = ['templates']
1396 pathsrel = ['templates']
1394 paths = [os.path.normpath(os.path.join(util.datapath, f))
1397 paths = [os.path.normpath(os.path.join(util.datapath, f))
1395 for f in pathsrel]
1398 for f in pathsrel]
1396 return [p for p in paths if os.path.isdir(p)]
1399 return [p for p in paths if os.path.isdir(p)]
1397
1400
1398 def templatepath(name):
1401 def templatepath(name):
1399 '''return location of template file. returns None if not found.'''
1402 '''return location of template file. returns None if not found.'''
1400 for p in templatepaths():
1403 for p in templatepaths():
1401 f = os.path.join(p, name)
1404 f = os.path.join(p, name)
1402 if os.path.exists(f):
1405 if os.path.exists(f):
1403 return f
1406 return f
1404 return None
1407 return None
1405
1408
1406 def stylemap(styles, paths=None):
1409 def stylemap(styles, paths=None):
1407 """Return path to mapfile for a given style.
1410 """Return path to mapfile for a given style.
1408
1411
1409 Searches mapfile in the following locations:
1412 Searches mapfile in the following locations:
1410 1. templatepath/style/map
1413 1. templatepath/style/map
1411 2. templatepath/map-style
1414 2. templatepath/map-style
1412 3. templatepath/map
1415 3. templatepath/map
1413 """
1416 """
1414
1417
1415 if paths is None:
1418 if paths is None:
1416 paths = templatepaths()
1419 paths = templatepaths()
1417 elif isinstance(paths, str):
1420 elif isinstance(paths, str):
1418 paths = [paths]
1421 paths = [paths]
1419
1422
1420 if isinstance(styles, str):
1423 if isinstance(styles, str):
1421 styles = [styles]
1424 styles = [styles]
1422
1425
1423 for style in styles:
1426 for style in styles:
1424 # only plain name is allowed to honor template paths
1427 # only plain name is allowed to honor template paths
1425 if (not style
1428 if (not style
1426 or style in (os.curdir, os.pardir)
1429 or style in (os.curdir, os.pardir)
1427 or pycompat.ossep in style
1430 or pycompat.ossep in style
1428 or pycompat.osaltsep and pycompat.osaltsep in style):
1431 or pycompat.osaltsep and pycompat.osaltsep in style):
1429 continue
1432 continue
1430 locations = [os.path.join(style, 'map'), 'map-' + style]
1433 locations = [os.path.join(style, 'map'), 'map-' + style]
1431 locations.append('map')
1434 locations.append('map')
1432
1435
1433 for path in paths:
1436 for path in paths:
1434 for location in locations:
1437 for location in locations:
1435 mapfile = os.path.join(path, location)
1438 mapfile = os.path.join(path, location)
1436 if os.path.isfile(mapfile):
1439 if os.path.isfile(mapfile):
1437 return style, mapfile
1440 return style, mapfile
1438
1441
1439 raise RuntimeError("No hgweb templates found in %r" % paths)
1442 raise RuntimeError("No hgweb templates found in %r" % paths)
1440
1443
1441 def loadfunction(ui, extname, registrarobj):
1444 def loadfunction(ui, extname, registrarobj):
1442 """Load template function from specified registrarobj
1445 """Load template function from specified registrarobj
1443 """
1446 """
1444 for name, func in registrarobj._table.iteritems():
1447 for name, func in registrarobj._table.iteritems():
1445 funcs[name] = func
1448 funcs[name] = func
1446
1449
1447 # tell hggettext to extract docstrings from these functions:
1450 # tell hggettext to extract docstrings from these functions:
1448 i18nfunctions = funcs.values()
1451 i18nfunctions = funcs.values()
@@ -1,4585 +1,4599 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:
3123
3124 $ hg log -R latesttag -r tip -T '{manifest}\n'
3125 11:2bc6e9006ce2
3126 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3127 string length: 15
3128 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3129 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3130
3131 Test manifest can be join()-ed as before, though it's silly:
3132
3133 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3134 11:2bc6e9006ce2
3135
3122 Test the sub function of templating for expansion:
3136 Test the sub function of templating for expansion:
3123
3137
3124 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3138 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3125 xx
3139 xx
3126
3140
3127 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3141 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3128 hg: parse error: sub got an invalid pattern: [
3142 hg: parse error: sub got an invalid pattern: [
3129 [255]
3143 [255]
3130 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3144 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3131 hg: parse error: sub got an invalid replacement: \1
3145 hg: parse error: sub got an invalid replacement: \1
3132 [255]
3146 [255]
3133
3147
3134 Test the strip function with chars specified:
3148 Test the strip function with chars specified:
3135
3149
3136 $ hg log -R latesttag --template '{desc}\n'
3150 $ hg log -R latesttag --template '{desc}\n'
3137 at3
3151 at3
3138 t5
3152 t5
3139 t4
3153 t4
3140 t3
3154 t3
3141 t2
3155 t2
3142 t1
3156 t1
3143 merge
3157 merge
3144 h2e
3158 h2e
3145 h2d
3159 h2d
3146 h1c
3160 h1c
3147 b
3161 b
3148 a
3162 a
3149
3163
3150 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3164 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3151 at3
3165 at3
3152 5
3166 5
3153 4
3167 4
3154 3
3168 3
3155 2
3169 2
3156 1
3170 1
3157 merg
3171 merg
3158 h2
3172 h2
3159 h2d
3173 h2d
3160 h1c
3174 h1c
3161 b
3175 b
3162 a
3176 a
3163
3177
3164 Test date format:
3178 Test date format:
3165
3179
3166 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3180 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3167 date: 70 01 01 10 +0000
3181 date: 70 01 01 10 +0000
3168 date: 70 01 01 09 +0000
3182 date: 70 01 01 09 +0000
3169 date: 70 01 01 04 +0000
3183 date: 70 01 01 04 +0000
3170 date: 70 01 01 08 +0000
3184 date: 70 01 01 08 +0000
3171 date: 70 01 01 07 +0000
3185 date: 70 01 01 07 +0000
3172 date: 70 01 01 06 +0000
3186 date: 70 01 01 06 +0000
3173 date: 70 01 01 05 +0100
3187 date: 70 01 01 05 +0100
3174 date: 70 01 01 04 +0000
3188 date: 70 01 01 04 +0000
3175 date: 70 01 01 03 +0000
3189 date: 70 01 01 03 +0000
3176 date: 70 01 01 02 +0000
3190 date: 70 01 01 02 +0000
3177 date: 70 01 01 01 +0000
3191 date: 70 01 01 01 +0000
3178 date: 70 01 01 00 +0000
3192 date: 70 01 01 00 +0000
3179
3193
3180 Test invalid date:
3194 Test invalid date:
3181
3195
3182 $ hg log -R latesttag -T '{date(rev)}\n'
3196 $ hg log -R latesttag -T '{date(rev)}\n'
3183 hg: parse error: date expects a date information
3197 hg: parse error: date expects a date information
3184 [255]
3198 [255]
3185
3199
3186 Test integer literal:
3200 Test integer literal:
3187
3201
3188 $ hg debugtemplate -v '{(0)}\n'
3202 $ hg debugtemplate -v '{(0)}\n'
3189 (template
3203 (template
3190 (group
3204 (group
3191 (integer '0'))
3205 (integer '0'))
3192 (string '\n'))
3206 (string '\n'))
3193 0
3207 0
3194 $ hg debugtemplate -v '{(123)}\n'
3208 $ hg debugtemplate -v '{(123)}\n'
3195 (template
3209 (template
3196 (group
3210 (group
3197 (integer '123'))
3211 (integer '123'))
3198 (string '\n'))
3212 (string '\n'))
3199 123
3213 123
3200 $ hg debugtemplate -v '{(-4)}\n'
3214 $ hg debugtemplate -v '{(-4)}\n'
3201 (template
3215 (template
3202 (group
3216 (group
3203 (negate
3217 (negate
3204 (integer '4')))
3218 (integer '4')))
3205 (string '\n'))
3219 (string '\n'))
3206 -4
3220 -4
3207 $ hg debugtemplate '{(-)}\n'
3221 $ hg debugtemplate '{(-)}\n'
3208 hg: parse error at 3: not a prefix: )
3222 hg: parse error at 3: not a prefix: )
3209 [255]
3223 [255]
3210 $ hg debugtemplate '{(-a)}\n'
3224 $ hg debugtemplate '{(-a)}\n'
3211 hg: parse error: negation needs an integer argument
3225 hg: parse error: negation needs an integer argument
3212 [255]
3226 [255]
3213
3227
3214 top-level integer literal is interpreted as symbol (i.e. variable name):
3228 top-level integer literal is interpreted as symbol (i.e. variable name):
3215
3229
3216 $ hg debugtemplate -D 1=one -v '{1}\n'
3230 $ hg debugtemplate -D 1=one -v '{1}\n'
3217 (template
3231 (template
3218 (integer '1')
3232 (integer '1')
3219 (string '\n'))
3233 (string '\n'))
3220 one
3234 one
3221 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3235 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3222 (template
3236 (template
3223 (func
3237 (func
3224 (symbol 'if')
3238 (symbol 'if')
3225 (list
3239 (list
3226 (string 't')
3240 (string 't')
3227 (template
3241 (template
3228 (integer '1'))))
3242 (integer '1'))))
3229 (string '\n'))
3243 (string '\n'))
3230 one
3244 one
3231 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3245 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3232 (template
3246 (template
3233 (|
3247 (|
3234 (integer '1')
3248 (integer '1')
3235 (symbol 'stringify'))
3249 (symbol 'stringify'))
3236 (string '\n'))
3250 (string '\n'))
3237 one
3251 one
3238
3252
3239 unless explicit symbol is expected:
3253 unless explicit symbol is expected:
3240
3254
3241 $ hg log -Ra -r0 -T '{desc|1}\n'
3255 $ hg log -Ra -r0 -T '{desc|1}\n'
3242 hg: parse error: expected a symbol, got 'integer'
3256 hg: parse error: expected a symbol, got 'integer'
3243 [255]
3257 [255]
3244 $ hg log -Ra -r0 -T '{1()}\n'
3258 $ hg log -Ra -r0 -T '{1()}\n'
3245 hg: parse error: expected a symbol, got 'integer'
3259 hg: parse error: expected a symbol, got 'integer'
3246 [255]
3260 [255]
3247
3261
3248 Test string literal:
3262 Test string literal:
3249
3263
3250 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3264 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3251 (template
3265 (template
3252 (string 'string with no template fragment')
3266 (string 'string with no template fragment')
3253 (string '\n'))
3267 (string '\n'))
3254 string with no template fragment
3268 string with no template fragment
3255 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3269 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3256 (template
3270 (template
3257 (template
3271 (template
3258 (string 'template: ')
3272 (string 'template: ')
3259 (symbol 'rev'))
3273 (symbol 'rev'))
3260 (string '\n'))
3274 (string '\n'))
3261 template: 0
3275 template: 0
3262 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3276 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3263 (template
3277 (template
3264 (string 'rawstring: {rev}')
3278 (string 'rawstring: {rev}')
3265 (string '\n'))
3279 (string '\n'))
3266 rawstring: {rev}
3280 rawstring: {rev}
3267 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3281 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3268 (template
3282 (template
3269 (%
3283 (%
3270 (symbol 'files')
3284 (symbol 'files')
3271 (string 'rawstring: {file}'))
3285 (string 'rawstring: {file}'))
3272 (string '\n'))
3286 (string '\n'))
3273 rawstring: {file}
3287 rawstring: {file}
3274
3288
3275 Test string escaping:
3289 Test string escaping:
3276
3290
3277 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3291 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3278 >
3292 >
3279 <>\n<[>
3293 <>\n<[>
3280 <>\n<]>
3294 <>\n<]>
3281 <>\n<
3295 <>\n<
3282
3296
3283 $ hg log -R latesttag -r 0 \
3297 $ hg log -R latesttag -r 0 \
3284 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3298 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3285 >
3299 >
3286 <>\n<[>
3300 <>\n<[>
3287 <>\n<]>
3301 <>\n<]>
3288 <>\n<
3302 <>\n<
3289
3303
3290 $ hg log -R latesttag -r 0 -T esc \
3304 $ hg log -R latesttag -r 0 -T esc \
3291 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3305 > --config templates.esc='>\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 $ cat <<'EOF' > esctmpl
3311 $ cat <<'EOF' > esctmpl
3298 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3312 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3299 > EOF
3313 > EOF
3300 $ hg log -R latesttag -r 0 --style ./esctmpl
3314 $ hg log -R latesttag -r 0 --style ./esctmpl
3301 >
3315 >
3302 <>\n<[>
3316 <>\n<[>
3303 <>\n<]>
3317 <>\n<]>
3304 <>\n<
3318 <>\n<
3305
3319
3306 Test string escaping of quotes:
3320 Test string escaping of quotes:
3307
3321
3308 $ hg log -Ra -r0 -T '{"\""}\n'
3322 $ hg log -Ra -r0 -T '{"\""}\n'
3309 "
3323 "
3310 $ hg log -Ra -r0 -T '{"\\\""}\n'
3324 $ hg log -Ra -r0 -T '{"\\\""}\n'
3311 \"
3325 \"
3312 $ hg log -Ra -r0 -T '{r"\""}\n'
3326 $ hg log -Ra -r0 -T '{r"\""}\n'
3313 \"
3327 \"
3314 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3328 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3315 \\\"
3329 \\\"
3316
3330
3317
3331
3318 $ hg log -Ra -r0 -T '{"\""}\n'
3332 $ hg log -Ra -r0 -T '{"\""}\n'
3319 "
3333 "
3320 $ hg log -Ra -r0 -T '{"\\\""}\n'
3334 $ hg log -Ra -r0 -T '{"\\\""}\n'
3321 \"
3335 \"
3322 $ hg log -Ra -r0 -T '{r"\""}\n'
3336 $ hg log -Ra -r0 -T '{r"\""}\n'
3323 \"
3337 \"
3324 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3338 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3325 \\\"
3339 \\\"
3326
3340
3327 Test exception in quoted template. single backslash before quotation mark is
3341 Test exception in quoted template. single backslash before quotation mark is
3328 stripped before parsing:
3342 stripped before parsing:
3329
3343
3330 $ cat <<'EOF' > escquotetmpl
3344 $ cat <<'EOF' > escquotetmpl
3331 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3345 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3332 > EOF
3346 > EOF
3333 $ cd latesttag
3347 $ cd latesttag
3334 $ hg log -r 2 --style ../escquotetmpl
3348 $ hg log -r 2 --style ../escquotetmpl
3335 " \" \" \\" head1
3349 " \" \" \\" head1
3336
3350
3337 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3351 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3338 valid
3352 valid
3339 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3353 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3340 valid
3354 valid
3341
3355
3342 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3356 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3343 _evalifliteral() templates (issue4733):
3357 _evalifliteral() templates (issue4733):
3344
3358
3345 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3359 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3346 "2
3360 "2
3347 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3361 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3348 "2
3362 "2
3349 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3363 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3350 "2
3364 "2
3351
3365
3352 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3366 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3353 \"
3367 \"
3354 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3368 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3355 \"
3369 \"
3356 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3370 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3357 \"
3371 \"
3358
3372
3359 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3373 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3360 \\\"
3374 \\\"
3361 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3375 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3362 \\\"
3376 \\\"
3363 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3377 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3364 \\\"
3378 \\\"
3365
3379
3366 escaped single quotes and errors:
3380 escaped single quotes and errors:
3367
3381
3368 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3382 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3369 foo
3383 foo
3370 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3384 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3371 foo
3385 foo
3372 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3386 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3373 hg: parse error at 21: unterminated string
3387 hg: parse error at 21: unterminated string
3374 [255]
3388 [255]
3375 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3389 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3376 hg: parse error: trailing \ in string
3390 hg: parse error: trailing \ in string
3377 [255]
3391 [255]
3378 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3392 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3379 hg: parse error: trailing \ in string
3393 hg: parse error: trailing \ in string
3380 [255]
3394 [255]
3381
3395
3382 $ cd ..
3396 $ cd ..
3383
3397
3384 Test leading backslashes:
3398 Test leading backslashes:
3385
3399
3386 $ cd latesttag
3400 $ cd latesttag
3387 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3401 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3388 {rev} {file}
3402 {rev} {file}
3389 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3403 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3390 \2 \head1
3404 \2 \head1
3391 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3405 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3392 \{rev} \{file}
3406 \{rev} \{file}
3393 $ cd ..
3407 $ cd ..
3394
3408
3395 Test leading backslashes in "if" expression (issue4714):
3409 Test leading backslashes in "if" expression (issue4714):
3396
3410
3397 $ cd latesttag
3411 $ cd latesttag
3398 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3412 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3399 {rev} \{rev}
3413 {rev} \{rev}
3400 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3414 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3401 \2 \\{rev}
3415 \2 \\{rev}
3402 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3416 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3403 \{rev} \\\{rev}
3417 \{rev} \\\{rev}
3404 $ cd ..
3418 $ cd ..
3405
3419
3406 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3420 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3407
3421
3408 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3422 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3409 \x6e
3423 \x6e
3410 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3424 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3411 \x5c\x786e
3425 \x5c\x786e
3412 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3426 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3413 \x6e
3427 \x6e
3414 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3428 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3415 \x5c\x786e
3429 \x5c\x786e
3416
3430
3417 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3431 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3418 \x6e
3432 \x6e
3419 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3433 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3420 \x5c\x786e
3434 \x5c\x786e
3421 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3435 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3422 \x6e
3436 \x6e
3423 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3437 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3424 \x5c\x786e
3438 \x5c\x786e
3425
3439
3426 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3440 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3427 fourth
3441 fourth
3428 second
3442 second
3429 third
3443 third
3430 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3444 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3431 fourth\nsecond\nthird
3445 fourth\nsecond\nthird
3432
3446
3433 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3447 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3434 <p>
3448 <p>
3435 1st
3449 1st
3436 </p>
3450 </p>
3437 <p>
3451 <p>
3438 2nd
3452 2nd
3439 </p>
3453 </p>
3440 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3454 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3441 <p>
3455 <p>
3442 1st\n\n2nd
3456 1st\n\n2nd
3443 </p>
3457 </p>
3444 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3458 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3445 1st
3459 1st
3446
3460
3447 2nd
3461 2nd
3448
3462
3449 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3463 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3450 o perso
3464 o perso
3451 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3465 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3452 no person
3466 no person
3453 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3467 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3454 o perso
3468 o perso
3455 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3469 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3456 no perso
3470 no perso
3457
3471
3458 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3472 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3459 -o perso-
3473 -o perso-
3460 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3474 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3461 no person
3475 no person
3462 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3476 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3463 \x2do perso\x2d
3477 \x2do perso\x2d
3464 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3478 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3465 -o perso-
3479 -o perso-
3466 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3480 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3467 \x2do perso\x6e
3481 \x2do perso\x6e
3468
3482
3469 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3483 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3470 fourth
3484 fourth
3471 second
3485 second
3472 third
3486 third
3473
3487
3474 Test string escaping in nested expression:
3488 Test string escaping in nested expression:
3475
3489
3476 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3490 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3477 fourth\x6esecond\x6ethird
3491 fourth\x6esecond\x6ethird
3478 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3492 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3479 fourth\x6esecond\x6ethird
3493 fourth\x6esecond\x6ethird
3480
3494
3481 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3495 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3482 fourth\x6esecond\x6ethird
3496 fourth\x6esecond\x6ethird
3483 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3497 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3484 fourth\x5c\x786esecond\x5c\x786ethird
3498 fourth\x5c\x786esecond\x5c\x786ethird
3485
3499
3486 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3500 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3487 3:\x6eo user, \x6eo domai\x6e
3501 3:\x6eo user, \x6eo domai\x6e
3488 4:\x5c\x786eew bra\x5c\x786ech
3502 4:\x5c\x786eew bra\x5c\x786ech
3489
3503
3490 Test quotes in nested expression are evaluated just like a $(command)
3504 Test quotes in nested expression are evaluated just like a $(command)
3491 substitution in POSIX shells:
3505 substitution in POSIX shells:
3492
3506
3493 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3507 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3494 8:95c24699272e
3508 8:95c24699272e
3495 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3509 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3496 {8} "95c24699272e"
3510 {8} "95c24699272e"
3497
3511
3498 Test recursive evaluation:
3512 Test recursive evaluation:
3499
3513
3500 $ hg init r
3514 $ hg init r
3501 $ cd r
3515 $ cd r
3502 $ echo a > a
3516 $ echo a > a
3503 $ hg ci -Am '{rev}'
3517 $ hg ci -Am '{rev}'
3504 adding a
3518 adding a
3505 $ hg log -r 0 --template '{if(rev, desc)}\n'
3519 $ hg log -r 0 --template '{if(rev, desc)}\n'
3506 {rev}
3520 {rev}
3507 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3521 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3508 test 0
3522 test 0
3509
3523
3510 $ hg branch -q 'text.{rev}'
3524 $ hg branch -q 'text.{rev}'
3511 $ echo aa >> aa
3525 $ echo aa >> aa
3512 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3526 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3513
3527
3514 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3528 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3515 {node|short}desc to
3529 {node|short}desc to
3516 text.{rev}be wrapped
3530 text.{rev}be wrapped
3517 text.{rev}desc to be
3531 text.{rev}desc to be
3518 text.{rev}wrapped (no-eol)
3532 text.{rev}wrapped (no-eol)
3519 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3533 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3520 bcc7ff960b8e:desc to
3534 bcc7ff960b8e:desc to
3521 text.1:be wrapped
3535 text.1:be wrapped
3522 text.1:desc to be
3536 text.1:desc to be
3523 text.1:wrapped (no-eol)
3537 text.1:wrapped (no-eol)
3524 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3538 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3525 hg: parse error: fill expects an integer width
3539 hg: parse error: fill expects an integer width
3526 [255]
3540 [255]
3527
3541
3528 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3542 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3529 bcc7ff960b8e:desc to be
3543 bcc7ff960b8e:desc to be
3530 termwidth.1:wrapped desc
3544 termwidth.1:wrapped desc
3531 termwidth.1:to be wrapped (no-eol)
3545 termwidth.1:to be wrapped (no-eol)
3532
3546
3533 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3547 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3534 {node|short} (no-eol)
3548 {node|short} (no-eol)
3535 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3549 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3536 bcc-ff---b-e (no-eol)
3550 bcc-ff---b-e (no-eol)
3537
3551
3538 $ cat >> .hg/hgrc <<EOF
3552 $ cat >> .hg/hgrc <<EOF
3539 > [extensions]
3553 > [extensions]
3540 > color=
3554 > color=
3541 > [color]
3555 > [color]
3542 > mode=ansi
3556 > mode=ansi
3543 > text.{rev} = red
3557 > text.{rev} = red
3544 > text.1 = green
3558 > text.1 = green
3545 > EOF
3559 > EOF
3546 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3560 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3547 \x1b[0;31mtext\x1b[0m (esc)
3561 \x1b[0;31mtext\x1b[0m (esc)
3548 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3562 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3549 \x1b[0;32mtext\x1b[0m (esc)
3563 \x1b[0;32mtext\x1b[0m (esc)
3550
3564
3551 color effect can be specified without quoting:
3565 color effect can be specified without quoting:
3552
3566
3553 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3567 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3554 \x1b[0;31mtext\x1b[0m (esc)
3568 \x1b[0;31mtext\x1b[0m (esc)
3555
3569
3556 color effects can be nested (issue5413)
3570 color effects can be nested (issue5413)
3557
3571
3558 $ hg debugtemplate --color=always \
3572 $ hg debugtemplate --color=always \
3559 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3573 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3560 \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)
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)
3561
3575
3562 pad() should interact well with color codes (issue5416)
3576 pad() should interact well with color codes (issue5416)
3563
3577
3564 $ hg debugtemplate --color=always \
3578 $ hg debugtemplate --color=always \
3565 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3579 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3566 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3580 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3567
3581
3568 label should be no-op if color is disabled:
3582 label should be no-op if color is disabled:
3569
3583
3570 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3584 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3571 text
3585 text
3572 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3586 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3573 text
3587 text
3574
3588
3575 Test branches inside if statement:
3589 Test branches inside if statement:
3576
3590
3577 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3591 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3578 no
3592 no
3579
3593
3580 Test dict constructor:
3594 Test dict constructor:
3581
3595
3582 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3596 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3583 y=f7769ec2ab97 x=0
3597 y=f7769ec2ab97 x=0
3584 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3598 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3585 x=0
3599 x=0
3586 y=f7769ec2ab97
3600 y=f7769ec2ab97
3587 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3601 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3588 {"x": 0, "y": "f7769ec2ab97"}
3602 {"x": 0, "y": "f7769ec2ab97"}
3589 $ hg log -r 0 -T '{dict()|json}\n'
3603 $ hg log -r 0 -T '{dict()|json}\n'
3590 {}
3604 {}
3591
3605
3592 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3606 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3593 rev=0 node=f7769ec2ab97
3607 rev=0 node=f7769ec2ab97
3594 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3608 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3595 rev=0 node=f7769ec2ab97
3609 rev=0 node=f7769ec2ab97
3596
3610
3597 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3611 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3598 hg: parse error: duplicated dict key 'rev' inferred
3612 hg: parse error: duplicated dict key 'rev' inferred
3599 [255]
3613 [255]
3600 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3614 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3601 hg: parse error: duplicated dict key 'node' inferred
3615 hg: parse error: duplicated dict key 'node' inferred
3602 [255]
3616 [255]
3603 $ hg log -r 0 -T '{dict(1 + 2)}'
3617 $ hg log -r 0 -T '{dict(1 + 2)}'
3604 hg: parse error: dict key cannot be inferred
3618 hg: parse error: dict key cannot be inferred
3605 [255]
3619 [255]
3606
3620
3607 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3621 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3608 hg: parse error: dict got multiple values for keyword argument 'x'
3622 hg: parse error: dict got multiple values for keyword argument 'x'
3609 [255]
3623 [255]
3610
3624
3611 Test get function:
3625 Test get function:
3612
3626
3613 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3627 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3614 default
3628 default
3615 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3629 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3616 default
3630 default
3617 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3631 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3618 hg: parse error: get() expects a dict as first argument
3632 hg: parse error: get() expects a dict as first argument
3619 [255]
3633 [255]
3620
3634
3621 Test json filter applied to hybrid object:
3635 Test json filter applied to hybrid object:
3622
3636
3623 $ hg log -r0 -T '{files|json}\n'
3637 $ hg log -r0 -T '{files|json}\n'
3624 ["a"]
3638 ["a"]
3625 $ hg log -r0 -T '{extras|json}\n'
3639 $ hg log -r0 -T '{extras|json}\n'
3626 {"branch": "default"}
3640 {"branch": "default"}
3627
3641
3628 Test localdate(date, tz) function:
3642 Test localdate(date, tz) function:
3629
3643
3630 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3644 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3631 1970-01-01 09:00 +0900
3645 1970-01-01 09:00 +0900
3632 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3646 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3633 1970-01-01 00:00 +0000
3647 1970-01-01 00:00 +0000
3634 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3648 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3635 hg: parse error: localdate expects a timezone
3649 hg: parse error: localdate expects a timezone
3636 [255]
3650 [255]
3637 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3651 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3638 1970-01-01 02:00 +0200
3652 1970-01-01 02:00 +0200
3639 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3653 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3640 1970-01-01 00:00 +0000
3654 1970-01-01 00:00 +0000
3641 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3655 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3642 1970-01-01 00:00 +0000
3656 1970-01-01 00:00 +0000
3643 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3657 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3644 hg: parse error: localdate expects a timezone
3658 hg: parse error: localdate expects a timezone
3645 [255]
3659 [255]
3646 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3660 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3647 hg: parse error: localdate expects a timezone
3661 hg: parse error: localdate expects a timezone
3648 [255]
3662 [255]
3649
3663
3650 Test shortest(node) function:
3664 Test shortest(node) function:
3651
3665
3652 $ echo b > b
3666 $ echo b > b
3653 $ hg ci -qAm b
3667 $ hg ci -qAm b
3654 $ hg log --template '{shortest(node)}\n'
3668 $ hg log --template '{shortest(node)}\n'
3655 e777
3669 e777
3656 bcc7
3670 bcc7
3657 f776
3671 f776
3658 $ hg log --template '{shortest(node, 10)}\n'
3672 $ hg log --template '{shortest(node, 10)}\n'
3659 e777603221
3673 e777603221
3660 bcc7ff960b
3674 bcc7ff960b
3661 f7769ec2ab
3675 f7769ec2ab
3662 $ hg log --template '{node|shortest}\n' -l1
3676 $ hg log --template '{node|shortest}\n' -l1
3663 e777
3677 e777
3664
3678
3665 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3679 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3666 f7769ec2ab
3680 f7769ec2ab
3667 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3681 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3668 hg: parse error: shortest() expects an integer minlength
3682 hg: parse error: shortest() expects an integer minlength
3669 [255]
3683 [255]
3670
3684
3671 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3685 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3672 ffff
3686 ffff
3673
3687
3674 $ cd ..
3688 $ cd ..
3675
3689
3676 Test shortest(node) with the repo having short hash collision:
3690 Test shortest(node) with the repo having short hash collision:
3677
3691
3678 $ hg init hashcollision
3692 $ hg init hashcollision
3679 $ cd hashcollision
3693 $ cd hashcollision
3680 $ cat <<EOF >> .hg/hgrc
3694 $ cat <<EOF >> .hg/hgrc
3681 > [experimental]
3695 > [experimental]
3682 > stabilization = createmarkers
3696 > stabilization = createmarkers
3683 > EOF
3697 > EOF
3684 $ echo 0 > a
3698 $ echo 0 > a
3685 $ hg ci -qAm 0
3699 $ hg ci -qAm 0
3686 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3700 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3687 > hg up -q 0
3701 > hg up -q 0
3688 > echo $i > a
3702 > echo $i > a
3689 > hg ci -qm $i
3703 > hg ci -qm $i
3690 > done
3704 > done
3691 $ hg up -q null
3705 $ hg up -q null
3692 $ hg log -r0: -T '{rev}:{node}\n'
3706 $ hg log -r0: -T '{rev}:{node}\n'
3693 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3707 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3694 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3708 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3695 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3709 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3696 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3710 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3697 4:10776689e627b465361ad5c296a20a487e153ca4
3711 4:10776689e627b465361ad5c296a20a487e153ca4
3698 5:a00be79088084cb3aff086ab799f8790e01a976b
3712 5:a00be79088084cb3aff086ab799f8790e01a976b
3699 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3713 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3700 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3714 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3701 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3715 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3702 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3716 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3703 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3717 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3704 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3718 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3705 obsoleted 1 changesets
3719 obsoleted 1 changesets
3706 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3720 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3707 obsoleted 1 changesets
3721 obsoleted 1 changesets
3708 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3722 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3709 obsoleted 1 changesets
3723 obsoleted 1 changesets
3710
3724
3711 nodes starting with '11' (we don't have the revision number '11' though)
3725 nodes starting with '11' (we don't have the revision number '11' though)
3712
3726
3713 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3727 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3714 1:1142
3728 1:1142
3715 2:1140
3729 2:1140
3716 3:11d
3730 3:11d
3717
3731
3718 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3732 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3719
3733
3720 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3734 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3721 6:a0b
3735 6:a0b
3722 7:a04
3736 7:a04
3723
3737
3724 node '10' conflicts with the revision number '10' even if it is hidden
3738 node '10' conflicts with the revision number '10' even if it is hidden
3725 (we could exclude hidden revision numbers, but currently we don't)
3739 (we could exclude hidden revision numbers, but currently we don't)
3726
3740
3727 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3741 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3728 4:107
3742 4:107
3729 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3743 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3730 4:107
3744 4:107
3731
3745
3732 node 'c562' should be unique if the other 'c562' nodes are hidden
3746 node 'c562' should be unique if the other 'c562' nodes are hidden
3733 (but we don't try the slow path to filter out hidden nodes for now)
3747 (but we don't try the slow path to filter out hidden nodes for now)
3734
3748
3735 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3749 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3736 8:c5625
3750 8:c5625
3737 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3751 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3738 8:c5625
3752 8:c5625
3739 9:c5623
3753 9:c5623
3740 10:c562d
3754 10:c562d
3741
3755
3742 $ cd ..
3756 $ cd ..
3743
3757
3744 Test pad function
3758 Test pad function
3745
3759
3746 $ cd r
3760 $ cd r
3747
3761
3748 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3762 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3749 2 test
3763 2 test
3750 1 {node|short}
3764 1 {node|short}
3751 0 test
3765 0 test
3752
3766
3753 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3767 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3754 2 test
3768 2 test
3755 1 {node|short}
3769 1 {node|short}
3756 0 test
3770 0 test
3757
3771
3758 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3772 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3759 2------------------- test
3773 2------------------- test
3760 1------------------- {node|short}
3774 1------------------- {node|short}
3761 0------------------- test
3775 0------------------- test
3762
3776
3763 Test template string in pad function
3777 Test template string in pad function
3764
3778
3765 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3779 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3766 {0} test
3780 {0} test
3767
3781
3768 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3782 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3769 \{rev} test
3783 \{rev} test
3770
3784
3771 Test width argument passed to pad function
3785 Test width argument passed to pad function
3772
3786
3773 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3787 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3774 0 test
3788 0 test
3775 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3789 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3776 hg: parse error: pad() expects an integer width
3790 hg: parse error: pad() expects an integer width
3777 [255]
3791 [255]
3778
3792
3779 Test invalid fillchar passed to pad function
3793 Test invalid fillchar passed to pad function
3780
3794
3781 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3795 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3782 hg: parse error: pad() expects a single fill character
3796 hg: parse error: pad() expects a single fill character
3783 [255]
3797 [255]
3784 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3798 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3785 hg: parse error: pad() expects a single fill character
3799 hg: parse error: pad() expects a single fill character
3786 [255]
3800 [255]
3787
3801
3788 Test boolean argument passed to pad function
3802 Test boolean argument passed to pad function
3789
3803
3790 no crash
3804 no crash
3791
3805
3792 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3806 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3793 ---------0
3807 ---------0
3794
3808
3795 string/literal
3809 string/literal
3796
3810
3797 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3811 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3798 ---------0
3812 ---------0
3799 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3813 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3800 0---------
3814 0---------
3801 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3815 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3802 0---------
3816 0---------
3803
3817
3804 unknown keyword is evaluated to ''
3818 unknown keyword is evaluated to ''
3805
3819
3806 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3820 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3807 0---------
3821 0---------
3808
3822
3809 Test separate function
3823 Test separate function
3810
3824
3811 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3825 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3812 a-b-c
3826 a-b-c
3813 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3827 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3814 0:f7769ec2ab97 test default
3828 0:f7769ec2ab97 test default
3815 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3829 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3816 a \x1b[0;31mb\x1b[0m c d (esc)
3830 a \x1b[0;31mb\x1b[0m c d (esc)
3817
3831
3818 Test boolean expression/literal passed to if function
3832 Test boolean expression/literal passed to if function
3819
3833
3820 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3834 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3821 rev 0 is True
3835 rev 0 is True
3822 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3836 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3823 literal 0 is True as well
3837 literal 0 is True as well
3824 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3838 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3825 empty string is False
3839 empty string is False
3826 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3840 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3827 empty list is False
3841 empty list is False
3828 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3842 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3829 true is True
3843 true is True
3830 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3844 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3831 false is False
3845 false is False
3832 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3846 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3833 non-empty string is True
3847 non-empty string is True
3834
3848
3835 Test ifcontains function
3849 Test ifcontains function
3836
3850
3837 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3851 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3838 2 is in the string
3852 2 is in the string
3839 1 is not
3853 1 is not
3840 0 is in the string
3854 0 is in the string
3841
3855
3842 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3856 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3843 2 is in the string
3857 2 is in the string
3844 1 is not
3858 1 is not
3845 0 is in the string
3859 0 is in the string
3846
3860
3847 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3861 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3848 2 did not add a
3862 2 did not add a
3849 1 did not add a
3863 1 did not add a
3850 0 added a
3864 0 added a
3851
3865
3852 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3866 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3853 2 is parent of 1
3867 2 is parent of 1
3854 1
3868 1
3855 0
3869 0
3856
3870
3857 Test revset function
3871 Test revset function
3858
3872
3859 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3873 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3860 2 current rev
3874 2 current rev
3861 1 not current rev
3875 1 not current rev
3862 0 not current rev
3876 0 not current rev
3863
3877
3864 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3878 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3865 2 match rev
3879 2 match rev
3866 1 match rev
3880 1 match rev
3867 0 not match rev
3881 0 not match rev
3868
3882
3869 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3883 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3870 2 Parents: 1
3884 2 Parents: 1
3871 1 Parents: 0
3885 1 Parents: 0
3872 0 Parents:
3886 0 Parents:
3873
3887
3874 $ cat >> .hg/hgrc <<EOF
3888 $ cat >> .hg/hgrc <<EOF
3875 > [revsetalias]
3889 > [revsetalias]
3876 > myparents(\$1) = parents(\$1)
3890 > myparents(\$1) = parents(\$1)
3877 > EOF
3891 > EOF
3878 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3892 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3879 2 Parents: 1
3893 2 Parents: 1
3880 1 Parents: 0
3894 1 Parents: 0
3881 0 Parents:
3895 0 Parents:
3882
3896
3883 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3897 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3884 Rev: 2
3898 Rev: 2
3885 Ancestor: 0
3899 Ancestor: 0
3886 Ancestor: 1
3900 Ancestor: 1
3887 Ancestor: 2
3901 Ancestor: 2
3888
3902
3889 Rev: 1
3903 Rev: 1
3890 Ancestor: 0
3904 Ancestor: 0
3891 Ancestor: 1
3905 Ancestor: 1
3892
3906
3893 Rev: 0
3907 Rev: 0
3894 Ancestor: 0
3908 Ancestor: 0
3895
3909
3896 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3910 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3897 2
3911 2
3898
3912
3899 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3913 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3900 2
3914 2
3901
3915
3902 a list template is evaluated for each item of revset/parents
3916 a list template is evaluated for each item of revset/parents
3903
3917
3904 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3918 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3905 2 p: 1:bcc7ff960b8e
3919 2 p: 1:bcc7ff960b8e
3906 1 p: 0:f7769ec2ab97
3920 1 p: 0:f7769ec2ab97
3907 0 p:
3921 0 p:
3908
3922
3909 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3923 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3910 2 p: 1:bcc7ff960b8e -1:000000000000
3924 2 p: 1:bcc7ff960b8e -1:000000000000
3911 1 p: 0:f7769ec2ab97 -1:000000000000
3925 1 p: 0:f7769ec2ab97 -1:000000000000
3912 0 p: -1:000000000000 -1:000000000000
3926 0 p: -1:000000000000 -1:000000000000
3913
3927
3914 therefore, 'revcache' should be recreated for each rev
3928 therefore, 'revcache' should be recreated for each rev
3915
3929
3916 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3930 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3917 2 aa b
3931 2 aa b
3918 p
3932 p
3919 1
3933 1
3920 p a
3934 p a
3921 0 a
3935 0 a
3922 p
3936 p
3923
3937
3924 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3938 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3925 2 aa b
3939 2 aa b
3926 p
3940 p
3927 1
3941 1
3928 p a
3942 p a
3929 0 a
3943 0 a
3930 p
3944 p
3931
3945
3932 a revset item must be evaluated as an integer revision, not an offset from tip
3946 a revset item must be evaluated as an integer revision, not an offset from tip
3933
3947
3934 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3948 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3935 -1:000000000000
3949 -1:000000000000
3936 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3950 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3937 -1:000000000000
3951 -1:000000000000
3938
3952
3939 join() should pick '{rev}' from revset items:
3953 join() should pick '{rev}' from revset items:
3940
3954
3941 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3955 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3942 4, 5
3956 4, 5
3943
3957
3944 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3958 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3945 default. join() should agree with the default formatting:
3959 default. join() should agree with the default formatting:
3946
3960
3947 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3961 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3948 5:13207e5a10d9, 4:bbe44766e73d
3962 5:13207e5a10d9, 4:bbe44766e73d
3949
3963
3950 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3964 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3951 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3965 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3952 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3966 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3953
3967
3954 Test files function
3968 Test files function
3955
3969
3956 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3970 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3957 2
3971 2
3958 a
3972 a
3959 aa
3973 aa
3960 b
3974 b
3961 1
3975 1
3962 a
3976 a
3963 0
3977 0
3964 a
3978 a
3965
3979
3966 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3980 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3967 2
3981 2
3968 aa
3982 aa
3969 1
3983 1
3970
3984
3971 0
3985 0
3972
3986
3973
3987
3974 Test relpath function
3988 Test relpath function
3975
3989
3976 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
3990 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
3977 a
3991 a
3978 $ cd ..
3992 $ cd ..
3979 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
3993 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
3980 r/a
3994 r/a
3981 $ cd r
3995 $ cd r
3982
3996
3983 Test active bookmark templating
3997 Test active bookmark templating
3984
3998
3985 $ hg book foo
3999 $ hg book foo
3986 $ hg book bar
4000 $ hg book bar
3987 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4001 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3988 2 bar* foo
4002 2 bar* foo
3989 1
4003 1
3990 0
4004 0
3991 $ hg log --template "{rev} {activebookmark}\n"
4005 $ hg log --template "{rev} {activebookmark}\n"
3992 2 bar
4006 2 bar
3993 1
4007 1
3994 0
4008 0
3995 $ hg bookmarks --inactive bar
4009 $ hg bookmarks --inactive bar
3996 $ hg log --template "{rev} {activebookmark}\n"
4010 $ hg log --template "{rev} {activebookmark}\n"
3997 2
4011 2
3998 1
4012 1
3999 0
4013 0
4000 $ hg book -r1 baz
4014 $ hg book -r1 baz
4001 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4015 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4002 2 bar foo
4016 2 bar foo
4003 1 baz
4017 1 baz
4004 0
4018 0
4005 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4019 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4006 2 t
4020 2 t
4007 1 f
4021 1 f
4008 0 f
4022 0 f
4009
4023
4010 Test namespaces dict
4024 Test namespaces dict
4011
4025
4012 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4026 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4013 2
4027 2
4014 bookmarks color=bookmark builtin=True
4028 bookmarks color=bookmark builtin=True
4015 bar,foo
4029 bar,foo
4016 tags color=tag builtin=True
4030 tags color=tag builtin=True
4017 tip
4031 tip
4018 branches color=branch builtin=True
4032 branches color=branch builtin=True
4019 text.{rev}
4033 text.{rev}
4020 revnames color=revname builtin=False
4034 revnames color=revname builtin=False
4021 r2
4035 r2
4022
4036
4023 1
4037 1
4024 bookmarks color=bookmark builtin=True
4038 bookmarks color=bookmark builtin=True
4025 baz
4039 baz
4026 tags color=tag builtin=True
4040 tags color=tag builtin=True
4027
4041
4028 branches color=branch builtin=True
4042 branches color=branch builtin=True
4029 text.{rev}
4043 text.{rev}
4030 revnames color=revname builtin=False
4044 revnames color=revname builtin=False
4031 r1
4045 r1
4032
4046
4033 0
4047 0
4034 bookmarks color=bookmark builtin=True
4048 bookmarks color=bookmark builtin=True
4035
4049
4036 tags color=tag builtin=True
4050 tags color=tag builtin=True
4037
4051
4038 branches color=branch builtin=True
4052 branches color=branch builtin=True
4039 default
4053 default
4040 revnames color=revname builtin=False
4054 revnames color=revname builtin=False
4041 r0
4055 r0
4042
4056
4043 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4057 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4044 bookmarks: bar foo
4058 bookmarks: bar foo
4045 tags: tip
4059 tags: tip
4046 branches: text.{rev}
4060 branches: text.{rev}
4047 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4061 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4048 bookmarks:
4062 bookmarks:
4049 bar
4063 bar
4050 foo
4064 foo
4051 tags:
4065 tags:
4052 tip
4066 tip
4053 branches:
4067 branches:
4054 text.{rev}
4068 text.{rev}
4055 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4069 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4056 bar
4070 bar
4057 foo
4071 foo
4058
4072
4059 Test stringify on sub expressions
4073 Test stringify on sub expressions
4060
4074
4061 $ cd ..
4075 $ cd ..
4062 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4076 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4063 fourth, second, third
4077 fourth, second, third
4064 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4078 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4065 abc
4079 abc
4066
4080
4067 Test splitlines
4081 Test splitlines
4068
4082
4069 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4083 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4070 @ foo Modify, add, remove, rename
4084 @ foo Modify, add, remove, rename
4071 |
4085 |
4072 o foo future
4086 o foo future
4073 |
4087 |
4074 o foo third
4088 o foo third
4075 |
4089 |
4076 o foo second
4090 o foo second
4077
4091
4078 o foo merge
4092 o foo merge
4079 |\
4093 |\
4080 | o foo new head
4094 | o foo new head
4081 | |
4095 | |
4082 o | foo new branch
4096 o | foo new branch
4083 |/
4097 |/
4084 o foo no user, no domain
4098 o foo no user, no domain
4085 |
4099 |
4086 o foo no person
4100 o foo no person
4087 |
4101 |
4088 o foo other 1
4102 o foo other 1
4089 | foo other 2
4103 | foo other 2
4090 | foo
4104 | foo
4091 | foo other 3
4105 | foo other 3
4092 o foo line 1
4106 o foo line 1
4093 foo line 2
4107 foo line 2
4094
4108
4095 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4109 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4096 line 1 line 2
4110 line 1 line 2
4097 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4111 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4098 line 1|line 2
4112 line 1|line 2
4099
4113
4100 Test startswith
4114 Test startswith
4101 $ hg log -Gv -R a --template "{startswith(desc)}"
4115 $ hg log -Gv -R a --template "{startswith(desc)}"
4102 hg: parse error: startswith expects two arguments
4116 hg: parse error: startswith expects two arguments
4103 [255]
4117 [255]
4104
4118
4105 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4119 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4106 @
4120 @
4107 |
4121 |
4108 o
4122 o
4109 |
4123 |
4110 o
4124 o
4111 |
4125 |
4112 o
4126 o
4113
4127
4114 o
4128 o
4115 |\
4129 |\
4116 | o
4130 | o
4117 | |
4131 | |
4118 o |
4132 o |
4119 |/
4133 |/
4120 o
4134 o
4121 |
4135 |
4122 o
4136 o
4123 |
4137 |
4124 o
4138 o
4125 |
4139 |
4126 o line 1
4140 o line 1
4127 line 2
4141 line 2
4128
4142
4129 Test bad template with better error message
4143 Test bad template with better error message
4130
4144
4131 $ hg log -Gv -R a --template '{desc|user()}'
4145 $ hg log -Gv -R a --template '{desc|user()}'
4132 hg: parse error: expected a symbol, got 'func'
4146 hg: parse error: expected a symbol, got 'func'
4133 [255]
4147 [255]
4134
4148
4135 Test word function (including index out of bounds graceful failure)
4149 Test word function (including index out of bounds graceful failure)
4136
4150
4137 $ hg log -Gv -R a --template "{word('1', desc)}"
4151 $ hg log -Gv -R a --template "{word('1', desc)}"
4138 @ add,
4152 @ add,
4139 |
4153 |
4140 o
4154 o
4141 |
4155 |
4142 o
4156 o
4143 |
4157 |
4144 o
4158 o
4145
4159
4146 o
4160 o
4147 |\
4161 |\
4148 | o head
4162 | o head
4149 | |
4163 | |
4150 o | branch
4164 o | branch
4151 |/
4165 |/
4152 o user,
4166 o user,
4153 |
4167 |
4154 o person
4168 o person
4155 |
4169 |
4156 o 1
4170 o 1
4157 |
4171 |
4158 o 1
4172 o 1
4159
4173
4160
4174
4161 Test word third parameter used as splitter
4175 Test word third parameter used as splitter
4162
4176
4163 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4177 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4164 @ M
4178 @ M
4165 |
4179 |
4166 o future
4180 o future
4167 |
4181 |
4168 o third
4182 o third
4169 |
4183 |
4170 o sec
4184 o sec
4171
4185
4172 o merge
4186 o merge
4173 |\
4187 |\
4174 | o new head
4188 | o new head
4175 | |
4189 | |
4176 o | new branch
4190 o | new branch
4177 |/
4191 |/
4178 o n
4192 o n
4179 |
4193 |
4180 o n
4194 o n
4181 |
4195 |
4182 o
4196 o
4183 |
4197 |
4184 o line 1
4198 o line 1
4185 line 2
4199 line 2
4186
4200
4187 Test word error messages for not enough and too many arguments
4201 Test word error messages for not enough and too many arguments
4188
4202
4189 $ hg log -Gv -R a --template "{word('0')}"
4203 $ hg log -Gv -R a --template "{word('0')}"
4190 hg: parse error: word expects two or three arguments, got 1
4204 hg: parse error: word expects two or three arguments, got 1
4191 [255]
4205 [255]
4192
4206
4193 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4207 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4194 hg: parse error: word expects two or three arguments, got 7
4208 hg: parse error: word expects two or three arguments, got 7
4195 [255]
4209 [255]
4196
4210
4197 Test word for integer literal
4211 Test word for integer literal
4198
4212
4199 $ hg log -R a --template "{word(2, desc)}\n" -r0
4213 $ hg log -R a --template "{word(2, desc)}\n" -r0
4200 line
4214 line
4201
4215
4202 Test word for invalid numbers
4216 Test word for invalid numbers
4203
4217
4204 $ hg log -Gv -R a --template "{word('a', desc)}"
4218 $ hg log -Gv -R a --template "{word('a', desc)}"
4205 hg: parse error: word expects an integer index
4219 hg: parse error: word expects an integer index
4206 [255]
4220 [255]
4207
4221
4208 Test word for out of range
4222 Test word for out of range
4209
4223
4210 $ hg log -R a --template "{word(10000, desc)}"
4224 $ hg log -R a --template "{word(10000, desc)}"
4211 $ hg log -R a --template "{word(-10000, desc)}"
4225 $ hg log -R a --template "{word(-10000, desc)}"
4212
4226
4213 Test indent and not adding to empty lines
4227 Test indent and not adding to empty lines
4214
4228
4215 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4229 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4216 -----
4230 -----
4217 > line 1
4231 > line 1
4218 >> line 2
4232 >> line 2
4219 -----
4233 -----
4220 > other 1
4234 > other 1
4221 >> other 2
4235 >> other 2
4222
4236
4223 >> other 3
4237 >> other 3
4224
4238
4225 Test with non-strings like dates
4239 Test with non-strings like dates
4226
4240
4227 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4241 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4228 1200000.00
4242 1200000.00
4229 1300000.00
4243 1300000.00
4230
4244
4231 Test broken string escapes:
4245 Test broken string escapes:
4232
4246
4233 $ hg log -T "bogus\\" -R a
4247 $ hg log -T "bogus\\" -R a
4234 hg: parse error: trailing \ in string
4248 hg: parse error: trailing \ in string
4235 [255]
4249 [255]
4236 $ hg log -T "\\xy" -R a
4250 $ hg log -T "\\xy" -R a
4237 hg: parse error: invalid \x escape
4251 hg: parse error: invalid \x escape
4238 [255]
4252 [255]
4239
4253
4240 json filter should escape HTML tags so that the output can be embedded in hgweb:
4254 json filter should escape HTML tags so that the output can be embedded in hgweb:
4241
4255
4242 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4256 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4243 "\u003cfoo@example.org\u003e"
4257 "\u003cfoo@example.org\u003e"
4244
4258
4245 Templater supports aliases of symbol and func() styles:
4259 Templater supports aliases of symbol and func() styles:
4246
4260
4247 $ hg clone -q a aliases
4261 $ hg clone -q a aliases
4248 $ cd aliases
4262 $ cd aliases
4249 $ cat <<EOF >> .hg/hgrc
4263 $ cat <<EOF >> .hg/hgrc
4250 > [templatealias]
4264 > [templatealias]
4251 > r = rev
4265 > r = rev
4252 > rn = "{r}:{node|short}"
4266 > rn = "{r}:{node|short}"
4253 > status(c, files) = files % "{c} {file}\n"
4267 > status(c, files) = files % "{c} {file}\n"
4254 > utcdate(d) = localdate(d, "UTC")
4268 > utcdate(d) = localdate(d, "UTC")
4255 > EOF
4269 > EOF
4256
4270
4257 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4271 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4258 (template
4272 (template
4259 (symbol 'rn')
4273 (symbol 'rn')
4260 (string ' ')
4274 (string ' ')
4261 (|
4275 (|
4262 (func
4276 (func
4263 (symbol 'utcdate')
4277 (symbol 'utcdate')
4264 (symbol 'date'))
4278 (symbol 'date'))
4265 (symbol 'isodate'))
4279 (symbol 'isodate'))
4266 (string '\n'))
4280 (string '\n'))
4267 * expanded:
4281 * expanded:
4268 (template
4282 (template
4269 (template
4283 (template
4270 (symbol 'rev')
4284 (symbol 'rev')
4271 (string ':')
4285 (string ':')
4272 (|
4286 (|
4273 (symbol 'node')
4287 (symbol 'node')
4274 (symbol 'short')))
4288 (symbol 'short')))
4275 (string ' ')
4289 (string ' ')
4276 (|
4290 (|
4277 (func
4291 (func
4278 (symbol 'localdate')
4292 (symbol 'localdate')
4279 (list
4293 (list
4280 (symbol 'date')
4294 (symbol 'date')
4281 (string 'UTC')))
4295 (string 'UTC')))
4282 (symbol 'isodate'))
4296 (symbol 'isodate'))
4283 (string '\n'))
4297 (string '\n'))
4284 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4298 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4285
4299
4286 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4300 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4287 (template
4301 (template
4288 (func
4302 (func
4289 (symbol 'status')
4303 (symbol 'status')
4290 (list
4304 (list
4291 (string 'A')
4305 (string 'A')
4292 (symbol 'file_adds'))))
4306 (symbol 'file_adds'))))
4293 * expanded:
4307 * expanded:
4294 (template
4308 (template
4295 (%
4309 (%
4296 (symbol 'file_adds')
4310 (symbol 'file_adds')
4297 (template
4311 (template
4298 (string 'A')
4312 (string 'A')
4299 (string ' ')
4313 (string ' ')
4300 (symbol 'file')
4314 (symbol 'file')
4301 (string '\n'))))
4315 (string '\n'))))
4302 A a
4316 A a
4303
4317
4304 A unary function alias can be called as a filter:
4318 A unary function alias can be called as a filter:
4305
4319
4306 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4320 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4307 (template
4321 (template
4308 (|
4322 (|
4309 (|
4323 (|
4310 (symbol 'date')
4324 (symbol 'date')
4311 (symbol 'utcdate'))
4325 (symbol 'utcdate'))
4312 (symbol 'isodate'))
4326 (symbol 'isodate'))
4313 (string '\n'))
4327 (string '\n'))
4314 * expanded:
4328 * expanded:
4315 (template
4329 (template
4316 (|
4330 (|
4317 (func
4331 (func
4318 (symbol 'localdate')
4332 (symbol 'localdate')
4319 (list
4333 (list
4320 (symbol 'date')
4334 (symbol 'date')
4321 (string 'UTC')))
4335 (string 'UTC')))
4322 (symbol 'isodate'))
4336 (symbol 'isodate'))
4323 (string '\n'))
4337 (string '\n'))
4324 1970-01-12 13:46 +0000
4338 1970-01-12 13:46 +0000
4325
4339
4326 Aliases should be applied only to command arguments and templates in hgrc.
4340 Aliases should be applied only to command arguments and templates in hgrc.
4327 Otherwise, our stock styles and web templates could be corrupted:
4341 Otherwise, our stock styles and web templates could be corrupted:
4328
4342
4329 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4343 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4330 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4344 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4331
4345
4332 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4346 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4333 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4347 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4334
4348
4335 $ cat <<EOF > tmpl
4349 $ cat <<EOF > tmpl
4336 > changeset = 'nothing expanded:{rn}\n'
4350 > changeset = 'nothing expanded:{rn}\n'
4337 > EOF
4351 > EOF
4338 $ hg log -r0 --style ./tmpl
4352 $ hg log -r0 --style ./tmpl
4339 nothing expanded:
4353 nothing expanded:
4340
4354
4341 Aliases in formatter:
4355 Aliases in formatter:
4342
4356
4343 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4357 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4344 default 6:d41e714fe50d
4358 default 6:d41e714fe50d
4345 foo 4:bbe44766e73d
4359 foo 4:bbe44766e73d
4346
4360
4347 Aliases should honor HGPLAIN:
4361 Aliases should honor HGPLAIN:
4348
4362
4349 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4363 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4350 nothing expanded:
4364 nothing expanded:
4351 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4365 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4352 0:1e4e1b8f71e0
4366 0:1e4e1b8f71e0
4353
4367
4354 Unparsable alias:
4368 Unparsable alias:
4355
4369
4356 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4370 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4357 (template
4371 (template
4358 (symbol 'bad'))
4372 (symbol 'bad'))
4359 abort: bad definition of template alias "bad": at 2: not a prefix: end
4373 abort: bad definition of template alias "bad": at 2: not a prefix: end
4360 [255]
4374 [255]
4361 $ hg log --config templatealias.bad='x(' -T '{bad}'
4375 $ hg log --config templatealias.bad='x(' -T '{bad}'
4362 abort: bad definition of template alias "bad": at 2: not a prefix: end
4376 abort: bad definition of template alias "bad": at 2: not a prefix: end
4363 [255]
4377 [255]
4364
4378
4365 $ cd ..
4379 $ cd ..
4366
4380
4367 Set up repository for non-ascii encoding tests:
4381 Set up repository for non-ascii encoding tests:
4368
4382
4369 $ hg init nonascii
4383 $ hg init nonascii
4370 $ cd nonascii
4384 $ cd nonascii
4371 $ $PYTHON <<EOF
4385 $ $PYTHON <<EOF
4372 > open('latin1', 'w').write('\xe9')
4386 > open('latin1', 'w').write('\xe9')
4373 > open('utf-8', 'w').write('\xc3\xa9')
4387 > open('utf-8', 'w').write('\xc3\xa9')
4374 > EOF
4388 > EOF
4375 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4389 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4376 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4390 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4377
4391
4378 json filter should try round-trip conversion to utf-8:
4392 json filter should try round-trip conversion to utf-8:
4379
4393
4380 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4394 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4381 "\u00e9"
4395 "\u00e9"
4382 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4396 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4383 "non-ascii branch: \u00e9"
4397 "non-ascii branch: \u00e9"
4384
4398
4385 json filter takes input as utf-8b:
4399 json filter takes input as utf-8b:
4386
4400
4387 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4401 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4388 "\u00e9"
4402 "\u00e9"
4389 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4403 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4390 "\udce9"
4404 "\udce9"
4391
4405
4392 utf8 filter:
4406 utf8 filter:
4393
4407
4394 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4408 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4395 round-trip: c3a9
4409 round-trip: c3a9
4396 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4410 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4397 decoded: c3a9
4411 decoded: c3a9
4398 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4412 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4399 abort: decoding near * (glob)
4413 abort: decoding near * (glob)
4400 [255]
4414 [255]
4401 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4415 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4402 abort: template filter 'utf8' is not compatible with keyword 'rev'
4416 abort: template filter 'utf8' is not compatible with keyword 'rev'
4403 [255]
4417 [255]
4404
4418
4405 pad width:
4419 pad width:
4406
4420
4407 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4421 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4408 \xc3\xa9- (esc)
4422 \xc3\xa9- (esc)
4409
4423
4410 $ cd ..
4424 $ cd ..
4411
4425
4412 Test that template function in extension is registered as expected
4426 Test that template function in extension is registered as expected
4413
4427
4414 $ cd a
4428 $ cd a
4415
4429
4416 $ cat <<EOF > $TESTTMP/customfunc.py
4430 $ cat <<EOF > $TESTTMP/customfunc.py
4417 > from mercurial import registrar
4431 > from mercurial import registrar
4418 >
4432 >
4419 > templatefunc = registrar.templatefunc()
4433 > templatefunc = registrar.templatefunc()
4420 >
4434 >
4421 > @templatefunc('custom()')
4435 > @templatefunc('custom()')
4422 > def custom(context, mapping, args):
4436 > def custom(context, mapping, args):
4423 > return 'custom'
4437 > return 'custom'
4424 > EOF
4438 > EOF
4425 $ cat <<EOF > .hg/hgrc
4439 $ cat <<EOF > .hg/hgrc
4426 > [extensions]
4440 > [extensions]
4427 > customfunc = $TESTTMP/customfunc.py
4441 > customfunc = $TESTTMP/customfunc.py
4428 > EOF
4442 > EOF
4429
4443
4430 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4444 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4431 custom
4445 custom
4432
4446
4433 $ cd ..
4447 $ cd ..
4434
4448
4435 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4449 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4436 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4450 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4437 columns. We don't care about other aspects of the graph rendering here.
4451 columns. We don't care about other aspects of the graph rendering here.
4438
4452
4439 $ hg init graphwidth
4453 $ hg init graphwidth
4440 $ cd graphwidth
4454 $ cd graphwidth
4441
4455
4442 $ wrappabletext="a a a a a a a a a a a a"
4456 $ wrappabletext="a a a a a a a a a a a a"
4443
4457
4444 $ printf "first\n" > file
4458 $ printf "first\n" > file
4445 $ hg add file
4459 $ hg add file
4446 $ hg commit -m "$wrappabletext"
4460 $ hg commit -m "$wrappabletext"
4447
4461
4448 $ printf "first\nsecond\n" > file
4462 $ printf "first\nsecond\n" > file
4449 $ hg commit -m "$wrappabletext"
4463 $ hg commit -m "$wrappabletext"
4450
4464
4451 $ hg checkout 0
4465 $ hg checkout 0
4452 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4466 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4453 $ printf "third\nfirst\n" > file
4467 $ printf "third\nfirst\n" > file
4454 $ hg commit -m "$wrappabletext"
4468 $ hg commit -m "$wrappabletext"
4455 created new head
4469 created new head
4456
4470
4457 $ hg merge
4471 $ hg merge
4458 merging file
4472 merging file
4459 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4473 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4460 (branch merge, don't forget to commit)
4474 (branch merge, don't forget to commit)
4461
4475
4462 $ hg log --graph -T "{graphwidth}"
4476 $ hg log --graph -T "{graphwidth}"
4463 @ 3
4477 @ 3
4464 |
4478 |
4465 | @ 5
4479 | @ 5
4466 |/
4480 |/
4467 o 3
4481 o 3
4468
4482
4469 $ hg commit -m "$wrappabletext"
4483 $ hg commit -m "$wrappabletext"
4470
4484
4471 $ hg log --graph -T "{graphwidth}"
4485 $ hg log --graph -T "{graphwidth}"
4472 @ 5
4486 @ 5
4473 |\
4487 |\
4474 | o 5
4488 | o 5
4475 | |
4489 | |
4476 o | 5
4490 o | 5
4477 |/
4491 |/
4478 o 3
4492 o 3
4479
4493
4480
4494
4481 $ hg checkout 0
4495 $ hg checkout 0
4482 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4496 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4483 $ printf "third\nfirst\nsecond\n" > file
4497 $ printf "third\nfirst\nsecond\n" > file
4484 $ hg commit -m "$wrappabletext"
4498 $ hg commit -m "$wrappabletext"
4485 created new head
4499 created new head
4486
4500
4487 $ hg log --graph -T "{graphwidth}"
4501 $ hg log --graph -T "{graphwidth}"
4488 @ 3
4502 @ 3
4489 |
4503 |
4490 | o 7
4504 | o 7
4491 | |\
4505 | |\
4492 +---o 7
4506 +---o 7
4493 | |
4507 | |
4494 | o 5
4508 | o 5
4495 |/
4509 |/
4496 o 3
4510 o 3
4497
4511
4498
4512
4499 $ hg log --graph -T "{graphwidth}" -r 3
4513 $ hg log --graph -T "{graphwidth}" -r 3
4500 o 5
4514 o 5
4501 |\
4515 |\
4502 ~ ~
4516 ~ ~
4503
4517
4504 $ hg log --graph -T "{graphwidth}" -r 1
4518 $ hg log --graph -T "{graphwidth}" -r 1
4505 o 3
4519 o 3
4506 |
4520 |
4507 ~
4521 ~
4508
4522
4509 $ hg merge
4523 $ hg merge
4510 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4524 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4511 (branch merge, don't forget to commit)
4525 (branch merge, don't forget to commit)
4512 $ hg commit -m "$wrappabletext"
4526 $ hg commit -m "$wrappabletext"
4513
4527
4514 $ printf "seventh\n" >> file
4528 $ printf "seventh\n" >> file
4515 $ hg commit -m "$wrappabletext"
4529 $ hg commit -m "$wrappabletext"
4516
4530
4517 $ hg log --graph -T "{graphwidth}"
4531 $ hg log --graph -T "{graphwidth}"
4518 @ 3
4532 @ 3
4519 |
4533 |
4520 o 5
4534 o 5
4521 |\
4535 |\
4522 | o 5
4536 | o 5
4523 | |
4537 | |
4524 o | 7
4538 o | 7
4525 |\ \
4539 |\ \
4526 | o | 7
4540 | o | 7
4527 | |/
4541 | |/
4528 o / 5
4542 o / 5
4529 |/
4543 |/
4530 o 3
4544 o 3
4531
4545
4532
4546
4533 The point of graphwidth is to allow wrapping that accounts for the space taken
4547 The point of graphwidth is to allow wrapping that accounts for the space taken
4534 by the graph.
4548 by the graph.
4535
4549
4536 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4550 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4537 @ a a a a
4551 @ a a a a
4538 | a a a a
4552 | a a a a
4539 | a a a a
4553 | a a a a
4540 o a a a
4554 o a a a
4541 |\ a a a
4555 |\ a a a
4542 | | a a a
4556 | | a a a
4543 | | a a a
4557 | | a a a
4544 | o a a a
4558 | o a a a
4545 | | a a a
4559 | | a a a
4546 | | a a a
4560 | | a a a
4547 | | a a a
4561 | | a a a
4548 o | a a
4562 o | a a
4549 |\ \ a a
4563 |\ \ a a
4550 | | | a a
4564 | | | a a
4551 | | | a a
4565 | | | a a
4552 | | | a a
4566 | | | a a
4553 | | | a a
4567 | | | a a
4554 | o | a a
4568 | o | a a
4555 | |/ a a
4569 | |/ a a
4556 | | a a
4570 | | a a
4557 | | a a
4571 | | a a
4558 | | a a
4572 | | a a
4559 | | a a
4573 | | a a
4560 o | a a a
4574 o | a a a
4561 |/ a a a
4575 |/ a a a
4562 | a a a
4576 | a a a
4563 | a a a
4577 | a a a
4564 o a a a a
4578 o a a a a
4565 a a a a
4579 a a a a
4566 a a a a
4580 a a a a
4567
4581
4568 Something tricky happens when there are elided nodes; the next drawn row of
4582 Something tricky happens when there are elided nodes; the next drawn row of
4569 edges can be more than one column wider, but the graph width only increases by
4583 edges can be more than one column wider, but the graph width only increases by
4570 one column. The remaining columns are added in between the nodes.
4584 one column. The remaining columns are added in between the nodes.
4571
4585
4572 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4586 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4573 o 5
4587 o 5
4574 |\
4588 |\
4575 | \
4589 | \
4576 | :\
4590 | :\
4577 o : : 7
4591 o : : 7
4578 :/ /
4592 :/ /
4579 : o 5
4593 : o 5
4580 :/
4594 :/
4581 o 3
4595 o 3
4582
4596
4583
4597
4584 $ cd ..
4598 $ cd ..
4585
4599
General Comments 0
You need to be logged in to leave comments. Login now