fix(walkthrough): use remote default branch

This commit is contained in:
RyderAsking
2026-08-04 16:48:24 +00:00
parent 746e0d4abd
commit b4ced01cc7
8 changed files with 121 additions and 10 deletions
@@ -105,6 +105,7 @@ The following functions are internal helpers used by exported functions:
- `ahead`: Number of commits ahead of upstream.
- `behind`: Number of commits behind upstream.
- `upstreamComparison`: Optional comparison against `upstream/<current-branch>`, with `{ remote, branch, ahead, behind }`.
- `defaultBranches`: Remote default branches derived from local symbolic refs such as `remotes/origin/HEAD -> origin/main`, keyed by remote name. Omitted by runtimes that do not provide this Git metadata.
- `files`: Array of file objects with `path`, `index`, `working_dir` status codes.
- `isClean`: Boolean indicating if working tree is clean.
- `diffStats`: Object mapping file paths to `{ insertions, deletions }`.
+25 -1
View File
@@ -3367,6 +3367,7 @@ export async function getBranches(directory) {
const allBranches = result.all;
const remoteBranches = allBranches.filter(branch => branch.startsWith('remotes/'));
const activeRemoteBranches = await filterActiveRemoteBranches(git, remoteBranches);
const defaultBranches = await getRemoteDefaultBranches(git);
const filteredAll = [
...allBranches.filter(branch => !branch.startsWith('remotes/')),
@@ -3376,7 +3377,8 @@ export async function getBranches(directory) {
return {
all: filteredAll,
current: result.current,
branches: result.branches
branches: result.branches,
defaultBranches,
};
} catch (error) {
console.error('Failed to get branches:', error);
@@ -3384,6 +3386,28 @@ export async function getBranches(directory) {
}
}
async function getRemoteDefaultBranches(git) {
try {
const refs = await git.raw([
'for-each-ref',
'--format=%(refname) %(symref)',
'refs/remotes',
]);
return Object.fromEntries(
refs.trim().split('\n').flatMap((line) => {
const [ref, symbolicRef] = line.split(' ');
const match = ref.match(/^refs\/remotes\/([^/]+)\/HEAD$/);
const prefix = match ? `refs/remotes/${match[1]}/` : '';
return match && typeof symbolicRef === 'string' && symbolicRef.startsWith(prefix)
? [[match[1], symbolicRef.slice(prefix.length)]]
: [];
})
);
} catch {
return {};
}
}
async function filterActiveRemoteBranches(git, remoteBranches) {
try {
const remotes = await git.getRemotes();
@@ -10,6 +10,7 @@ import {
cherryPick,
createWorktree,
getWorktreeBootstrapStatus,
getBranches,
getStatus,
isGitRepository,
populateWorktreeWithLockRecovery,
@@ -988,3 +989,25 @@ describe('hash validation', () => {
).rejects.not.toThrow('Invalid commit hash');
});
});
describe.runIf(canRunGit())('getBranches', () => {
it('returns a remote default branch whose name is not a conventional fallback', async () => {
const remote = createTempDir();
const repository = createTempDir();
runGit(remote, ['init', '--bare', '--initial-branch=react']);
runGit(repository, ['init', '-b', 'next']);
runGit(repository, ['config', 'user.email', 'test@example.com']);
runGit(repository, ['config', 'user.name', 'Test']);
fs.writeFileSync(path.join(repository, 'README.md'), '# Test\n');
runGit(repository, ['add', 'README.md']);
runGit(repository, ['commit', '-m', 'init']);
runGit(repository, ['remote', 'add', 'origin', remote]);
runGit(repository, ['push', 'origin', 'HEAD:react']);
runGit(repository, ['fetch', 'origin']);
runGit(repository, ['remote', 'set-head', 'origin', '--auto']);
await expect(getBranches(repository)).resolves.toMatchObject({
defaultBranches: { origin: 'react' },
});
});
});
@@ -55,6 +55,11 @@ written against staged code never silently re-anchors onto an unstaged edit.
| `branch` | `branch` | `getRangeDiff` uses three-dot `base...head`, so work merged in from the base branch is excluded |
| `pr` | `pr:<number>` | GitHub returns the merge-base diff, matching the branch semantics |
For the current-branch source, the UI prefers the default branch of the current
branch's tracking remote (from its local `remote/HEAD` symbolic ref), then uses
the existing conventional-branch fallback. It does not offer the source when the
chosen base cannot be resolved locally or through a remote-tracking ref.
The panel offers the current branch's pull request on its own: it registers with
the shared GitHub PR status store (`useGitHubPrStatusStore`) rather than waiting
for the pull request panel to have been visited. That store already dedupes