@shutootaki/gwm
Version:
git worktree manager CLI
112 lines • 5.15 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from 'react';
import { Text, Box } from 'ink';
import { MultiSelectList } from './MultiSelectList.js';
import { getWorktreesWithStatus, removeWorktree, deleteLocalBranch, localBranchExists, hasUnmergedCommits, } from '../utils/index.js';
import { loadConfig } from '../config.js';
import { Notice } from './ui/Notice.js';
import { LoadingSpinner } from './ui/LoadingSpinner.js';
export const WorktreeRemove = ({ query = '', force = false, cleanBranch, }) => {
const config = loadConfig();
const cleanMode = cleanBranch || config.clean_branch;
const [worktrees, setWorktrees] = useState([]);
const [error, setError] = useState(null);
const [success, setSuccess] = useState([]);
const [branchSuccess, setBranchSuccess] = useState([]);
const [removing, setRemoving] = useState(false);
useEffect(() => {
const loadWorktrees = async () => {
try {
const allWorktrees = await getWorktreesWithStatus();
// メインworktreeを除外
const nonMainWorktrees = allWorktrees.filter((w) => !w.isMain);
setWorktrees(nonMainWorktrees);
}
catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
}
};
loadWorktrees();
}, []);
const handleConfirm = async (selectedItems) => {
if (selectedItems.length === 0) {
setError('No worktrees selected');
return;
}
setRemoving(true);
const removedPaths = [];
const errors = [];
const removedBranches = new Set();
for (const item of selectedItems) {
try {
await removeWorktree(item.value, force);
removedPaths.push(item.value);
const wt = worktrees.find((w) => w.path === item.value);
if (wt)
removedBranches.add(wt.branch);
}
catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
errors.push(`${item.value}: ${errorMsg}`);
}
}
// ブランチクリーンアップ処理
if (removedBranches.size > 0 && cleanMode !== 'never') {
const remaining = await getWorktreesWithStatus();
const candidateBranches = Array.from(removedBranches).filter((br) => !remaining.some((w) => w.branch === br) && localBranchExists(br));
if (cleanMode === 'auto') {
const deleted = [];
for (const br of candidateBranches) {
const unmerged = hasUnmergedCommits(br);
try {
deleteLocalBranch(br, unmerged);
deleted.push(br + (unmerged ? ' (forced)' : ''));
}
catch (e) {
errors.push(`branch ${br}: ${e instanceof Error ? e.message : e}`);
}
}
if (deleted.length > 0) {
setBranchSuccess(deleted);
}
}
// "ask" モードは未実装。スキップして注意喚起
if (cleanMode === 'ask') {
if (candidateBranches.length > 0) {
errors.push(`Branches ${candidateBranches.join(', ')} remain locally. Re-run with --clean-branch=auto to remove.`);
}
}
}
if (errors.length > 0) {
setError(errors.join('\n'));
}
if (removedPaths.length > 0) {
setSuccess(removedPaths);
}
setRemoving(false);
};
const handleCancel = () => {
setError('Cancelled');
};
if (success.length > 0) {
return (_jsx(Notice, { variant: "success", title: `Successfully removed ${success.length} worktree(s)`, messages: [
...success.map((p) => `✓ ${p}`),
...branchSuccess.map((b) => `✓ cleaned branch ${b}`),
] }));
}
if (error) {
return _jsx(Notice, { variant: "error", title: "Error", messages: error });
}
if (removing) {
return _jsx(LoadingSpinner, { label: "Removing worktrees...", color: "yellow" });
}
if (worktrees.length === 0) {
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", padding: 1, children: [_jsx(Text, { color: "gray", bold: true, children: "No removable worktrees found" }), _jsx(Text, { color: "gray", children: "Main worktree cannot be removed" })] }));
}
const items = worktrees.map((worktree) => ({
label: `${worktree.branch.padEnd(30)} ${worktree.path}`,
value: worktree.path,
}));
return (_jsx(MultiSelectList, { items: items, onConfirm: handleConfirm, onCancel: handleCancel, title: `Remove worktrees${force ? ' (force mode)' : ''}`, placeholder: `Select worktrees to remove${force ? ' (force mode)' : ''}...`, initialQuery: query }));
};
//# sourceMappingURL=WorktreeRemove.js.map