mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 08:48:25 +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