eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/churn.py
changeset 69 c6bca38c1cbf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/churn.py	Sat Jan 08 11:20:57 2011 +0530
@@ -0,0 +1,198 @@
+# churn.py - create a graph of revisions count grouped by template
+#
+# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
+# Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''command to display statistics about repository history'''
+
+from mercurial.i18n import _
+from mercurial import patch, cmdutil, util, templater, commands
+import os
+import time, datetime
+
+def maketemplater(ui, repo, tmpl):
+    tmpl = templater.parsestring(tmpl, quoted=False)
+    try:
+        t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
+    except SyntaxError, inst:
+        raise util.Abort(inst.args[0])
+    t.use_template(tmpl)
+    return t
+
+def changedlines(ui, repo, ctx1, ctx2, fns):
+    added, removed = 0, 0
+    fmatch = cmdutil.matchfiles(repo, fns)
+    diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
+    for l in diff.split('\n'):
+        if l.startswith("+") and not l.startswith("+++ "):
+            added += 1
+        elif l.startswith("-") and not l.startswith("--- "):
+            removed += 1
+    return (added, removed)
+
+def countrate(ui, repo, amap, *pats, **opts):
+    """Calculate stats"""
+    if opts.get('dateformat'):
+        def getkey(ctx):
+            t, tz = ctx.date()
+            date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
+            return date.strftime(opts['dateformat'])
+    else:
+        tmpl = opts.get('template', '{author|email}')
+        tmpl = maketemplater(ui, repo, tmpl)
+        def getkey(ctx):
+            ui.pushbuffer()
+            tmpl.show(ctx)
+            return ui.popbuffer()
+
+    state = {'count': 0}
+    rate = {}
+    df = False
+    if opts.get('date'):
+        df = util.matchdate(opts['date'])
+
+    m = cmdutil.match(repo, pats, opts)
+    def prep(ctx, fns):
+        rev = ctx.rev()
+        if df and not df(ctx.date()[0]): # doesn't match date format
+            return
+
+        key = getkey(ctx)
+        key = amap.get(key, key) # alias remap
+        key = key.strip() # ignore leading and trailing spaces
+        if opts.get('changesets'):
+            rate[key] = (rate.get(key, (0,))[0] + 1, 0)
+        else:
+            parents = ctx.parents()
+            if len(parents) > 1:
+                ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
+                return
+
+            ctx1 = parents[0]
+            lines = changedlines(ui, repo, ctx1, ctx, fns)
+            rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
+
+        state['count'] += 1
+        ui.progress(_('analyzing'), state['count'], total=len(repo))
+
+    for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
+        continue
+
+    ui.progress(_('analyzing'), None)
+
+    return rate
+
+
+def churn(ui, repo, *pats, **opts):
+    '''histogram of changes to the repository
+
+    This command will display a histogram representing the number
+    of changed lines or revisions, grouped according to the given
+    template. The default template will group changes by author.
+    The --dateformat option may be used to group the results by
+    date instead.
+
+    Statistics are based on the number of changed lines, or
+    alternatively the number of matching revisions if the
+    --changesets option is specified.
+
+    Examples::
+
+      # display count of changed lines for every committer
+      hg churn -t '{author|email}'
+
+      # display daily activity graph
+      hg churn -f '%H' -s -c
+
+      # display activity of developers by month
+      hg churn -f '%Y-%m' -s -c
+
+      # display count of lines changed in every year
+      hg churn -f '%Y' -s
+
+    It is possible to map alternate email addresses to a main address
+    by providing a file using the following format::
+
+      <alias email> = <actual email>
+
+    Such a file may be specified with the --aliases option, otherwise
+    a .hgchurn file will be looked for in the working directory root.
+    '''
+    def pad(s, l):
+        return (s + " " * l)[:l]
+
+    amap = {}
+    aliases = opts.get('aliases')
+    if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
+        aliases = repo.wjoin('.hgchurn')
+    if aliases:
+        for l in open(aliases, "r"):
+            try:
+                alias, actual = l.split('=' in l and '=' or None, 1)
+                amap[alias.strip()] = actual.strip()
+            except ValueError:
+                l = l.strip()
+                if l:
+                    ui.warn(_("skipping malformed alias: %s\n" % l))
+                continue
+
+    rate = countrate(ui, repo, amap, *pats, **opts).items()
+    if not rate:
+        return
+
+    sortkey = ((not opts.get('sort')) and (lambda x: -sum(x[1])) or None)
+    rate.sort(key=sortkey)
+
+    # Be careful not to have a zero maxcount (issue833)
+    maxcount = float(max(sum(v) for k, v in rate)) or 1.0
+    maxname = max(len(k) for k, v in rate)
+
+    ttywidth = ui.termwidth()
+    ui.debug("assuming %i character terminal\n" % ttywidth)
+    width = ttywidth - maxname - 2 - 2 - 2
+
+    if opts.get('diffstat'):
+        width -= 15
+        def format(name, diffstat):
+            added, removed = diffstat
+            return "%s %15s %s%s\n" % (pad(name, maxname),
+                                       '+%d/-%d' % (added, removed),
+                                       ui.label('+' * charnum(added),
+                                                'diffstat.inserted'),
+                                       ui.label('-' * charnum(removed),
+                                                'diffstat.deleted'))
+    else:
+        width -= 6
+        def format(name, count):
+            return "%s %6d %s\n" % (pad(name, maxname), sum(count),
+                                    '*' * charnum(sum(count)))
+
+    def charnum(count):
+        return int(round(count * width / maxcount))
+
+    for name, count in rate:
+        ui.write(format(name, count))
+
+
+cmdtable = {
+    "churn":
+        (churn,
+         [('r', 'rev', [],
+           _('count rate for the specified revision or range'), _('REV')),
+          ('d', 'date', '',
+           _('count rate for revisions matching date spec'), _('DATE')),
+          ('t', 'template', '{author|email}',
+           _('template to group changesets'), _('TEMPLATE')),
+          ('f', 'dateformat', '',
+           _('strftime-compatible format for grouping by date'), _('FORMAT')),
+          ('c', 'changesets', False, _('count rate by number of changesets')),
+          ('s', 'sort', False, _('sort by key (default: sort by count)')),
+          ('', 'diffstat', False, _('display added/removed lines separately')),
+          ('', 'aliases', '',
+           _('file with email aliases'), _('FILE')),
+          ] + commands.walkopts,
+         _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]")),
+}