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).
--- 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
--- 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):
--- 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)
--- 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
--- 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):
--- 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
--- 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