mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-30 11:07:33 +00:00
77 lines
3.1 KiB
Markdown
77 lines
3.1 KiB
Markdown
# Accept No-Op Pushes (old_oid == new_oid)
|
|
|
|
**ID:** 6af5
|
|
|
|
## Problem
|
|
|
|
When a user pushes refs where `old_oid == new_oid` (no actual change), ngit-grasp rejects the push with "No state event found in purgatory from authorized publishers". This creates a poor user experience because:
|
|
|
|
1. **Race condition**: User fetches state, but between fetch and push, a state event arrives and is applied. Their push now has `old_oid == new_oid` but gets rejected.
|
|
2. **User confusion**: The rejection makes users think their desired state was denied, when in fact it's already accepted.
|
|
3. **Git compatibility**: Git accepts no-op pushes with "Everything up-to-date" - we should too.
|
|
|
|
### Example from logs (work/tmp.md)
|
|
|
|
```
|
|
Feb 03 14:10:12 de1 ngit-grasp[6385]: 2026-02-03T14:10:12.937204Z DEBUG ngit_grasp::git::authorization: refs/heads/main 1c1f162929df98557fa58c4a7a392c66de862a87 -> 1c1f162929df98557fa58c4a7a392c66de862a87
|
|
Feb 03 14:10:12 de1 ngit-grasp[6385]: 2026-02-03T14:10:12.938617Z WARN ngit_grasp::git::handlers: Push rejected for f1-sunset-racing: No state event found in purgatory from authorized publishers
|
|
```
|
|
|
|
The push is trying to update `main` from `1c1f162...` to `1c1f162...` (same commit), but it's rejected because:
|
|
- The state event was already applied (between fetch and push)
|
|
- No state event remains in purgatory
|
|
- Authorization requires a purgatory event
|
|
|
|
## Plan
|
|
|
|
- [ ] Phase 1: Detect no-op pushes in `get_state_authorization_for_specific_owner_repo`
|
|
- Check if all pushed refs have `old_oid == new_oid`
|
|
- Return success immediately without checking purgatory
|
|
- Include appropriate reason message
|
|
- [ ] Phase 2: Add tests for no-op push scenarios
|
|
- Test single ref no-op
|
|
- Test multiple refs all no-op
|
|
- Test mixed (some no-op, some real changes) - should still require purgatory
|
|
- [ ] Phase 3: Update documentation
|
|
- Document no-op push behavior in architecture docs
|
|
- Note Git compatibility
|
|
|
|
## Progress
|
|
|
|
### 2026-02-03 [Session 14:30]
|
|
- Created: Identified race condition from production logs
|
|
- Root cause: User's push becomes no-op after state event is applied
|
|
- Decision: Accept no-op pushes as successful without requiring purgatory event
|
|
- Next: Implement detection in authorization.rs
|
|
|
|
## Notes
|
|
|
|
### Location
|
|
- File: `src/git/authorization.rs`
|
|
- Function: `get_state_authorization_for_specific_owner_repo` (line 531)
|
|
- Before purgatory check (line 595)
|
|
|
|
### Implementation Strategy
|
|
```rust
|
|
// Early detection: check if all refs are no-ops
|
|
let all_noop = pushed_refs
|
|
.iter()
|
|
.filter(|(_, _, name)| !name.starts_with("refs/nostr/"))
|
|
.all(|(old_oid, new_oid, _)| old_oid == new_oid);
|
|
|
|
if all_noop && !pushed_refs.is_empty() {
|
|
return Ok(AuthorizationResult {
|
|
authorized: true,
|
|
reason: "Push accepted: all refs already at desired state (no-op)".to_string(),
|
|
state: None,
|
|
maintainers: authorized.into_iter().collect(),
|
|
purgatory_events: vec![],
|
|
});
|
|
}
|
|
```
|
|
|
|
### Edge Cases
|
|
- Empty push: Should not trigger no-op logic (no refs to check)
|
|
- Mixed push: Some refs no-op, some real changes → require purgatory (normal flow)
|
|
- refs/nostr/ refs: Should be excluded from no-op detection (different authorization path)
|