|
#!/bin/bash |
|
|
|
set -e |
|
|
|
# |
|
# Configuration - change these if your directories have different names |
|
|
|
SERVER_DIR_NAME="server" |
|
ENTERPRISE_DIR_NAME="enterprise" |
|
|
|
# List of additional files to copy to server worktree |
|
server_files=( |
|
"webapp/.dir-locals.el:webapp/.dir-locals.el" |
|
"config/config.json:config/config.json" |
|
"docker-compose.override.yaml:docker-compose.override.yaml" |
|
"server/config.override.mk:server/config.override.mk" |
|
# Add more files here in format "source:destination" |
|
# "sub-dir/other-file.txt:sub-dir/other-file.txt" |
|
) |
|
|
|
# List of additional files to copy to enterprise worktree |
|
enterprise_files=( |
|
# Add files here in format "source:destination" |
|
# "sub-dir/other-file.txt:sub-dir/other-file.txt" |
|
) |
|
|
|
|
|
|
|
# Get current directory name to use as base name |
|
BASENAME=$(basename "$(pwd)") |
|
|
|
# Colors for output |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
NC='\033[0m' # No Color |
|
|
|
# Function to print colored output |
|
print_status() { |
|
echo -e "${GREEN}[INFO]${NC} $1" |
|
} |
|
|
|
print_warning() { |
|
echo -e "${YELLOW}[WARN]${NC} $1" |
|
} |
|
|
|
print_error() { |
|
echo -e "${RED}[ERROR]${NC} $1" |
|
} |
|
|
|
# Function to show usage |
|
usage() { |
|
echo "Usage: $0 <command> [arguments]" |
|
echo "" |
|
echo "Commands:" |
|
echo " create <branch-name> <short-name> Create worktrees for branch with short directory name" |
|
echo " remove <branch-name> [--force] Remove worktrees and worktree base directory" |
|
echo " list List existing worktree directories" |
|
echo "" |
|
echo "Examples:" |
|
echo " $0 create MM-63556-compliance-export-download compliance-export" |
|
echo " $0 remove MM-63556-compliance-export-download" |
|
echo " $0 remove MM-63556-compliance-export-download --force" |
|
echo " $0 list" |
|
echo "" |
|
echo "Create will make:" |
|
echo " - $BASENAME-compliance-export/" |
|
echo " ├── CLAUDE.md, CLAUDE.local.md, etc. (copied from current base dir)" |
|
echo " ├── $SERVER_DIR_NAME/ (git worktree for MM-63556-compliance-export-download branch)" |
|
echo " └── $ENTERPRISE_DIR_NAME/ (git worktree for MM-63556-compliance-export-download branch)" |
|
exit 1 |
|
} |
|
|
|
# Check for help flags |
|
if [ "$1" = "--help" ] || [ "$1" = "-h" ] || [ "$1" = "help" ]; then |
|
usage |
|
fi |
|
|
|
# Parse command and arguments |
|
if [ $# -eq 0 ]; then |
|
print_error "Command is required" |
|
usage |
|
fi |
|
|
|
COMMAND="$1" |
|
BRANCH_NAME="" |
|
|
|
# Validate command |
|
if [ "$COMMAND" != "create" ] && [ "$COMMAND" != "remove" ] && [ "$COMMAND" != "list" ]; then |
|
print_error "Invalid command: $COMMAND" |
|
print_error "Valid commands: create, remove, list" |
|
usage |
|
fi |
|
|
|
# Check arguments for commands |
|
FORCE_MODE=false |
|
BRANCH_NAME="" |
|
SHORT_NAME="" |
|
|
|
if [ "$COMMAND" = "create" ]; then |
|
# Create command needs: create <branch-name> <short-name> |
|
if [ $# -ne 3 ]; then |
|
print_error "Create command requires both branch-name and short-name" |
|
print_error "Usage: $0 create <branch-name> <short-name>" |
|
usage |
|
fi |
|
BRANCH_NAME="$2" |
|
SHORT_NAME="$3" |
|
elif [ "$COMMAND" = "remove" ]; then |
|
# Remove command: remove <branch-name> [--force] |
|
if [ $# -eq 3 ] && [ "$3" = "--force" ]; then |
|
FORCE_MODE=true |
|
BRANCH_NAME="$2" |
|
elif [ $# -eq 3 ] && [ "$2" = "--force" ]; then |
|
FORCE_MODE=true |
|
BRANCH_NAME="$3" |
|
elif [ $# -eq 2 ]; then |
|
BRANCH_NAME="$2" |
|
else |
|
print_error "Remove command requires branch-name" |
|
print_error "Usage: $0 remove <branch-name> [--force]" |
|
usage |
|
fi |
|
elif [ "$COMMAND" = "list" ]; then |
|
# List command takes no additional arguments |
|
if [ $# -ne 1 ]; then |
|
print_error "List command takes no arguments" |
|
usage |
|
fi |
|
fi |
|
|
|
# Validate names for create/remove commands |
|
if [ "$COMMAND" = "create" ]; then |
|
if [[ ! "$BRANCH_NAME" =~ ^[a-zA-Z0-9_\.-]+$ ]]; then |
|
print_error "Invalid branch name. Use only letters, numbers, hyphens, periods, and underscores." |
|
exit 1 |
|
fi |
|
if [[ ! "$SHORT_NAME" =~ ^[a-zA-Z0-9_\.-]+$ ]]; then |
|
print_error "Invalid short name. Use only letters, numbers, hyphens, periods, and underscores." |
|
exit 1 |
|
fi |
|
elif [ "$COMMAND" = "remove" ]; then |
|
if [[ ! "$BRANCH_NAME" =~ ^[a-zA-Z0-9_\.-]+$ ]]; then |
|
print_error "Invalid branch name. Use only letters, numbers, hyphens, periods, and underscores." |
|
exit 1 |
|
fi |
|
fi |
|
|
|
# Function to list existing worktree directories |
|
list_worktrees() { |
|
local parent_dir=$(dirname "$CURRENT_DIR") |
|
local found=false |
|
|
|
print_status "Existing worktree directories:" |
|
for dir in "$parent_dir"/$BASENAME-*; do |
|
if [ -d "$dir" ]; then |
|
local short_name=$(basename "$dir" | sed "s/^$BASENAME-//") |
|
local branch_info="" |
|
|
|
# Try to get branch info from server worktree |
|
if [ -d "$dir/$SERVER_DIR_NAME" ]; then |
|
branch_info=$(cd "$dir/$SERVER_DIR_NAME" 2>/dev/null && git branch --show-current 2>/dev/null) |
|
if [ -n "$branch_info" ]; then |
|
echo " - $short_name (branch: $branch_info)" |
|
else |
|
echo " - $short_name (branch: unknown)" |
|
fi |
|
else |
|
echo " - $short_name (invalid - no $SERVER_DIR_NAME directory)" |
|
fi |
|
found=true |
|
fi |
|
done |
|
|
|
if [ "$found" = false ]; then |
|
echo " No worktree directories found" |
|
fi |
|
} |
|
|
|
# Function to remove worktrees and base directory |
|
remove_worktree() { |
|
local branch_name="$1" |
|
local force_mode="$2" |
|
local parent_dir=$(dirname "$CURRENT_DIR") |
|
local base_dir="" |
|
local server_worktree="" |
|
local enterprise_worktree="" |
|
|
|
print_status "Searching for worktrees with branch: $branch_name" |
|
|
|
# Find the base directory that contains this branch |
|
local found=false |
|
for dir in "$parent_dir"/$BASENAME-*; do |
|
if [ -d "$dir/$SERVER_DIR_NAME" ]; then |
|
local current_branch=$(cd "$dir/$SERVER_DIR_NAME" 2>/dev/null && git branch --show-current 2>/dev/null) |
|
if [ "$current_branch" = "$branch_name" ]; then |
|
base_dir="$dir" |
|
server_worktree="$dir/$SERVER_DIR_NAME" |
|
enterprise_worktree="$dir/$ENTERPRISE_DIR_NAME" |
|
found=true |
|
break |
|
fi |
|
fi |
|
done |
|
|
|
if [ "$found" = false ]; then |
|
print_error "No worktree found for branch: $branch_name" |
|
exit 1 |
|
fi |
|
|
|
print_status "Found base directory: $base_dir" |
|
|
|
# Show what will be removed |
|
echo "" |
|
print_warning "This will remove the following:" |
|
echo " - Git worktree: $server_worktree" |
|
echo " - Git worktree: $enterprise_worktree" |
|
echo " - Entire directory: $base_dir" |
|
echo "" |
|
|
|
# Confirmation prompt (unless force mode) |
|
if [ "$force_mode" = false ]; then |
|
print_warning "Are you sure you want to remove these worktrees and directory? [y/N]" |
|
read -r response |
|
|
|
case "$response" in |
|
[yY]|[yY][eE][sS]) |
|
print_status "Proceeding with removal..." |
|
;; |
|
*) |
|
print_status "Removal cancelled." |
|
exit 0 |
|
;; |
|
esac |
|
else |
|
print_status "Force mode enabled - skipping confirmation." |
|
fi |
|
|
|
# Remove server worktree |
|
if [ -d "$server_worktree" ]; then |
|
print_status "Removing server worktree: $server_worktree" |
|
(cd "$SERVER_DIR_NAME" && git worktree remove --force "$server_worktree" || true) |
|
fi |
|
|
|
# Remove enterprise worktree |
|
if [ -d "$enterprise_worktree" ]; then |
|
print_status "Removing enterprise worktree: $enterprise_worktree" |
|
(cd "$ENTERPRISE_DIR_NAME" && git worktree remove --force "$enterprise_worktree" || true) |
|
fi |
|
|
|
# Remove the entire base directory |
|
print_status "Removing base directory: $base_dir" |
|
rm -rf "$base_dir" |
|
|
|
print_status "Successfully removed worktrees and directory for branch: $branch_name" |
|
} |
|
|
|
# Get current directory and validate structure |
|
CURRENT_DIR=$(pwd) |
|
CURRENT_BASENAME=$(basename "$CURRENT_DIR") |
|
|
|
# Validate that server and enterprise directories exist and are git repos |
|
if [ ! -d "$SERVER_DIR_NAME" ]; then |
|
print_error "$SERVER_DIR_NAME directory not found in current directory" |
|
exit 1 |
|
fi |
|
|
|
if [ ! -d "$ENTERPRISE_DIR_NAME" ]; then |
|
print_error "$ENTERPRISE_DIR_NAME directory not found in current directory" |
|
exit 1 |
|
fi |
|
|
|
if [ ! -d "$SERVER_DIR_NAME/.git" ]; then |
|
print_error "$SERVER_DIR_NAME directory is not a git repository" |
|
exit 1 |
|
fi |
|
|
|
if [ ! -d "$ENTERPRISE_DIR_NAME/.git" ]; then |
|
print_error "$ENTERPRISE_DIR_NAME directory is not a git repository" |
|
exit 1 |
|
fi |
|
|
|
# Command dispatcher |
|
case "$COMMAND" in |
|
"list") |
|
list_worktrees |
|
exit 0 |
|
;; |
|
"remove") |
|
remove_worktree "$BRANCH_NAME" "$FORCE_MODE" |
|
exit 0 |
|
;; |
|
"create") |
|
# Continue with create logic below |
|
;; |
|
*) |
|
print_error "Unknown command: $COMMAND" |
|
usage |
|
;; |
|
esac |
|
|
|
# Create command logic starts here |
|
# Get parent directory (where we'll create the new base directory) |
|
PARENT_DIR=$(dirname "$CURRENT_DIR") |
|
NEW_BASE_DIR="$PARENT_DIR/$BASENAME-$SHORT_NAME" |
|
SERVER_WORKTREE_DIR="$NEW_BASE_DIR/$SERVER_DIR_NAME" |
|
ENTERPRISE_WORKTREE_DIR="$NEW_BASE_DIR/$ENTERPRISE_DIR_NAME" |
|
|
|
print_status "Branch name: $BRANCH_NAME" |
|
print_status "Short name: $SHORT_NAME" |
|
print_status "New base directory: $NEW_BASE_DIR" |
|
print_status "Server worktree: $SERVER_WORKTREE_DIR" |
|
print_status "Enterprise worktree: $ENTERPRISE_WORKTREE_DIR" |
|
|
|
# Check if new base directory already exists |
|
if [ -d "$NEW_BASE_DIR" ]; then |
|
print_error "Directory already exists: $NEW_BASE_DIR" |
|
exit 1 |
|
fi |
|
|
|
# Create the new base directory |
|
print_status "Creating new base directory: $NEW_BASE_DIR" |
|
mkdir -p "$NEW_BASE_DIR" |
|
|
|
# Function to cleanup on error |
|
cleanup() { |
|
print_error "Cleaning up due to error..." |
|
if [ -d "$NEW_BASE_DIR" ]; then |
|
rm -rf "$NEW_BASE_DIR" |
|
print_status "Cleaned up: $NEW_BASE_DIR" |
|
fi |
|
cd "$CURRENT_DIR" |
|
cd "$SERVER_DIR_NAME" |
|
git worktree prune |
|
cd "$CURRENT_DIR" |
|
cd "$ENTERPRISE_DIR_NAME" |
|
git worktree prune |
|
} |
|
|
|
# Set trap for cleanup on error |
|
trap cleanup ERR |
|
|
|
# Copy all files and directories from current base dir (except server and enterprise) |
|
print_status "Copying base files (excluding server and enterprise directories)..." |
|
for item in * .*; do |
|
# Skip . and .. and server and enterprise directories |
|
if [ "$item" = "." ] || [ "$item" = ".." ] || [ "$item" = "$SERVER_DIR_NAME" ] || [ "$item" = "$ENTERPRISE_DIR_NAME" ]; then |
|
continue |
|
fi |
|
|
|
# Skip if item doesn't exist (handles glob expansion issues) |
|
if [ ! -e "$item" ]; then |
|
continue |
|
fi |
|
|
|
print_status "Copying: $item" |
|
cp -r "$item" "$NEW_BASE_DIR/" |
|
done |
|
|
|
# Check if branch exists in server repo, create if it doesn't |
|
print_status "Checking $SERVER_DIR_NAME repository for branch: $BRANCH_NAME" |
|
cd "$SERVER_DIR_NAME" |
|
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then |
|
print_status "Branch $BRANCH_NAME already exists in $SERVER_DIR_NAME repo" |
|
elif git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then |
|
print_status "Branch $BRANCH_NAME exists on remote, will create local tracking branch" |
|
else |
|
print_warning "Branch $BRANCH_NAME does not exist in $SERVER_DIR_NAME repo, will create new branch" |
|
fi |
|
|
|
# Create server worktree |
|
print_status "Creating server worktree: $SERVER_WORKTREE_DIR" |
|
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then |
|
# Branch exists locally |
|
git worktree add "$SERVER_WORKTREE_DIR" "$BRANCH_NAME" |
|
elif git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then |
|
# Branch exists on remote, create local tracking branch |
|
git worktree add -b "$BRANCH_NAME" "$SERVER_WORKTREE_DIR" "origin/$BRANCH_NAME" |
|
else |
|
# Create new branch |
|
git worktree add -b "$BRANCH_NAME" "$SERVER_WORKTREE_DIR" |
|
fi |
|
|
|
# Go back to base directory |
|
cd "$CURRENT_DIR" |
|
|
|
# Check if branch exists in enterprise repo, create if it doesn't |
|
print_status "Checking $ENTERPRISE_DIR_NAME repository for branch: $BRANCH_NAME" |
|
cd "$ENTERPRISE_DIR_NAME" |
|
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then |
|
print_status "Branch $BRANCH_NAME already exists in $ENTERPRISE_DIR_NAME repo" |
|
elif git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then |
|
print_status "Branch $BRANCH_NAME exists on remote, will create local tracking branch" |
|
else |
|
print_warning "Branch $BRANCH_NAME does not exist in $ENTERPRISE_DIR_NAME repo, will create new branch" |
|
fi |
|
|
|
# Create enterprise worktree |
|
print_status "Creating enterprise worktree: $ENTERPRISE_WORKTREE_DIR" |
|
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then |
|
# Branch exists locally |
|
git worktree add "$ENTERPRISE_WORKTREE_DIR" "$BRANCH_NAME" |
|
elif git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then |
|
# Branch exists on remote, create local tracking branch |
|
git worktree add -b "$BRANCH_NAME" "$ENTERPRISE_WORKTREE_DIR" "origin/$BRANCH_NAME" |
|
else |
|
# Create new branch |
|
git worktree add -b "$BRANCH_NAME" "$ENTERPRISE_WORKTREE_DIR" |
|
fi |
|
|
|
# Go back to base directory |
|
cd "$CURRENT_DIR" |
|
|
|
# Copy go work files |
|
print_status "Copying go.work files and others..." |
|
# Copy go.work* from $SERVER_DIR_NAME/servre to new $SERVER_DIR_NAME/server |
|
for gowork_file in "$SERVER_DIR_NAME/server"/go.work*; do |
|
if [ -f "$gowork_file" ]; then |
|
print_status "Copying $(basename "$gowork_file") from $SERVER_DIR_NAME/server to new $SERVER_DIR_NAME/server" |
|
mkdir -p "$SERVER_WORKTREE_DIR/server" |
|
cp "$gowork_file" "$SERVER_WORKTREE_DIR/server/" |
|
fi |
|
done |
|
|
|
for file_mapping in "${server_files[@]}"; do |
|
source_file="$SERVER_DIR_NAME/${file_mapping%%:*}" |
|
dest_file="${file_mapping##*:}" |
|
|
|
if [ -f "$source_file" ]; then |
|
print_status "Copying $(basename "$source_file") to server worktree" |
|
mkdir -p "$(dirname "$SERVER_WORKTREE_DIR/$dest_file")" |
|
cp "$source_file" "$SERVER_WORKTREE_DIR/$dest_file" |
|
fi |
|
done |
|
|
|
# Copy go.work* from $ENTERPRISE_DIR_NAME to new $ENTERPRISE_DIR_NAME |
|
for gowork_file in "$ENTERPRISE_DIR_NAME"/go.work*; do |
|
if [ -f "$gowork_file" ]; then |
|
print_status "Copying $(basename "$gowork_file") from $ENTERPRISE_DIR_NAME to new $ENTERPRISE_DIR_NAME" |
|
cp "$gowork_file" "$ENTERPRISE_WORKTREE_DIR/" |
|
fi |
|
done |
|
|
|
for file_mapping in "${enterprise_files[@]}"; do |
|
source_file="$ENTERPRISE_DIR_NAME/${file_mapping%%:*}" |
|
dest_file="${file_mapping##*:}" |
|
|
|
if [ -f "$source_file" ]; then |
|
print_status "Copying $(basename "$source_file") to enterprise worktree" |
|
mkdir -p "$(dirname "$ENTERPRISE_WORKTREE_DIR/$dest_file")" |
|
cp "$source_file" "$ENTERPRISE_WORKTREE_DIR/$dest_file" |
|
fi |
|
done |
|
|
|
# Remove trap since we succeeded |
|
trap - ERR |
|
|
|
print_status "Successfully created worktrees and copied base files!" |
|
echo "" |
|
print_status "Directory structure:" |
|
echo " $NEW_BASE_DIR/" |
|
echo " ├── CLAUDE.md, CLAUDE.local.md, mise.toml, etc. (copied files)" |
|
echo " ├── $(basename "$SERVER_WORKTREE_DIR")/ (git worktree)" |
|
echo " └── $(basename "$ENTERPRISE_WORKTREE_DIR")/ (git worktree)" |
|
echo "" |
|
print_status "To remove these worktrees later, use:" |
|
echo " ./worktree remove $BRANCH_NAME" |