##// END OF EJS Templates
templater: abort when a template filter raises an exception (issue2987)
Neil Kodner -
r17383:099c778c default
parent child Browse files
Show More
@@ -1,394 +1,402 b''
1 1 # templater.py - template expansion for output
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import sys, os
10 10 import util, config, templatefilters, parser, error
11 11
12 12 # template parsing
13 13
14 14 elements = {
15 15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 16 ",": (2, None, ("list", 2)),
17 17 "|": (5, None, ("|", 5)),
18 18 "%": (6, None, ("%", 6)),
19 19 ")": (0, None, None),
20 20 "symbol": (0, ("symbol",), None),
21 21 "string": (0, ("string",), None),
22 22 "end": (0, None, None),
23 23 }
24 24
25 25 def tokenizer(data):
26 26 program, start, end = data
27 27 pos = start
28 28 while pos < end:
29 29 c = program[pos]
30 30 if c.isspace(): # skip inter-token whitespace
31 31 pass
32 32 elif c in "(,)%|": # handle simple operators
33 33 yield (c, None, pos)
34 34 elif (c in '"\'' or c == 'r' and
35 35 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
36 36 if c == 'r':
37 37 pos += 1
38 38 c = program[pos]
39 39 decode = lambda x: x
40 40 else:
41 41 decode = lambda x: x.decode('string-escape')
42 42 pos += 1
43 43 s = pos
44 44 while pos < end: # find closing quote
45 45 d = program[pos]
46 46 if d == '\\': # skip over escaped characters
47 47 pos += 2
48 48 continue
49 49 if d == c:
50 50 yield ('string', decode(program[s:pos]), s)
51 51 break
52 52 pos += 1
53 53 else:
54 54 raise error.ParseError(_("unterminated string"), s)
55 55 elif c.isalnum() or c in '_':
56 56 s = pos
57 57 pos += 1
58 58 while pos < end: # find end of symbol
59 59 d = program[pos]
60 60 if not (d.isalnum() or d == "_"):
61 61 break
62 62 pos += 1
63 63 sym = program[s:pos]
64 64 yield ('symbol', sym, s)
65 65 pos -= 1
66 66 elif c == '}':
67 67 pos += 1
68 68 break
69 69 else:
70 70 raise error.ParseError(_("syntax error"), pos)
71 71 pos += 1
72 72 yield ('end', None, pos)
73 73
74 74 def compiletemplate(tmpl, context):
75 75 parsed = []
76 76 pos, stop = 0, len(tmpl)
77 77 p = parser.parser(tokenizer, elements)
78 78
79 79 while pos < stop:
80 80 n = tmpl.find('{', pos)
81 81 if n < 0:
82 82 parsed.append(("string", tmpl[pos:]))
83 83 break
84 84 if n > 0 and tmpl[n - 1] == '\\':
85 85 # escaped
86 86 parsed.append(("string", tmpl[pos:n - 1] + "{"))
87 87 pos = n + 1
88 88 continue
89 89 if n > pos:
90 90 parsed.append(("string", tmpl[pos:n]))
91 91
92 92 pd = [tmpl, n + 1, stop]
93 93 parseres, pos = p.parse(pd)
94 94 parsed.append(parseres)
95 95
96 96 return [compileexp(e, context) for e in parsed]
97 97
98 98 def compileexp(exp, context):
99 99 t = exp[0]
100 100 if t in methods:
101 101 return methods[t](exp, context)
102 102 raise error.ParseError(_("unknown method '%s'") % t)
103 103
104 104 # template evaluation
105 105
106 106 def getsymbol(exp):
107 107 if exp[0] == 'symbol':
108 108 return exp[1]
109 109 raise error.ParseError(_("expected a symbol"))
110 110
111 111 def getlist(x):
112 112 if not x:
113 113 return []
114 114 if x[0] == 'list':
115 115 return getlist(x[1]) + [x[2]]
116 116 return [x]
117 117
118 118 def getfilter(exp, context):
119 119 f = getsymbol(exp)
120 120 if f not in context._filters:
121 121 raise error.ParseError(_("unknown function '%s'") % f)
122 122 return context._filters[f]
123 123
124 124 def gettemplate(exp, context):
125 125 if exp[0] == 'string':
126 126 return compiletemplate(exp[1], context)
127 127 if exp[0] == 'symbol':
128 128 return context._load(exp[1])
129 129 raise error.ParseError(_("expected template specifier"))
130 130
131 131 def runstring(context, mapping, data):
132 132 return data
133 133
134 134 def runsymbol(context, mapping, key):
135 135 v = mapping.get(key)
136 136 if v is None:
137 137 v = context._defaults.get(key, '')
138 138 if util.safehasattr(v, '__call__'):
139 139 return v(**mapping)
140 140 return v
141 141
142 142 def buildfilter(exp, context):
143 143 func, data = compileexp(exp[1], context)
144 144 filt = getfilter(exp[2], context)
145 145 return (runfilter, (func, data, filt))
146 146
147 147 def runfilter(context, mapping, data):
148 148 func, data, filt = data
149 try:
149 150 return filt(func(context, mapping, data))
151 except (ValueError, AttributeError, TypeError):
152 if isinstance(data, tuple):
153 dt = data[1]
154 else:
155 dt = data
156 raise util.Abort(_("template filter '%s' is not compatible with "
157 "keyword '%s'") % (filt.func_name, dt))
150 158
151 159 def buildmap(exp, context):
152 160 func, data = compileexp(exp[1], context)
153 161 ctmpl = gettemplate(exp[2], context)
154 162 return (runmap, (func, data, ctmpl))
155 163
156 164 def runmap(context, mapping, data):
157 165 func, data, ctmpl = data
158 166 d = func(context, mapping, data)
159 167 lm = mapping.copy()
160 168
161 169 for i in d:
162 170 if isinstance(i, dict):
163 171 lm.update(i)
164 172 for f, d in ctmpl:
165 173 yield f(context, lm, d)
166 174 else:
167 175 # v is not an iterable of dicts, this happen when 'key'
168 176 # has been fully expanded already and format is useless.
169 177 # If so, return the expanded value.
170 178 yield i
171 179
172 180 def buildfunc(exp, context):
173 181 n = getsymbol(exp[1])
174 182 args = [compileexp(x, context) for x in getlist(exp[2])]
175 183 if n in funcs:
176 184 f = funcs[n]
177 185 return (f, args)
178 186 if n in context._filters:
179 187 if len(args) != 1:
180 188 raise error.ParseError(_("filter %s expects one argument") % n)
181 189 f = context._filters[n]
182 190 return (runfilter, (args[0][0], args[0][1], f))
183 191
184 192 methods = {
185 193 "string": lambda e, c: (runstring, e[1]),
186 194 "symbol": lambda e, c: (runsymbol, e[1]),
187 195 "group": lambda e, c: compileexp(e[1], c),
188 196 # ".": buildmember,
189 197 "|": buildfilter,
190 198 "%": buildmap,
191 199 "func": buildfunc,
192 200 }
193 201
194 202 funcs = {
195 203 }
196 204
197 205 # template engine
198 206
199 207 path = ['templates', '../templates']
200 208 stringify = templatefilters.stringify
201 209
202 210 def _flatten(thing):
203 211 '''yield a single stream from a possibly nested set of iterators'''
204 212 if isinstance(thing, str):
205 213 yield thing
206 214 elif not util.safehasattr(thing, '__iter__'):
207 215 if thing is not None:
208 216 yield str(thing)
209 217 else:
210 218 for i in thing:
211 219 if isinstance(i, str):
212 220 yield i
213 221 elif not util.safehasattr(i, '__iter__'):
214 222 if i is not None:
215 223 yield str(i)
216 224 elif i is not None:
217 225 for j in _flatten(i):
218 226 yield j
219 227
220 228 def parsestring(s, quoted=True):
221 229 '''parse a string using simple c-like syntax.
222 230 string must be in quotes if quoted is True.'''
223 231 if quoted:
224 232 if len(s) < 2 or s[0] != s[-1]:
225 233 raise SyntaxError(_('unmatched quotes'))
226 234 return s[1:-1].decode('string_escape')
227 235
228 236 return s.decode('string_escape')
229 237
230 238 class engine(object):
231 239 '''template expansion engine.
232 240
233 241 template expansion works like this. a map file contains key=value
234 242 pairs. if value is quoted, it is treated as string. otherwise, it
235 243 is treated as name of template file.
236 244
237 245 templater is asked to expand a key in map. it looks up key, and
238 246 looks for strings like this: {foo}. it expands {foo} by looking up
239 247 foo in map, and substituting it. expansion is recursive: it stops
240 248 when there is no more {foo} to replace.
241 249
242 250 expansion also allows formatting and filtering.
243 251
244 252 format uses key to expand each item in list. syntax is
245 253 {key%format}.
246 254
247 255 filter uses function to transform value. syntax is
248 256 {key|filter1|filter2|...}.'''
249 257
250 258 def __init__(self, loader, filters={}, defaults={}):
251 259 self._loader = loader
252 260 self._filters = filters
253 261 self._defaults = defaults
254 262 self._cache = {}
255 263
256 264 def _load(self, t):
257 265 '''load, parse, and cache a template'''
258 266 if t not in self._cache:
259 267 self._cache[t] = compiletemplate(self._loader(t), self)
260 268 return self._cache[t]
261 269
262 270 def process(self, t, mapping):
263 271 '''Perform expansion. t is name of map element to expand.
264 272 mapping contains added elements for use during expansion. Is a
265 273 generator.'''
266 274 return _flatten(func(self, mapping, data) for func, data in
267 275 self._load(t))
268 276
269 277 engines = {'default': engine}
270 278
271 279 class templater(object):
272 280
273 281 def __init__(self, mapfile, filters={}, defaults={}, cache={},
274 282 minchunk=1024, maxchunk=65536):
275 283 '''set up template engine.
276 284 mapfile is name of file to read map definitions from.
277 285 filters is dict of functions. each transforms a value into another.
278 286 defaults is dict of default map definitions.'''
279 287 self.mapfile = mapfile or 'template'
280 288 self.cache = cache.copy()
281 289 self.map = {}
282 290 self.base = (mapfile and os.path.dirname(mapfile)) or ''
283 291 self.filters = templatefilters.filters.copy()
284 292 self.filters.update(filters)
285 293 self.defaults = defaults
286 294 self.minchunk, self.maxchunk = minchunk, maxchunk
287 295 self.ecache = {}
288 296
289 297 if not mapfile:
290 298 return
291 299 if not os.path.exists(mapfile):
292 300 raise util.Abort(_('style not found: %s') % mapfile)
293 301
294 302 conf = config.config()
295 303 conf.read(mapfile)
296 304
297 305 for key, val in conf[''].items():
298 306 if not val:
299 307 raise SyntaxError(_('%s: missing value') % conf.source('', key))
300 308 if val[0] in "'\"":
301 309 try:
302 310 self.cache[key] = parsestring(val)
303 311 except SyntaxError, inst:
304 312 raise SyntaxError('%s: %s' %
305 313 (conf.source('', key), inst.args[0]))
306 314 else:
307 315 val = 'default', val
308 316 if ':' in val[1]:
309 317 val = val[1].split(':', 1)
310 318 self.map[key] = val[0], os.path.join(self.base, val[1])
311 319
312 320 def __contains__(self, key):
313 321 return key in self.cache or key in self.map
314 322
315 323 def load(self, t):
316 324 '''Get the template for the given template name. Use a local cache.'''
317 325 if t not in self.cache:
318 326 try:
319 327 self.cache[t] = util.readfile(self.map[t][1])
320 328 except KeyError, inst:
321 329 raise util.Abort(_('"%s" not in template map') % inst.args[0])
322 330 except IOError, inst:
323 331 raise IOError(inst.args[0], _('template file %s: %s') %
324 332 (self.map[t][1], inst.args[1]))
325 333 return self.cache[t]
326 334
327 335 def __call__(self, t, **mapping):
328 336 ttype = t in self.map and self.map[t][0] or 'default'
329 337 if ttype not in self.ecache:
330 338 self.ecache[ttype] = engines[ttype](self.load,
331 339 self.filters, self.defaults)
332 340 proc = self.ecache[ttype]
333 341
334 342 stream = proc.process(t, mapping)
335 343 if self.minchunk:
336 344 stream = util.increasingchunks(stream, min=self.minchunk,
337 345 max=self.maxchunk)
338 346 return stream
339 347
340 348 def templatepath(name=None):
341 349 '''return location of template file or directory (if no name).
342 350 returns None if not found.'''
343 351 normpaths = []
344 352
345 353 # executable version (py2exe) doesn't support __file__
346 354 if util.mainfrozen():
347 355 module = sys.executable
348 356 else:
349 357 module = __file__
350 358 for f in path:
351 359 if f.startswith('/'):
352 360 p = f
353 361 else:
354 362 fl = f.split('/')
355 363 p = os.path.join(os.path.dirname(module), *fl)
356 364 if name:
357 365 p = os.path.join(p, name)
358 366 if name and os.path.exists(p):
359 367 return os.path.normpath(p)
360 368 elif os.path.isdir(p):
361 369 normpaths.append(os.path.normpath(p))
362 370
363 371 return normpaths
364 372
365 373 def stylemap(styles, paths=None):
366 374 """Return path to mapfile for a given style.
367 375
368 376 Searches mapfile in the following locations:
369 377 1. templatepath/style/map
370 378 2. templatepath/map-style
371 379 3. templatepath/map
372 380 """
373 381
374 382 if paths is None:
375 383 paths = templatepath()
376 384 elif isinstance(paths, str):
377 385 paths = [paths]
378 386
379 387 if isinstance(styles, str):
380 388 styles = [styles]
381 389
382 390 for style in styles:
383 391 if not style:
384 392 continue
385 393 locations = [os.path.join(style, 'map'), 'map-' + style]
386 394 locations.append('map')
387 395
388 396 for path in paths:
389 397 for location in locations:
390 398 mapfile = os.path.join(path, location)
391 399 if os.path.isfile(mapfile):
392 400 return style, mapfile
393 401
394 402 raise RuntimeError("No hgweb templates found in %r" % paths)
@@ -1,1390 +1,1414 b''
1 1 $ hg init a
2 2 $ cd a
3 3 $ echo a > a
4 4 $ hg add a
5 5 $ echo line 1 > b
6 6 $ echo line 2 >> b
7 7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8 8
9 9 $ hg add b
10 10 $ echo other 1 > c
11 11 $ echo other 2 >> c
12 12 $ echo >> c
13 13 $ echo other 3 >> c
14 14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15 15
16 16 $ hg add c
17 17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 18 $ echo c >> c
19 19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 20
21 21 $ echo foo > .hg/branch
22 22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23 23
24 24 $ hg co -q 3
25 25 $ echo other 4 >> d
26 26 $ hg add d
27 27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28 28
29 29 $ hg merge -q foo
30 30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31 31
32 32 Second branch starting at nullrev:
33 33
34 34 $ hg update null
35 35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
36 36 $ echo second > second
37 37 $ hg add second
38 38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
39 39 created new head
40 40
41 41 $ echo third > third
42 42 $ hg add third
43 43 $ hg mv second fourth
44 44 $ hg commit -m third -d "2020-01-01 10:01"
45 45
46 46 Quoting for ui.logtemplate
47 47
48 48 $ hg tip --config "ui.logtemplate={rev}\n"
49 49 8
50 50 $ hg tip --config "ui.logtemplate='{rev}\n'"
51 51 8
52 52 $ hg tip --config 'ui.logtemplate="{rev}\n"'
53 53 8
54 54
55 55 Make sure user/global hgrc does not affect tests
56 56
57 57 $ echo '[ui]' > .hg/hgrc
58 58 $ echo 'logtemplate =' >> .hg/hgrc
59 59 $ echo 'style =' >> .hg/hgrc
60 60
61 61 Default style is like normal output:
62 62
63 63 $ hg log > log.out
64 64 $ hg log --style default > style.out
65 65 $ cmp log.out style.out || diff -u log.out style.out
66 66
67 67 $ hg log -v > log.out
68 68 $ hg log -v --style default > style.out
69 69 $ cmp log.out style.out || diff -u log.out style.out
70 70
71 71 $ hg log --debug > log.out
72 72 $ hg log --debug --style default > style.out
73 73 $ cmp log.out style.out || diff -u log.out style.out
74 74
75 75 Revision with no copies (used to print a traceback):
76 76
77 77 $ hg tip -v --template '\n'
78 78
79 79
80 80 Compact style works:
81 81
82 82 $ hg log --style compact
83 83 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
84 84 third
85 85
86 86 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
87 87 second
88 88
89 89 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
90 90 merge
91 91
92 92 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
93 93 new head
94 94
95 95 4 bbe44766e73d 1970-01-17 04:53 +0000 person
96 96 new branch
97 97
98 98 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
99 99 no user, no domain
100 100
101 101 2 97054abb4ab8 1970-01-14 21:20 +0000 other
102 102 no person
103 103
104 104 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
105 105 other 1
106 106
107 107 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
108 108 line 1
109 109
110 110
111 111 $ hg log -v --style compact
112 112 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
113 113 third
114 114
115 115 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
116 116 second
117 117
118 118 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
119 119 merge
120 120
121 121 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
122 122 new head
123 123
124 124 4 bbe44766e73d 1970-01-17 04:53 +0000 person
125 125 new branch
126 126
127 127 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
128 128 no user, no domain
129 129
130 130 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
131 131 no person
132 132
133 133 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
134 134 other 1
135 135 other 2
136 136
137 137 other 3
138 138
139 139 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
140 140 line 1
141 141 line 2
142 142
143 143
144 144 $ hg log --debug --style compact
145 145 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
146 146 third
147 147
148 148 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
149 149 second
150 150
151 151 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
152 152 merge
153 153
154 154 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
155 155 new head
156 156
157 157 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
158 158 new branch
159 159
160 160 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
161 161 no user, no domain
162 162
163 163 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
164 164 no person
165 165
166 166 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
167 167 other 1
168 168 other 2
169 169
170 170 other 3
171 171
172 172 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
173 173 line 1
174 174 line 2
175 175
176 176
177 177 Test xml styles:
178 178
179 179 $ hg log --style xml
180 180 <?xml version="1.0"?>
181 181 <log>
182 182 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
183 183 <tag>tip</tag>
184 184 <author email="test">test</author>
185 185 <date>2020-01-01T10:01:00+00:00</date>
186 186 <msg xml:space="preserve">third</msg>
187 187 </logentry>
188 188 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
189 189 <parent revision="-1" node="0000000000000000000000000000000000000000" />
190 190 <author email="user@hostname">User Name</author>
191 191 <date>1970-01-12T13:46:40+00:00</date>
192 192 <msg xml:space="preserve">second</msg>
193 193 </logentry>
194 194 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
195 195 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
196 196 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
197 197 <author email="person">person</author>
198 198 <date>1970-01-18T08:40:01+00:00</date>
199 199 <msg xml:space="preserve">merge</msg>
200 200 </logentry>
201 201 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
202 202 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
203 203 <author email="person">person</author>
204 204 <date>1970-01-18T08:40:00+00:00</date>
205 205 <msg xml:space="preserve">new head</msg>
206 206 </logentry>
207 207 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
208 208 <branch>foo</branch>
209 209 <author email="person">person</author>
210 210 <date>1970-01-17T04:53:20+00:00</date>
211 211 <msg xml:space="preserve">new branch</msg>
212 212 </logentry>
213 213 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
214 214 <author email="person">person</author>
215 215 <date>1970-01-16T01:06:40+00:00</date>
216 216 <msg xml:space="preserve">no user, no domain</msg>
217 217 </logentry>
218 218 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
219 219 <author email="other@place">other</author>
220 220 <date>1970-01-14T21:20:00+00:00</date>
221 221 <msg xml:space="preserve">no person</msg>
222 222 </logentry>
223 223 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
224 224 <author email="other@place">A. N. Other</author>
225 225 <date>1970-01-13T17:33:20+00:00</date>
226 226 <msg xml:space="preserve">other 1
227 227 other 2
228 228
229 229 other 3</msg>
230 230 </logentry>
231 231 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
232 232 <author email="user@hostname">User Name</author>
233 233 <date>1970-01-12T13:46:40+00:00</date>
234 234 <msg xml:space="preserve">line 1
235 235 line 2</msg>
236 236 </logentry>
237 237 </log>
238 238
239 239 $ hg log -v --style xml
240 240 <?xml version="1.0"?>
241 241 <log>
242 242 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
243 243 <tag>tip</tag>
244 244 <author email="test">test</author>
245 245 <date>2020-01-01T10:01:00+00:00</date>
246 246 <msg xml:space="preserve">third</msg>
247 247 <paths>
248 248 <path action="A">fourth</path>
249 249 <path action="A">third</path>
250 250 <path action="R">second</path>
251 251 </paths>
252 252 <copies>
253 253 <copy source="second">fourth</copy>
254 254 </copies>
255 255 </logentry>
256 256 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
257 257 <parent revision="-1" node="0000000000000000000000000000000000000000" />
258 258 <author email="user@hostname">User Name</author>
259 259 <date>1970-01-12T13:46:40+00:00</date>
260 260 <msg xml:space="preserve">second</msg>
261 261 <paths>
262 262 <path action="A">second</path>
263 263 </paths>
264 264 </logentry>
265 265 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
266 266 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
267 267 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
268 268 <author email="person">person</author>
269 269 <date>1970-01-18T08:40:01+00:00</date>
270 270 <msg xml:space="preserve">merge</msg>
271 271 <paths>
272 272 </paths>
273 273 </logentry>
274 274 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
275 275 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
276 276 <author email="person">person</author>
277 277 <date>1970-01-18T08:40:00+00:00</date>
278 278 <msg xml:space="preserve">new head</msg>
279 279 <paths>
280 280 <path action="A">d</path>
281 281 </paths>
282 282 </logentry>
283 283 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
284 284 <branch>foo</branch>
285 285 <author email="person">person</author>
286 286 <date>1970-01-17T04:53:20+00:00</date>
287 287 <msg xml:space="preserve">new branch</msg>
288 288 <paths>
289 289 </paths>
290 290 </logentry>
291 291 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
292 292 <author email="person">person</author>
293 293 <date>1970-01-16T01:06:40+00:00</date>
294 294 <msg xml:space="preserve">no user, no domain</msg>
295 295 <paths>
296 296 <path action="M">c</path>
297 297 </paths>
298 298 </logentry>
299 299 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
300 300 <author email="other@place">other</author>
301 301 <date>1970-01-14T21:20:00+00:00</date>
302 302 <msg xml:space="preserve">no person</msg>
303 303 <paths>
304 304 <path action="A">c</path>
305 305 </paths>
306 306 </logentry>
307 307 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
308 308 <author email="other@place">A. N. Other</author>
309 309 <date>1970-01-13T17:33:20+00:00</date>
310 310 <msg xml:space="preserve">other 1
311 311 other 2
312 312
313 313 other 3</msg>
314 314 <paths>
315 315 <path action="A">b</path>
316 316 </paths>
317 317 </logentry>
318 318 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
319 319 <author email="user@hostname">User Name</author>
320 320 <date>1970-01-12T13:46:40+00:00</date>
321 321 <msg xml:space="preserve">line 1
322 322 line 2</msg>
323 323 <paths>
324 324 <path action="A">a</path>
325 325 </paths>
326 326 </logentry>
327 327 </log>
328 328
329 329 $ hg log --debug --style xml
330 330 <?xml version="1.0"?>
331 331 <log>
332 332 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
333 333 <tag>tip</tag>
334 334 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
335 335 <parent revision="-1" node="0000000000000000000000000000000000000000" />
336 336 <author email="test">test</author>
337 337 <date>2020-01-01T10:01:00+00:00</date>
338 338 <msg xml:space="preserve">third</msg>
339 339 <paths>
340 340 <path action="A">fourth</path>
341 341 <path action="A">third</path>
342 342 <path action="R">second</path>
343 343 </paths>
344 344 <copies>
345 345 <copy source="second">fourth</copy>
346 346 </copies>
347 347 <extra key="branch">default</extra>
348 348 </logentry>
349 349 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
350 350 <parent revision="-1" node="0000000000000000000000000000000000000000" />
351 351 <parent revision="-1" node="0000000000000000000000000000000000000000" />
352 352 <author email="user@hostname">User Name</author>
353 353 <date>1970-01-12T13:46:40+00:00</date>
354 354 <msg xml:space="preserve">second</msg>
355 355 <paths>
356 356 <path action="A">second</path>
357 357 </paths>
358 358 <extra key="branch">default</extra>
359 359 </logentry>
360 360 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
361 361 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
362 362 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
363 363 <author email="person">person</author>
364 364 <date>1970-01-18T08:40:01+00:00</date>
365 365 <msg xml:space="preserve">merge</msg>
366 366 <paths>
367 367 </paths>
368 368 <extra key="branch">default</extra>
369 369 </logentry>
370 370 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
371 371 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
372 372 <parent revision="-1" node="0000000000000000000000000000000000000000" />
373 373 <author email="person">person</author>
374 374 <date>1970-01-18T08:40:00+00:00</date>
375 375 <msg xml:space="preserve">new head</msg>
376 376 <paths>
377 377 <path action="A">d</path>
378 378 </paths>
379 379 <extra key="branch">default</extra>
380 380 </logentry>
381 381 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
382 382 <branch>foo</branch>
383 383 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
384 384 <parent revision="-1" node="0000000000000000000000000000000000000000" />
385 385 <author email="person">person</author>
386 386 <date>1970-01-17T04:53:20+00:00</date>
387 387 <msg xml:space="preserve">new branch</msg>
388 388 <paths>
389 389 </paths>
390 390 <extra key="branch">foo</extra>
391 391 </logentry>
392 392 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
393 393 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
394 394 <parent revision="-1" node="0000000000000000000000000000000000000000" />
395 395 <author email="person">person</author>
396 396 <date>1970-01-16T01:06:40+00:00</date>
397 397 <msg xml:space="preserve">no user, no domain</msg>
398 398 <paths>
399 399 <path action="M">c</path>
400 400 </paths>
401 401 <extra key="branch">default</extra>
402 402 </logentry>
403 403 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
404 404 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
405 405 <parent revision="-1" node="0000000000000000000000000000000000000000" />
406 406 <author email="other@place">other</author>
407 407 <date>1970-01-14T21:20:00+00:00</date>
408 408 <msg xml:space="preserve">no person</msg>
409 409 <paths>
410 410 <path action="A">c</path>
411 411 </paths>
412 412 <extra key="branch">default</extra>
413 413 </logentry>
414 414 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
415 415 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
416 416 <parent revision="-1" node="0000000000000000000000000000000000000000" />
417 417 <author email="other@place">A. N. Other</author>
418 418 <date>1970-01-13T17:33:20+00:00</date>
419 419 <msg xml:space="preserve">other 1
420 420 other 2
421 421
422 422 other 3</msg>
423 423 <paths>
424 424 <path action="A">b</path>
425 425 </paths>
426 426 <extra key="branch">default</extra>
427 427 </logentry>
428 428 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
429 429 <parent revision="-1" node="0000000000000000000000000000000000000000" />
430 430 <parent revision="-1" node="0000000000000000000000000000000000000000" />
431 431 <author email="user@hostname">User Name</author>
432 432 <date>1970-01-12T13:46:40+00:00</date>
433 433 <msg xml:space="preserve">line 1
434 434 line 2</msg>
435 435 <paths>
436 436 <path action="A">a</path>
437 437 </paths>
438 438 <extra key="branch">default</extra>
439 439 </logentry>
440 440 </log>
441 441
442 442
443 443 Error if style not readable:
444 444
445 445 #if unix-permissions
446 446 $ touch q
447 447 $ chmod 0 q
448 448 $ hg log --style ./q
449 449 abort: Permission denied: ./q
450 450 [255]
451 451 #endif
452 452
453 453 Error if no style:
454 454
455 455 $ hg log --style notexist
456 456 abort: style not found: notexist
457 457 [255]
458 458
459 459 Error if style missing key:
460 460
461 461 $ echo 'q = q' > t
462 462 $ hg log --style ./t
463 463 abort: "changeset" not in template map
464 464 [255]
465 465
466 466 Error if style missing value:
467 467
468 468 $ echo 'changeset =' > t
469 469 $ hg log --style t
470 470 abort: t:1: missing value
471 471 [255]
472 472
473 473 Error if include fails:
474 474
475 475 $ echo 'changeset = q' >> t
476 476 #if unix-permissions
477 477 $ hg log --style ./t
478 478 abort: template file ./q: Permission denied
479 479 [255]
480 480 $ rm q
481 481 #endif
482 482
483 483 Include works:
484 484
485 485 $ echo '{rev}' > q
486 486 $ hg log --style ./t
487 487 8
488 488 7
489 489 6
490 490 5
491 491 4
492 492 3
493 493 2
494 494 1
495 495 0
496 496
497 497 ui.style works:
498 498
499 499 $ echo '[ui]' > .hg/hgrc
500 500 $ echo 'style = t' >> .hg/hgrc
501 501 $ hg log
502 502 8
503 503 7
504 504 6
505 505 5
506 506 4
507 507 3
508 508 2
509 509 1
510 510 0
511 511
512 512
513 513 Issue338:
514 514
515 515 $ hg log --style=changelog > changelog
516 516
517 517 $ cat changelog
518 518 2020-01-01 test <test>
519 519
520 520 * fourth, second, third:
521 521 third
522 522 [95c24699272e] [tip]
523 523
524 524 1970-01-12 User Name <user@hostname>
525 525
526 526 * second:
527 527 second
528 528 [29114dbae42b]
529 529
530 530 1970-01-18 person <person>
531 531
532 532 * merge
533 533 [d41e714fe50d]
534 534
535 535 * d:
536 536 new head
537 537 [13207e5a10d9]
538 538
539 539 1970-01-17 person <person>
540 540
541 541 * new branch
542 542 [bbe44766e73d] <foo>
543 543
544 544 1970-01-16 person <person>
545 545
546 546 * c:
547 547 no user, no domain
548 548 [10e46f2dcbf4]
549 549
550 550 1970-01-14 other <other@place>
551 551
552 552 * c:
553 553 no person
554 554 [97054abb4ab8]
555 555
556 556 1970-01-13 A. N. Other <other@place>
557 557
558 558 * b:
559 559 other 1 other 2
560 560
561 561 other 3
562 562 [b608e9d1a3f0]
563 563
564 564 1970-01-12 User Name <user@hostname>
565 565
566 566 * a:
567 567 line 1 line 2
568 568 [1e4e1b8f71e0]
569 569
570 570
571 571 Issue2130: xml output for 'hg heads' is malformed
572 572
573 573 $ hg heads --style changelog
574 574 2020-01-01 test <test>
575 575
576 576 * fourth, second, third:
577 577 third
578 578 [95c24699272e] [tip]
579 579
580 580 1970-01-18 person <person>
581 581
582 582 * merge
583 583 [d41e714fe50d]
584 584
585 585 1970-01-17 person <person>
586 586
587 587 * new branch
588 588 [bbe44766e73d] <foo>
589 589
590 590
591 591 Keys work:
592 592
593 593 $ for key in author branch branches date desc file_adds file_dels file_mods \
594 594 > file_copies file_copies_switch files \
595 595 > manifest node parents rev tags diffstat extras; do
596 596 > for mode in '' --verbose --debug; do
597 597 > hg log $mode --template "$key$mode: {$key}\n"
598 598 > done
599 599 > done
600 600 author: test
601 601 author: User Name <user@hostname>
602 602 author: person
603 603 author: person
604 604 author: person
605 605 author: person
606 606 author: other@place
607 607 author: A. N. Other <other@place>
608 608 author: User Name <user@hostname>
609 609 author--verbose: test
610 610 author--verbose: User Name <user@hostname>
611 611 author--verbose: person
612 612 author--verbose: person
613 613 author--verbose: person
614 614 author--verbose: person
615 615 author--verbose: other@place
616 616 author--verbose: A. N. Other <other@place>
617 617 author--verbose: User Name <user@hostname>
618 618 author--debug: test
619 619 author--debug: User Name <user@hostname>
620 620 author--debug: person
621 621 author--debug: person
622 622 author--debug: person
623 623 author--debug: person
624 624 author--debug: other@place
625 625 author--debug: A. N. Other <other@place>
626 626 author--debug: User Name <user@hostname>
627 627 branch: default
628 628 branch: default
629 629 branch: default
630 630 branch: default
631 631 branch: foo
632 632 branch: default
633 633 branch: default
634 634 branch: default
635 635 branch: default
636 636 branch--verbose: default
637 637 branch--verbose: default
638 638 branch--verbose: default
639 639 branch--verbose: default
640 640 branch--verbose: foo
641 641 branch--verbose: default
642 642 branch--verbose: default
643 643 branch--verbose: default
644 644 branch--verbose: default
645 645 branch--debug: default
646 646 branch--debug: default
647 647 branch--debug: default
648 648 branch--debug: default
649 649 branch--debug: foo
650 650 branch--debug: default
651 651 branch--debug: default
652 652 branch--debug: default
653 653 branch--debug: default
654 654 branches:
655 655 branches:
656 656 branches:
657 657 branches:
658 658 branches: foo
659 659 branches:
660 660 branches:
661 661 branches:
662 662 branches:
663 663 branches--verbose:
664 664 branches--verbose:
665 665 branches--verbose:
666 666 branches--verbose:
667 667 branches--verbose: foo
668 668 branches--verbose:
669 669 branches--verbose:
670 670 branches--verbose:
671 671 branches--verbose:
672 672 branches--debug:
673 673 branches--debug:
674 674 branches--debug:
675 675 branches--debug:
676 676 branches--debug: foo
677 677 branches--debug:
678 678 branches--debug:
679 679 branches--debug:
680 680 branches--debug:
681 681 date: 1577872860.00
682 682 date: 1000000.00
683 683 date: 1500001.00
684 684 date: 1500000.00
685 685 date: 1400000.00
686 686 date: 1300000.00
687 687 date: 1200000.00
688 688 date: 1100000.00
689 689 date: 1000000.00
690 690 date--verbose: 1577872860.00
691 691 date--verbose: 1000000.00
692 692 date--verbose: 1500001.00
693 693 date--verbose: 1500000.00
694 694 date--verbose: 1400000.00
695 695 date--verbose: 1300000.00
696 696 date--verbose: 1200000.00
697 697 date--verbose: 1100000.00
698 698 date--verbose: 1000000.00
699 699 date--debug: 1577872860.00
700 700 date--debug: 1000000.00
701 701 date--debug: 1500001.00
702 702 date--debug: 1500000.00
703 703 date--debug: 1400000.00
704 704 date--debug: 1300000.00
705 705 date--debug: 1200000.00
706 706 date--debug: 1100000.00
707 707 date--debug: 1000000.00
708 708 desc: third
709 709 desc: second
710 710 desc: merge
711 711 desc: new head
712 712 desc: new branch
713 713 desc: no user, no domain
714 714 desc: no person
715 715 desc: other 1
716 716 other 2
717 717
718 718 other 3
719 719 desc: line 1
720 720 line 2
721 721 desc--verbose: third
722 722 desc--verbose: second
723 723 desc--verbose: merge
724 724 desc--verbose: new head
725 725 desc--verbose: new branch
726 726 desc--verbose: no user, no domain
727 727 desc--verbose: no person
728 728 desc--verbose: other 1
729 729 other 2
730 730
731 731 other 3
732 732 desc--verbose: line 1
733 733 line 2
734 734 desc--debug: third
735 735 desc--debug: second
736 736 desc--debug: merge
737 737 desc--debug: new head
738 738 desc--debug: new branch
739 739 desc--debug: no user, no domain
740 740 desc--debug: no person
741 741 desc--debug: other 1
742 742 other 2
743 743
744 744 other 3
745 745 desc--debug: line 1
746 746 line 2
747 747 file_adds: fourth third
748 748 file_adds: second
749 749 file_adds:
750 750 file_adds: d
751 751 file_adds:
752 752 file_adds:
753 753 file_adds: c
754 754 file_adds: b
755 755 file_adds: a
756 756 file_adds--verbose: fourth third
757 757 file_adds--verbose: second
758 758 file_adds--verbose:
759 759 file_adds--verbose: d
760 760 file_adds--verbose:
761 761 file_adds--verbose:
762 762 file_adds--verbose: c
763 763 file_adds--verbose: b
764 764 file_adds--verbose: a
765 765 file_adds--debug: fourth third
766 766 file_adds--debug: second
767 767 file_adds--debug:
768 768 file_adds--debug: d
769 769 file_adds--debug:
770 770 file_adds--debug:
771 771 file_adds--debug: c
772 772 file_adds--debug: b
773 773 file_adds--debug: a
774 774 file_dels: second
775 775 file_dels:
776 776 file_dels:
777 777 file_dels:
778 778 file_dels:
779 779 file_dels:
780 780 file_dels:
781 781 file_dels:
782 782 file_dels:
783 783 file_dels--verbose: second
784 784 file_dels--verbose:
785 785 file_dels--verbose:
786 786 file_dels--verbose:
787 787 file_dels--verbose:
788 788 file_dels--verbose:
789 789 file_dels--verbose:
790 790 file_dels--verbose:
791 791 file_dels--verbose:
792 792 file_dels--debug: second
793 793 file_dels--debug:
794 794 file_dels--debug:
795 795 file_dels--debug:
796 796 file_dels--debug:
797 797 file_dels--debug:
798 798 file_dels--debug:
799 799 file_dels--debug:
800 800 file_dels--debug:
801 801 file_mods:
802 802 file_mods:
803 803 file_mods:
804 804 file_mods:
805 805 file_mods:
806 806 file_mods: c
807 807 file_mods:
808 808 file_mods:
809 809 file_mods:
810 810 file_mods--verbose:
811 811 file_mods--verbose:
812 812 file_mods--verbose:
813 813 file_mods--verbose:
814 814 file_mods--verbose:
815 815 file_mods--verbose: c
816 816 file_mods--verbose:
817 817 file_mods--verbose:
818 818 file_mods--verbose:
819 819 file_mods--debug:
820 820 file_mods--debug:
821 821 file_mods--debug:
822 822 file_mods--debug:
823 823 file_mods--debug:
824 824 file_mods--debug: c
825 825 file_mods--debug:
826 826 file_mods--debug:
827 827 file_mods--debug:
828 828 file_copies: fourth (second)
829 829 file_copies:
830 830 file_copies:
831 831 file_copies:
832 832 file_copies:
833 833 file_copies:
834 834 file_copies:
835 835 file_copies:
836 836 file_copies:
837 837 file_copies--verbose: fourth (second)
838 838 file_copies--verbose:
839 839 file_copies--verbose:
840 840 file_copies--verbose:
841 841 file_copies--verbose:
842 842 file_copies--verbose:
843 843 file_copies--verbose:
844 844 file_copies--verbose:
845 845 file_copies--verbose:
846 846 file_copies--debug: fourth (second)
847 847 file_copies--debug:
848 848 file_copies--debug:
849 849 file_copies--debug:
850 850 file_copies--debug:
851 851 file_copies--debug:
852 852 file_copies--debug:
853 853 file_copies--debug:
854 854 file_copies--debug:
855 855 file_copies_switch:
856 856 file_copies_switch:
857 857 file_copies_switch:
858 858 file_copies_switch:
859 859 file_copies_switch:
860 860 file_copies_switch:
861 861 file_copies_switch:
862 862 file_copies_switch:
863 863 file_copies_switch:
864 864 file_copies_switch--verbose:
865 865 file_copies_switch--verbose:
866 866 file_copies_switch--verbose:
867 867 file_copies_switch--verbose:
868 868 file_copies_switch--verbose:
869 869 file_copies_switch--verbose:
870 870 file_copies_switch--verbose:
871 871 file_copies_switch--verbose:
872 872 file_copies_switch--verbose:
873 873 file_copies_switch--debug:
874 874 file_copies_switch--debug:
875 875 file_copies_switch--debug:
876 876 file_copies_switch--debug:
877 877 file_copies_switch--debug:
878 878 file_copies_switch--debug:
879 879 file_copies_switch--debug:
880 880 file_copies_switch--debug:
881 881 file_copies_switch--debug:
882 882 files: fourth second third
883 883 files: second
884 884 files:
885 885 files: d
886 886 files:
887 887 files: c
888 888 files: c
889 889 files: b
890 890 files: a
891 891 files--verbose: fourth second third
892 892 files--verbose: second
893 893 files--verbose:
894 894 files--verbose: d
895 895 files--verbose:
896 896 files--verbose: c
897 897 files--verbose: c
898 898 files--verbose: b
899 899 files--verbose: a
900 900 files--debug: fourth second third
901 901 files--debug: second
902 902 files--debug:
903 903 files--debug: d
904 904 files--debug:
905 905 files--debug: c
906 906 files--debug: c
907 907 files--debug: b
908 908 files--debug: a
909 909 manifest: 6:94961b75a2da
910 910 manifest: 5:f2dbc354b94e
911 911 manifest: 4:4dc3def4f9b4
912 912 manifest: 4:4dc3def4f9b4
913 913 manifest: 3:cb5a1327723b
914 914 manifest: 3:cb5a1327723b
915 915 manifest: 2:6e0e82995c35
916 916 manifest: 1:4e8d705b1e53
917 917 manifest: 0:a0c8bcbbb45c
918 918 manifest--verbose: 6:94961b75a2da
919 919 manifest--verbose: 5:f2dbc354b94e
920 920 manifest--verbose: 4:4dc3def4f9b4
921 921 manifest--verbose: 4:4dc3def4f9b4
922 922 manifest--verbose: 3:cb5a1327723b
923 923 manifest--verbose: 3:cb5a1327723b
924 924 manifest--verbose: 2:6e0e82995c35
925 925 manifest--verbose: 1:4e8d705b1e53
926 926 manifest--verbose: 0:a0c8bcbbb45c
927 927 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
928 928 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
929 929 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
930 930 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
931 931 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
932 932 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
933 933 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
934 934 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
935 935 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
936 936 node: 95c24699272ef57d062b8bccc32c878bf841784a
937 937 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
938 938 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
939 939 node: 13207e5a10d9fd28ec424934298e176197f2c67f
940 940 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
941 941 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
942 942 node: 97054abb4ab824450e9164180baf491ae0078465
943 943 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
944 944 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
945 945 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
946 946 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
947 947 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
948 948 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
949 949 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
950 950 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
951 951 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
952 952 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
953 953 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
954 954 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
955 955 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
956 956 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
957 957 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
958 958 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
959 959 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
960 960 node--debug: 97054abb4ab824450e9164180baf491ae0078465
961 961 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
962 962 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
963 963 parents:
964 964 parents: -1:000000000000
965 965 parents: 5:13207e5a10d9 4:bbe44766e73d
966 966 parents: 3:10e46f2dcbf4
967 967 parents:
968 968 parents:
969 969 parents:
970 970 parents:
971 971 parents:
972 972 parents--verbose:
973 973 parents--verbose: -1:000000000000
974 974 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
975 975 parents--verbose: 3:10e46f2dcbf4
976 976 parents--verbose:
977 977 parents--verbose:
978 978 parents--verbose:
979 979 parents--verbose:
980 980 parents--verbose:
981 981 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
982 982 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
983 983 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
984 984 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
985 985 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
986 986 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
987 987 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
988 988 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
989 989 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
990 990 rev: 8
991 991 rev: 7
992 992 rev: 6
993 993 rev: 5
994 994 rev: 4
995 995 rev: 3
996 996 rev: 2
997 997 rev: 1
998 998 rev: 0
999 999 rev--verbose: 8
1000 1000 rev--verbose: 7
1001 1001 rev--verbose: 6
1002 1002 rev--verbose: 5
1003 1003 rev--verbose: 4
1004 1004 rev--verbose: 3
1005 1005 rev--verbose: 2
1006 1006 rev--verbose: 1
1007 1007 rev--verbose: 0
1008 1008 rev--debug: 8
1009 1009 rev--debug: 7
1010 1010 rev--debug: 6
1011 1011 rev--debug: 5
1012 1012 rev--debug: 4
1013 1013 rev--debug: 3
1014 1014 rev--debug: 2
1015 1015 rev--debug: 1
1016 1016 rev--debug: 0
1017 1017 tags: tip
1018 1018 tags:
1019 1019 tags:
1020 1020 tags:
1021 1021 tags:
1022 1022 tags:
1023 1023 tags:
1024 1024 tags:
1025 1025 tags:
1026 1026 tags--verbose: tip
1027 1027 tags--verbose:
1028 1028 tags--verbose:
1029 1029 tags--verbose:
1030 1030 tags--verbose:
1031 1031 tags--verbose:
1032 1032 tags--verbose:
1033 1033 tags--verbose:
1034 1034 tags--verbose:
1035 1035 tags--debug: tip
1036 1036 tags--debug:
1037 1037 tags--debug:
1038 1038 tags--debug:
1039 1039 tags--debug:
1040 1040 tags--debug:
1041 1041 tags--debug:
1042 1042 tags--debug:
1043 1043 tags--debug:
1044 1044 diffstat: 3: +2/-1
1045 1045 diffstat: 1: +1/-0
1046 1046 diffstat: 0: +0/-0
1047 1047 diffstat: 1: +1/-0
1048 1048 diffstat: 0: +0/-0
1049 1049 diffstat: 1: +1/-0
1050 1050 diffstat: 1: +4/-0
1051 1051 diffstat: 1: +2/-0
1052 1052 diffstat: 1: +1/-0
1053 1053 diffstat--verbose: 3: +2/-1
1054 1054 diffstat--verbose: 1: +1/-0
1055 1055 diffstat--verbose: 0: +0/-0
1056 1056 diffstat--verbose: 1: +1/-0
1057 1057 diffstat--verbose: 0: +0/-0
1058 1058 diffstat--verbose: 1: +1/-0
1059 1059 diffstat--verbose: 1: +4/-0
1060 1060 diffstat--verbose: 1: +2/-0
1061 1061 diffstat--verbose: 1: +1/-0
1062 1062 diffstat--debug: 3: +2/-1
1063 1063 diffstat--debug: 1: +1/-0
1064 1064 diffstat--debug: 0: +0/-0
1065 1065 diffstat--debug: 1: +1/-0
1066 1066 diffstat--debug: 0: +0/-0
1067 1067 diffstat--debug: 1: +1/-0
1068 1068 diffstat--debug: 1: +4/-0
1069 1069 diffstat--debug: 1: +2/-0
1070 1070 diffstat--debug: 1: +1/-0
1071 1071 extras: branch=default
1072 1072 extras: branch=default
1073 1073 extras: branch=default
1074 1074 extras: branch=default
1075 1075 extras: branch=foo
1076 1076 extras: branch=default
1077 1077 extras: branch=default
1078 1078 extras: branch=default
1079 1079 extras: branch=default
1080 1080 extras--verbose: branch=default
1081 1081 extras--verbose: branch=default
1082 1082 extras--verbose: branch=default
1083 1083 extras--verbose: branch=default
1084 1084 extras--verbose: branch=foo
1085 1085 extras--verbose: branch=default
1086 1086 extras--verbose: branch=default
1087 1087 extras--verbose: branch=default
1088 1088 extras--verbose: branch=default
1089 1089 extras--debug: branch=default
1090 1090 extras--debug: branch=default
1091 1091 extras--debug: branch=default
1092 1092 extras--debug: branch=default
1093 1093 extras--debug: branch=foo
1094 1094 extras--debug: branch=default
1095 1095 extras--debug: branch=default
1096 1096 extras--debug: branch=default
1097 1097 extras--debug: branch=default
1098 1098
1099 1099
1100 1100 Filters work:
1101 1101
1102 1102 $ hg log --template '{author|domain}\n'
1103 1103
1104 1104 hostname
1105 1105
1106 1106
1107 1107
1108 1108
1109 1109 place
1110 1110 place
1111 1111 hostname
1112 1112
1113 1113 $ hg log --template '{author|person}\n'
1114 1114 test
1115 1115 User Name
1116 1116 person
1117 1117 person
1118 1118 person
1119 1119 person
1120 1120 other
1121 1121 A. N. Other
1122 1122 User Name
1123 1123
1124 1124 $ hg log --template '{author|user}\n'
1125 1125 test
1126 1126 user
1127 1127 person
1128 1128 person
1129 1129 person
1130 1130 person
1131 1131 other
1132 1132 other
1133 1133 user
1134 1134
1135 1135 $ hg log --template '{date|date}\n'
1136 1136 Wed Jan 01 10:01:00 2020 +0000
1137 1137 Mon Jan 12 13:46:40 1970 +0000
1138 1138 Sun Jan 18 08:40:01 1970 +0000
1139 1139 Sun Jan 18 08:40:00 1970 +0000
1140 1140 Sat Jan 17 04:53:20 1970 +0000
1141 1141 Fri Jan 16 01:06:40 1970 +0000
1142 1142 Wed Jan 14 21:20:00 1970 +0000
1143 1143 Tue Jan 13 17:33:20 1970 +0000
1144 1144 Mon Jan 12 13:46:40 1970 +0000
1145 1145
1146 1146 $ hg log --template '{date|isodate}\n'
1147 1147 2020-01-01 10:01 +0000
1148 1148 1970-01-12 13:46 +0000
1149 1149 1970-01-18 08:40 +0000
1150 1150 1970-01-18 08:40 +0000
1151 1151 1970-01-17 04:53 +0000
1152 1152 1970-01-16 01:06 +0000
1153 1153 1970-01-14 21:20 +0000
1154 1154 1970-01-13 17:33 +0000
1155 1155 1970-01-12 13:46 +0000
1156 1156
1157 1157 $ hg log --template '{date|isodatesec}\n'
1158 1158 2020-01-01 10:01:00 +0000
1159 1159 1970-01-12 13:46:40 +0000
1160 1160 1970-01-18 08:40:01 +0000
1161 1161 1970-01-18 08:40:00 +0000
1162 1162 1970-01-17 04:53:20 +0000
1163 1163 1970-01-16 01:06:40 +0000
1164 1164 1970-01-14 21:20:00 +0000
1165 1165 1970-01-13 17:33:20 +0000
1166 1166 1970-01-12 13:46:40 +0000
1167 1167
1168 1168 $ hg log --template '{date|rfc822date}\n'
1169 1169 Wed, 01 Jan 2020 10:01:00 +0000
1170 1170 Mon, 12 Jan 1970 13:46:40 +0000
1171 1171 Sun, 18 Jan 1970 08:40:01 +0000
1172 1172 Sun, 18 Jan 1970 08:40:00 +0000
1173 1173 Sat, 17 Jan 1970 04:53:20 +0000
1174 1174 Fri, 16 Jan 1970 01:06:40 +0000
1175 1175 Wed, 14 Jan 1970 21:20:00 +0000
1176 1176 Tue, 13 Jan 1970 17:33:20 +0000
1177 1177 Mon, 12 Jan 1970 13:46:40 +0000
1178 1178
1179 1179 $ hg log --template '{desc|firstline}\n'
1180 1180 third
1181 1181 second
1182 1182 merge
1183 1183 new head
1184 1184 new branch
1185 1185 no user, no domain
1186 1186 no person
1187 1187 other 1
1188 1188 line 1
1189 1189
1190 1190 $ hg log --template '{node|short}\n'
1191 1191 95c24699272e
1192 1192 29114dbae42b
1193 1193 d41e714fe50d
1194 1194 13207e5a10d9
1195 1195 bbe44766e73d
1196 1196 10e46f2dcbf4
1197 1197 97054abb4ab8
1198 1198 b608e9d1a3f0
1199 1199 1e4e1b8f71e0
1200 1200
1201 1201 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1202 1202 <changeset author="test"/>
1203 1203 <changeset author="User Name &lt;user@hostname&gt;"/>
1204 1204 <changeset author="person"/>
1205 1205 <changeset author="person"/>
1206 1206 <changeset author="person"/>
1207 1207 <changeset author="person"/>
1208 1208 <changeset author="other@place"/>
1209 1209 <changeset author="A. N. Other &lt;other@place&gt;"/>
1210 1210 <changeset author="User Name &lt;user@hostname&gt;"/>
1211 1211
1212 1212 $ hg log --template '{rev}: {children}\n'
1213 1213 8:
1214 1214 7: 8:95c24699272e
1215 1215 6:
1216 1216 5: 6:d41e714fe50d
1217 1217 4: 6:d41e714fe50d
1218 1218 3: 4:bbe44766e73d 5:13207e5a10d9
1219 1219 2: 3:10e46f2dcbf4
1220 1220 1: 2:97054abb4ab8
1221 1221 0: 1:b608e9d1a3f0
1222 1222
1223 1223 Formatnode filter works:
1224 1224
1225 1225 $ hg -q log -r 0 --template '{node|formatnode}\n'
1226 1226 1e4e1b8f71e0
1227 1227
1228 1228 $ hg log -r 0 --template '{node|formatnode}\n'
1229 1229 1e4e1b8f71e0
1230 1230
1231 1231 $ hg -v log -r 0 --template '{node|formatnode}\n'
1232 1232 1e4e1b8f71e0
1233 1233
1234 1234 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1235 1235 1e4e1b8f71e05681d422154f5421e385fec3454f
1236 1236
1237 1237 Age filter:
1238 1238
1239 1239 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1240 1240
1241 1241 >>> from datetime import datetime
1242 1242 >>> fp = open('a', 'w')
1243 1243 >>> fp.write(str(datetime.now().year + 8) + '-01-01 00:00')
1244 1244 >>> fp.close()
1245 1245 $ hg add a
1246 1246 $ hg commit -m future -d "`cat a`"
1247 1247
1248 1248 $ hg log -l1 --template '{date|age}\n'
1249 1249 7 years from now
1250 1250
1251 1251 Error on syntax:
1252 1252
1253 1253 $ echo 'x = "f' >> t
1254 1254 $ hg log
1255 1255 abort: t:3: unmatched quotes
1256 1256 [255]
1257 1257
1258 Behind the scenes, this will throw TypeError
1259
1260 $ hg log -l 3 --template '{date|obfuscate}\n'
1261 abort: Template filter 'obfuscate' is not compatible with keyword 'date'
1262 [255]
1263
1264 Behind the scenes, this will throw a ValueError
1265
1266 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
1267 abort: Template filter 'shortdate' is not compatible with keyword 'desc'
1268 [255]
1269
1270 Behind the scenes, this will throw AttributeError
1271
1272 $ hg log -l 3 --template 'line: {date|escape}\n'
1273 abort: Template filter 'escape' is not compatible with keyword 'date'
1274 [255]
1275
1276 Behind the scenes, this will throw ValueError
1277
1278 $ hg tip --template '{author|email|date}\n'
1279 abort: Template filter 'datefilter' is not compatible with keyword 'author'
1280 [255]
1281
1258 1282 $ cd ..
1259 1283
1260 1284
1261 1285 latesttag:
1262 1286
1263 1287 $ hg init latesttag
1264 1288 $ cd latesttag
1265 1289
1266 1290 $ echo a > file
1267 1291 $ hg ci -Am a -d '0 0'
1268 1292 adding file
1269 1293
1270 1294 $ echo b >> file
1271 1295 $ hg ci -m b -d '1 0'
1272 1296
1273 1297 $ echo c >> head1
1274 1298 $ hg ci -Am h1c -d '2 0'
1275 1299 adding head1
1276 1300
1277 1301 $ hg update -q 1
1278 1302 $ echo d >> head2
1279 1303 $ hg ci -Am h2d -d '3 0'
1280 1304 adding head2
1281 1305 created new head
1282 1306
1283 1307 $ echo e >> head2
1284 1308 $ hg ci -m h2e -d '4 0'
1285 1309
1286 1310 $ hg merge -q
1287 1311 $ hg ci -m merge -d '5 0'
1288 1312
1289 1313 No tag set:
1290 1314
1291 1315 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1292 1316 5: null+5
1293 1317 4: null+4
1294 1318 3: null+3
1295 1319 2: null+3
1296 1320 1: null+2
1297 1321 0: null+1
1298 1322
1299 1323 One common tag: longuest path wins:
1300 1324
1301 1325 $ hg tag -r 1 -m t1 -d '6 0' t1
1302 1326 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1303 1327 6: t1+4
1304 1328 5: t1+3
1305 1329 4: t1+2
1306 1330 3: t1+1
1307 1331 2: t1+1
1308 1332 1: t1+0
1309 1333 0: null+1
1310 1334
1311 1335 One ancestor tag: more recent wins:
1312 1336
1313 1337 $ hg tag -r 2 -m t2 -d '7 0' t2
1314 1338 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1315 1339 7: t2+3
1316 1340 6: t2+2
1317 1341 5: t2+1
1318 1342 4: t1+2
1319 1343 3: t1+1
1320 1344 2: t2+0
1321 1345 1: t1+0
1322 1346 0: null+1
1323 1347
1324 1348 Two branch tags: more recent wins:
1325 1349
1326 1350 $ hg tag -r 3 -m t3 -d '8 0' t3
1327 1351 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1328 1352 8: t3+5
1329 1353 7: t3+4
1330 1354 6: t3+3
1331 1355 5: t3+2
1332 1356 4: t3+1
1333 1357 3: t3+0
1334 1358 2: t2+0
1335 1359 1: t1+0
1336 1360 0: null+1
1337 1361
1338 1362 Merged tag overrides:
1339 1363
1340 1364 $ hg tag -r 5 -m t5 -d '9 0' t5
1341 1365 $ hg tag -r 3 -m at3 -d '10 0' at3
1342 1366 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1343 1367 10: t5+5
1344 1368 9: t5+4
1345 1369 8: t5+3
1346 1370 7: t5+2
1347 1371 6: t5+1
1348 1372 5: t5+0
1349 1373 4: at3:t3+1
1350 1374 3: at3:t3+0
1351 1375 2: t2+0
1352 1376 1: t1+0
1353 1377 0: null+1
1354 1378
1355 1379 $ cd ..
1356 1380
1357 1381
1358 1382 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1359 1383 if it is a relative path
1360 1384
1361 1385 $ mkdir -p home/styles
1362 1386
1363 1387 $ cat > home/styles/teststyle <<EOF
1364 1388 > changeset = 'test {rev}:{node|short}\n'
1365 1389 > EOF
1366 1390
1367 1391 $ HOME=`pwd`/home; export HOME
1368 1392
1369 1393 $ cat > latesttag/.hg/hgrc <<EOF
1370 1394 > [ui]
1371 1395 > style = ~/styles/teststyle
1372 1396 > EOF
1373 1397
1374 1398 $ hg -R latesttag tip
1375 1399 test 10:dee8f28249af
1376 1400
1377 1401 Test recursive showlist template (issue1989):
1378 1402
1379 1403 $ cat > style1989 <<EOF
1380 1404 > changeset = '{file_mods}{manifest}{extras}'
1381 1405 > file_mod = 'M|{author|person}\n'
1382 1406 > manifest = '{rev},{author}\n'
1383 1407 > extra = '{key}: {author}\n'
1384 1408 > EOF
1385 1409
1386 1410 $ hg -R latesttag log -r tip --style=style1989
1387 1411 M|test
1388 1412 10,test
1389 1413 branch: test
1390 1414
General Comments 0
You need to be logged in to leave comments. Login now