This commit is contained in:
2026-09-03 15:22:37 +07:00
parent c0f06d095e
commit 018395d0c5
6 changed files with 767 additions and 42 deletions
+59
View File
@@ -596,3 +596,62 @@ The manual e2e checklist above still needs a real GRASP-06 server run.
so a failure aborts creation cleanly with nothing announced. Two new
`signed_git` tests (`set_origin_creates_or_replaces_the_remote`,
`working_copy_cloned_from_the_mirror_matches_head_and_origin`).
- **Add (after e2e, user request)** — "ready to push" watch for the
user's own repositories: local commits made in a checkout (external
git) surface as a **sidebar badge** on the repository row (a
`CountBadge` with the unpushed commit count) and, when the repository
panel is open, as an info **banner with a Push button**. The
`CheckoutsStore` gains a second status family (`request_push_statuses`/
`push_statuses_of`): per checked-out branch it refreshes the remote
view (`git fetch` of the checkout's origin, offline-tolerant) and
counts `origin/<branch>..<branch>` (`origin/HEAD` for branches the
remote does not have yet); dirty/detached checkouts are skipped like
the PR suggestions. Poll cadence: 15 s while a repository panel is
open (`status_requested`), 60 s for the sidebar-only background watch;
request sets are cleared on signer change. The repo panel's
ready-to-contribute banner now applies only to repositories of other
authors — owned repositories get the push banner instead, whose Push
action calls the new `Backend::push_checkout` (shared body with the
existing mirror-based `push_repository`): publishes a fresh 30618
state event (keeping the announced `HEAD` branch when the checkout is
on a side branch) then pushes every branch and tag to the announced
grasp servers. New test
`checkout_push_status_counts_unpushed_commits_only`. The mirror's file
browser stays a snapshot (new commits appear after a branch switch),
like the rest of the browser.
- **Fix (after e2e, user report)** — a push warning "cannot lock ref
'refs/heads/main': is at X but expected Y" (server-side compare-and-
swap rejection, `incorrect old value provided`). Reproduced locally:
two concurrent plain pushes of the *same* ref from the same base make
the loser fail exactly this way — the app can race itself when two
push sources for one repository run at once (two panels of the same
repo, or the banner Push racing the header's Republish; each guard was
per-view only). Fix: pushes are now single-flight per repository in
`Backend::push_repo_from` via an `Arc<Mutex<HashSet<RepoAddr>>>` guard
(`PushGuard`, RAII: the lock is released on completion, on error and on
task cancellation alike); a second concurrent push fails fast with
"A push to this repository is already in progress" instead of racing.
Racing an external `git push` against the same server remains possible
(benign: the ref converges; the loser logs a warning only).
- **Fix (after e2e, user report)** — after a successful push the
repository panel's commit list stayed on the old commit (even across
restarts): the browser reads the GitCache mirror, and a fetch never
moves a mirror's *local* branches — `origin/main` advanced while local
`main` (what the commit list walks) stayed behind. ngit/nak never hit
this because they operate on real clones the user `git pull`s; nak also
publishes the updated 30618 state *before* each push, which Signed
already did. Fixes, mirroring a `git pull --ff-only` on the browser
clone: new `signed_git::fast_forward_branches(workdir)` (per local
branch, when it is an ancestor of its `refs/remotes/origin/*`
counterpart: the checked-out branch is merged so its worktree follows,
dirty worktrees and local-only commits are never touched; returns
whether anything moved); `RepoDetailView::load_repo`'s background
refresh fast-forwards after `fetch_all` and rebuilds the explorer,
previews and commit list (`reload_worktree`) when anything moved;
`push_unpushed_checkout` reloads the mirror on success so an owned
repo's pushed commit appears immediately; `ensure_origin` now also
configures the standard `remote.origin.fetch` refspec (create-flow
mirrors otherwise never map heads on fetch). New test
`fast_forward_branches_moves_the_mirror_and_keeps_local_work`; the
remote-only-branch limitation stays (a branch the mirror has never
checked out is not listed), as documented.