diff --git a/apps/desktop/src/lib/trpc/routers/changes/branches.ts b/apps/desktop/src/lib/trpc/routers/changes/branches.ts
index 792d40f5929..2aebd3da028 100644
--- a/apps/desktop/src/lib/trpc/routers/changes/branches.ts
+++ b/apps/desktop/src/lib/trpc/routers/changes/branches.ts
@@ -4,6 +4,11 @@ import { localDb } from "main/lib/local-db";
import simpleGit from "simple-git";
import { z } from "zod";
import { publicProcedure, router } from "../..";
+import {
+ getBranchBaseConfig,
+ setBranchBaseConfig,
+ unsetBranchBaseConfig,
+} from "../workspaces/utils/base-branch-config";
import { getCurrentBranch } from "../workspaces/utils/git";
import {
assertRegisteredWorktree,
@@ -31,12 +36,25 @@ export const createBranchesRouter = () => {
const branchSummary = await git.branch(["-a"]);
const currentBranch = await getCurrentBranch(input.worktreePath);
-
- const gitConfigBase = currentBranch
- ? await git
- .raw(["config", `branch.${currentBranch}.base`])
- .catch(() => "")
- : "";
+ const { baseBranch: configuredBaseBranch } = currentBranch
+ ? await getBranchBaseConfig({
+ repoPath: input.worktreePath,
+ branch: currentBranch,
+ })
+ : { baseBranch: null };
+ const persistedWorktree = localDb
+ .select({
+ branch: worktrees.branch,
+ baseBranch: worktrees.baseBranch,
+ })
+ .from(worktrees)
+ .where(eq(worktrees.path, input.worktreePath))
+ .get();
+ const persistedBaseBranch =
+ persistedWorktree &&
+ (!currentBranch || persistedWorktree.branch === currentBranch)
+ ? (persistedWorktree.baseBranch?.trim() ?? null)
+ : null;
const localBranches: string[] = [];
const remote: string[] = [];
@@ -63,7 +81,7 @@ export const createBranchesRouter = () => {
remote: remote.sort(),
defaultBranch,
checkedOutBranches,
- worktreeBaseBranch: gitConfigBase.trim() || null,
+ worktreeBaseBranch: configuredBaseBranch ?? persistedBaseBranch,
};
},
),
@@ -87,6 +105,7 @@ export const createBranchesRouter = () => {
.update(worktrees)
.set({
branch: input.branch,
+ baseBranch: null,
gitStatus,
})
.where(eq(worktrees.path, input.worktreePath))
@@ -105,24 +124,31 @@ export const createBranchesRouter = () => {
.mutation(async ({ input }): Promise<{ success: boolean }> => {
assertRegisteredWorktree(input.worktreePath);
- const git = simpleGit(input.worktreePath);
const currentBranch = await getCurrentBranch(input.worktreePath);
if (!currentBranch) {
throw new Error("Could not determine current branch");
}
if (input.baseBranch) {
- await git.raw([
- "config",
- `branch.${currentBranch}.base`,
- input.baseBranch,
- ]);
+ await setBranchBaseConfig({
+ repoPath: input.worktreePath,
+ branch: currentBranch,
+ baseBranch: input.baseBranch,
+ isExplicit: true,
+ });
} else {
- await git
- .raw(["config", "--unset", `branch.${currentBranch}.base`])
- .catch(() => {});
+ await unsetBranchBaseConfig({
+ repoPath: input.worktreePath,
+ branch: currentBranch,
+ });
}
+ localDb
+ .update(worktrees)
+ .set({ baseBranch: input.baseBranch })
+ .where(eq(worktrees.path, input.worktreePath))
+ .run();
+
return { success: true };
}),
});
diff --git a/apps/desktop/src/lib/trpc/routers/projects/projects.ts b/apps/desktop/src/lib/trpc/routers/projects/projects.ts
index 7bca2aa2fe5..d1b668298ee 100644
--- a/apps/desktop/src/lib/trpc/routers/projects/projects.ts
+++ b/apps/desktop/src/lib/trpc/routers/projects/projects.ts
@@ -812,6 +812,7 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => {
.optional(),
branchPrefixMode: z.enum(BRANCH_PREFIX_MODES).nullable().optional(),
branchPrefixCustom: z.string().nullable().optional(),
+ workspaceBaseBranch: z.string().nullable().optional(),
hideImage: z.boolean().optional(),
defaultApp: z.enum(EXTERNAL_APPS).nullable().optional(),
}),
@@ -840,6 +841,9 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => {
...(input.patch.branchPrefixCustom !== undefined && {
branchPrefixCustom: input.patch.branchPrefixCustom,
}),
+ ...(input.patch.workspaceBaseBranch !== undefined && {
+ workspaceBaseBranch: input.patch.workspaceBaseBranch,
+ }),
...(input.patch.hideImage !== undefined && {
hideImage: input.patch.hideImage,
}),
diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
index 9b8b8d62651..90abbcd4741 100644
--- a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
+++ b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
@@ -6,9 +6,10 @@ import { track } from "main/lib/analytics";
import { localDb } from "main/lib/local-db";
import { workspaceInitManager } from "main/lib/workspace-init-manager";
import { SUPERSET_DIR_NAME, WORKTREES_DIR_NAME } from "shared/constants";
-import simpleGit from "simple-git";
import { z } from "zod";
import { publicProcedure, router } from "../../..";
+import { resolveWorkspaceBaseBranch } from "../utils/base-branch";
+import { setBranchBaseConfig } from "../utils/base-branch-config";
import {
activateProject,
findOrphanedWorktreeByBranch,
@@ -170,6 +171,21 @@ interface HandleNewWorktreeParams {
workspaceName: string;
}
+async function getKnownBranchesSafe(
+ repoPath: string,
+): Promise {
+ try {
+ const { local, remote } = await listBranches(repoPath);
+ return [...local, ...remote];
+ } catch (error) {
+ console.warn(
+ `[workspaces/create] Failed to list branches for ${repoPath}:`,
+ error,
+ );
+ return undefined;
+ }
+}
+
async function handleNewWorktree({
project,
prInfo,
@@ -206,7 +222,12 @@ async function handleNewWorktree({
localBranchName,
});
- const defaultBranch = project.defaultBranch || "main";
+ const knownBranches = await getKnownBranchesSafe(project.mainRepoPath);
+ const baseBranch = resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: project.workspaceBaseBranch,
+ defaultBranch: project.defaultBranch,
+ knownBranches,
+ });
const worktree = localDb
.insert(worktrees)
@@ -214,7 +235,7 @@ async function handleNewWorktree({
projectId: project.id,
path: worktreePath,
branch: localBranchName,
- baseBranch: defaultBranch,
+ baseBranch,
gitStatus: null,
})
.returning()
@@ -233,14 +254,18 @@ async function handleNewWorktree({
workspace_id: workspace.id,
project_id: project.id,
branch: localBranchName,
+ base_branch: baseBranch,
source: "pr",
pr_number: prInfo.number,
is_fork: prInfo.isCrossRepository,
});
- await simpleGit(project.mainRepoPath)
- .raw(["config", `branch.${localBranchName}.base`, defaultBranch])
- .catch(() => {});
+ await setBranchBaseConfig({
+ repoPath: project.mainRepoPath,
+ branch: localBranchName,
+ baseBranch,
+ isExplicit: false,
+ });
workspaceInitManager.startJob(workspace.id, project.id);
initializeWorkspaceWorktree({
@@ -420,8 +445,12 @@ export const createCreateProcedures = () => {
branch,
);
- const defaultBranch = project.defaultBranch || "main";
- const targetBranch = input.baseBranch || defaultBranch;
+ const targetBranch = resolveWorkspaceBaseBranch({
+ explicitBaseBranch: input.baseBranch,
+ workspaceBaseBranch: project.workspaceBaseBranch,
+ defaultBranch: project.defaultBranch,
+ knownBranches: existingBranches,
+ });
const worktree = localDb
.insert(worktrees)
@@ -462,9 +491,12 @@ export const createCreateProcedures = () => {
use_existing_branch: input.useExistingBranch ?? false,
});
- await simpleGit(project.mainRepoPath)
- .raw(["config", `branch.${branch}.base`, targetBranch])
- .catch(() => {});
+ await setBranchBaseConfig({
+ repoPath: project.mainRepoPath,
+ branch,
+ baseBranch: targetBranch,
+ isExplicit: Boolean(input.baseBranch?.trim()),
+ });
workspaceInitManager.startJob(workspace.id, input.projectId);
initializeWorkspaceWorktree({
@@ -805,14 +837,20 @@ export const createCreateProcedures = () => {
};
}
- const defaultBranch = project.defaultBranch || "main";
+ const knownBranches = await getKnownBranchesSafe(project.mainRepoPath);
+ const baseBranch = resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: project.workspaceBaseBranch,
+ defaultBranch: project.defaultBranch,
+ knownBranches,
+ });
+
const worktree = localDb
.insert(worktrees)
.values({
projectId: input.projectId,
path: input.worktreePath,
branch: input.branch,
- baseBranch: defaultBranch,
+ baseBranch,
gitStatus: {
branch: input.branch,
needsRebase: false,
@@ -841,10 +879,6 @@ export const createCreateProcedures = () => {
setLastActiveWorkspace(workspace.id);
activateProject(project);
- await simpleGit(project.mainRepoPath)
- .raw(["config", `branch.${input.branch}.base`, defaultBranch])
- .catch(() => {});
-
copySupersetConfigToWorktree(project.mainRepoPath, input.worktreePath);
const setupConfig = loadSetupConfig({
mainRepoPath: project.mainRepoPath,
@@ -856,9 +890,17 @@ export const createCreateProcedures = () => {
workspace_id: workspace.id,
project_id: project.id,
branch: input.branch,
+ base_branch: baseBranch,
source: "external_import",
});
+ await setBranchBaseConfig({
+ repoPath: project.mainRepoPath,
+ branch: input.branch,
+ baseBranch,
+ isExplicit: false,
+ });
+
return {
workspace,
initialCommands: setupConfig?.setup || null,
@@ -933,6 +975,12 @@ export const createCreateProcedures = () => {
if (!project) {
throw new Error(`Project ${input.projectId} not found`);
}
+ const knownBranches = await getKnownBranchesSafe(project.mainRepoPath);
+ const baseBranch = resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: project.workspaceBaseBranch,
+ defaultBranch: project.defaultBranch,
+ knownBranches,
+ });
let imported = 0;
@@ -982,7 +1030,6 @@ export const createCreateProcedures = () => {
project.mainRepoPath,
);
const trackedPaths = new Set(projectWorktrees.map((wt) => wt.path));
- const defaultBranch = project.defaultBranch || "main";
const externalWorktrees = allExternalWorktrees.filter((wt) => {
if (wt.path === project.mainRepoPath) return false;
@@ -1003,7 +1050,7 @@ export const createCreateProcedures = () => {
projectId: input.projectId,
path: ext.path,
branch,
- baseBranch: defaultBranch,
+ baseBranch,
gitStatus: {
branch,
needsRebase: false,
@@ -1028,6 +1075,13 @@ export const createCreateProcedures = () => {
})
.run();
+ await setBranchBaseConfig({
+ repoPath: project.mainRepoPath,
+ branch,
+ baseBranch,
+ isExplicit: false,
+ });
+
copySupersetConfigToWorktree(project.mainRepoPath, ext.path);
imported++;
}
diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch-config.ts b/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch-config.ts
new file mode 100644
index 00000000000..cc012481aa8
--- /dev/null
+++ b/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch-config.ts
@@ -0,0 +1,80 @@
+import simpleGit from "simple-git";
+
+interface BranchConfigParams {
+ repoPath: string;
+ branch: string;
+}
+
+interface SetBranchBaseConfigParams extends BranchConfigParams {
+ baseBranch: string;
+ isExplicit: boolean;
+}
+
+interface BranchBaseConfig {
+ baseBranch: string | null;
+ isExplicit: boolean;
+}
+
+function parseBooleanConfig(value: string): boolean {
+ const normalized = value.trim().toLowerCase();
+ return (
+ normalized === "true" ||
+ normalized === "yes" ||
+ normalized === "on" ||
+ normalized === "1"
+ );
+}
+
+export async function getBranchBaseConfig({
+ repoPath,
+ branch,
+}: BranchConfigParams): Promise {
+ const git = simpleGit(repoPath);
+ const [baseOutput, explicitOutput] = await Promise.all([
+ git.raw(["config", `branch.${branch}.base`]).catch(() => ""),
+ git
+ .raw(["config", "--bool", `branch.${branch}.base-explicit`])
+ .catch(() => ""),
+ ]);
+
+ return {
+ baseBranch: baseOutput.trim() || null,
+ isExplicit: parseBooleanConfig(explicitOutput),
+ };
+}
+
+export async function setBranchBaseConfig({
+ repoPath,
+ branch,
+ baseBranch,
+ isExplicit,
+}: SetBranchBaseConfigParams): Promise {
+ const git = simpleGit(repoPath);
+
+ await git
+ .raw(["config", `branch.${branch}.base`, baseBranch])
+ .catch(() => {});
+ if (isExplicit) {
+ await git
+ .raw(["config", "--bool", `branch.${branch}.base-explicit`, "true"])
+ .catch(() => {});
+ return;
+ }
+
+ await git
+ .raw(["config", "--unset", `branch.${branch}.base-explicit`])
+ .catch(() => {});
+}
+
+export async function unsetBranchBaseConfig({
+ repoPath,
+ branch,
+}: BranchConfigParams): Promise {
+ const git = simpleGit(repoPath);
+ await Promise.all([
+ git.raw(["config", "--unset", `branch.${branch}.base`]).catch(() => {}),
+ git
+ .raw(["config", "--unset", `branch.${branch}.base-explicit`])
+ .catch(() => {}),
+ ]);
+}
diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch.test.ts b/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch.test.ts
new file mode 100644
index 00000000000..a4b11befd0e
--- /dev/null
+++ b/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch.test.ts
@@ -0,0 +1,56 @@
+import { describe, expect, test } from "bun:test";
+import { resolveWorkspaceBaseBranch } from "./base-branch";
+
+describe("resolveWorkspaceBaseBranch", () => {
+ test("uses explicit base branch when provided", () => {
+ const resolved = resolveWorkspaceBaseBranch({
+ explicitBaseBranch: "release/2026-q1",
+ workspaceBaseBranch: "feature/long-lived",
+ defaultBranch: "main",
+ knownBranches: ["main", "feature/long-lived"],
+ });
+
+ expect(resolved).toBe("release/2026-q1");
+ });
+
+ test("falls back to project workspace base branch when explicit is absent", () => {
+ const resolved = resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: "feature/long-lived",
+ defaultBranch: "main",
+ knownBranches: ["main", "feature/long-lived"],
+ });
+
+ expect(resolved).toBe("feature/long-lived");
+ });
+
+ test("falls back to repository default branch when project preference is absent", () => {
+ const resolved = resolveWorkspaceBaseBranch({
+ defaultBranch: "main",
+ knownBranches: ["main", "feature/long-lived"],
+ });
+
+ expect(resolved).toBe("main");
+ });
+
+ test("falls back to repository default when stored preference is stale", () => {
+ const resolved = resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: "feature/deleted",
+ defaultBranch: "main",
+ knownBranches: ["main", "feature/long-lived"],
+ });
+
+ expect(resolved).toBe("main");
+ });
+
+ test("uses workspace base branch when knownBranches is unavailable (offline)", () => {
+ const resolved = resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: "feature/long-lived",
+ defaultBranch: "main",
+ });
+ expect(resolved).toBe("feature/long-lived");
+ });
+ test('falls back to "main" when no defaultBranch or workspaceBaseBranch is provided', () => {
+ const resolved = resolveWorkspaceBaseBranch({});
+ expect(resolved).toBe("main");
+ });
+});
diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch.ts b/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch.ts
new file mode 100644
index 00000000000..6c50d7d82b0
--- /dev/null
+++ b/apps/desktop/src/lib/trpc/routers/workspaces/utils/base-branch.ts
@@ -0,0 +1,38 @@
+interface ResolveWorkspaceBaseBranchParams {
+ explicitBaseBranch?: string;
+ workspaceBaseBranch?: string | null;
+ defaultBranch?: string | null;
+ knownBranches?: string[];
+}
+
+function normalizeBranch(value?: string | null): string | undefined {
+ const trimmed = value?.trim();
+ return trimmed ? trimmed : undefined;
+}
+
+export function resolveWorkspaceBaseBranch({
+ explicitBaseBranch,
+ workspaceBaseBranch,
+ defaultBranch,
+ knownBranches,
+}: ResolveWorkspaceBaseBranchParams): string {
+ const fallbackBranch = normalizeBranch(defaultBranch) ?? "main";
+ const explicit = normalizeBranch(explicitBaseBranch);
+ if (explicit) {
+ return explicit;
+ }
+
+ const preferred = normalizeBranch(workspaceBaseBranch);
+ if (!preferred) {
+ return fallbackBranch;
+ }
+
+ if (knownBranches?.length) {
+ const knownBranchSet = new Set(knownBranches);
+ if (!knownBranchSet.has(preferred)) {
+ return fallbackBranch;
+ }
+ }
+
+ return preferred;
+}
diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/utils/workspace-init.ts b/apps/desktop/src/lib/trpc/routers/workspaces/utils/workspace-init.ts
index cd33faa4e22..200b7756a79 100644
--- a/apps/desktop/src/lib/trpc/routers/workspaces/utils/workspace-init.ts
+++ b/apps/desktop/src/lib/trpc/routers/workspaces/utils/workspace-init.ts
@@ -4,7 +4,8 @@ import { track } from "main/lib/analytics";
import { localDb } from "main/lib/local-db";
import { workspaceInitManager } from "main/lib/workspace-init-manager";
import type { WorkspaceInitStep } from "shared/types/workspace-init";
-import simpleGit from "simple-git";
+import { resolveWorkspaceBaseBranch } from "./base-branch";
+import { getBranchBaseConfig, setBranchBaseConfig } from "./base-branch-config";
import {
branchExistsOnRemote,
createWorktree,
@@ -65,12 +66,17 @@ export async function initializeWorkspaceWorktree({
.where(eq(projects.id, projectId))
.get();
- const gitConfigBase = await simpleGit(mainRepoPath)
- .raw(["config", `branch.${branch}.base`])
- .catch(() => "");
- const baseBranchWasConfigured = !!gitConfigBase.trim();
+ const { baseBranch: gitConfigBase, isExplicit: baseBranchWasExplicit } =
+ await getBranchBaseConfig({
+ repoPath: mainRepoPath,
+ branch,
+ });
let effectiveBaseBranch =
- gitConfigBase.trim() || project?.defaultBranch || "main";
+ gitConfigBase ||
+ resolveWorkspaceBaseBranch({
+ workspaceBaseBranch: project?.workspaceBaseBranch,
+ defaultBranch: project?.defaultBranch,
+ });
if (useExistingBranch) {
if (skipWorktreeCreation) {
@@ -198,7 +204,7 @@ export async function initializeWorkspaceWorktree({
return { ref: effectiveBaseBranch };
}
- if (baseBranchWasConfigured) {
+ if (baseBranchWasExplicit) {
console.log(
`[workspace-init] ${reason}. Base branch "${effectiveBaseBranch}" was explicitly set, not using fallback.`,
);
@@ -246,9 +252,17 @@ export async function initializeWorkspaceWorktree({
`[workspace-init] Updating baseBranch from "${originalBranch}" to "${result.fallbackBranch}" for workspace ${workspaceId}`,
);
effectiveBaseBranch = result.fallbackBranch;
- await simpleGit(mainRepoPath)
- .raw(["config", `branch.${branch}.base`, result.fallbackBranch])
- .catch(() => {});
+ await setBranchBaseConfig({
+ repoPath: mainRepoPath,
+ branch,
+ baseBranch: result.fallbackBranch,
+ isExplicit: false,
+ });
+ localDb
+ .update(worktrees)
+ .set({ baseBranch: result.fallbackBranch })
+ .where(eq(worktrees.id, worktreeId))
+ .run();
manager.updateProgress(
workspaceId,
progressStep,
@@ -299,7 +313,7 @@ export async function initializeWorkspaceWorktree({
workspaceId,
"failed",
"No local reference available",
- baseBranchWasConfigured
+ baseBranchWasExplicit
? `${failureDetail} and branch "${effectiveBaseBranch}" doesn't exist locally.${isNetworkError ? " Please check your network connection and try again." : " Please try again with a different base branch."}`
: `${failureDetail} and no local ref for "${effectiveBaseBranch}" exists.${isNetworkError ? " Please check your network connection and try again." : ""}`,
);
@@ -318,7 +332,7 @@ export async function initializeWorkspaceWorktree({
workspaceId,
"failed",
"No local reference available",
- baseBranchWasConfigured
+ baseBranchWasExplicit
? `No remote configured and branch "${effectiveBaseBranch}" doesn't exist locally.`
: `No remote configured and no local ref for "${effectiveBaseBranch}" exists.`,
);
diff --git a/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx b/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
index f3d537e19f8..fdec50533bf 100644
--- a/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
+++ b/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
@@ -39,6 +39,7 @@ import {
import { LuFolderOpen } from "react-icons/lu";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { formatRelativeTime } from "renderer/lib/formatRelativeTime";
+import { resolveEffectiveWorkspaceBaseBranch } from "renderer/lib/workspaceBaseBranch";
import { useOpenProject } from "renderer/react-query/projects";
import { useCreateWorkspace } from "renderer/react-query/workspaces";
import {
@@ -126,7 +127,12 @@ export function NewWorkspaceModal() {
}
}, [isOpen, selectedProjectId, preSelectedProjectId]);
- const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? null;
+ const effectiveBaseBranch = resolveEffectiveWorkspaceBaseBranch({
+ explicitBaseBranch: baseBranch,
+ workspaceBaseBranch: project?.workspaceBaseBranch,
+ defaultBranch: branchData?.defaultBranch,
+ branches: branchData?.branches,
+ });
// biome-ignore lint/correctness/useExhaustiveDependencies: intentionally reset when project changes
useEffect(() => {
@@ -227,7 +233,7 @@ export function NewWorkspaceModal() {
projectId: selectedProjectId,
name: workspaceName,
branchName: branchSlug || undefined,
- baseBranch: effectiveBaseBranch || undefined,
+ baseBranch: baseBranch || undefined,
applyPrefix,
});
@@ -355,7 +361,7 @@ export function NewWorkspaceModal() {
{branchPreview || "branch-name"}
- from {effectiveBaseBranch}
+ from {effectiveBaseBranch ?? "..."}
)}
diff --git a/apps/desktop/src/renderer/lib/workspaceBaseBranch/index.ts b/apps/desktop/src/renderer/lib/workspaceBaseBranch/index.ts
new file mode 100644
index 00000000000..96c9a161289
--- /dev/null
+++ b/apps/desktop/src/renderer/lib/workspaceBaseBranch/index.ts
@@ -0,0 +1 @@
+export { resolveEffectiveWorkspaceBaseBranch } from "./workspaceBaseBranch";
diff --git a/apps/desktop/src/renderer/lib/workspaceBaseBranch/workspaceBaseBranch.test.ts b/apps/desktop/src/renderer/lib/workspaceBaseBranch/workspaceBaseBranch.test.ts
new file mode 100644
index 00000000000..92c4d76783f
--- /dev/null
+++ b/apps/desktop/src/renderer/lib/workspaceBaseBranch/workspaceBaseBranch.test.ts
@@ -0,0 +1,72 @@
+import { describe, expect, test } from "bun:test";
+import { resolveEffectiveWorkspaceBaseBranch } from "./workspaceBaseBranch";
+
+describe("resolveEffectiveWorkspaceBaseBranch", () => {
+ test("prefers explicit base branch", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({
+ explicitBaseBranch: "release/2026-q1",
+ workspaceBaseBranch: "feature/preferred",
+ defaultBranch: "main",
+ branches: [{ name: "main" }, { name: "feature/preferred" }],
+ });
+
+ expect(resolved).toBe("release/2026-q1");
+ });
+
+ test("uses workspace base branch when branch exists", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({
+ workspaceBaseBranch: "feature/preferred",
+ defaultBranch: "main",
+ branches: [{ name: "main" }, { name: "feature/preferred" }],
+ });
+
+ expect(resolved).toBe("feature/preferred");
+ });
+
+ test("falls back to default branch when workspace branch is stale", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({
+ workspaceBaseBranch: "feature/deleted",
+ defaultBranch: "main",
+ branches: [{ name: "main" }, { name: "feature/preferred" }],
+ });
+
+ expect(resolved).toBe("main");
+ });
+
+ test("returns null when nothing resolves", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({});
+
+ expect(resolved).toBeNull();
+ });
+
+ test("trusts workspace base branch when branches are undefined (offline/loading)", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({
+ workspaceBaseBranch: "develop",
+ defaultBranch: "main",
+ branches: undefined,
+ });
+
+ expect(resolved).toBe("develop");
+ });
+
+ test("falls back to default branch when branches array is empty", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({
+ workspaceBaseBranch: "develop",
+ defaultBranch: "main",
+ branches: [],
+ });
+
+ expect(resolved).toBe("main");
+ });
+
+ test("ignores empty string explicit base branch", () => {
+ const resolved = resolveEffectiveWorkspaceBaseBranch({
+ explicitBaseBranch: "",
+ workspaceBaseBranch: "develop",
+ defaultBranch: "main",
+ branches: [{ name: "main" }, { name: "develop" }],
+ });
+
+ expect(resolved).toBe("develop");
+ });
+});
diff --git a/apps/desktop/src/renderer/lib/workspaceBaseBranch/workspaceBaseBranch.ts b/apps/desktop/src/renderer/lib/workspaceBaseBranch/workspaceBaseBranch.ts
new file mode 100644
index 00000000000..bfce5c50571
--- /dev/null
+++ b/apps/desktop/src/renderer/lib/workspaceBaseBranch/workspaceBaseBranch.ts
@@ -0,0 +1,39 @@
+interface BranchLike {
+ name: string;
+}
+
+interface ResolveEffectiveWorkspaceBaseBranchParams {
+ explicitBaseBranch?: string | null;
+ workspaceBaseBranch?: string | null;
+ defaultBranch?: string | null;
+ branches?: BranchLike[];
+}
+
+function normalizeBranch(value?: string | null): string | null {
+ const trimmed = value?.trim();
+ return trimmed ? trimmed : null;
+}
+
+export function resolveEffectiveWorkspaceBaseBranch({
+ explicitBaseBranch,
+ workspaceBaseBranch,
+ defaultBranch,
+ branches,
+}: ResolveEffectiveWorkspaceBaseBranchParams): string | null {
+ const explicit = normalizeBranch(explicitBaseBranch);
+ if (explicit) {
+ return explicit;
+ }
+
+ const preferred = normalizeBranch(workspaceBaseBranch);
+ if (preferred) {
+ if (!branches) {
+ return preferred;
+ }
+ if (branches.some((branch) => branch.name === preferred)) {
+ return preferred;
+ }
+ }
+
+ return normalizeBranch(defaultBranch);
+}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx
index bb52a707360..4ec263e0c91 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx
@@ -18,6 +18,7 @@ import { HiCheck, HiChevronDown, HiChevronUpDown } from "react-icons/hi2";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { formatRelativeTime } from "renderer/lib/formatRelativeTime";
import { electronTrpcClient as trpcClient } from "renderer/lib/trpc-client";
+import { resolveEffectiveWorkspaceBaseBranch } from "renderer/lib/workspaceBaseBranch";
import { useCreateWorkspace } from "renderer/react-query/workspaces";
import { NotFound } from "renderer/routes/not-found";
import { sanitizeSegment } from "shared/utils/branch";
@@ -102,7 +103,12 @@ function ProjectPage() {
);
}, [branchData?.branches, branchSearch]);
- const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? null;
+ const effectiveBaseBranch = resolveEffectiveWorkspaceBaseBranch({
+ explicitBaseBranch: baseBranch,
+ workspaceBaseBranch: project?.workspaceBaseBranch,
+ defaultBranch: branchData?.defaultBranch,
+ branches: branchData?.branches,
+ });
useEffect(() => {
const timer = setTimeout(() => {
@@ -130,7 +136,7 @@ function ProjectPage() {
projectId,
name: workspaceName,
branchName: generatedBranchName || undefined,
- baseBranch: effectiveBaseBranch || undefined,
+ baseBranch: baseBranch || undefined,
});
toast.success("Workspace created", {
diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/list-projects.ts b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/list-projects.ts
index 5e04117be60..4456aa594e0 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/list-projects.ts
+++ b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/list-projects.ts
@@ -21,6 +21,7 @@ async function execute(
name: p.name,
mainRepoPath: p.mainRepoPath,
defaultBranch: p.defaultBranch,
+ workspaceBaseBranch: p.workspaceBaseBranch,
color: p.color,
lastOpenedAt: p.lastOpenedAt,
tabOrder: p.tabOrder,
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/project/$projectId/components/ProjectSettings/ProjectSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/project/$projectId/components/ProjectSettings/ProjectSettings.tsx
index 1620f51f84e..e63b06f7989 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/settings/project/$projectId/components/ProjectSettings/ProjectSettings.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/project/$projectId/components/ProjectSettings/ProjectSettings.tsx
@@ -24,6 +24,8 @@ import { ClickablePath } from "../../../../components/ClickablePath";
import { BRANCH_PREFIX_MODE_LABELS_WITH_DEFAULT } from "../../../../utils/branch-prefix";
import { ScriptsEditor } from "./components/ScriptsEditor";
+const REPO_DEFAULT_BASE_BRANCH = "__repo_default__";
+
export function SettingsSection({
icon,
title,
@@ -60,6 +62,11 @@ export function ProjectSettings({ projectId }: ProjectSettingsProps) {
const { data: project } = electronTrpc.projects.get.useQuery({
id: projectId,
});
+ const { data: branchData, isLoading: isBranchDataLoading } =
+ electronTrpc.projects.getBranches.useQuery(
+ { projectId },
+ { enabled: !!projectId },
+ );
const { data: gitAuthor } = electronTrpc.projects.getGitAuthor.useQuery({
id: projectId,
});
@@ -155,6 +162,15 @@ export function ProjectSettings({ projectId }: ProjectSettingsProps) {
});
};
+ const handleWorkspaceBaseBranchChange = (value: string) => {
+ updateProject.mutate({
+ id: projectId,
+ patch: {
+ workspaceBaseBranch: value === REPO_DEFAULT_BASE_BRANCH ? null : value,
+ },
+ });
+ };
+
const getPreviewPrefix = (
mode: BranchPrefixMode | "default",
): string | null => {
@@ -182,6 +198,17 @@ export function ProjectSettings({ projectId }: ProjectSettingsProps) {
const currentMode = project.branchPrefixMode ?? "default";
const previewPrefix = getPreviewPrefix(currentMode);
+ const repoDefaultBranch =
+ branchData?.defaultBranch ?? project.defaultBranch ?? "main";
+ const workspaceBaseBranchValue =
+ project.workspaceBaseBranch ?? REPO_DEFAULT_BASE_BRANCH;
+ const workspaceBaseBranchMissing =
+ !isBranchDataLoading &&
+ !!project.workspaceBaseBranch &&
+ !!branchData &&
+ !branchData.branches.some(
+ (branch) => branch.name === project.workspaceBaseBranch,
+ );
return (
@@ -244,6 +271,56 @@ export function ProjectSettings({ projectId }: ProjectSettingsProps) {
+ }
+ title="Workspace Base Branch"
+ description="Set the default base branch for new workspaces in this repository."
+ >
+
+
+
+
+ Used when creating a workspace unless you choose a one-off base
+ branch.
+
+
+
+
+ {workspaceBaseBranchMissing && (
+
+ Branch "{project.workspaceBaseBranch}" no longer exists. New
+ workspaces will fall back to "{repoDefaultBranch}".
+
+ )}
+
+
diff --git a/biome.jsonc b/biome.jsonc
index 101e9c7eaf5..0f49d9ccf3c 100644
--- a/biome.jsonc
+++ b/biome.jsonc
@@ -1,5 +1,5 @@
{
- "$schema": "https://biomejs.dev/schemas/2.4.2/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
diff --git a/packages/local-db/drizzle/0029_add_workspace_base_branch.sql b/packages/local-db/drizzle/0029_add_workspace_base_branch.sql
new file mode 100644
index 00000000000..08d855a7b68
--- /dev/null
+++ b/packages/local-db/drizzle/0029_add_workspace_base_branch.sql
@@ -0,0 +1 @@
+ALTER TABLE `projects` ADD `workspace_base_branch` text;
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/0029_snapshot.json b/packages/local-db/drizzle/meta/0029_snapshot.json
new file mode 100644
index 00000000000..6c6a4afd754
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0029_snapshot.json
@@ -0,0 +1,1224 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "63568533-c315-4206-a426-d69f23b77874",
+ "prevId": "64e86972-e7d7-435c-8d6e-402b74ba1432",
+ "tables": {
+ "browser_history": {
+ "name": "browser_history",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "favicon_url": {
+ "name": "favicon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_visited_at": {
+ "name": "last_visited_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "visit_count": {
+ "name": "visit_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ }
+ },
+ "indexes": {
+ "browser_history_url_unique": {
+ "name": "browser_history_url_unique",
+ "columns": [
+ "url"
+ ],
+ "isUnique": true
+ },
+ "browser_history_url_idx": {
+ "name": "browser_history_url_idx",
+ "columns": [
+ "url"
+ ],
+ "isUnique": false
+ },
+ "browser_history_last_visited_at_idx": {
+ "name": "browser_history_last_visited_at_idx",
+ "columns": [
+ "last_visited_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "workspace_base_branch": {
+ "name": "workspace_base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_owner": {
+ "name": "github_owner",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_mode": {
+ "name": "branch_prefix_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_custom": {
+ "name": "branch_prefix_custom",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "hide_image": {
+ "name": "hide_image",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "neon_project_id": {
+ "name": "neon_project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_app": {
+ "name": "default_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "persist_terminal": {
+ "name": "persist_terminal",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "auto_apply_default_preset": {
+ "name": "auto_apply_default_preset",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_mode": {
+ "name": "branch_prefix_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_custom": {
+ "name": "branch_prefix_custom",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "notification_sounds_muted": {
+ "name": "notification_sounds_muted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "delete_local_branch": {
+ "name": "delete_local_branch",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "file_open_mode": {
+ "name": "file_open_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "show_presets_bar": {
+ "name": "show_presets_bar",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_font_family": {
+ "name": "terminal_font_family",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_font_size": {
+ "name": "terminal_font_size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "editor_font_family": {
+ "name": "editor_font_family",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "editor_font_size": {
+ "name": "editor_font_size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "show_resource_monitor": {
+ "name": "show_resource_monitor",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_unread": {
+ "name": "is_unread",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_unnamed": {
+ "name": "is_unnamed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "deleting_at": {
+ "name": "deleting_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "port_base": {
+ "name": "port_base",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/_journal.json b/packages/local-db/drizzle/meta/_journal.json
index 3ba8be371e9..6612d90fdbb 100644
--- a/packages/local-db/drizzle/meta/_journal.json
+++ b/packages/local-db/drizzle/meta/_journal.json
@@ -204,6 +204,13 @@
"when": 1771539756028,
"tag": "0028_add_show_resource_monitor_setting",
"breakpoints": true
+ },
+ {
+ "idx": 29,
+ "version": "6",
+ "when": 1771547603399,
+ "tag": "0029_add_workspace_base_branch",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/local-db/src/schema/schema.ts b/packages/local-db/src/schema/schema.ts
index 5d615ad1195..1c617ef78ba 100644
--- a/packages/local-db/src/schema/schema.ts
+++ b/packages/local-db/src/schema/schema.ts
@@ -35,6 +35,7 @@ export const projects = sqliteTable(
mode: "boolean",
}),
defaultBranch: text("default_branch"),
+ workspaceBaseBranch: text("workspace_base_branch"),
githubOwner: text("github_owner"),
branchPrefixMode: text("branch_prefix_mode").$type(),
branchPrefixCustom: text("branch_prefix_custom"),