From 00a48934f118c1b5da035dd576b92e5406fb8ca2 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Fri, 30 Mar 2012 08:13:44 -0700 Subject: [PATCH] t10-refs.c ported. --- tests-clar/refs/crashes.c | 2 +- tests-clar/refs/create.c | 153 ++++++++++++++++ tests-clar/refs/delete.c | 85 +++++++++ tests-clar/refs/list.c | 53 ++++++ tests-clar/refs/normalize.c | 197 +++++++++++++++++++++ tests-clar/refs/overwrite.c | 140 +++++++++++++++ tests-clar/refs/pack.c | 71 ++++++++ tests-clar/refs/read.c | 199 +++++++++++++++++++++ tests-clar/refs/reflog.c | 134 ++++++++++++++ tests-clar/refs/rename.c | 339 ++++++++++++++++++++++++++++++++++++ 10 files changed, 1372 insertions(+), 1 deletion(-) create mode 100644 tests-clar/refs/create.c create mode 100644 tests-clar/refs/delete.c create mode 100644 tests-clar/refs/list.c create mode 100644 tests-clar/refs/normalize.c create mode 100644 tests-clar/refs/overwrite.c create mode 100644 tests-clar/refs/pack.c create mode 100644 tests-clar/refs/read.c create mode 100644 tests-clar/refs/reflog.c create mode 100644 tests-clar/refs/rename.c diff --git a/tests-clar/refs/crashes.c b/tests-clar/refs/crashes.c index 26ce98a68..e1b289ace 100644 --- a/tests-clar/refs/crashes.c +++ b/tests-clar/refs/crashes.c @@ -11,7 +11,7 @@ void test_refs_crashes__double_free(void) cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME)); cl_git_pass(git_reference_delete(ref)); /* reference is gone from disk, so reloading it will fail */ - cl_must_fail(git_reference_reload(ref2)); + cl_git_fail(git_reference_reload(ref2)); git_repository_free(repo); } diff --git a/tests-clar/refs/create.c b/tests-clar/refs/create.c new file mode 100644 index 000000000..8f64c5120 --- /dev/null +++ b/tests-clar/refs/create.c @@ -0,0 +1,153 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + +static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; +static const char *current_head_target = "refs/heads/master"; + +static git_repository *g_repo; + + + +void test_ref_create__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_ref_create__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_ref_create__symbolic(void) +{ + // create a new symbolic reference + git_reference *new_reference, *looked_up_ref, *resolved_ref; + git_repository *repo2; + git_oid id; + git_buf ref_path = GIT_BUF_INIT; + + const char *new_head_tracker = "another-head-tracker"; + + git_oid_fromstr(&id, current_master_tip); + + /* Retrieve the physical path to the symbolic ref for further cleaning */ + cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head_tracker)); + git_buf_free(&ref_path); + + /* Create and write the new symbolic reference */ + cl_git_pass(git_reference_create_symbolic(&new_reference, g_repo, new_head_tracker, current_head_target, 0)); + + /* 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_REF_SYMBOLIC); + cl_assert(git_reference_is_packed(looked_up_ref) == 0); + cl_assert(strcmp(looked_up_ref->name, new_head_tracker) == 0); + + /* ...peeled.. */ + cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); + cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID); + + /* ...and that it points to the current master tip */ + cl_assert(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); + 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(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); + + git_repository_free(repo2); + + git_reference_free(new_reference); + git_reference_free(looked_up_ref); + git_reference_free(resolved_ref); +} + +void test_ref_create__deep_symbolic(void) +{ + // create a deep symbolic reference + git_reference *new_reference, *looked_up_ref, *resolved_ref; + git_oid id; + git_buf ref_path = GIT_BUF_INIT; + + const char *new_head_tracker = "deep/rooted/tracker"; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head_tracker)); + cl_git_pass(git_reference_create_symbolic(&new_reference, g_repo, new_head_tracker, current_head_target, 0)); + 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(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); + + git_reference_free(new_reference); + git_reference_free(looked_up_ref); + git_reference_free(resolved_ref); + git_buf_free(&ref_path); +} + +void test_ref_create__oid(void) +{ + // create a new OID reference + git_reference *new_reference, *looked_up_ref; + git_repository *repo2; + git_oid id; + git_buf ref_path = GIT_BUF_INIT; + + const char *new_head = "refs/heads/new-head"; + + git_oid_fromstr(&id, current_master_tip); + + /* Retrieve the physical path to the symbolic ref for further cleaning */ + cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head)); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create_oid(&new_reference, g_repo, new_head, &id, 0)); + + /* 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_REF_OID); + cl_assert(git_reference_is_packed(looked_up_ref) == 0); + cl_assert(strcmp(looked_up_ref->name, new_head) == 0); + + /* ...and that it points to the current master tip */ + cl_assert(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0); + 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(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0); + + git_repository_free(repo2); + + git_reference_free(new_reference); + git_reference_free(looked_up_ref); + git_buf_free(&ref_path); +} + +void test_ref_create__oid_unknown(void) +{ + // Can not create a new OID reference which targets at an unknown id + 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_oid(&new_reference, g_repo, new_head, &id, 0)); + + /* Ensure the reference can't be looked-up... */ + cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head)); +} diff --git a/tests-clar/refs/delete.c b/tests-clar/refs/delete.c new file mode 100644 index 000000000..cc3b93653 --- /dev/null +++ b/tests-clar/refs/delete.c @@ -0,0 +1,85 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.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_buf temp_path = GIT_BUF_INIT; + + /* Ensure the loose reference exists on the file system */ + cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_test_head_name)); + cl_git_pass(git_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(git_reference_is_packed(looked_up_ref) == 0); + + /* Now that the reference is deleted... */ + cl_git_pass(git_reference_delete(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_git_pass(!git_path_exists(temp_path.ptr)); + + git_reference_free(another_looked_up_ref); + git_buf_free(&temp_path); +} + +void test_refs_delete__packed_only(void) +{ + // can delete a just packed reference + git_reference *ref; + 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_oid(&ref, g_repo, new_ref, &id, 0)); + 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(git_reference_is_packed(ref) == 0); + + /* Pack all existing references */ + cl_git_pass(git_reference_packall(g_repo)); + + /* Reload the reference from disk */ + cl_git_pass(git_reference_reload(ref)); + + /* Ensure it's a packed reference */ + cl_assert(git_reference_is_packed(ref) == 1); + + /* This should pass */ + cl_git_pass(git_reference_delete(ref)); +} diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c new file mode 100644 index 000000000..f673bd9be --- /dev/null +++ b/tests-clar/refs/list.c @@ -0,0 +1,53 @@ +#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_listall(&ref_list, g_repo, GIT_REF_LISTALL)); + + /*{ + unsigned short i; + for (i = 0; i < ref_list.count; ++i) + printf("# %s\n", ref_list.strings[i]); + }*/ + + /* We have exactly 9 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(ref_list.count == 9); + + git_strarray_free(&ref_list); +} + +void test_refs_list__symbolic_only(void) +{ + // try to list only the symbolic references + git_strarray ref_list; + + cl_git_pass(git_reference_listall(&ref_list, g_repo, GIT_REF_SYMBOLIC)); + cl_assert(ref_list.count == 0); /* no symrefs in the test repo */ + + git_strarray_free(&ref_list); +} diff --git a/tests-clar/refs/normalize.c b/tests-clar/refs/normalize.c new file mode 100644 index 000000000..49bc9ac1b --- /dev/null +++ b/tests-clar/refs/normalize.c @@ -0,0 +1,197 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + + +// Helpers +static int ensure_refname_normalized(int is_oid_ref, const char *input_refname, const char *expected_refname) +{ + int error = GIT_SUCCESS; + char buffer_out[GIT_REFNAME_MAX]; + + if (is_oid_ref) + error = git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname); + else + error = git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname); + + if (error < GIT_SUCCESS) + return error; + + if (expected_refname == NULL) + return error; + + if (strcmp(buffer_out, expected_refname)) + error = GIT_ERROR; + + return error; +} + +#define OID_REF 1 +#define SYM_REF 0 + + + +void test_refs_normalize__direct(void) +{ + // normalize a direct (OID) reference name + cl_git_fail(ensure_refname_normalized(OID_REF, "a", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/a/", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/a.", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/a.lock", NULL)); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs/dummy/a", NULL)); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs/stash", NULL)); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs/tags/a", "refs/tags/a")); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs/heads/a/b", "refs/heads/a/b")); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs/heads/a./b", "refs/heads/a./b")); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo?bar", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads\foo", NULL)); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs/heads/v@ation", "refs/heads/v@ation")); + cl_git_pass(ensure_refname_normalized(OID_REF, "refs///heads///a", "refs/heads/a")); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/.a/b", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo/../bar", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo..bar", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/./foo", NULL)); + cl_git_fail(ensure_refname_normalized(OID_REF, "refs/heads/v@{ation", NULL)); +} + +void test_refs_normalize__symbolic(void) +{ + // normalize a symbolic reference name + cl_git_pass(ensure_refname_normalized(SYM_REF, "a", "a")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "a/b", "a/b")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs///heads///a", "refs/heads/a")); + cl_git_fail(ensure_refname_normalized(SYM_REF, "", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "heads\foo", NULL)); +} + +/* Ported from JGit, BSD licence. + * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 */ +void test_refs_normalize__jgit_suite(void) +{ + // tests borrowed from JGit + +/* EmptyString */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "/", NULL)); + +/* MustHaveTwoComponents */ + cl_git_fail(ensure_refname_normalized(OID_REF, "master", NULL)); + cl_git_pass(ensure_refname_normalized(SYM_REF, "heads/master", "heads/master")); + +/* ValidHead */ + + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/master", "refs/heads/master")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/pu", "refs/heads/pu")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/z", "refs/heads/z")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/FoO", "refs/heads/FoO")); + +/* ValidTag */ + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/tags/v1.0", "refs/tags/v1.0")); + +/* NoLockSuffix */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master.lock", NULL)); + +/* NoDirectorySuffix */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master/", NULL)); + +/* NoSpace */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/i haz space", NULL)); + +/* NoAsciiControlCharacters */ + { + char c; + char buffer[GIT_REFNAME_MAX]; + for (c = '\1'; c < ' '; c++) { + strncpy(buffer, "refs/heads/mast", 15); + strncpy(buffer + 15, (const char *)&c, 1); + strncpy(buffer + 16, "er", 2); + buffer[18 - 1] = '\0'; + cl_git_fail(ensure_refname_normalized(SYM_REF, buffer, NULL)); + } + } + +/* NoBareDot */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/.", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/..", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/./master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/../master", NULL)); + +/* NoLeadingOrTrailingDot */ + cl_git_fail(ensure_refname_normalized(SYM_REF, ".", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/.bar", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/..bar", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/bar.", NULL)); + +/* ContainsDot */ + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r")); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master..pu", NULL)); + +/* NoMagicRefCharacters */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master^", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/^master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "^refs/heads/master", NULL)); + + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master~", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/~master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "~refs/heads/master", NULL)); + + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master:", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/:master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, ":refs/heads/master", NULL)); + +/* ShellGlob */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master?", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/?master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "?refs/heads/master", NULL)); + + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master[", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/[master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "[refs/heads/master", NULL)); + + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master*", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/*master", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "*refs/heads/master", NULL)); + +/* ValidSpecialCharacters */ + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/!", "refs/heads/!")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/\"", "refs/heads/\"")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/#", "refs/heads/#")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/$", "refs/heads/$")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/%", "refs/heads/%")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/&", "refs/heads/&")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/'", "refs/heads/'")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/(", "refs/heads/(")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/)", "refs/heads/)")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/+", "refs/heads/+")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/,", "refs/heads/,")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/-", "refs/heads/-")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/;", "refs/heads/;")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/<", "refs/heads/<")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/=", "refs/heads/=")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/>", "refs/heads/>")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/@", "refs/heads/@")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/]", "refs/heads/]")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/_", "refs/heads/_")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/`", "refs/heads/`")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/{", "refs/heads/{")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/|", "refs/heads/|")); + cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/}", "refs/heads/}")); + + // This is valid on UNIX, but not on Windows + // hence we make in invalid due to non-portability + // + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/\\", NULL)); + +/* UnicodeNames */ + /* + * Currently this fails. + * cl_git_pass(ensure_refname_normalized(SYM_REF, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m")); + */ + +/* RefLogQueryIsValidRef */ + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1}", NULL)); + cl_git_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1.hour.ago}", NULL)); +} diff --git a/tests-clar/refs/overwrite.c b/tests-clar/refs/overwrite.c new file mode 100644 index 000000000..24e72f52c --- /dev/null +++ b/tests-clar/refs/overwrite.c @@ -0,0 +1,140 @@ +#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_ref_overwrite__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_ref_overwrite__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_ref_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_create_symbolic(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0)); + cl_git_pass(git_reference_create_symbolic(&ref, g_repo, ref_name, ref_branch_name, 0)); + 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_REF_SYMBOLIC); + cl_assert(!strcmp(git_reference_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_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 0)); + cl_git_pass(git_reference_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 1)); + 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_REF_SYMBOLIC); + cl_assert(!strcmp(git_reference_target(ref), ref_master_name)); + + git_reference_free(ref); + git_reference_free(branch_ref); +} + +void test_ref_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_REF_OID); + git_oid_cpy(&id, git_reference_oid(ref)); + git_reference_free(ref); + + /* Create it */ + cl_git_pass(git_reference_create_oid(&ref, g_repo, ref_name, &id, 0)); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_test_name)); + cl_assert(git_reference_type(ref) & GIT_REF_OID); + git_oid_cpy(&id, git_reference_oid(ref)); + git_reference_free(ref); + + /* Ensure we can't overwrite unless we force it */ + cl_git_fail(git_reference_create_oid(&ref, g_repo, ref_name, &id, 0)); + cl_git_pass(git_reference_create_oid(&ref, g_repo, ref_name, &id, 1)); + git_reference_free(ref); + + /* Ensure it has been overwritten */ + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_assert(!git_oid_cmp(&id, git_reference_oid(ref))); + + git_reference_free(ref); +} + +void test_ref_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_REF_OID); + git_oid_cpy(&id, git_reference_oid(ref)); + git_reference_free(ref); + + cl_git_pass(git_reference_create_oid(&ref, g_repo, ref_name, &id, 0)); + git_reference_free(ref); + cl_git_fail(git_reference_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 0)); + cl_git_pass(git_reference_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 1)); + 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_REF_SYMBOLIC); + cl_assert(!strcmp(git_reference_target(ref), ref_master_name)); + + git_reference_free(ref); +} + +void test_ref_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_REF_OID); + git_oid_cpy(&id, git_reference_oid(ref)); + git_reference_free(ref); + + /* Create the symbolic ref */ + cl_git_pass(git_reference_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 0)); + git_reference_free(ref); + /* It shouldn't overwrite unless we tell it to */ + cl_git_fail(git_reference_create_oid(&ref, g_repo, ref_name, &id, 0)); + cl_git_pass(git_reference_create_oid(&ref, g_repo, ref_name, &id, 1)); + 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_REF_OID); + cl_assert(!git_oid_cmp(git_reference_oid(ref), &id)); + + git_reference_free(ref); +} diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c new file mode 100644 index 000000000..232aa759c --- /dev/null +++ b/tests-clar/refs/pack.c @@ -0,0 +1,71 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + +static const char *loose_tag_ref_name = "refs/tags/e90810b"; + +static git_repository *g_repo; + + + +void test_ref_pack__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_ref_pack__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_ref_pack__empty(void) +{ + // create a packfile for an empty folder + git_buf temp_path = GIT_BUF_INIT; + + cl_git_pass(git_buf_join_n(&temp_path, '/', 3, g_repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir")); + cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE)); + git_buf_free(&temp_path); + + cl_git_pass(git_reference_packall(g_repo)); +} + +void test_ref_pack__loose(void) +{ + // create a packfile from all the loose rn a repo + git_reference *reference; + git_buf temp_path = GIT_BUF_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(git_reference_is_packed(reference) == 0); + cl_assert(strcmp(reference->name, loose_tag_ref_name) == 0); + 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 + */ + cl_git_pass(git_reference_packall(g_repo)); + + /* Ensure the packed-refs file exists */ + cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, GIT_PACKEDREFS_FILE)); + cl_git_pass(git_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(git_reference_is_packed(reference)); + cl_assert(strcmp(reference->name, loose_tag_ref_name) == 0); + + /* Ensure the known ref has been removed from the loose folder structure */ + cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, loose_tag_ref_name)); + cl_git_pass(!git_path_exists(temp_path.ptr)); + + git_reference_free(reference); + git_buf_free(&temp_path); +} diff --git a/tests-clar/refs/read.c b/tests-clar/refs/read.c new file mode 100644 index 000000000..560ec5640 --- /dev/null +++ b/tests-clar/refs/read.c @@ -0,0 +1,199 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.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_ref_read__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_ref_read__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_ref_read__loose_tag(void) +{ + // lookup a loose tag reference + git_reference *reference; + git_object *object; + git_buf ref_name_from_tag_name = GIT_BUF_INIT; + + cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name)); + cl_assert(git_reference_type(reference) & GIT_REF_OID); + cl_assert(git_reference_is_packed(reference) == 0); + cl_assert(strcmp(reference->name, loose_tag_ref_name) == 0); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_oid(reference), GIT_OBJ_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJ_TAG); + + /* Ensure the name of the tag matches the name of the reference */ + cl_git_pass(git_buf_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object))); + cl_assert(strcmp(ref_name_from_tag_name.ptr, loose_tag_ref_name) == 0); + git_buf_free(&ref_name_from_tag_name); + + git_object_free(object); + + git_reference_free(reference); +} + +void test_ref_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_ref_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_REF_SYMBOLIC); + cl_assert(git_reference_is_packed(reference) == 0); + cl_assert(strcmp(reference->name, GIT_HEAD_FILE) == 0); + + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJ_COMMIT); + + git_oid_fromstr(&id, current_master_tip); + cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0); + + git_object_free(object); + + git_reference_free(reference); + git_reference_free(resolved_ref); +} + +void test_ref_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_REF_SYMBOLIC); + cl_assert(git_reference_is_packed(reference) == 0); + cl_assert(strcmp(reference->name, head_tracker_sym_ref_name) == 0); + + cl_git_pass(git_reference_resolve(&resolved_ref, reference)); + cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJ_COMMIT); + + git_oid_fromstr(&id, current_master_tip); + cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0); + + git_object_free(object); + + git_reference_free(reference); + git_reference_free(resolved_ref); +} + +void test_ref_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_git_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(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_git_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref))); + git_reference_free(reference); + git_reference_free(resolved_ref); + + git_reference_free(comp_base_ref); +} + +void test_ref_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_git_pass(git_oid_cmp(git_reference_oid(master_ref), git_reference_oid(resolved_ref))); + + git_reference_free(reference); + git_reference_free(resolved_ref); + git_reference_free(master_ref); +} + + +void test_ref_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_REF_OID); + cl_assert(git_reference_is_packed(reference)); + cl_assert(strcmp(reference->name, packed_head_name) == 0); + + cl_git_pass(git_object_lookup(&object, g_repo, git_reference_oid(reference), GIT_OBJ_ANY)); + cl_assert(object != NULL); + cl_assert(git_object_type(object) == GIT_OBJ_COMMIT); + + git_object_free(object); + + git_reference_free(reference); +} + +void test_ref_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_REF_OID); + cl_assert(git_reference_is_packed(reference) == 0); + cl_assert(strcmp(reference->name, packed_test_head_name) == 0); + + git_reference_free(reference); +} diff --git a/tests-clar/refs/reflog.c b/tests-clar/refs/reflog.c new file mode 100644 index 000000000..cafb34f0e --- /dev/null +++ b/tests-clar/refs/reflog.c @@ -0,0 +1,134 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + + +static const char *new_ref = "refs/heads/test-reflog"; +static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; +static const char *commit_msg = "commit: bla bla"; + +static git_repository *g_repo; + + +// helpers +static int assert_signature(git_signature *expected, git_signature *actual) +{ + if (actual == NULL) + return GIT_ERROR; + + if (strcmp(expected->name, actual->name) != 0) + return GIT_ERROR; + + if (strcmp(expected->email, actual->email) != 0) + return GIT_ERROR; + + if (expected->when.offset != actual->when.offset) + return GIT_ERROR; + + if (expected->when.time != actual->when.time) + return GIT_ERROR; + + return GIT_SUCCESS; +} + + +// Fixture setup and teardown +void test_refs_reflog__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_reflog__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + + +void test_refs_reflog__write_then_read(void) +{ + // write a reflog for a given reference and ensure it can be read back + git_repository *repo2; + git_reference *ref, *lookedup_ref; + git_oid oid; + git_signature *committer; + git_reflog *reflog; + git_reflog_entry *entry; + char oid_str[GIT_OID_HEXSZ+1]; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&oid, current_master_tip); + cl_git_pass(git_reference_create_oid(&ref, g_repo, new_ref, &oid, 0)); + git_reference_free(ref); + cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref)); + + cl_git_pass(git_signature_now(&committer, "foo", "foo@bar")); + + cl_git_pass(git_reflog_write(ref, NULL, committer, NULL)); + cl_git_fail(git_reflog_write(ref, NULL, committer, "no ancestor NULL for an existing reflog")); + cl_git_fail(git_reflog_write(ref, NULL, committer, "no\nnewline")); + cl_git_pass(git_reflog_write(ref, &oid, committer, commit_msg)); + + /* Reopen a new instance of the repository */ + cl_git_pass(git_repository_open(&repo2, "testrepo")); + + /* Lookup the preivously 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, lookedup_ref)); + cl_assert(reflog->entries.length == 2); + + entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 0); + cl_git_pass(assert_signature(committer, entry->committer)); + git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old); + cl_assert(strcmp("0000000000000000000000000000000000000000", oid_str) == 0); + git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur); + cl_assert(strcmp(current_master_tip, oid_str) == 0); + cl_assert(entry->msg == NULL); + + entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 1); + cl_git_pass(assert_signature(committer, entry->committer)); + git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old); + cl_assert(strcmp(current_master_tip, oid_str) == 0); + git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur); + cl_assert(strcmp(current_master_tip, oid_str) == 0); + cl_assert(strcmp(commit_msg, entry->msg) == 0); + + git_signature_free(committer); + git_reflog_free(reflog); + git_repository_free(repo2); + + git_reference_free(ref); + git_reference_free(lookedup_ref); +} + +void test_refs_reflog__dont_write_bad(void) +{ + // avoid writing an obviously wrong reflog + git_reference *ref; + git_oid oid; + git_signature *committer; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&oid, current_master_tip); + cl_git_pass(git_reference_create_oid(&ref, g_repo, new_ref, &oid, 0)); + git_reference_free(ref); + cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref)); + + cl_git_pass(git_signature_now(&committer, "foo", "foo@bar")); + + /* Write the reflog for the new branch */ + cl_git_pass(git_reflog_write(ref, NULL, committer, NULL)); + + /* Try to update the reflog with wrong information: + * It's no new reference, so the ancestor OID cannot + * be NULL. */ + cl_git_fail(git_reflog_write(ref, NULL, committer, NULL)); + + git_signature_free(committer); + + git_reference_free(ref); +} diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c new file mode 100644 index 000000000..eec8e9836 --- /dev/null +++ b/tests-clar/refs/rename.c @@ -0,0 +1,339 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.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"); +} + +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, *another_looked_up_ref; + git_buf temp_path = GIT_BUF_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_buf_joinpath(&temp_path, g_repo->path_repository, new_name)); + cl_git_pass(!git_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(git_reference_is_packed(looked_up_ref) == 0); + + /* Now that the reference is renamed... */ + cl_git_pass(git_reference_rename(looked_up_ref, new_name, 0)); + cl_assert(!strcmp(looked_up_ref->name, new_name)); + + /* ...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(!strcmp(another_looked_up_ref->name, new_name)); + + /* .. the ref is still loose... */ + cl_assert(git_reference_is_packed(another_looked_up_ref) == 0); + cl_assert(git_reference_is_packed(looked_up_ref) == 0); + + /* ...and the ref can be found in the file system */ + cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, new_name)); + cl_git_pass(git_path_exists(temp_path.ptr)); + + git_reference_free(looked_up_ref); + git_reference_free(another_looked_up_ref); + git_buf_free(&temp_path); +} + +void test_refs_rename__packed(void) +{ + // rename a packed reference (should make it loose) + git_reference *looked_up_ref, *another_looked_up_ref; + git_buf temp_path = GIT_BUF_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_buf_joinpath(&temp_path, g_repo->path_repository, packed_head_name)); + cl_git_pass(!git_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(git_reference_is_packed(looked_up_ref) != 0); + + /* Now that the reference is renamed... */ + cl_git_pass(git_reference_rename(looked_up_ref, brand_new_name, 0)); + cl_assert(!strcmp(looked_up_ref->name, brand_new_name)); + + /* ...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(!strcmp(another_looked_up_ref->name, brand_new_name)); + + /* .. the ref is no longer packed... */ + cl_assert(git_reference_is_packed(another_looked_up_ref) == 0); + cl_assert(git_reference_is_packed(looked_up_ref) == 0); + + /* ...and the ref now happily lives in the file system */ + cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, brand_new_name)); + cl_git_pass(git_path_exists(temp_path.ptr)); + + git_reference_free(looked_up_ref); + git_reference_free(another_looked_up_ref); + git_buf_free(&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; + git_buf temp_path = GIT_BUF_INIT; + const char *brand_new_name = "refs/heads/brand_new_name"; + + /* Ensure the other reference exists on the file system */ + cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_test_head_name)); + cl_git_pass(git_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(git_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(git_reference_is_packed(looked_up_ref) != 0); + + /* Now that the reference is renamed... */ + cl_git_pass(git_reference_rename(looked_up_ref, brand_new_name, 0)); + + /* 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(git_reference_is_packed(another_looked_up_ref) == 0); + + /* Ensure the other ref still exists on the file system */ + cl_git_pass(git_path_exists(temp_path.ptr)); + + git_reference_free(looked_up_ref); + git_reference_free(another_looked_up_ref); + git_buf_free(&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; + + /* 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(looked_up_ref, packed_test_head_name, 0)); + 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(!strcmp(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; + + /* 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_git_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0)); + + /* Can not be renamed outside of the refs hierarchy. */ + cl_git_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you", 0)); + + /* 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(!strcmp(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; + 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_oid(looked_up_ref)); + + /* Can be force-renamed to the name of another existing reference. */ + cl_git_pass(git_reference_rename(looked_up_ref, packed_test_head_name, 1)); + git_reference_free(looked_up_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); + cl_assert(!strcmp(looked_up_ref->name, packed_test_head_name)); + cl_assert(!git_oid_cmp(&oid, git_reference_oid(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; + 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_oid(looked_up_ref)); + + /* Can be force-renamed to the name of another existing reference. */ + cl_git_pass(git_reference_rename(looked_up_ref, "refs/heads/test", 1)); + git_reference_free(looked_up_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test")); + cl_assert(!strcmp(looked_up_ref->name, "refs/heads/test")); + cl_assert(!git_oid_cmp(&oid, git_reference_oid(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_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REF_OID); + + git_oid_cpy(&id, git_reference_oid(ref)); + + /* Create loose references */ + cl_git_pass(git_reference_create_oid(&ref_one, g_repo, ref_one_name, &id, 0)); + cl_git_pass(git_reference_create_oid(&ref_two, g_repo, ref_two_name, &id, 0)); + + /* Pack everything */ + cl_git_pass(git_reference_packall(g_repo)); + + /* Attempt to create illegal reference */ + cl_git_fail(git_reference_create_oid(&ref_one_new, g_repo, ref_one_name_new, &id, 0)); + + /* 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); +} + + +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; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REF_OID); + + git_oid_cpy(&id, git_reference_oid(ref)); + + /* Create loose references */ + cl_git_pass(git_reference_create_oid(&ref_two, g_repo, ref_two_name, &id, 0)); + + /* 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(looked_up_ref, ref_two_name_new, 0)); + git_reference_free(looked_up_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new)); + cl_assert(!strcmp(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; + git_oid id; + + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); + cl_assert(git_reference_type(ref) & GIT_REF_OID); + + git_oid_cpy(&id, git_reference_oid(ref)); + + /* Create loose references */ + cl_git_pass(git_reference_create_oid(&ref_two, g_repo, ref_two_name_new, &id, 0)); + 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(looked_up_ref, ref_two_name, 0)); + git_reference_free(looked_up_ref); + + /* Check we actually renamed it */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name)); + cl_assert(!strcmp(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); +}