git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 0/3] Fix two --diff-filter bugs

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Jan 27, 2022, 19:08 UTC
Message-ID
<pull.1127.v2.git.1643310510.gitgitgadget@gmail.com>
In-Reply-To
<pull.1127.git.1643149759.gitgitgadget@gmail.com>

A colleague noticed that git diff --diff-filter=Dr behaved in an unexpected way. The expectation was that the command shows only deleted files, but not renamed ones.

Turns out that Git's code is incorrect and turns on all diff-filter flags because the argument contains a lower-case letter. But since it starts with an upper-case letter, we should actually not turn all those flags on.

While working on the fix, I realized that the documentation of the --diff-filter flag was not updated when intent-to-add files were no longer shown as modified by git diff, but as added.

Changes since v1:
 * Now even the case of multiple --diff-filter options is handled.
Johannes Schindelin (3):
  docs(diff): lose incorrect claim about `diff-files --diff-filter=A`
  diff.c: move the diff filter bits definitions up a bit
  diff-filter: be more careful when looking for negative bits
 Documentation/diff-options.txt |  7 +--
 diff.c                         | 97 +++++++++++++++-------------------
 diff.h                         |  2 +-
 t/t4202-log.sh                 | 13 +++++
 4 files changed, 60 insertions(+), 59 deletions(-)
base-commit: 89bece5c8c96f0b962cfc89e63f82d603fd60bed
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1127%2Fdscho%2Fdiff-filter-buglets-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1127/dscho/diff-filter-buglets-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1127
Range-diff vs v1:
 1:  704bb2ba18e = 1:  704bb2ba18e docs(diff): lose incorrect claim about `diff-files --diff-filter=A`
 -:  ----------- > 2:  19c7223e265 diff.c: move the diff filter bits definitions up a bit
 2:  e8006493a9e ! 3:  b041d2b7a3b diff-filter: be more careful when looking for negative bits
     @@ Commit message
          provided a lower-case letter: if the `--diff-filter` argument starts
          with an upper-case letter, we must not start with all bits turned on.
      
     +    Even worse, it is possible to specify the diff filters in multiple,
     +    separate options, e.g. `--diff-filter=AM [...] --diff-filter=m`.
     +
     +    Let's accumulate the include/exclude filters independently, and only
     +    special-case the "only exclude filters were specified" case after
     +    parsing the options altogether.
     +
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
      
       ## diff.c ##
     +@@ diff.c: void diff_setup_done(struct diff_options *options)
     + 	if (!options->use_color || external_diff())
     + 		options->color_moved = 0;
     + 
     ++	if (options->filter_not) {
     ++		if (!options->filter)
     ++			options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
     ++		options->filter &= ~options->filter_not;
     ++	}
     ++
     + 	FREE_AND_NULL(options->parseopts);
     + }
     + 
      @@ diff.c: static int diff_opt_diff_filter(const struct option *option,
     + 	BUG_ON_OPT_NEG(unset);
       	prepare_filter_bits();
       
     - 	/*
     +-	/*
      -	 * If there is a negation e.g. 'd' in the input, and we haven't
     -+	 * If the input starts with a negation e.g. 'd', and we haven't
     - 	 * initialized the filter field with another --diff-filter, start
     - 	 * from full set of bits, except for AON.
     - 	 */
     - 	if (!opt->filter) {
     +-	 * initialized the filter field with another --diff-filter, start
     +-	 * from full set of bits, except for AON.
     +-	 */
     +-	if (!opt->filter) {
      -		for (i = 0; (optch = optarg[i]) != '\0'; i++) {
      -			if (optch < 'a' || 'z' < optch)
      -				continue;
     -+		optch = optarg[0];
     -+		if (optch >= 'a' && 'z' >= optch) {
     - 			opt->filter = (1 << (ARRAY_SIZE(diff_status_letters) - 1)) - 1;
     - 			opt->filter &= ~filter_bit[DIFF_STATUS_FILTER_AON];
     +-			opt->filter = (1 << (ARRAY_SIZE(diff_status_letters) - 1)) - 1;
     +-			opt->filter &= ~filter_bit[DIFF_STATUS_FILTER_AON];
      -			break;
     - 		}
     +-		}
     +-	}
     +-
     + 	for (i = 0; (optch = optarg[i]) != '\0'; i++) {
     + 		unsigned int bit;
     + 		int negate;
     +@@ diff.c: static int diff_opt_diff_filter(const struct option *option,
     + 			return error(_("unknown change class '%c' in --diff-filter=%s"),
     + 				     optarg[i], optarg);
     + 		if (negate)
     +-			opt->filter &= ~bit;
     ++			opt->filter_not |= bit;
     + 		else
     + 			opt->filter |= bit;
       	}
     +
     + ## diff.h ##
     +@@ diff.h: struct diff_options {
     + 	struct diff_flags flags;
     + 
     + 	/* diff-filter bits */
     +-	unsigned int filter;
     ++	unsigned int filter, filter_not;
     + 
     + 	int use_color;
       
      
       ## t/t4202-log.sh ##
     @@ t/t4202-log.sh: test_expect_success 'diff-filter=R' '
       
       '
       
     -+test_expect_success 'diff-filter=Ra' '
     ++test_expect_success 'multiple --diff-filter bits' '
      +
      +	git log -M --pretty="format:%s" --diff-filter=R HEAD >expect &&
      +	git log -M --pretty="format:%s" --diff-filter=Ra HEAD >actual &&
     ++	test_cmp expect actual &&
     ++	git log -M --pretty="format:%s" --diff-filter=aR HEAD >actual &&
     ++	test_cmp expect actual &&
     ++	git log -M --pretty="format:%s" \
     ++		--diff-filter=a --diff-filter=R HEAD >actual &&
      +	test_cmp expect actual
      +
      +'
-- 
gitgitgadget
Previous: Ævar Arnfjörð BjarmasonNext: Johannes Schindelin via GitGitGadget
Message 12 of 21 in “Fix two --diff-filter bugs”
  1. 0/2 Fix two --diff-filter bugsJohannes Schindelin via GitGitGadget, Jan 25, 2022
  2. 1/2 docs(diff): lose incorrect claim about `diff-files --diff-filter=A`Johannes Schindelin via GitGitGadget, Jan 25, 2022
  3. Junio C HamanoJan 26, 2022
  4. 2/2 diff-filter: be more careful when looking for negative bitsJohannes Schindelin via GitGitGadget, Jan 25, 2022
  5. Taylor BlauJan 25, 2022
  6. Johannes SchindelinJan 28, 2022
  7. Junio C HamanoJan 26, 2022
  8. Taylor BlauJan 26, 2022
  9. Junio C HamanoJan 26, 2022
  10. Johannes SchindelinJan 28, 2022
  11. Ævar Arnfjörð BjarmasonJan 26, 2022
  12. 0/3 Fix two --diff-filter bugsJohannes Schindelin via GitGitGadget, Jan 27, 2022
  13. 1/3 docs(diff): lose incorrect claim about `diff-files --diff-filter=A`Johannes Schindelin via GitGitGadget, Jan 27, 2022
  14. 2/3 diff.c: move the diff filter bits definitions up a bitJohannes Schindelin via GitGitGadget, Jan 27, 2022
  15. 3/3 diff-filter: be more careful when looking for negative bitsJohannes Schindelin via GitGitGadget, Jan 27, 2022
  16. Junio C HamanoJan 28, 2022
  17. Johannes SchindelinJan 28, 2022
  18. 0/3 Fix two --diff-filter bugsJohannes Schindelin via GitGitGadget, Jan 28, 2022
  19. 1/3 docs(diff): lose incorrect claim about `diff-files --diff-filter=A`Johannes Schindelin via GitGitGadget, Jan 28, 2022
  20. 2/3 diff.c: move the diff filter bits definitions up a bitJohannes Schindelin via GitGitGadget, Jan 28, 2022
  21. 3/3 diff-filter: be more careful when looking for negative bitsJohannes Schindelin via GitGitGadget, Jan 28, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.