mercurial/cmdutil.py
changeset 3643 b4ad640a3bcf
parent 3531 730ca93ed788
child 3645 b984dcb1df71
--- a/mercurial/cmdutil.py	Mon Nov 13 13:26:57 2006 -0600
+++ b/mercurial/cmdutil.py	Mon Nov 13 13:26:57 2006 -0600
@@ -8,8 +8,8 @@
 from demandload import demandload
 from node import *
 from i18n import gettext as _
-demandload(globals(), 'mdiff util')
 demandload(globals(), 'os sys')
+demandload(globals(), 'mdiff util templater cStringIO')
 
 revrangesep = ':'
 
@@ -195,3 +195,330 @@
                                (oldrel, newrel, score * 100))
             if not dry_run:
                 repo.copy(old, new, wlock=wlock)
+
+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, copies=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])
+        extra = changes[5]
+        branch = extra.get("branch")
+
+        hexfunc = self.ui.debugflag and hex or short
+
+        parents = log.parentrevs(rev)
+        if not self.ui.debugflag:
+            if parents[1] == nullrev:
+                if parents[0] >= rev - 1:
+                    parents = []
+                else:
+                    parents = [parents[0]]
+        parents = [(p, hexfunc(log.node(p))) for p in parents]
+
+        self.ui.write(_("changeset:   %d:%s\n") % (rev, hexfunc(changenode)))
+
+        if branch:
+            self.ui.write(_("branch:      %s\n") % branch)
+        for tag in self.repo.nodetags(changenode):
+            self.ui.write(_("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))
+
+        if self.ui.debugflag:
+            self.ui.write(_("manifest:    %d:%s\n") %
+                          (self.repo.manifest.rev(changes[0]), hex(changes[0])))
+        self.ui.write(_("user:        %s\n") % changes[1])
+        self.ui.write(_("date:        %s\n") % date)
+
+        if self.ui.debugflag:
+            files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
+            for key, value in zip([_("files:"), _("files+:"), _("files-:")],
+                                  files):
+                if value:
+                    self.ui.write("%-12s %s\n" % (key, " ".join(value)))
+        elif changes[3] and self.ui.verbose:
+            self.ui.write(_("files:       %s\n") % " ".join(changes[3]))
+        if copies and self.ui.verbose:
+            copies = ['%s (%s)' % c for c in copies]
+            self.ui.write(_("copies:      %s\n") % ' '.join(copies))
+
+        if extra and self.ui.debugflag:
+            extraitems = extra.items()
+            extraitems.sort()
+            for key, value in extraitems:
+                self.ui.write(_("extra:       %s=%s\n")
+                              % (key, value.encode('string_escape')))
+
+        description = changes[4].strip()
+        if description:
+            if self.ui.verbose:
+                self.ui.write(_("description:\n"))
+                self.ui.write(description)
+                self.ui.write("\n\n")
+            else:
+                self.ui.write(_("summary:     %s\n") %
+                              description.splitlines()[0])
+        self.ui.write("\n")
+
+class changeset_templater(object):
+    '''format changeset information.'''
+
+    def __init__(self, ui, repo, mapfile, dest=None):
+        self.t = templater.templater(mapfile, templater.common_filters,
+                                     cache={'parent': '{rev}:{node|short} ',
+                                            'manifest': '{rev}:{node|short}',
+                                            'filecopy': '{name} ({source})'})
+        self.ui = ui
+        self.dest = dest
+        self.repo = repo
+
+    def use_template(self, t):
+        '''set template string to use'''
+        self.t.cache['changeset'] = t
+
+    def show(self, rev=0, changenode=None, brinfo=None, copies=[], **props):
+        '''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 (AttributeError, ValueError):
+                    try:
+                        for a, b in v:
+                            vargs[a] = b
+                    except ValueError:
+                        vargs[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)
+
+        def showbranches(**args):
+            branch = changes[5].get("branch")
+            if branch:
+                yield showlist('branch', [branch], plural='branches', **args)
+            # add old style branches if requested
+            if brinfo and changenode in brinfo:
+                yield showlist('branch', brinfo[changenode],
+                               plural='branches', **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
+            return showlist('parent', parents, **args)
+
+        def showtags(**args):
+            return showlist('tag', self.repo.nodetags(changenode), **args)
+
+        def showextras(**args):
+            extras = changes[5].items()
+            extras.sort()
+            for key, value in extras:
+                args = args.copy()
+                args.update(dict(key=key, value=value))
+                yield self.t('extra', **args)
+
+        def showcopies(**args):
+            c = [{'name': x[0], 'source': x[1]} for x in copies]
+            return showlist('file_copy', c, plural='file_copies', **args)
+
+        if self.ui.debugflag:
+            files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
+            def showfiles(**args):
+                return showlist('file', files[0], **args)
+            def showadds(**args):
+                return showlist('file_add', files[1], **args)
+            def showdels(**args):
+                return showlist('file_del', files[2], **args)
+            def showmanifest(**args):
+                args = args.copy()
+                args.update(dict(rev=self.repo.manifest.rev(changes[0]),
+                                 node=hex(changes[0])))
+                return self.t('manifest', **args)
+        else:
+            def showfiles(**args):
+                yield showlist('file', changes[3], **args)
+            showadds = ''
+            showdels = ''
+            showmanifest = ''
+
+        defprops = {
+            'author': changes[1],
+            'branches': showbranches,
+            'date': changes[2],
+            'desc': changes[4],
+            'file_adds': showadds,
+            'file_dels': showdels,
+            'files': showfiles,
+            'file_copies': showcopies,
+            'manifest': showmanifest,
+            'node': hex(changenode),
+            'parents': showparents,
+            'rev': rev,
+            'tags': showtags,
+            'extras': showextras,
+            }
+        props = props.copy()
+        props.update(defprops)
+
+        try:
+            dest = self.dest or self.ui
+            if self.ui.debugflag and 'header_debug' in self.t:
+                key = 'header_debug'
+            elif self.ui.quiet and 'header_quiet' in self.t:
+                key = 'header_quiet'
+            elif self.ui.verbose and 'header_verbose' in self.t:
+                key = 'header_verbose'
+            elif 'header' in self.t:
+                key = 'header'
+            else:
+                key = ''
+            if key:
+                dest.write_header(templater.stringify(self.t(key, **props)))
+            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'
+            dest.write(templater.stringify(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 stringio(object):
+    '''wrap cStringIO for use by changeset_templater.'''
+    def __init__(self):
+        self.fp = cStringIO.StringIO()
+
+    def write(self, *args):
+        for a in args:
+            self.fp.write(a)
+
+    write_header = write
+
+    def __getattr__(self, key):
+        return getattr(self.fp, key)
+
+def show_changeset(ui, repo, opts):
+    """show one changeset using template or regular display.
+
+    Display format will be the first non-empty hit of:
+    1. option 'template'
+    2. option 'style'
+    3. [ui] setting 'logtemplate'
+    4. [ui] setting 'style'
+    If all of these values are either the unset or the empty string,
+    regular display via changeset_printer() is done.
+    """
+    # options
+    tmpl = opts.get('template')
+    mapfile = None
+    if tmpl:
+        tmpl = templater.parsestring(tmpl, quoted=False)
+    else:
+        mapfile = opts.get('style')
+        # ui settings
+        if not mapfile:
+            tmpl = ui.config('ui', 'logtemplate')
+            if tmpl:
+                tmpl = templater.parsestring(tmpl)
+            else:
+                mapfile = ui.config('ui', 'style')
+
+    if tmpl or mapfile:
+        if mapfile:
+            if not os.path.split(mapfile)[0]:
+                mapname = (templater.templatepath('map-cmdline.' + mapfile)
+                           or 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)
+