[PATCH] python implementation of diffstat

Benoît Allard benoit at aeteurope.nl
Mon Dec 22 10:33:04 UTC 2008


Nice idea ! especially on windows ! thanks.

Minor comments follow

Alexander Solovyov wrote:
> # HG changeset patch
> # User Alexander Solovyov <piranha at piranha.org.ua>
> # Date 1229896341 -7200
> # Node ID c460e0ccdbf8beb293dc834f93fa854668b24dc8
> # Parent  3896310bd797a3c0db22654321e88db9cc73d3e7
> python implementation of diffstat
> 
> Implemented as a two function: 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 --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 = ''.join(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,55 @@
>      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):
> +    stats = list(diffstatdata(lines))
> +    width = 78

The churn extension reads here the width of the terminal look at 
get_tty_width.

> +
> +    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 = 1
> +    while (maxtotal / factor) > graphwidth:
> +        factor += 1
> +
> +    if stats:
> +        yield (' %d files changed, %d insertions(+), %d deletions(-)\n' %
> +               (len(stats), totaladds, totalremoves))
> +
> +    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 1
                                                         "one + or -"

When this make sense for big 'factor', this leads to printing a '-' even 
if there is no deletion (see test results), something like 
max(adds/factor, (adds or 0) and 1) should do the trick.

> +        pluses = '+' * max(adds/factor, 1)
> +        minuses = '-' * max(removes/factor, 1)
> +        yield ' %-*s |  %*.d %s%s\n' % (maxname, filename, countwidth,
> +                                        adds+removes, pluses, minuses)
> 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
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 4197 bytes
Desc: S/MIME Cryptographic Signature
Url : http://selenic.com/pipermail/mercurial-devel/attachments/20081222/d46720b5/attachment.bin 


More information about the Mercurial-devel mailing list