253 lines
7.4 KiB
Bash
Executable File
253 lines
7.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Git Worktree Cleanup
|
|
# Interactive removal of old/merged worktrees
|
|
|
|
set -e
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
CYAN='\033[0;36m'
|
|
GRAY='\033[0;90m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Check if we're in a git repository
|
|
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
echo -e "${RED}✗ Error: Not in a git repository${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
REPO_NAME=$(basename "$REPO_ROOT")
|
|
|
|
echo -e "${BLUE}╔════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${BLUE}║ Git Worktree Cleanup Tool ║${NC}"
|
|
echo -e "${BLUE}╚════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
# Get list of worktrees (excluding main)
|
|
WORKTREES=$(git worktree list --porcelain | grep -v "^$" | grep -v "^HEAD" | grep -v "^bare")
|
|
|
|
if [ -z "$WORKTREES" ]; then
|
|
echo -e "${YELLOW}No feature worktrees found${NC}"
|
|
echo "Only the main worktree exists."
|
|
exit 0
|
|
fi
|
|
|
|
# Arrays to store worktree info
|
|
declare -a PATHS
|
|
declare -a BRANCHES
|
|
declare -a STATUSES
|
|
declare -a MERGED_FLAGS
|
|
COUNT=0
|
|
|
|
# Parse worktrees
|
|
CURRENT_PATH=""
|
|
CURRENT_BRANCH=""
|
|
|
|
while IFS= read -r line; do
|
|
if [[ $line == worktree* ]]; then
|
|
CURRENT_PATH=$(echo "$line" | awk '{print $2}')
|
|
elif [[ $line == branch* ]]; then
|
|
CURRENT_BRANCH=$(echo "$line" | awk '{print $2}' | sed 's|refs/heads/||')
|
|
|
|
# Skip main worktree
|
|
if [ "$CURRENT_PATH" != "$REPO_ROOT" ] && [ -n "$CURRENT_BRANCH" ]; then
|
|
PATHS[$COUNT]="$CURRENT_PATH"
|
|
BRANCHES[$COUNT]="$CURRENT_BRANCH"
|
|
|
|
# Check if branch is merged into main
|
|
cd "$REPO_ROOT"
|
|
if git branch --merged main | grep -q "^[* ]*${CURRENT_BRANCH}$"; then
|
|
MERGED_FLAGS[$COUNT]="merged"
|
|
STATUSES[$COUNT]="${GREEN}✓ Merged to main${NC}"
|
|
else
|
|
MERGED_FLAGS[$COUNT]="not-merged"
|
|
|
|
# Check if worktree is clean
|
|
cd "$CURRENT_PATH"
|
|
if git diff-index --quiet HEAD -- 2>/dev/null; then
|
|
STATUSES[$COUNT]="${YELLOW}⚠ Not merged, clean${NC}"
|
|
else
|
|
CHANGE_COUNT=$(git status --short | wc -l | tr -d ' ')
|
|
STATUSES[$COUNT]="${RED}⚠ Not merged, $CHANGE_COUNT changes${NC}"
|
|
fi
|
|
fi
|
|
|
|
((COUNT++))
|
|
fi
|
|
|
|
CURRENT_PATH=""
|
|
CURRENT_BRANCH=""
|
|
fi
|
|
done <<< "$WORKTREES"
|
|
|
|
if [ $COUNT -eq 0 ]; then
|
|
echo -e "${YELLOW}No feature worktrees to clean up${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
# Display worktrees
|
|
echo -e "${CYAN}Found $COUNT feature worktree(s):${NC}"
|
|
echo ""
|
|
|
|
for i in "${!PATHS[@]}"; do
|
|
echo -e "${CYAN}[$((i+1))]${NC} ${BRANCHES[$i]}"
|
|
echo -e " Path: ${GRAY}${PATHS[$i]}${NC}"
|
|
echo -e " Status: ${STATUSES[$i]}"
|
|
echo ""
|
|
done
|
|
|
|
# Prompt for selection
|
|
echo -e "${YELLOW}Which worktrees would you like to remove?${NC}"
|
|
echo -e " Enter numbers separated by spaces (e.g., '1 3 4')"
|
|
echo -e " Enter 'merged' to remove all merged worktrees"
|
|
echo -e " Enter 'all' to remove all worktrees"
|
|
echo -e " Press Enter to cancel"
|
|
echo ""
|
|
read -p "Selection: " SELECTION
|
|
|
|
# Parse selection
|
|
if [ -z "$SELECTION" ]; then
|
|
echo -e "${GRAY}Cancelled${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
# Build list of indices to remove
|
|
declare -a TO_REMOVE
|
|
|
|
if [ "$SELECTION" = "all" ]; then
|
|
for i in "${!PATHS[@]}"; do
|
|
TO_REMOVE+=($i)
|
|
done
|
|
elif [ "$SELECTION" = "merged" ]; then
|
|
for i in "${!PATHS[@]}"; do
|
|
if [ "${MERGED_FLAGS[$i]}" = "merged" ]; then
|
|
TO_REMOVE+=($i)
|
|
fi
|
|
done
|
|
if [ ${#TO_REMOVE[@]} -eq 0 ]; then
|
|
echo -e "${YELLOW}No merged worktrees found${NC}"
|
|
exit 0
|
|
fi
|
|
else
|
|
# Parse space-separated numbers
|
|
for num in $SELECTION; do
|
|
# Validate number
|
|
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le "$COUNT" ]; then
|
|
TO_REMOVE+=($((num - 1)))
|
|
else
|
|
echo -e "${RED}Invalid selection: $num${NC}"
|
|
exit 1
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Confirmation
|
|
echo ""
|
|
echo -e "${YELLOW}About to remove ${#TO_REMOVE[@]} worktree(s):${NC}"
|
|
echo ""
|
|
for idx in "${TO_REMOVE[@]}"; do
|
|
echo -e " ${RED}✗${NC} ${BRANCHES[$idx]}"
|
|
echo -e " ${GRAY}${PATHS[$idx]}${NC}"
|
|
done
|
|
echo ""
|
|
|
|
# Extra warning for non-merged branches
|
|
HAS_UNMERGED=false
|
|
for idx in "${TO_REMOVE[@]}"; do
|
|
if [ "${MERGED_FLAGS[$idx]}" != "merged" ]; then
|
|
HAS_UNMERGED=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$HAS_UNMERGED" = true ]; then
|
|
echo -e "${RED}⚠ WARNING: Some branches are NOT merged!${NC}"
|
|
echo -e "${RED} You may lose uncommitted work.${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
read -p "Are you sure? (yes/no): " CONFIRM
|
|
|
|
if [ "$CONFIRM" != "yes" ]; then
|
|
echo -e "${GRAY}Cancelled${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
# Remove worktrees
|
|
echo ""
|
|
echo -e "${BLUE}Removing worktrees...${NC}"
|
|
echo ""
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
for idx in "${TO_REMOVE[@]}"; do
|
|
WORKTREE_PATH="${PATHS[$idx]}"
|
|
BRANCH_NAME="${BRANCHES[$idx]}"
|
|
|
|
echo -e "${CYAN}→${NC} Removing worktree: ${BRANCH_NAME}"
|
|
|
|
# Remove worktree
|
|
if git worktree remove "$WORKTREE_PATH" --force 2>/dev/null; then
|
|
echo -e " ${GREEN}✓${NC} Worktree removed"
|
|
else
|
|
# Try manual removal if git worktree remove fails
|
|
if [ -d "$WORKTREE_PATH" ]; then
|
|
rm -rf "$WORKTREE_PATH"
|
|
git worktree prune
|
|
echo -e " ${GREEN}✓${NC} Worktree removed (manual cleanup)"
|
|
else
|
|
echo -e " ${YELLOW}⚠${NC} Worktree already removed"
|
|
fi
|
|
fi
|
|
|
|
# Ask about deleting the branch
|
|
if [ "${MERGED_FLAGS[$idx]}" = "merged" ]; then
|
|
# Auto-delete merged branches
|
|
git branch -d "$BRANCH_NAME" 2>/dev/null && \
|
|
echo -e " ${GREEN}✓${NC} Branch deleted" || \
|
|
echo -e " ${GRAY}Branch already deleted or doesn't exist${NC}"
|
|
else
|
|
# Prompt for unmerged branches
|
|
echo -e " ${YELLOW}Branch '$BRANCH_NAME' is not merged.${NC}"
|
|
read -p " Delete branch anyway? (y/n): " DELETE_BRANCH
|
|
if [[ "$DELETE_BRANCH" =~ ^[Yy]$ ]]; then
|
|
git branch -D "$BRANCH_NAME" 2>/dev/null && \
|
|
echo -e " ${GREEN}✓${NC} Branch force-deleted" || \
|
|
echo -e " ${GRAY}Could not delete branch${NC}"
|
|
else
|
|
echo -e " ${GRAY}Branch kept${NC}"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
done
|
|
|
|
# Prune any stale references
|
|
echo -e "${CYAN}→${NC} Cleaning up stale references..."
|
|
git worktree prune
|
|
echo -e "${GREEN}✓${NC} Done"
|
|
echo ""
|
|
|
|
# Summary
|
|
echo -e "${GREEN}✓ Cleanup complete!${NC}"
|
|
echo ""
|
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
|
echo -e "Removed: ${CYAN}${#TO_REMOVE[@]}${NC} worktree(s)"
|
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
# Show remaining worktrees
|
|
REMAINING=$(git worktree list | wc -l)
|
|
if [ $REMAINING -gt 1 ]; then
|
|
echo -e "${CYAN}Remaining worktrees: $((REMAINING - 1))${NC}"
|
|
echo -e "${GRAY}Run 'scripts/list_worktrees.sh' to see them${NC}"
|
|
else
|
|
echo -e "${CYAN}No feature worktrees remaining${NC}"
|
|
fi
|
|
echo ""
|