1 # churn.py - create a graph of revisions count grouped by template
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
9 '''command to display statistics about repository history'''
11 from mercurial.i18n import _
12 from mercurial import patch, cmdutil, util, templater
16 def maketemplater(ui, repo, tmpl):
17 tmpl = templater.parsestring(tmpl, quoted=False)
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
20 except SyntaxError, inst:
21 raise util.Abort(inst.args[0])
25 def changedlines(ui, repo, ctx1, ctx2, fns):
27 fmatch = cmdutil.matchfiles(repo, fns)
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
29 for l in diff.split('\n'):
30 if (l.startswith("+") and not l.startswith("+++ ") or
31 l.startswith("-") and not l.startswith("--- ")):
35 def countrate(ui, repo, amap, *pats, **opts):
37 if opts.get('dateformat'):
40 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
41 return date.strftime(opts['dateformat'])
43 tmpl = opts.get('template', '{author|email}')
44 tmpl = maketemplater(ui, repo, tmpl)
54 df = util.matchdate(opts['date'])
56 get = util.cachefunc(lambda r: repo[r].changeset())
57 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
58 for st, rev, fns in changeiter:
61 if df and not df(get(rev)[2][0]): # doesn't match date format
66 key = amap.get(key, key) # alias remap
67 if opts.get('changesets'):
68 rate[key] = rate.get(key, 0) + 1
70 parents = ctx.parents()
72 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
76 lines = changedlines(ui, repo, ctx1, ctx, fns)
77 rate[key] = rate.get(key, 0) + lines
79 if opts.get('progress'):
81 newpct = int(100.0 * count / max(len(repo), 1))
84 ui.write("\r" + _("generating stats: %d%%") % pct)
87 if opts.get('progress'):
94 def churn(ui, repo, *pats, **opts):
95 '''histogram of changes to the repository
97 This command will display a histogram representing the number
98 of changed lines or revisions, grouped according to the given
99 template. The default template will group changes by author.
100 The --dateformat option may be used to group the results by
103 Statistics are based on the number of changed lines, or
104 alternatively the number of matching revisions if the
105 --changesets option is specified.
109 # display count of changed lines for every committer
110 hg churn -t '{author|email}'
112 # display daily activity graph
113 hg churn -f '%H' -s -c
115 # display activity of developers by month
116 hg churn -f '%Y-%m' -s -c
118 # display count of lines changed in every year
121 It is possible to map alternate email addresses to a main address
122 by providing a file using the following format::
124 <alias email> <actual email>
126 Such a file may be specified with the --aliases option, otherwise
127 a .hgchurn file will be looked for in the working directory root.
130 return (s + " " * l)[:l]
133 aliases = opts.get('aliases')
134 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
135 aliases = repo.wjoin('.hgchurn')
137 for l in open(aliases, "r"):
139 alias, actual = l.split()
142 rate = countrate(ui, repo, amap, *pats, **opts).items()
146 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
147 rate.sort(key=sortkey)
149 maxcount = float(max([v for k, v in rate]))
150 maxname = max([len(k) for k, v in rate])
152 ttywidth = util.termwidth()
153 ui.debug(_("assuming %i character terminal\n") % ttywidth)
154 width = ttywidth - maxname - 2 - 6 - 2 - 2
156 for date, count in rate:
157 print "%s %6d %s" % (pad(date, maxname), count,
158 "*" * int(count * width / maxcount))
164 [('r', 'rev', [], _('count rate for the specified revision or range')),
165 ('d', 'date', '', _('count rate for revisions matching date spec')),
166 ('t', 'template', '{author|email}', _('template to group changesets')),
167 ('f', 'dateformat', '',
168 _('strftime-compatible format for grouping by date')),
169 ('c', 'changesets', False, _('count rate by number of changesets')),
170 ('s', 'sort', False, _('sort by key (default: sort by count)')),
171 ('', 'aliases', '', _('file with email aliases')),
172 ('', 'progress', None, _('show progress'))],
173 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),