Merge branch 'jc/maint-blank-at-eof' into maint

* jc/maint-blank-at-eof:
  diff -B: colour whitespace errors
  diff.c: emit_add_line() takes only the rest of the line
  diff.c: split emit_line() from the first char and the rest of the line
  diff.c: shuffling code around
  diff --whitespace: fix blank lines at end
  core.whitespace: split trailing-space into blank-at-{eol,eof}
  diff --color: color blank-at-eof
  diff --whitespace=warn/error: fix blank-at-eof check
  diff --whitespace=warn/error: obey blank-at-eof
  diff.c: the builtin_diff() deals with only two-file comparison
  apply --whitespace: warn blank but not necessarily empty lines at EOF
  apply --whitespace=warn/error: diagnose blank at EOF
  apply.c: split check_whitespace() into two
  apply --whitespace=fix: detect new blank lines at eof correctly
  apply --whitespace=fix: fix handling of blank lines at the eof
This commit is contained in:
Junio C Hamano 2009-11-15 23:06:34 -08:00
commit 6dbdba00ea
8 changed files with 437 additions and 173 deletions

View File

@ -416,13 +416,17 @@ core.whitespace::
consider them as errors. You can prefix `-` to disable
any of them (e.g. `-trailing-space`):
+
* `trailing-space` treats trailing whitespaces at the end of the line
* `blank-at-eol` treats trailing whitespaces at the end of the line
as an error (enabled by default).
* `space-before-tab` treats a space character that appears immediately
before a tab character in the initial indent part of the line as an
error (enabled by default).
* `indent-with-non-tab` treats a line that is indented with 8 or more
space characters as an error (not enabled by default).
* `blank-at-eof` treats blank lines added at the end of file as an error
(enabled by default).
* `trailing-space` is a short-hand to cover both `blank-at-eol` and
`blank-at-eof`.
* `cr-at-eol` treats a carriage-return at the end of line as
part of the line terminator, i.e. with it, `trailing-space`
does not trigger if the character before such a carriage-return

View File

@ -153,6 +153,7 @@ struct fragment {
const char *patch;
int size;
int rejected;
int linenr;
struct fragment *next;
};
@ -1227,23 +1228,29 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
return -1;
}
static void check_whitespace(const char *line, int len, unsigned ws_rule)
static void record_ws_error(unsigned result, const char *line, int len, int linenr)
{
char *err;
unsigned result = ws_check(line + 1, len - 1, ws_rule);
if (!result)
return;
whitespace_error++;
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error)
;
else {
err = whitespace_error_string(result);
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
patch_input_file, linenr, err, len - 2, line + 1);
free(err);
}
return;
err = whitespace_error_string(result);
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
patch_input_file, linenr, err, len, line);
free(err);
}
static void check_whitespace(const char *line, int len, unsigned ws_rule)
{
unsigned result = ws_check(line + 1, len - 1, ws_rule);
record_ws_error(result, line + 1, len - 2, linenr);
}
/*
@ -1359,6 +1366,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
int len;
fragment = xcalloc(1, sizeof(*fragment));
fragment->linenr = linenr;
len = parse_fragment(line, size, patch, fragment);
if (len <= 0)
die("corrupt patch at line %d", linenr);
@ -2142,6 +2150,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
int len = linelen(patch, size);
int plen, added;
int added_blank_line = 0;
int is_blank_context = 0;
if (!len)
break;
@ -2174,8 +2183,12 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
*new++ = '\n';
add_line_info(&preimage, "\n", 1, LINE_COMMON);
add_line_info(&postimage, "\n", 1, LINE_COMMON);
is_blank_context = 1;
break;
case ' ':
if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
ws_blank_line(patch + 1, plen, ws_rule))
is_blank_context = 1;
case '-':
memcpy(old, patch + 1, plen);
add_line_info(&preimage, old, plen,
@ -2202,7 +2215,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
(first == '+' ? 0 : LINE_COMMON));
new += added;
if (first == '+' &&
added == 1 && new[-1] == '\n')
(ws_rule & WS_BLANK_AT_EOF) &&
ws_blank_line(patch + 1, plen, ws_rule))
added_blank_line = 1;
break;
case '@': case '\\':
@ -2215,6 +2229,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (added_blank_line)
new_blank_lines_at_end++;
else if (is_blank_context)
;
else
new_blank_lines_at_end = 0;
patch += len;
@ -2296,17 +2312,24 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (applied_pos >= 0) {
if (ws_error_action == correct_ws_error &&
new_blank_lines_at_end &&
postimage.nr + applied_pos == img->nr) {
if (new_blank_lines_at_end &&
preimage.nr + applied_pos == img->nr &&
(ws_rule & WS_BLANK_AT_EOF) &&
ws_error_action != nowarn_ws_error) {
record_ws_error(WS_BLANK_AT_EOF, "+", 1, frag->linenr);
if (ws_error_action == correct_ws_error) {
while (new_blank_lines_at_end--)
remove_last_line(&postimage);
}
/*
* If the patch application adds blank lines
* at the end, and if the patch applies at the
* end of the image, remove those added blank
* lines.
* We would want to prevent write_out_results()
* from taking place in apply_patch() that follows
* the callchain led us here, which is:
* apply_patch->check_patch_list->check_patch->
* apply_data->apply_fragments->apply_one_fragment
*/
while (new_blank_lines_at_end--)
remove_last_line(&postimage);
if (ws_error_action == die_on_ws_error)
apply = 0;
}
/*

View File

@ -986,10 +986,12 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
* whitespace rules.
* used by both diff and apply
*/
#define WS_TRAILING_SPACE 01
#define WS_BLANK_AT_EOL 01
#define WS_SPACE_BEFORE_TAB 02
#define WS_INDENT_WITH_NON_TAB 04
#define WS_CR_AT_EOL 010
#define WS_BLANK_AT_EOF 020
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *);

407
diff.c
View File

@ -174,6 +174,175 @@ static struct diff_tempfile {
char tmp_path[PATH_MAX];
} diff_temp[2];
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
struct emit_callback {
int color_diff;
unsigned ws_rule;
int blank_at_eof_in_preimage;
int blank_at_eof_in_postimage;
int lno_in_preimage;
int lno_in_postimage;
sane_truncate_fn truncate;
const char **label_path;
struct diff_words_data *diff_words;
int *found_changesp;
FILE *file;
};
static int count_lines(const char *data, int size)
{
int count, ch, completely_empty = 1, nl_just_seen = 0;
count = 0;
while (0 < size--) {
ch = *data++;
if (ch == '\n') {
count++;
nl_just_seen = 1;
completely_empty = 0;
}
else {
nl_just_seen = 0;
completely_empty = 0;
}
}
if (completely_empty)
return 0;
if (!nl_just_seen)
count++; /* no trailing newline */
return count;
}
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one)) {
mf->ptr = (char *)""; /* does not matter */
mf->size = 0;
return 0;
}
else if (diff_populate_filespec(one, 0))
return -1;
mf->ptr = one->data;
mf->size = one->size;
return 0;
}
static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
{
char *ptr = mf->ptr;
long size = mf->size;
int cnt = 0;
if (!size)
return cnt;
ptr += size - 1; /* pointing at the very end */
if (*ptr != '\n')
; /* incomplete line */
else
ptr--; /* skip the last LF */
while (mf->ptr < ptr) {
char *prev_eol;
for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
if (*prev_eol == '\n')
break;
if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
break;
cnt++;
ptr = prev_eol - 1;
}
return cnt;
}
static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
struct emit_callback *ecbdata)
{
int l1, l2, at;
unsigned ws_rule = ecbdata->ws_rule;
l1 = count_trailing_blank(mf1, ws_rule);
l2 = count_trailing_blank(mf2, ws_rule);
if (l2 <= l1) {
ecbdata->blank_at_eof_in_preimage = 0;
ecbdata->blank_at_eof_in_postimage = 0;
return;
}
at = count_lines(mf1->ptr, mf1->size);
ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
at = count_lines(mf2->ptr, mf2->size);
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
}
static void emit_line_0(FILE *file, const char *set, const char *reset,
int first, const char *line, int len)
{
int has_trailing_newline, has_trailing_carriage_return;
int nofirst;
if (len == 0) {
has_trailing_newline = (first == '\n');
has_trailing_carriage_return = (!has_trailing_newline &&
(first == '\r'));
nofirst = has_trailing_newline || has_trailing_carriage_return;
} else {
has_trailing_newline = (len > 0 && line[len-1] == '\n');
if (has_trailing_newline)
len--;
has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
if (has_trailing_carriage_return)
len--;
nofirst = 0;
}
fputs(set, file);
if (!nofirst)
fputc(first, file);
fwrite(line, len, 1, file);
fputs(reset, file);
if (has_trailing_carriage_return)
fputc('\r', file);
if (has_trailing_newline)
fputc('\n', file);
}
static void emit_line(FILE *file, const char *set, const char *reset,
const char *line, int len)
{
emit_line_0(file, set, reset, line[0], line+1, len-1);
}
static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
{
if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
ecbdata->blank_at_eof_in_preimage &&
ecbdata->blank_at_eof_in_postimage &&
ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
return 0;
return ws_blank_line(line, len, ecbdata->ws_rule);
}
static void emit_add_line(const char *reset,
struct emit_callback *ecbdata,
const char *line, int len)
{
const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
if (!*ws)
emit_line_0(ecbdata->file, set, reset, '+', line, len);
else if (new_blank_line_at_eof(ecbdata, line, len))
/* Blank line at EOF - paint '+' as well */
emit_line_0(ecbdata->file, ws, reset, '+', line, len);
else {
/* Emit just the prefix, then the rest. */
emit_line_0(ecbdata->file, set, reset, '+', "", 0);
ws_check_emit(line, len, ecbdata->ws_rule,
ecbdata->file, set, reset, ws);
}
}
static struct diff_tempfile *claim_diff_tempfile(void) {
int i;
for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
@ -201,29 +370,6 @@ static void remove_tempfile_on_signal(int signo)
raise(signo);
}
static int count_lines(const char *data, int size)
{
int count, ch, completely_empty = 1, nl_just_seen = 0;
count = 0;
while (0 < size--) {
ch = *data++;
if (ch == '\n') {
count++;
nl_just_seen = 1;
completely_empty = 0;
}
else {
nl_just_seen = 0;
completely_empty = 0;
}
}
if (completely_empty)
return 0;
if (!nl_just_seen)
count++; /* no trailing newline */
return count;
}
static void print_line_count(FILE *file, int count)
{
switch (count) {
@ -239,26 +385,36 @@ static void print_line_count(FILE *file, int count)
}
}
static void copy_file_with_prefix(FILE *file,
int prefix, const char *data, int size,
const char *set, const char *reset)
static void emit_rewrite_lines(struct emit_callback *ecb,
int prefix, const char *data, int size)
{
int ch, nl_just_seen = 1;
while (0 < size--) {
ch = *data++;
if (nl_just_seen) {
fputs(set, file);
putc(prefix, file);
const char *endp = NULL;
static const char *nneof = " No newline at end of file\n";
const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
while (0 < size) {
int len;
endp = memchr(data, '\n', size);
len = endp ? (endp - data + 1) : size;
if (prefix != '+') {
ecb->lno_in_preimage++;
emit_line_0(ecb->file, old, reset, '-',
data, len);
} else {
ecb->lno_in_postimage++;
emit_add_line(reset, ecb, data, len);
}
if (ch == '\n') {
nl_just_seen = 1;
fputs(reset, file);
} else
nl_just_seen = 0;
putc(ch, file);
size -= len;
data += len;
}
if (!endp) {
const char *plain = diff_get_color(ecb->color_diff,
DIFF_PLAIN);
emit_line_0(ecb->file, plain, reset, '\\',
nneof, strlen(nneof));
}
if (!nl_just_seen)
fprintf(file, "%s\n\\ No newline at end of file\n", reset);
}
static void emit_rewrite_diff(const char *name_a,
@ -274,13 +430,12 @@ static void emit_rewrite_diff(const char *name_a,
const char *name_a_tab, *name_b_tab;
const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO);
const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO);
const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
const char *reset = diff_get_color(color_diff, DIFF_RESET);
static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
const char *a_prefix, *b_prefix;
const char *data_one, *data_two;
size_t size_one, size_two;
struct emit_callback ecbdata;
if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
a_prefix = o->b_prefix;
@ -321,6 +476,22 @@ static void emit_rewrite_diff(const char *name_a,
size_two = two->size;
}
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.color_diff = color_diff;
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
ecbdata.file = o->file;
if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
mmfile_t mf1, mf2;
mf1.ptr = (char *)data_one;
mf2.ptr = (char *)data_two;
mf1.size = size_one;
mf2.size = size_two;
check_blank_at_eof(&mf1, &mf2, &ecbdata);
}
ecbdata.lno_in_preimage = 1;
ecbdata.lno_in_postimage = 1;
lc_a = count_lines(data_one, size_one);
lc_b = count_lines(data_two, size_two);
fprintf(o->file,
@ -332,24 +503,9 @@ static void emit_rewrite_diff(const char *name_a,
print_line_count(o->file, lc_b);
fprintf(o->file, " @@%s\n", reset);
if (lc_a)
copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset);
emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
if (lc_b)
copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset);
}
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one)) {
mf->ptr = (char *)""; /* does not matter */
mf->size = 0;
return 0;
}
else if (diff_populate_filespec(one, 0))
return -1;
mf->ptr = one->data;
mf->size = one->size;
return 0;
emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
}
struct diff_words_buffer {
@ -529,18 +685,6 @@ static void diff_words_show(struct diff_words_data *diff_words)
diff_words->minus.text.size = diff_words->plus.text.size = 0;
}
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
struct emit_callback {
int nparents, color_diff;
unsigned ws_rule;
sane_truncate_fn truncate;
const char **label_path;
struct diff_words_data *diff_words;
int *found_changesp;
FILE *file;
};
static void free_diff_words_data(struct emit_callback *ecbdata)
{
if (ecbdata->diff_words) {
@ -566,42 +710,6 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
return "";
}
static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
{
int has_trailing_newline, has_trailing_carriage_return;
has_trailing_newline = (len > 0 && line[len-1] == '\n');
if (has_trailing_newline)
len--;
has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
if (has_trailing_carriage_return)
len--;
fputs(set, file);
fwrite(line, len, 1, file);
fputs(reset, file);
if (has_trailing_carriage_return)
fputc('\r', file);
if (has_trailing_newline)
fputc('\n', file);
}
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
{
const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
if (!*ws)
emit_line(ecbdata->file, set, reset, line, len);
else {
/* Emit just the prefix, then the rest. */
emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
ws_check_emit(line + ecbdata->nparents,
len - ecbdata->nparents, ecbdata->ws_rule,
ecbdata->file, set, reset, ws);
}
}
static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
{
const char *cp;
@ -620,10 +728,23 @@ static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, u
return allot - l;
}
static void find_lno(const char *line, struct emit_callback *ecbdata)
{
const char *p;
ecbdata->lno_in_preimage = 0;
ecbdata->lno_in_postimage = 0;
p = strchr(line, '-');
if (!p)
return; /* cannot happen */
ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
p = strchr(p, '+');
if (!p)
return; /* cannot happen */
ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
}
static void fn_out_consume(void *priv, char *line, unsigned long len)
{
int i;
int color;
struct emit_callback *ecbdata = priv;
const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
@ -650,14 +771,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
len = 1;
}
/* This is not really necessary for now because
* this codepath only deals with two-way diffs.
*/
for (i = 0; i < len && line[i] == '@'; i++)
;
if (2 <= i && i < len && line[i] == ' ') {
ecbdata->nparents = i - 1;
if (line[0] == '@') {
len = sane_truncate_line(ecbdata, line, len);
find_lno(line, ecbdata);
emit_line(ecbdata->file,
diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
reset, line, len);
@ -666,15 +782,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
return;
}
if (len < ecbdata->nparents) {
if (len < 1) {
emit_line(ecbdata->file, reset, reset, line, len);
return;
}
color = DIFF_PLAIN;
if (ecbdata->diff_words && ecbdata->nparents != 1)
/* fall back to normal diff */
free_diff_words_data(ecbdata);
if (ecbdata->diff_words) {
if (line[0] == '-') {
diff_words_append(line, len,
@ -693,20 +805,19 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
emit_line(ecbdata->file, plain, reset, line, len);
return;
}
for (i = 0; i < ecbdata->nparents && len; i++) {
if (line[i] == '-')
color = DIFF_FILE_OLD;
else if (line[i] == '+')
color = DIFF_FILE_NEW;
}
if (color != DIFF_FILE_NEW) {
emit_line(ecbdata->file,
diff_get_color(ecbdata->color_diff, color),
reset, line, len);
return;
if (line[0] != '+') {
const char *color =
diff_get_color(ecbdata->color_diff,
line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
ecbdata->lno_in_preimage++;
if (line[0] == ' ')
ecbdata->lno_in_postimage++;
emit_line(ecbdata->file, color, reset, line, len);
} else {
ecbdata->lno_in_postimage++;
emit_add_line(reset, ecbdata, line + 1, len - 1);
}
emit_add_line(reset, ecbdata, line, len);
}
static char *pprint_rename(const char *a, const char *b)
@ -1211,7 +1322,6 @@ struct checkdiff_t {
struct diff_options *o;
unsigned ws_rule;
unsigned status;
int trailing_blanks_start;
};
static int is_conflict_marker(const char *line, unsigned long len)
@ -1255,10 +1365,6 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
if (line[0] == '+') {
unsigned bad;
data->lineno++;
if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
data->trailing_blanks_start = 0;
else if (!data->trailing_blanks_start)
data->trailing_blanks_start = data->lineno;
if (is_conflict_marker(line + 1, len - 1)) {
data->status |= 1;
fprintf(data->o->file,
@ -1278,14 +1384,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
data->o->file, set, reset, ws);
} else if (line[0] == ' ') {
data->lineno++;
data->trailing_blanks_start = 0;
} else if (line[0] == '@') {
char *plus = strchr(line, '+');
if (plus)
data->lineno = strtol(plus, NULL, 10) - 1;
else
die("invalid diff");
data->trailing_blanks_start = 0;
}
}
@ -1562,6 +1666,8 @@ static void builtin_diff(const char *name_a,
ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
check_blank_at_eof(&mf1, &mf2, &ecbdata);
ecbdata.file = o->file;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
@ -1704,11 +1810,22 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
&xpp, &xecfg, &ecb);
if ((data.ws_rule & WS_TRAILING_SPACE) &&
data.trailing_blanks_start) {
fprintf(o->file, "%s:%d: ends with blank lines.\n",
data.filename, data.trailing_blanks_start);
data.status = 1; /* report errors */
if (data.ws_rule & WS_BLANK_AT_EOF) {
struct emit_callback ecbdata;
int blank_at_eof;
ecbdata.ws_rule = data.ws_rule;
check_blank_at_eof(&mf1, &mf2, &ecbdata);
blank_at_eof = ecbdata.blank_at_eof_in_preimage;
if (blank_at_eof) {
static char *err;
if (!err)
err = whitespace_error_string(WS_BLANK_AT_EOF);
fprintf(o->file, "%s:%d: %s.\n",
data.filename, blank_at_eof, err);
data.status = 1; /* report errors */
}
}
}
free_and_return:

View File

@ -362,10 +362,17 @@ test_expect_success 'line numbers in --check output are correct' '
'
test_expect_success 'checkdiff detects trailing blank lines' '
test_expect_success 'checkdiff detects new trailing blank lines (1)' '
echo "foo();" >x &&
echo "" >>x &&
git diff --check | grep "ends with blank"
git diff --check | grep "new blank line"
'
test_expect_success 'checkdiff detects new trailing blank lines (2)' '
{ echo a; echo b; echo; echo; } >x &&
git add x &&
{ echo a; echo; echo; echo; echo; } >x &&
git diff --check | grep "new blank line"
'
test_expect_success 'checkdiff allows new blank lines' '

View File

@ -165,7 +165,7 @@ test_expect_success 'trailing empty lines (1)' '
rm -f .gitattributes &&
test_must_fail git diff --check >output &&
grep "ends with blank lines." output &&
grep "new blank line at" output &&
grep "trailing whitespace" output
'
@ -190,4 +190,13 @@ test_expect_success 'do not color trailing cr in context' '
'
test_expect_success 'color new trailing blank lines' '
{ echo a; echo b; echo; echo; } >x &&
git add x &&
{ echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
git diff --color x >output &&
cnt=$(grep "${blue_grep}" output | wc -l) &&
test $cnt = 2
'
test_done

View File

@ -170,4 +170,95 @@ test_expect_success 'trailing whitespace & no newline at the end of file' '
grep "^$" target
'
test_expect_success 'blank at EOF with --whitespace=fix (1)' '
: these can fail depending on what we did before
git config --unset core.whitespace
rm -f .gitattributes
{ echo a; echo b; echo c; } >one &&
git add one &&
{ echo a; echo b; echo c; } >expect &&
{ cat expect; echo; } >one &&
git diff -- one >patch &&
git checkout one &&
git apply --whitespace=fix patch &&
test_cmp expect one
'
test_expect_success 'blank at EOF with --whitespace=fix (2)' '
{ echo a; echo b; echo c; } >one &&
git add one &&
{ echo a; echo c; } >expect &&
{ cat expect; echo; echo; } >one &&
git diff -- one >patch &&
git checkout one &&
git apply --whitespace=fix patch &&
test_cmp expect one
'
test_expect_success 'blank at EOF with --whitespace=fix (3)' '
{ echo a; echo b; echo; } >one &&
git add one &&
{ echo a; echo c; echo; } >expect &&
{ cat expect; echo; echo; } >one &&
git diff -- one >patch &&
git checkout one &&
git apply --whitespace=fix patch &&
test_cmp expect one
'
test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
{ echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
git add one &&
{ echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
cp expect one &&
git diff -- one >patch &&
git checkout one &&
git apply --whitespace=fix patch &&
test_cmp expect one
'
test_expect_success 'blank at EOF with --whitespace=warn' '
{ echo a; echo b; echo c; } >one &&
git add one &&
echo >>one &&
cat one >expect &&
git diff -- one >patch &&
git checkout one &&
git apply --whitespace=warn patch 2>error &&
test_cmp expect one &&
grep "new blank line at EOF" error
'
test_expect_success 'blank at EOF with --whitespace=error' '
{ echo a; echo b; echo c; } >one &&
git add one &&
cat one >expect &&
echo >>one &&
git diff -- one >patch &&
git checkout one &&
test_must_fail git apply --whitespace=error patch 2>error &&
test_cmp expect one &&
grep "new blank line at EOF" error
'
test_expect_success 'blank but not empty at EOF' '
{ echo a; echo b; echo c; } >one &&
git add one &&
echo " " >>one &&
cat one >expect &&
git diff -- one >patch &&
git checkout one &&
git apply --whitespace=warn patch 2>error &&
test_cmp expect one &&
grep "new blank line at EOF" error
'
test_done

19
ws.c
View File

@ -16,6 +16,8 @@ static struct whitespace_rule {
{ "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
{ "indent-with-non-tab", WS_INDENT_WITH_NON_TAB, 0 },
{ "cr-at-eol", WS_CR_AT_EOL, 1 },
{ "blank-at-eol", WS_BLANK_AT_EOL, 0 },
{ "blank-at-eof", WS_BLANK_AT_EOF, 0 },
};
unsigned parse_whitespace_rule(const char *string)
@ -102,8 +104,17 @@ unsigned whitespace_rule(const char *pathname)
char *whitespace_error_string(unsigned ws)
{
struct strbuf err = STRBUF_INIT;
if (ws & WS_TRAILING_SPACE)
if ((ws & WS_TRAILING_SPACE) == WS_TRAILING_SPACE)
strbuf_addstr(&err, "trailing whitespace");
else {
if (ws & WS_BLANK_AT_EOL)
strbuf_addstr(&err, "trailing whitespace");
if (ws & WS_BLANK_AT_EOF) {
if (err.len)
strbuf_addstr(&err, ", ");
strbuf_addstr(&err, "new blank line at EOF");
}
}
if (ws & WS_SPACE_BEFORE_TAB) {
if (err.len)
strbuf_addstr(&err, ", ");
@ -141,11 +152,11 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
}
/* Check for trailing whitespace. */
if (ws_rule & WS_TRAILING_SPACE) {
if (ws_rule & WS_BLANK_AT_EOL) {
for (i = len - 1; i >= 0; i--) {
if (isspace(line[i])) {
trailing_whitespace = i;
result |= WS_TRAILING_SPACE;
result |= WS_BLANK_AT_EOL;
}
else
break;
@ -261,7 +272,7 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
/*
* Strip trailing whitespace
*/
if (ws_rule & WS_TRAILING_SPACE) {
if (ws_rule & WS_BLANK_AT_EOL) {
if (0 < len && src[len - 1] == '\n') {
add_nl_to_tail = 1;
len--;