summaryrefslogtreecommitdiff
path: root/tests/refs
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2013-11-20 12:54:24 +0100
committerVicent Marti <tanoku@gmail.com>2013-11-20 12:54:24 +0100
commit4b0a36e881506a02b43a4ae3c19c93c919b36eeb (patch)
tree026182fa30273a4c1649928b6db3fc5335bd1ea4 /tests/refs
parent29d7242b1dcd1f09a63417abd648a6217b85d301 (diff)
parent43cb8b32428b1b29994874349ec22eb5372e152c (diff)
downloadlibgit2-4b0a36e881506a02b43a4ae3c19c93c919b36eeb.tar.gz
Merge branch 'development'
Diffstat (limited to 'tests/refs')
-rw-r--r--tests/refs/branches/create.c76
-rw-r--r--tests/refs/branches/delete.c117
-rw-r--r--tests/refs/branches/ishead.c116
-rw-r--r--tests/refs/branches/iterator.c151
-rw-r--r--tests/refs/branches/lookup.c45
-rw-r--r--tests/refs/branches/move.c146
-rw-r--r--tests/refs/branches/name.c45
-rw-r--r--tests/refs/branches/remote.c86
-rw-r--r--tests/refs/branches/upstream.c143
-rw-r--r--tests/refs/branches/upstreamname.c42
-rw-r--r--tests/refs/crashes.c20
-rw-r--r--tests/refs/create.c168
-rw-r--r--tests/refs/delete.c93
-rw-r--r--tests/refs/foreachglob.c95
-rw-r--r--tests/refs/isvalidname.c31
-rw-r--r--tests/refs/iterator.c97
-rw-r--r--tests/refs/list.c57
-rw-r--r--tests/refs/listall.c47
-rw-r--r--tests/refs/lookup.c60
-rw-r--r--tests/refs/normalize.c403
-rw-r--r--tests/refs/overwrite.c136
-rw-r--r--tests/refs/pack.c105
-rw-r--r--tests/refs/peel.c119
-rw-r--r--tests/refs/read.c284
-rw-r--r--tests/refs/ref_helpers.c25
-rw-r--r--tests/refs/ref_helpers.h1
-rw-r--r--tests/refs/reflog/drop.c115
-rw-r--r--tests/refs/reflog/reflog.c209
-rw-r--r--tests/refs/rename.c367
-rw-r--r--tests/refs/revparse.c813
-rw-r--r--tests/refs/setter.c99
-rw-r--r--tests/refs/shorthand.c27
-rw-r--r--tests/refs/unicode.c56
-rw-r--r--tests/refs/update.c26
34 files changed, 4420 insertions, 0 deletions
diff --git a/tests/refs/branches/create.c b/tests/refs/branches/create.c
new file mode 100644
index 000000000..693a592a3
--- /dev/null
+++ b/tests/refs/branches/create.c
@@ -0,0 +1,76 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_commit *target;
+static git_reference *branch;
+
+void test_refs_branches_create__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ branch = NULL;
+}
+
+void test_refs_branches_create__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_commit_free(target);
+ target = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_commit_lookup(out, repo, &oid));
+}
+
+static void retrieve_known_commit(git_commit **commit, git_repository *repo)
+{
+ retrieve_target_from_oid(commit, repo, "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_create__can_create_a_local_branch(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+}
+
+void test_refs_branches_create__can_not_create_a_branch_if_its_name_collide_with_an_existing_one(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_assert_equal_i(GIT_EEXISTS, git_branch_create(&branch, repo, "br2", target, 0));
+}
+
+void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+ cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
+}
+
+
+void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_create(&branch, repo, "inv@{id", target, 0));
+} \ No newline at end of file
diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c
new file mode 100644
index 000000000..de90cb734
--- /dev/null
+++ b/tests/refs/branches/delete.c
@@ -0,0 +1,117 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+#include "config/config_helpers.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_delete__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_delete__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
+{
+ git_reference *head;
+ git_reference *branch;
+
+ /* Ensure HEAD targets the local master branch */
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+ git_reference_free(head);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_fail(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void)
+{
+ git_reference *head;
+ git_reference *branch;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ git_reference_delete(head);
+ git_reference_free(head);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_unborn(void)
+{
+ git_reference *branch;
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void)
+{
+ git_reference *head, *branch;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+ git_reference_free(head);
+
+ /* Detach HEAD and make it target the commit that "master" points to */
+ git_repository_detach_head(repo);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_local_branch(void)
+{
+ git_reference *branch;
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_remote_branch(void)
+{
+ git_reference *branch;
+ cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void)
+{
+ git_reference *branch;
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+}
diff --git a/tests/refs/branches/ishead.c b/tests/refs/branches/ishead.c
new file mode 100644
index 000000000..b1ad09c3e
--- /dev/null
+++ b/tests/refs/branches/ishead.c
@@ -0,0 +1,116 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+
+static git_repository *repo;
+static git_reference *branch;
+
+void test_refs_branches_ishead__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_branches_ishead__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(true, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void)
+{
+ git_repository_free(repo);
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void)
+{
+ git_repository_free(repo);
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ delete_head(repo);
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__wont_be_fooled_by_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+/*
+ * $ git init .
+ * Initialized empty Git repository in d:/temp/tempee/.git/
+ *
+ * $ touch a && git add a
+ * $ git commit -m" boom"
+ * [master (root-commit) b47b758] boom
+ * 0 files changed
+ * create mode 100644 a
+ *
+ * $ echo "ref: refs/heads/master" > .git/refs/heads/linked
+ * $ echo "ref: refs/heads/linked" > .git/refs/heads/super
+ * $ echo "ref: refs/heads/super" > .git/HEAD
+ *
+ * $ git branch
+ * linked -> master
+ * * master
+ * super -> master
+ */
+void test_refs_branches_ishead__only_direct_references_are_considered(void)
+{
+ git_reference *linked, *super, *head;
+
+ git_repository_free(repo);
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0));
+ cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0));
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1));
+
+ cl_assert_equal_i(false, git_branch_is_head(linked));
+ cl_assert_equal_i(false, git_branch_is_head(super));
+
+ cl_git_pass(git_repository_head(&branch, repo));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
+
+ git_reference_free(linked);
+ git_reference_free(super);
+ git_reference_free(head);
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
diff --git a/tests/refs/branches/iterator.c b/tests/refs/branches/iterator.c
new file mode 100644
index 000000000..904c6a146
--- /dev/null
+++ b/tests/refs/branches/iterator.c
@@ -0,0 +1,151 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_iterator__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_iterator__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_retrieval(unsigned int flags, unsigned int expected_count)
+{
+ git_branch_iterator *iter;
+ git_reference *ref;
+ int count = 0, error;
+ git_branch_t type;
+
+ cl_git_pass(git_branch_iterator_new(&iter, repo, flags));
+ while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+ count++;
+ git_reference_free(ref);
+ }
+
+ git_branch_iterator_free(iter);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_branches_iterator__retrieve_all_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14);
+}
+
+void test_refs_branches_iterator__retrieve_remote_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_REMOTE, 2);
+}
+
+void test_refs_branches_iterator__retrieve_local_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL, 12);
+}
+
+struct expectations {
+ const char *branch_name;
+ int encounters;
+};
+
+static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name)
+{
+ int pos = 0;
+
+ for (pos = 0; findings[pos].branch_name; ++pos) {
+ if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) {
+ cl_assert_equal_i(1, findings[pos].encounters);
+ return;
+ }
+ }
+
+ cl_fail("expected branch not found in list.");
+}
+
+static void contains_branches(struct expectations exp[], git_branch_iterator *iter)
+{
+ git_reference *ref;
+ git_branch_t type;
+ int error, pos = 0;
+
+ while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+ for (pos = 0; exp[pos].branch_name; ++pos) {
+ if (strcmp(git_reference_shorthand(ref), exp[pos].branch_name) == 0)
+ exp[pos].encounters++;
+ }
+
+ git_reference_free(ref);
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+}
+
+/*
+ * $ git branch -r
+ * nulltoken/HEAD -> nulltoken/master
+ * nulltoken/master
+ */
+void test_refs_branches_iterator__retrieve_remote_symbolic_HEAD_when_present(void)
+{
+ git_branch_iterator *iter;
+ struct expectations exp[] = {
+ { "nulltoken/HEAD", 0 },
+ { "nulltoken/master", 0 },
+ { NULL, 0 }
+ };
+
+ git_reference_free(fake_remote);
+ cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0));
+
+ assert_retrieval(GIT_BRANCH_REMOTE, 3);
+
+ cl_git_pass(git_branch_iterator_new(&iter, repo, GIT_BRANCH_REMOTE));
+ contains_branches(exp, iter);
+ git_branch_iterator_free(iter);
+
+ assert_branch_has_been_found(exp, "nulltoken/HEAD");
+ assert_branch_has_been_found(exp, "nulltoken/master");
+}
+
+void test_refs_branches_iterator__mix_of_packed_and_loose(void)
+{
+ git_branch_iterator *iter;
+ struct expectations exp[] = {
+ { "master", 0 },
+ { "origin/HEAD", 0 },
+ { "origin/master", 0 },
+ { "origin/packed", 0 },
+ { NULL, 0 }
+ };
+ git_repository *r2;
+
+ r2 = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE));
+ contains_branches(exp, iter);
+
+ git_branch_iterator_free(iter);
+
+ assert_branch_has_been_found(exp, "master");
+ assert_branch_has_been_found(exp, "origin/HEAD");
+ assert_branch_has_been_found(exp, "origin/master");
+ assert_branch_has_been_found(exp, "origin/packed");
+}
diff --git a/tests/refs/branches/lookup.c b/tests/refs/branches/lookup.c
new file mode 100644
index 000000000..95d49a4b3
--- /dev/null
+++ b/tests/refs/branches/lookup.c
@@ -0,0 +1,45 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch;
+
+void test_refs_branches_lookup__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+}
+
+void test_refs_branches_lookup__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_lookup__can_retrieve_a_local_branch(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_an_unknown_branch_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "where/are/you", GIT_BRANCH_LOCAL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "over/here", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "are/you/inv@{id", GIT_BRANCH_LOCAL));
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "yes/i am", GIT_BRANCH_REMOTE));
+}
diff --git a/tests/refs/branches/move.c b/tests/refs/branches/move.c
new file mode 100644
index 000000000..ecf14e006
--- /dev/null
+++ b/tests/refs/branches/move.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "config/config_helpers.h"
+
+static git_repository *repo;
+
+void test_refs_branches_move__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_branches_move__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_move__can_move_a_local_branch(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0));
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void)
+{
+ git_reference *original_ref, *new_ref, *newer_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ /* Downward */
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "somewhere/" NEW_BRANCH_NAME, 0));
+ git_reference_free(original_ref);
+
+ /* Upward */
+ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0));
+ git_reference_free(new_ref);
+
+ git_reference_free(newer_ref);
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void)
+{
+ git_reference *original_ref, *new_ref, *newer_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ /* Downward */
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "br2/" NEW_BRANCH_NAME, 0));
+ git_reference_free(original_ref);
+
+ /* Upward */
+ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0));
+ git_reference_free(new_ref);
+
+ git_reference_free(newer_ref);
+}
+
+void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_branch_move(&new_ref, original_ref, "master", 0));
+
+ git_reference_free(original_ref);
+}
+
+void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0));
+
+ git_reference_free(original_ref);
+}
+
+void test_refs_branches_move__can_not_move_a_non_branch(void)
+{
+ git_reference *tag, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&tag, repo, "refs/tags/e90810b"));
+ cl_git_fail(git_branch_move(&new_ref, tag, NEW_BRANCH_NAME, 0));
+
+ git_reference_free(tag);
+}
+
+void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__moving_a_branch_moves_related_configuration_data(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+ assert_config_entry_existence(repo, "branch.moved.remote", false);
+ assert_config_entry_existence(repo, "branch.moved.merge", false);
+
+ cl_git_pass(git_branch_move(&new_branch, branch, "moved", 0));
+ git_reference_free(branch);
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+ assert_config_entry_existence(repo, "branch.moved.remote", true);
+ assert_config_entry_existence(repo, "branch.moved.merge", true);
+
+ git_reference_free(new_branch);
+}
+
+void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+ cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
+ git_reference_free(branch);
+ git_reference_free(new_branch);
+
+ cl_git_pass(git_repository_head(&branch, repo));
+ cl_assert_equal_s("refs/heads/master2", git_reference_name(branch));
+ git_reference_free(branch);
+}
diff --git a/tests/refs/branches/name.c b/tests/refs/branches/name.c
new file mode 100644
index 000000000..176f836a4
--- /dev/null
+++ b/tests/refs/branches/name.c
@@ -0,0 +1,45 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_reference *ref;
+
+void test_refs_branches_name__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_branches_name__cleanup(void)
+{
+ git_reference_free(ref);
+ ref = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_name__can_get_local_branch_name(void)
+{
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,repo,"master",GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_name(&name,ref));
+ cl_assert_equal_s("master",name);
+}
+
+void test_refs_branches_name__can_get_remote_branch_name(void)
+{
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,repo,"test/master",GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name,ref));
+ cl_assert_equal_s("test/master",name);
+}
+
+void test_refs_branches_name__error_when_ref_is_no_branch(void)
+{
+ const char *name;
+
+ cl_git_pass(git_reference_lookup(&ref,repo,"refs/notes/fanout"));
+ cl_git_fail(git_branch_name(&name,ref));
+}
diff --git a/tests/refs/branches/remote.c b/tests/refs/branches/remote.c
new file mode 100644
index 000000000..c110adb33
--- /dev/null
+++ b/tests/refs/branches/remote.c
@@ -0,0 +1,86 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+#include "remote.h"
+
+static git_repository *g_repo;
+static const char *remote_tracking_branch_name = "refs/remotes/test/master";
+static const char *expected_remote_name = "test";
+static int expected_remote_name_length;
+
+void test_refs_branches_remote__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ expected_remote_name_length = (int)strlen(expected_remote_name) + 1;
+}
+
+void test_refs_branches_remote__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_remote__can_get_remote_for_branch(void)
+{
+ char remotename[1024] = {0};
+
+ cl_assert_equal_i(expected_remote_name_length,
+ git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name));
+
+ cl_assert_equal_i(expected_remote_name_length,
+ git_branch_remote_name(remotename, expected_remote_name_length, g_repo,
+ remote_tracking_branch_name));
+
+ cl_assert_equal_s("test", remotename);
+}
+
+void test_refs_branches_remote__insufficient_buffer_returns_error(void)
+{
+ char remotename[1024] = {0};
+
+ cl_assert_equal_i(expected_remote_name_length,
+ git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name));
+
+ cl_git_fail_with(git_branch_remote_name(remotename,
+ expected_remote_name_length - 1, g_repo, remote_tracking_branch_name),
+ expected_remote_name_length);
+}
+
+void test_refs_branches_remote__no_matching_remote_returns_error(void)
+{
+ const char *unknown = "refs/remotes/nonexistent/master";
+
+ giterr_clear();
+ cl_git_fail_with(git_branch_remote_name(
+ NULL, 0, g_repo, unknown), GIT_ENOTFOUND);
+ cl_assert(giterr_last() != NULL);
+}
+
+void test_refs_branches_remote__local_remote_returns_error(void)
+{
+ const char *local = "refs/heads/master";
+
+ giterr_clear();
+ cl_git_fail_with(git_branch_remote_name(
+ NULL, 0, g_repo, local), GIT_ERROR);
+ cl_assert(giterr_last() != NULL);
+}
+
+void test_refs_branches_remote__ambiguous_remote_returns_error(void)
+{
+ git_remote *remote;
+
+ /* Create the remote */
+ cl_git_pass(git_remote_create(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2"));
+
+ /* Update the remote fetch spec */
+ git_remote_clear_refspecs(remote);
+ cl_git_pass(git_remote_add_fetch(remote, "refs/heads/*:refs/remotes/test/*"));
+ cl_git_pass(git_remote_save(remote));
+
+ git_remote_free(remote);
+
+ giterr_clear();
+ cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo,
+ remote_tracking_branch_name), GIT_EAMBIGUOUS);
+ cl_assert(giterr_last() != NULL);
+}
diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c
new file mode 100644
index 000000000..69e55a0c5
--- /dev/null
+++ b/tests/refs/branches/upstream.c
@@ -0,0 +1,143 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch, *upstream;
+
+void test_refs_branches_upstream__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+ upstream = NULL;
+}
+
+void test_refs_branches_upstream__cleanup(void)
+{
+ git_reference_free(upstream);
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstream__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__can_retrieve_the_local_upstream_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/heads/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__cannot_retrieve_a_remote_upstream_reference_from_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_git_fail(git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_branch_with_no_fetchspec_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/cannot-fetch"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name)
+{
+ git_reference *branch;
+
+ cl_assert_equal_i(GIT_OBJ_COMMIT, git_object_type((git_object*)target));
+ cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+
+ git_reference_free(branch);
+}
+
+void test_refs_branches_upstream__retrieve_a_remote_tracking_reference_from_a_branch_with_no_remote_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *head;
+ git_repository *repository;
+ git_commit *target;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_head(&head, repository));
+ cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJ_COMMIT));
+ git_reference_free(head);
+
+ assert_merge_and_or_remote_key_missing(repository, target, "remoteless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless");
+
+ git_commit_free(target);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_upstream__set_unset_upstream(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+ const char *value;
+ git_config *config;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ /* remote */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ cl_git_pass(git_repository_config(&config, repository));
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.remote"));
+ cl_assert_equal_s(value, "test");
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.merge"));
+ cl_assert_equal_s(value, "refs/heads/master");
+
+ git_reference_free(branch);
+
+ /* local */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "master"));
+
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.remote"));
+ cl_assert_equal_s(value, ".");
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.merge"));
+ cl_assert_equal_s(value, "refs/heads/master");
+
+ /* unset */
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.test.merge"), GIT_ENOTFOUND);
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.test.remote"), GIT_ENOTFOUND);
+
+ git_reference_free(branch);
+
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/master"));
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.master.merge"), GIT_ENOTFOUND);
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.master.remote"), GIT_ENOTFOUND);
+
+ git_reference_free(branch);
+
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/refs/branches/upstreamname.c b/tests/refs/branches/upstreamname.c
new file mode 100644
index 000000000..f05607d44
--- /dev/null
+++ b/tests/refs/branches/upstreamname.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_buf upstream_name;
+
+void test_refs_branches_upstreamname__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ git_buf_init(&upstream_name, 0);
+}
+
+void test_refs_branches_upstreamname__cleanup(void)
+{
+ git_buf_free(&upstream_name);
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream__name(
+ &upstream_name, repo, "refs/heads/master"));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_buf_cstr(&upstream_name));
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_local_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream__name(
+ &upstream_name, repo, "refs/heads/track-local"));
+
+ cl_assert_equal_s("refs/heads/master", git_buf_cstr(&upstream_name));
+}
+
+void test_refs_branches_upstreamname__can_return_the_size_of_thelocal_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_assert_equal_i((int)strlen("refs/heads/master") + 1,
+ git_branch_upstream_name(NULL, 0, repo, "refs/heads/track-local"));
+}
diff --git a/tests/refs/crashes.c b/tests/refs/crashes.c
new file mode 100644
index 000000000..5a1885a7a
--- /dev/null
+++ b/tests/refs/crashes.c
@@ -0,0 +1,20 @@
+#include "clar_libgit2.h"
+
+void test_refs_crashes__double_free(void)
+{
+ git_repository *repo;
+ git_reference *ref, *ref2;
+ const char *REFNAME = "refs/heads/xxx";
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0));
+ cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ /* reference is gone from disk, so reloading it will fail */
+ cl_git_fail(git_reference_lookup(&ref2, repo, REFNAME));
+
+ git_repository_free(repo);
+}
diff --git a/tests/refs/create.c b/tests/refs/create.c
new file mode 100644
index 000000000..85ff05aa9
--- /dev/null
+++ b/tests/refs/create.c
@@ -0,0 +1,168 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *current_master_tip = "099fabac3a9ea935598528c27f866e34089c2eff";
+static const char *current_head_target = "refs/heads/master";
+
+static git_repository *g_repo;
+
+void test_refs_create__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_create__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_create__symbolic(void)
+{
+ // create a new symbolic reference
+ git_reference *new_reference, *looked_up_ref, *resolved_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head_tracker = "ANOTHER_HEAD_TRACKER";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new symbolic reference */
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0));
+
+ /* 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(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+
+ /* ...peeled.. */
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
+
+ /* ...and that it points to the current master tip */
+ cl_assert(git_oid_cmp(&id, git_reference_target(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_target(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_refs_create__deep_symbolic(void)
+{
+ // create a deep symbolic reference
+ git_reference *new_reference, *looked_up_ref, *resolved_ref;
+ git_oid id;
+
+ const char *new_head_tracker = "deep/rooted/tracker";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0));
+ 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_target(resolved_ref)) == 0);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_create__oid(void)
+{
+ // create a new OID reference
+ git_reference *new_reference, *looked_up_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0));
+
+ /* 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(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head);
+
+ /* ...and that it points to the current master tip */
+ cl_assert(git_oid_cmp(&id, git_reference_target(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_target(looked_up_ref)) == 0);
+
+ git_repository_free(repo2);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_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(&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));
+}
+
+void test_refs_create__propagate_eexists(void)
+{
+ int error;
+ git_oid oid;
+ git_reference *ref;
+
+ /* Make sure it works for oid and for symbolic both */
+ git_oid_fromstr(&oid, current_master_tip);
+ error = git_reference_create(&ref, g_repo, current_head_target, &oid, false);
+ cl_assert(error == GIT_EEXISTS);
+
+ error = git_reference_symbolic_create(&ref, g_repo, "HEAD", current_head_target, false);
+ cl_assert(error == GIT_EEXISTS);
+}
+
+void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *new_reference;
+ git_oid id;
+
+ const char *name = "refs/heads/inv@{id";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create(
+ &new_reference, g_repo, name, &id, 0));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(
+ &new_reference, g_repo, name, current_head_target, 0));
+}
diff --git a/tests/refs/delete.c b/tests/refs/delete.c
new file mode 100644
index 000000000..973768aeb
--- /dev/null
+++ b/tests/refs/delete.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "git2/refdb.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *packed_test_head_name = "refs/heads/packed-test";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+
+static git_repository *g_repo;
+
+
+
+void test_refs_delete__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_delete__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_delete__packed_loose(void)
+{
+ // deleting a ref which is both packed and loose should remove both tracks in the filesystem
+ git_reference *looked_up_ref, *another_looked_up_ref;
+ git_buf temp_path = GIT_BUF_INIT;
+
+ /* Ensure the loose reference exists on the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
+ cl_assert(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(reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is deleted... */
+ cl_git_pass(git_reference_delete(looked_up_ref));
+ git_reference_free(looked_up_ref);
+
+ /* Looking up the reference once again should not retrieve it */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure the loose reference doesn't exist any longer on the file system */
+ cl_assert(!git_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_refdb *refdb;
+ git_oid id;
+ const char *new_ref = "refs/heads/new_ref";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &id, 0));
+ git_reference_free(ref);
+
+ /* Lookup the reference */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ /* Ensure it's a loose reference */
+ cl_assert(reference_is_packed(ref) == 0);
+
+ /* Pack all existing references */
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+
+ /* Reload the reference from disk */
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ /* Ensure it's a packed reference */
+ cl_assert(reference_is_packed(ref) == 1);
+
+ /* This should pass */
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ git_refdb_free(refdb);
+}
diff --git a/tests/refs/foreachglob.c b/tests/refs/foreachglob.c
new file mode 100644
index 000000000..2c458082f
--- /dev/null
+++ b/tests/refs/foreachglob.c
@@ -0,0 +1,95 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_foreachglob__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_foreachglob__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static int count_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return 0;
+}
+
+static void assert_retrieval(const char *glob, int expected_count)
+{
+ int count = 0;
+
+ cl_git_pass(git_reference_foreach_glob(repo, glob, count_cb, &count));
+
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_foreachglob__retrieve_all_refs(void)
+{
+ /* 12 heads (including one packed head) + 1 note + 2 remotes + 7 tags */
+ assert_retrieval("*", 22);
+}
+
+void test_refs_foreachglob__retrieve_remote_branches(void)
+{
+ assert_retrieval("refs/remotes/*", 2);
+}
+
+void test_refs_foreachglob__retrieve_local_branches(void)
+{
+ assert_retrieval("refs/heads/*", 12);
+}
+
+void test_refs_foreachglob__retrieve_partially_named_references(void)
+{
+ /*
+ * refs/heads/packed-test, refs/heads/test
+ * refs/remotes/test/master, refs/tags/test
+ */
+
+ assert_retrieval("*test*", 4);
+}
+
+
+static int interrupt_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return (*count == 11);
+}
+
+void test_refs_foreachglob__can_cancel(void)
+{
+ int count = 0;
+
+ cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob(
+ repo, "*", interrupt_cb, &count) );
+
+ cl_assert_equal_i(11, count);
+}
diff --git a/tests/refs/isvalidname.c b/tests/refs/isvalidname.c
new file mode 100644
index 000000000..65c70ba4d
--- /dev/null
+++ b/tests/refs/isvalidname.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+
+void test_refs_isvalidname__can_detect_invalid_formats(void)
+{
+ cl_assert_equal_i(false, git_reference_is_valid_name("refs/tags/0.17.0^{}"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("TWO/LEVELS"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("ONE.LEVEL"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("HEAD/"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("NO_TRAILING_UNDERSCORE_"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("_NO_LEADING_UNDERSCORE"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("HEAD/aa"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("lower_case"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("/stupid/name/master"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("/"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("//"));
+ cl_assert_equal_i(false, git_reference_is_valid_name(""));
+ cl_assert_equal_i(false, git_reference_is_valid_name("refs/heads/sub.lock/webmatrix"));
+}
+
+void test_refs_isvalidname__wont_hopefully_choke_on_valid_formats(void)
+{
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/tags/0.17.0"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/LEVELS"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("HEAD"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("ONE_LEVEL"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/stash"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/remotes/origin/bim_with_3d@11296"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/master{yesterday"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/master}yesterday"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/master{yesterday}"));
+}
diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c
new file mode 100644
index 000000000..266410fdf
--- /dev/null
+++ b/tests/refs/iterator.c
@@ -0,0 +1,97 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "vector.h"
+
+static git_repository *repo;
+
+void test_refs_iterator__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_iterator__cleanup(void)
+{
+ git_repository_free(repo);
+}
+
+static const char *refnames[] = {
+ "refs/heads/br2",
+ "refs/heads/cannot-fetch",
+ "refs/heads/chomped",
+ "refs/heads/haacked",
+ "refs/heads/master",
+ "refs/heads/not-good",
+ "refs/heads/packed",
+ "refs/heads/packed-test",
+ "refs/heads/subtrees",
+ "refs/heads/test",
+ "refs/heads/track-local",
+ "refs/heads/trailing",
+ "refs/notes/fanout",
+ "refs/remotes/test/master",
+ "refs/tags/annotated_tag_to_blob",
+ "refs/tags/e90810b",
+ "refs/tags/hard_tag",
+ "refs/tags/point_to_blob",
+ "refs/tags/taggerless",
+ "refs/tags/test",
+ "refs/tags/wrapped_tag",
+};
+
+static int refcmp_cb(const void *a, const void *b)
+{
+ const git_reference *refa = (const git_reference *)a;
+ const git_reference *refb = (const git_reference *)b;
+
+ return strcmp(refa->name, refb->name);
+}
+
+void test_refs_iterator__list(void)
+{
+ git_reference_iterator *iter;
+ git_vector output;
+ git_reference *ref;
+ int error;
+ size_t i;
+
+ cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+
+ do {
+ error = git_reference_next(&ref, iter);
+ cl_assert(error == 0 || error == GIT_ITEROVER);
+ if (error != GIT_ITEROVER) {
+ cl_git_pass(git_vector_insert(&output, ref));
+ }
+ } while (!error);
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames));
+
+ git_vector_sort(&output);
+
+ git_vector_foreach(&output, i, ref) {
+ cl_assert_equal_s(ref->name, refnames[i]);
+ git_reference_free(ref);
+ }
+
+ git_vector_free(&output);
+}
+
+void test_refs_iterator__empty(void)
+{
+ git_reference_iterator *iter;
+ git_odb *odb;
+ git_reference *ref;
+ git_repository *empty;
+
+ cl_git_pass(git_odb_new(&odb));
+ cl_git_pass(git_repository_wrap_odb(&empty, odb));
+
+ cl_git_pass(git_reference_iterator_new(&iter, empty));
+ cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter));
+
+ git_reference_iterator_free(iter);
+ git_odb_free(odb);
+ git_repository_free(empty);
+}
diff --git a/tests/refs/list.c b/tests/refs/list.c
new file mode 100644
index 000000000..de5c0fd3d
--- /dev/null
+++ b/tests/refs/list.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static git_repository *g_repo;
+
+
+
+void test_refs_list__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_list__all(void)
+{
+ // try to list all the references in our test repo
+ git_strarray ref_list;
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+
+ /*{
+ unsigned short i;
+ for (i = 0; i < ref_list.count; ++i)
+ printf("# %s\n", ref_list.strings[i]);
+ }*/
+
+ /* We have exactly 12 refs in total if we include the packed ones:
+ * there is a reference that exists both in the packfile and as
+ * loose, but we only list it once */
+ cl_assert_equal_i((int)ref_list.count, 14);
+
+ git_strarray_free(&ref_list);
+}
+
+void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void)
+{
+ git_strarray ref_list;
+
+ /* Create a fake locked reference */
+ cl_git_mkfile(
+ "./testrepo/.git/refs/heads/hanwen.lock",
+ "144344043ba4d4a405da03de3844aa829ae8be0e\n");
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+ cl_assert_equal_i((int)ref_list.count, 14);
+
+ git_strarray_free(&ref_list);
+}
diff --git a/tests/refs/listall.c b/tests/refs/listall.c
new file mode 100644
index 000000000..c696fbb2e
--- /dev/null
+++ b/tests/refs/listall.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_strarray ref_list;
+
+static void ensure_no_refname_starts_with_a_forward_slash(const char *path)
+{
+ size_t i;
+
+ cl_git_pass(git_repository_open(&repo, path));
+ cl_git_pass(git_reference_list(&ref_list, repo));
+
+ cl_assert(ref_list.count > 0);
+
+ for (i = 0; i < ref_list.count; i++)
+ cl_assert(git__prefixcmp(ref_list.strings[i], "/") != 0);
+
+ git_strarray_free(&ref_list);
+ git_repository_free(repo);
+}
+
+void test_refs_listall__from_repository_opened_through_workdir_path(void)
+{
+ cl_fixture_sandbox("status");
+ cl_git_pass(p_rename("status/.gitted", "status/.git"));
+
+ ensure_no_refname_starts_with_a_forward_slash("status");
+
+ cl_fixture_cleanup("status");
+}
+
+void test_refs_listall__from_repository_opened_through_gitdir_path(void)
+{
+ ensure_no_refname_starts_with_a_forward_slash(cl_fixture("testrepo.git"));
+}
+
+void test_refs_listall__from_repository_with_no_trailing_newline(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("bad_tag.git")));
+ cl_git_pass(git_reference_list(&ref_list, repo));
+
+ cl_assert(ref_list.count > 0);
+
+ git_strarray_free(&ref_list);
+ git_repository_free(repo);
+}
diff --git a/tests/refs/lookup.c b/tests/refs/lookup.c
new file mode 100644
index 000000000..2e31cf0f6
--- /dev/null
+++ b/tests/refs/lookup.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_lookup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_lookup__with_resolve(void)
+{
+ git_reference *a, *b, *temp;
+
+ cl_git_pass(git_reference_lookup(&temp, g_repo, "HEAD"));
+ cl_git_pass(git_reference_resolve(&a, temp));
+ git_reference_free(temp);
+
+ cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD", 5));
+ cl_assert(git_reference_cmp(a, b) == 0);
+ git_reference_free(b);
+
+ cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD_TRACKER", 5));
+ cl_assert(git_reference_cmp(a, b) == 0);
+ git_reference_free(b);
+
+ git_reference_free(a);
+}
+
+void test_refs_lookup__invalid_name(void)
+{
+ git_oid oid;
+ cl_git_fail(git_reference_name_to_id(&oid, g_repo, "/refs/tags/point_to_blob"));
+}
+
+void test_refs_lookup__oid(void)
+{
+ git_oid tag, expected;
+
+ cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob"));
+ cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+ cl_assert(git_oid_cmp(&tag, &expected) == 0);
+}
+
+void test_refs_lookup__namespace(void)
+{
+ int error;
+ git_reference *ref;
+
+ error = git_reference_lookup(&ref, g_repo, "refs/heads");
+ cl_assert_equal_i(error, GIT_ENOTFOUND);
+
+ error = git_reference_lookup(&ref, g_repo, "refs/heads/");
+ cl_assert_equal_i(error, GIT_EINVALIDSPEC);
+}
diff --git a/tests/refs/normalize.c b/tests/refs/normalize.c
new file mode 100644
index 000000000..7f313ef38
--- /dev/null
+++ b/tests/refs/normalize.c
@@ -0,0 +1,403 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+// Helpers
+static void ensure_refname_normalized(
+ unsigned int flags,
+ const char *input_refname,
+ const char *expected_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ cl_git_pass(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags));
+
+ cl_assert_equal_s(expected_refname, buffer_out);
+}
+
+static void ensure_refname_invalid(unsigned int flags, const char *input_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags));
+}
+
+void test_refs_normalize__can_normalize_a_direct_reference_name(void)
+{
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/dummy/a", "refs/dummy/a");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/stash", "refs/stash");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/tags/a", "refs/tags/a");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a/b", "refs/heads/a/b");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a./b", "refs/heads/a./b");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a");
+}
+
+void test_refs_normalize__cannot_normalize_any_direct_reference_name(void)
+{
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "/a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "//a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "/refs/heads/a/");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a/");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a.");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a.lock");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/foo?bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads\foo");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/.a/b");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/foo/../bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/foo..bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/./foo");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/v@{ation");
+}
+
+void test_refs_normalize__symbolic(void)
+{
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "heads\foo");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "/");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "///");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "ALL_CAPS_AND_UNDERSCORES", "ALL_CAPS_AND_UNDERSCORES");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/MixedCasing", "refs/MixedCasing");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs///heads///a", "refs/heads/a");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "HEAD", "HEAD");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "MERGE_HEAD", "MERGE_HEAD");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "FETCH_HEAD", "FETCH_HEAD");
+}
+
+/* Ported from JGit, BSD licence.
+ * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739
+ *
+ * Copyright (C) 2009, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void test_refs_normalize__jgit_suite(void)
+{
+ // tests borrowed from JGit
+
+/* EmptyString */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "/");
+
+/* MustHaveTwoComponents */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "master");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "heads/master", "heads/master");
+
+/* ValidHead */
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master", "refs/heads/master");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/pu", "refs/heads/pu");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/z", "refs/heads/z");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/FoO", "refs/heads/FoO");
+
+/* ValidTag */
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/tags/v1.0", "refs/tags/v1.0");
+
+/* NoLockSuffix */
+ ensure_refname_invalid(GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master.lock");
+
+/* NoDirectorySuffix */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master/");
+
+/* NoSpace */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/i haz space");
+
+/* 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';
+ ensure_refname_invalid(GIT_REF_FORMAT_ALLOW_ONELEVEL, buffer);
+ }
+ }
+
+/* NoBareDot */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/.");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/..");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/./master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/../master");
+
+/* NoLeadingOrTrailingDot */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, ".");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/.bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/..bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/bar.");
+
+/* ContainsDot */
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master..pu");
+
+/* NoMagicRefCharacters */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master^");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/^master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "^refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master~");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/~master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "~refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master:");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/:master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, ":refs/heads/master");
+
+/* ShellGlob */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master?");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/?master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "?refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master[");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/[master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "[refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master*");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/*master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "*refs/heads/master");
+
+/* ValidSpecialCharacters */
+ ensure_refname_normalized
+ (GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/!", "refs/heads/!");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\"", "refs/heads/\"");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/#", "refs/heads/#");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/$", "refs/heads/$");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/%", "refs/heads/%");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/&", "refs/heads/&");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/'", "refs/heads/'");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/(", "refs/heads/(");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/)", "refs/heads/)");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/+", "refs/heads/+");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/,", "refs/heads/,");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/-", "refs/heads/-");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/;", "refs/heads/;");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/<", "refs/heads/<");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/=", "refs/heads/=");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/>", "refs/heads/>");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/@", "refs/heads/@");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/]", "refs/heads/]");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/_", "refs/heads/_");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/`", "refs/heads/`");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/{", "refs/heads/{");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/|", "refs/heads/|");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/}", "refs/heads/}");
+
+ // This is valid on UNIX, but not on Windows
+ // hence we make in invalid due to non-portability
+ //
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\\");
+
+/* UnicodeNames */
+ /*
+ * Currently this fails.
+ * ensure_refname_normalized(GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m");
+ */
+
+/* RefLogQueryIsValidRef */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1}");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1.hour.ago}");
+}
+
+void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_version(void)
+{
+ char buffer_out[21];
+
+ cl_git_pass(git_reference_normalize_name(
+ buffer_out, 21, "refs//heads///long///name", GIT_REF_FORMAT_NORMAL));
+ cl_git_fail(git_reference_normalize_name(
+ buffer_out, 20, "refs//heads///long///name", GIT_REF_FORMAT_NORMAL));
+}
+
+#define ONE_LEVEL_AND_REFSPEC \
+ GIT_REF_FORMAT_ALLOW_ONELEVEL \
+ | GIT_REF_FORMAT_REFSPEC_PATTERN
+
+void test_refs_normalize__refspec_pattern(void)
+{
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/*foo/bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/foo*/bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/f*o/bar");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "FOO", "FOO");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/bar", "foo/bar");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "foo/bar", "foo/bar");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*/foo", "*/foo");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "*/foo", "*/foo");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/*/bar", "foo/*/bar");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "foo/*/bar", "foo/*/bar");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "*", "*");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/*/*");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "foo/*/*");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*/foo/*");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "*/foo/*");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*/*/foo");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "*/*/foo");
+}
diff --git a/tests/refs/overwrite.c b/tests/refs/overwrite.c
new file mode 100644
index 000000000..ebe72069c
--- /dev/null
+++ b/tests/refs/overwrite.c
@@ -0,0 +1,136 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static const char *ref_name = "refs/heads/other";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_branch_name = "refs/heads/branch";
+static const char *ref_test_name = "refs/heads/test";
+
+static git_repository *g_repo;
+
+void test_refs_overwrite__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_overwrite__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_overwrite__symbolic(void)
+{
+ // Overwrite an existing symbolic reference
+ git_reference *ref, *branch_ref;
+
+ /* The target needds to exist and we need to check the name has changed */
+ cl_git_pass(git_reference_symbolic_create(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0));
+ cl_git_pass(git_reference_symbolic_create(&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_equal_s(git_reference_symbolic_target(ref), ref_branch_name);
+ git_reference_free(ref);
+
+ /* Ensure we can't create it unless we force it to */
+ cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+ cl_git_pass(git_reference_symbolic_create(&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_equal_s(git_reference_symbolic_target(ref), ref_master_name);
+
+ git_reference_free(ref);
+ git_reference_free(branch_ref);
+}
+
+void test_refs_overwrite__object_id(void)
+{
+ // Overwrite an existing object id reference
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create it */
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ 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_target(ref));
+ git_reference_free(ref);
+
+ /* Ensure we can't overwrite unless we force it */
+ cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ cl_git_pass(git_reference_create(&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_target(ref)));
+
+ git_reference_free(ref);
+}
+
+void test_refs_overwrite__object_id_with_symbolic(void)
+{
+ // Overwrite an existing object id reference with a symbolic one
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ git_reference_free(ref);
+ cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+ cl_git_pass(git_reference_symbolic_create(&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_equal_s(git_reference_symbolic_target(ref), ref_master_name);
+
+ git_reference_free(ref);
+}
+
+void test_refs_overwrite__symbolic_with_object_id(void)
+{
+ // Overwrite an existing symbolic reference with an object id one
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+ git_reference_free(ref);
+ /* It shouldn't overwrite unless we tell it to */
+ cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ cl_git_pass(git_reference_create(&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_target(ref), &id));
+
+ git_reference_free(ref);
+}
diff --git a/tests/refs/pack.c b/tests/refs/pack.c
new file mode 100644
index 000000000..849a052aa
--- /dev/null
+++ b/tests/refs/pack.c
@@ -0,0 +1,105 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "git2/refdb.h"
+#include "reflog.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+
+static git_repository *g_repo;
+
+void test_refs_pack__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_pack__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void packall(void)
+{
+ git_refdb *refdb;
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+}
+
+void test_refs_pack__empty(void)
+{
+ /* create a packfile for an empty folder */
+ git_buf temp_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
+ cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
+ git_buf_free(&temp_path);
+
+ packall();
+}
+
+void test_refs_pack__loose(void)
+{
+ /* create a packfile from all the loose refs in 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(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+ git_reference_free(reference);
+
+ /*
+ * We are now trying to pack also a loose reference
+ * called `points_to_blob`, to make sure we can properly
+ * pack weak tags
+ */
+ packall();
+
+ /* Ensure the packed-refs file exists */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE));
+ cl_assert(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(reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+
+ /* Ensure the known ref has been removed from the loose folder structure */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name));
+ cl_assert(!git_path_exists(temp_path.ptr));
+
+ git_reference_free(reference);
+ git_buf_free(&temp_path);
+}
+
+void test_refs_pack__symbolic(void)
+{
+ /* create a packfile from loose refs skipping symbolic refs */
+ int i;
+ git_oid head;
+ git_reference *ref;
+ char name[128];
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ /* make a bunch of references */
+
+ for (i = 0; i < 100; ++i) {
+ snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i);
+ cl_git_pass(git_reference_symbolic_create(
+ &ref, g_repo, name, "refs/heads/master", 0));
+ git_reference_free(ref);
+
+ snprintf(name, sizeof(name), "refs/heads/direct-%03d", i);
+ cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+ git_reference_free(ref);
+ }
+
+ packall();
+}
diff --git a/tests/refs/peel.c b/tests/refs/peel.c
new file mode 100644
index 000000000..f2fb6e259
--- /dev/null
+++ b/tests/refs/peel.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+static git_repository *g_peel_repo;
+
+void test_refs_peel__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&g_peel_repo, cl_fixture("peeled.git")));
+}
+
+void test_refs_peel__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ git_repository_free(g_peel_repo);
+ g_peel_repo = NULL;
+}
+
+static void assert_peel_generic(
+ git_repository *repo,
+ const char *ref_name,
+ git_otype requested_type,
+ const char* expected_sha,
+ git_otype expected_type)
+{
+ git_oid expected_oid;
+ git_reference *ref;
+ git_object *peeled;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, ref_name));
+
+ cl_git_pass(git_reference_peel(&peeled, ref, requested_type));
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
+ cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
+
+ cl_assert_equal_i(expected_type, git_object_type(peeled));
+
+ git_object_free(peeled);
+ git_reference_free(ref);
+}
+
+static void assert_peel(
+ const char *ref_name,
+ git_otype requested_type,
+ const char* expected_sha,
+ git_otype expected_type)
+{
+ assert_peel_generic(g_repo, ref_name, requested_type,
+ expected_sha, expected_type);
+}
+
+static void assert_peel_error(int error, const char *ref_name, git_otype requested_type)
+{
+ git_reference *ref;
+ git_object *peeled;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+
+ cl_assert_equal_i(error, git_reference_peel(&peeled, ref, requested_type));
+
+ git_reference_free(ref);
+}
+
+void test_refs_peel__can_peel_a_tag(void)
+{
+ assert_peel("refs/tags/test", GIT_OBJ_TAG,
+ "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", GIT_OBJ_TAG);
+ assert_peel("refs/tags/test", GIT_OBJ_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+ assert_peel("refs/tags/test", GIT_OBJ_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
+ assert_peel("refs/tags/point_to_blob", GIT_OBJ_BLOB,
+ "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJ_BLOB);
+}
+
+void test_refs_peel__can_peel_a_branch(void)
+{
+ assert_peel("refs/heads/master", GIT_OBJ_COMMIT,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJ_COMMIT);
+ assert_peel("refs/heads/master", GIT_OBJ_TREE,
+ "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJ_TREE);
+}
+
+void test_refs_peel__can_peel_a_symbolic_reference(void)
+{
+ assert_peel("HEAD", GIT_OBJ_COMMIT,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJ_COMMIT);
+ assert_peel("HEAD", GIT_OBJ_TREE,
+ "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJ_TREE);
+}
+
+void test_refs_peel__cannot_peel_into_a_non_existing_target(void)
+{
+ assert_peel_error(GIT_ENOTFOUND, "refs/tags/point_to_blob", GIT_OBJ_TAG);
+}
+
+void test_refs_peel__can_peel_into_any_non_tag_object(void)
+{
+ assert_peel("refs/heads/master", GIT_OBJ_ANY,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJ_COMMIT);
+ assert_peel("refs/tags/point_to_blob", GIT_OBJ_ANY,
+ "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJ_BLOB);
+ assert_peel("refs/tags/test", GIT_OBJ_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+}
+
+void test_refs_peel__can_peel_fully_peeled_packed_refs(void)
+{
+ assert_peel_generic(g_peel_repo,
+ "refs/tags/tag-inside-tags", GIT_OBJ_ANY,
+ "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
+ GIT_OBJ_COMMIT);
+ assert_peel_generic(g_peel_repo,
+ "refs/foo/tag-outside-tags", GIT_OBJ_ANY,
+ "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
+ GIT_OBJ_COMMIT);
+}
diff --git a/tests/refs/read.c b/tests/refs/read.c
new file mode 100644
index 000000000..35cf17e9e
--- /dev/null
+++ b/tests/refs/read.c
@@ -0,0 +1,284 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist";
+static const char *head_tracker_sym_ref_name = "HEAD_TRACKER";
+static const char *current_head_target = "refs/heads/master";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+static const char *packed_head_name = "refs/heads/packed";
+static const char *packed_test_head_name = "refs/heads/packed-test";
+
+static git_repository *g_repo;
+
+void test_refs_read__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_read__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_refs_read__loose_tag(void)
+{
+ // lookup a loose tag reference
+ git_reference *reference;
+ git_object *object;
+ git_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(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_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_equal_s(ref_name_from_tag_name.ptr, loose_tag_ref_name);
+ git_buf_free(&ref_name_from_tag_name);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__nonexisting_tag(void)
+{
+ // lookup a loose tag reference that doesn't exist
+ git_reference *reference;
+
+ cl_git_fail(git_reference_lookup(&reference, g_repo, non_existing_tag_ref_name));
+
+ git_reference_free(reference);
+}
+
+
+void test_refs_read__symbolic(void)
+{
+ // lookup a symbolic reference
+ git_reference *reference, *resolved_ref;
+ git_object *object;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+ cl_assert(git_reference_type(reference) & GIT_REF_SYMBOLIC);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, GIT_HEAD_FILE);
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(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_refs_read__nested_symbolic(void)
+{
+ // lookup a nested symbolic reference
+ git_reference *reference, *resolved_ref;
+ git_object *object;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_SYMBOLIC);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, head_tracker_sym_ref_name);
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(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_refs_read__head_then_master(void)
+{
+ // lookup the HEAD and resolve the master branch
+ git_reference *reference, *resolved_ref, *comp_base_ref;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
+ cl_git_pass(git_reference_resolve(&comp_base_ref, reference));
+ git_reference_free(reference);
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target));
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+
+ git_reference_free(comp_base_ref);
+}
+
+void test_refs_read__master_then_head(void)
+{
+ // lookup the master branch and then the HEAD
+ git_reference *reference, *master_ref, *resolved_ref;
+
+ cl_git_pass(git_reference_lookup(&master_ref, g_repo, current_head_target));
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_git_pass(git_oid_cmp(git_reference_target(master_ref), git_reference_target(resolved_ref)));
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+ git_reference_free(master_ref);
+}
+
+
+void test_refs_read__packed(void)
+{
+ // lookup a packed reference
+ git_reference *reference;
+ git_object *object;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_OID);
+ cl_assert(reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, packed_head_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_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_refs_read__loose_first(void)
+{
+ // assure that a loose reference is looked up before a packed reference
+ git_reference *reference;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
+ git_reference_free(reference);
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_test_head_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_OID);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, packed_test_head_name);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__chomped(void)
+{
+ git_reference *test, *chomped;
+
+ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
+ cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped"));
+ cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(chomped)));
+
+ git_reference_free(test);
+ git_reference_free(chomped);
+}
+
+void test_refs_read__trailing(void)
+{
+ git_reference *test, *trailing;
+
+ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
+ cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing"));
+ cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(trailing)));
+ git_reference_free(trailing);
+ cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD"));
+
+ git_reference_free(test);
+ git_reference_free(trailing);
+}
+
+void test_refs_read__unfound_return_ENOTFOUND(void)
+{
+ git_reference *reference;
+ git_oid id;
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "TEST_MASTER"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/test/master"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/tags/test/master"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_name_to_id(&id, g_repo, "refs/tags/test/farther/master"));
+}
+
+static void assert_is_branch(const char *name, bool expected_branchness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_branchness, git_reference_is_branch(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void)
+{
+ assert_is_branch("refs/heads/master", true);
+ assert_is_branch("refs/heads/packed", true);
+ assert_is_branch("refs/remotes/test/master", false);
+ assert_is_branch("refs/tags/e90810b", false);
+}
+
+static void assert_is_tag(const char *name, bool expected_tagness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_tagness, git_reference_is_tag(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_tag(void)
+{
+ assert_is_tag("refs/tags/e90810b", true);
+ assert_is_tag("refs/tags/test", true);
+ assert_is_tag("refs/heads/packed", false);
+ assert_is_tag("refs/remotes/test/master", false);
+}
+
+void test_refs_read__invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *reference;
+ git_oid id;
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reference_lookup(&reference, g_repo, "refs/heads/Inv@{id"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reference_name_to_id(&id, g_repo, "refs/heads/Inv@{id"));
+}
diff --git a/tests/refs/ref_helpers.c b/tests/refs/ref_helpers.c
new file mode 100644
index 000000000..7676e65a7
--- /dev/null
+++ b/tests/refs/ref_helpers.c
@@ -0,0 +1,25 @@
+#include "git2/repository.h"
+#include "git2/refs.h"
+#include "common.h"
+#include "util.h"
+#include "buffer.h"
+#include "path.h"
+
+int reference_is_packed(git_reference *ref)
+{
+ git_buf ref_path = GIT_BUF_INIT;
+ int packed;
+
+ assert(ref);
+
+ if (git_buf_joinpath(&ref_path,
+ git_repository_path(git_reference_owner(ref)),
+ git_reference_name(ref)) < 0)
+ return -1;
+
+ packed = !git_path_isfile(ref_path.ptr);
+
+ git_buf_free(&ref_path);
+
+ return packed;
+}
diff --git a/tests/refs/ref_helpers.h b/tests/refs/ref_helpers.h
new file mode 100644
index 000000000..0ef55bfce
--- /dev/null
+++ b/tests/refs/ref_helpers.h
@@ -0,0 +1 @@
+int reference_is_packed(git_reference *ref);
diff --git a/tests/refs/reflog/drop.c b/tests/refs/reflog/drop.c
new file mode 100644
index 000000000..916bd9933
--- /dev/null
+++ b/tests/refs/reflog/drop.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+
+#include "reflog.h"
+
+static git_repository *g_repo;
+static git_reflog *g_reflog;
+static size_t entrycount;
+
+void test_refs_reflog_drop__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ git_reflog_read(&g_reflog, g_repo, "HEAD");
+ entrycount = git_reflog_entrycount(g_reflog);
+}
+
+void test_refs_reflog_drop__cleanup(void)
+{
+ git_reflog_free(g_reflog);
+ g_reflog = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_reflog_drop__dropping_a_non_exisiting_entry_from_the_log_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reflog_drop(g_reflog, entrycount, 0));
+
+ cl_assert_equal_sz(entrycount, git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_drop_an_entry(void)
+{
+ cl_assert(entrycount > 4);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 2, 0));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void)
+{
+ const git_reflog_entry *before_current;
+ const git_reflog_entry *after_current;
+ git_oid before_current_old_oid, before_current_cur_oid;
+
+ cl_assert(entrycount > 4);
+
+ before_current = git_reflog_entry_byindex(g_reflog, 1);
+
+ git_oid_cpy(&before_current_old_oid, &before_current->oid_old);
+ git_oid_cpy(&before_current_cur_oid, &before_current->oid_cur);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 1, 1));
+
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ after_current = git_reflog_entry_byindex(g_reflog, 0);
+
+ cl_assert_equal_i(0, git_oid_cmp(&before_current_old_oid, &after_current->oid_old));
+ cl_assert(0 != git_oid_cmp(&before_current_cur_oid, &after_current->oid_cur));
+}
+
+void test_refs_reflog_drop__can_drop_the_oldest_entry(void)
+{
+ const git_reflog_entry *entry;
+
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) != 0);
+}
+
+void test_refs_reflog_drop__can_drop_the_oldest_entry_and_rewrite_the_log_history(void)
+{
+ const git_reflog_entry *entry;
+
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
+}
+
+void test_refs_reflog_drop__can_drop_all_the_entries(void)
+{
+ cl_assert(--entrycount > 0);
+
+ do {
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+ } while (--entrycount > 0);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+
+ cl_assert_equal_i(0, (int)git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_persist_deletion_on_disk(void)
+{
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+ cl_git_pass(git_reflog_write(g_reflog));
+
+ git_reflog_free(g_reflog);
+
+ git_reflog_read(&g_reflog, g_repo, "HEAD");
+
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+}
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
new file mode 100644
index 000000000..bcd224270
--- /dev/null
+++ b/tests/refs/reflog/reflog.c
@@ -0,0 +1,209 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+
+static const char *new_ref = "refs/heads/test-reflog";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+#define commit_msg "commit: bla bla"
+
+static git_repository *g_repo;
+
+
+// helpers
+static void assert_signature(const git_signature *expected, const git_signature *actual)
+{
+ cl_assert(actual);
+ cl_assert_equal_s(expected->name, actual->name);
+ cl_assert_equal_s(expected->email, actual->email);
+ cl_assert(expected->when.offset == actual->when.offset);
+ cl_assert(expected->when.time == actual->when.time);
+}
+
+
+// Fixture setup and teardown
+void test_refs_reflog_reflog__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_reflog_reflog__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_appends(const git_signature *committer, const git_oid *oid)
+{
+ git_repository *repo2;
+ git_reference *lookedup_ref;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ /* Reopen a new instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+ /* Lookup the previously created branch */
+ cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
+
+ /* Read and parse the reflog for this branch */
+ cl_git_pass(git_reflog_read(&reflog, repo2, new_ref));
+ cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog));
+
+ entry = git_reflog_entry_byindex(reflog, 1);
+ assert_signature(committer, entry->committer);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+ cl_assert(entry->msg == NULL);
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ assert_signature(committer, entry->committer);
+ cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+ cl_assert_equal_s(commit_msg, entry->msg);
+
+ git_reflog_free(reflog);
+ git_repository_free(repo2);
+
+ git_reference_free(lookedup_ref);
+}
+
+void test_refs_reflog_reflog__append_then_read(void)
+{
+ /* write a reflog for a given reference and ensure it can be read back */
+ git_reference *ref;
+ git_oid oid;
+ git_signature *committer;
+ git_reflog *reflog;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&oid, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
+ git_reference_free(ref);
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, new_ref));
+
+ cl_git_fail(git_reflog_append(reflog, &oid, committer, "no inner\nnewline"));
+ cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
+ cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
+ cl_git_pass(git_reflog_write(reflog));
+ git_reflog_free(reflog);
+
+ assert_appends(committer, &oid);
+
+ git_signature_free(committer);
+}
+
+void test_refs_reflog_reflog__append_to_then_read(void)
+{
+ /* write a reflog for a given reference and ensure it can be read back */
+ git_reference *ref;
+ git_oid oid;
+ git_signature *committer;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&oid, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
+ git_reference_free(ref);
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ cl_git_fail(git_reflog_append_to(g_repo, new_ref, &oid, committer, "no inner\nnewline"));
+ cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, NULL));
+ cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, commit_msg "\n"));
+
+ assert_appends(committer, &oid);
+
+ git_signature_free(committer);
+}
+
+void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
+{
+ git_reference *master, *new_master;
+ git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
+
+ git_buf_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_puts(&moved_log_path, git_buf_cstr(&master_log_path));
+ git_buf_joinpath(&master_log_path, git_buf_cstr(&master_log_path), "refs/heads/master");
+ git_buf_joinpath(&moved_log_path, git_buf_cstr(&moved_log_path), "refs/moved");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&master_log_path)));
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&moved_log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0));
+ git_reference_free(master);
+
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path)));
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&moved_log_path)));
+
+ git_reference_free(new_master);
+ git_buf_free(&moved_log_path);
+ git_buf_free(&master_log_path);
+}
+
+static void assert_has_reflog(bool expected_result, const char *name)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, name));
+
+ cl_assert_equal_i(expected_result, git_reference_has_log(ref));
+
+ git_reference_free(ref);
+}
+
+void test_refs_reflog_reflog__reference_has_reflog(void)
+{
+ assert_has_reflog(true, "HEAD");
+ assert_has_reflog(true, "refs/heads/master");
+ assert_has_reflog(false, "refs/heads/subtrees");
+}
+
+void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
+{
+ git_reflog *reflog;
+ const char *refname = "refs/heads/subtrees";
+ git_buf subtrees_log_path = GIT_BUF_INIT;
+
+ git_buf_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname);
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&subtrees_log_path)));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, refname));
+
+ cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+ git_buf_free(&subtrees_log_path);
+}
+
+void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
+{
+ git_reference *master, *new_master;
+ git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
+ git_reflog *reflog;
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reflog_read(&reflog, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_reflog_write(reflog));
+
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0));
+ git_reference_free(master);
+
+ cl_git_fail(git_reflog_write(reflog));
+
+ git_reflog_free(reflog);
+ git_reference_free(new_master);
+ git_buf_free(&moved_log_path);
+ git_buf_free(&master_log_path);
+}
+
+void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
+}
diff --git a/tests/refs/rename.c b/tests/refs/rename.c
new file mode 100644
index 000000000..543bc4d62
--- /dev/null
+++ b/tests/refs/rename.c
@@ -0,0 +1,367 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+static const char *packed_head_name = "refs/heads/packed";
+static const char *packed_test_head_name = "refs/heads/packed-test";
+static const char *ref_one_name = "refs/heads/one/branch";
+static const char *ref_one_name_new = "refs/heads/two/branch";
+static const char *ref_two_name = "refs/heads/two";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_two_name_new = "refs/heads/two/two";
+
+static git_repository *g_repo;
+
+
+
+void test_refs_rename__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_rename__loose(void)
+{
+ // rename a loose reference
+ git_reference *looked_up_ref, *new_ref, *another_looked_up_ref;
+ git_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, git_repository_path(g_repo), new_name));
+ cl_assert(!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(reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, new_name, 0));
+ cl_assert_equal_s(new_ref->name, new_name);
+ git_reference_free(looked_up_ref);
+
+ /* ...It can't be looked-up with the old name... */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, loose_tag_ref_name));
+
+ /* ...but the new name works ok... */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, new_name));
+ cl_assert_equal_s(new_ref->name, new_name);
+
+ /* .. the new ref is loose... */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(reference_is_packed(new_ref) == 0);
+
+ /* ...and the ref can be found in the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(new_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, *new_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, git_repository_path(g_repo), packed_head_name));
+ cl_assert(!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(reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, brand_new_name, 0));
+ cl_assert_equal_s(new_ref->name, brand_new_name);
+ git_reference_free(looked_up_ref);
+
+ /* ...It can't be looked-up with the old name... */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_head_name));
+
+ /* ...but the new name works ok... */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, brand_new_name));
+ cl_assert_equal_s(another_looked_up_ref->name, brand_new_name);
+
+ /* .. the ref is no longer packed... */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(reference_is_packed(new_ref) == 0);
+
+ /* ...and the ref now happily lives in the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), brand_new_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(new_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, *renamed_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, git_repository_path(g_repo), packed_test_head_name));
+ cl_assert(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(reference_is_packed(another_looked_up_ref) == 0);
+ git_reference_free(another_looked_up_ref);
+
+ /* Lookup the reference to rename */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* Ensure it's packed */
+ cl_assert(reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, brand_new_name, 0));
+ git_reference_free(looked_up_ref);
+
+ /* Lookup the other reference */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's loose */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+
+ /* Ensure the other ref still exists on the file system */
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(renamed_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, *renamed_ref;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* Can not be renamed to the name of another existing reference. */
+ cl_git_fail(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 0));
+ git_reference_free(looked_up_ref);
+
+ /* Failure to rename it hasn't corrupted its state */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_head_name);
+
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__invalid_name(void)
+{
+ // can not rename a reference with an invalid name
+ git_reference *looked_up_ref, *renamed_ref;
+
+ /* An existing oid reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Can not be renamed with an invalid name. */
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_reference_rename(&renamed_ref, looked_up_ref, "Hello! I'm a very invalid name.", 0));
+
+ /* Can not be renamed outside of the refs hierarchy
+ * unless it's ALL_CAPS_AND_UNDERSCORES.
+ */
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(&renamed_ref, looked_up_ref, "i-will-sudo-you", 0));
+
+ /* Failure to rename it hasn't corrupted its state */
+ git_reference_free(looked_up_ref);
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
+
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__force_loose_packed(void)
+{
+ // can force-rename a packed reference with the name of an existing loose and packed reference
+ git_reference *looked_up_ref, *renamed_ref;
+ git_oid oid;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+ git_oid_cpy(&oid, git_reference_target(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 1));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
+ cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref)));
+ git_reference_free(looked_up_ref);
+
+ /* And that the previous one doesn't exist any longer */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+}
+
+void test_refs_rename__force_loose(void)
+{
+ // can force-rename a loose reference with the name of an existing loose reference
+ git_reference *looked_up_ref, *renamed_ref;
+ git_oid oid;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2"));
+ git_oid_cpy(&oid, git_reference_target(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, "refs/heads/test", 1));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test"));
+ cl_assert_equal_s(looked_up_ref->name, "refs/heads/test");
+ cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref)));
+ git_reference_free(looked_up_ref);
+
+ /* And that the previous one doesn't exist any longer */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2"));
+
+ git_reference_free(looked_up_ref);
+}
+
+
+void test_refs_rename__overwrite(void)
+{
+ // can not overwrite name of existing reference
+ git_reference *ref, *ref_one, *ref_one_new, *ref_two;
+ git_refdb *refdb;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_one, g_repo, ref_one_name, &id, 0));
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0));
+
+ /* Pack everything */
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+
+ /* Attempt to create illegal reference */
+ cl_git_fail(git_reference_create(&ref_one_new, g_repo, ref_one_name_new, &id, 0));
+
+ /* Illegal reference couldn't be created so this is supposed to fail */
+ cl_git_fail(git_reference_lookup(&ref_one_new, g_repo, ref_one_name_new));
+
+ git_reference_free(ref);
+ git_reference_free(ref_one);
+ git_reference_free(ref_one_new);
+ git_reference_free(ref_two);
+ git_refdb_free(refdb);
+}
+
+
+void test_refs_rename__prefix(void)
+{
+ // can be renamed to a new name prefixed with the old name
+ git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0));
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+
+ /* Can be rename to a new name starting with the old name. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name_new, 0));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name_new);
+ git_reference_free(looked_up_ref);
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+
+ git_reference_free(ref);
+ git_reference_free(ref_two);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__move_up(void)
+{
+ // can move a reference to a upper reference hierarchy
+ git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0));
+ git_reference_free(ref_two);
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+
+ /* Can be renamed upward the reference tree. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name, 0));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name);
+ git_reference_free(looked_up_ref);
+
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ git_reference_free(ref);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__propagate_eexists(void)
+{
+ git_reference *ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, packed_head_name));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_reference_rename(&new_ref, ref, packed_test_head_name, 0));
+
+ git_reference_free(ref);
+}
diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c
new file mode 100644
index 000000000..37d3981bb
--- /dev/null
+++ b/tests/refs/revparse.c
@@ -0,0 +1,813 @@
+#include "clar_libgit2.h"
+
+#include "git2/revparse.h"
+#include "buffer.h"
+#include "refs.h"
+#include "path.h"
+
+static git_repository *g_repo;
+static git_object *g_obj;
+
+/* Helpers */
+static void test_object_and_ref_inrepo(
+ const char *spec,
+ const char *expected_oid,
+ const char *expected_refname,
+ git_repository *repo,
+ bool assert_reference_retrieval)
+{
+ char objstr[64] = {0};
+ git_object *obj = NULL;
+ git_reference *ref = NULL;
+ int error;
+
+ error = git_revparse_ext(&obj, &ref, repo, spec);
+
+ if (expected_oid != NULL) {
+ cl_assert_equal_i(0, error);
+ git_oid_fmt(objstr, git_object_id(obj));
+ cl_assert_equal_s(objstr, expected_oid);
+ } else
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ if (assert_reference_retrieval) {
+ if (expected_refname == NULL)
+ cl_assert(NULL == ref);
+ else
+ cl_assert_equal_s(expected_refname, git_reference_name(ref));
+ }
+
+ git_object_free(obj);
+ git_reference_free(ref);
+}
+
+static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo)
+{
+ test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false);
+}
+
+static void test_id_inrepo(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_mode_t expected_flags,
+ git_repository *repo)
+{
+ git_revspec revspec;
+ int error = git_revparse(&revspec, repo, spec);
+
+ if (expected_left) {
+ char str[64] = {0};
+ cl_assert_equal_i(0, error);
+ git_oid_fmt(str, git_object_id(revspec.from));
+ cl_assert_equal_s(str, expected_left);
+ git_object_free(revspec.from);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+ }
+
+ if (expected_right) {
+ char str[64] = {0};
+ git_oid_fmt(str, git_object_id(revspec.to));
+ cl_assert_equal_s(str, expected_right);
+ git_object_free(revspec.to);
+ }
+
+ if (expected_flags)
+ cl_assert_equal_i(expected_flags, revspec.flags);
+}
+
+static void test_object(const char *spec, const char *expected_oid)
+{
+ test_object_inrepo(spec, expected_oid, g_repo);
+}
+
+static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname)
+{
+ test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true);
+}
+
+static void test_rangelike(const char *rangelike,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_mode_t expected_revparseflags)
+{
+ char objstr[64] = {0};
+ git_revspec revspec;
+ int error;
+
+ error = git_revparse(&revspec, g_repo, rangelike);
+
+ if (expected_left != NULL) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(revspec.flags, expected_revparseflags);
+ git_oid_fmt(objstr, git_object_id(revspec.from));
+ cl_assert_equal_s(objstr, expected_left);
+ git_oid_fmt(objstr, git_object_id(revspec.to));
+ cl_assert_equal_s(objstr, expected_right);
+ } else
+ cl_assert(error != 0);
+
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+}
+
+
+static void test_id(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_mode_t expected_flags)
+{
+ test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
+}
+
+void test_refs_revparse__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_revparse__cleanup(void)
+{
+ git_repository_free(g_repo);
+}
+
+void test_refs_revparse__nonexistant_object(void)
+{
+ test_object("this-does-not-exist", NULL);
+ test_object("this-does-not-exist^1", NULL);
+ test_object("this-does-not-exist~2", NULL);
+}
+
+static void assert_invalid_single_spec(const char *invalid_spec)
+{
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC, git_revparse_single(&g_obj, g_repo, invalid_spec));
+}
+
+void test_refs_revparse__invalid_reference_name(void)
+{
+ assert_invalid_single_spec("this doesn't make sense");
+ assert_invalid_single_spec("Inv@{id");
+ assert_invalid_single_spec("");
+}
+
+void test_refs_revparse__shas(void)
+{
+ test_object("c47800c7266a2be04c571c04d5a6614691ea99bd", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("c47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+}
+
+void test_refs_revparse__head(void)
+{
+ test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD^0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__full_refs(void)
+{
+ test_object("refs/heads/master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("refs/heads/test", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("refs/tags/test", "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+}
+
+void test_refs_revparse__partial_refs(void)
+{
+ test_object("point_to_blob", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("packed-test", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("br2", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+}
+
+void test_refs_revparse__describe_output(void)
+{
+ test_object("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("not-good", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__nth_parent(void)
+{
+ assert_invalid_single_spec("be3563a^-1");
+ assert_invalid_single_spec("^");
+ assert_invalid_single_spec("be3563a^{tree}^");
+ assert_invalid_single_spec("point_to_blob^{blob}^");
+ assert_invalid_single_spec("this doesn't make sense^1");
+
+ test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("be3563a^^", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("be3563a^{commit}^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+
+ test_object("be3563a^42", NULL);
+}
+
+void test_refs_revparse__not_tag(void)
+{
+ test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master^{tree}^{}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("tags/e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("e908^{}", "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+void test_refs_revparse__to_type(void)
+{
+ assert_invalid_single_spec("wrapped_tag^{trip}");
+ test_object("point_to_blob^{commit}", NULL);
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}"));
+
+ test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("point_to_blob^{blob}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("master^{commit}^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__linear_history(void)
+{
+ assert_invalid_single_spec("~");
+ test_object("foo~bar", NULL);
+
+ assert_invalid_single_spec("master~bar");
+ assert_invalid_single_spec("master~-1");
+ assert_invalid_single_spec("master~0bar");
+ assert_invalid_single_spec("this doesn't make sense~2");
+ assert_invalid_single_spec("be3563a^{tree}~");
+ assert_invalid_single_spec("point_to_blob^{blob}~");
+
+ test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~~", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+}
+
+void test_refs_revparse__chaining(void)
+{
+ assert_invalid_single_spec("master@{0}@{0}");
+ assert_invalid_single_spec("@{u}@{-1}");
+ assert_invalid_single_spec("@{-1}@{-1}");
+ assert_invalid_single_spec("@{-3}@{0}");
+
+ test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("@{-1}@{0}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object("@{-4}@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^^2^", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479");
+ test_object("master^^1^2^1", NULL);
+}
+
+void test_refs_revparse__upstream(void)
+{
+ assert_invalid_single_spec("e90810b@{u}");
+ assert_invalid_single_spec("refs/tags/e90810b@{u}");
+ test_object("refs/heads/e90810b@{u}", NULL);
+
+ test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("refs/heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__ordinal(void)
+{
+ assert_invalid_single_spec("master@{-2}");
+
+ /* TODO: make the test below actually fail
+ * cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}"));
+ */
+
+ test_object("nope@{0}", NULL);
+ test_object("master@{31415}", NULL);
+ test_object("@{1000}", NULL);
+ test_object("@{2}", NULL);
+
+ test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("refs/heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__previous_head(void)
+{
+ assert_invalid_single_spec("@{-xyz}");
+ assert_invalid_single_spec("@{-0}");
+ assert_invalid_single_spec("@{-1b}");
+
+ test_object("@{-42}", NULL);
+
+ test_object("@{-2}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("@{-1}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+}
+
+static void create_fake_stash_reference_and_reflog(git_repository *repo)
+{
+ git_reference *master, *new_master;
+ git_buf log_path = GIT_BUF_INIT;
+
+ git_buf_joinpath(&log_path, git_repository_path(repo), "logs/refs/fakestash");
+
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0));
+ git_reference_free(master);
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
+
+ git_buf_free(&log_path);
+ git_reference_free(new_master);
+}
+
+void test_refs_revparse__reflog_of_a_ref_under_refs(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("refs/fakestash", NULL, repo);
+
+ create_fake_stash_reference_and_reflog(repo);
+
+ /*
+ * $ git reflog -1 refs/fakestash
+ * a65fedf refs/fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 refs/fakestash@{0}
+ * a65fedf refs/fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 fakestash
+ * a65fedf fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 fakestash@{0}
+ * a65fedf fakestash@{0}: commit: checking in
+ */
+ test_object_inrepo("refs/fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("refs/fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_revparse__revwalk(void)
+{
+ test_object("master^{/not found in any commit}", NULL);
+ test_object("master^{/merge}", NULL);
+ assert_invalid_single_spec("master^{/((}");
+
+ test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+}
+
+void test_refs_revparse__date(void)
+{
+ /*
+ * $ git reflog HEAD --date=iso
+ * a65fedf HEAD@{2012-04-30 08:23:41 -0900}: checkout: moving from br2 to master
+ * a4a7dce HEAD@{2012-04-30 08:23:37 -0900}: commit: checking in
+ * c47800c HEAD@{2012-04-30 08:23:28 -0900}: checkout: moving from master to br2
+ * a65fedf HEAD@{2012-04-30 08:23:23 -0900}: commit:
+ * be3563a HEAD@{2012-04-30 10:22:43 -0700}: clone: from /Users/ben/src/libgit2/tes
+ *
+ * $ git reflog HEAD --date=raw
+ * a65fedf HEAD@{1335806621 -0900}: checkout: moving from br2 to master
+ * a4a7dce HEAD@{1335806617 -0900}: commit: checking in
+ * c47800c HEAD@{1335806608 -0900}: checkout: moving from master to br2
+ * a65fedf HEAD@{1335806603 -0900}: commit:
+ * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour
+ */
+ test_object("HEAD@{10 years ago}", NULL);
+
+ test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog master --date=iso
+ * a65fedf master@{2012-04-30 09:23:23 -0800}: commit: checking in
+ * be3563a master@{2012-04-30 09:22:43 -0800}: clone: from /Users/ben/src...
+ *
+ * $ git reflog master --date=raw
+ * a65fedf master@{1335806603 -0800}: commit: checking in
+ * be3563a master@{1335806563 -0800}: clone: from /Users/ben/src/libgit2/tests/reso
+ */
+
+
+ /*
+ * $ git reflog -1 "master@{2012-04-30 17:22:42 +0000}"
+ * warning: Log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800.
+ */
+ test_object("master@{2012-04-30 17:22:42 +0000}", NULL);
+ test_object("master@{2012-04-30 09:22:42 -0800}", NULL);
+
+ /*
+ * $ git reflog -1 "master@{2012-04-30 17:22:43 +0000}"
+ * be3563a master@{Mon Apr 30 09:22:43 2012 -0800}: clone: from /Users/ben/src/libg
+ */
+ test_object("master@{2012-04-30 17:22:43 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{2012-04-30 09:22:43 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ /*
+ * $ git reflog -1 "master@{2012-4-30 09:23:27 -0800}"
+ * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
+ */
+ test_object("master@{2012-4-30 09:23:27 -0800}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog -1 master@{2012-05-03}
+ * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
+ */
+ test_object("master@{2012-05-03}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog -1 "master@{1335806603}"
+ * a65fedf
+ *
+ * $ git reflog -1 "master@{1335806602}"
+ * be3563a
+ */
+ test_object("master@{1335806603}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master@{1335806602}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__colon(void)
+{
+ assert_invalid_single_spec(":/");
+ assert_invalid_single_spec("point_to_blob:readme.txt");
+ cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); /* Not implemented */
+
+ test_object(":/not found in any commit", NULL);
+ test_object("subtrees:ab/42.txt", NULL);
+ test_object("subtrees:ab/4.txt/nope", NULL);
+ test_object("subtrees:nope", NULL);
+ test_object("test/master^1:branch_file.txt", NULL);
+
+ /* From tags */
+ test_object("test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("tags/test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("tags/e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+
+ /* From commits */
+ test_object("a65f:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+
+ /* From trees */
+ test_object("a65f^{tree}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+ test_object("944c:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+
+ /* Retrieving trees */
+ test_object("master:", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("subtrees:", "ae90f12eea699729ed24555e40b9fd669da12a12");
+ test_object("subtrees:ab", "f1425cef211cc08caa31e7b545ffb232acb098c3");
+ test_object("subtrees:ab/", "f1425cef211cc08caa31e7b545ffb232acb098c3");
+
+ /* Retrieving blobs */
+ test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f");
+ test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b");
+ test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
+ test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
+ test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9");
+ test_object("test/master^2:branch_file.txt", "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+ test_object("test/master@{1}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+}
+
+void test_refs_revparse__disambiguation(void)
+{
+ /*
+ * $ git show e90810b
+ * tag e90810b
+ * Tagger: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 12 03:59:17 2010 +0200
+ *
+ * This is a very simple tag.
+ *
+ * commit e90810b8df3e80c413d903f631643c716887138d
+ * Author: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 5 18:42:20 2010 +0200
+ *
+ * Test commit 2
+ *
+ * diff --git a/readme.txt b/readme.txt
+ * index 6336846..0266163 100644
+ * --- a/readme.txt
+ * +++ b/readme.txt
+ * @@ -1 +1,2 @@
+ * Testing a readme.txt
+ * +Now we add a single line here
+ *
+ * $ git show-ref e90810b
+ * 7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+ *
+ */
+ test_object("e90810b", "7b4384978d2493e851f9cca7858815fac9b10980");
+
+ /*
+ * $ git show e90810
+ * commit e90810b8df3e80c413d903f631643c716887138d
+ * Author: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 5 18:42:20 2010 +0200
+ *
+ * Test commit 2
+ *
+ * diff --git a/readme.txt b/readme.txt
+ * index 6336846..0266163 100644
+ * --- a/readme.txt
+ * +++ b/readme.txt
+ * @@ -1 +1,2 @@
+ * Testing a readme.txt
+ * +Now we add a single line here
+ */
+ test_object("e90810", "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void)
+{
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90"));
+}
+
+/*
+ * $ echo "aabqhq" | git hash-object -t blob --stdin
+ * dea509d0b3cb8ee0650f6ca210bc83f4678851ba
+ *
+ * $ echo "aaazvc" | git hash-object -t blob --stdin
+ * dea509d097ce692e167dfc6a48a7a280cc5e877e
+ */
+void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_object *obj;
+
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_mkfile("testrepo/one.txt", "aabqhq\n");
+ cl_git_mkfile("testrepo/two.txt", "aaazvc\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "one.txt"));
+ cl_git_pass(git_index_add_bypath(index, "two.txt"));
+
+ cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS);
+
+ cl_git_pass(git_revparse_single(&obj, repo, "dea509d09"));
+
+ git_object_free(obj);
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_revparse__issue_994(void)
+{
+ git_repository *repo;
+ git_reference *head, *with_at;
+ git_object *target;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
+
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_create(
+ &with_at,
+ repo,
+ "refs/remotes/origin/bim_with_3d@11296",
+ git_reference_target(head),
+ 0));
+
+ cl_git_pass(git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
+ git_object_free(target);
+
+ cl_git_pass(git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
+ git_object_free(target);
+
+ git_reference_free(with_at);
+ git_reference_free(head);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse blah-7-gc47800c
+ * c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch blah-7-gc47800c HEAD~3
+ *
+ * $ git rev-parse blah-7-gc47800c
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_branch_before_described_tag(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_HEXSZ + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
+
+ test_object_inrepo("blah-7-gc47800c", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch a65fedf39aefe402d3bb6e24df4d4f5fe4547750 HEAD~3
+ *
+ * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ *
+ * $ git rev-parse heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_sha_before_branch(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_HEXSZ + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
+
+ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse c47800
+ * c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch c47800 HEAD~3
+ *
+ * $ git rev-parse c47800
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_HEXSZ + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("c47800", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
+
+ test_object_inrepo("c47800", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_refs_revparse__range(void)
+{
+ assert_invalid_single_spec("be3563a^1..be3563a");
+
+ test_rangelike("be3563a^1..be3563a",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ GIT_REVPARSE_RANGE);
+
+ test_rangelike("be3563a^1...be3563a",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+ test_rangelike("be3563a^1.be3563a", NULL, NULL, 0);
+}
+
+void test_refs_revparse__parses_range_operator(void)
+{
+ test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVPARSE_SINGLE);
+ test_id("HEAD~3..HEAD",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE);
+
+ test_id("HEAD~3...HEAD",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+}
+
+void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
+{
+ test_object_and_ref(
+ "master@{upstream}",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ "refs/remotes/test/master");
+
+ test_object_and_ref(
+ "@{-1}",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ "refs/heads/br2");
+}
+
+void test_refs_revparse__ext_can_expand_short_reference_names(void)
+{
+ test_object_and_ref(
+ "master",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "refs/heads/master");
+
+ test_object_and_ref(
+ "HEAD",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "refs/heads/master");
+
+ test_object_and_ref(
+ "tags/test",
+ "b25fa35b38051e4ae45d4222e795f9df2e43f1d1",
+ "refs/tags/test");
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_revision(void)
+{
+ test_object_and_ref(
+ "HEAD~3",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ NULL);
+
+ test_object_and_ref(
+ "HEAD~0",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL);
+
+ test_object_and_ref(
+ "HEAD^0",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL);
+
+ test_object_and_ref(
+ "@{-1}@{0}",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ NULL);
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_tree_content(void)
+{
+ test_object_and_ref(
+ "tags/test:readme.txt",
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf",
+ NULL);
+}
diff --git a/tests/refs/setter.c b/tests/refs/setter.c
new file mode 100644
index 000000000..6d875f9b6
--- /dev/null
+++ b/tests/refs/setter.c
@@ -0,0 +1,99 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "git2/refs.h"
+
+static const char *ref_name = "refs/heads/other";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_test_name = "refs/heads/test";
+
+static git_repository *g_repo;
+
+void test_refs_setter__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_setter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_setter__update_direct(void)
+{
+ git_reference *ref, *test_ref, *new_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(test_ref) == GIT_REF_OID);
+
+ cl_git_pass(git_reference_set_target(&new_ref, test_ref, &id));
+
+ git_reference_free(test_ref);
+ git_reference_free(new_ref);
+
+ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(test_ref) == GIT_REF_OID);
+ cl_assert(git_oid_cmp(&id, git_reference_target(test_ref)) == 0);
+ git_reference_free(test_ref);
+}
+
+void test_refs_setter__update_symbolic(void)
+{
+ git_reference *head, *new_head;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC);
+ cl_assert(strcmp(git_reference_symbolic_target(head), ref_master_name) == 0);
+
+ cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name));
+ git_reference_free(new_head);
+ git_reference_free(head);
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC);
+ cl_assert(strcmp(git_reference_symbolic_target(head), ref_test_name) == 0);
+ git_reference_free(head);
+}
+
+void test_refs_setter__cant_update_direct_with_symbolic(void)
+{
+ // Overwrite an existing object id reference with a symbolic one
+ git_reference *ref, *new;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name));
+
+ git_reference_free(ref);
+}
+
+void test_refs_setter__cant_update_symbolic_with_direct(void)
+{
+ // Overwrite an existing symbolic reference with an object id one
+ git_reference *ref, *new;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+
+ /* Can't set an OID on a direct ref */
+ cl_git_fail(git_reference_set_target(&new, ref, &id));
+
+ git_reference_free(ref);
+}
diff --git a/tests/refs/shorthand.c b/tests/refs/shorthand.c
new file mode 100644
index 000000000..f995d26ca
--- /dev/null
+++ b/tests/refs/shorthand.c
@@ -0,0 +1,27 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+void assert_shorthand(git_repository *repo, const char *refname, const char *shorthand)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, refname));
+ cl_assert_equal_s(git_reference_shorthand(ref), shorthand);
+ git_reference_free(ref);
+}
+
+void test_refs_shorthand__0(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+
+ assert_shorthand(repo, "refs/heads/master", "master");
+ assert_shorthand(repo, "refs/tags/test", "test");
+ assert_shorthand(repo, "refs/remotes/test/master", "test/master");
+ assert_shorthand(repo, "refs/notes/fanout", "notes/fanout");
+
+ git_repository_free(repo);
+}
diff --git a/tests/refs/unicode.c b/tests/refs/unicode.c
new file mode 100644
index 000000000..b56012869
--- /dev/null
+++ b/tests/refs/unicode.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+
+void test_refs_unicode__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_unicode__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_unicode__create_and_lookup(void)
+{
+ git_reference *ref0, *ref1, *ref2;
+ git_repository *repo2;
+
+ const char *REFNAME = "refs/heads/" "\303\205" "ngstr" "\303\266" "m";
+ const char *master = "refs/heads/master";
+
+ /* Create the reference */
+ cl_git_pass(git_reference_lookup(&ref0, repo, master));
+ cl_git_pass(git_reference_create(
+ &ref1, repo, REFNAME, git_reference_target(ref0), 0));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref1));
+ git_reference_free(ref0);
+
+ /* Lookup the reference in a different instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
+ cl_assert_equal_i(
+ 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+ git_reference_free(ref2);
+
+#if GIT_USE_ICONV
+ /* Lookup reference by decomposed unicode name */
+
+#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m"
+
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED));
+ cl_assert_equal_i(
+ 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+ git_reference_free(ref2);
+#endif
+
+ /* Cleanup */
+
+ git_reference_free(ref1);
+ git_repository_free(repo2);
+}
diff --git a/tests/refs/update.c b/tests/refs/update.c
new file mode 100644
index 000000000..205b526a2
--- /dev/null
+++ b/tests/refs/update.c
@@ -0,0 +1,26 @@
+#include "clar_libgit2.h"
+
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_update__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_update__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_update__updating_the_target_of_a_symref_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ git_reference_free(head);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(&head, g_repo, GIT_HEAD_FILE, "refs/heads/inv@{id", 1));
+}