##// END OF EJS Templates
churn: lowercase output...
Martin Geisler -
r7626:551afd4a default
parent child Browse files
Show More
@@ -1,161 +1,161 b''
1 # churn.py - create a graph of revisions count grouped by template
1 # churn.py - create a graph of revisions count grouped by template
2 #
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8 '''command to show certain statistics about revision history'''
8 '''command to show certain statistics about revision history'''
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import patch, cmdutil, util, templater
11 from mercurial import patch, cmdutil, util, templater
12 import os, sys
12 import os, sys
13 import time, datetime
13 import time, datetime
14
14
15 def maketemplater(ui, repo, tmpl):
15 def maketemplater(ui, repo, tmpl):
16 tmpl = templater.parsestring(tmpl, quoted=False)
16 tmpl = templater.parsestring(tmpl, quoted=False)
17 try:
17 try:
18 t = cmdutil.changeset_templater(ui, repo, False, None, False)
18 t = cmdutil.changeset_templater(ui, repo, False, None, False)
19 except SyntaxError, inst:
19 except SyntaxError, inst:
20 raise util.Abort(inst.args[0])
20 raise util.Abort(inst.args[0])
21 t.use_template(tmpl)
21 t.use_template(tmpl)
22 return t
22 return t
23
23
24 def changedlines(ui, repo, ctx1, ctx2):
24 def changedlines(ui, repo, ctx1, ctx2):
25 lines = 0
25 lines = 0
26 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node()))
26 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node()))
27 for l in diff.split('\n'):
27 for l in diff.split('\n'):
28 if (l.startswith("+") and not l.startswith("+++ ") or
28 if (l.startswith("+") and not l.startswith("+++ ") or
29 l.startswith("-") and not l.startswith("--- ")):
29 l.startswith("-") and not l.startswith("--- ")):
30 lines += 1
30 lines += 1
31 return lines
31 return lines
32
32
33 def countrate(ui, repo, amap, *pats, **opts):
33 def countrate(ui, repo, amap, *pats, **opts):
34 """Calculate stats"""
34 """Calculate stats"""
35 if opts.get('dateformat'):
35 if opts.get('dateformat'):
36 def getkey(ctx):
36 def getkey(ctx):
37 t, tz = ctx.date()
37 t, tz = ctx.date()
38 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
38 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
39 return date.strftime(opts['dateformat'])
39 return date.strftime(opts['dateformat'])
40 else:
40 else:
41 tmpl = opts.get('template', '{author|email}')
41 tmpl = opts.get('template', '{author|email}')
42 tmpl = maketemplater(ui, repo, tmpl)
42 tmpl = maketemplater(ui, repo, tmpl)
43 def getkey(ctx):
43 def getkey(ctx):
44 ui.pushbuffer()
44 ui.pushbuffer()
45 tmpl.show(ctx)
45 tmpl.show(ctx)
46 return ui.popbuffer()
46 return ui.popbuffer()
47
47
48 count = pct = 0
48 count = pct = 0
49 rate = {}
49 rate = {}
50 df = False
50 df = False
51 if opts.get('date'):
51 if opts.get('date'):
52 df = util.matchdate(opts['date'])
52 df = util.matchdate(opts['date'])
53
53
54 get = util.cachefunc(lambda r: repo[r].changeset())
54 get = util.cachefunc(lambda r: repo[r].changeset())
55 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
55 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
56 for st, rev, fns in changeiter:
56 for st, rev, fns in changeiter:
57 if not st == 'add':
57 if not st == 'add':
58 continue
58 continue
59 if df and not df(get(rev)[2][0]): # doesn't match date format
59 if df and not df(get(rev)[2][0]): # doesn't match date format
60 continue
60 continue
61
61
62 ctx = repo[rev]
62 ctx = repo[rev]
63 key = getkey(ctx)
63 key = getkey(ctx)
64 key = amap.get(key, key) # alias remap
64 key = amap.get(key, key) # alias remap
65 if opts.get('changesets'):
65 if opts.get('changesets'):
66 rate[key] = rate.get(key, 0) + 1
66 rate[key] = rate.get(key, 0) + 1
67 else:
67 else:
68 parents = ctx.parents()
68 parents = ctx.parents()
69 if len(parents) > 1:
69 if len(parents) > 1:
70 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
70 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
71 continue
71 continue
72
72
73 ctx1 = parents[0]
73 ctx1 = parents[0]
74 lines = changedlines(ui, repo, ctx1, ctx)
74 lines = changedlines(ui, repo, ctx1, ctx)
75 rate[key] = rate.get(key, 0) + lines
75 rate[key] = rate.get(key, 0) + lines
76
76
77 if opts.get('progress'):
77 if opts.get('progress'):
78 count += 1
78 count += 1
79 newpct = int(100.0 * count / max(len(repo), 1))
79 newpct = int(100.0 * count / max(len(repo), 1))
80 if pct < newpct:
80 if pct < newpct:
81 pct = newpct
81 pct = newpct
82 ui.write(_("\rGenerating stats: %d%%") % pct)
82 ui.write(_("\rgenerating stats: %d%%") % pct)
83 sys.stdout.flush()
83 sys.stdout.flush()
84
84
85 if opts.get('progress'):
85 if opts.get('progress'):
86 ui.write("\r")
86 ui.write("\r")
87 sys.stdout.flush()
87 sys.stdout.flush()
88
88
89 return rate
89 return rate
90
90
91
91
92 def churn(ui, repo, *pats, **opts):
92 def churn(ui, repo, *pats, **opts):
93 '''graph count of revisions grouped by template
93 '''graph count of revisions grouped by template
94
94
95 Will graph count of changed lines or revisions grouped by template or
95 Will graph count of changed lines or revisions grouped by template or
96 alternatively by date, if dateformat is used. In this case it will override
96 alternatively by date, if dateformat is used. In this case it will override
97 template.
97 template.
98
98
99 By default statistics are counted for number of changed lines.
99 By default statistics are counted for number of changed lines.
100
100
101 Examples:
101 Examples:
102
102
103 # display count of changed lines for every committer
103 # display count of changed lines for every committer
104 hg churn -t '{author|email}'
104 hg churn -t '{author|email}'
105
105
106 # display daily activity graph
106 # display daily activity graph
107 hg churn -f '%H' -s -c
107 hg churn -f '%H' -s -c
108
108
109 # display activity of developers by month
109 # display activity of developers by month
110 hg churn -f '%Y-%m' -s -c
110 hg churn -f '%Y-%m' -s -c
111
111
112 # display count of lines changed in every year
112 # display count of lines changed in every year
113 hg churn -f '%Y' -s
113 hg churn -f '%Y' -s
114
114
115 The map file format used to specify aliases is fairly simple:
115 The map file format used to specify aliases is fairly simple:
116
116
117 <alias email> <actual email>'''
117 <alias email> <actual email>'''
118 def pad(s, l):
118 def pad(s, l):
119 return (s + " " * l)[:l]
119 return (s + " " * l)[:l]
120
120
121 amap = {}
121 amap = {}
122 aliases = opts.get('aliases')
122 aliases = opts.get('aliases')
123 if aliases:
123 if aliases:
124 for l in open(aliases, "r"):
124 for l in open(aliases, "r"):
125 l = l.strip()
125 l = l.strip()
126 alias, actual = l.split()
126 alias, actual = l.split()
127 amap[alias] = actual
127 amap[alias] = actual
128
128
129 rate = countrate(ui, repo, amap, *pats, **opts).items()
129 rate = countrate(ui, repo, amap, *pats, **opts).items()
130 if not rate:
130 if not rate:
131 return
131 return
132
132
133 sortfn = ((not opts.get('sort')) and (lambda a, b: cmp(b[1], a[1])) or None)
133 sortfn = ((not opts.get('sort')) and (lambda a, b: cmp(b[1], a[1])) or None)
134 rate.sort(sortfn)
134 rate.sort(sortfn)
135
135
136 maxcount = float(max([v for k, v in rate]))
136 maxcount = float(max([v for k, v in rate]))
137 maxname = max([len(k) for k, v in rate])
137 maxname = max([len(k) for k, v in rate])
138
138
139 ttywidth = util.termwidth()
139 ttywidth = util.termwidth()
140 ui.debug(_("assuming %i character terminal\n") % ttywidth)
140 ui.debug(_("assuming %i character terminal\n") % ttywidth)
141 width = ttywidth - maxname - 2 - 6 - 2 - 2
141 width = ttywidth - maxname - 2 - 6 - 2 - 2
142
142
143 for date, count in rate:
143 for date, count in rate:
144 print "%s %6d %s" % (pad(date, maxname), count,
144 print "%s %6d %s" % (pad(date, maxname), count,
145 "*" * int(count * width / maxcount))
145 "*" * int(count * width / maxcount))
146
146
147
147
148 cmdtable = {
148 cmdtable = {
149 "churn":
149 "churn":
150 (churn,
150 (churn,
151 [('r', 'rev', [], _('count rate for the specified revision or range')),
151 [('r', 'rev', [], _('count rate for the specified revision or range')),
152 ('d', 'date', '', _('count rate for revs matching date spec')),
152 ('d', 'date', '', _('count rate for revs matching date spec')),
153 ('t', 'template', '{author|email}', _('template to group changesets')),
153 ('t', 'template', '{author|email}', _('template to group changesets')),
154 ('f', 'dateformat', '',
154 ('f', 'dateformat', '',
155 _('strftime-compatible format for grouping by date')),
155 _('strftime-compatible format for grouping by date')),
156 ('c', 'changesets', False, _('count rate by number of changesets')),
156 ('c', 'changesets', False, _('count rate by number of changesets')),
157 ('s', 'sort', False, _('sort by key (default: sort by count)')),
157 ('s', 'sort', False, _('sort by key (default: sort by count)')),
158 ('', 'aliases', '', _('file with email aliases')),
158 ('', 'aliases', '', _('file with email aliases')),
159 ('', 'progress', None, _('show progress'))],
159 ('', 'progress', None, _('show progress'))],
160 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
160 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
161 }
161 }
General Comments 0
You need to be logged in to leave comments. Login now