]> git.proxmox.com Git - libgit2.git/commitdiff
t10-refs.c ported.
authorBen Straub <ben@straubnet.net>
Fri, 30 Mar 2012 15:13:44 +0000 (08:13 -0700)
committerBen Straub <ben@straubnet.net>
Fri, 30 Mar 2012 19:05:42 +0000 (12:05 -0700)
tests-clar/refs/crashes.c
tests-clar/refs/create.c [new file with mode: 0644]
tests-clar/refs/delete.c [new file with mode: 0644]
tests-clar/refs/list.c [new file with mode: 0644]
tests-clar/refs/normalize.c [new file with mode: 0644]
tests-clar/refs/overwrite.c [new file with mode: 0644]
tests-clar/refs/pack.c [new file with mode: 0644]
tests-clar/refs/read.c [new file with mode: 0644]
tests-clar/refs/reflog.c [new file with mode: 0644]
tests-clar/refs/rename.c [new file with mode: 0644]

index 26ce98a6811ba238895796f1bf5a8ffab092d99f..e1b289ace2d3175b57a549ccb18fb4538f3b1157 100644 (file)
@@ -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 (file)
index 0000000..8f64c51
--- /dev/null
@@ -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 (file)
index 0000000..cc3b936
--- /dev/null
@@ -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 (file)
index 0000000..f673bd9
--- /dev/null
@@ -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 (file)
index 0000000..49bc9ac
--- /dev/null
@@ -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 (file)
index 0000000..24e72f5
--- /dev/null
@@ -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 (file)
index 0000000..232aa75
--- /dev/null
@@ -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 (file)
index 0000000..560ec56
--- /dev/null
@@ -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 (file)
index 0000000..cafb34f
--- /dev/null
@@ -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 (file)
index 0000000..eec8e98
--- /dev/null
@@ -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);
+}