Skip to content

Commit c66b547

Browse files
committed
apply: revamp the parsing of incomplete lines
A patch file represents the incomplete line at the end of the file with two lines, one that is the usual "context" with " " as the first letter, "added" with "+" as the first letter, or "removed" with "-" as the first letter that shows the content of the line, plus an extra "\ No newline at the end of file" line that comes immediately after it. Ever since the apply machinery was written, the "git apply" machinery parses "\ No newline at the end of file" line independently, without even knowing what line the incomplete-ness applies to, simply because it does not even remember what the previous line was. This poses a problem if we want to check and warn on an incomplete line. Revamp the code that parses a fragment, to actually drop the '\n' at the end of the incoming patch file that terminates a line, so that check_whitespace() calls made from the code path actually sees an incomplete as incomplete. Note that the result of this parsing is not directly used by the code path that applies the patch. apply_one_fragment() function already checks if each of the patch text it handles is followed by a line that begins with a backslash to drop the newline at the end of the current line it is looking at. In a sense, this patch harmonizes the behaviour of the parsing side to what is already done in the application side. Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent dfc810b commit c66b547

File tree

1 file changed

+49
-21
lines changed

1 file changed

+49
-21
lines changed

apply.c

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,35 @@ static void check_old_for_crlf(struct patch *patch, const char *line, int len)
16701670
}
16711671

16721672

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

@@ -1710,6 +1740,22 @@ static int parse_fragment(struct apply_state *state,
17101740
len = linelen(line, size);
17111741
if (!len || line[len-1] != '\n')
17121742
return -1;
1743+
1744+
/*
1745+
* For an incomplete line, skip_len counts the bytes
1746+
* on "\\ No newline..." marker line that comes next
1747+
* to the current line.
1748+
*
1749+
* Reduce "len" to drop the newline at the end of
1750+
* line[], but add one to "skip_len", which will be
1751+
* added back to "len" for the next iteration, to
1752+
* compensate.
1753+
*/
1754+
skip_len = adjust_incomplete(line, len, size);
1755+
if (skip_len) {
1756+
len--;
1757+
skip_len++;
1758+
}
17131759
switch (*line) {
17141760
default:
17151761
return -1;
@@ -1745,20 +1791,10 @@ static int parse_fragment(struct apply_state *state,
17451791
newlines--;
17461792
trailing = 0;
17471793
break;
1748-
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;
17611794
}
1795+
1796+
/* eat the "\\ No newline..." as well, if exists */
1797+
len += skip_len;
17621798
}
17631799
if (oldlines || newlines)
17641800
return -1;
@@ -1768,14 +1804,6 @@ static int parse_fragment(struct apply_state *state,
17681804
fragment->leading = leading;
17691805
fragment->trailing = trailing;
17701806

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-
17791807
patch->lines_added += added;
17801808
patch->lines_deleted += deleted;
17811809

0 commit comments

Comments
 (0)