Skip to content
1 change: 1 addition & 0 deletions apps/desktop/src/lib/trpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ const sentryMiddleware = t.middleware(async ({ next, path, type }) => {
});

export const router = t.router;
export const mergeRouters = t.mergeRouters;
export const publicProcedure = t.procedure.use(sentryMiddleware);
export const trpc = createTRPCReact<AppRouter>();
105 changes: 105 additions & 0 deletions apps/desktop/src/lib/trpc/routers/workspaces/procedures/branch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { projects, workspaces } from "@superset/local-db";
import { eq } from "drizzle-orm";
import { localDb } from "main/lib/local-db";
import { terminalManager } from "main/lib/terminal";
import { z } from "zod";
import { publicProcedure, router } from "../../..";
import {
getBranchWorkspace,
getWorkspace,
setLastActiveWorkspace,
touchWorkspace,
} from "../utils/db-helpers";
import { listBranches, safeCheckoutBranch } from "../utils/git";

export const createBranchProcedures = () => {
return router({
getBranches: publicProcedure
.input(
z.object({
projectId: z.string(),
fetch: z.boolean().optional(), // Whether to fetch remote refs (default: false, avoids UI stalls)
}),
)
.query(async ({ input }) => {
const project = localDb
.select()
.from(projects)
.where(eq(projects.id, input.projectId))
.get();
if (!project) {
throw new Error(`Project ${input.projectId} not found`);
}

const branches = await listBranches(project.mainRepoPath, {
fetch: input.fetch,
});

// Get branches that are in use by worktrees, with their workspace IDs
const projectWorkspaces = localDb
.select()
.from(workspaces)
.where(eq(workspaces.projectId, input.projectId))
.all();
const worktreeBranchMap: Record<string, string> = {};
for (const ws of projectWorkspaces) {
if (ws.type === "worktree" && ws.branch) {
worktreeBranchMap[ws.branch] = ws.id;
}
}

return {
...branches,
inUse: Object.keys(worktreeBranchMap),
inUseWorkspaces: worktreeBranchMap, // branch -> workspaceId
};
}),
Comment thread
Kitenite marked this conversation as resolved.

// Switch an existing branch workspace to a different branch
switchBranchWorkspace: publicProcedure
.input(
z.object({
projectId: z.string(),
branch: z.string(),
}),
)
.mutation(async ({ input }) => {
const project = localDb
.select()
.from(projects)
.where(eq(projects.id, input.projectId))
.get();
if (!project) {
throw new Error(`Project ${input.projectId} not found`);
}

const workspace = getBranchWorkspace(input.projectId);
if (!workspace) {
throw new Error("No branch workspace found for this project");
}

// Checkout the new branch with safety checks (terminals continue running on the new branch)
await safeCheckoutBranch(project.mainRepoPath, input.branch);

// Send newline to terminals so their prompts refresh with new branch
terminalManager.refreshPromptsForWorkspace(workspace.id);

// Update the workspace - name is always the branch for branch workspaces
touchWorkspace(workspace.id, {
branch: input.branch,
name: input.branch,
});
setLastActiveWorkspace(workspace.id);

const updatedWorkspace = getWorkspace(workspace.id);
if (!updatedWorkspace) {
throw new Error(`Workspace ${workspace.id} not found after update`);
}

return {
workspace: updatedWorkspace,
worktreePath: project.mainRepoPath,
};
}),
});
};
Loading