mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-16 21:53:07 +00:00
feat: Add conflict source branch detection and fix re-render cascade in BoardView
This commit is contained in:
@@ -98,6 +98,7 @@ async function detectConflictState(worktreePath: string): Promise<{
|
|||||||
hasConflicts: boolean;
|
hasConflicts: boolean;
|
||||||
conflictType?: 'merge' | 'rebase' | 'cherry-pick';
|
conflictType?: 'merge' | 'rebase' | 'cherry-pick';
|
||||||
conflictFiles?: string[];
|
conflictFiles?: string[];
|
||||||
|
conflictSourceBranch?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
// Find the canonical .git directory for this worktree (execGitCommand avoids /bin/sh in CI)
|
// Find the canonical .git directory for this worktree (execGitCommand avoids /bin/sh in CI)
|
||||||
@@ -153,10 +154,84 @@ async function detectConflictState(worktreePath: string): Promise<{
|
|||||||
// Fall back to empty list if diff fails
|
// Fall back to empty list if diff fails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect the source branch involved in the conflict
|
||||||
|
let conflictSourceBranch: string | undefined;
|
||||||
|
try {
|
||||||
|
if (conflictType === 'merge' && mergeHeadExists) {
|
||||||
|
// For merges, resolve MERGE_HEAD to a branch name
|
||||||
|
const mergeHead = (
|
||||||
|
await secureFs.readFile(path.join(gitDir, 'MERGE_HEAD'), 'utf-8')
|
||||||
|
).trim();
|
||||||
|
try {
|
||||||
|
const branchName = await execGitCommand(
|
||||||
|
['name-rev', '--name-only', '--refs=refs/heads/*', mergeHead],
|
||||||
|
worktreePath
|
||||||
|
);
|
||||||
|
const cleaned = branchName.trim().replace(/~\d+$/, '');
|
||||||
|
if (cleaned && cleaned !== 'undefined') {
|
||||||
|
conflictSourceBranch = cleaned;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Could not resolve to branch name
|
||||||
|
}
|
||||||
|
} else if (conflictType === 'rebase') {
|
||||||
|
// For rebases, read the onto branch from rebase-merge/head-name or rebase-apply/head-name
|
||||||
|
const headNamePath = rebaseMergeExists
|
||||||
|
? path.join(gitDir, 'rebase-merge', 'onto-name')
|
||||||
|
: path.join(gitDir, 'rebase-apply', 'onto-name');
|
||||||
|
try {
|
||||||
|
const ontoName = (await secureFs.readFile(headNamePath, 'utf-8')).trim();
|
||||||
|
if (ontoName) {
|
||||||
|
conflictSourceBranch = ontoName.replace(/^refs\/heads\//, '');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// onto-name may not exist; try to resolve the onto commit
|
||||||
|
try {
|
||||||
|
const ontoPath = rebaseMergeExists
|
||||||
|
? path.join(gitDir, 'rebase-merge', 'onto')
|
||||||
|
: path.join(gitDir, 'rebase-apply', 'onto');
|
||||||
|
const ontoCommit = (await secureFs.readFile(ontoPath, 'utf-8')).trim();
|
||||||
|
if (ontoCommit) {
|
||||||
|
const branchName = await execGitCommand(
|
||||||
|
['name-rev', '--name-only', '--refs=refs/heads/*', ontoCommit],
|
||||||
|
worktreePath
|
||||||
|
);
|
||||||
|
const cleaned = branchName.trim().replace(/~\d+$/, '');
|
||||||
|
if (cleaned && cleaned !== 'undefined') {
|
||||||
|
conflictSourceBranch = cleaned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Could not resolve onto commit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (conflictType === 'cherry-pick' && cherryPickHeadExists) {
|
||||||
|
// For cherry-picks, try to resolve CHERRY_PICK_HEAD to a branch name
|
||||||
|
const cherryPickHead = (
|
||||||
|
await secureFs.readFile(path.join(gitDir, 'CHERRY_PICK_HEAD'), 'utf-8')
|
||||||
|
).trim();
|
||||||
|
try {
|
||||||
|
const branchName = await execGitCommand(
|
||||||
|
['name-rev', '--name-only', '--refs=refs/heads/*', cherryPickHead],
|
||||||
|
worktreePath
|
||||||
|
);
|
||||||
|
const cleaned = branchName.trim().replace(/~\d+$/, '');
|
||||||
|
if (cleaned && cleaned !== 'undefined') {
|
||||||
|
conflictSourceBranch = cleaned;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Could not resolve to branch name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore source branch detection errors
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasConflicts: conflictFiles.length > 0,
|
hasConflicts: conflictFiles.length > 0,
|
||||||
conflictType,
|
conflictType,
|
||||||
conflictFiles,
|
conflictFiles,
|
||||||
|
conflictSourceBranch,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
// If anything fails, assume no conflicts
|
// If anything fails, assume no conflicts
|
||||||
|
|||||||
@@ -715,15 +715,27 @@ export function BoardView({ initialFeatureId }: BoardViewProps) {
|
|||||||
const selectedWorktreeBranch =
|
const selectedWorktreeBranch =
|
||||||
currentWorktreeBranch || worktrees.find((w) => w.isMain)?.branch || 'main';
|
currentWorktreeBranch || worktrees.find((w) => w.isMain)?.branch || 'main';
|
||||||
|
|
||||||
// Aggregate running auto tasks across all worktrees for this project
|
// Aggregate running auto tasks across all worktrees for this project.
|
||||||
const autoModeByWorktree = useAppStore((state) => state.autoModeByWorktree);
|
// IMPORTANT: Use a derived selector with shallow equality instead of subscribing
|
||||||
const runningAutoTasksAllWorktrees = useMemo(() => {
|
// to the raw autoModeByWorktree object. The raw subscription caused the entire
|
||||||
if (!currentProject?.id) return [];
|
// BoardView to re-render on EVERY auto-mode state change (any worktree), which
|
||||||
const prefix = `${currentProject.id}::`;
|
// during worktree switches cascaded through DndContext/KanbanBoard and triggered
|
||||||
return Object.entries(autoModeByWorktree)
|
// React error #185 (maximum update depth exceeded), crashing the board view.
|
||||||
.filter(([key]) => key.startsWith(prefix))
|
const runningAutoTasksAllWorktrees = useAppStore(
|
||||||
.flatMap(([, state]) => state.runningTasks ?? []);
|
useShallow((state) => {
|
||||||
}, [autoModeByWorktree, currentProject?.id]);
|
if (!currentProject?.id) return [] as string[];
|
||||||
|
const prefix = `${currentProject.id}::`;
|
||||||
|
const tasks: string[] = [];
|
||||||
|
for (const [key, worktreeState] of Object.entries(state.autoModeByWorktree)) {
|
||||||
|
if (key.startsWith(prefix) && worktreeState.runningTasks) {
|
||||||
|
for (const task of worktreeState.runningTasks) {
|
||||||
|
tasks.push(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tasks;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Get in-progress features for keyboard shortcuts (needed before actions hook)
|
// Get in-progress features for keyboard shortcuts (needed before actions hook)
|
||||||
// Must be after runningAutoTasks is defined
|
// Must be after runningAutoTasks is defined
|
||||||
@@ -1506,6 +1518,7 @@ export function BoardView({ initialFeatureId }: BoardViewProps) {
|
|||||||
runningAutoTasks: runningAutoTasksAllWorktrees,
|
runningAutoTasks: runningAutoTasksAllWorktrees,
|
||||||
persistFeatureUpdate,
|
persistFeatureUpdate,
|
||||||
handleStartImplementation,
|
handleStartImplementation,
|
||||||
|
stopFeature: autoMode.stopFeature,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle dependency link creation
|
// Handle dependency link creation
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import { createLogger } from '@automaker/utils/logger';
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
|
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
|
||||||
import { Feature } from '@/store/app-store';
|
import { Feature } from '@/store/app-store';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { useAutoMode } from '@/hooks/use-auto-mode';
|
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { COLUMNS, ColumnId } from '../constants';
|
import { COLUMNS, ColumnId } from '../constants';
|
||||||
|
|
||||||
@@ -20,6 +19,7 @@ interface UseBoardDragDropProps {
|
|||||||
runningAutoTasks: string[];
|
runningAutoTasks: string[];
|
||||||
persistFeatureUpdate: (featureId: string, updates: Partial<Feature>) => Promise<void>;
|
persistFeatureUpdate: (featureId: string, updates: Partial<Feature>) => Promise<void>;
|
||||||
handleStartImplementation: (feature: Feature) => Promise<boolean>;
|
handleStartImplementation: (feature: Feature) => Promise<boolean>;
|
||||||
|
stopFeature: (featureId: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBoardDragDrop({
|
export function useBoardDragDrop({
|
||||||
@@ -28,6 +28,7 @@ export function useBoardDragDrop({
|
|||||||
runningAutoTasks,
|
runningAutoTasks,
|
||||||
persistFeatureUpdate,
|
persistFeatureUpdate,
|
||||||
handleStartImplementation,
|
handleStartImplementation,
|
||||||
|
stopFeature,
|
||||||
}: UseBoardDragDropProps) {
|
}: UseBoardDragDropProps) {
|
||||||
const [activeFeature, setActiveFeature] = useState<Feature | null>(null);
|
const [activeFeature, setActiveFeature] = useState<Feature | null>(null);
|
||||||
const [pendingDependencyLink, setPendingDependencyLink] = useState<PendingDependencyLink | null>(
|
const [pendingDependencyLink, setPendingDependencyLink] = useState<PendingDependencyLink | null>(
|
||||||
@@ -39,11 +40,22 @@ export function useBoardDragDrop({
|
|||||||
// and triggers React error #185 (maximum update depth exceeded).
|
// and triggers React error #185 (maximum update depth exceeded).
|
||||||
const moveFeature = useAppStore((s) => s.moveFeature);
|
const moveFeature = useAppStore((s) => s.moveFeature);
|
||||||
const updateFeature = useAppStore((s) => s.updateFeature);
|
const updateFeature = useAppStore((s) => s.updateFeature);
|
||||||
const autoMode = useAutoMode();
|
|
||||||
|
|
||||||
// Note: getOrCreateWorktreeForFeature removed - worktrees are now created server-side
|
// Note: getOrCreateWorktreeForFeature removed - worktrees are now created server-side
|
||||||
// at execution time based on feature.branchName
|
// at execution time based on feature.branchName
|
||||||
|
|
||||||
|
// Clear stale activeFeature when features list changes (e.g. during worktree switches).
|
||||||
|
// Without this, the DragOverlay in KanbanBoard can try to render a feature from
|
||||||
|
// a previous worktree, causing property access crashes.
|
||||||
|
useEffect(() => {
|
||||||
|
setActiveFeature((current) => {
|
||||||
|
if (!current) return null;
|
||||||
|
// If the active feature is no longer in the features list, clear it
|
||||||
|
const stillExists = features.some((f) => f.id === current.id);
|
||||||
|
return stillExists ? current : null;
|
||||||
|
});
|
||||||
|
}, [features]);
|
||||||
|
|
||||||
const handleDragStart = useCallback(
|
const handleDragStart = useCallback(
|
||||||
(event: DragStartEvent) => {
|
(event: DragStartEvent) => {
|
||||||
const { active } = event;
|
const { active } = event;
|
||||||
@@ -244,7 +256,7 @@ export function useBoardDragDrop({
|
|||||||
// If the feature is currently running, stop it first
|
// If the feature is currently running, stop it first
|
||||||
if (isRunningTask) {
|
if (isRunningTask) {
|
||||||
try {
|
try {
|
||||||
await autoMode.stopFeature(featureId);
|
await stopFeature(featureId);
|
||||||
logger.info('Stopped running feature via drag to backlog:', featureId);
|
logger.info('Stopped running feature via drag to backlog:', featureId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error stopping feature during drag to backlog:', error);
|
logger.error('Error stopping feature during drag to backlog:', error);
|
||||||
@@ -339,7 +351,7 @@ export function useBoardDragDrop({
|
|||||||
updateFeature,
|
updateFeature,
|
||||||
persistFeatureUpdate,
|
persistFeatureUpdate,
|
||||||
handleStartImplementation,
|
handleStartImplementation,
|
||||||
autoMode,
|
stopFeature,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,19 @@ import {
|
|||||||
ArrowLeftRight,
|
ArrowLeftRight,
|
||||||
Check,
|
Check,
|
||||||
Hash,
|
Hash,
|
||||||
|
Sparkles,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import type { WorktreeInfo, DevServerInfo, PRInfo, GitRepoStatus, TestSessionInfo } from '../types';
|
import type {
|
||||||
|
WorktreeInfo,
|
||||||
|
DevServerInfo,
|
||||||
|
PRInfo,
|
||||||
|
GitRepoStatus,
|
||||||
|
TestSessionInfo,
|
||||||
|
MergeConflictInfo,
|
||||||
|
} from '../types';
|
||||||
import { TooltipWrapper } from './tooltip-wrapper';
|
import { TooltipWrapper } from './tooltip-wrapper';
|
||||||
import { useAvailableEditors, useEffectiveDefaultEditor } from '../hooks/use-available-editors';
|
import { useAvailableEditors, useEffectiveDefaultEditor } from '../hooks/use-available-editors';
|
||||||
import {
|
import {
|
||||||
@@ -137,6 +145,8 @@ interface WorktreeActionsDropdownProps {
|
|||||||
onAbortOperation?: (worktree: WorktreeInfo) => void;
|
onAbortOperation?: (worktree: WorktreeInfo) => void;
|
||||||
/** Continue an in-progress merge/rebase/cherry-pick after resolving conflicts */
|
/** Continue an in-progress merge/rebase/cherry-pick after resolving conflicts */
|
||||||
onContinueOperation?: (worktree: WorktreeInfo) => void;
|
onContinueOperation?: (worktree: WorktreeInfo) => void;
|
||||||
|
/** Create a feature to resolve merge/rebase/cherry-pick conflicts with AI */
|
||||||
|
onCreateConflictResolutionFeature?: (conflictInfo: MergeConflictInfo) => void;
|
||||||
hasInitScript: boolean;
|
hasInitScript: boolean;
|
||||||
/** Terminal quick scripts configured for the project */
|
/** Terminal quick scripts configured for the project */
|
||||||
terminalScripts?: TerminalScript[];
|
terminalScripts?: TerminalScript[];
|
||||||
@@ -293,6 +303,7 @@ export function WorktreeActionsDropdown({
|
|||||||
onCherryPick,
|
onCherryPick,
|
||||||
onAbortOperation,
|
onAbortOperation,
|
||||||
onContinueOperation,
|
onContinueOperation,
|
||||||
|
onCreateConflictResolutionFeature,
|
||||||
hasInitScript,
|
hasInitScript,
|
||||||
terminalScripts,
|
terminalScripts,
|
||||||
onRunTerminalScript,
|
onRunTerminalScript,
|
||||||
@@ -467,6 +478,23 @@ export function WorktreeActionsDropdown({
|
|||||||
: 'Operation'}
|
: 'Operation'}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
{onCreateConflictResolutionFeature && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() =>
|
||||||
|
onCreateConflictResolutionFeature({
|
||||||
|
sourceBranch: worktree.branch,
|
||||||
|
targetBranch: worktree.branch,
|
||||||
|
targetWorktreePath: worktree.path,
|
||||||
|
conflictFiles: worktree.conflictFiles,
|
||||||
|
operationType: worktree.conflictType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="text-xs text-purple-500 focus:text-purple-600"
|
||||||
|
>
|
||||||
|
<Sparkles className="w-3.5 h-3.5 mr-2" />
|
||||||
|
Resolve with AI
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import type {
|
|||||||
PRInfo,
|
PRInfo,
|
||||||
GitRepoStatus,
|
GitRepoStatus,
|
||||||
TestSessionInfo,
|
TestSessionInfo,
|
||||||
|
MergeConflictInfo,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { WorktreeDropdownItem } from './worktree-dropdown-item';
|
import { WorktreeDropdownItem } from './worktree-dropdown-item';
|
||||||
import { BranchSwitchDropdown } from './branch-switch-dropdown';
|
import { BranchSwitchDropdown } from './branch-switch-dropdown';
|
||||||
@@ -129,6 +130,8 @@ export interface WorktreeDropdownProps {
|
|||||||
onAbortOperation?: (worktree: WorktreeInfo) => void;
|
onAbortOperation?: (worktree: WorktreeInfo) => void;
|
||||||
/** Continue an in-progress merge/rebase/cherry-pick after resolving conflicts */
|
/** Continue an in-progress merge/rebase/cherry-pick after resolving conflicts */
|
||||||
onContinueOperation?: (worktree: WorktreeInfo) => void;
|
onContinueOperation?: (worktree: WorktreeInfo) => void;
|
||||||
|
/** Create a feature to resolve merge/rebase/cherry-pick conflicts with AI */
|
||||||
|
onCreateConflictResolutionFeature?: (conflictInfo: MergeConflictInfo) => void;
|
||||||
/** Remotes cache: maps worktree path to list of remotes */
|
/** Remotes cache: maps worktree path to list of remotes */
|
||||||
remotesCache?: Record<string, Array<{ name: string; url: string }>>;
|
remotesCache?: Record<string, Array<{ name: string; url: string }>>;
|
||||||
/** Pull from a specific remote, bypassing the remote selection dialog */
|
/** Pull from a specific remote, bypassing the remote selection dialog */
|
||||||
@@ -241,6 +244,7 @@ export function WorktreeDropdown({
|
|||||||
onCherryPick,
|
onCherryPick,
|
||||||
onAbortOperation,
|
onAbortOperation,
|
||||||
onContinueOperation,
|
onContinueOperation,
|
||||||
|
onCreateConflictResolutionFeature,
|
||||||
remotesCache,
|
remotesCache,
|
||||||
onPullWithRemote,
|
onPullWithRemote,
|
||||||
onPushWithRemote,
|
onPushWithRemote,
|
||||||
@@ -607,6 +611,7 @@ export function WorktreeDropdown({
|
|||||||
onCherryPick={onCherryPick}
|
onCherryPick={onCherryPick}
|
||||||
onAbortOperation={onAbortOperation}
|
onAbortOperation={onAbortOperation}
|
||||||
onContinueOperation={onContinueOperation}
|
onContinueOperation={onContinueOperation}
|
||||||
|
onCreateConflictResolutionFeature={onCreateConflictResolutionFeature}
|
||||||
hasInitScript={hasInitScript}
|
hasInitScript={hasInitScript}
|
||||||
terminalScripts={terminalScripts}
|
terminalScripts={terminalScripts}
|
||||||
onRunTerminalScript={onRunTerminalScript}
|
onRunTerminalScript={onRunTerminalScript}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type {
|
|||||||
PRInfo,
|
PRInfo,
|
||||||
GitRepoStatus,
|
GitRepoStatus,
|
||||||
TestSessionInfo,
|
TestSessionInfo,
|
||||||
|
MergeConflictInfo,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { BranchSwitchDropdown } from './branch-switch-dropdown';
|
import { BranchSwitchDropdown } from './branch-switch-dropdown';
|
||||||
import { WorktreeActionsDropdown } from './worktree-actions-dropdown';
|
import { WorktreeActionsDropdown } from './worktree-actions-dropdown';
|
||||||
@@ -95,6 +96,8 @@ interface WorktreeTabProps {
|
|||||||
onAbortOperation?: (worktree: WorktreeInfo) => void;
|
onAbortOperation?: (worktree: WorktreeInfo) => void;
|
||||||
/** Continue an in-progress merge/rebase/cherry-pick after resolving conflicts */
|
/** Continue an in-progress merge/rebase/cherry-pick after resolving conflicts */
|
||||||
onContinueOperation?: (worktree: WorktreeInfo) => void;
|
onContinueOperation?: (worktree: WorktreeInfo) => void;
|
||||||
|
/** Create a feature to resolve merge/rebase/cherry-pick conflicts with AI */
|
||||||
|
onCreateConflictResolutionFeature?: (conflictInfo: MergeConflictInfo) => void;
|
||||||
hasInitScript: boolean;
|
hasInitScript: boolean;
|
||||||
/** Whether a test command is configured in project settings */
|
/** Whether a test command is configured in project settings */
|
||||||
hasTestCommand?: boolean;
|
hasTestCommand?: boolean;
|
||||||
@@ -195,6 +198,7 @@ export function WorktreeTab({
|
|||||||
onCherryPick,
|
onCherryPick,
|
||||||
onAbortOperation,
|
onAbortOperation,
|
||||||
onContinueOperation,
|
onContinueOperation,
|
||||||
|
onCreateConflictResolutionFeature,
|
||||||
hasInitScript,
|
hasInitScript,
|
||||||
hasTestCommand = false,
|
hasTestCommand = false,
|
||||||
remotes,
|
remotes,
|
||||||
@@ -579,6 +583,7 @@ export function WorktreeTab({
|
|||||||
onCherryPick={onCherryPick}
|
onCherryPick={onCherryPick}
|
||||||
onAbortOperation={onAbortOperation}
|
onAbortOperation={onAbortOperation}
|
||||||
onContinueOperation={onContinueOperation}
|
onContinueOperation={onContinueOperation}
|
||||||
|
onCreateConflictResolutionFeature={onCreateConflictResolutionFeature}
|
||||||
hasInitScript={hasInitScript}
|
hasInitScript={hasInitScript}
|
||||||
terminalScripts={terminalScripts}
|
terminalScripts={terminalScripts}
|
||||||
onRunTerminalScript={onRunTerminalScript}
|
onRunTerminalScript={onRunTerminalScript}
|
||||||
|
|||||||
@@ -1071,6 +1071,7 @@ export function WorktreePanel({
|
|||||||
onCherryPick={handleCherryPick}
|
onCherryPick={handleCherryPick}
|
||||||
onAbortOperation={handleAbortOperation}
|
onAbortOperation={handleAbortOperation}
|
||||||
onContinueOperation={handleContinueOperation}
|
onContinueOperation={handleContinueOperation}
|
||||||
|
onCreateConflictResolutionFeature={onCreateMergeConflictResolutionFeature}
|
||||||
hasInitScript={hasInitScript}
|
hasInitScript={hasInitScript}
|
||||||
terminalScripts={terminalScripts}
|
terminalScripts={terminalScripts}
|
||||||
onRunTerminalScript={handleRunTerminalScript}
|
onRunTerminalScript={handleRunTerminalScript}
|
||||||
@@ -1310,6 +1311,7 @@ export function WorktreePanel({
|
|||||||
onCherryPick={handleCherryPick}
|
onCherryPick={handleCherryPick}
|
||||||
onAbortOperation={handleAbortOperation}
|
onAbortOperation={handleAbortOperation}
|
||||||
onContinueOperation={handleContinueOperation}
|
onContinueOperation={handleContinueOperation}
|
||||||
|
onCreateConflictResolutionFeature={onCreateMergeConflictResolutionFeature}
|
||||||
terminalScripts={terminalScripts}
|
terminalScripts={terminalScripts}
|
||||||
onRunTerminalScript={handleRunTerminalScript}
|
onRunTerminalScript={handleRunTerminalScript}
|
||||||
onEditScripts={handleEditScripts}
|
onEditScripts={handleEditScripts}
|
||||||
@@ -1391,6 +1393,7 @@ export function WorktreePanel({
|
|||||||
onCherryPick={handleCherryPick}
|
onCherryPick={handleCherryPick}
|
||||||
onAbortOperation={handleAbortOperation}
|
onAbortOperation={handleAbortOperation}
|
||||||
onContinueOperation={handleContinueOperation}
|
onContinueOperation={handleContinueOperation}
|
||||||
|
onCreateConflictResolutionFeature={onCreateMergeConflictResolutionFeature}
|
||||||
hasInitScript={hasInitScript}
|
hasInitScript={hasInitScript}
|
||||||
hasTestCommand={hasTestCommand}
|
hasTestCommand={hasTestCommand}
|
||||||
terminalScripts={terminalScripts}
|
terminalScripts={terminalScripts}
|
||||||
@@ -1478,6 +1481,7 @@ export function WorktreePanel({
|
|||||||
onCherryPick={handleCherryPick}
|
onCherryPick={handleCherryPick}
|
||||||
onAbortOperation={handleAbortOperation}
|
onAbortOperation={handleAbortOperation}
|
||||||
onContinueOperation={handleContinueOperation}
|
onContinueOperation={handleContinueOperation}
|
||||||
|
onCreateConflictResolutionFeature={onCreateMergeConflictResolutionFeature}
|
||||||
hasInitScript={hasInitScript}
|
hasInitScript={hasInitScript}
|
||||||
hasTestCommand={hasTestCommand}
|
hasTestCommand={hasTestCommand}
|
||||||
terminalScripts={terminalScripts}
|
terminalScripts={terminalScripts}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ test.describe('Feature Deep Link', () => {
|
|||||||
let projectPath: string;
|
let projectPath: string;
|
||||||
let projectName: string;
|
let projectName: string;
|
||||||
|
|
||||||
test.beforeEach(async (_fixtures, testInfo) => {
|
test.beforeEach(async ({}, testInfo) => {
|
||||||
projectName = `test-project-${testInfo.workerIndex}-${Date.now()}`;
|
projectName = `test-project-${testInfo.workerIndex}-${Date.now()}`;
|
||||||
projectPath = path.join(TEST_TEMP_DIR, projectName);
|
projectPath = path.join(TEST_TEMP_DIR, projectName);
|
||||||
fs.mkdirSync(projectPath, { recursive: true });
|
fs.mkdirSync(projectPath, { recursive: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user