Commit 3f855fe863e7260ea4b7bc36a0c78ea2bff37b68

Patrick Steinhardt 2019-07-05T11:06:33

patch_parse: handle missing newline indicator in old file When either the old or new file contents have no newline at the end of the file, then git-diff(1) will print out a "\ No newline at end of file" indicator. While we do correctly handle this in the case where the new file has this indcator, we fail to parse patches where the old file is missing a newline at EOF. Fix this bug by handling and missing newline indicators in the old file. Add tests to verify that we can parse such files.

diff --git a/src/patch_parse.c b/src/patch_parse.c
index b44d4f0..9f5bef5 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -578,6 +578,16 @@ static int parse_hunk_body(
 			old_lineno = -1;
 			break;
 
+		case '\\':
+			/*
+			 * If there are no oldlines left, then this is probably
+			 * the "\ No newline at end of file" marker. Do not
+			 * verify its format, as it may be localized.
+			 */
+			if (!oldlines)
+				continue;
+			/* fall through */
+
 		default:
 			error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
 			goto done;
@@ -606,7 +616,8 @@ static int parse_hunk_body(
 		goto done;
 	}
 
-	/* Handle "\ No newline at end of file".  Only expect the leading
+	/*
+	 * Handle "\ No newline at end of file".  Only expect the leading
 	 * backslash, though, because the rest of the string could be
 	 * localized.  Because `diff` optimizes for the case where you
 	 * want to apply the patch by hand.
diff --git a/tests/patch/parse.c b/tests/patch/parse.c
index a40ad7b..8c29e31 100644
--- a/tests/patch/parse.c
+++ b/tests/patch/parse.c
@@ -102,6 +102,20 @@ void test_patch_parse__invalid_patches_fails(void)
 		strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
 }
 
+void test_patch_parse__no_newline_at_end_of_new_file(void)
+{
+	git_patch *patch;
+	cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL, strlen(PATCH_APPEND_NO_NL), NULL));
+	git_patch_free(patch);
+}
+
+void test_patch_parse__no_newline_at_end_of_old_file(void)
+{
+	git_patch *patch;
+	cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL_IN_OLD_FILE, strlen(PATCH_APPEND_NO_NL_IN_OLD_FILE), NULL));
+	git_patch_free(patch);
+}
+
 void test_patch_parse__files_with_whitespaces_succeeds(void)
 {
 	git_patch *patch;
diff --git a/tests/patch/patch_common.h b/tests/patch/patch_common.h
index 291ece9..2db8d93 100644
--- a/tests/patch/patch_common.h
+++ b/tests/patch/patch_common.h
@@ -681,6 +681,16 @@
 	"+added line with no nl\n" \
 	"\\ No newline at end of file\n"
 
+#define PATCH_APPEND_NO_NL_IN_OLD_FILE \
+	"diff --git a/file.txt b/file.txt\n" \
+	"index 9432026..83759c0 100644\n" \
+	"--- a/file.txt\n" \
+	"+++ b/file.txt\n" \
+	"@@ -1,1 +1,1 @@\n" \
+	"-foo\n" \
+	"\\ No newline at end of file\n" \
+	"+foo\n"
+
 #define PATCH_NAME_WHITESPACE \
 	"diff --git a/file with spaces.txt b/file with spaces.txt\n" \
 	"index 9432026..83759c0 100644\n" \