Skip to content

feat: replace boolean directMode with GitIsolationMode and flexible base branch#30

Merged
johannesjo merged 4 commits intojohannesjo:mainfrom
ASRagab:feat/git-isolation-mode-base-branch
Mar 24, 2026
Merged

feat: replace boolean directMode with GitIsolationMode and flexible base branch#30
johannesjo merged 4 commits intojohannesjo:mainfrom
ASRagab:feat/git-isolation-mode-base-branch

Conversation

@ASRagab
Copy link
Copy Markdown
Contributor

@ASRagab ASRagab commented Mar 23, 2026

Summary

  • Replace the boolean directMode field with a typed GitIsolationMode ('worktree' | 'direct') across the task model, UI, and persistence layer
  • Add a baseBranch field to tasks and projects so users can branch from and diff against any local branch — not just the auto-detected main
  • Add a GetBranches IPC channel and branch picker dropdown in the New Task dialog
  • Merge createDirectTask into createTask as a single unified code path
  • Include backward-compatible migration for persisted state (directModegitIsolation, defaultDirectModedefaultGitIsolation)

Details

Previously, task isolation was a binary checkbox: either work in a git worktree (default) or directly on main. The branch used for diffs, merges, rebases, and worktree creation was always auto-detected via detectMainBranch(), which looks for main or master.

This works for simple repos, but breaks down when:

  • The primary branch has a non-standard name (e.g. develop, trunk)
  • You want to branch a task off a feature branch rather than main
  • You want to compare diffs against a specific branch

This PR makes two changes:

  1. GitIsolationMode type — replaces the boolean directMode with a string union 'worktree' | 'direct'. All conditional checks (task.directModetask.gitIsolation === 'direct') are updated throughout the codebase. The UI switches from a checkbox to a segmented button selector (Worktree / Direct).

  2. Flexible base branch — every git operation that previously called detectMainBranch() now accepts an optional baseBranch parameter. When provided, it's used directly; when omitted, the existing auto-detection still runs. The New Task dialog fetches local branches via the new GetBranches IPC channel and presents them in a <select> dropdown. Projects can also set defaultBaseBranch in the Edit Project dialog.

The two separate functions createTask and createDirectTask are merged into a single createTask that branches on gitIsolation internally, reducing duplication.
CleanShot 2026-03-23 at 13 47 06

Changes

File Change
src/store/types.ts Add GitIsolationMode type; replace directMode?: boolean with gitIsolation: GitIsolationMode + baseBranch: string on Task and PersistedTask; replace defaultDirectMode with defaultGitIsolation + defaultBaseBranch on Project
src/store/tasks.ts Merge createDirectTask into createTask; thread baseBranch through merge/push/close; rename hasDirectModeTaskhasDirectTask
src/store/projects.ts Add getProjectDefaultBaseBranch, getProjectDefaultGitIsolation helpers; update updateProject fields
src/store/persistence.ts Migrate persisted directModegitIsolation and defaultDirectModedefaultGitIsolation on load
src/store/autosave.ts Persist gitIsolation + baseBranch instead of directMode
src/store/taskStatus.ts Pass baseBranch to GetWorktreeStatus
src/store/store.ts Update re-exports (createDirectTask removed, hasDirectModeTaskhasDirectTask)
electron/ipc/channels.ts Add GetBranches IPC channel
electron/ipc/git.ts Add getBranches(); thread optional baseBranch through detectMergeBase, createWorktree, getChangedFiles, getAllFileDiffs, getFileDiff, getWorktreeStatus, checkMergeStatus, mergeTask, getBranchLog, rebaseTask, and branch-based variants
electron/ipc/register.ts Register GetBranches handler; validate and pass baseBranch for all git IPC handlers
electron/ipc/tasks.ts Accept and forward baseBranch to createWorktree
electron/preload.cjs Add get_branches to preload allowlist
src/components/NewTaskDialog.tsx Replace direct-mode checkbox with segmented Worktree/Direct selector; add branch picker <select> populated via GetBranches IPC; fetch branches + main branch on project change
src/components/EditProjectDialog.tsx Replace defaultDirectMode checkbox with segmented selector + defaultBaseBranch text input
src/components/MergeDialog.tsx Thread baseBranch through branch log, worktree status, merge status, and rebase calls
src/components/CloseTaskDialog.tsx Replace directMode checks with gitIsolation checks
src/components/TaskPanel.tsx Replace directMode checks with gitIsolation checks; pass baseBranch to changed files list and diff viewer
src/components/DiffViewerDialog.tsx Accept and forward baseBranch prop
src/components/ScrollingDiffView.tsx Accept and forward baseBranch through gap/trailing/file section components
src/components/ChangedFilesList.tsx Accept and forward baseBranch to IPC calls
src/components/Sidebar.tsx Replace directMode checks with gitIsolation checks
src/components/TilingLayout.tsx Replace directMode check with gitIsolation check
src/arena/ResultsScreen.tsx Pass baseBranch: undefined for arena compatibility
src/arena/merge.ts Pass baseBranch: undefined for arena compatibility

Test plan

  • npm run check (typecheck + lint + prettier) passes
  • Create a worktree task with default base branch — verify branch/diff behavior unchanged
  • Create a worktree task with a non-main base branch — verify worktree is created from that branch and diffs compare against it
  • Create a direct-mode task — verify it works as before
  • Set project defaults for git isolation and base branch — verify new tasks inherit them
  • Load persisted state from previous version with directMode: true — verify migration to gitIsolation: 'direct'
  • Verify branch picker dropdown populates with local branches
  • Verify arena merge flow still works (passes baseBranch: undefined)

🤖 Generated with Claude Code

ASRagab added 2 commits March 23, 2026 13:48
… binary directMode

Replaces the binary `directMode` boolean with a `GitIsolationMode` enum ('worktree' | 'direct') and explicit `baseBranch` field, enabling worktree creation and direct work on any branch instead of just main. Unifies task creation into a single function, generalizes all git operations (merge, rebase, diff, status) to use explicit base branch, updates persistence with backward-compatible migration, and replaces UI controls with isolation mode selector and branch picker in NewTaskDialog and EditProjectDialog.
@ASRagab ASRagab changed the title feat: replace binary directMode with GitIsolationMode and flexible base branch feat: replace binary directMode with GitIsolationMode and flexible base branch Mar 23, 2026
@ASRagab ASRagab changed the title feat: replace binary directMode with GitIsolationMode and flexible base branch feat: replace boolean directMode with GitIsolationMode and flexible base branch Mar 23, 2026
Copy link
Copy Markdown
Owner

@johannesjo johannesjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much! Nice work consolidating the two task creation paths and threading baseBranch throughout – the code is cleaner now. A couple of issues to flag:

Critical: detectMergeBase cache ignores baseBranch

electron/ipc/git.ts:195-216

The cache key is just cacheKey(repoRoot), but baseBranch now varies per-task. If two tasks in the same repo use different base branches (e.g. one from main, one from develop), the second call returns the cached merge-base from the first — producing wrong diffs.

Fix: incorporate the resolved mainBranch into the cache key (e.g. ${repoRoot}:${mainBranch}).

Important: Migration fallback sets baseBranch to the task's own branch

src/store/persistence.ts:369-370 (and :389 for collapsed tasks)

baseBranch: legacy.baseBranch ?? pt.branchName,

For existing worktree tasks, pt.branchName is something like task/my-feature-abc123. Setting that as baseBranch means git merge-base task/my-feature-abc123 HEAD when HEAD is on that same branch — merge-base resolves to HEAD itself, so all diffs come back empty after upgrade.

Suggestion: either make baseBranch optional on Task (so undefined triggers the existing detectMainBranch() fallback in the git layer), or resolve it asynchronously during loadState.

Minor

  • EditProjectDialog: the "Worktree" button stores undefined rather than 'worktree' — works via fallback but means "user chose worktree" is indistinguishable from "no preference set."
  • Arena files: the explicit baseBranch: undefined additions are correct but noisy — omitting the property has the same effect.
  • The segmented Worktree/Direct button pattern is duplicated between NewTaskDialog and EditProjectDialog with ~30 lines of near-identical inline styles — worth extracting to a shared component.

…ntedButtons

- Include resolved baseBranch in detectMergeBase cache key so tasks
  with different base branches in the same repo get distinct cache entries
- Make baseBranch optional on Task/PersistedTask so migrated tasks
  without one fall back to detectMainBranch() instead of using the
  task's own branch name (which produced empty diffs)
- Store 'worktree' explicitly in EditProjectDialog instead of undefined
- Remove noisy baseBranch: undefined from arena files
- Extract shared SegmentedButtons component to deduplicate ~60 lines of
  inline button styles between NewTaskDialog and EditProjectDialog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ASRagab
Copy link
Copy Markdown
Contributor Author

ASRagab commented Mar 23, 2026

Thank you for the comments, I believe I have addressed them - and thank for responding to PRs. Enjoying using the tool very much (side scrolling is the way).

@johannesjo
Copy link
Copy Markdown
Owner

That's great! Thank you very much!

Copy link
Copy Markdown
Owner

@johannesjo johannesjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

X

Empty string '' from the UI (when branch fetch fails) would pass through
`??` (nullish coalescing) without triggering the detectMainBranch()
fallback, causing git commands like `git merge-base '' HEAD` to fail.

Also removes unused getProjectDefaultBaseBranch/getProjectDefaultGitIsolation
helper functions.
@johannesjo johannesjo merged commit 8d30d7e into johannesjo:main Mar 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants