UNPKG

kwgit

Version:

A command-line utility to help with cleaning up git branches. This tool provides a safe and efficient way to manage your git branches, helping you keep your repository clean and organized.

189 lines (161 loc) 6.44 kB
import chalk from 'chalk'; import { getBranchesWithMetadata } from '../services/gitService.js'; import { deleteBranch, deleteRemoteBranch } from '../services/gitService.js'; import { isProtectedBranch } from '../services/protectionService.js'; import { confirmBranchDeletion } from '../utils/prompts.js'; export const focusCommand = { command: 'focus <indices...>', describe: 'Perform operations on branches by their list index.', builder: yargs => yargs .positional('indices', { type: 'string', describe: '0-based indices from kwgit list', array: true, }) .option('remote', { alias: 'r', type: 'boolean', describe: 'Include remote branches (must match list filters)', default: false, }) .option('remote-only', { type: 'boolean', describe: 'Only remote branches (must match list filters)', default: false, }) .option('all-branches', { alias: 'a', type: 'boolean', describe: 'Local + remote branches (must match list filters)', default: false, }) .option('json', { type: 'boolean', describe: 'Output structured JSON', default: false, }) .option('view', { type: 'boolean', describe: 'Only view selected branches (do not delete)', default: false, }) .option('force', { alias: 'f', type: 'boolean', describe: 'Force delete', default: false, }) .option('yes', { alias: 'y', type: 'boolean', describe: 'Delete all selected branches without individual prompts', default: false, }), handler: async ({ indices, remote, remoteOnly, allBranches, json, view, force, yes }) => { if (!indices || indices.length === 0) { console.error(chalk.red('Error: At least one index is required.')); process.exit(1); } const includeRemote = remote || allBranches; const onlyRemote = remoteOnly; if (onlyRemote && !includeRemote) { console.error(chalk.red('Error: --remote-only requires remote branches to be included.')); process.exit(1); } const branches = await getBranchesWithMetadata({ includeRemote: includeRemote || onlyRemote, remoteOnly: onlyRemote, }); if (branches.length === 0) { console.log(chalk.yellow('No branches found.')); return; } const parsedIndices = indices.map(idx => { const num = parseInt(idx, 10); if (isNaN(num) || num < 0) { throw new Error(`Invalid index: ${idx}`); } return num; }); const invalidIndices = parsedIndices.filter(idx => idx >= branches.length); if (invalidIndices.length > 0) { console.error(chalk.red(`Error: Invalid indices: ${invalidIndices.join(', ')}. Maximum index is ${branches.length - 1}.`)); process.exit(1); } const selectedBranches = parsedIndices.map(idx => branches[idx]); const protectedBranches = selectedBranches.filter(b => b.isProtected); if (protectedBranches.length > 0) { console.error(chalk.red(`Error: Cannot focus on protected branches: ${protectedBranches.map(b => b.name).join(', ')}`)); console.error(chalk.yellow('Protected branches cannot be deleted. Remove them from your selection.')); process.exit(1); } if (json) { console.log(JSON.stringify(selectedBranches, null, 2)); return; } if (view) { console.log(chalk.bold(`\nSelected branches:`)); selectedBranches.forEach(branch => { const remoteLabel = branch.isRemote ? chalk.gray(' (remote)') : ''; const currentLabel = branch.isCurrent ? chalk.cyan(' (current)') : ''; const protectedLabel = branch.isProtected ? chalk.gray(' (protected)') : ''; console.log(` • ${chalk.cyan(branch.name)}${remoteLabel}${currentLabel}${protectedLabel}`); }); return; } const deleteAll = yes; const currentBranches = selectedBranches.filter(b => b.isCurrent); if (currentBranches.length > 0) { console.error(chalk.red(`Error: Cannot delete current branch: ${currentBranches.map(b => b.name).join(', ')}`)); console.error(chalk.yellow('Switch to a different branch before deleting.')); process.exit(1); } const deletableBranches = selectedBranches.filter(b => !b.isCurrent); if (deletableBranches.length === 0) { console.log(chalk.yellow('No deletable branches selected.')); return; } console.log(chalk.bold(`\nReviewing ${deletableBranches.length} branch${deletableBranches.length === 1 ? '' : 'es'} for deletion:\n`)); const deletedBranches = []; const skippedBranches = []; const failedBranches = []; for (const branch of deletableBranches) { let shouldDelete = deleteAll; if (!deleteAll) { const branchLabel = branch.isRemote ? `${branch.name} (remote)` : branch.name; shouldDelete = await confirmBranchDeletion(branchLabel); } if (!shouldDelete) { skippedBranches.push(branch); console.log(chalk.gray(` Skipped ${branch.name}`)); continue; } try { if (branch.isRemote) { await deleteRemoteBranch(branch.name); deletedBranches.push(branch); console.log(chalk.green(` ✓ Deleted remote branch: ${branch.name}`)); } else { await deleteBranch(branch.name, force); deletedBranches.push(branch); console.log(chalk.green(` ✓ Deleted local branch: ${branch.name}`)); } } catch (err) { failedBranches.push({ branch, error: err }); const remoteLabel = branch.isRemote ? 'remote ' : ''; console.error(chalk.red(` ✗ Failed to delete ${remoteLabel}branch ${branch.name}: ${err.message}`)); } } console.log(); if (deletedBranches.length > 0) { console.log(chalk.green(`✓ Successfully deleted ${deletedBranches.length} branch${deletedBranches.length === 1 ? '' : 'es'}.`)); } if (skippedBranches.length > 0) { console.log(chalk.yellow(`⊘ Skipped ${skippedBranches.length} branch${skippedBranches.length === 1 ? '' : 'es'}.`)); } if (failedBranches.length > 0) { console.log(chalk.red(`✗ Failed to delete ${failedBranches.length} branch${failedBranches.length === 1 ? '' : 'es'}.`)); } }, };