diff --git a/include/git2/repository.h b/include/git2/repository.h index 27c3138f7..ddadab4da 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -218,6 +218,30 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo); */ GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare); +/** + * Check if a repository's HEAD is detached + * + * A repository's HEAD is detached when it points directly to a commit + * instead of a branch. + * + * @param repo Repo to test + * @return 1 if HEAD is detached, 0 if i'ts not; error code if there + * was an error. + */ +int git_repository_is_detached(git_repository *repo); + +/** + * Check if the current branch is an orphan + * + * An orphan branch is one named from HEAD but which doesn't exist in + * the refs namespace, because it doesn't have any commit to point to. + * + * @param repo Repo to test + * @return 1 if the current branch is an orphan, 0 if it's not; error + * code if therewas an error + */ +int git_repository_is_orphan(git_repository *repo); + /** * Check if a repository is empty * diff --git a/src/repository.c b/src/repository.c index 7e3f26e1b..a99c3018e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -752,6 +752,47 @@ cleanup: return git__rethrow(error, "Failed to (re)init the repository `%s`", path); } +int git_repository_is_detached(git_repository *repo) +{ + git_reference *ref; + int error; + size_t GIT_UNUSED(_size); + git_otype type; + + error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); + if (error < GIT_SUCCESS) + return error; + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) + return 0; + + error = git_odb_read_header(&_size, &type, repo->db, git_reference_oid(ref)); + if (error < GIT_SUCCESS) + return error; + + if (type != GIT_OBJ_COMMIT) + return git__throw(GIT_EOBJCORRUPTED, "HEAD is not a commit"); + + return 1; +} + +int git_repository_is_orphan(git_repository *repo) +{ + git_reference *ref; + int error; + + error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); + if (error < GIT_SUCCESS) + return error; + + if (git_reference_type(ref) == GIT_REF_OID) + return 0; + + error = git_reference_resolve(&ref, ref); + + return error == GIT_ENOTFOUND ? 1 : error; +} + int git_repository_is_empty(git_repository *repo) { git_reference *head, *branch; diff --git a/tests/t12-repo.c b/tests/t12-repo.c index 3447f2b22..b67d27f71 100644 --- a/tests/t12-repo.c +++ b/tests/t12-repo.c @@ -268,6 +268,46 @@ BEGIN_TEST(empty0, "test if a repository is empty or not") git_repository_free(repo_empty); END_TEST +BEGIN_TEST(detached0, "test if HEAD is detached") + git_repository *repo; + git_reference *ref; + git_oid oid; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + must_be_true(git_repository_is_detached(repo) == 0); + + /* detach the HEAD */ + git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"); + must_pass(git_reference_create_oid_f(&ref, repo, "HEAD", &oid)); + must_be_true(git_repository_is_detached(repo) == 1); + + /* take the reop back to it's original state */ + must_pass(git_reference_create_symbolic_f(&ref, repo, "HEAD", "refs/heads/master")); + must_be_true(git_repository_is_detached(repo) == 0); + + git_repository_free(repo); +END_TEST + +BEGIN_TEST(orphan0, "test if HEAD is orphan") + git_repository *repo; + git_reference *ref; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + must_be_true(git_repository_is_orphan(repo) == 0); + + /* orphan HEAD */ + must_pass(git_reference_create_symbolic_f(&ref, repo, "HEAD", "refs/heads/orphan")); + must_be_true(git_repository_is_orphan(repo) == 1); + + /* take the reop back to it's original state */ + must_pass(git_reference_create_symbolic_f(&ref, repo, "HEAD", "refs/heads/master")); + must_be_true(git_repository_is_orphan(repo) == 0); + + git_repository_free(repo); +END_TEST + #define DISCOVER_FOLDER TEST_RESOURCES "/discover.git" #define SUB_REPOSITORY_FOLDER_NAME "sub_repo" @@ -416,6 +456,8 @@ BEGIN_SUITE(repository) ADD_TEST(open1); ADD_TEST(open2); ADD_TEST(empty0); + ADD_TEST(detached0); + ADD_TEST(orphan0); ADD_TEST(discover0); END_SUITE