mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 17:42:31 +00:00

The diff constructor functions had some confusing names, where the "old" side of the diff was coming after the "new" side. This reverses the order in the function name to make it less confusing. Specifically... * git_diff_index_to_tree becomes git_diff_tree_to_index * git_diff_workdir_to_index becomes git_diff_index_to_workdir * git_diff_workdir_to_tree becomes git_diff_tree_to_workdir
466 lines
12 KiB
C
466 lines
12 KiB
C
#include "clar_libgit2.h"
|
|
#include "diff_helpers.h"
|
|
|
|
void test_diff_diffiter__initialize(void)
|
|
{
|
|
}
|
|
|
|
void test_diff_diffiter__cleanup(void)
|
|
{
|
|
cl_git_sandbox_cleanup();
|
|
}
|
|
|
|
void test_diff_diffiter__create(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("attr");
|
|
git_diff_list *diff;
|
|
size_t d, num_d;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
for (d = 0; d < num_d; ++d) {
|
|
const git_diff_delta *delta;
|
|
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
|
}
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
void test_diff_diffiter__iterate_files(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("attr");
|
|
git_diff_list *diff;
|
|
size_t d, num_d;
|
|
int count = 0;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
cl_assert_equal_i(6, (int)num_d);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
const git_diff_delta *delta;
|
|
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
|
cl_assert(delta != NULL);
|
|
count++;
|
|
}
|
|
cl_assert_equal_i(6, count);
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
void test_diff_diffiter__iterate_files_2(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_list *diff;
|
|
size_t d, num_d;
|
|
int count = 0;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
cl_assert_equal_i(8, (int)num_d);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
const git_diff_delta *delta;
|
|
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
|
cl_assert(delta != NULL);
|
|
count++;
|
|
}
|
|
cl_assert_equal_i(8, count);
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
void test_diff_diffiter__iterate_files_and_hunks(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
git_diff_list *diff = NULL;
|
|
size_t d, num_d;
|
|
int file_count = 0, hunk_count = 0;
|
|
|
|
opts.context_lines = 3;
|
|
opts.interhunk_lines = 1;
|
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
git_diff_patch *patch;
|
|
const git_diff_delta *delta;
|
|
size_t h, num_h;
|
|
|
|
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
|
|
|
cl_assert(delta);
|
|
cl_assert(patch);
|
|
|
|
file_count++;
|
|
|
|
num_h = git_diff_patch_num_hunks(patch);
|
|
|
|
for (h = 0; h < num_h; h++) {
|
|
const git_diff_range *range;
|
|
const char *header;
|
|
size_t header_len, num_l;
|
|
|
|
cl_git_pass(git_diff_patch_get_hunk(
|
|
&range, &header, &header_len, &num_l, patch, h));
|
|
|
|
cl_assert(range);
|
|
cl_assert(header);
|
|
|
|
hunk_count++;
|
|
}
|
|
|
|
git_diff_patch_free(patch);
|
|
}
|
|
|
|
cl_assert_equal_i(13, file_count);
|
|
cl_assert_equal_i(8, hunk_count);
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
void test_diff_diffiter__max_size_threshold(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
git_diff_list *diff = NULL;
|
|
int file_count = 0, binary_count = 0, hunk_count = 0;
|
|
size_t d, num_d;
|
|
|
|
opts.context_lines = 3;
|
|
opts.interhunk_lines = 1;
|
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
num_d = git_diff_num_deltas(diff);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
git_diff_patch *patch;
|
|
const git_diff_delta *delta;
|
|
|
|
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
|
cl_assert(delta);
|
|
cl_assert(patch);
|
|
|
|
file_count++;
|
|
hunk_count += (int)git_diff_patch_num_hunks(patch);
|
|
|
|
assert(delta->binary == 0 || delta->binary == 1);
|
|
binary_count += delta->binary;
|
|
|
|
git_diff_patch_free(patch);
|
|
}
|
|
|
|
cl_assert_equal_i(13, file_count);
|
|
cl_assert_equal_i(0, binary_count);
|
|
cl_assert_equal_i(8, hunk_count);
|
|
|
|
git_diff_list_free(diff);
|
|
|
|
/* try again with low file size threshold */
|
|
|
|
file_count = binary_count = hunk_count = 0;
|
|
|
|
opts.context_lines = 3;
|
|
opts.interhunk_lines = 1;
|
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
opts.max_size = 50; /* treat anything over 50 bytes as binary! */
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
num_d = git_diff_num_deltas(diff);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
git_diff_patch *patch;
|
|
const git_diff_delta *delta;
|
|
|
|
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
|
|
|
file_count++;
|
|
hunk_count += (int)git_diff_patch_num_hunks(patch);
|
|
|
|
assert(delta->binary == 0 || delta->binary == 1);
|
|
binary_count += delta->binary;
|
|
|
|
git_diff_patch_free(patch);
|
|
}
|
|
|
|
cl_assert_equal_i(13, file_count);
|
|
/* Three files are over the 50 byte threshold:
|
|
* - staged_changes_file_deleted
|
|
* - staged_changes_modified_file
|
|
* - staged_new_file_modified_file
|
|
*/
|
|
cl_assert_equal_i(3, binary_count);
|
|
cl_assert_equal_i(5, hunk_count);
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
|
|
void test_diff_diffiter__iterate_all(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
git_diff_list *diff = NULL;
|
|
diff_expects exp = {0};
|
|
size_t d, num_d;
|
|
|
|
opts.context_lines = 3;
|
|
opts.interhunk_lines = 1;
|
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
for (d = 0; d < num_d; ++d) {
|
|
git_diff_patch *patch;
|
|
const git_diff_delta *delta;
|
|
size_t h, num_h;
|
|
|
|
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
|
cl_assert(patch && delta);
|
|
exp.files++;
|
|
|
|
num_h = git_diff_patch_num_hunks(patch);
|
|
for (h = 0; h < num_h; h++) {
|
|
const git_diff_range *range;
|
|
const char *header;
|
|
size_t header_len, l, num_l;
|
|
|
|
cl_git_pass(git_diff_patch_get_hunk(
|
|
&range, &header, &header_len, &num_l, patch, h));
|
|
cl_assert(range && header);
|
|
exp.hunks++;
|
|
|
|
for (l = 0; l < num_l; ++l) {
|
|
char origin;
|
|
const char *content;
|
|
size_t content_len;
|
|
|
|
cl_git_pass(git_diff_patch_get_line_in_hunk(
|
|
&origin, &content, &content_len, NULL, NULL, patch, h, l));
|
|
cl_assert(content);
|
|
exp.lines++;
|
|
}
|
|
}
|
|
|
|
git_diff_patch_free(patch);
|
|
}
|
|
|
|
cl_assert_equal_i(13, exp.files);
|
|
cl_assert_equal_i(8, exp.hunks);
|
|
cl_assert_equal_i(14, exp.lines);
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
static void iterate_over_patch(git_diff_patch *patch, diff_expects *exp)
|
|
{
|
|
size_t h, num_h = git_diff_patch_num_hunks(patch), num_l;
|
|
|
|
exp->files++;
|
|
exp->hunks += (int)num_h;
|
|
|
|
/* let's iterate in reverse, just because we can! */
|
|
for (h = 1, num_l = 0; h <= num_h; ++h)
|
|
num_l += git_diff_patch_num_lines_in_hunk(patch, num_h - h);
|
|
|
|
exp->lines += (int)num_l;
|
|
}
|
|
|
|
#define PATCH_CACHE 5
|
|
|
|
void test_diff_diffiter__iterate_randomly_while_saving_state(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
git_diff_list *diff = NULL;
|
|
diff_expects exp = {0};
|
|
git_diff_patch *patches[PATCH_CACHE];
|
|
size_t p, d, num_d;
|
|
|
|
memset(patches, 0, sizeof(patches));
|
|
|
|
opts.context_lines = 3;
|
|
opts.interhunk_lines = 1;
|
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
|
|
/* To make sure that references counts work for diff and patch objects,
|
|
* this generates patches and randomly caches them. Only when the patch
|
|
* is removed from the cache are hunks and lines counted. At the end,
|
|
* there are still patches in the cache, so free the diff and try to
|
|
* process remaining patches after the diff is freed.
|
|
*/
|
|
|
|
srand(121212);
|
|
p = rand() % PATCH_CACHE;
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
/* take old patch */
|
|
git_diff_patch *patch = patches[p];
|
|
patches[p] = NULL;
|
|
|
|
/* cache new patch */
|
|
cl_git_pass(git_diff_get_patch(&patches[p], NULL, diff, d));
|
|
cl_assert(patches[p] != NULL);
|
|
|
|
/* process old patch if non-NULL */
|
|
if (patch != NULL) {
|
|
iterate_over_patch(patch, &exp);
|
|
git_diff_patch_free(patch);
|
|
}
|
|
|
|
p = rand() % PATCH_CACHE;
|
|
}
|
|
|
|
/* free diff list now - refcounts should keep things safe */
|
|
git_diff_list_free(diff);
|
|
|
|
/* process remaining unprocessed patches */
|
|
for (p = 0; p < PATCH_CACHE; p++) {
|
|
git_diff_patch *patch = patches[p];
|
|
|
|
if (patch != NULL) {
|
|
iterate_over_patch(patch, &exp);
|
|
git_diff_patch_free(patch);
|
|
}
|
|
}
|
|
|
|
/* hopefully it all still added up right */
|
|
cl_assert_equal_i(13, exp.files);
|
|
cl_assert_equal_i(8, exp.hunks);
|
|
cl_assert_equal_i(14, exp.lines);
|
|
}
|
|
|
|
/* This output is taken directly from `git diff` on the status test data */
|
|
static const char *expected_patch_text[8] = {
|
|
/* 0 */
|
|
"diff --git a/file_deleted b/file_deleted\n"
|
|
"deleted file mode 100644\n"
|
|
"index 5452d32..0000000\n"
|
|
"--- a/file_deleted\n"
|
|
"+++ /dev/null\n"
|
|
"@@ -1 +0,0 @@\n"
|
|
"-file_deleted\n",
|
|
/* 1 */
|
|
"diff --git a/modified_file b/modified_file\n"
|
|
"index 452e424..0a53963 100644\n"
|
|
"--- a/modified_file\n"
|
|
"+++ b/modified_file\n"
|
|
"@@ -1 +1,2 @@\n"
|
|
" modified_file\n"
|
|
"+modified_file\n",
|
|
/* 2 */
|
|
"diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
|
|
"deleted file mode 100644\n"
|
|
"index a6be623..0000000\n"
|
|
"--- a/staged_changes_file_deleted\n"
|
|
"+++ /dev/null\n"
|
|
"@@ -1,2 +0,0 @@\n"
|
|
"-staged_changes_file_deleted\n"
|
|
"-staged_changes_file_deleted\n",
|
|
/* 3 */
|
|
"diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
|
|
"index 906ee77..011c344 100644\n"
|
|
"--- a/staged_changes_modified_file\n"
|
|
"+++ b/staged_changes_modified_file\n"
|
|
"@@ -1,2 +1,3 @@\n"
|
|
" staged_changes_modified_file\n"
|
|
" staged_changes_modified_file\n"
|
|
"+staged_changes_modified_file\n",
|
|
/* 4 */
|
|
"diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
|
|
"deleted file mode 100644\n"
|
|
"index 90b8c29..0000000\n"
|
|
"--- a/staged_new_file_deleted_file\n"
|
|
"+++ /dev/null\n"
|
|
"@@ -1 +0,0 @@\n"
|
|
"-staged_new_file_deleted_file\n",
|
|
/* 5 */
|
|
"diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
|
|
"index ed06290..8b090c0 100644\n"
|
|
"--- a/staged_new_file_modified_file\n"
|
|
"+++ b/staged_new_file_modified_file\n"
|
|
"@@ -1 +1,2 @@\n"
|
|
" staged_new_file_modified_file\n"
|
|
"+staged_new_file_modified_file\n",
|
|
/* 6 */
|
|
"diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
|
|
"deleted file mode 100644\n"
|
|
"index 1888c80..0000000\n"
|
|
"--- a/subdir/deleted_file\n"
|
|
"+++ /dev/null\n"
|
|
"@@ -1 +0,0 @@\n"
|
|
"-subdir/deleted_file\n",
|
|
/* 7 */
|
|
"diff --git a/subdir/modified_file b/subdir/modified_file\n"
|
|
"index a619198..57274b7 100644\n"
|
|
"--- a/subdir/modified_file\n"
|
|
"+++ b/subdir/modified_file\n"
|
|
"@@ -1 +1,2 @@\n"
|
|
" subdir/modified_file\n"
|
|
"+subdir/modified_file\n"
|
|
};
|
|
|
|
void test_diff_diffiter__iterate_and_generate_patch_text(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_list *diff;
|
|
size_t d, num_d;
|
|
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
|
|
|
num_d = git_diff_num_deltas(diff);
|
|
cl_assert_equal_i(8, (int)num_d);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
git_diff_patch *patch;
|
|
char *text;
|
|
|
|
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
|
|
cl_assert(patch != NULL);
|
|
|
|
cl_git_pass(git_diff_patch_to_str(&text, patch));
|
|
|
|
cl_assert_equal_s(expected_patch_text[d], text);
|
|
|
|
git__free(text);
|
|
git_diff_patch_free(patch);
|
|
}
|
|
|
|
git_diff_list_free(diff);
|
|
}
|
|
|
|
void test_diff_diffiter__checks_options_version(void)
|
|
{
|
|
git_repository *repo = cl_git_sandbox_init("status");
|
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
git_diff_list *diff = NULL;
|
|
const git_error *err;
|
|
|
|
opts.version = 0;
|
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
|
|
cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
err = giterr_last();
|
|
cl_assert_equal_i(GITERR_INVALID, err->klass);
|
|
|
|
giterr_clear();
|
|
opts.version = 1024;
|
|
cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
|
|
err = giterr_last();
|
|
cl_assert_equal_i(GITERR_INVALID, err->klass);
|
|
}
|
|
|