##// END OF EJS Templates
templating: make new-style templating features work with command line lists
Matt Mackall -
r17631:0b241d7a default
parent child Browse files
Show More
@@ -1,367 +1,392
1 1 # templatekw.py - common changeset template keywords
2 2 #
3 3 # Copyright 2005-2009 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 node import hex
9 9 import patch, util, error
10 10 import hbisect
11 11
12 def showlist(name, values, plural=None, **args):
12 # This helper class allows us to handle both:
13 # "{files}" (legacy command-line-specific list hack) and
14 # "{files % '{file}\n'}" (hgweb-style with inlining and function support)
15
16 class _hybrid(object):
17 def __init__(self, gen, values):
18 self.gen = gen
19 self.values = values
20 def __iter__(self):
21 return self.gen
22 def __call__(self):
23 for x in self.values:
24 yield x
25
26 def showlist(name, values, plural=None, element=None, **args):
27 if not element:
28 element = name
29 f = _showlist(name, values, plural, **args)
30 return _hybrid(f, [{element: x} for x in values])
31
32 def _showlist(name, values, plural=None, **args):
13 33 '''expand set of values.
14 34 name is name of key in template map.
15 35 values is list of strings or dicts.
16 36 plural is plural of name, if not simply name + 's'.
17 37
18 38 expansion works like this, given name 'foo'.
19 39
20 40 if values is empty, expand 'no_foos'.
21 41
22 42 if 'foo' not in template map, return values as a string,
23 43 joined by space.
24 44
25 45 expand 'start_foos'.
26 46
27 47 for each value, expand 'foo'. if 'last_foo' in template
28 48 map, expand it instead of 'foo' for last key.
29 49
30 50 expand 'end_foos'.
31 51 '''
32 52 templ = args['templ']
33 53 if plural:
34 54 names = plural
35 55 else: names = name + 's'
36 56 if not values:
37 57 noname = 'no_' + names
38 58 if noname in templ:
39 59 yield templ(noname, **args)
40 60 return
41 61 if name not in templ:
42 62 if isinstance(values[0], str):
43 63 yield ' '.join(values)
44 64 else:
45 65 for v in values:
46 66 yield dict(v, **args)
47 67 return
48 68 startname = 'start_' + names
49 69 if startname in templ:
50 70 yield templ(startname, **args)
51 71 vargs = args.copy()
52 72 def one(v, tag=name):
53 73 try:
54 74 vargs.update(v)
55 75 except (AttributeError, ValueError):
56 76 try:
57 77 for a, b in v:
58 78 vargs[a] = b
59 79 except ValueError:
60 80 vargs[name] = v
61 81 return templ(tag, **vargs)
62 82 lastname = 'last_' + name
63 83 if lastname in templ:
64 84 last = values.pop()
65 85 else:
66 86 last = None
67 87 for v in values:
68 88 yield one(v)
69 89 if last is not None:
70 90 yield one(last, tag=lastname)
71 91 endname = 'end_' + names
72 92 if endname in templ:
73 93 yield templ(endname, **args)
74 94
75 95 def getfiles(repo, ctx, revcache):
76 96 if 'files' not in revcache:
77 97 revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
78 98 return revcache['files']
79 99
80 100 def getlatesttags(repo, ctx, cache):
81 101 '''return date, distance and name for the latest tag of rev'''
82 102
83 103 if 'latesttags' not in cache:
84 104 # Cache mapping from rev to a tuple with tag date, tag
85 105 # distance and tag name
86 106 cache['latesttags'] = {-1: (0, 0, 'null')}
87 107 latesttags = cache['latesttags']
88 108
89 109 rev = ctx.rev()
90 110 todo = [rev]
91 111 while todo:
92 112 rev = todo.pop()
93 113 if rev in latesttags:
94 114 continue
95 115 ctx = repo[rev]
96 116 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
97 117 if tags:
98 118 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
99 119 continue
100 120 try:
101 121 # The tuples are laid out so the right one can be found by
102 122 # comparison.
103 123 pdate, pdist, ptag = max(
104 124 latesttags[p.rev()] for p in ctx.parents())
105 125 except KeyError:
106 126 # Cache miss - recurse
107 127 todo.append(rev)
108 128 todo.extend(p.rev() for p in ctx.parents())
109 129 continue
110 130 latesttags[rev] = pdate, pdist + 1, ptag
111 131 return latesttags[rev]
112 132
113 133 def getrenamedfn(repo, endrev=None):
114 134 rcache = {}
115 135 if endrev is None:
116 136 endrev = len(repo)
117 137
118 138 def getrenamed(fn, rev):
119 139 '''looks up all renames for a file (up to endrev) the first
120 140 time the file is given. It indexes on the changerev and only
121 141 parses the manifest if linkrev != changerev.
122 142 Returns rename info for fn at changerev rev.'''
123 143 if fn not in rcache:
124 144 rcache[fn] = {}
125 145 fl = repo.file(fn)
126 146 for i in fl:
127 147 lr = fl.linkrev(i)
128 148 renamed = fl.renamed(fl.node(i))
129 149 rcache[fn][lr] = renamed
130 150 if lr >= endrev:
131 151 break
132 152 if rev in rcache[fn]:
133 153 return rcache[fn][rev]
134 154
135 155 # If linkrev != rev (i.e. rev not found in rcache) fallback to
136 156 # filectx logic.
137 157 try:
138 158 return repo[rev][fn].renamed()
139 159 except error.LookupError:
140 160 return None
141 161
142 162 return getrenamed
143 163
144 164
145 165 def showauthor(repo, ctx, templ, **args):
146 166 """:author: String. The unmodified author of the changeset."""
147 167 return ctx.user()
148 168
149 169 def showbisect(repo, ctx, templ, **args):
150 170 """:bisect: String. The changeset bisection status."""
151 171 return hbisect.label(repo, ctx.node())
152 172
153 173 def showbranch(**args):
154 174 """:branch: String. The name of the branch on which the changeset was
155 175 committed.
156 176 """
157 177 return args['ctx'].branch()
158 178
159 179 def showbranches(**args):
160 180 """:branches: List of strings. The name of the branch on which the
161 181 changeset was committed. Will be empty if the branch name was
162 182 default.
163 183 """
164 184 branch = args['ctx'].branch()
165 185 if branch != 'default':
166 186 return showlist('branch', [branch], plural='branches', **args)
167 187
168 188 def showbookmarks(**args):
169 189 """:bookmarks: List of strings. Any bookmarks associated with the
170 190 changeset.
171 191 """
172 192 bookmarks = args['ctx'].bookmarks()
173 193 return showlist('bookmark', bookmarks, **args)
174 194
175 195 def showchildren(**args):
176 196 """:children: List of strings. The children of the changeset."""
177 197 ctx = args['ctx']
178 198 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
179 return showlist('children', childrevs, **args)
199 return showlist('children', childrevs, element='child', **args)
180 200
181 201 def showdate(repo, ctx, templ, **args):
182 202 """:date: Date information. The date when the changeset was committed."""
183 203 return ctx.date()
184 204
185 205 def showdescription(repo, ctx, templ, **args):
186 206 """:desc: String. The text of the changeset description."""
187 207 return ctx.description().strip()
188 208
189 209 def showdiffstat(repo, ctx, templ, **args):
190 210 """:diffstat: String. Statistics of changes with the following format:
191 211 "modified files: +added/-removed lines"
192 212 """
193 213 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
194 214 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
195 215 return '%s: +%s/-%s' % (len(stats), adds, removes)
196 216
197 217 def showextras(**args):
198 218 templ = args['templ']
199 219 for key, value in sorted(args['ctx'].extra().items()):
200 220 args = args.copy()
201 221 args.update(dict(key=key, value=value))
202 222 yield templ('extra', **args)
203 223
204 224 def showfileadds(**args):
205 225 """:file_adds: List of strings. Files added by this changeset."""
206 226 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
207 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
227 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
228 element='file', **args)
208 229
209 230 def showfilecopies(**args):
210 231 """:file_copies: List of strings. Files copied in this changeset with
211 232 their sources.
212 233 """
213 234 cache, ctx = args['cache'], args['ctx']
214 235 copies = args['revcache'].get('copies')
215 236 if copies is None:
216 237 if 'getrenamed' not in cache:
217 238 cache['getrenamed'] = getrenamedfn(args['repo'])
218 239 copies = []
219 240 getrenamed = cache['getrenamed']
220 241 for fn in ctx.files():
221 242 rename = getrenamed(fn, ctx.rev())
222 243 if rename:
223 244 copies.append((fn, rename[0]))
224 245
225 246 c = [{'name': x[0], 'source': x[1]} for x in copies]
226 return showlist('file_copy', c, plural='file_copies', **args)
247 return showlist('file_copy', c, plural='file_copies',
248 element='file', **args)
227 249
228 250 # showfilecopiesswitch() displays file copies only if copy records are
229 251 # provided before calling the templater, usually with a --copies
230 252 # command line switch.
231 253 def showfilecopiesswitch(**args):
232 254 """:file_copies_switch: List of strings. Like "file_copies" but displayed
233 255 only if the --copied switch is set.
234 256 """
235 257 copies = args['revcache'].get('copies') or []
236 258 c = [{'name': x[0], 'source': x[1]} for x in copies]
237 return showlist('file_copy', c, plural='file_copies', **args)
259 return showlist('file_copy', c, plural='file_copies',
260 element='file', **args)
238 261
239 262 def showfiledels(**args):
240 263 """:file_dels: List of strings. Files removed by this changeset."""
241 264 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
242 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
265 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
266 element='file', **args)
243 267
244 268 def showfilemods(**args):
245 269 """:file_mods: List of strings. Files modified by this changeset."""
246 270 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
247 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
271 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
272 element='file', **args)
248 273
249 274 def showfiles(**args):
250 275 """:files: List of strings. All files modified, added, or removed by this
251 276 changeset.
252 277 """
253 278 return showlist('file', args['ctx'].files(), **args)
254 279
255 280 def showlatesttag(repo, ctx, templ, cache, **args):
256 281 """:latesttag: String. Most recent global tag in the ancestors of this
257 282 changeset.
258 283 """
259 284 return getlatesttags(repo, ctx, cache)[2]
260 285
261 286 def showlatesttagdistance(repo, ctx, templ, cache, **args):
262 287 """:latesttagdistance: Integer. Longest path to the latest tag."""
263 288 return getlatesttags(repo, ctx, cache)[1]
264 289
265 290 def showmanifest(**args):
266 291 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
267 292 args = args.copy()
268 293 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
269 294 node=hex(ctx.changeset()[0])))
270 295 return templ('manifest', **args)
271 296
272 297 def shownode(repo, ctx, templ, **args):
273 298 """:node: String. The changeset identification hash, as a 40 hexadecimal
274 299 digit string.
275 300 """
276 301 return ctx.hex()
277 302
278 303 def showp1rev(repo, ctx, templ, **args):
279 304 """:p1rev: Integer. The repository-local revision number of the changeset's
280 305 first parent, or -1 if the changeset has no parents."""
281 306 return ctx.p1().rev()
282 307
283 308 def showp2rev(repo, ctx, templ, **args):
284 309 """:p2rev: Integer. The repository-local revision number of the changeset's
285 310 second parent, or -1 if the changeset has no second parent."""
286 311 return ctx.p2().rev()
287 312
288 313 def showp1node(repo, ctx, templ, **args):
289 314 """:p1node: String. The identification hash of the changeset's first parent,
290 315 as a 40 digit hexadecimal string. If the changeset has no parents, all
291 316 digits are 0."""
292 317 return ctx.p1().hex()
293 318
294 319 def showp2node(repo, ctx, templ, **args):
295 320 """:p2node: String. The identification hash of the changeset's second
296 321 parent, as a 40 digit hexadecimal string. If the changeset has no second
297 322 parent, all digits are 0."""
298 323 return ctx.p2().hex()
299 324
300 325 def showphase(repo, ctx, templ, **args):
301 326 """:phase: String. The changeset phase name."""
302 327 return ctx.phasestr()
303 328
304 329 def showphaseidx(repo, ctx, templ, **args):
305 330 """:phaseidx: Integer. The changeset phase index."""
306 331 return ctx.phase()
307 332
308 333 def showrev(repo, ctx, templ, **args):
309 334 """:rev: Integer. The repository-local changeset revision number."""
310 335 return ctx.rev()
311 336
312 337 def showtags(**args):
313 338 """:tags: List of strings. Any tags associated with the changeset."""
314 339 return showlist('tag', args['ctx'].tags(), **args)
315 340
316 341 # keywords are callables like:
317 342 # fn(repo, ctx, templ, cache, revcache, **args)
318 343 # with:
319 344 # repo - current repository instance
320 345 # ctx - the changectx being displayed
321 346 # templ - the templater instance
322 347 # cache - a cache dictionary for the whole templater run
323 348 # revcache - a cache dictionary for the current revision
324 349 keywords = {
325 350 'author': showauthor,
326 351 'bisect': showbisect,
327 352 'branch': showbranch,
328 353 'branches': showbranches,
329 354 'bookmarks': showbookmarks,
330 355 'children': showchildren,
331 356 'date': showdate,
332 357 'desc': showdescription,
333 358 'diffstat': showdiffstat,
334 359 'extras': showextras,
335 360 'file_adds': showfileadds,
336 361 'file_copies': showfilecopies,
337 362 'file_copies_switch': showfilecopiesswitch,
338 363 'file_dels': showfiledels,
339 364 'file_mods': showfilemods,
340 365 'files': showfiles,
341 366 'latesttag': showlatesttag,
342 367 'latesttagdistance': showlatesttagdistance,
343 368 'manifest': showmanifest,
344 369 'node': shownode,
345 370 'p1rev': showp1rev,
346 371 'p1node': showp1node,
347 372 'p2rev': showp2rev,
348 373 'p2node': showp2node,
349 374 'phase': showphase,
350 375 'phaseidx': showphaseidx,
351 376 'rev': showrev,
352 377 'tags': showtags,
353 378 }
354 379
355 380 def _showparents(**args):
356 381 """:parents: List of strings. The parents of the changeset in "rev:node"
357 382 format. If the changeset has only one "natural" parent (the predecessor
358 383 revision) nothing is shown."""
359 384 pass
360 385
361 386 dockeywords = {
362 387 'parents': _showparents,
363 388 }
364 389 dockeywords.update(keywords)
365 390
366 391 # tell hggettext to extract docstrings from these functions:
367 392 i18nfunctions = dockeywords.values()
@@ -1,402 +1,405
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 149 try:
150 150 return filt(func(context, mapping, data))
151 151 except (ValueError, AttributeError, TypeError):
152 152 if isinstance(data, tuple):
153 153 dt = data[1]
154 154 else:
155 155 dt = data
156 156 raise util.Abort(_("template filter '%s' is not compatible with "
157 157 "keyword '%s'") % (filt.func_name, dt))
158 158
159 159 def buildmap(exp, context):
160 160 func, data = compileexp(exp[1], context)
161 161 ctmpl = gettemplate(exp[2], context)
162 162 return (runmap, (func, data, ctmpl))
163 163
164 164 def runmap(context, mapping, data):
165 165 func, data, ctmpl = data
166 166 d = func(context, mapping, data)
167 if util.safehasattr(d, '__call__'):
168 d = d()
169
167 170 lm = mapping.copy()
168 171
169 172 for i in d:
170 173 if isinstance(i, dict):
171 174 lm.update(i)
172 175 for f, d in ctmpl:
173 176 yield f(context, lm, d)
174 177 else:
175 178 # v is not an iterable of dicts, this happen when 'key'
176 179 # has been fully expanded already and format is useless.
177 180 # If so, return the expanded value.
178 181 yield i
179 182
180 183 def buildfunc(exp, context):
181 184 n = getsymbol(exp[1])
182 185 args = [compileexp(x, context) for x in getlist(exp[2])]
183 186 if n in funcs:
184 187 f = funcs[n]
185 188 return (f, args)
186 189 if n in context._filters:
187 190 if len(args) != 1:
188 191 raise error.ParseError(_("filter %s expects one argument") % n)
189 192 f = context._filters[n]
190 193 return (runfilter, (args[0][0], args[0][1], f))
191 194
192 195 methods = {
193 196 "string": lambda e, c: (runstring, e[1]),
194 197 "symbol": lambda e, c: (runsymbol, e[1]),
195 198 "group": lambda e, c: compileexp(e[1], c),
196 199 # ".": buildmember,
197 200 "|": buildfilter,
198 201 "%": buildmap,
199 202 "func": buildfunc,
200 203 }
201 204
202 205 funcs = {
203 206 }
204 207
205 208 # template engine
206 209
207 210 path = ['templates', '../templates']
208 211 stringify = templatefilters.stringify
209 212
210 213 def _flatten(thing):
211 214 '''yield a single stream from a possibly nested set of iterators'''
212 215 if isinstance(thing, str):
213 216 yield thing
214 217 elif not util.safehasattr(thing, '__iter__'):
215 218 if thing is not None:
216 219 yield str(thing)
217 220 else:
218 221 for i in thing:
219 222 if isinstance(i, str):
220 223 yield i
221 224 elif not util.safehasattr(i, '__iter__'):
222 225 if i is not None:
223 226 yield str(i)
224 227 elif i is not None:
225 228 for j in _flatten(i):
226 229 yield j
227 230
228 231 def parsestring(s, quoted=True):
229 232 '''parse a string using simple c-like syntax.
230 233 string must be in quotes if quoted is True.'''
231 234 if quoted:
232 235 if len(s) < 2 or s[0] != s[-1]:
233 236 raise SyntaxError(_('unmatched quotes'))
234 237 return s[1:-1].decode('string_escape')
235 238
236 239 return s.decode('string_escape')
237 240
238 241 class engine(object):
239 242 '''template expansion engine.
240 243
241 244 template expansion works like this. a map file contains key=value
242 245 pairs. if value is quoted, it is treated as string. otherwise, it
243 246 is treated as name of template file.
244 247
245 248 templater is asked to expand a key in map. it looks up key, and
246 249 looks for strings like this: {foo}. it expands {foo} by looking up
247 250 foo in map, and substituting it. expansion is recursive: it stops
248 251 when there is no more {foo} to replace.
249 252
250 253 expansion also allows formatting and filtering.
251 254
252 255 format uses key to expand each item in list. syntax is
253 256 {key%format}.
254 257
255 258 filter uses function to transform value. syntax is
256 259 {key|filter1|filter2|...}.'''
257 260
258 261 def __init__(self, loader, filters={}, defaults={}):
259 262 self._loader = loader
260 263 self._filters = filters
261 264 self._defaults = defaults
262 265 self._cache = {}
263 266
264 267 def _load(self, t):
265 268 '''load, parse, and cache a template'''
266 269 if t not in self._cache:
267 270 self._cache[t] = compiletemplate(self._loader(t), self)
268 271 return self._cache[t]
269 272
270 273 def process(self, t, mapping):
271 274 '''Perform expansion. t is name of map element to expand.
272 275 mapping contains added elements for use during expansion. Is a
273 276 generator.'''
274 277 return _flatten(func(self, mapping, data) for func, data in
275 278 self._load(t))
276 279
277 280 engines = {'default': engine}
278 281
279 282 class templater(object):
280 283
281 284 def __init__(self, mapfile, filters={}, defaults={}, cache={},
282 285 minchunk=1024, maxchunk=65536):
283 286 '''set up template engine.
284 287 mapfile is name of file to read map definitions from.
285 288 filters is dict of functions. each transforms a value into another.
286 289 defaults is dict of default map definitions.'''
287 290 self.mapfile = mapfile or 'template'
288 291 self.cache = cache.copy()
289 292 self.map = {}
290 293 self.base = (mapfile and os.path.dirname(mapfile)) or ''
291 294 self.filters = templatefilters.filters.copy()
292 295 self.filters.update(filters)
293 296 self.defaults = defaults
294 297 self.minchunk, self.maxchunk = minchunk, maxchunk
295 298 self.ecache = {}
296 299
297 300 if not mapfile:
298 301 return
299 302 if not os.path.exists(mapfile):
300 303 raise util.Abort(_('style not found: %s') % mapfile)
301 304
302 305 conf = config.config()
303 306 conf.read(mapfile)
304 307
305 308 for key, val in conf[''].items():
306 309 if not val:
307 310 raise SyntaxError(_('%s: missing value') % conf.source('', key))
308 311 if val[0] in "'\"":
309 312 try:
310 313 self.cache[key] = parsestring(val)
311 314 except SyntaxError, inst:
312 315 raise SyntaxError('%s: %s' %
313 316 (conf.source('', key), inst.args[0]))
314 317 else:
315 318 val = 'default', val
316 319 if ':' in val[1]:
317 320 val = val[1].split(':', 1)
318 321 self.map[key] = val[0], os.path.join(self.base, val[1])
319 322
320 323 def __contains__(self, key):
321 324 return key in self.cache or key in self.map
322 325
323 326 def load(self, t):
324 327 '''Get the template for the given template name. Use a local cache.'''
325 328 if t not in self.cache:
326 329 try:
327 330 self.cache[t] = util.readfile(self.map[t][1])
328 331 except KeyError, inst:
329 332 raise util.Abort(_('"%s" not in template map') % inst.args[0])
330 333 except IOError, inst:
331 334 raise IOError(inst.args[0], _('template file %s: %s') %
332 335 (self.map[t][1], inst.args[1]))
333 336 return self.cache[t]
334 337
335 338 def __call__(self, t, **mapping):
336 339 ttype = t in self.map and self.map[t][0] or 'default'
337 340 if ttype not in self.ecache:
338 341 self.ecache[ttype] = engines[ttype](self.load,
339 342 self.filters, self.defaults)
340 343 proc = self.ecache[ttype]
341 344
342 345 stream = proc.process(t, mapping)
343 346 if self.minchunk:
344 347 stream = util.increasingchunks(stream, min=self.minchunk,
345 348 max=self.maxchunk)
346 349 return stream
347 350
348 351 def templatepath(name=None):
349 352 '''return location of template file or directory (if no name).
350 353 returns None if not found.'''
351 354 normpaths = []
352 355
353 356 # executable version (py2exe) doesn't support __file__
354 357 if util.mainfrozen():
355 358 module = sys.executable
356 359 else:
357 360 module = __file__
358 361 for f in path:
359 362 if f.startswith('/'):
360 363 p = f
361 364 else:
362 365 fl = f.split('/')
363 366 p = os.path.join(os.path.dirname(module), *fl)
364 367 if name:
365 368 p = os.path.join(p, name)
366 369 if name and os.path.exists(p):
367 370 return os.path.normpath(p)
368 371 elif os.path.isdir(p):
369 372 normpaths.append(os.path.normpath(p))
370 373
371 374 return normpaths
372 375
373 376 def stylemap(styles, paths=None):
374 377 """Return path to mapfile for a given style.
375 378
376 379 Searches mapfile in the following locations:
377 380 1. templatepath/style/map
378 381 2. templatepath/map-style
379 382 3. templatepath/map
380 383 """
381 384
382 385 if paths is None:
383 386 paths = templatepath()
384 387 elif isinstance(paths, str):
385 388 paths = [paths]
386 389
387 390 if isinstance(styles, str):
388 391 styles = [styles]
389 392
390 393 for style in styles:
391 394 if not style:
392 395 continue
393 396 locations = [os.path.join(style, 'map'), 'map-' + style]
394 397 locations.append('map')
395 398
396 399 for path in paths:
397 400 for location in locations:
398 401 mapfile = os.path.join(path, location)
399 402 if os.path.isfile(mapfile):
400 403 return style, mapfile
401 404
402 405 raise RuntimeError("No hgweb templates found in %r" % paths)
@@ -1,1522 +1,1527
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 \
596 596 > p1rev p2rev p1node p2node; do
597 597 > for mode in '' --verbose --debug; do
598 598 > hg log $mode --template "$key$mode: {$key}\n"
599 599 > done
600 600 > done
601 601 author: test
602 602 author: User Name <user@hostname>
603 603 author: person
604 604 author: person
605 605 author: person
606 606 author: person
607 607 author: other@place
608 608 author: A. N. Other <other@place>
609 609 author: User Name <user@hostname>
610 610 author--verbose: test
611 611 author--verbose: User Name <user@hostname>
612 612 author--verbose: person
613 613 author--verbose: person
614 614 author--verbose: person
615 615 author--verbose: person
616 616 author--verbose: other@place
617 617 author--verbose: A. N. Other <other@place>
618 618 author--verbose: User Name <user@hostname>
619 619 author--debug: test
620 620 author--debug: User Name <user@hostname>
621 621 author--debug: person
622 622 author--debug: person
623 623 author--debug: person
624 624 author--debug: person
625 625 author--debug: other@place
626 626 author--debug: A. N. Other <other@place>
627 627 author--debug: User Name <user@hostname>
628 628 branch: default
629 629 branch: default
630 630 branch: default
631 631 branch: default
632 632 branch: foo
633 633 branch: default
634 634 branch: default
635 635 branch: default
636 636 branch: default
637 637 branch--verbose: default
638 638 branch--verbose: default
639 639 branch--verbose: default
640 640 branch--verbose: default
641 641 branch--verbose: foo
642 642 branch--verbose: default
643 643 branch--verbose: default
644 644 branch--verbose: default
645 645 branch--verbose: default
646 646 branch--debug: default
647 647 branch--debug: default
648 648 branch--debug: default
649 649 branch--debug: default
650 650 branch--debug: foo
651 651 branch--debug: default
652 652 branch--debug: default
653 653 branch--debug: default
654 654 branch--debug: default
655 655 branches:
656 656 branches:
657 657 branches:
658 658 branches:
659 659 branches: foo
660 660 branches:
661 661 branches:
662 662 branches:
663 663 branches:
664 664 branches--verbose:
665 665 branches--verbose:
666 666 branches--verbose:
667 667 branches--verbose:
668 668 branches--verbose: foo
669 669 branches--verbose:
670 670 branches--verbose:
671 671 branches--verbose:
672 672 branches--verbose:
673 673 branches--debug:
674 674 branches--debug:
675 675 branches--debug:
676 676 branches--debug:
677 677 branches--debug: foo
678 678 branches--debug:
679 679 branches--debug:
680 680 branches--debug:
681 681 branches--debug:
682 682 date: 1577872860.00
683 683 date: 1000000.00
684 684 date: 1500001.00
685 685 date: 1500000.00
686 686 date: 1400000.00
687 687 date: 1300000.00
688 688 date: 1200000.00
689 689 date: 1100000.00
690 690 date: 1000000.00
691 691 date--verbose: 1577872860.00
692 692 date--verbose: 1000000.00
693 693 date--verbose: 1500001.00
694 694 date--verbose: 1500000.00
695 695 date--verbose: 1400000.00
696 696 date--verbose: 1300000.00
697 697 date--verbose: 1200000.00
698 698 date--verbose: 1100000.00
699 699 date--verbose: 1000000.00
700 700 date--debug: 1577872860.00
701 701 date--debug: 1000000.00
702 702 date--debug: 1500001.00
703 703 date--debug: 1500000.00
704 704 date--debug: 1400000.00
705 705 date--debug: 1300000.00
706 706 date--debug: 1200000.00
707 707 date--debug: 1100000.00
708 708 date--debug: 1000000.00
709 709 desc: third
710 710 desc: second
711 711 desc: merge
712 712 desc: new head
713 713 desc: new branch
714 714 desc: no user, no domain
715 715 desc: no person
716 716 desc: other 1
717 717 other 2
718 718
719 719 other 3
720 720 desc: line 1
721 721 line 2
722 722 desc--verbose: third
723 723 desc--verbose: second
724 724 desc--verbose: merge
725 725 desc--verbose: new head
726 726 desc--verbose: new branch
727 727 desc--verbose: no user, no domain
728 728 desc--verbose: no person
729 729 desc--verbose: other 1
730 730 other 2
731 731
732 732 other 3
733 733 desc--verbose: line 1
734 734 line 2
735 735 desc--debug: third
736 736 desc--debug: second
737 737 desc--debug: merge
738 738 desc--debug: new head
739 739 desc--debug: new branch
740 740 desc--debug: no user, no domain
741 741 desc--debug: no person
742 742 desc--debug: other 1
743 743 other 2
744 744
745 745 other 3
746 746 desc--debug: line 1
747 747 line 2
748 748 file_adds: fourth third
749 749 file_adds: second
750 750 file_adds:
751 751 file_adds: d
752 752 file_adds:
753 753 file_adds:
754 754 file_adds: c
755 755 file_adds: b
756 756 file_adds: a
757 757 file_adds--verbose: fourth third
758 758 file_adds--verbose: second
759 759 file_adds--verbose:
760 760 file_adds--verbose: d
761 761 file_adds--verbose:
762 762 file_adds--verbose:
763 763 file_adds--verbose: c
764 764 file_adds--verbose: b
765 765 file_adds--verbose: a
766 766 file_adds--debug: fourth third
767 767 file_adds--debug: second
768 768 file_adds--debug:
769 769 file_adds--debug: d
770 770 file_adds--debug:
771 771 file_adds--debug:
772 772 file_adds--debug: c
773 773 file_adds--debug: b
774 774 file_adds--debug: a
775 775 file_dels: second
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:
784 784 file_dels--verbose: second
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--verbose:
793 793 file_dels--debug: second
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_dels--debug:
802 802 file_mods:
803 803 file_mods:
804 804 file_mods:
805 805 file_mods:
806 806 file_mods:
807 807 file_mods: c
808 808 file_mods:
809 809 file_mods:
810 810 file_mods:
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:
816 816 file_mods--verbose: c
817 817 file_mods--verbose:
818 818 file_mods--verbose:
819 819 file_mods--verbose:
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:
825 825 file_mods--debug: c
826 826 file_mods--debug:
827 827 file_mods--debug:
828 828 file_mods--debug:
829 829 file_copies: fourth (second)
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:
838 838 file_copies--verbose: fourth (second)
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--verbose:
847 847 file_copies--debug: fourth (second)
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--debug:
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:
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--verbose:
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 file_copies_switch--debug:
883 883 files: fourth second third
884 884 files: second
885 885 files:
886 886 files: d
887 887 files:
888 888 files: c
889 889 files: c
890 890 files: b
891 891 files: a
892 892 files--verbose: fourth second third
893 893 files--verbose: second
894 894 files--verbose:
895 895 files--verbose: d
896 896 files--verbose:
897 897 files--verbose: c
898 898 files--verbose: c
899 899 files--verbose: b
900 900 files--verbose: a
901 901 files--debug: fourth second third
902 902 files--debug: second
903 903 files--debug:
904 904 files--debug: d
905 905 files--debug:
906 906 files--debug: c
907 907 files--debug: c
908 908 files--debug: b
909 909 files--debug: a
910 910 manifest: 6:94961b75a2da
911 911 manifest: 5:f2dbc354b94e
912 912 manifest: 4:4dc3def4f9b4
913 913 manifest: 4:4dc3def4f9b4
914 914 manifest: 3:cb5a1327723b
915 915 manifest: 3:cb5a1327723b
916 916 manifest: 2:6e0e82995c35
917 917 manifest: 1:4e8d705b1e53
918 918 manifest: 0:a0c8bcbbb45c
919 919 manifest--verbose: 6:94961b75a2da
920 920 manifest--verbose: 5:f2dbc354b94e
921 921 manifest--verbose: 4:4dc3def4f9b4
922 922 manifest--verbose: 4:4dc3def4f9b4
923 923 manifest--verbose: 3:cb5a1327723b
924 924 manifest--verbose: 3:cb5a1327723b
925 925 manifest--verbose: 2:6e0e82995c35
926 926 manifest--verbose: 1:4e8d705b1e53
927 927 manifest--verbose: 0:a0c8bcbbb45c
928 928 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
929 929 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
930 930 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
931 931 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
932 932 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
933 933 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
934 934 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
935 935 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
936 936 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
937 937 node: 95c24699272ef57d062b8bccc32c878bf841784a
938 938 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
939 939 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
940 940 node: 13207e5a10d9fd28ec424934298e176197f2c67f
941 941 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
942 942 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
943 943 node: 97054abb4ab824450e9164180baf491ae0078465
944 944 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
945 945 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
946 946 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
947 947 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
948 948 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
949 949 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
950 950 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
951 951 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
952 952 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
953 953 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
954 954 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
955 955 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
956 956 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
957 957 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
958 958 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
959 959 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
960 960 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
961 961 node--debug: 97054abb4ab824450e9164180baf491ae0078465
962 962 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
963 963 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
964 964 parents:
965 965 parents: -1:000000000000
966 966 parents: 5:13207e5a10d9 4:bbe44766e73d
967 967 parents: 3:10e46f2dcbf4
968 968 parents:
969 969 parents:
970 970 parents:
971 971 parents:
972 972 parents:
973 973 parents--verbose:
974 974 parents--verbose: -1:000000000000
975 975 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
976 976 parents--verbose: 3:10e46f2dcbf4
977 977 parents--verbose:
978 978 parents--verbose:
979 979 parents--verbose:
980 980 parents--verbose:
981 981 parents--verbose:
982 982 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
983 983 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
984 984 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
985 985 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
986 986 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
987 987 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
988 988 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
989 989 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
990 990 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
991 991 rev: 8
992 992 rev: 7
993 993 rev: 6
994 994 rev: 5
995 995 rev: 4
996 996 rev: 3
997 997 rev: 2
998 998 rev: 1
999 999 rev: 0
1000 1000 rev--verbose: 8
1001 1001 rev--verbose: 7
1002 1002 rev--verbose: 6
1003 1003 rev--verbose: 5
1004 1004 rev--verbose: 4
1005 1005 rev--verbose: 3
1006 1006 rev--verbose: 2
1007 1007 rev--verbose: 1
1008 1008 rev--verbose: 0
1009 1009 rev--debug: 8
1010 1010 rev--debug: 7
1011 1011 rev--debug: 6
1012 1012 rev--debug: 5
1013 1013 rev--debug: 4
1014 1014 rev--debug: 3
1015 1015 rev--debug: 2
1016 1016 rev--debug: 1
1017 1017 rev--debug: 0
1018 1018 tags: tip
1019 1019 tags:
1020 1020 tags:
1021 1021 tags:
1022 1022 tags:
1023 1023 tags:
1024 1024 tags:
1025 1025 tags:
1026 1026 tags:
1027 1027 tags--verbose: tip
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--verbose:
1036 1036 tags--debug: tip
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 tags--debug:
1045 1045 diffstat: 3: +2/-1
1046 1046 diffstat: 1: +1/-0
1047 1047 diffstat: 0: +0/-0
1048 1048 diffstat: 1: +1/-0
1049 1049 diffstat: 0: +0/-0
1050 1050 diffstat: 1: +1/-0
1051 1051 diffstat: 1: +4/-0
1052 1052 diffstat: 1: +2/-0
1053 1053 diffstat: 1: +1/-0
1054 1054 diffstat--verbose: 3: +2/-1
1055 1055 diffstat--verbose: 1: +1/-0
1056 1056 diffstat--verbose: 0: +0/-0
1057 1057 diffstat--verbose: 1: +1/-0
1058 1058 diffstat--verbose: 0: +0/-0
1059 1059 diffstat--verbose: 1: +1/-0
1060 1060 diffstat--verbose: 1: +4/-0
1061 1061 diffstat--verbose: 1: +2/-0
1062 1062 diffstat--verbose: 1: +1/-0
1063 1063 diffstat--debug: 3: +2/-1
1064 1064 diffstat--debug: 1: +1/-0
1065 1065 diffstat--debug: 0: +0/-0
1066 1066 diffstat--debug: 1: +1/-0
1067 1067 diffstat--debug: 0: +0/-0
1068 1068 diffstat--debug: 1: +1/-0
1069 1069 diffstat--debug: 1: +4/-0
1070 1070 diffstat--debug: 1: +2/-0
1071 1071 diffstat--debug: 1: +1/-0
1072 1072 extras: branch=default
1073 1073 extras: branch=default
1074 1074 extras: branch=default
1075 1075 extras: branch=default
1076 1076 extras: branch=foo
1077 1077 extras: branch=default
1078 1078 extras: branch=default
1079 1079 extras: branch=default
1080 1080 extras: 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=default
1085 1085 extras--verbose: branch=foo
1086 1086 extras--verbose: branch=default
1087 1087 extras--verbose: branch=default
1088 1088 extras--verbose: branch=default
1089 1089 extras--verbose: 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=default
1094 1094 extras--debug: branch=foo
1095 1095 extras--debug: branch=default
1096 1096 extras--debug: branch=default
1097 1097 extras--debug: branch=default
1098 1098 extras--debug: branch=default
1099 1099 p1rev: 7
1100 1100 p1rev: -1
1101 1101 p1rev: 5
1102 1102 p1rev: 3
1103 1103 p1rev: 3
1104 1104 p1rev: 2
1105 1105 p1rev: 1
1106 1106 p1rev: 0
1107 1107 p1rev: -1
1108 1108 p1rev--verbose: 7
1109 1109 p1rev--verbose: -1
1110 1110 p1rev--verbose: 5
1111 1111 p1rev--verbose: 3
1112 1112 p1rev--verbose: 3
1113 1113 p1rev--verbose: 2
1114 1114 p1rev--verbose: 1
1115 1115 p1rev--verbose: 0
1116 1116 p1rev--verbose: -1
1117 1117 p1rev--debug: 7
1118 1118 p1rev--debug: -1
1119 1119 p1rev--debug: 5
1120 1120 p1rev--debug: 3
1121 1121 p1rev--debug: 3
1122 1122 p1rev--debug: 2
1123 1123 p1rev--debug: 1
1124 1124 p1rev--debug: 0
1125 1125 p1rev--debug: -1
1126 1126 p2rev: -1
1127 1127 p2rev: -1
1128 1128 p2rev: 4
1129 1129 p2rev: -1
1130 1130 p2rev: -1
1131 1131 p2rev: -1
1132 1132 p2rev: -1
1133 1133 p2rev: -1
1134 1134 p2rev: -1
1135 1135 p2rev--verbose: -1
1136 1136 p2rev--verbose: -1
1137 1137 p2rev--verbose: 4
1138 1138 p2rev--verbose: -1
1139 1139 p2rev--verbose: -1
1140 1140 p2rev--verbose: -1
1141 1141 p2rev--verbose: -1
1142 1142 p2rev--verbose: -1
1143 1143 p2rev--verbose: -1
1144 1144 p2rev--debug: -1
1145 1145 p2rev--debug: -1
1146 1146 p2rev--debug: 4
1147 1147 p2rev--debug: -1
1148 1148 p2rev--debug: -1
1149 1149 p2rev--debug: -1
1150 1150 p2rev--debug: -1
1151 1151 p2rev--debug: -1
1152 1152 p2rev--debug: -1
1153 1153 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1154 1154 p1node: 0000000000000000000000000000000000000000
1155 1155 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1156 1156 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1157 1157 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1158 1158 p1node: 97054abb4ab824450e9164180baf491ae0078465
1159 1159 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1160 1160 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1161 1161 p1node: 0000000000000000000000000000000000000000
1162 1162 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1163 1163 p1node--verbose: 0000000000000000000000000000000000000000
1164 1164 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1165 1165 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1166 1166 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1167 1167 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1168 1168 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1169 1169 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1170 1170 p1node--verbose: 0000000000000000000000000000000000000000
1171 1171 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1172 1172 p1node--debug: 0000000000000000000000000000000000000000
1173 1173 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1174 1174 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1175 1175 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1176 1176 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1177 1177 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1178 1178 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1179 1179 p1node--debug: 0000000000000000000000000000000000000000
1180 1180 p2node: 0000000000000000000000000000000000000000
1181 1181 p2node: 0000000000000000000000000000000000000000
1182 1182 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1183 1183 p2node: 0000000000000000000000000000000000000000
1184 1184 p2node: 0000000000000000000000000000000000000000
1185 1185 p2node: 0000000000000000000000000000000000000000
1186 1186 p2node: 0000000000000000000000000000000000000000
1187 1187 p2node: 0000000000000000000000000000000000000000
1188 1188 p2node: 0000000000000000000000000000000000000000
1189 1189 p2node--verbose: 0000000000000000000000000000000000000000
1190 1190 p2node--verbose: 0000000000000000000000000000000000000000
1191 1191 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1192 1192 p2node--verbose: 0000000000000000000000000000000000000000
1193 1193 p2node--verbose: 0000000000000000000000000000000000000000
1194 1194 p2node--verbose: 0000000000000000000000000000000000000000
1195 1195 p2node--verbose: 0000000000000000000000000000000000000000
1196 1196 p2node--verbose: 0000000000000000000000000000000000000000
1197 1197 p2node--verbose: 0000000000000000000000000000000000000000
1198 1198 p2node--debug: 0000000000000000000000000000000000000000
1199 1199 p2node--debug: 0000000000000000000000000000000000000000
1200 1200 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1201 1201 p2node--debug: 0000000000000000000000000000000000000000
1202 1202 p2node--debug: 0000000000000000000000000000000000000000
1203 1203 p2node--debug: 0000000000000000000000000000000000000000
1204 1204 p2node--debug: 0000000000000000000000000000000000000000
1205 1205 p2node--debug: 0000000000000000000000000000000000000000
1206 1206 p2node--debug: 0000000000000000000000000000000000000000
1207 1207
1208 1208 Filters work:
1209 1209
1210 1210 $ hg log --template '{author|domain}\n'
1211 1211
1212 1212 hostname
1213 1213
1214 1214
1215 1215
1216 1216
1217 1217 place
1218 1218 place
1219 1219 hostname
1220 1220
1221 1221 $ hg log --template '{author|person}\n'
1222 1222 test
1223 1223 User Name
1224 1224 person
1225 1225 person
1226 1226 person
1227 1227 person
1228 1228 other
1229 1229 A. N. Other
1230 1230 User Name
1231 1231
1232 1232 $ hg log --template '{author|user}\n'
1233 1233 test
1234 1234 user
1235 1235 person
1236 1236 person
1237 1237 person
1238 1238 person
1239 1239 other
1240 1240 other
1241 1241 user
1242 1242
1243 1243 $ hg log --template '{date|date}\n'
1244 1244 Wed Jan 01 10:01:00 2020 +0000
1245 1245 Mon Jan 12 13:46:40 1970 +0000
1246 1246 Sun Jan 18 08:40:01 1970 +0000
1247 1247 Sun Jan 18 08:40:00 1970 +0000
1248 1248 Sat Jan 17 04:53:20 1970 +0000
1249 1249 Fri Jan 16 01:06:40 1970 +0000
1250 1250 Wed Jan 14 21:20:00 1970 +0000
1251 1251 Tue Jan 13 17:33:20 1970 +0000
1252 1252 Mon Jan 12 13:46:40 1970 +0000
1253 1253
1254 1254 $ hg log --template '{date|isodate}\n'
1255 1255 2020-01-01 10:01 +0000
1256 1256 1970-01-12 13:46 +0000
1257 1257 1970-01-18 08:40 +0000
1258 1258 1970-01-18 08:40 +0000
1259 1259 1970-01-17 04:53 +0000
1260 1260 1970-01-16 01:06 +0000
1261 1261 1970-01-14 21:20 +0000
1262 1262 1970-01-13 17:33 +0000
1263 1263 1970-01-12 13:46 +0000
1264 1264
1265 1265 $ hg log --template '{date|isodatesec}\n'
1266 1266 2020-01-01 10:01:00 +0000
1267 1267 1970-01-12 13:46:40 +0000
1268 1268 1970-01-18 08:40:01 +0000
1269 1269 1970-01-18 08:40:00 +0000
1270 1270 1970-01-17 04:53:20 +0000
1271 1271 1970-01-16 01:06:40 +0000
1272 1272 1970-01-14 21:20:00 +0000
1273 1273 1970-01-13 17:33:20 +0000
1274 1274 1970-01-12 13:46:40 +0000
1275 1275
1276 1276 $ hg log --template '{date|rfc822date}\n'
1277 1277 Wed, 01 Jan 2020 10:01:00 +0000
1278 1278 Mon, 12 Jan 1970 13:46:40 +0000
1279 1279 Sun, 18 Jan 1970 08:40:01 +0000
1280 1280 Sun, 18 Jan 1970 08:40:00 +0000
1281 1281 Sat, 17 Jan 1970 04:53:20 +0000
1282 1282 Fri, 16 Jan 1970 01:06:40 +0000
1283 1283 Wed, 14 Jan 1970 21:20:00 +0000
1284 1284 Tue, 13 Jan 1970 17:33:20 +0000
1285 1285 Mon, 12 Jan 1970 13:46:40 +0000
1286 1286
1287 1287 $ hg log --template '{desc|firstline}\n'
1288 1288 third
1289 1289 second
1290 1290 merge
1291 1291 new head
1292 1292 new branch
1293 1293 no user, no domain
1294 1294 no person
1295 1295 other 1
1296 1296 line 1
1297 1297
1298 1298 $ hg log --template '{node|short}\n'
1299 1299 95c24699272e
1300 1300 29114dbae42b
1301 1301 d41e714fe50d
1302 1302 13207e5a10d9
1303 1303 bbe44766e73d
1304 1304 10e46f2dcbf4
1305 1305 97054abb4ab8
1306 1306 b608e9d1a3f0
1307 1307 1e4e1b8f71e0
1308 1308
1309 1309 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1310 1310 <changeset author="test"/>
1311 1311 <changeset author="User Name &lt;user@hostname&gt;"/>
1312 1312 <changeset author="person"/>
1313 1313 <changeset author="person"/>
1314 1314 <changeset author="person"/>
1315 1315 <changeset author="person"/>
1316 1316 <changeset author="other@place"/>
1317 1317 <changeset author="A. N. Other &lt;other@place&gt;"/>
1318 1318 <changeset author="User Name &lt;user@hostname&gt;"/>
1319 1319
1320 1320 $ hg log --template '{rev}: {children}\n'
1321 1321 8:
1322 1322 7: 8:95c24699272e
1323 1323 6:
1324 1324 5: 6:d41e714fe50d
1325 1325 4: 6:d41e714fe50d
1326 1326 3: 4:bbe44766e73d 5:13207e5a10d9
1327 1327 2: 3:10e46f2dcbf4
1328 1328 1: 2:97054abb4ab8
1329 1329 0: 1:b608e9d1a3f0
1330 1330
1331 1331 Formatnode filter works:
1332 1332
1333 1333 $ hg -q log -r 0 --template '{node|formatnode}\n'
1334 1334 1e4e1b8f71e0
1335 1335
1336 1336 $ hg log -r 0 --template '{node|formatnode}\n'
1337 1337 1e4e1b8f71e0
1338 1338
1339 1339 $ hg -v log -r 0 --template '{node|formatnode}\n'
1340 1340 1e4e1b8f71e0
1341 1341
1342 1342 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1343 1343 1e4e1b8f71e05681d422154f5421e385fec3454f
1344 1344
1345 1345 Age filter:
1346 1346
1347 1347 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1348 1348
1349 1349 >>> from datetime import datetime
1350 1350 >>> fp = open('a', 'w')
1351 1351 >>> fp.write(str(datetime.now().year + 8) + '-01-01 00:00')
1352 1352 >>> fp.close()
1353 1353 $ hg add a
1354 1354 $ hg commit -m future -d "`cat a`"
1355 1355
1356 1356 $ hg log -l1 --template '{date|age}\n'
1357 1357 7 years from now
1358 1358
1359 1359 Error on syntax:
1360 1360
1361 1361 $ echo 'x = "f' >> t
1362 1362 $ hg log
1363 1363 abort: t:3: unmatched quotes
1364 1364 [255]
1365 1365
1366 1366 Behind the scenes, this will throw TypeError
1367 1367
1368 1368 $ hg log -l 3 --template '{date|obfuscate}\n'
1369 1369 abort: template filter 'obfuscate' is not compatible with keyword 'date'
1370 1370 [255]
1371 1371
1372 1372 Behind the scenes, this will throw a ValueError
1373 1373
1374 1374 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
1375 1375 abort: template filter 'shortdate' is not compatible with keyword 'desc'
1376 1376 [255]
1377 1377
1378 1378 Behind the scenes, this will throw AttributeError
1379 1379
1380 1380 $ hg log -l 3 --template 'line: {date|escape}\n'
1381 1381 abort: template filter 'escape' is not compatible with keyword 'date'
1382 1382 [255]
1383 1383
1384 1384 Behind the scenes, this will throw ValueError
1385 1385
1386 1386 $ hg tip --template '{author|email|date}\n'
1387 1387 abort: template filter 'datefilter' is not compatible with keyword 'author'
1388 1388 [255]
1389 1389
1390 1390 $ cd ..
1391 1391
1392 1392
1393 1393 latesttag:
1394 1394
1395 1395 $ hg init latesttag
1396 1396 $ cd latesttag
1397 1397
1398 1398 $ echo a > file
1399 1399 $ hg ci -Am a -d '0 0'
1400 1400 adding file
1401 1401
1402 1402 $ echo b >> file
1403 1403 $ hg ci -m b -d '1 0'
1404 1404
1405 1405 $ echo c >> head1
1406 1406 $ hg ci -Am h1c -d '2 0'
1407 1407 adding head1
1408 1408
1409 1409 $ hg update -q 1
1410 1410 $ echo d >> head2
1411 1411 $ hg ci -Am h2d -d '3 0'
1412 1412 adding head2
1413 1413 created new head
1414 1414
1415 1415 $ echo e >> head2
1416 1416 $ hg ci -m h2e -d '4 0'
1417 1417
1418 1418 $ hg merge -q
1419 1419 $ hg ci -m merge -d '5 0'
1420 1420
1421 1421 No tag set:
1422 1422
1423 1423 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1424 1424 5: null+5
1425 1425 4: null+4
1426 1426 3: null+3
1427 1427 2: null+3
1428 1428 1: null+2
1429 1429 0: null+1
1430 1430
1431 1431 One common tag: longuest path wins:
1432 1432
1433 1433 $ hg tag -r 1 -m t1 -d '6 0' t1
1434 1434 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1435 1435 6: t1+4
1436 1436 5: t1+3
1437 1437 4: t1+2
1438 1438 3: t1+1
1439 1439 2: t1+1
1440 1440 1: t1+0
1441 1441 0: null+1
1442 1442
1443 1443 One ancestor tag: more recent wins:
1444 1444
1445 1445 $ hg tag -r 2 -m t2 -d '7 0' t2
1446 1446 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1447 1447 7: t2+3
1448 1448 6: t2+2
1449 1449 5: t2+1
1450 1450 4: t1+2
1451 1451 3: t1+1
1452 1452 2: t2+0
1453 1453 1: t1+0
1454 1454 0: null+1
1455 1455
1456 1456 Two branch tags: more recent wins:
1457 1457
1458 1458 $ hg tag -r 3 -m t3 -d '8 0' t3
1459 1459 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1460 1460 8: t3+5
1461 1461 7: t3+4
1462 1462 6: t3+3
1463 1463 5: t3+2
1464 1464 4: t3+1
1465 1465 3: t3+0
1466 1466 2: t2+0
1467 1467 1: t1+0
1468 1468 0: null+1
1469 1469
1470 1470 Merged tag overrides:
1471 1471
1472 1472 $ hg tag -r 5 -m t5 -d '9 0' t5
1473 1473 $ hg tag -r 3 -m at3 -d '10 0' at3
1474 1474 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1475 1475 10: t5+5
1476 1476 9: t5+4
1477 1477 8: t5+3
1478 1478 7: t5+2
1479 1479 6: t5+1
1480 1480 5: t5+0
1481 1481 4: at3:t3+1
1482 1482 3: at3:t3+0
1483 1483 2: t2+0
1484 1484 1: t1+0
1485 1485 0: null+1
1486 1486
1487 1487 $ cd ..
1488 1488
1489 1489
1490 1490 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1491 1491 if it is a relative path
1492 1492
1493 1493 $ mkdir -p home/styles
1494 1494
1495 1495 $ cat > home/styles/teststyle <<EOF
1496 1496 > changeset = 'test {rev}:{node|short}\n'
1497 1497 > EOF
1498 1498
1499 1499 $ HOME=`pwd`/home; export HOME
1500 1500
1501 1501 $ cat > latesttag/.hg/hgrc <<EOF
1502 1502 > [ui]
1503 1503 > style = ~/styles/teststyle
1504 1504 > EOF
1505 1505
1506 1506 $ hg -R latesttag tip
1507 1507 test 10:dee8f28249af
1508 1508
1509 1509 Test recursive showlist template (issue1989):
1510 1510
1511 1511 $ cat > style1989 <<EOF
1512 1512 > changeset = '{file_mods}{manifest}{extras}'
1513 1513 > file_mod = 'M|{author|person}\n'
1514 1514 > manifest = '{rev},{author}\n'
1515 1515 > extra = '{key}: {author}\n'
1516 1516 > EOF
1517 1517
1518 1518 $ hg -R latesttag log -r tip --style=style1989
1519 1519 M|test
1520 1520 10,test
1521 1521 branch: test
1522 1522
1523 Test new-style inline templating:
1524
1525 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
1526 modified files: .hgtags
1527
General Comments 0
You need to be logged in to leave comments. Login now