diff --git a/include/git2/graph.h b/include/git2/graph.h index a2710219e..c997d8ca9 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 277f588ca..f39af5ed5 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 000000000..ffdd0cfc8 --- /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); + +}