Skip to content

Commit de00f4b

Browse files
committed
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
2 parents 7cb4274 + 8260bc5 commit de00f4b

File tree

6 files changed

+70
-10
lines changed

6 files changed

+70
-10
lines changed

builtin/log.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ static void log_setup_revisions_tweak(struct rev_info *rev,
866866
struct setup_revision_opt *opt)
867867
{
868868
if (rev->diffopt.flags.default_follow_renames &&
869-
rev->prune_data.nr == 1)
869+
diff_check_follow_pathspec(&rev->prune_data, 0))
870870
rev->diffopt.flags.follow_renames = 1;
871871

872872
if (rev->first_parent_only)

diff.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4751,6 +4751,31 @@ unsigned diff_filter_bit(char status)
47514751
return filter_bit[(int) status];
47524752
}
47534753

4754+
int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error)
4755+
{
4756+
unsigned forbidden_magic;
4757+
4758+
if (ps->nr != 1) {
4759+
if (die_on_error)
4760+
die(_("--follow requires exactly one pathspec"));
4761+
return 0;
4762+
}
4763+
4764+
forbidden_magic = ps->items[0].magic & ~(PATHSPEC_FROMTOP |
4765+
PATHSPEC_LITERAL);
4766+
if (forbidden_magic) {
4767+
if (die_on_error) {
4768+
struct strbuf sb = STRBUF_INIT;
4769+
pathspec_magic_names(forbidden_magic, &sb);
4770+
die(_("pathspec magic not supported by --follow: %s"),
4771+
sb.buf);
4772+
}
4773+
return 0;
4774+
}
4775+
4776+
return 1;
4777+
}
4778+
47544779
void diff_setup_done(struct diff_options *options)
47554780
{
47564781
unsigned check_mask = DIFF_FORMAT_NAME |
@@ -4858,8 +4883,8 @@ void diff_setup_done(struct diff_options *options)
48584883

48594884
options->diff_path_counter = 0;
48604885

4861-
if (options->flags.follow_renames && options->pathspec.nr != 1)
4862-
die(_("--follow requires exactly one pathspec"));
4886+
if (options->flags.follow_renames)
4887+
diff_check_follow_pathspec(&options->pathspec, 1);
48634888

48644889
if (!options->use_color || external_diff())
48654890
options->color_moved = 0;

diff.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,13 @@ void repo_diff_setup(struct repository *, struct diff_options *);
539539
struct option *add_diff_options(const struct option *, struct diff_options *);
540540
int diff_opt_parse(struct diff_options *, const char **, int, const char *);
541541
void diff_setup_done(struct diff_options *);
542+
543+
/*
544+
* Returns true if the pathspec can work with --follow mode. If die_on_error is
545+
* set, die() with a specific error message rather than returning false.
546+
*/
547+
int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error);
548+
542549
int git_config_rename(const char *var, const char *value);
543550

544551
#define DIFF_DETECT_RENAME 1

pathspec.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -531,24 +531,29 @@ static int pathspec_item_cmp(const void *a_, const void *b_)
531531
return strcmp(a->match, b->match);
532532
}
533533

534-
static void NORETURN unsupported_magic(const char *pattern,
535-
unsigned magic)
534+
void pathspec_magic_names(unsigned magic, struct strbuf *out)
536535
{
537-
struct strbuf sb = STRBUF_INIT;
538536
int i;
539537
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
540538
const struct pathspec_magic *m = pathspec_magic + i;
541539
if (!(magic & m->bit))
542540
continue;
543-
if (sb.len)
544-
strbuf_addstr(&sb, ", ");
541+
if (out->len)
542+
strbuf_addstr(out, ", ");
545543

546544
if (m->mnemonic)
547-
strbuf_addf(&sb, _("'%s' (mnemonic: '%c')"),
545+
strbuf_addf(out, _("'%s' (mnemonic: '%c')"),
548546
m->name, m->mnemonic);
549547
else
550-
strbuf_addf(&sb, "'%s'", m->name);
548+
strbuf_addf(out, "'%s'", m->name);
551549
}
550+
}
551+
552+
static void NORETURN unsupported_magic(const char *pattern,
553+
unsigned magic)
554+
{
555+
struct strbuf sb = STRBUF_INIT;
556+
pathspec_magic_names(magic, &sb);
552557
/*
553558
* We may want to substitute "this command" with a command
554559
* name. E.g. when "git add -p" or "git add -i" dies when running

pathspec.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ void parse_pathspec_file(struct pathspec *pathspec,
130130
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
131131
void clear_pathspec(struct pathspec *);
132132

133+
/*
134+
* Add a human-readable string to "out" representing the PATHSPEC_* flags set
135+
* in "magic". The result is suitable for error messages, but not for
136+
* parsing as pathspec magic itself (you get 'icase' with quotes, not
137+
* :(icase)).
138+
*/
139+
void pathspec_magic_names(unsigned magic, struct strbuf *out);
140+
133141
static inline int ps_strncmp(const struct pathspec_item *item,
134142
const char *s1, const char *s2, size_t n)
135143
{

t/t4202-log.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ test_expect_success 'git config log.follow does not die with no paths' '
187187
git log --
188188
'
189189

190+
test_expect_success 'git log --follow rejects unsupported pathspec magic' '
191+
test_must_fail git log --follow ":(top,glob,icase)ichi" 2>stderr &&
192+
# check full error message; we want to be sure we mention both
193+
# of the rejected types (glob,icase), but not the allowed one (top)
194+
echo "fatal: pathspec magic not supported by --follow: ${SQ}glob${SQ}, ${SQ}icase${SQ}" >expect &&
195+
test_cmp expect stderr
196+
'
197+
198+
test_expect_success 'log.follow disabled with unsupported pathspec magic' '
199+
test_config log.follow true &&
200+
git log --format=%s ":(glob,icase)ichi" >actual &&
201+
echo third >expect &&
202+
test_cmp expect actual
203+
'
204+
190205
test_expect_success 'git config log.follow is overridden by --no-follow' '
191206
test_config log.follow true &&
192207
git log --no-follow --pretty="format:%s" ichi >actual &&

0 commit comments

Comments
 (0)