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