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