commit bc0b2b45f9526f6c585958e4746589ebe2a0b7dd
parent 84a50ea1e2ec28ad1ca49d47cc511edf7cbc3e88
Author: Chris Bracken <chris@bracken.jp>
Date: Fri, 20 Feb 2026 12:15:38 +0900
commit: don't call git_oid_tostr() on NULL parentoid
Diffstat:
3 files changed, 93 insertions(+), 3 deletions(-)
diff --git a/BUILD.gn b/BUILD.gn
@@ -69,6 +69,7 @@ executable("gout_tests") {
sources = [
"src/format_tests.c",
"src/git/delta_tests.c",
+ "src/git/commit_tests.c",
"src/gout_tests.c",
"src/gout_tests_main.c",
"src/utils_tests.c",
diff --git a/src/git/commit.c b/src/git/commit.c
@@ -23,9 +23,13 @@ GitCommit* gitcommit_create(const git_oid* oid, git_repository* repo) {
// Get OID, parent OID.
commit->oid = ecalloc(GIT_OID_SHA1_HEXSIZE + 1, sizeof(char));
git_oid_tostr(commit->oid, GIT_OID_SHA1_HEXSIZE + 1, git_commit_id(gcommit));
- commit->parentoid = ecalloc(GIT_OID_SHA1_HEXSIZE + 1, sizeof(char));
- git_oid_tostr(commit->parentoid, GIT_OID_SHA1_HEXSIZE + 1,
- git_commit_parent_id(gcommit, 0));
+ if (git_commit_parentcount(gcommit) > 0) {
+ commit->parentoid = ecalloc(GIT_OID_SHA1_HEXSIZE + 1, sizeof(char));
+ git_oid_tostr(commit->parentoid, GIT_OID_SHA1_HEXSIZE + 1,
+ git_commit_parent_id(gcommit, 0));
+ } else {
+ commit->parentoid = NULL;
+ }
// Set commit summary, message.
const char* summary = git_commit_summary(gcommit);
diff --git a/src/git/commit_tests.c b/src/git/commit_tests.c
@@ -0,0 +1,85 @@
+#include <git2.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "git/internal.h"
+#include "utest.h"
+
+#include <ftw.h>
+
+struct git_commit_test {
+ char repo_path[1024];
+ git_repository* repo;
+};
+
+static int remove_obj(const char* path,
+ const struct stat* sb,
+ int typeflag,
+ struct FTW* ftwbuf) {
+ return remove(path);
+}
+
+UTEST_F_SETUP(git_commit_test) {
+ git_libgit2_init();
+ strcpy(utest_fixture->repo_path, "/tmp/gout_test_XXXXXX");
+ if (!mkdtemp(utest_fixture->repo_path)) {
+ exit(1);
+ }
+ if (git_repository_init(&utest_fixture->repo, utest_fixture->repo_path, 0)) {
+ exit(1);
+ }
+}
+
+UTEST_F_TEARDOWN(git_commit_test) {
+ git_repository_free(utest_fixture->repo);
+ nftw(utest_fixture->repo_path, remove_obj, 64, FTW_DEPTH | FTW_PHYS);
+ git_libgit2_shutdown();
+}
+
+UTEST_F(git_commit_test, CommitParentHandling) {
+ git_oid tree_id, commit_id, child_id;
+ git_treebuilder* builder = NULL;
+ git_tree* tree = NULL;
+ git_signature* sig = NULL;
+
+ /* Create an empty tree. */
+ ASSERT_EQ(0, git_treebuilder_new(&builder, utest_fixture->repo, NULL));
+ ASSERT_EQ(0, git_treebuilder_write(&tree_id, builder));
+ git_treebuilder_free(builder);
+ ASSERT_EQ(0, git_tree_lookup(&tree, utest_fixture->repo, &tree_id));
+
+ /* Create a signature. */
+ ASSERT_EQ(0, git_signature_now(&sig, "Test User", "test@example.com"));
+
+ /* Create the root commit (no parents). */
+ ASSERT_EQ(0, git_commit_create(&commit_id, utest_fixture->repo, "HEAD", sig,
+ sig, NULL, "Initial commit", tree, 0, NULL));
+
+ GitCommit* root = gitcommit_create(&commit_id, utest_fixture->repo);
+ ASSERT_NE(NULL, root);
+ /* parentoid should be exactly NULL, not an empty string. */
+ EXPECT_EQ(NULL, root->parentoid);
+ EXPECT_STREQ("Initial commit", root->summary);
+
+ /* Create a child commit. */
+ git_commit* root_obj = NULL;
+ ASSERT_EQ(0, git_commit_lookup(&root_obj, utest_fixture->repo, &commit_id));
+ ASSERT_EQ(0, git_commit_create(&child_id, utest_fixture->repo, "HEAD", sig,
+ sig, NULL, "Child commit", tree, 1,
+ (const git_commit**)&root_obj));
+
+ GitCommit* child = gitcommit_create(&child_id, utest_fixture->repo);
+ ASSERT_NE(NULL, child);
+ ASSERT_NE(NULL, child->parentoid);
+
+ char root_oid_str[GIT_OID_SHA1_HEXSIZE + 1];
+ git_oid_tostr(root_oid_str, sizeof(root_oid_str), &commit_id);
+ EXPECT_STREQ(root_oid_str, child->parentoid);
+ EXPECT_STREQ("Child commit", child->summary);
+
+ gitcommit_free(root);
+ gitcommit_free(child);
+ git_commit_free(root_obj);
+ git_tree_free(tree);
+ git_signature_free(sig);
+}