changeset 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 9dec2479622d
children be71c04d62c0
files doc/hg.1.txt doc/hgrc.5.txt mercurial/commands.py templates/map-log.compact templates/map-log.verbose
diffstat 5 files changed, 292 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hg.1.txt	Mon Feb 27 12:50:49 2006 -0800
+++ b/doc/hg.1.txt	Mon Feb 27 13:18:57 2006 -0800
@@ -294,6 +294,12 @@
     changesets. They are where development generally takes place and
     are the usual targets for update and merge operations.
 
+    options:
+    -b, --branches         show branches
+    --map-file <file>      display using template map file
+    -r, --rev <rev>        show only heads which are descendants of rev
+    -t, --template <tpl>   display using template
+
 identify::
     Print a short summary of the current state of the repo.
 
@@ -331,7 +337,11 @@
     Currently only local repositories are supported.
 
     options:
+    -M, --no-merges       do not show merges
+    --map-file <file>     display using template map file
+    -n, --newest-first    show newest records first
     -p, --patch           show patch
+    -t, --template <tpl>  display using template
 
     aliases: in
 
@@ -379,10 +389,12 @@
     -b, --branch          show branches
     -k, --keyword <str>   search for keywords
     -l, --limit <num>     print no more than this many changes
+    --map-file <file>     display using template map file
     -M, --no-merges       do not show merges
     -m, --only-merges     only show merges
     -r, --rev <A>         show the specified revision or range
     -p, --patch           show patch
+    -t, --template <tpl>  display using template
 
     aliases: history
 
@@ -400,13 +412,22 @@
     See pull for valid source format details.
 
     options:
+    -M, --no-merges       do not show merges
+    --map-file <file>     display using template map file
     -p, --patch           show patch
+    -n, --newest-first    show newest records first
+    -t, --template <tpl>  display using template
 
     aliases: out
 
 parents::
     Print the working directory's parent revisions.
 
+    options:
+    -b, --branches        show branches
+    --map-file <file>     display using template map file
+    -t, --template <tpl>  display using template
+
 paths [NAME]::
     Show definition of symbolic path name NAME. If no name is given, show
     definition of available names.
@@ -613,7 +634,10 @@
     Show the tip revision.
 
     options:
-    -p, --patch      show patch
+    -b, --branches         show branches
+    --map-file <file>      display using template map file
+    -p, --patch            show patch
+    -t, --template <tpl>   display using template
 
 unbundle <file>::
     (EXPERIMENTAL)
--- a/doc/hgrc.5.txt	Mon Feb 27 12:50:49 2006 -0800
+++ b/doc/hgrc.5.txt	Mon Feb 27 13:18:57 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.
+  logmap;;
+    Template map file for commands that print changesets.
   merge;;
     The conflict resolution program to use during a manual merge.
     Default is "hgmerge".
--- 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')),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/map-log.compact	Mon Feb 27 13:18:57 2006 -0800
@@ -0,0 +1,4 @@
+template = '{rev}{parents}   {node|short}   {date|isodate}   {author|user}\n  {desc|firstline|strip}\n\n'
+start_parents = ':'
+parent = '{rev},'
+last_parent = '{rev}'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/map-log.verbose	Mon Feb 27 13:18:57 2006 -0800
@@ -0,0 +1,10 @@
+template = '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'
+start_file_adds = 'files+:     '
+file_add = ' {file_add}'
+end_file_adds = '\n'
+start_file_dels = 'files-:     '
+file_del = ' {file_del}'
+end_file_dels = '\n'
+parent = 'parent:      {rev}:{node}\n'
+manifest = 'manifest:    {rev}:{node}\n'
+tag = 'tag:         {tag}\n'