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