fix: reject a lone '@' as a reference name, like Git does - #2886
Merged
Sebastian Thiel (Byron) merged 2 commits intoAug 6, 2026
Conversation
`@` is shorthand for `HEAD` in revision syntax, so Git refuses a reference
name consisting solely of it - see `check_or_sanitize_refname()` in `refs.c`,
which `check_refname_format()` delegates to. It substitutes `-` when
sanitizing, which is done here too.
The visible effect was in refspec destinations: `git push origin "HEAD:@"`
fails with `fatal: invalid refspec`, while `gix_refspec::parse("HEAD:@", Push)`
returned `Ok(dst = "@")`. Sources were already correct, as `parse()` rewrites a
bare `@` source to `HEAD`, but destinations reach `reference::name_partial()`.
`@` remains valid as a component, so `refs/heads/@` and a tag named `@` are
unaffected.
Pins that `@` is rejected by `name()` and `name_partial()` while staying valid
inside a path (`refs/heads/@`, `@/x`, `@@`), and that inputs collapsing to a
lone `@` during sanitization are replaced too. The tag expectation changes
because `mktests!` sanitizes as a reference name - a tag named `@` is still
valid, as `refs/tags/@` is a legal ref.
Sebastian Thiel (Byron)
force-pushed
the
validate-at-refname
branch
from
August 6, 2026 08:15
aadf244 to
7d5b4ae
Compare
Sebastian Thiel (Byron)
force-pushed
the
validate-at-refname
branch
from
August 6, 2026 08:21
7d5b4ae to
1384133
Compare
Member
|
Thank a lot, good catch! Very subtle behavior, and it still surprises me that |
Sebastian Thiel (Byron)
enabled auto-merge
August 6, 2026 08:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Created by Claude Code on behalf of Amey, who reviewed it before submitting. Everything below this line is the agent's writing, not his.
Summary
@, which Git refuses because@is shorthand forHEAD.-when sanitizing, which is what Git's sanitizer does.@valid as a component, sorefs/heads/@and a tag named@are unaffected.Git baseline
check_or_sanitize_refname()inrefs.c, whichcheck_refname_format()delegates to, rejects the name outright and substitutes-in its sanitizing path.git check-ref-formatrefuses@under every flag combination — plain,--allow-onelevel,--refspec-pattern, both together, and--normalize— while acceptingrefs/heads/@,refs/tags/@,@/xand@@.The visible consequence was in refspec destinations:
where
gix_refspec::parse("HEAD:@", Push)returnedOk(dst = "@"). Sources were already right, sinceparse()rewrites a bare@source toHEAD, but destinations reachreference::name_partial()directly.Two public behaviours change as a result:
name_partial_or_sanitize("@")now yields"-"rather than"@", andname("@")reportsReservedwhere it previously reportedSomeLowercase. No caller in the workspace observes either.Validation
A differential sweep of 1,030 reference names against git 2.52.0 — every byte
0x01–0xFFembedded in a component and as a whole component, plus.lockplacement, slash density, dot placement and@{forms — found@to be the only divergence, and none remaining afterwards. Two shapes lie outside what the oracle can express: names containing NUL, and names beginning with-, whichgit check-ref-formatwill not accept as an argument.cargo test— gix-validate 326, gix-ref 179, gix-refspec 76, gix-revision 112, gix-object 148, gix-url 144, gix-worktree 10, gix 417cargo clippy -p gix-validate --all-targetscargo fmt --check