Re: [PATCH v2 1/2] Introduce config variable "diff.primer"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Feb 02, 2009 at 10:20:54AM -0800, Keith Cascio wrote:

> Introduce config variable "diff.primer".

You don't need to repeat the subject in the body.

> Improve porcelain diff's accommodation of user preference by allowing
> some settings to (a) persist over all invocations and (b) stay consistent
> over multiple tools (e.g. command-line and gui).  The approach taken here
> is good because it delivers the consistency a user expects without breaking
> any plumbing.  It works by allowing the user, via git-config, to specify
> arbitrary options to pass to porcelain diff on every invocation, including
> internal invocations from other programs, e.g. git-gui.  Introduce diff
> command-line options --primer and --no-primer.  Affect only porcelain diff:
> we suppress primer options for plumbing diff-{files,index,tree},
> format-patch, and all other commands unless explicitly requested using
> --primer (opt-in).  Teach gitk to use --primer, but protect it from
> inapplicable options like --color.

Paragraph breaks might have made this a bit easier to read.

> +diff.primer::
> +	Whitespace-separated list of options to pass to 'git-diff'
> +	on every invocation, including internal invocations from
> +	linkgit:git-gui[1] and linkgit:gitk[1],
> +	e.g. `"--patience --color --ignore-space-at-eol --exit-code"`.
> +	See linkgit:git-diff[1]. You can suppress these at run time with
> +	option `--no-primer`.  Supports a subset of
> +	'git-diff'\'s many options, at least:
> +	`-b --binary --color --color-words --cumulative --dirstat-by-file
> +--exit-code --ext-diff --find-copies-harder --follow --full-index
> +--ignore-all-space --ignore-space-at-eol --ignore-space-change
> +--ignore-submodules --no-color --no-ext-diff --no-textconv --patience -q
> +--quiet -R -r --relative -t --text --textconv -w`

Funny indentation?

This seems really clunky to list all of the options here. I thought the
point was to respect _all_ of them, but do it from porcelain so that it
is up to the user what they want to put in.

How was this list chosen?

> +--no-primer::
> +	Ignore default options specified in '.git/config', i.e.
> +	those that were set using a command like
> +	`git config diff.primer "--patience --color --ignore-space-at-eol --exit-code"`
> +
> +--primer::
> +	Opt-in for default options specified in '.git/config'.  This option is
> +	most often used with the three plumbing commands diff-{files,index,tree}.
> +	These commands normally suppress default options.
> +

Some of the manpages use a more terse form for negatable options, like:

  --[no-]primer::

which often helps focus the text a bit. Something like:

  --[no-]primer::
    Respect (or ignore) options specifed in the diff.primer
    configuration variable. By default, porcelain commands (such as `git
    diff` and `git log`) respect this variable, but plumbing commands
    (such as `git diff-{files,index,tree}`) do not.

Also, don't mention ".git/config" by name: configuration can come from
~/.gitconfig, a system-wide gitconfig, or .git/config.

> @@ -284,6 +284,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
> [...]
> +	DIFF_OPT_SET(&rev.diffopt, PRIMER);

Probably ALLOW_PRIMER is a more sensible name, to match ALLOW_EXTERNAL
and ALLOW_TEXTCONV.

> +static const char blank[] = " \t\r\n";
> +
> +void parse_diff_primer(struct diff_options *options)
> +{
> +	char *str1, *token, *saveptr;
> +	int len;
> +
> +	if ((! diff_primer) || ((len = (strlen(diff_primer)+1)) < 3))
> +		return;
> +
> +	token = str1 = strncpy((char*) malloc(len), diff_primer, len);
> +	if ((saveptr = strpbrk(token += strspn(token, blank), blank)))
> +		*(saveptr++) = '\0';
> +	while (token) {
> +		if (*token == '-')
> +			diff_opt_parse(options, (const char **) &token, -1);
> +		if ((token = saveptr))
> +			if ((saveptr = strpbrk(token += strspn(token, blank), blank)))
> +				*(saveptr++) = '\0';
> +	}
> +
> +	free( str1 );
> +}

This doesn't appear to have any quoting mechanism. Is it impossible to
have an option with spaces (e.g., --relative='foo bar')? I guess that is
probably uncommon, but I would expect normal shell quoting rules to
apply.

> +struct diff_options* flatten_diff_options(struct diff_options *master, struct diff_options *slave)
> +{
> +	unsigned x0 = master->flags, x1 = master->mask, x2 = slave->flags, x3 = slave->mask;
> +	long w = master->xdl_opts, x = master->xdl_mask, y = slave->xdl_opts, z = slave->xdl_mask;
> +

Style: long lines.

> +	//minimized by Quine-McCluskey

Style: no C99/C++ comments.

> +	master->flags = (~x1&x2&x3)|(x0&~x3)|(x0&x1);

Style: whitespace between operands and operators.

I have to admit that this particular line is pretty dense to read. You
have eliminated any meaning from the variable names (like the fact that
you have a master/slave pair of flag/mask pairs). Yes, you point to the
Quine-McCluskey algorithm in the comment above, but I think something
like this would be easier to see what is going on:

  /*
   * Our desired flags are:
   *
   *   1. Anything the master hasn't explicitly set, we can take from
   *      the slave.
   *   2. Anything the slave didn't explicitly, we can take whether or
   *      not the master set it explicitly.
   *   3. Anything the master explicitly set, we take.
   */
  master->flags =
     /* (1) */ (~master->flags & slave->flags & slave->mask) |
     /* (2) */ (master->flags & ~slave->mask) |
     /* (3) */ (master->flags & master->mask);

> @@ -2326,14 +2369,15 @@ void diff_setup(struct diff_options *options)
>  	options->break_opt = -1;
>  	options->rename_limit = -1;
>  	options->dirstat_percent = 3;
> -	DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
> +	if (DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE))
> +		DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);

Hmm. I haven't gotten to any changes to DIFF_OPT_{SET,CLR} yet. But it
is a little worrisome that this patch is so invasive as to require a
change like this. I wouldn't be surprised to find other spots outside of
diff.c where the options are munged by various programs. Did you audit
for all such spots?

> +	if (DIFF_OPT_TST(options, PRIMER)) {
> +		if (! primer) {
> +			diff_setup(primer = (struct diff_options *) malloc(sizeof(struct diff_options)));

First, don't use malloc. Use the xmalloc wrapper that will try to free
pack memory and/or die if it fails.

Secondly, don't cast the result of malloc. At best it is pointless and
verbose, and at worst it can hide errors caused by a missing function
declaration.

>  	/* xdiff options */
>  	else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
> -		options->xdl_opts |= XDF_IGNORE_WHITESPACE;
> +		DIFF_XDL_SET(options, IGNORE_WHITESPACE);

It often makes the patch easier to review if you split changes like this
out into a separate patch. Then your series is

  1/2: use DIFF_XDL_SET instead of raw bit-masking

       This is a cleanup in preparation for option-setting doing
       something more complex than just setting a bit-mask. The code
       should behave exactly the same.

  2/2: primer patch

       ... DIFF_XDL_SET tracks not only the set options, but which ones
       were set explicitly via a mask ...

Then we can all see pretty easily that patch 1/2 doesn't change the
behavior, and each patch is a much smaller, succint chunk to review.

> -	else if (!strcmp(arg, "--color-words"))
> -		options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
> +	else if (!strcmp(arg, "--color-words")) {
> +		DIFF_OPT_SET(options, COLOR_DIFF);
> +		DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
> +	}

Ditto here with DIFF_OPT_SET.

> +#define DIFF_OPT_TST(opts, flag)    ((opts)->flags &   DIFF_OPT_##flag)
> +#define DIFF_OPT_SET(opts, flag)    ((opts)->flags |=  DIFF_OPT_##flag), ((opts)->mask |= DIFF_OPT_##flag)
> +#define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag), ((opts)->mask |= DIFF_OPT_##flag)
> +#define DIFF_OPT_DRT(opts, flag)    ((opts)->mask  &   DIFF_OPT_##flag)

OK, I see what it is supposed to do, but what does DRT stand for? Also,
what practical use does it have? I don't see anybody _calling_ it.

> --- a/gitk-git/gitk
> +++ b/gitk-git/gitk
> @@ -4259,7 +4259,7 @@ proc do_file_hl {serial} {
>  	# must be "containing:", i.e. we're searching commit info
>  	return
>      }
> -    set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
> +    set cmd [concat | git diff-tree --primer --no-color -r -s --stdin $gdtargs]

Does gitk really want to respect --primer? Might it not make more sense
for it, as a porcelain, to respect the diff.primer variable itself, and
prepend it to the list of diff args? Then it has the power to veto any
options which it doesn't handle.

Also, any gitk changes should almost certainly be split into a different
patch.



All in all, this was a lot more complicated than I was expecting. Why
isn't the behavior of "diff.primer" simply "pretend as if the options in
diff.primer were prepended to the command line"? That is easy to
explain, and easy to implement (the only trick is that you have to do an
extra pass to find --[no-]primer). Is there some drawback to such a
simple scheme that I am missing?

-Peff
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux