8000 commit: introduce `git_repository_commit_parents` · russell/libgit2@0572884 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0572884

Browse files
committed
commit: introduce git_repository_commit_parents
Emulating `git commit` is clunky - identifying your commit's parents is part of the problem. Provide a helper to give you the parents given the current repository state.
1 parent 198a1b2 commit 0572884

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

include/git2/commit.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,24 @@ typedef int (*git_commit_create_cb)(
541541
const git_commit *parents[],
542542
void *payload);
543543

544+
/** An array of commits returned from the library */
545+
typedef struct git_commitarray {
546+
git_commit **commits;
547+
size_t count;
548+
} git_commitarray;
549+
550+
/**
551+
* Free the commits contained in a commit array. This method should
552+
* be called on `git_commitarray` objects that were provided by the
553+
* library. Not doing so will result in a memory leak.
554+
*
555+
* This does not free the `git_commitarray` itself, since the library
556+
* will never allocate that object directly itself.
557+
*
558+
* @param array The git_commitarray that contains commits to free
559+
*/
560+
GIT_EXTERN(void) git_commitarray_dispose(git_commitarray *array);
561+
544562
/** @} */
545563
GIT_END_DECL
546564
#endif

include/git2/repository.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "types.h"
1212
#include "oid.h"
1313
#include "buffer.h"
14+
#include "commit.h"
1415

1516
/**
1617
* @file git2/repository.h
@@ -978,6 +979,17 @@ GIT_EXTERN(int) git_repository_set_ident(git_repository *repo, const char *name,
978979
*/
979980
GIT_EXTERN(git_oid_t) git_repository_oid_type(git_repository *repo);
980981

982+
/**
983+
* Gets the parents of the next commit, given the current repository state.
984+
* Generally, this is the HEAD commit, except when performing a merge, in
985+
* which case it is two or more commits.
986+
*
987+
* @param commits a `git_commitarray` that will contain the commit parents
988+
* @param repo the repository
989+
* @return 0 or an error code
990+
*/
991+
GIT_EXTERN(int) git_repository_commit_parents(git_commitarray *commits, git_repository *repo);
992+
981993
/** @} */
982994
GIT_END_DECL
983995
#endif

src/libgit2/commit.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,3 +1097,18 @@ int git_commit_author_with_mailmap(
10971097
{
10981098
return git_mailmap_resolve_signature(out, mailmap, commit->author);
10991099
}
1100+
1101+
void git_commitarray_dispose(git_commitarray *array)
1102+
{
1103+
size_t i;
1104+
1105+
if (array == NULL)
1106+
return;
1107+
1108+
for (i = 0; i < array->count; i++)
1109+
git_commit_free(array->commits[i]);
1110+
1111+
git__free(array->commits);
1112+
1113+
memset(array, 0, sizeof(*array));
1114+
}

src/libgit2/repository.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3874,3 +3874,65 @@ git_oid_t git_repository_oid_type(git_repository *repo)
38743874
{
38753875
return repo ? repo->oid_type : 0;
38763876
}
3877+
3878+
struct mergehead_data {
3879+
git_repository *repo;
3880+
git_vector *parents;
3881+
};
3882+
3883+
static int insert_mergehead(const git_oid *oid, void *payload)
3884+
{
3885+
git_commit *commit;
3886+
struct mergehead_data *data = (struct mergehead_data *)payload;
3887+
3888+
if (git_commit_lookup(&commit, data->repo, oid) < 0)
3889+
return -1;
3890+
3891+
return git_vector_insert(data->parents, commit);
3892+
}
3893+
3894+
int git_repository_commit_parents(git_commitarray *out, git_repository *repo)
3895+
{
3896+
git_commit *first_parent = NULL, *commit;
3897+
git_reference *head_ref = NULL;
3898+
git_vector parents = GIT_VECTOR_INIT;
3899+
struct mergehead_data data;
3900+
size_t i;
3901+
int error;
3902+
3903+
GIT_ASSERT_ARG(out && repo);
3904+
3905+
out->count = 0;
3906+
out->commits = NULL;
3907+
3908+
error = git_revparse_ext((git_object **)&first_parent, &head_ref, repo, "HEAD");
3909+
3910+
if (error != 0) {
3911+
if (error == GIT_ENOTFOUND)
3912+
error = 0;
3913+
3914+
goto done;
3915+
}
3916+
3917+
if ((error = git_vector_insert(&parents, first_parent)) < 0)
3918+
goto done;
3919+
3920+
data.repo = repo;
3921+
data.parents = &parents;
3922+
3923+
error = git_repository_mergehead_foreach(repo, insert_mergehead, &data);
3924+
3925+
if (error == GIT_ENOTFOUND)
3926+
error = 0;
3927+
else if (error != 0)
3928+
goto done;
3929+
3930+
out->commits = (git_commit **)git_vector_detach(&out->count, NULL, &parents);
3931+
3932+
done:
3933+
git_vector_foreach(&parents, i, commit)
3934+
git__free(commit);
3935+
3936+
git_reference_free(head_ref);
3937+
return error;
3938+
}

tests/libgit2/repo/getters.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,63 @@ void test_repo_getters__retrieving_the_odb_honors_the_refcount(void)
5151

5252
git_odb_free(odb);
5353
}
54+
55+
void test_repo_getters__commit_parents(void)
56+
{
57+
git_repository *repo;
58+
git_commitarray parents;
59+
git_oid first_parent;
60+
git_oid merge_parents[4];
61+
62+
git_oid__fromstr(&first_parent, "099fabac3a9ea935598528c27f866e34089c2eff", GIT_OID_SHA1);
63+
64+
/* A commit on a new repository has no parents */
65+
66+
cl_git_pass(git_repository_init(&repo, "new_repo", false));
67+
cl_git_pass(git_repository_commit_parents(&parents, repo));
68+
69+
cl_assert_equal_sz(0, parents.count);
70+
cl_assert_equal_p(NULL, parents.commits);
71+
72+
git_commitarray_dispose(&parents);
73+
git_repository_free(repo);
74+
75+
/* A standard commit has one parent */
76+
77+
repo = cl_git_sandbox_init("testrepo");
78+
cl_git_pass(git_repository_commit_parents(&parents, repo));
79+
80+
cl_assert_equal_sz(1, parents.count);
81+
cl_assert_equal_oid(&first_parent, git_commit_id(parents.commits[0]));
82+
83+
git_commitarray_dispose(&parents);
84+
85+
/* A merge commit has multiple parents */
86+
87+
cl_git_rewritefile("testrepo/.git/MERGE_HEAD",
88+
"8496071c1b46c854b31185ea97743be6a8774479\n"
89+
"5b5b025afb0b4c913b4c338a42934a3863bf3644\n"
90+
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045\n"
91+
"9fd738e8f7967c078dceed8190330fc8648ee56a\n");
92+
93+
cl_git_pass(git_repository_commit_parents(&parents, repo));
94+
95+
cl_assert_equal_sz(5, parents.count);
96+
97+
cl_assert_equal_oid(&first_parent, git_commit_id(parents.commits[0]));
98+
99+
git_oid__fromstr(&merge_parents[0], "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1);
100+
cl_assert_equal_oid(&merge_parents[0], git_commit_id(parents.commits[1]));
101+
git_oid__fromstr(&merge_parents[1], "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1);
102+
cl_assert_equal_oid(&merge_parents[1], git_commit_id(parents.commits[2]));
103+
git_oid__fromstr(&merge_parents[2], "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1);
104+
cl_assert_equal_oid(&merge_parents[2], git_commit_id(parents.commits[3]));
105+
git_oid__fromstr(&merge_parents[3], "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1);
106+
cl_assert_equal_oid(&merge_parents[3], git_commit_id(parents.commits[4]));
107+
108+
git_commitarray_dispose(&parents);
109+
110+
git_repository_free(repo);
111+
112+
cl_fixture_cleanup("testrepo");
113+
}

0 commit comments

Comments
 (0)
0