When using git worktrees for parallel development, gitignored files that are essential for development (like .claude/
, .env
, database configs, etc.) are not present in the new worktree. This breaks development workflows that depend on these files.
A git alias that wraps git worktree add
to automatically copy essential gitignored files to new worktrees.
First, create a .gitignore.essentials
file in your project root that lists which gitignored items should be copied to new worktrees:
# .gitignore.essentials
# This file lists gitignored items that should be copied to new worktrees
# Format: source:destination:type
# - source: path relative to project root
# - destination: path in new worktree (optional, defaults to source)
# - type: copy|link|mkdir (optional, defaults to copy)
# Claude configuration (symlink to share between worktrees)
.claude/::.claude/:link
# AI documentation (copy for independent editing)
.ai/::.ai/:copy
# Environment files (copy and customize per worktree)
.env.development
.env.test
# Database config (copy for different DBs per worktree)
config/database.yml
# Create empty directories
tmp/:mkdir
log/:mkdir
coverage/:mkdir
# Dummy app databases (copy)
spec/dummy/db/development.sqlite3
spec/dummy/db/test.sqlite3
Create a script at ~/.git-scripts/copy-worktree-essentials.sh
:
#!/bin/bash
# copy-worktree-essentials.sh
# Copies essential gitignored files to a new worktree
set -e
# Check if we have the right number of arguments
if [ $# -lt 1 ]; then
echo "Usage: $0 <worktree-path> [source-path]"
exit 1
fi
WORKTREE_PATH="$1"
SOURCE_PATH="${2:-$(pwd)}"
# Resolve absolute paths
WORKTREE_PATH=$(cd "$WORKTREE_PATH" 2>/dev/null && pwd)
SOURCE_PATH=$(cd "$SOURCE_PATH" 2>/dev/null && pwd)
if [ ! -d "$WORKTREE_PATH" ]; then
echo "Error: Worktree path does not exist: $WORKTREE_PATH"
exit 1
fi
# Look for .gitignore.essentials file
ESSENTIALS_FILE="$SOURCE_PATH/.gitignore.essentials"
if [ ! -f "$ESSENTIALS_FILE" ]; then
echo "No .gitignore.essentials file found in $SOURCE_PATH"
echo "Skipping essentials copy."
exit 0
fi
echo "Copying essentials from $SOURCE_PATH to $WORKTREE_PATH"
# Process each line in the essentials file
while IFS= read -r line || [ -n "$line" ]; do
# Skip comments and empty lines
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line// }" ]] && continue
# Parse the line (format: source:destination:type)
IFS=':' read -r source dest type <<< "$line"
# Trim whitespace
source=$(echo "$source" | xargs)
dest=$(echo "${dest:-$source}" | xargs)
type=$(echo "${type:-copy}" | xargs)
# Skip if source is empty
[ -z "$source" ] && continue
SOURCE_FILE="$SOURCE_PATH/$source"
DEST_FILE="$WORKTREE_PATH/$dest"
case "$type" in
"link"|"symlink")
if [ -e "$SOURCE_FILE" ]; then
echo " Linking: $source -> $dest"
# Remove existing file/link if it exists
[ -e "$DEST_FILE" ] && rm -rf "$DEST_FILE"
# Create parent directory if needed
mkdir -p "$(dirname "$DEST_FILE")"
# Create symlink with absolute path
ln -s "$SOURCE_FILE" "$DEST_FILE"
else
echo " Warning: Source not found for link: $source"
fi
;;
"mkdir"|"directory")
echo " Creating directory: $dest"
mkdir -p "$DEST_FILE"
;;
"copy"|*)
if [ -e "$SOURCE_FILE" ]; then
echo " Copying: $source -> $dest"
# Create parent directory if needed
mkdir -p "$(dirname "$DEST_FILE")"
# Copy preserving permissions and timestamps
if [ -d "$SOURCE_FILE" ]; then
cp -rp "$SOURCE_FILE" "$DEST_FILE"
else
cp -p "$SOURCE_FILE" "$DEST_FILE"
fi
else
echo " Warning: Source not found: $source"
fi
;;
esac
done < "$ESSENTIALS_FILE"
echo "Essentials copy complete!"
Make the script executable:
chmod +x ~/.git-scripts/copy-worktree-essentials.sh
Add this to your ~/.gitconfig
:
[alias]
# Create worktree with essentials
wt-add = "!f() { \
if [ $# -lt 1 ]; then \
echo 'Usage: git wt-add <path> [<branch>]'; \
return 1; \
fi; \
git worktree add \"$@\" && \
~/.git-scripts/copy-worktree-essentials.sh \"$1\" \"$(git rev-parse --show-toplevel)\"; \
}; f"
# List worktrees with status
wt-list = "worktree list --porcelain"
# Remove worktree and clean up
wt-remove = "!f() { \
if [ $# -lt 1 ]; then \
echo 'Usage: git wt-remove <path>'; \
return 1; \
fi; \
echo 'Removing worktree: $1'; \
git worktree remove \"$1\"; \
}; f"
Instead of using git worktree add
, use the new alias:
# Create a new worktree for a feature branch
git wt-add ../worktrees/feature-auth feature/authentication
# Create a worktree for a new branch based on main
git wt-add ../worktrees/bugfix-123 -b bugfix/issue-123 main
# Create a worktree with just a path (uses current branch point)
git wt-add ../worktrees/experiment
cd ~/projects/prompt-engine
# Check current worktrees
git wt-list
# Create a new worktree for library integration
git wt-add ../worktrees/library-integration -b feature/library-integration main
# Output:
# Preparing worktree (new branch 'feature/library-integration')
# HEAD is now at abc123 Latest commit message
# Copying essentials from /Users/avi/projects/prompt-engine to /Users/avi/projects/worktrees/library-integration
# Linking: .claude/ -> .claude/
# Copying: .ai/ -> .ai/
# Copying: .env.development -> .env.development
# Creating directory: tmp/
# Creating directory: log/
# Essentials copy complete!
# Now you can immediately use the new worktree
cd ../worktrees/library-integration
claude-swarm start .claude/rails-engine-dev-swarm.yml # Works immediately!
Different projects can have different .gitignore.essentials
files:
Rails Project:
# .gitignore.essentials
.claude/:link
config/database.yml:copy
config/credentials.yml.enc:copy
config/master.key:copy
.env:copy
tmp/:mkdir
log/:mkdir
storage/:mkdir
Node.js Project:
# .gitignore.essentials
.claude/:link
.env:copy
.env.local:copy
node_modules/:link # Share node_modules between worktrees
dist/:mkdir
coverage/:mkdir
Python Project:
# .gitignore.essentials
.claude/:link
.env:copy
venv/:mkdir # Create empty, will recreate venv per worktree
.pytest_cache/:mkdir
__pycache__/:mkdir
You can extend the script to support conditional copying based on file existence:
# .gitignore.essentials
# Only copy if exists in source
.env.development:copy:if-exists
.env.production:copy:if-exists
# Always create these directories
tmp/:mkdir:always
log/:mkdir:always
- Seamless Workflow - New worktrees are immediately ready for development
- Project-Specific - Each project defines its own essential files
- Flexible - Choose between copying, linking, or creating directories
- Version Controlled - The
.gitignore.essentials
file is tracked, ensuring consistency across team - No Overhead - Only runs when creating worktrees, not on every git operation
- Safe - Doesn't affect normal git operations or existing workflows
# Make sure the script is executable
chmod +x ~/.git-scripts/copy-worktree-essentials.sh
# Check the path in your git alias matches the script location
git config --get alias.wt-add
# Check if .gitignore.essentials exists
ls -la .gitignore.essentials
# Run the script manually to see detailed output
~/.git-scripts/copy-worktree-essentials.sh /path/to/worktree /path/to/source
# Some files might need special permissions preserved
# The script uses cp -p to preserve permissions
# For sensitive files like keys, ensure proper permissions after copying
chmod 600 config/master.key
- Use Symlinks for Shared Resources - Configuration directories that should be identical across worktrees
- Copy Environment Files - So each worktree can have different settings
- Create Empty Directories - For logs, tmp files, and build outputs
- Document in README - Let team members know about the
git wt-add
command - Regular Cleanup - Use
git worktree prune
to clean up deleted worktrees
You can also use this pattern in CI/CD pipelines:
# .github/workflows/test.yml
steps:
- uses: actions/checkout@v3
- name: Setup test worktree
run: |
git wt-add test-worktree
cd test-worktree
# Your tests have all necessary files
This approach solves the gitignored files problem elegantly by:
- Extending git's worktree functionality without modifying git itself
- Providing project-specific configuration through
.gitignore.essentials
- Maintaining compatibility with standard git workflows
- Offering flexibility in how different types of files are handled
The abstraction is clean: "Essential development files follow me to new worktrees."