Skip to content

Add default-branch safeguard with auto feature-branch fallback#131

Merged
juliusmarminge merged 15 commits intomainfrom
codething/2b37f0ce
Mar 2, 2026
Merged

Add default-branch safeguard with auto feature-branch fallback#131
juliusmarminge merged 15 commits intomainfrom
codething/2b37f0ce

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented Mar 1, 2026

Summary

  • Add a default-branch safety flow that can auto-generate a feature branch name (feature/stacked-YYYYMMDD with numeric suffixes on conflicts).
  • Introduce and test resolveAutoFeatureBranchName logic for deterministic branch naming and collision handling.
  • Simplify git branch handling to local branches only in server/core contracts and UI consumers.
  • Remove remote-branch-specific parsing, deduping, checkout fallback heuristics, and related tests.
  • Tighten branch and diff UI behavior (badge display simplification and diff panel interaction/layout cleanup).

Testing

  • Added unit tests for resolveAutoFeatureBranchName in apps/web/src/components/GitActionsControl.logic.test.ts (base-name and suffix-collision scenarios).
  • Updated branch-toolbar tests to reflect removal of remote-branch dedupe helpers.
  • Not run: full lint/test suite in this PR context.

Note

Medium Risk
Changes core git status/push logic and UI action flows around upstreamless branches and default-branch operations, which could alter when pushes are skipped or upstreams are auto-set. Risk is moderated by added integration/unit tests but still impacts common git workflows.

Overview
Improves upstream-less branch handling by computing aheadCount against a resolved base branch (configured branch.<name>.gh-merge-base, origin/HEAD, or main/master) when no upstream is set, and using that to drive statusDetails and pushCurrentBranch decisions.

Updates push behavior to skip when there’s no local delta and the remote branch already exists, and to auto-push with -u origin <branch> when no upstream exists but the branch needs publishing; adds server tests covering these edge cases and a manager test for creating a PR from a no-upstream branch.

Adjusts the web git actions UI to allow push/PR actions without an upstream, adds a default-branch confirmation dialog with improved copy plus an option to auto-create/checkout a dated feature branch (feature/stacked-YYYYMMDD[-N]) and continue the action; includes new unit tests for the naming and dialog-copy logic.

Written by Cursor Bugbot for commit 839feaf. This will update automatically on new commits. Configure here.

Note

Add a default-branch safeguard and auto feature-branch fallback by updating GitCore.pushCurrentBranch and GitActionsControl to compute ahead counts without upstream and gate push/PR flows on main/master

Introduce base-branch resolution and ahead-count computation for no-upstream branches in apps/server/src/git/Layers/GitCore.ts, adjust push behavior to skip or set upstream based on remote presence, and add UI flows to confirm actions on the default branch with auto feature-branch naming in apps/web/src/components/GitActionsControl.tsx.

📍Where to Start

Start with GitCore.readStatusDetails and GitCore.pushCurrentBranch in GitCore.ts, then review the default-branch confirmation flow in GitActionsControl.tsx.

Macroscope summarized 839feaf.

- Prompt before commit/push actions on the default branch
- Allow one-click create+checkout of an auto-named feature branch and continue
- Add logic/tests for date-based unique auto feature branch naming
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 1, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/2b37f0ce

Comment @coderabbitai help to get the list of available commands and usage tips.

- Treat no-upstream branches as publishable: enable Push/Create PR and update quick actions
- Route Create PR menu action through commit+push+PR flow when appropriate
- Simplify Git action dialog to commit-only and add "Commit on new branch" option
- test push-only quick action on default branch without upstream
- verify push and create PR stay disabled when branch is behind
- Change quick action to `Push` (not `Push & create PR`) when branch has no upstream and is not ahead
- Keep `Create PR` menu item disabled until the branch has commits ahead
- Add/adjust logic tests for both no-ahead and ahead scenarios
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: PR disabled reason inconsistent with updated canCreatePr logic
    • Removed the stale !gitStatus.hasUpstream check from getMenuActionDisabledReason so that when a branch has no upstream and no commits ahead, the correct message "No local commits to include in a PR" is shown instead of the misleading upstream message.
  • ✅ Fixed: Duplicated branch-checkout-and-continue logic across callbacks
    • Extracted the shared checkout-toast-chain pattern into a checkoutNewBranchAndRunAction helper callback, reducing both checkoutFeatureBranchAndContinuePendingAction and runDialogActionOnNewBranch to thin wrappers that delegate to it.

Create PR

Or push these changes by commenting:

@cursor push 175f39a882
Preview (175f39a882)
diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx
--- a/apps/web/src/components/GitActionsControl.tsx
+++ b/apps/web/src/components/GitActionsControl.tsx
@@ -105,9 +105,6 @@
   if (hasChanges) {
     return "Commit local changes before creating a PR.";
   }
-  if (!gitStatus.hasUpstream) {
-    return "Set an upstream branch before creating a PR.";
-  }
   if (!isAhead) {
     return "No local commits to include in a PR.";
   }
@@ -314,7 +311,8 @@
         stopProgressUpdates();
         const resultToast = summarizeGitResult(result);
 
-        const existingOpenPrUrl = actionStatus?.pr?.state === "open" ? actionStatus.pr.url : undefined;
+        const existingOpenPrUrl =
+          actionStatus?.pr?.state === "open" ? actionStatus.pr.url : undefined;
         const prUrl = result.pr.url ?? existingOpenPrUrl;
         const shouldOfferPushCta = action === "commit" && result.commit.status === "created";
         const shouldOfferOpenPrCta =
@@ -406,96 +404,72 @@
     });
   }, [pendingDefaultBranchAction, runGitActionWithToast]);
 
+  const checkoutNewBranchAndRunAction = useCallback(
+    (actionParams: {
+      action: GitStackedAction;
+      commitMessage?: string;
+      forcePushOnlyProgress?: boolean;
+      onConfirmed?: () => void;
+    }) => {
+      const branchName = resolveAutoFeatureBranchName(
+        branchList?.branches.map((branch) => branch.name) ?? [],
+      );
+
+      const checkoutPromise = createBranchAndCheckoutMutation.mutateAsync(branchName);
+      toastManager.promise(checkoutPromise, {
+        loading: { title: `Creating ${branchName}...`, data: threadToastData },
+        success: () => ({
+          title: `Checked out ${branchName}`,
+          data: threadToastData,
+        }),
+        error: (error) => ({
+          title: "Failed to checkout feature branch",
+          description: error instanceof Error ? error.message : "An error occurred.",
+          data: threadToastData,
+        }),
+      });
+
+      void checkoutPromise
+        .then(() => {
+          const statusOverride = gitStatusForActions
+            ? { ...gitStatusForActions, branch: branchName, pr: null }
+            : null;
+          return runGitActionWithToast({
+            ...actionParams,
+            skipDefaultBranchPrompt: true,
+            statusOverride,
+            isDefaultBranchOverride: false,
+          });
+        })
+        .catch(() => undefined);
+    },
+    [
+      branchList?.branches,
+      createBranchAndCheckoutMutation,
+      gitStatusForActions,
+      runGitActionWithToast,
+      threadToastData,
+    ],
+  );
+
   const checkoutFeatureBranchAndContinuePendingAction = useCallback(() => {
     if (!pendingDefaultBranchAction) return;
     const pendingAction = pendingDefaultBranchAction;
-    const branchName = resolveAutoFeatureBranchName(branchList?.branches.map((branch) => branch.name) ?? []);
     setPendingDefaultBranchAction(null);
+    checkoutNewBranchAndRunAction(pendingAction);
+  }, [pendingDefaultBranchAction, checkoutNewBranchAndRunAction]);
 
-    const checkoutPromise = createBranchAndCheckoutMutation.mutateAsync(branchName);
-    toastManager.promise(checkoutPromise, {
-      loading: { title: `Creating ${branchName}...`, data: threadToastData },
-      success: () => ({
-        title: `Checked out ${branchName}`,
-        data: threadToastData,
-      }),
-      error: (error) => ({
-        title: "Failed to checkout feature branch",
-        description: error instanceof Error ? error.message : "An error occurred.",
-        data: threadToastData,
-      }),
-    });
-
-    void checkoutPromise
-      .then(() => {
-        const statusOverride = gitStatusForActions
-          ? { ...gitStatusForActions, branch: branchName, pr: null }
-          : null;
-        return runGitActionWithToast({
-          ...pendingAction,
-          skipDefaultBranchPrompt: true,
-          statusOverride,
-          isDefaultBranchOverride: false,
-        });
-      })
-      .catch(() => undefined);
-  }, [
-    branchList?.branches,
-    createBranchAndCheckoutMutation,
-    gitStatusForActions,
-    pendingDefaultBranchAction,
-    runGitActionWithToast,
-    threadToastData,
-  ]);
-
   const runDialogActionOnNewBranch = useCallback(() => {
     if (!isCommitDialogOpen) return;
-    const action: GitStackedAction = "commit";
     const commitMessage = dialogCommitMessage.trim();
-    const branchName = resolveAutoFeatureBranchName(branchList?.branches.map((branch) => branch.name) ?? []);
-
     setIsCommitDialogOpen(false);
     setDialogCommitMessage("");
-
-    const checkoutPromise = createBranchAndCheckoutMutation.mutateAsync(branchName);
-    toastManager.promise(checkoutPromise, {
-      loading: { title: `Creating ${branchName}...`, data: threadToastData },
-      success: () => ({
-        title: `Checked out ${branchName}`,
-        data: threadToastData,
-      }),
-      error: (error) => ({
-        title: "Failed to checkout feature branch",
-        description: error instanceof Error ? error.message : "An error occurred.",
-        data: threadToastData,
-      }),
+    checkoutNewBranchAndRunAction({
+      action: "commit",
+      ...(commitMessage ? { commitMessage } : {}),
     });
+  }, [isCommitDialogOpen, dialogCommitMessage, checkoutNewBranchAndRunAction]);
 
-    void checkoutPromise
-      .then(() => {
-        const statusOverride = gitStatusForActions
-          ? { ...gitStatusForActions, branch: branchName, pr: null }
-          : null;
-        return runGitActionWithToast({
-          action,
-          ...(commitMessage ? { commitMessage } : {}),
-          skipDefaultBranchPrompt: true,
-          statusOverride,
-          isDefaultBranchOverride: false,
-        });
-      })
-      .catch(() => undefined);
-  }, [
-    branchList?.branches,
-    createBranchAndCheckoutMutation,
-    dialogCommitMessage,
-    gitStatusForActions,
-    isCommitDialogOpen,
-    runGitActionWithToast,
-    setIsCommitDialogOpen,
-    threadToastData,
-  ]);
-
   const runQuickAction = useCallback(() => {
     if (quickAction.kind === "open_pr") {
       void openExistingPr();
@@ -738,7 +712,9 @@
               <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">
                 <span className="text-muted-foreground">Branch</span>
                 <span className="flex items-center justify-between gap-2">
-                  <span className="font-medium">{gitStatusForActions?.branch ?? "(detached HEAD)"}</span>
+                  <span className="font-medium">
+                    {gitStatusForActions?.branch ?? "(detached HEAD)"}
+                  </span>
                   {isDefaultBranch && (
                     <span className="text-right text-warning text-xs">Warning: default branch</span>
                   )}

Comment thread apps/web/src/components/GitActionsControl.tsx
Comment thread apps/web/src/components/GitActionsControl.tsx Outdated
- Compute ahead count from base branch when upstream is missing
- Skip push/PR actions when no local commits are ahead
- Update server and UI tests for no-upstream push/PR behavior
Comment thread apps/server/src/git/Layers/GitCore.ts Outdated
juliusmarminge and others added 2 commits March 1, 2026 11:34
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale closure in toast CTA after feature-branch checkout
    • Added a useRef that always points to the latest runGitActionWithToast, and updated the toast CTA onClick handlers (Push and Create PR) to call via the ref instead of the stale closure, ensuring they always use current isDefaultBranch and gitStatusForActions values.

Create PR

Or push these changes by commenting:

@cursor push c2a5cab307
Preview (c2a5cab307)
diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx
--- a/apps/web/src/components/GitActionsControl.tsx
+++ b/apps/web/src/components/GitActionsControl.tsx
@@ -1,6 +1,6 @@
 import { type GitStatusResult, type GitStackedAction, type ThreadId } from "@t3tools/contracts";
 import { useIsMutating, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
 import { ChevronDownIcon, CloudUploadIcon, GitCommitIcon, InfoIcon } from "lucide-react";
 import { GitHubIcon } from "./Icons";
 import {
@@ -308,7 +308,8 @@
         stopProgressUpdates();
         const resultToast = summarizeGitResult(result);
 
-        const existingOpenPrUrl = actionStatus?.pr?.state === "open" ? actionStatus.pr.url : undefined;
+        const existingOpenPrUrl =
+          actionStatus?.pr?.state === "open" ? actionStatus.pr.url : undefined;
         const prUrl = result.pr.url ?? existingOpenPrUrl;
         const shouldOfferPushCta = action === "commit" && result.commit.status === "created";
         const shouldOfferOpenPrCta =
@@ -338,7 +339,7 @@
                 actionProps: {
                   children: "Push",
                   onClick: () => {
-                    void runGitActionWithToast({
+                    void runGitActionWithToastRef.current({
                       action: "commit_push",
                       forcePushOnlyProgress: true,
                       onConfirmed: closeResultToast,
@@ -364,7 +365,7 @@
                       children: "Create PR",
                       onClick: () => {
                         closeResultToast();
-                        void runGitActionWithToast({ action: "commit_push_pr" });
+                        void runGitActionWithToastRef.current({ action: "commit_push_pr" });
                       },
                     },
                   }
@@ -390,6 +391,9 @@
     ],
   );
 
+  const runGitActionWithToastRef = useRef(runGitActionWithToast);
+  runGitActionWithToastRef.current = runGitActionWithToast;
+
   const continuePendingDefaultBranchAction = useCallback(() => {
     if (!pendingDefaultBranchAction) return;
     const pendingAction = pendingDefaultBranchAction;
@@ -710,7 +714,9 @@
               <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">
                 <span className="text-muted-foreground">Branch</span>
                 <span className="flex items-center justify-between gap-2">
-                  <span className="font-medium">{gitStatusForActions?.branch ?? "(detached HEAD)"}</span>
+                  <span className="font-medium">
+                    {gitStatusForActions?.branch ?? "(detached HEAD)"}
+                  </span>
                   {isDefaultBranch && (
                     <span className="text-right text-warning text-xs">Warning: default branch</span>
                   )}

Comment thread apps/web/src/components/GitActionsControl.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Base branch lookup ignores remote-tracking refs
    • Added a fallback check for refs/remotes/origin/ in resolveBaseBranchForNoUpstream so that when a base branch exists only as a remote-tracking ref, it returns origin/ instead of null.

Create PR

Or push these changes by commenting:

@cursor push 09e1d6f726
Preview (09e1d6f726)
diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts
--- a/apps/server/src/git/Layers/GitCore.ts
+++ b/apps/server/src/git/Layers/GitCore.ts
@@ -314,6 +314,16 @@
         if (yield* branchExists(cwd, candidate)) {
           return candidate;
         }
+
+        const remoteResult = yield* executeGit(
+          "GitCore.resolveBaseBranchForNoUpstream.remoteRefCheck",
+          cwd,
+          ["show-ref", "--verify", "--quiet", `refs/remotes/origin/${candidate}`],
+          { allowNonZeroExit: true },
+        );
+        if (remoteResult.code === 0) {
+          return `origin/${candidate}`;
+        }
       }
 
       return null;

Comment thread apps/server/src/git/Layers/GitCore.ts
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Commit message not cleared after dialog submission
    • Added the missing setDialogCommitMessage("") call in runDialogAction to match the reset behavior of all other dialog close paths.

Create PR

Or push these changes by commenting:

@cursor push 671782ab19
Preview (671782ab19)
diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx
--- a/apps/web/src/components/GitActionsControl.tsx
+++ b/apps/web/src/components/GitActionsControl.tsx
@@ -538,6 +538,7 @@
     if (!isCommitDialogOpen) return;
     const commitMessage = dialogCommitMessage.trim();
     setIsCommitDialogOpen(false);
+    setDialogCommitMessage("");
     void runGitActionWithToast({
       action: "commit",
       ...(commitMessage ? { commitMessage } : {}),

Comment thread apps/web/src/components/GitActionsControl.tsx Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Dialog describes "commit" for push-only actions on default branch
    • Replaced the static DEFAULT_BRANCH_ACTION_DESCRIPTION record with a resolveDefaultBranchDialogWording function that adapts the dialog title, description, and confirm button label based on whether there are actual working tree changes, so push-only scenarios show "Push to default branch?" instead of commit-centric language.

Create PR

Or push these changes by commenting:

@cursor push 9bcf132a10
Preview (9bcf132a10)
diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx
--- a/apps/web/src/components/GitActionsControl.tsx
+++ b/apps/web/src/components/GitActionsControl.tsx
@@ -115,11 +115,28 @@
 const COMMIT_DIALOG_DESCRIPTION =
   "Review and confirm your commit. Leave the message blank to auto-generate one.";
 
-const DEFAULT_BRANCH_ACTION_DESCRIPTION: Record<GitStackedAction, string> = {
-  commit: "commit changes",
-  commit_push: "commit and push changes",
-  commit_push_pr: "commit, push, and create a PR",
-};
+function resolveDefaultBranchDialogWording(
+  action: GitStackedAction,
+  hasWorkingTreeChanges: boolean,
+): { title: string; description: string; confirmLabel: string } {
+  if (hasWorkingTreeChanges) {
+    return {
+      title: "Commit to default branch?",
+      description:
+        action === "commit_push_pr"
+          ? "commit, push, and create a PR"
+          : action === "commit_push"
+            ? "commit and push changes"
+            : "commit changes",
+      confirmLabel: "Commit to",
+    };
+  }
+  return {
+    title: "Push to default branch?",
+    description: action === "commit_push_pr" ? "push and create a PR" : "push changes",
+    confirmLabel: "Push to",
+  };
+}
 
 function GitActionItemIcon({ icon }: { icon: GitActionIconName }) {
   if (icon === "commit") return <GitCommitIcon />;
@@ -203,6 +220,17 @@
     ? (quickAction.hint ?? "This action is currently unavailable.")
     : null;
 
+  const defaultBranchDialogWording = useMemo(
+    () =>
+      pendingDefaultBranchAction
+        ? resolveDefaultBranchDialogWording(
+            pendingDefaultBranchAction.action,
+            !!gitStatusForActions?.hasWorkingTreeChanges,
+          )
+        : null,
+    [pendingDefaultBranchAction, gitStatusForActions?.hasWorkingTreeChanges],
+  );
+
   const openExistingPr = useCallback(async () => {
     const api = readNativeApi();
     if (!api) {
@@ -308,7 +336,8 @@
         stopProgressUpdates();
         const resultToast = summarizeGitResult(result);
 
-        const existingOpenPrUrl = actionStatus?.pr?.state === "open" ? actionStatus.pr.url : undefined;
+        const existingOpenPrUrl =
+          actionStatus?.pr?.state === "open" ? actionStatus.pr.url : undefined;
         const prUrl = result.pr.url ?? existingOpenPrUrl;
         const shouldOfferPushCta = action === "commit" && result.commit.status === "created";
         const shouldOfferOpenPrCta =
@@ -716,7 +745,9 @@
               <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">
                 <span className="text-muted-foreground">Branch</span>
                 <span className="flex items-center justify-between gap-2">
-                  <span className="font-medium">{gitStatusForActions?.branch ?? "(detached HEAD)"}</span>
+                  <span className="font-medium">
+                    {gitStatusForActions?.branch ?? "(detached HEAD)"}
+                  </span>
                   {isDefaultBranch && (
                     <span className="text-right text-warning text-xs">Warning: default branch</span>
                   )}
@@ -801,14 +832,13 @@
       >
         <DialogPopup>
           <DialogHeader>
-            <DialogTitle>Commit to default branch?</DialogTitle>
+            <DialogTitle>
+              {defaultBranchDialogWording?.title ?? "Push to default branch?"}
+            </DialogTitle>
             <DialogDescription>
-              This action will{" "}
-              {pendingDefaultBranchAction
-                ? DEFAULT_BRANCH_ACTION_DESCRIPTION[pendingDefaultBranchAction.action]
-                : "push changes"}{" "}
-              on "{gitStatusForActions?.branch ?? "default"}". You can continue on this branch or
-              create a feature branch and run the same action there.
+              This action will {defaultBranchDialogWording?.description ?? "push changes"} on "
+              {gitStatusForActions?.branch ?? "default"}". You can continue on this branch or create
+              a feature branch and run the same action there.
             </DialogDescription>
           </DialogHeader>
           <DialogFooter>
@@ -816,7 +846,8 @@
               Abort
             </Button>
             <Button variant="outline" size="sm" onClick={continuePendingDefaultBranchAction}>
-              Commit to {gitStatusForActions?.branch ?? "default"}
+              {defaultBranchDialogWording?.confirmLabel ?? "Push to"}{" "}
+              {gitStatusForActions?.branch ?? "default"}
             </Button>
             <Button size="sm" onClick={checkoutFeatureBranchAndContinuePendingAction}>
               Checkout feature branch & continue

Comment thread apps/web/src/components/GitActionsControl.tsx Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Status override omits hasUpstream reset after branch checkout
    • Added hasUpstream: false, aheadCount: 0, and behindCount: 0 to the statusOverride so a newly created branch correctly reflects that it has no upstream tracking.

Create PR

Or push these changes by commenting:

@cursor push 96ecc90a4b
Preview (96ecc90a4b)
diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx
--- a/apps/web/src/components/GitActionsControl.tsx
+++ b/apps/web/src/components/GitActionsControl.tsx
@@ -445,7 +445,14 @@
       void checkoutPromise
         .then(() => {
           const statusOverride = gitStatusForActions
-            ? { ...gitStatusForActions, branch: branchName, pr: null }
+            ? {
+                ...gitStatusForActions,
+                branch: branchName,
+                pr: null,
+                hasUpstream: false,
+                aheadCount: 0,
+                behindCount: 0,
+              }
             : null;
           return runGitActionWithToast({
             ...actionParams,

Comment thread apps/web/src/components/GitActionsControl.tsx
@juliusmarminge
Copy link
Copy Markdown
Member Author

@cursor push 96ecc90

cursoragent and others added 3 commits March 2, 2026 03:20
…er branch checkout

Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>

Applied via @cursor push command
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Feature-branch fallback silently skips push for no-change scenarios
    • Added a remoteBranchExists check so the comparableBaseBranch guard only returns skipped_up_to_date when the branch already exists on the remote, ensuring newly created feature branches are always pushed.
  • ✅ Fixed: Unreachable commit case in dialog copy function
    • Removed the dead input.action === "commit" branch from resolveDefaultBranchActionDialogCopy since requiresDefaultBranchConfirmation only returns true for commit_push and commit_push_pr.

Create PR

Or push these changes by commenting:

@cursor push 9c881ee5f3
Preview (9c881ee5f3)
diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts
--- a/apps/server/src/git/Layers/GitCore.ts
+++ b/apps/server/src/git/Layers/GitCore.ts
@@ -581,7 +581,10 @@
         const comparableBaseBranch = yield* resolveBaseBranchForNoUpstream(cwd, branch).pipe(
           Effect.catch(() => Effect.succeed(null)),
         );
-        if (comparableBaseBranch) {
+        const branchExistsOnRemote = yield* remoteBranchExists(cwd, branch).pipe(
+          Effect.catch(() => Effect.succeed(false)),
+        );
+        if (comparableBaseBranch && branchExistsOnRemote) {
           return {
             status: "skipped_up_to_date" as const,
             branch,

diff --git a/apps/web/src/components/GitActionsControl.logic.ts b/apps/web/src/components/GitActionsControl.logic.ts
--- a/apps/web/src/components/GitActionsControl.logic.ts
+++ b/apps/web/src/components/GitActionsControl.logic.ts
@@ -290,14 +290,6 @@
   const branchLabel = input.branchName;
   const suffix = ` on "${branchLabel}". You can continue on this branch or create a feature branch and run the same action there.`;
 
-  if (input.action === "commit") {
-    return {
-      title: "Commit to default branch?",
-      description: `This action will commit changes${suffix}`,
-      continueLabel: `Commit to ${branchLabel}`,
-    };
-  }
-
   if (input.action === "commit_push") {
     if (input.includesCommit) {
       return {

Comment thread apps/server/src/git/Layers/GitCore.ts
Comment thread apps/web/src/components/GitActionsControl.logic.ts Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
Comment thread apps/web/src/components/GitActionsControl.tsx
@juliusmarminge juliusmarminge merged commit ba03241 into main Mar 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants