Mercurial > hg
view hgext/churn.py @ 7177:09ed32b79656
mq: Fix --qrefresh --short to work with --exclude and --include
pmezard expects
hg qref -s -X b
to apply the -X to the list of files in the patch, and thus remove b from the
patch.
That's how it worked before f7fc5f5ecd62. That change seemed sensible, but it
wasn't...
mpm says
(17:22:30) pmezard_: kiilerix1: do you mean that -X should be forbidden with -s ?
(17:22:54) pmezard_: kiilerix1: and --include too
(17:23:03) mpm: No because you should be able to say hg qref -s foo* -X foo-bar
so mpm expects
hg qref -s -X b *
to apply the -X to the list of files in the working directory, and thus don't
include b in the patch
This patch tries to make both usecases work by creating a matchfn which uses
the include/excludes but not the filelist.
author | Mads Kiilerich <mads@kiilerich.com> |
---|---|
date | Sun, 19 Oct 2008 16:31:24 +0200 |
parents | 7e6a3bae0e8e |
children | b6f5490effbf |
line wrap: on
line source
# 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, incorporated herein by reference. '''command to show certain statistics about revision history''' from mercurial.i18n import _ from mercurial import patch, cmdutil, util, templater import os, sys import time, datetime def get_tty_width(): if 'COLUMNS' in os.environ: try: return int(os.environ['COLUMNS']) except ValueError: pass try: import termios, array, fcntl for dev in (sys.stdout, sys.stdin): try: fd = dev.fileno() if not os.isatty(fd): continue arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) return array.array('h', arri)[1] except ValueError: pass except ImportError: pass return 80 def maketemplater(ui, repo, tmpl): tmpl = templater.parsestring(tmpl, quoted=False) try: t = cmdutil.changeset_templater(ui, repo, False, None, False) except SyntaxError, inst: raise util.Abort(inst.args[0]) t.use_template(tmpl) return t def changedlines(ui, repo, ctx1, ctx2): lines = 0 ui.pushbuffer() patch.diff(repo, ctx1.node(), ctx2.node()) diff = ui.popbuffer() 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(changenode=ctx.node()) 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) 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(_("\rGenerating stats: %d%%") % pct) sys.stdout.flush() if opts.get('progress'): ui.write("\r") sys.stdout.flush() return rate def churn(ui, repo, *pats, **opts): '''Graph count of revisions grouped by template Will graph count of changed lines or revisions grouped by template or alternatively by date, if dateformat is used. In this case it will override template. By default statistics are counted for number of changed lines. 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 The map file format used to specify aliases is fairly simple: <alias email> <actual email>''' def pad(s, l): return (s + " " * l)[:l] amap = {} aliases = opts.get('aliases') 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) maxcount = float(max([v for k, v in rate])) maxname = max([len(k) for k, v in rate]) ttywidth = get_tty_width() 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 revs 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]")), }