fix(git): the branch switcher lists and checks out branches only the remote has

GET /api/lanes/:id/git/branches read only refs/heads, so a branch someone
else pushed never showed after a fetch or pull, and `switch` refused it
(ENOBRANCH). It now also returns `remotes`: fetched remote-tracking branches
with no local branch of that name (origin/HEAD left out), listed under
"On the remote"; switching to one runs `git switch -c <b> --track
refs/remotes/<remote>/<b>` with the remote ref looked up, never passed
through (bare name only when exactly one remote has it, else EAMBIGUOUS).
The bar also re-reads the branch list after a fetch or pull instead of
keeping the list it loaded first.
This commit is contained in:
2026-10-28 14:57:32 +07:00
parent fa27b299e7
commit 14e979837e
14 changed files with 197 additions and 24 deletions
+34
View File
@@ -228,6 +228,39 @@ async function listBranches(sourceRepo) {
return { branches, current };
}
/**
* A repo's remote-tracking branches that have NO local branch of the same
* name yet — what a fetch brought in that `listBranches` cannot show.
* `[{name: "origin/feature", remote: "origin", branch: "feature"}]`, the
* remotes' symbolic `HEAD`s left out. The remote's name is matched against
* `git remote` (longest first), so a remote whose name holds a `/` splits right.
*/
async function listRemoteBranches(dir) {
const remotes = (await git(dir, ["remote"])).stdout
.split("\n")
.map((r) => r.trim())
.filter(Boolean)
.sort((a, b) => b.length - a.length);
if (!remotes.length) return [];
const refs = (
await git(dir, ["for-each-ref", "--format=%(refname:short)%00%(symref)", "refs/remotes"])
).stdout
.split("\n")
.filter(Boolean)
.map((l) => l.split("\0"));
const local = new Set((await listBranches(dir)).branches);
const out = [];
for (const [name, symref] of refs) {
if (symref) continue; // origin/HEAD → origin/main
const remote = remotes.find((r) => name.startsWith(`${r}/`));
if (!remote) continue;
const branch = name.slice(remote.length + 1);
if (!branch || branch === "HEAD" || local.has(branch)) continue;
out.push({ name, remote, branch });
}
return out;
}
/**
* Resolve the base branch: try origin/<wanted>, then <wanted>, then HEAD.
*/
@@ -978,6 +1011,7 @@ module.exports = {
isGitRepo,
isValidBranchName,
listBranches,
listRemoteBranches,
resolveBase,
slugify,
listWorktrees,