Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
83e713e
feat(workspaces): add workspace base branch functionality
Ipriyankrajai Feb 18, 2026
9c95749
feat(branch-config): implement base branch configuration utilities
Ipriyankrajai Feb 18, 2026
3a38966
feat(workspace-base-branch): implement effective base branch resoluti…
Ipriyankrajai Feb 18, 2026
e810957
refactor(workspace-creation): streamline base branch resolution in cr…
Ipriyankrajai Feb 18, 2026
e76bf39
chore(dependencies): update @superset/desktop version to 0.0.79
Ipriyankrajai Feb 19, 2026
9076563
fix(workspace-init): update worktree base branch in local database
Ipriyankrajai Feb 19, 2026
d75dfde
fix(ProjectSettings): disable branch selection when data is loading
Ipriyankrajai Feb 19, 2026
153706f
Merge remote-tracking branch 'upstream/main' into feat/default-worksp…
Ipriyankrajai Feb 19, 2026
171dd4a
fix(ProjectSettings): add loading placeholder to branch selection
Ipriyankrajai Feb 19, 2026
dde5725
fix(ProjectSettings): improve loading indication in branch selection
Ipriyankrajai Feb 19, 2026
67b7a6e
Merge remote-tracking branch 'upstream/main' into feat/default-worksp…
Ipriyankrajai Feb 19, 2026
c4f96a5
fix(desktop): clear stale baseBranch on branch switch
Kitenite Feb 20, 2026
c37275a
chore: regenerate workspace base branch migration
Kitenite Feb 20, 2026
b30d658
Lint
Kitenite Feb 20, 2026
4d9b03c
Merge remote-tracking branch 'origin' into feat/default-workspace-branch
Kitenite Feb 20, 2026
80bb601
chore: regenerate workspace base branch migration after merge
Kitenite Feb 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 42 additions & 16 deletions apps/desktop/src/lib/trpc/routers/changes/branches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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[] = [];
Expand All @@ -63,7 +81,7 @@ export const createBranchesRouter = () => {
remote: remote.sort(),
defaultBranch,
checkedOutBranches,
worktreeBaseBranch: gitConfigBase.trim() || null,
worktreeBaseBranch: configuredBaseBranch ?? persistedBaseBranch,
};
},
),
Expand All @@ -87,6 +105,7 @@ export const createBranchesRouter = () => {
.update(worktrees)
.set({
branch: input.branch,
baseBranch: null,
gitStatus,
})
.where(eq(worktrees.path, input.worktreePath))
Expand All @@ -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 };
}),
});
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/lib/trpc/routers/projects/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}),
Expand Down Expand Up @@ -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,
}),
Expand Down
92 changes: 73 additions & 19 deletions apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -170,6 +171,21 @@ interface HandleNewWorktreeParams {
workspaceName: string;
}

async function getKnownBranchesSafe(
repoPath: string,
): Promise<string[] | undefined> {
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,
Expand Down Expand Up @@ -206,15 +222,20 @@ 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)
.values({
projectId: project.id,
path: worktreePath,
branch: localBranchName,
baseBranch: defaultBranch,
baseBranch,
gitStatus: null,
})
.returning()
Expand All @@ -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({
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -1003,7 +1050,7 @@ export const createCreateProcedures = () => {
projectId: input.projectId,
path: ext.path,
branch,
baseBranch: defaultBranch,
baseBranch,
gitStatus: {
branch,
needsRebase: false,
Expand All @@ -1028,6 +1075,13 @@ export const createCreateProcedures = () => {
})
.run();

await setBranchBaseConfig({
repoPath: project.mainRepoPath,
branch,
baseBranch,
isExplicit: false,
});

copySupersetConfigToWorktree(project.mainRepoPath, ext.path);
imported++;
}
Expand Down
Loading
Loading