mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
fix: ignore peeled tag entries (^{}) in state event ref parsing
State events (kind 30618) can include refs/tags/<name>^{} entries which
are git's notation for the dereferenced commit behind an annotated tag.
These are not real git refs and are never sent as part of a push.
extract_refs_from_state and RepositoryState::from_event were treating
them as real refs, causing can_satisfy_state to reject valid annotated
tag pushes: the would-be state after the push lacked the spurious ^{}
entry, so the exact-equality check always failed.
This commit is contained in:
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Push authorization now correctly ignores `refs/tags/<name>^{}` peeled-tag entries in state events (kind 30618). These entries are git's internal notation for the dereferenced commit behind an annotated tag and are never sent as part of a push. Previously, their presence in the state event caused `can_satisfy_state` to reject valid annotated-tag pushes because the would-be ref state after the push did not include the spurious `^{}` entry, making the exact-equality check fail.
|
||||
|
||||
### Changed
|
||||
|
||||
- Push auth rejections now send the reason to the git client via ERR pkt-line (e.g. "authorisation failed: No state events in purgatory") instead of a generic HTTP 403, so users see actionable error messages directly in their terminal
|
||||
|
||||
+3
-1
@@ -260,12 +260,14 @@ impl RepositoryState {
|
||||
|
||||
// Extract tags (refs/tags/*)
|
||||
// Tag format: ["refs/tags/v1.0", "commit_hash"]
|
||||
// Exclude peeled tag notation ("refs/tags/v1.0^{}") — these are git's internal
|
||||
// dereference markers pointing to the underlying commit, not real refs.
|
||||
let tags = event
|
||||
.tags
|
||||
.iter()
|
||||
.filter_map(|t| {
|
||||
if let TagKind::Custom(s) = t.kind() {
|
||||
if s.as_ref().starts_with("refs/tags/") {
|
||||
if s.as_ref().starts_with("refs/tags/") && !s.as_ref().ends_with("^{}") {
|
||||
let parts = t.clone().to_vec();
|
||||
if parts.len() >= 2 {
|
||||
Some(TagState {
|
||||
|
||||
@@ -58,7 +58,11 @@ pub fn extract_refs_from_state(event: &Event) -> Vec<RefPair> {
|
||||
let ref_str = ref_name.as_ref();
|
||||
|
||||
// Only process refs/heads/* and refs/tags/*
|
||||
if ref_str.starts_with("refs/heads/") || ref_str.starts_with("refs/tags/") {
|
||||
// Exclude peeled tag notation (e.g. "refs/tags/v1.0.0^{}") — these are
|
||||
// git's internal dereference markers, not real refs that get pushed.
|
||||
if (ref_str.starts_with("refs/heads/") || ref_str.starts_with("refs/tags/"))
|
||||
&& !ref_str.ends_with("^{}")
|
||||
{
|
||||
// Get the object SHA (first value in tag)
|
||||
let parts = tag.clone().to_vec();
|
||||
if parts.len() >= 2 {
|
||||
|
||||
Reference in New Issue
Block a user