Skip to content

Commit 75408ca

Browse files
dschogitster
authored andcommitted
diff-filter: be more careful when looking for negative bits
The `--diff-filter=<bits>` 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, `~<unwanted-bit>`). Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4d4d4ea commit 75408ca

File tree

3 files changed

+21
-17
lines changed

3 files changed

+21
-17
lines changed

diff.c

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4720,6 +4720,12 @@ void diff_setup_done(struct diff_options *options)
47204720
if (!options->use_color || external_diff())
47214721
options->color_moved = 0;
47224722

4723+
if (options->filter_not) {
4724+
if (!options->filter)
4725+
options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
4726+
options->filter &= ~options->filter_not;
4727+
}
4728+
47234729
FREE_AND_NULL(options->parseopts);
47244730
}
47254731

@@ -4820,21 +4826,6 @@ static int diff_opt_diff_filter(const struct option *option,
48204826
BUG_ON_OPT_NEG(unset);
48214827
prepare_filter_bits();
48224828

4823-
/*
4824-
* If there is a negation e.g. 'd' in the input, and we haven't
4825-
* initialized the filter field with another --diff-filter, start
4826-
* from full set of bits, except for AON.
4827-
*/
4828-
if (!opt->filter) {
4829-
for (i = 0; (optch = optarg[i]) != '\0'; i++) {
4830-
if (optch < 'a' || 'z' < optch)
4831-
continue;
4832-
opt->filter = (1 << (ARRAY_SIZE(diff_status_letters) - 1)) - 1;
4833-
opt->filter &= ~filter_bit[DIFF_STATUS_FILTER_AON];
4834-
break;
4835-
}
4836-
}
4837-
48384829
for (i = 0; (optch = optarg[i]) != '\0'; i++) {
48394830
unsigned int bit;
48404831
int negate;
@@ -4851,7 +4842,7 @@ static int diff_opt_diff_filter(const struct option *option,
48514842
return error(_("unknown change class '%c' in --diff-filter=%s"),
48524843
optarg[i], optarg);
48534844
if (negate)
4854-
opt->filter &= ~bit;
4845+
opt->filter_not |= bit;
48554846
else
48564847
opt->filter |= bit;
48574848
}

diff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ struct diff_options {
283283
struct diff_flags flags;
284284

285285
/* diff-filter bits */
286-
unsigned int filter;
286+
unsigned int filter, filter_not;
287287

288288
int use_color;
289289

t/t4202-log.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,19 @@ test_expect_success 'diff-filter=R' '
142142
143143
'
144144

145+
test_expect_success 'multiple --diff-filter bits' '
146+
147+
git log -M --pretty="format:%s" --diff-filter=R HEAD >expect &&
148+
git log -M --pretty="format:%s" --diff-filter=Ra HEAD >actual &&
149+
test_cmp expect actual &&
150+
git log -M --pretty="format:%s" --diff-filter=aR HEAD >actual &&
151+
test_cmp expect actual &&
152+
git log -M --pretty="format:%s" \
153+
--diff-filter=a --diff-filter=R HEAD >actual &&
154+
test_cmp expect actual
155+
156+
'
157+
145158
test_expect_success 'diff-filter=C' '
146159
147160
git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual &&

0 commit comments

Comments
 (0)