##// END OF EJS Templates
config: abort on indented non-continuation lines (issue1829)...
config: abort on indented non-continuation lines (issue1829) Previously, as soon as a continuation would be met, "cont" would stay forever set to True, but "item" was set back to "None". This caused the continuation code bits to run every time, until the next "self.get(section, item) + '\n'" which would crash.

File last commit:

r9467:4c041f1e default
r9469:7f0f882a default
Show More
churn.py
175 lines | 5.8 KiB | text/x-python | PythonLexer
# churn.py - create a graph of revisions count grouped by template
#
# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
# Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
'''command to display statistics about repository history'''
from mercurial.i18n import _
from mercurial import patch, cmdutil, util, templater
import sys, os
import time, datetime
def maketemplater(ui, repo, tmpl):
tmpl = templater.parsestring(tmpl, quoted=False)
try:
t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
except SyntaxError, inst:
raise util.Abort(inst.args[0])
t.use_template(tmpl)
return t
def changedlines(ui, repo, ctx1, ctx2, fns):
lines = 0
fmatch = cmdutil.match(repo, pats=fns)
diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
for l in diff.split('\n'):
if (l.startswith("+") and not l.startswith("+++ ") or
l.startswith("-") and not l.startswith("--- ")):
lines += 1
return lines
def countrate(ui, repo, amap, *pats, **opts):
"""Calculate stats"""
if opts.get('dateformat'):
def getkey(ctx):
t, tz = ctx.date()
date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
return date.strftime(opts['dateformat'])
else:
tmpl = opts.get('template', '{author|email}')
tmpl = maketemplater(ui, repo, tmpl)
def getkey(ctx):
ui.pushbuffer()
tmpl.show(ctx)
return ui.popbuffer()
count = pct = 0
rate = {}
df = False
if opts.get('date'):
df = util.matchdate(opts['date'])
get = util.cachefunc(lambda r: repo[r].changeset())
changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
for st, rev, fns in changeiter:
if not st == 'add':
continue
if df and not df(get(rev)[2][0]): # doesn't match date format
continue
ctx = repo[rev]
key = getkey(ctx)
key = amap.get(key, key) # alias remap
if opts.get('changesets'):
rate[key] = rate.get(key, 0) + 1
else:
parents = ctx.parents()
if len(parents) > 1:
ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
continue
ctx1 = parents[0]
lines = changedlines(ui, repo, ctx1, ctx, fns)
rate[key] = rate.get(key, 0) + lines
if opts.get('progress'):
count += 1
newpct = int(100.0 * count / max(len(repo), 1))
if pct < newpct:
pct = newpct
ui.write("\r" + _("generating stats: %d%%") % pct)
sys.stdout.flush()
if opts.get('progress'):
ui.write("\r")
sys.stdout.flush()
return rate
def churn(ui, repo, *pats, **opts):
'''histogram of changes to the repository
This command will display a histogram representing the number
of changed lines or revisions, grouped according to the given
template. The default template will group changes by author.
The --dateformat option may be used to group the results by
date instead.
Statistics are based on the number of changed lines, or
alternatively the number of matching revisions if the
--changesets option is specified.
Examples:
# display count of changed lines for every committer
hg churn -t '{author|email}'
# display daily activity graph
hg churn -f '%H' -s -c
# display activity of developers by month
hg churn -f '%Y-%m' -s -c
# display count of lines changed in every year
hg churn -f '%Y' -s
It is possible to map alternate email addresses to a main address
by providing a file using the following format:
<alias email> <actual email>
Such a file may be specified with the --aliases option, otherwise a
.hgchurn file will be looked for in the working directory root.
'''
def pad(s, l):
return (s + " " * l)[:l]
amap = {}
aliases = opts.get('aliases')
if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
aliases = repo.wjoin('.hgchurn')
if aliases:
for l in open(aliases, "r"):
l = l.strip()
alias, actual = l.split()
amap[alias] = actual
rate = countrate(ui, repo, amap, *pats, **opts).items()
if not rate:
return
sortfn = ((not opts.get('sort')) and (lambda a, b: cmp(b[1], a[1])) or None)
rate.sort(sortfn)
# Be careful not to have a zero maxcount (issue833)
maxcount = float(max([v for k, v in rate])) or 1.0
maxname = max([len(k) for k, v in rate])
ttywidth = util.termwidth()
ui.debug(_("assuming %i character terminal\n") % ttywidth)
width = ttywidth - maxname - 2 - 6 - 2 - 2
for date, count in rate:
print "%s %6d %s" % (pad(date, maxname), count,
"*" * int(count * width / maxcount))
cmdtable = {
"churn":
(churn,
[('r', 'rev', [], _('count rate for the specified revision or range')),
('d', 'date', '', _('count rate for revisions matching date spec')),
('t', 'template', '{author|email}', _('template to group changesets')),
('f', 'dateformat', '',
_('strftime-compatible format for grouping by date')),
('c', 'changesets', False, _('count rate by number of changesets')),
('s', 'sort', False, _('sort by key (default: sort by count)')),
('', 'aliases', '', _('file with email aliases')),
('', 'progress', None, _('show progress'))],
_("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
}