mirror of
https://github.com/git/git
synced 2024-11-04 16:17:49 +00:00
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:
commit
de00f4b7f3
6 changed files with 70 additions and 10 deletions
|
@ -866,7 +866,7 @@ static void log_setup_revisions_tweak(struct rev_info *rev,
|
||||||
struct setup_revision_opt *opt)
|
struct setup_revision_opt *opt)
|
||||||
{
|
{
|
||||||
if (rev->diffopt.flags.default_follow_renames &&
|
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;
|
rev->diffopt.flags.follow_renames = 1;
|
||||||
|
|
||||||
if (rev->first_parent_only)
|
if (rev->first_parent_only)
|
||||||
|
|
29
diff.c
29
diff.c
|
@ -4751,6 +4751,31 @@ unsigned diff_filter_bit(char status)
|
||||||
return filter_bit[(int) 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)
|
void diff_setup_done(struct diff_options *options)
|
||||||
{
|
{
|
||||||
unsigned check_mask = DIFF_FORMAT_NAME |
|
unsigned check_mask = DIFF_FORMAT_NAME |
|
||||||
|
@ -4858,8 +4883,8 @@ void diff_setup_done(struct diff_options *options)
|
||||||
|
|
||||||
options->diff_path_counter = 0;
|
options->diff_path_counter = 0;
|
||||||
|
|
||||||
if (options->flags.follow_renames && options->pathspec.nr != 1)
|
if (options->flags.follow_renames)
|
||||||
die(_("--follow requires exactly one pathspec"));
|
diff_check_follow_pathspec(&options->pathspec, 1);
|
||||||
|
|
||||||
if (!options->use_color || external_diff())
|
if (!options->use_color || external_diff())
|
||||||
options->color_moved = 0;
|
options->color_moved = 0;
|
||||||
|
|
7
diff.h
7
diff.h
|
@ -539,6 +539,13 @@ void repo_diff_setup(struct repository *, struct diff_options *);
|
||||||
struct option *add_diff_options(const struct option *, 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 *);
|
int diff_opt_parse(struct diff_options *, const char **, int, const char *);
|
||||||
void diff_setup_done(struct diff_options *);
|
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);
|
int git_config_rename(const char *var, const char *value);
|
||||||
|
|
||||||
#define DIFF_DETECT_RENAME 1
|
#define DIFF_DETECT_RENAME 1
|
||||||
|
|
19
pathspec.c
19
pathspec.c
|
@ -531,24 +531,29 @@ static int pathspec_item_cmp(const void *a_, const void *b_)
|
||||||
return strcmp(a->match, b->match);
|
return strcmp(a->match, b->match);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void NORETURN unsupported_magic(const char *pattern,
|
void pathspec_magic_names(unsigned magic, struct strbuf *out)
|
||||||
unsigned magic)
|
|
||||||
{
|
{
|
||||||
struct strbuf sb = STRBUF_INIT;
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
|
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
|
||||||
const struct pathspec_magic *m = pathspec_magic + i;
|
const struct pathspec_magic *m = pathspec_magic + i;
|
||||||
if (!(magic & m->bit))
|
if (!(magic & m->bit))
|
||||||
continue;
|
continue;
|
||||||
if (sb.len)
|
if (out->len)
|
||||||
strbuf_addstr(&sb, ", ");
|
strbuf_addstr(out, ", ");
|
||||||
|
|
||||||
if (m->mnemonic)
|
if (m->mnemonic)
|
||||||
strbuf_addf(&sb, _("'%s' (mnemonic: '%c')"),
|
strbuf_addf(out, _("'%s' (mnemonic: '%c')"),
|
||||||
m->name, m->mnemonic);
|
m->name, m->mnemonic);
|
||||||
else
|
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
|
* We may want to substitute "this command" with a command
|
||||||
* name. E.g. when "git add -p" or "git add -i" dies when running
|
* name. E.g. when "git add -p" or "git add -i" dies when running
|
||||||
|
|
|
@ -130,6 +130,14 @@ void parse_pathspec_file(struct pathspec *pathspec,
|
||||||
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
|
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
|
||||||
void clear_pathspec(struct pathspec *);
|
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,
|
static inline int ps_strncmp(const struct pathspec_item *item,
|
||||||
const char *s1, const char *s2, size_t n)
|
const char *s1, const char *s2, size_t n)
|
||||||
{
|
{
|
||||||
|
|
|
@ -187,6 +187,21 @@ test_expect_success 'git config log.follow does not die with no paths' '
|
||||||
git log --
|
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_expect_success 'git config log.follow is overridden by --no-follow' '
|
||||||
test_config log.follow true &&
|
test_config log.follow true &&
|
||||||
git log --no-follow --pretty="format:%s" ichi >actual &&
|
git log --no-follow --pretty="format:%s" ichi >actual &&
|
||||||
|
|
Loading…
Reference in a new issue