Skip to content

Commit 2388a9e

Browse files
committed
diff_file: properly refcount blobs when initializing file contents
When initializing a `git_diff_file_content` from a source whose data is derived from a blob, we simply assign the blob's pointer to the resulting struct without incrementing its refcount. Thus, the structure can only be used as long as the blob is kept alive by the caller. Fix the issue by using `git_blob_dup` instead of a direct assignment. This function will increment the refcount of the blob without allocating new memory, so it does exactly what we want. As `git_diff_file_content__unload` already frees the blob when `GIT_DIFF_FLAG__FREE_BLOB` is set, we don't need to add new code handling the free but only have to set that flag correctly.
1 parent c342c13 commit 2388a9e

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

src/diff_file.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ int git_diff_file_content__init_from_src(
139139
memset(fc, 0, sizeof(*fc));
140140
fc->repo = repo;
141141
fc->file = as_file;
142-
fc->blob = src->blob;
143142

144143
if (!src->blob && !src->buf) {
145144
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
@@ -149,12 +148,15 @@ int git_diff_file_content__init_from_src(
149148
fc->file->mode = GIT_FILEMODE_BLOB;
150149

151150
if (src->blob) {
151+
git_blob_dup((git_blob **)&fc->blob, (git_blob *) src->blob);
152152
fc->file->size = git_blob_rawsize(src->blob);
153153
git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
154154
fc->file->id_abbrev = GIT_OID_HEXSZ;
155155

156156
fc->map.len = (size_t)fc->file->size;
157157
fc->map.data = (char *)git_blob_rawcontent(src->blob);
158+
159+
fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
158160
} else {
159161
fc->file->size = src->buflen;
160162
git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);

tests/diff/blob.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
#include "clar_libgit2.h"
22
#include "diff_helpers.h"
33

4+
#define BLOB_DIFF \
5+
"diff --git a/file b/file\n" \
6+
"index 45141a7..4d713dc 100644\n" \
7+
"--- a/file\n" \
8+
"+++ b/file\n" \
9+
"@@ -1 +1,6 @@\n" \
10+
" Hello from the root\n" \
11+
"+\n" \
12+
"+Some additional lines\n" \
13+
"+\n" \
14+
"+Down here below\n" \
15+
"+\n"
16+
417
static git_repository *g_repo = NULL;
518
static diff_expects expected;
619
static git_diff_options opts;
@@ -65,6 +78,32 @@ static void assert_one_modified(
6578
cl_assert_equal_i(dels, exp->line_dels);
6679
}
6780

81+
void test_diff_blob__patch_with_freed_blobs(void)
82+
{
83+
git_oid a_oid, b_oid;
84+
git_blob *a, *b;
85+
git_patch *p;
86+
git_buf buf = GIT_BUF_INIT;
87+
88+
/* tests/resources/attr/root_test1 */
89+
cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
90+
cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
91+
/* tests/resources/attr/root_test2 */
92+
cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
93+
cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
94+
95+
cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, NULL));
96+
97+
git_blob_free(a);
98+
git_blob_free(b);
99+
100+
cl_git_pass(git_patch_to_buf(&buf, p));
101+
cl_assert_equal_s(buf.ptr, BLOB_DIFF);
102+
103+
git_patch_free(p);
104+
git_buf_free(&buf);
105+
}
106+
68107
void test_diff_blob__can_compare_text_blobs(void)
69108
{
70109
git_blob *a, *b, *c;

0 commit comments

Comments
 (0)