repository: introduce git_repository_set_head_detached()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
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));