From 17d60a9f984b9fdeacdfff2848e47215608dba6d Mon Sep 17 00:00:00 2001 From: Alex Southwell <4596216+saphid@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:01:42 +1100 Subject: [PATCH] fix(web): Push on the default branch no longer offers a feature branch the server rejects Co-Authored-By: Claude Opus 5.5 --- .../src/components/GitActionsControl.logic.test.ts | 12 ++++++++++++ apps/web/src/components/GitActionsControl.logic.ts | 12 +++++++++--- apps/web/src/components/GitActionsControl.tsx | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/GitActionsControl.logic.test.ts b/apps/web/src/components/GitActionsControl.logic.test.ts index 3c58675d6f6d9..fddfbac46b590 100644 --- a/apps/web/src/components/GitActionsControl.logic.test.ts +++ b/apps/web/src/components/GitActionsControl.logic.test.ts @@ -4,9 +4,11 @@ import { resolveAutoFeatureBranchName } from "@t3tools/shared/git"; import { assert, describe, it } from "vite-plus/test"; import { buildMenuItems, + type DefaultBranchConfirmableAction, requiresDefaultBranchConfirmation, resolveChangesTotals, resolveDefaultBranchActionDialogCopy, + resolveFeatureBranchUnavailableReason, resolveLiveThreadBranchUpdate, resolveQuickAction, } from "./GitActionsControl.logic"; @@ -93,6 +95,16 @@ describe("when: on default ref without open PR", () => { disabled: false, }); }); + + it("offers a feature branch only to actions that commit the changes", () => { + const reason = (action: DefaultBranchConfirmableAction, hasWorkingTreeChanges: boolean) => + resolveFeatureBranchUnavailableReason({ action, branchName: "main", hasWorkingTreeChanges }); + assert.isNull(reason("commit_push", true)); + assert.isNull(reason("commit_push_pr", true)); + assert.isNotNull(reason("commit_push", false)); + assert.isNotNull(reason("push", true)); + assert.isNotNull(reason("create_pr", true)); + }); }); describe("when: local changes are behind upstream", () => { diff --git a/apps/web/src/components/GitActionsControl.logic.ts b/apps/web/src/components/GitActionsControl.logic.ts index ca396893accda..6f8d70fe498ab 100644 --- a/apps/web/src/components/GitActionsControl.logic.ts +++ b/apps/web/src/components/GitActionsControl.logic.ts @@ -365,13 +365,19 @@ export function resolveDefaultBranchActionDialogCopy(input: { }; } -/** Clean trees cannot create feature branches because the server derives names from uncommitted changes. */ +/** The server names a feature branch from the commit it makes, so the action must commit changes. */ export function resolveFeatureBranchUnavailableReason(input: { + action: DefaultBranchConfirmableAction; branchName: string; hasWorkingTreeChanges: boolean; }): string | null { - if (input.hasWorkingTreeChanges) return null; - return `There are no changes to commit, so a feature branch can't be named. Create a branch yourself or continue on ${input.branchName}.`; + if (!input.hasWorkingTreeChanges) { + return `There are no changes to commit, so a feature branch can't be named. Create a branch yourself or continue on ${input.branchName}.`; + } + if (input.action === "push" || input.action === "create_pr") { + return `This action doesn't commit your changes, so a feature branch can't be named. Create a branch yourself or continue on ${input.branchName}.`; + } + return null; } export function resolveThreadBranchUpdate( diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx index 6afb76c3760e9..97095cab52562 100644 --- a/apps/web/src/components/GitActionsControl.tsx +++ b/apps/web/src/components/GitActionsControl.tsx @@ -1135,6 +1135,7 @@ export default function GitActionsControl({ // Keep enabled until live status loads; the server rejects stale requests. const featureBranchUnavailableReason = pendingDefaultBranchAction ? resolveFeatureBranchUnavailableReason({ + action: pendingDefaultBranchAction.action, branchName: pendingDefaultBranchAction.branchName, hasWorkingTreeChanges: gitStatus?.hasWorkingTreeChanges ?? true, })