view hgext/churn.py @ 6945:2cfdabe235fb

hgweb: return content iterator instead of using write() callable This is a new version of 4879468fa28f (which was backed out in 943f066c0d58), with an extra line removed to fix problems with hg serve. hg's internal web server contains checking if the app isn't trying to write more bytes than specified by the Content-Length header. The first try still contained an old line that wrote the response, so the response was sent twice.
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Sat, 30 Aug 2008 17:13:23 +0200
parents b114a8c7998f
children a9411e29773f
line wrap: on
line source

# churn.py - create a graph showing who changed the most lines
#
# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
'''allow graphing the number of lines changed per contributor'''

from mercurial.i18n import gettext as _
from mercurial import patch, cmdutil, util, node
import os, sys

def get_tty_width():
    if 'COLUMNS' in os.environ:
        try:
            return int(os.environ['COLUMNS'])
        except ValueError:
            pass
    try:
        import termios, array, fcntl
        for dev in (sys.stdout, sys.stdin):
            try:
                fd = dev.fileno()
                if not os.isatty(fd):
                    continue
                arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
                return array.array('h', arri)[1]
            except ValueError:
                pass
    except ImportError:
        pass
    return 80

def countrevs(ui, repo, amap, revs, progress=False):
    stats = {}
    count = pct = 0
    if not revs:
        revs = range(len(repo))

    for rev in revs:
        ctx2 = repo[rev]
        parents = ctx2.parents()
        if len(parents) > 1:
            ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
            continue

        ctx1 = parents[0]
        lines = 0
        ui.pushbuffer()
        patch.diff(repo, ctx1.node(), ctx2.node())
        diff = ui.popbuffer()

        for l in diff.split('\n'):
            if (l.startswith("+") and not l.startswith("+++ ") or
                l.startswith("-") and not l.startswith("--- ")):
                lines += 1

        user = util.email(ctx2.user())
        user = amap.get(user, user) # remap
        stats[user] = stats.get(user, 0) + lines
        ui.debug("rev %d: %d lines by %s\n" % (rev, lines, user))

        if progress:
            count += 1
            newpct = int(100.0 * count / max(len(revs), 1))
            if pct < newpct:
                pct = newpct
                ui.write("\rGenerating stats: %d%%" % pct)
                sys.stdout.flush()

    if progress:
        ui.write("\r")
        sys.stdout.flush()

    return stats

def churn(ui, repo, **opts):
    '''graphs the number of lines changed

    The map file format used to specify aliases is fairly simple:

    <alias email> <actual email>'''

    def pad(s, l):
        return (s + " " * l)[:l]

    amap = {}
    aliases = opts.get('aliases')
    if aliases:
        for l in open(aliases, "r"):
            l = l.strip()
            alias, actual = l.split()
            amap[alias] = actual

    revs = util.sort([int(r) for r in cmdutil.revrange(repo, opts['rev'])])
    stats = countrevs(ui, repo, amap, revs, opts.get('progress'))
    if not stats:
        return

    stats = util.sort([(-l, u, l) for u,l in stats.items()])
    maxchurn = float(max(1, stats[0][2]))
    maxuser = max([len(u) for k, u, l in stats])

    ttywidth = get_tty_width()
    ui.debug(_("assuming %i character terminal\n") % ttywidth)
    width = ttywidth - maxuser - 2 - 6 - 2 - 2

    for k, user, churn in stats:
        print "%s %6d %s" % (pad(user, maxuser), churn,
                             "*" * int(churn * width / maxchurn))

cmdtable = {
    "churn":
    (churn,
     [('r', 'rev', [], _('limit statistics to the specified revisions')),
      ('', 'aliases', '', _('file with email aliases')),
      ('', 'progress', None, _('show progress'))],
    'hg churn [-r REVISIONS] [--aliases FILE] [--progress]'),
}