Merge branch 'wc/diff'
* wc/diff: Test interaction between diff --check and --exit-code Use shorter error messages for whitespace problems Add tests for "git diff --check" with core.whitespace options Make "diff --check" output match "git apply" Unify whitespace checking diff --check: minor fixups "diff --check" should affect exit status
This commit is contained in:
commit
dfaf75b469
@ -93,7 +93,9 @@ endif::git-format-patch[]
|
|||||||
|
|
||||||
--check::
|
--check::
|
||||||
Warn if changes introduce trailing whitespace
|
Warn if changes introduce trailing whitespace
|
||||||
or an indent that uses a space before a tab.
|
or an indent that uses a space before a tab. Exits with
|
||||||
|
non-zero status if problems are found. Not compatible with
|
||||||
|
--exit-code.
|
||||||
|
|
||||||
--full-index::
|
--full-index::
|
||||||
Instead of the first handful characters, show full
|
Instead of the first handful characters, show full
|
||||||
|
@ -900,56 +900,22 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
|
|||||||
|
|
||||||
static void check_whitespace(const char *line, int len, unsigned ws_rule)
|
static void check_whitespace(const char *line, int len, unsigned ws_rule)
|
||||||
{
|
{
|
||||||
const char *err = "Adds trailing whitespace";
|
char *err;
|
||||||
int seen_space = 0;
|
unsigned result = check_and_emit_line(line + 1, len - 1, ws_rule,
|
||||||
int i;
|
NULL, NULL, NULL, NULL);
|
||||||
|
if (!result)
|
||||||
/*
|
|
||||||
* We know len is at least two, since we have a '+' and we
|
|
||||||
* checked that the last character was a '\n' before calling
|
|
||||||
* this function. That is, an addition of an empty line would
|
|
||||||
* check the '+' here. Sneaky...
|
|
||||||
*/
|
|
||||||
if ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure that there is no space followed by a tab in
|
|
||||||
* indentation.
|
|
||||||
*/
|
|
||||||
if (ws_rule & WS_SPACE_BEFORE_TAB) {
|
|
||||||
err = "Space in indent is followed by a tab";
|
|
||||||
for (i = 1; i < len; i++) {
|
|
||||||
if (line[i] == '\t') {
|
|
||||||
if (seen_space)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
else if (line[i] == ' ')
|
|
||||||
seen_space = 1;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure that the indentation does not contain more than
|
|
||||||
* 8 spaces.
|
|
||||||
*/
|
|
||||||
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
|
|
||||||
(8 < len) && !strncmp("+ ", line, 9)) {
|
|
||||||
err = "Indent more than 8 places with spaces";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
|
||||||
whitespace_error++;
|
whitespace_error++;
|
||||||
if (squelch_whitespace_errors &&
|
if (squelch_whitespace_errors &&
|
||||||
squelch_whitespace_errors < whitespace_error)
|
squelch_whitespace_errors < whitespace_error)
|
||||||
;
|
;
|
||||||
else
|
else {
|
||||||
fprintf(stderr, "%s.\n%s:%d:%.*s\n",
|
err = whitespace_error_string(result);
|
||||||
err, patch_input_file, linenr, len-2, line+1);
|
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
|
||||||
|
patch_input_file, linenr, err, len - 2, line + 1);
|
||||||
|
free(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,7 +31,5 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
|
|||||||
if (!rev.diffopt.output_format)
|
if (!rev.diffopt.output_format)
|
||||||
rev.diffopt.output_format = DIFF_FORMAT_RAW;
|
rev.diffopt.output_format = DIFF_FORMAT_RAW;
|
||||||
result = run_diff_files_cmd(&rev, argc, argv);
|
result = run_diff_files_cmd(&rev, argc, argv);
|
||||||
if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS))
|
return diff_result_code(&rev.diffopt, result);
|
||||||
return DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,5 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
result = run_diff_index(&rev, cached);
|
result = run_diff_index(&rev, cached);
|
||||||
if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS))
|
return diff_result_code(&rev.diffopt, result);
|
||||||
return DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
@ -117,10 +117,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!read_stdin)
|
if (read_stdin) {
|
||||||
return DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS)
|
|
||||||
&& DIFF_OPT_TST(&opt->diffopt, HAS_CHANGES);
|
|
||||||
|
|
||||||
if (opt->diffopt.detect_rename)
|
if (opt->diffopt.detect_rename)
|
||||||
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
|
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
|
||||||
DIFF_SETUP_USE_CACHE);
|
DIFF_SETUP_USE_CACHE);
|
||||||
@ -134,6 +131,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
|
|||||||
else
|
else
|
||||||
diff_tree_stdin(line);
|
diff_tree_stdin(line);
|
||||||
}
|
}
|
||||||
return DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS)
|
}
|
||||||
&& DIFF_OPT_TST(&opt->diffopt, HAS_CHANGES);
|
|
||||||
|
return diff_result_code(&opt->diffopt, 0);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
|||||||
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
|
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
|
||||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||||
|
|
||||||
/* If the user asked for our exit code then don't start a
|
/*
|
||||||
|
* If the user asked for our exit code then don't start a
|
||||||
* pager or we would end up reporting its exit code instead.
|
* pager or we would end up reporting its exit code instead.
|
||||||
*/
|
*/
|
||||||
if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS))
|
if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS))
|
||||||
@ -351,9 +352,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
|||||||
else
|
else
|
||||||
result = builtin_diff_combined(&rev, argc, argv,
|
result = builtin_diff_combined(&rev, argc, argv,
|
||||||
ent, ents);
|
ent, ents);
|
||||||
if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS))
|
result = diff_result_code(&rev.diffopt, result);
|
||||||
result = DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0;
|
|
||||||
|
|
||||||
if (1 < rev.diffopt.skip_stat_unmatch)
|
if (1 < rev.diffopt.skip_stat_unmatch)
|
||||||
refresh_index_quietly();
|
refresh_index_quietly();
|
||||||
return result;
|
return result;
|
||||||
|
4
cache.h
4
cache.h
@ -655,6 +655,10 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
|
|||||||
extern unsigned whitespace_rule_cfg;
|
extern unsigned whitespace_rule_cfg;
|
||||||
extern unsigned whitespace_rule(const char *);
|
extern unsigned whitespace_rule(const char *);
|
||||||
extern unsigned parse_whitespace_rule(const char *);
|
extern unsigned parse_whitespace_rule(const char *);
|
||||||
|
extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
|
||||||
|
FILE *stream, const char *set,
|
||||||
|
const char *reset, const char *ws);
|
||||||
|
extern char *whitespace_error_string(unsigned ws);
|
||||||
|
|
||||||
/* ls-files */
|
/* ls-files */
|
||||||
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
|
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
|
||||||
|
153
diff.c
153
diff.c
@ -486,88 +486,9 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
|
|||||||
|
|
||||||
static void emit_line(const char *set, const char *reset, const char *line, int len)
|
static void emit_line(const char *set, const char *reset, const char *line, int len)
|
||||||
{
|
{
|
||||||
if (len > 0 && line[len-1] == '\n')
|
|
||||||
len--;
|
|
||||||
fputs(set, stdout);
|
fputs(set, stdout);
|
||||||
fwrite(line, len, 1, stdout);
|
fwrite(line, len, 1, stdout);
|
||||||
puts(reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void emit_line_with_ws(int nparents,
|
|
||||||
const char *set, const char *reset, const char *ws,
|
|
||||||
const char *line, int len, unsigned ws_rule)
|
|
||||||
{
|
|
||||||
int col0 = nparents;
|
|
||||||
int last_tab_in_indent = -1;
|
|
||||||
int last_space_in_indent = -1;
|
|
||||||
int i;
|
|
||||||
int tail = len;
|
|
||||||
int need_highlight_leading_space = 0;
|
|
||||||
/*
|
|
||||||
* The line is a newly added line. Does it have funny leading
|
|
||||||
* whitespaces? In indent, SP should never precede a TAB. In
|
|
||||||
* addition, under "indent with non tab" rule, there should not
|
|
||||||
* be more than 8 consecutive spaces.
|
|
||||||
*/
|
|
||||||
for (i = col0; i < len; i++) {
|
|
||||||
if (line[i] == '\t') {
|
|
||||||
last_tab_in_indent = i;
|
|
||||||
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
|
|
||||||
0 <= last_space_in_indent)
|
|
||||||
need_highlight_leading_space = 1;
|
|
||||||
}
|
|
||||||
else if (line[i] == ' ')
|
|
||||||
last_space_in_indent = i;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
|
|
||||||
0 <= last_space_in_indent &&
|
|
||||||
last_tab_in_indent < 0 &&
|
|
||||||
8 <= (i - col0)) {
|
|
||||||
last_tab_in_indent = i;
|
|
||||||
need_highlight_leading_space = 1;
|
|
||||||
}
|
|
||||||
fputs(set, stdout);
|
|
||||||
fwrite(line, col0, 1, stdout);
|
|
||||||
fputs(reset, stdout);
|
fputs(reset, stdout);
|
||||||
if (((i == len) || line[i] == '\n') && i != col0) {
|
|
||||||
/* The whole line was indent */
|
|
||||||
emit_line(ws, reset, line + col0, len - col0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
i = col0;
|
|
||||||
if (need_highlight_leading_space) {
|
|
||||||
while (i < last_tab_in_indent) {
|
|
||||||
if (line[i] == ' ') {
|
|
||||||
fputs(ws, stdout);
|
|
||||||
putchar(' ');
|
|
||||||
fputs(reset, stdout);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
putchar(line[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tail = len - 1;
|
|
||||||
if (line[tail] == '\n' && i < tail)
|
|
||||||
tail--;
|
|
||||||
if (ws_rule & WS_TRAILING_SPACE) {
|
|
||||||
while (i < tail) {
|
|
||||||
if (!isspace(line[tail]))
|
|
||||||
break;
|
|
||||||
tail--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((i < tail && line[tail + 1] != '\n')) {
|
|
||||||
/* This has whitespace between tail+1..len */
|
|
||||||
fputs(set, stdout);
|
|
||||||
fwrite(line + i, tail - i + 1, 1, stdout);
|
|
||||||
fputs(reset, stdout);
|
|
||||||
emit_line(ws, reset, line + tail + 1, len - tail - 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
emit_line(set, reset, line + i, len - i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
|
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
|
||||||
@ -577,9 +498,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
|
|||||||
|
|
||||||
if (!*ws)
|
if (!*ws)
|
||||||
emit_line(set, reset, line, len);
|
emit_line(set, reset, line, len);
|
||||||
else
|
else {
|
||||||
emit_line_with_ws(ecbdata->nparents, set, reset, ws,
|
/* Emit just the prefix, then the rest. */
|
||||||
line, len, ecbdata->ws_rule);
|
emit_line(set, reset, line, ecbdata->nparents);
|
||||||
|
(void)check_and_emit_line(line + ecbdata->nparents,
|
||||||
|
len - ecbdata->nparents, ecbdata->ws_rule,
|
||||||
|
stdout, set, reset, ws);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn_out_consume(void *priv, char *line, unsigned long len)
|
static void fn_out_consume(void *priv, char *line, unsigned long len)
|
||||||
@ -1031,6 +956,7 @@ struct checkdiff_t {
|
|||||||
const char *filename;
|
const char *filename;
|
||||||
int lineno, color_diff;
|
int lineno, color_diff;
|
||||||
unsigned ws_rule;
|
unsigned ws_rule;
|
||||||
|
unsigned status;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void checkdiff_consume(void *priv, char *line, unsigned long len)
|
static void checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||||
@ -1039,44 +965,19 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
|
|||||||
const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE);
|
const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE);
|
||||||
const char *reset = diff_get_color(data->color_diff, DIFF_RESET);
|
const char *reset = diff_get_color(data->color_diff, DIFF_RESET);
|
||||||
const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW);
|
const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW);
|
||||||
|
char *err;
|
||||||
|
|
||||||
if (line[0] == '+') {
|
if (line[0] == '+') {
|
||||||
int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0;
|
data->status = check_and_emit_line(line + 1, len - 1,
|
||||||
|
data->ws_rule, NULL, NULL, NULL, NULL);
|
||||||
/* check space before tab */
|
if (!data->status)
|
||||||
for (i = 1; i < len; i++) {
|
return;
|
||||||
if (line[i] == ' ')
|
err = whitespace_error_string(data->status);
|
||||||
spaces++;
|
printf("%s:%d: %s.\n", data->filename, data->lineno, err);
|
||||||
else if (line[i] == '\t') {
|
free(err);
|
||||||
if (spaces) {
|
emit_line(set, reset, line, 1);
|
||||||
space_before_tab = 1;
|
(void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
|
||||||
break;
|
stdout, set, reset, ws);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check whitespace at line end */
|
|
||||||
if (line[len - 1] == '\n')
|
|
||||||
len--;
|
|
||||||
if (isspace(line[len - 1]))
|
|
||||||
white_space_at_end = 1;
|
|
||||||
|
|
||||||
if (space_before_tab || white_space_at_end) {
|
|
||||||
printf("%s:%d: %s", data->filename, data->lineno, ws);
|
|
||||||
if (space_before_tab) {
|
|
||||||
printf("space before tab");
|
|
||||||
if (white_space_at_end)
|
|
||||||
putchar(',');
|
|
||||||
}
|
|
||||||
if (white_space_at_end)
|
|
||||||
printf("whitespace at end");
|
|
||||||
printf(":%s ", reset);
|
|
||||||
emit_line_with_ws(1, set, reset, ws, line, len,
|
|
||||||
data->ws_rule);
|
|
||||||
}
|
|
||||||
|
|
||||||
data->lineno++;
|
data->lineno++;
|
||||||
} else if (line[0] == ' ')
|
} else if (line[0] == ' ')
|
||||||
data->lineno++;
|
data->lineno++;
|
||||||
@ -1491,6 +1392,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
|
|||||||
free_and_return:
|
free_and_return:
|
||||||
diff_free_filespec_data(one);
|
diff_free_filespec_data(one);
|
||||||
diff_free_filespec_data(two);
|
diff_free_filespec_data(two);
|
||||||
|
if (data.status)
|
||||||
|
DIFF_OPT_SET(o, CHECK_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct diff_filespec *alloc_filespec(const char *path)
|
struct diff_filespec *alloc_filespec(const char *path)
|
||||||
@ -3171,6 +3074,20 @@ void diffcore_std(struct diff_options *options)
|
|||||||
DIFF_OPT_CLR(options, HAS_CHANGES);
|
DIFF_OPT_CLR(options, HAS_CHANGES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int diff_result_code(struct diff_options *opt, int status)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
|
||||||
|
!(opt->output_format & DIFF_FORMAT_CHECKDIFF))
|
||||||
|
return status;
|
||||||
|
if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
|
||||||
|
DIFF_OPT_TST(opt, HAS_CHANGES))
|
||||||
|
result |= 01;
|
||||||
|
if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) &&
|
||||||
|
DIFF_OPT_TST(opt, CHECK_FAILED))
|
||||||
|
result |= 02;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void diff_addremove(struct diff_options *options,
|
void diff_addremove(struct diff_options *options,
|
||||||
int addremove, unsigned mode,
|
int addremove, unsigned mode,
|
||||||
|
3
diff.h
3
diff.h
@ -59,6 +59,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
|
|||||||
#define DIFF_OPT_ALLOW_EXTERNAL (1 << 13)
|
#define DIFF_OPT_ALLOW_EXTERNAL (1 << 13)
|
||||||
#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
|
#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
|
||||||
#define DIFF_OPT_REVERSE_DIFF (1 << 15)
|
#define DIFF_OPT_REVERSE_DIFF (1 << 15)
|
||||||
|
#define DIFF_OPT_CHECK_FAILED (1 << 16)
|
||||||
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
|
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
|
||||||
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
|
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
|
||||||
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
|
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
|
||||||
@ -246,4 +247,6 @@ extern int run_diff_index(struct rev_info *revs, int cached);
|
|||||||
extern int do_diff_cache(const unsigned char *, struct diff_options *);
|
extern int do_diff_cache(const unsigned char *, struct diff_options *);
|
||||||
extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
|
extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
|
||||||
|
|
||||||
|
extern int diff_result_code(struct diff_options *, int);
|
||||||
|
|
||||||
#endif /* DIFF_H */
|
#endif /* DIFF_H */
|
||||||
|
@ -117,12 +117,197 @@ EOF
|
|||||||
git diff -b > out
|
git diff -b > out
|
||||||
test_expect_success 'another test, with -b' 'git diff expect out'
|
test_expect_success 'another test, with -b' 'git diff expect out'
|
||||||
|
|
||||||
|
|
||||||
test_expect_success 'check mixed spaces and tabs in indent' '
|
test_expect_success 'check mixed spaces and tabs in indent' '
|
||||||
|
|
||||||
# This is indented with SP HT SP.
|
# This is indented with SP HT SP.
|
||||||
echo " foo();" > x &&
|
echo " foo();" > x &&
|
||||||
git diff --check | grep "space before tab"
|
git diff --check | grep "space before tab in indent"
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with no whitespace errors' '
|
||||||
|
|
||||||
|
git commit -m "snapshot" &&
|
||||||
|
echo "foo();" > x &&
|
||||||
|
git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with trailing whitespace' '
|
||||||
|
|
||||||
|
echo "foo(); " > x &&
|
||||||
|
! git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with space before tab in indent' '
|
||||||
|
|
||||||
|
# indent has space followed by hard tab
|
||||||
|
echo " foo();" > x &&
|
||||||
|
! git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--check and --exit-code are not exclusive' '
|
||||||
|
|
||||||
|
git checkout x &&
|
||||||
|
git diff --check --exit-code
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--check and --quiet are not exclusive' '
|
||||||
|
|
||||||
|
git diff --check --quiet
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check staged with no whitespace errors' '
|
||||||
|
|
||||||
|
echo "foo();" > x &&
|
||||||
|
git add x &&
|
||||||
|
git diff --cached --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check staged with trailing whitespace' '
|
||||||
|
|
||||||
|
echo "foo(); " > x &&
|
||||||
|
git add x &&
|
||||||
|
! git diff --cached --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check staged with space before tab in indent' '
|
||||||
|
|
||||||
|
# indent has space followed by hard tab
|
||||||
|
echo " foo();" > x &&
|
||||||
|
git add x &&
|
||||||
|
! git diff --cached --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with no whitespace errors (diff-index)' '
|
||||||
|
|
||||||
|
echo "foo();" > x &&
|
||||||
|
git add x &&
|
||||||
|
git diff-index --check HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with trailing whitespace (diff-index)' '
|
||||||
|
|
||||||
|
echo "foo(); " > x &&
|
||||||
|
git add x &&
|
||||||
|
! git diff-index --check HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with space before tab in indent (diff-index)' '
|
||||||
|
|
||||||
|
# indent has space followed by hard tab
|
||||||
|
echo " foo();" > x &&
|
||||||
|
git add x &&
|
||||||
|
! git diff-index --check HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check staged with no whitespace errors (diff-index)' '
|
||||||
|
|
||||||
|
echo "foo();" > x &&
|
||||||
|
git add x &&
|
||||||
|
git diff-index --cached --check HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check staged with trailing whitespace (diff-index)' '
|
||||||
|
|
||||||
|
echo "foo(); " > x &&
|
||||||
|
git add x &&
|
||||||
|
! git diff-index --cached --check HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check staged with space before tab in indent (diff-index)' '
|
||||||
|
|
||||||
|
# indent has space followed by hard tab
|
||||||
|
echo " foo();" > x &&
|
||||||
|
git add x &&
|
||||||
|
! git diff-index --cached --check HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with no whitespace errors (diff-tree)' '
|
||||||
|
|
||||||
|
echo "foo();" > x &&
|
||||||
|
git commit -m "new commit" x &&
|
||||||
|
git diff-tree --check HEAD^ HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with trailing whitespace (diff-tree)' '
|
||||||
|
|
||||||
|
echo "foo(); " > x &&
|
||||||
|
git commit -m "another commit" x &&
|
||||||
|
! git diff-tree --check HEAD^ HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check with space before tab in indent (diff-tree)' '
|
||||||
|
|
||||||
|
# indent has space followed by hard tab
|
||||||
|
echo " foo();" > x &&
|
||||||
|
git commit -m "yet another" x &&
|
||||||
|
! git diff-tree --check HEAD^ HEAD
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check trailing whitespace (trailing-space: off)' '
|
||||||
|
|
||||||
|
git config core.whitespace "-trailing-space" &&
|
||||||
|
echo "foo (); " > x &&
|
||||||
|
git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check trailing whitespace (trailing-space: on)' '
|
||||||
|
|
||||||
|
git config core.whitespace "trailing-space" &&
|
||||||
|
echo "foo (); " > x &&
|
||||||
|
! git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check space before tab in indent (space-before-tab: off)' '
|
||||||
|
|
||||||
|
# indent contains space followed by HT
|
||||||
|
git config core.whitespace "-space-before-tab" &&
|
||||||
|
echo " foo ();" > x &&
|
||||||
|
git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check space before tab in indent (space-before-tab: on)' '
|
||||||
|
|
||||||
|
# indent contains space followed by HT
|
||||||
|
git config core.whitespace "space-before-tab" &&
|
||||||
|
echo " foo (); " > x &&
|
||||||
|
! git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
|
||||||
|
|
||||||
|
git config core.whitespace "-indent-with-non-tab"
|
||||||
|
echo " foo ();" > x &&
|
||||||
|
git diff --check
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
|
||||||
|
|
||||||
|
git config core.whitespace "indent-with-non-tab" &&
|
||||||
|
echo " foo ();" > x &&
|
||||||
|
! git diff --check
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
@ -76,4 +76,33 @@ test_expect_success 'git diff-index --cached HEAD' '
|
|||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--check --exit-code returns 0 for no difference' '
|
||||||
|
|
||||||
|
git diff --check --exit-code
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--check --exit-code returns 1 for a clean difference' '
|
||||||
|
|
||||||
|
echo "good" > a &&
|
||||||
|
git diff --check --exit-code
|
||||||
|
test $? = 1
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--check --exit-code returns 3 for a dirty difference' '
|
||||||
|
|
||||||
|
echo "bad " >> a &&
|
||||||
|
git diff --check --exit-code
|
||||||
|
test $? = 3
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--check with --no-pager returns 2 for dirty difference' '
|
||||||
|
|
||||||
|
git --no-pager diff --check
|
||||||
|
test $? = 2
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
105
ws.c
105
ws.c
@ -94,3 +94,108 @@ unsigned whitespace_rule(const char *pathname)
|
|||||||
return whitespace_rule_cfg;
|
return whitespace_rule_cfg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The returned string should be freed by the caller. */
|
||||||
|
char *whitespace_error_string(unsigned ws)
|
||||||
|
{
|
||||||
|
struct strbuf err;
|
||||||
|
strbuf_init(&err, 0);
|
||||||
|
if (ws & WS_TRAILING_SPACE)
|
||||||
|
strbuf_addstr(&err, "trailing whitespace");
|
||||||
|
if (ws & WS_SPACE_BEFORE_TAB) {
|
||||||
|
if (err.len)
|
||||||
|
strbuf_addstr(&err, ", ");
|
||||||
|
strbuf_addstr(&err, "space before tab in indent");
|
||||||
|
}
|
||||||
|
if (ws & WS_INDENT_WITH_NON_TAB) {
|
||||||
|
if (err.len)
|
||||||
|
strbuf_addstr(&err, ", ");
|
||||||
|
strbuf_addstr(&err, "indent with spaces");
|
||||||
|
}
|
||||||
|
return strbuf_detach(&err, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If stream is non-NULL, emits the line after checking. */
|
||||||
|
unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
|
||||||
|
FILE *stream, const char *set,
|
||||||
|
const char *reset, const char *ws)
|
||||||
|
{
|
||||||
|
unsigned result = 0;
|
||||||
|
int leading_space = -1;
|
||||||
|
int trailing_whitespace = -1;
|
||||||
|
int trailing_newline = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Logic is simpler if we temporarily ignore the trailing newline. */
|
||||||
|
if (len > 0 && line[len - 1] == '\n') {
|
||||||
|
trailing_newline = 1;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for trailing whitespace. */
|
||||||
|
if (ws_rule & WS_TRAILING_SPACE) {
|
||||||
|
for (i = len - 1; i >= 0; i--) {
|
||||||
|
if (isspace(line[i])) {
|
||||||
|
trailing_whitespace = i;
|
||||||
|
result |= WS_TRAILING_SPACE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for space before tab in initial indent. */
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (line[i] == '\t') {
|
||||||
|
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
|
||||||
|
(leading_space != -1))
|
||||||
|
result |= WS_SPACE_BEFORE_TAB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (line[i] == ' ')
|
||||||
|
leading_space = i;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for indent using non-tab. */
|
||||||
|
if ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 8)
|
||||||
|
result |= WS_INDENT_WITH_NON_TAB;
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
/* Highlight errors in leading whitespace. */
|
||||||
|
if ((result & WS_SPACE_BEFORE_TAB) ||
|
||||||
|
(result & WS_INDENT_WITH_NON_TAB)) {
|
||||||
|
fputs(ws, stream);
|
||||||
|
fwrite(line, leading_space + 1, 1, stream);
|
||||||
|
fputs(reset, stream);
|
||||||
|
leading_space++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
leading_space = 0;
|
||||||
|
|
||||||
|
/* Now the rest of the line starts at leading_space.
|
||||||
|
* The non-highlighted part ends at trailing_whitespace. */
|
||||||
|
if (trailing_whitespace == -1)
|
||||||
|
trailing_whitespace = len;
|
||||||
|
|
||||||
|
/* Emit non-highlighted (middle) segment. */
|
||||||
|
if (trailing_whitespace - leading_space > 0) {
|
||||||
|
fputs(set, stream);
|
||||||
|
fwrite(line + leading_space,
|
||||||
|
trailing_whitespace - leading_space, 1, stream);
|
||||||
|
fputs(reset, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Highlight errors in trailing whitespace. */
|
||||||
|
if (trailing_whitespace != len) {
|
||||||
|
fputs(ws, stream);
|
||||||
|
fwrite(line + trailing_whitespace,
|
||||||
|
len - trailing_whitespace, 1, stream);
|
||||||
|
fputs(reset, stream);
|
||||||
|
}
|
||||||
|
if (trailing_newline)
|
||||||
|
fputc('\n', stream);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user