Files
dev-updater/server
irisandClaude Opus 5 3aa6f2f290 Move a project's checkout to a branch or commit from the phone
The project settings sheet now lists the checkout's branches and its last
fifty commits, and picking one moves the checkout on the build machine and
builds what that leaves behind. It is the same act as a pull on the same
single checkout, so it goes through the same machinery and reports in the
same place: `after_moving` is the half the two share, which is not the git
command but handing the run over under one lock, so nothing can observe
the moment between the move ending and the builds it decided on starting.

The two lists are read when the sheet opens rather than riding on the
manifest, since both spawn git and the manifest is fetched on every open,
resume and Refresh. Local reads only, so opening the sheet cannot stall on
a network round trip: what the remote has and this checkout has not
fetched is Pull's business, and the card already says when there is any.

Listing branches was wrong twice in ways only a real checkout showed.
`git branch --format` adds a `(HEAD detached at abc123)` pseudo-entry that
is not a branch and cannot be checked out, so the list comes from
`for-each-ref refs/heads`. And `refs/remotes/origin/HEAD` abbreviates to a
bare `origin`, so filtering names that end in `/HEAD` matches nothing and
a phantom branch called `origin` reached the phone; it is dropped by being
a symref instead. The test written to cover the second asserted the same
wrong thing and passed.

Moving is refused over tracked modifications only, where a pull refuses
over any dirt at all. The strict check makes this a one-way door: going
back to a commit from before the .gitignore that covers this project's
build output leaves that output untracked, so the tree is dirty and every
move afterwards is refused -- back but never forward, from a phone, with
the way out being the build machine. Nothing is lost by relaxing it,
because git refuses to overwrite an untracked file itself and carries
across the ones it would not, and its refusal arrives as the error the
card already shows. Found by moving a checkout back and forth rather than
by reading it: the first version passed its tests and trapped the checkout
on the second move.

Picking a commit leaves the checkout on no branch, which the card now says
in those words rather than showing git's literal `HEAD` -- beside a branch
icon that reads as a branch somebody named HEAD. Worth saying now that the
sheet can produce the state, where before it was reachable only on the
build machine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 06:41:49 -04:00
..