Merge branch 'jk/log-follow-with-non-literal-pathspec'

"git [-c log.follow=true] log [--follow] ':(glob)f**'" used to barf.

* jk/log-follow-with-non-literal-pathspec:
  diff: detect pathspec magic not supported by --follow
  diff: factor out --follow pathspec check
  pathspec: factor out magic-to-name function
This commit is contained in:
Junio C Hamano 2023-06-20 15:53:13 -07:00
commit de00f4b7f3
6 changed files with 70 additions and 10 deletions

View file

@ -866,7 +866,7 @@ static void log_setup_revisions_tweak(struct rev_info *rev,
struct setup_revision_opt *opt)
{
if (rev->diffopt.flags.default_follow_renames &&
rev->prune_data.nr == 1)
diff_check_follow_pathspec(&rev->prune_data, 0))
rev->diffopt.flags.follow_renames = 1;
if (rev->first_parent_only)

29
diff.c
View file

@ -4751,6 +4751,31 @@ unsigned diff_filter_bit(char status)
return filter_bit[(int) status];
}
int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error)
{
unsigned forbidden_magic;
if (ps->nr != 1) {
if (die_on_error)
die(_("--follow requires exactly one pathspec"));
return 0;
}
forbidden_magic = ps->items[0].magic & ~(PATHSPEC_FROMTOP |
PATHSPEC_LITERAL);
if (forbidden_magic) {
if (die_on_error) {
struct strbuf sb = STRBUF_INIT;
pathspec_magic_names(forbidden_magic, &sb);
die(_("pathspec magic not supported by --follow: %s"),
sb.buf);
}
return 0;
}
return 1;
}
void diff_setup_done(struct diff_options *options)
{
unsigned check_mask = DIFF_FORMAT_NAME |
@ -4858,8 +4883,8 @@ void diff_setup_done(struct diff_options *options)
options->diff_path_counter = 0;
if (options->flags.follow_renames && options->pathspec.nr != 1)
die(_("--follow requires exactly one pathspec"));
if (options->flags.follow_renames)
diff_check_follow_pathspec(&options->pathspec, 1);
if (!options->use_color || external_diff())
options->color_moved = 0;

7
diff.h
View file

@ -539,6 +539,13 @@ void repo_diff_setup(struct repository *, struct diff_options *);
struct option *add_diff_options(const struct option *, struct diff_options *);
int diff_opt_parse(struct diff_options *, const char **, int, const char *);
void diff_setup_done(struct diff_options *);
/*
* Returns true if the pathspec can work with --follow mode. If die_on_error is
* set, die() with a specific error message rather than returning false.
*/
int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error);
int git_config_rename(const char *var, const char *value);
#define DIFF_DETECT_RENAME 1

View file

@ -531,24 +531,29 @@ static int pathspec_item_cmp(const void *a_, const void *b_)
return strcmp(a->match, b->match);
}
static void NORETURN unsupported_magic(const char *pattern,
unsigned magic)
void pathspec_magic_names(unsigned magic, struct strbuf *out)
{
struct strbuf sb = STRBUF_INIT;
int i;
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
const struct pathspec_magic *m = pathspec_magic + i;
if (!(magic & m->bit))
continue;
if (sb.len)
strbuf_addstr(&sb, ", ");
if (out->len)
strbuf_addstr(out, ", ");
if (m->mnemonic)
strbuf_addf(&sb, _("'%s' (mnemonic: '%c')"),
strbuf_addf(out, _("'%s' (mnemonic: '%c')"),
m->name, m->mnemonic);
else
strbuf_addf(&sb, "'%s'", m->name);
strbuf_addf(out, "'%s'", m->name);
}
}
static void NORETURN unsupported_magic(const char *pattern,
unsigned magic)
{
struct strbuf sb = STRBUF_INIT;
pathspec_magic_names(magic, &sb);
/*
* We may want to substitute "this command" with a command
* name. E.g. when "git add -p" or "git add -i" dies when running

View file

@ -130,6 +130,14 @@ void parse_pathspec_file(struct pathspec *pathspec,
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
void clear_pathspec(struct pathspec *);
/*
* Add a human-readable string to "out" representing the PATHSPEC_* flags set
* in "magic". The result is suitable for error messages, but not for
* parsing as pathspec magic itself (you get 'icase' with quotes, not
* :(icase)).
*/
void pathspec_magic_names(unsigned magic, struct strbuf *out);
static inline int ps_strncmp(const struct pathspec_item *item,
const char *s1, const char *s2, size_t n)
{

View file

@ -187,6 +187,21 @@ test_expect_success 'git config log.follow does not die with no paths' '
git log --
'
test_expect_success 'git log --follow rejects unsupported pathspec magic' '
test_must_fail git log --follow ":(top,glob,icase)ichi" 2>stderr &&
# check full error message; we want to be sure we mention both
# of the rejected types (glob,icase), but not the allowed one (top)
echo "fatal: pathspec magic not supported by --follow: ${SQ}glob${SQ}, ${SQ}icase${SQ}" >expect &&
test_cmp expect stderr
'
test_expect_success 'log.follow disabled with unsupported pathspec magic' '
test_config log.follow true &&
git log --format=%s ":(glob,icase)ichi" >actual &&
echo third >expect &&
test_cmp expect actual
'
test_expect_success 'git config log.follow is overridden by --no-follow' '
test_config log.follow true &&
git log --no-follow --pretty="format:%s" ichi >actual &&