[PATCH] diff: add a --numstat option

Steve Losh steve at stevelosh.com
Thu Jun 10 17:30:44 CDT 2010


Sorry, I forgot that one-patch bombs need the --intro option.

This patch adds --numstat to 'hg diff', which acts exactly like the git version.  The output looks like this:

$ ./hg diff --numstat -c tip
12	6	mercurial/cmdutil.py
4	1	mercurial/commands.py
...

The first column is lines added, second is lines removed, and third is the filename relative to the root of the repo.

This command is a more machine-friendly version of 'hg diff --stat'.  The application I have in mind that would benefit from this is PeepOpen[1], but I'm sure there are others that could find it useful.

I picked git's output format over that of 'diffstat -t' because I wanted to make it easier for applications that already do something with 'git diff --numstat' to simply drop in support for Mercurial as well.

[1]: http://peepcode.com/products/peepopen

--
Steve Losh
http://stevelosh.com/



On Jun 10, 2010, at 6:25 PM, Steve Losh wrote:

> # HG changeset patch
> # User Steve Losh <steve at stevelosh.com>
> # Date 1276121593 14400
> # Node ID d5da202dea40fd5ee68617ebdc09119a17ade27b
> # Parent  666b62c527678b4655a7c293ee636eef25a65b8f
> diff: add a --numstat option
> 
> This patch adds a --numstat option to the diff command to output a numeric
> summary of changes.
> 
> The output is formatted exactly like the git version:
> 
>    ADDS\tREMOVES\tFILENAME
> 
> If the --stat and --numstat options are given together both summaries will be
> printed, with the numeric summary first (matching git's behavior).
> 
> The relevant tests are also updated.
> 
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -645,28 +645,34 @@
> 
>     for seqno, rev in enumerate(revs):
>         single(rev, seqno + 1, fp)
> 
> def diffordiffstat(ui, repo, diffopts, node1, node2, match,
> -                   changes=None, stat=False, fp=None):
> +                   changes=None, stat=False, fp=None, numstat=False):
>     '''show diff or diffstat.'''
>     if fp is None:
>         write = ui.write
>     else:
>         def write(s, **kw):
>             fp.write(s)
> 
> -    if stat:
> +    if stat or numstat:
>         diffopts.context = 0
>         width = 80
>         if not ui.plain():
>             width = util.termwidth()
>         chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
> -        for chunk, label in patch.diffstatui(util.iterlines(chunks),
> -                                             width=width,
> -                                             git=diffopts.git):
> -            write(chunk, label=label)
> +        if stat and numstat:
> +            chunks = list(chunks)
> +        if numstat:
> +            for chunk, label in patch.numstatui(util.iterlines(chunks)):
> +                write(chunk, label=label)
> +        if stat:
> +            for chunk, label in patch.diffstatui(util.iterlines(chunks),
> +                                                 width=width,
> +                                                 git=diffopts.git):
> +                write(chunk, label=label)
>     else:
>         for chunk, label in patch.diffui(repo, node1, node2, match,
>                                          changes, diffopts):
>             write(chunk, label=label)
> 
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -1212,10 +1212,11 @@
>     """
> 
>     revs = opts.get('rev')
>     change = opts.get('change')
>     stat = opts.get('stat')
> +    numstat = opts.get('numstat')
>     reverse = opts.get('reverse')
> 
>     if revs and change:
>         msg = _('cannot specify --rev and --change at the same time')
>         raise util.Abort(msg)
> @@ -1228,11 +1229,12 @@
>     if reverse:
>         node1, node2 = node2, node1
> 
>     diffopts = patch.diffopts(ui, opts)
>     m = cmdutil.match(repo, pats, opts)
> -    cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
> +    cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m,
> +                           stat=stat, numstat=numstat)
> 
> def export(ui, repo, *changesets, **opts):
>     """dump the header and diffs for one or more changesets
> 
>     Print the changeset header and diffs for one or more revisions.
> @@ -3655,10 +3657,11 @@
>      _('ignore changes in the amount of white space')),
>     ('B', 'ignore-blank-lines', None,
>      _('ignore changes whose lines are all blank')),
>     ('U', 'unified', '', _('number of lines of context to show')),
>     ('', 'stat', None, _('output diffstat-style summary of changes')),
> +    ('', 'numstat', None, _('output numeric-style summary of changes')),
> ]
> 
> similarityopts = [
>     ('s', 'similarity', '',
>            _('guess renamed files by similarity (0<=s<=100)'))
> diff --git a/mercurial/patch.py b/mercurial/patch.py
> --- a/mercurial/patch.py
> +++ b/mercurial/patch.py
> @@ -1708,5 +1708,12 @@
>             if m:
>                 yield (m.group(0), 'diffstat.deleted')
>         else:
>             yield (line, '')
>         yield ('\n', '')
> +
> +def numstatui(lines):
> +    output = ('%d\t%d\t%s\n' % (adds, removes, filename)
> +        for filename, adds, removes, isbinary in diffstatdata(lines)
> +        if not isbinary)
> +    for line in output:
> +        yield (line, '')
> diff --git a/tests/test-debugcomplete.out b/tests/test-debugcomplete.out
> --- a/tests/test-debugcomplete.out
> +++ b/tests/test-debugcomplete.out
> @@ -166,11 +166,11 @@
> % Show all commands + options
> add: include, exclude, dry-run
> annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
> clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd
> commit: addremove, close-branch, include, exclude, message, logfile, date, user
> -diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude
> +diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, numstat, include, exclude
> export: output, switch-parent, rev, text, git, nodates
> forget: include, exclude
> init: ssh, remotecmd
> log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
> merge: force, rev, preview
> diff --git a/tests/test-diffstat b/tests/test-diffstat
> --- a/tests/test-diffstat
> +++ b/tests/test-diffstat
> @@ -4,11 +4,11 @@
> cd repo
> i=0; while [ "$i" -lt 213 ]; do echo a >> a; i=`expr $i + 1`; done
> hg add a
> 
> echo '% wide diffstat'
> -hg diff --stat
> +hg diff --stat --numstat
> 
> echo '% diffstat width'
> COLUMNS=24 hg diff --config ui.interactive=true --stat
> 
> hg ci -m adda
> @@ -18,17 +18,17 @@
> a
> a
> EOF
> 
> echo '% narrow diffstat'
> -hg diff --stat
> +hg diff --stat --numstat
> 
> hg ci -m appenda
> 
> printf '\0' > b
> hg add b
> 
> echo '% binary diffstat'
> -hg diff --stat
> +hg diff --stat --numstat
> 
> echo '% binary git diffstat'
> -hg diff --stat --git
> +hg diff --stat --git --numstat
> diff --git a/tests/test-diffstat.out b/tests/test-diffstat.out
> --- a/tests/test-diffstat.out
> +++ b/tests/test-diffstat.out
> @@ -1,12 +1,14 @@
> % wide diffstat
> +213	0	a
>  a |  213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 213 insertions(+), 0 deletions(-)
> % diffstat width
>  a |  213 ++++++++++++++
>  1 files changed, 213 insertions(+), 0 deletions(-)
> % narrow diffstat
> +3	0	a
>  a |  3 +++
>  1 files changed, 3 insertions(+), 0 deletions(-)
> % binary diffstat
>  b |    0 
>  1 files changed, 0 insertions(+), 0 deletions(-)
> diff --git a/tests/test-help.out b/tests/test-help.out
> --- a/tests/test-help.out
> +++ b/tests/test-help.out
> @@ -411,10 +411,11 @@
>  -w --ignore-all-space     ignore white space when comparing lines
>  -b --ignore-space-change  ignore changes in the amount of white space
>  -B --ignore-blank-lines   ignore changes whose lines are all blank
>  -U --unified              number of lines of context to show
>     --stat                 output diffstat-style summary of changes
> +    --numstat              output numeric-style summary of changes
>  -I --include              include names matching the given patterns
>  -X --exclude              exclude names matching the given patterns
> 
> use "hg -v help diff" to show global options
> hg status [OPTION]... [FILE]...
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel



More information about the Mercurial-devel mailing list