Loading
Handle dangling reference error from RefExists
What does this MR do and why?
When a repository has a dangling reference (a ref that exists but whose target
object is missing, such as a refs/keep-around/<sha> pointing at a
garbage-collected commit), RefExists surfaces an opaque
Gitlab::Git::CommandError. This breaks repository mirroring and requires a
manual console fix. Gitaly gitlab-org/gitaly!8967
now returns a typed error for this case, but Rails must consume it.
This MR:
- Adds
Gitlab::Git::ReferenceUnresolvableErrorcarrying the offending reference name and oid. It subclassesGitlab::Git::CommandError, so existing callers that rescueCommandErrorkeep working. - Unwraps the new
Gitaly::ReferenceUnresolvableErrordetail (nested inGitaly::RefExistsError) into the typed error inWrapsGitalyErrors, so callers get an actionable error instead of a genericCommandError. - Self-heals in
Gitlab::Git::KeepAround#execute: when the keep-around ref is dangling, it logs and deletes the ref, then continues. If the commit is present, the ref is re-created; if it is gone, the SHA is skipped.#kept_around?itself still raises, so onlyexecutedeletes refs. - Rescues the errors
delete_refscan raise (Repository::GitError,ReferencesLockedError,InvalidRefFormatError) inexecute. These are notCommandErrorsubclasses, so without this a failed delete would escape into inlineafter_commitcallers. A failed delete is tracked and the SHA is returned for retry.
The gitaly gem already includes the new proto from gitaly!8967 (merged),
so no gem bump is needed.
References
- Customer RFH: https://gitlab.com/gitlab-com/request-for-help/-/work_items/4994
- Gitaly change: gitaly!8967 (merged)
Verification
| Check | Before this MR | With this MR |
|---|---|---|
repository.ref_exists?(ref) on the dangling ref |
Raised Gitlab::Git::CommandError |
Raised the typed Gitlab::Git::ReferenceUnresolvableError, with the ref name and missing object ID |
Gitlab::Git::KeepAround.execute(repository, [sha], source: :verifier) |
Returned the SHA in the failed-SHAs array; the dangling ref remained | Returned []; the dangling ref was removed and a valid keep-around ref was created |
| Repeat the keep-around call with the valid ref | Not tested | Returned []; the ref remained valid |
How to set up and validate locally
- Use a Gitaly version that includes gitaly!8967 (merged).
- Fabricate a dangling keep-around ref in a test repo: write a commit, point
refs/keep-around/<oid>at it, then delete the loose object atobjects/<oid[0:2]>/<oid[2:]>. - In the Rails console:
p = Project.find(<id>) p.repository.ref_exists?("refs/keep-around/<oid>") # => raises Gitlab::Git::ReferenceUnresolvableError p.repository.keep_around("<oid>", source: 'manual') # => [] ; logs 'Deleting dangling keep-around reference' and deletes the ref p.repository.ref_exists?("refs/keep-around/<oid>") # => false
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.
Edited by Vasilii Iakliushin