##// END OF EJS Templates
Returns lines changed for paths specified as arguments correctly....
madhu@madhu -
r7870:7bcce39e default
parent child Browse files
Show More
@@ -1,161 +1,162
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 '''command to show certain statistics about revision history'''
8 '''command to show certain statistics about revision history'''
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 maketemplater(ui, repo, tmpl):
15 def maketemplater(ui, repo, tmpl):
16 tmpl = templater.parsestring(tmpl, quoted=False)
16 tmpl = templater.parsestring(tmpl, quoted=False)
17 try:
17 try:
18 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
18 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
19 except SyntaxError, inst:
19 except SyntaxError, inst:
20 raise util.Abort(inst.args[0])
20 raise util.Abort(inst.args[0])
21 t.use_template(tmpl)
21 t.use_template(tmpl)
22 return t
22 return t
23
23
24 def changedlines(ui, repo, ctx1, ctx2):
24 def changedlines(ui, repo, ctx1, ctx2, fns):
25 lines = 0
25 lines = 0
26 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node()))
26 fmatch = cmdutil.match(repo, pats=fns)
27 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
27 for l in diff.split('\n'):
28 for l in diff.split('\n'):
28 if (l.startswith("+") and not l.startswith("+++ ") or
29 if (l.startswith("+") and not l.startswith("+++ ") or
29 l.startswith("-") and not l.startswith("--- ")):
30 l.startswith("-") and not l.startswith("--- ")):
30 lines += 1
31 lines += 1
31 return lines
32 return lines
32
33
33 def countrate(ui, repo, amap, *pats, **opts):
34 def countrate(ui, repo, amap, *pats, **opts):
34 """Calculate stats"""
35 """Calculate stats"""
35 if opts.get('dateformat'):
36 if opts.get('dateformat'):
36 def getkey(ctx):
37 def getkey(ctx):
37 t, tz = ctx.date()
38 t, tz = ctx.date()
38 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
39 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
39 return date.strftime(opts['dateformat'])
40 return date.strftime(opts['dateformat'])
40 else:
41 else:
41 tmpl = opts.get('template', '{author|email}')
42 tmpl = opts.get('template', '{author|email}')
42 tmpl = maketemplater(ui, repo, tmpl)
43 tmpl = maketemplater(ui, repo, tmpl)
43 def getkey(ctx):
44 def getkey(ctx):
44 ui.pushbuffer()
45 ui.pushbuffer()
45 tmpl.show(ctx)
46 tmpl.show(ctx)
46 return ui.popbuffer()
47 return ui.popbuffer()
47
48
48 count = pct = 0
49 count = pct = 0
49 rate = {}
50 rate = {}
50 df = False
51 df = False
51 if opts.get('date'):
52 if opts.get('date'):
52 df = util.matchdate(opts['date'])
53 df = util.matchdate(opts['date'])
53
54
54 get = util.cachefunc(lambda r: repo[r].changeset())
55 get = util.cachefunc(lambda r: repo[r].changeset())
55 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
56 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
56 for st, rev, fns in changeiter:
57 for st, rev, fns in changeiter:
57 if not st == 'add':
58 if not st == 'add':
58 continue
59 continue
59 if df and not df(get(rev)[2][0]): # doesn't match date format
60 if df and not df(get(rev)[2][0]): # doesn't match date format
60 continue
61 continue
61
62
62 ctx = repo[rev]
63 ctx = repo[rev]
63 key = getkey(ctx)
64 key = getkey(ctx)
64 key = amap.get(key, key) # alias remap
65 key = amap.get(key, key) # alias remap
65 if opts.get('changesets'):
66 if opts.get('changesets'):
66 rate[key] = rate.get(key, 0) + 1
67 rate[key] = rate.get(key, 0) + 1
67 else:
68 else:
68 parents = ctx.parents()
69 parents = ctx.parents()
69 if len(parents) > 1:
70 if len(parents) > 1:
70 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
71 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
71 continue
72 continue
72
73
73 ctx1 = parents[0]
74 ctx1 = parents[0]
74 lines = changedlines(ui, repo, ctx1, ctx)
75 lines = changedlines(ui, repo, ctx1, ctx, fns)
75 rate[key] = rate.get(key, 0) + lines
76 rate[key] = rate.get(key, 0) + lines
76
77
77 if opts.get('progress'):
78 if opts.get('progress'):
78 count += 1
79 count += 1
79 newpct = int(100.0 * count / max(len(repo), 1))
80 newpct = int(100.0 * count / max(len(repo), 1))
80 if pct < newpct:
81 if pct < newpct:
81 pct = newpct
82 pct = newpct
82 ui.write(_("\rgenerating stats: %d%%") % pct)
83 ui.write(_("\rgenerating stats: %d%%") % pct)
83 sys.stdout.flush()
84 sys.stdout.flush()
84
85
85 if opts.get('progress'):
86 if opts.get('progress'):
86 ui.write("\r")
87 ui.write("\r")
87 sys.stdout.flush()
88 sys.stdout.flush()
88
89
89 return rate
90 return rate
90
91
91
92
92 def churn(ui, repo, *pats, **opts):
93 def churn(ui, repo, *pats, **opts):
93 '''graph count of revisions grouped by template
94 '''graph count of revisions grouped by template
94
95
95 Will graph count of changed lines or revisions grouped by template or
96 Will graph count of changed lines or revisions grouped by template or
96 alternatively by date, if dateformat is used. In this case it will override
97 alternatively by date, if dateformat is used. In this case it will override
97 template.
98 template.
98
99
99 By default statistics are counted for number of changed lines.
100 By default statistics are counted for number of changed lines.
100
101
101 Examples:
102 Examples:
102
103
103 # display count of changed lines for every committer
104 # display count of changed lines for every committer
104 hg churn -t '{author|email}'
105 hg churn -t '{author|email}'
105
106
106 # display daily activity graph
107 # display daily activity graph
107 hg churn -f '%H' -s -c
108 hg churn -f '%H' -s -c
108
109
109 # display activity of developers by month
110 # display activity of developers by month
110 hg churn -f '%Y-%m' -s -c
111 hg churn -f '%Y-%m' -s -c
111
112
112 # display count of lines changed in every year
113 # display count of lines changed in every year
113 hg churn -f '%Y' -s
114 hg churn -f '%Y' -s
114
115
115 The map file format used to specify aliases is fairly simple:
116 The map file format used to specify aliases is fairly simple:
116
117
117 <alias email> <actual email>'''
118 <alias email> <actual email>'''
118 def pad(s, l):
119 def pad(s, l):
119 return (s + " " * l)[:l]
120 return (s + " " * l)[:l]
120
121
121 amap = {}
122 amap = {}
122 aliases = opts.get('aliases')
123 aliases = opts.get('aliases')
123 if aliases:
124 if aliases:
124 for l in open(aliases, "r"):
125 for l in open(aliases, "r"):
125 l = l.strip()
126 l = l.strip()
126 alias, actual = l.split()
127 alias, actual = l.split()
127 amap[alias] = actual
128 amap[alias] = actual
128
129
129 rate = countrate(ui, repo, amap, *pats, **opts).items()
130 rate = countrate(ui, repo, amap, *pats, **opts).items()
130 if not rate:
131 if not rate:
131 return
132 return
132
133
133 sortfn = ((not opts.get('sort')) and (lambda a, b: cmp(b[1], a[1])) or None)
134 sortfn = ((not opts.get('sort')) and (lambda a, b: cmp(b[1], a[1])) or None)
134 rate.sort(sortfn)
135 rate.sort(sortfn)
135
136
136 maxcount = float(max([v for k, v in rate]))
137 maxcount = float(max([v for k, v in rate]))
137 maxname = max([len(k) for k, v in rate])
138 maxname = max([len(k) for k, v in rate])
138
139
139 ttywidth = util.termwidth()
140 ttywidth = util.termwidth()
140 ui.debug(_("assuming %i character terminal\n") % ttywidth)
141 ui.debug(_("assuming %i character terminal\n") % ttywidth)
141 width = ttywidth - maxname - 2 - 6 - 2 - 2
142 width = ttywidth - maxname - 2 - 6 - 2 - 2
142
143
143 for date, count in rate:
144 for date, count in rate:
144 print "%s %6d %s" % (pad(date, maxname), count,
145 print "%s %6d %s" % (pad(date, maxname), count,
145 "*" * int(count * width / maxcount))
146 "*" * int(count * width / maxcount))
146
147
147
148
148 cmdtable = {
149 cmdtable = {
149 "churn":
150 "churn":
150 (churn,
151 (churn,
151 [('r', 'rev', [], _('count rate for the specified revision or range')),
152 [('r', 'rev', [], _('count rate for the specified revision or range')),
152 ('d', 'date', '', _('count rate for revs matching date spec')),
153 ('d', 'date', '', _('count rate for revs matching date spec')),
153 ('t', 'template', '{author|email}', _('template to group changesets')),
154 ('t', 'template', '{author|email}', _('template to group changesets')),
154 ('f', 'dateformat', '',
155 ('f', 'dateformat', '',
155 _('strftime-compatible format for grouping by date')),
156 _('strftime-compatible format for grouping by date')),
156 ('c', 'changesets', False, _('count rate by number of changesets')),
157 ('c', 'changesets', False, _('count rate by number of changesets')),
157 ('s', 'sort', False, _('sort by key (default: sort by count)')),
158 ('s', 'sort', False, _('sort by key (default: sort by count)')),
158 ('', 'aliases', '', _('file with email aliases')),
159 ('', 'aliases', '', _('file with email aliases')),
159 ('', 'progress', None, _('show progress'))],
160 ('', 'progress', None, _('show progress'))],
160 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
161 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
161 }
162 }
@@ -1,38 +1,46
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "[extensions]" >> $HGRCPATH
3 echo "[extensions]" >> $HGRCPATH
4 echo "churn=" >> $HGRCPATH
4 echo "churn=" >> $HGRCPATH
5
5
6 COLUMNS=80; export COLUMNS
6 COLUMNS=80; export COLUMNS
7
7
8 echo % create test repository
8 echo % create test repository
9 hg init repo
9 hg init repo
10 cd repo
10 cd repo
11 echo a > a
11 echo a > a
12 hg ci -Am adda -u user1 -d 6:00
12 hg ci -Am adda -u user1 -d 6:00
13 echo b >> a
13 echo b >> a
14 echo b > b
14 echo b > b
15 hg ci -m changeba -u user2 -d 9:00 a
15 hg ci -m changeba -u user2 -d 9:00 a
16 hg ci -Am addb -u user2 -d 9:30
16 hg ci -Am addb -u user2 -d 9:30
17 echo c >> a
17 echo c >> a
18 echo c >> b
18 echo c >> b
19 echo c > c
19 echo c > c
20 hg ci -m changeca -u user3 -d 12:00 a
20 hg ci -m changeca -u user3 -d 12:00 a
21 hg ci -m changecb -u user3 -d 12:15 b
21 hg ci -m changecb -u user3 -d 12:15 b
22 hg ci -Am addc -u user3 -d 12:30
22 hg ci -Am addc -u user3 -d 12:30
23 mkdir -p d/e
24 echo abc > d/e/f1.txt
25 hg ci -Am "add d/e/f1.txt" -u user1 -d 12:45 d/e/f1.txt
26 mkdir -p d/g
27 echo def > d/g/f2.txt
28 hg ci -Am "add d/g/f2.txt" -u user1 -d 13:00 d/g/f2.txt
23
29
30 echo % churn separate directories
31 hg churn d/e
24 echo % churn all
32 echo % churn all
25 hg churn
33 hg churn
26 echo % churn up to rev 2
34 echo % churn up to rev 2
27 hg churn -r :2
35 hg churn -r :2
28 echo % churn with aliases
36 echo % churn with aliases
29 cat > ../aliases <<EOF
37 cat > ../aliases <<EOF
30 user1 alias1
38 user1 alias1
31 user3 alias3
39 user3 alias3
32 EOF
40 EOF
33 hg churn --aliases ../aliases
41 hg churn --aliases ../aliases
34 echo % churn with column specifier
42 echo % churn with column specifier
35 COLUMNS=40 hg churn
43 COLUMNS=40 hg churn
36 echo % churn by hour
44 echo % churn by hour
37 hg churn -f '%H' -s
45 hg churn -f '%H' -s
38
46
@@ -1,23 +1,26
1 % create test repository
1 % create test repository
2 adding a
2 adding a
3 adding b
3 adding b
4 adding c
4 adding c
5 % churn separate directories
6 user1 1 ***************************************************************
5 % churn all
7 % churn all
6 user3 3 ***************************************************************
8 user3 3 ***************************************************************
9 user1 3 ***************************************************************
7 user2 2 ******************************************
10 user2 2 ******************************************
8 user1 1 *********************
9 % churn up to rev 2
11 % churn up to rev 2
10 user2 2 ***************************************************************
12 user2 2 ***************************************************************
11 user1 1 *******************************
13 user1 1 *******************************
12 % churn with aliases
14 % churn with aliases
13 alias3 3 **************************************************************
15 alias3 3 **************************************************************
16 alias1 3 **************************************************************
14 user2 2 *****************************************
17 user2 2 *****************************************
15 alias1 1 ********************
16 % churn with column specifier
18 % churn with column specifier
17 user3 3 ***********************
19 user3 3 ***********************
20 user1 3 ***********************
18 user2 2 ***************
21 user2 2 ***************
19 user1 1 *******
20 % churn by hour
22 % churn by hour
21 06 1 **********************
23 06 1 ****************
22 09 2 ********************************************
24 09 2 *********************************
23 12 3 ******************************************************************
25 12 4 ******************************************************************
26 13 1 ****************
General Comments 0
You need to be logged in to leave comments. Login now