diff --git a/include/git2/diff.h b/include/git2/diff.h index 71a8b72bf..711967501 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -942,6 +942,24 @@ GIT_EXTERN(int) git_diff_patch_get_line_in_hunk( size_t hunk_idx, size_t line_of_hunk); +/** + * Look up size of patch diff data in bytes + * + * This returns the raw size of the patch data. This only includes the + * actual data from the lines of the diff, not the file or hunk headers. + * + * If you pass `include_context` as true (non-zero), this will be the size + * of all of the diff output; if you pass it as false (zero), this will + * only include the actual changed lines (as if `context_lines` was 0). + * + * @param patch A git_diff_patch representing changes to one file + * @param include_context Include context lines in size if non-zero + * @return The number of bytes of data + */ +GIT_EXTERN(size_t) git_diff_patch_size( + git_diff_patch *patch, + int include_context); + /** * Serialize the patch to text via callback. * diff --git a/src/diff_patch.c b/src/diff_patch.c index 1b4adac03..5febc883c 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -42,7 +42,7 @@ struct git_diff_patch { git_array_t(diff_patch_hunk) hunks; git_array_t(diff_patch_line) lines; size_t oldno, newno; - size_t content_size; + size_t content_size, context_size; git_pool flattened; }; @@ -806,6 +806,20 @@ notfound: return diff_error_outofrange(thing); } +size_t git_diff_patch_size(git_diff_patch *patch, int include_context) +{ + size_t out; + + assert(patch); + + out = patch->content_size; + + if (!include_context) + out -= patch->context_size; + + return out; +} + git_diff_list *git_diff_patch__diff(git_diff_patch *patch) { return patch->diff; @@ -934,7 +948,11 @@ static int diff_patch_line_cb( line->len = content_len; line->origin = line_origin; - patch->content_size += content_len; + patch->content_size += content_len + 1; /* +1 for line_origin */ + + if (line_origin == GIT_DIFF_LINE_CONTEXT || + line_origin == GIT_DIFF_LINE_CONTEXT_EOFNL) + patch->context_size += content_len + 1; /* do some bookkeeping so we can provide old/new line numbers */ diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 3f14a0de7..51baadf2e 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -128,6 +128,9 @@ void test_diff_patch__to_string(void) cl_assert_equal_s(expected, text); + cl_assert_equal_sz(31, git_diff_patch_size(patch, 0)); + cl_assert_equal_sz(31, git_diff_patch_size(patch, 1)); + git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); @@ -409,6 +412,7 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) static void check_single_patch_stats( git_repository *repo, size_t hunks, size_t adds, size_t dels, size_t ctxt, + size_t size_with_context, size_t size_without_context, const char *expected) { git_diff_list *diff; @@ -439,6 +443,13 @@ static void check_single_patch_stats( git__free(text); } + if (size_with_context) + cl_assert_equal_sz( + size_with_context, git_diff_patch_size(patch, 1)); + if (size_without_context) + cl_assert_equal_sz( + size_without_context, git_diff_patch_size(patch, 0)); + /* walk lines in hunk with basic sanity checks */ for (; hunks > 0; --hunks) { size_t i, max_i; @@ -495,14 +506,14 @@ void test_diff_patch__line_counts_with_eofnl(void) git_buf_consume(&content, end); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL); + check_single_patch_stats(g_repo, 1, 0, 1, 3, 0, 0, NULL); /* remove trailing whitespace */ git_buf_rtrim(&content); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL); + check_single_patch_stats(g_repo, 2, 1, 2, 6, 0, 0, NULL); /* add trailing whitespace */ @@ -514,7 +525,7 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_pass(git_buf_putc(&content, '\n')); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL); + check_single_patch_stats(g_repo, 1, 1, 1, 3, 0, 0, NULL); /* no trailing whitespace as context line */ @@ -537,7 +548,7 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_rewritefile("renames/songof7cities.txt", content.ptr); check_single_patch_stats( - g_repo, 1, 1, 1, 6, + g_repo, 1, 1, 1, 6, 349, 115, /* below is pasted output of 'git diff' with fn context removed */ "diff --git a/songof7cities.txt b/songof7cities.txt\n" "index 378a7d9..3d0154e 100644\n"