##// END OF EJS Templates
templatekw: fix extras, manifest and showlist args (issue1989)...
Patrick Mezard -
r10260:fe699ca0 default
parent child Browse files
Show More
@@ -1,1173 +1,1174
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 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, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from i18n import _
10 10 import os, sys, errno, re, glob, tempfile, time
11 11 import mdiff, bdiff, util, templater, patch, error, encoding, templatekw
12 12 import match as _match
13 13
14 14 revrangesep = ':'
15 15
16 16 def findpossible(cmd, table, strict=False):
17 17 """
18 18 Return cmd -> (aliases, command table entry)
19 19 for each matching command.
20 20 Return debug commands (or their aliases) only if no normal command matches.
21 21 """
22 22 choice = {}
23 23 debugchoice = {}
24 24 for e in table.keys():
25 25 aliases = e.lstrip("^").split("|")
26 26 found = None
27 27 if cmd in aliases:
28 28 found = cmd
29 29 elif not strict:
30 30 for a in aliases:
31 31 if a.startswith(cmd):
32 32 found = a
33 33 break
34 34 if found is not None:
35 35 if aliases[0].startswith("debug") or found.startswith("debug"):
36 36 debugchoice[found] = (aliases, table[e])
37 37 else:
38 38 choice[found] = (aliases, table[e])
39 39
40 40 if not choice and debugchoice:
41 41 choice = debugchoice
42 42
43 43 return choice
44 44
45 45 def findcmd(cmd, table, strict=True):
46 46 """Return (aliases, command table entry) for command string."""
47 47 choice = findpossible(cmd, table, strict)
48 48
49 49 if cmd in choice:
50 50 return choice[cmd]
51 51
52 52 if len(choice) > 1:
53 53 clist = choice.keys()
54 54 clist.sort()
55 55 raise error.AmbiguousCommand(cmd, clist)
56 56
57 57 if choice:
58 58 return choice.values()[0]
59 59
60 60 raise error.UnknownCommand(cmd)
61 61
62 62 def bail_if_changed(repo):
63 63 if repo.dirstate.parents()[1] != nullid:
64 64 raise util.Abort(_('outstanding uncommitted merge'))
65 65 modified, added, removed, deleted = repo.status()[:4]
66 66 if modified or added or removed or deleted:
67 67 raise util.Abort(_("outstanding uncommitted changes"))
68 68
69 69 def logmessage(opts):
70 70 """ get the log message according to -m and -l option """
71 71 message = opts.get('message')
72 72 logfile = opts.get('logfile')
73 73
74 74 if message and logfile:
75 75 raise util.Abort(_('options --message and --logfile are mutually '
76 76 'exclusive'))
77 77 if not message and logfile:
78 78 try:
79 79 if logfile == '-':
80 80 message = sys.stdin.read()
81 81 else:
82 82 message = open(logfile).read()
83 83 except IOError, inst:
84 84 raise util.Abort(_("can't read commit message '%s': %s") %
85 85 (logfile, inst.strerror))
86 86 return message
87 87
88 88 def loglimit(opts):
89 89 """get the log limit according to option -l/--limit"""
90 90 limit = opts.get('limit')
91 91 if limit:
92 92 try:
93 93 limit = int(limit)
94 94 except ValueError:
95 95 raise util.Abort(_('limit must be a positive integer'))
96 96 if limit <= 0: raise util.Abort(_('limit must be positive'))
97 97 else:
98 98 limit = None
99 99 return limit
100 100
101 101 def remoteui(src, opts):
102 102 'build a remote ui from ui or repo and opts'
103 103 if hasattr(src, 'baseui'): # looks like a repository
104 104 dst = src.baseui.copy() # drop repo-specific config
105 105 src = src.ui # copy target options from repo
106 106 else: # assume it's a global ui object
107 107 dst = src.copy() # keep all global options
108 108
109 109 # copy ssh-specific options
110 110 for o in 'ssh', 'remotecmd':
111 111 v = opts.get(o) or src.config('ui', o)
112 112 if v:
113 113 dst.setconfig("ui", o, v)
114 114
115 115 # copy bundle-specific options
116 116 r = src.config('bundle', 'mainreporoot')
117 117 if r:
118 118 dst.setconfig('bundle', 'mainreporoot', r)
119 119
120 120 # copy auth section settings
121 121 for key, val in src.configitems('auth'):
122 122 dst.setconfig('auth', key, val)
123 123
124 124 return dst
125 125
126 126 def revpair(repo, revs):
127 127 '''return pair of nodes, given list of revisions. second item can
128 128 be None, meaning use working dir.'''
129 129
130 130 def revfix(repo, val, defval):
131 131 if not val and val != 0 and defval is not None:
132 132 val = defval
133 133 return repo.lookup(val)
134 134
135 135 if not revs:
136 136 return repo.dirstate.parents()[0], None
137 137 end = None
138 138 if len(revs) == 1:
139 139 if revrangesep in revs[0]:
140 140 start, end = revs[0].split(revrangesep, 1)
141 141 start = revfix(repo, start, 0)
142 142 end = revfix(repo, end, len(repo) - 1)
143 143 else:
144 144 start = revfix(repo, revs[0], None)
145 145 elif len(revs) == 2:
146 146 if revrangesep in revs[0] or revrangesep in revs[1]:
147 147 raise util.Abort(_('too many revisions specified'))
148 148 start = revfix(repo, revs[0], None)
149 149 end = revfix(repo, revs[1], None)
150 150 else:
151 151 raise util.Abort(_('too many revisions specified'))
152 152 return start, end
153 153
154 154 def revrange(repo, revs):
155 155 """Yield revision as strings from a list of revision specifications."""
156 156
157 157 def revfix(repo, val, defval):
158 158 if not val and val != 0 and defval is not None:
159 159 return defval
160 160 return repo.changelog.rev(repo.lookup(val))
161 161
162 162 seen, l = set(), []
163 163 for spec in revs:
164 164 if revrangesep in spec:
165 165 start, end = spec.split(revrangesep, 1)
166 166 start = revfix(repo, start, 0)
167 167 end = revfix(repo, end, len(repo) - 1)
168 168 step = start > end and -1 or 1
169 169 for rev in xrange(start, end+step, step):
170 170 if rev in seen:
171 171 continue
172 172 seen.add(rev)
173 173 l.append(rev)
174 174 else:
175 175 rev = revfix(repo, spec, None)
176 176 if rev in seen:
177 177 continue
178 178 seen.add(rev)
179 179 l.append(rev)
180 180
181 181 return l
182 182
183 183 def make_filename(repo, pat, node,
184 184 total=None, seqno=None, revwidth=None, pathname=None):
185 185 node_expander = {
186 186 'H': lambda: hex(node),
187 187 'R': lambda: str(repo.changelog.rev(node)),
188 188 'h': lambda: short(node),
189 189 }
190 190 expander = {
191 191 '%': lambda: '%',
192 192 'b': lambda: os.path.basename(repo.root),
193 193 }
194 194
195 195 try:
196 196 if node:
197 197 expander.update(node_expander)
198 198 if node:
199 199 expander['r'] = (lambda:
200 200 str(repo.changelog.rev(node)).zfill(revwidth or 0))
201 201 if total is not None:
202 202 expander['N'] = lambda: str(total)
203 203 if seqno is not None:
204 204 expander['n'] = lambda: str(seqno)
205 205 if total is not None and seqno is not None:
206 206 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
207 207 if pathname is not None:
208 208 expander['s'] = lambda: os.path.basename(pathname)
209 209 expander['d'] = lambda: os.path.dirname(pathname) or '.'
210 210 expander['p'] = lambda: pathname
211 211
212 212 newname = []
213 213 patlen = len(pat)
214 214 i = 0
215 215 while i < patlen:
216 216 c = pat[i]
217 217 if c == '%':
218 218 i += 1
219 219 c = pat[i]
220 220 c = expander[c]()
221 221 newname.append(c)
222 222 i += 1
223 223 return ''.join(newname)
224 224 except KeyError, inst:
225 225 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
226 226 inst.args[0])
227 227
228 228 def make_file(repo, pat, node=None,
229 229 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
230 230
231 231 writable = 'w' in mode or 'a' in mode
232 232
233 233 if not pat or pat == '-':
234 234 return writable and sys.stdout or sys.stdin
235 235 if hasattr(pat, 'write') and writable:
236 236 return pat
237 237 if hasattr(pat, 'read') and 'r' in mode:
238 238 return pat
239 239 return open(make_filename(repo, pat, node, total, seqno, revwidth,
240 240 pathname),
241 241 mode)
242 242
243 243 def expandpats(pats):
244 244 if not util.expandglobs:
245 245 return list(pats)
246 246 ret = []
247 247 for p in pats:
248 248 kind, name = _match._patsplit(p, None)
249 249 if kind is None:
250 250 try:
251 251 globbed = glob.glob(name)
252 252 except re.error:
253 253 globbed = [name]
254 254 if globbed:
255 255 ret.extend(globbed)
256 256 continue
257 257 ret.append(p)
258 258 return ret
259 259
260 260 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
261 261 if not globbed and default == 'relpath':
262 262 pats = expandpats(pats or [])
263 263 m = _match.match(repo.root, repo.getcwd(), pats,
264 264 opts.get('include'), opts.get('exclude'), default)
265 265 def badfn(f, msg):
266 266 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
267 267 m.bad = badfn
268 268 return m
269 269
270 270 def matchall(repo):
271 271 return _match.always(repo.root, repo.getcwd())
272 272
273 273 def matchfiles(repo, files):
274 274 return _match.exact(repo.root, repo.getcwd(), files)
275 275
276 276 def findrenames(repo, added, removed, threshold):
277 277 '''find renamed files -- yields (before, after, score) tuples'''
278 278 copies = {}
279 279 ctx = repo['.']
280 280 for r in removed:
281 281 if r not in ctx:
282 282 continue
283 283 fctx = ctx.filectx(r)
284 284
285 285 def score(text):
286 286 if not len(text):
287 287 return 0.0
288 288 if not fctx.cmp(text):
289 289 return 1.0
290 290 if threshold == 1.0:
291 291 return 0.0
292 292 orig = fctx.data()
293 293 # bdiff.blocks() returns blocks of matching lines
294 294 # count the number of bytes in each
295 295 equal = 0
296 296 alines = mdiff.splitnewlines(text)
297 297 matches = bdiff.blocks(text, orig)
298 298 for x1, x2, y1, y2 in matches:
299 299 for line in alines[x1:x2]:
300 300 equal += len(line)
301 301
302 302 lengths = len(text) + len(orig)
303 303 return equal * 2.0 / lengths
304 304
305 305 for a in added:
306 306 bestscore = copies.get(a, (None, threshold))[1]
307 307 myscore = score(repo.wread(a))
308 308 if myscore >= bestscore:
309 309 copies[a] = (r, myscore)
310 310
311 311 for dest, v in copies.iteritems():
312 312 source, score = v
313 313 yield source, dest, score
314 314
315 315 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
316 316 if dry_run is None:
317 317 dry_run = opts.get('dry_run')
318 318 if similarity is None:
319 319 similarity = float(opts.get('similarity') or 0)
320 320 # we'd use status here, except handling of symlinks and ignore is tricky
321 321 added, unknown, deleted, removed = [], [], [], []
322 322 audit_path = util.path_auditor(repo.root)
323 323 m = match(repo, pats, opts)
324 324 for abs in repo.walk(m):
325 325 target = repo.wjoin(abs)
326 326 good = True
327 327 try:
328 328 audit_path(abs)
329 329 except:
330 330 good = False
331 331 rel = m.rel(abs)
332 332 exact = m.exact(abs)
333 333 if good and abs not in repo.dirstate:
334 334 unknown.append(abs)
335 335 if repo.ui.verbose or not exact:
336 336 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
337 337 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
338 338 or (os.path.isdir(target) and not os.path.islink(target))):
339 339 deleted.append(abs)
340 340 if repo.ui.verbose or not exact:
341 341 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
342 342 # for finding renames
343 343 elif repo.dirstate[abs] == 'r':
344 344 removed.append(abs)
345 345 elif repo.dirstate[abs] == 'a':
346 346 added.append(abs)
347 347 if not dry_run:
348 348 repo.remove(deleted)
349 349 repo.add(unknown)
350 350 if similarity > 0:
351 351 for old, new, score in findrenames(repo, added + unknown,
352 352 removed + deleted, similarity):
353 353 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
354 354 repo.ui.status(_('recording removal of %s as rename to %s '
355 355 '(%d%% similar)\n') %
356 356 (m.rel(old), m.rel(new), score * 100))
357 357 if not dry_run:
358 358 repo.copy(old, new)
359 359
360 360 def copy(ui, repo, pats, opts, rename=False):
361 361 # called with the repo lock held
362 362 #
363 363 # hgsep => pathname that uses "/" to separate directories
364 364 # ossep => pathname that uses os.sep to separate directories
365 365 cwd = repo.getcwd()
366 366 targets = {}
367 367 after = opts.get("after")
368 368 dryrun = opts.get("dry_run")
369 369
370 370 def walkpat(pat):
371 371 srcs = []
372 372 m = match(repo, [pat], opts, globbed=True)
373 373 for abs in repo.walk(m):
374 374 state = repo.dirstate[abs]
375 375 rel = m.rel(abs)
376 376 exact = m.exact(abs)
377 377 if state in '?r':
378 378 if exact and state == '?':
379 379 ui.warn(_('%s: not copying - file is not managed\n') % rel)
380 380 if exact and state == 'r':
381 381 ui.warn(_('%s: not copying - file has been marked for'
382 382 ' remove\n') % rel)
383 383 continue
384 384 # abs: hgsep
385 385 # rel: ossep
386 386 srcs.append((abs, rel, exact))
387 387 return srcs
388 388
389 389 # abssrc: hgsep
390 390 # relsrc: ossep
391 391 # otarget: ossep
392 392 def copyfile(abssrc, relsrc, otarget, exact):
393 393 abstarget = util.canonpath(repo.root, cwd, otarget)
394 394 reltarget = repo.pathto(abstarget, cwd)
395 395 target = repo.wjoin(abstarget)
396 396 src = repo.wjoin(abssrc)
397 397 state = repo.dirstate[abstarget]
398 398
399 399 # check for collisions
400 400 prevsrc = targets.get(abstarget)
401 401 if prevsrc is not None:
402 402 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
403 403 (reltarget, repo.pathto(abssrc, cwd),
404 404 repo.pathto(prevsrc, cwd)))
405 405 return
406 406
407 407 # check for overwrites
408 408 exists = os.path.exists(target)
409 409 if not after and exists or after and state in 'mn':
410 410 if not opts['force']:
411 411 ui.warn(_('%s: not overwriting - file exists\n') %
412 412 reltarget)
413 413 return
414 414
415 415 if after:
416 416 if not exists:
417 417 return
418 418 elif not dryrun:
419 419 try:
420 420 if exists:
421 421 os.unlink(target)
422 422 targetdir = os.path.dirname(target) or '.'
423 423 if not os.path.isdir(targetdir):
424 424 os.makedirs(targetdir)
425 425 util.copyfile(src, target)
426 426 except IOError, inst:
427 427 if inst.errno == errno.ENOENT:
428 428 ui.warn(_('%s: deleted in working copy\n') % relsrc)
429 429 else:
430 430 ui.warn(_('%s: cannot copy - %s\n') %
431 431 (relsrc, inst.strerror))
432 432 return True # report a failure
433 433
434 434 if ui.verbose or not exact:
435 435 if rename:
436 436 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
437 437 else:
438 438 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
439 439
440 440 targets[abstarget] = abssrc
441 441
442 442 # fix up dirstate
443 443 origsrc = repo.dirstate.copied(abssrc) or abssrc
444 444 if abstarget == origsrc: # copying back a copy?
445 445 if state not in 'mn' and not dryrun:
446 446 repo.dirstate.normallookup(abstarget)
447 447 else:
448 448 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
449 449 if not ui.quiet:
450 450 ui.warn(_("%s has not been committed yet, so no copy "
451 451 "data will be stored for %s.\n")
452 452 % (repo.pathto(origsrc, cwd), reltarget))
453 453 if repo.dirstate[abstarget] in '?r' and not dryrun:
454 454 repo.add([abstarget])
455 455 elif not dryrun:
456 456 repo.copy(origsrc, abstarget)
457 457
458 458 if rename and not dryrun:
459 459 repo.remove([abssrc], not after)
460 460
461 461 # pat: ossep
462 462 # dest ossep
463 463 # srcs: list of (hgsep, hgsep, ossep, bool)
464 464 # return: function that takes hgsep and returns ossep
465 465 def targetpathfn(pat, dest, srcs):
466 466 if os.path.isdir(pat):
467 467 abspfx = util.canonpath(repo.root, cwd, pat)
468 468 abspfx = util.localpath(abspfx)
469 469 if destdirexists:
470 470 striplen = len(os.path.split(abspfx)[0])
471 471 else:
472 472 striplen = len(abspfx)
473 473 if striplen:
474 474 striplen += len(os.sep)
475 475 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
476 476 elif destdirexists:
477 477 res = lambda p: os.path.join(dest,
478 478 os.path.basename(util.localpath(p)))
479 479 else:
480 480 res = lambda p: dest
481 481 return res
482 482
483 483 # pat: ossep
484 484 # dest ossep
485 485 # srcs: list of (hgsep, hgsep, ossep, bool)
486 486 # return: function that takes hgsep and returns ossep
487 487 def targetpathafterfn(pat, dest, srcs):
488 488 if _match.patkind(pat):
489 489 # a mercurial pattern
490 490 res = lambda p: os.path.join(dest,
491 491 os.path.basename(util.localpath(p)))
492 492 else:
493 493 abspfx = util.canonpath(repo.root, cwd, pat)
494 494 if len(abspfx) < len(srcs[0][0]):
495 495 # A directory. Either the target path contains the last
496 496 # component of the source path or it does not.
497 497 def evalpath(striplen):
498 498 score = 0
499 499 for s in srcs:
500 500 t = os.path.join(dest, util.localpath(s[0])[striplen:])
501 501 if os.path.exists(t):
502 502 score += 1
503 503 return score
504 504
505 505 abspfx = util.localpath(abspfx)
506 506 striplen = len(abspfx)
507 507 if striplen:
508 508 striplen += len(os.sep)
509 509 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
510 510 score = evalpath(striplen)
511 511 striplen1 = len(os.path.split(abspfx)[0])
512 512 if striplen1:
513 513 striplen1 += len(os.sep)
514 514 if evalpath(striplen1) > score:
515 515 striplen = striplen1
516 516 res = lambda p: os.path.join(dest,
517 517 util.localpath(p)[striplen:])
518 518 else:
519 519 # a file
520 520 if destdirexists:
521 521 res = lambda p: os.path.join(dest,
522 522 os.path.basename(util.localpath(p)))
523 523 else:
524 524 res = lambda p: dest
525 525 return res
526 526
527 527
528 528 pats = expandpats(pats)
529 529 if not pats:
530 530 raise util.Abort(_('no source or destination specified'))
531 531 if len(pats) == 1:
532 532 raise util.Abort(_('no destination specified'))
533 533 dest = pats.pop()
534 534 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
535 535 if not destdirexists:
536 536 if len(pats) > 1 or _match.patkind(pats[0]):
537 537 raise util.Abort(_('with multiple sources, destination must be an '
538 538 'existing directory'))
539 539 if util.endswithsep(dest):
540 540 raise util.Abort(_('destination %s is not a directory') % dest)
541 541
542 542 tfn = targetpathfn
543 543 if after:
544 544 tfn = targetpathafterfn
545 545 copylist = []
546 546 for pat in pats:
547 547 srcs = walkpat(pat)
548 548 if not srcs:
549 549 continue
550 550 copylist.append((tfn(pat, dest, srcs), srcs))
551 551 if not copylist:
552 552 raise util.Abort(_('no files to copy'))
553 553
554 554 errors = 0
555 555 for targetpath, srcs in copylist:
556 556 for abssrc, relsrc, exact in srcs:
557 557 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
558 558 errors += 1
559 559
560 560 if errors:
561 561 ui.warn(_('(consider using --after)\n'))
562 562
563 563 return errors
564 564
565 565 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
566 566 runargs=None, appendpid=False):
567 567 '''Run a command as a service.'''
568 568
569 569 if opts['daemon'] and not opts['daemon_pipefds']:
570 570 # Signal child process startup with file removal
571 571 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
572 572 os.close(lockfd)
573 573 try:
574 574 if not runargs:
575 575 runargs = util.hgcmd() + sys.argv[1:]
576 576 runargs.append('--daemon-pipefds=%s' % lockpath)
577 577 # Don't pass --cwd to the child process, because we've already
578 578 # changed directory.
579 579 for i in xrange(1,len(runargs)):
580 580 if runargs[i].startswith('--cwd='):
581 581 del runargs[i]
582 582 break
583 583 elif runargs[i].startswith('--cwd'):
584 584 del runargs[i:i+2]
585 585 break
586 586 pid = util.spawndetached(runargs)
587 587 while os.path.exists(lockpath):
588 588 time.sleep(0.1)
589 589 finally:
590 590 try:
591 591 os.unlink(lockpath)
592 592 except OSError, e:
593 593 if e.errno != errno.ENOENT:
594 594 raise
595 595 if parentfn:
596 596 return parentfn(pid)
597 597 else:
598 598 return
599 599
600 600 if initfn:
601 601 initfn()
602 602
603 603 if opts['pid_file']:
604 604 mode = appendpid and 'a' or 'w'
605 605 fp = open(opts['pid_file'], mode)
606 606 fp.write(str(os.getpid()) + '\n')
607 607 fp.close()
608 608
609 609 if opts['daemon_pipefds']:
610 610 lockpath = opts['daemon_pipefds']
611 611 try:
612 612 os.setsid()
613 613 except AttributeError:
614 614 pass
615 615 os.unlink(lockpath)
616 616 util.hidewindow()
617 617 sys.stdout.flush()
618 618 sys.stderr.flush()
619 619
620 620 nullfd = os.open(util.nulldev, os.O_RDWR)
621 621 logfilefd = nullfd
622 622 if logfile:
623 623 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
624 624 os.dup2(nullfd, 0)
625 625 os.dup2(logfilefd, 1)
626 626 os.dup2(logfilefd, 2)
627 627 if nullfd not in (0, 1, 2):
628 628 os.close(nullfd)
629 629 if logfile and logfilefd not in (0, 1, 2):
630 630 os.close(logfilefd)
631 631
632 632 if runfn:
633 633 return runfn()
634 634
635 635 class changeset_printer(object):
636 636 '''show changeset information when templating not requested.'''
637 637
638 638 def __init__(self, ui, repo, patch, diffopts, buffered):
639 639 self.ui = ui
640 640 self.repo = repo
641 641 self.buffered = buffered
642 642 self.patch = patch
643 643 self.diffopts = diffopts
644 644 self.header = {}
645 645 self.hunk = {}
646 646 self.lastheader = None
647 647 self.footer = None
648 648
649 649 def flush(self, rev):
650 650 if rev in self.header:
651 651 h = self.header[rev]
652 652 if h != self.lastheader:
653 653 self.lastheader = h
654 654 self.ui.write(h)
655 655 del self.header[rev]
656 656 if rev in self.hunk:
657 657 self.ui.write(self.hunk[rev])
658 658 del self.hunk[rev]
659 659 return 1
660 660 return 0
661 661
662 662 def close(self):
663 663 if self.footer:
664 664 self.ui.write(self.footer)
665 665
666 666 def show(self, ctx, copies=None, **props):
667 667 if self.buffered:
668 668 self.ui.pushbuffer()
669 669 self._show(ctx, copies, props)
670 670 self.hunk[ctx.rev()] = self.ui.popbuffer()
671 671 else:
672 672 self._show(ctx, copies, props)
673 673
674 674 def _show(self, ctx, copies, props):
675 675 '''show a single changeset or file revision'''
676 676 changenode = ctx.node()
677 677 rev = ctx.rev()
678 678
679 679 if self.ui.quiet:
680 680 self.ui.write("%d:%s\n" % (rev, short(changenode)))
681 681 return
682 682
683 683 log = self.repo.changelog
684 684 date = util.datestr(ctx.date())
685 685
686 686 hexfunc = self.ui.debugflag and hex or short
687 687
688 688 parents = [(p, hexfunc(log.node(p)))
689 689 for p in self._meaningful_parentrevs(log, rev)]
690 690
691 691 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
692 692
693 693 branch = ctx.branch()
694 694 # don't show the default branch name
695 695 if branch != 'default':
696 696 branch = encoding.tolocal(branch)
697 697 self.ui.write(_("branch: %s\n") % branch)
698 698 for tag in self.repo.nodetags(changenode):
699 699 self.ui.write(_("tag: %s\n") % tag)
700 700 for parent in parents:
701 701 self.ui.write(_("parent: %d:%s\n") % parent)
702 702
703 703 if self.ui.debugflag:
704 704 mnode = ctx.manifestnode()
705 705 self.ui.write(_("manifest: %d:%s\n") %
706 706 (self.repo.manifest.rev(mnode), hex(mnode)))
707 707 self.ui.write(_("user: %s\n") % ctx.user())
708 708 self.ui.write(_("date: %s\n") % date)
709 709
710 710 if self.ui.debugflag:
711 711 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
712 712 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
713 713 files):
714 714 if value:
715 715 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
716 716 elif ctx.files() and self.ui.verbose:
717 717 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
718 718 if copies and self.ui.verbose:
719 719 copies = ['%s (%s)' % c for c in copies]
720 720 self.ui.write(_("copies: %s\n") % ' '.join(copies))
721 721
722 722 extra = ctx.extra()
723 723 if extra and self.ui.debugflag:
724 724 for key, value in sorted(extra.items()):
725 725 self.ui.write(_("extra: %s=%s\n")
726 726 % (key, value.encode('string_escape')))
727 727
728 728 description = ctx.description().strip()
729 729 if description:
730 730 if self.ui.verbose:
731 731 self.ui.write(_("description:\n"))
732 732 self.ui.write(description)
733 733 self.ui.write("\n\n")
734 734 else:
735 735 self.ui.write(_("summary: %s\n") %
736 736 description.splitlines()[0])
737 737 self.ui.write("\n")
738 738
739 739 self.showpatch(changenode)
740 740
741 741 def showpatch(self, node):
742 742 if self.patch:
743 743 prev = self.repo.changelog.parents(node)[0]
744 744 chunks = patch.diff(self.repo, prev, node, match=self.patch,
745 745 opts=patch.diffopts(self.ui, self.diffopts))
746 746 for chunk in chunks:
747 747 self.ui.write(chunk)
748 748 self.ui.write("\n")
749 749
750 750 def _meaningful_parentrevs(self, log, rev):
751 751 """Return list of meaningful (or all if debug) parentrevs for rev.
752 752
753 753 For merges (two non-nullrev revisions) both parents are meaningful.
754 754 Otherwise the first parent revision is considered meaningful if it
755 755 is not the preceding revision.
756 756 """
757 757 parents = log.parentrevs(rev)
758 758 if not self.ui.debugflag and parents[1] == nullrev:
759 759 if parents[0] >= rev - 1:
760 760 parents = []
761 761 else:
762 762 parents = [parents[0]]
763 763 return parents
764 764
765 765
766 766 class changeset_templater(changeset_printer):
767 767 '''format changeset information.'''
768 768
769 769 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
770 770 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
771 771 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
772 772 defaulttempl = {
773 773 'parent': '{rev}:{node|formatnode} ',
774 774 'manifest': '{rev}:{node|formatnode}',
775 775 'file_copy': '{name} ({source})',
776 776 'extra': '{key}={value|stringescape}'
777 777 }
778 778 # filecopy is preserved for compatibility reasons
779 779 defaulttempl['filecopy'] = defaulttempl['file_copy']
780 780 self.t = templater.templater(mapfile, {'formatnode': formatnode},
781 781 cache=defaulttempl)
782 782 self.cache = {}
783 783
784 784 def use_template(self, t):
785 785 '''set template string to use'''
786 786 self.t.cache['changeset'] = t
787 787
788 788 def _meaningful_parentrevs(self, ctx):
789 789 """Return list of meaningful (or all if debug) parentrevs for rev.
790 790 """
791 791 parents = ctx.parents()
792 792 if len(parents) > 1:
793 793 return parents
794 794 if self.ui.debugflag:
795 795 return [parents[0], self.repo['null']]
796 796 if parents[0].rev() >= ctx.rev() - 1:
797 797 return []
798 798 return parents
799 799
800 800 def _show(self, ctx, copies, props):
801 801 '''show a single changeset or file revision'''
802 802
803 803 showlist = templatekw.showlist
804 804
805 805 # showparents() behaviour depends on ui trace level which
806 806 # causes unexpected behaviours at templating level and makes
807 807 # it harder to extract it in a standalone function. Its
808 808 # behaviour cannot be changed so leave it here for now.
809 def showparents(repo, ctx, templ, **args):
809 def showparents(**args):
810 ctx = args['ctx']
810 811 parents = [[('rev', p.rev()), ('node', p.hex())]
811 812 for p in self._meaningful_parentrevs(ctx)]
812 return showlist(templ, 'parent', parents, **args)
813 return showlist('parent', parents, **args)
813 814
814 815 props = props.copy()
815 816 props.update(templatekw.keywords)
816 817 props['parents'] = showparents
817 818 props['templ'] = self.t
818 819 props['ctx'] = ctx
819 820 props['repo'] = self.repo
820 821 props['revcache'] = {'copies': copies}
821 822 props['cache'] = self.cache
822 823
823 824 # find correct templates for current mode
824 825
825 826 tmplmodes = [
826 827 (True, None),
827 828 (self.ui.verbose, 'verbose'),
828 829 (self.ui.quiet, 'quiet'),
829 830 (self.ui.debugflag, 'debug'),
830 831 ]
831 832
832 833 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
833 834 for mode, postfix in tmplmodes:
834 835 for type in types:
835 836 cur = postfix and ('%s_%s' % (type, postfix)) or type
836 837 if mode and cur in self.t:
837 838 types[type] = cur
838 839
839 840 try:
840 841
841 842 # write header
842 843 if types['header']:
843 844 h = templater.stringify(self.t(types['header'], **props))
844 845 if self.buffered:
845 846 self.header[ctx.rev()] = h
846 847 else:
847 848 self.ui.write(h)
848 849
849 850 # write changeset metadata, then patch if requested
850 851 key = types['changeset']
851 852 self.ui.write(templater.stringify(self.t(key, **props)))
852 853 self.showpatch(ctx.node())
853 854
854 855 if types['footer']:
855 856 if not self.footer:
856 857 self.footer = templater.stringify(self.t(types['footer'],
857 858 **props))
858 859
859 860 except KeyError, inst:
860 861 msg = _("%s: no key named '%s'")
861 862 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
862 863 except SyntaxError, inst:
863 864 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
864 865
865 866 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
866 867 """show one changeset using template or regular display.
867 868
868 869 Display format will be the first non-empty hit of:
869 870 1. option 'template'
870 871 2. option 'style'
871 872 3. [ui] setting 'logtemplate'
872 873 4. [ui] setting 'style'
873 874 If all of these values are either the unset or the empty string,
874 875 regular display via changeset_printer() is done.
875 876 """
876 877 # options
877 878 patch = False
878 879 if opts.get('patch'):
879 880 patch = matchfn or matchall(repo)
880 881
881 882 tmpl = opts.get('template')
882 883 style = None
883 884 if tmpl:
884 885 tmpl = templater.parsestring(tmpl, quoted=False)
885 886 else:
886 887 style = opts.get('style')
887 888
888 889 # ui settings
889 890 if not (tmpl or style):
890 891 tmpl = ui.config('ui', 'logtemplate')
891 892 if tmpl:
892 893 tmpl = templater.parsestring(tmpl)
893 894 else:
894 895 style = util.expandpath(ui.config('ui', 'style', ''))
895 896
896 897 if not (tmpl or style):
897 898 return changeset_printer(ui, repo, patch, opts, buffered)
898 899
899 900 mapfile = None
900 901 if style and not tmpl:
901 902 mapfile = style
902 903 if not os.path.split(mapfile)[0]:
903 904 mapname = (templater.templatepath('map-cmdline.' + mapfile)
904 905 or templater.templatepath(mapfile))
905 906 if mapname: mapfile = mapname
906 907
907 908 try:
908 909 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
909 910 except SyntaxError, inst:
910 911 raise util.Abort(inst.args[0])
911 912 if tmpl: t.use_template(tmpl)
912 913 return t
913 914
914 915 def finddate(ui, repo, date):
915 916 """Find the tipmost changeset that matches the given date spec"""
916 917
917 918 df = util.matchdate(date)
918 919 m = matchall(repo)
919 920 results = {}
920 921
921 922 def prep(ctx, fns):
922 923 d = ctx.date()
923 924 if df(d[0]):
924 925 results[ctx.rev()] = d
925 926
926 927 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
927 928 rev = ctx.rev()
928 929 if rev in results:
929 930 ui.status(_("Found revision %s from %s\n") %
930 931 (rev, util.datestr(results[rev])))
931 932 return str(rev)
932 933
933 934 raise util.Abort(_("revision matching date not found"))
934 935
935 936 def walkchangerevs(repo, match, opts, prepare):
936 937 '''Iterate over files and the revs in which they changed.
937 938
938 939 Callers most commonly need to iterate backwards over the history
939 940 in which they are interested. Doing so has awful (quadratic-looking)
940 941 performance, so we use iterators in a "windowed" way.
941 942
942 943 We walk a window of revisions in the desired order. Within the
943 944 window, we first walk forwards to gather data, then in the desired
944 945 order (usually backwards) to display it.
945 946
946 947 This function returns an iterator yielding contexts. Before
947 948 yielding each context, the iterator will first call the prepare
948 949 function on each context in the window in forward order.'''
949 950
950 951 def increasing_windows(start, end, windowsize=8, sizelimit=512):
951 952 if start < end:
952 953 while start < end:
953 954 yield start, min(windowsize, end-start)
954 955 start += windowsize
955 956 if windowsize < sizelimit:
956 957 windowsize *= 2
957 958 else:
958 959 while start > end:
959 960 yield start, min(windowsize, start-end-1)
960 961 start -= windowsize
961 962 if windowsize < sizelimit:
962 963 windowsize *= 2
963 964
964 965 follow = opts.get('follow') or opts.get('follow_first')
965 966
966 967 if not len(repo):
967 968 return []
968 969
969 970 if follow:
970 971 defrange = '%s:0' % repo['.'].rev()
971 972 else:
972 973 defrange = '-1:0'
973 974 revs = revrange(repo, opts['rev'] or [defrange])
974 975 wanted = set()
975 976 slowpath = match.anypats() or (match.files() and opts.get('removed'))
976 977 fncache = {}
977 978 change = util.cachefunc(repo.changectx)
978 979
979 980 if not slowpath and not match.files():
980 981 # No files, no patterns. Display all revs.
981 982 wanted = set(revs)
982 983 copies = []
983 984
984 985 if not slowpath:
985 986 # Only files, no patterns. Check the history of each file.
986 987 def filerevgen(filelog, node):
987 988 cl_count = len(repo)
988 989 if node is None:
989 990 last = len(filelog) - 1
990 991 else:
991 992 last = filelog.rev(node)
992 993 for i, window in increasing_windows(last, nullrev):
993 994 revs = []
994 995 for j in xrange(i - window, i + 1):
995 996 n = filelog.node(j)
996 997 revs.append((filelog.linkrev(j),
997 998 follow and filelog.renamed(n)))
998 999 for rev in reversed(revs):
999 1000 # only yield rev for which we have the changelog, it can
1000 1001 # happen while doing "hg log" during a pull or commit
1001 1002 if rev[0] < cl_count:
1002 1003 yield rev
1003 1004 def iterfiles():
1004 1005 for filename in match.files():
1005 1006 yield filename, None
1006 1007 for filename_node in copies:
1007 1008 yield filename_node
1008 1009 minrev, maxrev = min(revs), max(revs)
1009 1010 for file_, node in iterfiles():
1010 1011 filelog = repo.file(file_)
1011 1012 if not len(filelog):
1012 1013 if node is None:
1013 1014 # A zero count may be a directory or deleted file, so
1014 1015 # try to find matching entries on the slow path.
1015 1016 if follow:
1016 1017 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1017 1018 slowpath = True
1018 1019 break
1019 1020 else:
1020 1021 continue
1021 1022 for rev, copied in filerevgen(filelog, node):
1022 1023 if rev <= maxrev:
1023 1024 if rev < minrev:
1024 1025 break
1025 1026 fncache.setdefault(rev, [])
1026 1027 fncache[rev].append(file_)
1027 1028 wanted.add(rev)
1028 1029 if follow and copied:
1029 1030 copies.append(copied)
1030 1031 if slowpath:
1031 1032 if follow:
1032 1033 raise util.Abort(_('can only follow copies/renames for explicit '
1033 1034 'filenames'))
1034 1035
1035 1036 # The slow path checks files modified in every changeset.
1036 1037 def changerevgen():
1037 1038 for i, window in increasing_windows(len(repo) - 1, nullrev):
1038 1039 for j in xrange(i - window, i + 1):
1039 1040 yield change(j)
1040 1041
1041 1042 for ctx in changerevgen():
1042 1043 matches = filter(match, ctx.files())
1043 1044 if matches:
1044 1045 fncache[ctx.rev()] = matches
1045 1046 wanted.add(ctx.rev())
1046 1047
1047 1048 class followfilter(object):
1048 1049 def __init__(self, onlyfirst=False):
1049 1050 self.startrev = nullrev
1050 1051 self.roots = set()
1051 1052 self.onlyfirst = onlyfirst
1052 1053
1053 1054 def match(self, rev):
1054 1055 def realparents(rev):
1055 1056 if self.onlyfirst:
1056 1057 return repo.changelog.parentrevs(rev)[0:1]
1057 1058 else:
1058 1059 return filter(lambda x: x != nullrev,
1059 1060 repo.changelog.parentrevs(rev))
1060 1061
1061 1062 if self.startrev == nullrev:
1062 1063 self.startrev = rev
1063 1064 return True
1064 1065
1065 1066 if rev > self.startrev:
1066 1067 # forward: all descendants
1067 1068 if not self.roots:
1068 1069 self.roots.add(self.startrev)
1069 1070 for parent in realparents(rev):
1070 1071 if parent in self.roots:
1071 1072 self.roots.add(rev)
1072 1073 return True
1073 1074 else:
1074 1075 # backwards: all parents
1075 1076 if not self.roots:
1076 1077 self.roots.update(realparents(self.startrev))
1077 1078 if rev in self.roots:
1078 1079 self.roots.remove(rev)
1079 1080 self.roots.update(realparents(rev))
1080 1081 return True
1081 1082
1082 1083 return False
1083 1084
1084 1085 # it might be worthwhile to do this in the iterator if the rev range
1085 1086 # is descending and the prune args are all within that range
1086 1087 for rev in opts.get('prune', ()):
1087 1088 rev = repo.changelog.rev(repo.lookup(rev))
1088 1089 ff = followfilter()
1089 1090 stop = min(revs[0], revs[-1])
1090 1091 for x in xrange(rev, stop-1, -1):
1091 1092 if ff.match(x):
1092 1093 wanted.discard(x)
1093 1094
1094 1095 def iterate():
1095 1096 if follow and not match.files():
1096 1097 ff = followfilter(onlyfirst=opts.get('follow_first'))
1097 1098 def want(rev):
1098 1099 return ff.match(rev) and rev in wanted
1099 1100 else:
1100 1101 def want(rev):
1101 1102 return rev in wanted
1102 1103
1103 1104 for i, window in increasing_windows(0, len(revs)):
1104 1105 change = util.cachefunc(repo.changectx)
1105 1106 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1106 1107 for rev in sorted(nrevs):
1107 1108 fns = fncache.get(rev)
1108 1109 ctx = change(rev)
1109 1110 if not fns:
1110 1111 def fns_generator():
1111 1112 for f in ctx.files():
1112 1113 if match(f):
1113 1114 yield f
1114 1115 fns = fns_generator()
1115 1116 prepare(ctx, fns)
1116 1117 for rev in nrevs:
1117 1118 yield change(rev)
1118 1119 return iterate()
1119 1120
1120 1121 def commit(ui, repo, commitfunc, pats, opts):
1121 1122 '''commit the specified files or all outstanding changes'''
1122 1123 date = opts.get('date')
1123 1124 if date:
1124 1125 opts['date'] = util.parsedate(date)
1125 1126 message = logmessage(opts)
1126 1127
1127 1128 # extract addremove carefully -- this function can be called from a command
1128 1129 # that doesn't support addremove
1129 1130 if opts.get('addremove'):
1130 1131 addremove(repo, pats, opts)
1131 1132
1132 1133 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1133 1134
1134 1135 def commiteditor(repo, ctx, subs):
1135 1136 if ctx.description():
1136 1137 return ctx.description()
1137 1138 return commitforceeditor(repo, ctx, subs)
1138 1139
1139 1140 def commitforceeditor(repo, ctx, subs):
1140 1141 edittext = []
1141 1142 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1142 1143 if ctx.description():
1143 1144 edittext.append(ctx.description())
1144 1145 edittext.append("")
1145 1146 edittext.append("") # Empty line between message and comments.
1146 1147 edittext.append(_("HG: Enter commit message."
1147 1148 " Lines beginning with 'HG:' are removed."))
1148 1149 edittext.append(_("HG: Leave message empty to abort commit."))
1149 1150 edittext.append("HG: --")
1150 1151 edittext.append(_("HG: user: %s") % ctx.user())
1151 1152 if ctx.p2():
1152 1153 edittext.append(_("HG: branch merge"))
1153 1154 if ctx.branch():
1154 1155 edittext.append(_("HG: branch '%s'")
1155 1156 % encoding.tolocal(ctx.branch()))
1156 1157 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1157 1158 edittext.extend([_("HG: added %s") % f for f in added])
1158 1159 edittext.extend([_("HG: changed %s") % f for f in modified])
1159 1160 edittext.extend([_("HG: removed %s") % f for f in removed])
1160 1161 if not added and not modified and not removed:
1161 1162 edittext.append(_("HG: no files changed"))
1162 1163 edittext.append("")
1163 1164 # run editor in the repository root
1164 1165 olddir = os.getcwd()
1165 1166 os.chdir(repo.root)
1166 1167 text = repo.ui.edit("\n".join(edittext), ctx.user())
1167 1168 text = re.sub("(?m)^HG:.*\n", "", text)
1168 1169 os.chdir(olddir)
1169 1170
1170 1171 if not text.strip():
1171 1172 raise util.Abort(_("empty commit message"))
1172 1173
1173 1174 return text
@@ -1,249 +1,264
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, incorporated herein by reference.
7 7
8 8 from node import hex
9 9 import encoding, patch, util, error
10 10
11 def showlist(templ, name, values, plural=None, **args):
11 def showlist(name, values, plural=None, **args):
12 12 '''expand set of values.
13 13 name is name of key in template map.
14 14 values is list of strings or dicts.
15 15 plural is plural of name, if not simply name + 's'.
16 16
17 17 expansion works like this, given name 'foo'.
18 18
19 19 if values is empty, expand 'no_foos'.
20 20
21 21 if 'foo' not in template map, return values as a string,
22 22 joined by space.
23 23
24 24 expand 'start_foos'.
25 25
26 26 for each value, expand 'foo'. if 'last_foo' in template
27 27 map, expand it instead of 'foo' for last key.
28 28
29 29 expand 'end_foos'.
30 30 '''
31 templ = args['templ']
31 32 if plural: names = plural
32 33 else: names = name + 's'
33 34 if not values:
34 35 noname = 'no_' + names
35 36 if noname in templ:
36 37 yield templ(noname, **args)
37 38 return
38 39 if name not in templ:
39 40 if isinstance(values[0], str):
40 41 yield ' '.join(values)
41 42 else:
42 43 for v in values:
43 44 yield dict(v, **args)
44 45 return
45 46 startname = 'start_' + names
46 47 if startname in templ:
47 48 yield templ(startname, **args)
48 49 vargs = args.copy()
49 50 def one(v, tag=name):
50 51 try:
51 52 vargs.update(v)
52 53 except (AttributeError, ValueError):
53 54 try:
54 55 for a, b in v:
55 56 vargs[a] = b
56 57 except ValueError:
57 58 vargs[name] = v
58 59 return templ(tag, **vargs)
59 60 lastname = 'last_' + name
60 61 if lastname in templ:
61 62 last = values.pop()
62 63 else:
63 64 last = None
64 65 for v in values:
65 66 yield one(v)
66 67 if last is not None:
67 68 yield one(last, tag=lastname)
68 69 endname = 'end_' + names
69 70 if endname in templ:
70 71 yield templ(endname, **args)
71 72
72 73 def getfiles(repo, ctx, revcache):
73 74 if 'files' not in revcache:
74 75 revcache['files'] = repo.status(ctx.parents()[0].node(),
75 76 ctx.node())[:3]
76 77 return revcache['files']
77 78
78 79 def getlatesttags(repo, ctx, cache):
79 80 '''return date, distance and name for the latest tag of rev'''
80 81
81 82 if 'latesttags' not in cache:
82 83 # Cache mapping from rev to a tuple with tag date, tag
83 84 # distance and tag name
84 85 cache['latesttags'] = {-1: (0, 0, 'null')}
85 86 latesttags = cache['latesttags']
86 87
87 88 rev = ctx.rev()
88 89 todo = [rev]
89 90 while todo:
90 91 rev = todo.pop()
91 92 if rev in latesttags:
92 93 continue
93 94 ctx = repo[rev]
94 95 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
95 96 if tags:
96 97 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
97 98 continue
98 99 try:
99 100 # The tuples are laid out so the right one can be found by
100 101 # comparison.
101 102 pdate, pdist, ptag = max(
102 103 latesttags[p.rev()] for p in ctx.parents())
103 104 except KeyError:
104 105 # Cache miss - recurse
105 106 todo.append(rev)
106 107 todo.extend(p.rev() for p in ctx.parents())
107 108 continue
108 109 latesttags[rev] = pdate, pdist + 1, ptag
109 110 return latesttags[rev]
110 111
111 112 def getrenamedfn(repo, endrev=None):
112 113 rcache = {}
113 114 if endrev is None:
114 115 endrev = len(repo)
115 116
116 117 def getrenamed(fn, rev):
117 118 '''looks up all renames for a file (up to endrev) the first
118 119 time the file is given. It indexes on the changerev and only
119 120 parses the manifest if linkrev != changerev.
120 121 Returns rename info for fn at changerev rev.'''
121 122 if fn not in rcache:
122 123 rcache[fn] = {}
123 124 fl = repo.file(fn)
124 125 for i in fl:
125 126 lr = fl.linkrev(i)
126 127 renamed = fl.renamed(fl.node(i))
127 128 rcache[fn][lr] = renamed
128 129 if lr >= endrev:
129 130 break
130 131 if rev in rcache[fn]:
131 132 return rcache[fn][rev]
132 133
133 134 # If linkrev != rev (i.e. rev not found in rcache) fallback to
134 135 # filectx logic.
135 136 try:
136 137 return repo[rev][fn].renamed()
137 138 except error.LookupError:
138 139 return None
139 140
140 141 return getrenamed
141 142
142 143
143 144 def showauthor(repo, ctx, templ, **args):
144 145 return ctx.user()
145 146
146 def showbranches(repo, ctx, templ, **args):
147 branch = ctx.branch()
147 def showbranches(**args):
148 branch = args['ctx'].branch()
148 149 if branch != 'default':
149 150 branch = encoding.tolocal(branch)
150 return showlist(templ, 'branch', [branch], plural='branches', **args)
151 return showlist('branch', [branch], plural='branches', **args)
151 152
152 153 def showdate(repo, ctx, templ, **args):
153 154 return ctx.date()
154 155
155 156 def showdescription(repo, ctx, templ, **args):
156 157 return ctx.description().strip()
157 158
158 159 def showdiffstat(repo, ctx, templ, **args):
159 160 diff = patch.diff(repo, ctx.parents()[0].node(), ctx.node())
160 161 files, adds, removes = 0, 0, 0
161 162 for i in patch.diffstatdata(util.iterlines(diff)):
162 163 files += 1
163 164 adds += i[1]
164 165 removes += i[2]
165 166 return '%s: +%s/-%s' % (files, adds, removes)
166 167
167 def showextras(repo, ctx, templ, **args):
168 for key, value in sorted(ctx.extra().items()):
168 def showextras(**args):
169 templ = args['templ']
170 for key, value in sorted(args['ctx'].extra().items()):
169 171 args = args.copy()
170 172 args.update(dict(key=key, value=value))
171 173 yield templ('extra', **args)
172 174
173 def showfileadds(repo, ctx, templ, revcache, **args):
174 return showlist(templ, 'file_add', getfiles(repo, ctx, revcache)[1], **args)
175 def showfileadds(**args):
176 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
177 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
175 178
176 def showfilecopies(repo, ctx, templ, cache, revcache, **args):
177 copies = revcache.get('copies')
179 def showfilecopies(**args):
180 cache, ctx= args['cache'], args['ctx']
181 copies = args['revcache'].get('copies')
178 182 if copies is None:
179 183 if 'getrenamed' not in cache:
180 cache['getrenamed'] = getrenamedfn(repo)
184 cache['getrenamed'] = getrenamedfn(args['repo'])
181 185 copies = []
182 186 getrenamed = cache['getrenamed']
183 187 for fn in ctx.files():
184 188 rename = getrenamed(fn, ctx.rev())
185 189 if rename:
186 190 copies.append((fn, rename[0]))
187 191
188 192 c = [{'name': x[0], 'source': x[1]} for x in copies]
189 return showlist(templ, 'file_copy', c, plural='file_copies', **args)
193 return showlist('file_copy', c, plural='file_copies', **args)
190 194
191 195 # showfilecopiesswitch() displays file copies only if copy records are
192 196 # provided before calling the templater, usually with a --copies
193 197 # command line switch.
194 def showfilecopiesswitch(repo, ctx, templ, cache, revcache, **args):
195 copies = revcache.get('copies') or []
198 def showfilecopiesswitch(**args):
199 copies = args['revcache'].get('copies') or []
196 200 c = [{'name': x[0], 'source': x[1]} for x in copies]
197 return showlist(templ, 'file_copy', c, plural='file_copies', **args)
201 return showlist('file_copy', c, plural='file_copies', **args)
198 202
199 def showfiledels(repo, ctx, templ, revcache, **args):
200 return showlist(templ, 'file_del', getfiles(repo, ctx, revcache)[2], **args)
203 def showfiledels(**args):
204 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
205 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
201 206
202 def showfilemods(repo, ctx, templ, revcache, **args):
203 return showlist(templ, 'file_mod', getfiles(repo, ctx, revcache)[0], **args)
207 def showfilemods(**args):
208 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
209 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
204 210
205 def showfiles(repo, ctx, templ, **args):
206 return showlist(templ, 'file', ctx.files(), **args)
211 def showfiles(**args):
212 return showlist('file', args['ctx'].files(), **args)
207 213
208 214 def showlatesttag(repo, ctx, templ, cache, **args):
209 215 return getlatesttags(repo, ctx, cache)[2]
210 216
211 217 def showlatesttagdistance(repo, ctx, templ, cache, **args):
212 218 return getlatesttags(repo, ctx, cache)[1]
213 219
214 def showmanifest(repo, ctx, templ, **args):
220 def showmanifest(**args):
221 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
215 222 args = args.copy()
216 223 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
217 224 node=hex(ctx.changeset()[0])))
218 225 return templ('manifest', **args)
219 226
220 227 def shownode(repo, ctx, templ, **args):
221 228 return ctx.hex()
222 229
223 230 def showrev(repo, ctx, templ, **args):
224 231 return ctx.rev()
225 232
226 def showtags(repo, ctx, templ, **args):
227 return showlist(templ, 'tag', ctx.tags(), **args)
233 def showtags(**args):
234 return showlist('tag', args['ctx'].tags(), **args)
228 235
236 # keywords are callables like:
237 # fn(repo, ctx, templ, cache, revcache, **args)
238 # with:
239 # repo - current repository instance
240 # ctx - the changectx being displayed
241 # templ - the templater instance
242 # cache - a cache dictionary for the whole templater run
243 # revcache - a cache dictionary for the current revision
229 244 keywords = {
230 245 'author': showauthor,
231 246 'branches': showbranches,
232 247 'date': showdate,
233 248 'desc': showdescription,
234 249 'diffstat': showdiffstat,
235 250 'extras': showextras,
236 251 'file_adds': showfileadds,
237 252 'file_copies': showfilecopies,
238 253 'file_copies_switch': showfilecopiesswitch,
239 254 'file_dels': showfiledels,
240 255 'file_mods': showfilemods,
241 256 'files': showfiles,
242 257 'latesttag': showlatesttag,
243 258 'latesttagdistance': showlatesttagdistance,
244 259 'manifest': showmanifest,
245 260 'node': shownode,
246 261 'rev': showrev,
247 262 'tags': showtags,
248 263 }
249 264
@@ -1,198 +1,207
1 1 #!/bin/sh
2 2
3 3 hg init a
4 4 cd a
5 5 echo a > a
6 6 hg add a
7 7 echo line 1 > b
8 8 echo line 2 >> b
9 9 hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
10 10 hg add b
11 11 echo other 1 > c
12 12 echo other 2 >> c
13 13 echo >> c
14 14 echo other 3 >> c
15 15 hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
16 16 hg add c
17 17 hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 18 echo c >> c
19 19 hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 20 echo foo > .hg/branch
21 21 hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 22 hg co -q 3
23 23 echo other 4 >> d
24 24 hg add d
25 25 hg commit -m 'new head' -d '1500000 0' -u 'person'
26 26 hg merge -q foo
27 27 hg commit -m 'merge' -d '1500001 0' -u 'person'
28 28 # second branch starting at nullrev
29 29 hg update null
30 30 echo second > second
31 31 hg add second
32 32 hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
33 33 echo third > third
34 34 hg add third
35 35 hg mv second fourth
36 36 hg commit -m third -d "2020-01-01 10:01"
37 37
38 38 # make sure user/global hgrc does not affect tests
39 39 echo '[ui]' > .hg/hgrc
40 40 echo 'logtemplate =' >> .hg/hgrc
41 41 echo 'style =' >> .hg/hgrc
42 42
43 43 echo '# default style is like normal output'
44 44 echo '# normal'
45 45 hg log > log.out
46 46 hg log --style default > style.out
47 47 cmp log.out style.out || diff -u log.out style.out
48 48 echo '# verbose'
49 49 hg log -v > log.out
50 50 hg log -v --style default > style.out
51 51 cmp log.out style.out || diff -u log.out style.out
52 52 echo '# debug'
53 53 hg log --debug > log.out
54 54 hg log --debug --style default > style.out
55 55 cmp log.out style.out || diff -u log.out style.out
56 56
57 57 echo '# revision with no copies (used to print a traceback)'
58 58 hg tip -v --template '\n'
59 59
60 60 echo '# compact style works'
61 61 hg log --style compact
62 62 hg log -v --style compact
63 63 hg log --debug --style compact
64 64
65 65 # Test xml styles
66 66 echo '# xml style works (--style xml)'
67 67 hg log --style xml
68 68 echo '# xml style works (-v --style xml)'
69 69 hg log -v --style xml
70 70 echo '# xml style works (--debug --style xml)'
71 71 hg log --debug --style xml
72 72
73 73 echo '# error if style not readable'
74 74 touch q
75 75 chmod 0 q
76 76 hg log --style ./q
77 77
78 78 echo '# error if no style'
79 79 hg log --style notexist
80 80
81 81 echo '# error if style missing key'
82 82 echo 'q = q' > t
83 83 hg log --style ./t
84 84
85 85 echo '# error if include fails'
86 86 echo 'changeset = q' >> t
87 87 hg log --style ./t
88 88
89 89 echo '# include works'
90 90 rm q
91 91 echo '{rev}' > q
92 92 hg log --style ./t
93 93
94 94 echo '# ui.style works'
95 95 echo '[ui]' > .hg/hgrc
96 96 echo 'style = t' >> .hg/hgrc
97 97 hg log
98 98
99 99 echo '# issue338'
100 100 hg log --style=changelog > changelog
101 101 cat changelog
102 102
103 103 echo "# keys work"
104 104 for key in author branches date desc file_adds file_dels file_mods \
105 105 file_copies file_copies_switch files \
106 106 manifest node parents rev tags diffstat extras; do
107 107 for mode in '' --verbose --debug; do
108 108 hg log $mode --template "$key$mode: {$key}\n"
109 109 done
110 110 done
111 111
112 112 echo '# filters work'
113 113 hg log --template '{author|domain}\n'
114 114 hg log --template '{author|person}\n'
115 115 hg log --template '{author|user}\n'
116 116 hg log --template '{date|age}\n' > /dev/null || exit 1
117 117 hg log -l1 --template '{date|age}\n'
118 118 hg log --template '{date|date}\n'
119 119 hg log --template '{date|isodate}\n'
120 120 hg log --template '{date|isodatesec}\n'
121 121 hg log --template '{date|rfc822date}\n'
122 122 hg log --template '{desc|firstline}\n'
123 123 hg log --template '{node|short}\n'
124 124 hg log --template '<changeset author="{author|xmlescape}"/>\n'
125 125
126 126 echo '# formatnode filter works'
127 127 echo '# quiet'
128 128 hg -q log -r 0 --template '{node|formatnode}\n'
129 129 echo '# normal'
130 130 hg log -r 0 --template '{node|formatnode}\n'
131 131 echo '# verbose'
132 132 hg -v log -r 0 --template '{node|formatnode}\n'
133 133 echo '# debug'
134 134 hg --debug log -r 0 --template '{node|formatnode}\n'
135 135
136 136 echo '# error on syntax'
137 137 echo 'x = "f' >> t
138 138 hg log
139 139
140 140 cd ..
141 141
142 142 echo '# latesttag'
143 143 hg init latesttag
144 144 cd latesttag
145 145
146 146 echo a > file
147 147 hg ci -Am a -d '0 0'
148 148
149 149 echo b >> file
150 150 hg ci -m b -d '1 0'
151 151
152 152 echo c >> head1
153 153 hg ci -Am h1c -d '2 0'
154 154
155 155 hg update -q 1
156 156 echo d >> head2
157 157 hg ci -Am h2d -d '3 0'
158 158
159 159 echo e >> head2
160 160 hg ci -m h2e -d '4 0'
161 161
162 162 hg merge -q
163 163 hg ci -m merge -d '5 0'
164 164
165 165 echo '# No tag set'
166 166 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
167 167
168 168 echo '# one common tag: longuest path wins'
169 169 hg tag -r 1 -m t1 -d '6 0' t1
170 170 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
171 171
172 172 echo '# one ancestor tag: more recent wins'
173 173 hg tag -r 2 -m t2 -d '7 0' t2
174 174 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
175 175
176 176 echo '# two branch tags: more recent wins'
177 177 hg tag -r 3 -m t3 -d '8 0' t3
178 178 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
179 179
180 180 echo '# merged tag overrides'
181 181 hg tag -r 5 -m t5 -d '9 0' t5
182 182 hg tag -r 3 -m at3 -d '10 0' at3
183 183 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
184 184 cd ..
185 185
186 186 echo '# style path expansion (issue1948)'
187 187 mkdir -p home/styles
188 188 cat > home/styles/teststyle <<EOF
189 189 changeset = 'test {rev}:{node|short}\n'
190 190 EOF
191 191 HOME=`pwd`/home; export HOME
192 192 cat > latesttag/.hg/hgrc <<EOF
193 193 [ui]
194 194 style = ~/styles/teststyle
195 195 EOF
196 196 hg -R latesttag tip
197 197
198 echo '# test recursive showlist template (issue1989)'
199 cat > style1989 <<EOF
200 changeset = '{file_mods}{manifest}{extras}'
201 file_mod = 'M|{author|person}\n'
202 manifest = '{rev},{author}\n'
203 extra = '{key}: {author}\n'
204 EOF
205 hg -R latesttag log -r tip --style=style1989
206
198 207 echo '# done'
@@ -1,1069 +1,1073
1 1 created new head
2 2 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
3 3 created new head
4 4 # default style is like normal output
5 5 # normal
6 6 # verbose
7 7 # debug
8 8 # revision with no copies (used to print a traceback)
9 9
10 10 # compact style works
11 11 8[tip] 3bdecc1cde0c 2020-01-01 10:01 +0000 test
12 12 third
13 13
14 14 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
15 15 second
16 16
17 17 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
18 18 merge
19 19
20 20 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
21 21 new head
22 22
23 23 4 32a18f097fcc 1970-01-17 04:53 +0000 person
24 24 new branch
25 25
26 26 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
27 27 no user, no domain
28 28
29 29 2 97054abb4ab8 1970-01-14 21:20 +0000 other
30 30 no person
31 31
32 32 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
33 33 other 1
34 34
35 35 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
36 36 line 1
37 37
38 38 8[tip] 3bdecc1cde0c 2020-01-01 10:01 +0000 test
39 39 third
40 40
41 41 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
42 42 second
43 43
44 44 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
45 45 merge
46 46
47 47 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
48 48 new head
49 49
50 50 4 32a18f097fcc 1970-01-17 04:53 +0000 person
51 51 new branch
52 52
53 53 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
54 54 no user, no domain
55 55
56 56 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
57 57 no person
58 58
59 59 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
60 60 other 1
61 61 other 2
62 62
63 63 other 3
64 64
65 65 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
66 66 line 1
67 67 line 2
68 68
69 69 8[tip]:7,-1 3bdecc1cde0c 2020-01-01 10:01 +0000 test
70 70 third
71 71
72 72 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
73 73 second
74 74
75 75 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
76 76 merge
77 77
78 78 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
79 79 new head
80 80
81 81 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
82 82 new branch
83 83
84 84 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
85 85 no user, no domain
86 86
87 87 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
88 88 no person
89 89
90 90 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
91 91 other 1
92 92 other 2
93 93
94 94 other 3
95 95
96 96 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
97 97 line 1
98 98 line 2
99 99
100 100 # xml style works (--style xml)
101 101 <?xml version="1.0"?>
102 102 <log>
103 103 <logentry revision="8" node="3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57">
104 104 <tag>tip</tag>
105 105 <author email="test">test</author>
106 106 <date>2020-01-01T10:01:00+00:00</date>
107 107 <msg xml:space="preserve">third</msg>
108 108 </logentry>
109 109 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
110 110 <parent revision="-1" node="0000000000000000000000000000000000000000" />
111 111 <author email="user@hostname">User Name</author>
112 112 <date>1970-01-12T13:46:40+00:00</date>
113 113 <msg xml:space="preserve">second</msg>
114 114 </logentry>
115 115 <logentry revision="6" node="c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f">
116 116 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
117 117 <parent revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4" />
118 118 <author email="person">person</author>
119 119 <date>1970-01-18T08:40:01+00:00</date>
120 120 <msg xml:space="preserve">merge</msg>
121 121 </logentry>
122 122 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
123 123 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
124 124 <author email="person">person</author>
125 125 <date>1970-01-18T08:40:00+00:00</date>
126 126 <msg xml:space="preserve">new head</msg>
127 127 </logentry>
128 128 <logentry revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4">
129 129 <branch>foo</branch>
130 130 <author email="person">person</author>
131 131 <date>1970-01-17T04:53:20+00:00</date>
132 132 <msg xml:space="preserve">new branch</msg>
133 133 </logentry>
134 134 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
135 135 <author email="person">person</author>
136 136 <date>1970-01-16T01:06:40+00:00</date>
137 137 <msg xml:space="preserve">no user, no domain</msg>
138 138 </logentry>
139 139 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
140 140 <author email="other@place">other</author>
141 141 <date>1970-01-14T21:20:00+00:00</date>
142 142 <msg xml:space="preserve">no person</msg>
143 143 </logentry>
144 144 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
145 145 <author email="other@place">A. N. Other</author>
146 146 <date>1970-01-13T17:33:20+00:00</date>
147 147 <msg xml:space="preserve">other 1
148 148 other 2
149 149
150 150 other 3</msg>
151 151 </logentry>
152 152 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
153 153 <author email="user@hostname">User Name</author>
154 154 <date>1970-01-12T13:46:40+00:00</date>
155 155 <msg xml:space="preserve">line 1
156 156 line 2</msg>
157 157 </logentry>
158 158 </log>
159 159 # xml style works (-v --style xml)
160 160 <?xml version="1.0"?>
161 161 <log>
162 162 <logentry revision="8" node="3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57">
163 163 <tag>tip</tag>
164 164 <author email="test">test</author>
165 165 <date>2020-01-01T10:01:00+00:00</date>
166 166 <msg xml:space="preserve">third</msg>
167 167 <paths>
168 168 <path action="A">fourth</path>
169 169 <path action="A">third</path>
170 170 <path action="R">second</path>
171 171 </paths>
172 172 <copies>
173 173 <copy source="second">fourth</copy>
174 174 </copies>
175 175 </logentry>
176 176 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
177 177 <parent revision="-1" node="0000000000000000000000000000000000000000" />
178 178 <author email="user@hostname">User Name</author>
179 179 <date>1970-01-12T13:46:40+00:00</date>
180 180 <msg xml:space="preserve">second</msg>
181 181 <paths>
182 182 <path action="A">second</path>
183 183 </paths>
184 184 </logentry>
185 185 <logentry revision="6" node="c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f">
186 186 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
187 187 <parent revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4" />
188 188 <author email="person">person</author>
189 189 <date>1970-01-18T08:40:01+00:00</date>
190 190 <msg xml:space="preserve">merge</msg>
191 191 <paths>
192 192 </paths>
193 193 </logentry>
194 194 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
195 195 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
196 196 <author email="person">person</author>
197 197 <date>1970-01-18T08:40:00+00:00</date>
198 198 <msg xml:space="preserve">new head</msg>
199 199 <paths>
200 200 <path action="A">d</path>
201 201 </paths>
202 202 </logentry>
203 203 <logentry revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4">
204 204 <branch>foo</branch>
205 205 <author email="person">person</author>
206 206 <date>1970-01-17T04:53:20+00:00</date>
207 207 <msg xml:space="preserve">new branch</msg>
208 208 <paths>
209 209 </paths>
210 210 </logentry>
211 211 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
212 212 <author email="person">person</author>
213 213 <date>1970-01-16T01:06:40+00:00</date>
214 214 <msg xml:space="preserve">no user, no domain</msg>
215 215 <paths>
216 216 <path action="M">c</path>
217 217 </paths>
218 218 </logentry>
219 219 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
220 220 <author email="other@place">other</author>
221 221 <date>1970-01-14T21:20:00+00:00</date>
222 222 <msg xml:space="preserve">no person</msg>
223 223 <paths>
224 224 <path action="A">c</path>
225 225 </paths>
226 226 </logentry>
227 227 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
228 228 <author email="other@place">A. N. Other</author>
229 229 <date>1970-01-13T17:33:20+00:00</date>
230 230 <msg xml:space="preserve">other 1
231 231 other 2
232 232
233 233 other 3</msg>
234 234 <paths>
235 235 <path action="A">b</path>
236 236 </paths>
237 237 </logentry>
238 238 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
239 239 <author email="user@hostname">User Name</author>
240 240 <date>1970-01-12T13:46:40+00:00</date>
241 241 <msg xml:space="preserve">line 1
242 242 line 2</msg>
243 243 <paths>
244 244 <path action="A">a</path>
245 245 </paths>
246 246 </logentry>
247 247 </log>
248 248 # xml style works (--debug --style xml)
249 249 <?xml version="1.0"?>
250 250 <log>
251 251 <logentry revision="8" node="3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57">
252 252 <tag>tip</tag>
253 253 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
254 254 <parent revision="-1" node="0000000000000000000000000000000000000000" />
255 255 <author email="test">test</author>
256 256 <date>2020-01-01T10:01:00+00:00</date>
257 257 <msg xml:space="preserve">third</msg>
258 258 <paths>
259 259 <path action="A">fourth</path>
260 260 <path action="A">third</path>
261 261 <path action="R">second</path>
262 262 </paths>
263 263 <copies>
264 264 <copy source="second">fourth</copy>
265 265 </copies>
266 266 <extra key="branch">default</extra>
267 267 </logentry>
268 268 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
269 269 <parent revision="-1" node="0000000000000000000000000000000000000000" />
270 270 <parent revision="-1" node="0000000000000000000000000000000000000000" />
271 271 <author email="user@hostname">User Name</author>
272 272 <date>1970-01-12T13:46:40+00:00</date>
273 273 <msg xml:space="preserve">second</msg>
274 274 <paths>
275 275 <path action="A">second</path>
276 276 </paths>
277 277 <extra key="branch">default</extra>
278 278 </logentry>
279 279 <logentry revision="6" node="c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f">
280 280 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
281 281 <parent revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4" />
282 282 <author email="person">person</author>
283 283 <date>1970-01-18T08:40:01+00:00</date>
284 284 <msg xml:space="preserve">merge</msg>
285 285 <paths>
286 286 </paths>
287 287 <extra key="branch">default</extra>
288 288 </logentry>
289 289 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
290 290 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
291 291 <parent revision="-1" node="0000000000000000000000000000000000000000" />
292 292 <author email="person">person</author>
293 293 <date>1970-01-18T08:40:00+00:00</date>
294 294 <msg xml:space="preserve">new head</msg>
295 295 <paths>
296 296 <path action="A">d</path>
297 297 </paths>
298 298 <extra key="branch">default</extra>
299 299 </logentry>
300 300 <logentry revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4">
301 301 <branch>foo</branch>
302 302 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
303 303 <parent revision="-1" node="0000000000000000000000000000000000000000" />
304 304 <author email="person">person</author>
305 305 <date>1970-01-17T04:53:20+00:00</date>
306 306 <msg xml:space="preserve">new branch</msg>
307 307 <paths>
308 308 </paths>
309 309 <extra key="branch">foo</extra>
310 310 </logentry>
311 311 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
312 312 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
313 313 <parent revision="-1" node="0000000000000000000000000000000000000000" />
314 314 <author email="person">person</author>
315 315 <date>1970-01-16T01:06:40+00:00</date>
316 316 <msg xml:space="preserve">no user, no domain</msg>
317 317 <paths>
318 318 <path action="M">c</path>
319 319 </paths>
320 320 <extra key="branch">default</extra>
321 321 </logentry>
322 322 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
323 323 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
324 324 <parent revision="-1" node="0000000000000000000000000000000000000000" />
325 325 <author email="other@place">other</author>
326 326 <date>1970-01-14T21:20:00+00:00</date>
327 327 <msg xml:space="preserve">no person</msg>
328 328 <paths>
329 329 <path action="A">c</path>
330 330 </paths>
331 331 <extra key="branch">default</extra>
332 332 </logentry>
333 333 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
334 334 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
335 335 <parent revision="-1" node="0000000000000000000000000000000000000000" />
336 336 <author email="other@place">A. N. Other</author>
337 337 <date>1970-01-13T17:33:20+00:00</date>
338 338 <msg xml:space="preserve">other 1
339 339 other 2
340 340
341 341 other 3</msg>
342 342 <paths>
343 343 <path action="A">b</path>
344 344 </paths>
345 345 <extra key="branch">default</extra>
346 346 </logentry>
347 347 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
348 348 <parent revision="-1" node="0000000000000000000000000000000000000000" />
349 349 <parent revision="-1" node="0000000000000000000000000000000000000000" />
350 350 <author email="user@hostname">User Name</author>
351 351 <date>1970-01-12T13:46:40+00:00</date>
352 352 <msg xml:space="preserve">line 1
353 353 line 2</msg>
354 354 <paths>
355 355 <path action="A">a</path>
356 356 </paths>
357 357 <extra key="branch">default</extra>
358 358 </logentry>
359 359 </log>
360 360 # error if style not readable
361 361 abort: Permission denied: ./q
362 362 # error if no style
363 363 abort: style not found: notexist
364 364 # error if style missing key
365 365 abort: ./t: no key named 'changeset'
366 366 # error if include fails
367 367 abort: template file ./q: Permission denied
368 368 # include works
369 369 8
370 370 7
371 371 6
372 372 5
373 373 4
374 374 3
375 375 2
376 376 1
377 377 0
378 378 # ui.style works
379 379 8
380 380 7
381 381 6
382 382 5
383 383 4
384 384 3
385 385 2
386 386 1
387 387 0
388 388 # issue338
389 389 2020-01-01 test <test>
390 390
391 391 * fourth, second, third:
392 392 third
393 393 [3bdecc1cde0c] [tip]
394 394
395 395 1970-01-12 User Name <user@hostname>
396 396
397 397 * second:
398 398 second
399 399 [29114dbae42b]
400 400
401 401 1970-01-18 person <person>
402 402
403 403 * merge
404 404 [c7b487c6c50e]
405 405
406 406 * d:
407 407 new head
408 408 [13207e5a10d9]
409 409
410 410 1970-01-17 person <person>
411 411
412 412 * new branch
413 413 [32a18f097fcc] <foo>
414 414
415 415 1970-01-16 person <person>
416 416
417 417 * c:
418 418 no user, no domain
419 419 [10e46f2dcbf4]
420 420
421 421 1970-01-14 other <other@place>
422 422
423 423 * c:
424 424 no person
425 425 [97054abb4ab8]
426 426
427 427 1970-01-13 A. N. Other <other@place>
428 428
429 429 * b:
430 430 other 1 other 2
431 431
432 432 other 3
433 433 [b608e9d1a3f0]
434 434
435 435 1970-01-12 User Name <user@hostname>
436 436
437 437 * a:
438 438 line 1 line 2
439 439 [1e4e1b8f71e0]
440 440
441 441 # keys work
442 442 author: test
443 443 author: User Name <user@hostname>
444 444 author: person
445 445 author: person
446 446 author: person
447 447 author: person
448 448 author: other@place
449 449 author: A. N. Other <other@place>
450 450 author: User Name <user@hostname>
451 451 author--verbose: test
452 452 author--verbose: User Name <user@hostname>
453 453 author--verbose: person
454 454 author--verbose: person
455 455 author--verbose: person
456 456 author--verbose: person
457 457 author--verbose: other@place
458 458 author--verbose: A. N. Other <other@place>
459 459 author--verbose: User Name <user@hostname>
460 460 author--debug: test
461 461 author--debug: User Name <user@hostname>
462 462 author--debug: person
463 463 author--debug: person
464 464 author--debug: person
465 465 author--debug: person
466 466 author--debug: other@place
467 467 author--debug: A. N. Other <other@place>
468 468 author--debug: User Name <user@hostname>
469 469 branches:
470 470 branches:
471 471 branches:
472 472 branches:
473 473 branches: foo
474 474 branches:
475 475 branches:
476 476 branches:
477 477 branches:
478 478 branches--verbose:
479 479 branches--verbose:
480 480 branches--verbose:
481 481 branches--verbose:
482 482 branches--verbose: foo
483 483 branches--verbose:
484 484 branches--verbose:
485 485 branches--verbose:
486 486 branches--verbose:
487 487 branches--debug:
488 488 branches--debug:
489 489 branches--debug:
490 490 branches--debug:
491 491 branches--debug: foo
492 492 branches--debug:
493 493 branches--debug:
494 494 branches--debug:
495 495 branches--debug:
496 496 date: 1577872860.00
497 497 date: 1000000.00
498 498 date: 1500001.00
499 499 date: 1500000.00
500 500 date: 1400000.00
501 501 date: 1300000.00
502 502 date: 1200000.00
503 503 date: 1100000.00
504 504 date: 1000000.00
505 505 date--verbose: 1577872860.00
506 506 date--verbose: 1000000.00
507 507 date--verbose: 1500001.00
508 508 date--verbose: 1500000.00
509 509 date--verbose: 1400000.00
510 510 date--verbose: 1300000.00
511 511 date--verbose: 1200000.00
512 512 date--verbose: 1100000.00
513 513 date--verbose: 1000000.00
514 514 date--debug: 1577872860.00
515 515 date--debug: 1000000.00
516 516 date--debug: 1500001.00
517 517 date--debug: 1500000.00
518 518 date--debug: 1400000.00
519 519 date--debug: 1300000.00
520 520 date--debug: 1200000.00
521 521 date--debug: 1100000.00
522 522 date--debug: 1000000.00
523 523 desc: third
524 524 desc: second
525 525 desc: merge
526 526 desc: new head
527 527 desc: new branch
528 528 desc: no user, no domain
529 529 desc: no person
530 530 desc: other 1
531 531 other 2
532 532
533 533 other 3
534 534 desc: line 1
535 535 line 2
536 536 desc--verbose: third
537 537 desc--verbose: second
538 538 desc--verbose: merge
539 539 desc--verbose: new head
540 540 desc--verbose: new branch
541 541 desc--verbose: no user, no domain
542 542 desc--verbose: no person
543 543 desc--verbose: other 1
544 544 other 2
545 545
546 546 other 3
547 547 desc--verbose: line 1
548 548 line 2
549 549 desc--debug: third
550 550 desc--debug: second
551 551 desc--debug: merge
552 552 desc--debug: new head
553 553 desc--debug: new branch
554 554 desc--debug: no user, no domain
555 555 desc--debug: no person
556 556 desc--debug: other 1
557 557 other 2
558 558
559 559 other 3
560 560 desc--debug: line 1
561 561 line 2
562 562 file_adds: fourth third
563 563 file_adds: second
564 564 file_adds:
565 565 file_adds: d
566 566 file_adds:
567 567 file_adds:
568 568 file_adds: c
569 569 file_adds: b
570 570 file_adds: a
571 571 file_adds--verbose: fourth third
572 572 file_adds--verbose: second
573 573 file_adds--verbose:
574 574 file_adds--verbose: d
575 575 file_adds--verbose:
576 576 file_adds--verbose:
577 577 file_adds--verbose: c
578 578 file_adds--verbose: b
579 579 file_adds--verbose: a
580 580 file_adds--debug: fourth third
581 581 file_adds--debug: second
582 582 file_adds--debug:
583 583 file_adds--debug: d
584 584 file_adds--debug:
585 585 file_adds--debug:
586 586 file_adds--debug: c
587 587 file_adds--debug: b
588 588 file_adds--debug: a
589 589 file_dels: second
590 590 file_dels:
591 591 file_dels:
592 592 file_dels:
593 593 file_dels:
594 594 file_dels:
595 595 file_dels:
596 596 file_dels:
597 597 file_dels:
598 598 file_dels--verbose: second
599 599 file_dels--verbose:
600 600 file_dels--verbose:
601 601 file_dels--verbose:
602 602 file_dels--verbose:
603 603 file_dels--verbose:
604 604 file_dels--verbose:
605 605 file_dels--verbose:
606 606 file_dels--verbose:
607 607 file_dels--debug: second
608 608 file_dels--debug:
609 609 file_dels--debug:
610 610 file_dels--debug:
611 611 file_dels--debug:
612 612 file_dels--debug:
613 613 file_dels--debug:
614 614 file_dels--debug:
615 615 file_dels--debug:
616 616 file_mods:
617 617 file_mods:
618 618 file_mods:
619 619 file_mods:
620 620 file_mods:
621 621 file_mods: c
622 622 file_mods:
623 623 file_mods:
624 624 file_mods:
625 625 file_mods--verbose:
626 626 file_mods--verbose:
627 627 file_mods--verbose:
628 628 file_mods--verbose:
629 629 file_mods--verbose:
630 630 file_mods--verbose: c
631 631 file_mods--verbose:
632 632 file_mods--verbose:
633 633 file_mods--verbose:
634 634 file_mods--debug:
635 635 file_mods--debug:
636 636 file_mods--debug:
637 637 file_mods--debug:
638 638 file_mods--debug:
639 639 file_mods--debug: c
640 640 file_mods--debug:
641 641 file_mods--debug:
642 642 file_mods--debug:
643 643 file_copies: fourth (second)
644 644 file_copies:
645 645 file_copies:
646 646 file_copies:
647 647 file_copies:
648 648 file_copies:
649 649 file_copies:
650 650 file_copies:
651 651 file_copies:
652 652 file_copies--verbose: fourth (second)
653 653 file_copies--verbose:
654 654 file_copies--verbose:
655 655 file_copies--verbose:
656 656 file_copies--verbose:
657 657 file_copies--verbose:
658 658 file_copies--verbose:
659 659 file_copies--verbose:
660 660 file_copies--verbose:
661 661 file_copies--debug: fourth (second)
662 662 file_copies--debug:
663 663 file_copies--debug:
664 664 file_copies--debug:
665 665 file_copies--debug:
666 666 file_copies--debug:
667 667 file_copies--debug:
668 668 file_copies--debug:
669 669 file_copies--debug:
670 670 file_copies_switch:
671 671 file_copies_switch:
672 672 file_copies_switch:
673 673 file_copies_switch:
674 674 file_copies_switch:
675 675 file_copies_switch:
676 676 file_copies_switch:
677 677 file_copies_switch:
678 678 file_copies_switch:
679 679 file_copies_switch--verbose:
680 680 file_copies_switch--verbose:
681 681 file_copies_switch--verbose:
682 682 file_copies_switch--verbose:
683 683 file_copies_switch--verbose:
684 684 file_copies_switch--verbose:
685 685 file_copies_switch--verbose:
686 686 file_copies_switch--verbose:
687 687 file_copies_switch--verbose:
688 688 file_copies_switch--debug:
689 689 file_copies_switch--debug:
690 690 file_copies_switch--debug:
691 691 file_copies_switch--debug:
692 692 file_copies_switch--debug:
693 693 file_copies_switch--debug:
694 694 file_copies_switch--debug:
695 695 file_copies_switch--debug:
696 696 file_copies_switch--debug:
697 697 files: fourth second third
698 698 files: second
699 699 files:
700 700 files: d
701 701 files:
702 702 files: c
703 703 files: c
704 704 files: b
705 705 files: a
706 706 files--verbose: fourth second third
707 707 files--verbose: second
708 708 files--verbose:
709 709 files--verbose: d
710 710 files--verbose:
711 711 files--verbose: c
712 712 files--verbose: c
713 713 files--verbose: b
714 714 files--verbose: a
715 715 files--debug: fourth second third
716 716 files--debug: second
717 717 files--debug:
718 718 files--debug: d
719 719 files--debug:
720 720 files--debug: c
721 721 files--debug: c
722 722 files--debug: b
723 723 files--debug: a
724 724 manifest: 8:79c71159cb0a
725 725 manifest: 7:f2dbc354b94e
726 726 manifest: 6:91015e9dbdd7
727 727 manifest: 5:4dc3def4f9b4
728 728 manifest: 4:90ae8dda64e1
729 729 manifest: 3:cb5a1327723b
730 730 manifest: 2:6e0e82995c35
731 731 manifest: 1:4e8d705b1e53
732 732 manifest: 0:a0c8bcbbb45c
733 733 manifest--verbose: 8:79c71159cb0a
734 734 manifest--verbose: 7:f2dbc354b94e
735 735 manifest--verbose: 6:91015e9dbdd7
736 736 manifest--verbose: 5:4dc3def4f9b4
737 737 manifest--verbose: 4:90ae8dda64e1
738 738 manifest--verbose: 3:cb5a1327723b
739 739 manifest--verbose: 2:6e0e82995c35
740 740 manifest--verbose: 1:4e8d705b1e53
741 741 manifest--verbose: 0:a0c8bcbbb45c
742 742 manifest--debug: 8:79c71159cb0a1a84add78e7922a1e5e7be34c499
743 743 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
744 744 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
745 745 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
746 746 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
747 747 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
748 748 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
749 749 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
750 750 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
751 751 node: 3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57
752 752 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
753 753 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
754 754 node: 13207e5a10d9fd28ec424934298e176197f2c67f
755 755 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
756 756 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
757 757 node: 97054abb4ab824450e9164180baf491ae0078465
758 758 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
759 759 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
760 760 node--verbose: 3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57
761 761 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
762 762 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
763 763 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
764 764 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
765 765 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
766 766 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
767 767 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
768 768 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
769 769 node--debug: 3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57
770 770 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
771 771 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
772 772 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
773 773 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
774 774 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
775 775 node--debug: 97054abb4ab824450e9164180baf491ae0078465
776 776 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
777 777 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
778 778 parents:
779 779 parents: -1:000000000000
780 780 parents: 5:13207e5a10d9 4:32a18f097fcc
781 781 parents: 3:10e46f2dcbf4
782 782 parents:
783 783 parents:
784 784 parents:
785 785 parents:
786 786 parents:
787 787 parents--verbose:
788 788 parents--verbose: -1:000000000000
789 789 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
790 790 parents--verbose: 3:10e46f2dcbf4
791 791 parents--verbose:
792 792 parents--verbose:
793 793 parents--verbose:
794 794 parents--verbose:
795 795 parents--verbose:
796 796 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
797 797 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
798 798 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
799 799 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
800 800 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
801 801 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
802 802 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
803 803 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
804 804 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
805 805 rev: 8
806 806 rev: 7
807 807 rev: 6
808 808 rev: 5
809 809 rev: 4
810 810 rev: 3
811 811 rev: 2
812 812 rev: 1
813 813 rev: 0
814 814 rev--verbose: 8
815 815 rev--verbose: 7
816 816 rev--verbose: 6
817 817 rev--verbose: 5
818 818 rev--verbose: 4
819 819 rev--verbose: 3
820 820 rev--verbose: 2
821 821 rev--verbose: 1
822 822 rev--verbose: 0
823 823 rev--debug: 8
824 824 rev--debug: 7
825 825 rev--debug: 6
826 826 rev--debug: 5
827 827 rev--debug: 4
828 828 rev--debug: 3
829 829 rev--debug: 2
830 830 rev--debug: 1
831 831 rev--debug: 0
832 832 tags: tip
833 833 tags:
834 834 tags:
835 835 tags:
836 836 tags:
837 837 tags:
838 838 tags:
839 839 tags:
840 840 tags:
841 841 tags--verbose: tip
842 842 tags--verbose:
843 843 tags--verbose:
844 844 tags--verbose:
845 845 tags--verbose:
846 846 tags--verbose:
847 847 tags--verbose:
848 848 tags--verbose:
849 849 tags--verbose:
850 850 tags--debug: tip
851 851 tags--debug:
852 852 tags--debug:
853 853 tags--debug:
854 854 tags--debug:
855 855 tags--debug:
856 856 tags--debug:
857 857 tags--debug:
858 858 tags--debug:
859 859 diffstat: 3: +2/-1
860 860 diffstat: 1: +1/-0
861 861 diffstat: 0: +0/-0
862 862 diffstat: 1: +1/-0
863 863 diffstat: 0: +0/-0
864 864 diffstat: 1: +1/-0
865 865 diffstat: 1: +4/-0
866 866 diffstat: 1: +2/-0
867 867 diffstat: 1: +1/-0
868 868 diffstat--verbose: 3: +2/-1
869 869 diffstat--verbose: 1: +1/-0
870 870 diffstat--verbose: 0: +0/-0
871 871 diffstat--verbose: 1: +1/-0
872 872 diffstat--verbose: 0: +0/-0
873 873 diffstat--verbose: 1: +1/-0
874 874 diffstat--verbose: 1: +4/-0
875 875 diffstat--verbose: 1: +2/-0
876 876 diffstat--verbose: 1: +1/-0
877 877 diffstat--debug: 3: +2/-1
878 878 diffstat--debug: 1: +1/-0
879 879 diffstat--debug: 0: +0/-0
880 880 diffstat--debug: 1: +1/-0
881 881 diffstat--debug: 0: +0/-0
882 882 diffstat--debug: 1: +1/-0
883 883 diffstat--debug: 1: +4/-0
884 884 diffstat--debug: 1: +2/-0
885 885 diffstat--debug: 1: +1/-0
886 886 extras: branch=default
887 887 extras: branch=default
888 888 extras: branch=default
889 889 extras: branch=default
890 890 extras: branch=foo
891 891 extras: branch=default
892 892 extras: branch=default
893 893 extras: branch=default
894 894 extras: branch=default
895 895 extras--verbose: branch=default
896 896 extras--verbose: branch=default
897 897 extras--verbose: branch=default
898 898 extras--verbose: branch=default
899 899 extras--verbose: branch=foo
900 900 extras--verbose: branch=default
901 901 extras--verbose: branch=default
902 902 extras--verbose: branch=default
903 903 extras--verbose: branch=default
904 904 extras--debug: branch=default
905 905 extras--debug: branch=default
906 906 extras--debug: branch=default
907 907 extras--debug: branch=default
908 908 extras--debug: branch=foo
909 909 extras--debug: branch=default
910 910 extras--debug: branch=default
911 911 extras--debug: branch=default
912 912 extras--debug: branch=default
913 913 # filters work
914 914
915 915 hostname
916 916
917 917
918 918
919 919
920 920 place
921 921 place
922 922 hostname
923 923 test
924 924 User Name
925 925 person
926 926 person
927 927 person
928 928 person
929 929 other
930 930 A. N. Other
931 931 User Name
932 932 test
933 933 user
934 934 person
935 935 person
936 936 person
937 937 person
938 938 other
939 939 other
940 940 user
941 941 in the future
942 942 Wed Jan 01 10:01:00 2020 +0000
943 943 Mon Jan 12 13:46:40 1970 +0000
944 944 Sun Jan 18 08:40:01 1970 +0000
945 945 Sun Jan 18 08:40:00 1970 +0000
946 946 Sat Jan 17 04:53:20 1970 +0000
947 947 Fri Jan 16 01:06:40 1970 +0000
948 948 Wed Jan 14 21:20:00 1970 +0000
949 949 Tue Jan 13 17:33:20 1970 +0000
950 950 Mon Jan 12 13:46:40 1970 +0000
951 951 2020-01-01 10:01 +0000
952 952 1970-01-12 13:46 +0000
953 953 1970-01-18 08:40 +0000
954 954 1970-01-18 08:40 +0000
955 955 1970-01-17 04:53 +0000
956 956 1970-01-16 01:06 +0000
957 957 1970-01-14 21:20 +0000
958 958 1970-01-13 17:33 +0000
959 959 1970-01-12 13:46 +0000
960 960 2020-01-01 10:01:00 +0000
961 961 1970-01-12 13:46:40 +0000
962 962 1970-01-18 08:40:01 +0000
963 963 1970-01-18 08:40:00 +0000
964 964 1970-01-17 04:53:20 +0000
965 965 1970-01-16 01:06:40 +0000
966 966 1970-01-14 21:20:00 +0000
967 967 1970-01-13 17:33:20 +0000
968 968 1970-01-12 13:46:40 +0000
969 969 Wed, 01 Jan 2020 10:01:00 +0000
970 970 Mon, 12 Jan 1970 13:46:40 +0000
971 971 Sun, 18 Jan 1970 08:40:01 +0000
972 972 Sun, 18 Jan 1970 08:40:00 +0000
973 973 Sat, 17 Jan 1970 04:53:20 +0000
974 974 Fri, 16 Jan 1970 01:06:40 +0000
975 975 Wed, 14 Jan 1970 21:20:00 +0000
976 976 Tue, 13 Jan 1970 17:33:20 +0000
977 977 Mon, 12 Jan 1970 13:46:40 +0000
978 978 third
979 979 second
980 980 merge
981 981 new head
982 982 new branch
983 983 no user, no domain
984 984 no person
985 985 other 1
986 986 line 1
987 987 3bdecc1cde0c
988 988 29114dbae42b
989 989 c7b487c6c50e
990 990 13207e5a10d9
991 991 32a18f097fcc
992 992 10e46f2dcbf4
993 993 97054abb4ab8
994 994 b608e9d1a3f0
995 995 1e4e1b8f71e0
996 996 <changeset author="test"/>
997 997 <changeset author="User Name &lt;user@hostname&gt;"/>
998 998 <changeset author="person"/>
999 999 <changeset author="person"/>
1000 1000 <changeset author="person"/>
1001 1001 <changeset author="person"/>
1002 1002 <changeset author="other@place"/>
1003 1003 <changeset author="A. N. Other &lt;other@place&gt;"/>
1004 1004 <changeset author="User Name &lt;user@hostname&gt;"/>
1005 1005 # formatnode filter works
1006 1006 # quiet
1007 1007 1e4e1b8f71e0
1008 1008 # normal
1009 1009 1e4e1b8f71e0
1010 1010 # verbose
1011 1011 1e4e1b8f71e0
1012 1012 # debug
1013 1013 1e4e1b8f71e05681d422154f5421e385fec3454f
1014 1014 # error on syntax
1015 1015 abort: t:3: unmatched quotes
1016 1016 # latesttag
1017 1017 adding file
1018 1018 adding head1
1019 1019 adding head2
1020 1020 created new head
1021 1021 # No tag set
1022 1022 5: null+5
1023 1023 4: null+4
1024 1024 3: null+3
1025 1025 2: null+3
1026 1026 1: null+2
1027 1027 0: null+1
1028 1028 # one common tag: longuest path wins
1029 1029 6: t1+4
1030 1030 5: t1+3
1031 1031 4: t1+2
1032 1032 3: t1+1
1033 1033 2: t1+1
1034 1034 1: t1+0
1035 1035 0: null+1
1036 1036 # one ancestor tag: more recent wins
1037 1037 7: t2+3
1038 1038 6: t2+2
1039 1039 5: t2+1
1040 1040 4: t1+2
1041 1041 3: t1+1
1042 1042 2: t2+0
1043 1043 1: t1+0
1044 1044 0: null+1
1045 1045 # two branch tags: more recent wins
1046 1046 8: t3+5
1047 1047 7: t3+4
1048 1048 6: t3+3
1049 1049 5: t3+2
1050 1050 4: t3+1
1051 1051 3: t3+0
1052 1052 2: t2+0
1053 1053 1: t1+0
1054 1054 0: null+1
1055 1055 # merged tag overrides
1056 1056 10: t5+5
1057 1057 9: t5+4
1058 1058 8: t5+3
1059 1059 7: t5+2
1060 1060 6: t5+1
1061 1061 5: t5+0
1062 1062 4: at3:t3+1
1063 1063 3: at3:t3+0
1064 1064 2: t2+0
1065 1065 1: t1+0
1066 1066 0: null+1
1067 1067 # style path expansion (issue1948)
1068 1068 test 10:dee8f28249af
1069 # test recursive showlist template (issue1989)
1070 M|test
1071 10,test
1072 branch: test
1069 1073 # done
General Comments 0
You need to be logged in to leave comments. Login now