##// END OF EJS Templates
Fix for Python 2.3 compatibility....
Shun-ichi GOTO -
r3060:50e0392d default
parent child Browse files
Show More
@@ -1,179 +1,179 b''
1 # churn.py - create a graph showing who changed the most lines
1 # churn.py - create a graph showing who changed the most lines
2 #
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 #
8 #
9 # Aliases map file format is simple one alias per line in the following
9 # Aliases map file format is simple one alias per line in the following
10 # format:
10 # format:
11 #
11 #
12 # <alias email> <actual email>
12 # <alias email> <actual email>
13
13
14 from mercurial.demandload import *
14 from mercurial.demandload import *
15 from mercurial.i18n import gettext as _
15 from mercurial.i18n import gettext as _
16 demandload(globals(), 'time sys signal os')
16 demandload(globals(), 'time sys signal os')
17 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util,templater,node')
17 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util,templater,node')
18
18
19 def __gather(ui, repo, node1, node2):
19 def __gather(ui, repo, node1, node2):
20 def dirtywork(f, mmap1, mmap2):
20 def dirtywork(f, mmap1, mmap2):
21 lines = 0
21 lines = 0
22
22
23 to = mmap1 and repo.file(f).read(mmap1[f]) or None
23 to = mmap1 and repo.file(f).read(mmap1[f]) or None
24 tn = mmap2 and repo.file(f).read(mmap2[f]) or None
24 tn = mmap2 and repo.file(f).read(mmap2[f]) or None
25
25
26 diff = mdiff.unidiff(to, "", tn, "", f).split("\n")
26 diff = mdiff.unidiff(to, "", tn, "", f).split("\n")
27
27
28 for line in diff:
28 for line in diff:
29 if not line:
29 if not line:
30 continue # skip EOF
30 continue # skip EOF
31 if line.startswith(" "):
31 if line.startswith(" "):
32 continue # context line
32 continue # context line
33 if line.startswith("--- ") or line.startswith("+++ "):
33 if line.startswith("--- ") or line.startswith("+++ "):
34 continue # begining of diff
34 continue # begining of diff
35 if line.startswith("@@ "):
35 if line.startswith("@@ "):
36 continue # info line
36 continue # info line
37
37
38 # changed lines
38 # changed lines
39 lines += 1
39 lines += 1
40
40
41 return lines
41 return lines
42
42
43 ##
43 ##
44
44
45 lines = 0
45 lines = 0
46
46
47 changes = repo.status(node1, node2, None, util.always)[:5]
47 changes = repo.status(node1, node2, None, util.always)[:5]
48
48
49 modified, added, removed, deleted, unknown = changes
49 modified, added, removed, deleted, unknown = changes
50
50
51 who = repo.changelog.read(node2)[1]
51 who = repo.changelog.read(node2)[1]
52 who = templater.email(who) # get the email of the person
52 who = templater.email(who) # get the email of the person
53
53
54 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
54 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
55 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
55 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
56 for f in modified:
56 for f in modified:
57 lines += dirtywork(f, mmap1, mmap2)
57 lines += dirtywork(f, mmap1, mmap2)
58
58
59 for f in added:
59 for f in added:
60 lines += dirtywork(f, None, mmap2)
60 lines += dirtywork(f, None, mmap2)
61
61
62 for f in removed:
62 for f in removed:
63 lines += dirtywork(f, mmap1, None)
63 lines += dirtywork(f, mmap1, None)
64
64
65 for f in deleted:
65 for f in deleted:
66 lines += dirtywork(f, mmap1, mmap2)
66 lines += dirtywork(f, mmap1, mmap2)
67
67
68 for f in unknown:
68 for f in unknown:
69 lines += dirtywork(f, mmap1, mmap2)
69 lines += dirtywork(f, mmap1, mmap2)
70
70
71 return (who, lines)
71 return (who, lines)
72
72
73 def gather_stats(ui, repo, amap, revs=None, progress=False):
73 def gather_stats(ui, repo, amap, revs=None, progress=False):
74 stats = {}
74 stats = {}
75
75
76 cl = repo.changelog
76 cl = repo.changelog
77
77
78 if not revs:
78 if not revs:
79 revs = range(0, cl.count())
79 revs = range(0, cl.count())
80
80
81 nr_revs = len(revs)
81 nr_revs = len(revs)
82 cur_rev = 0
82 cur_rev = 0
83
83
84 for rev in revs:
84 for rev in revs:
85 cur_rev += 1 # next revision
85 cur_rev += 1 # next revision
86
86
87 node2 = cl.node(rev)
87 node2 = cl.node(rev)
88 node1 = cl.parents(node2)[0]
88 node1 = cl.parents(node2)[0]
89
89
90 if cl.parents(node2)[1] != node.nullid:
90 if cl.parents(node2)[1] != node.nullid:
91 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
91 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
92 continue
92 continue
93
93
94 who, lines = __gather(ui, repo, node1, node2)
94 who, lines = __gather(ui, repo, node1, node2)
95
95
96 # remap the owner if possible
96 # remap the owner if possible
97 if amap.has_key(who):
97 if amap.has_key(who):
98 ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
98 ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
99 who = amap[who]
99 who = amap[who]
100
100
101 if not stats.has_key(who):
101 if not stats.has_key(who):
102 stats[who] = 0
102 stats[who] = 0
103 stats[who] += lines
103 stats[who] += lines
104
104
105 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
105 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
106
106
107 if progress:
107 if progress:
108 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
108 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
109 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),))
109 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),))
110 sys.stdout.flush()
110 sys.stdout.flush()
111
111
112 if progress:
112 if progress:
113 ui.write("done\n")
113 ui.write("done\n")
114 sys.stdout.flush()
114 sys.stdout.flush()
115
115
116 return stats
116 return stats
117
117
118 def churn(ui, repo, **opts):
118 def churn(ui, repo, **opts):
119 "Graphs the number of lines changed"
119 "Graphs the number of lines changed"
120
120
121 def pad(s, l):
121 def pad(s, l):
122 if len(s) < l:
122 if len(s) < l:
123 return s + " " * (l-len(s))
123 return s + " " * (l-len(s))
124 return s[0:l]
124 return s[0:l]
125
125
126 def graph(n, maximum, width, char):
126 def graph(n, maximum, width, char):
127 n = int(n * width / float(maximum))
127 n = int(n * width / float(maximum))
128
128
129 return char * (n)
129 return char * (n)
130
130
131 def get_aliases(f):
131 def get_aliases(f):
132 aliases = {}
132 aliases = {}
133
133
134 for l in f.readlines():
134 for l in f.readlines():
135 l = l.strip()
135 l = l.strip()
136 alias, actual = l.split(" ")
136 alias, actual = l.split(" ")
137 aliases[alias] = actual
137 aliases[alias] = actual
138
138
139 return aliases
139 return aliases
140
140
141 amap = {}
141 amap = {}
142 aliases = opts.get('aliases')
142 aliases = opts.get('aliases')
143 if aliases:
143 if aliases:
144 try:
144 try:
145 f = open(aliases,"r")
145 f = open(aliases,"r")
146 except OSError, e:
146 except OSError, e:
147 print "Error: " + e
147 print "Error: " + e
148 return
148 return
149
149
150 amap = get_aliases(f)
150 amap = get_aliases(f)
151 f.close()
151 f.close()
152
152
153 revs = [int(r) for r in commands.revrange(ui, repo, opts['rev'])]
153 revs = [int(r) for r in commands.revrange(ui, repo, opts['rev'])]
154 revs.sort()
154 revs.sort()
155 stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
155 stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
156
156
157 # make a list of tuples (name, lines) and sort it in descending order
157 # make a list of tuples (name, lines) and sort it in descending order
158 ordered = stats.items()
158 ordered = stats.items()
159 ordered.sort(cmp=lambda x, y: cmp(y[1], x[1]))
159 ordered.sort(lambda x, y: cmp(y[1], x[1]))
160
160
161 maximum = ordered[0][1]
161 maximum = ordered[0][1]
162
162
163 ui.note("Assuming 80 character terminal\n")
163 ui.note("Assuming 80 character terminal\n")
164 width = 80 - 1
164 width = 80 - 1
165
165
166 for i in ordered:
166 for i in ordered:
167 person = i[0]
167 person = i[0]
168 lines = i[1]
168 lines = i[1]
169 print "%s %6d %s" % (pad(person, 20), lines,
169 print "%s %6d %s" % (pad(person, 20), lines,
170 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*'))
170 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*'))
171
171
172 cmdtable = {
172 cmdtable = {
173 "churn":
173 "churn":
174 (churn,
174 (churn,
175 [('r', 'rev', [], _('limit statistics to the specified revisions')),
175 [('r', 'rev', [], _('limit statistics to the specified revisions')),
176 ('', 'aliases', '', _('file with email aliases')),
176 ('', 'aliases', '', _('file with email aliases')),
177 ('', 'progress', None, _('show progress'))],
177 ('', 'progress', None, _('show progress'))],
178 'hg churn [-r revision range] [-a file] [--progress]'),
178 'hg churn [-r revision range] [-a file] [--progress]'),
179 }
179 }
General Comments 0
You need to be logged in to leave comments. Login now