From 4d4d4eaa7b7f7ceda0648273ad33b71fc68e36c1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 28 Jan 2022 12:02:49 +0000 Subject: diff.c: move the diff filter bits definitions up a bit This prepares for a more careful handling of the `--diff-filter` options over the next few commits. This commit is best viewed with `--color-moved`. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 74 +++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c862771a58..5081052c43 100644 --- a/diff.c +++ b/diff.c @@ -4570,6 +4570,43 @@ void repo_diff_setup(struct repository *r, struct diff_options *options) prep_parse_options(options); } +static const char diff_status_letters[] = { + DIFF_STATUS_ADDED, + DIFF_STATUS_COPIED, + DIFF_STATUS_DELETED, + DIFF_STATUS_MODIFIED, + DIFF_STATUS_RENAMED, + DIFF_STATUS_TYPE_CHANGED, + DIFF_STATUS_UNKNOWN, + DIFF_STATUS_UNMERGED, + DIFF_STATUS_FILTER_AON, + DIFF_STATUS_FILTER_BROKEN, + '\0', +}; + +static unsigned int filter_bit['Z' + 1]; + +static void prepare_filter_bits(void) +{ + int i; + + if (!filter_bit[DIFF_STATUS_ADDED]) { + for (i = 0; diff_status_letters[i]; i++) + filter_bit[(int) diff_status_letters[i]] = (1 << i); + } +} + +static unsigned filter_bit_tst(char status, const struct diff_options *opt) +{ + return opt->filter & filter_bit[(int) status]; +} + +unsigned diff_filter_bit(char status) +{ + prepare_filter_bits(); + return filter_bit[(int) status]; +} + void diff_setup_done(struct diff_options *options) { unsigned check_mask = DIFF_FORMAT_NAME | @@ -4774,43 +4811,6 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params) return 1; } -static const char diff_status_letters[] = { - DIFF_STATUS_ADDED, - DIFF_STATUS_COPIED, - DIFF_STATUS_DELETED, - DIFF_STATUS_MODIFIED, - DIFF_STATUS_RENAMED, - DIFF_STATUS_TYPE_CHANGED, - DIFF_STATUS_UNKNOWN, - DIFF_STATUS_UNMERGED, - DIFF_STATUS_FILTER_AON, - DIFF_STATUS_FILTER_BROKEN, - '\0', -}; - -static unsigned int filter_bit['Z' + 1]; - -static void prepare_filter_bits(void) -{ - int i; - - if (!filter_bit[DIFF_STATUS_ADDED]) { - for (i = 0; diff_status_letters[i]; i++) - filter_bit[(int) diff_status_letters[i]] = (1 << i); - } -} - -static unsigned filter_bit_tst(char status, const struct diff_options *opt) -{ - return opt->filter & filter_bit[(int) status]; -} - -unsigned diff_filter_bit(char status) -{ - prepare_filter_bits(); - return filter_bit[(int) status]; -} - static int diff_opt_diff_filter(const struct option *option, const char *optarg, int unset) { -- cgit v1.3 From 75408ca94980c125b3a800003c3e6b7cdcb27d44 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 28 Jan 2022 12:02:50 +0000 Subject: diff-filter: be more careful when looking for negative bits The `--diff-filter=` option allows to filter the diff by certain criteria, for example `R` to only show renamed files. It also supports negating a filter via a down-cased letter, i.e. `r` to show _everything but_ renamed files. However, the code is a bit overzealous when trying to figure out whether `git diff` should start with all diff-filters turned on because the user 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. Note: The code replaced by this commit took pains to avoid setting any unused bits of `options->filter`. That was unnecessary, though, as all accesses happen via the `filter_bit_tst()` function using specific bits, and setting the unused bits has no effect. Therefore, we can simplify the code by using `~0` (or in this instance, `~`). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 23 +++++++---------------- diff.h | 2 +- t/t4202-log.sh | 13 +++++++++++++ 3 files changed, 21 insertions(+), 17 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5081052c43..4ab4299b81 100644 --- a/diff.c +++ b/diff.c @@ -4720,6 +4720,12 @@ 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); } @@ -4820,21 +4826,6 @@ 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 - * 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; - 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; @@ -4851,7 +4842,7 @@ 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 --git a/diff.h b/diff.h index 8ba85c5e60..a70e7c478c 100644 --- a/diff.h +++ b/diff.h @@ -283,7 +283,7 @@ struct diff_options { struct diff_flags flags; /* diff-filter bits */ - unsigned int filter; + unsigned int filter, filter_not; int use_color; diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 5049559861..b25182379f 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -142,6 +142,19 @@ test_expect_success 'diff-filter=R' ' ' +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 + +' + test_expect_success 'diff-filter=C' ' git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual && -- cgit v1.3