Subject: Re: [PATCH v2 2/3] diff: add -I<regex> that ignores matching changes

From: Junio C Hamano <[email-masked]>
Date: Mon, 12 Oct 2020 11:01:02 -0700

Michał Kępień <[email-masked]> writes:

> Add a new diff option that enables ignoring changes whose all lines
> (changed, removed, and added) match a given regular expression.  This is
> similar to the -I option in standalone diff utilities and can be used
> e.g. to ignore changes which only affect code comments or to look for
> unrelated changes in commits containing a large number of automatically
> applied modifications (e.g. a tree-wide string replacement).  The
> difference between -G/-S and the new -I option is that the latter
> filters output on a per-change basis.
>
> Use the 'ignore' field of xdchange_t for marking a change as ignored or
> not.  Since the same field is used by --ignore-blank-lines, identical
> hunk emitting rules apply for --ignore-blank-lines and -I.  These two
> options can also be used together in the same git invocation (they are
> complementary to each other).
>
> Rename xdl_mark_ignorable() to xdl_mark_ignorable_lines(), to indicate
> that it is logically a "sibling" of xdl_mark_ignorable_regex() rather
> than its "parent".
>
> Signed-off-by: Michał Kępień <[email-masked]>

Well explained.

> +-I<regex>::

Since we are mimicking other folks' feature, giving also the

--ignore-matching-lines=<regex>

synonym to that their users are familiar with would be a good idea,
no?

> +	Ignore changes whose all lines match <regex>.  This option may
> +	be specified more than once.
> +
>  --inter-hunk-context=<lines>::
>  	Show the context between diff hunks, up to the specified number
>  	of lines, thereby fusing hunks that are close to each other.
> diff --git a/diff.c b/diff.c
> index 2bb2f8f57e..c5d4920fee 100644
> --- a/diff.c
> +++ b/diff.c
> @@ -3587,6 +3587,8 @@ static void builtin_diff(const char *name_a,
>  		if (header.len && !o->flags.suppress_diff_headers)
>  			ecbdata.header = &header;
>  		xpp.flags = o->xdl_opts;
> +		xpp.ignore_regex = o->ignore_regex;
> +		xpp.ignore_regex_nr = o->ignore_regex_nr;
>  		xpp.anchors = o->anchors;
>  		xpp.anchors_nr = o->anchors_nr;
>  		xecfg.ctxlen = o->context;
> @@ -3716,6 +3718,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
>  		memset(&xpp, 0, sizeof(xpp));
>  		memset(&xecfg, 0, sizeof(xecfg));
>  		xpp.flags = o->xdl_opts;
> +		xpp.ignore_regex = o->ignore_regex;
> +		xpp.ignore_regex_nr = o->ignore_regex_nr;
>  		xpp.anchors = o->anchors;
>  		xpp.anchors_nr = o->anchors_nr;
>  		xecfg.ctxlen = o->context;
> @@ -5203,6 +5207,22 @@ static int diff_opt_patience(const struct option *opt,
>  	return 0;
>  }
>  
> +static int diff_opt_ignore_regex(const struct option *opt,
> +				 const char *arg, int unset)
> +{
> +	struct diff_options *options = opt->value;
> +	regex_t *regex;
> +
> +	BUG_ON_OPT_NEG(unset);
> +	regex = xcalloc(1, sizeof(*regex));
> +	if (regcomp(regex, arg, REG_EXTENDED | REG_NEWLINE))
> +		die("invalid regex: %s", arg);
> +	ALLOC_GROW(options->ignore_regex, options->ignore_regex_nr + 1,
> +		   options->ignore_regex_alloc);
> +	options->ignore_regex[options->ignore_regex_nr++] = regex;
> +	return 0;
> +}

Don't these parse-options callback functions have a way to tell the
caller die instead of them themselves dying like this?  Would it
work better to "return error(... message ...)", or would that give
two error messages?  In anycase, this is end-user facing, so we'd
want it to be localizable, e.g.

	die(_("invalid regexp given to -I: '%s'", arg));

probably.

>  static int diff_opt_pickaxe_regex(const struct option *opt,
>  				  const char *arg, int unset)
>  {

Other than that, nicely done.

Thanks.