##// END OF EJS Templates
churn: ability to display added/removed lines separately
Alexander Solovyov -
r9669:9b127e88 default
parent child Browse files
Show More
@@ -1,175 +1,192
1 1 # churn.py - create a graph of revisions count grouped by template
2 2 #
3 3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 '''command to display statistics about repository history'''
10 10
11 11 from mercurial.i18n import _
12 12 from mercurial import patch, cmdutil, util, templater
13 13 import sys, os
14 14 import time, datetime
15 15
16 16 def maketemplater(ui, repo, tmpl):
17 17 tmpl = templater.parsestring(tmpl, quoted=False)
18 18 try:
19 19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
20 20 except SyntaxError, inst:
21 21 raise util.Abort(inst.args[0])
22 22 t.use_template(tmpl)
23 23 return t
24 24
25 25 def changedlines(ui, repo, ctx1, ctx2, fns):
26 lines = 0
26 added, removed = 0, 0
27 27 fmatch = cmdutil.matchfiles(repo, fns)
28 28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
29 29 for l in diff.split('\n'):
30 if (l.startswith("+") and not l.startswith("+++ ") or
31 l.startswith("-") and not l.startswith("--- ")):
32 lines += 1
33 return lines
30 if l.startswith("+") and not l.startswith("+++ "):
31 added += 1
32 elif l.startswith("-") and not l.startswith("--- "):
33 removed += 1
34 return (added, removed)
34 35
35 36 def countrate(ui, repo, amap, *pats, **opts):
36 37 """Calculate stats"""
37 38 if opts.get('dateformat'):
38 39 def getkey(ctx):
39 40 t, tz = ctx.date()
40 41 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
41 42 return date.strftime(opts['dateformat'])
42 43 else:
43 44 tmpl = opts.get('template', '{author|email}')
44 45 tmpl = maketemplater(ui, repo, tmpl)
45 46 def getkey(ctx):
46 47 ui.pushbuffer()
47 48 tmpl.show(ctx)
48 49 return ui.popbuffer()
49 50
50 51 count = pct = 0
51 52 rate = {}
52 53 df = False
53 54 if opts.get('date'):
54 55 df = util.matchdate(opts['date'])
55 56
56 57 m = cmdutil.match(repo, pats, opts)
57 58 def prep(ctx, fns):
58 59 rev = ctx.rev()
59 60 if df and not df(ctx.date()[0]): # doesn't match date format
60 61 return
61 62
62 63 key = getkey(ctx)
63 64 key = amap.get(key, key) # alias remap
64 65 if opts.get('changesets'):
65 66 rate[key] = rate.get(key, 0) + 1
66 67 else:
67 68 parents = ctx.parents()
68 69 if len(parents) > 1:
69 70 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
70 71 return
71 72
72 73 ctx1 = parents[0]
73 74 lines = changedlines(ui, repo, ctx1, ctx, fns)
74 rate[key] = rate.get(key, 0) + lines
75 rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
75 76
76 77 if opts.get('progress'):
77 78 count += 1
78 79 newpct = int(100.0 * count / max(len(repo), 1))
79 80 if pct < newpct:
80 81 pct = newpct
81 82 ui.write("\r" + _("generating stats: %d%%") % pct)
82 83 sys.stdout.flush()
83 84
84 85 for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
85 86 continue
86 87
87 88 if opts.get('progress'):
88 89 ui.write("\r")
89 90 sys.stdout.flush()
90 91
91 92 return rate
92 93
93 94
94 95 def churn(ui, repo, *pats, **opts):
95 96 '''histogram of changes to the repository
96 97
97 98 This command will display a histogram representing the number
98 99 of changed lines or revisions, grouped according to the given
99 100 template. The default template will group changes by author.
100 101 The --dateformat option may be used to group the results by
101 102 date instead.
102 103
103 104 Statistics are based on the number of changed lines, or
104 105 alternatively the number of matching revisions if the
105 106 --changesets option is specified.
106 107
107 108 Examples::
108 109
109 110 # display count of changed lines for every committer
110 111 hg churn -t '{author|email}'
111 112
112 113 # display daily activity graph
113 114 hg churn -f '%H' -s -c
114 115
115 116 # display activity of developers by month
116 117 hg churn -f '%Y-%m' -s -c
117 118
118 119 # display count of lines changed in every year
119 120 hg churn -f '%Y' -s
120 121
121 122 It is possible to map alternate email addresses to a main address
122 123 by providing a file using the following format::
123 124
124 125 <alias email> <actual email>
125 126
126 127 Such a file may be specified with the --aliases option, otherwise
127 128 a .hgchurn file will be looked for in the working directory root.
128 129 '''
129 130 def pad(s, l):
130 131 return (s + " " * l)[:l]
131 132
132 133 amap = {}
133 134 aliases = opts.get('aliases')
134 135 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
135 136 aliases = repo.wjoin('.hgchurn')
136 137 if aliases:
137 138 for l in open(aliases, "r"):
138 139 l = l.strip()
139 140 alias, actual = l.split()
140 141 amap[alias] = actual
141 142
142 143 rate = countrate(ui, repo, amap, *pats, **opts).items()
143 144 if not rate:
144 145 return
145 146
146 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
147 sortkey = ((not opts.get('sort')) and (lambda x: -sum(x[1])) or None)
147 148 rate.sort(key=sortkey)
148 149
149 150 # Be careful not to have a zero maxcount (issue833)
150 maxcount = float(max(v for k, v in rate)) or 1.0
151 maxcount = float(max(sum(v) for k, v in rate)) or 1.0
151 152 maxname = max(len(k) for k, v in rate)
152 153
153 154 ttywidth = util.termwidth()
154 155 ui.debug("assuming %i character terminal\n" % ttywidth)
155 width = ttywidth - maxname - 2 - 6 - 2 - 2
156 width = ttywidth - maxname - 2 - 2 - 2
156 157
157 for date, count in rate:
158 print "%s %6d %s" % (pad(date, maxname), count,
159 "*" * int(count * width / maxcount))
158 if opts.get('diffstat'):
159 width -= 15
160 def format(name, (added, removed)):
161 return "%s %15s %s%s\n" % (pad(name, maxname),
162 '+%d/-%d' % (added, removed),
163 '+' * charnum(added),
164 '-' * charnum(removed))
165 else:
166 width -= 6
167 def format(name, count):
168 return "%s %6d %s\n" % (pad(name, maxname), sum(count),
169 '*' * charnum(sum(count)))
170
171 def charnum(count):
172 return int(round(count*width/maxcount))
173
174 for name, count in rate:
175 ui.write(format(name, count))
160 176
161 177
162 178 cmdtable = {
163 179 "churn":
164 180 (churn,
165 181 [('r', 'rev', [], _('count rate for the specified revision or range')),
166 182 ('d', 'date', '', _('count rate for revisions matching date spec')),
167 183 ('t', 'template', '{author|email}', _('template to group changesets')),
168 184 ('f', 'dateformat', '',
169 185 _('strftime-compatible format for grouping by date')),
170 186 ('c', 'changesets', False, _('count rate by number of changesets')),
171 187 ('s', 'sort', False, _('sort by key (default: sort by count)')),
188 ('', 'diffstat', False, _('display added/removed lines separately')),
172 189 ('', 'aliases', '', _('file with email aliases')),
173 190 ('', 'progress', None, _('show progress'))],
174 191 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
175 192 }
@@ -1,62 +1,67
1 1 #!/bin/sh
2 2
3 3 echo "[extensions]" >> $HGRCPATH
4 4 echo "churn=" >> $HGRCPATH
5 5
6 6 COLUMNS=80; export COLUMNS
7 7
8 8 echo % create test repository
9 9 hg init repo
10 10 cd repo
11 11 echo a > a
12 12 hg ci -Am adda -u user1 -d 6:00
13 13 echo b >> a
14 14 echo b > b
15 15 hg ci -m changeba -u user2 -d 9:00 a
16 16 hg ci -Am addb -u user2 -d 9:30
17 17 echo c >> a
18 18 echo c >> b
19 19 echo c > c
20 20 hg ci -m changeca -u user3 -d 12:00 a
21 21 hg ci -m changecb -u user3 -d 12:15 b
22 22 hg ci -Am addc -u user3 -d 12:30
23 23 mkdir -p d/e
24 24 echo abc > d/e/f1.txt
25 25 hg ci -Am "add d/e/f1.txt" -u user1 -d 12:45 d/e/f1.txt
26 26 mkdir -p d/g
27 27 echo def > d/g/f2.txt
28 28 hg ci -Am "add d/g/f2.txt" -u user1 -d 13:00 d/g/f2.txt
29 29
30 30 echo % churn separate directories
31 31 cd d
32 32 hg churn e
33 33 echo % churn all
34 34 hg churn
35 35 echo % churn up to rev 2
36 36 hg churn -r :2
37 37 cd ..
38 38 echo % churn with aliases
39 39 cat > ../aliases <<EOF
40 40 user1 alias1
41 41 user3 alias3
42 42 EOF
43 43 hg churn --aliases ../aliases
44 44 echo % churn with .hgchurn
45 45 mv ../aliases .hgchurn
46 46 hg churn
47 47 rm .hgchurn
48 48 echo % churn with column specifier
49 49 COLUMNS=40 hg churn
50 50 echo % churn by hour
51 51 hg churn -f '%H' -s
52 52
53 echo % churn with separated added/removed lines
54 hg rm d/g/f2.txt
55 hg ci -Am "removed d/g/f2.txt" -u user1 -d 14:00 d/g/f2.txt
56 hg churn --diffstat
57
53 58 cd ..
54 59
55 60 # issue 833: ZeroDivisionError
56 61 hg init issue-833
57 62 cd issue-833
58 63 touch foo
59 64 hg ci -Am foo
60 65 # this was failing with a ZeroDivisionError
61 66 hg churn
62 67 cd ..
@@ -1,32 +1,36
1 1 % create test repository
2 2 adding a
3 3 adding b
4 4 adding c
5 5 % churn separate directories
6 6 user1 1 ***************************************************************
7 7 % churn all
8 8 user3 3 ***************************************************************
9 9 user1 3 ***************************************************************
10 10 user2 2 ******************************************
11 11 % churn up to rev 2
12 12 user2 2 ***************************************************************
13 user1 1 *******************************
13 user1 1 ********************************
14 14 % churn with aliases
15 15 alias3 3 **************************************************************
16 16 alias1 3 **************************************************************
17 17 user2 2 *****************************************
18 18 % churn with .hgchurn
19 19 alias3 3 **************************************************************
20 20 alias1 3 **************************************************************
21 21 user2 2 *****************************************
22 22 % churn with column specifier
23 23 user3 3 ***********************
24 24 user1 3 ***********************
25 25 user2 2 ***************
26 26 % churn by hour
27 06 1 ****************
27 06 1 *****************
28 28 09 2 *********************************
29 29 12 4 ******************************************************************
30 13 1 ****************
30 13 1 *****************
31 % churn with separated added/removed lines
32 user1 +3/-1 +++++++++++++++++++++++++++++++++++++++++--------------
33 user3 +3/-0 +++++++++++++++++++++++++++++++++++++++++
34 user2 +2/-0 +++++++++++++++++++++++++++
31 35 adding foo
32 36 test 0
General Comments 0
You need to be logged in to leave comments. Login now