Skip to content

Commit 6b82720

Browse files
committed
Merge branch 'jc/whitespace-incomplete-line' into jch
Both "git apply" and "git diff" learn a new whitespace error class, "incomplete-line". Comments? * 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: fix incorrect counting of line numbers diff: correct suppress_blank_empty hack diff: emit_line_ws_markup() if/else style fix whitespace: correct bit assignment comments
2 parents 45922e9 + 33c5ae4 commit 6b82720

File tree

9 files changed

+448
-87
lines changed

9 files changed

+448
-87
lines changed

.gitattributes

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
* whitespace=!indent,trail,space
2-
*.[ch] whitespace=indent,trail,space diff=cpp
3-
*.sh whitespace=indent,trail,space text eol=lf
2+
*.[ch] whitespace=indent,trail,space,incomplete diff=cpp
3+
*.sh whitespace=indent,trail,space,incomplete text eol=lf
44
*.perl text eol=lf diff=perl
55
*.pl text eof=lf diff=perl
66
*.pm text eol=lf diff=perl

Documentation/config/core.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,8 @@ core.whitespace::
629629
part of the line terminator, i.e. with it, `trailing-space`
630630
does not trigger if the character before such a carriage-return
631631
is not a whitespace (not enabled by default).
632+
* `incomplete-line` treats the last line of a file that is missing the
633+
newline at the end as an error (not enabled by default).
632634
* `tabwidth=<n>` tells how many character positions a tab occupies; this
633635
is relevant for `indent-with-non-tab` and when Git fixes `tab-in-indent`
634636
errors. The default tab width is 8. Allowed values are 1 to 63.

apply.c

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,14 @@ static void record_ws_error(struct apply_state *state,
16401640
state->squelch_whitespace_errors < state->whitespace_error)
16411641
return;
16421642

1643+
/*
1644+
* line[len] for an incomplete line points at the "\n" at the end
1645+
* of patch input line, so "%.*s" would drop the last letter on line;
1646+
* compensate for it.
1647+
*/
1648+
if (result & WS_INCOMPLETE_LINE)
1649+
len++;
1650+
16431651
err = whitespace_error_string(result);
16441652
if (state->apply_verbosity > verbosity_silent)
16451653
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)
16701678
}
16711679

16721680

1681+
/*
1682+
* Just saw a single line in a fragment. If it is a part of this hunk
1683+
* that is a context " ", an added "+", or a removed "-" line, it may
1684+
* be followed by "\\ No newline..." to signal that the last "\n" on
1685+
* this line needs to be dropped. Depending on locale settings when
1686+
* the patch was produced we don't know what this line would exactly
1687+
* say. The only thing we do know is that it begins with "\ ".
1688+
* Checking for 12 is just for sanity check; "\ No newline..." would
1689+
* be at least that long in any l10n.
1690+
*
1691+
* Return 0 if the line we saw is not followed by "\ No newline...",
1692+
* or length of that line. The caller will use it to skip over the
1693+
* "\ No newline..." line.
1694+
*/
1695+
static int adjust_incomplete(const char *line, int len,
1696+
unsigned long size)
1697+
{
1698+
int nextlen;
1699+
1700+
if (*line != '\n' && *line != ' ' && *line != '+' && *line != '-')
1701+
return 0;
1702+
if (size - len < 12 || memcmp(line + len, "\\ ", 2))
1703+
return 0;
1704+
nextlen = linelen(line + len, size - len);
1705+
if (nextlen < 12)
1706+
return 0;
1707+
return nextlen;
1708+
}
1709+
16731710
/*
16741711
* Parse a unified diff. Note that this really needs to parse each
16751712
* fragment separately, since the only way to know the difference
@@ -1684,6 +1721,7 @@ static int parse_fragment(struct apply_state *state,
16841721
{
16851722
int added, deleted;
16861723
int len = linelen(line, size), offset;
1724+
int skip_len = 0;
16871725
unsigned long oldlines, newlines;
16881726
unsigned long leading, trailing;
16891727

@@ -1710,6 +1748,22 @@ static int parse_fragment(struct apply_state *state,
17101748
len = linelen(line, size);
17111749
if (!len || line[len-1] != '\n')
17121750
return -1;
1751+
1752+
/*
1753+
* For an incomplete line, skip_len counts the bytes
1754+
* on "\\ No newline..." marker line that comes next
1755+
* to the current line.
1756+
*
1757+
* Reduce "len" to drop the newline at the end of
1758+
* line[], but add one to "skip_len", which will be
1759+
* added back to "len" for the next iteration, to
1760+
* compensate.
1761+
*/
1762+
skip_len = adjust_incomplete(line, len, size);
1763+
if (skip_len) {
1764+
len--;
1765+
skip_len++;
1766+
}
17131767
switch (*line) {
17141768
default:
17151769
return -1;
@@ -1745,19 +1799,12 @@ static int parse_fragment(struct apply_state *state,
17451799
newlines--;
17461800
trailing = 0;
17471801
break;
1802+
}
17481803

1749-
/*
1750-
* We allow "\ No newline at end of file". Depending
1751-
* on locale settings when the patch was produced we
1752-
* don't know what this line looks like. The only
1753-
* thing we do know is that it begins with "\ ".
1754-
* Checking for 12 is just for sanity check -- any
1755-
* l10n of "\ No newline..." is at least that long.
1756-
*/
1757-
case '\\':
1758-
if (len < 12 || memcmp(line, "\\ ", 2))
1759-
return -1;
1760-
break;
1804+
/* eat the "\\ No newline..." as well, if exists */
1805+
if (skip_len) {
1806+
len += skip_len;
1807+
state->linenr++;
17611808
}
17621809
}
17631810
if (oldlines || newlines)
@@ -1768,14 +1815,6 @@ static int parse_fragment(struct apply_state *state,
17681815
fragment->leading = leading;
17691816
fragment->trailing = trailing;
17701817

1771-
/*
1772-
* If a fragment ends with an incomplete line, we failed to include
1773-
* it in the above loop because we hit oldlines == newlines == 0
1774-
* before seeing it.
1775-
*/
1776-
if (12 < size && !memcmp(line, "\\ ", 2))
1777-
offset += linelen(line, size);
1778-
17791818
patch->lines_added += added;
17801819
patch->lines_deleted += deleted;
17811820

0 commit comments

Comments
 (0)