# HG changeset patch # User Vadim Gelfer # Date 1142209319 28800 # Node ID b7cc0f323a4cd98e70f45c18830484dd5a74b03d # Parent 8f565af14095d8c44e188d441d5c70f03f95bd48# Parent d7c038e805e91f79bfa108175fba6b957a968e35 merge with crew. diff -r d7c038e805e9 -r b7cc0f323a4c doc/hg.1.txt diff -r d7c038e805e9 -r b7cc0f323a4c doc/hgrc.5.txt --- a/doc/hgrc.5.txt Mon Mar 13 00:02:33 2006 +0100 +++ b/doc/hgrc.5.txt Sun Mar 12 16:21:59 2006 -0800 @@ -238,6 +238,10 @@ The editor to use during a commit. Default is $EDITOR or "vi". interactive;; Allow to prompt the user. True or False. Default is True. + logtemplate;; + Template string for commands that print changesets. + style;; + Name of style to use for command output. merge;; The conflict resolution program to use during a manual merge. Default is "hgmerge". diff -r d7c038e805e9 -r b7cc0f323a4c mercurial/commands.py --- a/mercurial/commands.py Mon Mar 13 00:02:33 2006 +0100 +++ b/mercurial/commands.py Sun Mar 12 16:21:59 2006 -0800 @@ -9,7 +9,7 @@ from node import * from i18n import gettext as _ demandload(globals(), "os re sys signal shutil imp urllib pdb") -demandload(globals(), "fancyopts ui hg util lock revlog") +demandload(globals(), "fancyopts ui hg util lock revlog templater") demandload(globals(), "fnmatch hgweb mdiff random signal time traceback") demandload(globals(), "errno socket version struct atexit sets bz2") @@ -339,63 +339,265 @@ user = revcache[rev] = ui.shortuser(name) return user -def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None): - """show a single changeset or file revision""" - log = repo.changelog - if changenode is None: - changenode = log.node(rev) - elif not rev: - rev = log.rev(changenode) - - if ui.quiet: - ui.write("%d:%s\n" % (rev, short(changenode))) - return - - changes = log.read(changenode) - date = util.datestr(changes[2]) - - parents = [(log.rev(p), ui.verbose and hex(p) or short(p)) - for p in log.parents(changenode) - if ui.debugflag or p != nullid] - if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1: - parents = [] - - if ui.verbose: - ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode))) +class changeset_templater(object): + '''use templater module to format changeset information.''' + + def __init__(self, ui, repo, mapfile): + self.t = templater.templater(mapfile, templater.common_filters, + cache={'parent': '{rev}:{node|short} ', + 'manifest': '{rev}:{node|short}'}) + self.ui = ui + self.repo = repo + + def use_template(self, t): + '''set template string to use''' + self.t.cache['changeset'] = t + + def write(self, thing): + '''write expanded template. + uses in-order recursive traverse of iterators.''' + for t in thing: + if hasattr(t, '__iter__'): + self.write(t) + else: + self.ui.write(t) + + def show(self, rev=0, changenode=None, brinfo=None): + '''show a single changeset or file revision''' + log = self.repo.changelog + if changenode is None: + changenode = log.node(rev) + elif not rev: + rev = log.rev(changenode) + + changes = log.read(changenode) + + def showlist(name, values, plural=None, **args): + '''expand set of values. + name is name of key in template map. + values is list of strings or dicts. + plural is plural of name, if not simply name + 's'. + + expansion works like this, given name 'foo'. + + if values is empty, expand 'no_foos'. + + if 'foo' not in template map, return values as a string, + joined by space. + + expand 'start_foos'. + + for each value, expand 'foo'. if 'last_foo' in template + map, expand it instead of 'foo' for last key. + + expand 'end_foos'. + ''' + if plural: names = plural + else: names = name + 's' + if not values: + noname = 'no_' + names + if noname in self.t: + yield self.t(noname, **args) + return + if name not in self.t: + if isinstance(values[0], str): + yield ' '.join(values) + else: + for v in values: + yield dict(v, **args) + return + startname = 'start_' + names + if startname in self.t: + yield self.t(startname, **args) + vargs = args.copy() + def one(v, tag=name): + try: + vargs.update(v) + except ValueError: + vargs.update([(name, v)]) + return self.t(tag, **vargs) + lastname = 'last_' + name + if lastname in self.t: + last = values.pop() + else: + last = None + for v in values: + yield one(v) + if last is not None: + yield one(last, tag=lastname) + endname = 'end_' + names + if endname in self.t: + yield self.t(endname, **args) + + if brinfo: + def showbranches(**args): + if changenode in brinfo: + for x in showlist('branch', brinfo[changenode], + plural='branches', **args): + yield x + else: + showbranches = '' + + if self.ui.debugflag: + def showmanifest(**args): + args = args.copy() + args.update(rev=self.repo.manifest.rev(changes[0]), + node=hex(changes[0])) + yield self.t('manifest', **args) + else: + showmanifest = '' + + def showparents(**args): + parents = [[('rev', log.rev(p)), ('node', hex(p))] + for p in log.parents(changenode) + if self.ui.debugflag or p != nullid] + if (not self.ui.debugflag and len(parents) == 1 and + parents[0][0][1] == rev - 1): + return + for x in showlist('parent', parents, **args): + yield x + + def showtags(**args): + for x in showlist('tag', self.repo.nodetags(changenode), **args): + yield x + + if self.ui.debugflag: + files = self.repo.changes(log.parents(changenode)[0], changenode) + def showfiles(**args): + for x in showlist('file', files[0], **args): yield x + def showadds(**args): + for x in showlist('file_add', files[1], **args): yield x + def showdels(**args): + for x in showlist('file_del', files[2], **args): yield x + else: + def showfiles(**args): + for x in showlist('file', changes[3], **args): yield x + showadds = '' + showdels = '' + + props = { + 'author': changes[1], + 'branches': showbranches, + 'date': changes[2], + 'desc': changes[4], + 'file_adds': showadds, + 'file_dels': showdels, + 'files': showfiles, + 'manifest': showmanifest, + 'node': hex(changenode), + 'parents': showparents, + 'rev': rev, + 'tags': showtags, + } + + try: + if self.ui.debugflag and 'changeset_debug' in self.t: + key = 'changeset_debug' + elif self.ui.quiet and 'changeset_quiet' in self.t: + key = 'changeset_quiet' + elif self.ui.verbose and 'changeset_verbose' in self.t: + key = 'changeset_verbose' + else: + key = 'changeset' + self.write(self.t(key, **props)) + except KeyError, inst: + raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile, + inst.args[0])) + except SyntaxError, inst: + raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0])) + +class changeset_printer(object): + '''show changeset information when templating not requested.''' + + def __init__(self, ui, repo): + self.ui = ui + self.repo = repo + + def show(self, rev=0, changenode=None, brinfo=None): + '''show a single changeset or file revision''' + log = self.repo.changelog + if changenode is None: + changenode = log.node(rev) + elif not rev: + rev = log.rev(changenode) + + if self.ui.quiet: + self.ui.write("%d:%s\n" % (rev, short(changenode))) + return + + changes = log.read(changenode) + date = util.datestr(changes[2]) + + parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p)) + for p in log.parents(changenode) + if self.ui.debugflag or p != nullid] + if (not self.ui.debugflag and len(parents) == 1 and + parents[0][0] == rev-1): + parents = [] + + if self.ui.verbose: + self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode))) + else: + self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode))) + + for tag in self.repo.nodetags(changenode): + self.ui.status(_("tag: %s\n") % tag) + for parent in parents: + self.ui.write(_("parent: %d:%s\n") % parent) + + if brinfo and changenode in brinfo: + br = brinfo[changenode] + self.ui.write(_("branch: %s\n") % " ".join(br)) + + self.ui.debug(_("manifest: %d:%s\n") % + (self.repo.manifest.rev(changes[0]), hex(changes[0]))) + self.ui.status(_("user: %s\n") % changes[1]) + self.ui.status(_("date: %s\n") % date) + + if self.ui.debugflag: + files = self.repo.changes(log.parents(changenode)[0], changenode) + for key, value in zip([_("files:"), _("files+:"), _("files-:")], + files): + if value: + self.ui.note("%-12s %s\n" % (key, " ".join(value))) + else: + self.ui.note(_("files: %s\n") % " ".join(changes[3])) + + description = changes[4].strip() + if description: + if self.ui.verbose: + self.ui.status(_("description:\n")) + self.ui.status(description) + self.ui.status("\n\n") + else: + self.ui.status(_("summary: %s\n") % + description.splitlines()[0]) + self.ui.status("\n") + +def show_changeset(ui, repo, opts): + '''show one changeset. uses template or regular display. caller + can pass in 'style' and 'template' options in opts.''' + + tmpl = opts.get('template') + if tmpl: + tmpl = templater.parsestring(tmpl, quoted=False) else: - ui.write(_("changeset: %d:%s\n") % (rev, short(changenode))) - - for tag in repo.nodetags(changenode): - ui.status(_("tag: %s\n") % tag) - for parent in parents: - ui.write(_("parent: %d:%s\n") % parent) - - if brinfo and changenode in brinfo: - br = brinfo[changenode] - ui.write(_("branch: %s\n") % " ".join(br)) - - ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]), - hex(changes[0]))) - ui.status(_("user: %s\n") % changes[1]) - ui.status(_("date: %s\n") % date) - - if ui.debugflag: - files = repo.changes(log.parents(changenode)[0], changenode) - for key, value in zip([_("files:"), _("files+:"), _("files-:")], files): - if value: - ui.note("%-12s %s\n" % (key, " ".join(value))) - else: - ui.note(_("files: %s\n") % " ".join(changes[3])) - - description = changes[4].strip() - if description: - if ui.verbose: - ui.status(_("description:\n")) - ui.status(description) - ui.status("\n\n") - else: - ui.status(_("summary: %s\n") % description.splitlines()[0]) - ui.status("\n") + tmpl = ui.config('ui', 'logtemplate') + if tmpl: tmpl = templater.parsestring(tmpl) + mapfile = opts.get('style') or ui.config('ui', 'style') + if tmpl or mapfile: + if mapfile: + if not os.path.isfile(mapfile): + mapname = templater.templatepath('map-cmdline.' + mapfile) + if not mapname: mapname = templater.templatepath(mapfile) + if mapname: mapfile = mapname + try: + t = changeset_templater(ui, repo, mapfile) + except SyntaxError, inst: + raise util.Abort(inst.args[0]) + if tmpl: t.use_template(tmpl) + return t + return changeset_printer(ui, repo) def show_version(ui): """output version and copyright information""" @@ -1425,8 +1627,9 @@ br = None if opts['branches']: br = repo.branchlookup(heads) + displayer = show_changeset(ui, repo, opts) for n in heads: - show_changeset(ui, repo, changenode=n, brinfo=br) + displayer.show(changenode=n, brinfo=br) def identify(ui, repo): """print information about the working copy @@ -1553,11 +1756,12 @@ o = other.changelog.nodesbetween(o)[0] if opts['newest_first']: o.reverse() + displayer = show_changeset(ui, other, opts) for n in o: parents = [p for p in other.changelog.parents(n) if p != nullid] if opts['no_merges'] and len(parents) == 2: continue - show_changeset(ui, other, changenode=n) + displayer.show(changenode=n) if opts['patch']: prev = (parents and parents[0]) or nullid dodiff(ui, ui, other, prev, n) @@ -1654,9 +1858,11 @@ limit = sys.maxint count = 0 + displayer = show_changeset(ui, repo, opts) for st, rev, fns in changeiter: if st == 'window': du = dui(ui) + displayer.ui = du elif st == 'add': du.bump(rev) changenode = repo.changelog.node(rev) @@ -1683,7 +1889,7 @@ if opts['branches']: br = repo.branchlookup([repo.changelog.node(rev)]) - show_changeset(du, repo, rev, brinfo=br) + displayer.show(rev, brinfo=br) if opts['patch']: prev = (parents and parents[0]) or nullid dodiff(du, du, repo, prev, changenode, match=matchfn) @@ -1736,17 +1942,18 @@ o = repo.changelog.nodesbetween(o)[0] if opts['newest_first']: o.reverse() + displayer = show_changeset(ui, repo, opts) for n in o: parents = [p for p in repo.changelog.parents(n) if p != nullid] if opts['no_merges'] and len(parents) == 2: continue - show_changeset(ui, repo, changenode=n) + displayer.show(changenode=n) if opts['patch']: prev = (parents and parents[0]) or nullid dodiff(ui, ui, repo, prev, n) ui.write("\n") -def parents(ui, repo, rev=None, branches=None): +def parents(ui, repo, rev=None, branches=None, **opts): """show the parents of the working dir or revision Print the working directory's parent revisions. @@ -1759,9 +1966,10 @@ br = None if branches is not None: br = repo.branchlookup(p) + displayer = show_changeset(ui, repo, opts) for n in p: if n != nullid: - show_changeset(ui, repo, changenode=n, brinfo=br) + displayer.show(changenode=n, brinfo=br) def paths(ui, repo, search=None): """show definition of symbolic path names @@ -2272,7 +2480,7 @@ br = None if opts['branches']: br = repo.branchlookup([n]) - show_changeset(ui, repo, changenode=n, brinfo=br) + show_changeset(ui, repo, opts).show(changenode=n, brinfo=br) if opts['patch']: dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n) @@ -2317,7 +2525,7 @@ repo.undo() def update(ui, repo, node=None, merge=False, clean=False, force=None, - branch=None): + branch=None, **opts): """update or merge working directory Update the working directory to the specified revision. @@ -2344,7 +2552,7 @@ if len(found) > 1: ui.warn(_("Found multiple heads for %s\n") % branch) for x in found: - show_changeset(ui, repo, changenode=x, brinfo=br) + show_changeset(ui, repo, opts).show(changenode=x, brinfo=br) return 1 if len(found) == 1: node = found[0] @@ -2488,7 +2696,9 @@ "heads": (heads, [('b', 'branches', None, _('show branches')), - ('r', 'rev', '', _('show only heads which are descendants of rev'))], + ('', 'style', '', _('display using template map file')), + ('r', 'rev', '', _('show only heads which are descendants of rev')), + ('', 'template', '', _('display with template'))], _('hg heads [-b] [-r ]')), "help": (help_, [], _('hg help [COMMAND]')), "identify|id": (identify, [], _('hg identify')), @@ -2503,8 +2713,10 @@ _('hg import [-p NUM] [-b BASE] [-f] PATCH...')), "incoming|in": (incoming, [('M', 'no-merges', None, _('do not show merges')), + ('', 'style', '', _('display using template map file')), + ('n', 'newest-first', None, _('show newest record first')), ('p', 'patch', None, _('show patch')), - ('n', 'newest-first', None, _('show newest record first'))], + ('', 'template', '', _('display with template'))], _('hg incoming [-p] [-n] [-M] [SOURCE]')), "^init": (init, [], _('hg init [DEST]')), "locate": @@ -2524,8 +2736,10 @@ ('l', 'limit', '', _('limit number of changes displayed')), ('r', 'rev', [], _('show the specified revision or range')), ('M', 'no-merges', None, _('do not show merges')), + ('', 'style', '', _('display using template map file')), ('m', 'only-merges', None, _('show only merges')), ('p', 'patch', None, _('show patch')), + ('', 'template', '', _('display with template')), ('I', 'include', [], _('include names matching the given patterns')), ('X', 'exclude', [], _('exclude names matching the given patterns'))], _('hg log [OPTION]... [FILE]')), @@ -2533,11 +2747,15 @@ "outgoing|out": (outgoing, [('M', 'no-merges', None, _('do not show merges')), ('p', 'patch', None, _('show patch')), - ('n', 'newest-first', None, _('show newest record first'))], + ('', 'style', '', _('display using template map file')), + ('n', 'newest-first', None, _('show newest record first')), + ('', 'template', '', _('display with template'))], _('hg outgoing [-M] [-p] [-n] [DEST]')), "^parents": (parents, - [('b', 'branches', None, _('show branches'))], + [('b', 'branches', None, _('show branches')), + ('', 'style', '', _('display using template map file')), + ('', 'template', '', _('display with template'))], _('hg parents [-b] [REV]')), "paths": (paths, [], _('hg paths [NAME]')), "^pull": @@ -2629,7 +2847,9 @@ "tip": (tip, [('b', 'branches', None, _('show branches')), - ('p', 'patch', None, _('show patch'))], + ('', 'style', '', _('display using template map file')), + ('p', 'patch', None, _('show patch')), + ('', 'template', '', _('display with template'))], _('hg tip [-b] [-p]')), "unbundle": (unbundle, @@ -2640,9 +2860,11 @@ "^update|up|checkout|co": (update, [('b', 'branch', '', _('checkout the head of a specific branch')), + ('', 'style', '', _('display using template map file')), ('m', 'merge', None, _('allow merging of branches')), ('C', 'clean', None, _('overwrite locally modified files')), - ('f', 'force', None, _('force a merge with outstanding changes'))], + ('f', 'force', None, _('force a merge with outstanding changes')), + ('', 'template', '', _('display with template'))], _('hg update [-b TAG] [-m] [-C] [-f] [REV]')), "verify": (verify, [], _('hg verify')), "version": (show_version, [], _('hg version')), @@ -2909,7 +3131,7 @@ help_(u, 'shortlist') sys.exit(-1) except AmbiguousCommand, inst: - u.warn(_("hg: command '%s' is ambiguous:\n %s\n") % + u.warn(_("hg: command '%s' is ambiguous:\n %s\n") % (inst.args[0], " ".join(inst.args[1]))) sys.exit(1) except UnknownCommand, inst: diff -r d7c038e805e9 -r b7cc0f323a4c mercurial/hgweb.py --- a/mercurial/hgweb.py Mon Mar 13 00:02:33 2006 +0100 +++ b/mercurial/hgweb.py Sun Mar 12 16:21:59 2006 -0800 @@ -6,58 +6,15 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os, cgi, sys, urllib +import os, cgi, sys import mimetypes from demandload import demandload demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser") demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util") -demandload(globals(), "mimetypes") +demandload(globals(), "mimetypes templater") from node import * from i18n import gettext as _ -def templatepath(): - for f in "templates", "../templates": - p = os.path.join(os.path.dirname(__file__), f) - if os.path.isdir(p): - return os.path.normpath(p) - else: - # executable version (py2exe) doesn't support __file__ - if hasattr(sys, 'frozen'): - return os.path.join(sys.prefix, "templates") - -def age(x): - def plural(t, c): - if c == 1: - return t - return t + "s" - def fmt(t, c): - return "%d %s" % (c, plural(t, c)) - - now = time.time() - then = x[0] - delta = max(1, int(now - then)) - - scales = [["second", 1], - ["minute", 60], - ["hour", 3600], - ["day", 3600 * 24], - ["week", 3600 * 24 * 7], - ["month", 3600 * 24 * 30], - ["year", 3600 * 24 * 365]] - - scales.reverse() - - for t, s in scales: - n = delta / s - if n >= 2 or s == 1: - return fmt(t, n) - -def nl2br(text): - return text.replace('\n', '
\n') - -def obfuscate(text): - return ''.join(['&#%d;' % ord(c) for c in text]) - def up(p): if p[0] != "/": p = "/" + p @@ -133,78 +90,6 @@ headers.append(('Content-length', str(size))) self.header(headers) -class templater(object): - def __init__(self, mapfile, filters={}, defaults={}): - self.cache = {} - self.map = {} - self.base = os.path.dirname(mapfile) - self.filters = filters - self.defaults = defaults - - for l in file(mapfile): - m = re.match(r'(\S+)\s*=\s*"(.*)"$', l) - if m: - self.cache[m.group(1)] = m.group(2) - else: - m = re.match(r'(\S+)\s*=\s*(\S+)', l) - if m: - self.map[m.group(1)] = os.path.join(self.base, m.group(2)) - else: - raise LookupError(_("unknown map entry '%s'") % l) - - def __call__(self, t, **map): - m = self.defaults.copy() - m.update(map) - try: - tmpl = self.cache[t] - except KeyError: - tmpl = self.cache[t] = file(self.map[t]).read() - return self.template(tmpl, self.filters, **m) - - def template(self, tmpl, filters={}, **map): - while tmpl: - m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl) - if m: - yield tmpl[:m.start(0)] - v = map.get(m.group(1), "") - v = callable(v) and v(**map) or v - - format = m.group(2) - fl = m.group(4) - - if format: - q = v.__iter__ - for i in q(): - lm = map.copy() - lm.update(i) - yield self(format[1:], **lm) - - v = "" - - elif fl: - for f in fl.split("|")[1:]: - v = filters[f](v) - - yield v - tmpl = tmpl[m.end(0):] - else: - yield tmpl - return - -common_filters = { - "escape": lambda x: cgi.escape(x, True), - "urlescape": urllib.quote, - "strip": lambda x: x.strip(), - "age": age, - "date": lambda x: util.datestr(x), - "addbreaks": nl2br, - "obfuscate": obfuscate, - "short": (lambda x: x[:12]), - "firstline": (lambda x: x.splitlines(1)[0]), - "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"), - "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), - } - class hgweb(object): def __init__(self, repo, name=None): if type(repo) == type(""): @@ -889,7 +774,7 @@ expand_form(req.form) - t = self.repo.ui.config("web", "templates", templatepath()) + t = self.repo.ui.config("web", "templates", templater.templatepath()) static = self.repo.ui.config("web", "static", os.path.join(t,"static")) m = os.path.join(t, "map") style = self.repo.ui.config("web", "style", "") @@ -911,12 +796,12 @@ self.reponame = (self.repo.ui.config("web", "name") or uri.strip('/') or self.repo.root) - self.t = templater(m, common_filters, - {"url": url, - "repo": self.reponame, - "header": header, - "footer": footer, - }) + self.t = templater.templater(m, templater.common_filters, + {"url": url, + "repo": self.reponame, + "header": header, + "footer": footer, + }) if not req.form.has_key('cmd'): req.form['cmd'] = [self.t.cache['default'],] @@ -1143,9 +1028,9 @@ def footer(**map): yield tmpl("footer", **map) - m = os.path.join(templatepath(), "map") - tmpl = templater(m, common_filters, - {"header": header, "footer": footer}) + m = os.path.join(templater.templatepath(), "map") + tmpl = templater.templater(m, templater.common_filters, + {"header": header, "footer": footer}) def entries(**map): parity = 0 @@ -1191,7 +1076,7 @@ req.write(tmpl("notfound", repo=virtual)) else: if req.form.has_key('static'): - static = os.path.join(templatepath(), "static") + static = os.path.join(templater.templatepath(), "static") fname = req.form['static'][0] req.write(staticfile(static, fname) or tmpl("error", error="%r not found" % fname)) diff -r d7c038e805e9 -r b7cc0f323a4c mercurial/templater.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templater.py Sun Mar 12 16:21:59 2006 -0800 @@ -0,0 +1,237 @@ +# templater.py - template expansion for output +# +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +import re +from demandload import demandload +from i18n import gettext as _ +demandload(globals(), "cStringIO cgi os time urllib util") + +esctable = { + '\\': '\\', + 'r': '\r', + 't': '\t', + 'n': '\n', + 'v': '\v', + } + +def parsestring(s, quoted=True): + '''parse a string using simple c-like syntax. + string must be in quotes if quoted is True.''' + fp = cStringIO.StringIO() + if quoted: + first = s[0] + if len(s) < 2: raise SyntaxError(_('string too short')) + if first not in "'\"": raise SyntaxError(_('invalid quote')) + if s[-1] != first: raise SyntaxError(_('unmatched quotes')) + s = s[1:-1] + escape = False + for c in s: + if escape: + fp.write(esctable.get(c, c)) + escape = False + elif c == '\\': escape = True + elif quoted and c == first: raise SyntaxError(_('string ends early')) + else: fp.write(c) + if escape: raise SyntaxError(_('unterminated escape')) + return fp.getvalue() + +class templater(object): + '''template expansion engine. + + template expansion works like this. a map file contains key=value + pairs. if value is quoted, it is treated as string. otherwise, it + is treated as name of template file. + + templater is asked to expand a key in map. it looks up key, and + looks for atrings like this: {foo}. it expands {foo} by looking up + foo in map, and substituting it. expansion is recursive: it stops + when there is no more {foo} to replace. + + expansion also allows formatting and filtering. + + format uses key to expand each item in list. syntax is + {key%format}. + + filter uses function to transform value. syntax is + {key|filter1|filter2|...}.''' + + def __init__(self, mapfile, filters={}, cache={}): + '''set up template engine. + mapfile is name of file to read map definitions from. + filters is dict of functions. each transforms a value into another. + defaults is dict of default map definitions.''' + self.mapfile = mapfile or 'template' + self.cache = {} + self.map = {} + self.base = (mapfile and os.path.dirname(mapfile)) or '' + self.filters = filters + self.defaults = {} + self.cache = cache + + if not mapfile: + return + i = 0 + for l in file(mapfile): + l = l.strip() + i += 1 + if not l or l[0] in '#;': continue + m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l) + if m: + key, val = m.groups() + if val[0] in "'\"": + try: + self.cache[key] = parsestring(val) + except SyntaxError, inst: + raise SyntaxError('%s:%s: %s' % + (mapfile, i, inst.args[0])) + else: + self.map[key] = os.path.join(self.base, val) + else: + raise SyntaxError(_("%s:%s: parse error") % (mapfile, i)) + + def __contains__(self, key): + return key in self.cache + + def __call__(self, t, **map): + '''perform expansion. + t is name of map element to expand. + map is added elements to use during expansion.''' + m = self.defaults.copy() + m.update(map) + try: + tmpl = self.cache[t] + except KeyError: + try: + tmpl = self.cache[t] = file(self.map[t]).read() + except IOError, inst: + raise IOError(inst.args[0], _('template file %s: %s') % + (self.map[t], inst.args[1])) + return self.template(tmpl, self.filters, **m) + + template_re = re.compile(r"[#{]([a-zA-Z_][a-zA-Z0-9_]*)" + r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)" + r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)[#}]") + + def template(self, tmpl, filters={}, **map): + lm = map.copy() + while tmpl: + m = self.template_re.search(tmpl) + if m: + start, end = m.span(0) + s, e = tmpl[start], tmpl[end - 1] + key = m.group(1) + if ((s == '#' and e != '#') or (s == '{' and e != '}')): + raise SyntaxError(_("'%s'/'%s' mismatch expanding '%s'") % + (s, e, key)) + if start: + yield tmpl[:start] + v = map.get(key, "") + v = callable(v) and v(**map) or v + + format = m.group(2) + fl = m.group(4) + + if format: + q = v.__iter__ + for i in q(): + lm.update(i) + yield self(format[1:], **lm) + + v = "" + + elif fl: + for f in fl.split("|")[1:]: + v = filters[f](v) + + yield v + tmpl = tmpl[end:] + else: + yield tmpl + break + +agescales = [("second", 1), + ("minute", 60), + ("hour", 3600), + ("day", 3600 * 24), + ("week", 3600 * 24 * 7), + ("month", 3600 * 24 * 30), + ("year", 3600 * 24 * 365)] + +agescales.reverse() + +def age(date): + '''turn a (timestamp, tzoff) tuple into an age string.''' + + def plural(t, c): + if c == 1: + return t + return t + "s" + def fmt(t, c): + return "%d %s" % (c, plural(t, c)) + + now = time.time() + then = date[0] + delta = max(1, int(now - then)) + + for t, s in agescales: + n = delta / s + if n >= 2 or s == 1: + return fmt(t, n) + +def isodate(date): + '''turn a (timestamp, tzoff) tuple into an iso 8631 date.''' + return util.datestr(date, format='%Y-%m-%d %H:%M') + +def nl2br(text): + '''replace raw newlines with xhtml line breaks.''' + return text.replace('\n', '
\n') + +def obfuscate(text): + return ''.join(['&#%d;' % ord(c) for c in text]) + +def domain(author): + '''get domain of author, or empty string if none.''' + f = author.find('@') + if f == -1: return '' + author = author[f+1:] + f = author.find('>') + if f >= 0: author = author[:f] + return author + +def person(author): + '''get name of author, or else username.''' + f = author.find('<') + if f == -1: return util.shortuser(author) + return author[:f].rstrip() + +common_filters = { + "addbreaks": nl2br, + "age": age, + "date": lambda x: util.datestr(x), + "domain": domain, + "escape": lambda x: cgi.escape(x, True), + "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'), + "isodate": isodate, + "obfuscate": obfuscate, + "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--", + "person": person, + "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), + "short": lambda x: x[:12], + "strip": lambda x: x.strip(), + "urlescape": lambda x: urllib.quote(x), + "user": lambda x: util.shortuser(x), + } + +def templatepath(name=None): + '''return location of template file or directory (if no name). + returns None if not found.''' + for f in 'templates', '../templates': + fl = f.split('/') + if name: fl.append(name) + p = os.path.join(os.path.dirname(__file__), *fl) + if (name and os.path.exists(p)) or os.path.isdir(p): + return os.path.normpath(p) diff -r d7c038e805e9 -r b7cc0f323a4c mercurial/ui.py --- a/mercurial/ui.py Mon Mar 13 00:02:33 2006 +0100 +++ b/mercurial/ui.py Sun Mar 12 16:21:59 2006 -0800 @@ -150,13 +150,7 @@ def shortuser(self, user): """Return a short representation of a user name or email address.""" - if not self.verbose: - f = user.find('@') - if f >= 0: - user = user[:f] - f = user.find('<') - if f >= 0: - user = user[f+1:] + if not self.verbose: user = util.shortuser(user) return user def expandpath(self, loc): diff -r d7c038e805e9 -r b7cc0f323a4c mercurial/util.py --- a/mercurial/util.py Mon Mar 13 00:02:33 2006 +0100 +++ b/mercurial/util.py Sun Mar 12 16:21:59 2006 -0800 @@ -746,6 +746,16 @@ -tz / 3600, ((-tz % 3600) / 60))) +def shortuser(user): + """Return a short representation of a user name or email address.""" + f = user.find('@') + if f >= 0: + user = user[:f] + f = user.find('<') + if f >= 0: + user = user[f+1:] + return user + def walkrepos(path): '''yield every hg repository under path, recursively.''' def errhandler(err): diff -r d7c038e805e9 -r b7cc0f323a4c templates/map --- a/templates/map Mon Mar 13 00:02:33 2006 +0100 +++ b/templates/map Sun Mar 12 16:21:59 2006 -0800 @@ -1,50 +1,50 @@ -default = "changelog" +default = 'changelog' header = header.tmpl footer = footer.tmpl search = search.tmpl changelog = changelog.tmpl -naventry = "#label|escape# " -filedifflink = "#file|escape# " -filenodelink = "#file|escape# " -fileellipses = "..." +naventry = '#label|escape# ' +filedifflink = '#file|escape# ' +filenodelink = '#file|escape# ' +fileellipses = '...' changelogentry = changelogentry.tmpl searchentry = changelogentry.tmpl changeset = changeset.tmpl manifest = manifest.tmpl -manifestdirentry = "drwxr-xr-x #basename|escape#/" -manifestfileentry = "#permissions|permissions# #basename|escape#" +manifestdirentry = 'drwxr-xr-x #basename|escape#/' +manifestfileentry = '#permissions|permissions# #basename|escape#' filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl filediff = filediff.tmpl filelog = filelog.tmpl -fileline = "
#linenumber##line|escape#
" +fileline = '
#linenumber##line|escape#
' filelogentry = filelogentry.tmpl -annotateline = "#author|obfuscate#@#rev#
#line|escape#
" -difflineplus = "#line|escape#" -difflineminus = "#line|escape#" -difflineat = "#line|escape#" -diffline = "#line|escape#" -changelogparent = "parent #rev#:#node|short#" -changesetparent = "parent #rev#:#node|short#" -filerevparent = "parent:#node|short#" -filerename = "parent:#file|escape#@#node|short#" -filelogrename = "base: #file|escape#@#node|short#" -fileannotateparent = "parent:#node|short#" -changesetchild = "child #rev#:#node|short#" -changelogchild = "child #rev#:#node|short#" -filerevchild = "child:#node|short#" -fileannotatechild = "child:#node|short#" +annotateline = '#author|obfuscate#@#rev#
#line|escape#
' +difflineplus = '#line|escape#' +difflineminus = '#line|escape#' +difflineat = '#line|escape#' +diffline = '#line|escape#' +changelogparent = 'parent #rev#:#node|short#' +changesetparent = 'parent #rev#:#node|short#' +filerevparent = 'parent:#node|short#' +filerename = 'parent:#file|escape#@#node|short#' +filelogrename = 'base: #file|escape#@#node|short#' +fileannotateparent = 'parent:#node|short#' +changesetchild = 'child #rev#:#node|short#' +changelogchild = 'child #rev#:#node|short#' +filerevchild = 'child:#node|short#' +fileannotatechild = 'child:#node|short#' tags = tags.tmpl -tagentry = "
  • #node# #tag|escape#
  • " -diffblock = "
    #lines#
    " -changelogtag = "tag:#tag|escape#" -changesettag = "tag:#tag|escape#" -filediffparent = "parent #rev#:#node|short#" -filelogparent = "parent #rev#: #node|short#" -filediffchild = "child #rev#:#node|short#" -filelogchild = "child #rev#: #node|short#" -indexentry = "#name|escape##shortdesc##contact|obfuscate##lastupdate|age# agoRSS" +tagentry = '
  • #node# #tag|escape#
  • ' +diffblock = '
    #lines#
    ' +changelogtag = 'tag:#tag|escape#' +changesettag = 'tag:#tag|escape#' +filediffparent = 'parent #rev#:#node|short#' +filelogparent = 'parent #rev#: #node|short#' +filediffchild = 'child #rev#:#node|short#' +filelogchild = 'child #rev#: #node|short#' +indexentry = '#name|escape##shortdesc##contact|obfuscate##lastupdate|age# agoRSS' index = index.tmpl -archiveentry = "#type|escape# " +archiveentry = '#type|escape# ' notfound = notfound.tmpl error = error.tmpl diff -r d7c038e805e9 -r b7cc0f323a4c templates/map-cmdline.compact --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/map-cmdline.compact Sun Mar 12 16:21:59 2006 -0800 @@ -0,0 +1,8 @@ +changeset = '{rev}{tags}{parents} {node|short} {date|isodate} {author|user}\n {desc|firstline|strip}\n\n' +changeset_quiet = '{rev}:{node|short}\n' +start_tags = '[' +tag = '{tag},' +last_tag = '{tag}]' +start_parents = ':' +parent = '{rev},' +last_parent = '{rev}' diff -r d7c038e805e9 -r b7cc0f323a4c templates/map-cmdline.default --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/map-cmdline.default Sun Mar 12 16:21:59 2006 -0800 @@ -0,0 +1,13 @@ +changeset = 'changeset: {rev}:{node|short}\n{tags}{short_parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n' +changeset_quiet = '{rev}:{node|short}\n' +changeset_verbose = 'changeset: {rev}:{node}\n{tags}{parents}{manifest}user: {author}\ndate: {date|date}\nfiles: {files}\n{file_adds}{file_dels}description:\n{desc|strip}\n\n\n' +start_file_adds = 'files+: ' +file_add = ' {file_add}' +end_file_adds = '\n' +start_file_dels = 'files-: ' +file_del = ' {file_del}' +end_file_dels = '\n' +short_parent = 'parent: {rev}:{node|short}\n' +parent = 'parent: {rev}:{node}\n' +manifest = 'manifest: {rev}:{node}\n' +tag = 'tag: {tag}\n' diff -r d7c038e805e9 -r b7cc0f323a4c templates/map-gitweb --- a/templates/map-gitweb Mon Mar 13 00:02:33 2006 +0100 +++ b/templates/map-gitweb Sun Mar 12 16:21:59 2006 -0800 @@ -1,50 +1,50 @@ -default = "summary" +default = 'summary' header = header-gitweb.tmpl footer = footer-gitweb.tmpl search = search-gitweb.tmpl changelog = changelog-gitweb.tmpl summary = summary-gitweb.tmpl error = error-gitweb.tmpl -naventry = "#label|escape# " -navshortentry = "#label|escape# " -filedifflink = "#file|escape# " -filenodelink = "#file|escape#file | revisions" -fileellipses = "..." +naventry = '#label|escape# ' +navshortentry = '#label|escape# ' +filedifflink = '#file|escape# ' +filenodelink = '#file|escape#file | revisions' +fileellipses = '...' changelogentry = changelogentry-gitweb.tmpl searchentry = changelogentry-gitweb.tmpl changeset = changeset-gitweb.tmpl manifest = manifest-gitweb.tmpl -manifestdirentry = "drwxr-xr-x#basename|escape#/manifest" -manifestfileentry = "#permissions|permissions##basename|escape#file | revisions | annotate" +manifestdirentry = 'drwxr-xr-x#basename|escape#/manifest' +manifestfileentry = '#permissions|permissions##basename|escape#file | revisions | annotate' filerevision = filerevision-gitweb.tmpl fileannotate = fileannotate-gitweb.tmpl filelog = filelog-gitweb.tmpl -fileline = "
    #linenumber# #line|escape#
    " -annotateline = "#author|obfuscate#@#rev##line|escape#" -difflineplus = "
    #line|escape#
    " -difflineminus = "
    #line|escape#
    " -difflineat = "
    #line|escape#
    " -diffline = "
    #line|escape#
    " -changelogparent = "parent #rev#:#node|short#" -changesetparent = "parent#node|short#" -filerevparent = "parent:#node|short#" -filerename = "parent:#file|escape#@#node|short#" -filelogrename = "| base" -fileannotateparent = "parent:#node|short#" -changelogchild = "child #rev#:#node|short#" -changesetchild = "child#node|short#" -filerevchild = "child:#node|short#" -fileannotatechild = "child:#node|short#" +fileline = '
    #linenumber# #line|escape#
    ' +annotateline = '#author|obfuscate#@#rev##line|escape#' +difflineplus = '
    #line|escape#
    ' +difflineminus = '
    #line|escape#
    ' +difflineat = '
    #line|escape#
    ' +diffline = '
    #line|escape#
    ' +changelogparent = 'parent #rev#:#node|short#' +changesetparent = 'parent#node|short#' +filerevparent = 'parent:#node|short#' +filerename = 'parent:#file|escape#@#node|short#' +filelogrename = '| base' +fileannotateparent = 'parent:#node|short#' +changelogchild = 'child #rev#:#node|short#' +changesetchild = 'child#node|short#' +filerevchild = 'child:#node|short#' +fileannotatechild = 'child:#node|short#' tags = tags-gitweb.tmpl -tagentry = "#date|age# ago#tag|escape#changeset | changelog | manifest" -diffblock = "#lines#" -changelogtag = "tag:#tag|escape#" -changesettag = "tag#tag|escape#" -filediffparent = "parent #rev#:#node|short#" -filelogparent = "parent #rev#: #node|short#" -filediffchild = "child #rev#:#node|short#" -filelogchild = "child #rev#: #node|short#" +tagentry = '#date|age# ago#tag|escape#changeset | changelog | manifest' +diffblock = '#lines#' +changelogtag = 'tag:#tag|escape#' +changesettag = 'tag#tag|escape#' +filediffparent = 'parent #rev#:#node|short#' +filelogparent = 'parent #rev#: #node|short#' +filediffchild = 'child #rev#:#node|short#' +filelogchild = 'child #rev#: #node|short#' shortlog = shortlog-gitweb.tmpl -shortlogentry = "#date|age# ago#desc|firstline|escape#changeset | manifest" -filelogentry = "#date|age# ago#desc|firstline|escape# annotate #rename%filelogrename#" -archiveentry = " | #type|escape# " +shortlogentry = '#date|age# ago#desc|firstline|escape#changeset | manifest' +filelogentry = '#date|age# ago#desc|firstline|escape# annotate #rename%filelogrename#' +archiveentry = ' | #type|escape# ' diff -r d7c038e805e9 -r b7cc0f323a4c templates/map-raw --- a/templates/map-raw Mon Mar 13 00:02:33 2006 +0100 +++ b/templates/map-raw Sun Mar 12 16:21:59 2006 -0800 @@ -1,16 +1,16 @@ header = header-raw.tmpl -footer = "" +footer = '' changeset = changeset-raw.tmpl -difflineplus = "#line#" -difflineminus = "#line#" -difflineat = "#line#" -diffline = "#line#" -changesetparent = "# parent: #node#" -changesetchild = "# child: #node#" -filenodelink = "" +difflineplus = '#line#' +difflineminus = '#line#' +difflineat = '#line#' +diffline = '#line#' +changesetparent = '# parent: #node#' +changesetchild = '# child: #node#' +filenodelink = '' filerevision = filerevision-raw.tmpl -fileline = "#line#" -diffblock = "#lines#" +fileline = '#line#' +diffblock = '#lines#' filediff = filediff-raw.tmpl fileannotate = fileannotate-raw.tmpl -annotateline = "#author#@#rev#: #line#" +annotateline = '#author#@#rev#: #line#' diff -r d7c038e805e9 -r b7cc0f323a4c templates/map-rss --- a/templates/map-rss Mon Mar 13 00:02:33 2006 +0100 +++ b/templates/map-rss Sun Mar 12 16:21:59 2006 -0800 @@ -1,4 +1,4 @@ -default = "changelog" +default = 'changelog' header = header-rss.tmpl changelog = changelog-rss.tmpl changelogentry = changelogentry-rss.tmpl diff -r d7c038e805e9 -r b7cc0f323a4c tests/test-command-template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-command-template Sun Mar 12 16:21:59 2006 -0800 @@ -0,0 +1,91 @@ +#!/bin/sh + +hg init a +cd a +echo a > a +hg add a +echo line 1 > b +echo line 2 >> b +hg commit -l b -d '1111111111 0' -u 'User Name ' +hg add b +echo other 1 > c +echo other 2 >> c +echo >> c +echo other 3 >> c +hg commit -l c -d '1123456789 0' -u 'A. N. Other ' +hg add c +hg commit -m 'no person' -d '1134567890 0' -u 'other@place' +echo c >> c +hg commit -m 'no user, no domain' -d '1144567890 0' -u 'person' + +# make sure user/global hgrc does not affect tests +echo '[ui]' > .hg/hgrc +echo 'logtemplate =' >> .hg/hgrc +echo 'style =' >> .hg/hgrc + +echo '# default style is like normal output' +hg log > log.out +hg log --style default > style.out +diff log.out style.out +hg log -v > log.out +hg log -v --style default > style.out +diff log.out style.out +hg log --debug > log.out +hg log --debug --style default > style.out +diff log.out style.out + +echo '# compact style works' +hg log --style compact +hg log -v --style compact +hg log --debug --style compact + +echo '# error if style not readable' +touch q +chmod 0 q +hg log --style ./q + +echo '# error if no style' +hg log --style notexist + +echo '# error if style missing key' +echo 'q = q' > t +hg log --style ./t + +echo '# error if include fails' +echo 'changeset = q' >> t +hg log --style ./t + +echo '# include works' +rm -f q +echo '{rev}' > q +hg log --style ./t + +echo '# ui.style works' +echo '[ui]' > .hg/hgrc +echo 'style = t' >> .hg/hgrc +hg log + +echo "# keys work" +for key in author branches date desc file_adds file_dels files \ + manifest node parents rev tags; do + for mode in '' --verbose --debug; do + hg log $mode --template "$key$mode: {$key}\n" + done +done + +echo '# filters work' +hg log --template '{author|domain}\n' +hg log --template '{author|person}\n' +hg log --template '{author|user}\n' +hg log --template '{date|age}\n' > /dev/null || exit 1 +hg log --template '{date|date}\n' +hg log --template '{date|isodate}\n' +hg log --template '{date|rfc822date}\n' +hg log --template '{desc|firstline}\n' +hg log --template '{node|short}\n' + +echo '# error on syntax' +echo 'x = "f' >> t +hg log + +echo '# done' diff -r d7c038e805e9 -r b7cc0f323a4c tests/test-command-template.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-command-template.out Sun Mar 12 16:21:59 2006 -0800 @@ -0,0 +1,261 @@ +# default style is like normal output +18a19 +> files: +29a31 +> files: +43a46 +> files: +# compact style works +3[tip] 2bacb094274c 2006-04-09 07:31 +0000 person + no user, no domain + +2 cdc488b3baa4 2005-12-14 13:44 +0000 other + no person + +1 55b647117689 2005-08-07 23:19 +0000 other + other 1 + +0 debcd367d974 2005-03-18 01:58 +0000 user + line 1 + +3[tip] 2bacb094274c 2006-04-09 07:31 +0000 person + no user, no domain + +2 cdc488b3baa4 2005-12-14 13:44 +0000 other + no person + +1 55b647117689 2005-08-07 23:19 +0000 other + other 1 + +0 debcd367d974 2005-03-18 01:58 +0000 user + line 1 + +3[tip]:2,-1 2bacb094274c 2006-04-09 07:31 +0000 person + no user, no domain + +2:1,-1 cdc488b3baa4 2005-12-14 13:44 +0000 other + no person + +1:0,-1 55b647117689 2005-08-07 23:19 +0000 other + other 1 + +0:-1,-1 debcd367d974 2005-03-18 01:58 +0000 user + line 1 + +# error if style not readable +abort: Permission denied - ./q +# error if no style +abort: No such file or directory - notexist +# error if style missing key +abort: ./t: no key named 'changeset' +# error if include fails +abort: template file ./q: Permission denied +# include works +3 +2 +1 +0 +# ui.style works +3 +2 +1 +0 +# keys work +author: person +author: other@place +author: A. N. Other +author: User Name +author--verbose: person +author--verbose: other@place +author--verbose: A. N. Other +author--verbose: User Name +author--debug: person +author--debug: other@place +author--debug: A. N. Other +author--debug: User Name +branches: +branches: +branches: +branches: +branches--verbose: +branches--verbose: +branches--verbose: +branches--verbose: +branches--debug: +branches--debug: +branches--debug: +branches--debug: +date: 1144567890.00 +date: 1134567890.00 +date: 1123456789.00 +date: 1111111111.00 +date--verbose: 1144567890.00 +date--verbose: 1134567890.00 +date--verbose: 1123456789.00 +date--verbose: 1111111111.00 +date--debug: 1144567890.00 +date--debug: 1134567890.00 +date--debug: 1123456789.00 +date--debug: 1111111111.00 +desc: no user, no domain +desc: no person +desc: other 1 +other 2 + +other 3 + +desc: line 1 +line 2 + +desc--verbose: no user, no domain +desc--verbose: no person +desc--verbose: other 1 +other 2 + +other 3 + +desc--verbose: line 1 +line 2 + +desc--debug: no user, no domain +desc--debug: no person +desc--debug: other 1 +other 2 + +other 3 + +desc--debug: line 1 +line 2 + +file_adds: +file_adds: +file_adds: +file_adds: +file_adds--verbose: +file_adds--verbose: +file_adds--verbose: +file_adds--verbose: +file_adds--debug: +file_adds--debug: c +file_adds--debug: b +file_adds--debug: a +file_dels: +file_dels: +file_dels: +file_dels: +file_dels--verbose: +file_dels--verbose: +file_dels--verbose: +file_dels--verbose: +file_dels--debug: +file_dels--debug: +file_dels--debug: +file_dels--debug: +files: c +files: c +files: b +files: a +files--verbose: c +files--verbose: c +files--verbose: b +files--verbose: a +files--debug: c +files--debug: +files--debug: +files--debug: +manifest: +manifest: +manifest: +manifest: +manifest--verbose: +manifest--verbose: +manifest--verbose: +manifest--verbose: +manifest--debug: 3:cb5a1327723b +manifest--debug: 2:6e0e82995c35 +manifest--debug: 1:4e8d705b1e53 +manifest--debug: 0:a0c8bcbbb45c +node: 2bacb094274c6ad120b419cab77a39e51b69cbaa +node: cdc488b3baa4a2cf316d4d85a3a1f17c5e1695d8 +node: 55b64711768911f37c6d244b12785623aa64e7c3 +node: debcd367d97455db85bba7b583b14b166172de25 +node--verbose: 2bacb094274c6ad120b419cab77a39e51b69cbaa +node--verbose: cdc488b3baa4a2cf316d4d85a3a1f17c5e1695d8 +node--verbose: 55b64711768911f37c6d244b12785623aa64e7c3 +node--verbose: debcd367d97455db85bba7b583b14b166172de25 +node--debug: 2bacb094274c6ad120b419cab77a39e51b69cbaa +node--debug: cdc488b3baa4a2cf316d4d85a3a1f17c5e1695d8 +node--debug: 55b64711768911f37c6d244b12785623aa64e7c3 +node--debug: debcd367d97455db85bba7b583b14b166172de25 +parents: +parents: +parents: +parents: +parents--verbose: +parents--verbose: +parents--verbose: +parents--verbose: +parents--debug: 2:cdc488b3baa4 -1:000000000000 +parents--debug: 1:55b647117689 -1:000000000000 +parents--debug: 0:debcd367d974 -1:000000000000 +parents--debug: -1:000000000000 -1:000000000000 +rev: 3 +rev: 2 +rev: 1 +rev: 0 +rev--verbose: 3 +rev--verbose: 2 +rev--verbose: 1 +rev--verbose: 0 +rev--debug: 3 +rev--debug: 2 +rev--debug: 1 +rev--debug: 0 +tags: tip +tags: +tags: +tags: +tags--verbose: tip +tags--verbose: +tags--verbose: +tags--verbose: +tags--debug: tip +tags--debug: +tags--debug: +tags--debug: +# filters work + +place +place +hostname +person +other +A. N. Other +User Name +person +other +other +user +Sun Apr 9 07:31:30 2006 +0000 +Wed Dec 14 13:44:50 2005 +0000 +Sun Aug 7 23:19:49 2005 +0000 +Fri Mar 18 01:58:31 2005 +0000 +2006-04-09 07:31 +0000 +2005-12-14 13:44 +0000 +2005-08-07 23:19 +0000 +2005-03-18 01:58 +0000 +Sun, 09 Apr 2006 07:31:30 +0000 +Wed, 14 Dec 2005 13:44:50 +0000 +Sun, 07 Aug 2005 23:19:49 +0000 +Fri, 18 Mar 2005 01:58:31 +0000 +no user, no domain +no person +other 1 +line 1 +2bacb094274c +cdc488b3baa4 +55b647117689 +debcd367d974 +# error on syntax +abort: t:3: unmatched quotes +# done