##// END OF EJS Templates
churn: py2.3 compatibility fix...
Alexander Solovyov -
r7076:c29d3f4e default
parent child Browse files
Show More
@@ -1,184 +1,184 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 '''allow graphing the number of lines (or count of revisions) grouped by template'''
8 '''allow graphing the number of lines (or count of revisions) grouped by template'''
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 get_tty_width():
15 def get_tty_width():
16 if 'COLUMNS' in os.environ:
16 if 'COLUMNS' in os.environ:
17 try:
17 try:
18 return int(os.environ['COLUMNS'])
18 return int(os.environ['COLUMNS'])
19 except ValueError:
19 except ValueError:
20 pass
20 pass
21 try:
21 try:
22 import termios, array, fcntl
22 import termios, array, fcntl
23 for dev in (sys.stdout, sys.stdin):
23 for dev in (sys.stdout, sys.stdin):
24 try:
24 try:
25 fd = dev.fileno()
25 fd = dev.fileno()
26 if not os.isatty(fd):
26 if not os.isatty(fd):
27 continue
27 continue
28 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
28 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
29 return array.array('h', arri)[1]
29 return array.array('h', arri)[1]
30 except ValueError:
30 except ValueError:
31 pass
31 pass
32 except ImportError:
32 except ImportError:
33 pass
33 pass
34 return 80
34 return 80
35
35
36 def maketemplater(ui, repo, tmpl):
36 def maketemplater(ui, repo, tmpl):
37 tmpl = templater.parsestring(tmpl, quoted=False)
37 tmpl = templater.parsestring(tmpl, quoted=False)
38 try:
38 try:
39 t = cmdutil.changeset_templater(ui, repo, False, None, False)
39 t = cmdutil.changeset_templater(ui, repo, False, None, False)
40 except SyntaxError, inst:
40 except SyntaxError, inst:
41 raise util.Abort(inst.args[0])
41 raise util.Abort(inst.args[0])
42 t.use_template(tmpl)
42 t.use_template(tmpl)
43 return t
43 return t
44
44
45 def changedlines(ui, repo, ctx1, ctx2):
45 def changedlines(ui, repo, ctx1, ctx2):
46 lines = 0
46 lines = 0
47 ui.pushbuffer()
47 ui.pushbuffer()
48 patch.diff(repo, ctx1.node(), ctx2.node())
48 patch.diff(repo, ctx1.node(), ctx2.node())
49 diff = ui.popbuffer()
49 diff = ui.popbuffer()
50 for l in diff.split('\n'):
50 for l in diff.split('\n'):
51 if (l.startswith("+") and not l.startswith("+++ ") or
51 if (l.startswith("+") and not l.startswith("+++ ") or
52 l.startswith("-") and not l.startswith("--- ")):
52 l.startswith("-") and not l.startswith("--- ")):
53 lines += 1
53 lines += 1
54 return lines
54 return lines
55
55
56 def countrate(ui, repo, amap, *pats, **opts):
56 def countrate(ui, repo, amap, *pats, **opts):
57 """Calculate stats"""
57 """Calculate stats"""
58 if opts.get('dateformat'):
58 if opts.get('dateformat'):
59 def getkey(ctx):
59 def getkey(ctx):
60 t, tz = ctx.date()
60 t, tz = ctx.date()
61 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
61 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
62 return date.strftime(opts['dateformat'])
62 return date.strftime(opts['dateformat'])
63 else:
63 else:
64 tmpl = opts.get('template', '{author|email}')
64 tmpl = opts.get('template', '{author|email}')
65 tmpl = maketemplater(ui, repo, tmpl)
65 tmpl = maketemplater(ui, repo, tmpl)
66 def getkey(ctx):
66 def getkey(ctx):
67 ui.pushbuffer()
67 ui.pushbuffer()
68 tmpl.show(changenode=ctx.node())
68 tmpl.show(changenode=ctx.node())
69 return ui.popbuffer()
69 return ui.popbuffer()
70
70
71 count = pct = 0
71 count = pct = 0
72 rate = {}
72 rate = {}
73 df = False
73 df = False
74 if opts.get('date'):
74 if opts.get('date'):
75 df = util.matchdate(opts['date'])
75 df = util.matchdate(opts['date'])
76
76
77 get = util.cachefunc(lambda r: repo[r].changeset())
77 get = util.cachefunc(lambda r: repo[r].changeset())
78 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
78 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
79 for st, rev, fns in changeiter:
79 for st, rev, fns in changeiter:
80 if not st == 'add':
80 if not st == 'add':
81 continue
81 continue
82 if df and not df(get(rev)[2][0]): # doesn't match date format
82 if df and not df(get(rev)[2][0]): # doesn't match date format
83 continue
83 continue
84
84
85 ctx = repo[rev]
85 ctx = repo[rev]
86 key = getkey(ctx)
86 key = getkey(ctx)
87 key = amap.get(key, key) # alias remap
87 key = amap.get(key, key) # alias remap
88 if opts.get('changesets'):
88 if opts.get('changesets'):
89 rate[key] = rate.get(key, 0) + 1
89 rate[key] = rate.get(key, 0) + 1
90 else:
90 else:
91 parents = ctx.parents()
91 parents = ctx.parents()
92 if len(parents) > 1:
92 if len(parents) > 1:
93 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
93 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
94 continue
94 continue
95
95
96 ctx1 = parents[0]
96 ctx1 = parents[0]
97 lines = changedlines(ui, repo, ctx1, ctx)
97 lines = changedlines(ui, repo, ctx1, ctx)
98 rate[key] = rate.get(key, 0) + lines
98 rate[key] = rate.get(key, 0) + lines
99
99
100 if opts.get('progress'):
100 if opts.get('progress'):
101 count += 1
101 count += 1
102 newpct = int(100.0 * count / max(len(repo), 1))
102 newpct = int(100.0 * count / max(len(repo), 1))
103 if pct < newpct:
103 if pct < newpct:
104 pct = newpct
104 pct = newpct
105 ui.write(_("\rGenerating stats: %d%%") % pct)
105 ui.write(_("\rGenerating stats: %d%%") % pct)
106 sys.stdout.flush()
106 sys.stdout.flush()
107
107
108 if opts.get('progress'):
108 if opts.get('progress'):
109 ui.write("\r")
109 ui.write("\r")
110 sys.stdout.flush()
110 sys.stdout.flush()
111
111
112 return rate
112 return rate
113
113
114
114
115 def churn(ui, repo, *pats, **opts):
115 def churn(ui, repo, *pats, **opts):
116 '''Graph count of revisions grouped by template
116 '''Graph count of revisions grouped by template
117
117
118 Will graph count of changed lines or revisions grouped by template or
118 Will graph count of changed lines or revisions grouped by template or
119 alternatively by date, if dateformat is used. In this case it will override
119 alternatively by date, if dateformat is used. In this case it will override
120 template.
120 template.
121
121
122 By default statistics are counted for number of changed lines.
122 By default statistics are counted for number of changed lines.
123
123
124 Examples:
124 Examples:
125
125
126 # display count of changed lines for every committer
126 # display count of changed lines for every committer
127 hg churn -t '{author|email}'
127 hg churn -t '{author|email}'
128
128
129 # display daily activity graph
129 # display daily activity graph
130 hg churn -f '%H' -s -c
130 hg churn -f '%H' -s -c
131
131
132 # display activity of developers by month
132 # display activity of developers by month
133 hg churn -f '%Y-%m' -s -c
133 hg churn -f '%Y-%m' -s -c
134
134
135 # display count of lines changed in every year
135 # display count of lines changed in every year
136 hg churn -f '%Y' -s
136 hg churn -f '%Y' -s
137
137
138 The map file format used to specify aliases is fairly simple:
138 The map file format used to specify aliases is fairly simple:
139
139
140 <alias email> <actual email>'''
140 <alias email> <actual email>'''
141 def pad(s, l):
141 def pad(s, l):
142 return (s + " " * l)[:l]
142 return (s + " " * l)[:l]
143
143
144 amap = {}
144 amap = {}
145 aliases = opts.get('aliases')
145 aliases = opts.get('aliases')
146 if aliases:
146 if aliases:
147 for l in open(aliases, "r"):
147 for l in open(aliases, "r"):
148 l = l.strip()
148 l = l.strip()
149 alias, actual = l.split()
149 alias, actual = l.split()
150 amap[alias] = actual
150 amap[alias] = actual
151
151
152 rate = countrate(ui, repo, amap, *pats, **opts).items()
152 rate = countrate(ui, repo, amap, *pats, **opts).items()
153 if not rate:
153 if not rate:
154 return
154 return
155
155
156 keyfn = (not opts.get('sort')) and (lambda (k,v): (v,k)) or None
156 sortfn = ((not opts.get('sort')) and (lambda a, b: cmp(b[1], a[1])) or None)
157 rate.sort(key=keyfn, reverse=not opts.get('sort'))
157 rate.sort(sortfn)
158
158
159 maxcount = float(max(v for k, v in rate))
159 maxcount = float(max([v for k, v in rate]))
160 maxname = max(len(k) for k, v in rate)
160 maxname = max([len(k) for k, v in rate])
161
161
162 ttywidth = get_tty_width()
162 ttywidth = get_tty_width()
163 ui.debug(_("assuming %i character terminal\n") % ttywidth)
163 ui.debug(_("assuming %i character terminal\n") % ttywidth)
164 width = ttywidth - maxname - 2 - 6 - 2 - 2
164 width = ttywidth - maxname - 2 - 6 - 2 - 2
165
165
166 for date, count in rate:
166 for date, count in rate:
167 print "%s %6d %s" % (pad(date, maxname), count,
167 print "%s %6d %s" % (pad(date, maxname), count,
168 "*" * int(count * width / maxcount))
168 "*" * int(count * width / maxcount))
169
169
170
170
171 cmdtable = {
171 cmdtable = {
172 "churn":
172 "churn":
173 (churn,
173 (churn,
174 [('r', 'rev', [], _('count rate for the specified revision or range')),
174 [('r', 'rev', [], _('count rate for the specified revision or range')),
175 ('d', 'date', '', _('count rate for revs matching date spec')),
175 ('d', 'date', '', _('count rate for revs matching date spec')),
176 ('t', 'template', '{author|email}', _('template to group changesets')),
176 ('t', 'template', '{author|email}', _('template to group changesets')),
177 ('f', 'dateformat', '',
177 ('f', 'dateformat', '',
178 _('strftime-compatible format for grouping by date')),
178 _('strftime-compatible format for grouping by date')),
179 ('c', 'changesets', False, _('count rate by number of changesets')),
179 ('c', 'changesets', False, _('count rate by number of changesets')),
180 ('s', 'sort', False, _('sort by key (default: sort by count)')),
180 ('s', 'sort', False, _('sort by key (default: sort by count)')),
181 ('', 'aliases', '', _('file with email aliases')),
181 ('', 'aliases', '', _('file with email aliases')),
182 ('', 'progress', None, _('show progress'))],
182 ('', 'progress', None, _('show progress'))],
183 _("hg stats [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
183 _("hg stats [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
184 }
184 }
General Comments 0
You need to be logged in to leave comments. Login now