Commit ad5a909cfbb07541e3e2548313043cc3f3a0918a

Patrick Steinhardt 2017-03-14T09:39:37

patch_parse: fix parsing minimal trailing diff line In a diff, the shortest possible hunk with a modification (that is, no deletion) results from a file with only one line with a single character which is removed. Thus the following hunk @@ -1 +1 @@ -a + is the shortest valid hunk modifying a line. The function parsing the hunk body though assumes that there must always be at least 4 bytes present to make up a valid hunk, which is obviously wrong in this case. The absolute minimum number of bytes required for a modification is actually 2 bytes, that is the "+" and the following newline. Note: if there is no trailing newline, the assumption will not be offended as the diff will have a line "\ No trailing newline" at its end. This patch fixes the issue by lowering the amount of bytes required.

diff --git a/src/patch_parse.c b/src/patch_parse.c
index f527594..d993c03 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -562,8 +562,9 @@ static int parse_hunk_body(
 	int newlines = hunk->hunk.new_lines;
 
 	for (;
-		ctx->remain_len > 4 && (oldlines || newlines) &&
-		memcmp(ctx->line, "@@ -", 4) != 0;
+		ctx->remain_len > 1 &&
+		(oldlines || newlines) &&
+		(ctx->remain_len <= 4 || memcmp(ctx->line, "@@ -", 4) != 0);
 		parse_advance_line(ctx)) {
 
 		int origin;
diff --git a/tests/diff/parse.c b/tests/diff/parse.c
index b79b19e..3587059 100644
--- a/tests/diff/parse.c
+++ b/tests/diff/parse.c
@@ -225,3 +225,24 @@ void test_diff_parse__foreach_works_with_parsed_patch(void)
 
 	git_diff_free(diff);
 }
+
+void test_diff_parse__parsing_minimal_patch_succeeds(void)
+{
+	const char patch[] =
+	    "diff --git a/obj1 b/obj2\n"
+	    "index 1234567..7654321 10644\n"
+	    "--- a/obj1\n"
+	    "+++ b/obj2\n"
+	    "@@ -1 +1 @@\n"
+	    "-a\n"
+	    "+\n";
+	git_buf buf = GIT_BUF_INIT;
+	git_diff *diff;
+
+	cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
+	cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
+	cl_assert_equal_s(patch, buf.ptr);
+
+	git_diff_free(diff);
+	git_buf_free(&buf);
+}