commit: introduce git_commit_nth_gen_ancestor()
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
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 640adf5..5b6da52 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -179,6 +179,25 @@ GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsig
GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned int n);
/**
+ * Get the commit object that is the <n>th generation ancestor
+ * of the named commit object, following only the first parents.
+ * The returned commit has to be freed by the caller.
+ *
+ * Passing `0` as the generation number returns another instance of the
+ * base commit itself.
+ *
+ * @param ancestor Pointer where to store the ancestor commit
+ * @param commit a previously loaded commit.
+ * @param n the requested generation
+ * @return 0 on success; GIT_ENOTFOUND if no matching ancestor exists
+ * or an error code
+ */
+int git_commit_nth_gen_ancestor(
+ git_commit **ancestor,
+ const git_commit *commit,
+ unsigned int n);
+
+/**
* Create a new commit in the repository using `git_object`
* instances as parameters.
*
diff --git a/src/commit.c b/src/commit.c
index 5acbbc3..32c4794 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -255,3 +255,37 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
return git_commit_lookup(parent, commit->object.repo, parent_oid);
}
+
+int git_commit_nth_gen_ancestor(
+ git_commit **ancestor,
+ const git_commit *commit,
+ unsigned int n)
+{
+ git_commit *current, *parent;
+ int error;
+
+ assert(ancestor && commit);
+
+ current = (git_commit *)commit;
+
+ if (n == 0)
+ return git_commit_lookup(
+ ancestor,
+ commit->object.repo,
+ git_object_id((const git_object *)commit));
+
+ while (n--) {
+ error = git_commit_parent(&parent, (git_commit *)current, 0);
+
+ if (current != commit)
+ git_commit_free(current);
+
+ if (error < 0)
+ return error;
+
+ current = parent;
+ }
+
+ *ancestor = parent;
+ return 0;
+}
diff --git a/tests-clar/commit/parent.c b/tests-clar/commit/parent.c
new file mode 100644
index 0000000..a007577
--- /dev/null
+++ b/tests-clar/commit/parent.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_commit *commit;
+
+void test_commit_parent__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_commit_parent__cleanup(void)
+{
+ git_commit_free(commit);
+ git_repository_free(_repo);
+}
+
+static void assert_nth_gen_parent(unsigned int gen, const char *expected_oid)
+{
+ git_commit *parent = NULL;
+ int error;
+
+ error = git_commit_nth_gen_ancestor(&parent, commit, gen);
+
+ if (expected_oid != NULL) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(0, git_oid_streq(git_commit_id(parent), expected_oid));
+ } else
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ git_commit_free(parent);
+}
+
+/*
+ * $ git show be35~0
+ * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ *
+ * $ git show be35~1
+ * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
+ *
+ * $ git show be35~3
+ * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
+ *
+ * $ git show be35~42
+ * fatal: ambiguous argument 'be35~42': unknown revision or path not in the working tree.
+ */
+void test_commit_parent__can_retrieve_nth_generation_parent(void)
+{
+ assert_nth_gen_parent(0, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ assert_nth_gen_parent(1, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ assert_nth_gen_parent(3, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ assert_nth_gen_parent(42, NULL);
+}