Initial commit
This commit is contained in:
252
skills/git-worktrees/scripts/cleanup_worktrees.sh
Executable file
252
skills/git-worktrees/scripts/cleanup_worktrees.sh
Executable file
@@ -0,0 +1,252 @@
|
||||
#!/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 ""
|
||||
211
skills/git-worktrees/scripts/create_worktree.sh
Executable file
211
skills/git-worktrees/scripts/create_worktree.sh
Executable file
@@ -0,0 +1,211 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Git Worktree Creator for Claude Code
|
||||
# Makes creating worktrees super simple with interactive prompts
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
BASE_BRANCH="main"
|
||||
CUSTOM_DIR=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--base)
|
||||
BASE_BRANCH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--dir)
|
||||
CUSTOM_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --base <branch> Base branch to branch from (default: main)"
|
||||
echo " --dir <path> Custom directory for worktree"
|
||||
echo " -h, --help Show this help"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0"
|
||||
echo " $0 --base develop"
|
||||
echo " $0 --dir ~/worktrees/my-feature"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
echo "Use -h for help"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Git Worktree Creator for Claude Code ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# 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}"
|
||||
echo " Please run this script from within a git repository."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get repository info
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
REPO_NAME=$(basename "$REPO_ROOT")
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
|
||||
echo -e "${GREEN}✓${NC} Found git repository: ${CYAN}$REPO_NAME${NC}"
|
||||
echo -e "${GREEN}✓${NC} Current branch: ${CYAN}$CURRENT_BRANCH${NC}"
|
||||
echo ""
|
||||
|
||||
# Prompt for feature name
|
||||
echo -e "${YELLOW}What are you building?${NC}"
|
||||
echo -e " Enter a feature name (e.g., feature-api, refactor-auth, hotfix-bug)"
|
||||
echo ""
|
||||
read -p "Feature name: " FEATURE_NAME
|
||||
|
||||
# Validate feature name
|
||||
if [ -z "$FEATURE_NAME" ]; then
|
||||
echo -e "${RED}✗ Feature name cannot be empty${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sanitize feature name (replace spaces with hyphens, lowercase)
|
||||
FEATURE_NAME=$(echo "$FEATURE_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed 's/[^a-z0-9-]//g')
|
||||
|
||||
if [ -z "$FEATURE_NAME" ]; then
|
||||
echo -e "${RED}✗ Invalid feature name${NC}"
|
||||
echo " Use only letters, numbers, and hyphens"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Using branch name: ${CYAN}$FEATURE_NAME${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if branch already exists
|
||||
if git show-ref --verify --quiet "refs/heads/$FEATURE_NAME"; then
|
||||
echo -e "${YELLOW}⚠${NC} Branch ${CYAN}$FEATURE_NAME${NC} already exists"
|
||||
echo ""
|
||||
read -p "Use existing branch? (y/n): " USE_EXISTING
|
||||
if [[ ! "$USE_EXISTING" =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}✗ Cancelled${NC}"
|
||||
exit 1
|
||||
fi
|
||||
CREATE_BRANCH=false
|
||||
else
|
||||
# Prompt for base branch
|
||||
echo -e "${YELLOW}Base branch${NC} (press Enter for default: $BASE_BRANCH)"
|
||||
read -p "Base branch: " CUSTOM_BASE
|
||||
if [ -n "$CUSTOM_BASE" ]; then
|
||||
BASE_BRANCH="$CUSTOM_BASE"
|
||||
fi
|
||||
|
||||
# Verify base branch exists
|
||||
if ! git show-ref --verify --quiet "refs/heads/$BASE_BRANCH"; then
|
||||
echo -e "${RED}✗ Base branch '$BASE_BRANCH' does not exist${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Will create branch from: ${CYAN}$BASE_BRANCH${NC}"
|
||||
CREATE_BRANCH=true
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Determine worktree directory
|
||||
if [ -n "$CUSTOM_DIR" ]; then
|
||||
WORKTREE_DIR="$CUSTOM_DIR"
|
||||
else
|
||||
# Default: create sibling directory
|
||||
PARENT_DIR=$(dirname "$REPO_ROOT")
|
||||
WORKTREE_DIR="$PARENT_DIR/${REPO_NAME}-${FEATURE_NAME}"
|
||||
fi
|
||||
|
||||
# Check if directory already exists
|
||||
if [ -d "$WORKTREE_DIR" ]; then
|
||||
echo -e "${RED}✗ Directory already exists: $WORKTREE_DIR${NC}"
|
||||
echo " Please remove it first or use --dir to specify a different location"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Creating worktree...${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Branch:${NC} $FEATURE_NAME"
|
||||
echo -e " ${CYAN}Location:${NC} $WORKTREE_DIR"
|
||||
echo ""
|
||||
|
||||
# Create the worktree
|
||||
if [ "$CREATE_BRANCH" = true ]; then
|
||||
echo -e "${CYAN}→${NC} Creating branch from $BASE_BRANCH..."
|
||||
git worktree add -b "$FEATURE_NAME" "$WORKTREE_DIR" "$BASE_BRANCH"
|
||||
else
|
||||
echo -e "${CYAN}→${NC} Using existing branch $FEATURE_NAME..."
|
||||
git worktree add "$WORKTREE_DIR" "$FEATURE_NAME"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Worktree created successfully!${NC}"
|
||||
echo ""
|
||||
|
||||
# Success message with instructions
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Next Steps ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}1. Open Claude Code in the new worktree:${NC}"
|
||||
echo -e " ${YELLOW}cd $WORKTREE_DIR${NC}"
|
||||
echo -e " ${YELLOW}code .${NC} (or your editor command)"
|
||||
echo ""
|
||||
echo -e "${CYAN}2. In Claude Code, run /init to orient Claude:${NC}"
|
||||
echo -e " ${YELLOW}/init${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}3. Give Claude your task:${NC}"
|
||||
echo -e " ${YELLOW}\"Build the $FEATURE_NAME feature\"${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}4. When done, merge back to main:${NC}"
|
||||
echo -e " ${YELLOW}cd $REPO_ROOT${NC}"
|
||||
echo -e " ${YELLOW}git checkout main${NC}"
|
||||
echo -e " ${YELLOW}git merge $FEATURE_NAME${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}5. Clean up the worktree:${NC}"
|
||||
echo -e " ${YELLOW}scripts/cleanup_worktrees.sh${NC}"
|
||||
echo ""
|
||||
|
||||
# Offer to open in editor
|
||||
echo ""
|
||||
read -p "Open in VS Code now? (y/n): " OPEN_NOW
|
||||
if [[ "$OPEN_NOW" =~ ^[Yy]$ ]]; then
|
||||
if command -v code &> /dev/null; then
|
||||
echo -e "${CYAN}→${NC} Opening in VS Code..."
|
||||
code "$WORKTREE_DIR"
|
||||
echo -e "${GREEN}✓${NC} VS Code opened!"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Remember to run /init when Claude Code loads!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} VS Code command 'code' not found"
|
||||
echo " Please open manually: $WORKTREE_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Done!${NC}"
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||
echo -e "Worktree: ${CYAN}$WORKTREE_DIR${NC}"
|
||||
echo -e "Branch: ${CYAN}$FEATURE_NAME${NC}"
|
||||
echo -e "Status: ${GREEN}Ready for Claude Code${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||
117
skills/git-worktrees/scripts/list_worktrees.sh
Executable file
117
skills/git-worktrees/scripts/list_worktrees.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
|
||||
# List Git Worktrees
|
||||
# Shows all active worktrees with clean formatting
|
||||
|
||||
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 Worktrees - $REPO_NAME"
|
||||
printf "${BLUE}║${NC}"
|
||||
printf "%*s${BLUE}║${NC}\n" $((47 - ${#REPO_NAME})) ""
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# Get worktree list
|
||||
WORKTREES=$(git worktree list --porcelain)
|
||||
|
||||
if [ -z "$WORKTREES" ]; then
|
||||
echo -e "${YELLOW}No worktrees found${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Parse and display worktrees
|
||||
MAIN_WORKTREE=""
|
||||
FEATURE_WORKTREES=""
|
||||
WORKTREE_COUNT=0
|
||||
|
||||
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/||')
|
||||
elif [[ $line == "" ]]; then
|
||||
# End of worktree block
|
||||
if [ -n "$CURRENT_PATH" ]; then
|
||||
((WORKTREE_COUNT++))
|
||||
|
||||
# Get status
|
||||
cd "$CURRENT_PATH"
|
||||
if git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
STATUS="${GREEN}clean${NC}"
|
||||
else
|
||||
CHANGE_COUNT=$(git status --short | wc -l | tr -d ' ')
|
||||
STATUS="${YELLOW}$CHANGE_COUNT uncommitted changes${NC}"
|
||||
fi
|
||||
|
||||
# Get last commit info
|
||||
LAST_COMMIT=$(git log -1 --format="%cr" 2>/dev/null || echo "no commits")
|
||||
|
||||
# Check if it's the main worktree
|
||||
if [ "$CURRENT_PATH" = "$REPO_ROOT" ]; then
|
||||
MAIN_WORKTREE+="📁 ${CYAN}Main Worktree${NC}\n"
|
||||
MAIN_WORKTREE+=" Path: ${GRAY}$CURRENT_PATH${NC}\n"
|
||||
MAIN_WORKTREE+=" Branch: ${CYAN}$CURRENT_BRANCH${NC}\n"
|
||||
MAIN_WORKTREE+=" Status: $STATUS\n"
|
||||
MAIN_WORKTREE+=" Last: ${GRAY}$LAST_COMMIT${NC}\n"
|
||||
else
|
||||
FEATURE_WORKTREES+="📦 ${CYAN}$CURRENT_BRANCH${NC}\n"
|
||||
FEATURE_WORKTREES+=" Path: ${GRAY}$CURRENT_PATH${NC}\n"
|
||||
FEATURE_WORKTREES+=" Status: $STATUS\n"
|
||||
FEATURE_WORKTREES+=" Last: ${GRAY}$LAST_COMMIT${NC}\n\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Reset for next worktree
|
||||
CURRENT_PATH=""
|
||||
CURRENT_BRANCH=""
|
||||
fi
|
||||
done <<< "$WORKTREES"
|
||||
|
||||
# Display main worktree
|
||||
if [ -n "$MAIN_WORKTREE" ]; then
|
||||
echo -e "$MAIN_WORKTREE"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Display feature worktrees
|
||||
if [ -n "$FEATURE_WORKTREES" ]; then
|
||||
echo -e "${BLUE}Feature Worktrees:${NC}"
|
||||
echo ""
|
||||
echo -e "$FEATURE_WORKTREES"
|
||||
else
|
||||
echo -e "${GRAY}No feature worktrees (only main)${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||
echo -e "Total worktrees: ${CYAN}$WORKTREE_COUNT${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# Tips
|
||||
if [ $WORKTREE_COUNT -gt 1 ]; then
|
||||
echo -e "${GRAY}Tip: Run 'scripts/cleanup_worktrees.sh' to remove old worktrees${NC}"
|
||||
else
|
||||
echo -e "${GRAY}Tip: Run 'scripts/create_worktree.sh' to create a new worktree${NC}"
|
||||
fi
|
||||
echo ""
|
||||
232
skills/git-worktrees/scripts/sync_worktree.sh
Executable file
232
skills/git-worktrees/scripts/sync_worktree.sh
Executable file
@@ -0,0 +1,232 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Git Worktree Sync
|
||||
# Keep worktree branch up-to-date with main branch
|
||||
|
||||
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
|
||||
|
||||
# Default values
|
||||
TARGET_WORKTREE=""
|
||||
BASE_BRANCH="main"
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--base)
|
||||
BASE_BRANCH="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [WORKTREE_PATH] [OPTIONS]"
|
||||
echo ""
|
||||
echo "Syncs a worktree with the latest changes from main (or specified base branch)."
|
||||
echo ""
|
||||
echo "Arguments:"
|
||||
echo " WORKTREE_PATH Path to worktree (optional if run from within worktree)"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --base <branch> Base branch to sync from (default: main)"
|
||||
echo " -h, --help Show this help"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " # From within a worktree"
|
||||
echo " $0"
|
||||
echo ""
|
||||
echo " # Specify worktree path"
|
||||
echo " $0 ../repo-feature-api"
|
||||
echo ""
|
||||
echo " # Sync from develop instead of main"
|
||||
echo " $0 --base develop"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
TARGET_WORKTREE="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Git Worktree Sync Tool ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# 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
|
||||
|
||||
# Determine target worktree
|
||||
if [ -z "$TARGET_WORKTREE" ]; then
|
||||
# Use current directory
|
||||
TARGET_WORKTREE=$(pwd)
|
||||
echo -e "${CYAN}→${NC} Using current directory"
|
||||
else
|
||||
# Use specified path
|
||||
if [ ! -d "$TARGET_WORKTREE" ]; then
|
||||
echo -e "${RED}✗ Error: Directory not found: $TARGET_WORKTREE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
cd "$TARGET_WORKTREE"
|
||||
echo -e "${CYAN}→${NC} Using specified worktree: $TARGET_WORKTREE"
|
||||
fi
|
||||
|
||||
# Verify we're in a git worktree
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
echo -e "${RED}✗ Error: Not a git worktree: $TARGET_WORKTREE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
REPO_NAME=$(basename "$REPO_ROOT")
|
||||
|
||||
echo -e "${GREEN}✓${NC} Repository: ${CYAN}$REPO_NAME${NC}"
|
||||
echo -e "${GREEN}✓${NC} Current branch: ${CYAN}$CURRENT_BRANCH${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if current branch is the base branch
|
||||
if [ "$CURRENT_BRANCH" = "$BASE_BRANCH" ]; then
|
||||
echo -e "${YELLOW}⚠${NC} You're on the base branch ($BASE_BRANCH)"
|
||||
echo " Nothing to sync!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
echo -e "${YELLOW}⚠ Warning: You have uncommitted changes${NC}"
|
||||
echo ""
|
||||
git status --short
|
||||
echo ""
|
||||
read -p "Stash changes and continue? (y/n): " STASH_CHANGES
|
||||
if [[ ! "$STASH_CHANGES" =~ ^[Yy]$ ]]; then
|
||||
echo -e "${GRAY}Cancelled${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}→${NC} Stashing changes..."
|
||||
git stash push -m "Auto-stash before sync at $(date)"
|
||||
STASHED=true
|
||||
echo -e "${GREEN}✓${NC} Changes stashed"
|
||||
echo ""
|
||||
else
|
||||
STASHED=false
|
||||
fi
|
||||
|
||||
# Fetch latest
|
||||
echo -e "${CYAN}→${NC} Fetching latest from remote..."
|
||||
git fetch origin
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}✗ Failed to fetch from remote${NC}"
|
||||
if [ "$STASHED" = true ]; then
|
||||
echo -e "${CYAN}→${NC} Restoring stashed changes..."
|
||||
git stash pop
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Fetched latest"
|
||||
echo ""
|
||||
|
||||
# Check if base branch exists locally
|
||||
if ! git show-ref --verify --quiet "refs/heads/$BASE_BRANCH"; then
|
||||
echo -e "${RED}✗ Base branch '$BASE_BRANCH' does not exist locally${NC}"
|
||||
if [ "$STASHED" = true ]; then
|
||||
echo -e "${CYAN}→${NC} Restoring stashed changes..."
|
||||
git stash pop
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Show what we're about to do
|
||||
BEHIND_COUNT=$(git rev-list --count HEAD..origin/$BASE_BRANCH 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$BEHIND_COUNT" = "0" ]; then
|
||||
echo -e "${GREEN}✓${NC} Already up-to-date with $BASE_BRANCH!"
|
||||
if [ "$STASHED" = true ]; then
|
||||
echo ""
|
||||
echo -e "${CYAN}→${NC} Restoring stashed changes..."
|
||||
git stash pop
|
||||
echo -e "${GREEN}✓${NC} Changes restored"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Your branch is $BEHIND_COUNT commit(s) behind $BASE_BRANCH${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}Recent commits in $BASE_BRANCH:${NC}"
|
||||
git log --oneline HEAD..origin/$BASE_BRANCH | head -5
|
||||
echo ""
|
||||
|
||||
read -p "Merge $BASE_BRANCH into $CURRENT_BRANCH? (y/n): " CONFIRM_MERGE
|
||||
if [[ ! "$CONFIRM_MERGE" =~ ^[Yy]$ ]]; then
|
||||
echo -e "${GRAY}Cancelled${NC}"
|
||||
if [ "$STASHED" = true ]; then
|
||||
echo -e "${CYAN}→${NC} Restoring stashed changes..."
|
||||
git stash pop
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Perform the merge
|
||||
echo ""
|
||||
echo -e "${CYAN}→${NC} Merging $BASE_BRANCH into $CURRENT_BRANCH..."
|
||||
|
||||
if git merge origin/$BASE_BRANCH -m "Merge $BASE_BRANCH into $CURRENT_BRANCH"; then
|
||||
echo -e "${GREEN}✓${NC} Merge successful!"
|
||||
|
||||
# Show summary
|
||||
echo ""
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||
echo -e "Branch: ${CYAN}$CURRENT_BRANCH${NC}"
|
||||
echo -e "Status: ${GREEN}Synced with $BASE_BRANCH${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||
|
||||
# Restore stashed changes if any
|
||||
if [ "$STASHED" = true ]; then
|
||||
echo ""
|
||||
echo -e "${CYAN}→${NC} Restoring stashed changes..."
|
||||
if git stash pop; then
|
||||
echo -e "${GREEN}✓${NC} Changes restored successfully"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Conflicts while restoring stashed changes"
|
||||
echo " Resolve conflicts manually, then run: git stash drop"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Sync complete!${NC}"
|
||||
echo ""
|
||||
|
||||
else
|
||||
# Merge failed (likely conflicts)
|
||||
echo -e "${RED}✗ Merge conflicts detected${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Conflicting files:${NC}"
|
||||
git diff --name-only --diff-filter=U
|
||||
echo ""
|
||||
echo -e "${CYAN}To resolve:${NC}"
|
||||
echo " 1. Fix conflicts in the files above"
|
||||
echo " 2. Stage resolved files: ${YELLOW}git add <file>${NC}"
|
||||
echo " 3. Complete the merge: ${YELLOW}git commit${NC}"
|
||||
|
||||
if [ "$STASHED" = true ]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}Note: Stashed changes will be restored after merge is complete${NC}"
|
||||
echo " Run: ${YELLOW}git stash pop${NC}"
|
||||
fi
|
||||
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user