Mercurial > hg > mercurial-crew-with-dirclash
annotate contrib/churn.py @ 5483:0c43f87baba3 default tip
Fix file-changed-to-dir and dir-to-file commits (issue660).
Allow adding to dirstate files that clash with previously existing
but marked for removal. Protect from reintroducing clashes by revert.
This change doesn't address related issues with update. Current
workaround is to do "clean" update by manually removing conflicting
files/dirs from working directory.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sat, 27 Oct 2007 16:27:55 +0400 |
parents | 041bd297f01e |
children |
rev | line source |
---|---|
3037 | 1 # churn.py - create a graph showing who changed the most lines |
2 # | |
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net> | |
4 # | |
5 # This software may be used and distributed according to the terms | |
6 # of the GNU General Public License, incorporated herein by reference. | |
7 # | |
8 # | |
9 # Aliases map file format is simple one alias per line in the following | |
10 # format: | |
11 # | |
12 # <alias email> <actual email> | |
13 | |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
14 from mercurial.i18n import gettext as _ |
3886
abaee83ce0a6
Replace demandload with new demandimport
Matt Mackall <mpm@selenic.com>
parents:
3792
diff
changeset
|
15 from mercurial import hg, mdiff, cmdutil, ui, util, templater, node |
4937
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
16 import os, sys |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
17 |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
18 def get_tty_width(): |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
19 if 'COLUMNS' in os.environ: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
20 try: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
21 return int(os.environ['COLUMNS']) |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
22 except ValueError: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
23 pass |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
24 try: |
5419
041bd297f01e
churn: simplify code to get terminal width
Christian Ebert <blacktrash@gmx.net>
parents:
4937
diff
changeset
|
25 import termios, array, fcntl |
4937
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
26 for dev in (sys.stdout, sys.stdin): |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
27 try: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
28 fd = dev.fileno() |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
29 if not os.isatty(fd): |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
30 continue |
5419
041bd297f01e
churn: simplify code to get terminal width
Christian Ebert <blacktrash@gmx.net>
parents:
4937
diff
changeset
|
31 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) |
041bd297f01e
churn: simplify code to get terminal width
Christian Ebert <blacktrash@gmx.net>
parents:
4937
diff
changeset
|
32 return array.array('h', arri)[1] |
4937
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
33 except ValueError: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
34 pass |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
35 except ImportError: |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
36 pass |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
37 return 80 |
3037 | 38 |
39 def __gather(ui, repo, node1, node2): | |
40 def dirtywork(f, mmap1, mmap2): | |
41 lines = 0 | |
42 | |
3039
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3038
diff
changeset
|
43 to = mmap1 and repo.file(f).read(mmap1[f]) or None |
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3038
diff
changeset
|
44 tn = mmap2 and repo.file(f).read(mmap2[f]) or None |
3037 | 45 |
46 diff = mdiff.unidiff(to, "", tn, "", f).split("\n") | |
47 | |
48 for line in diff: | |
3040
fe0e3508ec6e
[churn] Trivial cleanup
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3039
diff
changeset
|
49 if not line: |
3037 | 50 continue # skip EOF |
3039
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3038
diff
changeset
|
51 if line.startswith(" "): |
3037 | 52 continue # context line |
3039
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3038
diff
changeset
|
53 if line.startswith("--- ") or line.startswith("+++ "): |
3037 | 54 continue # begining of diff |
3039
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3038
diff
changeset
|
55 if line.startswith("@@ "): |
3037 | 56 continue # info line |
57 | |
58 # changed lines | |
59 lines += 1 | |
60 | |
61 return lines | |
62 | |
63 ## | |
64 | |
65 lines = 0 | |
66 | |
3045
8d344bc72e68
[churn] repo.changes was renamed to repo.status
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3043
diff
changeset
|
67 changes = repo.status(node1, node2, None, util.always)[:5] |
3037 | 68 |
69 modified, added, removed, deleted, unknown = changes | |
70 | |
71 who = repo.changelog.read(node2)[1] | |
72 who = templater.email(who) # get the email of the person | |
73 | |
74 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0]) | |
75 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0]) | |
76 for f in modified: | |
77 lines += dirtywork(f, mmap1, mmap2) | |
78 | |
79 for f in added: | |
80 lines += dirtywork(f, None, mmap2) | |
3215
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3087
diff
changeset
|
81 |
3037 | 82 for f in removed: |
83 lines += dirtywork(f, mmap1, None) | |
84 | |
85 for f in deleted: | |
86 lines += dirtywork(f, mmap1, mmap2) | |
87 | |
88 for f in unknown: | |
89 lines += dirtywork(f, mmap1, mmap2) | |
90 | |
91 return (who, lines) | |
92 | |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
93 def gather_stats(ui, repo, amap, revs=None, progress=False): |
3037 | 94 stats = {} |
3215
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3087
diff
changeset
|
95 |
3037 | 96 cl = repo.changelog |
97 | |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
98 if not revs: |
3043 | 99 revs = range(0, cl.count()) |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
100 |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
101 nr_revs = len(revs) |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
102 cur_rev = 0 |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
103 |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
104 for rev in revs: |
3048
7ffaf5aba4d8
[churn] Fix progress bar not incrementing when merge cset is encountered
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3047
diff
changeset
|
105 cur_rev += 1 # next revision |
7ffaf5aba4d8
[churn] Fix progress bar not incrementing when merge cset is encountered
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3047
diff
changeset
|
106 |
3037 | 107 node2 = cl.node(rev) |
108 node1 = cl.parents(node2)[0] | |
109 | |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
110 if cl.parents(node2)[1] != node.nullid: |
3046
461573aa02ef
[churn] Ignore merge csets
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3045
diff
changeset
|
111 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,)) |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
112 continue |
3046
461573aa02ef
[churn] Ignore merge csets
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3045
diff
changeset
|
113 |
3037 | 114 who, lines = __gather(ui, repo, node1, node2) |
115 | |
116 # remap the owner if possible | |
117 if amap.has_key(who): | |
118 ui.note("using '%s' alias for '%s'\n" % (amap[who], who)) | |
119 who = amap[who] | |
120 | |
121 if not stats.has_key(who): | |
122 stats[who] = 0 | |
123 stats[who] += lines | |
124 | |
125 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who)) | |
126 | |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
127 if progress: |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
128 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs): |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
129 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),)) |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
130 sys.stdout.flush() |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
131 |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
132 if progress: |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
133 ui.write("done\n") |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
134 sys.stdout.flush() |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
135 |
3037 | 136 return stats |
137 | |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
138 def churn(ui, repo, **opts): |
3037 | 139 "Graphs the number of lines changed" |
3215
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3087
diff
changeset
|
140 |
3037 | 141 def pad(s, l): |
142 if len(s) < l: | |
143 return s + " " * (l-len(s)) | |
144 return s[0:l] | |
145 | |
146 def graph(n, maximum, width, char): | |
147 n = int(n * width / float(maximum)) | |
3215
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3087
diff
changeset
|
148 |
3037 | 149 return char * (n) |
150 | |
151 def get_aliases(f): | |
152 aliases = {} | |
153 | |
154 for l in f.readlines(): | |
155 l = l.strip() | |
156 alias, actual = l.split(" ") | |
157 aliases[alias] = actual | |
158 | |
159 return aliases | |
3215
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3087
diff
changeset
|
160 |
3037 | 161 amap = {} |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
162 aliases = opts.get('aliases') |
3037 | 163 if aliases: |
164 try: | |
165 f = open(aliases,"r") | |
166 except OSError, e: | |
167 print "Error: " + e | |
168 return | |
169 | |
170 amap = get_aliases(f) | |
171 f.close() | |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
172 |
3792
4670470b97bd
Fix revrange() call in the churn contrib
Edouard Gomez <ed.gomez@free.fr>
parents:
3215
diff
changeset
|
173 revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])] |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
174 revs.sort() |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
175 stats = gather_stats(ui, repo, amap, revs, opts.get('progress')) |
3037 | 176 |
177 # make a list of tuples (name, lines) and sort it in descending order | |
178 ordered = stats.items() | |
3057
50e0392d51df
Fix for Python 2.3 compatibility.
Shun-ichi GOTO <shunichi.goto@gmail.com>
parents:
3049
diff
changeset
|
179 ordered.sort(lambda x, y: cmp(y[1], x[1])) |
3037 | 180 |
181 maximum = ordered[0][1] | |
182 | |
4937
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
183 width = get_tty_width() |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
184 ui.note(_("assuming %i character terminal\n") % width) |
9bbc0217209b
churn: get current terminal width if possible
Christian Ebert <blacktrash@gmx.net>
parents:
3963
diff
changeset
|
185 width -= 1 |
3037 | 186 |
187 for i in ordered: | |
188 person = i[0] | |
189 lines = i[1] | |
190 print "%s %6d %s" % (pad(person, 20), lines, | |
191 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*')) | |
192 | |
193 cmdtable = { | |
194 "churn": | |
195 (churn, | |
3042
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3040
diff
changeset
|
196 [('r', 'rev', [], _('limit statistics to the specified revisions')), |
3047
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
197 ('', 'aliases', '', _('file with email aliases')), |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
198 ('', 'progress', None, _('show progress'))], |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
199 'hg churn [-r revision range] [-a file] [--progress]'), |
3037 | 200 } |