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:
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;