Repository navigation
feat: record HEAD at creation, heal a dangling HEAD on push, admin route to move it - #99
Open
jonasgrosch wants to merge 4 commits into
Open
jonasgrosch wants to merge 4 commits into
jonasgrosch wants to merge 4 commits into
Conversation
A receive-pack command naming `HEAD` (e.g. `<old> <new> HEAD`) was accepted and moved HEAD's branch through the symref, under a name that no `protect` rule on refs/heads/* matches. Any principal with write could therefore move a protected branch that HEAD points at. git's own receive-pack refuses such commands as a funny refname; walgit now does the same under every policy, including none. Reproduced against a running server: with HEAD -> refs/heads/production and production protected, a push to refs/heads/production is rejected by the rule while a raw `HEAD` command answered `ok HEAD` and moved production. Plain `git push` cannot send it (the client wants a full refname), but any HTTP client can. Symbolic HEAD updates (import) are not pushed commands and still pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ute to move it
A repository created by a push that contains no `main` kept HEAD -> refs/heads/main
(nonexistent): the UI showed "This repository is empty" and clones warned "remote HEAD
refers to nonexistent ref" although the repository had branches. Receive-pack cannot
set HEAD, and only `walgit import` wrote symbolic HEAD updates.
HEAD stays where refs live (checkpoint RefSnapshot.head_target + symbolic HEAD updates in
log transactions); no record means walgit_git::IMPLICIT_HEAD (refs/heads/main), now a
named format constant. No proto change.
- `git.default_branch` (default "main", validated as a short Git branch name) and
`PUT /{o}/{r}?default_branch=` name HEAD's branch for a new repository. The implicit
target records nothing (one manifest Create, as before); any other target is the
repository's first entry (HEAD-only REF_UPDATE): its log slot is claimed as a publish
claims one and the manifest Create lists it, so the repository appears with its HEAD in
one commit. Lost Create replies resolve through cas_landed. `walgit repo create
--default-branch`, `Registry::create_with_default_branch`.
- Heal: a publish that creates refs/heads/* while HEAD resolves to nothing before and
after it appends `HEAD -> <target>` to its own entry's transaction: default_branch if
created, else the lexicographically first created branch. Computed on each attempt's
CAS basis inside process_batch, so it commits with the refs that justify it. A txn that
names HEAD keeps it; deleting HEAD's branch leaves it dangling until such a publish.
Clients never see HEAD in report-status.
- `PUT /{o}/{r}/api[-browser]/head {"branch"}` (admin): `RepoHandle::publish_head` through
the same publisher; the target must exist on the attempt's CAS basis (re-checked on
every retry); idempotent (seq 0). 409 when the branch does not exist.
- Policy: the pushed-`HEAD <oid>` refusal (`ng funny refname` under every policy) comes
from the base, conxai/receive-pack-refname (689d96e); this commit only rewords its
comment to name the heal rule and the admin route as HEAD's symbolic writers.
- Fix: apply_txn_to_map stored a symbolic HEAD update in the ref map instead of
head_target, so later requests of the same batch saw the old HEAD.
- Events: unchanged. Symbolic retargets already emit nothing.
Round trips (docs/ROUNDTRIPS.md): push/publish unchanged (heal is 0 extra); create
1 -> 1 for the implicit HEAD, 2 sequential for any other (once per repository); admin
HEAD route = refs sync -> log PUT -> CAS (3 rounds, like a settings publish). Budget test
healthy_request_round_trip_budgets unchanged.
Docs: AGENTS.md D50, walgit.example.toml, web/API.md, SDK (repo.create({defaultBranch}),
repo.setHead), docs/CONTRACT.md, docs/POLICY.md, docs/ROUNDTRIPS.md.
Rebased onto conxai/receive-pack-refname from 846d8ed, dropping its duplicate of the
funny-refname check and test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
default_branch_is_chosen_at_creation_healed_by_push_and_moved_by_admin declared two consts after statements; clippy -D warnings (items_after_statements, all targets) failed on them. They are promoted 'static lets now, renamed so they do not shadow the test's array-valued writer/admin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every sim scenario pushes refs/heads/p<N> into a fresh repository, so since the HEAD heal (D52) each first push's log entry also carries `HEAD -> refs/heads/p0` (a symbolic update, no oid). check_truth folded every update's new_oid and then parsed each as an object id: `A hash sized 0 hexadecimal characters is invalid`, 10 of 19 sim tests red under `just sim`. conxai/default-head did not touch sim.rs; the failure is the topic's, surfaced by the full tree run. The fold now tracks object moves only; HEAD's target is not an object to materialize. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Recording HEAD at creation and fixing it inside the same entry looks right to me. One problem with the heal though: when the default branch isn't one of the new branches, it picks Also, now that HEAD can move between branches on the same commit, the refs ETag (just the head sha) can answer 304 with the old branch name. |
This branch has not been deployed
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.
A repository whose first pushes contain no
mainkeeps HEAD →refs/heads/main, which does not exist: the UI reports an empty repository and clones warn "remote HEAD refers to nonexistent ref" although branches exist. Receive-pack cannot set HEAD.HEAD stays WAL state (checkpoint
head_target+ symbolicHEADupdates in log transactions); an unrecorded HEAD meansIMPLICIT_HEAD(refs/heads/main). No proto change.git.default_branch(defaultmain) andPUT /{o}/{r}?default_branch=<name>(short name, validated).mainrecords nothing (one Create, unchanged); any other branch is the repository's first entry — its log slot claimed like a publish and listed by the manifest Create, so the repository appears with its HEAD atomically (2 requests, once per repository).walgit repo create --default-branch.refs/heads/*while HEAD resolves to nothing before and after it, the same entry carriesHEAD → default_branchif created, else the lexicographically first created branch — computed on each attempt's CAS basis inprocess_batch, zero extra requests, recomputed on retry. Explicit HEAD updates (import) win; the client report is unchanged.PUT /{o}/{r}/api[-browser]/head {"branch": "…"}— admin, target must exist (re-checked on every CAS attempt), idempotent (seq: 0), 409 for an unknown branch. SDKrepo.setHead(),repo.create({defaultBranch}).apply_txn_to_maptracks a HEAD retarget ashead_target, so later requests in a batch see it; the sim's truth fold skips symbolic HEAD updates.Round trips: push unchanged; create 1 (
main) / 2 (other, once per repository); HEAD route = refs sync → log PUT → CAS.healthy_request_round_trip_budgetsunchanged.Stacked on #93 (the pushed-HEAD "funny refname" fix): this branch's first commit is #93's; review from
e3cab23.Tests: WAL (create records only a non-implicit HEAD, survives a lost Create reply; heal by name order; default-branch preference; explicit HEAD respected; one heal per batch;
publish_headexistence/idempotence/stale replica), HTTP (create override, heal on an auto-created push, admin route auth/validation/409/404/both lanes). Standalone on git 2.47: fmt, clippy--workspace --all-targets -D warnings,api_v16/6,policy3/3. Two sim tests (base_rebuild_resumes_after_a_kill_between_any_two_phases,geometric_fold_never_touches_the_base_or_the_history_pack) fail in our container onmain80e9a20 as well, serially — pre-existing there, unchanged by this PR. Combined with #96/#97/#98 the full workspace suite is green (conxai-technologies/walgitconxai/main).Part of #94.
🤖 Generated with Claude Code