Commit f9e28026753f7b6c871a160ad584b2dc2639d30f

Etienne Samson 2018-06-18T20:37:18

patch_parse: populate line numbers while parsing diffs

diff --git a/src/patch_parse.c b/src/patch_parse.c
index 2fd6e97..f9dcecd 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -563,6 +563,8 @@ static int parse_hunk_body(
 		char c;
 		int origin;
 		int prefix = 1;
+		int old_lineno = hunk->hunk.old_start + (hunk->hunk.old_lines - oldlines);
+		int new_lineno = hunk->hunk.new_start + (hunk->hunk.new_lines - newlines);
 
 		if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') {
 			error = git_parse_err("invalid patch instruction at line %"PRIuZ,
@@ -586,11 +588,13 @@ static int parse_hunk_body(
 		case '-':
 			origin = GIT_DIFF_LINE_DELETION;
 			oldlines--;
+			new_lineno = -1;
 			break;
 
 		case '+':
 			origin = GIT_DIFF_LINE_ADDITION;
 			newlines--;
+			old_lineno = -1;
 			break;
 
 		default:
@@ -607,6 +611,9 @@ static int parse_hunk_body(
 		line->content_len = ctx->parse_ctx.line_len - prefix;
 		line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
 		line->origin = origin;
+		line->num_lines = 1;
+		line->old_lineno = old_lineno;
+		line->new_lineno = new_lineno;
 
 		hunk->line_count++;
 	}
diff --git a/tests/diff/parse.c b/tests/diff/parse.c
index bb18196..9cdaa92 100644
--- a/tests/diff/parse.c
+++ b/tests/diff/parse.c
@@ -288,3 +288,74 @@ void test_diff_parse__patch_roundtrip_succeeds(void)
 	git_buf_dispose(&patchbuf);
 	git_buf_dispose(&diffbuf);
 }
+
+#define cl_assert_equal_i_src(i1,i2,file,line) clar__assert_equal(file,line,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+
+static void cl_git_assert_lineinfo_(int old_lineno, int new_lineno, int num_lines, git_patch *patch, size_t hunk_idx, size_t line_idx, const char *file, int lineno)
+{
+	const git_diff_line *line;
+
+	cl_git_expect(git_patch_get_line_in_hunk(&line, patch, hunk_idx, line_idx), 0, file, lineno);
+	cl_assert_equal_i_src(old_lineno, line->old_lineno, file, lineno);
+	cl_assert_equal_i_src(new_lineno, line->new_lineno, file, lineno);
+	cl_assert_equal_i_src(num_lines, line->num_lines, file, lineno);
+}
+
+#define cl_git_assert_lineinfo(old, new, num, p, h, l) \
+	cl_git_assert_lineinfo_(old,new,num,p,h,l,__FILE__,__LINE__)
+
+
+void test_diff_parse__issue4672(void)
+{
+	const char *text = "diff --git a/a b/a\n"
+	"index 7f129fd..af431f2 100644\n"
+	"--- a/a\n"
+	"+++ b/a\n"
+	"@@ -3 +3 @@\n"
+	"-a contents 2\n"
+	"+a contents\n";
+
+	git_diff *diff;
+	git_patch *patch;
+	const git_diff_hunk *hunk;
+	size_t n, l = 0;
+
+	cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
+
+	cl_git_assert_lineinfo(3, -1, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(-1, 3, 1, patch, 0, l++);
+
+	cl_assert_equal_i(n, l);
+
+	git_patch_free(patch);
+	git_diff_free(diff);
+}
+
+void test_diff_parse__lineinfo(void)
+{
+	const char *text = PATCH_ORIGINAL_TO_CHANGE_MIDDLE;
+	git_diff *diff;
+	git_patch *patch;
+	const git_diff_hunk *hunk;
+	size_t n, l = 0;
+
+	cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
+
+	cl_git_assert_lineinfo(3, 3, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(4, 4, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(5, 5, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(6, -1, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(-1, 6, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(7, 7, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(8, 8, 1, patch, 0, l++);
+	cl_git_assert_lineinfo(9, 9, 1, patch, 0, l++);
+
+	cl_assert_equal_i(n, l);
+
+	git_patch_free(patch);
+	git_diff_free(diff);
+}