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