Split out from #83 / PR #91 (the new-project form unification).
The unified source field opens a local path directly but has no branch axis for local repos. Two pieces:
1. Branch picker for a local repo (backend).
GET /api/branches is deliberately remote-only (git ls-remote, no filesystem access; returns 400 for non-remote). Supporting local means a new endpoint (or extension) that runs git branch / git for-each-ref on the given path — gated behind CODECITY_ALLOW_LOCAL_REPOS, with path validation. Then NewProjectForm shows the branch dropdown for a local path too.
2. Display the current local branch (mostly frontend).
The manifest already carries a branch field (api/models/manifest.py). If the server populates it from the local repo's HEAD, the header/recents branch pill could fall back to manifest.branch when no branch was explicitly chosen — so a loaded local repo shows its checked-out branch.
(2) is the smaller, higher-value piece and could land first.
Split out from #83 / PR #91 (the new-project form unification).
The unified source field opens a local path directly but has no branch axis for local repos. Two pieces:
1. Branch picker for a local repo (backend).
GET /api/branchesis deliberately remote-only (git ls-remote, no filesystem access; returns 400 for non-remote). Supporting local means a new endpoint (or extension) that runsgit branch/git for-each-refon the given path — gated behindCODECITY_ALLOW_LOCAL_REPOS, with path validation. ThenNewProjectFormshows the branch dropdown for a local path too.2. Display the current local branch (mostly frontend).
The manifest already carries a
branchfield (api/models/manifest.py). If the server populates it from the local repo's HEAD, the header/recents branch pill could fall back tomanifest.branchwhen no branch was explicitly chosen — so a loaded local repo shows its checked-out branch.(2) is the smaller, higher-value piece and could land first.