Skip to content

fix: reject a lone '@' as a reference name, like Git does - #2886

Merged
Sebastian Thiel (Byron) merged 2 commits into
GitoxideLabs:mainfrom
ameyypawar:validate-at-refname
Aug 6, 2026
Merged

fix: reject a lone '@' as a reference name, like Git does#2886
Sebastian Thiel (Byron) merged 2 commits into
GitoxideLabs:mainfrom
ameyypawar:validate-at-refname

Conversation

@ameyypawar

Copy link
Copy Markdown
Contributor

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

  • Reject a reference name consisting solely of @, which Git refuses because @ is shorthand for HEAD.
  • Replace it with - when sanitizing, which is what Git's sanitizer does.
  • Leave @ valid as a component, so refs/heads/@ and a tag named @ are unaffected.

Git baseline

check_or_sanitize_refname() in refs.c, which check_refname_format() delegates to, rejects the name outright and substitutes - in its sanitizing path. git check-ref-format refuses @ under every flag combination — plain, --allow-onelevel, --refspec-pattern, both together, and --normalize — while accepting refs/heads/@, refs/tags/@, @/x and @@.

The visible consequence was in refspec destinations:

$ git push origin "HEAD:@"
fatal: invalid refspec 'HEAD:@'

where gix_refspec::parse("HEAD:@", Push) returned Ok(dst = "@"). Sources were already right, since parse() rewrites a bare @ source to HEAD, but destinations reach reference::name_partial() directly.

Two public behaviours change as a result: name_partial_or_sanitize("@") now yields "-" rather than "@", and name("@") reports Reserved where it previously reported SomeLowercase. No caller in the workspace observes either.

Validation

A differential sweep of 1,030 reference names against git 2.52.0 — every byte 0x010xFF embedded in a component and as a whole component, plus .lock placement, 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 -, which git check-ref-format will 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 417
  • cargo clippy -p gix-validate --all-targets
  • cargo fmt --check

`@` 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.
Assisted-by: GPT 5.6
Co-authored-by: GPT 5.6 <codex@openai.com>
@Byron

Copy link
Copy Markdown
Member

Thank a lot, good catch! Very subtle behavior, and it still surprises me that @ as tag name is allowed. It actually can't be looked up as revspec directly, one will need tags/@ to disambiguate, but nothing informs about the ambiguity either. Interesting niche case, moving on 😅.

@Byron
Sebastian Thiel (Byron) merged commit 12240ab into GitoxideLabs:main Aug 6, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants