##// END OF EJS Templates
templatekw: make negrev return empty for wdir() and nullrev...
Jordi Gutiérrez Hermoso -
r41875:36b62a52 default
parent child Browse files
Show More
@@ -1,892 +1,895
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 wdirid,
15 15 wdirrev,
16 16 )
17 17
18 18 from . import (
19 19 diffutil,
20 20 encoding,
21 21 error,
22 22 hbisect,
23 23 i18n,
24 24 obsutil,
25 25 patch,
26 26 pycompat,
27 27 registrar,
28 28 scmutil,
29 29 templateutil,
30 30 util,
31 31 )
32 32 from .utils import (
33 33 stringutil,
34 34 )
35 35
36 36 _hybrid = templateutil.hybrid
37 37 hybriddict = templateutil.hybriddict
38 38 hybridlist = templateutil.hybridlist
39 39 compatdict = templateutil.compatdict
40 40 compatlist = templateutil.compatlist
41 41 _showcompatlist = templateutil._showcompatlist
42 42
43 43 def getlatesttags(context, mapping, pattern=None):
44 44 '''return date, distance and name for the latest tag of rev'''
45 45 repo = context.resource(mapping, 'repo')
46 46 ctx = context.resource(mapping, 'ctx')
47 47 cache = context.resource(mapping, 'cache')
48 48
49 49 cachename = 'latesttags'
50 50 if pattern is not None:
51 51 cachename += '-' + pattern
52 52 match = stringutil.stringmatcher(pattern)[2]
53 53 else:
54 54 match = util.always
55 55
56 56 if cachename not in cache:
57 57 # Cache mapping from rev to a tuple with tag date, tag
58 58 # distance and tag name
59 59 cache[cachename] = {-1: (0, 0, ['null'])}
60 60 latesttags = cache[cachename]
61 61
62 62 rev = ctx.rev()
63 63 todo = [rev]
64 64 while todo:
65 65 rev = todo.pop()
66 66 if rev in latesttags:
67 67 continue
68 68 ctx = repo[rev]
69 69 tags = [t for t in ctx.tags()
70 70 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
71 71 and match(t))]
72 72 if tags:
73 73 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
74 74 continue
75 75 try:
76 76 ptags = [latesttags[p.rev()] for p in ctx.parents()]
77 77 if len(ptags) > 1:
78 78 if ptags[0][2] == ptags[1][2]:
79 79 # The tuples are laid out so the right one can be found by
80 80 # comparison in this case.
81 81 pdate, pdist, ptag = max(ptags)
82 82 else:
83 83 def key(x):
84 84 tag = x[2][0]
85 85 if ctx.rev() is None:
86 86 # only() doesn't support wdir
87 87 prevs = [c.rev() for c in ctx.parents()]
88 88 changes = repo.revs('only(%ld, %s)', prevs, tag)
89 89 changessincetag = len(changes) + 1
90 90 else:
91 91 changes = repo.revs('only(%d, %s)', ctx.rev(), tag)
92 92 changessincetag = len(changes)
93 93 # Smallest number of changes since tag wins. Date is
94 94 # used as tiebreaker.
95 95 return [-changessincetag, x[0]]
96 96 pdate, pdist, ptag = max(ptags, key=key)
97 97 else:
98 98 pdate, pdist, ptag = ptags[0]
99 99 except KeyError:
100 100 # Cache miss - recurse
101 101 todo.append(rev)
102 102 todo.extend(p.rev() for p in ctx.parents())
103 103 continue
104 104 latesttags[rev] = pdate, pdist + 1, ptag
105 105 return latesttags[rev]
106 106
107 107 def getrenamedfn(repo, endrev=None):
108 108 rcache = {}
109 109 if endrev is None:
110 110 endrev = len(repo)
111 111
112 112 def getrenamed(fn, rev):
113 113 '''looks up all renames for a file (up to endrev) the first
114 114 time the file is given. It indexes on the changerev and only
115 115 parses the manifest if linkrev != changerev.
116 116 Returns rename info for fn at changerev rev.'''
117 117 if fn not in rcache:
118 118 rcache[fn] = {}
119 119 fl = repo.file(fn)
120 120 for i in fl:
121 121 lr = fl.linkrev(i)
122 122 renamed = fl.renamed(fl.node(i))
123 123 rcache[fn][lr] = renamed and renamed[0]
124 124 if lr >= endrev:
125 125 break
126 126 if rev in rcache[fn]:
127 127 return rcache[fn][rev]
128 128
129 129 # If linkrev != rev (i.e. rev not found in rcache) fallback to
130 130 # filectx logic.
131 131 try:
132 132 renamed = repo[rev][fn].renamed()
133 133 return renamed and renamed[0]
134 134 except error.LookupError:
135 135 return None
136 136
137 137 return getrenamed
138 138
139 139 def getlogcolumns():
140 140 """Return a dict of log column labels"""
141 141 _ = pycompat.identity # temporarily disable gettext
142 142 # i18n: column positioning for "hg log"
143 143 columns = _('bookmark: %s\n'
144 144 'branch: %s\n'
145 145 'changeset: %s\n'
146 146 'copies: %s\n'
147 147 'date: %s\n'
148 148 'extra: %s=%s\n'
149 149 'files+: %s\n'
150 150 'files-: %s\n'
151 151 'files: %s\n'
152 152 'instability: %s\n'
153 153 'manifest: %s\n'
154 154 'obsolete: %s\n'
155 155 'parent: %s\n'
156 156 'phase: %s\n'
157 157 'summary: %s\n'
158 158 'tag: %s\n'
159 159 'user: %s\n')
160 160 return dict(zip([s.split(':', 1)[0] for s in columns.splitlines()],
161 161 i18n._(columns).splitlines(True)))
162 162
163 163 # basic internal templates
164 164 _changeidtmpl = '{rev}:{node|formatnode}'
165 165
166 166 # default templates internally used for rendering of lists
167 167 defaulttempl = {
168 168 'parent': _changeidtmpl + ' ',
169 169 'manifest': _changeidtmpl,
170 170 'file_copy': '{name} ({source})',
171 171 'envvar': '{key}={value}',
172 172 'extra': '{key}={value|stringescape}'
173 173 }
174 174 # filecopy is preserved for compatibility reasons
175 175 defaulttempl['filecopy'] = defaulttempl['file_copy']
176 176
177 177 # keywords are callables (see registrar.templatekeyword for details)
178 178 keywords = {}
179 179 templatekeyword = registrar.templatekeyword(keywords)
180 180
181 181 @templatekeyword('author', requires={'ctx'})
182 182 def showauthor(context, mapping):
183 183 """Alias for ``{user}``"""
184 184 return showuser(context, mapping)
185 185
186 186 @templatekeyword('bisect', requires={'repo', 'ctx'})
187 187 def showbisect(context, mapping):
188 188 """String. The changeset bisection status."""
189 189 repo = context.resource(mapping, 'repo')
190 190 ctx = context.resource(mapping, 'ctx')
191 191 return hbisect.label(repo, ctx.node())
192 192
193 193 @templatekeyword('branch', requires={'ctx'})
194 194 def showbranch(context, mapping):
195 195 """String. The name of the branch on which the changeset was
196 196 committed.
197 197 """
198 198 ctx = context.resource(mapping, 'ctx')
199 199 return ctx.branch()
200 200
201 201 @templatekeyword('branches', requires={'ctx'})
202 202 def showbranches(context, mapping):
203 203 """List of strings. The name of the branch on which the
204 204 changeset was committed. Will be empty if the branch name was
205 205 default. (DEPRECATED)
206 206 """
207 207 ctx = context.resource(mapping, 'ctx')
208 208 branch = ctx.branch()
209 209 if branch != 'default':
210 210 return compatlist(context, mapping, 'branch', [branch],
211 211 plural='branches')
212 212 return compatlist(context, mapping, 'branch', [], plural='branches')
213 213
214 214 @templatekeyword('bookmarks', requires={'repo', 'ctx'})
215 215 def showbookmarks(context, mapping):
216 216 """List of strings. Any bookmarks associated with the
217 217 changeset. Also sets 'active', the name of the active bookmark.
218 218 """
219 219 repo = context.resource(mapping, 'repo')
220 220 ctx = context.resource(mapping, 'ctx')
221 221 bookmarks = ctx.bookmarks()
222 222 active = repo._activebookmark
223 223 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
224 224 f = _showcompatlist(context, mapping, 'bookmark', bookmarks)
225 225 return _hybrid(f, bookmarks, makemap, pycompat.identity)
226 226
227 227 @templatekeyword('children', requires={'ctx'})
228 228 def showchildren(context, mapping):
229 229 """List of strings. The children of the changeset."""
230 230 ctx = context.resource(mapping, 'ctx')
231 231 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()]
232 232 return compatlist(context, mapping, 'children', childrevs, element='child')
233 233
234 234 # Deprecated, but kept alive for help generation a purpose.
235 235 @templatekeyword('currentbookmark', requires={'repo', 'ctx'})
236 236 def showcurrentbookmark(context, mapping):
237 237 """String. The active bookmark, if it is associated with the changeset.
238 238 (DEPRECATED)"""
239 239 return showactivebookmark(context, mapping)
240 240
241 241 @templatekeyword('activebookmark', requires={'repo', 'ctx'})
242 242 def showactivebookmark(context, mapping):
243 243 """String. The active bookmark, if it is associated with the changeset."""
244 244 repo = context.resource(mapping, 'repo')
245 245 ctx = context.resource(mapping, 'ctx')
246 246 active = repo._activebookmark
247 247 if active and active in ctx.bookmarks():
248 248 return active
249 249 return ''
250 250
251 251 @templatekeyword('date', requires={'ctx'})
252 252 def showdate(context, mapping):
253 253 """Date information. The date when the changeset was committed."""
254 254 ctx = context.resource(mapping, 'ctx')
255 255 # the default string format is '<float(unixtime)><tzoffset>' because
256 256 # python-hglib splits date at decimal separator.
257 257 return templateutil.date(ctx.date(), showfmt='%d.0%d')
258 258
259 259 @templatekeyword('desc', requires={'ctx'})
260 260 def showdescription(context, mapping):
261 261 """String. The text of the changeset description."""
262 262 ctx = context.resource(mapping, 'ctx')
263 263 s = ctx.description()
264 264 if isinstance(s, encoding.localstr):
265 265 # try hard to preserve utf-8 bytes
266 266 return encoding.tolocal(encoding.fromlocal(s).strip())
267 267 elif isinstance(s, encoding.safelocalstr):
268 268 return encoding.safelocalstr(s.strip())
269 269 else:
270 270 return s.strip()
271 271
272 272 @templatekeyword('diffstat', requires={'ui', 'ctx'})
273 273 def showdiffstat(context, mapping):
274 274 """String. Statistics of changes with the following format:
275 275 "modified files: +added/-removed lines"
276 276 """
277 277 ui = context.resource(mapping, 'ui')
278 278 ctx = context.resource(mapping, 'ctx')
279 279 diffopts = diffutil.diffallopts(ui, {'noprefix': False})
280 280 diff = ctx.diff(opts=diffopts)
281 281 stats = patch.diffstatdata(util.iterlines(diff))
282 282 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
283 283 return '%d: +%d/-%d' % (len(stats), adds, removes)
284 284
285 285 @templatekeyword('envvars', requires={'ui'})
286 286 def showenvvars(context, mapping):
287 287 """A dictionary of environment variables. (EXPERIMENTAL)"""
288 288 ui = context.resource(mapping, 'ui')
289 289 env = ui.exportableenviron()
290 290 env = util.sortdict((k, env[k]) for k in sorted(env))
291 291 return compatdict(context, mapping, 'envvar', env, plural='envvars')
292 292
293 293 @templatekeyword('extras', requires={'ctx'})
294 294 def showextras(context, mapping):
295 295 """List of dicts with key, value entries of the 'extras'
296 296 field of this changeset."""
297 297 ctx = context.resource(mapping, 'ctx')
298 298 extras = ctx.extra()
299 299 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
300 300 makemap = lambda k: {'key': k, 'value': extras[k]}
301 301 c = [makemap(k) for k in extras]
302 302 f = _showcompatlist(context, mapping, 'extra', c, plural='extras')
303 303 return _hybrid(f, extras, makemap,
304 304 lambda k: '%s=%s' % (k, stringutil.escapestr(extras[k])))
305 305
306 306 def _getfilestatus(context, mapping, listall=False):
307 307 ctx = context.resource(mapping, 'ctx')
308 308 revcache = context.resource(mapping, 'revcache')
309 309 if 'filestatus' not in revcache or revcache['filestatusall'] < listall:
310 310 stat = ctx.p1().status(ctx, listignored=listall, listclean=listall,
311 311 listunknown=listall)
312 312 revcache['filestatus'] = stat
313 313 revcache['filestatusall'] = listall
314 314 return revcache['filestatus']
315 315
316 316 def _getfilestatusmap(context, mapping, listall=False):
317 317 revcache = context.resource(mapping, 'revcache')
318 318 if 'filestatusmap' not in revcache or revcache['filestatusall'] < listall:
319 319 stat = _getfilestatus(context, mapping, listall=listall)
320 320 revcache['filestatusmap'] = statmap = {}
321 321 for char, files in zip(pycompat.iterbytestr('MAR!?IC'), stat):
322 322 statmap.update((f, char) for f in files)
323 323 return revcache['filestatusmap'] # {path: statchar}
324 324
325 325 def _showfilesbystat(context, mapping, name, index):
326 326 stat = _getfilestatus(context, mapping)
327 327 files = stat[index]
328 328 return templateutil.compatfileslist(context, mapping, name, files)
329 329
330 330 @templatekeyword('file_adds', requires={'ctx', 'revcache'})
331 331 def showfileadds(context, mapping):
332 332 """List of strings. Files added by this changeset."""
333 333 return _showfilesbystat(context, mapping, 'file_add', 1)
334 334
335 335 @templatekeyword('file_copies',
336 336 requires={'repo', 'ctx', 'cache', 'revcache'})
337 337 def showfilecopies(context, mapping):
338 338 """List of strings. Files copied in this changeset with
339 339 their sources.
340 340 """
341 341 repo = context.resource(mapping, 'repo')
342 342 ctx = context.resource(mapping, 'ctx')
343 343 cache = context.resource(mapping, 'cache')
344 344 copies = context.resource(mapping, 'revcache').get('copies')
345 345 if copies is None:
346 346 if 'getrenamed' not in cache:
347 347 cache['getrenamed'] = getrenamedfn(repo)
348 348 copies = []
349 349 getrenamed = cache['getrenamed']
350 350 for fn in ctx.files():
351 351 rename = getrenamed(fn, ctx.rev())
352 352 if rename:
353 353 copies.append((fn, rename))
354 354 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
355 355 copies)
356 356
357 357 # showfilecopiesswitch() displays file copies only if copy records are
358 358 # provided before calling the templater, usually with a --copies
359 359 # command line switch.
360 360 @templatekeyword('file_copies_switch', requires={'revcache'})
361 361 def showfilecopiesswitch(context, mapping):
362 362 """List of strings. Like "file_copies" but displayed
363 363 only if the --copied switch is set.
364 364 """
365 365 copies = context.resource(mapping, 'revcache').get('copies') or []
366 366 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy',
367 367 copies)
368 368
369 369 @templatekeyword('file_dels', requires={'ctx', 'revcache'})
370 370 def showfiledels(context, mapping):
371 371 """List of strings. Files removed by this changeset."""
372 372 return _showfilesbystat(context, mapping, 'file_del', 2)
373 373
374 374 @templatekeyword('file_mods', requires={'ctx', 'revcache'})
375 375 def showfilemods(context, mapping):
376 376 """List of strings. Files modified by this changeset."""
377 377 return _showfilesbystat(context, mapping, 'file_mod', 0)
378 378
379 379 @templatekeyword('files', requires={'ctx'})
380 380 def showfiles(context, mapping):
381 381 """List of strings. All files modified, added, or removed by this
382 382 changeset.
383 383 """
384 384 ctx = context.resource(mapping, 'ctx')
385 385 return templateutil.compatfileslist(context, mapping, 'file', ctx.files())
386 386
387 387 @templatekeyword('graphnode', requires={'repo', 'ctx'})
388 388 def showgraphnode(context, mapping):
389 389 """String. The character representing the changeset node in an ASCII
390 390 revision graph."""
391 391 repo = context.resource(mapping, 'repo')
392 392 ctx = context.resource(mapping, 'ctx')
393 393 return getgraphnode(repo, ctx)
394 394
395 395 def getgraphnode(repo, ctx):
396 396 return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx)
397 397
398 398 def getgraphnodecurrent(repo, ctx):
399 399 wpnodes = repo.dirstate.parents()
400 400 if wpnodes[1] == nullid:
401 401 wpnodes = wpnodes[:1]
402 402 if ctx.node() in wpnodes:
403 403 return '@'
404 404 else:
405 405 return ''
406 406
407 407 def getgraphnodesymbol(ctx):
408 408 if ctx.obsolete():
409 409 return 'x'
410 410 elif ctx.isunstable():
411 411 return '*'
412 412 elif ctx.closesbranch():
413 413 return '_'
414 414 else:
415 415 return 'o'
416 416
417 417 @templatekeyword('graphwidth', requires=())
418 418 def showgraphwidth(context, mapping):
419 419 """Integer. The width of the graph drawn by 'log --graph' or zero."""
420 420 # just hosts documentation; should be overridden by template mapping
421 421 return 0
422 422
423 423 @templatekeyword('index', requires=())
424 424 def showindex(context, mapping):
425 425 """Integer. The current iteration of the loop. (0 indexed)"""
426 426 # just hosts documentation; should be overridden by template mapping
427 427 raise error.Abort(_("can't use index in this context"))
428 428
429 429 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'})
430 430 def showlatesttag(context, mapping):
431 431 """List of strings. The global tags on the most recent globally
432 432 tagged ancestor of this changeset. If no such tags exist, the list
433 433 consists of the single string "null".
434 434 """
435 435 return showlatesttags(context, mapping, None)
436 436
437 437 def showlatesttags(context, mapping, pattern):
438 438 """helper method for the latesttag keyword and function"""
439 439 latesttags = getlatesttags(context, mapping, pattern)
440 440
441 441 # latesttag[0] is an implementation detail for sorting csets on different
442 442 # branches in a stable manner- it is the date the tagged cset was created,
443 443 # not the date the tag was created. Therefore it isn't made visible here.
444 444 makemap = lambda v: {
445 445 'changes': _showchangessincetag,
446 446 'distance': latesttags[1],
447 447 'latesttag': v, # BC with {latesttag % '{latesttag}'}
448 448 'tag': v
449 449 }
450 450
451 451 tags = latesttags[2]
452 452 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':')
453 453 return _hybrid(f, tags, makemap, pycompat.identity)
454 454
455 455 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'})
456 456 def showlatesttagdistance(context, mapping):
457 457 """Integer. Longest path to the latest tag."""
458 458 return getlatesttags(context, mapping)[1]
459 459
460 460 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
461 461 def showchangessincelatesttag(context, mapping):
462 462 """Integer. All ancestors not in the latest tag."""
463 463 tag = getlatesttags(context, mapping)[2][0]
464 464 mapping = context.overlaymap(mapping, {'tag': tag})
465 465 return _showchangessincetag(context, mapping)
466 466
467 467 def _showchangessincetag(context, mapping):
468 468 repo = context.resource(mapping, 'repo')
469 469 ctx = context.resource(mapping, 'ctx')
470 470 offset = 0
471 471 revs = [ctx.rev()]
472 472 tag = context.symbol(mapping, 'tag')
473 473
474 474 # The only() revset doesn't currently support wdir()
475 475 if ctx.rev() is None:
476 476 offset = 1
477 477 revs = [p.rev() for p in ctx.parents()]
478 478
479 479 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
480 480
481 481 # teach templater latesttags.changes is switched to (context, mapping) API
482 482 _showchangessincetag._requires = {'repo', 'ctx'}
483 483
484 484 @templatekeyword('manifest', requires={'repo', 'ctx'})
485 485 def showmanifest(context, mapping):
486 486 repo = context.resource(mapping, 'repo')
487 487 ctx = context.resource(mapping, 'ctx')
488 488 mnode = ctx.manifestnode()
489 489 if mnode is None:
490 490 mnode = wdirid
491 491 mrev = wdirrev
492 492 else:
493 493 mrev = repo.manifestlog.rev(mnode)
494 494 mhex = hex(mnode)
495 495 mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex})
496 496 f = context.process('manifest', mapping)
497 497 return templateutil.hybriditem(f, None, f,
498 498 lambda x: {'rev': mrev, 'node': mhex})
499 499
500 500 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'})
501 501 def showobsfate(context, mapping):
502 502 # this function returns a list containing pre-formatted obsfate strings.
503 503 #
504 504 # This function will be replaced by templates fragments when we will have
505 505 # the verbosity templatekw available.
506 506 succsandmarkers = showsuccsandmarkers(context, mapping)
507 507
508 508 ui = context.resource(mapping, 'ui')
509 509 repo = context.resource(mapping, 'repo')
510 510 values = []
511 511
512 512 for x in succsandmarkers.tovalue(context, mapping):
513 513 v = obsutil.obsfateprinter(ui, repo, x['successors'], x['markers'],
514 514 scmutil.formatchangeid)
515 515 values.append(v)
516 516
517 517 return compatlist(context, mapping, "fate", values)
518 518
519 519 def shownames(context, mapping, namespace):
520 520 """helper method to generate a template keyword for a namespace"""
521 521 repo = context.resource(mapping, 'repo')
522 522 ctx = context.resource(mapping, 'ctx')
523 523 ns = repo.names[namespace]
524 524 names = ns.names(repo, ctx.node())
525 525 return compatlist(context, mapping, ns.templatename, names,
526 526 plural=namespace)
527 527
528 528 @templatekeyword('namespaces', requires={'repo', 'ctx'})
529 529 def shownamespaces(context, mapping):
530 530 """Dict of lists. Names attached to this changeset per
531 531 namespace."""
532 532 repo = context.resource(mapping, 'repo')
533 533 ctx = context.resource(mapping, 'ctx')
534 534
535 535 namespaces = util.sortdict()
536 536 def makensmapfn(ns):
537 537 # 'name' for iterating over namespaces, templatename for local reference
538 538 return lambda v: {'name': v, ns.templatename: v}
539 539
540 540 for k, ns in repo.names.iteritems():
541 541 names = ns.names(repo, ctx.node())
542 542 f = _showcompatlist(context, mapping, 'name', names)
543 543 namespaces[k] = _hybrid(f, names, makensmapfn(ns), pycompat.identity)
544 544
545 545 f = _showcompatlist(context, mapping, 'namespace', list(namespaces))
546 546
547 547 def makemap(ns):
548 548 return {
549 549 'namespace': ns,
550 550 'names': namespaces[ns],
551 551 'builtin': repo.names[ns].builtin,
552 552 'colorname': repo.names[ns].colorname,
553 553 }
554 554
555 555 return _hybrid(f, namespaces, makemap, pycompat.identity)
556 556
557 557 @templatekeyword('negrev', requires={'repo', 'ctx'})
558 558 def shownegrev(context, mapping):
559 559 """Integer. The repository-local changeset negative revision number,
560 560 which counts in the opposite direction."""
561 561 ctx = context.resource(mapping, 'ctx')
562 rev = ctx.rev()
563 if rev is None or rev < 0: # wdir() or nullrev?
564 return None
562 565 repo = context.resource(mapping, 'repo')
563 return scmutil.intrev(ctx) - len(repo)
566 return rev - len(repo)
564 567
565 568 @templatekeyword('node', requires={'ctx'})
566 569 def shownode(context, mapping):
567 570 """String. The changeset identification hash, as a 40 hexadecimal
568 571 digit string.
569 572 """
570 573 ctx = context.resource(mapping, 'ctx')
571 574 return ctx.hex()
572 575
573 576 @templatekeyword('obsolete', requires={'ctx'})
574 577 def showobsolete(context, mapping):
575 578 """String. Whether the changeset is obsolete. (EXPERIMENTAL)"""
576 579 ctx = context.resource(mapping, 'ctx')
577 580 if ctx.obsolete():
578 581 return 'obsolete'
579 582 return ''
580 583
581 584 @templatekeyword('path', requires={'fctx'})
582 585 def showpath(context, mapping):
583 586 """String. Repository-absolute path of the current file. (EXPERIMENTAL)"""
584 587 fctx = context.resource(mapping, 'fctx')
585 588 return fctx.path()
586 589
587 590 @templatekeyword('peerurls', requires={'repo'})
588 591 def showpeerurls(context, mapping):
589 592 """A dictionary of repository locations defined in the [paths] section
590 593 of your configuration file."""
591 594 repo = context.resource(mapping, 'repo')
592 595 # see commands.paths() for naming of dictionary keys
593 596 paths = repo.ui.paths
594 597 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
595 598 def makemap(k):
596 599 p = paths[k]
597 600 d = {'name': k, 'url': p.rawloc}
598 601 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
599 602 return d
600 603 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
601 604
602 605 @templatekeyword("predecessors", requires={'repo', 'ctx'})
603 606 def showpredecessors(context, mapping):
604 607 """Returns the list of the closest visible successors. (EXPERIMENTAL)"""
605 608 repo = context.resource(mapping, 'repo')
606 609 ctx = context.resource(mapping, 'ctx')
607 610 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
608 611 predecessors = pycompat.maplist(hex, predecessors)
609 612
610 613 return _hybrid(None, predecessors,
611 614 lambda x: {'ctx': repo[x]},
612 615 lambda x: scmutil.formatchangeid(repo[x]))
613 616
614 617 @templatekeyword('reporoot', requires={'repo'})
615 618 def showreporoot(context, mapping):
616 619 """String. The root directory of the current repository."""
617 620 repo = context.resource(mapping, 'repo')
618 621 return repo.root
619 622
620 623 @templatekeyword('size', requires={'fctx'})
621 624 def showsize(context, mapping):
622 625 """Integer. Size of the current file in bytes. (EXPERIMENTAL)"""
623 626 fctx = context.resource(mapping, 'fctx')
624 627 return fctx.size()
625 628
626 629 # requires 'fctx' to denote {status} depends on (ctx, path) pair
627 630 @templatekeyword('status', requires={'ctx', 'fctx', 'revcache'})
628 631 def showstatus(context, mapping):
629 632 """String. Status code of the current file. (EXPERIMENTAL)"""
630 633 path = templateutil.runsymbol(context, mapping, 'path')
631 634 path = templateutil.stringify(context, mapping, path)
632 635 if not path:
633 636 return
634 637 statmap = _getfilestatusmap(context, mapping)
635 638 if path not in statmap:
636 639 statmap = _getfilestatusmap(context, mapping, listall=True)
637 640 return statmap.get(path)
638 641
639 642 @templatekeyword("successorssets", requires={'repo', 'ctx'})
640 643 def showsuccessorssets(context, mapping):
641 644 """Returns a string of sets of successors for a changectx. Format used
642 645 is: [ctx1, ctx2], [ctx3] if ctx has been split into ctx1 and ctx2
643 646 while also diverged into ctx3. (EXPERIMENTAL)"""
644 647 repo = context.resource(mapping, 'repo')
645 648 ctx = context.resource(mapping, 'ctx')
646 649 if not ctx.obsolete():
647 650 return ''
648 651
649 652 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
650 653 ssets = [[hex(n) for n in ss] for ss in ssets]
651 654
652 655 data = []
653 656 for ss in ssets:
654 657 h = _hybrid(None, ss, lambda x: {'ctx': repo[x]},
655 658 lambda x: scmutil.formatchangeid(repo[x]))
656 659 data.append(h)
657 660
658 661 # Format the successorssets
659 662 def render(d):
660 663 return templateutil.stringify(context, mapping, d)
661 664
662 665 def gen(data):
663 666 yield "; ".join(render(d) for d in data)
664 667
665 668 return _hybrid(gen(data), data, lambda x: {'successorset': x},
666 669 pycompat.identity)
667 670
668 671 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'})
669 672 def showsuccsandmarkers(context, mapping):
670 673 """Returns a list of dict for each final successor of ctx. The dict
671 674 contains successors node id in "successors" keys and the list of
672 675 obs-markers from ctx to the set of successors in "markers".
673 676 (EXPERIMENTAL)
674 677 """
675 678 repo = context.resource(mapping, 'repo')
676 679 ctx = context.resource(mapping, 'ctx')
677 680
678 681 values = obsutil.successorsandmarkers(repo, ctx)
679 682
680 683 if values is None:
681 684 values = []
682 685
683 686 # Format successors and markers to avoid exposing binary to templates
684 687 data = []
685 688 for i in values:
686 689 # Format successors
687 690 successors = i['successors']
688 691
689 692 successors = [hex(n) for n in successors]
690 693 successors = _hybrid(None, successors,
691 694 lambda x: {'ctx': repo[x]},
692 695 lambda x: scmutil.formatchangeid(repo[x]))
693 696
694 697 # Format markers
695 698 finalmarkers = []
696 699 for m in i['markers']:
697 700 hexprec = hex(m[0])
698 701 hexsucs = tuple(hex(n) for n in m[1])
699 702 hexparents = None
700 703 if m[5] is not None:
701 704 hexparents = tuple(hex(n) for n in m[5])
702 705 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
703 706 finalmarkers.append(newmarker)
704 707
705 708 data.append({'successors': successors, 'markers': finalmarkers})
706 709
707 710 return templateutil.mappinglist(data)
708 711
709 712 @templatekeyword('p1', requires={'ctx'})
710 713 def showp1(context, mapping):
711 714 """Changeset. The changeset's first parent. ``{p1.rev}`` for the revision
712 715 number, and ``{p1.node}`` for the identification hash."""
713 716 ctx = context.resource(mapping, 'ctx')
714 717 return templateutil.mappingdict({'ctx': ctx.p1()}, tmpl=_changeidtmpl)
715 718
716 719 @templatekeyword('p2', requires={'ctx'})
717 720 def showp2(context, mapping):
718 721 """Changeset. The changeset's second parent. ``{p2.rev}`` for the revision
719 722 number, and ``{p2.node}`` for the identification hash."""
720 723 ctx = context.resource(mapping, 'ctx')
721 724 return templateutil.mappingdict({'ctx': ctx.p2()}, tmpl=_changeidtmpl)
722 725
723 726 @templatekeyword('p1rev', requires={'ctx'})
724 727 def showp1rev(context, mapping):
725 728 """Integer. The repository-local revision number of the changeset's
726 729 first parent, or -1 if the changeset has no parents. (DEPRECATED)"""
727 730 ctx = context.resource(mapping, 'ctx')
728 731 return ctx.p1().rev()
729 732
730 733 @templatekeyword('p2rev', requires={'ctx'})
731 734 def showp2rev(context, mapping):
732 735 """Integer. The repository-local revision number of the changeset's
733 736 second parent, or -1 if the changeset has no second parent. (DEPRECATED)"""
734 737 ctx = context.resource(mapping, 'ctx')
735 738 return ctx.p2().rev()
736 739
737 740 @templatekeyword('p1node', requires={'ctx'})
738 741 def showp1node(context, mapping):
739 742 """String. The identification hash of the changeset's first parent,
740 743 as a 40 digit hexadecimal string. If the changeset has no parents, all
741 744 digits are 0. (DEPRECATED)"""
742 745 ctx = context.resource(mapping, 'ctx')
743 746 return ctx.p1().hex()
744 747
745 748 @templatekeyword('p2node', requires={'ctx'})
746 749 def showp2node(context, mapping):
747 750 """String. The identification hash of the changeset's second
748 751 parent, as a 40 digit hexadecimal string. If the changeset has no second
749 752 parent, all digits are 0. (DEPRECATED)"""
750 753 ctx = context.resource(mapping, 'ctx')
751 754 return ctx.p2().hex()
752 755
753 756 @templatekeyword('parents', requires={'repo', 'ctx'})
754 757 def showparents(context, mapping):
755 758 """List of strings. The parents of the changeset in "rev:node"
756 759 format. If the changeset has only one "natural" parent (the predecessor
757 760 revision) nothing is shown."""
758 761 repo = context.resource(mapping, 'repo')
759 762 ctx = context.resource(mapping, 'ctx')
760 763 pctxs = scmutil.meaningfulparents(repo, ctx)
761 764 prevs = [p.rev() for p in pctxs]
762 765 parents = [[('rev', p.rev()),
763 766 ('node', p.hex()),
764 767 ('phase', p.phasestr())]
765 768 for p in pctxs]
766 769 f = _showcompatlist(context, mapping, 'parent', parents)
767 770 return _hybrid(f, prevs, lambda x: {'ctx': repo[x]},
768 771 lambda x: scmutil.formatchangeid(repo[x]), keytype=int)
769 772
770 773 @templatekeyword('phase', requires={'ctx'})
771 774 def showphase(context, mapping):
772 775 """String. The changeset phase name."""
773 776 ctx = context.resource(mapping, 'ctx')
774 777 return ctx.phasestr()
775 778
776 779 @templatekeyword('phaseidx', requires={'ctx'})
777 780 def showphaseidx(context, mapping):
778 781 """Integer. The changeset phase index. (ADVANCED)"""
779 782 ctx = context.resource(mapping, 'ctx')
780 783 return ctx.phase()
781 784
782 785 @templatekeyword('rev', requires={'ctx'})
783 786 def showrev(context, mapping):
784 787 """Integer. The repository-local changeset revision number."""
785 788 ctx = context.resource(mapping, 'ctx')
786 789 return scmutil.intrev(ctx)
787 790
788 791 def showrevslist(context, mapping, name, revs):
789 792 """helper to generate a list of revisions in which a mapped template will
790 793 be evaluated"""
791 794 repo = context.resource(mapping, 'repo')
792 795 # revs may be a smartset; don't compute it until f() has to be evaluated
793 796 def f():
794 797 srevs = ['%d' % r for r in revs]
795 798 return _showcompatlist(context, mapping, name, srevs)
796 799 return _hybrid(f, revs,
797 800 lambda x: {name: x, 'ctx': repo[x]},
798 801 pycompat.identity, keytype=int)
799 802
800 803 @templatekeyword('subrepos', requires={'ctx'})
801 804 def showsubrepos(context, mapping):
802 805 """List of strings. Updated subrepositories in the changeset."""
803 806 ctx = context.resource(mapping, 'ctx')
804 807 substate = ctx.substate
805 808 if not substate:
806 809 return compatlist(context, mapping, 'subrepo', [])
807 810 psubstate = ctx.p1().substate or {}
808 811 subrepos = []
809 812 for sub in substate:
810 813 if sub not in psubstate or substate[sub] != psubstate[sub]:
811 814 subrepos.append(sub) # modified or newly added in ctx
812 815 for sub in psubstate:
813 816 if sub not in substate:
814 817 subrepos.append(sub) # removed in ctx
815 818 return compatlist(context, mapping, 'subrepo', sorted(subrepos))
816 819
817 820 # don't remove "showtags" definition, even though namespaces will put
818 821 # a helper function for "tags" keyword into "keywords" map automatically,
819 822 # because online help text is built without namespaces initialization
820 823 @templatekeyword('tags', requires={'repo', 'ctx'})
821 824 def showtags(context, mapping):
822 825 """List of strings. Any tags associated with the changeset."""
823 826 return shownames(context, mapping, 'tags')
824 827
825 828 @templatekeyword('termwidth', requires={'ui'})
826 829 def showtermwidth(context, mapping):
827 830 """Integer. The width of the current terminal."""
828 831 ui = context.resource(mapping, 'ui')
829 832 return ui.termwidth()
830 833
831 834 @templatekeyword('user', requires={'ctx'})
832 835 def showuser(context, mapping):
833 836 """String. The unmodified author of the changeset."""
834 837 ctx = context.resource(mapping, 'ctx')
835 838 return ctx.user()
836 839
837 840 @templatekeyword('instabilities', requires={'ctx'})
838 841 def showinstabilities(context, mapping):
839 842 """List of strings. Evolution instabilities affecting the changeset.
840 843 (EXPERIMENTAL)
841 844 """
842 845 ctx = context.resource(mapping, 'ctx')
843 846 return compatlist(context, mapping, 'instability', ctx.instabilities(),
844 847 plural='instabilities')
845 848
846 849 @templatekeyword('verbosity', requires={'ui'})
847 850 def showverbosity(context, mapping):
848 851 """String. The current output verbosity in 'debug', 'quiet', 'verbose',
849 852 or ''."""
850 853 ui = context.resource(mapping, 'ui')
851 854 # see logcmdutil.changesettemplater for priority of these flags
852 855 if ui.debugflag:
853 856 return 'debug'
854 857 elif ui.quiet:
855 858 return 'quiet'
856 859 elif ui.verbose:
857 860 return 'verbose'
858 861 return ''
859 862
860 863 @templatekeyword('whyunstable', requires={'repo', 'ctx'})
861 864 def showwhyunstable(context, mapping):
862 865 """List of dicts explaining all instabilities of a changeset.
863 866 (EXPERIMENTAL)
864 867 """
865 868 repo = context.resource(mapping, 'repo')
866 869 ctx = context.resource(mapping, 'ctx')
867 870
868 871 def formatnode(ctx):
869 872 return '%s (%s)' % (scmutil.formatchangeid(ctx), ctx.phasestr())
870 873
871 874 entries = obsutil.whyunstable(repo, ctx)
872 875
873 876 for entry in entries:
874 877 if entry.get('divergentnodes'):
875 878 dnodes = entry['divergentnodes']
876 879 dnhybrid = _hybrid(None, [dnode.hex() for dnode in dnodes],
877 880 lambda x: {'ctx': repo[x]},
878 881 lambda x: formatnode(repo[x]))
879 882 entry['divergentnodes'] = dnhybrid
880 883
881 884 tmpl = ('{instability}:{if(divergentnodes, " ")}{divergentnodes} '
882 885 '{reason} {node|short}')
883 886 return templateutil.mappinglist(entries, tmpl=tmpl, sep='\n')
884 887
885 888 def loadkeyword(ui, extname, registrarobj):
886 889 """Load template keyword from specified registrarobj
887 890 """
888 891 for name, func in registrarobj._table.iteritems():
889 892 keywords[name] = func
890 893
891 894 # tell hggettext to extract docstrings from these functions:
892 895 i18nfunctions = keywords.values()
@@ -1,1357 +1,1363
1 1 Test template keywords
2 2 ======================
3 3
4 4 $ hg init a
5 5 $ cd a
6 6 $ echo a > a
7 7 $ hg add a
8 8 $ echo line 1 > b
9 9 $ echo line 2 >> b
10 10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
11 11
12 12 $ hg add b
13 13 $ echo other 1 > c
14 14 $ echo other 2 >> c
15 15 $ echo >> c
16 16 $ echo other 3 >> c
17 17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
18 18
19 19 $ hg add c
20 20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
21 21 $ echo c >> c
22 22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
23 23
24 24 $ echo foo > .hg/branch
25 25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
26 26
27 27 $ hg co -q 3
28 28 $ echo other 4 >> d
29 29 $ hg add d
30 30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
31 31
32 32 $ hg merge -q foo
33 33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
34 34
35 35 Second branch starting at nullrev:
36 36
37 37 $ hg update null
38 38 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
39 39 $ echo second > second
40 40 $ hg add second
41 41 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
42 42 created new head
43 43
44 44 $ echo third > third
45 45 $ hg add third
46 46 $ hg mv second fourth
47 47 $ hg commit -m third -d "2020-01-01 10:01"
48 48
49 49 Working-directory revision has special identifiers, though they are still
50 50 experimental:
51 51
52 52 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
53 53 2147483647:ffffffffffffffffffffffffffffffffffffffff
54 54
55 55 $ hg log -r 'wdir()' -Tjson --debug
56 56 [
57 57 {
58 58 "added": [],
59 59 "bookmarks": [],
60 60 "branch": "default",
61 61 "date": [0, 0],
62 62 "desc": "",
63 63 "extra": {"branch": "default"},
64 64 "manifest": "ffffffffffffffffffffffffffffffffffffffff",
65 65 "modified": [],
66 66 "node": "ffffffffffffffffffffffffffffffffffffffff",
67 67 "parents": ["95c24699272ef57d062b8bccc32c878bf841784a"],
68 68 "phase": "draft",
69 69 "removed": [],
70 70 "rev": 2147483647,
71 71 "tags": [],
72 72 "user": "test"
73 73 }
74 74 ]
75 75
76 76 $ hg log -r 'wdir()' -T '{manifest}\n'
77 77 2147483647:ffffffffffff
78 78
79 However, for negrev, we refuse to output anything (as well as for null)
80
81 $ hg log -r 'wdir() + null' -T 'bla{negrev}nk\n'
82 blank
83 blank
84
79 85 Changectx-derived keywords are disabled within {manifest} as {node} changes:
80 86
81 87 $ hg log -r0 -T 'outer:{p1node} {manifest % "inner:{p1node}"}\n'
82 88 outer:0000000000000000000000000000000000000000 inner:
83 89
84 90 Check that {phase} works correctly on parents:
85 91
86 92 $ cat << EOF > parentphase
87 93 > changeset_debug = '{rev} ({phase}):{parents}\n'
88 94 > parent = ' {rev} ({phase})'
89 95 > EOF
90 96 $ hg phase -r 5 --public
91 97 $ hg phase -r 7 --secret --force
92 98 $ hg log --debug -G --style ./parentphase
93 99 @ 8 (secret): 7 (secret) -1 (public)
94 100 |
95 101 o 7 (secret): -1 (public) -1 (public)
96 102
97 103 o 6 (draft): 5 (public) 4 (draft)
98 104 |\
99 105 | o 5 (public): 3 (public) -1 (public)
100 106 | |
101 107 o | 4 (draft): 3 (public) -1 (public)
102 108 |/
103 109 o 3 (public): 2 (public) -1 (public)
104 110 |
105 111 o 2 (public): 1 (public) -1 (public)
106 112 |
107 113 o 1 (public): 0 (public) -1 (public)
108 114 |
109 115 o 0 (public): -1 (public) -1 (public)
110 116
111 117
112 118 Keys work:
113 119
114 120 $ for key in author branch branches date desc file_adds file_dels file_mods \
115 121 > file_copies file_copies_switch files \
116 122 > manifest node parents rev tags diffstat extras \
117 123 > p1rev p2rev p1node p2node user; do
118 124 > for mode in '' --verbose --debug; do
119 125 > hg log $mode --template "$key$mode: {$key}\n"
120 126 > done
121 127 > done
122 128 author: test
123 129 author: User Name <user@hostname>
124 130 author: person
125 131 author: person
126 132 author: person
127 133 author: person
128 134 author: other@place
129 135 author: A. N. Other <other@place>
130 136 author: User Name <user@hostname>
131 137 author--verbose: test
132 138 author--verbose: User Name <user@hostname>
133 139 author--verbose: person
134 140 author--verbose: person
135 141 author--verbose: person
136 142 author--verbose: person
137 143 author--verbose: other@place
138 144 author--verbose: A. N. Other <other@place>
139 145 author--verbose: User Name <user@hostname>
140 146 author--debug: test
141 147 author--debug: User Name <user@hostname>
142 148 author--debug: person
143 149 author--debug: person
144 150 author--debug: person
145 151 author--debug: person
146 152 author--debug: other@place
147 153 author--debug: A. N. Other <other@place>
148 154 author--debug: User Name <user@hostname>
149 155 branch: default
150 156 branch: default
151 157 branch: default
152 158 branch: default
153 159 branch: foo
154 160 branch: default
155 161 branch: default
156 162 branch: default
157 163 branch: default
158 164 branch--verbose: default
159 165 branch--verbose: default
160 166 branch--verbose: default
161 167 branch--verbose: default
162 168 branch--verbose: foo
163 169 branch--verbose: default
164 170 branch--verbose: default
165 171 branch--verbose: default
166 172 branch--verbose: default
167 173 branch--debug: default
168 174 branch--debug: default
169 175 branch--debug: default
170 176 branch--debug: default
171 177 branch--debug: foo
172 178 branch--debug: default
173 179 branch--debug: default
174 180 branch--debug: default
175 181 branch--debug: default
176 182 branches:
177 183 branches:
178 184 branches:
179 185 branches:
180 186 branches: foo
181 187 branches:
182 188 branches:
183 189 branches:
184 190 branches:
185 191 branches--verbose:
186 192 branches--verbose:
187 193 branches--verbose:
188 194 branches--verbose:
189 195 branches--verbose: foo
190 196 branches--verbose:
191 197 branches--verbose:
192 198 branches--verbose:
193 199 branches--verbose:
194 200 branches--debug:
195 201 branches--debug:
196 202 branches--debug:
197 203 branches--debug:
198 204 branches--debug: foo
199 205 branches--debug:
200 206 branches--debug:
201 207 branches--debug:
202 208 branches--debug:
203 209 date: 1577872860.00
204 210 date: 1000000.00
205 211 date: 1500001.00
206 212 date: 1500000.00
207 213 date: 1400000.00
208 214 date: 1300000.00
209 215 date: 1200000.00
210 216 date: 1100000.00
211 217 date: 1000000.00
212 218 date--verbose: 1577872860.00
213 219 date--verbose: 1000000.00
214 220 date--verbose: 1500001.00
215 221 date--verbose: 1500000.00
216 222 date--verbose: 1400000.00
217 223 date--verbose: 1300000.00
218 224 date--verbose: 1200000.00
219 225 date--verbose: 1100000.00
220 226 date--verbose: 1000000.00
221 227 date--debug: 1577872860.00
222 228 date--debug: 1000000.00
223 229 date--debug: 1500001.00
224 230 date--debug: 1500000.00
225 231 date--debug: 1400000.00
226 232 date--debug: 1300000.00
227 233 date--debug: 1200000.00
228 234 date--debug: 1100000.00
229 235 date--debug: 1000000.00
230 236 desc: third
231 237 desc: second
232 238 desc: merge
233 239 desc: new head
234 240 desc: new branch
235 241 desc: no user, no domain
236 242 desc: no person
237 243 desc: other 1
238 244 other 2
239 245
240 246 other 3
241 247 desc: line 1
242 248 line 2
243 249 desc--verbose: third
244 250 desc--verbose: second
245 251 desc--verbose: merge
246 252 desc--verbose: new head
247 253 desc--verbose: new branch
248 254 desc--verbose: no user, no domain
249 255 desc--verbose: no person
250 256 desc--verbose: other 1
251 257 other 2
252 258
253 259 other 3
254 260 desc--verbose: line 1
255 261 line 2
256 262 desc--debug: third
257 263 desc--debug: second
258 264 desc--debug: merge
259 265 desc--debug: new head
260 266 desc--debug: new branch
261 267 desc--debug: no user, no domain
262 268 desc--debug: no person
263 269 desc--debug: other 1
264 270 other 2
265 271
266 272 other 3
267 273 desc--debug: line 1
268 274 line 2
269 275 file_adds: fourth third
270 276 file_adds: second
271 277 file_adds:
272 278 file_adds: d
273 279 file_adds:
274 280 file_adds:
275 281 file_adds: c
276 282 file_adds: b
277 283 file_adds: a
278 284 file_adds--verbose: fourth third
279 285 file_adds--verbose: second
280 286 file_adds--verbose:
281 287 file_adds--verbose: d
282 288 file_adds--verbose:
283 289 file_adds--verbose:
284 290 file_adds--verbose: c
285 291 file_adds--verbose: b
286 292 file_adds--verbose: a
287 293 file_adds--debug: fourth third
288 294 file_adds--debug: second
289 295 file_adds--debug:
290 296 file_adds--debug: d
291 297 file_adds--debug:
292 298 file_adds--debug:
293 299 file_adds--debug: c
294 300 file_adds--debug: b
295 301 file_adds--debug: a
296 302 file_dels: second
297 303 file_dels:
298 304 file_dels:
299 305 file_dels:
300 306 file_dels:
301 307 file_dels:
302 308 file_dels:
303 309 file_dels:
304 310 file_dels:
305 311 file_dels--verbose: second
306 312 file_dels--verbose:
307 313 file_dels--verbose:
308 314 file_dels--verbose:
309 315 file_dels--verbose:
310 316 file_dels--verbose:
311 317 file_dels--verbose:
312 318 file_dels--verbose:
313 319 file_dels--verbose:
314 320 file_dels--debug: second
315 321 file_dels--debug:
316 322 file_dels--debug:
317 323 file_dels--debug:
318 324 file_dels--debug:
319 325 file_dels--debug:
320 326 file_dels--debug:
321 327 file_dels--debug:
322 328 file_dels--debug:
323 329 file_mods:
324 330 file_mods:
325 331 file_mods:
326 332 file_mods:
327 333 file_mods:
328 334 file_mods: c
329 335 file_mods:
330 336 file_mods:
331 337 file_mods:
332 338 file_mods--verbose:
333 339 file_mods--verbose:
334 340 file_mods--verbose:
335 341 file_mods--verbose:
336 342 file_mods--verbose:
337 343 file_mods--verbose: c
338 344 file_mods--verbose:
339 345 file_mods--verbose:
340 346 file_mods--verbose:
341 347 file_mods--debug:
342 348 file_mods--debug:
343 349 file_mods--debug:
344 350 file_mods--debug:
345 351 file_mods--debug:
346 352 file_mods--debug: c
347 353 file_mods--debug:
348 354 file_mods--debug:
349 355 file_mods--debug:
350 356 file_copies: fourth (second)
351 357 file_copies:
352 358 file_copies:
353 359 file_copies:
354 360 file_copies:
355 361 file_copies:
356 362 file_copies:
357 363 file_copies:
358 364 file_copies:
359 365 file_copies--verbose: fourth (second)
360 366 file_copies--verbose:
361 367 file_copies--verbose:
362 368 file_copies--verbose:
363 369 file_copies--verbose:
364 370 file_copies--verbose:
365 371 file_copies--verbose:
366 372 file_copies--verbose:
367 373 file_copies--verbose:
368 374 file_copies--debug: fourth (second)
369 375 file_copies--debug:
370 376 file_copies--debug:
371 377 file_copies--debug:
372 378 file_copies--debug:
373 379 file_copies--debug:
374 380 file_copies--debug:
375 381 file_copies--debug:
376 382 file_copies--debug:
377 383 file_copies_switch:
378 384 file_copies_switch:
379 385 file_copies_switch:
380 386 file_copies_switch:
381 387 file_copies_switch:
382 388 file_copies_switch:
383 389 file_copies_switch:
384 390 file_copies_switch:
385 391 file_copies_switch:
386 392 file_copies_switch--verbose:
387 393 file_copies_switch--verbose:
388 394 file_copies_switch--verbose:
389 395 file_copies_switch--verbose:
390 396 file_copies_switch--verbose:
391 397 file_copies_switch--verbose:
392 398 file_copies_switch--verbose:
393 399 file_copies_switch--verbose:
394 400 file_copies_switch--verbose:
395 401 file_copies_switch--debug:
396 402 file_copies_switch--debug:
397 403 file_copies_switch--debug:
398 404 file_copies_switch--debug:
399 405 file_copies_switch--debug:
400 406 file_copies_switch--debug:
401 407 file_copies_switch--debug:
402 408 file_copies_switch--debug:
403 409 file_copies_switch--debug:
404 410 files: fourth second third
405 411 files: second
406 412 files:
407 413 files: d
408 414 files:
409 415 files: c
410 416 files: c
411 417 files: b
412 418 files: a
413 419 files--verbose: fourth second third
414 420 files--verbose: second
415 421 files--verbose:
416 422 files--verbose: d
417 423 files--verbose:
418 424 files--verbose: c
419 425 files--verbose: c
420 426 files--verbose: b
421 427 files--verbose: a
422 428 files--debug: fourth second third
423 429 files--debug: second
424 430 files--debug:
425 431 files--debug: d
426 432 files--debug:
427 433 files--debug: c
428 434 files--debug: c
429 435 files--debug: b
430 436 files--debug: a
431 437 manifest: 6:94961b75a2da
432 438 manifest: 5:f2dbc354b94e
433 439 manifest: 4:4dc3def4f9b4
434 440 manifest: 4:4dc3def4f9b4
435 441 manifest: 3:cb5a1327723b
436 442 manifest: 3:cb5a1327723b
437 443 manifest: 2:6e0e82995c35
438 444 manifest: 1:4e8d705b1e53
439 445 manifest: 0:a0c8bcbbb45c
440 446 manifest--verbose: 6:94961b75a2da
441 447 manifest--verbose: 5:f2dbc354b94e
442 448 manifest--verbose: 4:4dc3def4f9b4
443 449 manifest--verbose: 4:4dc3def4f9b4
444 450 manifest--verbose: 3:cb5a1327723b
445 451 manifest--verbose: 3:cb5a1327723b
446 452 manifest--verbose: 2:6e0e82995c35
447 453 manifest--verbose: 1:4e8d705b1e53
448 454 manifest--verbose: 0:a0c8bcbbb45c
449 455 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
450 456 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
451 457 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
452 458 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
453 459 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
454 460 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
455 461 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
456 462 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
457 463 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
458 464 node: 95c24699272ef57d062b8bccc32c878bf841784a
459 465 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
460 466 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
461 467 node: 13207e5a10d9fd28ec424934298e176197f2c67f
462 468 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
463 469 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
464 470 node: 97054abb4ab824450e9164180baf491ae0078465
465 471 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
466 472 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
467 473 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
468 474 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
469 475 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
470 476 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
471 477 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
472 478 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
473 479 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
474 480 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
475 481 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
476 482 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
477 483 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
478 484 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
479 485 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
480 486 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
481 487 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
482 488 node--debug: 97054abb4ab824450e9164180baf491ae0078465
483 489 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
484 490 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
485 491 parents:
486 492 parents: -1:000000000000
487 493 parents: 5:13207e5a10d9 4:bbe44766e73d
488 494 parents: 3:10e46f2dcbf4
489 495 parents:
490 496 parents:
491 497 parents:
492 498 parents:
493 499 parents:
494 500 parents--verbose:
495 501 parents--verbose: -1:000000000000
496 502 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
497 503 parents--verbose: 3:10e46f2dcbf4
498 504 parents--verbose:
499 505 parents--verbose:
500 506 parents--verbose:
501 507 parents--verbose:
502 508 parents--verbose:
503 509 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
504 510 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
505 511 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
506 512 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
507 513 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
508 514 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
509 515 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
510 516 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
511 517 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
512 518 rev: 8
513 519 rev: 7
514 520 rev: 6
515 521 rev: 5
516 522 rev: 4
517 523 rev: 3
518 524 rev: 2
519 525 rev: 1
520 526 rev: 0
521 527 rev--verbose: 8
522 528 rev--verbose: 7
523 529 rev--verbose: 6
524 530 rev--verbose: 5
525 531 rev--verbose: 4
526 532 rev--verbose: 3
527 533 rev--verbose: 2
528 534 rev--verbose: 1
529 535 rev--verbose: 0
530 536 rev--debug: 8
531 537 rev--debug: 7
532 538 rev--debug: 6
533 539 rev--debug: 5
534 540 rev--debug: 4
535 541 rev--debug: 3
536 542 rev--debug: 2
537 543 rev--debug: 1
538 544 rev--debug: 0
539 545 tags: tip
540 546 tags:
541 547 tags:
542 548 tags:
543 549 tags:
544 550 tags:
545 551 tags:
546 552 tags:
547 553 tags:
548 554 tags--verbose: tip
549 555 tags--verbose:
550 556 tags--verbose:
551 557 tags--verbose:
552 558 tags--verbose:
553 559 tags--verbose:
554 560 tags--verbose:
555 561 tags--verbose:
556 562 tags--verbose:
557 563 tags--debug: tip
558 564 tags--debug:
559 565 tags--debug:
560 566 tags--debug:
561 567 tags--debug:
562 568 tags--debug:
563 569 tags--debug:
564 570 tags--debug:
565 571 tags--debug:
566 572 diffstat: 3: +2/-1
567 573 diffstat: 1: +1/-0
568 574 diffstat: 0: +0/-0
569 575 diffstat: 1: +1/-0
570 576 diffstat: 0: +0/-0
571 577 diffstat: 1: +1/-0
572 578 diffstat: 1: +4/-0
573 579 diffstat: 1: +2/-0
574 580 diffstat: 1: +1/-0
575 581 diffstat--verbose: 3: +2/-1
576 582 diffstat--verbose: 1: +1/-0
577 583 diffstat--verbose: 0: +0/-0
578 584 diffstat--verbose: 1: +1/-0
579 585 diffstat--verbose: 0: +0/-0
580 586 diffstat--verbose: 1: +1/-0
581 587 diffstat--verbose: 1: +4/-0
582 588 diffstat--verbose: 1: +2/-0
583 589 diffstat--verbose: 1: +1/-0
584 590 diffstat--debug: 3: +2/-1
585 591 diffstat--debug: 1: +1/-0
586 592 diffstat--debug: 0: +0/-0
587 593 diffstat--debug: 1: +1/-0
588 594 diffstat--debug: 0: +0/-0
589 595 diffstat--debug: 1: +1/-0
590 596 diffstat--debug: 1: +4/-0
591 597 diffstat--debug: 1: +2/-0
592 598 diffstat--debug: 1: +1/-0
593 599 extras: branch=default
594 600 extras: branch=default
595 601 extras: branch=default
596 602 extras: branch=default
597 603 extras: branch=foo
598 604 extras: branch=default
599 605 extras: branch=default
600 606 extras: branch=default
601 607 extras: branch=default
602 608 extras--verbose: branch=default
603 609 extras--verbose: branch=default
604 610 extras--verbose: branch=default
605 611 extras--verbose: branch=default
606 612 extras--verbose: branch=foo
607 613 extras--verbose: branch=default
608 614 extras--verbose: branch=default
609 615 extras--verbose: branch=default
610 616 extras--verbose: branch=default
611 617 extras--debug: branch=default
612 618 extras--debug: branch=default
613 619 extras--debug: branch=default
614 620 extras--debug: branch=default
615 621 extras--debug: branch=foo
616 622 extras--debug: branch=default
617 623 extras--debug: branch=default
618 624 extras--debug: branch=default
619 625 extras--debug: branch=default
620 626 p1rev: 7
621 627 p1rev: -1
622 628 p1rev: 5
623 629 p1rev: 3
624 630 p1rev: 3
625 631 p1rev: 2
626 632 p1rev: 1
627 633 p1rev: 0
628 634 p1rev: -1
629 635 p1rev--verbose: 7
630 636 p1rev--verbose: -1
631 637 p1rev--verbose: 5
632 638 p1rev--verbose: 3
633 639 p1rev--verbose: 3
634 640 p1rev--verbose: 2
635 641 p1rev--verbose: 1
636 642 p1rev--verbose: 0
637 643 p1rev--verbose: -1
638 644 p1rev--debug: 7
639 645 p1rev--debug: -1
640 646 p1rev--debug: 5
641 647 p1rev--debug: 3
642 648 p1rev--debug: 3
643 649 p1rev--debug: 2
644 650 p1rev--debug: 1
645 651 p1rev--debug: 0
646 652 p1rev--debug: -1
647 653 p2rev: -1
648 654 p2rev: -1
649 655 p2rev: 4
650 656 p2rev: -1
651 657 p2rev: -1
652 658 p2rev: -1
653 659 p2rev: -1
654 660 p2rev: -1
655 661 p2rev: -1
656 662 p2rev--verbose: -1
657 663 p2rev--verbose: -1
658 664 p2rev--verbose: 4
659 665 p2rev--verbose: -1
660 666 p2rev--verbose: -1
661 667 p2rev--verbose: -1
662 668 p2rev--verbose: -1
663 669 p2rev--verbose: -1
664 670 p2rev--verbose: -1
665 671 p2rev--debug: -1
666 672 p2rev--debug: -1
667 673 p2rev--debug: 4
668 674 p2rev--debug: -1
669 675 p2rev--debug: -1
670 676 p2rev--debug: -1
671 677 p2rev--debug: -1
672 678 p2rev--debug: -1
673 679 p2rev--debug: -1
674 680 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
675 681 p1node: 0000000000000000000000000000000000000000
676 682 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
677 683 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
678 684 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
679 685 p1node: 97054abb4ab824450e9164180baf491ae0078465
680 686 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
681 687 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
682 688 p1node: 0000000000000000000000000000000000000000
683 689 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
684 690 p1node--verbose: 0000000000000000000000000000000000000000
685 691 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
686 692 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
687 693 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
688 694 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
689 695 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
690 696 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
691 697 p1node--verbose: 0000000000000000000000000000000000000000
692 698 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
693 699 p1node--debug: 0000000000000000000000000000000000000000
694 700 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
695 701 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
696 702 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
697 703 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
698 704 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
699 705 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
700 706 p1node--debug: 0000000000000000000000000000000000000000
701 707 p2node: 0000000000000000000000000000000000000000
702 708 p2node: 0000000000000000000000000000000000000000
703 709 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
704 710 p2node: 0000000000000000000000000000000000000000
705 711 p2node: 0000000000000000000000000000000000000000
706 712 p2node: 0000000000000000000000000000000000000000
707 713 p2node: 0000000000000000000000000000000000000000
708 714 p2node: 0000000000000000000000000000000000000000
709 715 p2node: 0000000000000000000000000000000000000000
710 716 p2node--verbose: 0000000000000000000000000000000000000000
711 717 p2node--verbose: 0000000000000000000000000000000000000000
712 718 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
713 719 p2node--verbose: 0000000000000000000000000000000000000000
714 720 p2node--verbose: 0000000000000000000000000000000000000000
715 721 p2node--verbose: 0000000000000000000000000000000000000000
716 722 p2node--verbose: 0000000000000000000000000000000000000000
717 723 p2node--verbose: 0000000000000000000000000000000000000000
718 724 p2node--verbose: 0000000000000000000000000000000000000000
719 725 p2node--debug: 0000000000000000000000000000000000000000
720 726 p2node--debug: 0000000000000000000000000000000000000000
721 727 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
722 728 p2node--debug: 0000000000000000000000000000000000000000
723 729 p2node--debug: 0000000000000000000000000000000000000000
724 730 p2node--debug: 0000000000000000000000000000000000000000
725 731 p2node--debug: 0000000000000000000000000000000000000000
726 732 p2node--debug: 0000000000000000000000000000000000000000
727 733 p2node--debug: 0000000000000000000000000000000000000000
728 734 user: test
729 735 user: User Name <user@hostname>
730 736 user: person
731 737 user: person
732 738 user: person
733 739 user: person
734 740 user: other@place
735 741 user: A. N. Other <other@place>
736 742 user: User Name <user@hostname>
737 743 user--verbose: test
738 744 user--verbose: User Name <user@hostname>
739 745 user--verbose: person
740 746 user--verbose: person
741 747 user--verbose: person
742 748 user--verbose: person
743 749 user--verbose: other@place
744 750 user--verbose: A. N. Other <other@place>
745 751 user--verbose: User Name <user@hostname>
746 752 user--debug: test
747 753 user--debug: User Name <user@hostname>
748 754 user--debug: person
749 755 user--debug: person
750 756 user--debug: person
751 757 user--debug: person
752 758 user--debug: other@place
753 759 user--debug: A. N. Other <other@place>
754 760 user--debug: User Name <user@hostname>
755 761
756 762 Add a dummy commit to make up for the instability of the above:
757 763
758 764 $ echo a > a
759 765 $ hg add a
760 766 $ hg ci -m future
761 767
762 768 Add a commit that does all possible modifications at once
763 769
764 770 $ echo modify >> third
765 771 $ touch b
766 772 $ hg add b
767 773 $ hg mv fourth fifth
768 774 $ hg rm a
769 775 $ hg ci -m "Modify, add, remove, rename"
770 776
771 777 Test files list:
772 778
773 779 $ hg log -l1 -T '{join(file_mods, " ")}\n'
774 780 third
775 781 $ hg log -l1 -T '{file_mods % "{file}\n"}'
776 782 third
777 783 $ hg log -l1 -T '{file_mods % "{path}\n"}'
778 784 third
779 785
780 786 $ hg log -l1 -T '{join(files, " ")}\n'
781 787 a b fifth fourth third
782 788 $ hg log -l1 -T '{files % "{file}\n"}'
783 789 a
784 790 b
785 791 fifth
786 792 fourth
787 793 third
788 794 $ hg log -l1 -T '{files % "{path}\n"}'
789 795 a
790 796 b
791 797 fifth
792 798 fourth
793 799 third
794 800
795 801 Test file copies dict:
796 802
797 803 $ hg log -r8 -T '{join(file_copies, " ")}\n'
798 804 fourth (second)
799 805 $ hg log -r8 -T '{file_copies % "{name} <- {source}\n"}'
800 806 fourth <- second
801 807 $ hg log -r8 -T '{file_copies % "{path} <- {source}\n"}'
802 808 fourth <- second
803 809
804 810 $ hg log -r8 -T '{join(file_copies_switch, " ")}\n'
805 811
806 812 $ hg log -r8 -C -T '{join(file_copies_switch, " ")}\n'
807 813 fourth (second)
808 814 $ hg log -r8 -C -T '{file_copies_switch % "{name} <- {source}\n"}'
809 815 fourth <- second
810 816 $ hg log -r8 -C -T '{file_copies_switch % "{path} <- {source}\n"}'
811 817 fourth <- second
812 818
813 819 Test file attributes:
814 820
815 821 $ hg log -l1 -T '{files % "{status} {pad(size, 3, left=True)} {path}\n"}'
816 822 R a
817 823 A 0 b
818 824 A 7 fifth
819 825 R fourth
820 826 M 13 third
821 827
822 828 Test file status including clean ones:
823 829
824 830 $ hg log -r9 -T '{files("**") % "{status} {path}\n"}'
825 831 A a
826 832 C fourth
827 833 C third
828 834
829 835 Test index keyword:
830 836
831 837 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
832 838 10 0:a 1:b 2:fifth 3:fourth 4:third
833 839 11 0:a
834 840
835 841 $ hg branches -T '{index} {branch}\n'
836 842 0 default
837 843 1 foo
838 844
839 845 p1/p2 keywords:
840 846
841 847 $ hg log -r4:7 -GT '{rev} p1:{p1} p2:{p2} p1.rev:{p1.rev} p2.node:{p2.node}\n'
842 848 o 7 p1:-1:000000000000 p2:-1:000000000000 p1.rev:-1 p2.node:0000000000000000000000000000000000000000
843 849
844 850 o 6 p1:5:13207e5a10d9 p2:4:bbe44766e73d p1.rev:5 p2.node:bbe44766e73d5f11ed2177f1838de10c53ef3e74
845 851 |\
846 852 | o 5 p1:3:10e46f2dcbf4 p2:-1:000000000000 p1.rev:3 p2.node:0000000000000000000000000000000000000000
847 853 | |
848 854 | ~
849 855 o 4 p1:3:10e46f2dcbf4 p2:-1:000000000000 p1.rev:3 p2.node:0000000000000000000000000000000000000000
850 856 |
851 857 ~
852 858
853 859 TODO: no idea what should be displayed as a JSON representation
854 860 $ hg log -r6 -T 'p1:{p1|json}\np2:{p2|json}\n'
855 861 p1:{}
856 862 p2:{}
857 863
858 864 ui verbosity:
859 865
860 866 $ hg log -l1 -T '{verbosity}\n'
861 867
862 868 $ hg log -l1 -T '{verbosity}\n' --debug
863 869 debug
864 870 $ hg log -l1 -T '{verbosity}\n' --quiet
865 871 quiet
866 872 $ hg log -l1 -T '{verbosity}\n' --verbose
867 873 verbose
868 874
869 875 $ cd ..
870 876
871 877 latesttag:
872 878
873 879 $ hg init latesttag
874 880 $ cd latesttag
875 881
876 882 $ echo a > file
877 883 $ hg ci -Am a -d '0 0'
878 884 adding file
879 885
880 886 $ echo b >> file
881 887 $ hg ci -m b -d '1 0'
882 888
883 889 $ echo c >> head1
884 890 $ hg ci -Am h1c -d '2 0'
885 891 adding head1
886 892
887 893 $ hg update -q 1
888 894 $ echo d >> head2
889 895 $ hg ci -Am h2d -d '3 0'
890 896 adding head2
891 897 created new head
892 898
893 899 $ echo e >> head2
894 900 $ hg ci -m h2e -d '4 0'
895 901
896 902 $ hg merge -q
897 903 $ hg ci -m merge -d '5 -3600'
898 904
899 905 No tag set:
900 906
901 907 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
902 908 @ 5: null+5
903 909 |\
904 910 | o 4: null+4
905 911 | |
906 912 | o 3: null+3
907 913 | |
908 914 o | 2: null+3
909 915 |/
910 916 o 1: null+2
911 917 |
912 918 o 0: null+1
913 919
914 920
915 921 One common tag: longest path wins for {latesttagdistance}:
916 922
917 923 $ hg tag -r 1 -m t1 -d '6 0' t1
918 924 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
919 925 @ 6: t1+4
920 926 |
921 927 o 5: t1+3
922 928 |\
923 929 | o 4: t1+2
924 930 | |
925 931 | o 3: t1+1
926 932 | |
927 933 o | 2: t1+1
928 934 |/
929 935 o 1: t1+0
930 936 |
931 937 o 0: null+1
932 938
933 939
934 940 One ancestor tag: closest wins:
935 941
936 942 $ hg tag -r 2 -m t2 -d '7 0' t2
937 943 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
938 944 @ 7: t2+3
939 945 |
940 946 o 6: t2+2
941 947 |
942 948 o 5: t2+1
943 949 |\
944 950 | o 4: t1+2
945 951 | |
946 952 | o 3: t1+1
947 953 | |
948 954 o | 2: t2+0
949 955 |/
950 956 o 1: t1+0
951 957 |
952 958 o 0: null+1
953 959
954 960
955 961 Two branch tags: more recent wins if same number of changes:
956 962
957 963 $ hg tag -r 3 -m t3 -d '8 0' t3
958 964 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
959 965 @ 8: t3+5
960 966 |
961 967 o 7: t3+4
962 968 |
963 969 o 6: t3+3
964 970 |
965 971 o 5: t3+2
966 972 |\
967 973 | o 4: t3+1
968 974 | |
969 975 | o 3: t3+0
970 976 | |
971 977 o | 2: t2+0
972 978 |/
973 979 o 1: t1+0
974 980 |
975 981 o 0: null+1
976 982
977 983
978 984 Two branch tags: fewest changes wins:
979 985
980 986 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
981 987 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
982 988 @ 9: t4+5,6
983 989 |
984 990 o 8: t4+4,5
985 991 |
986 992 o 7: t4+3,4
987 993 |
988 994 o 6: t4+2,3
989 995 |
990 996 o 5: t4+1,2
991 997 |\
992 998 | o 4: t4+0,0
993 999 | |
994 1000 | o 3: t3+0,0
995 1001 | |
996 1002 o | 2: t2+0,0
997 1003 |/
998 1004 o 1: t1+0,0
999 1005 |
1000 1006 o 0: null+1,1
1001 1007
1002 1008
1003 1009 Merged tag overrides:
1004 1010
1005 1011 $ hg tag -r 5 -m t5 -d '9 0' t5
1006 1012 $ hg tag -r 3 -m at3 -d '10 0' at3
1007 1013 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
1008 1014 @ 11: t5+6
1009 1015 |
1010 1016 o 10: t5+5
1011 1017 |
1012 1018 o 9: t5+4
1013 1019 |
1014 1020 o 8: t5+3
1015 1021 |
1016 1022 o 7: t5+2
1017 1023 |
1018 1024 o 6: t5+1
1019 1025 |
1020 1026 o 5: t5+0
1021 1027 |\
1022 1028 | o 4: t4+0
1023 1029 | |
1024 1030 | o 3: at3:t3+0
1025 1031 | |
1026 1032 o | 2: t2+0
1027 1033 |/
1028 1034 o 1: t1+0
1029 1035 |
1030 1036 o 0: null+1
1031 1037
1032 1038
1033 1039 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
1034 1040 @ 11: t5+6,6
1035 1041 |
1036 1042 o 10: t5+5,5
1037 1043 |
1038 1044 o 9: t5+4,4
1039 1045 |
1040 1046 o 8: t5+3,3
1041 1047 |
1042 1048 o 7: t5+2,2
1043 1049 |
1044 1050 o 6: t5+1,1
1045 1051 |
1046 1052 o 5: t5+0,0
1047 1053 |\
1048 1054 | o 4: t4+0,0
1049 1055 | |
1050 1056 | o 3: at3+0,0 t3+0,0
1051 1057 | |
1052 1058 o | 2: t2+0,0
1053 1059 |/
1054 1060 o 1: t1+0,0
1055 1061 |
1056 1062 o 0: null+1,1
1057 1063
1058 1064
1059 1065 Tags of working-directory parents (issue6055):
1060 1066
1061 1067 $ hg update -q 3
1062 1068 $ echo a > head3
1063 1069 $ hg ci -qAm h3a
1064 1070 $ hg merge -q 2
1065 1071 $ hg log -Gr'::wdir()' -T "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
1066 1072 o 2147483647: at3+2,3 t3+2,3
1067 1073 |\
1068 1074 | @ 12: at3+1,1 t3+1,1
1069 1075 | |
1070 1076 | o 3: at3+0,0 t3+0,0
1071 1077 | |
1072 1078 @ | 2: t2+0,0
1073 1079 |/
1074 1080 o 1: t1+0,0
1075 1081 |
1076 1082 o 0: null+1,1
1077 1083
1078 1084
1079 1085 $ hg ci -m merge
1080 1086 $ hg log -Gr'::.' -T "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
1081 1087 @ 13: at3+2,3 t3+2,3
1082 1088 |\
1083 1089 | o 12: at3+1,1 t3+1,1
1084 1090 | |
1085 1091 | o 3: at3+0,0 t3+0,0
1086 1092 | |
1087 1093 o | 2: t2+0,0
1088 1094 |/
1089 1095 o 1: t1+0,0
1090 1096 |
1091 1097 o 0: null+1,1
1092 1098
1093 1099
1094 1100 $ cd ..
1095 1101
1096 1102 Set up repository containing template fragments in commit metadata:
1097 1103
1098 1104 $ hg init r
1099 1105 $ cd r
1100 1106 $ echo a > a
1101 1107 $ hg ci -Am '{rev}'
1102 1108 adding a
1103 1109
1104 1110 $ hg branch -q 'text.{rev}'
1105 1111 $ echo aa >> aa
1106 1112 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
1107 1113
1108 1114 Test termwidth:
1109 1115
1110 1116 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
1111 1117 bcc7ff960b8e:desc to be
1112 1118 termwidth.1:wrapped desc
1113 1119 termwidth.1:to be wrapped (no-eol)
1114 1120
1115 1121 Just one more commit:
1116 1122
1117 1123 $ echo b > b
1118 1124 $ hg ci -qAm b
1119 1125
1120 1126 Test 'originalnode'
1121 1127
1122 1128 $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n'
1123 1129 000000000000 bcc7ff960b8e
1124 1130 $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n'
1125 1131 a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1
1126 1132
1127 1133 Test active bookmark templating
1128 1134
1129 1135 $ hg book foo
1130 1136 $ hg book bar
1131 1137 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
1132 1138 2 bar* foo
1133 1139 1
1134 1140 0
1135 1141 $ hg log --template "{rev} {activebookmark}\n"
1136 1142 2 bar
1137 1143 1
1138 1144 0
1139 1145 $ hg bookmarks --inactive bar
1140 1146 $ hg log --template "{rev} {activebookmark}\n"
1141 1147 2
1142 1148 1
1143 1149 0
1144 1150 $ hg book -r1 baz
1145 1151 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
1146 1152 2 bar foo
1147 1153 1 baz
1148 1154 0
1149 1155 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
1150 1156 2 t
1151 1157 1 f
1152 1158 0 f
1153 1159
1154 1160 Test namespaces dict
1155 1161
1156 1162 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
1157 1163 2
1158 1164 bookmarks color=bookmark builtin=True
1159 1165 bar,foo
1160 1166 tags color=tag builtin=True
1161 1167 tip
1162 1168 branches color=branch builtin=True
1163 1169 text.{rev}
1164 1170 revnames color=revname builtin=False
1165 1171 r2
1166 1172
1167 1173 1
1168 1174 bookmarks color=bookmark builtin=True
1169 1175 baz
1170 1176 tags color=tag builtin=True
1171 1177
1172 1178 branches color=branch builtin=True
1173 1179 text.{rev}
1174 1180 revnames color=revname builtin=False
1175 1181 r1
1176 1182
1177 1183 0
1178 1184 bookmarks color=bookmark builtin=True
1179 1185
1180 1186 tags color=tag builtin=True
1181 1187
1182 1188 branches color=branch builtin=True
1183 1189 default
1184 1190 revnames color=revname builtin=False
1185 1191 r0
1186 1192
1187 1193 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
1188 1194 bookmarks: bar foo
1189 1195 tags: tip
1190 1196 branches: text.{rev}
1191 1197 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
1192 1198 bookmarks:
1193 1199 bar
1194 1200 foo
1195 1201 tags:
1196 1202 tip
1197 1203 branches:
1198 1204 text.{rev}
1199 1205 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
1200 1206 bar
1201 1207 foo
1202 1208 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
1203 1209 bar
1204 1210 foo
1205 1211
1206 1212 $ cd ..
1207 1213
1208 1214 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
1209 1215 printed graphwidths 3, 5, 7, etc. should all line up in their respective
1210 1216 columns. We don't care about other aspects of the graph rendering here.
1211 1217
1212 1218 $ hg init graphwidth
1213 1219 $ cd graphwidth
1214 1220
1215 1221 $ wrappabletext="a a a a a a a a a a a a"
1216 1222
1217 1223 $ printf "first\n" > file
1218 1224 $ hg add file
1219 1225 $ hg commit -m "$wrappabletext"
1220 1226
1221 1227 $ printf "first\nsecond\n" > file
1222 1228 $ hg commit -m "$wrappabletext"
1223 1229
1224 1230 $ hg checkout 0
1225 1231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1226 1232 $ printf "third\nfirst\n" > file
1227 1233 $ hg commit -m "$wrappabletext"
1228 1234 created new head
1229 1235
1230 1236 $ hg merge
1231 1237 merging file
1232 1238 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1233 1239 (branch merge, don't forget to commit)
1234 1240
1235 1241 $ hg log --graph -T "{graphwidth}"
1236 1242 @ 3
1237 1243 |
1238 1244 | @ 5
1239 1245 |/
1240 1246 o 3
1241 1247
1242 1248 $ hg commit -m "$wrappabletext"
1243 1249
1244 1250 $ hg log --graph -T "{graphwidth}"
1245 1251 @ 5
1246 1252 |\
1247 1253 | o 5
1248 1254 | |
1249 1255 o | 5
1250 1256 |/
1251 1257 o 3
1252 1258
1253 1259
1254 1260 $ hg checkout 0
1255 1261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1256 1262 $ printf "third\nfirst\nsecond\n" > file
1257 1263 $ hg commit -m "$wrappabletext"
1258 1264 created new head
1259 1265
1260 1266 $ hg log --graph -T "{graphwidth}"
1261 1267 @ 3
1262 1268 |
1263 1269 | o 7
1264 1270 | |\
1265 1271 +---o 7
1266 1272 | |
1267 1273 | o 5
1268 1274 |/
1269 1275 o 3
1270 1276
1271 1277
1272 1278 $ hg log --graph -T "{graphwidth}" -r 3
1273 1279 o 5
1274 1280 |\
1275 1281 ~ ~
1276 1282
1277 1283 $ hg log --graph -T "{graphwidth}" -r 1
1278 1284 o 3
1279 1285 |
1280 1286 ~
1281 1287
1282 1288 $ hg merge
1283 1289 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1284 1290 (branch merge, don't forget to commit)
1285 1291 $ hg commit -m "$wrappabletext"
1286 1292
1287 1293 $ printf "seventh\n" >> file
1288 1294 $ hg commit -m "$wrappabletext"
1289 1295
1290 1296 $ hg log --graph -T "{graphwidth}"
1291 1297 @ 3
1292 1298 |
1293 1299 o 5
1294 1300 |\
1295 1301 | o 5
1296 1302 | |
1297 1303 o | 7
1298 1304 |\ \
1299 1305 | o | 7
1300 1306 | |/
1301 1307 o / 5
1302 1308 |/
1303 1309 o 3
1304 1310
1305 1311
1306 1312 The point of graphwidth is to allow wrapping that accounts for the space taken
1307 1313 by the graph.
1308 1314
1309 1315 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
1310 1316 @ a a a a
1311 1317 | a a a a
1312 1318 | a a a a
1313 1319 o a a a
1314 1320 |\ a a a
1315 1321 | | a a a
1316 1322 | | a a a
1317 1323 | o a a a
1318 1324 | | a a a
1319 1325 | | a a a
1320 1326 | | a a a
1321 1327 o | a a
1322 1328 |\ \ a a
1323 1329 | | | a a
1324 1330 | | | a a
1325 1331 | | | a a
1326 1332 | | | a a
1327 1333 | o | a a
1328 1334 | |/ a a
1329 1335 | | a a
1330 1336 | | a a
1331 1337 | | a a
1332 1338 | | a a
1333 1339 o | a a a
1334 1340 |/ a a a
1335 1341 | a a a
1336 1342 | a a a
1337 1343 o a a a a
1338 1344 a a a a
1339 1345 a a a a
1340 1346
1341 1347 Something tricky happens when there are elided nodes; the next drawn row of
1342 1348 edges can be more than one column wider, but the graph width only increases by
1343 1349 one column. The remaining columns are added in between the nodes.
1344 1350
1345 1351 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
1346 1352 o 5
1347 1353 |\
1348 1354 | \
1349 1355 | :\
1350 1356 o : : 7
1351 1357 :/ /
1352 1358 : o 5
1353 1359 :/
1354 1360 o 3
1355 1361
1356 1362
1357 1363 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now