From 7c8c2bba3ff7184e8b356bb641ff5ef7fa5c36e2 Mon Sep 17 00:00:00 2001 From: bot-hermes Date: Thu, 13 Aug 2026 20:33:16 +0000 Subject: [PATCH] feat(ui): head branch selector in the GitHub pull request create form --- .../views/git/PullRequestSection.tsx | 57 +++++++++++++++++-- packages/ui/src/lib/i18n/messages/de.ts | 1 + packages/ui/src/lib/i18n/messages/en.ts | 1 + packages/ui/src/lib/i18n/messages/es.ts | 1 + packages/ui/src/lib/i18n/messages/fr.ts | 1 + packages/ui/src/lib/i18n/messages/ja.ts | 1 + packages/ui/src/lib/i18n/messages/ko.ts | 1 + packages/ui/src/lib/i18n/messages/pl.ts | 1 + packages/ui/src/lib/i18n/messages/pt-BR.ts | 1 + packages/ui/src/lib/i18n/messages/uk.ts | 1 + packages/ui/src/lib/i18n/messages/zh-CN.ts | 1 + packages/ui/src/lib/i18n/messages/zh-TW.ts | 1 + 12 files changed, 63 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/components/views/git/PullRequestSection.tsx b/packages/ui/src/components/views/git/PullRequestSection.tsx index 3a258526..08546c36 100644 --- a/packages/ui/src/components/views/git/PullRequestSection.tsx +++ b/packages/ui/src/components/views/git/PullRequestSection.tsx @@ -367,6 +367,7 @@ export const PullRequestSection: React.FC<{ } return normalizeBranchRef(baseBranch); }); + const [headBranch, setHeadBranch] = React.useState(branch); const [mergeMethod, setMergeMethod] = React.useState('squash'); const [isGenerating, setIsGenerating] = React.useState(false); @@ -443,6 +444,37 @@ export const PullRequestSection: React.FC<{ return Array.from(unique).sort((a, b) => a.localeCompare(b)); }, [baseBranch, remoteBranches, selectedRemote?.name, targetBaseBranch, upstreamBranches, useDetectedUpstream]); + const availableHeadBranches = React.useMemo(() => { + const selectedRemoteName = useDetectedUpstream ? null : (selectedRemote?.name?.trim() || null); + const unique = new Set(); + + // The current local branch must always be offered, even before the branch list resolves. + unique.add(branch); + + for (const remoteBranch of remoteBranches) { + const branchName = remoteBranchToName(remoteBranch, selectedRemoteName); + if (!branchName || branchName === 'HEAD') { + continue; + } + unique.add(branchName); + } + + // When using detected upstream, include all upstream repo branches + if (useDetectedUpstream) { + for (const b of upstreamBranches) { + if (b && b !== 'HEAD') { + unique.add(b); + } + } + } + + const sorted = Array.from(unique).sort((a, b) => a.localeCompare(b)); + if (branch && sorted[0] !== branch) { + return [branch, ...sorted.filter((candidate) => candidate !== branch)]; + } + return sorted; + }, [branch, remoteBranches, selectedRemote?.name, upstreamBranches, useDetectedUpstream]); + // Update selected remote when remotes change React.useEffect(() => { if (remotes.length === 0) { @@ -1141,6 +1173,7 @@ export const PullRequestSection: React.FC<{ setBody(snapshot?.body ?? ''); setDraft(snapshot?.draft ?? false); setTargetBaseBranch(snapshot?.targetBaseBranch ? normalizeBranchRef(snapshot.targetBaseBranch) : normalizeBranchRef(baseBranch)); + setHeadBranch(branch); const nextRemote = pickInitialPrRemote(remotes, { selectedRemoteName: snapshot?.selectedRemoteName, trackingBranch, @@ -1278,7 +1311,7 @@ export const PullRequestSection: React.FC<{ toast.error(t('gitView.pr.toast.baseBranchRequired')); return; } - if (!useDetectedUpstream && trimmedBase === branch) { + if (!useDetectedUpstream && trimmedBase === headBranch) { toast.error(t('gitView.pr.toast.baseMustDifferFromHead')); return; } @@ -1292,7 +1325,7 @@ export const PullRequestSection: React.FC<{ const pr = await github.prCreate({ directory, title: trimmedTitle, - head: branch, + head: headBranch, base: trimmedBase, ...(body.trim() ? { body } : {}), draft, @@ -1315,7 +1348,7 @@ export const PullRequestSection: React.FC<{ } finally { setIsCreating(false); } - }, [body, branch, detectedUpstream, directory, draft, github, prStatusKey, refresh, scheduleActionRefresh, selectedRemote, targetBaseBranch, title, trackingBranch, updatePrStatus, useDetectedUpstream, t]); + }, [body, detectedUpstream, directory, draft, github, headBranch, prStatusKey, refresh, scheduleActionRefresh, selectedRemote, targetBaseBranch, title, trackingBranch, updatePrStatus, useDetectedUpstream, t]); const mergePr = React.useCallback(async (pr: GitHubPullRequest) => { if (!github?.prMerge) { @@ -1996,7 +2029,7 @@ export const PullRequestSection: React.FC<{
{t('gitView.pr.createTitle')}
- {branch} (local) → {targetBaseBranch} ({useDetectedUpstream && detectedUpstream ? 'upstream' : 'remote'}) + {headBranch}{headBranch === branch ? (local) : null} → {targetBaseBranch} ({useDetectedUpstream && detectedUpstream ? 'upstream' : 'remote'})
{repoUrl ? ( @@ -2021,6 +2054,20 @@ export const PullRequestSection: React.FC<{ /> + +