Skip to content

Commit 51670fc

Browse files
jherlandgitster
authored andcommitted
Improve error handling when parsing dirstat parameters
When encountering errors or unknown tokens while parsing parameters to the --dirstat option, it makes sense to die() with an error message informing the user of which parameter did not make sense. However, when parsing the diff.dirstat config variable, we cannot simply die(), but should instead (after warning the user) ignore the erroneous or unrecognized parameter. After all, future Git versions might add more dirstat parameters, and using two different Git versions on the same repo should not cripple the older Git version just because of a parameter that is only understood by a more recent Git version. This patch fixes the issue by refactoring the dirstat parameter parsing so that parse_dirstat_params() keeps on parsing parameters, even if an earlier parameter was not recognized. When parsing has finished, it returns zero if all parameters were successfully parsed, and non-zero if one or more parameters were not recognized (with appropriate error messages appended to the 'errmsg' argument). The parse_dirstat_params() callers then decide (based on the return value from parse_dirstat_params()) whether to warn and ignore (in case of diff.dirstat), or to warn and die (in case of --dirstat). The patch also adds a couple of tests verifying the correct behavior of --dirstat and diff.dirstat in the face of unknown (possibly future) dirstat parameters. Suggested-by: Junio C Hamano <[email protected]> Improved-by: Junio C Hamano <[email protected]> Signed-off-by: Johan Herland <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1c57a62 commit 51670fc

File tree

2 files changed

+78
-30
lines changed

2 files changed

+78
-30
lines changed

diff.c

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -67,52 +67,56 @@ static int parse_diff_color_slot(const char *var, int ofs)
6767
return -1;
6868
}
6969

70-
static int parse_dirstat_params(struct diff_options *options, const char *params)
70+
static int parse_dirstat_params(struct diff_options *options, const char *params,
71+
struct strbuf *errmsg)
7172
{
7273
const char *p = params;
74+
int p_len, ret = 0;
75+
7376
while (*p) {
74-
if (!prefixcmp(p, "changes")) {
75-
p += 7;
77+
p_len = strchrnul(p, ',') - p;
78+
if (!memcmp(p, "changes", p_len)) {
7679
DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
7780
DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
78-
} else if (!prefixcmp(p, "lines")) {
79-
p += 5;
81+
} else if (!memcmp(p, "lines", p_len)) {
8082
DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
8183
DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
82-
} else if (!prefixcmp(p, "files")) {
83-
p += 5;
84+
} else if (!memcmp(p, "files", p_len)) {
8485
DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
8586
DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
86-
} else if (!prefixcmp(p, "noncumulative")) {
87-
p += 13;
87+
} else if (!memcmp(p, "noncumulative", p_len)) {
8888
DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
89-
} else if (!prefixcmp(p, "cumulative")) {
90-
p += 10;
89+
} else if (!memcmp(p, "cumulative", p_len)) {
9190
DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
9291
} else if (isdigit(*p)) {
9392
char *end;
94-
options->dirstat_permille = strtoul(p, &end, 10) * 10;
95-
p = end;
96-
if (*p == '.' && isdigit(*++p)) {
93+
int permille = strtoul(p, &end, 10) * 10;
94+
if (*end == '.' && isdigit(*++end)) {
9795
/* only use first digit */
98-
options->dirstat_permille += *p - '0';
96+
permille += *end - '0';
9997
/* .. and ignore any further digits */
100-
while (isdigit(*++p))
98+
while (isdigit(*++end))
10199
; /* nothing */
102100
}
103-
} else
104-
return error("Unknown --dirstat parameter '%s'", p);
105-
106-
if (*p) {
107-
/* more parameters, swallow separator */
108-
if (*p != ',')
109-
return error("Missing comma separator at char "
110-
"%"PRIuMAX" of '%s'",
111-
(uintmax_t) (p - params), params);
112-
p++;
101+
if (end - p == p_len)
102+
options->dirstat_permille = permille;
103+
else {
104+
strbuf_addf(errmsg, " Failed to parse dirstat cut-off percentage '%.*s'\n",
105+
p_len, p);
106+
ret++;
107+
}
108+
} else {
109+
strbuf_addf(errmsg, " Unknown dirstat parameter '%.*s'\n",
110+
p_len, p);
111+
ret++;
113112
}
113+
114+
p += p_len;
115+
116+
if (*p)
117+
p++; /* more parameters, swallow separator */
114118
}
115-
return 0;
119+
return ret;
116120
}
117121

118122
static int git_config_rename(const char *var, const char *value)
@@ -195,8 +199,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
195199
}
196200

197201
if (!strcmp(var, "diff.dirstat")) {
202+
struct strbuf errmsg = STRBUF_INIT;
198203
default_diff_options.dirstat_permille = diff_dirstat_permille_default;
199-
(void) parse_dirstat_params(&default_diff_options, value);
204+
if (parse_dirstat_params(&default_diff_options, value, &errmsg))
205+
warning("Found errors in 'diff.dirstat' config variable:\n%s",
206+
errmsg.buf);
207+
strbuf_release(&errmsg);
200208
diff_dirstat_permille_default = default_diff_options.dirstat_permille;
201209
return 0;
202210
}
@@ -3250,8 +3258,11 @@ static int stat_opt(struct diff_options *options, const char **av)
32503258

32513259
static int parse_dirstat_opt(struct diff_options *options, const char *params)
32523260
{
3253-
if (parse_dirstat_params(options, params))
3254-
die("Failed to parse --dirstat/-X option parameter");
3261+
struct strbuf errmsg = STRBUF_INIT;
3262+
if (parse_dirstat_params(options, params, &errmsg))
3263+
die("Failed to parse --dirstat/-X option parameter:\n%s",
3264+
errmsg.buf);
3265+
strbuf_release(&errmsg);
32553266
/*
32563267
* The caller knows a dirstat-related option is given from the command
32573268
* line; allow it to say "return this_function();"

t/t4047-diff-dirstat.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,4 +939,41 @@ test_expect_success 'diff.dirstat=0,lines' '
939939
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
940940
'
941941

942+
test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
943+
test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
944+
test_debug "cat actual_error" &&
945+
test_cmp /dev/null actual_diff_dirstat &&
946+
grep -q "future_param" actual_error &&
947+
grep -q "\--dirstat" actual_error
948+
'
949+
950+
test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
951+
test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
952+
test_debug "cat actual_error" &&
953+
test_cmp /dev/null actual_diff_dirstat &&
954+
grep -q "dummy1" actual_error &&
955+
grep -q "2dummy" actual_error &&
956+
grep -q "\--dirstat" actual_error
957+
'
958+
959+
test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
960+
git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
961+
test_debug "cat actual_error" &&
962+
test_cmp expect_diff_dirstat actual_diff_dirstat &&
963+
grep -q "future_param" actual_error &&
964+
grep -q "diff\\.dirstat" actual_error &&
965+
966+
git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
967+
test_debug "cat actual_error" &&
968+
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
969+
grep -q "future_param" actual_error &&
970+
grep -q "diff\\.dirstat" actual_error &&
971+
972+
git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
973+
test_debug "cat actual_error" &&
974+
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
975+
grep -q "future_param" actual_error &&
976+
grep -q "diff\\.dirstat" actual_error
977+
'
978+
942979
test_done

0 commit comments

Comments
 (0)