# HG changeset patch # User Alexander Solovyov # Date 1230194904 -7200 # Node ID 4949729ee9ee5f1e22d1212c557bed3ecc6ff5d3 # Parent c7f48414f3adaf8c5b750dd7c764551007482848 python implementation of diffstat Implemented as two functions: diffstat, which yields lines of text, formatted as a usual diffstat output, and diffstatdata, which is called inside diffstat to do real performing and yield file names with appropriate data (numbers of added and removed lines). diff -r c7f48414f3ad -r 4949729ee9ee hgext/churn.py --- a/hgext/churn.py Sun Dec 28 19:59:42 2008 +0100 +++ b/hgext/churn.py Thu Dec 25 10:48:24 2008 +0200 @@ -12,27 +12,6 @@ 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: @@ -157,7 +136,7 @@ maxcount = float(max([v for k, v in rate])) maxname = max([len(k) for k, v in rate]) - ttywidth = get_tty_width() + ttywidth = util.termwidth() ui.debug(_("assuming %i character terminal\n") % ttywidth) width = ttywidth - maxname - 2 - 6 - 2 - 2 diff -r c7f48414f3ad -r 4949729ee9ee hgext/patchbomb.py --- a/hgext/patchbomb.py Sun Dec 28 19:59:42 2008 +0100 +++ b/hgext/patchbomb.py Thu Dec 25 10:48:24 2008 +0200 @@ -9,8 +9,7 @@ The remainder of the changeset description. - [Optional] If the diffstat program is installed, the result of - running diffstat on the patch. + [Optional] The result of running diffstat on the patch. The patch itself, as generated by "hg export". @@ -99,16 +98,12 @@ def cdiffstat(ui, summary, patchlines): s = patch.diffstat(patchlines) - if s: - if summary: - ui.write(summary, '\n') - ui.write(s, '\n') - ans = prompt(ui, _('Does the diffstat above look okay? '), 'y') - if not ans.lower().startswith('y'): - raise util.Abort(_('diffstat rejected')) - elif s is None: - ui.warn(_('no diffstat information available\n')) - s = '' + if summary: + ui.write(summary, '\n') + ui.write(s, '\n') + ans = prompt(ui, _('Does the diffstat above look okay? '), 'y') + if not ans.lower().startswith('y'): + raise util.Abort(_('diffstat rejected')) return s def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None): diff -r c7f48414f3ad -r 4949729ee9ee mercurial/patch.py --- a/mercurial/patch.py Sun Dec 28 19:59:42 2008 +0100 +++ b/mercurial/patch.py Thu Dec 25 10:48:24 2008 +0200 @@ -9,7 +9,7 @@ from i18n import _ from node import hex, nullid, short import base85, cmdutil, mdiff, util, revlog, diffhelpers, copies -import cStringIO, email.Parser, os, re, errno +import cStringIO, email.Parser, os, re, errno, math import sys, tempfile, zlib gitre = re.compile('diff --git a/(.*) b/(.*)') @@ -1344,13 +1344,57 @@ for seqno, rev in enumerate(revs): single(rev, seqno+1, fp) -def diffstat(patchlines): - if not util.find_exe('diffstat'): - return - output = util.filter('\n'.join(patchlines), - 'diffstat -p1 -w79 2>%s' % util.nulldev) - stat = [l.lstrip() for l in output.splitlines(True)] - last = stat.pop() - stat.insert(0, last) - stat = ''.join(stat) - return stat +def diffstatdata(lines): + filename = None + for line in lines: + if line.startswith('diff'): + if filename: + yield (filename, adds, removes) + # set numbers to 0 anyway when starting new file + adds = 0 + removes = 0 + if line.startswith('diff --git'): + filename = gitre.search(line).group(1) + else: + # format: "diff -r ... -r ... file name" + filename = line.split(None, 5)[-1] + elif line.startswith('+') and not line.startswith('+++'): + adds += 1 + elif line.startswith('-') and not line.startswith('---'): + removes += 1 + yield (filename, adds, removes) + +def diffstat(lines): + output = [] + stats = list(diffstatdata(lines)) + width = util.termwidth() - 2 + + maxtotal, maxname = 0, 0 + totaladds, totalremoves = 0, 0 + for filename, adds, removes in stats: + totaladds += adds + totalremoves += removes + maxname = max(maxname, len(filename)) + maxtotal = max(maxtotal, adds+removes) + + countwidth = len(str(maxtotal)) + graphwidth = width - countwidth - maxname + if graphwidth < 10: + graphwidth = 10 + + factor = int(math.ceil(float(maxtotal) / graphwidth)) + + for filename, adds, removes in stats: + # If diffstat runs out of room it doesn't print anything, which + # isn't very useful, so always print at least one + or - if there + # were at least some changes + pluses = '+' * max(adds/factor, int(bool(adds))) + minuses = '-' * max(removes/factor, int(bool(removes))) + output.append(' %-*s | %*.d %s%s\n' % (maxname, filename, countwidth, + adds+removes, pluses, minuses)) + + if stats: + output.append(' %d files changed, %d insertions(+), %d deletions(-)\n' % + (len(stats), totaladds, totalremoves)) + + return ''.join(output) diff -r c7f48414f3ad -r 4949729ee9ee mercurial/util.py --- a/mercurial/util.py Sun Dec 28 19:59:42 2008 +0100 +++ b/mercurial/util.py Thu Dec 25 10:48:24 2008 +0200 @@ -1985,3 +1985,24 @@ def uirepr(s): # Avoid double backslash in Windows path repr() return repr(s).replace('\\\\', '\\') + +def termwidth(): + 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 diff -r c7f48414f3ad -r 4949729ee9ee tests/test-notify.out --- a/tests/test-notify.out Sun Dec 28 19:59:42 2008 +0100 +++ b/tests/test-notify.out Thu Dec 25 10:48:24 2008 +0200 @@ -150,7 +150,8 @@ b diffstat: -files patched: 1 + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (6 lines): diff -r c7f48414f3ad -r 4949729ee9ee tests/test-patchbomb --- a/tests/test-patchbomb Sun Dec 28 19:59:42 2008 +0100 +++ b/tests/test-patchbomb Thu Dec 25 10:48:24 2008 +0200 @@ -11,6 +11,8 @@ echo "[extensions]" >> $HGRCPATH echo "patchbomb=" >> $HGRCPATH +COLUMNS=80; export COLUMNS + hg init t cd t echo a > a diff -r c7f48414f3ad -r 4949729ee9ee tests/test-patchbomb.out --- a/tests/test-patchbomb.out Sun Dec 28 19:59:42 2008 +0100 +++ b/tests/test-patchbomb.out Thu Dec 25 10:48:24 2008 +0200 @@ -196,7 +196,8 @@ c -files patched: 1 + c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) Displaying [PATCH] test ... @@ -211,7 +212,8 @@ To: foo Cc: bar -files patched: 1 + c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) # HG changeset patch @@ -232,15 +234,19 @@ a -files patched: 1 + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) b -files patched: 1 + b | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) Final summary: -files patched: 2 + a | 1 + + b | 1 + + 2 files changed, 2 insertions(+), 0 deletions(-) Write the introductory message for the patch series. @@ -258,7 +264,9 @@ Cc: bar -files patched: 2 + a | 1 + + b | 1 + + 2 files changed, 2 insertions(+), 0 deletions(-) Displaying [PATCH 1 of 2] a ... Content-Type: text/plain; charset="us-ascii" @@ -274,7 +282,8 @@ To: foo Cc: bar -files patched: 1 + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) # HG changeset patch @@ -304,7 +313,8 @@ To: foo Cc: bar -files patched: 1 + b | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) # HG changeset patch