Skip to content

Commit ab2693c

Browse files
committed
diff: highlight and error out on incomplete lines
Teach "git diff" to highlight "\ No newline at end of file" message as a whitespace error when incomplete-line whitespace error class is in effect. Thanks to the previous refactoring of complete rewrite code path, we can do this at a single place. Unlike whitespace errors in the payload where we need to annotate in line, possibly using colors, the line that has whitespace problems, we have a dedicated line already that can serve as the error message, so paint it as a whitespace error message. Also teach "git diff --check" to notice incomplete lines as whitespace errors and report when incomplete-line whitespace error class is in effect. Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9fb15a8 commit ab2693c

File tree

2 files changed

+90
-6
lines changed

2 files changed

+90
-6
lines changed

diff.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,7 +1370,11 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
13701370
emit_line(o, "", "", line, len);
13711371
break;
13721372
case DIFF_SYMBOL_CONTEXT_INCOMPLETE:
1373-
set = diff_get_color_opt(o, DIFF_CONTEXT);
1373+
if ((flags & WS_INCOMPLETE_LINE) &&
1374+
(flags & o->ws_error_highlight))
1375+
set = diff_get_color_opt(o, DIFF_WHITESPACE);
1376+
else
1377+
set = diff_get_color_opt(o, DIFF_CONTEXT);
13741378
reset = diff_get_color_opt(o, DIFF_RESET);
13751379
emit_line(o, set, reset, line, len);
13761380
break;
@@ -1666,8 +1670,14 @@ static void emit_context_line(struct emit_callback *ecbdata,
16661670
static void emit_incomplete_line_marker(struct emit_callback *ecbdata,
16671671
const char *line, int len)
16681672
{
1673+
int last_line_kind = ecbdata->last_line_kind;
1674+
unsigned flags = (last_line_kind == '+'
1675+
? WSEH_NEW
1676+
: last_line_kind == '-'
1677+
? WSEH_OLD
1678+
: WSEH_CONTEXT) | ecbdata->ws_rule;
16691679
emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
1670-
line, len, 0);
1680+
line, len, flags);
16711681
}
16721682

16731683
static void emit_hunk_header(struct emit_callback *ecbdata,
@@ -3254,6 +3264,7 @@ struct checkdiff_t {
32543264
struct diff_options *o;
32553265
unsigned ws_rule;
32563266
unsigned status;
3267+
int last_line_kind;
32573268
};
32583269

32593270
static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
@@ -3292,6 +3303,7 @@ static void checkdiff_consume_hunk(void *priv,
32923303
static int checkdiff_consume(void *priv, char *line, unsigned long len)
32933304
{
32943305
struct checkdiff_t *data = priv;
3306+
int last_line_kind;
32953307
int marker_size = data->conflict_marker_size;
32963308
const char *ws = diff_get_color(data->o->use_color, DIFF_WHITESPACE);
32973309
const char *reset = diff_get_color(data->o->use_color, DIFF_RESET);
@@ -3302,6 +3314,8 @@ static int checkdiff_consume(void *priv, char *line, unsigned long len)
33023314
assert(data->o);
33033315
line_prefix = diff_line_prefix(data->o);
33043316

3317+
last_line_kind = data->last_line_kind;
3318+
data->last_line_kind = line[0];
33053319
if (line[0] == '+') {
33063320
unsigned bad;
33073321
data->lineno++;
@@ -3324,6 +3338,17 @@ static int checkdiff_consume(void *priv, char *line, unsigned long len)
33243338
data->o->file, set, reset, ws);
33253339
} else if (line[0] == ' ') {
33263340
data->lineno++;
3341+
} else if (line[0] == '\\') {
3342+
/* no newline at the end of the line */
3343+
if ((data->ws_rule & WS_INCOMPLETE_LINE) &&
3344+
(last_line_kind == '+')) {
3345+
unsigned bad = WS_INCOMPLETE_LINE;
3346+
data->status |= bad;
3347+
err = whitespace_error_string(bad);
3348+
fprintf(data->o->file, "%s%s:%d: %s.\n",
3349+
line_prefix, data->filename, data->lineno, err);
3350+
free(err);
3351+
}
33273352
}
33283353
return 0;
33293354
}

t/t4015-diff-whitespace.sh

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,53 @@ do
4343
'
4444
done
4545

46+
test_expect_success "incomplete line in both pre- and post-image context" '
47+
(echo foo && echo baz | tr -d "\012") >x &&
48+
git add x &&
49+
(echo bar && echo baz | tr -d "\012") >x &&
50+
git diff x &&
51+
git -c core.whitespace=incomplete diff --check x &&
52+
git diff -R x &&
53+
git -c core.whitespace=incomplete diff -R --check x
54+
'
55+
56+
test_expect_success "incomplete lines on both pre- and post-image" '
57+
# The interpretation taken here is "since you are touching
58+
# the line anyway, you would better fix the incomplete line
59+
# while you are at it." but this is debatable.
60+
echo foo | tr -d "\012" >x &&
61+
git add x &&
62+
echo bar | tr -d "\012" >x &&
63+
git diff x &&
64+
test_must_fail git -c core.whitespace=incomplete diff --check x >error &&
65+
test_grep "no newline at the end of file" error &&
66+
git diff -R x &&
67+
test_must_fail git -c core.whitespace=incomplete diff -R --check x >error &&
68+
test_grep "no newline at the end of file" error
69+
'
70+
71+
test_expect_success "fix incomplete line in pre-image" '
72+
echo foo | tr -d "\012" >x &&
73+
git add x &&
74+
echo bar >x &&
75+
git diff x &&
76+
git -c core.whitespace=incomplete diff --check x &&
77+
git diff -R x &&
78+
test_must_fail git -c core.whitespace=incomplete diff -R --check x >error &&
79+
test_grep "no newline at the end of file" error
80+
'
81+
82+
test_expect_success "new incomplete line in post-image" '
83+
echo foo >x &&
84+
git add x &&
85+
echo bar | tr -d "\012" >x &&
86+
git diff x &&
87+
test_must_fail git -c core.whitespace=incomplete diff --check x >error &&
88+
test_grep "no newline at the end of file" error &&
89+
git diff -R x &&
90+
git -c core.whitespace=incomplete diff -R --check x
91+
'
92+
4693
test_expect_success "Ray Lehtiniemi's example" '
4794
cat <<-\EOF >x &&
4895
do {
@@ -1040,7 +1087,8 @@ test_expect_success 'ws-error-highlight test setup' '
10401087
{
10411088
echo "0. blank-at-eol " &&
10421089
echo "1. still-blank-at-eol " &&
1043-
echo "2. and a new line "
1090+
echo "2. and a new line " &&
1091+
printf "3. and more"
10441092
} >x &&
10451093
new_hash_x=$(git hash-object x) &&
10461094
after=$(git rev-parse --short "$new_hash_x") &&
@@ -1050,40 +1098,47 @@ test_expect_success 'ws-error-highlight test setup' '
10501098
<BOLD>index $before..$after 100644<RESET>
10511099
<BOLD>--- a/x<RESET>
10521100
<BOLD>+++ b/x<RESET>
1053-
<CYAN>@@ -1,2 +1,3 @@<RESET>
1101+
<CYAN>@@ -1,2 +1,4 @@<RESET>
10541102
0. blank-at-eol <RESET>
10551103
<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
10561104
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
10571105
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
1106+
<GREEN>+<RESET><GREEN>3. and more<RESET>
1107+
<BLUE>\ No newline at end of file<RESET>
10581108
EOF
10591109
10601110
cat >expect.all <<-EOF &&
10611111
<BOLD>diff --git a/x b/x<RESET>
10621112
<BOLD>index $before..$after 100644<RESET>
10631113
<BOLD>--- a/x<RESET>
10641114
<BOLD>+++ b/x<RESET>
1065-
<CYAN>@@ -1,2 +1,3 @@<RESET>
1115+
<CYAN>@@ -1,2 +1,4 @@<RESET>
10661116
<RESET>0. blank-at-eol<RESET><BLUE> <RESET>
10671117
<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
10681118
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
10691119
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
1120+
<GREEN>+<RESET><GREEN>3. and more<RESET>
1121+
<BLUE>\ No newline at end of file<RESET>
10701122
EOF
10711123
10721124
cat >expect.none <<-EOF
10731125
<BOLD>diff --git a/x b/x<RESET>
10741126
<BOLD>index $before..$after 100644<RESET>
10751127
<BOLD>--- a/x<RESET>
10761128
<BOLD>+++ b/x<RESET>
1077-
<CYAN>@@ -1,2 +1,3 @@<RESET>
1129+
<CYAN>@@ -1,2 +1,4 @@<RESET>
10781130
0. blank-at-eol <RESET>
10791131
<RED>-1. blank-at-eol <RESET>
10801132
<GREEN>+1. still-blank-at-eol <RESET>
10811133
<GREEN>+2. and a new line <RESET>
1134+
<GREEN>+3. and more<RESET>
1135+
\ No newline at end of file<RESET>
10821136
EOF
10831137
10841138
'
10851139

10861140
test_expect_success 'test --ws-error-highlight option' '
1141+
git config core.whitespace blank-at-eol,incomplete-line &&
10871142
10881143
git diff --color --ws-error-highlight=default,old >current.raw &&
10891144
test_decode_color <current.raw >current &&
@@ -1100,6 +1155,7 @@ test_expect_success 'test --ws-error-highlight option' '
11001155
'
11011156

11021157
test_expect_success 'test diff.wsErrorHighlight config' '
1158+
git config core.whitespace blank-at-eol,incomplete-line &&
11031159
11041160
git -c diff.wsErrorHighlight=default,old diff --color >current.raw &&
11051161
test_decode_color <current.raw >current &&
@@ -1116,6 +1172,7 @@ test_expect_success 'test diff.wsErrorHighlight config' '
11161172
'
11171173

11181174
test_expect_success 'option overrides diff.wsErrorHighlight' '
1175+
git config core.whitespace blank-at-eol,incomplete-line &&
11191176
11201177
git -c diff.wsErrorHighlight=none \
11211178
diff --color --ws-error-highlight=default,old >current.raw &&
@@ -1135,6 +1192,8 @@ test_expect_success 'option overrides diff.wsErrorHighlight' '
11351192
'
11361193

11371194
test_expect_success 'detect moved code, complete file' '
1195+
git config core.whitespace blank-at-eol &&
1196+
11381197
git reset --hard &&
11391198
cat <<-\EOF >test.c &&
11401199
#include<stdio.h>

0 commit comments

Comments
 (0)