diff --git a/apps/web/src/components/GitActionsControl.logic.test.ts b/apps/web/src/components/GitActionsControl.logic.test.ts index 628f2f359e119eb562b972a2b3b627b2eaa737be..720f32af631e784f826b36d05b304c657a615dba 100644 --- a/apps/web/src/components/GitActionsControl.logic.test.ts +++ b/apps/web/src/components/GitActionsControl.logic.test.ts @@ -250,15 +250,20 @@ describe("when: branch is clean, ahead, and has no open PR", () => { }); describe("when: branch is clean, up to date, and has no open PR", () => { - it("resolveQuickAction returns disabled no-action state", () => { + it("resolveQuickAction offers create PR", () => { const quick = resolveQuickAction( status({ aheadCount: 0, behindCount: 0, hasWorkingTreeChanges: false, pr: null }), false, ); - assert.deepInclude(quick, { kind: "show_hint", label: "Commit", disabled: true }); + assert.deepInclude(quick, { + kind: "run_action", + action: "commit_push_pr", + label: "Create PR", + disabled: false, + }); }); - it("buildMenuItems disables commit, push, and create PR", () => { + it("buildMenuItems enables create PR and keeps commit/push disabled", () => { const items = buildMenuItems(status({ aheadCount: 0, behindCount: 0, pr: null }), false); assert.deepEqual(items, [ { @@ -280,13 +285,25 @@ describe("when: branch is clean, up to date, and has no open PR", () => { { id: "pr", label: "Create PR", - disabled: true, + disabled: false, icon: "pr", kind: "open_dialog", dialogAction: "create_pr", }, ]); }); + + it("buildMenuItems keeps create PR disabled on default branch", () => { + const items = buildMenuItems(status({ branch: "main", aheadCount: 0, behindCount: 0 }), false, true); + assert.deepEqual(items[2], { + id: "pr", + label: "Create PR", + disabled: true, + icon: "pr", + kind: "open_dialog", + dialogAction: "create_pr", + }); + }); }); describe("when: branch is behind upstream", () => { diff --git a/apps/web/src/components/GitActionsControl.logic.ts b/apps/web/src/components/GitActionsControl.logic.ts index 9a9476e4cea571851055b7a80693d94c4e0a61cb..ccb78be31862bc56c7fe15b970f208c76227097f 100644 --- a/apps/web/src/components/GitActionsControl.logic.ts +++ b/apps/web/src/components/GitActionsControl.logic.ts @@ -113,6 +113,7 @@ export function summarizeGitResult(result: GitRunStackedActionResult): { export function buildMenuItems( gitStatus: GitStatusResult | null, isBusy: boolean, + isDefaultBranch = false, ): GitActionMenuItem[] { if (!gitStatus) return []; @@ -127,8 +128,9 @@ export function buildMenuItems( hasBranch && !hasChanges && !hasOpenPr && - gitStatus.aheadCount > 0 && - !isBehind; + !isBehind && + !isDefaultBranch && + (gitStatus.aheadCount > 0 || gitStatus.hasUpstream); const canOpenPr = !isBusy && hasOpenPr; return [ @@ -265,6 +267,15 @@ export function resolveQuickAction( }; } + if (!hasOpenPr && !isDefaultBranch && gitStatus.hasUpstream) { + return { + label: "Create PR", + disabled: false, + kind: "run_action", + action: "commit_push_pr", + }; + } + if (hasOpenPr && gitStatus.hasUpstream) { return { label: "Open PR", disabled: false, kind: "open_pr" }; } diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx index 55115a363895034ce47e95447ebde90858da976b..e1b48e9244c7b511244e7985e608d804a6287148 100644 --- a/apps/web/src/components/GitActionsControl.tsx +++ b/apps/web/src/components/GitActionsControl.tsx @@ -180,10 +180,19 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions const current = branchList?.branches.find((branch) => branch.name === branchName); return current?.isDefault ?? (branchName === "main" || branchName === "master"); }, [branchList?.branches, gitStatusForActions?.branch]); + const isDefaultBranchName = useCallback( + (branchName: string | null | undefined) => { + if (!branchName) return false; + const defaultBranchName = branchList?.branches.find((branch) => branch.isDefault)?.name; + if (defaultBranchName) return branchName === defaultBranchName; + return branchName === "main" || branchName === "master"; + }, + [branchList?.branches], + ); const gitActionMenuItems = useMemo( - () => buildMenuItems(gitStatusForActions, isGitActionRunning), - [gitStatusForActions, isGitActionRunning], + () => buildMenuItems(gitStatusForActions, isGitActionRunning, isDefaultBranch), + [gitStatusForActions, isGitActionRunning, isDefaultBranch], ); const quickAction = useMemo( () => resolveQuickAction(gitStatusForActions, isGitActionRunning, isDefaultBranch), @@ -237,6 +246,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions onConfirmed, skipDefaultBranchPrompt = false, featureBranch = false, + isDefaultBranchOverride, }: { action: GitStackedAction; commitMessage?: string; @@ -244,10 +254,12 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions onConfirmed?: () => void; skipDefaultBranchPrompt?: boolean; featureBranch?: boolean; + isDefaultBranchOverride?: boolean; }) => { const actionStatus = gitStatusForActions; - const actionIsDefaultBranch = featureBranch ? false : isDefaultBranch; const actionBranch = actionStatus?.branch ?? null; + const actionIsDefaultBranch = + isDefaultBranchOverride ?? (featureBranch ? false : isDefaultBranchName(actionBranch)); const includesCommit = !forcePushOnlyProgress && (action === "commit" || !!actionStatus?.hasWorkingTreeChanges); if ( @@ -348,6 +360,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions forcePushOnlyProgress: true, onConfirmed: closeResultToast, ...(featureBranch ? { skipDefaultBranchPrompt: true } : {}), + ...(featureBranch ? { isDefaultBranchOverride: false } : {}), }); }, }, @@ -374,6 +387,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions action: "commit_push_pr", forcePushOnlyProgress: true, ...(featureBranch ? { skipDefaultBranchPrompt: true } : {}), + ...(featureBranch ? { isDefaultBranchOverride: false } : {}), }); }, }, @@ -392,7 +406,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions }, [ - isDefaultBranch, + isDefaultBranchName, runImmediateGitActionMutation, setPendingDefaultBranchAction, threadToastData, @@ -410,6 +424,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions forcePushOnlyProgress, ...(onConfirmed ? { onConfirmed } : {}), skipDefaultBranchPrompt: true, + isDefaultBranchOverride: true, }); }, [pendingDefaultBranchAction, runGitActionWithToast]);