fix(session): handle worktree deletion without sessions

This commit is contained in:
Bohdan Triapitsyn
2026-01-03 01:20:56 +02:00
parent 17df8a43a9
commit da7f0679d8
@@ -28,6 +28,7 @@ import { cn, formatPathForDisplay } from '@/lib/utils';
import type { Session } from '@opencode-ai/sdk'; import type { Session } from '@opencode-ai/sdk';
import type { WorktreeMetadata } from '@/types/worktree'; import type { WorktreeMetadata } from '@/types/worktree';
import { import {
archiveWorktree,
createWorktree, createWorktree,
getWorktreeStatus, getWorktreeStatus,
listWorktrees as listGitWorktrees, listWorktrees as listGitWorktrees,
@@ -591,13 +592,30 @@ export const SessionDialogs: React.FC = () => {
setIsProcessingDelete(true); setIsProcessingDelete(true);
try { try {
const archiveWorktree = shouldArchiveWorktree; const shouldArchive = shouldArchiveWorktree;
const removeRemoteBranch = archiveWorktree && deleteDialogShouldRemoveRemote; const removeRemoteBranch = shouldArchive && deleteDialogShouldRemoveRemote;
if (deleteDialog.sessions.length === 0 && isWorktreeDelete && deleteDialog.worktree) {
const shouldRemoveRemote = deleteDialogShouldRemoveRemote && canRemoveRemoteBranches;
await archiveWorktree({
projectDirectory: projectDirectory,
path: deleteDialog.worktree.path,
branch: deleteDialog.worktree.branch,
force: true,
deleteRemote: shouldRemoveRemote,
});
const archiveNote = shouldRemoveRemote ? 'Worktree and remote branch removed.' : 'Worktree removed.';
toast.success('Worktree removed', {
description: renderToastDescription(archiveNote),
});
closeDeleteDialog();
return;
}
if (deleteDialog.sessions.length === 1) { if (deleteDialog.sessions.length === 1) {
const target = deleteDialog.sessions[0]; const target = deleteDialog.sessions[0];
const success = await deleteSession(target.id, { const success = await deleteSession(target.id, {
archiveWorktree, archiveWorktree: shouldArchive,
deleteRemoteBranch: removeRemoteBranch, deleteRemoteBranch: removeRemoteBranch,
}); });
if (!success) { if (!success) {
@@ -605,7 +623,7 @@ export const SessionDialogs: React.FC = () => {
setIsProcessingDelete(false); setIsProcessingDelete(false);
return; return;
} }
const archiveNote = archiveWorktree const archiveNote = shouldArchive
? removeRemoteBranch ? removeRemoteBranch
? 'Worktree and remote branch removed.' ? 'Worktree and remote branch removed.'
: 'Attached worktree archived.' : 'Attached worktree archived.'
@@ -616,12 +634,12 @@ export const SessionDialogs: React.FC = () => {
} else { } else {
const ids = deleteDialog.sessions.map((session) => session.id); const ids = deleteDialog.sessions.map((session) => session.id);
const { deletedIds, failedIds } = await deleteSessions(ids, { const { deletedIds, failedIds } = await deleteSessions(ids, {
archiveWorktree, archiveWorktree: shouldArchive,
deleteRemoteBranch: removeRemoteBranch, deleteRemoteBranch: removeRemoteBranch,
}); });
if (deletedIds.length > 0) { if (deletedIds.length > 0) {
const archiveNote = archiveWorktree const archiveNote = shouldArchive
? removeRemoteBranch ? removeRemoteBranch
? 'Archived worktrees and removed remote branches.' ? 'Archived worktrees and removed remote branches.'
: 'Attached worktrees archived.' : 'Attached worktrees archived.'
@@ -653,7 +671,7 @@ export const SessionDialogs: React.FC = () => {
} finally { } finally {
setIsProcessingDelete(false); setIsProcessingDelete(false);
} }
}, [deleteDialog, deleteDialogShouldRemoveRemote, deleteSession, deleteSessions, closeDeleteDialog, shouldArchiveWorktree]); }, [deleteDialog, deleteDialogShouldRemoveRemote, deleteSession, deleteSessions, closeDeleteDialog, shouldArchiveWorktree, isWorktreeDelete, canRemoveRemoteBranches, projectDirectory]);
const worktreeManagerBody = ( const worktreeManagerBody = (
<div className="space-y-4 w-full min-w-0"> <div className="space-y-4 w-full min-w-0">
@@ -929,27 +947,31 @@ export const SessionDialogs: React.FC = () => {
const targetWorktree = deleteDialog?.worktree ?? deleteDialogSummaries[0]?.metadata ?? null; const targetWorktree = deleteDialog?.worktree ?? deleteDialogSummaries[0]?.metadata ?? null;
const deleteDialogDescription = deleteDialog const deleteDialogDescription = deleteDialog
? deleteDialog.mode === 'worktree' ? deleteDialog.mode === 'worktree'
? `This removes the selected worktree and ${deleteDialog.sessions.length === 1 ? '1 linked session' : `${deleteDialog.sessions.length} linked sessions`}.` ? deleteDialog.sessions.length === 0
? 'This removes the selected worktree.'
: `This removes the selected worktree and ${deleteDialog.sessions.length === 1 ? '1 linked session' : `${deleteDialog.sessions.length} linked sessions`}.`
: `This action permanently removes ${deleteDialog.sessions.length === 1 ? '1 session' : `${deleteDialog.sessions.length} sessions`}${deleteDialog.dateLabel ? ` from ${deleteDialog.dateLabel}` : '' : `This action permanently removes ${deleteDialog.sessions.length === 1 ? '1 session' : `${deleteDialog.sessions.length} sessions`}${deleteDialog.dateLabel ? ` from ${deleteDialog.dateLabel}` : ''
}.` }.`
: ''; : '';
const deleteDialogBody = deleteDialog ? ( const deleteDialogBody = deleteDialog ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="space-y-1.5 rounded-xl border border-border/40 bg-sidebar/60 p-3"> {deleteDialog.sessions.length > 0 && (
<ul className="space-y-0.5"> <div className="space-y-1.5 rounded-xl border border-border/40 bg-sidebar/60 p-3">
{deleteDialog.sessions.slice(0, 3).map((session) => ( <ul className="space-y-0.5">
<li key={session.id} className="typography-micro text-muted-foreground/80"> {deleteDialog.sessions.slice(0, 3).map((session) => (
{session.title || 'Untitled Session'} <li key={session.id} className="typography-micro text-muted-foreground/80">
</li> {session.title || 'Untitled Session'}
))} </li>
{deleteDialog.sessions.length > 3 && ( ))}
<li className="typography-micro text-muted-foreground/70"> {deleteDialog.sessions.length > 3 && (
+{deleteDialog.sessions.length - 3} more <li className="typography-micro text-muted-foreground/70">
</li> +{deleteDialog.sessions.length - 3} more
)} </li>
</ul> )}
</div> </ul>
</div>
)}
{isWorktreeDelete ? ( {isWorktreeDelete ? (
<div className="space-y-2 rounded-xl border border-border/40 bg-sidebar/60 p-3"> <div className="space-y-2 rounded-xl border border-border/40 bg-sidebar/60 p-3">