Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:16:40 +08:00
commit f125e90b9f
370 changed files with 67769 additions and 0 deletions

View File

@@ -0,0 +1,498 @@
#!/bin/bash
# Git Worktree Batch Setup Script
# Generated by git-worktree-setup skill
#
# This script automates the creation of multiple git worktrees with parallel dependency installation.
#
# Usage:
# ./batch-setup.sh branch1 branch2 branch3 [--no-install] [--pattern=/path/{branch}]
set -euo pipefail
# ============================================================================
# Configuration
# ============================================================================
BRANCHES=()
SKIP_INSTALL=false
PATH_PATTERN=""
# Parse arguments
for arg in "$@"; do
case $arg in
--no-install)
SKIP_INSTALL=true
;;
--pattern=*)
PATH_PATTERN="${arg#*=}"
;;
*)
BRANCHES+=("$arg")
;;
esac
done
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ============================================================================
# Helper Functions
# ============================================================================
print_success() {
echo -e "${GREEN}${NC} $1"
}
print_error() {
echo -e "${RED}${NC} $1" >&2
}
print_warning() {
echo -e "${YELLOW}${NC} $1"
}
print_info() {
echo -e "${BLUE}${NC} $1"
}
print_header() {
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$1"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
}
# ============================================================================
# Prerequisite Checks
# ============================================================================
check_prerequisites() {
print_header "Checking Prerequisites"
# Check if in git repository
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
print_error "Not in a git repository"
exit 1
fi
print_success "Git repository detected"
# Get repository info
REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
REPO_ROOT=$(git rev-parse --show-toplevel)
CURRENT_BRANCH=$(git branch --show-current)
print_info "Repository: $REPO_NAME"
print_info "Current branch: $CURRENT_BRANCH"
# Check for branch names
if [ ${#BRANCHES[@]} -eq 0 ]; then
print_error "No branch names provided"
echo "Usage: $0 branch1 branch2 branch3 [--no-install] [--pattern=/path/{branch}]"
exit 1
fi
print_info "Branches to create: ${#BRANCHES[@]}"
for branch in "${BRANCHES[@]}"; do
echo " - $branch"
done
}
validate_branches() {
print_header "Validating Branches"
declare -gA BRANCH_TYPES
declare -gA WORKTREE_PATHS
for branch in "${BRANCHES[@]}"; do
# Check if branch exists
if git show-ref --verify "refs/heads/$branch" &>/dev/null; then
BRANCH_TYPES[$branch]="existing"
print_info "$branch: existing branch"
elif git show-ref --verify "refs/remotes/origin/$branch" &>/dev/null; then
BRANCH_TYPES[$branch]="remote"
print_info "$branch: exists on remote"
else
BRANCH_TYPES[$branch]="new"
print_info "$branch: will create new branch"
fi
# Determine worktree path
if [ -n "$PATH_PATTERN" ]; then
# Use custom pattern
WORKTREE_PATHS[$branch]="${PATH_PATTERN//\{branch\}/$branch}"
else
# Use default pattern
WORKTREE_PATHS[$branch]="$(dirname "$REPO_ROOT")/$REPO_NAME-$branch"
fi
echo "${WORKTREE_PATHS[$branch]}"
done
}
check_directory_conflicts() {
print_header "Checking for Directory Conflicts"
CONFLICTS=()
for branch in "${BRANCHES[@]}"; do
path="${WORKTREE_PATHS[$branch]}"
if [ -d "$path" ]; then
CONFLICTS+=("$branch$path")
fi
done
if [ ${#CONFLICTS[@]} -gt 0 ]; then
print_warning "Found ${#CONFLICTS[@]} directory conflicts:"
for conflict in "${CONFLICTS[@]}"; do
echo " $conflict"
done
echo ""
echo "Options:"
echo " 1. Skip conflicting worktrees"
echo " 2. Remove and recreate"
echo " 3. Abort"
read -p "Choose (1/2/3): " -r choice
case $choice in
1)
print_info "Will skip conflicting directories"
for conflict in "${CONFLICTS[@]}"; do
branch="${conflict%% →*}"
# Remove from BRANCHES array
BRANCHES=("${BRANCHES[@]/$branch}")
done
# Remove empty elements
BRANCHES=("${BRANCHES[@]}")
;;
2)
print_warning "Removing conflicting directories..."
for conflict in "${CONFLICTS[@]}"; do
path="${conflict##*→ }"
rm -rf "$path"
print_success "Removed: $path"
done
;;
3)
print_info "Aborted by user"
exit 0
;;
esac
else
print_success "No directory conflicts"
fi
}
# ============================================================================
# Worktree Creation
# ============================================================================
create_worktrees() {
print_header "Creating Worktrees"
declare -gA CREATED_WORKTREES
declare -ga FAILED_WORKTREES
local total=${#BRANCHES[@]}
local current=0
for branch in "${BRANCHES[@]}"; do
((current++))
echo ""
print_info "[$current/$total] Creating worktree for $branch..."
path="${WORKTREE_PATHS[$branch]}"
branch_type="${BRANCH_TYPES[$branch]}"
case $branch_type in
new)
if git worktree add "$path" -b "$branch" 2>&1; then
CREATED_WORKTREES[$branch]="$path"
print_success "$branch: Created with new branch"
else
FAILED_WORKTREES+=("$branch")
print_error "$branch: Failed to create"
fi
;;
existing)
if git worktree add "$path" "$branch" 2>&1; then
CREATED_WORKTREES[$branch]="$path"
print_success "$branch: Created from existing branch"
else
FAILED_WORKTREES+=("$branch")
print_error "$branch: Failed to create"
fi
;;
remote)
if git worktree add "$path" -b "$branch" --track "origin/$branch" 2>&1; then
CREATED_WORKTREES[$branch]="$path"
print_success "$branch: Created tracking remote"
else
FAILED_WORKTREES+=("$branch")
print_error "$branch: Failed to create"
fi
;;
esac
done
echo ""
print_header "Worktree Creation Summary"
print_success "Created: ${#CREATED_WORKTREES[@]}/$total worktrees"
if [ ${#FAILED_WORKTREES[@]} -gt 0 ]; then
print_error "Failed: ${#FAILED_WORKTREES[@]} worktrees"
for failed in "${FAILED_WORKTREES[@]}"; do
echo " - $failed"
done
fi
}
# ============================================================================
# Development Environment Setup
# ============================================================================
detect_package_manager() {
cd "$REPO_ROOT"
if [ -f "pnpm-lock.yaml" ]; then
PKG_MANAGER="pnpm"
INSTALL_CMD="pnpm install"
elif [ -f "yarn.lock" ]; then
PKG_MANAGER="yarn"
INSTALL_CMD="yarn install"
elif [ -f "bun.lockb" ]; then
PKG_MANAGER="bun"
INSTALL_CMD="bun install"
elif [ -f "package-lock.json" ]; then
PKG_MANAGER="npm"
INSTALL_CMD="npm install"
elif [ -f "package.json" ]; then
PKG_MANAGER="npm"
INSTALL_CMD="npm install"
else
PKG_MANAGER="none"
INSTALL_CMD=""
fi
if [ "$PKG_MANAGER" != "none" ]; then
print_info "Using $PKG_MANAGER for all worktrees"
fi
}
install_dependencies() {
if [ "$SKIP_INSTALL" = true ]; then
print_info "Skipping dependency installation (--no-install flag)"
return 0
fi
if [ "$PKG_MANAGER" = "none" ]; then
print_info "No package.json found, skipping dependency installation"
return 0
fi
if [ ${#CREATED_WORKTREES[@]} -eq 0 ]; then
print_warning "No worktrees created, skipping dependency installation"
return 0
fi
print_header "Installing Dependencies (Parallel)"
declare -gA INSTALL_SUCCESS
declare -gA INSTALL_FAILED
declare -A INSTALL_PIDS
# Start installations in parallel
for branch in "${!CREATED_WORKTREES[@]}"; do
path="${CREATED_WORKTREES[$branch]}"
print_info "Starting installation in $branch..."
# Run in background
(
cd "$path"
$INSTALL_CMD > "/tmp/install-$branch.log" 2>&1
) &
INSTALL_PIDS[$branch]=$!
done
# Wait for all installations
for branch in "${!INSTALL_PIDS[@]}"; do
pid=${INSTALL_PIDS[$branch]}
print_info "Waiting for $branch (PID: $pid)..."
if wait "$pid"; then
INSTALL_SUCCESS[$branch]=true
print_success "$branch: Dependencies installed"
else
INSTALL_FAILED[$branch]=true
print_error "$branch: Installation failed (see /tmp/install-$branch.log)"
fi
done
echo ""
print_header "Installation Summary"
print_success "Successful: ${#INSTALL_SUCCESS[@]}/${#CREATED_WORKTREES[@]}"
if [ ${#INSTALL_FAILED[@]} -gt 0 ]; then
print_warning "Failed: ${#INSTALL_FAILED[@]}"
for branch in "${!INSTALL_FAILED[@]}"; do
path="${CREATED_WORKTREES[$branch]}"
echo " - $branch (manually run: cd $path && $INSTALL_CMD)"
done
fi
}
# ============================================================================
# Summary and Next Steps
# ============================================================================
print_final_summary() {
print_header "Batch Worktree Creation Complete"
echo "Created: ${#CREATED_WORKTREES[@]}/${#BRANCHES[@]} worktrees"
if [ ${#FAILED_WORKTREES[@]} -gt 0 ]; then
echo "Failed: ${#FAILED_WORKTREES[@]}/${#BRANCHES[@]} worktrees"
fi
echo ""
if [ ${#CREATED_WORKTREES[@]} -gt 0 ]; then
echo "Successful worktrees:"
for branch in "${!CREATED_WORKTREES[@]}"; do
path="${CREATED_WORKTREES[$branch]}"
branch_type="${BRANCH_TYPES[$branch]}"
print_success "$branch$path ($branch_type)"
done
fi
echo ""
if [ ${#FAILED_WORKTREES[@]} -gt 0 ]; then
echo "Failed worktrees:"
for branch in "${FAILED_WORKTREES[@]}"; do
print_error "$branch"
done
echo ""
fi
print_header "Next Steps"
echo "Start working on each worktree:"
echo ""
for branch in "${!CREATED_WORKTREES[@]}"; do
path="${CREATED_WORKTREES[$branch]}"
echo "${BLUE}# $branch${NC}"
echo "cd $path"
echo "claude"
echo ""
done
print_header "All Worktrees"
git worktree list
echo ""
print_header "Batch Management"
echo "List all: git worktree list"
echo "Remove all:"
for branch in "${!CREATED_WORKTREES[@]}"; do
path="${CREATED_WORKTREES[$branch]}"
echo " git worktree remove $path"
done
echo ""
# Offer to generate cleanup script
read -p "Generate cleanup script? (yes/no): " -r
if [[ $REPLY =~ ^[Yy]es$ ]]; then
generate_cleanup_script
fi
}
generate_cleanup_script() {
CLEANUP_SCRIPT="cleanup-worktrees.sh"
cat > "$CLEANUP_SCRIPT" << 'EOF'
#!/bin/bash
# Cleanup script for batch-created worktrees
# Generated by git-worktree-setup skill
set -euo pipefail
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
echo "Cleaning up worktrees..."
EOF
for branch in "${!CREATED_WORKTREES[@]}"; do
path="${CREATED_WORKTREES[$branch]}"
cat >> "$CLEANUP_SCRIPT" << EOF
echo "Removing $branch..."
if git worktree remove "$path" 2>/dev/null; then
echo -e "\${GREEN}✓\${NC} Removed: $branch"
read -p "Delete branch '$branch'? (yes/no): " -r
if [[ \$REPLY =~ ^[Yy]es$ ]]; then
git branch -d "$branch" 2>/dev/null && echo -e "\${GREEN}✓\${NC} Deleted branch: $branch" || echo -e "\${RED}✗\${NC} Failed to delete branch: $branch"
fi
else
echo -e "\${RED}✗\${NC} Failed to remove: $branch"
fi
EOF
done
cat >> "$CLEANUP_SCRIPT" << 'EOF'
echo ""
echo "Cleanup complete!"
echo ""
echo "Remaining worktrees:"
git worktree list
EOF
chmod +x "$CLEANUP_SCRIPT"
print_success "Generated cleanup script: $CLEANUP_SCRIPT"
print_info "Run with: ./$CLEANUP_SCRIPT"
}
# ============================================================================
# Main Execution
# ============================================================================
main() {
print_header "Git Worktree Batch Setup"
# Phase 0: Prerequisites
check_prerequisites
validate_branches
check_directory_conflicts
# Confirm before proceeding
echo ""
read -p "Create ${#BRANCHES[@]} worktrees? (yes/no): " -r
if [[ ! $REPLY =~ ^[Yy]es$ ]]; then
print_info "Aborted by user"
exit 0
fi
# Phase 1: Create worktrees
create_worktrees
# Phase 2: Setup development environments
detect_package_manager
install_dependencies
# Phase 3: Summary
print_final_summary
print_success "Batch setup complete!"
}
# Run main function
main "$@"

View File

@@ -0,0 +1,382 @@
#!/bin/bash
# Git Worktree Setup Script
# Generated by git-worktree-setup skill
#
# This script automates the creation of a git worktree with development environment setup.
#
# Usage:
# ./worktree-setup.sh <branch-name> [worktree-path] [--no-install]
set -euo pipefail
# ============================================================================
# Configuration
# ============================================================================
BRANCH_NAME="${1:-}"
WORKTREE_PATH="${2:-}"
SKIP_INSTALL=false
# Parse flags
for arg in "$@"; do
case $arg in
--no-install)
SKIP_INSTALL=true
shift
;;
esac
done
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ============================================================================
# Helper Functions
# ============================================================================
print_success() {
echo -e "${GREEN}${NC} $1"
}
print_error() {
echo -e "${RED}${NC} $1" >&2
}
print_warning() {
echo -e "${YELLOW}${NC} $1"
}
print_info() {
echo -e "${BLUE}${NC} $1"
}
print_header() {
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$1"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
}
# ============================================================================
# Prerequisite Checks
# ============================================================================
check_prerequisites() {
print_header "Checking Prerequisites"
# Check if in git repository
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
print_error "Not in a git repository"
exit 1
fi
print_success "Git repository detected"
# Get repository info
REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
REPO_ROOT=$(git rev-parse --show-toplevel)
CURRENT_BRANCH=$(git branch --show-current)
print_info "Repository: $REPO_NAME"
print_info "Current branch: $CURRENT_BRANCH"
# Check for branch name
if [ -z "$BRANCH_NAME" ]; then
print_error "Branch name required"
echo "Usage: $0 <branch-name> [worktree-path] [--no-install]"
exit 1
fi
# Set default worktree path if not provided
if [ -z "$WORKTREE_PATH" ]; then
WORKTREE_PATH="$(dirname "$REPO_ROOT")/$REPO_NAME-$BRANCH_NAME"
fi
print_info "Target branch: $BRANCH_NAME"
print_info "Worktree path: $WORKTREE_PATH"
}
check_working_directory() {
# Check for uncommitted changes
if [ -n "$(git status --porcelain)" ]; then
print_warning "You have uncommitted changes in current worktree"
git status --short
echo ""
read -p "Continue anyway? (yes/no): " -r
if [[ ! $REPLY =~ ^[Yy]es$ ]]; then
print_info "Aborted by user"
exit 0
fi
else
print_success "Working directory is clean"
fi
}
check_branch_status() {
# Check if branch exists
if git show-ref --verify "refs/heads/$BRANCH_NAME" &>/dev/null; then
BRANCH_TYPE="existing"
print_info "Branch exists: $BRANCH_NAME"
else
BRANCH_TYPE="new"
print_info "Will create new branch: $BRANCH_NAME"
# Check if exists on remote
if git show-ref --verify "refs/remotes/origin/$BRANCH_NAME" &>/dev/null; then
print_info "Branch exists on remote, will track it"
BRANCH_TYPE="remote"
fi
fi
}
check_directory_conflict() {
if [ -d "$WORKTREE_PATH" ]; then
print_error "Directory already exists: $WORKTREE_PATH"
echo ""
read -p "Remove and continue? (yes/no): " -r
if [[ $REPLY =~ ^[Yy]es$ ]]; then
rm -rf "$WORKTREE_PATH"
print_success "Removed existing directory"
else
print_info "Aborted by user"
exit 0
fi
else
print_success "Target directory available"
fi
}
# ============================================================================
# Worktree Creation
# ============================================================================
create_worktree() {
print_header "Creating Worktree"
case $BRANCH_TYPE in
new)
print_info "Creating new branch: $BRANCH_NAME"
if git worktree add "$WORKTREE_PATH" -b "$BRANCH_NAME"; then
print_success "Worktree created with new branch"
else
print_error "Failed to create worktree"
exit 1
fi
;;
existing)
print_info "Checking out existing branch: $BRANCH_NAME"
if git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"; then
print_success "Worktree created from existing branch"
else
print_error "Failed to create worktree"
exit 1
fi
;;
remote)
print_info "Tracking remote branch: origin/$BRANCH_NAME"
if git worktree add "$WORKTREE_PATH" -b "$BRANCH_NAME" --track "origin/$BRANCH_NAME"; then
print_success "Worktree created tracking remote branch"
else
print_error "Failed to create worktree"
exit 1
fi
;;
esac
}
verify_worktree() {
print_header "Verifying Worktree"
# Check if worktree appears in list
if git worktree list | grep -q "$WORKTREE_PATH"; then
print_success "Worktree appears in git worktree list"
else
print_error "Worktree not in git worktree list"
exit 1
fi
# Check if directory exists
if [ -d "$WORKTREE_PATH" ]; then
print_success "Worktree directory exists"
else
print_error "Worktree directory not found"
exit 1
fi
# Check if .git file exists
if [ -f "$WORKTREE_PATH/.git" ]; then
print_success "Git metadata configured"
else
print_error "Missing .git file in worktree"
exit 1
fi
# Check if has files
if [ -n "$(ls -A "$WORKTREE_PATH")" ]; then
print_success "Worktree populated with files"
else
print_error "Worktree directory is empty"
exit 1
fi
}
# ============================================================================
# Development Environment Setup
# ============================================================================
detect_package_manager() {
cd "$WORKTREE_PATH"
if [ -f "pnpm-lock.yaml" ]; then
PKG_MANAGER="pnpm"
INSTALL_CMD="pnpm install"
elif [ -f "yarn.lock" ]; then
PKG_MANAGER="yarn"
INSTALL_CMD="yarn install"
elif [ -f "bun.lockb" ]; then
PKG_MANAGER="bun"
INSTALL_CMD="bun install"
elif [ -f "package-lock.json" ]; then
PKG_MANAGER="npm"
INSTALL_CMD="npm install"
elif [ -f "package.json" ]; then
PKG_MANAGER="npm"
INSTALL_CMD="npm install"
else
PKG_MANAGER="none"
INSTALL_CMD=""
fi
if [ "$PKG_MANAGER" != "none" ]; then
print_info "Detected package manager: $PKG_MANAGER"
fi
}
install_dependencies() {
if [ "$SKIP_INSTALL" = true ]; then
print_info "Skipping dependency installation (--no-install flag)"
return 0
fi
if [ "$PKG_MANAGER" = "none" ]; then
print_info "No package.json found, skipping dependency installation"
return 0
fi
print_header "Installing Dependencies"
cd "$WORKTREE_PATH"
print_info "Running: $INSTALL_CMD"
if $INSTALL_CMD; then
print_success "Dependencies installed successfully"
else
print_warning "Dependency installation failed"
print_info "You can manually install later with: cd $WORKTREE_PATH && $INSTALL_CMD"
return 1
fi
}
copy_environment_files() {
if [ "$SKIP_INSTALL" = true ]; then
return 0
fi
if [ -f "$REPO_ROOT/.env" ]; then
print_info "Found .env file in repository root"
read -p "Copy .env to worktree? (yes/no): " -r
if [[ $REPLY =~ ^[Yy]es$ ]]; then
cp "$REPO_ROOT/.env" "$WORKTREE_PATH/.env"
print_success "Copied .env file"
fi
fi
# Copy other common env files
for env_file in .env.local .env.development .env.test; do
if [ -f "$REPO_ROOT/$env_file" ]; then
read -p "Copy $env_file to worktree? (yes/no): " -r
if [[ $REPLY =~ ^[Yy]es$ ]]; then
cp "$REPO_ROOT/$env_file" "$WORKTREE_PATH/$env_file"
print_success "Copied $env_file"
fi
fi
done
}
# ============================================================================
# Summary and Next Steps
# ============================================================================
print_summary() {
print_header "Worktree Created Successfully"
echo "Location: $WORKTREE_PATH"
echo "Branch: $BRANCH_NAME ($BRANCH_TYPE)"
if [ "$PKG_MANAGER" != "none" ] && [ "$SKIP_INSTALL" = false ]; then
echo "Dev Setup: ✓ Complete ($PKG_MANAGER)"
else
echo "Dev Setup: ⊘ Skipped"
fi
echo ""
print_header "Next Steps"
echo "1. Navigate to worktree:"
echo " ${BLUE}cd $WORKTREE_PATH${NC}"
echo ""
echo "2. Start Claude Code:"
echo " ${BLUE}claude${NC}"
echo ""
echo "3. Begin development on $BRANCH_NAME"
echo ""
print_header "All Worktrees"
git worktree list
echo ""
print_header "Quick Reference"
echo "List worktrees: git worktree list"
echo "Remove worktree: git worktree remove $WORKTREE_PATH"
echo "Navigate: cd $WORKTREE_PATH"
echo "Return to main: cd $REPO_ROOT"
echo ""
}
# ============================================================================
# Main Execution
# ============================================================================
main() {
print_header "Git Worktree Setup"
# Phase 0: Prerequisites
check_prerequisites
check_working_directory
check_branch_status
check_directory_conflict
# Phase 1: Create worktree
create_worktree
verify_worktree
# Phase 2: Setup development environment
detect_package_manager
install_dependencies
copy_environment_files
# Phase 3: Summary
print_summary
print_success "Setup complete!"
}
# Run main function
main "$@"