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