Commit cd4aa602c9d104f146cdfcb89399438214dc75d2

Vicent Marti 2014-01-28T10:50:04

Merge pull request #2083 from arthurschreiber/arthur/add-git_commit_descendant_of Add `git_commit_descendant_of`.

diff --git a/include/git2/graph.h b/include/git2/graph.h
index a271021..c997d8c 100644
--- a/include/git2/graph.h
+++ b/include/git2/graph.h
@@ -36,6 +36,20 @@ GIT_BEGIN_DECL
  */
 GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);
 
+
+/**
+ * Determine if a commit is the descendant of another commit.
+ *
+ * @param commit a previously loaded commit.
+ * @param ancestor a potential ancestor commit.
+ * @return 1 if the given commit is a descendant of the potential ancestor,
+ * 0 if not, error code otherwise.
+ */
+GIT_EXTERN(int) git_graph_descendant_of(
+	git_repository *repo,
+	const git_oid *commit,
+	const git_oid *ancestor);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/graph.c b/src/graph.c
index 277f588..f39af5e 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -176,3 +176,17 @@ on_error:
 	git_revwalk_free(walk);
 	return -1;
 }
+
+int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor)
+{
+	git_oid merge_base;
+	int error;
+
+	if (git_oid_equal(commit, ancestor))
+		return 0;
+
+	if ((error = git_merge_base(&merge_base, repo, commit, ancestor) < 0))
+		return error;
+
+	return git_oid_equal(&merge_base, ancestor);
+}
diff --git a/tests/graph/descendant_of.c b/tests/graph/descendant_of.c
new file mode 100644
index 0000000..ffdd0cf
--- /dev/null
+++ b/tests/graph/descendant_of.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_commit *commit;
+
+void test_graph_descendant_of__initialize(void)
+{
+	git_oid oid;
+
+	cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+
+	git_oid_fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+	cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+}
+
+void test_graph_descendant_of__cleanup(void)
+{
+	git_commit_free(commit);
+	commit = NULL;
+
+	git_repository_free(_repo);
+	_repo = NULL;
+}
+
+void test_graph_descendant_of__returns_correct_result(void)
+{
+	git_commit *other;
+
+	cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(commit)));
+
+
+	cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 1));
+
+	cl_assert_equal_i(1, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(other)));
+	cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(other), git_commit_id(commit)));
+
+	git_commit_free(other);
+
+
+	cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 3));
+
+	cl_assert_equal_i(1, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(other)));
+	cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(other), git_commit_id(commit)));
+
+	git_commit_free(other);
+
+}