##// END OF EJS Templates
templatekw: add option to include ignored/clean/unknown files in cache...
Yuya Nishihara -
r39635:a5da9063 default
parent child Browse files
Show More
@@ -1,831 +1,834 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 __future__ import absolute_import
9 9
10 10 from .i18n import _
11 11 from .node import (
12 12 hex,
13 13 nullid,
14 14 )
15 15
16 16 from . import (
17 17 diffutil,
18 18 encoding,
19 19 error,
20 20 hbisect,
21 21 i18n,
22 22 obsutil,
23 23 patch,
24 24 pycompat,
25 25 registrar,
26 26 scmutil,
27 27 templateutil,
28 28 util,
29 29 )
30 30 from .utils import (
31 31 stringutil,
32 32 )
33 33
34 34 _hybrid = templateutil.hybrid
35 35 hybriddict = templateutil.hybriddict
36 36 hybridlist = templateutil.hybridlist
37 37 compatdict = templateutil.compatdict
38 38 compatlist = templateutil.compatlist
39 39 _showcompatlist = templateutil._showcompatlist
40 40
41 41 def getlatesttags(context, mapping, pattern=None):
42 42 '''return date, distance and name for the latest tag of rev'''
43 43 repo = context.resource(mapping, 'repo')
44 44 ctx = context.resource(mapping, 'ctx')
45 45 cache = context.resource(mapping, 'cache')
46 46
47 47 cachename = 'latesttags'
48 48 if pattern is not None:
49 49 cachename += '-' + pattern
50 50 match = stringutil.stringmatcher(pattern)[2]
51 51 else:
52 52 match = util.always
53 53
54 54 if cachename not in cache:
55 55 # Cache mapping from rev to a tuple with tag date, tag
56 56 # distance and tag name
57 57 cache[cachename] = {-1: (0, 0, ['null'])}
58 58 latesttags = cache[cachename]
59 59
60 60 rev = ctx.rev()
61 61 todo = [rev]
62 62 while todo:
63 63 rev = todo.pop()
64 64 if rev in latesttags:
65 65 continue
66 66 ctx = repo[rev]
67 67 tags = [t for t in ctx.tags()
68 68 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
69 69 and match(t))]
70 70 if tags:
71 71 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
72 72 continue
73 73 try:
74 74 ptags = [latesttags[p.rev()] for p in ctx.parents()]
75 75 if len(ptags) > 1:
76 76 if ptags[0][2] == ptags[1][2]:
77 77 # The tuples are laid out so the right one can be found by
78 78 # comparison in this case.
79 79 pdate, pdist, ptag = max(ptags)
80 80 else:
81 81 def key(x):
82 82 changessincetag = len(repo.revs('only(%d, %s)',
83 83 ctx.rev(), x[2][0]))
84 84 # Smallest number of changes since tag wins. Date is
85 85 # used as tiebreaker.
86 86 return [-changessincetag, x[0]]
87 87 pdate, pdist, ptag = max(ptags, key=key)
88 88 else:
89 89 pdate, pdist, ptag = ptags[0]
90 90 except KeyError:
91 91 # Cache miss - recurse
92 92 todo.append(rev)
93 93 todo.extend(p.rev() for p in ctx.parents())
94 94 continue
95 95 latesttags[rev] = pdate, pdist + 1, ptag
96 96 return latesttags[rev]
97 97
98 98 def getrenamedfn(repo, endrev=None):
99 99 rcache = {}
100 100 if endrev is None:
101 101 endrev = len(repo)
102 102
103 103 def getrenamed(fn, rev):
104 104 '''looks up all renames for a file (up to endrev) the first
105 105 time the file is given. It indexes on the changerev and only
106 106 parses the manifest if linkrev != changerev.
107 107 Returns rename info for fn at changerev rev.'''
108 108 if fn not in rcache:
109 109 rcache[fn] = {}
110 110 fl = repo.file(fn)
111 111 for i in fl:
112 112 lr = fl.linkrev(i)
113 113 renamed = fl.renamed(fl.node(i))
114 114 rcache[fn][lr] = renamed and renamed[0]
115 115 if lr >= endrev:
116 116 break
117 117 if rev in rcache[fn]:
118 118 return rcache[fn][rev]
119 119
120 120 # If linkrev != rev (i.e. rev not found in rcache) fallback to
121 121 # filectx logic.
122 122 try:
123 123 renamed = repo[rev][fn].renamed()
124 124 return renamed and renamed[0]
125 125 except error.LookupError:
126 126 return None
127 127
128 128 return getrenamed
129 129
130 130 def getlogcolumns():
131 131 """Return a dict of log column labels"""
132 132 _ = pycompat.identity # temporarily disable gettext
133 133 # i18n: column positioning for "hg log"
134 134 columns = _('bookmark: %s\n'
135 135 'branch: %s\n'
136 136 'changeset: %s\n'
137 137 'copies: %s\n'
138 138 'date: %s\n'
139 139 'extra: %s=%s\n'
140 140 'files+: %s\n'
141 141 'files-: %s\n'
142 142 'files: %s\n'
143 143 'instability: %s\n'
144 144 'manifest: %s\n'
145 145 'obsolete: %s\n'
146 146 'parent: %s\n'
147 147 'phase: %s\n'
148 148 'summary: %s\n'
149 149 'tag: %s\n'
150 150 'user: %s\n')
151 151 return dict(zip([s.split(':', 1)[0] for s in columns.splitlines()],
152 152 i18n._(columns).splitlines(True)))
153 153
154 154 # default templates internally used for rendering of lists
155 155 defaulttempl = {
156 156 'parent': '{rev}:{node|formatnode} ',
157 157 'manifest': '{rev}:{node|formatnode}',
158 158 'file_copy': '{name} ({source})',
159 159 'envvar': '{key}={value}',
160 160 'extra': '{key}={value|stringescape}'
161 161 }
162 162 # filecopy is preserved for compatibility reasons
163 163 defaulttempl['filecopy'] = defaulttempl['file_copy']
164 164
165 165 # keywords are callables (see registrar.templatekeyword for details)
166 166 keywords = {}
167 167 templatekeyword = registrar.templatekeyword(keywords)
168 168
169 169 @templatekeyword('author', requires={'ctx'})
170 170 def showauthor(context, mapping):
171 171 """Alias for ``{user}``"""
172 172 return showuser(context, mapping)
173 173
174 174 @templatekeyword('bisect', requires={'repo', 'ctx'})
175 175 def showbisect(context, mapping):
176 176 """String. The changeset bisection status."""
177 177 repo = context.resource(mapping, 'repo')
178 178 ctx = context.resource(mapping, 'ctx')
179 179 return hbisect.label(repo, ctx.node())
180 180
181 181 @templatekeyword('branch', requires={'ctx'})
182 182 def showbranch(context, mapping):
183 183 """String. The name of the branch on which the changeset was
184 184 committed.
185 185 """
186 186 ctx = context.resource(mapping, 'ctx')
187 187 return ctx.branch()
188 188
189 189 @templatekeyword('branches', requires={'ctx'})
190 190 def showbranches(context, mapping):
191 191 """List of strings. The name of the branch on which the
192 192 changeset was committed. Will be empty if the branch name was
193 193 default. (DEPRECATED)
194 194 """
195 195 ctx = context.resource(mapping, 'ctx')
196 196 branch = ctx.branch()
197 197 if branch != 'default':
198 198 return compatlist(context, mapping, 'branch', [branch],
199 199 plural='branches')
200 200 return compatlist(context, mapping, 'branch', [], plural='branches')
201 201
202 202 @templatekeyword('bookmarks', requires={'repo', 'ctx'})
203 203 def showbookmarks(context, mapping):
204 204 """List of strings. Any bookmarks associated with the
205 205 changeset. Also sets 'active', the name of the active bookmark.
206 206 """
207 207 repo = context.resource(mapping, 'repo')
208 208 ctx = context.resource(mapping, 'ctx')
209 209 bookmarks = ctx.bookmarks()
210 210 active = repo._activebookmark
211 211 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
212 212 f = _showcompatlist(context, mapping, 'bookmark', bookmarks)
213 213 return _hybrid(f, bookmarks, makemap, pycompat.identity)
214 214
215 215 @templatekeyword('children', requires={'ctx'})
216 216 def showchildren(context, mapping):
217 217 """List of strings. The children of the changeset."""
218 218 ctx = context.resource(mapping, 'ctx')
219 219 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()]
220 220 return compatlist(context, mapping, 'children', childrevs, element='child')
221 221
222 222 # Deprecated, but kept alive for help generation a purpose.
223 223 @templatekeyword('currentbookmark', requires={'repo', 'ctx'})
224 224 def showcurrentbookmark(context, mapping):
225 225 """String. The active bookmark, if it is associated with the changeset.
226 226 (DEPRECATED)"""
227 227 return showactivebookmark(context, mapping)
228 228
229 229 @templatekeyword('activebookmark', requires={'repo', 'ctx'})
230 230 def showactivebookmark(context, mapping):
231 231 """String. The active bookmark, if it is associated with the changeset."""
232 232 repo = context.resource(mapping, 'repo')
233 233 ctx = context.resource(mapping, 'ctx')
234 234 active = repo._activebookmark
235 235 if active and active in ctx.bookmarks():
236 236 return active
237 237 return ''
238 238
239 239 @templatekeyword('date', requires={'ctx'})
240 240 def showdate(context, mapping):
241 241 """Date information. The date when the changeset was committed."""
242 242 ctx = context.resource(mapping, 'ctx')
243 243 # the default string format is '<float(unixtime)><tzoffset>' because
244 244 # python-hglib splits date at decimal separator.
245 245 return templateutil.date(ctx.date(), showfmt='%d.0%d')
246 246
247 247 @templatekeyword('desc', requires={'ctx'})
248 248 def showdescription(context, mapping):
249 249 """String. The text of the changeset description."""
250 250 ctx = context.resource(mapping, 'ctx')
251 251 s = ctx.description()
252 252 if isinstance(s, encoding.localstr):
253 253 # try hard to preserve utf-8 bytes
254 254 return encoding.tolocal(encoding.fromlocal(s).strip())
255 255 elif isinstance(s, encoding.safelocalstr):
256 256 return encoding.safelocalstr(s.strip())
257 257 else:
258 258 return s.strip()
259 259
260 260 @templatekeyword('diffstat', requires={'ui', 'ctx'})
261 261 def showdiffstat(context, mapping):
262 262 """String. Statistics of changes with the following format:
263 263 "modified files: +added/-removed lines"
264 264 """
265 265 ui = context.resource(mapping, 'ui')
266 266 ctx = context.resource(mapping, 'ctx')
267 267 diffopts = diffutil.diffallopts(ui, {'noprefix': False})
268 268 diff = ctx.diff(opts=diffopts)
269 269 stats = patch.diffstatdata(util.iterlines(diff))
270 270 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
271 271 return '%d: +%d/-%d' % (len(stats), adds, removes)
272 272
273 273 @templatekeyword('envvars', requires={'ui'})
274 274 def showenvvars(context, mapping):
275 275 """A dictionary of environment variables. (EXPERIMENTAL)"""
276 276 ui = context.resource(mapping, 'ui')
277 277 env = ui.exportableenviron()
278 278 env = util.sortdict((k, env[k]) for k in sorted(env))
279 279 return compatdict(context, mapping, 'envvar', env, plural='envvars')
280 280
281 281 @templatekeyword('extras', requires={'ctx'})
282 282 def showextras(context, mapping):
283 283 """List of dicts with key, value entries of the 'extras'
284 284 field of this changeset."""
285 285 ctx = context.resource(mapping, 'ctx')
286 286 extras = ctx.extra()
287 287 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
288 288 makemap = lambda k: {'key': k, 'value': extras[k]}
289 289 c = [makemap(k) for k in extras]
290 290 f = _showcompatlist(context, mapping, 'extra', c, plural='extras')
291 291 return _hybrid(f, extras, makemap,
292 292 lambda k: '%s=%s' % (k, stringutil.escapestr(extras[k])))
293 293
294 def _getfilestatus(context, mapping):
294 def _getfilestatus(context, mapping, listall=False):
295 295 ctx = context.resource(mapping, 'ctx')
296 296 revcache = context.resource(mapping, 'revcache')
297 if 'filestatus' not in revcache:
298 revcache['filestatus'] = ctx.p1().status(ctx)
297 if 'filestatus' not in revcache or revcache['filestatusall'] < listall:
298 stat = ctx.p1().status(ctx, listignored=listall, listclean=listall,
299 listunknown=listall)
300 revcache['filestatus'] = stat
301 revcache['filestatusall'] = listall
299 302 return revcache['filestatus']
300 303
301 304 def _showfilesbystat(context, mapping, name, index):
302 305 stat = _getfilestatus(context, mapping)
303 306 files = stat[index]
304 307 return templateutil.compatfileslist(context, mapping, name, files)
305 308
306 309 @templatekeyword('file_adds', requires={'ctx', 'revcache'})
307 310 def showfileadds(context, mapping):
308 311 """List of strings. Files added by this changeset."""
309 312 return _showfilesbystat(context, mapping, 'file_add', 1)
310 313
311 314 @templatekeyword('file_copies',
312 315 requires={'repo', 'ctx', 'cache', 'revcache'})
313 316 def showfilecopies(context, mapping):
314 317 """List of strings. Files copied in this changeset with
315 318 their sources.
316 319 """
317 320 repo = context.resource(mapping, 'repo')
318 321 ctx = context.resource(mapping, 'ctx')
319 322 cache = context.resource(mapping, 'cache')
320 323 copies = context.resource(mapping, 'revcache').get('copies')
321 324 if copies is None:
322 325 if 'getrenamed' not in cache:
323 326 cache['getrenamed'] = getrenamedfn(repo)
324 327 copies = []
325 328 getrenamed = cache['getrenamed']
326 329 for fn in ctx.files():
327 330 rename = getrenamed(fn, ctx.rev())
328 331 if rename:
329 332 copies.append((fn, rename))
330 333 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
331 334 copies)
332 335
333 336 # showfilecopiesswitch() displays file copies only if copy records are
334 337 # provided before calling the templater, usually with a --copies
335 338 # command line switch.
336 339 @templatekeyword('file_copies_switch', requires={'revcache'})
337 340 def showfilecopiesswitch(context, mapping):
338 341 """List of strings. Like "file_copies" but displayed
339 342 only if the --copied switch is set.
340 343 """
341 344 copies = context.resource(mapping, 'revcache').get('copies') or []
342 345 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
343 346 copies)
344 347
345 348 @templatekeyword('file_dels', requires={'ctx', 'revcache'})
346 349 def showfiledels(context, mapping):
347 350 """List of strings. Files removed by this changeset."""
348 351 return _showfilesbystat(context, mapping, 'file_del', 2)
349 352
350 353 @templatekeyword('file_mods', requires={'ctx', 'revcache'})
351 354 def showfilemods(context, mapping):
352 355 """List of strings. Files modified by this changeset."""
353 356 return _showfilesbystat(context, mapping, 'file_mod', 0)
354 357
355 358 @templatekeyword('files', requires={'ctx'})
356 359 def showfiles(context, mapping):
357 360 """List of strings. All files modified, added, or removed by this
358 361 changeset.
359 362 """
360 363 ctx = context.resource(mapping, 'ctx')
361 364 return templateutil.compatfileslist(context, mapping, 'file', ctx.files())
362 365
363 366 @templatekeyword('graphnode', requires={'repo', 'ctx'})
364 367 def showgraphnode(context, mapping):
365 368 """String. The character representing the changeset node in an ASCII
366 369 revision graph."""
367 370 repo = context.resource(mapping, 'repo')
368 371 ctx = context.resource(mapping, 'ctx')
369 372 return getgraphnode(repo, ctx)
370 373
371 374 def getgraphnode(repo, ctx):
372 375 return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx)
373 376
374 377 def getgraphnodecurrent(repo, ctx):
375 378 wpnodes = repo.dirstate.parents()
376 379 if wpnodes[1] == nullid:
377 380 wpnodes = wpnodes[:1]
378 381 if ctx.node() in wpnodes:
379 382 return '@'
380 383 else:
381 384 return ''
382 385
383 386 def getgraphnodesymbol(ctx):
384 387 if ctx.obsolete():
385 388 return 'x'
386 389 elif ctx.isunstable():
387 390 return '*'
388 391 elif ctx.closesbranch():
389 392 return '_'
390 393 else:
391 394 return 'o'
392 395
393 396 @templatekeyword('graphwidth', requires=())
394 397 def showgraphwidth(context, mapping):
395 398 """Integer. The width of the graph drawn by 'log --graph' or zero."""
396 399 # just hosts documentation; should be overridden by template mapping
397 400 return 0
398 401
399 402 @templatekeyword('index', requires=())
400 403 def showindex(context, mapping):
401 404 """Integer. The current iteration of the loop. (0 indexed)"""
402 405 # just hosts documentation; should be overridden by template mapping
403 406 raise error.Abort(_("can't use index in this context"))
404 407
405 408 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'})
406 409 def showlatesttag(context, mapping):
407 410 """List of strings. The global tags on the most recent globally
408 411 tagged ancestor of this changeset. If no such tags exist, the list
409 412 consists of the single string "null".
410 413 """
411 414 return showlatesttags(context, mapping, None)
412 415
413 416 def showlatesttags(context, mapping, pattern):
414 417 """helper method for the latesttag keyword and function"""
415 418 latesttags = getlatesttags(context, mapping, pattern)
416 419
417 420 # latesttag[0] is an implementation detail for sorting csets on different
418 421 # branches in a stable manner- it is the date the tagged cset was created,
419 422 # not the date the tag was created. Therefore it isn't made visible here.
420 423 makemap = lambda v: {
421 424 'changes': _showchangessincetag,
422 425 'distance': latesttags[1],
423 426 'latesttag': v, # BC with {latesttag % '{latesttag}'}
424 427 'tag': v
425 428 }
426 429
427 430 tags = latesttags[2]
428 431 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':')
429 432 return _hybrid(f, tags, makemap, pycompat.identity)
430 433
431 434 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'})
432 435 def showlatesttagdistance(context, mapping):
433 436 """Integer. Longest path to the latest tag."""
434 437 return getlatesttags(context, mapping)[1]
435 438
436 439 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
437 440 def showchangessincelatesttag(context, mapping):
438 441 """Integer. All ancestors not in the latest tag."""
439 442 tag = getlatesttags(context, mapping)[2][0]
440 443 mapping = context.overlaymap(mapping, {'tag': tag})
441 444 return _showchangessincetag(context, mapping)
442 445
443 446 def _showchangessincetag(context, mapping):
444 447 repo = context.resource(mapping, 'repo')
445 448 ctx = context.resource(mapping, 'ctx')
446 449 offset = 0
447 450 revs = [ctx.rev()]
448 451 tag = context.symbol(mapping, 'tag')
449 452
450 453 # The only() revset doesn't currently support wdir()
451 454 if ctx.rev() is None:
452 455 offset = 1
453 456 revs = [p.rev() for p in ctx.parents()]
454 457
455 458 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
456 459
457 460 # teach templater latesttags.changes is switched to (context, mapping) API
458 461 _showchangessincetag._requires = {'repo', 'ctx'}
459 462
460 463 @templatekeyword('manifest', requires={'repo', 'ctx'})
461 464 def showmanifest(context, mapping):
462 465 repo = context.resource(mapping, 'repo')
463 466 ctx = context.resource(mapping, 'ctx')
464 467 mnode = ctx.manifestnode()
465 468 if mnode is None:
466 469 # just avoid crash, we might want to use the 'ff...' hash in future
467 470 return
468 471 mrev = repo.manifestlog.rev(mnode)
469 472 mhex = hex(mnode)
470 473 mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex})
471 474 f = context.process('manifest', mapping)
472 475 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
473 476 # rev and node are completely different from changeset's.
474 477 return templateutil.hybriditem(f, None, f,
475 478 lambda x: {'rev': mrev, 'node': mhex})
476 479
477 480 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'})
478 481 def showobsfate(context, mapping):
479 482 # this function returns a list containing pre-formatted obsfate strings.
480 483 #
481 484 # This function will be replaced by templates fragments when we will have
482 485 # the verbosity templatekw available.
483 486 succsandmarkers = showsuccsandmarkers(context, mapping)
484 487
485 488 ui = context.resource(mapping, 'ui')
486 489 repo = context.resource(mapping, 'repo')
487 490 values = []
488 491
489 492 for x in succsandmarkers.tovalue(context, mapping):
490 493 v = obsutil.obsfateprinter(ui, repo, x['successors'], x['markers'],
491 494 scmutil.formatchangeid)
492 495 values.append(v)
493 496
494 497 return compatlist(context, mapping, "fate", values)
495 498
496 499 def shownames(context, mapping, namespace):
497 500 """helper method to generate a template keyword for a namespace"""
498 501 repo = context.resource(mapping, 'repo')
499 502 ctx = context.resource(mapping, 'ctx')
500 503 ns = repo.names[namespace]
501 504 names = ns.names(repo, ctx.node())
502 505 return compatlist(context, mapping, ns.templatename, names,
503 506 plural=namespace)
504 507
505 508 @templatekeyword('namespaces', requires={'repo', 'ctx'})
506 509 def shownamespaces(context, mapping):
507 510 """Dict of lists. Names attached to this changeset per
508 511 namespace."""
509 512 repo = context.resource(mapping, 'repo')
510 513 ctx = context.resource(mapping, 'ctx')
511 514
512 515 namespaces = util.sortdict()
513 516 def makensmapfn(ns):
514 517 # 'name' for iterating over namespaces, templatename for local reference
515 518 return lambda v: {'name': v, ns.templatename: v}
516 519
517 520 for k, ns in repo.names.iteritems():
518 521 names = ns.names(repo, ctx.node())
519 522 f = _showcompatlist(context, mapping, 'name', names)
520 523 namespaces[k] = _hybrid(f, names, makensmapfn(ns), pycompat.identity)
521 524
522 525 f = _showcompatlist(context, mapping, 'namespace', list(namespaces))
523 526
524 527 def makemap(ns):
525 528 return {
526 529 'namespace': ns,
527 530 'names': namespaces[ns],
528 531 'builtin': repo.names[ns].builtin,
529 532 'colorname': repo.names[ns].colorname,
530 533 }
531 534
532 535 return _hybrid(f, namespaces, makemap, pycompat.identity)
533 536
534 537 @templatekeyword('node', requires={'ctx'})
535 538 def shownode(context, mapping):
536 539 """String. The changeset identification hash, as a 40 hexadecimal
537 540 digit string.
538 541 """
539 542 ctx = context.resource(mapping, 'ctx')
540 543 return ctx.hex()
541 544
542 545 @templatekeyword('obsolete', requires={'ctx'})
543 546 def showobsolete(context, mapping):
544 547 """String. Whether the changeset is obsolete. (EXPERIMENTAL)"""
545 548 ctx = context.resource(mapping, 'ctx')
546 549 if ctx.obsolete():
547 550 return 'obsolete'
548 551 return ''
549 552
550 553 @templatekeyword('path', requires={'fctx'})
551 554 def showpath(context, mapping):
552 555 """String. Repository-absolute path of the current file. (EXPERIMENTAL)"""
553 556 fctx = context.resource(mapping, 'fctx')
554 557 return fctx.path()
555 558
556 559 @templatekeyword('peerurls', requires={'repo'})
557 560 def showpeerurls(context, mapping):
558 561 """A dictionary of repository locations defined in the [paths] section
559 562 of your configuration file."""
560 563 repo = context.resource(mapping, 'repo')
561 564 # see commands.paths() for naming of dictionary keys
562 565 paths = repo.ui.paths
563 566 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
564 567 def makemap(k):
565 568 p = paths[k]
566 569 d = {'name': k, 'url': p.rawloc}
567 570 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
568 571 return d
569 572 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
570 573
571 574 @templatekeyword("predecessors", requires={'repo', 'ctx'})
572 575 def showpredecessors(context, mapping):
573 576 """Returns the list if the closest visible successors. (EXPERIMENTAL)"""
574 577 repo = context.resource(mapping, 'repo')
575 578 ctx = context.resource(mapping, 'ctx')
576 579 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
577 580 predecessors = pycompat.maplist(hex, predecessors)
578 581
579 582 return _hybrid(None, predecessors,
580 583 lambda x: {'ctx': repo[x]},
581 584 lambda x: scmutil.formatchangeid(repo[x]))
582 585
583 586 @templatekeyword('reporoot', requires={'repo'})
584 587 def showreporoot(context, mapping):
585 588 """String. The root directory of the current repository."""
586 589 repo = context.resource(mapping, 'repo')
587 590 return repo.root
588 591
589 592 @templatekeyword('size', requires={'fctx'})
590 593 def showsize(context, mapping):
591 594 """Integer. Size of the current file in bytes. (EXPERIMENTAL)"""
592 595 fctx = context.resource(mapping, 'fctx')
593 596 return fctx.size()
594 597
595 598 @templatekeyword("successorssets", requires={'repo', 'ctx'})
596 599 def showsuccessorssets(context, mapping):
597 600 """Returns a string of sets of successors for a changectx. Format used
598 601 is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and ctx2
599 602 while also diverged into ctx3. (EXPERIMENTAL)"""
600 603 repo = context.resource(mapping, 'repo')
601 604 ctx = context.resource(mapping, 'ctx')
602 605 if not ctx.obsolete():
603 606 return ''
604 607
605 608 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
606 609 ssets = [[hex(n) for n in ss] for ss in ssets]
607 610
608 611 data = []
609 612 for ss in ssets:
610 613 h = _hybrid(None, ss, lambda x: {'ctx': repo[x]},
611 614 lambda x: scmutil.formatchangeid(repo[x]))
612 615 data.append(h)
613 616
614 617 # Format the successorssets
615 618 def render(d):
616 619 return templateutil.stringify(context, mapping, d)
617 620
618 621 def gen(data):
619 622 yield "; ".join(render(d) for d in data)
620 623
621 624 return _hybrid(gen(data), data, lambda x: {'successorset': x},
622 625 pycompat.identity)
623 626
624 627 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'})
625 628 def showsuccsandmarkers(context, mapping):
626 629 """Returns a list of dict for each final successor of ctx. The dict
627 630 contains successors node id in "successors" keys and the list of
628 631 obs-markers from ctx to the set of successors in "markers".
629 632 (EXPERIMENTAL)
630 633 """
631 634 repo = context.resource(mapping, 'repo')
632 635 ctx = context.resource(mapping, 'ctx')
633 636
634 637 values = obsutil.successorsandmarkers(repo, ctx)
635 638
636 639 if values is None:
637 640 values = []
638 641
639 642 # Format successors and markers to avoid exposing binary to templates
640 643 data = []
641 644 for i in values:
642 645 # Format successors
643 646 successors = i['successors']
644 647
645 648 successors = [hex(n) for n in successors]
646 649 successors = _hybrid(None, successors,
647 650 lambda x: {'ctx': repo[x]},
648 651 lambda x: scmutil.formatchangeid(repo[x]))
649 652
650 653 # Format markers
651 654 finalmarkers = []
652 655 for m in i['markers']:
653 656 hexprec = hex(m[0])
654 657 hexsucs = tuple(hex(n) for n in m[1])
655 658 hexparents = None
656 659 if m[5] is not None:
657 660 hexparents = tuple(hex(n) for n in m[5])
658 661 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
659 662 finalmarkers.append(newmarker)
660 663
661 664 data.append({'successors': successors, 'markers': finalmarkers})
662 665
663 666 return templateutil.mappinglist(data)
664 667
665 668 @templatekeyword('p1rev', requires={'ctx'})
666 669 def showp1rev(context, mapping):
667 670 """Integer. The repository-local revision number of the changeset's
668 671 first parent, or -1 if the changeset has no parents."""
669 672 ctx = context.resource(mapping, 'ctx')
670 673 return ctx.p1().rev()
671 674
672 675 @templatekeyword('p2rev', requires={'ctx'})
673 676 def showp2rev(context, mapping):
674 677 """Integer. The repository-local revision number of the changeset's
675 678 second parent, or -1 if the changeset has no second parent."""
676 679 ctx = context.resource(mapping, 'ctx')
677 680 return ctx.p2().rev()
678 681
679 682 @templatekeyword('p1node', requires={'ctx'})
680 683 def showp1node(context, mapping):
681 684 """String. The identification hash of the changeset's first parent,
682 685 as a 40 digit hexadecimal string. If the changeset has no parents, all
683 686 digits are 0."""
684 687 ctx = context.resource(mapping, 'ctx')
685 688 return ctx.p1().hex()
686 689
687 690 @templatekeyword('p2node', requires={'ctx'})
688 691 def showp2node(context, mapping):
689 692 """String. The identification hash of the changeset's second
690 693 parent, as a 40 digit hexadecimal string. If the changeset has no second
691 694 parent, all digits are 0."""
692 695 ctx = context.resource(mapping, 'ctx')
693 696 return ctx.p2().hex()
694 697
695 698 @templatekeyword('parents', requires={'repo', 'ctx'})
696 699 def showparents(context, mapping):
697 700 """List of strings. The parents of the changeset in "rev:node"
698 701 format. If the changeset has only one "natural" parent (the predecessor
699 702 revision) nothing is shown."""
700 703 repo = context.resource(mapping, 'repo')
701 704 ctx = context.resource(mapping, 'ctx')
702 705 pctxs = scmutil.meaningfulparents(repo, ctx)
703 706 prevs = [p.rev() for p in pctxs]
704 707 parents = [[('rev', p.rev()),
705 708 ('node', p.hex()),
706 709 ('phase', p.phasestr())]
707 710 for p in pctxs]
708 711 f = _showcompatlist(context, mapping, 'parent', parents)
709 712 return _hybrid(f, prevs, lambda x: {'ctx': repo[x]},
710 713 lambda x: scmutil.formatchangeid(repo[x]), keytype=int)
711 714
712 715 @templatekeyword('phase', requires={'ctx'})
713 716 def showphase(context, mapping):
714 717 """String. The changeset phase name."""
715 718 ctx = context.resource(mapping, 'ctx')
716 719 return ctx.phasestr()
717 720
718 721 @templatekeyword('phaseidx', requires={'ctx'})
719 722 def showphaseidx(context, mapping):
720 723 """Integer. The changeset phase index. (ADVANCED)"""
721 724 ctx = context.resource(mapping, 'ctx')
722 725 return ctx.phase()
723 726
724 727 @templatekeyword('rev', requires={'ctx'})
725 728 def showrev(context, mapping):
726 729 """Integer. The repository-local changeset revision number."""
727 730 ctx = context.resource(mapping, 'ctx')
728 731 return scmutil.intrev(ctx)
729 732
730 733 def showrevslist(context, mapping, name, revs):
731 734 """helper to generate a list of revisions in which a mapped template will
732 735 be evaluated"""
733 736 repo = context.resource(mapping, 'repo')
734 737 f = _showcompatlist(context, mapping, name, ['%d' % r for r in revs])
735 738 return _hybrid(f, revs,
736 739 lambda x: {name: x, 'ctx': repo[x]},
737 740 pycompat.identity, keytype=int)
738 741
739 742 @templatekeyword('subrepos', requires={'ctx'})
740 743 def showsubrepos(context, mapping):
741 744 """List of strings. Updated subrepositories in the changeset."""
742 745 ctx = context.resource(mapping, 'ctx')
743 746 substate = ctx.substate
744 747 if not substate:
745 748 return compatlist(context, mapping, 'subrepo', [])
746 749 psubstate = ctx.parents()[0].substate or {}
747 750 subrepos = []
748 751 for sub in substate:
749 752 if sub not in psubstate or substate[sub] != psubstate[sub]:
750 753 subrepos.append(sub) # modified or newly added in ctx
751 754 for sub in psubstate:
752 755 if sub not in substate:
753 756 subrepos.append(sub) # removed in ctx
754 757 return compatlist(context, mapping, 'subrepo', sorted(subrepos))
755 758
756 759 # don't remove "showtags" definition, even though namespaces will put
757 760 # a helper function for "tags" keyword into "keywords" map automatically,
758 761 # because online help text is built without namespaces initialization
759 762 @templatekeyword('tags', requires={'repo', 'ctx'})
760 763 def showtags(context, mapping):
761 764 """List of strings. Any tags associated with the changeset."""
762 765 return shownames(context, mapping, 'tags')
763 766
764 767 @templatekeyword('termwidth', requires={'ui'})
765 768 def showtermwidth(context, mapping):
766 769 """Integer. The width of the current terminal."""
767 770 ui = context.resource(mapping, 'ui')
768 771 return ui.termwidth()
769 772
770 773 @templatekeyword('user', requires={'ctx'})
771 774 def showuser(context, mapping):
772 775 """String. The unmodified author of the changeset."""
773 776 ctx = context.resource(mapping, 'ctx')
774 777 return ctx.user()
775 778
776 779 @templatekeyword('instabilities', requires={'ctx'})
777 780 def showinstabilities(context, mapping):
778 781 """List of strings. Evolution instabilities affecting the changeset.
779 782 (EXPERIMENTAL)
780 783 """
781 784 ctx = context.resource(mapping, 'ctx')
782 785 return compatlist(context, mapping, 'instability', ctx.instabilities(),
783 786 plural='instabilities')
784 787
785 788 @templatekeyword('verbosity', requires={'ui'})
786 789 def showverbosity(context, mapping):
787 790 """String. The current output verbosity in 'debug', 'quiet', 'verbose',
788 791 or ''."""
789 792 ui = context.resource(mapping, 'ui')
790 793 # see logcmdutil.changesettemplater for priority of these flags
791 794 if ui.debugflag:
792 795 return 'debug'
793 796 elif ui.quiet:
794 797 return 'quiet'
795 798 elif ui.verbose:
796 799 return 'verbose'
797 800 return ''
798 801
799 802 @templatekeyword('whyunstable', requires={'repo', 'ctx'})
800 803 def showwhyunstable(context, mapping):
801 804 """List of dicts explaining all instabilities of a changeset.
802 805 (EXPERIMENTAL)
803 806 """
804 807 repo = context.resource(mapping, 'repo')
805 808 ctx = context.resource(mapping, 'ctx')
806 809
807 810 def formatnode(ctx):
808 811 return '%s (%s)' % (scmutil.formatchangeid(ctx), ctx.phasestr())
809 812
810 813 entries = obsutil.whyunstable(repo, ctx)
811 814
812 815 for entry in entries:
813 816 if entry.get('divergentnodes'):
814 817 dnodes = entry['divergentnodes']
815 818 dnhybrid = _hybrid(None, [dnode.hex() for dnode in dnodes],
816 819 lambda x: {'ctx': repo[x]},
817 820 lambda x: formatnode(repo[x]))
818 821 entry['divergentnodes'] = dnhybrid
819 822
820 823 tmpl = ('{instability}:{if(divergentnodes, " ")}{divergentnodes} '
821 824 '{reason} {node|short}')
822 825 return templateutil.mappinglist(entries, tmpl=tmpl, sep='\n')
823 826
824 827 def loadkeyword(ui, extname, registrarobj):
825 828 """Load template keyword from specified registrarobj
826 829 """
827 830 for name, func in registrarobj._table.iteritems():
828 831 keywords[name] = func
829 832
830 833 # tell hggettext to extract docstrings from these functions:
831 834 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now