diff mercurial/commands.py @ 1907:7718885070b1

let commands that show changesets use templates. mechanism is same as hgweb templates. old show_changeset code is still used for now if no template given, because it is faster than template code when verbose or debug. simple template can be given on command line using -t, --template. example: hg log -t '{author|person}\n' complex template can be put in template map file, given on command line using --map-file. we give two example map files: map-log.compact prints 3 lines of output for every change. map-log.verbose prints exact same output as default "hg log -v". map files are searched where user says, then in template path as backup. example: hg log --map-file map-log.compact defaults can be set in hgrc with ui.logtemplate and ui.logmap.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Mon, 27 Feb 2006 13:18:57 -0800
parents 414e81ae971f
children be71c04d62c0
line wrap: on
line diff
--- a/mercurial/commands.py	Mon Feb 27 12:50:49 2006 -0800
+++ b/mercurial/commands.py	Mon Feb 27 13:18:57 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")
 
@@ -337,63 +337,219 @@
         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):
+    def __init__(self, ui, repo, mapfile):
+        self.t = templater.templater(mapfile, templater.common_filters)
+        self.ui = ui
+        self.repo = repo
+
+    def use_template(self, t):
+        self.t.cache['template'] = t
+
+    def write(self, thing):
+        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):
+            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
+            vargs = args.copy()
+            if name not in self.t:
+                yield ' '.join(values)
+                return
+            startname = 'start_' + names
+            if startname in self.t:
+                yield self.t(startname, **args)
+            def one(v):
+                try:
+                    vargs.update(v)
+                except ValueError:
+                    vargs.update([(name, v)])
+                return self.t(name, **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:
+                name = lastname
+                yield one(last)
+            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 = ''
+
+        def showmanifest(**args):
+            args = args.copy()
+            args.update(rev=self.repo.manifest.rev(changes[0]),
+                        node=hex(changes[0]))
+            yield self.t('manifest', **args)
+
+        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:
+            self.write(self.t('template', **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):
+    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):
+    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('map_file') or ui.config('ui', 'logmap')
+    if tmpl or mapfile:
+        if mapfile:
+            if not os.path.isfile(mapfile):
+                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"""
@@ -1409,8 +1565,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
@@ -1537,11 +1694,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)
@@ -1638,9 +1796,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)
@@ -1667,7 +1827,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)
@@ -1718,17 +1878,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.
@@ -1741,9 +1902,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, search=None):
     """show definition of symbolic path names
@@ -2246,7 +2408,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)
 
@@ -2291,7 +2453,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.
@@ -2318,7 +2480,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]
@@ -2462,7 +2624,9 @@
     "heads":
         (heads,
          [('b', 'branches', None, _('show branches')),
-          ('r', 'rev', '', _('show only heads which are descendants of rev'))],
+          ('', 'map-file', '', _('display using template map file')),
+          ('r', 'rev', '', _('show only heads which are descendants of rev')),
+          ('t', 'template', '', _('display with template'))],
          _('hg heads [-b] [-r <rev>]')),
     "help": (help_, [], _('hg help [COMMAND]')),
     "identify|id": (identify, [], _('hg identify')),
@@ -2477,8 +2641,10 @@
          _('hg import [-f] [-p NUM] [-b BASE] PATCH...')),
     "incoming|in": (incoming,
          [('M', 'no-merges', None, _('do not show merges')),
+          ('', 'map-file', '', _('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'))],
+          ('t', 'template', '', _('display with template'))],
          _('hg incoming [-p] [-n] [-M] [SOURCE]')),
     "^init": (init, [], _('hg init [DEST]')),
     "locate":
@@ -2500,18 +2666,24 @@
           ('l', 'limit', '', _('limit number of changes displayed')),
           ('r', 'rev', [], _('show the specified revision or range')),
           ('M', 'no-merges', None, _('do not show merges')),
+          ('', 'map-file', '', _('display using template map file')),
           ('m', 'only-merges', None, _('show only merges')),
-          ('p', 'patch', None, _('show patch'))],
+          ('p', 'patch', None, _('show patch')),
+          ('t', 'template', '', _('display with template'))],
          _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
     "manifest": (manifest, [], _('hg manifest [REV]')),
     "outgoing|out": (outgoing,
          [('M', 'no-merges', None, _('do not show merges')),
           ('p', 'patch', None, _('show patch')),
-          ('n', 'newest-first', None, _('show newest record first'))],
+          ('', 'map-file', '', _('display using template map file')),
+          ('n', 'newest-first', None, _('show newest record first')),
+          ('t', 'template', '', _('display with template'))],
          _('hg outgoing [-p] [-n] [-M] [DEST]')),
     "^parents":
         (parents,
-         [('b', 'branches', None, _('show branches'))],
+         [('b', 'branches', None, _('show branches')),
+          ('', 'map-file', '', _('display using template map file')),
+          ('t', 'template', '', _('display with template'))],
          _('hg parents [-b] [REV]')),
     "paths": (paths, [], _('hg paths [NAME]')),
     "^pull":
@@ -2602,7 +2774,9 @@
     "tip":
         (tip,
          [('b', 'branches', None, _('show branches')),
-          ('p', 'patch', None, _('show patch'))],
+          ('', 'map-file', '', _('display using template map file')),
+          ('p', 'patch', None, _('show patch')),
+          ('t', 'template', '', _('display with template'))],
          _('hg [-b] [-p] tip')),
     "unbundle":
         (unbundle,
@@ -2613,9 +2787,11 @@
     "^update|up|checkout|co":
         (update,
          [('b', 'branch', '', _('checkout the head of a specific branch')),
+          ('', 'map-file', '', _('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')),
+          ('t', 'template', '', _('display with template'))],
          _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
     "verify": (verify, [], _('hg verify')),
     "version": (show_version, [], _('hg version')),