fix: merge worktree handler now uses correct branch name and path

The merge handler previously hardcoded branch names as `feature/${featureId}`
and worktree paths as `.worktrees/${featureId}`, which failed for auto-generated
branches (e.g., `feature/v0.11.0rc-1768413895104-31pa`) and custom worktrees.

Changes:
- Server handler now accepts branchName and worktreePath directly from the UI
- Added branch existence validation before attempting merge
- Updated merge dialog with 2-step confirmation (type "merge" to confirm)
- Removed feature branch naming restriction - any branch can now be merged
- Updated API types and client to pass correct parameters

Closes #408

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Shirone
2026-01-14 20:49:17 +01:00
parent ca3286a374
commit ee0d0c6c59
10 changed files with 335 additions and 22 deletions

View File

@@ -58,6 +58,7 @@ import { DeleteWorktreeDialog } from './board-view/dialogs/delete-worktree-dialo
import { CommitWorktreeDialog } from './board-view/dialogs/commit-worktree-dialog';
import { CreatePRDialog } from './board-view/dialogs/create-pr-dialog';
import { CreateBranchDialog } from './board-view/dialogs/create-branch-dialog';
import { MergeWorktreeDialog } from './board-view/dialogs/merge-worktree-dialog';
import { WorktreePanel } from './board-view/worktree-panel';
import type { PRInfo, WorktreeInfo } from './board-view/worktree-panel/types';
import { COLUMNS, getColumnsWithPipeline } from './board-view/constants';
@@ -148,6 +149,7 @@ export function BoardView() {
const [showCommitWorktreeDialog, setShowCommitWorktreeDialog] = useState(false);
const [showCreatePRDialog, setShowCreatePRDialog] = useState(false);
const [showCreateBranchDialog, setShowCreateBranchDialog] = useState(false);
const [showMergeWorktreeDialog, setShowMergeWorktreeDialog] = useState(false);
const [selectedWorktreeForAction, setSelectedWorktreeForAction] = useState<{
path: string;
branch: string;
@@ -1354,6 +1356,10 @@ export function BoardView() {
}}
onAddressPRComments={handleAddressPRComments}
onResolveConflicts={handleResolveConflicts}
onMerge={(worktree) => {
setSelectedWorktreeForAction(worktree);
setShowMergeWorktreeDialog(true);
}}
onRemovedWorktrees={handleRemovedWorktrees}
runningFeatureIds={runningAutoTasks}
branchCardCounts={branchCardCounts}
@@ -1698,6 +1704,35 @@ export function BoardView() {
}}
/>
{/* Merge Worktree Dialog */}
<MergeWorktreeDialog
open={showMergeWorktreeDialog}
onOpenChange={setShowMergeWorktreeDialog}
projectPath={currentProject.path}
worktree={selectedWorktreeForAction}
affectedFeatureCount={
selectedWorktreeForAction
? hookFeatures.filter((f) => f.branchName === selectedWorktreeForAction.branch).length
: 0
}
onMerged={(mergedWorktree) => {
// Reset features that were assigned to the merged worktree (by branch)
hookFeatures.forEach((feature) => {
if (feature.branchName === mergedWorktree.branch) {
// Reset the feature's branch assignment - update both local state and persist
const updates = {
branchName: null as unknown as string | undefined,
};
updateFeature(feature.id, updates);
persistFeatureUpdate(feature.id, updates);
}
});
setWorktreeRefreshKey((k) => k + 1);
setSelectedWorktreeForAction(null);
}}
/>
{/* Commit Worktree Dialog */}
<CommitWorktreeDialog
open={showCommitWorktreeDialog}