From 4aaed77705081009fe9e3d7025b545108f7db205 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 28 Feb 2026 18:32:21 -0800 Subject: Remove git worktree/branch management Session-scoped artifacts already solve the concurrent session problem. No need for worktrees or branches. Simpler is better. --- src/main/git/worktree.ts | 179 ----------------------------------------------- src/main/ipc/handlers.ts | 45 ++---------- src/main/preload.ts | 18 +---- 3 files changed, 5 insertions(+), 237 deletions(-) delete mode 100644 src/main/git/worktree.ts diff --git a/src/main/git/worktree.ts b/src/main/git/worktree.ts deleted file mode 100644 index 3264e5e..0000000 --- a/src/main/git/worktree.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { execSync } from "node:child_process"; -import fs from "node:fs"; -import path from "node:path"; - -export interface GitWorktreeInfo { - path: string; - branch: string; - commit?: string; -} - -/** - * Check if a directory is a git repository - */ -export function isGitRepo(dir: string): boolean { - try { - execSync("git rev-parse --git-dir", { cwd: dir, stdio: "pipe" }); - return true; - } catch { - return false; - } -} - -/** - * Get the default branch name (main or master) - */ -export function getDefaultBranch(dir: string): string { - try { - const branches = execSync("git branch -rl '*/HEAD'", { cwd: dir, stdio: "pipe", encoding: "utf-8" }); - const match = branches.match(/origin\/(main|master)/); - return match?.[1] || "main"; - } catch { - return "main"; - } -} - -/** - * Create a new worktree for a session - * Returns the worktree path - */ -export function createWorktree(projectPath: string, sessionId: string, baseBranch?: string): string { - if (!isGitRepo(projectPath)) { - throw new Error("Not a git repository"); - } - - const branchName = `claude-flow/${sessionId}`; - const worktreePath = path.join(projectPath, ".claude-flow", "worktrees", sessionId); - - // Ensure parent directory exists - fs.mkdirSync(path.dirname(worktreePath), { recursive: true }); - - // Create branch from base (or current HEAD) - const base = baseBranch || getDefaultBranch(projectPath); - - try { - // Create worktree with new branch - execSync( - `git worktree add -b "${branchName}" "${worktreePath}" ${base}`, - { cwd: projectPath, stdio: "pipe" } - ); - } catch (error) { - // If branch already exists, just add worktree pointing to it - try { - execSync( - `git worktree add "${worktreePath}" "${branchName}"`, - { cwd: projectPath, stdio: "pipe" } - ); - } catch { - throw new Error(`Failed to create worktree: ${error}`); - } - } - - return worktreePath; -} - -/** - * Remove a worktree - */ -export function removeWorktree(projectPath: string, sessionId: string): void { - const worktreePath = path.join(projectPath, ".claude-flow", "worktrees", sessionId); - const branchName = `claude-flow/${sessionId}`; - - try { - // Remove worktree - execSync(`git worktree remove "${worktreePath}" --force`, { cwd: projectPath, stdio: "pipe" }); - } catch { - // Worktree might not exist, that's ok - } - - try { - // Delete branch - execSync(`git branch -D "${branchName}"`, { cwd: projectPath, stdio: "pipe" }); - } catch { - // Branch might not exist, that's ok - } -} - -/** - * Get worktree info for a session - */ -export function getWorktreeInfo(projectPath: string, sessionId: string): GitWorktreeInfo | null { - const worktreePath = path.join(projectPath, ".claude-flow", "worktrees", sessionId); - - if (!fs.existsSync(worktreePath)) { - return null; - } - - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - cwd: worktreePath, - encoding: "utf-8" - }).trim(); - - const commit = execSync("git rev-parse --short HEAD", { - cwd: worktreePath, - encoding: "utf-8" - }).trim(); - - return { path: worktreePath, branch, commit }; - } catch { - return null; - } -} - -/** - * Commit changes in a worktree - */ -export function commitChanges( - worktreePath: string, - message: string, - files: string[] = ["."] -): void { - try { - // Stage files - execSync(`git add ${files.join(" ")}`, { cwd: worktreePath, stdio: "pipe" }); - - // Commit - execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { cwd: worktreePath, stdio: "pipe" }); - } catch (error) { - throw new Error(`Failed to commit: ${error}`); - } -} - -/** - * Check if there are uncommitted changes - */ -export function hasUncommittedChanges(worktreePath: string): boolean { - try { - const status = execSync("git status --porcelain", { cwd: worktreePath, encoding: "utf-8" }); - return status.trim().length > 0; - } catch { - return false; - } -} - -/** - * Get diff summary of uncommitted changes - */ -export function getDiffSummary(worktreePath: string): string { - try { - return execSync("git diff --stat", { cwd: worktreePath, encoding: "utf-8" }); - } catch { - return ""; - } -} - -/** - * Get the main project path from a worktree path - */ -export function getMainRepoPath(worktreePath: string): string { - try { - const gitDir = execSync("git rev-parse --git-common-dir", { - cwd: worktreePath, - encoding: "utf-8" - }).trim(); - return path.dirname(gitDir); - } catch { - return worktreePath; - } -} diff --git a/src/main/ipc/handlers.ts b/src/main/ipc/handlers.ts index bc0d024..2d5e3d3 100644 --- a/src/main/ipc/handlers.ts +++ b/src/main/ipc/handlers.ts @@ -3,7 +3,6 @@ import * as projects from "../db/projects"; import * as sessions from "../db/sessions"; import * as claude from "../claude"; import type { UserPermissionMode } from "../claude/phases"; -import * as git from "../git/worktree"; export function registerIpcHandlers(mainWindow: BrowserWindow): void { // Projects @@ -19,35 +18,16 @@ export function registerIpcHandlers(mainWindow: BrowserWindow): void { ipcMain.handle("sessions:list", (_, projectId: string) => sessions.listSessions(projectId) ); - - ipcMain.handle("sessions:create", async (_, projectId: string, name: string) => { - const session = sessions.createSession(projectId, name); - const project = projects.getProject(projectId); - - if (project && git.isGitRepo(project.path)) { - try { - // Create git worktree for this session - git.createWorktree(project.path, session.id); - } catch (error) { - console.error("Failed to create worktree:", error); - // Continue without worktree - not fatal - } - } - - return session; - }); + + ipcMain.handle("sessions:create", (_, projectId: string, name: string) => + sessions.createSession(projectId, name) + ); ipcMain.handle("sessions:delete", (_, id: string) => { const session = sessions.getSession(id); if (session) { const project = projects.getProject(session.project_id); if (project) { - // Clean up worktree if exists - try { - git.removeWorktree(project.path, id); - } catch { - // Worktree might not exist, that's ok - } // Clean up session artifacts claude.clearSessionArtifacts(project.path, id); } @@ -162,23 +142,6 @@ export function registerIpcHandlers(mainWindow: BrowserWindow): void { } ); - // Git - ipcMain.handle("git:isRepo", (_, projectPath: string) => { - return git.isGitRepo(projectPath); - }); - - ipcMain.handle("git:worktreeInfo", (_, projectPath: string, sessionId: string) => { - return git.getWorktreeInfo(projectPath, sessionId); - }); - - ipcMain.handle("git:commit", (_, worktreePath: string, message: string, files?: string[]) => { - git.commitChanges(worktreePath, message, files); - }); - - ipcMain.handle("git:hasChanges", (_, worktreePath: string) => { - return git.hasUncommittedChanges(worktreePath); - }); - // Dialogs ipcMain.handle("dialog:selectDirectory", async () => { const result = await dialog.showOpenDialog(mainWindow, { diff --git a/src/main/preload.ts b/src/main/preload.ts index 1747763..7c1d634 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -3,7 +3,6 @@ import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk"; import type { Project } from "./db/projects"; import type { Session, Message } from "./db/sessions"; import type { Phase, UserPermissionMode } from "./claude/phases"; -import type { GitWorktreeInfo } from "./git/worktree"; export interface ClaudeFlowAPI { // Projects @@ -32,7 +31,7 @@ export interface ClaudeFlowAPI { mode: UserPermissionMode ) => Promise; - // Session Artifacts (new session-specific) + // Session Artifacts (session-specific) readSessionArtifact: ( projectPath: string, sessionId: string, @@ -60,12 +59,6 @@ export interface ClaudeFlowAPI { content: string ) => Promise; - // Git - isGitRepo: (projectPath: string) => Promise; - getWorktreeInfo: (projectPath: string, sessionId: string) => Promise; - commitChanges: (worktreePath: string, message: string, files?: string[]) => Promise; - hasUncommittedChanges: (worktreePath: string) => Promise; - // Events onClaudeMessage: ( callback: (sessionId: string, message: SDKMessage) => void @@ -121,15 +114,6 @@ const api: ClaudeFlowAPI = { writeArtifact: (projectPath, filename, content) => ipcRenderer.invoke("artifact:write", projectPath, filename, content), - // Git - isGitRepo: (projectPath) => ipcRenderer.invoke("git:isRepo", projectPath), - getWorktreeInfo: (projectPath, sessionId) => - ipcRenderer.invoke("git:worktreeInfo", projectPath, sessionId), - commitChanges: (worktreePath, message, files) => - ipcRenderer.invoke("git:commit", worktreePath, message, files), - hasUncommittedChanges: (worktreePath) => - ipcRenderer.invoke("git:hasChanges", worktreePath), - // Events onClaudeMessage: (callback) => { const handler = ( -- cgit v1.2.3