From 3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Nov 2021 23:29:22 -0500 Subject: refactor: `tests` is now `tests/libgit2` Like we want to separate libgit2 and utility source code, we want to separate libgit2 and utility tests. Start by moving all the tests into libgit2. --- tests/libgit2/refs/basic.c | 85 +++ tests/libgit2/refs/branches/checkedout.c | 53 ++ tests/libgit2/refs/branches/create.c | 279 +++++++++ tests/libgit2/refs/branches/delete.c | 188 ++++++ tests/libgit2/refs/branches/ishead.c | 98 ++++ tests/libgit2/refs/branches/iterator.c | 151 +++++ tests/libgit2/refs/branches/lookup.c | 68 +++ tests/libgit2/refs/branches/move.c | 212 +++++++ tests/libgit2/refs/branches/name.c | 62 ++ tests/libgit2/refs/branches/remote.c | 65 +++ tests/libgit2/refs/branches/upstream.c | 218 +++++++ tests/libgit2/refs/branches/upstreamname.c | 35 ++ tests/libgit2/refs/crashes.c | 44 ++ tests/libgit2/refs/create.c | 362 ++++++++++++ tests/libgit2/refs/delete.c | 118 ++++ tests/libgit2/refs/dup.c | 42 ++ tests/libgit2/refs/foreachglob.c | 100 ++++ tests/libgit2/refs/isvalidname.c | 38 ++ tests/libgit2/refs/iterator.c | 274 +++++++++ tests/libgit2/refs/list.c | 57 ++ tests/libgit2/refs/listall.c | 47 ++ tests/libgit2/refs/lookup.c | 68 +++ tests/libgit2/refs/namespaces.c | 36 ++ tests/libgit2/refs/normalize.c | 401 +++++++++++++ tests/libgit2/refs/overwrite.c | 136 +++++ tests/libgit2/refs/pack.c | 105 ++++ tests/libgit2/refs/peel.c | 131 +++++ tests/libgit2/refs/races.c | 170 ++++++ tests/libgit2/refs/read.c | 299 ++++++++++ tests/libgit2/refs/ref_helpers.c | 25 + tests/libgit2/refs/ref_helpers.h | 1 + tests/libgit2/refs/reflog/drop.c | 115 ++++ tests/libgit2/refs/reflog/messages.c | 421 ++++++++++++++ tests/libgit2/refs/reflog/reflog.c | 469 +++++++++++++++ tests/libgit2/refs/reflog/reflog_helpers.c | 120 ++++ tests/libgit2/refs/reflog/reflog_helpers.h | 12 + tests/libgit2/refs/rename.c | 368 ++++++++++++ tests/libgit2/refs/revparse.c | 890 +++++++++++++++++++++++++++++ tests/libgit2/refs/setter.c | 99 ++++ tests/libgit2/refs/shorthand.c | 27 + tests/libgit2/refs/tags/name.c | 17 + tests/libgit2/refs/transactions.c | 157 +++++ tests/libgit2/refs/unicode.c | 54 ++ tests/libgit2/refs/update.c | 26 + 44 files changed, 6743 insertions(+) create mode 100644 tests/libgit2/refs/basic.c create mode 100644 tests/libgit2/refs/branches/checkedout.c create mode 100644 tests/libgit2/refs/branches/create.c create mode 100644 tests/libgit2/refs/branches/delete.c create mode 100644 tests/libgit2/refs/branches/ishead.c create mode 100644 tests/libgit2/refs/branches/iterator.c create mode 100644 tests/libgit2/refs/branches/lookup.c create mode 100644 tests/libgit2/refs/branches/move.c create mode 100644 tests/libgit2/refs/branches/name.c create mode 100644 tests/libgit2/refs/branches/remote.c create mode 100644 tests/libgit2/refs/branches/upstream.c create mode 100644 tests/libgit2/refs/branches/upstreamname.c create mode 100644 tests/libgit2/refs/crashes.c create mode 100644 tests/libgit2/refs/create.c create mode 100644 tests/libgit2/refs/delete.c create mode 100644 tests/libgit2/refs/dup.c create mode 100644 tests/libgit2/refs/foreachglob.c create mode 100644 tests/libgit2/refs/isvalidname.c create mode 100644 tests/libgit2/refs/iterator.c create mode 100644 tests/libgit2/refs/list.c create mode 100644 tests/libgit2/refs/listall.c create mode 100644 tests/libgit2/refs/lookup.c create mode 100644 tests/libgit2/refs/namespaces.c create mode 100644 tests/libgit2/refs/normalize.c create mode 100644 tests/libgit2/refs/overwrite.c create mode 100644 tests/libgit2/refs/pack.c create mode 100644 tests/libgit2/refs/peel.c create mode 100644 tests/libgit2/refs/races.c create mode 100644 tests/libgit2/refs/read.c create mode 100644 tests/libgit2/refs/ref_helpers.c create mode 100644 tests/libgit2/refs/ref_helpers.h create mode 100644 tests/libgit2/refs/reflog/drop.c create mode 100644 tests/libgit2/refs/reflog/messages.c create mode 100644 tests/libgit2/refs/reflog/reflog.c create mode 100644 tests/libgit2/refs/reflog/reflog_helpers.c create mode 100644 tests/libgit2/refs/reflog/reflog_helpers.h create mode 100644 tests/libgit2/refs/rename.c create mode 100644 tests/libgit2/refs/revparse.c create mode 100644 tests/libgit2/refs/setter.c create mode 100644 tests/libgit2/refs/shorthand.c create mode 100644 tests/libgit2/refs/tags/name.c create mode 100644 tests/libgit2/refs/transactions.c create mode 100644 tests/libgit2/refs/unicode.c create mode 100644 tests/libgit2/refs/update.c (limited to 'tests/libgit2/refs') diff --git a/tests/libgit2/refs/basic.c b/tests/libgit2/refs/basic.c new file mode 100644 index 000000000..32742f9cc --- /dev/null +++ b/tests/libgit2/refs/basic.c @@ -0,0 +1,85 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "refs.h" +#include "ref_helpers.h" + +static git_repository *g_repo; + +static const char *loose_tag_ref_name = "refs/tags/e90810b"; + +void test_refs_basic__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_repository_set_ident(g_repo, "me", "foo@example.com")); +} + +void test_refs_basic__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_basic__reference_realloc(void) +{ + git_reference *ref; + git_reference *new_ref; + const char *new_name = "refs/tags/awful/name-which-is/clearly/really-that-much/longer-than/the-old-one"; + + /* Retrieval of the reference to rename */ + cl_git_pass(git_reference_lookup(&ref, g_repo, loose_tag_ref_name)); + + new_ref = git_reference__realloc(&ref, new_name); + cl_assert(new_ref != NULL); + git_reference_free(new_ref); + git_reference_free(ref); + + /* Reload, so we restore the value */ + cl_git_pass(git_reference_lookup(&ref, g_repo, loose_tag_ref_name)); + + cl_git_pass(git_reference_rename(&new_ref, ref, new_name, 1, "log message")); + cl_assert(ref != NULL); + cl_assert(new_ref != NULL); + git_reference_free(new_ref); + git_reference_free(ref); +} + +void test_refs_basic__longpaths(void) +{ +#ifdef GIT_WIN32 + const char *base; + size_t base_len, extra_len; + ssize_t remain_len, i; + git_str refname = GIT_STR_INIT; + git_reference *one = NULL, *two = NULL; + git_oid id; + + cl_git_pass(git_oid_fromstr(&id, "099fabac3a9ea935598528c27f866e34089c2eff")); + + base = git_repository_path(g_repo); + base_len = git_utf8_char_length(base, strlen(base)); + extra_len = CONST_STRLEN("logs/refs/heads/") + CONST_STRLEN(".lock"); + + remain_len = (ssize_t)MAX_PATH - (base_len + extra_len); + cl_assert(remain_len > 0); + + cl_git_pass(git_str_puts(&refname, "refs/heads/")); + + for (i = 0; i < remain_len; i++) { + cl_git_pass(git_str_putc(&refname, 'a')); + } + + /* + * The full path to the reflog lockfile is 260 characters, + * this is permitted. + */ + cl_git_pass(git_reference_create(&one, g_repo, refname.ptr, &id, 0, NULL)); + + /* Adding one more character gives us a path that is too long. */ + cl_git_pass(git_str_putc(&refname, 'z')); + cl_git_fail(git_reference_create(&two, g_repo, refname.ptr, &id, 0, NULL)); + + git_reference_free(one); + git_reference_free(two); + git_str_dispose(&refname); +#endif +} diff --git a/tests/libgit2/refs/branches/checkedout.c b/tests/libgit2/refs/branches/checkedout.c new file mode 100644 index 000000000..d6dab2c0e --- /dev/null +++ b/tests/libgit2/refs/branches/checkedout.c @@ -0,0 +1,53 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "worktree/worktree_helpers.h" + +static git_repository *repo; + +static void assert_checked_out(git_repository *repo, const char *branch, int checked_out) +{ + git_reference *ref; + + cl_git_pass(git_reference_lookup(&ref, repo, branch)); + cl_assert(git_branch_is_checked_out(ref) == checked_out); + + git_reference_free(ref); +} + +void test_refs_branches_checkedout__simple_repo(void) +{ + repo = cl_git_sandbox_init("testrepo"); + assert_checked_out(repo, "refs/heads/master", 1); + assert_checked_out(repo, "refs/heads/executable", 0); + cl_git_sandbox_cleanup(); +} + +void test_refs_branches_checkedout__worktree(void) +{ + static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT("testrepo", "testrepo-worktree"); + + setup_fixture_worktree(&fixture); + + assert_checked_out(fixture.repo, "refs/heads/master", 1); + assert_checked_out(fixture.repo, "refs/heads/testrepo-worktree", 1); + + assert_checked_out(fixture.worktree, "refs/heads/master", 1); + assert_checked_out(fixture.worktree, "refs/heads/testrepo-worktree", 1); + + cleanup_fixture_worktree(&fixture); +} + +void test_refs_branches_checkedout__head_is_not_checked_out(void) +{ + repo = cl_git_sandbox_init("testrepo"); + assert_checked_out(repo, "HEAD", 0); + cl_git_sandbox_cleanup(); +} + +void test_refs_branches_checkedout__master_in_bare_repo_is_not_checked_out(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + assert_checked_out(repo, "refs/heads/master", 0); + cl_git_sandbox_cleanup(); +} diff --git a/tests/libgit2/refs/branches/create.c b/tests/libgit2/refs/branches/create.c new file mode 100644 index 000000000..2fb11668b --- /dev/null +++ b/tests/libgit2/refs/branches/create.c @@ -0,0 +1,279 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "path.h" + +static git_repository *repo; +static git_commit *target; +static git_reference *branch; + +void test_refs_branches_create__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + branch = NULL; + target = NULL; +} + +void test_refs_branches_create__cleanup(void) +{ + git_reference_free(branch); + branch = NULL; + + git_commit_free(target); + target = NULL; + + cl_git_sandbox_cleanup(); + repo = NULL; +} + +static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha) +{ + git_object *obj; + + cl_git_pass(git_revparse_single(&obj, repo, sha)); + cl_git_pass(git_commit_lookup(out, repo, git_object_id(obj))); + git_object_free(obj); +} + +static void retrieve_known_commit(git_commit **commit, git_repository *repo) +{ + retrieve_target_from_oid(commit, repo, "e90810b8df3"); +} + +#define NEW_BRANCH_NAME "new-branch-on-the-block" + +void test_refs_branches_create__can_create_a_local_branch(void) +{ + retrieve_known_commit(&target, repo); + + cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); +} + +void test_refs_branches_create__can_not_create_a_branch_if_its_name_collide_with_an_existing_one(void) +{ + retrieve_known_commit(&target, repo); + + cl_assert_equal_i(GIT_EEXISTS, git_branch_create(&branch, repo, "br2", target, 0)); +} + +void test_refs_branches_create__can_force_create_over_an_existing_branch(void) +{ + retrieve_known_commit(&target, repo); + + cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_assert_equal_s("refs/heads/br2", git_reference_name(branch)); +} + +void test_refs_branches_create__cannot_force_create_over_current_branch_in_nonbare_repo(void) +{ + const git_oid *oid; + git_reference *branch2; + + /* Default repo for these tests is a bare repo, but this test requires a non-bare one */ + cl_git_sandbox_cleanup(); + repo = cl_git_sandbox_init("testrepo"); + retrieve_known_commit(&target, repo); + + cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch2)); + cl_assert_equal_i(true, git_branch_is_head(branch2)); + oid = git_reference_target(branch2); + + cl_git_fail_with(-1, git_branch_create(&branch, repo, "master", target, 1)); + branch = NULL; + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), oid)); + git_reference_free(branch2); +} + +void test_refs_branches_create__can_force_create_over_current_branch_in_bare_repo(void) +{ + const git_oid *oid; + git_reference *branch2; + retrieve_known_commit(&target, repo); + + cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch2)); + cl_assert_equal_i(true, git_branch_is_head(branch2)); + oid = git_commit_id(target); + + cl_git_pass(git_branch_create(&branch, repo, "master", target, 1)); + git_reference_free(branch); + branch = NULL; + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), oid)); + git_reference_free(branch2); +} + +void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + retrieve_known_commit(&target, repo); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_create(&branch, repo, "inv@{id", target, 0)); +} + +static void assert_branch_matches_name( + const char *expected, const char *lookup_as) +{ + git_reference *ref; + git_str b = GIT_STR_INIT; + + cl_git_pass(git_branch_lookup(&ref, repo, lookup_as, GIT_BRANCH_LOCAL)); + + cl_git_pass(git_str_sets(&b, "refs/heads/")); + cl_git_pass(git_str_puts(&b, expected)); + cl_assert_equal_s(b.ptr, git_reference_name(ref)); + + cl_git_pass( + git_oid_cmp(git_reference_target(ref), git_commit_id(target))); + + git_reference_free(ref); + git_str_dispose(&b); +} + +void test_refs_branches_create__can_create_branch_with_unicode(void) +{ + const char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D"; + const char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; + const char *emoji = "\xF0\x9F\x8D\xB7"; + const char *names[] = { nfc, nfd, emoji }; + const char *alt[] = { nfd, nfc, NULL }; + const char *expected[] = { nfc, nfd, emoji }; + unsigned int i; + bool fs_decompose_unicode = + git_fs_path_does_decompose_unicode(git_repository_path(repo)); + + retrieve_known_commit(&target, repo); + + if (cl_repo_get_bool(repo, "core.precomposeunicode")) + expected[1] = nfc; + /* test decomp. because not all Mac filesystems decompose unicode */ + else if (fs_decompose_unicode) + expected[0] = nfd; + + for (i = 0; i < ARRAY_SIZE(names); ++i) { + const char *name; + cl_git_pass(git_branch_create( + &branch, repo, names[i], target, 0)); + cl_git_pass(git_oid_cmp( + git_reference_target(branch), git_commit_id(target))); + + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(expected[i], name); + assert_branch_matches_name(expected[i], names[i]); + if (fs_decompose_unicode && alt[i]) + assert_branch_matches_name(expected[i], alt[i]); + + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + branch = NULL; + } +} + +/** + * Verify that we can create a branch with a name that matches the + * namespace of a previously delete branch. + * + * git branch level_one/level_two + * git branch -D level_one/level_two + * git branch level_one + * + * We expect the delete to have deleted the files: + * ".git/refs/heads/level_one/level_two" + * ".git/logs/refs/heads/level_one/level_two" + * It may or may not have deleted the (now empty) + * containing directories. To match git.git behavior, + * the second create needs to implicilty delete the + * directories and create the new files. + * "refs/heads/level_one" + * "logs/refs/heads/level_one" + * + * We should not fail to create the branch or its + * reflog because of an obsolete namespace container + * directory. + */ +void test_refs_branches_create__name_vs_namespace(void) +{ + const char * name; + struct item { + const char *first; + const char *second; + }; + static const struct item item[] = { + { "level_one/level_two", "level_one" }, + { "a/b/c/d/e", "a/b/c/d" }, + { "ss/tt/uu/vv/ww", "ss" }, + /* And one test case that is deeper. */ + { "xx1/xx2/xx3/xx4", "xx1/xx2/xx3/xx4/xx5/xx6" }, + { NULL, NULL }, + }; + const struct item *p; + + retrieve_known_commit(&target, repo); + + for (p=item; p->first; p++) { + cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(name, p->first); + + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + branch = NULL; + + cl_git_pass(git_branch_create(&branch, repo, p->second, target, 0)); + git_reference_free(branch); + branch = NULL; + } +} + +/** + * We still need to fail if part of the namespace is + * still in use. + */ +void test_refs_branches_create__name_vs_namespace_fail(void) +{ + const char * name; + struct item { + const char *first; + const char *first_alternate; + const char *second; + }; + static const struct item item[] = { + { "level_one/level_two", "level_one/alternate", "level_one" }, + { "a/b/c/d/e", "a/b/c/d/alternate", "a/b/c/d" }, + { "ss/tt/uu/vv/ww", "ss/alternate", "ss" }, + { NULL, NULL, NULL }, + }; + const struct item *p; + + retrieve_known_commit(&target, repo); + + for (p=item; p->first; p++) { + cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(name, p->first); + + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + branch = NULL; + + cl_git_pass(git_branch_create(&branch, repo, p->first_alternate, target, 0)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(name, p->first_alternate); + + /* we do not delete the alternate. */ + git_reference_free(branch); + branch = NULL; + + cl_git_fail(git_branch_create(&branch, repo, p->second, target, 0)); + git_reference_free(branch); + branch = NULL; + } +} diff --git a/tests/libgit2/refs/branches/delete.c b/tests/libgit2/refs/branches/delete.c new file mode 100644 index 000000000..27995f9e2 --- /dev/null +++ b/tests/libgit2/refs/branches/delete.c @@ -0,0 +1,188 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "repo/repo_helpers.h" +#include "config/config_helpers.h" +#include "futils.h" +#include "reflog.h" + +static git_repository *repo; +static git_reference *fake_remote; + +void test_refs_branches_delete__initialize(void) +{ + git_oid id; + + repo = cl_git_sandbox_init("testrepo.git"); + + cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL)); +} + +void test_refs_branches_delete__cleanup(void) +{ + git_reference_free(fake_remote); + fake_remote = NULL; + + cl_git_sandbox_cleanup(); + repo = NULL; +} + +void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void) +{ + git_reference *head; + git_reference *branch; + + /* Ensure HEAD targets the local master branch */ + cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); + cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); + git_reference_free(head); + + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_git_fail(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void) +{ + git_reference *head; + git_reference *branch; + + cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); + git_reference_delete(head); + git_reference_free(head); + + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_unborn(void) +{ + git_reference *branch; + + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void) +{ + git_reference *head, *branch; + + cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); + cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head)); + cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); + git_reference_free(head); + + /* Detach HEAD and make it target the commit that "master" points to */ + git_repository_detach_head(repo); + + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_refs_branches_delete__can_delete_a_local_branch(void) +{ + git_reference *branch; + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_refs_branches_delete__can_delete_a_remote_branch(void) +{ + git_reference *branch; + cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void) +{ + git_reference *branch; + + assert_config_entry_existence(repo, "branch.track-local.remote", true); + assert_config_entry_existence(repo, "branch.track-local.merge", true); + + cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + + assert_config_entry_existence(repo, "branch.track-local.remote", false); + assert_config_entry_existence(repo, "branch.track-local.merge", false); +} + +void test_refs_branches_delete__removes_reflog(void) +{ + git_reference *branch; + git_reflog *log; + git_oid oidzero = {{0}}; + git_signature *sig; + + /* Ensure the reflog has at least one entry */ + cl_git_pass(git_signature_now(&sig, "Me", "user@example.com")); + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); + cl_git_pass(git_reflog_append(log, &oidzero, sig, "message")); + cl_assert(git_reflog_entrycount(log) > 0); + git_signature_free(sig); + git_reflog_free(log); + + cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + + cl_assert_equal_i(false, git_reference_has_log(repo, "refs/heads/track-local")); + + /* Reading a non-existent reflog creates it, but it should be empty */ + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); + cl_assert_equal_i(0, git_reflog_entrycount(log)); + git_reflog_free(log); +} + +void test_refs_branches_delete__removes_empty_folders(void) +{ + const char *commondir = git_repository_commondir(repo); + git_oid commit_id; + git_commit *commit; + git_reference *branch; + + git_reflog *log; + git_oid oidzero = {{0}}; + git_signature *sig; + + git_str ref_folder = GIT_STR_INIT; + git_str reflog_folder = GIT_STR_INIT; + + /* Create a new branch with a nested name */ + cl_git_pass(git_oid_fromstr(&commit_id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + cl_git_pass(git_commit_lookup(&commit, repo, &commit_id)); + cl_git_pass(git_branch_create(&branch, repo, "some/deep/ref", commit, 0)); + git_commit_free(commit); + + /* Ensure the reflog has at least one entry */ + cl_git_pass(git_signature_now(&sig, "Me", "user@example.com")); + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/some/deep/ref")); + cl_git_pass(git_reflog_append(log, &oidzero, sig, "message")); + cl_assert(git_reflog_entrycount(log) > 0); + git_signature_free(sig); + git_reflog_free(log); + + cl_git_pass(git_str_joinpath(&ref_folder, commondir, "refs/heads/some/deep")); + cl_git_pass(git_str_join3(&reflog_folder, '/', commondir, GIT_REFLOG_DIR, "refs/heads/some/deep")); + + cl_assert(git_fs_path_exists(git_str_cstr(&ref_folder)) == true); + cl_assert(git_fs_path_exists(git_str_cstr(&reflog_folder)) == true); + + cl_git_pass(git_branch_delete(branch)); + + cl_assert(git_fs_path_exists(git_str_cstr(&ref_folder)) == false); + cl_assert(git_fs_path_exists(git_str_cstr(&reflog_folder)) == false); + + git_reference_free(branch); + git_str_dispose(&ref_folder); + git_str_dispose(&reflog_folder); +} + diff --git a/tests/libgit2/refs/branches/ishead.c b/tests/libgit2/refs/branches/ishead.c new file mode 100644 index 000000000..1df70b789 --- /dev/null +++ b/tests/libgit2/refs/branches/ishead.c @@ -0,0 +1,98 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "repo/repo_helpers.h" + +static git_repository *repo; +static git_reference *branch; + +void test_refs_branches_ishead__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + branch = NULL; +} + +void test_refs_branches_ishead__cleanup(void) +{ + git_reference_free(branch); + branch = NULL; + + cl_git_sandbox_cleanup(); + repo = NULL; +} + +void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + + cl_assert_equal_i(true, git_branch_is_head(branch)); +} + +void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void) +{ + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + + cl_assert_equal_i(false, git_branch_is_head(branch)); +} + +void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void) +{ + delete_head(repo); + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + + cl_assert_equal_i(false, git_branch_is_head(branch)); +} + +void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/br2")); + + cl_assert_equal_i(false, git_branch_is_head(branch)); +} + +void test_refs_branches_ishead__wont_be_fooled_by_a_non_branch(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b")); + + cl_assert_equal_i(false, git_branch_is_head(branch)); +} + +/* + * $ git init . + * Initialized empty Git repository in d:/temp/tempee/.git/ + * + * $ touch a && git add a + * $ git commit -m" boom" + * [master (root-commit) b47b758] boom + * 0 files changed + * create mode 100644 a + * + * $ echo "ref: refs/heads/master" > .git/refs/heads/linked + * $ echo "ref: refs/heads/linked" > .git/refs/heads/super + * $ echo "ref: refs/heads/super" > .git/HEAD + * + * $ git branch + * linked -> master + * * master + * super -> master + */ +void test_refs_branches_ishead__only_direct_references_are_considered(void) +{ + git_reference *linked, *super, *head; + + cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0, NULL)); + cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0, NULL)); + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1, NULL)); + + cl_assert_equal_i(false, git_branch_is_head(linked)); + cl_assert_equal_i(false, git_branch_is_head(super)); + + cl_git_pass(git_repository_head(&branch, repo)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch)); + + git_reference_free(linked); + git_reference_free(super); + git_reference_free(head); +} diff --git a/tests/libgit2/refs/branches/iterator.c b/tests/libgit2/refs/branches/iterator.c new file mode 100644 index 000000000..e086681e5 --- /dev/null +++ b/tests/libgit2/refs/branches/iterator.c @@ -0,0 +1,151 @@ +#include "clar_libgit2.h" +#include "refs.h" + +static git_repository *repo; +static git_reference *fake_remote; + +void test_refs_branches_iterator__initialize(void) +{ + git_oid id; + + cl_fixture_sandbox("testrepo.git"); + cl_git_pass(git_repository_open(&repo, "testrepo.git")); + + cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL)); +} + +void test_refs_branches_iterator__cleanup(void) +{ + git_reference_free(fake_remote); + fake_remote = NULL; + + git_repository_free(repo); + repo = NULL; + + cl_fixture_cleanup("testrepo.git"); + + cl_git_sandbox_cleanup(); +} + +static void assert_retrieval(unsigned int flags, unsigned int expected_count) +{ + git_branch_iterator *iter; + git_reference *ref; + int count = 0, error; + git_branch_t type; + + cl_git_pass(git_branch_iterator_new(&iter, repo, flags)); + while ((error = git_branch_next(&ref, &type, iter)) == 0) { + count++; + git_reference_free(ref); + } + + git_branch_iterator_free(iter); + cl_assert_equal_i(error, GIT_ITEROVER); + cl_assert_equal_i(expected_count, count); +} + +void test_refs_branches_iterator__retrieve_all_branches(void) +{ + assert_retrieval(GIT_BRANCH_ALL, 15); +} + +void test_refs_branches_iterator__retrieve_remote_branches(void) +{ + assert_retrieval(GIT_BRANCH_REMOTE, 2); +} + +void test_refs_branches_iterator__retrieve_local_branches(void) +{ + assert_retrieval(GIT_BRANCH_LOCAL, 13); +} + +struct expectations { + const char *branch_name; + int encounters; +}; + +static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name) +{ + int pos = 0; + + for (pos = 0; findings[pos].branch_name; ++pos) { + if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) { + cl_assert_equal_i(1, findings[pos].encounters); + return; + } + } + + cl_fail("expected branch not found in list."); +} + +static void contains_branches(struct expectations exp[], git_branch_iterator *iter) +{ + git_reference *ref; + git_branch_t type; + int error, pos = 0; + + while ((error = git_branch_next(&ref, &type, iter)) == 0) { + for (pos = 0; exp[pos].branch_name; ++pos) { + if (strcmp(git_reference_shorthand(ref), exp[pos].branch_name) == 0) + exp[pos].encounters++; + } + + git_reference_free(ref); + } + + cl_assert_equal_i(error, GIT_ITEROVER); +} + +/* + * $ git branch -r + * nulltoken/HEAD -> nulltoken/master + * nulltoken/master + */ +void test_refs_branches_iterator__retrieve_remote_symbolic_HEAD_when_present(void) +{ + git_branch_iterator *iter; + struct expectations exp[] = { + { "nulltoken/HEAD", 0 }, + { "nulltoken/master", 0 }, + { NULL, 0 } + }; + + git_reference_free(fake_remote); + cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0, NULL)); + + assert_retrieval(GIT_BRANCH_REMOTE, 3); + + cl_git_pass(git_branch_iterator_new(&iter, repo, GIT_BRANCH_REMOTE)); + contains_branches(exp, iter); + git_branch_iterator_free(iter); + + assert_branch_has_been_found(exp, "nulltoken/HEAD"); + assert_branch_has_been_found(exp, "nulltoken/master"); +} + +void test_refs_branches_iterator__mix_of_packed_and_loose(void) +{ + git_branch_iterator *iter; + struct expectations exp[] = { + { "master", 0 }, + { "origin/HEAD", 0 }, + { "origin/master", 0 }, + { "origin/packed", 0 }, + { NULL, 0 } + }; + git_repository *r2; + + r2 = cl_git_sandbox_init("testrepo2"); + + cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_ALL)); + contains_branches(exp, iter); + + git_branch_iterator_free(iter); + + assert_branch_has_been_found(exp, "master"); + assert_branch_has_been_found(exp, "origin/HEAD"); + assert_branch_has_been_found(exp, "origin/master"); + assert_branch_has_been_found(exp, "origin/packed"); +} diff --git a/tests/libgit2/refs/branches/lookup.c b/tests/libgit2/refs/branches/lookup.c new file mode 100644 index 000000000..ef0c1f97d --- /dev/null +++ b/tests/libgit2/refs/branches/lookup.c @@ -0,0 +1,68 @@ +#include "clar_libgit2.h" +#include "refs.h" + +static git_repository *repo; +static git_reference *branch; + +void test_refs_branches_lookup__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + branch = NULL; +} + +void test_refs_branches_lookup__cleanup(void) +{ + git_reference_free(branch); + branch = NULL; + + git_repository_free(repo); + repo = NULL; +} + +void test_refs_branches_lookup__can_retrieve_a_local_branch_local(void) +{ + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); +} + +void test_refs_branches_lookup__can_retrieve_a_local_branch_all(void) +{ + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_ALL)); +} + +void test_refs_branches_lookup__trying_to_retrieve_a_local_branch_remote(void) +{ + cl_git_fail(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_REMOTE)); +} + +void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch_remote(void) +{ + cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_REMOTE)); +} + +void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch_all(void) +{ + cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_ALL)); +} + +void test_refs_branches_lookup__trying_to_retrieve_a_remote_tracking_branch_local(void) +{ + cl_git_fail(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_LOCAL)); +} + +void test_refs_branches_lookup__trying_to_retrieve_an_unknown_branch_returns_ENOTFOUND(void) +{ + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "where/are/you", GIT_BRANCH_LOCAL)); + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "over/here", GIT_BRANCH_REMOTE)); + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "maybe/here", GIT_BRANCH_ALL)); +} + +void test_refs_branches_lookup__trying_to_retrieve_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_lookup(&branch, repo, "are/you/inv@{id", GIT_BRANCH_LOCAL)); + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_lookup(&branch, repo, "yes/i am", GIT_BRANCH_REMOTE)); + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_lookup(&branch, repo, "inv al/id", GIT_BRANCH_ALL)); +} diff --git a/tests/libgit2/refs/branches/move.c b/tests/libgit2/refs/branches/move.c new file mode 100644 index 000000000..46a5082d2 --- /dev/null +++ b/tests/libgit2/refs/branches/move.c @@ -0,0 +1,212 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "config/config_helpers.h" + +static git_repository *repo; + +void test_refs_branches_move__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_branches_move__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +#define NEW_BRANCH_NAME "new-branch-on-the-block" + +void test_refs_branches_move__can_move_a_local_branch(void) +{ + git_reference *original_ref, *new_ref; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + + cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0)); + cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref)); + + git_reference_free(original_ref); + git_reference_free(new_ref); +} + +void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void) +{ + git_reference *original_ref, *new_ref, *newer_ref; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + + /* Downward */ + cl_git_pass(git_branch_move(&new_ref, original_ref, "somewhere/" NEW_BRANCH_NAME, 0)); + git_reference_free(original_ref); + + /* Upward */ + cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0)); + git_reference_free(new_ref); + + git_reference_free(newer_ref); +} + +void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void) +{ + git_reference *original_ref, *new_ref, *newer_ref; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + + /* Downward */ + cl_git_pass(git_branch_move(&new_ref, original_ref, "br2/" NEW_BRANCH_NAME, 0)); + git_reference_free(original_ref); + + /* Upward */ + cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0)); + git_reference_free(new_ref); + + git_reference_free(newer_ref); +} + +void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void) +{ + git_reference *original_ref, *new_ref; + git_config *config; + git_buf original_remote = GIT_BUF_INIT, + original_merge = GIT_BUF_INIT; + const char *str; + + cl_git_pass(git_repository_config_snapshot(&config, repo)); + + cl_git_pass(git_config_get_string_buf(&original_remote, config, "branch.master.remote")); + cl_git_pass(git_config_get_string_buf(&original_merge, config, "branch.master.merge")); + git_config_free(config); + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0)); + + cl_assert(git_error_last()->message != NULL); + + cl_git_pass(git_repository_config_snapshot(&config, repo)); + cl_git_pass(git_config_get_string(&str, config, "branch.master.remote")); + cl_assert_equal_s(original_remote.ptr, str); + cl_git_pass(git_config_get_string(&str, config, "branch.master.merge")); + cl_assert_equal_s(original_merge.ptr, str); + git_config_free(config); + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "cannot-fetch", 0)); + + cl_assert(git_error_last()->message != NULL); + + cl_git_pass(git_repository_config_snapshot(&config, repo)); + cl_git_pass(git_config_get_string(&str, config, "branch.master.remote")); + cl_assert_equal_s(original_remote.ptr, str); + cl_git_pass(git_config_get_string(&str, config, "branch.master.merge")); + cl_assert_equal_s(original_merge.ptr, str); + git_config_free(config); + + git_reference_free(original_ref); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/track-local")); + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0)); + + cl_assert(git_error_last()->message != NULL); + + cl_git_pass(git_repository_config_snapshot(&config, repo)); + cl_git_pass(git_config_get_string(&str, config, "branch.master.remote")); + cl_assert_equal_s(original_remote.ptr, str); + cl_git_pass(git_config_get_string(&str, config, "branch.master.merge")); + cl_assert_equal_s(original_merge.ptr, str); + + git_buf_dispose(&original_remote); + git_buf_dispose(&original_merge); + git_reference_free(original_ref); + git_config_free(config); +} + +void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *original_ref, *new_ref; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0)); + + git_reference_free(original_ref); +} + +void test_refs_branches_move__can_not_move_a_non_branch(void) +{ + git_reference *tag, *new_ref; + + cl_git_pass(git_reference_lookup(&tag, repo, "refs/tags/e90810b")); + cl_git_fail(git_branch_move(&new_ref, tag, NEW_BRANCH_NAME, 0)); + + git_reference_free(tag); +} + +void test_refs_branches_move__can_force_move_over_an_existing_branch(void) +{ + git_reference *original_ref, *new_ref; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + + cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1)); + + git_reference_free(original_ref); + git_reference_free(new_ref); +} + +void test_refs_branches_move__moving_a_branch_moves_related_configuration_data(void) +{ + git_reference *branch; + git_reference *new_branch; + + cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); + + assert_config_entry_existence(repo, "branch.track-local.remote", true); + assert_config_entry_existence(repo, "branch.track-local.merge", true); + assert_config_entry_existence(repo, "branch.moved.remote", false); + assert_config_entry_existence(repo, "branch.moved.merge", false); + + cl_git_pass(git_branch_move(&new_branch, branch, "moved", 0)); + git_reference_free(branch); + + assert_config_entry_existence(repo, "branch.track-local.remote", false); + assert_config_entry_existence(repo, "branch.track-local.merge", false); + assert_config_entry_existence(repo, "branch.moved.remote", true); + assert_config_entry_existence(repo, "branch.moved.merge", true); + + git_reference_free(new_branch); +} + +void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(void) +{ + git_reference *branch; + git_reference *new_branch; + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0)); + git_reference_free(branch); + git_reference_free(new_branch); + + cl_git_pass(git_repository_head(&branch, repo)); + cl_assert_equal_s("refs/heads/master2", git_reference_name(branch)); + git_reference_free(branch); +} + +void test_refs_branches_move__can_move_with_unicode(void) +{ + git_reference *original_ref, *new_ref; + const char *new_branch_name = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + cl_git_pass(git_branch_move(&new_ref, original_ref, new_branch_name, 0)); + + if (cl_repo_get_bool(repo, "core.precomposeunicode")) + cl_assert_equal_s(GIT_REFS_HEADS_DIR "\xC3\x85\x73\x74\x72\xC3\xB6\x6D", git_reference_name(new_ref)); + else + cl_assert_equal_s(GIT_REFS_HEADS_DIR "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D", git_reference_name(new_ref)); + + git_reference_free(original_ref); + git_reference_free(new_ref); +} diff --git a/tests/libgit2/refs/branches/name.c b/tests/libgit2/refs/branches/name.c new file mode 100644 index 000000000..efa68e32b --- /dev/null +++ b/tests/libgit2/refs/branches/name.c @@ -0,0 +1,62 @@ +#include "clar_libgit2.h" +#include "branch.h" + +static git_repository *repo; +static git_reference *ref; + +void test_refs_branches_name__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); +} + +void test_refs_branches_name__cleanup(void) +{ + git_reference_free(ref); + ref = NULL; + + git_repository_free(repo); + repo = NULL; +} + +void test_refs_branches_name__can_get_local_branch_name(void) +{ + const char *name; + + cl_git_pass(git_branch_lookup(&ref,repo,"master",GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_name(&name,ref)); + cl_assert_equal_s("master",name); +} + +void test_refs_branches_name__can_get_remote_branch_name(void) +{ + const char *name; + + cl_git_pass(git_branch_lookup(&ref,repo,"test/master",GIT_BRANCH_REMOTE)); + cl_git_pass(git_branch_name(&name,ref)); + cl_assert_equal_s("test/master",name); +} + +void test_refs_branches_name__error_when_ref_is_no_branch(void) +{ + const char *name; + + cl_git_pass(git_reference_lookup(&ref,repo,"refs/notes/fanout")); + cl_git_fail(git_branch_name(&name,ref)); +} + +static int name_is_valid(const char *name) +{ + int valid; + cl_git_pass(git_branch_name_is_valid(&valid, name)); + return valid; +} + +void test_refs_branches_name__is_name_valid(void) +{ + cl_assert_equal_i(true, name_is_valid("master")); + cl_assert_equal_i(true, name_is_valid("test/master")); + + cl_assert_equal_i(false, name_is_valid("")); + cl_assert_equal_i(false, name_is_valid("HEAD")); + cl_assert_equal_i(false, name_is_valid("-dash")); +} diff --git a/tests/libgit2/refs/branches/remote.c b/tests/libgit2/refs/branches/remote.c new file mode 100644 index 000000000..e2bd3485a --- /dev/null +++ b/tests/libgit2/refs/branches/remote.c @@ -0,0 +1,65 @@ +#include "clar_libgit2.h" +#include "branch.h" +#include "remote.h" + +static git_repository *g_repo; +static const char *remote_tracking_branch_name = "refs/remotes/test/master"; +static const char *expected_remote_name = "test"; +static int expected_remote_name_length; + +void test_refs_branches_remote__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + + expected_remote_name_length = (int)strlen(expected_remote_name) + 1; +} + +void test_refs_branches_remote__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_branches_remote__can_get_remote_for_branch(void) +{ + git_buf remotename = {0}; + + cl_git_pass(git_branch_remote_name(&remotename, g_repo, remote_tracking_branch_name)); + + cl_assert_equal_s("test", remotename.ptr); + git_buf_dispose(&remotename); +} + +void test_refs_branches_remote__no_matching_remote_returns_error(void) +{ + const char *unknown = "refs/remotes/nonexistent/master"; + git_buf buf = GIT_BUF_INIT; + + git_error_clear(); + cl_git_fail_with(git_branch_remote_name(&buf, g_repo, unknown), GIT_ENOTFOUND); + cl_assert(git_error_last() != NULL); +} + +void test_refs_branches_remote__local_remote_returns_error(void) +{ + const char *local = "refs/heads/master"; + git_buf buf = GIT_BUF_INIT; + + git_error_clear(); + cl_git_fail_with(git_branch_remote_name(&buf, g_repo, local), GIT_ERROR); + cl_assert(git_error_last() != NULL); +} + +void test_refs_branches_remote__ambiguous_remote_returns_error(void) +{ + git_remote *remote; + git_buf buf = GIT_BUF_INIT; + + /* Create the remote */ + cl_git_pass(git_remote_create_with_fetchspec(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2", "refs/heads/*:refs/remotes/test/*")); + + git_remote_free(remote); + + git_error_clear(); + cl_git_fail_with(git_branch_remote_name(&buf, g_repo, remote_tracking_branch_name), GIT_EAMBIGUOUS); + cl_assert(git_error_last() != NULL); +} diff --git a/tests/libgit2/refs/branches/upstream.c b/tests/libgit2/refs/branches/upstream.c new file mode 100644 index 000000000..919705e07 --- /dev/null +++ b/tests/libgit2/refs/branches/upstream.c @@ -0,0 +1,218 @@ +#include "clar_libgit2.h" +#include "config/config_helpers.h" +#include "refs.h" + +static git_repository *repo; +static git_reference *branch, *upstream; + +void test_refs_branches_upstream__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + branch = NULL; + upstream = NULL; +} + +void test_refs_branches_upstream__cleanup(void) +{ + git_reference_free(upstream); + git_reference_free(branch); + branch = NULL; + + git_repository_free(repo); + repo = NULL; +} + +void test_refs_branches_upstream__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + + cl_git_pass(git_branch_upstream(&upstream, branch)); + + cl_assert_equal_s("refs/remotes/test/master", git_reference_name(upstream)); +} + +void test_refs_branches_upstream__can_retrieve_the_local_upstream_reference_of_a_local_branch(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local")); + + cl_git_pass(git_branch_upstream(&upstream, branch)); + + cl_assert_equal_s("refs/heads/master", git_reference_name(upstream)); +} + +void test_refs_branches_upstream__cannot_retrieve_a_remote_upstream_reference_from_a_non_branch(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b")); + + cl_git_fail(git_branch_upstream(&upstream, branch)); +} + +void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees")); + + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch)); +} + +void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_branch_with_no_fetchspec_returns_GIT_ENOTFOUND(void) +{ + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/cannot-fetch")); + + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch)); +} + +void test_refs_branches_upstream__upstream_remote(void) +{ + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_branch_upstream_remote(&buf, repo, "refs/heads/master")); + cl_assert_equal_s("test", buf.ptr); + git_buf_dispose(&buf); +} + +void test_refs_branches_upstream__upstream_merge(void) +{ + git_reference *branch; + git_repository *repository; + git_buf buf = GIT_BUF_INIT; + + repository = cl_git_sandbox_init("testrepo.git"); + + /* check repository */ + cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test")); + cl_git_pass(git_branch_set_upstream(branch, "test/master")); + + assert_config_entry_value(repository, "branch.test.remote", "test"); + assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master"); + + git_reference_free(branch); + + /* check merge branch */ + cl_git_pass(git_branch_upstream_merge(&buf, repository, "refs/heads/test")); + cl_assert_equal_s("refs/heads/master", buf.ptr); + git_buf_dispose(&buf); + + cl_git_sandbox_cleanup(); +} + +void test_refs_branches_upstream__upstream_remote_empty_value(void) +{ + git_repository *repository; + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + + repository = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&cfg, repository)); + cl_git_pass(git_config_set_string(cfg, "branch.master.remote", "")); + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master")); + + cl_git_pass(git_config_delete_entry(cfg, "branch.master.remote")); + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master")); + cl_git_sandbox_cleanup(); +} + +static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name) +{ + git_reference *branch; + + cl_assert_equal_i(GIT_OBJECT_COMMIT, git_object_type((git_object*)target)); + cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0)); + + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch)); + + git_reference_free(branch); +} + +void test_refs_branches_upstream__retrieve_a_remote_tracking_reference_from_a_branch_with_no_remote_returns_GIT_ENOTFOUND(void) +{ + git_reference *head; + git_repository *repository; + git_commit *target; + + repository = cl_git_sandbox_init("testrepo.git"); + + cl_git_pass(git_repository_head(&head, repository)); + cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJECT_COMMIT)); + git_reference_free(head); + + assert_merge_and_or_remote_key_missing(repository, target, "remoteless"); + assert_merge_and_or_remote_key_missing(repository, target, "mergeless"); + assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless"); + + git_commit_free(target); + + cl_git_sandbox_cleanup(); +} + +void test_refs_branches_upstream__set_unset_upstream(void) +{ + git_reference *branch; + git_repository *repository; + + repository = cl_git_sandbox_init("testrepo.git"); + + /* remote */ + cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test")); + cl_git_pass(git_branch_set_upstream(branch, "test/master")); + + assert_config_entry_value(repository, "branch.test.remote", "test"); + assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master"); + + git_reference_free(branch); + + /* local */ + cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test")); + cl_git_pass(git_branch_set_upstream(branch, "master")); + + assert_config_entry_value(repository, "branch.test.remote", "."); + assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master"); + + /* unset */ + cl_git_pass(git_branch_set_upstream(branch, NULL)); + assert_config_entry_existence(repository, "branch.test.remote", false); + assert_config_entry_existence(repository, "branch.test.merge", false); + + git_reference_free(branch); + + cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/master")); + cl_git_pass(git_branch_set_upstream(branch, NULL)); + assert_config_entry_existence(repository, "branch.test.remote", false); + assert_config_entry_existence(repository, "branch.test.merge", false); + + git_reference_free(branch); + + cl_git_sandbox_cleanup(); +} + +void test_refs_branches_upstream__no_fetch_refspec(void) +{ + git_reference *ref, *branch; + git_repository *repo; + git_remote *remote; + git_config *cfg; + + repo = cl_git_sandbox_init("testrepo.git"); + + cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "matching", ".", NULL)); + cl_git_pass(git_remote_add_push(repo, "matching", ":")); + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/test")); + cl_git_pass(git_reference_create(&ref, repo, "refs/remotes/matching/master", git_reference_target(branch), 1, "fetch")); + cl_git_fail(git_branch_set_upstream(branch, "matching/master")); + cl_assert_equal_s("could not determine remote for 'refs/remotes/matching/master'", + git_error_last()->message); + + /* we can't set it automatically, so let's test the user setting it by hand */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_string(cfg, "branch.test.remote", "matching")); + cl_git_pass(git_config_set_string(cfg, "branch.test.merge", "refs/heads/master")); + /* we still can't find it because there is no rule for that reference */ + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream(&ref, branch)); + + git_reference_free(ref); + git_reference_free(branch); + git_remote_free(remote); + + cl_git_sandbox_cleanup(); +} diff --git a/tests/libgit2/refs/branches/upstreamname.c b/tests/libgit2/refs/branches/upstreamname.c new file mode 100644 index 000000000..5bae154d2 --- /dev/null +++ b/tests/libgit2/refs/branches/upstreamname.c @@ -0,0 +1,35 @@ +#include "clar_libgit2.h" +#include "branch.h" + +static git_repository *repo; +static git_buf upstream_name; + +void test_refs_branches_upstreamname__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + +} + +void test_refs_branches_upstreamname__cleanup(void) +{ + git_buf_dispose(&upstream_name); + + git_repository_free(repo); + repo = NULL; +} + +void test_refs_branches_upstreamname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void) +{ + cl_git_pass(git_branch_upstream_name( + &upstream_name, repo, "refs/heads/master")); + + cl_assert_equal_s("refs/remotes/test/master", upstream_name.ptr); +} + +void test_refs_branches_upstreamname__can_retrieve_the_local_upstream_reference_name_of_a_local_branch(void) +{ + cl_git_pass(git_branch_upstream_name( + &upstream_name, repo, "refs/heads/track-local")); + + cl_assert_equal_s("refs/heads/master", upstream_name.ptr); +} diff --git a/tests/libgit2/refs/crashes.c b/tests/libgit2/refs/crashes.c new file mode 100644 index 000000000..4f508aed2 --- /dev/null +++ b/tests/libgit2/refs/crashes.c @@ -0,0 +1,44 @@ +#include "clar_libgit2.h" +#include "refs.h" + +void test_refs_crashes__double_free(void) +{ + git_repository *repo; + git_reference *ref, *ref2; + const char *REFNAME = "refs/heads/xxx"; + + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0, NULL)); + cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME)); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + git_reference_free(ref2); + + /* reference is gone from disk, so reloading it will fail */ + cl_git_fail(git_reference_lookup(&ref2, repo, REFNAME)); + + cl_git_sandbox_cleanup(); +} + +void test_refs_crashes__empty_packedrefs(void) +{ + git_repository *repo; + git_reference *ref; + const char *REFNAME = "refs/heads/xxx"; + git_str temp_path = GIT_STR_INIT; + int fd = 0; + + repo = cl_git_sandbox_init("empty_bare.git"); + + /* create zero-length packed-refs file */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(repo), GIT_PACKEDREFS_FILE)); + cl_git_pass(((fd = p_creat(temp_path.ptr, 0644)) < 0)); + cl_git_pass(p_close(fd)); + + /* should fail gracefully */ + cl_git_fail_with( + GIT_ENOTFOUND, git_reference_lookup(&ref, repo, REFNAME)); + + cl_git_sandbox_cleanup(); + git_str_dispose(&temp_path); +} diff --git a/tests/libgit2/refs/create.c b/tests/libgit2/refs/create.c new file mode 100644 index 000000000..01eb62a52 --- /dev/null +++ b/tests/libgit2/refs/create.c @@ -0,0 +1,362 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *current_master_tip = "099fabac3a9ea935598528c27f866e34089c2eff"; +static const char *current_head_target = "refs/heads/master"; + +static git_repository *g_repo; + +void test_refs_create__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + p_fsync__cnt = 0; +} + +void test_refs_create__cleanup(void) +{ + cl_git_sandbox_cleanup(); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 1)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0)); +} + +void test_refs_create__symbolic(void) +{ + /* create a new symbolic reference */ + git_reference *new_reference, *looked_up_ref, *resolved_ref; + git_repository *repo2; + git_oid id; + + const char *new_head_tracker = "ANOTHER_HEAD_TRACKER"; + + git_oid_fromstr(&id, current_master_tip); + + /* Create and write the new symbolic reference */ + cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL)); + + /* Ensure the reference can be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); + cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_SYMBOLIC); + cl_assert(reference_is_packed(looked_up_ref) == 0); + cl_assert_equal_s(looked_up_ref->name, new_head_tracker); + + /* ...peeled.. */ + cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); + cl_assert(git_reference_type(resolved_ref) == GIT_REFERENCE_DIRECT); + + /* ...and that it points to the current master tip */ + cl_assert_equal_oid(&id, git_reference_target(resolved_ref)); + git_reference_free(looked_up_ref); + git_reference_free(resolved_ref); + + /* Similar test with a fresh new repository */ + cl_git_pass(git_repository_open(&repo2, "testrepo")); + + cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker)); + cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); + cl_assert_equal_oid(&id, git_reference_target(resolved_ref)); + + git_repository_free(repo2); + + git_reference_free(new_reference); + git_reference_free(looked_up_ref); + git_reference_free(resolved_ref); +} + +void test_refs_create__symbolic_with_arbitrary_content(void) +{ + git_reference *new_reference, *looked_up_ref; + git_repository *repo2; + git_oid id; + + const char *new_head_tracker = "ANOTHER_HEAD_TRACKER"; + const char *arbitrary_target = "ARBITRARY DATA"; + + git_oid_fromstr(&id, current_master_tip); + + /* Attempt to create symbolic ref with arbitrary data in target + * fails by default + */ + cl_git_fail(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL)); + + git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 0); + + /* With strict target validation disabled, ref creation succeeds */ + cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL)); + + /* Ensure the reference can be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); + cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_SYMBOLIC); + cl_assert(reference_is_packed(looked_up_ref) == 0); + cl_assert_equal_s(looked_up_ref->name, new_head_tracker); + git_reference_free(looked_up_ref); + + /* Ensure the target is what we expect it to be */ + cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target); + + /* Similar test with a fresh new repository object */ + cl_git_pass(git_repository_open(&repo2, "testrepo")); + + /* Ensure the reference can be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker)); + cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_SYMBOLIC); + cl_assert(reference_is_packed(looked_up_ref) == 0); + cl_assert_equal_s(looked_up_ref->name, new_head_tracker); + + /* Ensure the target is what we expect it to be */ + cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target); + + git_repository_free(repo2); + git_reference_free(new_reference); + git_reference_free(looked_up_ref); +} + +void test_refs_create__deep_symbolic(void) +{ + /* create a deep symbolic reference */ + git_reference *new_reference, *looked_up_ref, *resolved_ref; + git_oid id; + + const char *new_head_tracker = "deep/rooted/tracker"; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL)); + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); + cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); + cl_assert_equal_oid(&id, git_reference_target(resolved_ref)); + + git_reference_free(new_reference); + git_reference_free(looked_up_ref); + git_reference_free(resolved_ref); +} + +void test_refs_create__oid(void) +{ + /* create a new OID reference */ + git_reference *new_reference, *looked_up_ref; + git_repository *repo2; + git_oid id; + + const char *new_head = "refs/heads/new-head"; + + git_oid_fromstr(&id, current_master_tip); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL)); + + /* Ensure the reference can be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head)); + cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_DIRECT); + cl_assert(reference_is_packed(looked_up_ref) == 0); + cl_assert_equal_s(looked_up_ref->name, new_head); + + /* ...and that it points to the current master tip */ + cl_assert_equal_oid(&id, git_reference_target(looked_up_ref)); + git_reference_free(looked_up_ref); + + /* Similar test with a fresh new repository */ + cl_git_pass(git_repository_open(&repo2, "testrepo")); + + cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head)); + cl_assert_equal_oid(&id, git_reference_target(looked_up_ref)); + + git_repository_free(repo2); + + git_reference_free(new_reference); + git_reference_free(looked_up_ref); +} + +/* Can by default create a reference that targets at an unknown id */ +void test_refs_create__oid_unknown_succeeds_without_strict(void) +{ + git_reference *new_reference, *looked_up_ref; + git_oid id; + + const char *new_head = "refs/heads/new-head"; + + git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL)); + git_reference_free(new_reference); + + /* Ensure the reference can't be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head)); + git_reference_free(looked_up_ref); +} + +/* Strict object enforcement enforces valid object id */ +void test_refs_create__oid_unknown_fails_by_default(void) +{ + git_reference *new_reference, *looked_up_ref; + git_oid id; + + const char *new_head = "refs/heads/new-head"; + + git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); + + /* Create and write the new object id reference */ + cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL)); + + /* Ensure the reference can't be looked-up... */ + cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head)); +} + +void test_refs_create__propagate_eexists(void) +{ + git_oid oid; + + /* Make sure it works for oid and for symbolic both */ + cl_git_pass(git_oid_fromstr(&oid, current_master_tip)); + cl_git_fail_with(GIT_EEXISTS, git_reference_create(NULL, g_repo, current_head_target, &oid, false, NULL)); + cl_git_fail_with(GIT_EEXISTS, git_reference_symbolic_create(NULL, g_repo, "HEAD", current_head_target, false, NULL)); +} + +void test_refs_create__existing_dir_propagates_edirectory(void) +{ + git_reference *new_reference, *fail_reference; + git_oid id; + const char *dir_head = "refs/heads/new-dir/new-head", + *fail_head = "refs/heads/new-dir"; + + git_oid_fromstr(&id, current_master_tip); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&new_reference, g_repo, dir_head, &id, 1, NULL)); + cl_git_fail_with(GIT_EDIRECTORY, + git_reference_create(&fail_reference, g_repo, fail_head, &id, false, NULL)); + + git_reference_free(new_reference); +} + +static void test_invalid_name(const char *name) +{ + git_reference *new_reference; + git_oid id; + + git_oid_fromstr(&id, current_master_tip); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create( + &new_reference, g_repo, name, &id, 0, NULL)); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create( + &new_reference, g_repo, name, current_head_target, 0, NULL)); +} + +void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + test_invalid_name("refs/heads/inv@{id"); + test_invalid_name("refs/heads/back\\slash"); + + test_invalid_name("refs/heads/foo "); + test_invalid_name("refs/heads/foo /bar"); + test_invalid_name("refs/heads/com1:bar/foo"); + + test_invalid_name("refs/heads/e:"); + test_invalid_name("refs/heads/c:/foo"); + + test_invalid_name("refs/heads/foo."); +} + +static void test_win32_name(const char *name) +{ + git_reference *new_reference = NULL; + git_oid id; + int ret; + + git_oid_fromstr(&id, current_master_tip); + + ret = git_reference_create(&new_reference, g_repo, name, &id, 0, NULL); + +#ifdef GIT_WIN32 + cl_assert_equal_i(GIT_EINVALIDSPEC, ret); +#else + cl_git_pass(ret); +#endif + + git_reference_free(new_reference); +} + +void test_refs_create__creating_a_loose_ref_with_invalid_windows_name(void) +{ + test_win32_name("refs/heads/foo./bar"); + + test_win32_name("refs/heads/aux"); + test_win32_name("refs/heads/aux.foo/bar"); + + test_win32_name("refs/heads/com1"); +} + +/* Creating a loose ref involves fsync'ing the reference, the + * reflog and (on non-Windows) the containing directories. + * Creating a packed ref involves fsync'ing the packed ref file + * and (on non-Windows) the containing directory. + */ +#ifdef GIT_WIN32 +static int expected_fsyncs_create = 2, expected_fsyncs_compress = 1; +#else +static int expected_fsyncs_create = 4, expected_fsyncs_compress = 2; +#endif + +static void count_fsyncs(size_t *create_count, size_t *compress_count) +{ + git_reference *ref = NULL; + git_refdb *refdb; + git_oid id; + + p_fsync__cnt = 0; + + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/fsync_test", &id, 0, "log message")); + git_reference_free(ref); + + *create_count = p_fsync__cnt; + p_fsync__cnt = 0; + + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + + *compress_count = p_fsync__cnt; + p_fsync__cnt = 0; +} + +void test_refs_create__does_not_fsync_by_default(void) +{ + size_t create_count, compress_count; + count_fsyncs(&create_count, &compress_count); + + cl_assert_equal_i(0, create_count); + cl_assert_equal_i(0, compress_count); +} + +void test_refs_create__fsyncs_when_global_opt_set(void) +{ + size_t create_count, compress_count; + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1)); + count_fsyncs(&create_count, &compress_count); + + cl_assert_equal_i(expected_fsyncs_create, create_count); + cl_assert_equal_i(expected_fsyncs_compress, compress_count); +} + +void test_refs_create__fsyncs_when_repo_config_set(void) +{ + size_t create_count, compress_count; + + cl_repo_set_bool(g_repo, "core.fsyncObjectFiles", true); + + count_fsyncs(&create_count, &compress_count); + + cl_assert_equal_i(expected_fsyncs_create, create_count); + cl_assert_equal_i(expected_fsyncs_compress, compress_count); +} diff --git a/tests/libgit2/refs/delete.c b/tests/libgit2/refs/delete.c new file mode 100644 index 000000000..42cc534b5 --- /dev/null +++ b/tests/libgit2/refs/delete.c @@ -0,0 +1,118 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "git2/reflog.h" +#include "git2/refdb.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *packed_test_head_name = "refs/heads/packed-test"; +static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; + +static git_repository *g_repo; + + + +void test_refs_delete__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_delete__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_refs_delete__packed_loose(void) +{ + /* deleting a ref which is both packed and loose should remove both tracks in the filesystem */ + git_reference *looked_up_ref, *another_looked_up_ref; + git_str temp_path = GIT_STR_INIT; + + /* Ensure the loose reference exists on the file system */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name)); + cl_assert(git_fs_path_exists(temp_path.ptr)); + + /* Lookup the reference */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); + + /* Ensure it's the loose version that has been found */ + cl_assert(reference_is_packed(looked_up_ref) == 0); + + /* Now that the reference is deleted... */ + cl_git_pass(git_reference_delete(looked_up_ref)); + git_reference_free(looked_up_ref); + + /* Looking up the reference once again should not retrieve it */ + cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name)); + + /* Ensure the loose reference doesn't exist any longer on the file system */ + cl_assert(!git_fs_path_exists(temp_path.ptr)); + + git_reference_free(another_looked_up_ref); + git_str_dispose(&temp_path); +} + +void test_refs_delete__packed_only(void) +{ + /* can delete a just packed reference */ + git_reference *ref; + git_refdb *refdb; + git_oid id; + const char *new_ref = "refs/heads/new_ref"; + + git_oid_fromstr(&id, current_master_tip); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &id, 0, NULL)); + git_reference_free(ref); + + /* Lookup the reference */ + cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref)); + + /* Ensure it's a loose reference */ + cl_assert(reference_is_packed(ref) == 0); + + /* Pack all existing references */ + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + + /* Reload the reference from disk */ + git_reference_free(ref); + cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref)); + + /* Ensure it's a packed reference */ + cl_assert(reference_is_packed(ref) == 1); + + /* This should pass */ + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + git_refdb_free(refdb); +} + +void test_refs_delete__remove(void) +{ + git_reference *ref; + + /* Check that passing no old values lets us delete */ + + cl_git_pass(git_reference_lookup(&ref, g_repo, packed_test_head_name)); + git_reference_free(ref); + + cl_git_pass(git_reference_remove(g_repo, packed_test_head_name)); + + cl_git_fail(git_reference_lookup(&ref, g_repo, packed_test_head_name)); +} + +void test_refs_delete__head(void) +{ + git_reference *ref; + + /* Check that it is not possible to delete HEAD */ + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_git_fail(git_reference_delete(ref)); + git_reference_free(ref); +} diff --git a/tests/libgit2/refs/dup.c b/tests/libgit2/refs/dup.c new file mode 100644 index 000000000..8a89cd95c --- /dev/null +++ b/tests/libgit2/refs/dup.c @@ -0,0 +1,42 @@ +#include "clar_libgit2.h" +#include "refs.h" + +static git_repository *g_repo; + +void test_refs_dup__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_dup__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_dup__direct(void) +{ + git_reference *a, *b; + + cl_git_pass(git_reference_lookup(&a, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_dup(&b, a)); + + cl_assert(git_reference_cmp(a, b) == 0); + cl_assert(git_reference_owner(b) == g_repo); + + git_reference_free(b); + git_reference_free(a); +} + +void test_refs_dup__symbolic(void) +{ + git_reference *a, *b; + + cl_git_pass(git_reference_lookup(&a, g_repo, "HEAD")); + cl_git_pass(git_reference_dup(&b, a)); + + cl_assert(git_reference_cmp(a, b) == 0); + cl_assert(git_reference_owner(b) == g_repo); + + git_reference_free(b); + git_reference_free(a); +} diff --git a/tests/libgit2/refs/foreachglob.c b/tests/libgit2/refs/foreachglob.c new file mode 100644 index 000000000..b208a95a2 --- /dev/null +++ b/tests/libgit2/refs/foreachglob.c @@ -0,0 +1,100 @@ +#include "clar_libgit2.h" +#include "refs.h" + +static git_repository *repo; +static git_reference *fake_remote; + +void test_refs_foreachglob__initialize(void) +{ + git_oid id; + + cl_fixture_sandbox("testrepo.git"); + cl_git_pass(git_repository_open(&repo, "testrepo.git")); + + cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL)); +} + +void test_refs_foreachglob__cleanup(void) +{ + git_reference_free(fake_remote); + fake_remote = NULL; + + git_repository_free(repo); + repo = NULL; + + cl_fixture_cleanup("testrepo.git"); +} + +static int count_cb(const char *reference_name, void *payload) +{ + int *count = (int *)payload; + + GIT_UNUSED(reference_name); + + (*count)++; + + return 0; +} + +static void assert_retrieval(const char *glob, int expected_count) +{ + int count = 0; + + cl_git_pass(git_reference_foreach_glob(repo, glob, count_cb, &count)); + + cl_assert_equal_i(expected_count, count); +} + +void test_refs_foreachglob__retrieve_all_refs(void) +{ + /* 13 heads (including one packed head) + 1 note + 2 remotes + 7 tags + 1 blob */ + assert_retrieval("*", 24); +} + +void test_refs_foreachglob__retrieve_remote_branches(void) +{ + assert_retrieval("refs/remotes/*", 2); +} + +void test_refs_foreachglob__retrieve_local_branches(void) +{ + assert_retrieval("refs/heads/*", 13); +} + +void test_refs_foreachglob__retrieve_nonexistant(void) +{ + assert_retrieval("refs/nonexistent/*", 0); +} + +void test_refs_foreachglob__retrieve_partially_named_references(void) +{ + /* + * refs/heads/packed-test, refs/heads/test + * refs/remotes/test/master, refs/tags/test + */ + + assert_retrieval("*test*", 4); +} + + +static int interrupt_cb(const char *reference_name, void *payload) +{ + int *count = (int *)payload; + + GIT_UNUSED(reference_name); + + (*count)++; + + return (*count == 11) ? -1000 : 0; +} + +void test_refs_foreachglob__can_cancel(void) +{ + int count = 0; + + cl_assert_equal_i(-1000, git_reference_foreach_glob( + repo, "*", interrupt_cb, &count) ); + + cl_assert_equal_i(11, count); +} diff --git a/tests/libgit2/refs/isvalidname.c b/tests/libgit2/refs/isvalidname.c new file mode 100644 index 000000000..063f0f798 --- /dev/null +++ b/tests/libgit2/refs/isvalidname.c @@ -0,0 +1,38 @@ +#include "clar_libgit2.h" + +static bool is_valid_name(const char *name) +{ + int valid; + cl_git_pass(git_reference_name_is_valid(&valid, name)); + return valid; +} + +void test_refs_isvalidname__can_detect_invalid_formats(void) +{ + cl_assert_equal_i(false, is_valid_name("refs/tags/0.17.0^{}")); + cl_assert_equal_i(false, is_valid_name("TWO/LEVELS")); + cl_assert_equal_i(false, is_valid_name("ONE.LEVEL")); + cl_assert_equal_i(false, is_valid_name("HEAD/")); + cl_assert_equal_i(false, is_valid_name("NO_TRAILING_UNDERSCORE_")); + cl_assert_equal_i(false, is_valid_name("_NO_LEADING_UNDERSCORE")); + cl_assert_equal_i(false, is_valid_name("HEAD/aa")); + cl_assert_equal_i(false, is_valid_name("lower_case")); + cl_assert_equal_i(false, is_valid_name("/stupid/name/master")); + cl_assert_equal_i(false, is_valid_name("/")); + cl_assert_equal_i(false, is_valid_name("//")); + cl_assert_equal_i(false, is_valid_name("")); + cl_assert_equal_i(false, is_valid_name("refs/heads/sub.lock/webmatrix")); +} + +void test_refs_isvalidname__wont_hopefully_choke_on_valid_formats(void) +{ + cl_assert_equal_i(true, is_valid_name("refs/tags/0.17.0")); + cl_assert_equal_i(true, is_valid_name("refs/LEVELS")); + cl_assert_equal_i(true, is_valid_name("HEAD")); + cl_assert_equal_i(true, is_valid_name("ONE_LEVEL")); + cl_assert_equal_i(true, is_valid_name("refs/stash")); + cl_assert_equal_i(true, is_valid_name("refs/remotes/origin/bim_with_3d@11296")); + cl_assert_equal_i(true, is_valid_name("refs/master{yesterday")); + cl_assert_equal_i(true, is_valid_name("refs/master}yesterday")); + cl_assert_equal_i(true, is_valid_name("refs/master{yesterday}")); +} diff --git a/tests/libgit2/refs/iterator.c b/tests/libgit2/refs/iterator.c new file mode 100644 index 000000000..a4f9e62ec --- /dev/null +++ b/tests/libgit2/refs/iterator.c @@ -0,0 +1,274 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "vector.h" + +static git_repository *repo; + +void test_refs_iterator__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_iterator__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static const char *refnames[] = { + "refs/blobs/annotated_tag_to_blob", + "refs/heads/br2", + "refs/heads/cannot-fetch", + "refs/heads/chomped", + "refs/heads/haacked", + "refs/heads/master", + "refs/heads/not-good", + "refs/heads/packed", + "refs/heads/packed-test", + "refs/heads/subtrees", + "refs/heads/test", + "refs/heads/track-local", + "refs/heads/trailing", + "refs/heads/with-empty-log", + "refs/notes/fanout", + "refs/remotes/test/master", + "refs/tags/annotated_tag_to_blob", + "refs/tags/e90810b", + "refs/tags/hard_tag", + "refs/tags/point_to_blob", + "refs/tags/taggerless", + "refs/tags/test", + "refs/tags/wrapped_tag", + NULL +}; + +static const char *refnames_with_symlink[] = { + "refs/blobs/annotated_tag_to_blob", + "refs/heads/br2", + "refs/heads/cannot-fetch", + "refs/heads/chomped", + "refs/heads/haacked", + "refs/heads/link/a", + "refs/heads/link/b", + "refs/heads/link/c", + "refs/heads/link/d", + "refs/heads/master", + "refs/heads/not-good", + "refs/heads/packed", + "refs/heads/packed-test", + "refs/heads/subtrees", + "refs/heads/test", + "refs/heads/track-local", + "refs/heads/trailing", + "refs/heads/with-empty-log", + "refs/notes/fanout", + "refs/remotes/test/master", + "refs/tags/annotated_tag_to_blob", + "refs/tags/e90810b", + "refs/tags/hard_tag", + "refs/tags/point_to_blob", + "refs/tags/taggerless", + "refs/tags/test", + "refs/tags/wrapped_tag", + NULL +}; + +static int refcmp_cb(const void *a, const void *b) +{ + const git_reference *refa = (const git_reference *)a; + const git_reference *refb = (const git_reference *)b; + + return strcmp(refa->name, refb->name); +} + +static void assert_all_refnames_match(const char **expected, git_vector *names) +{ + size_t i; + git_reference *ref; + + git_vector_sort(names); + + git_vector_foreach(names, i, ref) { + cl_assert(expected[i] != NULL); + cl_assert_equal_s(expected[i], ref->name); + git_reference_free(ref); + } + cl_assert(expected[i] == NULL); + + git_vector_free(names); +} + +void test_refs_iterator__list(void) +{ + git_reference_iterator *iter; + git_vector output; + git_reference *ref; + + cl_git_pass(git_vector_init(&output, 33, &refcmp_cb)); + cl_git_pass(git_reference_iterator_new(&iter, repo)); + + while (1) { + int error = git_reference_next(&ref, iter); + if (error == GIT_ITEROVER) + break; + cl_git_pass(error); + cl_git_pass(git_vector_insert(&output, ref)); + } + + git_reference_iterator_free(iter); + + assert_all_refnames_match(refnames, &output); +} + +void test_refs_iterator__empty(void) +{ + git_reference_iterator *iter; + git_odb *odb; + git_reference *ref; + git_repository *empty; + + cl_git_pass(git_odb_new(&odb)); + cl_git_pass(git_repository_wrap_odb(&empty, odb)); + + cl_git_pass(git_reference_iterator_new(&iter, empty)); + cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter)); + + git_reference_iterator_free(iter); + git_odb_free(odb); + git_repository_free(empty); +} + +static int refs_foreach_cb(git_reference *reference, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, reference)); + return 0; +} + +void test_refs_iterator__foreach(void) +{ + git_vector output; + cl_git_pass(git_vector_init(&output, 33, &refcmp_cb)); + cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output)); + assert_all_refnames_match(refnames, &output); +} + +void test_refs_iterator__foreach_through_symlink(void) +{ + git_vector output; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); + + cl_git_pass(p_mkdir("refs", 0777)); + cl_git_mkfile("refs/a", "1234567890123456789012345678901234567890"); + cl_git_mkfile("refs/b", "1234567890123456789012345678901234567890"); + cl_git_mkfile("refs/c", "1234567890123456789012345678901234567890"); + cl_git_mkfile("refs/d", "1234567890123456789012345678901234567890"); + + cl_git_pass(p_symlink("../../../refs", "testrepo.git/refs/heads/link")); + + cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output)); + assert_all_refnames_match(refnames_with_symlink, &output); +} + +static int refs_foreach_cancel_cb(git_reference *reference, void *payload) +{ + int *cancel_after = payload; + + git_reference_free(reference); + + if (!*cancel_after) + return -333; + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_can_cancel(void) +{ + int cancel_after = 3; + cl_git_fail_with( + git_reference_foreach(repo, refs_foreach_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} + +static int refs_foreach_name_cb(const char *name, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, git__strdup(name))); + return 0; +} + +void test_refs_iterator__foreach_name(void) +{ + git_vector output; + size_t i; + char *name; + + cl_git_pass(git_vector_init(&output, 32, &git__strcmp_cb)); + cl_git_pass( + git_reference_foreach_name(repo, refs_foreach_name_cb, &output)); + + git_vector_sort(&output); + + git_vector_foreach(&output, i, name) { + cl_assert(refnames[i] != NULL); + cl_assert_equal_s(refnames[i], name); + git__free(name); + } + + git_vector_free(&output); +} + +static int refs_foreach_name_cancel_cb(const char *name, void *payload) +{ + int *cancel_after = payload; + if (!*cancel_after) + return -333; + GIT_UNUSED(name); + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_name_can_cancel(void) +{ + int cancel_after = 5; + cl_git_fail_with( + git_reference_foreach_name( + repo, refs_foreach_name_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} + +void test_refs_iterator__concurrent_delete(void) +{ + git_reference_iterator *iter; + size_t full_count = 0, concurrent_count = 0; + const char *name; + int error; + + cl_git_sandbox_cleanup(); + repo = cl_git_sandbox_init("testrepo"); + + cl_git_pass(git_reference_iterator_new(&iter, repo)); + while ((error = git_reference_next_name(&name, iter)) == 0) { + full_count++; + } + + git_reference_iterator_free(iter); + cl_assert_equal_i(GIT_ITEROVER, error); + + cl_git_pass(git_reference_iterator_new(&iter, repo)); + while ((error = git_reference_next_name(&name, iter)) == 0) { + cl_git_pass(git_reference_remove(repo, name)); + concurrent_count++; + } + + git_reference_iterator_free(iter); + cl_assert_equal_i(GIT_ITEROVER, error); + + cl_assert_equal_i(full_count, concurrent_count); +} diff --git a/tests/libgit2/refs/list.c b/tests/libgit2/refs/list.c new file mode 100644 index 000000000..8085ff84b --- /dev/null +++ b/tests/libgit2/refs/list.c @@ -0,0 +1,57 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + +static git_repository *g_repo; + + + +void test_refs_list__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_list__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_refs_list__all(void) +{ + /* try to list all the references in our test repo */ + git_strarray ref_list; + + cl_git_pass(git_reference_list(&ref_list, g_repo)); + + /*{ + unsigned short i; + for (i = 0; i < ref_list.count; ++i) + printf("# %s\n", ref_list.strings[i]); + }*/ + + /* We have exactly 12 refs in total if we include the packed ones: + * there is a reference that exists both in the packfile and as + * loose, but we only list it once */ + cl_assert_equal_i((int)ref_list.count, 19); + + git_strarray_dispose(&ref_list); +} + +void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void) +{ + git_strarray ref_list; + + /* Create a fake locked reference */ + cl_git_mkfile( + "./testrepo/.git/refs/heads/hanwen.lock", + "144344043ba4d4a405da03de3844aa829ae8be0e\n"); + + cl_git_pass(git_reference_list(&ref_list, g_repo)); + cl_assert_equal_i((int)ref_list.count, 19); + + git_strarray_dispose(&ref_list); +} diff --git a/tests/libgit2/refs/listall.c b/tests/libgit2/refs/listall.c new file mode 100644 index 000000000..9da8d1ac3 --- /dev/null +++ b/tests/libgit2/refs/listall.c @@ -0,0 +1,47 @@ +#include "clar_libgit2.h" +#include "posix.h" + +static git_repository *repo; +static git_strarray ref_list; + +static void ensure_no_refname_starts_with_a_forward_slash(const char *path) +{ + size_t i; + + cl_git_pass(git_repository_open(&repo, path)); + cl_git_pass(git_reference_list(&ref_list, repo)); + + cl_assert(ref_list.count > 0); + + for (i = 0; i < ref_list.count; i++) + cl_assert(git__prefixcmp(ref_list.strings[i], "/") != 0); + + git_strarray_dispose(&ref_list); + git_repository_free(repo); +} + +void test_refs_listall__from_repository_opened_through_workdir_path(void) +{ + cl_fixture_sandbox("status"); + cl_git_pass(p_rename("status/.gitted", "status/.git")); + + ensure_no_refname_starts_with_a_forward_slash("status"); + + cl_fixture_cleanup("status"); +} + +void test_refs_listall__from_repository_opened_through_gitdir_path(void) +{ + ensure_no_refname_starts_with_a_forward_slash(cl_fixture("testrepo.git")); +} + +void test_refs_listall__from_repository_with_no_trailing_newline(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("bad_tag.git"))); + cl_git_pass(git_reference_list(&ref_list, repo)); + + cl_assert(ref_list.count > 0); + + git_strarray_dispose(&ref_list); + git_repository_free(repo); +} diff --git a/tests/libgit2/refs/lookup.c b/tests/libgit2/refs/lookup.c new file mode 100644 index 000000000..01e956de2 --- /dev/null +++ b/tests/libgit2/refs/lookup.c @@ -0,0 +1,68 @@ +#include "clar_libgit2.h" +#include "refs.h" + +static git_repository *g_repo; + +void test_refs_lookup__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_lookup__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_lookup__with_resolve(void) +{ + git_reference *a, *b, *temp; + + cl_git_pass(git_reference_lookup(&temp, g_repo, "HEAD")); + cl_git_pass(git_reference_resolve(&a, temp)); + git_reference_free(temp); + + cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD", 5)); + cl_assert(git_reference_cmp(a, b) == 0); + git_reference_free(b); + + cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD_TRACKER", 5)); + cl_assert(git_reference_cmp(a, b) == 0); + git_reference_free(b); + + git_reference_free(a); +} + +void test_refs_lookup__invalid_name(void) +{ + git_oid oid; + cl_git_fail(git_reference_name_to_id(&oid, g_repo, "/refs/tags/point_to_blob")); +} + +void test_refs_lookup__oid(void) +{ + git_oid tag, expected; + + cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob")); + cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08")); + cl_assert_equal_oid(&expected, &tag); +} + +void test_refs_lookup__namespace(void) +{ + int error; + git_reference *ref; + + error = git_reference_lookup(&ref, g_repo, "refs/heads"); + cl_assert_equal_i(error, GIT_ENOTFOUND); + + error = git_reference_lookup(&ref, g_repo, "refs/heads/"); + cl_assert_equal_i(error, GIT_EINVALIDSPEC); +} + +void test_refs_lookup__dwim_notfound(void) +{ + git_reference *ref; + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_dwim(&ref, g_repo, "idontexist")); + cl_assert_equal_s("no reference found for shorthand 'idontexist'", git_error_last()->message); +} diff --git a/tests/libgit2/refs/namespaces.c b/tests/libgit2/refs/namespaces.c new file mode 100644 index 000000000..19456b5a4 --- /dev/null +++ b/tests/libgit2/refs/namespaces.c @@ -0,0 +1,36 @@ +#include "clar_libgit2.h" + +#include "repository.h" + +static git_repository *g_repo; + +void test_refs_namespaces__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_namespaces__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_namespaces__get_and_set(void) +{ + cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo)); + + cl_git_pass(git_repository_set_namespace(g_repo, "namespace")); + cl_assert_equal_s("namespace", git_repository_get_namespace(g_repo)); + + cl_git_pass(git_repository_set_namespace(g_repo, NULL)); + cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo)); +} + +void test_refs_namespaces__namespace_doesnt_show_normal_refs(void) +{ + static git_strarray ref_list; + + cl_git_pass(git_repository_set_namespace(g_repo, "namespace")); + cl_git_pass(git_reference_list(&ref_list, g_repo)); + cl_assert_equal_i(0, ref_list.count); + git_strarray_dispose(&ref_list); +} diff --git a/tests/libgit2/refs/normalize.c b/tests/libgit2/refs/normalize.c new file mode 100644 index 000000000..ff815002c --- /dev/null +++ b/tests/libgit2/refs/normalize.c @@ -0,0 +1,401 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + +/* Helpers */ +static void ensure_refname_normalized( + unsigned int flags, + const char *input_refname, + const char *expected_refname) +{ + char buffer_out[GIT_REFNAME_MAX]; + + cl_git_pass(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags)); + + cl_assert_equal_s(expected_refname, buffer_out); +} + +static void ensure_refname_invalid(unsigned int flags, const char *input_refname) +{ + char buffer_out[GIT_REFNAME_MAX]; + + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags)); +} + +void test_refs_normalize__can_normalize_a_direct_reference_name(void) +{ + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/dummy/a", "refs/dummy/a"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/stash", "refs/stash"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/tags/a", "refs/tags/a"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a/b", "refs/heads/a/b"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a./b", "refs/heads/a./b"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a"); +} + +void test_refs_normalize__cannot_normalize_any_direct_reference_name(void) +{ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "a"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "/a"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "//a"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, ""); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "/refs/heads/a/"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a/"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a."); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a.lock"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/foo?bar"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads\foo"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/.a/b"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/foo/../bar"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/foo..bar"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/./foo"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/v@{ation"); +} + +void test_refs_normalize__symbolic(void) +{ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, ""); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "heads\foo"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "/"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "///"); + + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "ALL_CAPS_AND_UNDERSCORES", "ALL_CAPS_AND_UNDERSCORES"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/MixedCasing", "refs/MixedCasing"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs///heads///a", "refs/heads/a"); + + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "HEAD", "HEAD"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "MERGE_HEAD", "MERGE_HEAD"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "FETCH_HEAD", "FETCH_HEAD"); +} + +/* Ported from JGit, BSD licence. + * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 + * + * Copyright (C) 2009, Google Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Git Development Community nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void test_refs_normalize__jgit_suite(void) +{ + /* tests borrowed from JGit */ + +/* EmptyString */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, ""); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "/"); + +/* MustHaveTwoComponents */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_NORMAL, "master"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_NORMAL, "heads/master", "heads/master"); + +/* ValidHead */ + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master", "refs/heads/master"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/pu", "refs/heads/pu"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/z", "refs/heads/z"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/FoO", "refs/heads/FoO"); + +/* ValidTag */ + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/tags/v1.0", "refs/tags/v1.0"); + +/* NoLockSuffix */ + ensure_refname_invalid(GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master.lock"); + +/* NoDirectorySuffix */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master/"); + +/* NoSpace */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/i haz space"); + +/* NoAsciiControlCharacters */ + { + char c; + char buffer[GIT_REFNAME_MAX]; + for (c = '\1'; c < ' '; c++) { + p_snprintf(buffer, sizeof(buffer), "refs/heads/mast%cer", c); + ensure_refname_invalid(GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, buffer); + } + } + +/* NoBareDot */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/."); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/.."); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/./master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/../master"); + +/* NoLeadingOrTrailingDot */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "."); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/.bar"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/..bar"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/bar."); + +/* ContainsDot */ + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master..pu"); + +/* NoMagicRefCharacters */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master^"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/^master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "^refs/heads/master"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master~"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/~master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "~refs/heads/master"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master:"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/:master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, ":refs/heads/master"); + +/* ShellGlob */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master?"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/?master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "?refs/heads/master"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master["); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/[master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "[refs/heads/master"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master*"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/*master"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "*refs/heads/master"); + +/* ValidSpecialCharacters */ + ensure_refname_normalized + (GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/!", "refs/heads/!"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/\"", "refs/heads/\""); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/#", "refs/heads/#"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/$", "refs/heads/$"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/%", "refs/heads/%"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/&", "refs/heads/&"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/'", "refs/heads/'"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/(", "refs/heads/("); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/)", "refs/heads/)"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/+", "refs/heads/+"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/,", "refs/heads/,"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/-", "refs/heads/-"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/;", "refs/heads/;"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/<", "refs/heads/<"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/=", "refs/heads/="); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/>", "refs/heads/>"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/@", "refs/heads/@"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/]", "refs/heads/]"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/_", "refs/heads/_"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/`", "refs/heads/`"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/{", "refs/heads/{"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/|", "refs/heads/|"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/}", "refs/heads/}"); + + /* + * This is valid on UNIX, but not on Windows + * hence we make in invalid due to non-portability + */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/\\"); + +/* UnicodeNames */ + /* + * Currently this fails. + * ensure_refname_normalized(GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m"); + */ + +/* RefLogQueryIsValidRef */ + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1}"); + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1.hour.ago}"); +} + +void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_version(void) +{ + char buffer_out[21]; + + cl_git_pass(git_reference_normalize_name( + buffer_out, 21, "refs//heads///long///name", GIT_REFERENCE_FORMAT_NORMAL)); + cl_git_fail(git_reference_normalize_name( + buffer_out, 20, "refs//heads///long///name", GIT_REFERENCE_FORMAT_NORMAL)); +} + +#define ONE_LEVEL_AND_REFSPEC \ + GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL \ + | GIT_REFERENCE_FORMAT_REFSPEC_PATTERN + +void test_refs_normalize__refspec_pattern(void) +{ + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "heads/*foo/bar", "heads/*foo/bar"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "heads/foo*/bar", "heads/foo*/bar"); + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "heads/f*o/bar", "heads/f*o/bar"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo"); + ensure_refname_normalized( + ONE_LEVEL_AND_REFSPEC, "FOO", "FOO"); + + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo/bar", "foo/bar"); + ensure_refname_normalized( + ONE_LEVEL_AND_REFSPEC, "foo/bar", "foo/bar"); + + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*/foo", "*/foo"); + ensure_refname_normalized( + ONE_LEVEL_AND_REFSPEC, "*/foo", "*/foo"); + + ensure_refname_normalized( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo/*/bar", "foo/*/bar"); + ensure_refname_normalized( + ONE_LEVEL_AND_REFSPEC, "foo/*/bar", "foo/*/bar"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*"); + ensure_refname_normalized( + ONE_LEVEL_AND_REFSPEC, "*", "*"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo/*/*"); + ensure_refname_invalid( + ONE_LEVEL_AND_REFSPEC, "foo/*/*"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*/foo/*"); + ensure_refname_invalid( + ONE_LEVEL_AND_REFSPEC, "*/foo/*"); + + ensure_refname_invalid( + GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*/*/foo"); + ensure_refname_invalid( + ONE_LEVEL_AND_REFSPEC, "*/*/foo"); +} diff --git a/tests/libgit2/refs/overwrite.c b/tests/libgit2/refs/overwrite.c new file mode 100644 index 000000000..1826b1257 --- /dev/null +++ b/tests/libgit2/refs/overwrite.c @@ -0,0 +1,136 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + +static const char *ref_name = "refs/heads/other"; +static const char *ref_master_name = "refs/heads/master"; +static const char *ref_branch_name = "refs/heads/branch"; +static const char *ref_test_name = "refs/heads/test"; + +static git_repository *g_repo; + +void test_refs_overwrite__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_overwrite__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_overwrite__symbolic(void) +{ + /* Overwrite an existing symbolic reference */ + git_reference *ref, *branch_ref; + + /* The target needds to exist and we need to check the name has changed */ + cl_git_pass(git_reference_symbolic_create(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0, NULL)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_branch_name, 0, NULL)); + git_reference_free(ref); + + /* Ensure it points to the right place*/ + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_SYMBOLIC); + cl_assert_equal_s(git_reference_symbolic_target(ref), ref_branch_name); + git_reference_free(ref); + + /* Ensure we can't create it unless we force it to */ + cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1, NULL)); + git_reference_free(ref); + + /* Ensure it points to the right place */ + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_SYMBOLIC); + cl_assert_equal_s(git_reference_symbolic_target(ref), ref_master_name); + + git_reference_free(ref); + git_reference_free(branch_ref); +} + +void test_refs_overwrite__object_id(void) +{ + /* Overwrite an existing object id reference */ + git_reference *ref; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + git_reference_free(ref); + + /* Create it */ + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL)); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_test_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + git_reference_free(ref); + + /* Ensure we can't overwrite unless we force it */ + cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL)); + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1, NULL)); + git_reference_free(ref); + + /* Ensure it has been overwritten */ + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_assert_equal_oid(&id, git_reference_target(ref)); + + git_reference_free(ref); +} + +void test_refs_overwrite__object_id_with_symbolic(void) +{ + /* Overwrite an existing object id reference with a symbolic one */ + git_reference *ref; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + git_reference_free(ref); + + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL)); + git_reference_free(ref); + cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1, NULL)); + git_reference_free(ref); + + /* Ensure it points to the right place */ + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_SYMBOLIC); + cl_assert_equal_s(git_reference_symbolic_target(ref), ref_master_name); + + git_reference_free(ref); +} + +void test_refs_overwrite__symbolic_with_object_id(void) +{ + /* Overwrite an existing symbolic reference with an object id one */ + git_reference *ref; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + git_reference_free(ref); + + /* Create the symbolic ref */ + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL)); + git_reference_free(ref); + /* It shouldn't overwrite unless we tell it to */ + cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL)); + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1, NULL)); + git_reference_free(ref); + + /* Ensure it points to the right place */ + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + cl_assert_equal_oid(&id, git_reference_target(ref)); + + git_reference_free(ref); +} diff --git a/tests/libgit2/refs/pack.c b/tests/libgit2/refs/pack.c new file mode 100644 index 000000000..1c1cd51cb --- /dev/null +++ b/tests/libgit2/refs/pack.c @@ -0,0 +1,105 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "git2/reflog.h" +#include "git2/refdb.h" +#include "reflog.h" +#include "refs.h" +#include "ref_helpers.h" + +static const char *loose_tag_ref_name = "refs/tags/e90810b"; + +static git_repository *g_repo; + +void test_refs_pack__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_pack__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void packall(void) +{ + git_refdb *refdb; + + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); +} + +void test_refs_pack__empty(void) +{ + /* create a packfile for an empty folder */ + git_str temp_path = GIT_STR_INIT; + + cl_git_pass(git_str_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir")); + cl_git_pass(git_futils_mkdir_r(temp_path.ptr, GIT_REFS_DIR_MODE)); + git_str_dispose(&temp_path); + + packall(); +} + +void test_refs_pack__loose(void) +{ + /* create a packfile from all the loose refs in a repo */ + git_reference *reference; + git_str temp_path = GIT_STR_INIT; + + /* Ensure a known loose ref can be looked up */ + cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name)); + cl_assert(reference_is_packed(reference) == 0); + cl_assert_equal_s(reference->name, loose_tag_ref_name); + git_reference_free(reference); + + /* + * We are now trying to pack also a loose reference + * called `points_to_blob`, to make sure we can properly + * pack weak tags + */ + packall(); + + /* Ensure the packed-refs file exists */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE)); + cl_assert(git_fs_path_exists(temp_path.ptr)); + + /* Ensure the known ref can still be looked up but is now packed */ + cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name)); + cl_assert(reference_is_packed(reference)); + cl_assert_equal_s(reference->name, loose_tag_ref_name); + + /* Ensure the known ref has been removed from the loose folder structure */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name)); + cl_assert(!git_fs_path_exists(temp_path.ptr)); + + git_reference_free(reference); + git_str_dispose(&temp_path); +} + +void test_refs_pack__symbolic(void) +{ + /* create a packfile from loose refs skipping symbolic refs */ + int i; + git_oid head; + git_reference *ref; + char name[128]; + + cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); + + /* make a bunch of references */ + + for (i = 0; i < 100; ++i) { + p_snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i); + cl_git_pass(git_reference_symbolic_create( + &ref, g_repo, name, "refs/heads/master", 0, NULL)); + git_reference_free(ref); + + p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", i); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL)); + git_reference_free(ref); + } + + packall(); +} diff --git a/tests/libgit2/refs/peel.c b/tests/libgit2/refs/peel.c new file mode 100644 index 000000000..38f3465a0 --- /dev/null +++ b/tests/libgit2/refs/peel.c @@ -0,0 +1,131 @@ +#include "clar_libgit2.h" + +static git_repository *g_repo; +static git_repository *g_peel_repo; + +void test_refs_peel__initialize(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + cl_git_pass(git_repository_open(&g_peel_repo, cl_fixture("peeled.git"))); +} + +void test_refs_peel__cleanup(void) +{ + git_repository_free(g_repo); + g_repo = NULL; + git_repository_free(g_peel_repo); + g_peel_repo = NULL; +} + +static void assert_peel_generic( + git_repository *repo, + const char *ref_name, + git_object_t requested_type, + const char* expected_sha, + git_object_t expected_type) +{ + git_oid expected_oid; + git_reference *ref; + git_object *peeled; + + cl_git_pass(git_reference_lookup(&ref, repo, ref_name)); + + cl_git_pass(git_reference_peel(&peeled, ref, requested_type)); + + cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha)); + cl_assert_equal_oid(&expected_oid, git_object_id(peeled)); + + cl_assert_equal_i(expected_type, git_object_type(peeled)); + + git_object_free(peeled); + git_reference_free(ref); +} + +static void assert_peel( + const char *ref_name, + git_object_t requested_type, + const char* expected_sha, + git_object_t expected_type) +{ + assert_peel_generic(g_repo, ref_name, requested_type, + expected_sha, expected_type); +} + +static void assert_peel_error(int error, const char *ref_name, git_object_t requested_type) +{ + git_reference *ref; + git_object *peeled; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + + cl_assert_equal_i(error, git_reference_peel(&peeled, ref, requested_type)); + + git_reference_free(ref); +} + +void test_refs_peel__can_peel_a_tag(void) +{ + assert_peel("refs/tags/test", GIT_OBJECT_TAG, + "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", GIT_OBJECT_TAG); + assert_peel("refs/tags/test", GIT_OBJECT_COMMIT, + "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT); + assert_peel("refs/tags/test", GIT_OBJECT_TREE, + "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE); + assert_peel("refs/tags/point_to_blob", GIT_OBJECT_BLOB, + "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJECT_BLOB); +} + +void test_refs_peel__can_peel_a_branch(void) +{ + assert_peel("refs/heads/master", GIT_OBJECT_COMMIT, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJECT_COMMIT); + assert_peel("refs/heads/master", GIT_OBJECT_TREE, + "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJECT_TREE); +} + +void test_refs_peel__can_peel_a_symbolic_reference(void) +{ + assert_peel("HEAD", GIT_OBJECT_COMMIT, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJECT_COMMIT); + assert_peel("HEAD", GIT_OBJECT_TREE, + "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJECT_TREE); +} + +void test_refs_peel__cannot_peel_into_a_non_existing_target(void) +{ + assert_peel_error(GIT_EINVALIDSPEC, "refs/tags/point_to_blob", GIT_OBJECT_TAG); +} + +void test_refs_peel__can_peel_into_any_non_tag_object(void) +{ + assert_peel("refs/heads/master", GIT_OBJECT_ANY, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJECT_COMMIT); + assert_peel("refs/tags/point_to_blob", GIT_OBJECT_ANY, + "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJECT_BLOB); + assert_peel("refs/tags/test", GIT_OBJECT_ANY, + "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT); +} + +void test_refs_peel__can_peel_fully_peeled_packed_refs(void) +{ + assert_peel_generic(g_peel_repo, + "refs/tags/tag-inside-tags", GIT_OBJECT_ANY, + "0df1a5865c8abfc09f1f2182e6a31be550e99f07", + GIT_OBJECT_COMMIT); + assert_peel_generic(g_peel_repo, + "refs/foo/tag-outside-tags", GIT_OBJECT_ANY, + "0df1a5865c8abfc09f1f2182e6a31be550e99f07", + GIT_OBJECT_COMMIT); +} + +void test_refs_peel__can_peel_fully_peeled_tag_to_tag(void) +{ + assert_peel_generic(g_peel_repo, + "refs/tags/tag-inside-tags", GIT_OBJECT_TAG, + "c2596aa0151888587ec5c0187f261e63412d9e11", + GIT_OBJECT_TAG); + assert_peel_generic(g_peel_repo, + "refs/foo/tag-outside-tags", GIT_OBJECT_TAG, + "c2596aa0151888587ec5c0187f261e63412d9e11", + GIT_OBJECT_TAG); +} diff --git a/tests/libgit2/refs/races.c b/tests/libgit2/refs/races.c new file mode 100644 index 000000000..476789358 --- /dev/null +++ b/tests/libgit2/refs/races.c @@ -0,0 +1,170 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *commit_id = "099fabac3a9ea935598528c27f866e34089c2eff"; +static const char *refname = "refs/heads/master"; +static const char *other_refname = "refs/heads/foo"; +static const char *other_commit_id = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; + +static git_repository *g_repo; + +void test_refs_races__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_races__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_races__create_matching_zero_old(void) +{ + git_reference *ref; + git_oid id, zero_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&zero_id, "0000000000000000000000000000000000000000"); + + cl_git_fail(git_reference_create_matching(&ref, g_repo, refname, &id, 1, &zero_id, NULL)); + git_reference_free(ref); + + cl_git_pass(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL)); + git_reference_free(ref); + + cl_git_fail(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL)); + git_reference_free(ref); +} + +void test_refs_races__create_matching(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + cl_git_fail_with(GIT_EMODIFIED, git_reference_create_matching(&ref, g_repo, refname, &other_id, 1, &other_id, NULL)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, &id, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_set_target(&ref3, ref, &other_id, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} + +void test_refs_races__symbolic_create_matching(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_create_matching(&ref, g_repo, "HEAD", other_refname, 1, other_refname, NULL)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "HEAD", other_refname, 1, NULL, refname)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} + +void test_refs_races__delete(void) +{ + git_reference *ref, *ref2; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + /* We can delete a value that matches */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + /* We cannot delete a symbolic value that doesn't match */ + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref")); + cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "refs/symref", other_refname, 1, NULL, refname)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL)); + git_reference_free(ref); + + /* We cannot delete an oid value that doesn't match */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, &id, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); +} + +void test_refs_races__switch_oid_to_symbolic(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + /* Removing a direct ref when it's currently symbolic should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, refname, other_refname, 1, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL)); + git_reference_free(ref); + + /* Updating a direct ref when it's currently symbolic should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, refname, other_refname, 1, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_set_target(&ref3, ref, &other_id, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} + +void test_refs_races__switch_symbolic_to_oid(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + /* Removing a symbolic ref when it's currently direct should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref")); + cl_git_pass(git_reference_create(&ref2, g_repo, "refs/symref", &id, 1, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "refs/symref", refname, 1, NULL)); + git_reference_free(ref); + + /* Updating a symbolic ref when it's currently direct should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref")); + cl_git_pass(git_reference_create(&ref2, g_repo, "refs/symref", &id, 1, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} diff --git a/tests/libgit2/refs/read.c b/tests/libgit2/refs/read.c new file mode 100644 index 000000000..a622c770b --- /dev/null +++ b/tests/libgit2/refs/read.c @@ -0,0 +1,299 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *loose_tag_ref_name = "refs/tags/e90810b"; +static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist"; +static const char *head_tracker_sym_ref_name = "HEAD_TRACKER"; +static const char *current_head_target = "refs/heads/master"; +static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; +static const char *packed_head_name = "refs/heads/packed"; +static const char *packed_test_head_name = "refs/heads/packed-test"; + +static git_repository *g_repo; + +void test_refs_read__initialize(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); +} + +void test_refs_read__cleanup(void) +{ + git_repository_free(g_repo); + g_repo = NULL; +} + +void test_refs_read__loose_tag(void) +{ + /* lookup a loose tag reference */ + git_reference *reference; + git_object *object; + git_str ref_name_from_tag_name = GIT_STR_INIT; + + cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name)); + cl_assert(git_reference_type(reference) & GIT_REFERENCE_DIRECT); + cl_assert(reference_is_packed(reference) == 0); + cl_assert_equal_s(reference->name, loose_tag_ref_name); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJECT_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJECT_TAG); + + /* Ensure the name of the tag matches the name of the reference */ + cl_git_pass(git_str_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object))); + cl_assert_equal_s(ref_name_from_tag_name.ptr, loose_tag_ref_name); + git_str_dispose(&ref_name_from_tag_name); + + git_object_free(object); + + git_reference_free(reference); +} + +void test_refs_read__nonexisting_tag(void) +{ + /* lookup a loose tag reference that doesn't exist */ + git_reference *reference; + + cl_git_fail(git_reference_lookup(&reference, g_repo, non_existing_tag_ref_name)); + + git_reference_free(reference); +} + + +void test_refs_read__symbolic(void) +{ + /* lookup a symbolic reference */ + git_reference *reference, *resolved_ref; + git_object *object; + git_oid id; + + cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE)); + cl_assert(git_reference_type(reference) & GIT_REFERENCE_SYMBOLIC); + cl_assert(reference_is_packed(reference) == 0); + cl_assert_equal_s(reference->name, GIT_HEAD_FILE); + + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert(git_reference_type(resolved_ref) == GIT_REFERENCE_DIRECT); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJECT_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJECT_COMMIT); + + git_oid_fromstr(&id, current_master_tip); + cl_assert_equal_oid(&id, git_object_id(object)); + + git_object_free(object); + + git_reference_free(reference); + git_reference_free(resolved_ref); +} + +void test_refs_read__nested_symbolic(void) +{ + /* lookup a nested symbolic reference */ + git_reference *reference, *resolved_ref; + git_object *object; + git_oid id; + + cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name)); + cl_assert(git_reference_type(reference) & GIT_REFERENCE_SYMBOLIC); + cl_assert(reference_is_packed(reference) == 0); + cl_assert_equal_s(reference->name, head_tracker_sym_ref_name); + + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert(git_reference_type(resolved_ref) == GIT_REFERENCE_DIRECT); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJECT_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJECT_COMMIT); + + git_oid_fromstr(&id, current_master_tip); + cl_assert_equal_oid(&id, git_object_id(object)); + + git_object_free(object); + + git_reference_free(reference); + git_reference_free(resolved_ref); +} + +void test_refs_read__head_then_master(void) +{ + /* lookup the HEAD and resolve the master branch */ + git_reference *reference, *resolved_ref, *comp_base_ref; + + cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name)); + cl_git_pass(git_reference_resolve(&comp_base_ref, reference)); + git_reference_free(reference); + + cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE)); + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)); + git_reference_free(reference); + git_reference_free(resolved_ref); + + cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target)); + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)); + git_reference_free(reference); + git_reference_free(resolved_ref); + + git_reference_free(comp_base_ref); +} + +void test_refs_read__master_then_head(void) +{ + /* lookup the master branch and then the HEAD */ + git_reference *reference, *master_ref, *resolved_ref; + + cl_git_pass(git_reference_lookup(&master_ref, g_repo, current_head_target)); + cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE)); + + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert_equal_oid(git_reference_target(master_ref), git_reference_target(resolved_ref)); + + git_reference_free(reference); + git_reference_free(resolved_ref); + git_reference_free(master_ref); +} + + +void test_refs_read__packed(void) +{ + /* lookup a packed reference */ + git_reference *reference; + git_object *object; + + cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name)); + cl_assert(git_reference_type(reference) & GIT_REFERENCE_DIRECT); + cl_assert(reference_is_packed(reference)); + cl_assert_equal_s(reference->name, packed_head_name); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJECT_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJECT_COMMIT); + + git_object_free(object); + + git_reference_free(reference); +} + +void test_refs_read__loose_first(void) +{ + /* assure that a loose reference is looked up before a packed reference */ + git_reference *reference; + + cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name)); + git_reference_free(reference); + cl_git_pass(git_reference_lookup(&reference, g_repo, packed_test_head_name)); + cl_assert(git_reference_type(reference) & GIT_REFERENCE_DIRECT); + cl_assert(reference_is_packed(reference) == 0); + cl_assert_equal_s(reference->name, packed_test_head_name); + + git_reference_free(reference); +} + +void test_refs_read__chomped(void) +{ + git_reference *test, *chomped; + + cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test")); + cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped")); + cl_assert_equal_oid(git_reference_target(test), git_reference_target(chomped)); + + git_reference_free(test); + git_reference_free(chomped); +} + +void test_refs_read__trailing(void) +{ + git_reference *test, *trailing; + + cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test")); + cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing")); + cl_assert_equal_oid(git_reference_target(test), git_reference_target(trailing)); + git_reference_free(trailing); + cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD")); + + git_reference_free(test); + git_reference_free(trailing); +} + +void test_refs_read__unfound_return_ENOTFOUND(void) +{ + git_reference *reference; + git_oid id; + + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "TEST_MASTER")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "refs/test/master")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "refs/tags/test/master")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master")); + + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_name_to_id(&id, g_repo, "refs/tags/test/farther/master")); +} + +static void assert_is_branch(const char *name, bool expected_branchness) +{ + git_reference *reference; + cl_git_pass(git_reference_lookup(&reference, g_repo, name)); + cl_assert_equal_i(expected_branchness, git_reference_is_branch(reference)); + git_reference_free(reference); +} + +void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void) +{ + assert_is_branch("refs/heads/master", true); + assert_is_branch("refs/heads/packed", true); + assert_is_branch("refs/remotes/test/master", false); + assert_is_branch("refs/tags/e90810b", false); +} + +static void assert_is_tag(const char *name, bool expected_tagness) +{ + git_reference *reference; + cl_git_pass(git_reference_lookup(&reference, g_repo, name)); + cl_assert_equal_i(expected_tagness, git_reference_is_tag(reference)); + git_reference_free(reference); +} + +void test_refs_read__can_determine_if_a_reference_is_a_tag(void) +{ + assert_is_tag("refs/tags/e90810b", true); + assert_is_tag("refs/tags/test", true); + assert_is_tag("refs/heads/packed", false); + assert_is_tag("refs/remotes/test/master", false); +} + +static void assert_is_note(const char *name, bool expected_noteness) +{ + git_reference *reference; + cl_git_pass(git_reference_lookup(&reference, g_repo, name)); + cl_assert_equal_i(expected_noteness, git_reference_is_note(reference)); + git_reference_free(reference); +} + +void test_refs_read__can_determine_if_a_reference_is_a_note(void) +{ + assert_is_note("refs/notes/fanout", true); + assert_is_note("refs/heads/packed", false); + assert_is_note("refs/remotes/test/master", false); +} + +void test_refs_read__invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *reference; + git_oid id; + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_reference_lookup(&reference, g_repo, "refs/heads/Inv@{id")); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_reference_name_to_id(&id, g_repo, "refs/heads/Inv@{id")); +} diff --git a/tests/libgit2/refs/ref_helpers.c b/tests/libgit2/refs/ref_helpers.c new file mode 100644 index 000000000..70d5d36d5 --- /dev/null +++ b/tests/libgit2/refs/ref_helpers.c @@ -0,0 +1,25 @@ +#include "git2/repository.h" +#include "git2/refs.h" +#include "common.h" +#include "util.h" +#include "path.h" +#include "ref_helpers.h" + +int reference_is_packed(git_reference *ref) +{ + git_str ref_path = GIT_STR_INIT; + int packed; + + assert(ref); + + if (git_str_joinpath(&ref_path, + git_repository_path(git_reference_owner(ref)), + git_reference_name(ref)) < 0) + return -1; + + packed = !git_fs_path_isfile(ref_path.ptr); + + git_str_dispose(&ref_path); + + return packed; +} diff --git a/tests/libgit2/refs/ref_helpers.h b/tests/libgit2/refs/ref_helpers.h new file mode 100644 index 000000000..0ef55bfce --- /dev/null +++ b/tests/libgit2/refs/ref_helpers.h @@ -0,0 +1 @@ +int reference_is_packed(git_reference *ref); diff --git a/tests/libgit2/refs/reflog/drop.c b/tests/libgit2/refs/reflog/drop.c new file mode 100644 index 000000000..916bd9933 --- /dev/null +++ b/tests/libgit2/refs/reflog/drop.c @@ -0,0 +1,115 @@ +#include "clar_libgit2.h" + +#include "reflog.h" + +static git_repository *g_repo; +static git_reflog *g_reflog; +static size_t entrycount; + +void test_refs_reflog_drop__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + + git_reflog_read(&g_reflog, g_repo, "HEAD"); + entrycount = git_reflog_entrycount(g_reflog); +} + +void test_refs_reflog_drop__cleanup(void) +{ + git_reflog_free(g_reflog); + g_reflog = NULL; + + cl_git_sandbox_cleanup(); +} + +void test_refs_reflog_drop__dropping_a_non_exisiting_entry_from_the_log_returns_ENOTFOUND(void) +{ + cl_assert_equal_i(GIT_ENOTFOUND, git_reflog_drop(g_reflog, entrycount, 0)); + + cl_assert_equal_sz(entrycount, git_reflog_entrycount(g_reflog)); +} + +void test_refs_reflog_drop__can_drop_an_entry(void) +{ + cl_assert(entrycount > 4); + + cl_git_pass(git_reflog_drop(g_reflog, 2, 0)); + cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog)); +} + +void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void) +{ + const git_reflog_entry *before_current; + const git_reflog_entry *after_current; + git_oid before_current_old_oid, before_current_cur_oid; + + cl_assert(entrycount > 4); + + before_current = git_reflog_entry_byindex(g_reflog, 1); + + git_oid_cpy(&before_current_old_oid, &before_current->oid_old); + git_oid_cpy(&before_current_cur_oid, &before_current->oid_cur); + + cl_git_pass(git_reflog_drop(g_reflog, 1, 1)); + + cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog)); + + after_current = git_reflog_entry_byindex(g_reflog, 0); + + cl_assert_equal_i(0, git_oid_cmp(&before_current_old_oid, &after_current->oid_old)); + cl_assert(0 != git_oid_cmp(&before_current_cur_oid, &after_current->oid_cur)); +} + +void test_refs_reflog_drop__can_drop_the_oldest_entry(void) +{ + const git_reflog_entry *entry; + + cl_assert(entrycount > 2); + + cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0)); + cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog)); + + entry = git_reflog_entry_byindex(g_reflog, entrycount - 2); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) != 0); +} + +void test_refs_reflog_drop__can_drop_the_oldest_entry_and_rewrite_the_log_history(void) +{ + const git_reflog_entry *entry; + + cl_assert(entrycount > 2); + + cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1)); + cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog)); + + entry = git_reflog_entry_byindex(g_reflog, entrycount - 2); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); +} + +void test_refs_reflog_drop__can_drop_all_the_entries(void) +{ + cl_assert(--entrycount > 0); + + do { + cl_git_pass(git_reflog_drop(g_reflog, 0, 1)); + } while (--entrycount > 0); + + cl_git_pass(git_reflog_drop(g_reflog, 0, 1)); + + cl_assert_equal_i(0, (int)git_reflog_entrycount(g_reflog)); +} + +void test_refs_reflog_drop__can_persist_deletion_on_disk(void) +{ + cl_assert(entrycount > 2); + + cl_git_pass(git_reflog_drop(g_reflog, 0, 1)); + cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog)); + cl_git_pass(git_reflog_write(g_reflog)); + + git_reflog_free(g_reflog); + + git_reflog_read(&g_reflog, g_repo, "HEAD"); + + cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog)); +} diff --git a/tests/libgit2/refs/reflog/messages.c b/tests/libgit2/refs/reflog/messages.c new file mode 100644 index 000000000..ed183d2f2 --- /dev/null +++ b/tests/libgit2/refs/reflog/messages.c @@ -0,0 +1,421 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "refs.h" +#include "reflog_helpers.h" + +static const char *g_email = "foo@example.com"; +static git_repository *g_repo; + +/* Fixture setup and teardown */ +void test_refs_reflog_messages__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_set_ident(g_repo, "Foo Bar", g_email)); +} + +void test_refs_reflog_messages__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_reflog_messages__setting_head_updates_reflog(void) +{ + git_object *tag; + git_annotated_commit *annotated; + + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 4 */ + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/unborn")); + cl_git_pass(git_revparse_single(&tag, g_repo, "tags/test")); + cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(tag))); /* 3 */ + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 2 */ + cl_git_pass(git_repository_set_head(g_repo, "refs/tags/test")); /* 1 */ + cl_git_pass(git_repository_set_head(g_repo, "refs/remotes/test/master")); /* 0 */ + + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 4, + NULL, "refs/heads/haacked", + "foo@example.com", + "checkout: moving from master to haacked"); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 3, + NULL, "tags/test^{commit}", + "foo@example.com", + "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d"); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 2, + "tags/test^{commit}", "refs/heads/haacked", + "foo@example.com", + "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked"); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 1, + "refs/heads/haacked", "tags/test^{commit}", + "foo@example.com", + "checkout: moving from haacked to test"); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "tags/test^{commit}", "refs/remotes/test/master", + "foo@example.com", + "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master"); + + cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "haacked~0")); + cl_git_pass(git_repository_set_head_detached_from_annotated(g_repo, annotated)); + + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + NULL, "refs/heads/haacked", + "foo@example.com", + "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0"); + + git_annotated_commit_free(annotated); + git_object_free(tag); +} + +void test_refs_reflog_messages__setting_head_to_same_target_ignores_reflog(void) +{ + size_t nentries, nentries_after; + + nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE); + + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); + + nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE); + + cl_assert_equal_i(nentries + 1, nentries_after); +} + +void test_refs_reflog_messages__detaching_writes_reflog(void) +{ + git_oid id; + const char *msg; + + msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d"; + git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d"); + cl_git_pass(git_repository_set_head_detached(g_repo, &id)); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "e90810b8df3e80c413d903f631643c716887138d", + NULL, msg); + + msg = "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked"; + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "e90810b8df3e80c413d903f631643c716887138d", + "258f0e2a959a364e40ed6603d5d44fbb24765b10", + NULL, msg); +} + +void test_refs_reflog_messages__orphan_branch_does_not_count(void) +{ + git_oid id; + const char *msg; + + /* Have something known */ + msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d"; + git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d"); + cl_git_pass(git_repository_set_head_detached(g_repo, &id)); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "e90810b8df3e80c413d903f631643c716887138d", + NULL, msg); + + /* Switching to an orphan branch does not write to the reflog */ + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan")); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "e90810b8df3e80c413d903f631643c716887138d", + NULL, msg); + + /* And coming back, we set the source to zero */ + msg = "checkout: moving from orphan to haacked"; + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "0000000000000000000000000000000000000000", + "258f0e2a959a364e40ed6603d5d44fbb24765b10", + NULL, msg); +} + +void test_refs_reflog_messages__branch_birth(void) +{ + git_signature *sig; + git_oid id; + git_tree *tree; + git_reference *ref; + const char *msg; + size_t nentries, nentries_after; + + nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE); + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + cl_git_pass(git_repository_head(&ref, g_repo)); + cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJECT_TREE)); + + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan")); + + nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE); + + cl_assert_equal_i(nentries, nentries_after); + + msg = "message 2"; + cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL)); + + cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/orphan")); + + nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE); + + cl_assert_equal_i(nentries + 1, nentries_after); + + git_signature_free(sig); + git_tree_free(tree); + git_reference_free(ref); +} + +void test_refs_reflog_messages__commit_on_symbolic_ref_updates_head_reflog(void) +{ + git_signature *sig; + git_oid id; + git_tree *tree; + git_reference *ref1, *ref2; + const char *msg; + size_t nentries_head, nentries_master; + + nentries_head = reflog_entrycount(g_repo, GIT_HEAD_FILE); + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + cl_git_pass(git_repository_head(&ref1, g_repo)); + cl_git_pass(git_reference_peel((git_object **) &tree, ref1, GIT_OBJECT_TREE)); + + nentries_master = reflog_entrycount(g_repo, "refs/heads/master"); + + msg = "message 1"; + cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, "refs/heads/master", "refs/heads/foo", 1, msg)); + + cl_assert_equal_i(0, reflog_entrycount(g_repo, "refs/heads/foo")); + cl_assert_equal_i(nentries_head, reflog_entrycount(g_repo, GIT_HEAD_FILE)); + cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master")); + + msg = "message 2"; + cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL)); + + cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/foo")); + cl_assert_equal_i(nentries_head + 1, reflog_entrycount(g_repo, GIT_HEAD_FILE)); + cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master")); + + git_signature_free(sig); + git_reference_free(ref1); + git_reference_free(ref2); + git_tree_free(tree); +} + +void test_refs_reflog_messages__show_merge_for_merge_commits(void) +{ + git_oid b1_oid; + git_oid b2_oid; + git_oid merge_commit_oid; + git_commit *b1_commit; + git_commit *b2_commit; + git_signature *s; + git_commit *parent_commits[2]; + git_tree *tree; + + cl_git_pass(git_signature_now(&s, "alice", "alice@example.com")); + + cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD")); + cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test")); + + cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid)); + cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid)); + + parent_commits[0] = b1_commit; + parent_commits[1] = b2_commit; + + cl_git_pass(git_commit_tree(&tree, b1_commit)); + + cl_git_pass(git_commit_create(&merge_commit_oid, + g_repo, "HEAD", s, s, NULL, + "Merge commit", tree, + 2, (const struct git_commit **) parent_commits)); + + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + NULL, + git_oid_tostr_s(&merge_commit_oid), + NULL, "commit (merge): Merge commit"); + + git_tree_free(tree); + git_commit_free(b1_commit); + git_commit_free(b2_commit); + git_signature_free(s); +} + +void test_refs_reflog_messages__creating_a_direct_reference(void) +{ + git_reference *reference; + git_oid id; + git_reflog *reflog; + const git_reflog_entry *entry; + + const char *name = "refs/heads/new-head"; + const char *message = "You've been logged, mate!"; + + cl_git_pass(git_reference_name_to_id(&id, g_repo, "HEAD")); + + cl_git_pass(git_reference_create(&reference, g_repo, name, &id, 0, message)); + + cl_git_pass(git_reflog_read(&reflog, g_repo, name)); + cl_assert_equal_sz(1, git_reflog_entrycount(reflog)); + + entry = git_reflog_entry_byindex(reflog, 0); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); + cl_assert_equal_oid(&id, &entry->oid_cur); + cl_assert_equal_s(message, entry->msg); + + git_reflog_free(reflog); + git_reference_free(reference); +} + +void test_refs_reflog_messages__newline_gets_replaced(void) +{ + const git_reflog_entry *entry; + git_signature *signature; + git_reflog *reflog; + git_oid oid; + + cl_git_pass(git_signature_now(&signature, "me", "foo@example.com")); + cl_git_pass(git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + + cl_git_pass(git_reflog_read(&reflog, g_repo, "HEAD")); + cl_assert_equal_sz(7, git_reflog_entrycount(reflog)); + cl_git_pass(git_reflog_append(reflog, &oid, signature, "inner\nnewline")); + cl_assert_equal_sz(8, git_reflog_entrycount(reflog)); + + cl_assert(entry = git_reflog_entry_byindex(reflog, 0)); + cl_assert_equal_s(git_reflog_entry_message(entry), "inner newline"); + + git_signature_free(signature); + git_reflog_free(reflog); +} + +void test_refs_reflog_messages__renaming_ref(void) +{ + git_reference *ref, *new_ref; + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_rename(&new_ref, ref, "refs/heads/renamed", false, + "message")); + + cl_reflog_check_entry(g_repo, git_reference_name(new_ref), 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "foo@example.com", "message"); + + git_reference_free(ref); + git_reference_free(new_ref); +} + +void test_refs_reflog_messages__updating_a_direct_reference(void) +{ + git_reference *ref, *ref_out, *target_ref; + git_oid target_id; + const char *message = "You've been logged, mate!"; + + git_reference_name_to_id(&target_id, g_repo, "refs/heads/haacked"); + cl_git_pass(git_reference_lookup(&target_ref, g_repo, "refs/heads/haacked")); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + + cl_git_pass(git_reference_set_target(&ref_out, ref, &target_id, message)); + + cl_reflog_check_entry(g_repo, "refs/heads/master", 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "258f0e2a959a364e40ed6603d5d44fbb24765b10", + NULL, message); + + git_reference_free(target_ref); + git_reference_free(ref); + git_reference_free(ref_out); +} + +#define NEW_BRANCH_NAME "new-branch-on-the-block" + +void test_refs_reflog_messages__creating_branches_default_messages(void) +{ + git_str buf = GIT_STR_INIT; + git_annotated_commit *annotated; + git_object *obj; + git_commit *target; + git_reference *branch1, *branch2; + + cl_git_pass(git_revparse_single(&obj, g_repo, "e90810b8df3")); + cl_git_pass(git_commit_lookup(&target, g_repo, git_object_id(obj))); + git_object_free(obj); + + cl_git_pass(git_branch_create(&branch1, g_repo, NEW_BRANCH_NAME, target, false)); + + cl_git_pass(git_str_printf(&buf, "branch: Created from %s", git_oid_tostr_s(git_commit_id(target)))); + cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0, + GIT_OID_HEX_ZERO, + git_oid_tostr_s(git_commit_id(target)), + g_email, git_str_cstr(&buf)); + + cl_git_pass(git_reference_remove(g_repo, "refs/heads/" NEW_BRANCH_NAME)); + + cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "e90810b8df3")); + cl_git_pass(git_branch_create_from_annotated(&branch2, g_repo, NEW_BRANCH_NAME, annotated, true)); + + cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0, + GIT_OID_HEX_ZERO, + git_oid_tostr_s(git_commit_id(target)), + g_email, "branch: Created from e90810b8df3"); + + git_annotated_commit_free(annotated); + git_str_dispose(&buf); + git_commit_free(target); + git_reference_free(branch1); + git_reference_free(branch2); +} + +void test_refs_reflog_messages__moving_branch_default_message(void) +{ + git_reference *branch; + git_reference *new_branch; + git_oid id; + + cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master")); + git_oid_cpy(&id, git_reference_target(branch)); + cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0)); + + cl_reflog_check_entry(g_repo, git_reference_name(new_branch), 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + g_email, + "branch: renamed refs/heads/master to refs/heads/master2"); + + git_reference_free(branch); + git_reference_free(new_branch); +} + +void test_refs_reflog_messages__detaching_head_default_message(void) +{ + git_reference *ref; + + cl_assert_equal_i(false, git_repository_head_detached(g_repo)); + + cl_git_pass(git_repository_detach_head(g_repo)); + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL, "checkout: moving from master to a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_assert_equal_i(true, git_repository_head_detached(g_repo)); + + /* take the repo back to its original state */ + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "HEAD", "refs/heads/master", + true, "REATTACH")); + + cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0, + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL, "REATTACH"); + + cl_assert_equal_i(false, git_repository_head_detached(g_repo)); + + git_reference_free(ref); +} diff --git a/tests/libgit2/refs/reflog/reflog.c b/tests/libgit2/refs/reflog/reflog.c new file mode 100644 index 000000000..32ce7ffb7 --- /dev/null +++ b/tests/libgit2/refs/reflog/reflog.c @@ -0,0 +1,469 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "git2/reflog.h" +#include "reflog.h" + +static const char *new_ref = "refs/heads/test-reflog"; +static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; +#define commit_msg "commit: bla bla" + +static git_repository *g_repo; + + +/* helpers */ +static void assert_signature(const git_signature *expected, const git_signature *actual) +{ + cl_assert(actual); + cl_assert_equal_s(expected->name, actual->name); + cl_assert_equal_s(expected->email, actual->email); + cl_assert(expected->when.offset == actual->when.offset); + cl_assert(expected->when.time == actual->when.time); +} + + +/* Fixture setup and teardown */ +void test_refs_reflog_reflog__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_reflog_reflog__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void assert_appends(const git_signature *committer, const git_oid *oid) +{ + git_repository *repo2; + git_reference *lookedup_ref; + git_reflog *reflog; + const git_reflog_entry *entry; + + /* Reopen a new instance of the repository */ + cl_git_pass(git_repository_open(&repo2, "testrepo.git")); + + /* Lookup the previously created branch */ + cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref)); + + /* Read and parse the reflog for this branch */ + cl_git_pass(git_reflog_read(&reflog, repo2, new_ref)); + cl_assert_equal_i(3, (int)git_reflog_entrycount(reflog)); + + /* The first one was the creation of the branch */ + entry = git_reflog_entry_byindex(reflog, 2); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); + + entry = git_reflog_entry_byindex(reflog, 1); + assert_signature(committer, entry->committer); + cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0); + cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); + cl_assert(entry->msg == NULL); + + entry = git_reflog_entry_byindex(reflog, 0); + assert_signature(committer, entry->committer); + cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); + cl_assert_equal_s(commit_msg, entry->msg); + + git_reflog_free(reflog); + git_repository_free(repo2); + + git_reference_free(lookedup_ref); +} + +void test_refs_reflog_reflog__append_then_read(void) +{ + /* write a reflog for a given reference and ensure it can be read back */ + git_reference *ref; + git_oid oid; + git_signature *committer; + git_reflog *reflog; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&oid, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0, NULL)); + git_reference_free(ref); + + cl_git_pass(git_signature_now(&committer, "foo", "foo@bar")); + + cl_git_pass(git_reflog_read(&reflog, g_repo, new_ref)); + cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL)); + cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n")); + cl_git_pass(git_reflog_write(reflog)); + + assert_appends(committer, &oid); + + git_reflog_free(reflog); + git_signature_free(committer); +} + +void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void) +{ + git_reference *master, *new_master; + git_str master_log_path = GIT_STR_INIT, moved_log_path = GIT_STR_INIT; + + git_str_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_str_puts(&moved_log_path, git_str_cstr(&master_log_path)); + git_str_joinpath(&master_log_path, git_str_cstr(&master_log_path), "refs/heads/master"); + git_str_joinpath(&moved_log_path, git_str_cstr(&moved_log_path), "refs/moved"); + + cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path))); + cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&moved_log_path))); + + cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL)); + git_reference_free(master); + + cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path))); + cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&moved_log_path))); + + git_reference_free(new_master); + git_str_dispose(&moved_log_path); + git_str_dispose(&master_log_path); +} + +void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void) +{ + git_reference *master; + git_str master_log_path = GIT_STR_INIT; + + git_str_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_str_joinpath(&master_log_path, git_str_cstr(&master_log_path), "refs/heads/master"); + + cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path))); + + cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_delete(master)); + git_reference_free(master); + + cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path))); + git_str_dispose(&master_log_path); +} + +void test_refs_reflog_reflog__removes_empty_reflog_dir(void) +{ + git_reference *ref; + git_str log_path = GIT_STR_INIT; + git_oid id; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL)); + + git_str_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_str_joinpath(&log_path, git_str_cstr(&log_path), "refs/heads/new-dir/new-head"); + + cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path))); + + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + /* new ref creation should succeed since new-dir is empty */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL)); + git_reference_free(ref); + + git_str_dispose(&log_path); +} + +void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void) +{ + git_reference *ref; + git_str log_path = GIT_STR_INIT; + git_oid id; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL)); + git_reference_free(ref); + + git_str_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_str_joinpath(&log_path, git_str_cstr(&log_path), "refs/heads/new-dir/new-head"); + + cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path))); + + /* delete the ref manually, leave the reflog */ + cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head")); + + /* new ref creation should fail since new-dir contains reflogs still */ + git_oid_fromstr(&id, current_master_tip); + cl_git_fail_with(GIT_EDIRECTORY, git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL)); + git_reference_free(ref); + + git_str_dispose(&log_path); +} + +static void assert_has_reflog(bool expected_result, const char *name) +{ + cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name)); +} + +void test_refs_reflog_reflog__reference_has_reflog(void) +{ + assert_has_reflog(true, "HEAD"); + assert_has_reflog(true, "refs/heads/master"); + assert_has_reflog(false, "refs/heads/subtrees"); +} + +void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void) +{ + git_reflog *reflog; + const char *refname = "refs/heads/subtrees"; + git_str subtrees_log_path = GIT_STR_INIT; + + git_str_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname); + cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&subtrees_log_path))); + + cl_git_pass(git_reflog_read(&reflog, g_repo, refname)); + + cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog)); + + git_reflog_free(reflog); + git_str_dispose(&subtrees_log_path); +} + +void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_succeeds(void) +{ + git_reflog *reflog; + const char *refname = "refs/heads/newline"; + const char *refmessage = + "Reflog*message with a newline and enough content after it to pass the GIT_REFLOG_SIZE_MIN check inside reflog_parse."; + const git_reflog_entry *entry; + git_reference *ref; + git_oid id; + git_str logpath = GIT_STR_INIT, logcontents = GIT_STR_INIT; + char *star; + + /* Create a new branch. */ + cl_git_pass(git_oid_fromstr(&id, current_master_tip)); + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, refmessage)); + + /* + * Corrupt the branch reflog by introducing a newline inside the reflog message. + * We do this by replacing '*' with '\n' + */ + cl_git_pass(git_str_join_n(&logpath, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname)); + cl_git_pass(git_futils_readbuffer(&logcontents, git_str_cstr(&logpath))); + cl_assert((star = strchr(git_str_cstr(&logcontents), '*')) != NULL); + *star = '\n'; + cl_git_rewritefile(git_str_cstr(&logpath), git_str_cstr(&logcontents)); + + /* + * Confirm that the file was rewritten successfully + * and now contains a '\n' in the expected location + */ + cl_git_pass(git_futils_readbuffer(&logcontents, git_str_cstr(&logpath))); + cl_assert(strstr(git_str_cstr(&logcontents), "Reflog\nmessage") != NULL); + + cl_git_pass(git_reflog_read(&reflog, g_repo, refname)); + cl_assert(entry = git_reflog_entry_byindex(reflog, 0)); + cl_assert_equal_s(git_reflog_entry_message(entry), "Reflog"); + + git_reference_free(ref); + git_reflog_free(reflog); + git_str_dispose(&logpath); + git_str_dispose(&logcontents); +} + +void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void) +{ + git_reference *master, *new_master; + git_str master_log_path = GIT_STR_INIT, moved_log_path = GIT_STR_INIT; + git_reflog *reflog; + + cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); + cl_git_pass(git_reflog_read(&reflog, g_repo, "refs/heads/master")); + + cl_git_pass(git_reflog_write(reflog)); + + cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL)); + git_reference_free(master); + + cl_git_fail(git_reflog_write(reflog)); + + git_reflog_free(reflog); + git_reference_free(new_master); + git_str_dispose(&moved_log_path); + git_str_dispose(&master_log_path); +} + +void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id")); +} + +void test_refs_reflog_reflog__write_only_std_locations(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/foo", &id, 1, NULL)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1, NULL)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/notes/foo", &id, 1, NULL)); + git_reference_free(ref); + + assert_has_reflog(true, "refs/heads/foo"); + assert_has_reflog(false, "refs/tags/foo"); + assert_has_reflog(true, "refs/notes/foo"); + +} + +void test_refs_reflog_reflog__write_when_explicitly_active(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, current_master_tip); + git_reference_ensure_log(g_repo, "refs/tags/foo"); + + cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1, NULL)); + git_reference_free(ref); + assert_has_reflog(true, "refs/tags/foo"); +} + +void test_refs_reflog_reflog__append_to_HEAD_when_changing_current_branch(void) +{ + size_t nlogs, nlogs_after; + git_reference *ref; + git_reflog *log; + git_oid id; + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs = git_reflog_entrycount(log); + git_reflog_free(log); + + /* Move it back */ + git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/master", &id, 1, NULL)); + git_reference_free(ref); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs + 1); +} + +void test_refs_reflog_reflog__do_not_append_when_no_update(void) +{ + size_t nlogs, nlogs_after; + git_reference *ref, *ref2; + git_reflog *log; + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_create(&ref2, g_repo, "refs/heads/master", + git_reference_target(ref), 1, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs); +} + +static void assert_no_reflog_update(void) +{ + size_t nlogs, nlogs_after; + size_t nlogs_master, nlogs_master_after; + git_reference *ref; + git_reflog *log; + git_oid id; + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_git_pass(git_reflog_read(&log, g_repo, "refs/heads/master")); + nlogs_master = git_reflog_entrycount(log); + git_reflog_free(log); + + /* Move it back */ + git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/master", &id, 1, NULL)); + git_reference_free(ref); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs); + + cl_git_pass(git_reflog_read(&log, g_repo, "refs/heads/master")); + nlogs_master_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs); + cl_assert_equal_i(nlogs_master_after, nlogs_master); + +} + +void test_refs_reflog_reflog__logallrefupdates_bare_set_false(void) +{ + git_config *config; + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_set_bool(config, "core.logallrefupdates", false)); + git_config_free(config); + + assert_no_reflog_update(); +} + +void test_refs_reflog_reflog__logallrefupdates_bare_set_always(void) +{ + git_config *config; + git_reference *ref; + git_reflog *log; + git_oid id; + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_set_string(config, "core.logallrefupdates", "always")); + git_config_free(config); + + git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/bork", &id, 1, "message")); + + cl_git_pass(git_reflog_read(&log, g_repo, "refs/bork")); + cl_assert_equal_i(1, git_reflog_entrycount(log)); + cl_assert_equal_s("message", git_reflog_entry_byindex(log, 0)->msg); + + git_reflog_free(log); + git_reference_free(ref); +} + +void test_refs_reflog_reflog__logallrefupdates_bare_unset(void) +{ + git_config *config; + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_delete_entry(config, "core.logallrefupdates")); + git_config_free(config); + + assert_no_reflog_update(); +} + +void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void) +{ + git_config *config; + + cl_git_sandbox_cleanup(); + g_repo = cl_git_sandbox_init("testrepo"); + + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_set_bool(config, "core.logallrefupdates", false)); + git_config_free(config); + + assert_no_reflog_update(); +} diff --git a/tests/libgit2/refs/reflog/reflog_helpers.c b/tests/libgit2/refs/reflog/reflog_helpers.c new file mode 100644 index 000000000..2ea41ee06 --- /dev/null +++ b/tests/libgit2/refs/reflog/reflog_helpers.c @@ -0,0 +1,120 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "reflog.h" +#include "reflog_helpers.h" + +int reflog_entry_tostr(git_str *out, const git_reflog_entry *entry) +{ + char old_oid[GIT_OID_HEXSZ], new_oid[GIT_OID_HEXSZ]; + + assert(out && entry); + + git_oid_tostr((char *)&old_oid, GIT_OID_HEXSZ, git_reflog_entry_id_old(entry)); + git_oid_tostr((char *)&new_oid, GIT_OID_HEXSZ, git_reflog_entry_id_new(entry)); + + return git_str_printf(out, "%s %s %s %s", old_oid, new_oid, "somesig", git_reflog_entry_message(entry)); +} + +size_t reflog_entrycount(git_repository *repo, const char *name) +{ + git_reflog *log; + size_t ret; + + cl_git_pass(git_reflog_read(&log, repo, name)); + ret = git_reflog_entrycount(log); + git_reflog_free(log); + + return ret; +} + +void cl_reflog_check_entry_(git_repository *repo, const char *reflog, size_t idx, + const char *old_spec, const char *new_spec, + const char *email, const char *message, + const char *file, const char *func, int line) +{ + git_reflog *log; + const git_reflog_entry *entry; + git_str result = GIT_STR_INIT; + + cl_git_pass(git_reflog_read(&log, repo, reflog)); + entry = git_reflog_entry_byindex(log, idx); + if (entry == NULL) + clar__fail(file, func, line, "Reflog has no such entry", NULL, 1); + + if (old_spec) { + git_object *obj = NULL; + if (git_revparse_single(&obj, repo, old_spec) == GIT_OK) { + if (git_oid_cmp(git_object_id(obj), git_reflog_entry_id_old(entry)) != 0) { + git_oid__writebuf(&result, "\tOld OID: \"", git_object_id(obj)); + git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_old(entry)); + git_str_puts(&result, "\"\n"); + } + git_object_free(obj); + } else { + git_oid *oid = git__calloc(1, sizeof(*oid)); + git_oid_fromstr(oid, old_spec); + if (git_oid_cmp(oid, git_reflog_entry_id_old(entry)) != 0) { + git_oid__writebuf(&result, "\tOld OID: \"", oid); + git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_old(entry)); + git_str_puts(&result, "\"\n"); + } + git__free(oid); + } + } + if (new_spec) { + git_object *obj = NULL; + if (git_revparse_single(&obj, repo, new_spec) == GIT_OK) { + if (git_oid_cmp(git_object_id(obj), git_reflog_entry_id_new(entry)) != 0) { + git_oid__writebuf(&result, "\tNew OID: \"", git_object_id(obj)); + git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_new(entry)); + git_str_puts(&result, "\"\n"); + } + git_object_free(obj); + } else { + git_oid *oid = git__calloc(1, sizeof(*oid)); + git_oid_fromstr(oid, new_spec); + if (git_oid_cmp(oid, git_reflog_entry_id_new(entry)) != 0) { + git_oid__writebuf(&result, "\tNew OID: \"", oid); + git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_new(entry)); + git_str_puts(&result, "\"\n"); + } + git__free(oid); + } + } + + if (email && strcmp(email, git_reflog_entry_committer(entry)->email) != 0) + git_str_printf(&result, "\tEmail: \"%s\" != \"%s\"\n", email, git_reflog_entry_committer(entry)->email); + + if (message) { + const char *entry_msg = git_reflog_entry_message(entry); + if (entry_msg == NULL) entry_msg = ""; + + if (entry_msg && strcmp(message, entry_msg) != 0) + git_str_printf(&result, "\tMessage: \"%s\" != \"%s\"\n", message, entry_msg); + } + if (git_str_len(&result) != 0) + clar__fail(file, func, line, "Reflog entry mismatch", git_str_cstr(&result), 1); + + git_str_dispose(&result); + git_reflog_free(log); +} + +void reflog_print(git_repository *repo, const char *reflog_name) +{ + git_reflog *reflog; + size_t idx; + git_str out = GIT_STR_INIT; + + git_reflog_read(&reflog, repo, reflog_name); + + for (idx = 0; idx < git_reflog_entrycount(reflog); idx++) { + const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, idx); + reflog_entry_tostr(&out, entry); + git_str_putc(&out, '\n'); + } + + fprintf(stderr, "%s", git_str_cstr(&out)); + git_str_dispose(&out); + git_reflog_free(reflog); +} diff --git a/tests/libgit2/refs/reflog/reflog_helpers.h b/tests/libgit2/refs/reflog/reflog_helpers.h new file mode 100644 index 000000000..4cd92cadc --- /dev/null +++ b/tests/libgit2/refs/reflog/reflog_helpers.h @@ -0,0 +1,12 @@ +size_t reflog_entrycount(git_repository *repo, const char *name); + +#define cl_reflog_check_entry(repo, reflog, idx, old_spec, new_spec, email, message) \ + cl_reflog_check_entry_(repo, reflog, idx, old_spec, new_spec, email, message, __FILE__, __FUNCTION__, __LINE__) + +void cl_reflog_check_entry_(git_repository *repo, const char *reflog, size_t idx, + const char *old_spec, const char *new_spec, + const char *email, const char *message, + const char *file, const char *func, int line); + +void reflog_print(git_repository *repo, const char *reflog_name); +int reflog_entry_tostr(git_str *out, const git_reflog_entry *entry); diff --git a/tests/libgit2/refs/rename.c b/tests/libgit2/refs/rename.c new file mode 100644 index 000000000..f71e65782 --- /dev/null +++ b/tests/libgit2/refs/rename.c @@ -0,0 +1,368 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "refs.h" +#include "ref_helpers.h" + +static const char *loose_tag_ref_name = "refs/tags/e90810b"; +static const char *packed_head_name = "refs/heads/packed"; +static const char *packed_test_head_name = "refs/heads/packed-test"; +static const char *ref_one_name = "refs/heads/one/branch"; +static const char *ref_one_name_new = "refs/heads/two/branch"; +static const char *ref_two_name = "refs/heads/two"; +static const char *ref_master_name = "refs/heads/master"; +static const char *ref_two_name_new = "refs/heads/two/two"; + +static git_repository *g_repo; + + + +void test_refs_rename__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_repository_set_ident(g_repo, "me", "foo@example.com")); +} + +void test_refs_rename__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_refs_rename__loose(void) +{ + /* rename a loose reference */ + git_reference *looked_up_ref, *new_ref, *another_looked_up_ref; + git_str temp_path = GIT_STR_INIT; + const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu"; + + /* Ensure the ref doesn't exist on the file system */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), new_name)); + cl_assert(!git_fs_path_exists(temp_path.ptr)); + + /* Retrieval of the reference to rename */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, loose_tag_ref_name)); + + /* ... which is indeed loose */ + cl_assert(reference_is_packed(looked_up_ref) == 0); + + /* Now that the reference is renamed... */ + cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, new_name, 0, NULL)); + cl_assert_equal_s(new_ref->name, new_name); + git_reference_free(looked_up_ref); + + /* ...It can't be looked-up with the old name... */ + cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, loose_tag_ref_name)); + + /* ...but the new name works ok... */ + cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, new_name)); + cl_assert_equal_s(new_ref->name, new_name); + + /* .. the new ref is loose... */ + cl_assert(reference_is_packed(another_looked_up_ref) == 0); + cl_assert(reference_is_packed(new_ref) == 0); + + /* ...and the ref can be found in the file system */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), new_name)); + cl_assert(git_fs_path_exists(temp_path.ptr)); + + git_reference_free(new_ref); + git_reference_free(another_looked_up_ref); + git_str_dispose(&temp_path); +} + +void test_refs_rename__packed(void) +{ + /* rename a packed reference (should make it loose) */ + git_reference *looked_up_ref, *new_ref, *another_looked_up_ref; + git_str temp_path = GIT_STR_INIT; + const char *brand_new_name = "refs/heads/brand_new_name"; + + /* Ensure the ref doesn't exist on the file system */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), packed_head_name)); + cl_assert(!git_fs_path_exists(temp_path.ptr)); + + /* The reference can however be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); + + /* .. and it's packed */ + cl_assert(reference_is_packed(looked_up_ref) != 0); + + /* Now that the reference is renamed... */ + cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, brand_new_name, 0, NULL)); + cl_assert_equal_s(new_ref->name, brand_new_name); + git_reference_free(looked_up_ref); + + /* ...It can't be looked-up with the old name... */ + cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_head_name)); + + /* ...but the new name works ok... */ + cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, brand_new_name)); + cl_assert_equal_s(another_looked_up_ref->name, brand_new_name); + + /* .. the ref is no longer packed... */ + cl_assert(reference_is_packed(another_looked_up_ref) == 0); + cl_assert(reference_is_packed(new_ref) == 0); + + /* ...and the ref now happily lives in the file system */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), brand_new_name)); + cl_assert(git_fs_path_exists(temp_path.ptr)); + + git_reference_free(new_ref); + git_reference_free(another_looked_up_ref); + git_str_dispose(&temp_path); +} + +void test_refs_rename__packed_doesnt_pack_others(void) +{ + /* renaming a packed reference does not pack another reference which happens to be in both loose and pack state */ + git_reference *looked_up_ref, *another_looked_up_ref, *renamed_ref; + git_str temp_path = GIT_STR_INIT; + const char *brand_new_name = "refs/heads/brand_new_name"; + + /* Ensure the other reference exists on the file system */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name)); + cl_assert(git_fs_path_exists(temp_path.ptr)); + + /* Lookup the other reference */ + cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name)); + + /* Ensure it's loose */ + cl_assert(reference_is_packed(another_looked_up_ref) == 0); + git_reference_free(another_looked_up_ref); + + /* Lookup the reference to rename */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); + + /* Ensure it's packed */ + cl_assert(reference_is_packed(looked_up_ref) != 0); + + /* Now that the reference is renamed... */ + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, brand_new_name, 0, NULL)); + git_reference_free(looked_up_ref); + + /* Lookup the other reference */ + cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name)); + + /* Ensure it's loose */ + cl_assert(reference_is_packed(another_looked_up_ref) == 0); + + /* Ensure the other ref still exists on the file system */ + cl_assert(git_fs_path_exists(temp_path.ptr)); + + git_reference_free(renamed_ref); + git_reference_free(another_looked_up_ref); + git_str_dispose(&temp_path); +} + +void test_refs_rename__name_collision(void) +{ + /* can not rename a reference with the name of an existing reference */ + git_reference *looked_up_ref, *renamed_ref; + + /* An existing reference... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); + + /* Can not be renamed to the name of another existing reference. */ + cl_git_fail(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 0, NULL)); + git_reference_free(looked_up_ref); + + /* Failure to rename it hasn't corrupted its state */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); + cl_assert_equal_s(looked_up_ref->name, packed_head_name); + + git_reference_free(looked_up_ref); +} + +void test_refs_rename__invalid_name(void) +{ + /* can not rename a reference with an invalid name */ + git_reference *looked_up_ref, *renamed_ref; + + /* An existing oid reference... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); + + /* Can not be renamed with an invalid name. */ + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_reference_rename(&renamed_ref, looked_up_ref, "Hello! I'm a very invalid name.", 0, NULL)); + + /* Can not be renamed outside of the refs hierarchy + * unless it's ALL_CAPS_AND_UNDERSCORES. + */ + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(&renamed_ref, looked_up_ref, "i-will-sudo-you", 0, NULL)); + + /* Failure to rename it hasn't corrupted its state */ + git_reference_free(looked_up_ref); + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); + cl_assert_equal_s(looked_up_ref->name, packed_test_head_name); + + git_reference_free(looked_up_ref); +} + +void test_refs_rename__force_loose_packed(void) +{ + /* can force-rename a packed reference with the name of an existing loose and packed reference */ + git_reference *looked_up_ref, *renamed_ref; + git_oid oid; + + /* An existing reference... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); + git_oid_cpy(&oid, git_reference_target(looked_up_ref)); + + /* Can be force-renamed to the name of another existing reference. */ + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 1, NULL)); + git_reference_free(looked_up_ref); + git_reference_free(renamed_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); + cl_assert_equal_s(looked_up_ref->name, packed_test_head_name); + cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref)); + git_reference_free(looked_up_ref); + + /* And that the previous one doesn't exist any longer */ + cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); +} + +void test_refs_rename__force_loose(void) +{ + /* can force-rename a loose reference with the name of an existing loose reference */ + git_reference *looked_up_ref, *renamed_ref; + git_oid oid; + + /* An existing reference... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2")); + git_oid_cpy(&oid, git_reference_target(looked_up_ref)); + + /* Can be force-renamed to the name of another existing reference. */ + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, "refs/heads/test", 1, NULL)); + git_reference_free(looked_up_ref); + git_reference_free(renamed_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test")); + cl_assert_equal_s(looked_up_ref->name, "refs/heads/test"); + cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref)); + git_reference_free(looked_up_ref); + + /* And that the previous one doesn't exist any longer */ + cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2")); + + git_reference_free(looked_up_ref); +} + + +void test_refs_rename__overwrite(void) +{ + /* can not overwrite name of existing reference */ + git_reference *ref, *ref_one, *ref_one_new, *ref_two; + git_refdb *refdb; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + + git_oid_cpy(&id, git_reference_target(ref)); + + /* Create loose references */ + cl_git_pass(git_reference_create(&ref_one, g_repo, ref_one_name, &id, 0, NULL)); + cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0, NULL)); + + /* Pack everything */ + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + + /* Attempt to create illegal reference */ + cl_git_fail(git_reference_create(&ref_one_new, g_repo, ref_one_name_new, &id, 0, NULL)); + + /* Illegal reference couldn't be created so this is supposed to fail */ + cl_git_fail(git_reference_lookup(&ref_one_new, g_repo, ref_one_name_new)); + + git_reference_free(ref); + git_reference_free(ref_one); + git_reference_free(ref_one_new); + git_reference_free(ref_two); + git_refdb_free(refdb); +} + + +void test_refs_rename__prefix(void) +{ + /* can be renamed to a new name prefixed with the old name */ + git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + + git_oid_cpy(&id, git_reference_target(ref)); + + /* Create loose references */ + cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0, NULL)); + + /* An existing reference... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name)); + + /* Can be rename to a new name starting with the old name. */ + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name_new, 0, NULL)); + git_reference_free(looked_up_ref); + git_reference_free(renamed_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new)); + cl_assert_equal_s(looked_up_ref->name, ref_two_name_new); + git_reference_free(looked_up_ref); + cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name)); + + git_reference_free(ref); + git_reference_free(ref_two); + git_reference_free(looked_up_ref); +} + +void test_refs_rename__move_up(void) +{ + /* can move a reference to a upper reference hierarchy */ + git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT); + + git_oid_cpy(&id, git_reference_target(ref)); + + /* Create loose references */ + cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0, NULL)); + git_reference_free(ref_two); + + /* An existing reference... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new)); + + /* Can be renamed upward the reference tree. */ + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name, 0, NULL)); + git_reference_free(looked_up_ref); + git_reference_free(renamed_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name)); + cl_assert_equal_s(looked_up_ref->name, ref_two_name); + git_reference_free(looked_up_ref); + + cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new)); + git_reference_free(ref); + git_reference_free(looked_up_ref); +} + +void test_refs_rename__propagate_eexists(void) +{ + git_reference *ref, *new_ref; + + cl_git_pass(git_reference_lookup(&ref, g_repo, packed_head_name)); + + cl_assert_equal_i(GIT_EEXISTS, git_reference_rename(&new_ref, ref, packed_test_head_name, 0, NULL)); + + git_reference_free(ref); +} diff --git a/tests/libgit2/refs/revparse.c b/tests/libgit2/refs/revparse.c new file mode 100644 index 000000000..0bd2ae5bc --- /dev/null +++ b/tests/libgit2/refs/revparse.c @@ -0,0 +1,890 @@ +#include "clar_libgit2.h" + +#include "git2/revparse.h" +#include "refs.h" +#include "path.h" + +static git_repository *g_repo; +static git_object *g_obj; + +/* Helpers */ +static void test_object_and_ref_inrepo( + const char *spec, + const char *expected_oid, + const char *expected_refname, + git_repository *repo, + bool assert_reference_retrieval) +{ + char objstr[64] = {0}; + git_object *obj = NULL; + git_reference *ref = NULL; + int error; + + error = git_revparse_ext(&obj, &ref, repo, spec); + + if (expected_oid != NULL) { + cl_git_pass(error); + git_oid_fmt(objstr, git_object_id(obj)); + cl_assert_equal_s(objstr, expected_oid); + } else + cl_git_fail(error); + + if (assert_reference_retrieval) { + if (expected_refname == NULL) + cl_assert(NULL == ref); + else + cl_assert_equal_s(expected_refname, git_reference_name(ref)); + } + + git_object_free(obj); + git_reference_free(ref); +} + +static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo) +{ + test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false); +} + +static void test_id_inrepo( + const char *spec, + const char *expected_left, + const char *expected_right, + git_revspec_t expected_flags, + git_repository *repo) +{ + git_revspec revspec; + int error = git_revparse(&revspec, repo, spec); + + if (expected_left) { + char str[64] = {0}; + cl_assert_equal_i(0, error); + git_oid_fmt(str, git_object_id(revspec.from)); + cl_assert_equal_s(str, expected_left); + git_object_free(revspec.from); + } else { + cl_assert_equal_i(GIT_ENOTFOUND, error); + } + + if (expected_right) { + char str[64] = {0}; + git_oid_fmt(str, git_object_id(revspec.to)); + cl_assert_equal_s(str, expected_right); + git_object_free(revspec.to); + } + + if (expected_flags) + cl_assert_equal_i(expected_flags, revspec.flags); +} + +static void test_object(const char *spec, const char *expected_oid) +{ + test_object_inrepo(spec, expected_oid, g_repo); +} + +static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname) +{ + test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true); +} + +static void test_rangelike(const char *rangelike, + const char *expected_left, + const char *expected_right, + git_revspec_t expected_revparseflags) +{ + char objstr[64] = {0}; + git_revspec revspec; + int error; + + error = git_revparse(&revspec, g_repo, rangelike); + + if (expected_left != NULL) { + cl_assert_equal_i(0, error); + cl_assert_equal_i(revspec.flags, expected_revparseflags); + git_oid_fmt(objstr, git_object_id(revspec.from)); + cl_assert_equal_s(objstr, expected_left); + git_oid_fmt(objstr, git_object_id(revspec.to)); + cl_assert_equal_s(objstr, expected_right); + } else + cl_assert(error != 0); + + git_object_free(revspec.from); + git_object_free(revspec.to); +} + + +static void test_id( + const char *spec, + const char *expected_left, + const char *expected_right, + git_revspec_t expected_flags) +{ + test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo); +} + +static void test_invalid_revspec(const char* invalid_spec) +{ + git_revspec revspec; + + cl_assert_equal_i( + GIT_EINVALIDSPEC, git_revparse(&revspec, g_repo, invalid_spec)); +} + +void test_refs_revparse__initialize(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); +} + +void test_refs_revparse__cleanup(void) +{ + git_repository_free(g_repo); +} + +void test_refs_revparse__nonexistant_object(void) +{ + test_object("this-does-not-exist", NULL); + test_object("this-does-not-exist^1", NULL); + test_object("this-does-not-exist~2", NULL); +} + +static void assert_invalid_single_spec(const char *invalid_spec) +{ + cl_assert_equal_i( + GIT_EINVALIDSPEC, git_revparse_single(&g_obj, g_repo, invalid_spec)); +} + +void test_refs_revparse__invalid_reference_name(void) +{ + assert_invalid_single_spec("this doesn't make sense"); + assert_invalid_single_spec("Inv@{id"); + assert_invalid_single_spec(""); +} + +void test_refs_revparse__shas(void) +{ + test_object("c47800c7266a2be04c571c04d5a6614691ea99bd", "c47800c7266a2be04c571c04d5a6614691ea99bd"); + test_object("c47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd"); +} + +void test_refs_revparse__head(void) +{ + test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("HEAD^0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("HEAD~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); +} + +void test_refs_revparse__full_refs(void) +{ + test_object("refs/heads/master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("refs/heads/test", "e90810b8df3e80c413d903f631643c716887138d"); + test_object("refs/tags/test", "b25fa35b38051e4ae45d4222e795f9df2e43f1d1"); +} + +void test_refs_revparse__partial_refs(void) +{ + test_object("point_to_blob", "1385f264afb75a56a5bec74243be9b367ba4ca08"); + test_object("packed-test", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"); + test_object("br2", "a4a7dce85cf63874e984719f4fdd239f5145052f"); +} + +void test_refs_revparse__describe_output(void) +{ + test_object("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd"); + test_object("not-good", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); +} + +void test_refs_revparse__nth_parent(void) +{ + assert_invalid_single_spec("be3563a^-1"); + assert_invalid_single_spec("^"); + assert_invalid_single_spec("be3563a^{tree}^"); + assert_invalid_single_spec("point_to_blob^{blob}^"); + assert_invalid_single_spec("this doesn't make sense^1"); + + test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd"); + test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"); + test_object("be3563a^^", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"); + test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); + test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("be3563a^{commit}^", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + + test_object("be3563a^42", NULL); +} + +void test_refs_revparse__not_tag(void) +{ + test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08"); + test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("master^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("master^{tree}^{}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"); + test_object("e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d"); + test_object("tags/e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d"); + test_object("e908^{}", "e90810b8df3e80c413d903f631643c716887138d"); +} + +void test_refs_revparse__to_type(void) +{ + assert_invalid_single_spec("wrapped_tag^{trip}"); + test_object("point_to_blob^{commit}", NULL); + cl_assert_equal_i( + GIT_EPEEL, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}")); + + test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"); + test_object("point_to_blob^{blob}", "1385f264afb75a56a5bec74243be9b367ba4ca08"); + test_object("master^{commit}^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); +} + +void test_refs_revparse__linear_history(void) +{ + assert_invalid_single_spec("~"); + test_object("foo~bar", NULL); + + assert_invalid_single_spec("master~bar"); + assert_invalid_single_spec("master~-1"); + assert_invalid_single_spec("master~0bar"); + assert_invalid_single_spec("this doesn't make sense~2"); + assert_invalid_single_spec("be3563a^{tree}~"); + assert_invalid_single_spec("point_to_blob^{blob}~"); + + test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + test_object("master~~", "9fd738e8f7967c078dceed8190330fc8648ee56a"); +} + +void test_refs_revparse__chaining(void) +{ + assert_invalid_single_spec("master@{0}@{0}"); + assert_invalid_single_spec("@{u}@{-1}"); + assert_invalid_single_spec("@{-1}@{-1}"); + assert_invalid_single_spec("@{-3}@{0}"); + + test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("@{-1}@{0}", "a4a7dce85cf63874e984719f4fdd239f5145052f"); + test_object("@{-4}@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a"); + test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd"); + test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); + test_object("master^^2^", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); + test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479"); + test_object("master^^1^2^1", NULL); +} + +void test_refs_revparse__upstream(void) +{ + assert_invalid_single_spec("e90810b@{u}"); + assert_invalid_single_spec("refs/tags/e90810b@{u}"); + test_object("refs/heads/e90810b@{u}", NULL); + + test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("refs/heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); +} + +void test_refs_revparse__ordinal(void) +{ + assert_invalid_single_spec("master@{-2}"); + + /* TODO: make the test below actually fail + * cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}")); + */ + + test_object("nope@{0}", NULL); + test_object("master@{31415}", NULL); + test_object("@{1000}", NULL); + test_object("@{2}", NULL); + + test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + + test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("refs/heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); +} + +void test_refs_revparse__previous_head(void) +{ + assert_invalid_single_spec("@{-xyz}"); + assert_invalid_single_spec("@{-0}"); + assert_invalid_single_spec("@{-1b}"); + + test_object("@{-42}", NULL); + + test_object("@{-2}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("@{-1}", "a4a7dce85cf63874e984719f4fdd239f5145052f"); +} + +static void create_fake_stash_reference_and_reflog(git_repository *repo) +{ + git_reference *master, *new_master; + git_str log_path = GIT_STR_INIT; + + git_str_joinpath(&log_path, git_repository_path(repo), "logs/refs/fakestash"); + + cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&log_path))); + + cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master")); + cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0, NULL)); + git_reference_free(master); + + cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path))); + + git_str_dispose(&log_path); + git_reference_free(new_master); +} + +void test_refs_revparse__reflog_of_a_ref_under_refs(void) +{ + git_repository *repo = cl_git_sandbox_init("testrepo.git"); + + test_object_inrepo("refs/fakestash", NULL, repo); + + create_fake_stash_reference_and_reflog(repo); + + /* + * $ git reflog -1 refs/fakestash + * a65fedf refs/fakestash@{0}: commit: checking in + * + * $ git reflog -1 refs/fakestash@{0} + * a65fedf refs/fakestash@{0}: commit: checking in + * + * $ git reflog -1 fakestash + * a65fedf fakestash@{0}: commit: checking in + * + * $ git reflog -1 fakestash@{0} + * a65fedf fakestash@{0}: commit: checking in + */ + test_object_inrepo("refs/fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); + test_object_inrepo("refs/fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); + test_object_inrepo("fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); + test_object_inrepo("fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); + + cl_git_sandbox_cleanup(); +} + +void test_refs_revparse__revwalk(void) +{ + test_object("master^{/not found in any commit}", NULL); + test_object("master^{/merge}", NULL); + assert_invalid_single_spec("master^{/((}"); + + test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); + test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f"); + test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a"); +} + +void test_refs_revparse__date(void) +{ + /* + * $ git reflog HEAD --date=iso + * a65fedf HEAD@{2012-04-30 08:23:41 -0900}: checkout: moving from br2 to master + * a4a7dce HEAD@{2012-04-30 08:23:37 -0900}: commit: checking in + * c47800c HEAD@{2012-04-30 08:23:28 -0900}: checkout: moving from master to br2 + * a65fedf HEAD@{2012-04-30 08:23:23 -0900}: commit: + * be3563a HEAD@{2012-04-30 10:22:43 -0700}: clone: from /Users/ben/src/libgit2/tes + * + * $ git reflog HEAD --date=raw + * a65fedf HEAD@{1335806621 -0900}: checkout: moving from br2 to master + * a4a7dce HEAD@{1335806617 -0900}: commit: checking in + * c47800c HEAD@{1335806608 -0900}: checkout: moving from master to br2 + * a65fedf HEAD@{1335806603 -0900}: commit: + * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour + */ + test_object("HEAD@{10 years ago}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + + test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + /* + * $ git reflog master --date=iso + * a65fedf master@{2012-04-30 09:23:23 -0800}: commit: checking in + * be3563a master@{2012-04-30 09:22:43 -0800}: clone: from /Users/ben/src... + * + * $ git reflog master --date=raw + * a65fedf master@{1335806603 -0800}: commit: checking in + * be3563a master@{1335806563 -0800}: clone: from /Users/ben/src/libgit2/tests/reso + */ + + + /* + * $ git rev-parse "master@{2012-04-30 17:22:42 +0000}" + * warning: log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800 + * be3563ae3f795b2b4353bcce3a527ad0a4f7f644 + */ + test_object("master@{2012-04-30 17:22:42 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("master@{2012-04-30 09:22:42 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + + /* + * $ git reflog -1 "master@{2012-04-30 17:22:43 +0000}" + * be3563a master@{Mon Apr 30 09:22:43 2012 -0800}: clone: from /Users/ben/src/libg + */ + test_object("master@{2012-04-30 17:22:43 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("master@{2012-04-30 09:22:43 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + + /* + * $ git reflog -1 "master@{2012-4-30 09:23:27 -0800}" + * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in + */ + test_object("master@{2012-4-30 09:23:27 -0800}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + /* + * $ git reflog -1 master@{2012-05-03} + * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in + */ + test_object("master@{2012-05-03}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + /* + * $ git reflog -1 "master@{1335806603}" + * a65fedf + * + * $ git reflog -1 "master@{1335806602}" + * be3563a + */ + test_object("master@{1335806603}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + test_object("master@{1335806602}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + + /* + * $ git rev-parse "with-empty-log@{2 days ago}" -- + * fatal: log for refs/heads/with-empty-log is empty + */ + test_object("with-empty-log@{2 days ago}", NULL); +} + +void test_refs_revparse__invalid_date(void) +{ + /* + * $ git rev-parse HEAD@{} -- + * fatal: bad revision 'HEAD@{}' + * + * $ git rev-parse HEAD@{NEITHER_INTEGER_NOR_DATETIME} -- + * fatal: bad revision 'HEAD@{NEITHER_INTEGER_NOR_DATETIME}' + */ + test_object("HEAD@{}", NULL); + test_object("HEAD@{NEITHER_INTEGER_NOR_DATETIME}", NULL); +} + +void test_refs_revparse__colon(void) +{ + assert_invalid_single_spec(":/"); + assert_invalid_single_spec("point_to_blob:readme.txt"); + cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); /* Not implemented */ + + test_object(":/not found in any commit", NULL); + test_object("subtrees:ab/42.txt", NULL); + test_object("subtrees:ab/4.txt/nope", NULL); + test_object("subtrees:nope", NULL); + test_object("test/master^1:branch_file.txt", NULL); + + /* From tags */ + test_object("test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf"); + test_object("tags/test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf"); + test_object("e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf"); + test_object("tags/e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf"); + + /* From commits */ + test_object("a65f:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc"); + + /* From trees */ + test_object("a65f^{tree}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc"); + test_object("944c:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc"); + + /* Retrieving trees */ + test_object("master:", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"); + test_object("subtrees:", "ae90f12eea699729ed24555e40b9fd669da12a12"); + test_object("subtrees:ab", "f1425cef211cc08caa31e7b545ffb232acb098c3"); + test_object("subtrees:ab/", "f1425cef211cc08caa31e7b545ffb232acb098c3"); + + /* Retrieving blobs */ + test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f"); + test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b"); + test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"); + test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f"); + test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd"); + test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9"); + test_object("test/master^2:branch_file.txt", "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); + test_object("test/master@{1}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc"); +} + +void test_refs_revparse__disambiguation(void) +{ + /* + * $ git show e90810b + * tag e90810b + * Tagger: Vicent Marti + * Date: Thu Aug 12 03:59:17 2010 +0200 + * + * This is a very simple tag. + * + * commit e90810b8df3e80c413d903f631643c716887138d + * Author: Vicent Marti + * Date: Thu Aug 5 18:42:20 2010 +0200 + * + * Test commit 2 + * + * diff --git a/readme.txt b/readme.txt + * index 6336846..0266163 100644 + * --- a/readme.txt + * +++ b/readme.txt + * @@ -1 +1,2 @@ + * Testing a readme.txt + * +Now we add a single line here + * + * $ git show-ref e90810b + * 7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b + * + */ + test_object("e90810b", "7b4384978d2493e851f9cca7858815fac9b10980"); + + /* + * $ git show e90810 + * commit e90810b8df3e80c413d903f631643c716887138d + * Author: Vicent Marti + * Date: Thu Aug 5 18:42:20 2010 +0200 + * + * Test commit 2 + * + * diff --git a/readme.txt b/readme.txt + * index 6336846..0266163 100644 + * --- a/readme.txt + * +++ b/readme.txt + * @@ -1 +1,2 @@ + * Testing a readme.txt + * +Now we add a single line here + */ + test_object("e90810", "e90810b8df3e80c413d903f631643c716887138d"); +} + +void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void) +{ + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90")); +} + +/* + * $ echo "aabqhq" | git hash-object -t blob --stdin + * dea509d0b3cb8ee0650f6ca210bc83f4678851ba + * + * $ echo "aaazvc" | git hash-object -t blob --stdin + * dea509d097ce692e167dfc6a48a7a280cc5e877e + */ +void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void) +{ + git_repository *repo; + git_index *index; + git_object *obj; + + repo = cl_git_sandbox_init("testrepo"); + + cl_git_mkfile("testrepo/one.txt", "aabqhq\n"); + cl_git_mkfile("testrepo/two.txt", "aaazvc\n"); + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_add_bypath(index, "one.txt")); + cl_git_pass(git_index_add_bypath(index, "two.txt")); + + cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS); + + cl_git_pass(git_revparse_single(&obj, repo, "dea509d09")); + + git_object_free(obj); + git_index_free(index); + cl_git_sandbox_cleanup(); +} + +void test_refs_revparse__issue_994(void) +{ + git_repository *repo; + git_reference *head, *with_at; + git_object *target; + + repo = cl_git_sandbox_init("testrepo.git"); + + cl_assert_equal_i(GIT_ENOTFOUND, + git_revparse_single(&target, repo, "origin/bim_with_3d@11296")); + + cl_assert_equal_i(GIT_ENOTFOUND, + git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296")); + + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_create( + &with_at, + repo, + "refs/remotes/origin/bim_with_3d@11296", + git_reference_target(head), + 0, + NULL)); + + cl_git_pass(git_revparse_single(&target, repo, "origin/bim_with_3d@11296")); + git_object_free(target); + + cl_git_pass(git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296")); + git_object_free(target); + + git_reference_free(with_at); + git_reference_free(head); + cl_git_sandbox_cleanup(); +} + +/** + * $ git rev-parse blah-7-gc47800c + * c47800c7266a2be04c571c04d5a6614691ea99bd + * + * $ git rev-parse HEAD~3 + * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 + * + * $ git branch blah-7-gc47800c HEAD~3 + * + * $ git rev-parse blah-7-gc47800c + * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 + */ +void test_refs_revparse__try_to_retrieve_branch_before_described_tag(void) +{ + git_repository *repo; + git_reference *branch; + git_object *target; + char sha[GIT_OID_HEXSZ + 1]; + + repo = cl_git_sandbox_init("testrepo.git"); + + test_object_inrepo("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo); + + cl_git_pass(git_revparse_single(&target, repo, "HEAD~3")); + cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0)); + + git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target)); + + test_object_inrepo("blah-7-gc47800c", sha, repo); + + git_reference_free(branch); + git_object_free(target); + cl_git_sandbox_cleanup(); +} + +/** + * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750 + * a65fedf39aefe402d3bb6e24df4d4f5fe4547750 + * + * $ git rev-parse HEAD~3 + * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 + * + * $ git branch a65fedf39aefe402d3bb6e24df4d4f5fe4547750 HEAD~3 + * + * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750 + * a65fedf39aefe402d3bb6e24df4d4f5fe4547750 + * + * $ git rev-parse heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750 + * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 + */ +void test_refs_revparse__try_to_retrieve_sha_before_branch(void) +{ + git_repository *repo; + git_reference *branch; + git_object *target; + char sha[GIT_OID_HEXSZ + 1]; + + repo = cl_git_sandbox_init("testrepo.git"); + + test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); + + cl_git_pass(git_revparse_single(&target, repo, "HEAD~3")); + cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0)); + + git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target)); + + test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); + test_object_inrepo("heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750", sha, repo); + + git_reference_free(branch); + git_object_free(target); + cl_git_sandbox_cleanup(); +} + +/** + * $ git rev-parse c47800 + * c47800c7266a2be04c571c04d5a6614691ea99bd + * + * $ git rev-parse HEAD~3 + * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 + * + * $ git branch c47800 HEAD~3 + * + * $ git rev-parse c47800 + * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 + */ +void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void) +{ + git_repository *repo; + git_reference *branch; + git_object *target; + char sha[GIT_OID_HEXSZ + 1]; + + repo = cl_git_sandbox_init("testrepo.git"); + + test_object_inrepo("c47800", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo); + + cl_git_pass(git_revparse_single(&target, repo, "HEAD~3")); + cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0)); + + git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target)); + + test_object_inrepo("c47800", sha, repo); + + git_reference_free(branch); + git_object_free(target); + cl_git_sandbox_cleanup(); +} + + +void test_refs_revparse__range(void) +{ + assert_invalid_single_spec("be3563a^1..be3563a"); + + test_rangelike("be3563a^1..be3563a", + "9fd738e8f7967c078dceed8190330fc8648ee56a", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + GIT_REVSPEC_RANGE); + + test_rangelike("be3563a^1...be3563a", + "9fd738e8f7967c078dceed8190330fc8648ee56a", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE); + + test_rangelike("be3563a^1.be3563a", NULL, NULL, 0); +} + +void test_refs_revparse__parses_range_operator(void) +{ + test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); + test_id("HEAD~3..HEAD", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVSPEC_RANGE); + + test_id("HEAD~3...HEAD", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE); + + test_id("HEAD~3..", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVSPEC_RANGE); + + test_id("HEAD~3...", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE); + + test_id("..HEAD~3", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + GIT_REVSPEC_RANGE); + + test_id("...HEAD~3", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE); + + test_id("...", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE); + + test_invalid_revspec(".."); +} + +void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void) +{ + test_object_and_ref( + "master@{upstream}", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + "refs/remotes/test/master"); + + test_object_and_ref( + "@{-1}", + "a4a7dce85cf63874e984719f4fdd239f5145052f", + "refs/heads/br2"); +} + +void test_refs_revparse__ext_can_expand_short_reference_names(void) +{ + test_object_and_ref( + "master", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "refs/heads/master"); + + test_object_and_ref( + "HEAD", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "refs/heads/master"); + + test_object_and_ref( + "tags/test", + "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", + "refs/tags/test"); +} + +void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_revision(void) +{ + test_object_and_ref( + "HEAD~3", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + NULL); + + test_object_and_ref( + "HEAD~0", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL); + + test_object_and_ref( + "HEAD^0", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL); + + test_object_and_ref( + "@{-1}@{0}", + "a4a7dce85cf63874e984719f4fdd239f5145052f", + NULL); +} + +void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_tree_content(void) +{ + test_object_and_ref( + "tags/test:readme.txt", + "0266163a49e280c4f5ed1e08facd36a2bd716bcf", + NULL); +} + +void test_refs_revparse__uneven_sizes(void) +{ + test_object("a65fedf39aefe402d3bb6e24df4d4f5fe454775", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + test_object("a65fedf39aefe402d3bb6e24df4d4f5fe45477", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + test_object("a65fedf39aefe402d3bb6e24df4d4f5fe4547", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + test_object("a65fedf39aefe402d3bb6e24df4d", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); +} + +void test_refs_revparse__parses_at_head(void) +{ + test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); + test_id("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); + test_id("@", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); +} diff --git a/tests/libgit2/refs/setter.c b/tests/libgit2/refs/setter.c new file mode 100644 index 000000000..b34c71eb5 --- /dev/null +++ b/tests/libgit2/refs/setter.c @@ -0,0 +1,99 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "git2/refs.h" + +static const char *ref_name = "refs/heads/other"; +static const char *ref_master_name = "refs/heads/master"; +static const char *ref_test_name = "refs/heads/test"; + +static git_repository *g_repo; + +void test_refs_setter__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_setter__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_setter__update_direct(void) +{ + git_reference *ref, *test_ref, *new_ref; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name)); + cl_assert(git_reference_type(test_ref) == GIT_REFERENCE_DIRECT); + + cl_git_pass(git_reference_set_target(&new_ref, test_ref, &id, NULL)); + + git_reference_free(test_ref); + git_reference_free(new_ref); + + cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name)); + cl_assert(git_reference_type(test_ref) == GIT_REFERENCE_DIRECT); + cl_assert_equal_oid(&id, git_reference_target(test_ref)); + git_reference_free(test_ref); +} + +void test_refs_setter__update_symbolic(void) +{ + git_reference *head, *new_head; + + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + cl_assert(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC); + cl_assert(strcmp(git_reference_symbolic_target(head), ref_master_name) == 0); + + cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name, NULL)); + git_reference_free(new_head); + git_reference_free(head); + + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + cl_assert(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC); + cl_assert(strcmp(git_reference_symbolic_target(head), ref_test_name) == 0); + git_reference_free(head); +} + +void test_refs_setter__cant_update_direct_with_symbolic(void) +{ + /* Overwrite an existing object id reference with a symbolic one */ + git_reference *ref, *new; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + + cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name, NULL)); + + git_reference_free(ref); +} + +void test_refs_setter__cant_update_symbolic_with_direct(void) +{ + /* Overwrite an existing symbolic reference with an object id one */ + git_reference *ref, *new; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT); + git_oid_cpy(&id, git_reference_target(ref)); + git_reference_free(ref); + + /* Create the symbolic ref */ + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL)); + + /* Can't set an OID on a direct ref */ + cl_git_fail(git_reference_set_target(&new, ref, &id, NULL)); + + git_reference_free(ref); +} diff --git a/tests/libgit2/refs/shorthand.c b/tests/libgit2/refs/shorthand.c new file mode 100644 index 000000000..e008adc74 --- /dev/null +++ b/tests/libgit2/refs/shorthand.c @@ -0,0 +1,27 @@ +#include "clar_libgit2.h" + +#include "repository.h" + +static void assert_shorthand(git_repository *repo, const char *refname, const char *shorthand) +{ + git_reference *ref; + + cl_git_pass(git_reference_lookup(&ref, repo, refname)); + cl_assert_equal_s(git_reference_shorthand(ref), shorthand); + git_reference_free(ref); +} + +void test_refs_shorthand__0(void) +{ + git_repository *repo; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + + assert_shorthand(repo, "refs/heads/master", "master"); + assert_shorthand(repo, "refs/tags/test", "test"); + assert_shorthand(repo, "refs/remotes/test/master", "test/master"); + assert_shorthand(repo, "refs/notes/fanout", "notes/fanout"); + + git_repository_free(repo); +} diff --git a/tests/libgit2/refs/tags/name.c b/tests/libgit2/refs/tags/name.c new file mode 100644 index 000000000..1dd1760b9 --- /dev/null +++ b/tests/libgit2/refs/tags/name.c @@ -0,0 +1,17 @@ +#include "clar_libgit2.h" + +static int name_is_valid(const char *name) +{ + int valid; + cl_git_pass(git_tag_name_is_valid(&valid, name)); + return valid; +} + +void test_refs_tags_name__is_name_valid(void) +{ + cl_assert_equal_i(true, name_is_valid("sometag")); + cl_assert_equal_i(true, name_is_valid("test/sometag")); + + cl_assert_equal_i(false, name_is_valid("")); + cl_assert_equal_i(false, name_is_valid("-dash")); +} diff --git a/tests/libgit2/refs/transactions.c b/tests/libgit2/refs/transactions.c new file mode 100644 index 000000000..50c102ad0 --- /dev/null +++ b/tests/libgit2/refs/transactions.c @@ -0,0 +1,157 @@ +#include "clar_libgit2.h" +#include "git2/transaction.h" + +static git_repository *g_repo; +static git_transaction *g_tx; + +void test_refs_transactions__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_transaction_new(&g_tx, g_repo)); +} + +void test_refs_transactions__cleanup(void) +{ + git_transaction_free(g_tx); + cl_git_sandbox_cleanup(); +} + +void test_refs_transactions__single_ref_oid(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_symbolic(void) +{ + git_reference *ref; + + cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD")); + cl_git_pass(git_transaction_set_symbolic_target(g_tx, "HEAD", "refs/heads/foo", NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + + cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref)); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_mix_types(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD")); + cl_git_pass(git_transaction_set_symbolic_target(g_tx, "refs/heads/master", "refs/heads/foo", NULL, NULL)); + cl_git_pass(git_transaction_set_target(g_tx, "HEAD", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref)); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_delete(void) +{ + git_reference *ref; + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_remove(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, "refs/heads/master")); +} + +void test_refs_transactions__single_create(void) +{ + git_reference *ref; + const char *name = "refs/heads/new-branch"; + git_oid id; + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, name)); + + cl_git_pass(git_transaction_lock_ref(g_tx, name)); + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_pass(git_transaction_set_target(g_tx, name, &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, name)); + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__unlocked_set(void) +{ + git_oid id; + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/foo", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); +} + +void test_refs_transactions__error_on_locking_locked_ref(void) +{ + git_oid id; + git_transaction *g_tx_with_lock; + git_repository *g_repo_with_locking_tx; + const char *g_repo_path = git_repository_path(g_repo); + + /* prepare a separate transaction in another instance of testrepo and lock master */ + cl_git_pass(git_repository_open(&g_repo_with_locking_tx, g_repo_path)); + cl_git_pass(git_transaction_new(&g_tx_with_lock, g_repo_with_locking_tx)); + cl_git_pass(git_transaction_lock_ref(g_tx_with_lock, "refs/heads/master")); + + /* lock reference for set_target */ + cl_git_pass(git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + cl_git_fail_with(GIT_ELOCKED, git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL)); + + git_transaction_free(g_tx_with_lock); + git_repository_free(g_repo_with_locking_tx); +} + +void test_refs_transactions__commit_unlocks_unmodified_ref(void) +{ + git_transaction *second_tx; + + cl_git_pass(git_transaction_new(&second_tx, g_repo)); + cl_git_pass(git_transaction_lock_ref(second_tx, "refs/heads/master")); + cl_git_pass(git_transaction_commit(second_tx)); + + /* a transaction must now be able to get the lock */ + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + + git_transaction_free(second_tx); +} + +void test_refs_transactions__free_unlocks_unmodified_ref(void) +{ + git_transaction *second_tx; + + cl_git_pass(git_transaction_new(&second_tx, g_repo)); + cl_git_pass(git_transaction_lock_ref(second_tx, "refs/heads/master")); + git_transaction_free(second_tx); + + /* a transaction must now be able to get the lock */ + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); +} diff --git a/tests/libgit2/refs/unicode.c b/tests/libgit2/refs/unicode.c new file mode 100644 index 000000000..a279d5006 --- /dev/null +++ b/tests/libgit2/refs/unicode.c @@ -0,0 +1,54 @@ +#include "clar_libgit2.h" + +static git_repository *repo; + +void test_refs_unicode__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_unicode__cleanup(void) +{ + cl_git_sandbox_cleanup(); + repo = NULL; +} + +void test_refs_unicode__create_and_lookup(void) +{ + git_reference *ref0, *ref1, *ref2; + git_repository *repo2; + + const char *REFNAME = "refs/heads/" "\303\205" "ngstr" "\303\266" "m"; + const char *master = "refs/heads/master"; + + /* Create the reference */ + cl_git_pass(git_reference_lookup(&ref0, repo, master)); + cl_git_pass(git_reference_create( + &ref1, repo, REFNAME, git_reference_target(ref0), 0, NULL)); + cl_assert_equal_s(REFNAME, git_reference_name(ref1)); + git_reference_free(ref0); + + /* Lookup the reference in a different instance of the repository */ + cl_git_pass(git_repository_open(&repo2, "testrepo.git")); + + cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME)); + cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2)); + cl_assert_equal_s(REFNAME, git_reference_name(ref2)); + git_reference_free(ref2); + +#if GIT_USE_ICONV + /* Lookup reference by decomposed unicode name */ + +#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m" + + cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED)); + cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2)); + cl_assert_equal_s(REFNAME, git_reference_name(ref2)); + git_reference_free(ref2); +#endif + + /* Cleanup */ + + git_reference_free(ref1); + git_repository_free(repo2); +} diff --git a/tests/libgit2/refs/update.c b/tests/libgit2/refs/update.c new file mode 100644 index 000000000..1c5127d9e --- /dev/null +++ b/tests/libgit2/refs/update.c @@ -0,0 +1,26 @@ +#include "clar_libgit2.h" + +#include "refs.h" + +static git_repository *g_repo; + +void test_refs_update__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_update__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_update__updating_the_target_of_a_symref_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *head; + + cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE)); + cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head)); + git_reference_free(head); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(&head, g_repo, GIT_HEAD_FILE, "refs/heads/inv@{id", 1, NULL)); +} -- cgit v1.2.1 From bdab22384cc61d315005a65456a9f9563bb27c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 3 May 2022 09:28:43 +0200 Subject: revparse: adjust reflog test as we move away from the dates The dates we use in `refs::revparse::date` has just passed the ten years, so now everything is beyond ten years, leading to an unexpected commit being returned. Adjust it to 11 years so it all looks fine again. --- tests/libgit2/refs/revparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/libgit2/refs') diff --git a/tests/libgit2/refs/revparse.c b/tests/libgit2/refs/revparse.c index 0bd2ae5bc..93d7ce6db 100644 --- a/tests/libgit2/refs/revparse.c +++ b/tests/libgit2/refs/revparse.c @@ -399,7 +399,7 @@ void test_refs_revparse__date(void) * a65fedf HEAD@{1335806603 -0900}: commit: * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour */ - test_object("HEAD@{10 years ago}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("HEAD@{11 years ago}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); -- cgit v1.2.1 From 7f53d7b600a838c19276e835fd0188c361a63801 Mon Sep 17 00:00:00 2001 From: Carl Dong Date: Mon, 9 May 2022 12:09:08 -0400 Subject: revparse: Remove error-prone, redundant test Originally introduced in: 776a6a8e5f8e258d31aded73c0ce38df6ac7bdc4 This test case has recently been fixed in bdab22384cc61d315005a65456a9f9563bb27c8f, but that fix will only last for a year. Next year the same problem will crop up and the test will need to be re-edited. This is not ideal as - This test case becomes an unnecessary burden for developers - Downstream distros or even just users who want to build older versions of libgit2 are guaranteed to have this test fail Furthermore, this test case is entirely unnecessary, as the functionality that was originally (see 776a6a8e5f8e258d31aded73c0ce38df6ac7bdc4) intended to be tested is well-covered by subsequent tests which specify a date instead of a "x ago" specification. --- tests/libgit2/refs/revparse.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'tests/libgit2/refs') diff --git a/tests/libgit2/refs/revparse.c b/tests/libgit2/refs/revparse.c index 93d7ce6db..56af3c939 100644 --- a/tests/libgit2/refs/revparse.c +++ b/tests/libgit2/refs/revparse.c @@ -399,8 +399,6 @@ void test_refs_revparse__date(void) * a65fedf HEAD@{1335806603 -0900}: commit: * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour */ - test_object("HEAD@{11 years ago}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); - test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); -- cgit v1.2.1