Commit c4d8df27bc4d3c0ef5ea46f4953614955fc4bdc2

Vicent Martí 2012-11-23T15:19:47

Merge pull request #1097 from nulltoken/topic/head_tree_error Make `git_repository_head_tree()` return error codes

diff --git a/src/checkout.c b/src/checkout.c
index eff1481..c4e4f99 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -440,7 +440,9 @@ static int checkout_get_actions(
 	const git_index_entry *he;
 
 	/* if there is no HEAD, that's okay - we'll make an empty iterator */
-	(void)git_repository_head_tree(&head, data->repo);
+	if (((error = git_repository_head_tree(&head, data->repo)) < 0) &&
+		!(error == GIT_ENOTFOUND || error == GIT_EORPHANEDHEAD))
+			return -1;
 
 	if ((error = git_iterator_for_tree_range(
 			 &hiter, data->repo, head, pfx, pfx)) < 0)
diff --git a/src/object.c b/src/object.c
index 2e45eb8..3d95303 100644
--- a/src/object.c
+++ b/src/object.c
@@ -304,46 +304,6 @@ size_t git_object__size(git_otype type)
 	return git_objects_table[type].size;
 }
 
-int git_object__resolve_to_type(git_object **obj, git_otype type)
-{
-	int error = 0;
-	git_object *scan, *next;
-
-	if (type == GIT_OBJ_ANY)
-		return 0;
-
-	scan = *obj;
-
-	while (!error && scan && git_object_type(scan) != type) {
-
-		switch (git_object_type(scan)) {
-		case GIT_OBJ_COMMIT:
-		{
-			git_tree *tree = NULL;
-			error = git_commit_tree(&tree, (git_commit *)scan);
-			next = (git_object *)tree;
-			break;
-		}
-
-		case GIT_OBJ_TAG:
-			error = git_tag_target(&next, (git_tag *)scan);
-			break;
-
-		default:
-			giterr_set(GITERR_REFERENCE, "Object does not resolve to type");
-			error = -1;
-			next = NULL;
-			break;
-		}
-
-		git_object_free(scan);
-		scan = next;
-	}
-
-	*obj = scan;
-	return error;
-}
-
 static int peel_error(int error, const char* msg)
 {
 	giterr_set(GITERR_INVALID, "The given object cannot be peeled - %s", msg);
diff --git a/src/repository.c b/src/repository.c
index f82dc10..deab771 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1384,22 +1384,21 @@ int git_repository_is_bare(git_repository *repo)
 
 int git_repository_head_tree(git_tree **tree, git_repository *repo)
 {
-	git_oid head_oid;
-	git_object *obj = NULL;
+	git_reference *head;
+	git_object *obj;
+	int error;
 
-	if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
-		/* cannot resolve HEAD - probably brand new repo */
-		giterr_clear();
-		*tree = NULL;
-		return 0;
-	}
+	if ((error = git_repository_head(&head, repo)) < 0)
+		return error;
 
-	if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0 ||
-		git_object__resolve_to_type(&obj, GIT_OBJ_TREE) < 0)
-		return -1;
+	if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0)
+		goto cleanup;
 
 	*tree = (git_tree *)obj;
-	return 0;
+
+cleanup:
+	git_reference_free(head);
+	return error;
 }
 
 int git_repository_message(char *buffer, size_t len, git_repository *repo)
diff --git a/src/status.c b/src/status.c
index b8c15ef..b832cfe 100644
--- a/src/status.c
+++ b/src/status.c
@@ -121,8 +121,10 @@ int git_status_foreach_ext(
 		(err = git_repository__ensure_not_bare(repo, "status")) < 0)
 		return err;
 
-	if ((err = git_repository_head_tree(&head, repo)) < 0)
-		return err;
+	/* if there is no HEAD, that's okay - we'll make an empty iterator */
+	if (((err = git_repository_head_tree(&head, repo)) < 0) &&
+		!(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD))
+			return err;
 
 	memset(&diffopt, 0, sizeof(diffopt));
 	memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
diff --git a/tests-clar/repo/head.c b/tests-clar/repo/head.c
index 23d14d6..1c1e905 100644
--- a/tests-clar/repo/head.c
+++ b/tests-clar/repo/head.c
@@ -19,9 +19,9 @@ void test_repo_head__head_detached(void)
 {
 	git_reference *ref;
 
-	cl_assert(git_repository_head_detached(repo) == 0);
+	cl_git_pass(git_repository_head_detached(repo));
 
-	git_repository_detach_head(repo);
+	cl_git_pass(git_repository_detach_head(repo));
 
 	cl_assert_equal_i(true, git_repository_head_detached(repo));
 
@@ -36,7 +36,7 @@ void test_repo_head__head_orphan(void)
 {
 	git_reference *ref;
 
-	cl_assert(git_repository_head_orphan(repo) == 0);
+	cl_git_pass(git_repository_head_detached(repo));
 
 	make_head_orphaned(repo, NON_EXISTING_HEAD);
 
diff --git a/tests-clar/repo/headtree.c b/tests-clar/repo/headtree.c
new file mode 100644
index 0000000..0e7fe93
--- /dev/null
+++ b/tests-clar/repo/headtree.c
@@ -0,0 +1,53 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "repo_helpers.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_tree *tree;
+
+void test_repo_headtree__initialize(void)
+{
+	repo = cl_git_sandbox_init("testrepo.git");
+	tree = NULL;
+}
+
+void test_repo_headtree__cleanup(void)
+{
+	git_tree_free(tree);
+	cl_git_sandbox_cleanup();
+}
+
+void test_repo_headtree__can_retrieve_the_root_tree_from_a_detached_head(void)
+{
+	cl_git_pass(git_repository_detach_head(repo));
+
+	cl_git_pass(git_repository_head_tree(&tree, repo));
+
+	cl_assert(git_oid_streq(git_tree_id(tree), "az"));
+}
+
+void test_repo_headtree__can_retrieve_the_root_tree_from_a_non_detached_head(void)
+{
+	cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+	cl_git_pass(git_repository_head_tree(&tree, repo));
+
+	cl_assert(git_oid_streq(git_tree_id(tree), "az"));
+}
+
+void test_repo_headtree__when_head_is_orphaned_returns_EORPHANEDHEAD(void)
+{
+	make_head_orphaned(repo, NON_EXISTING_HEAD);
+
+	cl_assert_equal_i(true, git_repository_head_orphan(repo));
+
+	cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head_tree(&tree, repo));
+}
+
+void test_repo_headtree__when_head_is_missing_returns_ENOTFOUND(void)
+{
+	delete_head(repo);
+
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head_tree(&tree, repo));
+}