Commit 4ebe38bd589b7b99427b2822ca7a486c8bb3bf02

nulltoken 2012-09-15T22:07:09

repository: introduce git_repository_set_head_detached()

diff --git a/include/git2/repository.h b/include/git2/repository.h
index e68e054..59a7d2c 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -507,6 +507,26 @@ GIT_EXTERN(int) git_repository_hashfile(
     const char *as_path);
 
 /**
+ * Make the repository HEAD directly point to the Commit.
+ *
+ * If the provided committish cannot be found in the repository, the HEAD
+ * is unaltered and GIT_ENOTFOUND is returned.
+ *
+ * If the provided commitish cannot be peeled into a commit, the HEAD
+ * is unaltered and -1 is returned.
+ *
+ * Otherwise, the HEAD will eventually be detached and will directly point to
+ * the peeled Commit.
+ *
+ * @param repo Repository pointer
+ * @param commitish Object id of the Commit the HEAD should point to
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_head_detached(
+	git_repository* repo,
+	const git_oid* commitish);
+
+/**
  * Detach the HEAD.
  *
  * If the HEAD is already detached and points to a Commit, 0 is returned.
diff --git a/src/repository.c b/src/repository.c
index def9681..a3e7814 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1442,6 +1442,32 @@ cleanup:
 	return error;
 }
 
+int git_repository_set_head_detached(
+	git_repository* repo,
+	const git_oid* commitish)
+{
+	int error;
+	git_object *object,
+		*peeled = NULL;
+	git_reference *new_head = NULL;
+
+	assert(repo && commitish);
+
+	if ((error = git_object_lookup(&object, repo, commitish, GIT_OBJ_ANY)) < 0)
+		return error;
+
+	if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
+		goto cleanup;
+
+	error = git_reference_create_oid(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1);
+
+cleanup:
+	git_object_free(object);
+	git_object_free(peeled);
+	git_reference_free(new_head);
+	return error;
+}
+
 int git_repository_detach_head(
 	git_repository* repo)
 {
diff --git a/tests-clar/repo/head.c b/tests-clar/repo/head.c
index 74d2a1c..372cdd6 100644
--- a/tests-clar/repo/head.c
+++ b/tests-clar/repo/head.c
@@ -66,6 +66,40 @@ static void assert_head_is_correctly_detached(void)
 	git_reference_free(head);
 }
 
+void test_repo_head__set_head_detached_Return_ENOTFOUND_when_the_object_doesnt_exist(void)
+{
+	git_oid oid;
+
+	cl_git_pass(git_oid_fromstr(&oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid));
+}
+
+void test_repo_head__set_head_detached_Fails_when_the_object_isnt_a_commitish(void)
+{
+	git_object *blob;
+
+	cl_git_pass(git_revparse_single(&blob, repo, "point_to_blob"));
+
+	cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob)));
+
+	git_object_free(blob);
+}
+
+void test_repo_head__set_head_detached_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
+{
+	git_object *tag;
+
+	cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
+	cl_assert_equal_i(GIT_OBJ_TAG, git_object_type(tag));
+
+	cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
+
+	assert_head_is_correctly_detached();
+
+	git_object_free(tag);
+}
+
 void test_repo_head__detach_head_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
 {
 	cl_assert_equal_i(false, git_repository_head_detached(repo));