gout

A static git page generator
git clone https://git.bracken.jp/gout.git
Log | Files | Refs | README | LICENSE

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:
MBUILD.gn | 1+
Msrc/git/commit.c | 10+++++++---
Asrc/git/commit_tests.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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); +}