Merge pull request #279 from carlosmn/detached-orphan Add detached and orphan convenience functions
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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 27c3138..ddadab4 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -219,6 +219,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
*
* An empty repository has just been initialized and contains
diff --git a/src/repository.c b/src/repository.c
index 7e3f26e..a99c301 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 3447f2b..b67d27f 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