gout

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

commit fdedf8cca985829fcb0dd6e51b0c901d87e84e9b
parent e0932dba5cbf0d1e125989bb0fc9ad71ba5168fd
Author: Chris Bracken <chris@bracken.jp>
Date:   Fri, 20 Feb 2026 14:26:47 +0900

git reference: handle dangling symbolic references

Diffstat:
Msrc/git/commit_tests.c | 13+++++++++++++
Msrc/git/git.c | 5+++++
Msrc/git/reference.c | 19+++++++++++++------
3 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/git/commit_tests.c b/src/git/commit_tests.c @@ -83,3 +83,16 @@ UTEST_F(git_commit_test, CommitParentHandling) { git_tree_free(tree); git_signature_free(sig); } + +UTEST_F(git_commit_test, DanglingReference) { + git_reference* dangling = NULL; + + /* Create a symbolic reference pointing to a non-existent target. */ + ASSERT_EQ(0, git_reference_symbolic_create( + &dangling, utest_fixture->repo, "refs/heads/dangling", + "refs/heads/nonexistent", 0, NULL)); + + /* gitreference_create should return NULL for dangling references. */ + GitReference* ref = gitreference_create(utest_fixture->repo, dangling); + EXPECT_EQ(NULL, ref); +} diff --git a/src/git/git.c b/src/git/git.c @@ -175,6 +175,11 @@ static void libgit2_for_each_reference(Git* git, continue; } GitReference* ref = gitreference_create(repo, current); + if (!ref) { + warnx("skipping reference with missing target: %s", + git_reference_shorthand(current)); + continue; + } repos = reallocarray(repos, repos_len + 1, sizeof(GitReference*)); if (!repos) { err(1, "reallocarray"); diff --git a/src/git/reference.c b/src/git/reference.c @@ -14,19 +14,26 @@ GitReference* gitreference_create(git_repository* repo, git_reference* git_ref) { - GitReference* ref = ecalloc(1, sizeof(GitReference)); - - // Set ref. + // Resolve git_ref to a direct reference. if (git_reference_type(git_ref) == GIT_REFERENCE_SYMBOLIC) { git_reference* direct_ref = NULL; - git_reference_resolve(&direct_ref, git_ref); + int error = git_reference_resolve(&direct_ref, git_ref); + if (error == GIT_ENOTFOUND) { + git_reference_free(git_ref); + return NULL; + } else if (error < 0) { + errx(1, "git_reference_resolve"); + } git_reference_free(git_ref); git_ref = direct_ref; } - if (!git_reference_target(git_ref)) { - errx(1, "git_reference_target"); + if (!git_ref || !git_reference_target(git_ref)) { + git_reference_free(git_ref); + return NULL; } + GitReference* ref = ecalloc(1, sizeof(GitReference)); + // Set type. if (git_reference_is_branch(git_ref)) { ref->type = kReftypeBranch;