mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
Merge branch 'jc/whitespace-incomplete-line'
Both "git apply" and "git diff" learn a new whitespace error class, "incomplete-line". * jc/whitespace-incomplete-line: attr: enable incomplete-line whitespace error for this project diff: highlight and error out on incomplete lines apply: check and fix incomplete lines whitespace: allocate a few more bits and define WS_INCOMPLETE_LINE apply: revamp the parsing of incomplete lines diff: update the way rewrite diff handles incomplete lines diff: call emit_callback ecbdata everywhere diff: refactor output of incomplete line diff: keep track of the type of the last line seen diff: correct suppress_blank_empty hack diff: emit_line_ws_markup() if/else style fix whitespace: correct bit assignment comments
This commit is contained in:
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -1,13 +1,13 @@
|
||||
* whitespace=trail,space
|
||||
*.[ch] whitespace=indent,trail,space diff=cpp
|
||||
*.sh whitespace=indent,trail,space text eol=lf
|
||||
*.[ch] whitespace=indent,trail,space,incomplete diff=cpp
|
||||
*.sh whitespace=indent,trail,space,incomplete text eol=lf
|
||||
*.perl text eol=lf diff=perl
|
||||
*.pl text eof=lf diff=perl
|
||||
*.pm text eol=lf diff=perl
|
||||
*.py text eol=lf diff=python
|
||||
*.bat text eol=crlf
|
||||
CODE_OF_CONDUCT.md -whitespace
|
||||
/Documentation/**/*.adoc text eol=lf
|
||||
/Documentation/**/*.adoc text eol=lf whitespace=trail,space,incomplete
|
||||
/command-list.txt text eol=lf
|
||||
/GIT-VERSION-GEN text eol=lf
|
||||
/mergetools/* text eol=lf
|
||||
|
||||
@@ -629,6 +629,8 @@ core.whitespace::
|
||||
part of the line terminator, i.e. with it, `trailing-space`
|
||||
does not trigger if the character before such a carriage-return
|
||||
is not a whitespace (not enabled by default).
|
||||
* `incomplete-line` treats the last line of a file that is missing the
|
||||
newline at the end as an error (not enabled by default).
|
||||
* `tabwidth=<n>` tells how many character positions a tab occupies; this
|
||||
is relevant for `indent-with-non-tab` and when Git fixes `tab-in-indent`
|
||||
errors. The default tab width is 8. Allowed values are 1 to 63.
|
||||
|
||||
79
apply.c
79
apply.c
@@ -1640,6 +1640,14 @@ static void record_ws_error(struct apply_state *state,
|
||||
state->squelch_whitespace_errors < state->whitespace_error)
|
||||
return;
|
||||
|
||||
/*
|
||||
* line[len] for an incomplete line points at the "\n" at the end
|
||||
* of patch input line, so "%.*s" would drop the last letter on line;
|
||||
* compensate for it.
|
||||
*/
|
||||
if (result & WS_INCOMPLETE_LINE)
|
||||
len++;
|
||||
|
||||
err = whitespace_error_string(result);
|
||||
if (state->apply_verbosity > verbosity_silent)
|
||||
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
|
||||
@@ -1670,6 +1678,35 @@ static void check_old_for_crlf(struct patch *patch, const char *line, int len)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Just saw a single line in a fragment. If it is a part of this hunk
|
||||
* that is a context " ", an added "+", or a removed "-" line, it may
|
||||
* be followed by "\\ No newline..." to signal that the last "\n" on
|
||||
* this line needs to be dropped. Depending on locale settings when
|
||||
* the patch was produced we don't know what this line would exactly
|
||||
* say. The only thing we do know is that it begins with "\ ".
|
||||
* Checking for 12 is just for sanity check; "\ No newline..." would
|
||||
* be at least that long in any l10n.
|
||||
*
|
||||
* Return 0 if the line we saw is not followed by "\ No newline...",
|
||||
* or length of that line. The caller will use it to skip over the
|
||||
* "\ No newline..." line.
|
||||
*/
|
||||
static int adjust_incomplete(const char *line, int len,
|
||||
unsigned long size)
|
||||
{
|
||||
int nextlen;
|
||||
|
||||
if (*line != '\n' && *line != ' ' && *line != '+' && *line != '-')
|
||||
return 0;
|
||||
if (size - len < 12 || memcmp(line + len, "\\ ", 2))
|
||||
return 0;
|
||||
nextlen = linelen(line + len, size - len);
|
||||
if (nextlen < 12)
|
||||
return 0;
|
||||
return nextlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a unified diff. Note that this really needs to parse each
|
||||
* fragment separately, since the only way to know the difference
|
||||
@@ -1684,6 +1721,7 @@ static int parse_fragment(struct apply_state *state,
|
||||
{
|
||||
int added, deleted;
|
||||
int len = linelen(line, size), offset;
|
||||
int skip_len = 0;
|
||||
unsigned long oldlines, newlines;
|
||||
unsigned long leading, trailing;
|
||||
|
||||
@@ -1710,6 +1748,22 @@ static int parse_fragment(struct apply_state *state,
|
||||
len = linelen(line, size);
|
||||
if (!len || line[len-1] != '\n')
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* For an incomplete line, skip_len counts the bytes
|
||||
* on "\\ No newline..." marker line that comes next
|
||||
* to the current line.
|
||||
*
|
||||
* Reduce "len" to drop the newline at the end of
|
||||
* line[], but add one to "skip_len", which will be
|
||||
* added back to "len" for the next iteration, to
|
||||
* compensate.
|
||||
*/
|
||||
skip_len = adjust_incomplete(line, len, size);
|
||||
if (skip_len) {
|
||||
len--;
|
||||
skip_len++;
|
||||
}
|
||||
switch (*line) {
|
||||
default:
|
||||
return -1;
|
||||
@@ -1745,19 +1799,12 @@ static int parse_fragment(struct apply_state *state,
|
||||
newlines--;
|
||||
trailing = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We allow "\ No newline at end of file". Depending
|
||||
* on locale settings when the patch was produced we
|
||||
* don't know what this line looks like. The only
|
||||
* thing we do know is that it begins with "\ ".
|
||||
* Checking for 12 is just for sanity check -- any
|
||||
* l10n of "\ No newline..." is at least that long.
|
||||
*/
|
||||
case '\\':
|
||||
if (len < 12 || memcmp(line, "\\ ", 2))
|
||||
return -1;
|
||||
break;
|
||||
/* eat the "\\ No newline..." as well, if exists */
|
||||
if (skip_len) {
|
||||
len += skip_len;
|
||||
state->linenr++;
|
||||
}
|
||||
}
|
||||
if (oldlines || newlines)
|
||||
@@ -1768,14 +1815,6 @@ static int parse_fragment(struct apply_state *state,
|
||||
fragment->leading = leading;
|
||||
fragment->trailing = trailing;
|
||||
|
||||
/*
|
||||
* If a fragment ends with an incomplete line, we failed to include
|
||||
* it in the above loop because we hit oldlines == newlines == 0
|
||||
* before seeing it.
|
||||
*/
|
||||
if (12 < size && !memcmp(line, "\\ ", 2))
|
||||
offset += linelen(line, size);
|
||||
|
||||
patch->lines_added += added;
|
||||
patch->lines_deleted += deleted;
|
||||
|
||||
|
||||
147
diff.c
147
diff.c
@@ -601,6 +601,7 @@ struct emit_callback {
|
||||
int blank_at_eof_in_postimage;
|
||||
int lno_in_preimage;
|
||||
int lno_in_postimage;
|
||||
int last_line_kind;
|
||||
const char **label_path;
|
||||
struct diff_words_data *diff_words;
|
||||
struct diff_options *opt;
|
||||
@@ -796,21 +797,23 @@ enum diff_symbol {
|
||||
DIFF_SYMBOL_CONTEXT_INCOMPLETE,
|
||||
DIFF_SYMBOL_PLUS,
|
||||
DIFF_SYMBOL_MINUS,
|
||||
DIFF_SYMBOL_NO_LF_EOF,
|
||||
DIFF_SYMBOL_CONTEXT_FRAGINFO,
|
||||
DIFF_SYMBOL_CONTEXT_MARKER,
|
||||
DIFF_SYMBOL_SEPARATOR
|
||||
};
|
||||
|
||||
/*
|
||||
* Flags for content lines:
|
||||
* 0..12 are whitespace rules
|
||||
* 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT
|
||||
* 16 is marking if the line is blank at EOF
|
||||
* 0..15 are whitespace rules (see ws.h)
|
||||
* 16..18 are WSEH_NEW | WSEH_CONTEXT | WSEH_OLD
|
||||
* 19 is marking if the line is blank at EOF
|
||||
* 20..22 are used for color-moved.
|
||||
*/
|
||||
#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16)
|
||||
#define DIFF_SYMBOL_MOVED_LINE (1<<17)
|
||||
#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<18)
|
||||
#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<19)
|
||||
#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<19)
|
||||
#define DIFF_SYMBOL_MOVED_LINE (1<<20)
|
||||
#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<21)
|
||||
#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<22)
|
||||
|
||||
#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)
|
||||
|
||||
/*
|
||||
@@ -1318,20 +1321,25 @@ static void emit_line_ws_markup(struct diff_options *o,
|
||||
const char *ws = NULL;
|
||||
int sign = o->output_indicators[sign_index];
|
||||
|
||||
if (diff_suppress_blank_empty &&
|
||||
sign_index == OUTPUT_INDICATOR_CONTEXT &&
|
||||
len == 1 && line[0] == '\n')
|
||||
sign = 0;
|
||||
|
||||
if (o->ws_error_highlight & ws_rule) {
|
||||
ws = diff_get_color_opt(o, DIFF_WHITESPACE);
|
||||
if (!*ws)
|
||||
ws = NULL;
|
||||
}
|
||||
|
||||
if (!ws && !set_sign)
|
||||
if (!ws && !set_sign) {
|
||||
emit_line_0(o, set, NULL, 0, reset, sign, line, len);
|
||||
else if (!ws) {
|
||||
} else if (!ws) {
|
||||
emit_line_0(o, set_sign, set, !!set_sign, reset, sign, line, len);
|
||||
} else if (blank_at_eof)
|
||||
} else if (blank_at_eof) {
|
||||
/* Blank line at EOF - paint '+' as well */
|
||||
emit_line_0(o, ws, NULL, 0, reset, sign, line, len);
|
||||
else {
|
||||
} else {
|
||||
/* Emit just the prefix, then the rest. */
|
||||
emit_line_0(o, set_sign ? set_sign : set, NULL, !!set_sign, reset,
|
||||
sign, "", 0);
|
||||
@@ -1343,7 +1351,6 @@ static void emit_line_ws_markup(struct diff_options *o,
|
||||
static void emit_diff_symbol_from_struct(struct diff_options *o,
|
||||
struct emitted_diff_symbol *eds)
|
||||
{
|
||||
static const char *nneof = " No newline at end of file\n";
|
||||
const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
|
||||
|
||||
enum diff_symbol s = eds->s;
|
||||
@@ -1355,13 +1362,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
|
||||
return;
|
||||
|
||||
switch (s) {
|
||||
case DIFF_SYMBOL_NO_LF_EOF:
|
||||
context = diff_get_color_opt(o, DIFF_CONTEXT);
|
||||
reset = diff_get_color_opt(o, DIFF_RESET);
|
||||
putc('\n', o->file);
|
||||
emit_line_0(o, context, NULL, 0, reset, '\\',
|
||||
nneof, strlen(nneof));
|
||||
break;
|
||||
case DIFF_SYMBOL_SUBMODULE_HEADER:
|
||||
case DIFF_SYMBOL_SUBMODULE_ERROR:
|
||||
case DIFF_SYMBOL_SUBMODULE_PIPETHROUGH:
|
||||
@@ -1373,6 +1373,14 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
|
||||
emit_line(o, "", "", line, len);
|
||||
break;
|
||||
case DIFF_SYMBOL_CONTEXT_INCOMPLETE:
|
||||
if ((flags & WS_INCOMPLETE_LINE) &&
|
||||
(flags & o->ws_error_highlight))
|
||||
set = diff_get_color_opt(o, DIFF_WHITESPACE);
|
||||
else
|
||||
set = diff_get_color_opt(o, DIFF_CONTEXT);
|
||||
reset = diff_get_color_opt(o, DIFF_RESET);
|
||||
emit_line(o, set, reset, line, len);
|
||||
break;
|
||||
case DIFF_SYMBOL_CONTEXT_MARKER:
|
||||
context = diff_get_color_opt(o, DIFF_CONTEXT);
|
||||
reset = diff_get_color_opt(o, DIFF_RESET);
|
||||
@@ -1498,15 +1506,9 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
|
||||
case DIFF_SYMBOL_WORDS:
|
||||
context = diff_get_color_opt(o, DIFF_CONTEXT);
|
||||
reset = diff_get_color_opt(o, DIFF_RESET);
|
||||
/*
|
||||
* Skip the prefix character, if any. With
|
||||
* diff_suppress_blank_empty, there may be
|
||||
* none.
|
||||
*/
|
||||
if (line[0] != '\n') {
|
||||
line++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* Skip the prefix character */
|
||||
line++; len--;
|
||||
emit_line(o, context, reset, line, len);
|
||||
break;
|
||||
case DIFF_SYMBOL_FILEPAIR_PLUS:
|
||||
@@ -1668,6 +1670,19 @@ static void emit_context_line(struct emit_callback *ecbdata,
|
||||
emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags);
|
||||
}
|
||||
|
||||
static void emit_incomplete_line_marker(struct emit_callback *ecbdata,
|
||||
const char *line, int len)
|
||||
{
|
||||
int last_line_kind = ecbdata->last_line_kind;
|
||||
unsigned flags = (last_line_kind == '+'
|
||||
? WSEH_NEW
|
||||
: last_line_kind == '-'
|
||||
? WSEH_OLD
|
||||
: WSEH_CONTEXT) | ecbdata->ws_rule;
|
||||
emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
|
||||
line, len, flags);
|
||||
}
|
||||
|
||||
static void emit_hunk_header(struct emit_callback *ecbdata,
|
||||
const char *line, int len)
|
||||
{
|
||||
@@ -1769,28 +1784,44 @@ static void add_line_count(struct strbuf *out, int count)
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_rewrite_lines(struct emit_callback *ecb,
|
||||
static void emit_rewrite_lines(struct emit_callback *ecbdata,
|
||||
int prefix, const char *data, int size)
|
||||
{
|
||||
const char *endp = NULL;
|
||||
|
||||
while (0 < size) {
|
||||
int len;
|
||||
int len, plen;
|
||||
char *pdata = NULL;
|
||||
|
||||
endp = memchr(data, '\n', size);
|
||||
len = endp ? (endp - data + 1) : size;
|
||||
if (prefix != '+') {
|
||||
ecb->lno_in_preimage++;
|
||||
emit_del_line(ecb, data, len);
|
||||
|
||||
if (endp) {
|
||||
len = endp - data + 1;
|
||||
plen = len;
|
||||
} else {
|
||||
ecb->lno_in_postimage++;
|
||||
emit_add_line(ecb, data, len);
|
||||
len = size;
|
||||
plen = len + 1;
|
||||
pdata = xmalloc(plen + 2);
|
||||
memcpy(pdata, data, len);
|
||||
pdata[len] = '\n';
|
||||
pdata[len + 1] = '\0';
|
||||
}
|
||||
if (prefix != '+') {
|
||||
ecbdata->lno_in_preimage++;
|
||||
emit_del_line(ecbdata, pdata ? pdata : data, plen);
|
||||
} else {
|
||||
ecbdata->lno_in_postimage++;
|
||||
emit_add_line(ecbdata, pdata ? pdata : data, plen);
|
||||
}
|
||||
free(pdata);
|
||||
size -= len;
|
||||
data += len;
|
||||
}
|
||||
if (!endp)
|
||||
emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0, 0);
|
||||
if (!endp) {
|
||||
static const char nneof[] = "\\ No newline at end of file\n";
|
||||
ecbdata->last_line_kind = prefix;
|
||||
emit_incomplete_line_marker(ecbdata, nneof, sizeof(nneof) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_rewrite_diff(const char *name_a,
|
||||
@@ -2375,12 +2406,6 @@ static int fn_out_consume(void *priv, char *line, unsigned long len)
|
||||
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
|
||||
}
|
||||
|
||||
if (diff_suppress_blank_empty
|
||||
&& len == 2 && line[0] == ' ' && line[1] == '\n') {
|
||||
line[0] = '\n';
|
||||
len = 1;
|
||||
}
|
||||
|
||||
if (line[0] == '@') {
|
||||
if (ecbdata->diff_words)
|
||||
diff_words_flush(ecbdata);
|
||||
@@ -2431,13 +2456,24 @@ static int fn_out_consume(void *priv, char *line, unsigned long len)
|
||||
ecbdata->lno_in_preimage++;
|
||||
emit_context_line(ecbdata, line + 1, len - 1);
|
||||
break;
|
||||
default:
|
||||
case '\\':
|
||||
/* incomplete line at the end */
|
||||
switch (ecbdata->last_line_kind) {
|
||||
case '+':
|
||||
case '-':
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
BUG("fn_out_consume: '\\No newline' after unknown line (%c)",
|
||||
ecbdata->last_line_kind);
|
||||
}
|
||||
ecbdata->lno_in_preimage++;
|
||||
emit_diff_symbol(o, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
|
||||
line, len, 0);
|
||||
emit_incomplete_line_marker(ecbdata, line, len);
|
||||
break;
|
||||
default:
|
||||
BUG("fn_out_consume: unknown line '%s'", line);
|
||||
}
|
||||
ecbdata->last_line_kind = line[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3231,6 +3267,7 @@ struct checkdiff_t {
|
||||
struct diff_options *o;
|
||||
unsigned ws_rule;
|
||||
unsigned status;
|
||||
int last_line_kind;
|
||||
};
|
||||
|
||||
static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
|
||||
@@ -3269,6 +3306,7 @@ static void checkdiff_consume_hunk(void *priv,
|
||||
static int checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||
{
|
||||
struct checkdiff_t *data = priv;
|
||||
int last_line_kind;
|
||||
int marker_size = data->conflict_marker_size;
|
||||
const char *ws = diff_get_color(data->o->use_color, DIFF_WHITESPACE);
|
||||
const char *reset = diff_get_color(data->o->use_color, DIFF_RESET);
|
||||
@@ -3279,6 +3317,8 @@ static int checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||
assert(data->o);
|
||||
line_prefix = diff_line_prefix(data->o);
|
||||
|
||||
last_line_kind = data->last_line_kind;
|
||||
data->last_line_kind = line[0];
|
||||
if (line[0] == '+') {
|
||||
unsigned bad;
|
||||
data->lineno++;
|
||||
@@ -3301,6 +3341,17 @@ static int checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||
data->o->file, set, reset, ws);
|
||||
} else if (line[0] == ' ') {
|
||||
data->lineno++;
|
||||
} else if (line[0] == '\\') {
|
||||
/* no newline at the end of the line */
|
||||
if ((data->ws_rule & WS_INCOMPLETE_LINE) &&
|
||||
(last_line_kind == '+')) {
|
||||
unsigned bad = WS_INCOMPLETE_LINE;
|
||||
data->status |= bad;
|
||||
err = whitespace_error_string(bad);
|
||||
fprintf(data->o->file, "%s%s:%d: %s.\n",
|
||||
line_prefix, data->filename, data->lineno, err);
|
||||
free(err);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
6
diff.h
6
diff.h
@@ -331,9 +331,9 @@ struct diff_options {
|
||||
|
||||
int ita_invisible_in_index;
|
||||
/* white-space error highlighting */
|
||||
#define WSEH_NEW (1<<12)
|
||||
#define WSEH_CONTEXT (1<<13)
|
||||
#define WSEH_OLD (1<<14)
|
||||
#define WSEH_NEW (1<<16)
|
||||
#define WSEH_CONTEXT (1<<17)
|
||||
#define WSEH_OLD (1<<18)
|
||||
unsigned ws_error_highlight;
|
||||
const char *prefix;
|
||||
int prefix_length;
|
||||
|
||||
@@ -43,6 +43,53 @@ do
|
||||
'
|
||||
done
|
||||
|
||||
test_expect_success "incomplete line in both pre- and post-image context" '
|
||||
(echo foo && echo baz | tr -d "\012") >x &&
|
||||
git add x &&
|
||||
(echo bar && echo baz | tr -d "\012") >x &&
|
||||
git diff x &&
|
||||
git -c core.whitespace=incomplete diff --check x &&
|
||||
git diff -R x &&
|
||||
git -c core.whitespace=incomplete diff -R --check x
|
||||
'
|
||||
|
||||
test_expect_success "incomplete lines on both pre- and post-image" '
|
||||
# The interpretation taken here is "since you are touching
|
||||
# the line anyway, you would better fix the incomplete line
|
||||
# while you are at it." but this is debatable.
|
||||
echo foo | tr -d "\012" >x &&
|
||||
git add x &&
|
||||
echo bar | tr -d "\012" >x &&
|
||||
git diff x &&
|
||||
test_must_fail git -c core.whitespace=incomplete diff --check x >error &&
|
||||
test_grep "no newline at the end of file" error &&
|
||||
git diff -R x &&
|
||||
test_must_fail git -c core.whitespace=incomplete diff -R --check x >error &&
|
||||
test_grep "no newline at the end of file" error
|
||||
'
|
||||
|
||||
test_expect_success "fix incomplete line in pre-image" '
|
||||
echo foo | tr -d "\012" >x &&
|
||||
git add x &&
|
||||
echo bar >x &&
|
||||
git diff x &&
|
||||
git -c core.whitespace=incomplete diff --check x &&
|
||||
git diff -R x &&
|
||||
test_must_fail git -c core.whitespace=incomplete diff -R --check x >error &&
|
||||
test_grep "no newline at the end of file" error
|
||||
'
|
||||
|
||||
test_expect_success "new incomplete line in post-image" '
|
||||
echo foo >x &&
|
||||
git add x &&
|
||||
echo bar | tr -d "\012" >x &&
|
||||
git diff x &&
|
||||
test_must_fail git -c core.whitespace=incomplete diff --check x >error &&
|
||||
test_grep "no newline at the end of file" error &&
|
||||
git diff -R x &&
|
||||
git -c core.whitespace=incomplete diff -R --check x
|
||||
'
|
||||
|
||||
test_expect_success "Ray Lehtiniemi's example" '
|
||||
cat <<-\EOF >x &&
|
||||
do {
|
||||
@@ -1040,7 +1087,8 @@ test_expect_success 'ws-error-highlight test setup' '
|
||||
{
|
||||
echo "0. blank-at-eol " &&
|
||||
echo "1. still-blank-at-eol " &&
|
||||
echo "2. and a new line "
|
||||
echo "2. and a new line " &&
|
||||
printf "3. and more"
|
||||
} >x &&
|
||||
new_hash_x=$(git hash-object x) &&
|
||||
after=$(git rev-parse --short "$new_hash_x") &&
|
||||
@@ -1050,11 +1098,13 @@ test_expect_success 'ws-error-highlight test setup' '
|
||||
<BOLD>index $before..$after 100644<RESET>
|
||||
<BOLD>--- a/x<RESET>
|
||||
<BOLD>+++ b/x<RESET>
|
||||
<CYAN>@@ -1,2 +1,3 @@<RESET>
|
||||
<CYAN>@@ -1,2 +1,4 @@<RESET>
|
||||
0. blank-at-eol <RESET>
|
||||
<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
|
||||
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
|
||||
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
|
||||
<GREEN>+<RESET><GREEN>3. and more<RESET>
|
||||
<BLUE>\ No newline at end of file<RESET>
|
||||
EOF
|
||||
|
||||
cat >expect.all <<-EOF &&
|
||||
@@ -1062,11 +1112,13 @@ test_expect_success 'ws-error-highlight test setup' '
|
||||
<BOLD>index $before..$after 100644<RESET>
|
||||
<BOLD>--- a/x<RESET>
|
||||
<BOLD>+++ b/x<RESET>
|
||||
<CYAN>@@ -1,2 +1,3 @@<RESET>
|
||||
<CYAN>@@ -1,2 +1,4 @@<RESET>
|
||||
<RESET>0. blank-at-eol<RESET><BLUE> <RESET>
|
||||
<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
|
||||
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
|
||||
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
|
||||
<GREEN>+<RESET><GREEN>3. and more<RESET>
|
||||
<BLUE>\ No newline at end of file<RESET>
|
||||
EOF
|
||||
|
||||
cat >expect.none <<-EOF
|
||||
@@ -1074,16 +1126,19 @@ test_expect_success 'ws-error-highlight test setup' '
|
||||
<BOLD>index $before..$after 100644<RESET>
|
||||
<BOLD>--- a/x<RESET>
|
||||
<BOLD>+++ b/x<RESET>
|
||||
<CYAN>@@ -1,2 +1,3 @@<RESET>
|
||||
<CYAN>@@ -1,2 +1,4 @@<RESET>
|
||||
0. blank-at-eol <RESET>
|
||||
<RED>-1. blank-at-eol <RESET>
|
||||
<GREEN>+1. still-blank-at-eol <RESET>
|
||||
<GREEN>+2. and a new line <RESET>
|
||||
<GREEN>+3. and more<RESET>
|
||||
\ No newline at end of file<RESET>
|
||||
EOF
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'test --ws-error-highlight option' '
|
||||
git config core.whitespace blank-at-eol,incomplete-line &&
|
||||
|
||||
git diff --color --ws-error-highlight=default,old >current.raw &&
|
||||
test_decode_color <current.raw >current &&
|
||||
@@ -1100,6 +1155,7 @@ test_expect_success 'test --ws-error-highlight option' '
|
||||
'
|
||||
|
||||
test_expect_success 'test diff.wsErrorHighlight config' '
|
||||
git config core.whitespace blank-at-eol,incomplete-line &&
|
||||
|
||||
git -c diff.wsErrorHighlight=default,old diff --color >current.raw &&
|
||||
test_decode_color <current.raw >current &&
|
||||
@@ -1116,6 +1172,7 @@ test_expect_success 'test diff.wsErrorHighlight config' '
|
||||
'
|
||||
|
||||
test_expect_success 'option overrides diff.wsErrorHighlight' '
|
||||
git config core.whitespace blank-at-eol,incomplete-line &&
|
||||
|
||||
git -c diff.wsErrorHighlight=none \
|
||||
diff --color --ws-error-highlight=default,old >current.raw &&
|
||||
@@ -1135,6 +1192,8 @@ test_expect_success 'option overrides diff.wsErrorHighlight' '
|
||||
'
|
||||
|
||||
test_expect_success 'detect moved code, complete file' '
|
||||
git config core.whitespace blank-at-eol &&
|
||||
|
||||
git reset --hard &&
|
||||
cat <<-\EOF >test.c &&
|
||||
#include<stdio.h>
|
||||
|
||||
@@ -556,4 +556,191 @@ test_expect_success 'whitespace check skipped for excluded paths' '
|
||||
git apply --include=used --stat --whitespace=error <patch
|
||||
'
|
||||
|
||||
test_expect_success 'check incomplete lines (setup)' '
|
||||
rm -f .gitattributes &&
|
||||
git config core.whitespace incomplete-line
|
||||
'
|
||||
|
||||
test_expect_success 'incomplete context line (not an error)' '
|
||||
(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
|
||||
(test_write_lines 1 2 3 0 5 && printf 6) >sample2-i &&
|
||||
cat sample-i >target &&
|
||||
git add target &&
|
||||
cat sample2-i >target &&
|
||||
git diff-files -p target >patch &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error <patch &&
|
||||
test_cmp sample2-i target &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error --check <patch 2>error &&
|
||||
test_cmp sample-i target &&
|
||||
test_must_be_empty error &&
|
||||
|
||||
cat sample2-i >target &&
|
||||
git apply --whitespace=error -R <patch &&
|
||||
test_cmp sample-i target &&
|
||||
|
||||
cat sample2-i >target &&
|
||||
git apply -R --whitespace=error --check <patch 2>error &&
|
||||
test_cmp sample2-i target &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
||||
test_expect_success 'last line made incomplete (error)' '
|
||||
test_write_lines 1 2 3 4 5 6 >sample &&
|
||||
(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
|
||||
cat sample >target &&
|
||||
git add target &&
|
||||
cat sample-i >target &&
|
||||
git diff-files -p target >patch &&
|
||||
|
||||
cat sample >target &&
|
||||
test_must_fail git apply --whitespace=error <patch 2>error &&
|
||||
test_grep "no newline" error &&
|
||||
|
||||
cat sample >target &&
|
||||
test_must_fail git apply --whitespace=error --check <patch 2>actual &&
|
||||
test_cmp sample target &&
|
||||
cat >expect <<-\EOF &&
|
||||
<stdin>:10: no newline at the end of file.
|
||||
6
|
||||
error: 1 line adds whitespace errors.
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error -R <patch &&
|
||||
test_cmp sample target &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error --check -R <patch 2>error &&
|
||||
test_cmp sample-i target &&
|
||||
test_must_be_empty error &&
|
||||
|
||||
cat sample >target &&
|
||||
git apply --whitespace=fix <patch &&
|
||||
test_cmp sample target
|
||||
'
|
||||
|
||||
test_expect_success 'incomplete line removed at the end (not an error)' '
|
||||
(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
|
||||
test_write_lines 1 2 3 4 5 6 >sample &&
|
||||
cat sample-i >target &&
|
||||
git add target &&
|
||||
cat sample >target &&
|
||||
git diff-files -p target >patch &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error <patch &&
|
||||
test_cmp sample target &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error --check <patch 2>error &&
|
||||
test_cmp sample-i target &&
|
||||
test_must_be_empty error &&
|
||||
|
||||
cat sample >target &&
|
||||
test_must_fail git apply --whitespace=error -R <patch 2>error &&
|
||||
test_grep "no newline" error &&
|
||||
|
||||
cat sample >target &&
|
||||
test_must_fail git apply --whitespace=error --check -R <patch 2>actual &&
|
||||
test_cmp sample target &&
|
||||
cat >expect <<-\EOF &&
|
||||
<stdin>:9: no newline at the end of file.
|
||||
6
|
||||
error: 1 line adds whitespace errors.
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
cat sample >target &&
|
||||
git apply --whitespace=fix -R <patch &&
|
||||
test_cmp sample target
|
||||
'
|
||||
|
||||
test_expect_success 'incomplete line corrected at the end (not an error)' '
|
||||
(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
|
||||
test_write_lines 1 2 3 4 5 7 >sample3 &&
|
||||
cat sample-i >target &&
|
||||
git add target &&
|
||||
cat sample3 >target &&
|
||||
git diff-files -p target >patch &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error <patch &&
|
||||
test_cmp sample3 target &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=error --check <patch 2>error &&
|
||||
test_cmp sample-i target &&
|
||||
test_must_be_empty error &&
|
||||
|
||||
cat sample3 >target &&
|
||||
test_must_fail git apply --whitespace=error -R <patch 2>error &&
|
||||
test_grep "no newline" error &&
|
||||
|
||||
cat sample3 >target &&
|
||||
test_must_fail git apply --whitespace=error -R --check <patch 2>actual &&
|
||||
test_cmp sample3 target &&
|
||||
cat >expect <<-\EOF &&
|
||||
<stdin>:9: no newline at the end of file.
|
||||
6
|
||||
error: 1 line adds whitespace errors.
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
cat sample3 >target &&
|
||||
git apply --whitespace=fix -R <patch &&
|
||||
test_cmp sample target
|
||||
'
|
||||
|
||||
test_expect_success 'incomplete line modified at the end (error)' '
|
||||
(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
|
||||
(test_write_lines 1 2 3 4 5 && printf 7) >sample3-i &&
|
||||
test_write_lines 1 2 3 4 5 6 >sample &&
|
||||
test_write_lines 1 2 3 4 5 7 >sample3 &&
|
||||
cat sample-i >target &&
|
||||
git add target &&
|
||||
cat sample3-i >target &&
|
||||
git diff-files -p target >patch &&
|
||||
|
||||
cat sample-i >target &&
|
||||
test_must_fail git apply --whitespace=error <patch 2>error &&
|
||||
test_grep "no newline" error &&
|
||||
|
||||
cat sample-i >target &&
|
||||
test_must_fail git apply --whitespace=error --check <patch 2>actual &&
|
||||
test_cmp sample-i target &&
|
||||
cat >expect <<-\EOF &&
|
||||
<stdin>:11: no newline at the end of file.
|
||||
7
|
||||
error: 1 line adds whitespace errors.
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
cat sample3-i >target &&
|
||||
test_must_fail git apply --whitespace=error -R <patch 2>error &&
|
||||
test_grep "no newline" error &&
|
||||
|
||||
cat sample3-i >target &&
|
||||
test_must_fail git apply --whitespace=error --check -R <patch 2>actual &&
|
||||
test_cmp sample3-i target &&
|
||||
cat >expect <<-\EOF &&
|
||||
<stdin>:9: no newline at the end of file.
|
||||
6
|
||||
error: 1 line adds whitespace errors.
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
cat sample-i >target &&
|
||||
git apply --whitespace=fix <patch &&
|
||||
test_cmp sample3 target &&
|
||||
|
||||
cat sample3-i >target &&
|
||||
git apply --whitespace=fix -R <patch &&
|
||||
test_cmp sample target
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
20
ws.c
20
ws.c
@@ -26,6 +26,7 @@ static struct whitespace_rule {
|
||||
{ "blank-at-eol", WS_BLANK_AT_EOL, 0 },
|
||||
{ "blank-at-eof", WS_BLANK_AT_EOF, 0 },
|
||||
{ "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 },
|
||||
{ "incomplete-line", WS_INCOMPLETE_LINE, 0, 0 },
|
||||
};
|
||||
|
||||
unsigned parse_whitespace_rule(const char *string)
|
||||
@@ -139,6 +140,11 @@ char *whitespace_error_string(unsigned ws)
|
||||
strbuf_addstr(&err, ", ");
|
||||
strbuf_addstr(&err, "tab in indent");
|
||||
}
|
||||
if (ws & WS_INCOMPLETE_LINE) {
|
||||
if (err.len)
|
||||
strbuf_addstr(&err, ", ");
|
||||
strbuf_addstr(&err, "no newline at the end of file");
|
||||
}
|
||||
return strbuf_detach(&err, NULL);
|
||||
}
|
||||
|
||||
@@ -180,6 +186,9 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
|
||||
if (trailing_whitespace == -1)
|
||||
trailing_whitespace = len;
|
||||
|
||||
if (!trailing_newline && (ws_rule & WS_INCOMPLETE_LINE))
|
||||
result |= WS_INCOMPLETE_LINE;
|
||||
|
||||
/* Check indentation */
|
||||
for (i = 0; i < trailing_whitespace; i++) {
|
||||
if (line[i] == ' ')
|
||||
@@ -291,6 +300,17 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
|
||||
int last_space_in_indent = -1;
|
||||
int need_fix_leading_space = 0;
|
||||
|
||||
/*
|
||||
* Remembering that we need to add '\n' at the end
|
||||
* is sufficient to fix an incomplete line.
|
||||
*/
|
||||
if (ws_rule & WS_INCOMPLETE_LINE) {
|
||||
if (0 < len && src[len - 1] != '\n') {
|
||||
fixed = 1;
|
||||
add_nl_to_tail = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Strip trailing whitespace
|
||||
*/
|
||||
|
||||
26
ws.h
26
ws.h
@@ -7,19 +7,23 @@ struct strbuf;
|
||||
/*
|
||||
* whitespace rules.
|
||||
* used by both diff and apply
|
||||
* last two digits are tab width
|
||||
* last two octal-digits are tab width (we support only up to 63).
|
||||
*/
|
||||
#define WS_BLANK_AT_EOL 0100
|
||||
#define WS_SPACE_BEFORE_TAB 0200
|
||||
#define WS_INDENT_WITH_NON_TAB 0400
|
||||
#define WS_CR_AT_EOL 01000
|
||||
#define WS_BLANK_AT_EOF 02000
|
||||
#define WS_TAB_IN_INDENT 04000
|
||||
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
|
||||
#define WS_BLANK_AT_EOL (1<<6)
|
||||
#define WS_SPACE_BEFORE_TAB (1<<7)
|
||||
#define WS_INDENT_WITH_NON_TAB (1<<8)
|
||||
#define WS_CR_AT_EOL (1<<9)
|
||||
#define WS_BLANK_AT_EOF (1<<10)
|
||||
#define WS_TAB_IN_INDENT (1<<11)
|
||||
#define WS_INCOMPLETE_LINE (1<<12)
|
||||
|
||||
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
|
||||
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
|
||||
#define WS_TAB_WIDTH_MASK 077
|
||||
/* All WS_* -- when extended, adapt diff.c emit_symbol */
|
||||
#define WS_RULE_MASK 07777
|
||||
#define WS_TAB_WIDTH_MASK ((1<<6)-1)
|
||||
|
||||
/* All WS_* -- when extended, adapt constants defined after diff.c:diff_symbol */
|
||||
#define WS_RULE_MASK ((1<<16)-1)
|
||||
|
||||
extern unsigned whitespace_rule_cfg;
|
||||
unsigned whitespace_rule(struct index_state *, const char *);
|
||||
unsigned parse_whitespace_rule(const char *);
|
||||
|
||||
Reference in New Issue
Block a user