Return structured error for dangling refs in RefExists
What does this MR do and why?
When a reference exists but the object it points to is missing,
RefExists returned a generic INTERNAL: exit status 128 error.
This surfaced in GitLab Rails as an opaque GRPC::Internal from
ref_exists?, breaking repository mirroring and giving operators no
actionable signal about the dangling reference.
This MR detects that condition and returns a typed FAILED_PRECONDITION
error carrying a RefExistsError detail with the reference name and
object ID:
- Detect the exit code 128 from
git show-ref --verifythat indicates the reference exists but its target object is missing. - Confirm the dangling state by resolving the reference and reading the
object via
ReadObjectInfo. - Return a typed
FAILED_PRECONDITIONerror so callers can identify and handle the dangling reference.
Verified end-to-end via Rails console against a project with a fabricated
dangling keep-around ref: ref_exists? now raises
9:reference "refs/keep-around/<oid>" points to missing object <oid>
(gRPC FAILED_PRECONDITION) instead of the previous 13:exit status 128
(gRPC INTERNAL).
References
Contributes to https://gitlab.com/gitlab-com/request-for-help/-/work_items/4994
A follow-up GitLab Rails change is required to consume the new error
(bump the gitaly gem and add a handle_detailed_error branch for
Gitaly::ReferenceUnresolvableError) to fully unblock mirroring.
How to set up and validate locally
- Build Gitaly with this branch.
- Create a dangling keep-around ref in a test repo: write a commit,
point
refs/keep-around/<oid>at it, then delete the loose object. - In the Rails console:
Project.find(<id>).repository.ref_exists?(\"refs/keep-around/<oid>\"). - Observe it raises
9:reference \"...\" points to missing object ...(FAILED_PRECONDITION) rather than13:exit status 128.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.