Skip to content

Commit dedf70a

Browse files
committed
patch_parse: do not depend on parsed buffer's lifetime
When parsing a patch from a buffer, we let the patch lines point into the original buffer. While this is efficient use of resources, this also ties the lifetime of the parsed patch to the parsed buffer. As this behaviour is not documented anywhere in our API it is very surprising to its users. Untie the lifetime by duplicating the lines into the parsed patch. Add a test that verifies that lifetimes are indeed independent of each other.
1 parent c4c1500 commit dedf70a

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

src/patch_parse.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,8 +588,8 @@ static int parse_hunk_body(
588588

589589
memset(line, 0x0, sizeof(git_diff_line));
590590

591-
line->content = ctx->parse_ctx.line + prefix;
592591
line->content_len = ctx->parse_ctx.line_len - prefix;
592+
line->content = git__strndup(ctx->parse_ctx.line + prefix, line->content_len);
593593
line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
594594
line->origin = origin;
595595
line->num_lines = 1;
@@ -1038,6 +1038,8 @@ int git_patch_parsed_from_diff(git_patch **out, git_diff *d, size_t idx)
10381038
static void patch_parsed__free(git_patch *p)
10391039
{
10401040
git_patch_parsed *patch = (git_patch_parsed *)p;
1041+
git_diff_line *line;
1042+
size_t i;
10411043

10421044
if (!patch)
10431045
return;
@@ -1047,6 +1049,8 @@ static void patch_parsed__free(git_patch *p)
10471049
git__free((char *)patch->base.binary.old_file.data);
10481050
git__free((char *)patch->base.binary.new_file.data);
10491051
git_array_clear(patch->base.hunks);
1052+
git_array_foreach(patch->base.lines, i, line)
1053+
git__free((char *) line->content);
10501054
git_array_clear(patch->base.lines);
10511055
git__free(patch->base.delta);
10521056

tests/patch/parse.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,23 @@ void test_patch_parse__files_with_whitespaces_succeeds(void)
108108
cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL));
109109
git_patch_free(patch);
110110
}
111+
112+
void test_patch_parse__lifetime_of_patch_does_not_depend_on_buffer(void)
113+
{
114+
git_buf diff = GIT_BUF_INIT, rendered = GIT_BUF_INIT;
115+
git_patch *patch;
116+
117+
cl_git_pass(git_buf_sets(&diff, PATCH_ORIGINAL_TO_CHANGE_MIDDLE));
118+
cl_git_pass(git_patch_from_buffer(&patch, diff.ptr, diff.size, NULL));
119+
git_buf_dispose(&diff);
120+
121+
cl_git_pass(git_patch_to_buf(&rendered, patch));
122+
cl_assert_equal_s(PATCH_ORIGINAL_TO_CHANGE_MIDDLE, rendered.ptr);
123+
git_buf_dispose(&rendered);
124+
125+
cl_git_pass(git_patch_to_buf(&rendered, patch));
126+
cl_assert_equal_s(PATCH_ORIGINAL_TO_CHANGE_MIDDLE, rendered.ptr);
127+
git_buf_dispose(&rendered);
128+
129+
git_patch_free(patch);
130+
}

0 commit comments

Comments
 (0)