mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 21:34:15 +00:00
Add helper for diff line stats
This adds a `git_diff_patch_line_stats()` API that gets the total number of adds, deletes, and context lines in a patch. This will make it a little easier to emulate `git diff --stat` and the like. Right now, this relies on generating the `git_diff_patch` object, which is a pretty heavyweight way to get stat information. At some future point, it would probably be nice to be able to get this information without allocating the entire `git_diff_patch`, but that's a much larger project.
This commit is contained in:
parent
d204121657
commit
f1e2735c74
@ -703,6 +703,28 @@ GIT_EXTERN(const git_diff_delta *) git_diff_patch_delta(
|
||||
GIT_EXTERN(size_t) git_diff_patch_num_hunks(
|
||||
git_diff_patch *patch);
|
||||
|
||||
/**
|
||||
* Get line counts of each type in a patch.
|
||||
*
|
||||
* This helps imitate a diff --numstat type of output. For that purpose,
|
||||
* you only need the `total_additions` and `total_deletions` values, but we
|
||||
* include the `total_context` line count in case you want the total number
|
||||
* of lines of diff output that will be generated.
|
||||
*
|
||||
* All outputs are optional. Pass NULL if you don't need a particular count.
|
||||
*
|
||||
* @param total_context Count of context lines in output, can be NULL.
|
||||
* @param total_additions Count of addition lines in output, can be NULL.
|
||||
* @param total_deletions Count of deletion lines in output, can be NULL.
|
||||
* @param patch The git_diff_patch object
|
||||
* @return Number of lines in hunk or -1 if invalid hunk index
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_patch_line_stats(
|
||||
size_t *total_context,
|
||||
size_t *total_additions,
|
||||
size_t *total_deletions,
|
||||
const git_diff_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the information about a hunk in a patch
|
||||
*
|
||||
|
@ -134,6 +134,13 @@ GIT_INLINE(ssize_t) git_buf_rfind(git_buf *buf, char ch)
|
||||
return idx;
|
||||
}
|
||||
|
||||
GIT_INLINE(ssize_t) git_buf_find(git_buf *buf, char ch)
|
||||
{
|
||||
size_t idx = 0;
|
||||
while (idx < buf->size && buf->ptr[idx] != ch) idx++;
|
||||
return (idx == buf->size) ? -1 : (ssize_t)idx;
|
||||
}
|
||||
|
||||
/* Remove whitespace from the end of the buffer */
|
||||
void git_buf_rtrim(git_buf *buf);
|
||||
|
||||
|
@ -1501,6 +1501,39 @@ size_t git_diff_patch_num_hunks(git_diff_patch *patch)
|
||||
return patch->hunks_size;
|
||||
}
|
||||
|
||||
int git_diff_patch_line_stats(
|
||||
size_t *total_ctxt,
|
||||
size_t *total_adds,
|
||||
size_t *total_dels,
|
||||
const git_diff_patch *patch)
|
||||
{
|
||||
size_t totals[3], idx;
|
||||
|
||||
memset(totals, 0, sizeof(totals));
|
||||
|
||||
for (idx = 0; idx < patch->lines_size; ++idx) {
|
||||
switch (patch->lines[idx].origin) {
|
||||
case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
|
||||
case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
|
||||
case GIT_DIFF_LINE_DELETION: totals[2]++; break;
|
||||
default:
|
||||
/* diff --stat and --numstat don't count EOFNL marks because
|
||||
* they will always be paired with a ADDITION or DELETION line.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_ctxt)
|
||||
*total_ctxt = totals[0];
|
||||
if (total_adds)
|
||||
*total_adds = totals[1];
|
||||
if (total_dels)
|
||||
*total_dels = totals[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_diff_patch_get_hunk(
|
||||
const git_diff_range **range,
|
||||
const char **header,
|
||||
@ -1706,4 +1739,3 @@ int git_diff__paired_foreach(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -235,3 +235,68 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
|
||||
git_diff_list_free(diff);
|
||||
git_tree_free(head);
|
||||
}
|
||||
|
||||
static void check_single_patch_stats(
|
||||
git_repository *repo, size_t hunks, size_t adds, size_t dels)
|
||||
{
|
||||
git_diff_list *diff;
|
||||
git_diff_patch *patch;
|
||||
const git_diff_delta *delta;
|
||||
size_t actual_adds, actual_dels;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
||||
|
||||
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
|
||||
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
|
||||
cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
|
||||
|
||||
cl_assert_equal_i(hunks, (int)git_diff_patch_num_hunks(patch));
|
||||
|
||||
cl_git_pass(
|
||||
git_diff_patch_line_stats(NULL, &actual_adds, &actual_dels, patch));
|
||||
|
||||
cl_assert_equal_i(adds, actual_adds);
|
||||
cl_assert_equal_i(dels, actual_dels);
|
||||
|
||||
git_diff_patch_free(patch);
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
void test_diff_patch__line_counts_with_eofnl(void)
|
||||
{
|
||||
git_buf content = GIT_BUF_INIT;
|
||||
const char *end;
|
||||
git_index *index;
|
||||
|
||||
g_repo = cl_git_sandbox_init("renames");
|
||||
|
||||
cl_git_pass(git_futils_readbuffer(&content, "renames/songofseven.txt"));
|
||||
|
||||
/* remove first line */
|
||||
|
||||
end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
|
||||
git_buf_consume(&content, end);
|
||||
cl_git_rewritefile("renames/songofseven.txt", content.ptr);
|
||||
|
||||
check_single_patch_stats(g_repo, 1, 0, 1);
|
||||
|
||||
/* remove trailing whitespace */
|
||||
|
||||
git_buf_rtrim(&content);
|
||||
cl_git_rewritefile("renames/songofseven.txt", content.ptr);
|
||||
|
||||
check_single_patch_stats(g_repo, 2, 1, 2);
|
||||
|
||||
/* add trailing whitespace */
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
cl_git_pass(git_index_add_bypath(index, "songofseven.txt"));
|
||||
cl_git_pass(git_index_write(index));
|
||||
git_index_free(index);
|
||||
|
||||
cl_git_pass(git_buf_putc(&content, '\n'));
|
||||
cl_git_rewritefile("renames/songofseven.txt", content.ptr);
|
||||
|
||||
check_single_patch_stats(g_repo, 1, 1, 1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user