Initial commit - full huishou project

This commit is contained in:
jiapengyu
2026-07-27 14:07:26 +08:00
commit 60790ad1b3
39127 changed files with 5989265 additions and 0 deletions
+546
View File
@@ -0,0 +1,546 @@
#include "clar_libgit2.h"
#include "git2/sys/diff.h"
#include "buffer.h"
#include "delta.h"
#include "filebuf.h"
#include "repository.h"
static git_repository *repo;
void test_diff_binary__initialize(void)
{
}
void test_diff_binary__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_patch(
const char *one,
const char *two,
const git_diff_options *opts,
const char *expected)
{
git_oid id_one, id_two;
git_index *index = NULL;
git_commit *commit_one, *commit_two = NULL;
git_tree *tree_one, *tree_two;
git_diff *diff;
git_patch *patch;
git_buf actual = GIT_BUF_INIT;
cl_git_pass(git_oid_fromstr(&id_one, one));
cl_git_pass(git_commit_lookup(&commit_one, repo, &id_one));
cl_git_pass(git_commit_tree(&tree_one, commit_one));
if (two) {
cl_git_pass(git_oid_fromstr(&id_two, two));
cl_git_pass(git_commit_lookup(&commit_two, repo, &id_two));
cl_git_pass(git_commit_tree(&tree_two, commit_two));
} else {
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_write_tree(&id_two, index));
cl_git_pass(git_tree_lookup(&tree_two, repo, &id_two));
}
cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree_one, tree_two, opts));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected, actual.ptr);
git_buf_clear(&actual);
cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_buf, &actual));
cl_assert_equal_s(expected, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
git_tree_free(tree_one);
git_tree_free(tree_two);
git_commit_free(commit_one);
git_commit_free(commit_two);
git_index_free(index);
}
void test_diff_binary__add_normal(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/binary.bin b/binary.bin\n" \
"new file mode 100644\n" \
"index 0000000..bd474b2\n" \
"Binary files /dev/null and b/binary.bin differ\n";
repo = cl_git_sandbox_init("diff_format_email");
test_patch(
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
&opts,
expected);
}
void test_diff_binary__add(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/binary.bin b/binary.bin\n" \
"new file mode 100644\n" \
"index 0000000000000000000000000000000000000000..bd474b2519cc15eab801ff851cc7d50f0dee49a1\n" \
"GIT binary patch\n" \
"literal 3\n" \
"Kc${Nk-~s>u4FC%O\n"
"\n" \
"literal 0\n" \
"Hc$@<O00001\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("diff_format_email");
test_patch(
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
&opts,
expected);
}
void test_diff_binary__modify_normal(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/binary.bin b/binary.bin\n" \
"index bd474b2..9ac35ff 100644\n" \
"Binary files a/binary.bin and b/binary.bin differ\n";
repo = cl_git_sandbox_init("diff_format_email");
test_patch(
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
"8d7523f6fcb2404257889abe0d96f093d9f524f9",
&opts,
expected);
}
void test_diff_binary__modify(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/binary.bin b/binary.bin\n" \
"index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \
"GIT binary patch\n" \
"literal 5\n" \
"Mc${NkU}WL~000&M4gdfE\n" \
"\n" \
"literal 3\n" \
"Kc${Nk-~s>u4FC%O\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
repo = cl_git_sandbox_init("diff_format_email");
test_patch(
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
"8d7523f6fcb2404257889abe0d96f093d9f524f9",
&opts,
expected);
}
void test_diff_binary__delete_normal(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/binary.bin b/binary.bin\n" \
"deleted file mode 100644\n" \
"index bd474b2..0000000\n" \
"Binary files a/binary.bin and /dev/null differ\n";
repo = cl_git_sandbox_init("diff_format_email");
test_patch(
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
&opts,
expected);
}
void test_diff_binary__delete(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/binary.bin b/binary.bin\n" \
"deleted file mode 100644\n" \
"index bd474b2519cc15eab801ff851cc7d50f0dee49a1..0000000000000000000000000000000000000000\n" \
"GIT binary patch\n" \
"literal 0\n" \
"Hc$@<O00001\n" \
"\n" \
"literal 3\n" \
"Kc${Nk-~s>u4FC%O\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("diff_format_email");
test_patch(
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
&opts,
expected);
}
void test_diff_binary__delta(void)
{
git_index *index;
git_buf contents = GIT_BUF_INIT;
size_t i;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/songof7cities.txt b/songof7cities.txt\n" \
"index 4210ffd5c390b21dd5483375e75288dea9ede512..cc84ec183351c9944ed90a619ca08911924055b5 100644\n" \
"GIT binary patch\n" \
"delta 198\n" \
"zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pa)Ye#M3o+qJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \
"zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \
"JfH567LIG)KJdFSV\n" \
"\n" \
"delta 198\n" \
"zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \
"zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \
"JfH567LIF3FM2!Fd\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("renames");
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_futils_readbuffer(&contents, "renames/songof7cities.txt"));
for (i = 0; i < contents.size - 6; i++) {
if (strncmp(&contents.ptr[i], "Cities", 6) == 0)
memcpy(&contents.ptr[i], "cITIES", 6);
}
cl_git_rewritefile("renames/songof7cities.txt", contents.ptr);
cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
cl_git_pass(git_index_write(index));
test_patch(
"19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13",
NULL,
&opts,
expected);
git_index_free(index);
git_buf_dispose(&contents);
}
void test_diff_binary__delta_append(void)
{
git_index *index;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/untimely.txt b/untimely.txt\n" \
"index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \
"GIT binary patch\n" \
"delta 32\n" \
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
"Oc%18D`@*{63ljhg(E~C7\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("renames");
cl_git_pass(git_repository_index(&index, repo));
cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
cl_git_pass(git_index_write(index));
test_patch(
"19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13",
NULL,
&opts,
expected);
git_index_free(index);
}
void test_diff_binary__empty_for_no_diff(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_oid id;
git_commit *commit;
git_tree *tree;
git_diff *diff;
git_buf actual = GIT_BUF_INIT;
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("renames");
cl_git_pass(git_oid_fromstr(&id, "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"));
cl_git_pass(git_commit_lookup(&commit, repo, &id));
cl_git_pass(git_commit_tree(&tree, commit));
cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree, tree, &opts));
cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_buf, &actual));
cl_assert_equal_s("", actual.ptr);
git_buf_dispose(&actual);
git_diff_free(diff);
git_commit_free(commit);
git_tree_free(tree);
}
void test_diff_binary__index_to_workdir(void)
{
git_index *index;
git_diff *diff;
git_patch *patch;
git_buf actual = GIT_BUF_INIT;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/untimely.txt b/untimely.txt\n" \
"index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \
"GIT binary patch\n" \
"delta 32\n" \
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
"Oc%18D`@*{63ljhg(E~C7\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("renames");
cl_git_pass(git_repository_index(&index, repo));
cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, &opts));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected, actual.ptr);
cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
cl_git_pass(git_index_write(index));
test_patch(
"19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13",
NULL,
&opts,
expected);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
git_index_free(index);
}
static int print_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
git_buf *buf = (git_buf *)payload;
GIT_UNUSED(delta);
if (hunk)
git_buf_put(buf, hunk->header, hunk->header_len);
if (line)
git_buf_put(buf, line->content, line->content_len);
return git_buf_oom(buf) ? -1 : 0;
}
void test_diff_binary__print_patch_from_diff(void)
{
git_index *index;
git_diff *diff;
git_buf actual = GIT_BUF_INIT;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const char *expected =
"diff --git a/untimely.txt b/untimely.txt\n" \
"index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \
"GIT binary patch\n" \
"delta 32\n" \
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
"Oc%18D`@*{63ljhg(E~C7\n" \
"\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("renames");
cl_git_pass(git_repository_index(&index, repo));
cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, &opts));
cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, print_cb, &actual));
cl_assert_equal_s(expected, actual.ptr);
git_buf_dispose(&actual);
git_diff_free(diff);
git_index_free(index);
}
struct diff_data {
char *old_path;
git_oid old_id;
git_buf old_binary_base85;
size_t old_binary_inflatedlen;
git_diff_binary_t old_binary_type;
char *new_path;
git_oid new_id;
git_buf new_binary_base85;
size_t new_binary_inflatedlen;
git_diff_binary_t new_binary_type;
};
static int file_cb(
const git_diff_delta *delta,
float progress,
void *payload)
{
struct diff_data *diff_data = payload;
GIT_UNUSED(progress);
if (delta->old_file.path)
diff_data->old_path = git__strdup(delta->old_file.path);
if (delta->new_file.path)
diff_data->new_path = git__strdup(delta->new_file.path);
git_oid_cpy(&diff_data->old_id, &delta->old_file.id);
git_oid_cpy(&diff_data->new_id, &delta->new_file.id);
return 0;
}
static int binary_cb(
const git_diff_delta *delta,
const git_diff_binary *binary,
void *payload)
{
struct diff_data *diff_data = payload;
GIT_UNUSED(delta);
git_buf_encode_base85(&diff_data->old_binary_base85,
binary->old_file.data, binary->old_file.datalen);
diff_data->old_binary_inflatedlen = binary->old_file.inflatedlen;
diff_data->old_binary_type = binary->old_file.type;
git_buf_encode_base85(&diff_data->new_binary_base85,
binary->new_file.data, binary->new_file.datalen);
diff_data->new_binary_inflatedlen = binary->new_file.inflatedlen;
diff_data->new_binary_type = binary->new_file.type;
return 0;
}
static int hunk_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(delta);
GIT_UNUSED(hunk);
GIT_UNUSED(payload);
cl_fail("did not expect hunk callback");
return 0;
}
static int line_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
GIT_UNUSED(delta);
GIT_UNUSED(hunk);
GIT_UNUSED(line);
GIT_UNUSED(payload);
cl_fail("did not expect line callback");
return 0;
}
void test_diff_binary__blob_to_blob(void)
{
git_index *index;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_blob *old_blob, *new_blob;
git_oid old_id, new_id;
struct diff_data diff_data = {0};
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
repo = cl_git_sandbox_init("renames");
cl_git_pass(git_repository_index__weakptr(&index, repo));
cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
cl_git_pass(git_index_write(index));
git_oid_fromstr(&old_id, "9a69d960ae94b060f56c2a8702545e2bb1abb935");
git_oid_fromstr(&new_id, "1111d4f11f4b35bf6759e0fb714fe09731ef0840");
cl_git_pass(git_blob_lookup(&old_blob, repo, &old_id));
cl_git_pass(git_blob_lookup(&new_blob, repo, &new_id));
cl_git_pass(git_diff_blobs(old_blob,
"untimely.txt", new_blob, "untimely.txt", &opts,
file_cb, binary_cb, hunk_cb, line_cb, &diff_data));
cl_assert_equal_s("untimely.txt", diff_data.old_path);
cl_assert_equal_oid(&old_id, &diff_data.old_id);
cl_assert_equal_i(GIT_DIFF_BINARY_DELTA, diff_data.old_binary_type);
cl_assert_equal_i(7, diff_data.old_binary_inflatedlen);
cl_assert_equal_s("c%18D`@*{63ljhg(E~C7",
diff_data.old_binary_base85.ptr);
cl_assert_equal_s("untimely.txt", diff_data.new_path);
cl_assert_equal_oid(&new_id, &diff_data.new_id);
cl_assert_equal_i(GIT_DIFF_BINARY_DELTA, diff_data.new_binary_type);
cl_assert_equal_i(32, diff_data.new_binary_inflatedlen);
cl_assert_equal_s("c%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW",
diff_data.new_binary_base85.ptr);
git_blob_free(old_blob);
git_blob_free(new_blob);
git__free(diff_data.old_path);
git__free(diff_data.new_path);
git_buf_dispose(&diff_data.old_binary_base85);
git_buf_dispose(&diff_data.new_binary_base85);
}
File diff suppressed because it is too large Load Diff
+316
View File
@@ -0,0 +1,316 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
#include "git2/sys/diff.h"
git_tree *resolve_commit_oid_to_tree(
git_repository *repo,
const char *partial_oid)
{
size_t len = strlen(partial_oid);
git_oid oid;
git_object *obj = NULL;
git_tree *tree = NULL;
if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
cl_git_pass(git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJECT_ANY));
cl_git_pass(git_object_peel((git_object **) &tree, obj, GIT_OBJECT_TREE));
git_object_free(obj);
return tree;
}
static char diff_pick_suffix(int mode)
{
if (S_ISDIR(mode))
return '/';
else if (GIT_PERMS_IS_EXEC(mode))
return '*';
else
return ' ';
}
static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
{
char code = git_diff_status_char(delta->status);
char old_suffix = diff_pick_suffix(delta->old_file.mode);
char new_suffix = diff_pick_suffix(delta->new_file.mode);
fprintf(fp, "%c\t%s", code, delta->old_file.path);
if ((delta->old_file.path != delta->new_file.path &&
strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
(delta->old_file.mode != delta->new_file.mode &&
delta->old_file.mode != 0 && delta->new_file.mode != 0))
fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
else if (old_suffix != ' ')
fprintf(fp, "%c", old_suffix);
fprintf(fp, "\t[%.2f]\n", progress);
}
int diff_file_cb(
const git_diff_delta *delta,
float progress,
void *payload)
{
diff_expects *e = payload;
if (e->debug)
fprintf_delta(stderr, delta, progress);
if (e->names)
cl_assert_equal_s(e->names[e->files], delta->old_file.path);
if (e->statuses)
cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
e->files++;
if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
e->files_binary++;
cl_assert(delta->status <= GIT_DELTA_CONFLICTED);
e->file_status[delta->status] += 1;
return 0;
}
int diff_print_file_cb(
const git_diff_delta *delta,
float progress,
void *payload)
{
if (!payload) {
fprintf_delta(stderr, delta, progress);
return 0;
}
if (!((diff_expects *)payload)->debug)
fprintf_delta(stderr, delta, progress);
return diff_file_cb(delta, progress, payload);
}
int diff_binary_cb(
const git_diff_delta *delta,
const git_diff_binary *binary,
void *payload)
{
GIT_UNUSED(delta);
GIT_UNUSED(binary);
GIT_UNUSED(payload);
return 0;
}
int diff_hunk_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
void *payload)
{
diff_expects *e = payload;
const char *scan = hunk->header, *scan_end = scan + hunk->header_len;
GIT_UNUSED(delta);
/* confirm no NUL bytes in header text */
while (scan < scan_end)
cl_assert('\0' != *scan++);
e->hunks++;
e->hunk_old_lines += hunk->old_lines;
e->hunk_new_lines += hunk->new_lines;
return 0;
}
int diff_line_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
diff_expects *e = payload;
GIT_UNUSED(delta);
GIT_UNUSED(hunk);
e->lines++;
switch (line->origin) {
case GIT_DIFF_LINE_CONTEXT:
case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
e->line_ctxt++;
break;
case GIT_DIFF_LINE_ADDITION:
case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
e->line_adds++;
break;
case GIT_DIFF_LINE_DELETION:
case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
e->line_dels++;
break;
default:
break;
}
return 0;
}
int diff_foreach_via_iterator(
git_diff *diff,
git_diff_file_cb file_cb,
git_diff_binary_cb binary_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb line_cb,
void *data)
{
size_t d, num_d = git_diff_num_deltas(diff);
GIT_UNUSED(binary_cb);
for (d = 0; d < num_d; ++d) {
git_patch *patch;
const git_diff_delta *delta;
size_t h, num_h;
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
/* call file_cb for this file */
if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
git_patch_free(patch);
goto abort;
}
/* if there are no changes, then the patch will be NULL */
if (!patch) {
cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
(delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
continue;
}
if (!hunk_cb && !line_cb) {
git_patch_free(patch);
continue;
}
num_h = git_patch_num_hunks(patch);
for (h = 0; h < num_h; h++) {
const git_diff_hunk *hunk;
size_t l, num_l;
cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
if (hunk_cb && hunk_cb(delta, hunk, data) != 0) {
git_patch_free(patch);
goto abort;
}
for (l = 0; l < num_l; ++l) {
const git_diff_line *line;
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
if (line_cb &&
line_cb(delta, hunk, line, data) != 0) {
git_patch_free(patch);
goto abort;
}
}
}
git_patch_free(patch);
}
return 0;
abort:
git_error_clear();
return GIT_EUSER;
}
void diff_print(FILE *fp, git_diff *diff)
{
cl_git_pass(
git_diff_print(diff, GIT_DIFF_FORMAT_PATCH,
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
}
void diff_print_raw(FILE *fp, git_diff *diff)
{
cl_git_pass(
git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
}
static size_t num_modified_deltas(git_diff *diff)
{
const git_diff_delta *delta;
size_t i, cnt = 0;
for (i = 0; i < git_diff_num_deltas(diff); i++) {
delta = git_diff_get_delta(diff, i);
if (delta->status != GIT_DELTA_UNMODIFIED)
cnt++;
}
return cnt;
}
void diff_assert_equal(git_diff *a, git_diff *b)
{
const git_diff_delta *ad, *bd;
size_t i, j;
assert(a && b);
cl_assert_equal_i(num_modified_deltas(a), num_modified_deltas(b));
for (i = 0, j = 0;
i < git_diff_num_deltas(a) && j < git_diff_num_deltas(b); ) {
ad = git_diff_get_delta(a, i);
bd = git_diff_get_delta(b, j);
if (ad->status == GIT_DELTA_UNMODIFIED) {
i++;
continue;
}
if (bd->status == GIT_DELTA_UNMODIFIED) {
j++;
continue;
}
cl_assert_equal_i(ad->status, bd->status);
cl_assert_equal_i(ad->flags, bd->flags);
cl_assert_equal_i(ad->similarity, bd->similarity);
cl_assert_equal_i(ad->nfiles, bd->nfiles);
/* Don't examine the size or the flags of the deltas;
* computed deltas have sizes (parsed deltas do not) and
* computed deltas will have flags of `VALID_ID` and
* `EXISTS` (parsed deltas will not query the ODB.)
*/
/* an empty id indicates that it wasn't presented, because
* the diff was identical. (eg, pure rename, mode change only, etc)
*/
if (ad->old_file.id_abbrev && bd->old_file.id_abbrev) {
cl_assert_equal_i(ad->old_file.id_abbrev, bd->old_file.id_abbrev);
cl_assert_equal_oid(&ad->old_file.id, &bd->old_file.id);
cl_assert_equal_i(ad->old_file.mode, bd->old_file.mode);
}
cl_assert_equal_s(ad->old_file.path, bd->old_file.path);
if (ad->new_file.id_abbrev && bd->new_file.id_abbrev) {
cl_assert_equal_oid(&ad->new_file.id, &bd->new_file.id);
cl_assert_equal_i(ad->new_file.id_abbrev, bd->new_file.id_abbrev);
cl_assert_equal_i(ad->new_file.mode, bd->new_file.mode);
}
cl_assert_equal_s(ad->new_file.path, bd->new_file.path);
i++;
j++;
}
}
+73
View File
@@ -0,0 +1,73 @@
#include "futils.h"
#include "git2/diff.h"
extern git_tree *resolve_commit_oid_to_tree(
git_repository *repo, const char *partial_oid);
typedef struct {
int files;
int files_binary;
int file_status[11]; /* indexed by git_delta_t value */
int hunks;
int hunk_new_lines;
int hunk_old_lines;
int lines;
int line_ctxt;
int line_adds;
int line_dels;
/* optional arrays of expected specific values */
const char **names;
int *statuses;
int debug;
} diff_expects;
typedef struct {
const char *path;
const char *matched_pathspec;
} notify_expected;
extern int diff_file_cb(
const git_diff_delta *delta,
float progress,
void *cb_data);
extern int diff_print_file_cb(
const git_diff_delta *delta,
float progress,
void *cb_data);
extern int diff_binary_cb(
const git_diff_delta *delta,
const git_diff_binary *binary,
void *cb_data);
extern int diff_hunk_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
void *cb_data);
extern int diff_line_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *cb_data);
extern int diff_foreach_via_iterator(
git_diff *diff,
git_diff_file_cb file_cb,
git_diff_binary_cb binary_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb line_cb,
void *data);
extern void diff_print(FILE *fp, git_diff *diff);
extern void diff_print_raw(FILE *fp, git_diff *diff);
extern void diff_assert_equal(git_diff *a, git_diff *b);
+453
View File
@@ -0,0 +1,453 @@
#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 *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 = git_diff_get_delta(diff, d);
cl_assert(delta != NULL);
}
cl_assert(!git_diff_get_delta(diff, num_d));
git_diff_free(diff);
}
void test_diff_diffiter__iterate_files_1(void)
{
git_repository *repo = cl_git_sandbox_init("attr");
git_diff *diff;
size_t d, num_d;
diff_expects exp = { 0 };
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 = git_diff_get_delta(diff, d);
cl_assert(delta != NULL);
diff_file_cb(delta, (float)d / (float)num_d, &exp);
}
cl_assert_equal_sz(6, exp.files);
git_diff_free(diff);
}
void test_diff_diffiter__iterate_files_2(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_diff *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 = git_diff_get_delta(diff, d);
cl_assert(delta != NULL);
count++;
}
cl_assert_equal_i(8, count);
git_diff_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 *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_patch *patch;
size_t h, num_h;
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert(patch);
file_count++;
num_h = git_patch_num_hunks(patch);
for (h = 0; h < num_h; h++) {
const git_diff_hunk *hunk;
cl_git_pass(git_patch_get_hunk(&hunk, NULL, patch, h));
cl_assert(hunk);
hunk_count++;
}
git_patch_free(patch);
}
cl_assert_equal_i(13, file_count);
cl_assert_equal_i(8, hunk_count);
git_diff_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 *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_patch *patch;
const git_diff_delta *delta;
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert(patch);
delta = git_patch_get_delta(patch);
cl_assert(delta);
file_count++;
hunk_count += (int)git_patch_num_hunks(patch);
assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
git_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_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_patch *patch;
const git_diff_delta *delta;
cl_git_pass(git_patch_from_diff(&patch, diff, d));
delta = git_patch_get_delta(patch);
file_count++;
hunk_count += (int)git_patch_num_hunks(patch);
assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
git_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_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 *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_patch *patch;
size_t h, num_h;
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert(patch);
exp.files++;
num_h = git_patch_num_hunks(patch);
for (h = 0; h < num_h; h++) {
const git_diff_hunk *range;
size_t l, num_l;
cl_git_pass(git_patch_get_hunk(&range, &num_l, patch, h));
cl_assert(range);
exp.hunks++;
for (l = 0; l < num_l; ++l) {
const git_diff_line *line;
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
cl_assert(line && line->content);
exp.lines++;
}
}
git_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_free(diff);
}
static void iterate_over_patch(git_patch *patch, diff_expects *exp)
{
size_t h, num_h = git_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_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 *diff = NULL;
diff_expects exp = {0};
git_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_patch *patch = patches[p];
patches[p] = NULL;
/* cache new patch */
cl_git_pass(git_patch_from_diff(&patches[p], diff, d));
cl_assert(patches[p] != NULL);
/* process old patch if non-NULL */
if (patch != NULL) {
iterate_over_patch(patch, &exp);
git_patch_free(patch);
}
p = rand() % PATCH_CACHE;
}
/* free diff list now - refcounts should keep things safe */
git_diff_free(diff);
/* process remaining unprocessed patches */
for (p = 0; p < PATCH_CACHE; p++) {
git_patch *patch = patches[p];
if (patch != NULL) {
iterate_over_patch(patch, &exp);
git_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 *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_patch *patch;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert(patch != NULL);
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected_patch_text[d], buf.ptr);
git_buf_dispose(&buf);
git_patch_free(patch);
}
git_diff_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 *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 = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
git_error_clear();
opts.version = 1024;
cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
err = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
}
+278
View File
@@ -0,0 +1,278 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
#include "repository.h"
#include "diff_driver.h"
static git_repository *g_repo = NULL;
void test_diff_drivers__initialize(void)
{
}
void test_diff_drivers__cleanup(void)
{
cl_git_sandbox_cleanup();
g_repo = NULL;
}
static void overwrite_filemode(const char *expected, git_buf *actual)
{
size_t offset;
char *found;
found = strstr(expected, "100644");
if (!found)
return;
offset = ((const char *)found) - expected;
if (actual->size < offset + 6)
return;
if (memcmp(&actual->ptr[offset], "100644", 6) != 0)
memcpy(&actual->ptr[offset], "100644", 6);
}
void test_diff_drivers__patterns(void)
{
git_config *cfg;
const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
git_tree *one;
git_diff *diff;
git_patch *patch;
git_buf actual = GIT_BUF_INIT;
const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
g_repo = cl_git_sandbox_init("renames");
one = resolve_commit_oid_to_tree(g_repo, one_sha);
/* no diff */
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
git_diff_free(diff);
/* default diff */
cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected0, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
/* attribute diff set to false */
cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected1, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
/* attribute diff set to unconfigured value (should use default) */
cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected0, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
/* let's define that driver */
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1));
git_config_free(cfg);
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected1, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
/* let's use a real driver with some regular expressions */
git_diff_driver_registry_free(g_repo->diff_drivers);
g_repo->diff_drivers = NULL;
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0));
cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H.*$"));
git_config_free(cfg);
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
cl_assert_equal_s(expected2, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
git_tree_free(one);
}
void test_diff_drivers__long_lines(void)
{
const char *base = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non nisi ligula. Ut viverra enim sed lobortis suscipit.\nPhasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissim risus. Suspendisse at nisi quis turpis fringilla rutrum id sit amet nulla.\nNam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\nMauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\nAliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n";
git_index *idx;
git_diff *diff;
git_patch *patch;
git_buf actual = GIT_BUF_INIT;
const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n";
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_mkfile("empty_standard_repo/longlines.txt", base);
cl_git_pass(git_repository_index(&idx, g_repo));
cl_git_pass(git_index_add_bypath(idx, "longlines.txt"));
cl_git_pass(git_index_write(idx));
git_index_free(idx);
cl_git_append2file("empty_standard_repo/longlines.txt", "newline\nnewline\n");
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
cl_assert_equal_sz(1, git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
/* if chmod not supported, overwrite mode bits since anything is possible */
overwrite_filemode(expected, &actual);
cl_assert_equal_s(expected, actual.ptr);
git_buf_dispose(&actual);
git_patch_free(patch);
git_diff_free(diff);
}
void test_diff_drivers__builtins(void)
{
git_diff *diff;
git_patch *patch;
git_buf file = GIT_BUF_INIT, actual = GIT_BUF_INIT, expected = GIT_BUF_INIT;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_vector files = GIT_VECTOR_INIT;
size_t i;
char *path, *extension;
g_repo = cl_git_sandbox_init("userdiff");
cl_git_pass(git_path_dirload(&files, "userdiff/files", 9, 0));
opts.interhunk_lines = 1;
opts.context_lines = 1;
opts.pathspec.count = 1;
git_vector_foreach(&files, i, path) {
if (git__prefixcmp(path, "files/file."))
continue;
extension = path + strlen("files/file.");
opts.pathspec.strings = &path;
/* do diff with no special driver */
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_assert_equal_sz(1, git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
git_buf_sets(&expected, "userdiff/expected/nodriver/diff.");
git_buf_puts(&expected, extension);
cl_git_pass(git_futils_readbuffer(&expected, expected.ptr));
overwrite_filemode(expected.ptr, &actual);
cl_assert_equal_s(expected.ptr, actual.ptr);
git_buf_clear(&actual);
git_patch_free(patch);
git_diff_free(diff);
/* do diff with driver */
{
FILE *fp = fopen("userdiff/.gitattributes", "w");
fprintf(fp, "*.%s diff=%s\n", extension, extension);
fclose(fp);
}
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_assert_equal_sz(1, git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&actual, patch));
git_buf_sets(&expected, "userdiff/expected/driver/diff.");
git_buf_puts(&expected, extension);
cl_git_pass(git_futils_readbuffer(&expected, expected.ptr));
overwrite_filemode(expected.ptr, &actual);
cl_assert_equal_s(expected.ptr, actual.ptr);
git_buf_clear(&actual);
git_patch_free(patch);
git_diff_free(diff);
git__free(path);
}
git_buf_dispose(&file);
git_buf_dispose(&actual);
git_buf_dispose(&expected);
git_vector_free(&files);
}
void test_diff_drivers__invalid_pattern(void)
{
git_config *cfg;
git_index *idx;
git_diff *diff;
git_patch *patch;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
g_repo = cl_git_sandbox_init("userdiff");
cl_git_mkfile("userdiff/.gitattributes", "*.storyboard diff=storyboard\n");
cl_git_pass(git_repository_config__weakptr(&cfg, g_repo));
cl_git_pass(git_config_set_string(cfg, "diff.storyboard.xfuncname", "<!--(.*?)-->"));
cl_git_mkfile("userdiff/dummy.storyboard", "");
cl_git_pass(git_repository_index__weakptr(&idx, g_repo));
cl_git_pass(git_index_add_bypath(idx, "dummy.storyboard"));
cl_git_mkfile("userdiff/dummy.storyboard", "some content\n");
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
git_patch_free(patch);
git_diff_free(diff);
}
+506
View File
@@ -0,0 +1,506 @@
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "commit.h"
#include "diff.h"
#include "diff_generate.h"
static git_repository *repo;
void test_diff_format_email__initialize(void)
{
repo = cl_git_sandbox_init("diff_format_email");
}
void test_diff_format_email__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void assert_email_match(
const char *expected,
const char *oidstr,
git_diff_format_email_options *opts)
{
git_oid oid;
git_commit *commit = NULL;
git_diff *diff = NULL;
git_buf buf = GIT_BUF_INIT;
git_oid_fromstr(&oid, oidstr);
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
opts->id = git_commit_id(commit);
opts->author = git_commit_author(commit);
if (!opts->summary)
opts->summary = git_commit_summary(commit);
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_pass(git_diff_format_email(&buf, diff, opts));
cl_assert_equal_s(expected, git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_diff_commit_as_email(
&buf, repo, commit, 1, 1, opts->flags, NULL));
cl_assert_equal_s(expected, git_buf_cstr(&buf));
git_diff_free(diff);
git_commit_free(commit);
git_buf_dispose(&buf);
}
void test_diff_format_email__simple(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
"Subject: [PATCH] Modify some content\n" \
"\n" \
"---\n" \
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"index 94aaae8..af8f41d 100644\n" \
"--- a/file1.txt\n" \
"+++ b/file1.txt\n" \
"@@ -1,15 +1,17 @@\n" \
" file1.txt\n" \
" file1.txt\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"+\n" \
"+\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
assert_email_match(
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
}
void test_diff_format_email__with_message(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \
"From: Patrick Steinhardt <ps@pks.im>\n" \
"Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \
"Subject: [PATCH] Modify content with message\n" \
"\n" \
"Modify content of file3.txt by appending a new line. Make this\n" \
"commit message somewhat longer to test behavior with newlines\n" \
"embedded in the message body.\n" \
"\n" \
"Also test if new paragraphs are included correctly.\n" \
"---\n" \
" file3.txt | 1 +\n" \
" 1 file changed, 1 insertion(+)\n" \
"\n" \
"diff --git a/file3.txt b/file3.txt\n" \
"index 9a2d780..7309653 100644\n" \
"--- a/file3.txt\n" \
"+++ b/file3.txt\n" \
"@@ -3,3 +3,4 @@ file3!\n" \
" file3\n" \
" file3\n" \
" file3\n" \
"+file3\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
opts.body = "Modify content of file3.txt by appending a new line. Make this\n" \
"commit message somewhat longer to test behavior with newlines\n" \
"embedded in the message body.\n" \
"\n" \
"Also test if new paragraphs are included correctly.";
assert_email_match(
email, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270", &opts);
}
void test_diff_format_email__multiple(void)
{
git_oid oid;
git_commit *commit = NULL;
git_diff *diff = NULL;
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
git_buf buf = GIT_BUF_INIT;
const char *email =
"From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \
"Subject: [PATCH 1/2] Added file2.txt file3.txt\n" \
"\n" \
"---\n" \
" file2.txt | 5 +++++\n" \
" file3.txt | 5 +++++\n" \
" 2 files changed, 10 insertions(+)\n" \
" create mode 100644 file2.txt\n" \
" create mode 100644 file3.txt\n" \
"\n" \
"diff --git a/file2.txt b/file2.txt\n" \
"new file mode 100644\n" \
"index 0000000..e909123\n" \
"--- /dev/null\n" \
"+++ b/file2.txt\n" \
"@@ -0,0 +1,5 @@\n" \
"+file2\n" \
"+file2\n" \
"+file2\n" \
"+file2\n" \
"+file2\n" \
"diff --git a/file3.txt b/file3.txt\n" \
"new file mode 100644\n" \
"index 0000000..9435022\n" \
"--- /dev/null\n" \
"+++ b/file3.txt\n" \
"@@ -0,0 +1,5 @@\n" \
"+file3\n" \
"+file3\n" \
"+file3\n" \
"+file3\n" \
"+file3\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n" \
"From 873806f6f27e631eb0b23e4b56bea2bfac14a373 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Thu, 10 Apr 2014 19:37:36 +0200\n" \
"Subject: [PATCH 2/2] Modified file2.txt, file3.txt\n" \
"\n" \
"---\n" \
" file2.txt | 2 +-\n" \
" file3.txt | 2 +-\n" \
" 2 files changed, 2 insertions(+), 2 deletions(-)\n" \
"\n" \
"diff --git a/file2.txt b/file2.txt\n" \
"index e909123..7aff11d 100644\n" \
"--- a/file2.txt\n" \
"+++ b/file2.txt\n" \
"@@ -1,5 +1,5 @@\n" \
" file2\n" \
" file2\n" \
" file2\n" \
"-file2\n" \
"+file2!\n" \
" file2\n" \
"diff --git a/file3.txt b/file3.txt\n" \
"index 9435022..9a2d780 100644\n" \
"--- a/file3.txt\n" \
"+++ b/file3.txt\n" \
"@@ -1,5 +1,5 @@\n" \
" file3\n" \
"-file3\n" \
"+file3!\n" \
" file3\n" \
" file3\n" \
" file3\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
git_oid_fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
opts.id = git_commit_id(commit);
opts.author = git_commit_author(commit);
opts.summary = git_commit_summary(commit);
opts.patch_no = 1;
opts.total_patches = 2;
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_pass(git_diff_format_email(&buf, diff, &opts));
git_diff_free(diff);
git_commit_free(commit);
diff = NULL;
commit = NULL;
git_oid_fromstr(&oid, "873806f6f27e631eb0b23e4b56bea2bfac14a373");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
opts.id = git_commit_id(commit);
opts.author = git_commit_author(commit);
opts.summary = git_commit_summary(commit);
opts.patch_no = 2;
opts.total_patches = 2;
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_pass(git_diff_format_email(&buf, diff, &opts));
cl_assert_equal_s(email, git_buf_cstr(&buf));
git_diff_free(diff);
git_commit_free(commit);
git_buf_dispose(&buf);
}
void test_diff_format_email__exclude_marker(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
"Subject: Modify some content\n" \
"\n" \
"---\n" \
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"index 94aaae8..af8f41d 100644\n" \
"--- a/file1.txt\n" \
"+++ b/file1.txt\n" \
"@@ -1,15 +1,17 @@\n" \
" file1.txt\n" \
" file1.txt\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"+\n" \
"+\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
opts.flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER;
assert_email_match(
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
}
void test_diff_format_email__invalid_no(void)
{
git_oid oid;
git_commit *commit = NULL;
git_diff *diff = NULL;
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
git_buf buf = GIT_BUF_INIT;
git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
opts.id = git_commit_id(commit);
opts.author = git_commit_author(commit);
opts.summary = git_commit_summary(commit);
opts.patch_no = 2;
opts.total_patches = 1;
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_fail(git_diff_format_email(&buf, diff, &opts));
cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL));
cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 0, 0, 0, NULL));
git_diff_free(diff);
git_commit_free(commit);
git_buf_dispose(&buf);
}
void test_diff_format_email__mode_change(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \
"Subject: [PATCH] Update permissions\n" \
"\n" \
"---\n" \
" file1.txt.renamed | 0\n" \
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
" mode change 100644 => 100755 file1.txt.renamed\n" \
"\n" \
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
"old mode 100644\n" \
"new mode 100755\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
assert_email_match(
email, "7ade76dd34bba4733cf9878079f9fd4a456a9189", &opts);
}
void test_diff_format_email__rename_add_remove(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
"Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
"\n" \
"---\n" \
" file1.txt | 17 -----------------\n" \
" file1.txt.renamed | 17 +++++++++++++++++\n" \
" 2 files changed, 17 insertions(+), 17 deletions(-)\n" \
" delete mode 100644 file1.txt\n" \
" create mode 100644 file1.txt.renamed\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"deleted file mode 100644\n" \
"index af8f41d..0000000\n" \
"--- a/file1.txt\n" \
"+++ /dev/null\n" \
"@@ -1,17 +0,0 @@\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-_file1.txt_\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-\n" \
"-\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-_file1.txt_\n" \
"-_file1.txt_\n" \
"-file1.txt\n" \
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
"new file mode 100644\n" \
"index 0000000..a97157a\n" \
"--- /dev/null\n" \
"+++ b/file1.txt.renamed\n" \
"@@ -0,0 +1,17 @@\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+_file1.txt_\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+file1.txt_renamed\n" \
"+file1.txt\n" \
"+\n" \
"+\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+file1.txt_renamed\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
"+file1.txt\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
assert_email_match(
email, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts);
}
void test_diff_format_email__multiline_summary(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
"Subject: [PATCH] Modify some content\n" \
"\n" \
"---\n" \
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"index 94aaae8..af8f41d 100644\n" \
"--- a/file1.txt\n" \
"+++ b/file1.txt\n" \
"@@ -1,15 +1,17 @@\n" \
" file1.txt\n" \
" file1.txt\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"+\n" \
"+\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
opts.summary = "Modify some content\nSome extra stuff here";
assert_email_match(
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
}
void test_diff_format_email__binary(void)
{
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
"Subject: [PATCH] Modified binary file\n" \
"\n" \
"---\n" \
" binary.bin | Bin 3 -> 0 bytes\n" \
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
"\n" \
"diff --git a/binary.bin b/binary.bin\n" \
"index bd474b2..9ac35ff 100644\n" \
"Binary files a/binary.bin and b/binary.bin differ\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
/* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */
opts.summary = "Modified binary file";
assert_email_match(
email, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts);
}
+302
View File
@@ -0,0 +1,302 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_index__initialize(void)
{
g_repo = cl_git_sandbox_init("status");
}
void test_diff_index__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_diff_index__0(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
const char *b_commit = "0017bd4ab1ec3"; /* the start */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
diff_expects exp;
cl_assert(a);
cl_assert(b);
opts.context_lines = 1;
opts.interhunk_lines = 1;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
/* to generate these values:
* - cd to tests/resources/status,
* - mv .gitted .git
* - git diff --name-status --cached 26a125ee1bf
* - git diff -U1 --cached 26a125ee1bf
* - mv .git .gitted
*/
cl_assert_equal_i(8, exp.files);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(8, exp.hunks);
cl_assert_equal_i(11, exp.lines);
cl_assert_equal_i(3, exp.line_ctxt);
cl_assert_equal_i(6, exp.line_adds);
cl_assert_equal_i(2, exp.line_dels);
git_diff_free(diff);
diff = NULL;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
/* to generate these values:
* - cd to tests/resources/status,
* - mv .gitted .git
* - git diff --name-status --cached 0017bd4ab1ec3
* - git diff -U1 --cached 0017bd4ab1ec3
* - mv .git .gitted
*/
cl_assert_equal_i(12, exp.files);
cl_assert_equal_i(7, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(12, exp.hunks);
cl_assert_equal_i(16, exp.lines);
cl_assert_equal_i(3, exp.line_ctxt);
cl_assert_equal_i(11, exp.line_adds);
cl_assert_equal_i(2, exp.line_dels);
git_diff_free(diff);
diff = NULL;
git_tree_free(a);
git_tree_free(b);
}
static int diff_stop_after_2_files(
const git_diff_delta *delta,
float progress,
void *payload)
{
diff_expects *e = payload;
GIT_UNUSED(progress);
GIT_UNUSED(delta);
e->files++;
return (e->files == 2);
}
void test_diff_index__1(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
const char *b_commit = "0017bd4ab1ec3"; /* the start */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
diff_expects exp;
cl_assert(a);
cl_assert(b);
opts.context_lines = 1;
opts.interhunk_lines = 1;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
cl_assert_equal_i(1, git_diff_foreach(
diff, diff_stop_after_2_files, NULL, NULL, NULL, &exp) );
cl_assert_equal_i(2, exp.files);
git_diff_free(diff);
diff = NULL;
git_tree_free(a);
git_tree_free(b);
}
void test_diff_index__checks_options_version(void)
{
const char *a_commit = "26a125ee1bf";
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
const git_error *err;
opts.version = 0;
cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
err = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
cl_assert_equal_p(diff, NULL);
git_error_clear();
opts.version = 1024;
cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
err = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
cl_assert_equal_p(diff, NULL);
git_tree_free(a);
}
static void do_conflicted_diff(diff_expects *exp, unsigned long flags)
{
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_index_entry ancestor = {{0}}, ours = {{0}}, theirs = {{0}};
git_diff *diff = NULL;
git_index *index;
cl_assert(a);
opts.context_lines = 1;
opts.interhunk_lines = 1;
opts.flags |= flags;
memset(exp, 0, sizeof(diff_expects));
cl_git_pass(git_repository_index(&index, g_repo));
ancestor.path = ours.path = theirs.path = "staged_changes";
ancestor.mode = ours.mode = theirs.mode = GIT_FILEMODE_BLOB;
git_oid_fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, exp));
git_diff_free(diff);
git_tree_free(a);
git_index_free(index);
}
void test_diff_index__reports_conflicts(void)
{
diff_expects exp;
do_conflicted_diff(&exp, 0);
cl_assert_equal_i(8, exp.files);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_CONFLICTED]);
cl_assert_equal_i(7, exp.hunks);
cl_assert_equal_i(9, exp.lines);
cl_assert_equal_i(2, exp.line_ctxt);
cl_assert_equal_i(5, exp.line_adds);
cl_assert_equal_i(2, exp.line_dels);
}
void test_diff_index__reports_conflicts_when_reversed(void)
{
diff_expects exp;
do_conflicted_diff(&exp, GIT_DIFF_REVERSE);
cl_assert_equal_i(8, exp.files);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_CONFLICTED]);
cl_assert_equal_i(7, exp.hunks);
cl_assert_equal_i(9, exp.lines);
cl_assert_equal_i(2, exp.line_ctxt);
cl_assert_equal_i(2, exp.line_adds);
cl_assert_equal_i(5, exp.line_dels);
}
void test_diff_index__not_in_head_conflicted(void)
{
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
git_index_entry theirs = {{0}};
git_index *index;
git_diff *diff;
const git_diff_delta *delta;
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_read_tree(index, a));
theirs.path = "file_not_in_head";
theirs.mode = GIT_FILEMODE_BLOB;
git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_index_conflict_add(index, NULL, NULL, &theirs));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, NULL));
cl_assert_equal_i(git_diff_num_deltas(diff), 1);
delta = git_diff_get_delta(diff, 0);
cl_assert_equal_i(delta->status, GIT_DELTA_CONFLICTED);
git_diff_free(diff);
git_index_free(index);
git_tree_free(a);
}
void test_diff_index__to_index(void)
{
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
git_tree *old_tree;
git_index *old_index;
git_index *new_index;
git_diff *diff;
diff_expects exp;
cl_git_pass(git_index_new(&old_index));
old_tree = resolve_commit_oid_to_tree(g_repo, a_commit);
cl_git_pass(git_index_read_tree(old_index, old_tree));
cl_git_pass(git_repository_index(&new_index, g_repo));
cl_git_pass(git_diff_index_to_index(&diff, g_repo, old_index, new_index, NULL));
memset(&exp, 0, sizeof(diff_expects));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(8, exp.files);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_CONFLICTED]);
git_diff_free(diff);
git_index_free(new_index);
git_index_free(old_index);
git_tree_free(old_tree);
}
+258
View File
@@ -0,0 +1,258 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_notify__initialize(void)
{
}
void test_diff_notify__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int assert_called_notifications(
const git_diff *diff_so_far,
const git_diff_delta *delta_to_add,
const char *matched_pathspec,
void *payload)
{
bool found = false;
notify_expected *exp = (notify_expected*)payload;
notify_expected *e;
GIT_UNUSED(diff_so_far);
for (e = exp; e->path != NULL; e++) {
if (strcmp(e->path, delta_to_add->new_file.path))
continue;
cl_assert_equal_s(e->matched_pathspec, matched_pathspec);
found = true;
break;
}
cl_assert(found);
return 0;
}
static void test_notify(
char **searched_pathspecs,
int pathspecs_count,
notify_expected *expected_matched_pathspecs,
int expected_diffed_files_count)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("status");
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
opts.notify_cb = assert_called_notifications;
opts.pathspec.strings = searched_pathspecs;
opts.pathspec.count = pathspecs_count;
opts.payload = expected_matched_pathspecs;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
cl_assert_equal_i(expected_diffed_files_count, exp.files);
git_diff_free(diff);
}
void test_diff_notify__notify_single_pathspec(void)
{
char *searched_pathspecs[] = {
"*_deleted",
};
notify_expected expected_matched_pathspecs[] = {
{ "file_deleted", "*_deleted" },
{ "staged_changes_file_deleted", "*_deleted" },
{ NULL, NULL }
};
test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 2);
}
void test_diff_notify__notify_multiple_pathspec(void)
{
char *searched_pathspecs[] = {
"staged_changes_cant_find_me",
"subdir/modified_cant_find_me",
"subdir/*",
"staged*"
};
notify_expected expected_matched_pathspecs[] = {
{ "staged_changes_file_deleted", "staged*" },
{ "staged_changes_modified_file", "staged*" },
{ "staged_delete_modified_file", "staged*" },
{ "staged_new_file_deleted_file", "staged*" },
{ "staged_new_file_modified_file", "staged*" },
{ "subdir/deleted_file", "subdir/*" },
{ "subdir/modified_file", "subdir/*" },
{ "subdir/new_file", "subdir/*" },
{ NULL, NULL }
};
test_notify(searched_pathspecs, 4, expected_matched_pathspecs, 8);
}
void test_diff_notify__notify_catchall_with_empty_pathspecs(void)
{
char *searched_pathspecs[] = {
"",
""
};
notify_expected expected_matched_pathspecs[] = {
{ "file_deleted", NULL },
{ "ignored_file", NULL },
{ "modified_file", NULL },
{ "new_file", NULL },
{ "\xe8\xbf\x99", NULL },
{ "staged_changes_file_deleted", NULL },
{ "staged_changes_modified_file", NULL },
{ "staged_delete_modified_file", NULL },
{ "staged_new_file_deleted_file", NULL },
{ "staged_new_file_modified_file", NULL },
{ "subdir/deleted_file", NULL },
{ "subdir/modified_file", NULL },
{ "subdir/new_file", NULL },
{ NULL, NULL }
};
test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
}
void test_diff_notify__notify_catchall(void)
{
char *searched_pathspecs[] = {
"*",
};
notify_expected expected_matched_pathspecs[] = {
{ "file_deleted", "*" },
{ "ignored_file", "*" },
{ "modified_file", "*" },
{ "new_file", "*" },
{ "\xe8\xbf\x99", "*" },
{ "staged_changes_file_deleted", "*" },
{ "staged_changes_modified_file", "*" },
{ "staged_delete_modified_file", "*" },
{ "staged_new_file_deleted_file", "*" },
{ "staged_new_file_modified_file", "*" },
{ "subdir/deleted_file", "*" },
{ "subdir/modified_file", "*" },
{ "subdir/new_file", "*" },
{ NULL, NULL }
};
test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
}
static int abort_diff(
const git_diff *diff_so_far,
const git_diff_delta *delta_to_add,
const char *matched_pathspec,
void *payload)
{
GIT_UNUSED(diff_so_far);
GIT_UNUSED(delta_to_add);
GIT_UNUSED(matched_pathspec);
GIT_UNUSED(payload);
return -42;
}
void test_diff_notify__notify_cb_can_abort_diff(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
char *pathspec = NULL;
g_repo = cl_git_sandbox_init("status");
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
opts.notify_cb = abort_diff;
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
pathspec = "file_deleted";
cl_git_fail_with(
git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
pathspec = "staged_changes_modified_file";
cl_git_fail_with(
git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
}
static int filter_all(
const git_diff *diff_so_far,
const git_diff_delta *delta_to_add,
const char *matched_pathspec,
void *payload)
{
GIT_UNUSED(diff_so_far);
GIT_UNUSED(delta_to_add);
GIT_UNUSED(matched_pathspec);
GIT_UNUSED(payload);
return 42;
}
void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
char *pathspec = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("status");
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
opts.notify_cb = filter_all;
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
pathspec = "*_deleted";
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
cl_assert_equal_i(0, exp.files);
git_diff_free(diff);
}
static int progress_abort_diff(
const git_diff *diff_so_far,
const char *old_path,
const char *new_path,
void *payload)
{
GIT_UNUSED(diff_so_far);
GIT_UNUSED(old_path);
GIT_UNUSED(new_path);
GIT_UNUSED(payload);
return -42;
}
void test_diff_notify__progress_cb_can_abort_diff(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
g_repo = cl_git_sandbox_init("status");
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
opts.progress_cb = progress_abort_diff;
cl_git_fail_with(
git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
}
+428
View File
@@ -0,0 +1,428 @@
#include "clar_libgit2.h"
#include "patch.h"
#include "patch_parse.h"
#include "diff_helpers.h"
#include "../src/diff.h"
#include "../patch/patch_common.h"
void test_diff_parse__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_diff_parse__nonpatches_fail_with_notfound(void)
{
git_diff *diff;
const char *not = PATCH_NOT_A_PATCH;
const char *not_with_leading = "Leading text.\n" PATCH_NOT_A_PATCH;
const char *not_with_trailing = PATCH_NOT_A_PATCH "Trailing text.\n";
const char *not_with_both = "Lead.\n" PATCH_NOT_A_PATCH "Trail.\n";
cl_git_fail_with(GIT_ENOTFOUND,
git_diff_from_buffer(&diff,
not,
strlen(not)));
cl_git_fail_with(GIT_ENOTFOUND,
git_diff_from_buffer(&diff,
not_with_leading,
strlen(not_with_leading)));
cl_git_fail_with(GIT_ENOTFOUND,
git_diff_from_buffer(&diff,
not_with_trailing,
strlen(not_with_trailing)));
cl_git_fail_with(GIT_ENOTFOUND,
git_diff_from_buffer(&diff,
not_with_both,
strlen(not_with_both)));
}
static void test_parse_invalid_diff(const char *invalid_diff)
{
git_diff *diff;
git_buf buf = GIT_BUF_INIT;
/* throw some random (legitimate) diffs in with the given invalid
* one.
*/
git_buf_puts(&buf, PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE);
git_buf_puts(&buf, PATCH_BINARY_DELTA);
git_buf_puts(&buf, invalid_diff);
git_buf_puts(&buf, PATCH_ORIGINAL_TO_CHANGE_MIDDLE);
git_buf_puts(&buf, PATCH_BINARY_LITERAL);
cl_git_fail_with(GIT_ERROR,
git_diff_from_buffer(&diff, buf.ptr, buf.size));
git_buf_dispose(&buf);
}
void test_diff_parse__exact_rename(void)
{
const char *content =
"---\n"
" old_name.c => new_name.c | 0\n"
" 1 file changed, 0 insertions(+), 0 deletions(-)\n"
" rename old_name.c => new_name.c (100%)\n"
"\n"
"diff --git a/old_name.c b/new_name.c\n"
"similarity index 100%\n"
"rename from old_name.c\n"
"rename to new_name.c\n"
"-- \n"
"2.9.3\n";
git_diff *diff;
cl_git_pass(git_diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
void test_diff_parse__empty_file(void)
{
const char *content =
"---\n"
" file | 0\n"
" 1 file changed, 0 insertions(+), 0 deletions(-)\n"
" created mode 100644 file\n"
"\n"
"diff --git a/file b/file\n"
"new file mode 100644\n"
"index 0000000..e69de29\n"
"-- \n"
"2.20.1\n";
git_diff *diff;
cl_git_pass(git_diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
void test_diff_parse__no_extended_headers(void)
{
const char *content = PATCH_NO_EXTENDED_HEADERS;
git_diff *diff;
cl_git_pass(git_diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
void test_diff_parse__invalid_patches_fails(void)
{
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_NEW_FILE);
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_OLD_FILE);
test_parse_invalid_diff(PATCH_CORRUPT_NO_CHANGES);
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_HUNK_HEADER);
}
static void test_tree_to_tree_computed_to_parsed(
const char *sandbox, const char *a_id, const char *b_id,
uint32_t diff_flags, uint32_t find_flags)
{
git_repository *repo;
git_diff *computed, *parsed;
git_tree *a, *b;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
git_buf computed_buf = GIT_BUF_INIT;
repo = cl_git_sandbox_init(sandbox);
opts.id_abbrev = GIT_OID_HEXSZ;
opts.flags = GIT_DIFF_SHOW_BINARY | diff_flags;
findopts.flags = find_flags;
cl_assert((a = resolve_commit_oid_to_tree(repo, a_id)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(repo, b_id)) != NULL);
cl_git_pass(git_diff_tree_to_tree(&computed, repo, a, b, &opts));
if (find_flags)
cl_git_pass(git_diff_find_similar(computed, &findopts));
cl_git_pass(git_diff_to_buf(&computed_buf,
computed, GIT_DIFF_FORMAT_PATCH));
cl_git_pass(git_diff_from_buffer(&parsed,
computed_buf.ptr, computed_buf.size));
diff_assert_equal(computed, parsed);
git_tree_free(a);
git_tree_free(b);
git_diff_free(computed);
git_diff_free(parsed);
git_buf_dispose(&computed_buf);
cl_git_sandbox_cleanup();
}
void test_diff_parse__can_parse_generated_diff(void)
{
test_tree_to_tree_computed_to_parsed(
"diff", "d70d245e", "7a9e0b02", 0, 0);
test_tree_to_tree_computed_to_parsed(
"unsymlinked.git", "806999", "a8595c", 0, 0);
test_tree_to_tree_computed_to_parsed("diff",
"d70d245ed97ed2aa596dd1af6536e4bfdb047b69",
"7a9e0b02e63179929fed24f0a3e0f19168114d10", 0, 0);
test_tree_to_tree_computed_to_parsed(
"unsymlinked.git", "7fccd7", "806999", 0, 0);
test_tree_to_tree_computed_to_parsed(
"unsymlinked.git", "7fccd7", "a8595c", 0, 0);
test_tree_to_tree_computed_to_parsed(
"attr", "605812a", "370fe9ec22", 0, 0);
test_tree_to_tree_computed_to_parsed(
"attr", "f5b0af1fb4f5c", "370fe9ec22", 0, 0);
test_tree_to_tree_computed_to_parsed(
"diff", "d70d245e", "d70d245e", 0, 0);
test_tree_to_tree_computed_to_parsed("diff_format_email",
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
GIT_DIFF_SHOW_BINARY, 0);
test_tree_to_tree_computed_to_parsed("diff_format_email",
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
GIT_DIFF_SHOW_BINARY, 0);
test_tree_to_tree_computed_to_parsed("renames",
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
"2bc7f351d20b53f1c72c16c4b036e491c478c49a",
0, GIT_DIFF_FIND_RENAMES);
test_tree_to_tree_computed_to_parsed("renames",
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
"2bc7f351d20b53f1c72c16c4b036e491c478c49a",
GIT_DIFF_INCLUDE_UNMODIFIED,
0);
test_tree_to_tree_computed_to_parsed("renames",
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
"2bc7f351d20b53f1c72c16c4b036e491c478c49a",
GIT_DIFF_INCLUDE_UNMODIFIED,
GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED | GIT_DIFF_FIND_EXACT_MATCH_ONLY);
}
void test_diff_parse__get_patch_from_diff(void)
{
git_repository *repo;
git_diff *computed, *parsed;
git_tree *a, *b;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_buf computed_buf = GIT_BUF_INIT;
git_patch *patch_computed, *patch_parsed;
repo = cl_git_sandbox_init("diff");
opts.flags = GIT_DIFF_SHOW_BINARY;
cl_assert((a = resolve_commit_oid_to_tree(repo,
"d70d245ed97ed2aa596dd1af6536e4bfdb047b69")) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(repo,
"7a9e0b02e63179929fed24f0a3e0f19168114d10")) != NULL);
cl_git_pass(git_diff_tree_to_tree(&computed, repo, a, b, &opts));
cl_git_pass(git_diff_to_buf(&computed_buf,
computed, GIT_DIFF_FORMAT_PATCH));
cl_git_pass(git_patch_from_diff(&patch_computed, computed, 0));
cl_git_pass(git_diff_from_buffer(&parsed,
computed_buf.ptr, computed_buf.size));
cl_git_pass(git_patch_from_diff(&patch_parsed, parsed, 0));
cl_assert_equal_i(
git_patch_num_hunks(patch_computed),
git_patch_num_hunks(patch_parsed));
git_patch_free(patch_computed);
git_patch_free(patch_parsed);
git_tree_free(a);
git_tree_free(b);
git_diff_free(computed);
git_diff_free(parsed);
git_buf_dispose(&computed_buf);
cl_git_sandbox_cleanup();
}
static int file_cb(const git_diff_delta *delta, float progress, void *payload)
{
int *called = (int *) payload;
GIT_UNUSED(delta);
GIT_UNUSED(progress);
(*called)++;
return 0;
}
void test_diff_parse__foreach_works_with_parsed_patch(void)
{
const char patch[] =
"diff --git a/obj1 b/obj2\n"
"index 1234567..7654321 10644\n"
"--- a/obj1\n"
"+++ b/obj2\n"
"@@ -1 +1 @@\n"
"-abcde\n"
"+12345\n";
int called = 0;
git_diff *diff;
cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
cl_git_pass(git_diff_foreach(diff, file_cb, NULL, NULL, NULL, &called));
cl_assert_equal_i(called, 1);
git_diff_free(diff);
}
void test_diff_parse__parsing_minimal_patch_succeeds(void)
{
const char patch[] =
"diff --git a/obj1 b/obj2\n"
"index 1234567..7654321 10644\n"
"--- a/obj1\n"
"+++ b/obj2\n"
"@@ -1 +1 @@\n"
"-a\n"
"+\n";
git_buf buf = GIT_BUF_INIT;
git_diff *diff;
cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
cl_assert_equal_s(patch, buf.ptr);
git_diff_free(diff);
git_buf_dispose(&buf);
}
void test_diff_parse__patch_roundtrip_succeeds(void)
{
const char buf1[] = "a\n", buf2[] = "b\n";
git_buf patchbuf = GIT_BUF_INIT, diffbuf = GIT_BUF_INIT;
git_patch *patch;
git_diff *diff;
cl_git_pass(git_patch_from_buffers(&patch, buf1, strlen(buf1), "obj1", buf2, strlen(buf2), "obj2", NULL));
cl_git_pass(git_patch_to_buf(&patchbuf, patch));
cl_git_pass(git_diff_from_buffer(&diff, patchbuf.ptr, patchbuf.size));
cl_git_pass(git_diff_to_buf(&diffbuf, diff, GIT_DIFF_FORMAT_PATCH));
cl_assert_equal_s(patchbuf.ptr, diffbuf.ptr);
git_patch_free(patch);
git_diff_free(diff);
git_buf_dispose(&patchbuf);
git_buf_dispose(&diffbuf);
}
#define cl_assert_equal_i_src(i1,i2,file,line) clar__assert_equal(file,line,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
static void cl_git_assert_lineinfo_(int old_lineno, int new_lineno, int num_lines, git_patch *patch, size_t hunk_idx, size_t line_idx, const char *file, int lineno)
{
const git_diff_line *line;
cl_git_expect(git_patch_get_line_in_hunk(&line, patch, hunk_idx, line_idx), 0, file, lineno);
cl_assert_equal_i_src(old_lineno, line->old_lineno, file, lineno);
cl_assert_equal_i_src(new_lineno, line->new_lineno, file, lineno);
cl_assert_equal_i_src(num_lines, line->num_lines, file, lineno);
}
#define cl_git_assert_lineinfo(old, new, num, p, h, l) \
cl_git_assert_lineinfo_(old,new,num,p,h,l,__FILE__,__LINE__)
void test_diff_parse__issue4672(void)
{
const char *text = "diff --git a/a b/a\n"
"index 7f129fd..af431f2 100644\n"
"--- a/a\n"
"+++ b/a\n"
"@@ -3 +3 @@\n"
"-a contents 2\n"
"+a contents\n";
git_diff *diff;
git_patch *patch;
const git_diff_hunk *hunk;
size_t n, l = 0;
cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
cl_git_assert_lineinfo(3, -1, 1, patch, 0, l++);
cl_git_assert_lineinfo(-1, 3, 1, patch, 0, l++);
cl_assert_equal_i(n, l);
git_patch_free(patch);
git_diff_free(diff);
}
void test_diff_parse__lineinfo(void)
{
const char *text = PATCH_ORIGINAL_TO_CHANGE_MIDDLE;
git_diff *diff;
git_patch *patch;
const git_diff_hunk *hunk;
size_t n, l = 0;
cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
cl_git_assert_lineinfo(3, 3, 1, patch, 0, l++);
cl_git_assert_lineinfo(4, 4, 1, patch, 0, l++);
cl_git_assert_lineinfo(5, 5, 1, patch, 0, l++);
cl_git_assert_lineinfo(6, -1, 1, patch, 0, l++);
cl_git_assert_lineinfo(-1, 6, 1, patch, 0, l++);
cl_git_assert_lineinfo(7, 7, 1, patch, 0, l++);
cl_git_assert_lineinfo(8, 8, 1, patch, 0, l++);
cl_git_assert_lineinfo(9, 9, 1, patch, 0, l++);
cl_assert_equal_i(n, l);
git_patch_free(patch);
git_diff_free(diff);
}
void test_diff_parse__new_file_with_space(void)
{
const char *content = PATCH_ORIGINAL_NEW_FILE_WITH_SPACE;
git_patch *patch;
git_diff *diff;
cl_git_pass(git_diff_from_buffer(&diff, content, strlen(content)));
cl_git_pass(git_patch_from_diff((git_patch **) &patch, diff, 0));
cl_assert_equal_p(patch->diff_opts.old_prefix, NULL);
cl_assert_equal_p(patch->delta->old_file.path, NULL);
cl_assert_equal_s(patch->diff_opts.new_prefix, "b/");
cl_assert_equal_s(patch->delta->new_file.path, "sp ace.txt");
git_patch_free(patch);
git_diff_free(diff);
}
void test_diff_parse__crlf(void)
{
const char *text = PATCH_CRLF;
git_diff *diff;
git_patch *patch;
const git_diff_delta *delta;
cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
delta = git_patch_get_delta(patch);
cl_assert_equal_s(delta->old_file.path, "test-file");
cl_assert_equal_s(delta->new_file.path, "test-file");
git_patch_free(patch);
git_diff_free(diff);
}
+704
View File
@@ -0,0 +1,704 @@
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "diff_helpers.h"
#include "diff.h"
#include "repository.h"
#include "buf_text.h"
static git_repository *g_repo = NULL;
void test_diff_patch__initialize(void)
{
}
void test_diff_patch__cleanup(void)
{
cl_git_sandbox_cleanup();
}
#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
"deleted file mode 100644\n" \
"index e8ee89e..0000000\n" \
"--- a/subdir.txt\n" \
"+++ /dev/null\n"
#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
#define UTF8_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\n"
#define UTF8_TRUNCATED_A_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\n"
#define UTF8_TRUNCATED_L_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\n"
static int check_removal_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
switch (line->origin) {
case GIT_DIFF_LINE_FILE_HDR:
cl_assert_equal_s(EXPECTED_HEADER, line->content);
cl_assert(hunk == NULL);
goto check_delta;
case GIT_DIFF_LINE_HUNK_HDR:
cl_assert_equal_s(EXPECTED_HUNK, line->content);
goto check_hunk;
case GIT_DIFF_LINE_CONTEXT:
case GIT_DIFF_LINE_DELETION:
if (payload != NULL)
return *(int *)payload;
goto check_hunk;
default:
/* unexpected code path */
return -1;
}
check_hunk:
cl_assert(hunk != NULL);
cl_assert_equal_i(1, hunk->old_start);
cl_assert_equal_i(2, hunk->old_lines);
cl_assert_equal_i(0, hunk->new_start);
cl_assert_equal_i(0, hunk->new_lines);
check_delta:
cl_assert_equal_s("subdir.txt", delta->old_file.path);
cl_assert_equal_s("subdir.txt", delta->new_file.path);
cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
return 0;
}
void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
{
/*
* $ git diff 26a125e..735b6a2
* diff --git a/subdir.txt b/subdir.txt
* deleted file mode 100644
* index e8ee89e..0000000
* --- a/subdir.txt
* +++ /dev/null
* @@ -1,2 +0,0 @@
* -Is it a bird?
* -Is it a plane?
*/
const char *one_sha = "26a125e";
const char *another_sha = "735b6a2";
git_tree *one, *another;
git_diff *diff;
g_repo = cl_git_sandbox_init("status");
one = resolve_commit_oid_to_tree(g_repo, one_sha);
another = resolve_commit_oid_to_tree(g_repo, another_sha);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
cl_git_pass(git_diff_print(
diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, NULL));
git_diff_free(diff);
git_tree_free(another);
git_tree_free(one);
}
void test_diff_patch__can_cancel_diff_print(void)
{
const char *one_sha = "26a125e";
const char *another_sha = "735b6a2";
git_tree *one, *another;
git_diff *diff;
int fail_with;
g_repo = cl_git_sandbox_init("status");
one = resolve_commit_oid_to_tree(g_repo, one_sha);
another = resolve_commit_oid_to_tree(g_repo, another_sha);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
fail_with = -2323;
cl_git_fail_with(git_diff_print(
diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with),
fail_with);
fail_with = 45;
cl_git_fail_with(git_diff_print(
diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with),
fail_with);
git_diff_free(diff);
git_tree_free(another);
git_tree_free(one);
}
void test_diff_patch__to_string(void)
{
const char *one_sha = "26a125e";
const char *another_sha = "735b6a2";
git_tree *one, *another;
git_diff *diff;
git_patch *patch;
git_buf buf = GIT_BUF_INIT;
const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
g_repo = cl_git_sandbox_init("status");
one = resolve_commit_oid_to_tree(g_repo, one_sha);
another = resolve_commit_oid_to_tree(g_repo, another_sha);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected, buf.ptr);
cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0));
cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0));
cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0));
cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1));
git_buf_dispose(&buf);
git_patch_free(patch);
git_diff_free(diff);
git_tree_free(another);
git_tree_free(one);
}
void test_diff_patch__config_options(void)
{
const char *one_sha = "26a125e"; /* current HEAD */
git_tree *one;
git_config *cfg;
git_diff *diff;
git_patch *patch;
git_buf buf = GIT_BUF_INIT;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
char *onefile = "staged_changes_modified_file";
const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/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";
const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ 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";
const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
g_repo = cl_git_sandbox_init("status");
cl_git_pass(git_repository_config(&cfg, g_repo));
one = resolve_commit_oid_to_tree(g_repo, one_sha);
opts.pathspec.count = 1;
opts.pathspec.strings = &onefile;
cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected1, buf.ptr);
git_buf_clear(&buf);
git_patch_free(patch);
git_diff_free(diff);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected2, buf.ptr);
git_buf_clear(&buf);
git_patch_free(patch);
git_diff_free(diff);
cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected3, buf.ptr);
git_buf_clear(&buf);
git_patch_free(patch);
git_diff_free(diff);
cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected4, buf.ptr);
git_buf_clear(&buf);
git_patch_free(patch);
git_diff_free(diff);
git_buf_dispose(&buf);
git_tree_free(one);
git_config_free(cfg);
}
void test_diff_patch__hunks_have_correct_line_numbers(void)
{
git_config *cfg;
git_tree *head;
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
git_diff *diff;
git_patch *patch;
const git_diff_delta *delta;
const git_diff_hunk *hunk;
const git_diff_line *line;
size_t hunklen;
git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n";
g_repo = cl_git_sandbox_init("renames");
cl_git_pass(git_config_new(&cfg));
git_repository_set_config(g_repo, cfg);
git_config_free(cfg);
git_repository_reinit_filesystem(g_repo, false);
cl_git_pass(
git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
cl_git_rewritefile("renames/songof7cities.txt", new_content);
cl_git_pass(git_repository_head_tree(&head, g_repo));
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
cl_assert_equal_i(2, (int)git_patch_num_hunks(patch));
/* check hunk 0 */
cl_git_pass(
git_patch_get_hunk(&hunk, &hunklen, patch, 0));
cl_assert_equal_i(18, (int)hunklen);
cl_assert_equal_i(6, (int)hunk->old_start);
cl_assert_equal_i(15, (int)hunk->old_lines);
cl_assert_equal_i(6, (int)hunk->new_start);
cl_assert_equal_i(9, (int)hunk->new_lines);
cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 0));
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 0));
cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
cl_assert_equal_i(6, line->old_lineno);
cl_assert_equal_i(6, line->new_lineno);
cl_assert_equal_i(-1, line->content_offset);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
cl_assert_equal_i(9, line->old_lineno);
cl_assert_equal_i(-1, line->new_lineno);
cl_assert_equal_i(252, line->content_offset);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 12));
cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("This is some new text;\n", actual.ptr);
cl_assert_equal_i(-1, line->old_lineno);
cl_assert_equal_i(9, line->new_lineno);
cl_assert_equal_i(252, line->content_offset);
/* check hunk 1 */
cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 1));
cl_assert_equal_i(18, (int)hunklen);
cl_assert_equal_i(31, (int)hunk->old_start);
cl_assert_equal_i(15, (int)hunk->old_lines);
cl_assert_equal_i(25, (int)hunk->new_start);
cl_assert_equal_i(9, (int)hunk->new_lines);
cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 1));
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 0));
cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
cl_assert_equal_i(31, line->old_lineno);
cl_assert_equal_i(25, line->new_lineno);
cl_assert_equal_i(-1, line->content_offset);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 3));
cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
cl_assert_equal_i(34, line->old_lineno);
cl_assert_equal_i(-1, line->new_lineno);
cl_assert_equal_i(1468, line->content_offset);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 12));
cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("Another replacement;\n", actual.ptr);
cl_assert_equal_i(-1, line->old_lineno);
cl_assert_equal_i(28, line->new_lineno);
cl_assert_equal_i(1066, line->content_offset);
git_patch_free(patch);
git_diff_free(diff);
/* Let's check line numbers when there is no newline */
git_buf_rtrim(&old_content);
cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
cl_assert_equal_i(1, (int)git_patch_num_hunks(patch));
/* check hunk 0 */
cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 0));
cl_assert_equal_i(6, (int)hunklen);
cl_assert_equal_i(46, (int)hunk->old_start);
cl_assert_equal_i(4, (int)hunk->old_lines);
cl_assert_equal_i(46, (int)hunk->new_start);
cl_assert_equal_i(4, (int)hunk->new_lines);
cl_assert_equal_i(6, (int)git_patch_num_lines_in_hunk(patch, 0));
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 1));
cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
cl_assert_equal_i(47, line->old_lineno);
cl_assert_equal_i(47, line->new_lineno);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 2));
cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("\n", actual.ptr);
cl_assert_equal_i(48, line->old_lineno);
cl_assert_equal_i(48, line->new_lineno);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr);
cl_assert_equal_i(49, line->old_lineno);
cl_assert_equal_i(-1, line->new_lineno);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 4));
cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr);
cl_assert_equal_i(-1, line->old_lineno);
cl_assert_equal_i(49, line->new_lineno);
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 5));
cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)line->origin);
cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
cl_assert_equal_i(-1, line->old_lineno);
cl_assert_equal_i(49, line->new_lineno);
git_patch_free(patch);
git_diff_free(diff);
git_buf_dispose(&actual);
git_buf_dispose(&old_content);
git_tree_free(head);
}
static void check_single_patch_stats(
git_repository *repo, size_t hunks,
size_t adds, size_t dels, size_t ctxt, size_t *sizes,
const char *expected)
{
git_diff *diff;
git_patch *patch;
const git_diff_delta *delta;
size_t actual_ctxt, 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_patch_from_diff(&patch, diff, 0));
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
cl_assert_equal_i((int)hunks, (int)git_patch_num_hunks(patch));
cl_git_pass( git_patch_line_stats(
&actual_ctxt, &actual_adds, &actual_dels, patch) );
cl_assert_equal_sz(ctxt, actual_ctxt);
cl_assert_equal_sz(adds, actual_adds);
cl_assert_equal_sz(dels, actual_dels);
if (expected != NULL) {
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected, buf.ptr);
git_buf_dispose(&buf);
cl_assert_equal_sz(
strlen(expected), git_patch_size(patch, 1, 1, 1));
}
if (sizes) {
if (sizes[0])
cl_assert_equal_sz(sizes[0], git_patch_size(patch, 0, 0, 0));
if (sizes[1])
cl_assert_equal_sz(sizes[1], git_patch_size(patch, 1, 0, 0));
if (sizes[2])
cl_assert_equal_sz(sizes[2], git_patch_size(patch, 1, 1, 0));
}
/* walk lines in hunk with basic sanity checks */
for (; hunks > 0; --hunks) {
size_t i, max_i;
const git_diff_line *line;
int last_new_lineno = -1, last_old_lineno = -1;
max_i = git_patch_num_lines_in_hunk(patch, hunks - 1);
for (i = 0; i < max_i; ++i) {
int expected = 1;
cl_git_pass(
git_patch_get_line_in_hunk(&line, patch, hunks - 1, i));
if (line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
line->origin == GIT_DIFF_LINE_DEL_EOFNL ||
line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
expected = 0;
if (line->old_lineno >= 0) {
if (last_old_lineno >= 0)
cl_assert_equal_i(
expected, line->old_lineno - last_old_lineno);
last_old_lineno = line->old_lineno;
}
if (line->new_lineno >= 0) {
if (last_new_lineno >= 0)
cl_assert_equal_i(
expected, line->new_lineno - last_new_lineno);
last_new_lineno = line->new_lineno;
}
}
}
git_patch_free(patch);
git_diff_free(diff);
}
void test_diff_patch__line_counts_with_eofnl(void)
{
git_config *cfg;
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");
cl_git_pass(git_config_new(&cfg));
git_repository_set_config(g_repo, cfg);
git_config_free(cfg);
git_repository_reinit_filesystem(g_repo, false);
cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
/* remove first line */
end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
git_buf_consume(&content, end);
cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
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, NULL);
/* add trailing whitespace */
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
cl_git_pass(git_index_write(index));
git_index_free(index);
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, NULL);
/* no trailing whitespace as context line */
{
/* walk back a couple lines, make space and insert char */
char *scan = content.ptr + content.size;
int i;
for (i = 0; i < 5; ++i) {
for (--scan; scan > content.ptr && *scan != '\n'; --scan)
/* seek to prev \n */;
}
cl_assert(scan > content.ptr);
/* overwrite trailing \n with right-shifted content */
memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
/* insert '#' char into space we created */
scan[1] = '#';
}
cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
check_single_patch_stats(
g_repo, 1, 1, 1, 6, expected_sizes, expected);
git_buf_dispose(&content);
}
void test_diff_patch__can_strip_bad_utf8(void)
{
const char *a = "A " UTF8_HUNK_HEADER
" B\n"
" C\n"
" D\n"
" E\n"
" F\n"
" G\n"
" H\n"
" I\n"
" J\n"
" K\n"
"L " UTF8_HUNK_HEADER
" M\n"
" N\n"
" O\n"
" P\n"
" Q\n"
" R\n"
" S\n"
" T\n"
" U\n"
" V\n";
const char *b = "A " UTF8_HUNK_HEADER
" B\n"
" C\n"
" D\n"
" E modified\n"
" F\n"
" G\n"
" H\n"
" I\n"
" J\n"
" K\n"
"L " UTF8_HUNK_HEADER
" M\n"
" N\n"
" O\n"
" P modified\n"
" Q\n"
" R\n"
" S\n"
" T\n"
" U\n"
" V\n";
const char *expected = "diff --git a/file b/file\n"
"index d0647c4..7827ce5 100644\n"
"--- a/file\n"
"+++ b/file\n"
"@@ -2,7 +2,7 @@ A " UTF8_TRUNCATED_A_HUNK_HEADER
" B\n"
" C\n"
" D\n"
"- E\n"
"+ E modified\n"
" F\n"
" G\n"
" H\n"
"@@ -13,7 +13,7 @@ L " UTF8_TRUNCATED_L_HUNK_HEADER
" M\n"
" N\n"
" O\n"
"- P\n"
"+ P modified\n"
" Q\n"
" R\n"
" S\n";
git_diff_options opts;
git_patch *patch;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
cl_git_pass(git_patch_from_buffers(&patch, a, strlen(a), NULL, b, strlen(b), NULL, &opts));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s(expected, buf.ptr);
git_patch_free(patch);
git_buf_dispose(&buf);
}
+93
View File
@@ -0,0 +1,93 @@
#include "clar_libgit2.h"
#include "patch/patch_common.h"
static void verify_patch_id(const char *diff_content, const char *expected_id)
{
git_oid expected_oid, actual_oid;
git_diff *diff;
cl_git_pass(git_oid_fromstr(&expected_oid, expected_id));
cl_git_pass(git_diff_from_buffer(&diff, diff_content, strlen(diff_content)));
cl_git_pass(git_diff_patchid(&actual_oid, diff, NULL));
cl_assert_equal_oid(&expected_oid, &actual_oid);
git_diff_free(diff);
}
void test_diff_patchid__simple_commit(void)
{
verify_patch_id(PATCH_SIMPLE_COMMIT, "06094b1948b878b7d9ff7560b4eae672a014b0ec");
}
void test_diff_patchid__deleted_file(void)
{
verify_patch_id(PATCH_DELETE_ORIGINAL, "d18507fe189f49c028b32c8c34e1ad98dd6a1aad");
verify_patch_id(PATCH_DELETED_FILE_2_HUNKS, "f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7");
}
void test_diff_patchid__created_file(void)
{
verify_patch_id(PATCH_ADD_ORIGINAL, "a7d39379308021465ae2ce65e338c048a3110db6");
}
void test_diff_patchid__binary_file(void)
{
verify_patch_id(PATCH_ADD_BINARY_NOT_PRINTED, "2b31236b485faa30cf4dd33e4d6539829996739f");
}
void test_diff_patchid__renamed_file(void)
{
verify_patch_id(PATCH_RENAME_EXACT, "4666d50cea4976f6f727448046d43461912058fd");
verify_patch_id(PATCH_RENAME_SIMILAR, "a795087575fcb940227be524488bedd6b3d3f438");
}
void test_diff_patchid__modechange(void)
{
verify_patch_id(PATCH_MODECHANGE_UNCHANGED, "dbf3423ee98375ef1c72a79fbd29a049a2bae771");
verify_patch_id(PATCH_MODECHANGE_MODIFIED, "93aba696e1bbd2bbb73e3e3e62ed71f232137657");
}
void test_diff_patchid__shuffle_hunks(void)
{
verify_patch_id(PATCH_DELETED_FILE_2_HUNKS_SHUFFLED, "f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7");
}
void test_diff_patchid__filename_with_spaces(void)
{
verify_patch_id(PATCH_APPEND_NO_NL, "f0ba05413beaef743b630e796153839462ee477a");
}
void test_diff_patchid__multiple_hunks(void)
{
verify_patch_id(PATCH_MULTIPLE_HUNKS, "81e26c34643d17f521e57c483a6a637e18ba1f57");
}
void test_diff_patchid__multiple_files(void)
{
verify_patch_id(PATCH_MULTIPLE_FILES, "192d1f49d23f2004517963aecd3f8a6c467f50ff");
}
void test_diff_patchid__same_diff_with_differing_whitespace_has_same_id(void)
{
const char *tabs =
"diff --git a/file.txt b/file.txt\n"
"index 8fecc09..1d43a92 100644\n"
"--- a/file.txt\n"
"+++ b/file.txt\n"
"@@ -1 +1 @@\n"
"-old text\n"
"+ new text\n";
const char *spaces =
"diff --git a/file.txt b/file.txt\n"
"index 8fecc09..1d43a92 100644\n"
"--- a/file.txt\n"
"+++ b/file.txt\n"
"@@ -1 +1 @@\n"
"-old text\n"
"+ new text\n";
const char *id = "11efdd13c30f7a1056eac2ae2fb952da475e2c23";
verify_patch_id(tabs, id);
verify_patch_id(spaces, id);
}
+93
View File
@@ -0,0 +1,93 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_pathspec__initialize(void)
{
g_repo = cl_git_sandbox_init("status");
}
void test_diff_pathspec__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_diff_pathspec__0(void)
{
const char *a_commit = "26a125ee"; /* the current HEAD */
const char *b_commit = "0017bd4a"; /* the start */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
git_strarray paths = { NULL, 1 };
char *path;
git_pathspec *ps;
git_pathspec_match_list *matches;
cl_assert(a);
cl_assert(b);
path = "*_file";
paths.strings = &path;
cl_git_pass(git_pathspec_new(&ps, &paths));
cl_git_pass(git_pathspec_match_tree(&matches, a, GIT_PATHSPEC_DEFAULT, ps));
cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
cl_assert_equal_s("current_file", git_pathspec_match_list_entry(matches,0));
cl_assert(git_pathspec_match_list_diff_entry(matches,0) == NULL);
git_pathspec_match_list_free(matches);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, NULL, a, &opts));
cl_git_pass(git_pathspec_match_diff(
&matches, diff, GIT_PATHSPEC_DEFAULT, ps));
cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
cl_assert_equal_s("current_file",
git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
cl_assert_equal_i(GIT_DELTA_ADDED,
(int)git_pathspec_match_list_diff_entry(matches,0)->status);
git_pathspec_match_list_free(matches);
git_diff_free(diff);
diff = NULL;
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
cl_git_pass(git_pathspec_match_diff(
&matches, diff, GIT_PATHSPEC_DEFAULT, ps));
cl_assert_equal_i(3, (int)git_pathspec_match_list_entrycount(matches));
cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
cl_assert_equal_s("subdir/current_file",
git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
cl_assert_equal_i(GIT_DELTA_DELETED,
(int)git_pathspec_match_list_diff_entry(matches,0)->status);
git_pathspec_match_list_free(matches);
git_diff_free(diff);
diff = NULL;
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
cl_git_pass(git_pathspec_match_diff(
&matches, diff, GIT_PATHSPEC_DEFAULT, ps));
cl_assert_equal_i(4, (int)git_pathspec_match_list_entrycount(matches));
cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
cl_assert_equal_s("modified_file",
git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
cl_assert_equal_i(GIT_DELTA_MODIFIED,
(int)git_pathspec_match_list_diff_entry(matches,0)->status);
git_pathspec_match_list_free(matches);
git_diff_free(diff);
diff = NULL;
git_tree_free(a);
git_tree_free(b);
git_pathspec_free(ps);
}
+129
View File
@@ -0,0 +1,129 @@
/* This test exercises the problem described in
** https://github.com/libgit2/libgit2/pull/3568
** where deleting a directory during a diff/status
** operation can cause an access violation.
**
** The "test_diff_racediffiter__basic() test confirms
** the normal operation of diff on the given repo.
**
** The "test_diff_racediffiter__racy_rmdir() test
** uses the new diff progress callback to delete
** a directory (after the initial readdir() and
** before the directory itself is visited) causing
** the recursion and iteration to fail.
*/
#include "clar_libgit2.h"
#include "diff_helpers.h"
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
void test_diff_racediffiter__initialize(void)
{
}
void test_diff_racediffiter__cleanup(void)
{
cl_git_sandbox_cleanup();
}
typedef struct
{
const char *path;
git_delta_t t;
} basic_payload;
static int notify_cb__basic(
const git_diff *diff_so_far,
const git_diff_delta *delta_to_add,
const char *matched_pathspec,
void *payload)
{
basic_payload *exp = (basic_payload *)payload;
basic_payload *e;
GIT_UNUSED(diff_so_far);
GIT_UNUSED(matched_pathspec);
for (e = exp; e->path; e++) {
if (strcmp(e->path, delta_to_add->new_file.path) == 0) {
cl_assert_equal_i(e->t, delta_to_add->status);
return 0;
}
}
cl_assert(0);
return GIT_ENOTFOUND;
}
void test_diff_racediffiter__basic(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_repository *repo = cl_git_sandbox_init("diff");
git_diff *diff;
basic_payload exp_a[] = {
{ "another.txt", GIT_DELTA_MODIFIED },
{ "readme.txt", GIT_DELTA_MODIFIED },
{ "zzzzz/", GIT_DELTA_IGNORED },
{ NULL, 0 }
};
cl_must_pass(p_mkdir("diff/zzzzz", 0777));
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
opts.notify_cb = notify_cb__basic;
opts.payload = exp_a;
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
git_diff_free(diff);
}
typedef struct {
bool first_time;
const char *dir;
basic_payload *basic_payload;
} racy_payload;
static int notify_cb__racy_rmdir(
const git_diff *diff_so_far,
const git_diff_delta *delta_to_add,
const char *matched_pathspec,
void *payload)
{
racy_payload *pay = (racy_payload *)payload;
if (pay->first_time) {
cl_must_pass(p_rmdir(pay->dir));
pay->first_time = false;
}
return notify_cb__basic(diff_so_far, delta_to_add, matched_pathspec, pay->basic_payload);
}
void test_diff_racediffiter__racy(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_repository *repo = cl_git_sandbox_init("diff");
git_diff *diff;
basic_payload exp_a[] = {
{ "another.txt", GIT_DELTA_MODIFIED },
{ "readme.txt", GIT_DELTA_MODIFIED },
{ NULL, 0 }
};
racy_payload pay = { true, "diff/zzzzz", exp_a };
cl_must_pass(p_mkdir("diff/zzzzz", 0777));
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
opts.notify_cb = notify_cb__racy_rmdir;
opts.payload = &pay;
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
git_diff_free(diff);
}
File diff suppressed because it is too large Load Diff
+345
View File
@@ -0,0 +1,345 @@
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "commit.h"
#include "diff.h"
#include "diff_generate.h"
static git_repository *_repo;
static git_diff_stats *_stats;
void test_diff_stats__initialize(void)
{
_repo = cl_git_sandbox_init("diff_format_email");
}
void test_diff_stats__cleanup(void)
{
git_diff_stats_free(_stats); _stats = NULL;
cl_git_sandbox_cleanup();
}
static void diff_stats_from_commit_oid(
git_diff_stats **stats, const char *oidstr, bool rename)
{
git_oid oid;
git_commit *commit;
git_diff *diff;
git_oid_fromstr(&oid, oidstr);
cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
cl_git_pass(git_diff__commit(&diff, _repo, commit, NULL));
if (rename)
cl_git_pass(git_diff_find_similar(diff, NULL));
cl_git_pass(git_diff_get_stats(stats, diff));
git_diff_free(diff);
git_commit_free(commit);
}
void test_diff_stats__stat(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false);
cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(5, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(3, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
git_buf_dispose(&buf);
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 80));
cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
git_buf_dispose(&buf);
}
void test_diff_stats__multiple_hunks(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt | 5 +++--\n" \
" file3.txt | 6 ++++--\n" \
" 2 files changed, 7 insertions(+), 4 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false);
cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(7, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(4, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__numstat(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
"3 2 file2.txt\n"
"4 2 file3.txt\n";
diff_stats_from_commit_oid(
&_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false);
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__shortstat(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" 1 file changed, 5 insertions(+), 3 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false);
cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(5, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(3, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__shortstat_noinsertions(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" 1 file changed, 2 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "06b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6", false);
cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(2, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__shortstat_nodeletions(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" 1 file changed, 3 insertions(+)\n";
diff_stats_from_commit_oid(
&_stats, "5219b9784f9a92d7bd7cb567a6d6a21bfb86697e", false);
cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(3, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt => file2.txt.renamed | 1 +\n"
" file3.txt => file3.txt.renamed | 4 +++-\n"
" 2 files changed, 4 insertions(+), 1 deletion(-)\n";
diff_stats_from_commit_oid(
&_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", true);
cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(4, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(1, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename_nochanges(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt.renamed => file2.txt.renamed2 | 0\n"
" file3.txt.renamed => file3.txt.renamed2 | 0\n"
" 2 files changed, 0 insertions(+), 0 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", true);
cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename_and_modifiy(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt.renamed2 | 2 +-\n"
" file3.txt.renamed2 => file3.txt.renamed | 0\n"
" 2 files changed, 1 insertion(+), 1 deletion(-)\n";
diff_stats_from_commit_oid(
&_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", true);
cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(1, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(1, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename_in_subdirectory(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" dir/{orig.txt => renamed.txt} | 0\n"
" 1 file changed, 0 insertions(+), 0 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "0db2a262bc8c5c3cba55254730045a8258da7a37", true);
cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename_no_find(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt | 5 -----\n"
" file2.txt.renamed | 6 ++++++\n"
" file3.txt | 5 -----\n"
" file3.txt.renamed | 7 +++++++\n"
" 4 files changed, 13 insertions(+), 10 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", false);
cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(13, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(10, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename_nochanges_no_find(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt.renamed | 6 ------\n"
" file2.txt.renamed2 | 6 ++++++\n"
" file3.txt.renamed | 7 -------\n"
" file3.txt.renamed2 | 7 +++++++\n"
" 4 files changed, 13 insertions(+), 13 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", false);
cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(13, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(13, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__rename_and_modify_no_find(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file2.txt.renamed2 | 2 +-\n"
" file3.txt.renamed | 7 +++++++\n"
" file3.txt.renamed2 | 7 -------\n"
" 3 files changed, 8 insertions(+), 8 deletions(-)\n";
diff_stats_from_commit_oid(
&_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", false);
cl_assert_equal_sz(3, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(8, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(8, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__binary(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" binary.bin | Bin 3 -> 0 bytes\n"
" 1 file changed, 0 insertions(+), 0 deletions(-)\n";
/* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */
diff_stats_from_commit_oid(
&_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false);
cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__binary_numstat(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
"- - binary.bin\n";
diff_stats_from_commit_oid(
&_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false);
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
void test_diff_stats__mode_change(void)
{
git_buf buf = GIT_BUF_INIT;
const char *stat =
" file1.txt.renamed | 0\n" \
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
" mode change 100644 => 100755 file1.txt.renamed\n";
diff_stats_from_commit_oid(
&_stats, "7ade76dd34bba4733cf9878079f9fd4a456a9189", false);
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY, 0));
cl_assert_equal_s(stat, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
+542
View File
@@ -0,0 +1,542 @@
#include "clar_libgit2.h"
#include "repository.h"
#include "posix.h"
#include "diff_helpers.h"
#include "../submodule/submodule_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_submodules__initialize(void)
{
}
void test_diff_submodules__cleanup(void)
{
cl_git_sandbox_cleanup();
}
#define get_buf_ptr(buf) ((buf)->asize ? (buf)->ptr : NULL)
static void check_diff_patches_at_line(
git_diff *diff, const char **expected, const char *file, int line)
{
const git_diff_delta *delta;
git_patch *patch = NULL;
size_t d, num_d = git_diff_num_deltas(diff);
git_buf buf = GIT_BUF_INIT;
for (d = 0; d < num_d; ++d, git_patch_free(patch)) {
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
if (delta->status == GIT_DELTA_UNMODIFIED) {
cl_assert_at_line(expected[d] == NULL, file, line);
continue;
}
if (expected[d] && !strcmp(expected[d], "<SKIP>"))
continue;
if (expected[d] && !strcmp(expected[d], "<UNTRACKED>")) {
cl_assert_at_line(delta->status == GIT_DELTA_UNTRACKED, file, line);
continue;
}
if (expected[d] && !strcmp(expected[d], "<END>")) {
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_at_line(!strcmp(expected[d], "<END>"), file, line);
}
cl_git_pass(git_patch_to_buf(&buf, patch));
clar__assert_equal(
file, line, "expected diff did not match actual diff", 1,
"%s", expected[d], get_buf_ptr(&buf));
git_buf_dispose(&buf);
}
cl_assert_at_line(expected[d] && !strcmp(expected[d], "<END>"), file, line);
}
#define check_diff_patches(diff, exp) \
check_diff_patches_at_line(diff, exp, __FILE__, __LINE__)
void test_diff_submodules__unmodified_submodule(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
static const char *expected[] = {
"<SKIP>", /* .gitmodules */
NULL, /* added */
NULL, /* ignored */
"diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
NULL, /* testrepo.git */
NULL, /* unmodified */
NULL, /* untracked */
"<END>"
};
g_repo = setup_fixture_submodules();
opts.flags = GIT_DIFF_INCLUDE_IGNORED |
GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_INCLUDE_UNMODIFIED;
opts.old_prefix = "a"; opts.new_prefix = "b";
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected);
git_diff_free(diff);
}
void test_diff_submodules__dirty_submodule(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
static const char *expected[] = {
"<SKIP>", /* .gitmodules */
NULL, /* added */
NULL, /* ignored */
"diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
"diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
NULL, /* unmodified */
NULL, /* untracked */
"<END>"
};
g_repo = setup_fixture_submodules();
cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
opts.flags = GIT_DIFF_INCLUDE_IGNORED |
GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_INCLUDE_UNMODIFIED;
opts.old_prefix = "a"; opts.new_prefix = "b";
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected);
git_diff_free(diff);
}
void test_diff_submodules__dirty_submodule_2(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL, *diff2 = NULL;
char *smpath = "testrepo";
static const char *expected_none[] = {
"<END>"
};
static const char *expected_dirty[] = {
"diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
"<END>"
};
g_repo = setup_fixture_submodules();
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_SHOW_UNTRACKED_CONTENT |
GIT_DIFF_RECURSE_UNTRACKED_DIRS |
GIT_DIFF_DISABLE_PATHSPEC_MATCH;
opts.old_prefix = "a"; opts.new_prefix = "b";
opts.pathspec.count = 1;
opts.pathspec.strings = &smpath;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_none);
git_diff_free(diff);
cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
{
git_tree *head;
cl_git_pass(git_repository_head_tree(&head, g_repo));
cl_git_pass(git_diff_tree_to_index(&diff2, g_repo, head, NULL, &opts));
cl_git_pass(git_diff_merge(diff, diff2));
git_diff_free(diff2);
git_tree_free(head);
check_diff_patches(diff, expected_dirty);
}
git_diff_free(diff);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
}
void test_diff_submodules__submod2_index_to_wd(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
static const char *expected[] = {
"<SKIP>", /* .gitmodules */
"<UNTRACKED>", /* not-submodule */
"<UNTRACKED>", /* not */
"diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
"<UNTRACKED>", /* sm_changed_head- */
"<UNTRACKED>", /* sm_changed_head_ */
"diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
"diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
"<END>"
};
g_repo = setup_fixture_submod2();
/* bracket existing submodule with similarly named items */
cl_git_mkfile("submod2/sm_changed_head-", "hello");
cl_git_mkfile("submod2/sm_changed_head_", "hello");
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
opts.old_prefix = "a"; opts.new_prefix = "b";
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected);
git_diff_free(diff);
}
void test_diff_submodules__submod2_head_to_index(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_tree *head;
git_diff *diff = NULL;
static const char *expected[] = {
"<SKIP>", /* .gitmodules */
"diff --git a/sm_added_and_uncommited b/sm_added_and_uncommited\nnew file mode 160000\nindex 0000000..4800958\n--- /dev/null\n+++ b/sm_added_and_uncommited\n@@ -0,0 +1 @@\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n", /* sm_added_and_uncommited */
"<END>"
};
g_repo = setup_fixture_submod2();
cl_git_pass(git_repository_head_tree(&head, g_repo));
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
opts.old_prefix = "a"; opts.new_prefix = "b";
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts));
check_diff_patches(diff, expected);
git_diff_free(diff);
git_tree_free(head);
}
void test_diff_submodules__invalid_cache(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
git_submodule *sm;
char *smpath = "sm_changed_head";
git_repository *smrepo;
git_index *smindex;
static const char *expected_baseline[] = {
"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
"<END>"
};
static const char *expected_unchanged[] = { "<END>" };
static const char *expected_dirty[] = {
"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247-dirty\n",
"<END>"
};
static const char *expected_moved[] = {
"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae\n",
"<END>"
};
static const char *expected_moved_dirty[] = {
"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae-dirty\n",
"<END>"
};
g_repo = setup_fixture_submod2();
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
opts.old_prefix = "a"; opts.new_prefix = "b";
opts.pathspec.count = 1;
opts.pathspec.strings = &smpath;
/* baseline */
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_baseline);
git_diff_free(diff);
/* update index with new HEAD */
cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
cl_git_pass(git_submodule_add_to_index(sm, 1));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
git_diff_free(diff);
/* create untracked file in submodule working directory */
cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello");
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
git_diff_free(diff);
/* modify tracked file in submodule working directory */
cl_git_append2file(
"submod2/sm_changed_head/file_to_modify", "\nmore stuff\n");
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
git_diff_free(diff);
/* add file to index in submodule */
cl_git_pass(git_submodule_open(&smrepo, sm));
cl_git_pass(git_repository_index(&smindex, smrepo));
cl_git_pass(git_index_add_bypath(smindex, "file_to_modify"));
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
git_diff_free(diff);
/* commit changed index of submodule */
cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it");
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved);
git_diff_free(diff);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_ALL);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
git_diff_free(diff);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved_dirty);
git_diff_free(diff);
p_unlink("submod2/sm_changed_head/new_around_here");
git_submodule_free(sm);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved);
git_diff_free(diff);
git_index_free(smindex);
git_repository_free(smrepo);
}
void test_diff_submodules__diff_ignore_options(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
git_config *cfg;
static const char *expected_normal[] = {
"<SKIP>", /* .gitmodules */
"<UNTRACKED>", /* not-submodule */
"<UNTRACKED>", /* not */
"diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
"diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
"diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
"<END>"
};
static const char *expected_ignore_all[] = {
"<SKIP>", /* .gitmodules */
"<UNTRACKED>", /* not-submodule */
"<UNTRACKED>", /* not */
"<END>"
};
static const char *expected_ignore_dirty[] = {
"<SKIP>", /* .gitmodules */
"<UNTRACKED>", /* not-submodule */
"<UNTRACKED>", /* not */
"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
"<END>"
};
g_repo = setup_fixture_submod2();
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
opts.old_prefix = "a"; opts.new_prefix = "b";
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_normal);
git_diff_free(diff);
opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_ignore_all);
git_diff_free(diff);
opts.flags &= ~GIT_DIFF_IGNORE_SUBMODULES;
opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_ignore_all);
git_diff_free(diff);
opts.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_ignore_dirty);
git_diff_free(diff);
opts.ignore_submodules = 0;
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", false));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_normal);
git_diff_free(diff);
cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", true));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_ignore_all);
git_diff_free(diff);
cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "none"));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_normal);
git_diff_free(diff);
cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "dirty"));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_ignore_dirty);
git_diff_free(diff);
git_config_free(cfg);
}
void test_diff_submodules__skips_empty_includes_used(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
diff_expects exp;
/* A side effect of of Git's handling of untracked directories and
* auto-ignoring of ".git" entries is that a newly initialized Git
* repo inside another repo will be skipped by diff, but one that
* actually has a commit it in will show as an untracked directory.
* Let's make sure that works.
*/
g_repo = cl_git_sandbox_init("empty_standard_repo");
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(0, exp.files);
git_diff_free(diff);
{
git_repository *r2;
cl_git_pass(git_repository_init(&r2, "empty_standard_repo/subrepo", 0));
git_repository_free(r2);
}
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
git_diff_free(diff);
cl_git_mkfile("empty_standard_repo/subrepo/README.txt", "hello\n");
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
git_diff_free(diff);
}
static void ensure_submodules_found(
git_repository *repo,
const char **paths,
size_t cnt)
{
git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
const git_diff_delta *delta;
size_t i, pathlen;
opts.pathspec.strings = (char **)paths;
opts.pathspec.count = cnt;
git_diff_index_to_workdir(&diff, repo, NULL, &opts);
cl_assert_equal_i(cnt, git_diff_num_deltas(diff));
for (i = 0; i < cnt; i++) {
delta = git_diff_get_delta(diff, i);
/* ensure that the given path is returned w/o trailing slashes. */
pathlen = strlen(opts.pathspec.strings[i]);
while (pathlen && opts.pathspec.strings[i][pathlen - 1] == '/')
pathlen--;
cl_assert_equal_strn(opts.pathspec.strings[i], delta->new_file.path, pathlen);
}
git_diff_free(diff);
}
void test_diff_submodules__can_be_identified_by_trailing_slash_in_pathspec(void)
{
const char *one_path_without_slash[] = { "sm_changed_head" };
const char *one_path_with_slash[] = { "sm_changed_head/" };
const char *many_paths_without_slashes[] = { "sm_changed_head", "sm_changed_index" };
const char *many_paths_with_slashes[] = { "sm_changed_head/", "sm_changed_index/" };
g_repo = setup_fixture_submod2();
ensure_submodules_found(g_repo, one_path_without_slash, ARRAY_SIZE(one_path_without_slash));
ensure_submodules_found(g_repo, one_path_with_slash, ARRAY_SIZE(one_path_with_slash));
ensure_submodules_found(g_repo, many_paths_without_slashes, ARRAY_SIZE(many_paths_without_slashes));
ensure_submodules_found(g_repo, many_paths_with_slashes, ARRAY_SIZE(many_paths_with_slashes));
}
+526
View File
@@ -0,0 +1,526 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
static git_diff_options opts;
static git_diff *diff;
static git_tree *a, *b;
static diff_expects expect;
void test_diff_tree__initialize(void)
{
cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
memset(&expect, 0, sizeof(expect));
diff = NULL;
a = NULL;
b = NULL;
}
void test_diff_tree__cleanup(void)
{
git_diff_free(diff);
git_tree_free(a);
git_tree_free(b);
cl_git_sandbox_cleanup();
}
void test_diff_tree__0(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "605812a";
const char *b_commit = "370fe9ec22";
const char *c_commit = "f5b0af1fb4f5c";
git_tree *c;
g_repo = cl_git_sandbox_init("attr");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
opts.context_lines = 1;
opts.interhunk_lines = 1;
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(5, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(5, expect.hunks);
cl_assert_equal_i(7 + 24 + 1 + 6 + 6, expect.lines);
cl_assert_equal_i(1, expect.line_ctxt);
cl_assert_equal_i(24 + 1 + 5 + 5, expect.line_adds);
cl_assert_equal_i(7 + 1, expect.line_dels);
git_diff_free(diff);
diff = NULL;
memset(&expect, 0, sizeof(expect));
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, b, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(2, expect.files);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(2, expect.hunks);
cl_assert_equal_i(8 + 15, expect.lines);
cl_assert_equal_i(1, expect.line_ctxt);
cl_assert_equal_i(1, expect.line_adds);
cl_assert_equal_i(7 + 14, expect.line_dels);
git_tree_free(c);
}
#define DIFF_OPTS(FLAGS, CTXT) \
{GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_UNSPECIFIED, \
{NULL,0}, NULL, NULL, NULL, (CTXT), 1}
void test_diff_tree__options(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "6bab5c79cd5140d0";
const char *b_commit = "605812ab7fe421fdd";
const char *c_commit = "f5b0af1fb4f5";
const char *d_commit = "a97cc019851";
git_tree *c, *d;
diff_expects actual;
int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
git_diff_options test_options[] = {
/* a vs b tests */
DIFF_OPTS(GIT_DIFF_NORMAL, 1),
DIFF_OPTS(GIT_DIFF_NORMAL, 3),
DIFF_OPTS(GIT_DIFF_REVERSE, 2),
DIFF_OPTS(GIT_DIFF_FORCE_TEXT, 2),
/* c vs d tests */
DIFF_OPTS(GIT_DIFF_NORMAL, 3),
DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE, 3),
DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3),
DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_EOL, 3),
DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1),
};
/* to generate these values:
* - cd to tests/resources/attr,
* - mv .gitted .git
* - git diff [options] 6bab5c79cd5140d0 605812ab7fe421fdd
* - mv .git .gitted
*/
#define EXPECT_STATUS_ADM(ADDS,DELS,MODS) { 0, ADDS, DELS, MODS, 0, 0, 0, 0, 0 }
diff_expects test_expects[] = {
/* a vs b tests */
{ 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 51, 2, 46, 3 },
{ 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 53, 4, 46, 3 },
{ 5, 0, EXPECT_STATUS_ADM(0, 3, 2), 4, 0, 0, 52, 3, 3, 46 },
{ 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 5, 0, 0, 54, 3, 47, 4 },
/* c vs d tests */
{ 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 22, 9, 10, 3 },
{ 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 19, 12, 7, 0 },
{ 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
{ 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
{ 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 18, 11, 0, 7 },
{ 0 },
};
diff_expects *expected;
int i, j;
g_repo = cl_git_sandbox_init("attr");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
cl_assert((d = resolve_commit_oid_to_tree(g_repo, d_commit)) != NULL);
for (i = 0; test_expects[i].files > 0; i++) {
memset(&actual, 0, sizeof(actual)); /* clear accumulator */
opts = test_options[i];
if (test_ab_or_cd[i] == 0)
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
else
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, d, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &actual));
expected = &test_expects[i];
cl_assert_equal_i(actual.files, expected->files);
for (j = GIT_DELTA_UNMODIFIED; j <= GIT_DELTA_TYPECHANGE; ++j)
cl_assert_equal_i(expected->file_status[j], actual.file_status[j]);
cl_assert_equal_i(actual.hunks, expected->hunks);
cl_assert_equal_i(actual.lines, expected->lines);
cl_assert_equal_i(actual.line_ctxt, expected->line_ctxt);
cl_assert_equal_i(actual.line_adds, expected->line_adds);
cl_assert_equal_i(actual.line_dels, expected->line_dels);
git_diff_free(diff);
diff = NULL;
}
git_tree_free(c);
git_tree_free(d);
}
void test_diff_tree__bare(void)
{
const char *a_commit = "8496071c1b46c85";
const char *b_commit = "be3563ae3f79";
g_repo = cl_git_sandbox_init("testrepo.git");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
opts.context_lines = 1;
opts.interhunk_lines = 1;
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(3, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(3, expect.hunks);
cl_assert_equal_i(4, expect.lines);
cl_assert_equal_i(0, expect.line_ctxt);
cl_assert_equal_i(3, expect.line_adds);
cl_assert_equal_i(1, expect.line_dels);
}
void test_diff_tree__merge(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "605812a";
const char *b_commit = "370fe9ec22";
const char *c_commit = "f5b0af1fb4f5c";
git_tree *c;
git_diff *diff1 = NULL, *diff2 = NULL;
g_repo = cl_git_sandbox_init("attr");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
cl_git_pass(git_diff_tree_to_tree(&diff1, g_repo, a, b, NULL));
cl_git_pass(git_diff_tree_to_tree(&diff2, g_repo, c, b, NULL));
git_tree_free(c);
cl_git_pass(git_diff_merge(diff1, diff2));
git_diff_free(diff2);
cl_git_pass(git_diff_foreach(
diff1, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(6, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(3, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(6, expect.hunks);
cl_assert_equal_i(59, expect.lines);
cl_assert_equal_i(1, expect.line_ctxt);
cl_assert_equal_i(36, expect.line_adds);
cl_assert_equal_i(22, expect.line_dels);
git_diff_free(diff1);
}
void test_diff_tree__larger_hunks(void)
{
const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
size_t d, num_d, h, num_h, l, num_l;
git_patch *patch;
const git_diff_hunk *hunk;
const git_diff_line *line;
g_repo = cl_git_sandbox_init("diff");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
opts.context_lines = 1;
opts.interhunk_lines = 0;
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
num_d = git_diff_num_deltas(diff);
for (d = 0; d < num_d; ++d) {
cl_git_pass(git_patch_from_diff(&patch, diff, d));
cl_assert(patch);
num_h = git_patch_num_hunks(patch);
for (h = 0; h < num_h; h++) {
cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
for (l = 0; l < num_l; ++l) {
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
cl_assert(line);
}
cl_git_fail(git_patch_get_line_in_hunk(&line, patch, h, num_l));
}
cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
git_patch_free(patch);
}
cl_git_fail(git_patch_from_diff(&patch, diff, num_d));
cl_assert_equal_i(2, (int)num_d);
}
void test_diff_tree__checks_options_version(void)
{
const char *a_commit = "8496071c1b46c85";
const char *b_commit = "be3563ae3f79";
const git_error *err;
g_repo = cl_git_sandbox_init("testrepo.git");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
opts.version = 0;
cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
err = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
git_error_clear();
opts.version = 1024;
cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
err = git_error_last();
}
void process_tree_to_tree_diffing(
const char *old_commit,
const char *new_commit)
{
g_repo = cl_git_sandbox_init("unsymlinked.git");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, old_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, new_commit)) != NULL);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, NULL, NULL, NULL, &expect));
}
void test_diff_tree__symlink_blob_mode_changed_to_regular_file(void)
{
/*
* $ git diff 7fccd7..806999
* diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
* deleted file mode 120000
* index 19bf568..0000000
* --- a/include/Nu/Nu.h
* +++ /dev/null
* @@ -1 +0,0 @@
* -../../objc/Nu.h
* \ No newline at end of file
* diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
* new file mode 100644
* index 0000000..f9e6561
* --- /dev/null
* +++ b/include/Nu/Nu.h
* @@ -0,0 +1 @@
* +awesome content
* diff --git a/objc/Nu.h b/objc/Nu.h
* deleted file mode 100644
* index f9e6561..0000000
* --- a/objc/Nu.h
* +++ /dev/null
* @@ -1 +0,0 @@
* -awesome content
*/
process_tree_to_tree_diffing("7fccd7", "806999");
cl_assert_equal_i(3, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
}
void test_diff_tree__symlink_blob_mode_changed_to_regular_file_as_typechange(void)
{
/*
* $ git diff 7fccd7..a8595c
* diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
* deleted file mode 120000
* index 19bf568..0000000
* --- a/include/Nu/Nu.h
* +++ /dev/null
* @@ -1 +0,0 @@
* -../../objc/Nu.h
* \ No newline at end of file
* diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
* new file mode 100755
* index 0000000..f9e6561
* --- /dev/null
* +++ b/include/Nu/Nu.h
* @@ -0,0 +1 @@
* +awesome content
* diff --git a/objc/Nu.h b/objc/Nu.h
* deleted file mode 100644
* index f9e6561..0000000
* --- a/objc/Nu.h
* +++ /dev/null
* @@ -1 +0,0 @@
* -awesome content
*/
opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
process_tree_to_tree_diffing("7fccd7", "a8595c");
cl_assert_equal_i(2, expect.files);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_TYPECHANGE]);
}
void test_diff_tree__regular_blob_mode_changed_to_executable_file(void)
{
/*
* $ git diff 806999..a8595c
* diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
* old mode 100644
* new mode 100755
*/
process_tree_to_tree_diffing("806999", "a8595c");
cl_assert_equal_i(1, expect.files);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
}
void test_diff_tree__issue_1397(void)
{
/* this test shows that it is not needed */
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_assert((a = resolve_commit_oid_to_tree(g_repo, "8a7ef04")) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, "7f483a7")) != NULL);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
cl_git_pass(git_diff_foreach(diff,
diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(1, expect.files);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
}
static void set_config_int(git_repository *repo, const char *name, int value)
{
git_config *cfg;
cl_git_pass(git_repository_config(&cfg, repo));
cl_git_pass(git_config_set_int32(cfg, name, value));
git_config_free(cfg);
}
void test_diff_tree__diff_configs(void)
{
const char *a_commit = "d70d245e";
const char *b_commit = "7a9e0b02";
g_repo = cl_git_sandbox_init("diff");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
cl_git_pass(git_diff_foreach(diff,
diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(2, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(6, expect.hunks);
cl_assert_equal_i(55, expect.lines);
cl_assert_equal_i(33, expect.line_ctxt);
cl_assert_equal_i(7, expect.line_adds);
cl_assert_equal_i(15, expect.line_dels);
git_diff_free(diff);
diff = NULL;
set_config_int(g_repo, "diff.context", 1);
memset(&expect, 0, sizeof(expect));
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
cl_git_pass(git_diff_foreach(diff,
diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(2, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(7, expect.hunks);
cl_assert_equal_i(34, expect.lines);
cl_assert_equal_i(12, expect.line_ctxt);
cl_assert_equal_i(7, expect.line_adds);
cl_assert_equal_i(15, expect.line_dels);
git_diff_free(diff);
diff = NULL;
set_config_int(g_repo, "diff.context", 0);
set_config_int(g_repo, "diff.noprefix", 1);
memset(&expect, 0, sizeof(expect));
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
cl_git_pass(git_diff_foreach(diff,
diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(2, expect.files);
cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(7, expect.hunks);
cl_assert_equal_i(22, expect.lines);
cl_assert_equal_i(0, expect.line_ctxt);
cl_assert_equal_i(7, expect.line_adds);
cl_assert_equal_i(15, expect.line_dels);
}
File diff suppressed because it is too large Load Diff