mirror of
https://git.proxmox.com/git/libgit2
synced 2025-07-09 06:23:16 +00:00
Merge pull request #1738 from libgit2/diff-patch-content-size
Add API for getting at git_diff_patch->content_size
This commit is contained in:
commit
5e96f31638
@ -391,7 +391,7 @@ typedef enum {
|
||||
*/
|
||||
GIT_DIFF_LINE_FILE_HDR = 'F',
|
||||
GIT_DIFF_LINE_HUNK_HDR = 'H',
|
||||
GIT_DIFF_LINE_BINARY = 'B'
|
||||
GIT_DIFF_LINE_BINARY = 'B' /**< For "Binary files x and y differ" */
|
||||
} git_diff_line_t;
|
||||
|
||||
/**
|
||||
@ -942,6 +942,28 @@ 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
|
||||
* @param include_hunk_headers Include hunk header lines if non-zero
|
||||
* @param include_file_headers Include file header lines 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,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers);
|
||||
|
||||
/**
|
||||
* Serialize the patch to text via callback.
|
||||
*
|
||||
|
@ -82,6 +82,13 @@ extern const char *git_diff_delta__path(const git_diff_delta *delta);
|
||||
extern bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta);
|
||||
|
||||
extern int git_diff_delta__format_file_header(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
int oid_strlen);
|
||||
|
||||
extern int git_diff__oid_for_file(
|
||||
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
|
||||
|
||||
|
@ -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, header_size;
|
||||
git_pool flattened;
|
||||
};
|
||||
|
||||
@ -810,6 +810,39 @@ notfound:
|
||||
return diff_error_outofrange(thing);
|
||||
}
|
||||
|
||||
size_t git_diff_patch_size(
|
||||
git_diff_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers)
|
||||
{
|
||||
size_t out;
|
||||
|
||||
assert(patch);
|
||||
|
||||
out = patch->content_size;
|
||||
|
||||
if (!include_context)
|
||||
out -= patch->context_size;
|
||||
|
||||
if (include_hunk_headers)
|
||||
out += patch->header_size;
|
||||
|
||||
if (include_file_headers) {
|
||||
git_buf file_header = GIT_BUF_INIT;
|
||||
|
||||
if (git_diff_delta__format_file_header(
|
||||
&file_header, patch->delta, NULL, NULL, 0) < 0)
|
||||
giterr_clear();
|
||||
else
|
||||
out += git_buf_len(&file_header);
|
||||
|
||||
git_buf_free(&file_header);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
|
||||
{
|
||||
return patch->diff;
|
||||
@ -904,6 +937,8 @@ static int diff_patch_hunk_cb(
|
||||
hunk->header[header_len] = '\0';
|
||||
hunk->header_len = header_len;
|
||||
|
||||
patch->header_size += header_len;
|
||||
|
||||
hunk->line_start = git_array_size(patch->lines);
|
||||
hunk->line_count = 0;
|
||||
|
||||
@ -924,6 +959,7 @@ static int diff_patch_line_cb(
|
||||
git_diff_patch *patch = payload;
|
||||
diff_patch_hunk *hunk;
|
||||
diff_patch_line *line;
|
||||
const char *content_end = content + content_len;
|
||||
|
||||
GIT_UNUSED(delta);
|
||||
GIT_UNUSED(range);
|
||||
@ -938,34 +974,43 @@ static int diff_patch_line_cb(
|
||||
line->len = content_len;
|
||||
line->origin = line_origin;
|
||||
|
||||
patch->content_size += content_len;
|
||||
|
||||
/* do some bookkeeping so we can provide old/new line numbers */
|
||||
|
||||
for (line->lines = 0; content_len > 0; --content_len) {
|
||||
line->lines = 0;
|
||||
while (content < content_end)
|
||||
if (*content++ == '\n')
|
||||
++line->lines;
|
||||
}
|
||||
|
||||
patch->content_size += content_len;
|
||||
|
||||
switch (line_origin) {
|
||||
case GIT_DIFF_LINE_ADDITION:
|
||||
patch->content_size += 1;
|
||||
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||
line->oldno = -1;
|
||||
line->newno = patch->newno;
|
||||
patch->newno += line->lines;
|
||||
break;
|
||||
case GIT_DIFF_LINE_DELETION:
|
||||
patch->content_size += 1;
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
line->oldno = patch->oldno;
|
||||
line->newno = -1;
|
||||
patch->oldno += line->lines;
|
||||
break;
|
||||
default:
|
||||
case GIT_DIFF_LINE_CONTEXT:
|
||||
patch->content_size += 1;
|
||||
patch->context_size += 1;
|
||||
case GIT_DIFF_LINE_CONTEXT_EOFNL:
|
||||
patch->context_size += content_len;
|
||||
line->oldno = patch->oldno;
|
||||
line->newno = patch->newno;
|
||||
patch->oldno += line->lines;
|
||||
patch->newno += line->lines;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
hunk->line_count++;
|
||||
|
110
src/diff_print.c
110
src/diff_print.c
@ -98,12 +98,12 @@ static int diff_print_one_compact(
|
||||
|
||||
if (delta->old_file.path != delta->new_file.path &&
|
||||
strcomp(delta->old_file.path,delta->new_file.path) != 0)
|
||||
git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
|
||||
git_buf_printf(out, "%c\t%s%c %s%c\n", code,
|
||||
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
|
||||
else if (delta->old_file.mode != delta->new_file.mode &&
|
||||
delta->old_file.mode != 0 && delta->new_file.mode != 0)
|
||||
git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code,
|
||||
delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
|
||||
git_buf_printf(out, "%c\t%s%c %s%c\n", code,
|
||||
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
|
||||
else if (old_suffix != ' ')
|
||||
git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
|
||||
else
|
||||
@ -198,13 +198,13 @@ int git_diff_print_raw(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
||||
static int diff_print_oid_range(
|
||||
git_buf *out, const git_diff_delta *delta, int oid_strlen)
|
||||
{
|
||||
git_buf *out = pi->buf;
|
||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||
|
||||
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
|
||||
git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
|
||||
git_oid_tostr(start_oid, oid_strlen, &delta->old_file.oid);
|
||||
git_oid_tostr(end_oid, oid_strlen, &delta->new_file.oid);
|
||||
|
||||
/* TODO: Match git diff more closely */
|
||||
if (delta->old_file.mode == delta->new_file.mode) {
|
||||
@ -228,14 +228,65 @@ static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_delta_format_with_paths(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
const char *template)
|
||||
{
|
||||
const char *oldpath = delta->old_file.path;
|
||||
const char *newpath = delta->new_file.path;
|
||||
|
||||
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||
oldpfx = "";
|
||||
oldpath = "/dev/null";
|
||||
}
|
||||
if (git_oid_iszero(&delta->new_file.oid)) {
|
||||
newpfx = "";
|
||||
newpath = "/dev/null";
|
||||
}
|
||||
|
||||
return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
|
||||
}
|
||||
|
||||
int git_diff_delta__format_file_header(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
int oid_strlen)
|
||||
{
|
||||
if (!oldpfx)
|
||||
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
|
||||
if (!newpfx)
|
||||
newpfx = DIFF_NEW_PREFIX_DEFAULT;
|
||||
if (!oid_strlen)
|
||||
oid_strlen = GIT_ABBREV_DEFAULT + 1;
|
||||
|
||||
git_buf_clear(out);
|
||||
|
||||
git_buf_printf(out, "diff --git %s%s %s%s\n",
|
||||
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||
|
||||
if (diff_print_oid_range(out, delta, oid_strlen) < 0)
|
||||
return -1;
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
diff_delta_format_with_paths(
|
||||
out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
|
||||
|
||||
return git_buf_oom(out) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int diff_print_patch_file(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL;
|
||||
const char *oldpath = delta->old_file.path;
|
||||
const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL;
|
||||
const char *newpath = delta->new_file.path;
|
||||
const char *oldpfx =
|
||||
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
|
||||
const char *newpfx =
|
||||
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
@ -247,33 +298,8 @@ static int diff_print_patch_file(
|
||||
(opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
|
||||
return 0;
|
||||
|
||||
if (!oldpfx)
|
||||
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
|
||||
if (!newpfx)
|
||||
newpfx = DIFF_NEW_PREFIX_DEFAULT;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n",
|
||||
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||
|
||||
if (diff_print_oid_range(pi, delta) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||
oldpfx = "";
|
||||
oldpath = "/dev/null";
|
||||
}
|
||||
if (git_oid_iszero(&delta->new_file.oid)) {
|
||||
newpfx = "";
|
||||
newpath = "/dev/null";
|
||||
}
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
|
||||
git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
|
||||
git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
|
||||
}
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
if (git_diff_delta__format_file_header(
|
||||
pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
@ -284,10 +310,10 @@ static int diff_print_patch_file(
|
||||
return 0;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
git_buf_printf(
|
||||
pi->buf, "Binary files %s%s and %s%s differ\n",
|
||||
oldpfx, oldpath, newpfx, newpath);
|
||||
if (git_buf_oom(pi->buf))
|
||||
|
||||
if (diff_delta_format_with_paths(
|
||||
pi->buf, delta, oldpfx, newpfx,
|
||||
"Binary files %s%s and %s%s differ\n") < 0)
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
|
||||
|
@ -128,6 +128,11 @@ void test_diff_patch__to_string(void)
|
||||
|
||||
cl_assert_equal_s(expected, text);
|
||||
|
||||
cl_assert_equal_sz(31, git_diff_patch_size(patch, 0, 0, 0));
|
||||
cl_assert_equal_sz(31, git_diff_patch_size(patch, 1, 0, 0));
|
||||
cl_assert_equal_sz(31 + 16, git_diff_patch_size(patch, 1, 1, 0));
|
||||
cl_assert_equal_sz(strlen(expected), git_diff_patch_size(patch, 1, 1, 1));
|
||||
|
||||
git__free(text);
|
||||
git_diff_patch_free(patch);
|
||||
git_diff_list_free(diff);
|
||||
@ -408,7 +413,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 adds, size_t dels, size_t ctxt, size_t *sizes,
|
||||
const char *expected)
|
||||
{
|
||||
git_diff_list *diff;
|
||||
@ -437,6 +442,18 @@ static void check_single_patch_stats(
|
||||
cl_git_pass(git_diff_patch_to_str(&text, patch));
|
||||
cl_assert_equal_s(expected, text);
|
||||
git__free(text);
|
||||
|
||||
cl_assert_equal_sz(
|
||||
strlen(expected), git_diff_patch_size(patch, 1, 1, 1));
|
||||
}
|
||||
|
||||
if (sizes) {
|
||||
if (sizes[0])
|
||||
cl_assert_equal_sz(sizes[0], git_diff_patch_size(patch, 0, 0, 0));
|
||||
if (sizes[1])
|
||||
cl_assert_equal_sz(sizes[1], git_diff_patch_size(patch, 1, 0, 0));
|
||||
if (sizes[2])
|
||||
cl_assert_equal_sz(sizes[2], git_diff_patch_size(patch, 1, 1, 0));
|
||||
}
|
||||
|
||||
/* walk lines in hunk with basic sanity checks */
|
||||
@ -481,6 +498,23 @@ void test_diff_patch__line_counts_with_eofnl(void)
|
||||
git_buf content = GIT_BUF_INIT;
|
||||
const char *end;
|
||||
git_index *index;
|
||||
const char *expected =
|
||||
/* 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"
|
||||
"--- a/songof7cities.txt\n"
|
||||
"+++ b/songof7cities.txt\n"
|
||||
"@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
|
||||
" \n"
|
||||
" To the sound of trumpets shall their seed restore my Cities\n"
|
||||
" Wealthy and well-weaponed, that once more may I behold\n"
|
||||
"-All the world go softly when it walks before my Cities,\n"
|
||||
"+#All the world go softly when it walks before my Cities,\n"
|
||||
" And the horses and the chariots fleeing from them as of old!\n"
|
||||
" \n"
|
||||
" -- Rudyard Kipling\n"
|
||||
"\\ No newline at end of file\n";
|
||||
size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 };
|
||||
|
||||
g_repo = cl_git_sandbox_init("renames");
|
||||
|
||||
@ -495,14 +529,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, NULL, 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, NULL, NULL);
|
||||
|
||||
/* add trailing whitespace */
|
||||
|
||||
@ -514,7 +548,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, NULL, NULL);
|
||||
|
||||
/* no trailing whitespace as context line */
|
||||
|
||||
@ -537,22 +571,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,
|
||||
/* 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"
|
||||
"--- a/songof7cities.txt\n"
|
||||
"+++ b/songof7cities.txt\n"
|
||||
"@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
|
||||
" \n"
|
||||
" To the sound of trumpets shall their seed restore my Cities\n"
|
||||
" Wealthy and well-weaponed, that once more may I behold\n"
|
||||
"-All the world go softly when it walks before my Cities,\n"
|
||||
"+#All the world go softly when it walks before my Cities,\n"
|
||||
" And the horses and the chariots fleeing from them as of old!\n"
|
||||
" \n"
|
||||
" -- Rudyard Kipling\n"
|
||||
"\\ No newline at end of file\n");
|
||||
g_repo, 1, 1, 1, 6, expected_sizes, expected);
|
||||
|
||||
git_buf_free(&content);
|
||||
git_config_free(cfg);
|
||||
|
Loading…
Reference in New Issue
Block a user