##// END OF EJS Templates
Bugfix and test for hg log XML output
Robert Bachmann -
r10160:48653dea default
parent child Browse files
Show More
@@ -1,1166 +1,1166 b''
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
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 rfd, wfd = os.pipe()
571 571 if not runargs:
572 572 runargs = sys.argv[:]
573 573 runargs.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
574 574 # Don't pass --cwd to the child process, because we've already
575 575 # changed directory.
576 576 for i in xrange(1,len(runargs)):
577 577 if runargs[i].startswith('--cwd='):
578 578 del runargs[i]
579 579 break
580 580 elif runargs[i].startswith('--cwd'):
581 581 del runargs[i:i+2]
582 582 break
583 583 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
584 584 runargs[0], runargs)
585 585 os.close(wfd)
586 586 os.read(rfd, 1)
587 587 if parentfn:
588 588 return parentfn(pid)
589 589 else:
590 590 return
591 591
592 592 if initfn:
593 593 initfn()
594 594
595 595 if opts['pid_file']:
596 596 mode = appendpid and 'a' or 'w'
597 597 fp = open(opts['pid_file'], mode)
598 598 fp.write(str(os.getpid()) + '\n')
599 599 fp.close()
600 600
601 601 if opts['daemon_pipefds']:
602 602 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
603 603 os.close(rfd)
604 604 try:
605 605 os.setsid()
606 606 except AttributeError:
607 607 pass
608 608 os.write(wfd, 'y')
609 609 os.close(wfd)
610 610 sys.stdout.flush()
611 611 sys.stderr.flush()
612 612
613 613 nullfd = os.open(util.nulldev, os.O_RDWR)
614 614 logfilefd = nullfd
615 615 if logfile:
616 616 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
617 617 os.dup2(nullfd, 0)
618 618 os.dup2(logfilefd, 1)
619 619 os.dup2(logfilefd, 2)
620 620 if nullfd not in (0, 1, 2):
621 621 os.close(nullfd)
622 622 if logfile and logfilefd not in (0, 1, 2):
623 623 os.close(logfilefd)
624 624
625 625 if runfn:
626 626 return runfn()
627 627
628 628 class changeset_printer(object):
629 629 '''show changeset information when templating not requested.'''
630 630
631 631 def __init__(self, ui, repo, patch, diffopts, buffered):
632 632 self.ui = ui
633 633 self.repo = repo
634 634 self.buffered = buffered
635 635 self.patch = patch
636 636 self.diffopts = diffopts
637 637 self.header = {}
638 638 self.hunk = {}
639 639 self.lastheader = None
640 640 self.footer = None
641 641
642 642 def flush(self, rev):
643 643 if rev in self.header:
644 644 h = self.header[rev]
645 645 if h != self.lastheader:
646 646 self.lastheader = h
647 647 self.ui.write(h)
648 648 del self.header[rev]
649 649 if rev in self.hunk:
650 650 self.ui.write(self.hunk[rev])
651 651 del self.hunk[rev]
652 652 return 1
653 653 return 0
654 654
655 655 def close(self):
656 656 if self.footer:
657 657 self.ui.write(self.footer)
658 658
659 659 def show(self, ctx, copies=None, **props):
660 660 if self.buffered:
661 661 self.ui.pushbuffer()
662 662 self._show(ctx, copies, props)
663 663 self.hunk[ctx.rev()] = self.ui.popbuffer()
664 664 else:
665 665 self._show(ctx, copies, props)
666 666
667 667 def _show(self, ctx, copies, props):
668 668 '''show a single changeset or file revision'''
669 669 changenode = ctx.node()
670 670 rev = ctx.rev()
671 671
672 672 if self.ui.quiet:
673 673 self.ui.write("%d:%s\n" % (rev, short(changenode)))
674 674 return
675 675
676 676 log = self.repo.changelog
677 677 date = util.datestr(ctx.date())
678 678
679 679 hexfunc = self.ui.debugflag and hex or short
680 680
681 681 parents = [(p, hexfunc(log.node(p)))
682 682 for p in self._meaningful_parentrevs(log, rev)]
683 683
684 684 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
685 685
686 686 branch = ctx.branch()
687 687 # don't show the default branch name
688 688 if branch != 'default':
689 689 branch = encoding.tolocal(branch)
690 690 self.ui.write(_("branch: %s\n") % branch)
691 691 for tag in self.repo.nodetags(changenode):
692 692 self.ui.write(_("tag: %s\n") % tag)
693 693 for parent in parents:
694 694 self.ui.write(_("parent: %d:%s\n") % parent)
695 695
696 696 if self.ui.debugflag:
697 697 mnode = ctx.manifestnode()
698 698 self.ui.write(_("manifest: %d:%s\n") %
699 699 (self.repo.manifest.rev(mnode), hex(mnode)))
700 700 self.ui.write(_("user: %s\n") % ctx.user())
701 701 self.ui.write(_("date: %s\n") % date)
702 702
703 703 if self.ui.debugflag:
704 704 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
705 705 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
706 706 files):
707 707 if value:
708 708 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
709 709 elif ctx.files() and self.ui.verbose:
710 710 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
711 711 if copies and self.ui.verbose:
712 712 copies = ['%s (%s)' % c for c in copies]
713 713 self.ui.write(_("copies: %s\n") % ' '.join(copies))
714 714
715 715 extra = ctx.extra()
716 716 if extra and self.ui.debugflag:
717 717 for key, value in sorted(extra.items()):
718 718 self.ui.write(_("extra: %s=%s\n")
719 719 % (key, value.encode('string_escape')))
720 720
721 721 description = ctx.description().strip()
722 722 if description:
723 723 if self.ui.verbose:
724 724 self.ui.write(_("description:\n"))
725 725 self.ui.write(description)
726 726 self.ui.write("\n\n")
727 727 else:
728 728 self.ui.write(_("summary: %s\n") %
729 729 description.splitlines()[0])
730 730 self.ui.write("\n")
731 731
732 732 self.showpatch(changenode)
733 733
734 734 def showpatch(self, node):
735 735 if self.patch:
736 736 prev = self.repo.changelog.parents(node)[0]
737 737 chunks = patch.diff(self.repo, prev, node, match=self.patch,
738 738 opts=patch.diffopts(self.ui, self.diffopts))
739 739 for chunk in chunks:
740 740 self.ui.write(chunk)
741 741 self.ui.write("\n")
742 742
743 743 def _meaningful_parentrevs(self, log, rev):
744 744 """Return list of meaningful (or all if debug) parentrevs for rev.
745 745
746 746 For merges (two non-nullrev revisions) both parents are meaningful.
747 747 Otherwise the first parent revision is considered meaningful if it
748 748 is not the preceding revision.
749 749 """
750 750 parents = log.parentrevs(rev)
751 751 if not self.ui.debugflag and parents[1] == nullrev:
752 752 if parents[0] >= rev - 1:
753 753 parents = []
754 754 else:
755 755 parents = [parents[0]]
756 756 return parents
757 757
758 758
759 759 class changeset_templater(changeset_printer):
760 760 '''format changeset information.'''
761 761
762 762 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
763 763 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
764 764 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
765 765 defaulttempl = {
766 766 'parent': '{rev}:{node|formatnode} ',
767 767 'manifest': '{rev}:{node|formatnode}',
768 768 'file_copy': '{name} ({source})',
769 769 'extra': '{key}={value|stringescape}'
770 770 }
771 771 # filecopy is preserved for compatibility reasons
772 772 defaulttempl['filecopy'] = defaulttempl['file_copy']
773 773 self.t = templater.templater(mapfile, {'formatnode': formatnode},
774 774 cache=defaulttempl)
775 775 self.cache = {}
776 776
777 777 def use_template(self, t):
778 778 '''set template string to use'''
779 779 self.t.cache['changeset'] = t
780 780
781 781 def _meaningful_parentrevs(self, ctx):
782 782 """Return list of meaningful (or all if debug) parentrevs for rev.
783 783 """
784 784 parents = ctx.parents()
785 785 if len(parents) > 1:
786 786 return parents
787 787 if self.ui.debugflag:
788 788 return [parents[0], self.repo['null']]
789 789 if parents[0].rev() >= ctx.rev() - 1:
790 790 return []
791 791 return parents
792 792
793 793 def _show(self, ctx, copies, props):
794 794 '''show a single changeset or file revision'''
795 795
796 796 showlist = templatekw.showlist
797 797
798 798 # showparents() behaviour depends on ui trace level which
799 799 # causes unexpected behaviours at templating level and makes
800 800 # it harder to extract it in a standalone function. Its
801 801 # behaviour cannot be changed so leave it here for now.
802 802 def showparents(repo, ctx, templ, **args):
803 803 parents = [[('rev', p.rev()), ('node', p.hex())]
804 804 for p in self._meaningful_parentrevs(ctx)]
805 805 return showlist(templ, 'parent', parents, **args)
806 806
807 807 props = props.copy()
808 808 props.update(templatekw.keywords)
809 809 props['parents'] = showparents
810 810 props['templ'] = self.t
811 811 props['ctx'] = ctx
812 812 props['repo'] = self.repo
813 813 props['revcache'] = {'copies': copies}
814 814 props['cache'] = self.cache
815 815
816 816 # find correct templates for current mode
817 817
818 818 tmplmodes = [
819 819 (True, None),
820 820 (self.ui.verbose, 'verbose'),
821 821 (self.ui.quiet, 'quiet'),
822 822 (self.ui.debugflag, 'debug'),
823 823 ]
824 824
825 825 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
826 826 for mode, postfix in tmplmodes:
827 827 for type in types:
828 828 cur = postfix and ('%s_%s' % (type, postfix)) or type
829 829 if mode and cur in self.t:
830 830 types[type] = cur
831 831
832 832 try:
833 833
834 834 # write header
835 835 if types['header']:
836 836 h = templater.stringify(self.t(types['header'], **props))
837 837 if self.buffered:
838 838 self.header[ctx.rev()] = h
839 839 else:
840 840 self.ui.write(h)
841 841
842 842 # write changeset metadata, then patch if requested
843 843 key = types['changeset']
844 844 self.ui.write(templater.stringify(self.t(key, **props)))
845 845 self.showpatch(ctx.node())
846 846
847 if types['header']:
847 if types['footer']:
848 848 if not self.footer:
849 849 self.footer = templater.stringify(self.t(types['footer'],
850 850 **props))
851 851
852 852 except KeyError, inst:
853 853 msg = _("%s: no key named '%s'")
854 854 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
855 855 except SyntaxError, inst:
856 856 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
857 857
858 858 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
859 859 """show one changeset using template or regular display.
860 860
861 861 Display format will be the first non-empty hit of:
862 862 1. option 'template'
863 863 2. option 'style'
864 864 3. [ui] setting 'logtemplate'
865 865 4. [ui] setting 'style'
866 866 If all of these values are either the unset or the empty string,
867 867 regular display via changeset_printer() is done.
868 868 """
869 869 # options
870 870 patch = False
871 871 if opts.get('patch'):
872 872 patch = matchfn or matchall(repo)
873 873
874 874 tmpl = opts.get('template')
875 875 style = None
876 876 if tmpl:
877 877 tmpl = templater.parsestring(tmpl, quoted=False)
878 878 else:
879 879 style = opts.get('style')
880 880
881 881 # ui settings
882 882 if not (tmpl or style):
883 883 tmpl = ui.config('ui', 'logtemplate')
884 884 if tmpl:
885 885 tmpl = templater.parsestring(tmpl)
886 886 else:
887 887 style = ui.config('ui', 'style')
888 888
889 889 if not (tmpl or style):
890 890 return changeset_printer(ui, repo, patch, opts, buffered)
891 891
892 892 mapfile = None
893 893 if style and not tmpl:
894 894 mapfile = style
895 895 if not os.path.split(mapfile)[0]:
896 896 mapname = (templater.templatepath('map-cmdline.' + mapfile)
897 897 or templater.templatepath(mapfile))
898 898 if mapname: mapfile = mapname
899 899
900 900 try:
901 901 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
902 902 except SyntaxError, inst:
903 903 raise util.Abort(inst.args[0])
904 904 if tmpl: t.use_template(tmpl)
905 905 return t
906 906
907 907 def finddate(ui, repo, date):
908 908 """Find the tipmost changeset that matches the given date spec"""
909 909
910 910 df = util.matchdate(date)
911 911 m = matchall(repo)
912 912 results = {}
913 913
914 914 def prep(ctx, fns):
915 915 d = ctx.date()
916 916 if df(d[0]):
917 917 results[ctx.rev()] = d
918 918
919 919 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
920 920 rev = ctx.rev()
921 921 if rev in results:
922 922 ui.status(_("Found revision %s from %s\n") %
923 923 (rev, util.datestr(results[rev])))
924 924 return str(rev)
925 925
926 926 raise util.Abort(_("revision matching date not found"))
927 927
928 928 def walkchangerevs(repo, match, opts, prepare):
929 929 '''Iterate over files and the revs in which they changed.
930 930
931 931 Callers most commonly need to iterate backwards over the history
932 932 in which they are interested. Doing so has awful (quadratic-looking)
933 933 performance, so we use iterators in a "windowed" way.
934 934
935 935 We walk a window of revisions in the desired order. Within the
936 936 window, we first walk forwards to gather data, then in the desired
937 937 order (usually backwards) to display it.
938 938
939 939 This function returns an iterator yielding contexts. Before
940 940 yielding each context, the iterator will first call the prepare
941 941 function on each context in the window in forward order.'''
942 942
943 943 def increasing_windows(start, end, windowsize=8, sizelimit=512):
944 944 if start < end:
945 945 while start < end:
946 946 yield start, min(windowsize, end-start)
947 947 start += windowsize
948 948 if windowsize < sizelimit:
949 949 windowsize *= 2
950 950 else:
951 951 while start > end:
952 952 yield start, min(windowsize, start-end-1)
953 953 start -= windowsize
954 954 if windowsize < sizelimit:
955 955 windowsize *= 2
956 956
957 957 follow = opts.get('follow') or opts.get('follow_first')
958 958
959 959 if not len(repo):
960 960 return []
961 961
962 962 if follow:
963 963 defrange = '%s:0' % repo['.'].rev()
964 964 else:
965 965 defrange = '-1:0'
966 966 revs = revrange(repo, opts['rev'] or [defrange])
967 967 wanted = set()
968 968 slowpath = match.anypats() or (match.files() and opts.get('removed'))
969 969 fncache = {}
970 970 change = util.cachefunc(repo.changectx)
971 971
972 972 if not slowpath and not match.files():
973 973 # No files, no patterns. Display all revs.
974 974 wanted = set(revs)
975 975 copies = []
976 976
977 977 if not slowpath:
978 978 # Only files, no patterns. Check the history of each file.
979 979 def filerevgen(filelog, node):
980 980 cl_count = len(repo)
981 981 if node is None:
982 982 last = len(filelog) - 1
983 983 else:
984 984 last = filelog.rev(node)
985 985 for i, window in increasing_windows(last, nullrev):
986 986 revs = []
987 987 for j in xrange(i - window, i + 1):
988 988 n = filelog.node(j)
989 989 revs.append((filelog.linkrev(j),
990 990 follow and filelog.renamed(n)))
991 991 for rev in reversed(revs):
992 992 # only yield rev for which we have the changelog, it can
993 993 # happen while doing "hg log" during a pull or commit
994 994 if rev[0] < cl_count:
995 995 yield rev
996 996 def iterfiles():
997 997 for filename in match.files():
998 998 yield filename, None
999 999 for filename_node in copies:
1000 1000 yield filename_node
1001 1001 minrev, maxrev = min(revs), max(revs)
1002 1002 for file_, node in iterfiles():
1003 1003 filelog = repo.file(file_)
1004 1004 if not len(filelog):
1005 1005 if node is None:
1006 1006 # A zero count may be a directory or deleted file, so
1007 1007 # try to find matching entries on the slow path.
1008 1008 if follow:
1009 1009 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1010 1010 slowpath = True
1011 1011 break
1012 1012 else:
1013 1013 continue
1014 1014 for rev, copied in filerevgen(filelog, node):
1015 1015 if rev <= maxrev:
1016 1016 if rev < minrev:
1017 1017 break
1018 1018 fncache.setdefault(rev, [])
1019 1019 fncache[rev].append(file_)
1020 1020 wanted.add(rev)
1021 1021 if follow and copied:
1022 1022 copies.append(copied)
1023 1023 if slowpath:
1024 1024 if follow:
1025 1025 raise util.Abort(_('can only follow copies/renames for explicit '
1026 1026 'filenames'))
1027 1027
1028 1028 # The slow path checks files modified in every changeset.
1029 1029 def changerevgen():
1030 1030 for i, window in increasing_windows(len(repo) - 1, nullrev):
1031 1031 for j in xrange(i - window, i + 1):
1032 1032 yield change(j)
1033 1033
1034 1034 for ctx in changerevgen():
1035 1035 matches = filter(match, ctx.files())
1036 1036 if matches:
1037 1037 fncache[ctx.rev()] = matches
1038 1038 wanted.add(ctx.rev())
1039 1039
1040 1040 class followfilter(object):
1041 1041 def __init__(self, onlyfirst=False):
1042 1042 self.startrev = nullrev
1043 1043 self.roots = set()
1044 1044 self.onlyfirst = onlyfirst
1045 1045
1046 1046 def match(self, rev):
1047 1047 def realparents(rev):
1048 1048 if self.onlyfirst:
1049 1049 return repo.changelog.parentrevs(rev)[0:1]
1050 1050 else:
1051 1051 return filter(lambda x: x != nullrev,
1052 1052 repo.changelog.parentrevs(rev))
1053 1053
1054 1054 if self.startrev == nullrev:
1055 1055 self.startrev = rev
1056 1056 return True
1057 1057
1058 1058 if rev > self.startrev:
1059 1059 # forward: all descendants
1060 1060 if not self.roots:
1061 1061 self.roots.add(self.startrev)
1062 1062 for parent in realparents(rev):
1063 1063 if parent in self.roots:
1064 1064 self.roots.add(rev)
1065 1065 return True
1066 1066 else:
1067 1067 # backwards: all parents
1068 1068 if not self.roots:
1069 1069 self.roots.update(realparents(self.startrev))
1070 1070 if rev in self.roots:
1071 1071 self.roots.remove(rev)
1072 1072 self.roots.update(realparents(rev))
1073 1073 return True
1074 1074
1075 1075 return False
1076 1076
1077 1077 # it might be worthwhile to do this in the iterator if the rev range
1078 1078 # is descending and the prune args are all within that range
1079 1079 for rev in opts.get('prune', ()):
1080 1080 rev = repo.changelog.rev(repo.lookup(rev))
1081 1081 ff = followfilter()
1082 1082 stop = min(revs[0], revs[-1])
1083 1083 for x in xrange(rev, stop-1, -1):
1084 1084 if ff.match(x):
1085 1085 wanted.discard(x)
1086 1086
1087 1087 def iterate():
1088 1088 if follow and not match.files():
1089 1089 ff = followfilter(onlyfirst=opts.get('follow_first'))
1090 1090 def want(rev):
1091 1091 return ff.match(rev) and rev in wanted
1092 1092 else:
1093 1093 def want(rev):
1094 1094 return rev in wanted
1095 1095
1096 1096 for i, window in increasing_windows(0, len(revs)):
1097 1097 change = util.cachefunc(repo.changectx)
1098 1098 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1099 1099 for rev in sorted(nrevs):
1100 1100 fns = fncache.get(rev)
1101 1101 ctx = change(rev)
1102 1102 if not fns:
1103 1103 def fns_generator():
1104 1104 for f in ctx.files():
1105 1105 if match(f):
1106 1106 yield f
1107 1107 fns = fns_generator()
1108 1108 prepare(ctx, fns)
1109 1109 for rev in nrevs:
1110 1110 yield change(rev)
1111 1111 return iterate()
1112 1112
1113 1113 def commit(ui, repo, commitfunc, pats, opts):
1114 1114 '''commit the specified files or all outstanding changes'''
1115 1115 date = opts.get('date')
1116 1116 if date:
1117 1117 opts['date'] = util.parsedate(date)
1118 1118 message = logmessage(opts)
1119 1119
1120 1120 # extract addremove carefully -- this function can be called from a command
1121 1121 # that doesn't support addremove
1122 1122 if opts.get('addremove'):
1123 1123 addremove(repo, pats, opts)
1124 1124
1125 1125 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1126 1126
1127 1127 def commiteditor(repo, ctx, subs):
1128 1128 if ctx.description():
1129 1129 return ctx.description()
1130 1130 return commitforceeditor(repo, ctx, subs)
1131 1131
1132 1132 def commitforceeditor(repo, ctx, subs):
1133 1133 edittext = []
1134 1134 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1135 1135 if ctx.description():
1136 1136 edittext.append(ctx.description())
1137 1137 edittext.append("")
1138 1138 edittext.append("") # Empty line between message and comments.
1139 1139 edittext.append(_("HG: Enter commit message."
1140 1140 " Lines beginning with 'HG:' are removed."))
1141 1141 edittext.append(_("HG: Leave message empty to abort commit."))
1142 1142 edittext.append("HG: --")
1143 1143 edittext.append(_("HG: user: %s") % ctx.user())
1144 1144 if ctx.p2():
1145 1145 edittext.append(_("HG: branch merge"))
1146 1146 if ctx.branch():
1147 1147 edittext.append(_("HG: branch '%s'")
1148 1148 % encoding.tolocal(ctx.branch()))
1149 1149 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1150 1150 edittext.extend([_("HG: added %s") % f for f in added])
1151 1151 edittext.extend([_("HG: changed %s") % f for f in modified])
1152 1152 edittext.extend([_("HG: removed %s") % f for f in removed])
1153 1153 if not added and not modified and not removed:
1154 1154 edittext.append(_("HG: no files changed"))
1155 1155 edittext.append("")
1156 1156 # run editor in the repository root
1157 1157 olddir = os.getcwd()
1158 1158 os.chdir(repo.root)
1159 1159 text = repo.ui.edit("\n".join(edittext), ctx.user())
1160 1160 text = re.sub("(?m)^HG:.*\n", "", text)
1161 1161 os.chdir(olddir)
1162 1162
1163 1163 if not text.strip():
1164 1164 raise util.Abort(_("empty commit message"))
1165 1165
1166 1166 return text
@@ -1,177 +1,185 b''
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 # Test xml styles
66 echo '# xml style works (--style xml)'
67 hg log --style xml
68 echo '# xml style works (-v --style xml)'
69 hg log -v --style xml
70 echo '# xml style works (--debug --style xml)'
71 hg log --debug --style xml
72
65 73 echo '# error if style not readable'
66 74 touch q
67 75 chmod 0 q
68 76 hg log --style ./q
69 77
70 78 echo '# error if no style'
71 79 hg log --style notexist
72 80
73 81 echo '# error if style missing key'
74 82 echo 'q = q' > t
75 83 hg log --style ./t
76 84
77 85 echo '# error if include fails'
78 86 echo 'changeset = q' >> t
79 87 hg log --style ./t
80 88
81 89 echo '# include works'
82 90 rm q
83 91 echo '{rev}' > q
84 92 hg log --style ./t
85 93
86 94 echo '# ui.style works'
87 95 echo '[ui]' > .hg/hgrc
88 96 echo 'style = t' >> .hg/hgrc
89 97 hg log
90 98
91 99 echo '# issue338'
92 100 hg log --style=changelog > changelog
93 101 cat changelog
94 102
95 103 echo "# keys work"
96 104 for key in author branches date desc file_adds file_dels file_mods \
97 105 file_copies file_copies_switch files \
98 106 manifest node parents rev tags diffstat extras; do
99 107 for mode in '' --verbose --debug; do
100 108 hg log $mode --template "$key$mode: {$key}\n"
101 109 done
102 110 done
103 111
104 112 echo '# filters work'
105 113 hg log --template '{author|domain}\n'
106 114 hg log --template '{author|person}\n'
107 115 hg log --template '{author|user}\n'
108 116 hg log --template '{date|age}\n' > /dev/null || exit 1
109 117 hg log -l1 --template '{date|age}\n'
110 118 hg log --template '{date|date}\n'
111 119 hg log --template '{date|isodate}\n'
112 120 hg log --template '{date|isodatesec}\n'
113 121 hg log --template '{date|rfc822date}\n'
114 122 hg log --template '{desc|firstline}\n'
115 123 hg log --template '{node|short}\n'
116 124 hg log --template '<changeset author="{author|xmlescape}"/>\n'
117 125
118 126 echo '# formatnode filter works'
119 127 echo '# quiet'
120 128 hg -q log -r 0 --template '{node|formatnode}\n'
121 129 echo '# normal'
122 130 hg log -r 0 --template '{node|formatnode}\n'
123 131 echo '# verbose'
124 132 hg -v log -r 0 --template '{node|formatnode}\n'
125 133 echo '# debug'
126 134 hg --debug log -r 0 --template '{node|formatnode}\n'
127 135
128 136 echo '# error on syntax'
129 137 echo 'x = "f' >> t
130 138 hg log
131 139
132 140 cd ..
133 141
134 142 echo '# latesttag'
135 143 hg init latesttag
136 144 cd latesttag
137 145
138 146 echo a > file
139 147 hg ci -Am a -d '0 0'
140 148
141 149 echo b >> file
142 150 hg ci -m b -d '1 0'
143 151
144 152 echo c >> head1
145 153 hg ci -Am h1c -d '2 0'
146 154
147 155 hg update -q 1
148 156 echo d >> head2
149 157 hg ci -Am h2d -d '3 0'
150 158
151 159 echo e >> head2
152 160 hg ci -m h2e -d '4 0'
153 161
154 162 hg merge -q
155 163 hg ci -m merge -d '5 0'
156 164
157 165 echo '# No tag set'
158 166 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
159 167
160 168 echo '# one common tag: longuest path wins'
161 169 hg tag -r 1 -m t1 -d '6 0' t1
162 170 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
163 171
164 172 echo '# one ancestor tag: more recent wins'
165 173 hg tag -r 2 -m t2 -d '7 0' t2
166 174 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
167 175
168 176 echo '# two branch tags: more recent wins'
169 177 hg tag -r 3 -m t3 -d '8 0' t3
170 178 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
171 179
172 180 echo '# merged tag overrides'
173 181 hg tag -r 5 -m t5 -d '9 0' t5
174 182 hg tag -r 3 -m at3 -d '10 0' at3
175 183 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
176 184
177 185 echo '# done'
@@ -1,807 +1,1067 b''
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 # xml style works (--style xml)
101 <?xml version="1.0"?>
102 <log>
103 <logentry revision="8" node="3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57">
104 <tag>tip</tag>
105 <author email="test">test</author>
106 <date>2020-01-01T10:01:00+00:00</date>
107 <msg xml:space="preserve">third</msg>
108 </logentry>
109 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
110 <parent revision="-1" node="0000000000000000000000000000000000000000" />
111 <author email="user@hostname">User Name</author>
112 <date>1970-01-12T13:46:40+00:00</date>
113 <msg xml:space="preserve">second</msg>
114 </logentry>
115 <logentry revision="6" node="c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f">
116 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
117 <parent revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4" />
118 <author email="person">person</author>
119 <date>1970-01-18T08:40:01+00:00</date>
120 <msg xml:space="preserve">merge</msg>
121 </logentry>
122 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
123 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
124 <author email="person">person</author>
125 <date>1970-01-18T08:40:00+00:00</date>
126 <msg xml:space="preserve">new head</msg>
127 </logentry>
128 <logentry revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4">
129 <branch>foo</branch>
130 <author email="person">person</author>
131 <date>1970-01-17T04:53:20+00:00</date>
132 <msg xml:space="preserve">new branch</msg>
133 </logentry>
134 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
135 <author email="person">person</author>
136 <date>1970-01-16T01:06:40+00:00</date>
137 <msg xml:space="preserve">no user, no domain</msg>
138 </logentry>
139 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
140 <author email="other@place">other</author>
141 <date>1970-01-14T21:20:00+00:00</date>
142 <msg xml:space="preserve">no person</msg>
143 </logentry>
144 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
145 <author email="other@place">A. N. Other</author>
146 <date>1970-01-13T17:33:20+00:00</date>
147 <msg xml:space="preserve">other 1
148 other 2
149
150 other 3</msg>
151 </logentry>
152 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
153 <author email="user@hostname">User Name</author>
154 <date>1970-01-12T13:46:40+00:00</date>
155 <msg xml:space="preserve">line 1
156 line 2</msg>
157 </logentry>
158 </log>
159 # xml style works (-v --style xml)
160 <?xml version="1.0"?>
161 <log>
162 <logentry revision="8" node="3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57">
163 <tag>tip</tag>
164 <author email="test">test</author>
165 <date>2020-01-01T10:01:00+00:00</date>
166 <msg xml:space="preserve">third</msg>
167 <paths>
168 <path action="A">fourth</path>
169 <path action="A">third</path>
170 <path action="R">second</path>
171 </paths>
172 <copies>
173 <copy source="second">fourth</copy>
174 </copies>
175 </logentry>
176 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
177 <parent revision="-1" node="0000000000000000000000000000000000000000" />
178 <author email="user@hostname">User Name</author>
179 <date>1970-01-12T13:46:40+00:00</date>
180 <msg xml:space="preserve">second</msg>
181 <paths>
182 <path action="A">second</path>
183 </paths>
184 </logentry>
185 <logentry revision="6" node="c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f">
186 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
187 <parent revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4" />
188 <author email="person">person</author>
189 <date>1970-01-18T08:40:01+00:00</date>
190 <msg xml:space="preserve">merge</msg>
191 <paths>
192 </paths>
193 </logentry>
194 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
195 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
196 <author email="person">person</author>
197 <date>1970-01-18T08:40:00+00:00</date>
198 <msg xml:space="preserve">new head</msg>
199 <paths>
200 <path action="A">d</path>
201 </paths>
202 </logentry>
203 <logentry revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4">
204 <branch>foo</branch>
205 <author email="person">person</author>
206 <date>1970-01-17T04:53:20+00:00</date>
207 <msg xml:space="preserve">new branch</msg>
208 <paths>
209 </paths>
210 </logentry>
211 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
212 <author email="person">person</author>
213 <date>1970-01-16T01:06:40+00:00</date>
214 <msg xml:space="preserve">no user, no domain</msg>
215 <paths>
216 <path action="M">c</path>
217 </paths>
218 </logentry>
219 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
220 <author email="other@place">other</author>
221 <date>1970-01-14T21:20:00+00:00</date>
222 <msg xml:space="preserve">no person</msg>
223 <paths>
224 <path action="A">c</path>
225 </paths>
226 </logentry>
227 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
228 <author email="other@place">A. N. Other</author>
229 <date>1970-01-13T17:33:20+00:00</date>
230 <msg xml:space="preserve">other 1
231 other 2
232
233 other 3</msg>
234 <paths>
235 <path action="A">b</path>
236 </paths>
237 </logentry>
238 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
239 <author email="user@hostname">User Name</author>
240 <date>1970-01-12T13:46:40+00:00</date>
241 <msg xml:space="preserve">line 1
242 line 2</msg>
243 <paths>
244 <path action="A">a</path>
245 </paths>
246 </logentry>
247 </log>
248 # xml style works (--debug --style xml)
249 <?xml version="1.0"?>
250 <log>
251 <logentry revision="8" node="3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57">
252 <tag>tip</tag>
253 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
254 <parent revision="-1" node="0000000000000000000000000000000000000000" />
255 <author email="test">test</author>
256 <date>2020-01-01T10:01:00+00:00</date>
257 <msg xml:space="preserve">third</msg>
258 <paths>
259 <path action="A">fourth</path>
260 <path action="A">third</path>
261 <path action="R">second</path>
262 </paths>
263 <copies>
264 <copy source="second">fourth</copy>
265 </copies>
266 <extra key="branch">default</extra>
267 </logentry>
268 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
269 <parent revision="-1" node="0000000000000000000000000000000000000000" />
270 <parent revision="-1" node="0000000000000000000000000000000000000000" />
271 <author email="user@hostname">User Name</author>
272 <date>1970-01-12T13:46:40+00:00</date>
273 <msg xml:space="preserve">second</msg>
274 <paths>
275 <path action="A">second</path>
276 </paths>
277 <extra key="branch">default</extra>
278 </logentry>
279 <logentry revision="6" node="c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f">
280 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
281 <parent revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4" />
282 <author email="person">person</author>
283 <date>1970-01-18T08:40:01+00:00</date>
284 <msg xml:space="preserve">merge</msg>
285 <paths>
286 </paths>
287 <extra key="branch">default</extra>
288 </logentry>
289 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
290 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
291 <parent revision="-1" node="0000000000000000000000000000000000000000" />
292 <author email="person">person</author>
293 <date>1970-01-18T08:40:00+00:00</date>
294 <msg xml:space="preserve">new head</msg>
295 <paths>
296 <path action="A">d</path>
297 </paths>
298 <extra key="branch">default</extra>
299 </logentry>
300 <logentry revision="4" node="32a18f097fcccf76ef282f62f8a85b3adf8d13c4">
301 <branch>foo</branch>
302 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
303 <parent revision="-1" node="0000000000000000000000000000000000000000" />
304 <author email="person">person</author>
305 <date>1970-01-17T04:53:20+00:00</date>
306 <msg xml:space="preserve">new branch</msg>
307 <paths>
308 </paths>
309 <extra key="branch">foo</extra>
310 </logentry>
311 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
312 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
313 <parent revision="-1" node="0000000000000000000000000000000000000000" />
314 <author email="person">person</author>
315 <date>1970-01-16T01:06:40+00:00</date>
316 <msg xml:space="preserve">no user, no domain</msg>
317 <paths>
318 <path action="M">c</path>
319 </paths>
320 <extra key="branch">default</extra>
321 </logentry>
322 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
323 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
324 <parent revision="-1" node="0000000000000000000000000000000000000000" />
325 <author email="other@place">other</author>
326 <date>1970-01-14T21:20:00+00:00</date>
327 <msg xml:space="preserve">no person</msg>
328 <paths>
329 <path action="A">c</path>
330 </paths>
331 <extra key="branch">default</extra>
332 </logentry>
333 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
334 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
335 <parent revision="-1" node="0000000000000000000000000000000000000000" />
336 <author email="other@place">A. N. Other</author>
337 <date>1970-01-13T17:33:20+00:00</date>
338 <msg xml:space="preserve">other 1
339 other 2
340
341 other 3</msg>
342 <paths>
343 <path action="A">b</path>
344 </paths>
345 <extra key="branch">default</extra>
346 </logentry>
347 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
348 <parent revision="-1" node="0000000000000000000000000000000000000000" />
349 <parent revision="-1" node="0000000000000000000000000000000000000000" />
350 <author email="user@hostname">User Name</author>
351 <date>1970-01-12T13:46:40+00:00</date>
352 <msg xml:space="preserve">line 1
353 line 2</msg>
354 <paths>
355 <path action="A">a</path>
356 </paths>
357 <extra key="branch">default</extra>
358 </logentry>
359 </log>
100 360 # error if style not readable
101 361 abort: Permission denied: ./q
102 362 # error if no style
103 363 abort: style not found: notexist
104 364 # error if style missing key
105 365 abort: ./t: no key named 'changeset'
106 366 # error if include fails
107 367 abort: template file ./q: Permission denied
108 368 # include works
109 369 8
110 370 7
111 371 6
112 372 5
113 373 4
114 374 3
115 375 2
116 376 1
117 377 0
118 378 # ui.style works
119 379 8
120 380 7
121 381 6
122 382 5
123 383 4
124 384 3
125 385 2
126 386 1
127 387 0
128 388 # issue338
129 389 2020-01-01 test <test>
130 390
131 391 * fourth, second, third:
132 392 third
133 393 [3bdecc1cde0c] [tip]
134 394
135 395 1970-01-12 User Name <user@hostname>
136 396
137 397 * second:
138 398 second
139 399 [29114dbae42b]
140 400
141 401 1970-01-18 person <person>
142 402
143 403 * merge
144 404 [c7b487c6c50e]
145 405
146 406 * d:
147 407 new head
148 408 [13207e5a10d9]
149 409
150 410 1970-01-17 person <person>
151 411
152 412 * new branch
153 413 [32a18f097fcc] <foo>
154 414
155 415 1970-01-16 person <person>
156 416
157 417 * c:
158 418 no user, no domain
159 419 [10e46f2dcbf4]
160 420
161 421 1970-01-14 other <other@place>
162 422
163 423 * c:
164 424 no person
165 425 [97054abb4ab8]
166 426
167 427 1970-01-13 A. N. Other <other@place>
168 428
169 429 * b:
170 430 other 1 other 2
171 431
172 432 other 3
173 433 [b608e9d1a3f0]
174 434
175 435 1970-01-12 User Name <user@hostname>
176 436
177 437 * a:
178 438 line 1 line 2
179 439 [1e4e1b8f71e0]
180 440
181 441 # keys work
182 442 author: test
183 443 author: User Name <user@hostname>
184 444 author: person
185 445 author: person
186 446 author: person
187 447 author: person
188 448 author: other@place
189 449 author: A. N. Other <other@place>
190 450 author: User Name <user@hostname>
191 451 author--verbose: test
192 452 author--verbose: User Name <user@hostname>
193 453 author--verbose: person
194 454 author--verbose: person
195 455 author--verbose: person
196 456 author--verbose: person
197 457 author--verbose: other@place
198 458 author--verbose: A. N. Other <other@place>
199 459 author--verbose: User Name <user@hostname>
200 460 author--debug: test
201 461 author--debug: User Name <user@hostname>
202 462 author--debug: person
203 463 author--debug: person
204 464 author--debug: person
205 465 author--debug: person
206 466 author--debug: other@place
207 467 author--debug: A. N. Other <other@place>
208 468 author--debug: User Name <user@hostname>
209 469 branches:
210 470 branches:
211 471 branches:
212 472 branches:
213 473 branches: foo
214 474 branches:
215 475 branches:
216 476 branches:
217 477 branches:
218 478 branches--verbose:
219 479 branches--verbose:
220 480 branches--verbose:
221 481 branches--verbose:
222 482 branches--verbose: foo
223 483 branches--verbose:
224 484 branches--verbose:
225 485 branches--verbose:
226 486 branches--verbose:
227 487 branches--debug:
228 488 branches--debug:
229 489 branches--debug:
230 490 branches--debug:
231 491 branches--debug: foo
232 492 branches--debug:
233 493 branches--debug:
234 494 branches--debug:
235 495 branches--debug:
236 496 date: 1577872860.00
237 497 date: 1000000.00
238 498 date: 1500001.00
239 499 date: 1500000.00
240 500 date: 1400000.00
241 501 date: 1300000.00
242 502 date: 1200000.00
243 503 date: 1100000.00
244 504 date: 1000000.00
245 505 date--verbose: 1577872860.00
246 506 date--verbose: 1000000.00
247 507 date--verbose: 1500001.00
248 508 date--verbose: 1500000.00
249 509 date--verbose: 1400000.00
250 510 date--verbose: 1300000.00
251 511 date--verbose: 1200000.00
252 512 date--verbose: 1100000.00
253 513 date--verbose: 1000000.00
254 514 date--debug: 1577872860.00
255 515 date--debug: 1000000.00
256 516 date--debug: 1500001.00
257 517 date--debug: 1500000.00
258 518 date--debug: 1400000.00
259 519 date--debug: 1300000.00
260 520 date--debug: 1200000.00
261 521 date--debug: 1100000.00
262 522 date--debug: 1000000.00
263 523 desc: third
264 524 desc: second
265 525 desc: merge
266 526 desc: new head
267 527 desc: new branch
268 528 desc: no user, no domain
269 529 desc: no person
270 530 desc: other 1
271 531 other 2
272 532
273 533 other 3
274 534 desc: line 1
275 535 line 2
276 536 desc--verbose: third
277 537 desc--verbose: second
278 538 desc--verbose: merge
279 539 desc--verbose: new head
280 540 desc--verbose: new branch
281 541 desc--verbose: no user, no domain
282 542 desc--verbose: no person
283 543 desc--verbose: other 1
284 544 other 2
285 545
286 546 other 3
287 547 desc--verbose: line 1
288 548 line 2
289 549 desc--debug: third
290 550 desc--debug: second
291 551 desc--debug: merge
292 552 desc--debug: new head
293 553 desc--debug: new branch
294 554 desc--debug: no user, no domain
295 555 desc--debug: no person
296 556 desc--debug: other 1
297 557 other 2
298 558
299 559 other 3
300 560 desc--debug: line 1
301 561 line 2
302 562 file_adds: fourth third
303 563 file_adds: second
304 564 file_adds:
305 565 file_adds: d
306 566 file_adds:
307 567 file_adds:
308 568 file_adds: c
309 569 file_adds: b
310 570 file_adds: a
311 571 file_adds--verbose: fourth third
312 572 file_adds--verbose: second
313 573 file_adds--verbose:
314 574 file_adds--verbose: d
315 575 file_adds--verbose:
316 576 file_adds--verbose:
317 577 file_adds--verbose: c
318 578 file_adds--verbose: b
319 579 file_adds--verbose: a
320 580 file_adds--debug: fourth third
321 581 file_adds--debug: second
322 582 file_adds--debug:
323 583 file_adds--debug: d
324 584 file_adds--debug:
325 585 file_adds--debug:
326 586 file_adds--debug: c
327 587 file_adds--debug: b
328 588 file_adds--debug: a
329 589 file_dels: second
330 590 file_dels:
331 591 file_dels:
332 592 file_dels:
333 593 file_dels:
334 594 file_dels:
335 595 file_dels:
336 596 file_dels:
337 597 file_dels:
338 598 file_dels--verbose: second
339 599 file_dels--verbose:
340 600 file_dels--verbose:
341 601 file_dels--verbose:
342 602 file_dels--verbose:
343 603 file_dels--verbose:
344 604 file_dels--verbose:
345 605 file_dels--verbose:
346 606 file_dels--verbose:
347 607 file_dels--debug: second
348 608 file_dels--debug:
349 609 file_dels--debug:
350 610 file_dels--debug:
351 611 file_dels--debug:
352 612 file_dels--debug:
353 613 file_dels--debug:
354 614 file_dels--debug:
355 615 file_dels--debug:
356 616 file_mods:
357 617 file_mods:
358 618 file_mods:
359 619 file_mods:
360 620 file_mods:
361 621 file_mods: c
362 622 file_mods:
363 623 file_mods:
364 624 file_mods:
365 625 file_mods--verbose:
366 626 file_mods--verbose:
367 627 file_mods--verbose:
368 628 file_mods--verbose:
369 629 file_mods--verbose:
370 630 file_mods--verbose: c
371 631 file_mods--verbose:
372 632 file_mods--verbose:
373 633 file_mods--verbose:
374 634 file_mods--debug:
375 635 file_mods--debug:
376 636 file_mods--debug:
377 637 file_mods--debug:
378 638 file_mods--debug:
379 639 file_mods--debug: c
380 640 file_mods--debug:
381 641 file_mods--debug:
382 642 file_mods--debug:
383 643 file_copies: fourth (second)
384 644 file_copies:
385 645 file_copies:
386 646 file_copies:
387 647 file_copies:
388 648 file_copies:
389 649 file_copies:
390 650 file_copies:
391 651 file_copies:
392 652 file_copies--verbose: fourth (second)
393 653 file_copies--verbose:
394 654 file_copies--verbose:
395 655 file_copies--verbose:
396 656 file_copies--verbose:
397 657 file_copies--verbose:
398 658 file_copies--verbose:
399 659 file_copies--verbose:
400 660 file_copies--verbose:
401 661 file_copies--debug: fourth (second)
402 662 file_copies--debug:
403 663 file_copies--debug:
404 664 file_copies--debug:
405 665 file_copies--debug:
406 666 file_copies--debug:
407 667 file_copies--debug:
408 668 file_copies--debug:
409 669 file_copies--debug:
410 670 file_copies_switch:
411 671 file_copies_switch:
412 672 file_copies_switch:
413 673 file_copies_switch:
414 674 file_copies_switch:
415 675 file_copies_switch:
416 676 file_copies_switch:
417 677 file_copies_switch:
418 678 file_copies_switch:
419 679 file_copies_switch--verbose:
420 680 file_copies_switch--verbose:
421 681 file_copies_switch--verbose:
422 682 file_copies_switch--verbose:
423 683 file_copies_switch--verbose:
424 684 file_copies_switch--verbose:
425 685 file_copies_switch--verbose:
426 686 file_copies_switch--verbose:
427 687 file_copies_switch--verbose:
428 688 file_copies_switch--debug:
429 689 file_copies_switch--debug:
430 690 file_copies_switch--debug:
431 691 file_copies_switch--debug:
432 692 file_copies_switch--debug:
433 693 file_copies_switch--debug:
434 694 file_copies_switch--debug:
435 695 file_copies_switch--debug:
436 696 file_copies_switch--debug:
437 697 files: fourth second third
438 698 files: second
439 699 files:
440 700 files: d
441 701 files:
442 702 files: c
443 703 files: c
444 704 files: b
445 705 files: a
446 706 files--verbose: fourth second third
447 707 files--verbose: second
448 708 files--verbose:
449 709 files--verbose: d
450 710 files--verbose:
451 711 files--verbose: c
452 712 files--verbose: c
453 713 files--verbose: b
454 714 files--verbose: a
455 715 files--debug: fourth second third
456 716 files--debug: second
457 717 files--debug:
458 718 files--debug: d
459 719 files--debug:
460 720 files--debug: c
461 721 files--debug: c
462 722 files--debug: b
463 723 files--debug: a
464 724 manifest: 8:79c71159cb0a
465 725 manifest: 7:f2dbc354b94e
466 726 manifest: 6:91015e9dbdd7
467 727 manifest: 5:4dc3def4f9b4
468 728 manifest: 4:90ae8dda64e1
469 729 manifest: 3:cb5a1327723b
470 730 manifest: 2:6e0e82995c35
471 731 manifest: 1:4e8d705b1e53
472 732 manifest: 0:a0c8bcbbb45c
473 733 manifest--verbose: 8:79c71159cb0a
474 734 manifest--verbose: 7:f2dbc354b94e
475 735 manifest--verbose: 6:91015e9dbdd7
476 736 manifest--verbose: 5:4dc3def4f9b4
477 737 manifest--verbose: 4:90ae8dda64e1
478 738 manifest--verbose: 3:cb5a1327723b
479 739 manifest--verbose: 2:6e0e82995c35
480 740 manifest--verbose: 1:4e8d705b1e53
481 741 manifest--verbose: 0:a0c8bcbbb45c
482 742 manifest--debug: 8:79c71159cb0a1a84add78e7922a1e5e7be34c499
483 743 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
484 744 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
485 745 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
486 746 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
487 747 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
488 748 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
489 749 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
490 750 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
491 751 node: 3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57
492 752 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
493 753 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
494 754 node: 13207e5a10d9fd28ec424934298e176197f2c67f
495 755 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
496 756 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
497 757 node: 97054abb4ab824450e9164180baf491ae0078465
498 758 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
499 759 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
500 760 node--verbose: 3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57
501 761 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
502 762 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
503 763 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
504 764 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
505 765 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
506 766 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
507 767 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
508 768 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
509 769 node--debug: 3bdecc1cde0c3d5fa6eaee3d9d9828f6ac468d57
510 770 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
511 771 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
512 772 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
513 773 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
514 774 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
515 775 node--debug: 97054abb4ab824450e9164180baf491ae0078465
516 776 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
517 777 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
518 778 parents:
519 779 parents: -1:000000000000
520 780 parents: 5:13207e5a10d9 4:32a18f097fcc
521 781 parents: 3:10e46f2dcbf4
522 782 parents:
523 783 parents:
524 784 parents:
525 785 parents:
526 786 parents:
527 787 parents--verbose:
528 788 parents--verbose: -1:000000000000
529 789 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
530 790 parents--verbose: 3:10e46f2dcbf4
531 791 parents--verbose:
532 792 parents--verbose:
533 793 parents--verbose:
534 794 parents--verbose:
535 795 parents--verbose:
536 796 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
537 797 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
538 798 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
539 799 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
540 800 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
541 801 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
542 802 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
543 803 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
544 804 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
545 805 rev: 8
546 806 rev: 7
547 807 rev: 6
548 808 rev: 5
549 809 rev: 4
550 810 rev: 3
551 811 rev: 2
552 812 rev: 1
553 813 rev: 0
554 814 rev--verbose: 8
555 815 rev--verbose: 7
556 816 rev--verbose: 6
557 817 rev--verbose: 5
558 818 rev--verbose: 4
559 819 rev--verbose: 3
560 820 rev--verbose: 2
561 821 rev--verbose: 1
562 822 rev--verbose: 0
563 823 rev--debug: 8
564 824 rev--debug: 7
565 825 rev--debug: 6
566 826 rev--debug: 5
567 827 rev--debug: 4
568 828 rev--debug: 3
569 829 rev--debug: 2
570 830 rev--debug: 1
571 831 rev--debug: 0
572 832 tags: tip
573 833 tags:
574 834 tags:
575 835 tags:
576 836 tags:
577 837 tags:
578 838 tags:
579 839 tags:
580 840 tags:
581 841 tags--verbose: tip
582 842 tags--verbose:
583 843 tags--verbose:
584 844 tags--verbose:
585 845 tags--verbose:
586 846 tags--verbose:
587 847 tags--verbose:
588 848 tags--verbose:
589 849 tags--verbose:
590 850 tags--debug: tip
591 851 tags--debug:
592 852 tags--debug:
593 853 tags--debug:
594 854 tags--debug:
595 855 tags--debug:
596 856 tags--debug:
597 857 tags--debug:
598 858 tags--debug:
599 859 diffstat: 3: +2/-1
600 860 diffstat: 1: +1/-0
601 861 diffstat: 0: +0/-0
602 862 diffstat: 1: +1/-0
603 863 diffstat: 0: +0/-0
604 864 diffstat: 1: +1/-0
605 865 diffstat: 1: +4/-0
606 866 diffstat: 1: +2/-0
607 867 diffstat: 1: +1/-0
608 868 diffstat--verbose: 3: +2/-1
609 869 diffstat--verbose: 1: +1/-0
610 870 diffstat--verbose: 0: +0/-0
611 871 diffstat--verbose: 1: +1/-0
612 872 diffstat--verbose: 0: +0/-0
613 873 diffstat--verbose: 1: +1/-0
614 874 diffstat--verbose: 1: +4/-0
615 875 diffstat--verbose: 1: +2/-0
616 876 diffstat--verbose: 1: +1/-0
617 877 diffstat--debug: 3: +2/-1
618 878 diffstat--debug: 1: +1/-0
619 879 diffstat--debug: 0: +0/-0
620 880 diffstat--debug: 1: +1/-0
621 881 diffstat--debug: 0: +0/-0
622 882 diffstat--debug: 1: +1/-0
623 883 diffstat--debug: 1: +4/-0
624 884 diffstat--debug: 1: +2/-0
625 885 diffstat--debug: 1: +1/-0
626 886 extras: branch=default
627 887 extras: branch=default
628 888 extras: branch=default
629 889 extras: branch=default
630 890 extras: branch=foo
631 891 extras: branch=default
632 892 extras: branch=default
633 893 extras: branch=default
634 894 extras: branch=default
635 895 extras--verbose: branch=default
636 896 extras--verbose: branch=default
637 897 extras--verbose: branch=default
638 898 extras--verbose: branch=default
639 899 extras--verbose: branch=foo
640 900 extras--verbose: branch=default
641 901 extras--verbose: branch=default
642 902 extras--verbose: branch=default
643 903 extras--verbose: branch=default
644 904 extras--debug: branch=default
645 905 extras--debug: branch=default
646 906 extras--debug: branch=default
647 907 extras--debug: branch=default
648 908 extras--debug: branch=foo
649 909 extras--debug: branch=default
650 910 extras--debug: branch=default
651 911 extras--debug: branch=default
652 912 extras--debug: branch=default
653 913 # filters work
654 914
655 915 hostname
656 916
657 917
658 918
659 919
660 920 place
661 921 place
662 922 hostname
663 923 test
664 924 User Name
665 925 person
666 926 person
667 927 person
668 928 person
669 929 other
670 930 A. N. Other
671 931 User Name
672 932 test
673 933 user
674 934 person
675 935 person
676 936 person
677 937 person
678 938 other
679 939 other
680 940 user
681 941 in the future
682 942 Wed Jan 01 10:01:00 2020 +0000
683 943 Mon Jan 12 13:46:40 1970 +0000
684 944 Sun Jan 18 08:40:01 1970 +0000
685 945 Sun Jan 18 08:40:00 1970 +0000
686 946 Sat Jan 17 04:53:20 1970 +0000
687 947 Fri Jan 16 01:06:40 1970 +0000
688 948 Wed Jan 14 21:20:00 1970 +0000
689 949 Tue Jan 13 17:33:20 1970 +0000
690 950 Mon Jan 12 13:46:40 1970 +0000
691 951 2020-01-01 10:01 +0000
692 952 1970-01-12 13:46 +0000
693 953 1970-01-18 08:40 +0000
694 954 1970-01-18 08:40 +0000
695 955 1970-01-17 04:53 +0000
696 956 1970-01-16 01:06 +0000
697 957 1970-01-14 21:20 +0000
698 958 1970-01-13 17:33 +0000
699 959 1970-01-12 13:46 +0000
700 960 2020-01-01 10:01:00 +0000
701 961 1970-01-12 13:46:40 +0000
702 962 1970-01-18 08:40:01 +0000
703 963 1970-01-18 08:40:00 +0000
704 964 1970-01-17 04:53:20 +0000
705 965 1970-01-16 01:06:40 +0000
706 966 1970-01-14 21:20:00 +0000
707 967 1970-01-13 17:33:20 +0000
708 968 1970-01-12 13:46:40 +0000
709 969 Wed, 01 Jan 2020 10:01:00 +0000
710 970 Mon, 12 Jan 1970 13:46:40 +0000
711 971 Sun, 18 Jan 1970 08:40:01 +0000
712 972 Sun, 18 Jan 1970 08:40:00 +0000
713 973 Sat, 17 Jan 1970 04:53:20 +0000
714 974 Fri, 16 Jan 1970 01:06:40 +0000
715 975 Wed, 14 Jan 1970 21:20:00 +0000
716 976 Tue, 13 Jan 1970 17:33:20 +0000
717 977 Mon, 12 Jan 1970 13:46:40 +0000
718 978 third
719 979 second
720 980 merge
721 981 new head
722 982 new branch
723 983 no user, no domain
724 984 no person
725 985 other 1
726 986 line 1
727 987 3bdecc1cde0c
728 988 29114dbae42b
729 989 c7b487c6c50e
730 990 13207e5a10d9
731 991 32a18f097fcc
732 992 10e46f2dcbf4
733 993 97054abb4ab8
734 994 b608e9d1a3f0
735 995 1e4e1b8f71e0
736 996 <changeset author="test"/>
737 997 <changeset author="User Name &lt;user@hostname&gt;"/>
738 998 <changeset author="person"/>
739 999 <changeset author="person"/>
740 1000 <changeset author="person"/>
741 1001 <changeset author="person"/>
742 1002 <changeset author="other@place"/>
743 1003 <changeset author="A. N. Other &lt;other@place&gt;"/>
744 1004 <changeset author="User Name &lt;user@hostname&gt;"/>
745 1005 # formatnode filter works
746 1006 # quiet
747 1007 1e4e1b8f71e0
748 1008 # normal
749 1009 1e4e1b8f71e0
750 1010 # verbose
751 1011 1e4e1b8f71e0
752 1012 # debug
753 1013 1e4e1b8f71e05681d422154f5421e385fec3454f
754 1014 # error on syntax
755 1015 abort: t:3: unmatched quotes
756 1016 # latesttag
757 1017 adding file
758 1018 adding head1
759 1019 adding head2
760 1020 created new head
761 1021 # No tag set
762 1022 5: null+5
763 1023 4: null+4
764 1024 3: null+3
765 1025 2: null+3
766 1026 1: null+2
767 1027 0: null+1
768 1028 # one common tag: longuest path wins
769 1029 6: t1+4
770 1030 5: t1+3
771 1031 4: t1+2
772 1032 3: t1+1
773 1033 2: t1+1
774 1034 1: t1+0
775 1035 0: null+1
776 1036 # one ancestor tag: more recent wins
777 1037 7: t2+3
778 1038 6: t2+2
779 1039 5: t2+1
780 1040 4: t1+2
781 1041 3: t1+1
782 1042 2: t2+0
783 1043 1: t1+0
784 1044 0: null+1
785 1045 # two branch tags: more recent wins
786 1046 8: t3+5
787 1047 7: t3+4
788 1048 6: t3+3
789 1049 5: t3+2
790 1050 4: t3+1
791 1051 3: t3+0
792 1052 2: t2+0
793 1053 1: t1+0
794 1054 0: null+1
795 1055 # merged tag overrides
796 1056 10: t5+5
797 1057 9: t5+4
798 1058 8: t5+3
799 1059 7: t5+2
800 1060 6: t5+1
801 1061 5: t5+0
802 1062 4: at3:t3+1
803 1063 3: at3:t3+0
804 1064 2: t2+0
805 1065 1: t1+0
806 1066 0: null+1
807 1067 # done
General Comments 0
You need to be logged in to leave comments. Login now