Mercurial > public > mercurial-scm > hg-stable
annotate contrib/churn.py @ 4531:b51a8138292a
Avoid extra filelogs entries.
Right now, there are some situations in which localrepo.filecommit can
create filelog entries even though they're not needed. For example:
- permissions for a file have changed;
- qrefresh can create a filelog entry identical to its parent (see the
added test);
- convert-repo creates extra filelog entries in every merge where the
first parent has added files (for example, changeset ebebe9577a1a of
the kernel repo added extra filelog entries to files in the
arch/blackfin directory, even though the merge should only touch the
drivers/ata directory). This makes "hg log file" in a converted repo
less useful than it could be, since it may mention many merges that
don't actually touch that specific file.
They all come from the same basic problem: localrepo.commit (through
filecommit) creates new filelog entries for all files passed to it
(except for some cases during a merge).
Patch and test case provided by Benoit.
This should fix issue351.
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Sat, 09 Jun 2007 01:04:28 -0300 |
parents | ba45041827a2 |
children | 9bbc0217209b |
rev | line source |
---|---|
3040 | 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 | |
3963
ba45041827a2
remove various unused import
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
3877
diff
changeset
|
14 import sys |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
15 from mercurial.i18n import gettext as _ |
3877
abaee83ce0a6
Replace demandload with new demandimport
Matt Mackall <mpm@selenic.com>
parents:
3792
diff
changeset
|
16 from mercurial import hg, mdiff, cmdutil, ui, util, templater, node |
3040 | 17 |
18 def __gather(ui, repo, node1, node2): | |
19 def dirtywork(f, mmap1, mmap2): | |
20 lines = 0 | |
21 | |
3042
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3041
diff
changeset
|
22 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:
3041
diff
changeset
|
23 tn = mmap2 and repo.file(f).read(mmap2[f]) or None |
3040 | 24 |
25 diff = mdiff.unidiff(to, "", tn, "", f).split("\n") | |
26 | |
27 for line in diff: | |
3043
fe0e3508ec6e
[churn] Trivial cleanup
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3042
diff
changeset
|
28 if not line: |
3040 | 29 continue # skip EOF |
3042
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3041
diff
changeset
|
30 if line.startswith(" "): |
3040 | 31 continue # context line |
3042
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3041
diff
changeset
|
32 if line.startswith("--- ") or line.startswith("+++ "): |
3040 | 33 continue # begining of diff |
3042
2d35d7c6f251
[churn] Trivial cleanup suggested by Thomas
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3041
diff
changeset
|
34 if line.startswith("@@ "): |
3040 | 35 continue # info line |
36 | |
37 # changed lines | |
38 lines += 1 | |
39 | |
40 return lines | |
41 | |
42 ## | |
43 | |
44 lines = 0 | |
45 | |
3048
8d344bc72e68
[churn] repo.changes was renamed to repo.status
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3046
diff
changeset
|
46 changes = repo.status(node1, node2, None, util.always)[:5] |
3040 | 47 |
48 modified, added, removed, deleted, unknown = changes | |
49 | |
50 who = repo.changelog.read(node2)[1] | |
51 who = templater.email(who) # get the email of the person | |
52 | |
53 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0]) | |
54 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0]) | |
55 for f in modified: | |
56 lines += dirtywork(f, mmap1, mmap2) | |
57 | |
58 for f in added: | |
59 lines += dirtywork(f, None, mmap2) | |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
60 |
3040 | 61 for f in removed: |
62 lines += dirtywork(f, mmap1, None) | |
63 | |
64 for f in deleted: | |
65 lines += dirtywork(f, mmap1, mmap2) | |
66 | |
67 for f in unknown: | |
68 lines += dirtywork(f, mmap1, mmap2) | |
69 | |
70 return (who, lines) | |
71 | |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
72 def gather_stats(ui, repo, amap, revs=None, progress=False): |
3040 | 73 stats = {} |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
74 |
3040 | 75 cl = repo.changelog |
76 | |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
77 if not revs: |
3046 | 78 revs = range(0, cl.count()) |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
79 |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
80 nr_revs = len(revs) |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
81 cur_rev = 0 |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
82 |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
83 for rev in revs: |
3051
7ffaf5aba4d8
[churn] Fix progress bar not incrementing when merge cset is encountered
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3050
diff
changeset
|
84 cur_rev += 1 # next revision |
7ffaf5aba4d8
[churn] Fix progress bar not incrementing when merge cset is encountered
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3050
diff
changeset
|
85 |
3040 | 86 node2 = cl.node(rev) |
87 node1 = cl.parents(node2)[0] | |
88 | |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
89 if cl.parents(node2)[1] != node.nullid: |
3049
461573aa02ef
[churn] Ignore merge csets
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3048
diff
changeset
|
90 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,)) |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
91 continue |
3049
461573aa02ef
[churn] Ignore merge csets
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3048
diff
changeset
|
92 |
3040 | 93 who, lines = __gather(ui, repo, node1, node2) |
94 | |
95 # remap the owner if possible | |
96 if amap.has_key(who): | |
97 ui.note("using '%s' alias for '%s'\n" % (amap[who], who)) | |
98 who = amap[who] | |
99 | |
100 if not stats.has_key(who): | |
101 stats[who] = 0 | |
102 stats[who] += lines | |
103 | |
104 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who)) | |
105 | |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
106 if progress: |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
107 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:
3049
diff
changeset
|
108 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),)) |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
109 sys.stdout.flush() |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
110 |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
111 if progress: |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
112 ui.write("done\n") |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
113 sys.stdout.flush() |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
114 |
3040 | 115 return stats |
116 | |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
117 def churn(ui, repo, **opts): |
3040 | 118 "Graphs the number of lines changed" |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
119 |
3040 | 120 def pad(s, l): |
121 if len(s) < l: | |
122 return s + " " * (l-len(s)) | |
123 return s[0:l] | |
124 | |
125 def graph(n, maximum, width, char): | |
126 n = int(n * width / float(maximum)) | |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
127 |
3040 | 128 return char * (n) |
129 | |
130 def get_aliases(f): | |
131 aliases = {} | |
132 | |
133 for l in f.readlines(): | |
134 l = l.strip() | |
135 alias, actual = l.split(" ") | |
136 aliases[alias] = actual | |
137 | |
138 return aliases | |
3223
53e843840349
Whitespace/Tab cleanup
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3090
diff
changeset
|
139 |
3040 | 140 amap = {} |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
141 aliases = opts.get('aliases') |
3040 | 142 if aliases: |
143 try: | |
144 f = open(aliases,"r") | |
145 except OSError, e: | |
146 print "Error: " + e | |
147 return | |
148 | |
149 amap = get_aliases(f) | |
150 f.close() | |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
151 |
3792
4670470b97bd
Fix revrange() call in the churn contrib
Edouard Gomez <ed.gomez@free.fr>
parents:
3223
diff
changeset
|
152 revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])] |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
153 revs.sort() |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
154 stats = gather_stats(ui, repo, amap, revs, opts.get('progress')) |
3040 | 155 |
156 # make a list of tuples (name, lines) and sort it in descending order | |
157 ordered = stats.items() | |
3060
50e0392d51df
Fix for Python 2.3 compatibility.
Shun-ichi GOTO <shunichi.goto@gmail.com>
parents:
3052
diff
changeset
|
158 ordered.sort(lambda x, y: cmp(y[1], x[1])) |
3040 | 159 |
160 maximum = ordered[0][1] | |
161 | |
162 ui.note("Assuming 80 character terminal\n") | |
163 width = 80 - 1 | |
164 | |
165 for i in ordered: | |
166 person = i[0] | |
167 lines = i[1] | |
168 print "%s %6d %s" % (pad(person, 20), lines, | |
169 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*')) | |
170 | |
171 cmdtable = { | |
172 "churn": | |
173 (churn, | |
3045
c0be8990e819
Add revision range support
Brendan Cully <brendan@kublai.com>
parents:
3043
diff
changeset
|
174 [('r', 'rev', [], _('limit statistics to the specified revisions')), |
3050
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
175 ('', 'aliases', '', _('file with email aliases')), |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
176 ('', 'progress', None, _('show progress'))], |
dd1a142988d3
[churn] progress meter
Josef "Jeff" Sipek <jeffpc@josefsipek.net>
parents:
3049
diff
changeset
|
177 'hg churn [-r revision range] [-a file] [--progress]'), |
3040 | 178 } |