author | Matt Mackall <mpm@selenic.com> |
Tue, 22 Jul 2008 13:03:21 -0500 | |
changeset 6829 | fec1da46006e |
parent 6762 | f67d1468ac50 |
child 6843 | b114a8c7998f |
permissions | -rw-r--r-- |
3040 | 1 |
# churn.py - create a graph showing who changed the most lines |
2 |
# |
|
3 |
# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net> |
|
4 |
# |
|
5 |
# This software may be used and distributed according to the terms |
|
6 |
# of the GNU General Public License, incorporated herein by reference. |
|
6666
53465a7464e2
convert comments to docstrings in a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6598
diff
changeset
|
7 |
'''allow graphing the number of lines changed per contributor''' |
3040 | 8 |
|
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
9 |
from mercurial.i18n import gettext as _ |
6759 | 10 |
from mercurial import patch, cmdutil, util, node |
4955
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
11 |
import os, sys |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
12 |
|
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
13 |
def get_tty_width(): |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
14 |
if 'COLUMNS' in os.environ: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
15 |
try: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
16 |
return int(os.environ['COLUMNS']) |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
17 |
except ValueError: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
18 |
pass |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
19 |
try: |
5419
041bd297f01e
churn: simplify code to get terminal width
Christian Ebert <blacktrash@gmx.net>
parents:
4955
diff
changeset
|
20 |
import termios, array, fcntl |
4955
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
21 |
for dev in (sys.stdout, sys.stdin): |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
22 |
try: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
23 |
fd = dev.fileno() |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
24 |
if not os.isatty(fd): |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
25 |
continue |
5419
041bd297f01e
churn: simplify code to get terminal width
Christian Ebert <blacktrash@gmx.net>
parents:
4955
diff
changeset
|
26 |
arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) |
041bd297f01e
churn: simplify code to get terminal width
Christian Ebert <blacktrash@gmx.net>
parents:
4955
diff
changeset
|
27 |
return array.array('h', arri)[1] |
4955
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
28 |
except ValueError: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
29 |
pass |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
30 |
except ImportError: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
31 |
pass |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
32 |
return 80 |
3040 | 33 |
|
6759 | 34 |
def countrevs(ui, repo, amap, revs, progress=False): |
3040 | 35 |
stats = {} |
6759 | 36 |
count = pct = 0 |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
37 |
if not revs: |
6759 | 38 |
revs = range(len(repo)) |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
39 |
|
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
40 |
for rev in revs: |
6759 | 41 |
ctx2 = repo[rev] |
42 |
parents = ctx2.parents() |
|
43 |
if len(parents) > 1: |
|
3049
461573aa02ef
[churn] Ignore merge csets
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3048
diff
changeset
|
44 |
ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,)) |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
45 |
continue |
3049
461573aa02ef
[churn] Ignore merge csets
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3048
diff
changeset
|
46 |
|
6759 | 47 |
ctx1 = parents[0] |
48 |
lines = 0 |
|
49 |
ui.pushbuffer() |
|
50 |
patch.diff(repo, ctx1.node(), ctx2.node()) |
|
51 |
diff = ui.popbuffer() |
|
3040 | 52 |
|
6759 | 53 |
for l in diff.split('\n'): |
54 |
if (l.startswith("+") and not l.startswith("+++ ") or |
|
55 |
l.startswith("-") and not l.startswith("--- ")): |
|
56 |
lines += 1 |
|
3040 | 57 |
|
6759 | 58 |
user = util.email(ctx2.user()) |
59 |
user = amap.get(user, user) # remap |
|
60 |
stats[user] = stats.get(user, 0) + lines |
|
61 |
ui.debug("rev %d: %d lines by %s\n" % (rev, lines, user)) |
|
3040 | 62 |
|
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
63 |
if progress: |
6759 | 64 |
count += 1 |
65 |
newpct = int(100.0 * count / max(len(revs), 1)) |
|
66 |
if pct < newpct: |
|
67 |
pct = newpct |
|
68 |
ui.write("\rGenerating stats: %d%%" % pct) |
|
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
69 |
sys.stdout.flush() |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
70 |
|
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
71 |
if progress: |
5989
a7817ad608ea
added \r for progress counting in churn extension
Armin Ronacher <armin.ronacher@active-4.com>
parents:
5976
diff
changeset
|
72 |
ui.write("\r") |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
73 |
sys.stdout.flush() |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
74 |
|
3040 | 75 |
return stats |
76 |
||
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
77 |
def churn(ui, repo, **opts): |
6666
53465a7464e2
convert comments to docstrings in a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6598
diff
changeset
|
78 |
'''graphs the number of lines changed |
53465a7464e2
convert comments to docstrings in a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6598
diff
changeset
|
79 |
|
53465a7464e2
convert comments to docstrings in a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6598
diff
changeset
|
80 |
The map file format used to specify aliases is fairly simple: |
53465a7464e2
convert comments to docstrings in a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6598
diff
changeset
|
81 |
|
53465a7464e2
convert comments to docstrings in a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6598
diff
changeset
|
82 |
<alias email> <actual email>''' |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
83 |
|
3040 | 84 |
def pad(s, l): |
6759 | 85 |
return (s + " " * l)[:l] |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
86 |
|
3040 | 87 |
amap = {} |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
88 |
aliases = opts.get('aliases') |
3040 | 89 |
if aliases: |
6759 | 90 |
for l in open(aliases, "r"): |
91 |
l = l.strip() |
|
92 |
alias, actual = l.split() |
|
93 |
amap[alias] = actual |
|
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
94 |
|
6762 | 95 |
revs = util.sort([int(r) for r in cmdutil.revrange(repo, opts['rev'])]) |
6759 | 96 |
stats = countrevs(ui, repo, amap, revs, opts.get('progress')) |
97 |
if not stats: |
|
5588
083b6e3142a2
churn: avoid division by zero
Matt Mackall <mpm@selenic.com>
parents:
5482
diff
changeset
|
98 |
return |
3040 | 99 |
|
6762 | 100 |
stats = util.sort([(-l, u, l) for u,l in stats.items()]) |
6759 | 101 |
maxchurn = float(max(1, stats[0][2])) |
102 |
maxuser = max([len(u) for k, u, l in stats]) |
|
6223
bab6c8f2bb1a
churn: show comitter email addresses unclipped (bug 1023)
Stephen Deasey <sdeasey@gmail.com>
parents:
6212
diff
changeset
|
103 |
|
6759 | 104 |
ttywidth = get_tty_width() |
105 |
ui.debug(_("assuming %i character terminal\n") % ttywidth) |
|
106 |
width = ttywidth - maxuser - 2 - 6 - 2 - 2 |
|
6223
bab6c8f2bb1a
churn: show comitter email addresses unclipped (bug 1023)
Stephen Deasey <sdeasey@gmail.com>
parents:
6212
diff
changeset
|
107 |
|
6759 | 108 |
for k, user, churn in stats: |
109 |
print "%s %6d %s" % (pad(user, maxuser), churn, |
|
110 |
"*" * int(churn * width / maxchurn)) |
|
3040 | 111 |
|
112 |
cmdtable = { |
|
113 |
"churn": |
|
114 |
(churn, |
|
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
115 |
[('r', 'rev', [], _('limit statistics to the specified revisions')), |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
116 |
('', 'aliases', '', _('file with email aliases')), |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
117 |
('', 'progress', None, _('show progress'))], |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
118 |
'hg churn [-r revision range] [-a file] [--progress]'), |
3040 | 119 |
} |