diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
index 1082135510a8bc5d96612c5c0056a29a3f474660..2297703ad2649e9b2a95ab0abe62acb293d340d3 100644
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -57,14 +57,12 @@ function editorLabel(editor: (typeof EDITORS)[number]): string {
const LAST_EDITOR_KEY = "t3code:last-editor";
const MAX_VISIBLE_WORK_LOG_ENTRIES = 6;
-const GIT_ACTION_ITEMS: Array<{
+
+interface GitActionMenuItem {
id: GitStackedAction;
label: string;
-}> = [
- { id: "commit", label: "Commit" },
- { id: "commit_push", label: "Commit & Push" },
- { id: "commit_push_pr", label: "Commit, Push & Create PR" },
-];
+ disabled: boolean;
+}
function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {
if (tone === "error") return "text-rose-300/50 dark:text-rose-300/50";
@@ -299,9 +297,74 @@ export default function ChatView() {
sandboxMode: "workspace-write",
} as const);
const gitCwd = activeThread?.worktreePath ?? activeProject?.cwd ?? null;
- const gitActionsDisabled =
+ const gitBaseDisabled =
!api || !gitCwd || !gitStatus || isGitActionRunning;
- const pushActionDisabled = gitActionsDisabled || gitStatus.branch === null;
+ const gitActionMenuItems = useMemo(() => {
+ if (!gitStatus) return [];
+
+ const hasBranch = gitStatus.branch !== null;
+ const canCommit = !gitBaseDisabled && gitStatus.hasWorkingTreeChanges;
+ const canPushOrPr = !gitBaseDisabled && hasBranch;
+
+ if (!hasBranch) {
+ return [
+ {
+ id: "commit",
+ label: "Commit",
+ disabled: !canCommit,
+ },
+ ];
+ }
+
+ if (gitStatus.hasWorkingTreeChanges) {
+ return [
+ {
+ id: "commit",
+ label: "Commit",
+ disabled: !canCommit,
+ },
+ {
+ id: "commit_push",
+ label: "Commit & Push",
+ disabled: !canPushOrPr,
+ },
+ {
+ id: "commit_push_pr",
+ label: "Commit, Push & Create PR",
+ disabled: !canPushOrPr,
+ },
+ ];
+ }
+
+ if (gitStatus.aheadCount > 0) {
+ return [
+ {
+ id: "commit",
+ label: "Commit",
+ disabled: true,
+ },
+ {
+ id: "commit_push",
+ label: "Commit & Push",
+ disabled: !canPushOrPr,
+ },
+ {
+ id: "commit_push_pr",
+ label: "Commit, Push & Create PR",
+ disabled: !canPushOrPr,
+ },
+ ];
+ }
+
+ const openPrDisabled = !canPushOrPr || gitStatus.behindCount > 0;
+ return [
+ {
+ id: "commit_push_pr",
+ label: "Open PR",
+ disabled: openPrDisabled,
+ },
+ ];
+ }, [gitBaseDisabled, gitStatus]);
useEffect(() => {
latestGitCwdRef.current = gitCwd;
@@ -809,15 +872,13 @@ export default function ChatView() {
Git actions
- {GIT_ACTION_ITEMS.map((item) => {
- const disabled =
- item.id === "commit" ? gitActionsDisabled : pushActionDisabled;
+ {gitActionMenuItems.map((item) => {
return (
)}
+ {gitStatus &&
+ gitStatus.branch !== null &&
+ !gitStatus.hasWorkingTreeChanges &&
+ gitStatus.aheadCount === 0 &&
+ gitStatus.behindCount > 0 && (
+
+ Branch is behind upstream. Pull/rebase before opening a PR.
+
+ )}
)}
@@ -858,13 +928,13 @@ export default function ChatView() {
)}
{gitActionError && (
-
+
{gitActionError}
)}
{gitActionNotice && (
-