Merge branch 'js/diff-filter-negation-fix'

"git diff --diff-filter=aR" is now parsed correctly.

* js/diff-filter-negation-fix:
  diff-filter: be more careful when looking for negative bits
  diff.c: move the diff filter bits definitions up a bit
  docs(diff): lose incorrect claim about `diff-files --diff-filter=A`
This commit is contained in:
Junio C Hamano 2022-02-16 15:14:30 -08:00
commit 9a160990ef
4 changed files with 60 additions and 59 deletions

View File

@ -628,11 +628,8 @@ ifndef::git-format-patch[]
Also, these upper-case letters can be downcased to exclude. E.g. Also, these upper-case letters can be downcased to exclude. E.g.
`--diff-filter=ad` excludes added and deleted paths. `--diff-filter=ad` excludes added and deleted paths.
+ +
Note that not all diffs can feature all types. For instance, diffs Note that not all diffs can feature all types. For instance, copied and
from the index to the working tree can never have Added entries renamed entries cannot appear if detection for those types is disabled.
(because the set of paths included in the diff is limited by what is in
the index). Similarly, copied and renamed entries cannot appear if
detection for those types is disabled.
-S<string>:: -S<string>::
Look for differences that change the number of occurrences of Look for differences that change the number of occurrences of

97
diff.c
View File

@ -4613,6 +4613,43 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
prep_parse_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) void diff_setup_done(struct diff_options *options)
{ {
unsigned check_mask = DIFF_FORMAT_NAME | unsigned check_mask = DIFF_FORMAT_NAME |
@ -4726,6 +4763,12 @@ void diff_setup_done(struct diff_options *options)
if (!options->use_color || external_diff()) if (!options->use_color || external_diff())
options->color_moved = 0; 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); FREE_AND_NULL(options->parseopts);
} }
@ -4817,43 +4860,6 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
return 1; 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, static int diff_opt_diff_filter(const struct option *option,
const char *optarg, int unset) const char *optarg, int unset)
{ {
@ -4863,21 +4869,6 @@ static int diff_opt_diff_filter(const struct option *option,
BUG_ON_OPT_NEG(unset); BUG_ON_OPT_NEG(unset);
prepare_filter_bits(); 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++) { for (i = 0; (optch = optarg[i]) != '\0'; i++) {
unsigned int bit; unsigned int bit;
int negate; int negate;
@ -4894,7 +4885,7 @@ static int diff_opt_diff_filter(const struct option *option,
return error(_("unknown change class '%c' in --diff-filter=%s"), return error(_("unknown change class '%c' in --diff-filter=%s"),
optarg[i], optarg); optarg[i], optarg);
if (negate) if (negate)
opt->filter &= ~bit; opt->filter_not |= bit;
else else
opt->filter |= bit; opt->filter |= bit;
} }

2
diff.h
View File

@ -283,7 +283,7 @@ struct diff_options {
struct diff_flags flags; struct diff_flags flags;
/* diff-filter bits */ /* diff-filter bits */
unsigned int filter; unsigned int filter, filter_not;
int use_color; int use_color;

View File

@ -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' ' test_expect_success 'diff-filter=C' '
git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual && git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual &&