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