feat: add automatic Git worktree creation

Add Git hook and management scripts for automatic worktree creation when branching from main.

## Features

**Automatic Worktree Creation:**
- Post-checkout Git hook automatically creates worktrees for new branches
- Creates worktrees at `../repo-name-branch-name`
- Only activates when branching from main/master
- Smart detection to avoid duplicate worktrees

**Worktree Manager Script:**
- `list` - List all worktrees with branches
- `create <branch>` - Manually create worktree
- `remove <branch>` - Remove worktree
- `clean` - Remove all worktrees except main
- `goto <branch>` - Get path to worktree (for cd)
- `status` - Show git status of all worktrees

## Benefits

- Work on multiple branches simultaneously
- No need to stash when switching branches
- Run dev servers on different branches in parallel
- Compare code across branches easily
- Keep main branch clean

## Files Added

- `.git/hooks/post-checkout` - Auto-creates worktrees on branch creation
- `scripts/worktree-manager.sh` - Manual worktree management CLI
- `WORKTREE_SETUP.md` - Complete documentation and usage guide

## Usage

**Automatic (when branching from main):**
```bash
git checkout -b feature/new-feature
# Worktree automatically created at ../canvas-website-feature-new-feature
```

**Manual:**
```bash
./scripts/worktree-manager.sh create feature/my-feature
./scripts/worktree-manager.sh list
cd $(./scripts/worktree-manager.sh goto feature/my-feature)
```

See WORKTREE_SETUP.md for complete documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-11-19 20:55:30 -07:00
parent 495fea2a54
commit 08c8cc8d23
2 changed files with 590 additions and 0 deletions

341
WORKTREE_SETUP.md Normal file
View File

@ -0,0 +1,341 @@
# Git Worktree Automation Setup
This repository is configured to automatically create Git worktrees for new branches, allowing you to work on multiple branches simultaneously without switching contexts.
## What Are Worktrees?
Git worktrees allow you to have multiple working directories (copies of your repo) checked out to different branches at the same time. This means:
- No need to stash or commit work when switching branches
- Run dev servers on multiple branches simultaneously
- Compare code across branches easily
- Keep your main branch clean while working on features
## Automatic Worktree Creation
A Git hook (`.git/hooks/post-checkout`) is installed that automatically creates worktrees when you create a new branch from `main`:
```bash
# This will automatically create a worktree at ../canvas-website-feature-name
git checkout -b feature/new-feature
```
**Worktree Location Pattern:**
```
/home/jeffe/Github/
├── canvas-website/ # Main repo (main branch)
├── canvas-website-feature-name/ # Worktree for feature branch
└── canvas-website-bugfix-something/ # Worktree for bugfix branch
```
## Manual Worktree Management
Use the `worktree-manager.sh` script for manual management:
### List All Worktrees
```bash
./scripts/worktree-manager.sh list
```
### Create a New Worktree
```bash
# Creates worktree for existing branch
./scripts/worktree-manager.sh create feature/my-feature
# Or create new branch with worktree
./scripts/worktree-manager.sh create feature/new-branch
```
### Remove a Worktree
```bash
./scripts/worktree-manager.sh remove feature/old-feature
```
### Clean Up All Worktrees (Keep Main)
```bash
./scripts/worktree-manager.sh clean
```
### Show Status of All Worktrees
```bash
./scripts/worktree-manager.sh status
```
### Navigate to a Worktree
```bash
# Get worktree path
./scripts/worktree-manager.sh goto feature/my-feature
# Or use with cd
cd $(./scripts/worktree-manager.sh goto feature/my-feature)
```
### Help
```bash
./scripts/worktree-manager.sh help
```
## Workflow Examples
### Starting a New Feature
**With automatic worktree creation:**
```bash
# In main repo
cd /home/jeffe/Github/canvas-website
# Create and switch to new branch (worktree auto-created)
git checkout -b feature/terminal-tool
# Notification appears:
# 🌳 Creating worktree for branch: feature/terminal-tool
# 📁 Location: /home/jeffe/Github/canvas-website-feature-terminal-tool
# Continue working in current directory or switch to worktree
cd ../canvas-website-feature-terminal-tool
```
**Manual worktree creation:**
```bash
./scripts/worktree-manager.sh create feature/my-feature
cd $(./scripts/worktree-manager.sh goto feature/my-feature)
```
### Working on Multiple Features Simultaneously
```bash
# Terminal 1: Main repo (main branch)
cd /home/jeffe/Github/canvas-website
npm run dev # Port 5173
# Terminal 2: Feature branch 1
cd /home/jeffe/Github/canvas-website-feature-auth
npm run dev # Different port
# Terminal 3: Feature branch 2
cd /home/jeffe/Github/canvas-website-feature-ui
npm run dev # Another port
# All running simultaneously, no conflicts!
```
### Comparing Code Across Branches
```bash
# Use diff or your IDE to compare files
diff /home/jeffe/Github/canvas-website/src/App.tsx \
/home/jeffe/Github/canvas-website-feature-auth/src/App.tsx
# Or open both in VS Code
code /home/jeffe/Github/canvas-website \
/home/jeffe/Github/canvas-website-feature-auth
```
### Cleaning Up After Merging
```bash
# After merging feature/my-feature to main
cd /home/jeffe/Github/canvas-website
# Remove the worktree
./scripts/worktree-manager.sh remove feature/my-feature
# Or clean all worktrees except main
./scripts/worktree-manager.sh clean
```
## How It Works
### Post-Checkout Hook
The `.git/hooks/post-checkout` script runs automatically after `git checkout` and:
1. Detects if you're creating a new branch from `main`
2. Creates a worktree in `../canvas-website-{branch-name}`
3. Links the worktree to the new branch
4. Shows a notification with the worktree path
**Hook Behavior:**
- ✅ Creates worktree when: `git checkout -b new-branch` (from main)
- ❌ Skips creation when:
- Switching to existing branches
- Already in a worktree
- Worktree already exists for that branch
- Not branching from main/master
### Worktree Manager Script
The `scripts/worktree-manager.sh` script provides:
- User-friendly commands for worktree operations
- Colored output for better readability
- Error handling and validation
- Status reporting across all worktrees
## Git Commands with Worktrees
Most Git commands work the same way in worktrees:
```bash
# In any worktree
git status # Shows status of current worktree
git add . # Stages files in current worktree
git commit -m "..." # Commits in current branch
git push # Pushes current branch
git pull # Pulls current branch
# List all worktrees (works from any worktree)
git worktree list
# Remove a worktree (from main repo)
git worktree remove feature/branch-name
# Prune deleted worktrees
git worktree prune
```
## Important Notes
### Shared Git Directory
All worktrees share the same `.git` directory (in the main repo), which means:
- ✅ Commits, branches, and remotes are shared across all worktrees
- ✅ One `git fetch` or `git pull` in main updates all worktrees
- ⚠️ Don't delete the main repo while worktrees exist
- ⚠️ Stashes are shared (stash in one worktree, pop in another)
### Node Modules
Each worktree has its own `node_modules`:
- First time entering a worktree: run `npm install`
- Dependencies may differ across branches
- More disk space usage (one `node_modules` per worktree)
### Port Conflicts
When running dev servers in multiple worktrees:
```bash
# Main repo
npm run dev # Uses default port 5173
# In worktree, specify different port
npm run dev -- --port 5174
```
### IDE Integration
**VS Code:**
```bash
# Open specific worktree
code /home/jeffe/Github/canvas-website-feature-name
# Or open multiple worktrees as workspace
code --add /home/jeffe/Github/canvas-website \
--add /home/jeffe/Github/canvas-website-feature-name
```
## Troubleshooting
### Worktree Path Already Exists
If you see:
```
fatal: '/path/to/worktree' already exists
```
Remove the directory manually:
```bash
rm -rf /home/jeffe/Github/canvas-website-feature-name
git worktree prune
```
### Can't Delete Main Repo
If you have active worktrees, you can't delete the main repo. Clean up first:
```bash
./scripts/worktree-manager.sh clean
```
### Worktree Out of Sync
If a worktree seems out of sync:
```bash
cd /path/to/worktree
git fetch origin
git reset --hard origin/branch-name
```
### Hook Not Running
If the post-checkout hook isn't running:
```bash
# Check if it's executable
ls -la .git/hooks/post-checkout
# Make it executable if needed
chmod +x .git/hooks/post-checkout
# Test the hook manually
.git/hooks/post-checkout HEAD HEAD 1
```
## Disabling Automatic Worktrees
To disable automatic worktree creation:
```bash
# Remove or rename the hook
mv .git/hooks/post-checkout .git/hooks/post-checkout.disabled
```
To re-enable:
```bash
mv .git/hooks/post-checkout.disabled .git/hooks/post-checkout
```
## Advanced Usage
### Custom Worktree Location
Modify the `post-checkout` hook to change the worktree location:
```bash
# Edit .git/hooks/post-checkout
# Change this line:
WORKTREE_BASE=$(dirname "$REPO_ROOT")
# To (example):
WORKTREE_BASE="$HOME/worktrees"
```
### Worktree for Remote Branches
```bash
# Create worktree for remote branch
git worktree add ../canvas-website-remote-branch origin/feature-branch
# Or use the script
./scripts/worktree-manager.sh create origin/feature-branch
```
### Detached HEAD Worktree
```bash
# Create worktree at specific commit
git worktree add ../canvas-website-commit-abc123 abc123
```
## Best Practices
1. **Clean up regularly**: Remove worktrees for merged branches
2. **Name branches clearly**: Worktree names mirror branch names
3. **Run npm install**: Always run in new worktrees
4. **Check branch**: Always verify which branch you're on before committing
5. **Use status command**: Check all worktrees before major operations
## Resources
- [Git Worktree Documentation](https://git-scm.com/docs/git-worktree)
- [Git Hooks Documentation](https://git-scm.com/docs/githooks)
---
**Setup Complete!** New branches will automatically create worktrees. Use `./scripts/worktree-manager.sh help` for manual management.

249
scripts/worktree-manager.sh Executable file
View File

@ -0,0 +1,249 @@
#!/bin/bash
#
# Worktree Manager - Helper script for managing Git worktrees
#
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
REPO_NAME=$(basename "$REPO_ROOT")
WORKTREE_BASE=$(dirname "$REPO_ROOT")
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
show_help() {
cat << EOF
${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}
${GREEN}Worktree Manager${NC} - Manage Git worktrees easily
${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}
${YELLOW}Usage:${NC}
./worktree-manager.sh <command> [arguments]
${YELLOW}Commands:${NC}
${GREEN}list${NC} List all worktrees
${GREEN}create${NC} <branch> Create a new worktree for a branch
${GREEN}remove${NC} <branch> Remove a worktree
${GREEN}clean${NC} Remove all worktrees except main
${GREEN}goto${NC} <branch> Print command to cd to worktree
${GREEN}status${NC} Show status of all worktrees
${GREEN}help${NC} Show this help message
${YELLOW}Examples:${NC}
./worktree-manager.sh create feature/new-feature
./worktree-manager.sh list
./worktree-manager.sh remove feature/old-feature
./worktree-manager.sh clean
cd \$(./worktree-manager.sh goto feature/new-feature)
${YELLOW}Automatic Worktrees:${NC}
A Git hook is installed that automatically creates worktrees
when you run: ${CYAN}git checkout -b new-branch${NC}
${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}
EOF
}
list_worktrees() {
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}Git Worktrees:${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
cd "$REPO_ROOT"
git worktree list --porcelain | awk '
/^worktree/ { path=$2 }
/^HEAD/ { head=$2 }
/^branch/ {
branch=$2
gsub(/^refs\/heads\//, "", branch)
printf "%-40s %s\n", branch, path
}
/^detached/ {
printf "%-40s %s (detached)\n", head, path
}
' | while read line; do
if [[ $line == *"(detached)"* ]]; then
echo -e "${YELLOW} $line${NC}"
else
echo -e "${GREEN} $line${NC}"
fi
done
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
create_worktree() {
local branch=$1
if [ -z "$branch" ]; then
echo -e "${RED}Error: Branch name required${NC}"
echo "Usage: $0 create <branch-name>"
exit 1
fi
local worktree_path="${WORKTREE_BASE}/${REPO_NAME}-${branch}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}Creating worktree for branch: ${YELLOW}$branch${NC}"
echo -e "${BLUE}Location: ${YELLOW}$worktree_path${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
cd "$REPO_ROOT"
# Check if branch exists
if git show-ref --verify --quiet "refs/heads/$branch"; then
# Branch exists, just create worktree
git worktree add "$worktree_path" "$branch"
else
# Branch doesn't exist, create it
echo -e "${YELLOW}Branch doesn't exist, creating new branch...${NC}"
git worktree add -b "$branch" "$worktree_path"
fi
echo -e "${GREEN}✅ Worktree created successfully!${NC}"
echo -e ""
echo -e "To switch to the worktree:"
echo -e " ${CYAN}cd $worktree_path${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
remove_worktree() {
local branch=$1
if [ -z "$branch" ]; then
echo -e "${RED}Error: Branch name required${NC}"
echo "Usage: $0 remove <branch-name>"
exit 1
fi
cd "$REPO_ROOT"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}Removing worktree for branch: $branch${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
git worktree remove "$branch" --force
echo -e "${GREEN}✅ Worktree removed successfully!${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
clean_worktrees() {
cd "$REPO_ROOT"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}Cleaning up worktrees (keeping main/master)...${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
# Get list of worktrees excluding main/master
git worktree list --porcelain | grep "^branch" | sed 's/^branch refs\/heads\///' | while read branch; do
if [[ "$branch" != "main" ]] && [[ "$branch" != "master" ]]; then
echo -e "${YELLOW}Removing: $branch${NC}"
git worktree remove "$branch" --force 2>/dev/null || echo -e "${RED} Failed to remove $branch${NC}"
fi
done
# Prune deleted worktrees
git worktree prune
echo -e "${GREEN}✅ Cleanup complete!${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
goto_worktree() {
local branch=$1
if [ -z "$branch" ]; then
echo -e "${RED}Error: Branch name required${NC}" >&2
exit 1
fi
cd "$REPO_ROOT"
# Find worktree path for branch
local worktree_path=$(git worktree list --porcelain | awk -v branch="$branch" '
/^worktree/ { path=$2 }
/^branch/ {
b=$2
gsub(/^refs\/heads\//, "", b)
if (b == branch) {
print path
exit
}
}
')
if [ -n "$worktree_path" ]; then
echo "$worktree_path"
else
echo -e "${RED}Error: No worktree found for branch '$branch'${NC}" >&2
exit 1
fi
}
show_status() {
cd "$REPO_ROOT"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}Worktree Status:${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
git worktree list --porcelain | awk '
/^worktree/ { path=$2 }
/^branch/ {
branch=$2
gsub(/^refs\/heads\//, "", branch)
printf "\n%s%s%s\n", "Branch: ", branch, ""
printf "%s%s%s\n", "Path: ", path, ""
system("cd " path " && git status --short --branch | head -5")
}
' | while IFS= read -r line; do
if [[ $line == Branch:* ]]; then
echo -e "${GREEN}$line${NC}"
elif [[ $line == Path:* ]]; then
echo -e "${BLUE}$line${NC}"
else
echo -e "${YELLOW}$line${NC}"
fi
done
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
# Main command dispatcher
case "${1:-help}" in
list|ls)
list_worktrees
;;
create|add)
create_worktree "$2"
;;
remove|rm|delete)
remove_worktree "$2"
;;
clean|cleanup)
clean_worktrees
;;
goto|cd)
goto_worktree "$2"
;;
status|st)
show_status
;;
help|--help|-h)
show_help
;;
*)
echo -e "${RED}Unknown command: $1${NC}"
echo ""
show_help
exit 1
;;
esac