From: Stefan Sperling Subject: fix gotadmin cleanup failure with bad HEAD To: gameoftrees@openbsd.org Date: Fri, 13 Feb 2026 12:58:26 +0100 gotadmin cleanup currently errors out if the HEAD reference does not point at an existing branch. The most usual case looks like this: $ got ref -l HEAD: refs/heads/main refs/heads/master: de66c9099a70943844a8350b63e14b42d7cc7a7d $ gotadmin cleanup gotadmin: reference refs/heads/main not found This can happen when people neglect to set a remote repository's HEAD and never send a branch called 'main' to the server. I've seen this happen on gothub.org which defaults to 'main' when repositories are initially created. If users never send a main branch then the HEAD will be left dangling. Users don't necesarily realize that there is an issue because 'git clone' and 'got clone' have workarounds for this situation. Cloning the repository will succeed anyway. However, cleanup triggered from cronjobs will always fail, which results in performance degrading over time as more and more pack files accumulate. This patch makes gotadmin cleanup succeed in this case by ignoring symbolic references which cannot be resolved. ok? M lib/reference.c | 4+ 1- M lib/repository_admin.c | 7+ 1- M regress/cmdline/cleanup.sh | 36+ 0- 3 files changed, 47 insertions(+), 2 deletions(-) commit - c357a25b5b44cc7a70e26d7a0a3d3177d4919751 commit + 9e76ce4eb8b9924c5f809d70e2b73a35c36ffa62 blob - cd44d800f2110bb1a8fa6f3eb5b03265ed174be3 blob + 792d13a093587be73459dbc1856937bce61a1b6c --- lib/reference.c +++ lib/reference.c @@ -678,8 +678,11 @@ get_committer_time(struct got_reference *ref, struct g struct got_object_id *id = NULL; err = got_ref_resolve(&id, repo, ref); - if (err) + if (err) { + if (err->code == GOT_ERR_NOT_REF && got_ref_is_symbolic(ref)) + return NULL; return err; + } err = got_object_get_type(&obj_type, repo, id); if (err) blob - fe0433e0d9628d4eecbf4e2a7b0e0f1327ee3cb5 blob + 431fcc73acd1fbef092a6e8ade063a761cf08f63 --- lib/repository_admin.c +++ lib/repository_admin.c @@ -96,8 +96,14 @@ get_reflist_object_ids(struct got_object_id ***ids, in } err = got_ref_resolve(&id, repo, re->ref); - if (err) + if (err) { + if (err->code == GOT_ERR_NOT_REF && + got_ref_is_symbolic(re->ref)) { + err = NULL; + continue; + } goto done; + } if (wanted_obj_type_mask != GOT_OBJ_TYPE_ANY) { int obj_type; blob - f55eceed41973eb52bc69e9fb467054747cde376 blob + ccc45f26994ec87846efcba9b463bdb3b0968fc9 --- regress/cmdline/cleanup.sh +++ regress/cmdline/cleanup.sh @@ -694,6 +694,41 @@ EOF test_done "$testroot" "$ret" } +test_cleanup_no_head_branch() { + local testroot=`test_init cleanup_no_head_branch` + local commit_id=`git_show_head $testroot/repo` + + # create a dangling HEAD reference + echo 'ref: refs/heads/main' > $testroot/repo/.git/HEAD + + # list references + got ref -r $testroot/repo -l > $testroot/stdout + cat > $testroot/stdout.expected < $testroot/stdout \ + 2> $testroot/stderr + ret=$? + if [ $ret -ne 0 ]; then + echo "gotadmin cleanup failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + + test_done "$testroot" "$ret" +} + test_parseargs "$@" run_test test_cleanup_unreferenced_loose_objects run_test test_cleanup_redundant_loose_objects @@ -701,3 +736,4 @@ run_test test_cleanup_redundant_pack_files run_test test_cleanup_precious_objects run_test test_cleanup_missing_pack_file run_test test_cleanup_non_commit_ref +run_test test_cleanup_no_head_branch