Mercurial > hg
view hgext/churn.py @ 7004:90227c42b5f6
c0bd7d8b69ef uses err() instead of warn() but prototype doesn't match
we might want to make warn() look more like err() in the future
to avoid this kind of problem.
author | Benoit Boissinot <benoit.boissinot@ens-lyon.org> |
---|---|
date | Mon, 08 Sep 2008 00:50:34 +0200 |
parents | a9411e29773f |
children | 5f201f711932 |
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]'), }