[PATCH 1 of 1] python implementation of diffstat

Alexander Solovyov piranha at piranha.org.ua
Sun Dec 21 03:54:30 CST 2008


 4 files changed, 97 insertions(+), 32 deletions(-)
 mercurial/patch.py       |  79 ++++++++++++++++++++++++++++++++++-----
 hgext/patchbomb.py       |  21 ++++------
 tests/test-patchbomb.out |  26 +++++++++----
 tests/test-notify.out    |   3 +-


# HG changeset patch
# User Alexander Solovyov <piranha at piranha.org.ua>
# Date 1229853225 -7200
# Node ID 1797935e306ae8f95ae81f436440822592788514
# Parent  3896310bd797a3c0db22654321e88db9cc73d3e7
python implementation of diffstat

Implemented as an class, string representation of which is similar to
the output of system diffstat command. There is possibility to access
attributes of a diffstat object to get already parsed statistics in
python code.

diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py
--- a/hgext/patchbomb.py
+++ b/hgext/patchbomb.py
@@ -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".
 
@@ -98,17 +97,13 @@
         ui.warn(_('Please enter a valid value.\n'))
 
 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 = ''
+    s = str(patch.diffstat(patchlines))
+    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 --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -1344,13 +1344,72 @@
     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
+class diffstat(object):
+    def __init__(self, lines):
+        self.maxname = 0
+        self.maxtotal = 0
+        self.adds = 0
+        self.removes = 0
+        self.stats = {}
+        self._parse(lines)
+
+    def _parse(self, lines):
+        adds = 0
+        removes = 0
+        filename = None
+
+        for line in lines:
+            if line.startswith('diff'):
+                if filename:
+                    self._add_stats(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
+
+        self._add_stats(filename, adds, removes)
+
+    def _add_stats(self, filename, adds, removes):
+        if filename is None:
+            return
+        self.stats[filename] = (adds, removes)
+        self.maxname = max(self.maxname, len(filename))
+        self.maxtotal = max(self.maxtotal, adds + removes)
+        self.adds += adds
+        self.removes += removes
+
+    def __str__(self):
+        width = 78
+        countwidth = len(str(self.maxtotal))
+        graphwidth = width - countwidth - self.maxname
+        factor = 1
+
+        # The graph width can be <= 0 if there is a modified file with a
+        # filename longer than 'width'. Use a minimum of 10.
+        if graphwidth < 10:
+            graphwidth = 10
+
+        while (self.maxtotal/factor) > graphwidth:
+            factor += 1
+
+        if self.stats:
+            s = (' %d files changed, %d insertions(+), %d deletions(-)\n' %
+                 (len(self.stats), self.adds, self.removes))
+
+        for filename, (adds, removes) in self.stats.items():
+            # If diffstat runs out of room it doesn't print anything, which
+            # isn't very useful, so always print at least one + or 1
+            pluses = '+' * max(adds/factor, 1)
+            minuses = '-' * max(removes/factor, 1)
+            s += ' %-*s |  %*.d %s%s\n' % (self.maxname, filename, countwidth,
+                                           adds+removes, pluses, minuses)
+
+        return s
diff --git a/tests/test-notify.out b/tests/test-notify.out
--- a/tests/test-notify.out
+++ b/tests/test-notify.out
@@ -150,7 +150,8 @@
 	b
 diffstat:
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ a |  1 +-
 
 diffs (6 lines):
 
diff --git a/tests/test-patchbomb.out b/tests/test-patchbomb.out
--- a/tests/test-patchbomb.out
+++ b/tests/test-patchbomb.out
@@ -196,7 +196,8 @@
 
 c
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ c |  1 +-
 
 
 Displaying [PATCH] test ...
@@ -211,7 +212,8 @@
 To: foo
 Cc: bar
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ c |  1 +-
 
 
 # HG changeset patch
@@ -232,15 +234,19 @@
 
 a
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ a |  1 +-
 
 b
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ b |  1 +-
 
 Final summary:
 
-files patched: 2
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+ a |  1 +-
+ b |  1 +-
 
 
 Write the introductory message for the patch series.
@@ -258,7 +264,9 @@
 Cc: bar
 
 
-files patched: 2
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+ a |  1 +-
+ b |  1 +-
 
 Displaying [PATCH 1 of 2] a ...
 Content-Type: text/plain; charset="us-ascii"
@@ -274,7 +282,8 @@
 To: foo
 Cc: bar
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ a |  1 +-
 
 
 # HG changeset patch
@@ -304,7 +313,8 @@
 To: foo
 Cc: bar
 
-files patched: 1
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ b |  1 +-
 
 
 # HG changeset patch


More information about the Mercurial-devel mailing list