Skip to content

Instantly share code, notes, and snippets.

@jtsternberg
Created January 29, 2026 15:50
Show Gist options
  • Select an option

  • Save jtsternberg/ea58569dbc7531d51325621e7f5ec1fe to your computer and use it in GitHub Desktop.

Select an option

Save jtsternberg/ea58569dbc7531d51325621e7f5ec1fe to your computer and use it in GitHub Desktop.
git-worktree - Git worktree management with dependency symlinks

Git Worktree Guide

Work on multiple branches simultaneously without switching branches or duplicating dependencies.

Quick Start

# Create a worktree for an existing branch
/git-tree feature-branch

# Work in the new worktree
cd local-frontend/gittree-feature-branch

# Dependencies are already available via symlinks
git status
./vendor/bin/sail artisan test

What Are Git Worktrees?

Git worktrees let you checkout multiple branches at once into separate directories. The Lindris implementation adds:

  • Automatic symlinks to vendor/, node_modules/, and .env
  • Shared Docker containers (no duplicate environments)
  • Works seamlessly with existing tools and workflows

Creating a Worktree

Using the Claude Command (Recommended)

# Create and automatically cd to worktree (default)
/git-tree feature-branch

# Create without changing directory
/git-tree feature-branch --no-cd

The command will:

  1. Prompt to create the branch if it doesn't exist
  2. Create the worktree in a parallel directory
  3. Set up symlinks to dependencies automatically
  4. Automatically cd to the new worktree (unless --no-cd is specified)

Using the Script Directly

# Create and automatically cd to worktree (default)
./bin/git-tree.sh feature-branch

# Create without changing directory
./bin/git-tree.sh feature-branch --no-cd

What Gets Created

lindris-monorepo/
├── local-frontend/
│   ├── lindris-frontend/              # Main repository
│   │   ├── vendor/                    # Real dependencies
│   │   ├── node_modules/              # Real dependencies
│   │   └── .env                       # Real environment
│   └── gittree-feature-branch/        # New worktree
│       ├── vendor -> ../lindris-frontend/vendor/
│       ├── node_modules -> ../lindris-frontend/node_modules/
│       ├── .env -> ../lindris-frontend/.env
│       └── [all your code from feature-branch]

Working in a Worktree

# After creating a worktree, you're automatically in the new directory
# (unless you used --no-cd flag)

# All standard git commands work
git status
git commit -m "Changes"
git push

# Dependencies are already available
./vendor/bin/sail artisan test
./vendor/bin/sail npm run dev
npm run build

# Docker containers are shared with main repo
./vendor/bin/sail up  # Uses existing containers

Managing Worktrees

List Active Worktrees

git worktree list

Remove a Worktree

# Option 1: Use git command
git worktree remove gittree-feature-branch

# Option 2: Delete directory and prune
rm -rf local-frontend/gittree-feature-branch
git worktree prune

Common Workflows

Working on Multiple Features

# Main repo: feature A
cd local-frontend/lindris-frontend
git checkout feature-a

# Worktree 1: bugfix (automatically cd's there)
/git-tree bugfix-123
# Now in: local-frontend/gittree-bugfix-123

# Worktree 2: reviewing PR (automatically cd's there)
/git-tree pr-review-456
# Now in: local-frontend/gittree-pr-review-456

Quick PR Testing

/git-tree pr-123-feature
# You're now in the worktree directory - ready to test immediately
./vendor/bin/sail artisan test

Important Notes

Shared Dependencies

All worktrees share the same vendor/, node_modules/, and .env:

  • Pro: No reinstalling dependencies, saves disk space
  • Con: If branches need different dependency versions, use a full clone instead

Shared Docker Environment

Worktrees use the main repo's Docker containers:

  • Pro: No duplicate containers, consistent database state
  • Con: Database changes affect all worktrees

Git Operations

All worktrees share the same git repository:

  • Cannot checkout the same branch in multiple worktrees
  • Commits in any worktree affect the shared repository
  • Use unique branches for each worktree

Known Issues & TODO

Pre-commit Hook with Sail (Frontend)

Issue: The pre-commit hook in the frontend repository currently has issues when running from worktrees due to Sail container context.

Status: Needs investigation and fix

Details:

  • Pre-commit hooks attempt to run tests via Sail
  • Worktrees may not have proper Docker environment context
  • Similar to pre-push hook, needs Docker environment detection logic

Potential Solution:

  • Add isDockerEnvironment() function to pre-commit hook (similar to pre-push)
  • Detect if running inside Docker vs. from worktree
  • Adjust Sail command execution based on context

Workaround:

  • Temporarily disable pre-commit hook when working in worktrees
  • Run tests manually before committing
  • Or commit from main repo instead of worktree

Worktree Swapping for UI Testing

Feature Request: Enable swapping which worktree is served by LocalWP/webserver for UI testing.

Status: Future enhancement

Use Case:

  • Test UI changes from worktree in browser
  • Compare UI behavior between branches
  • Quickly switch which branch is served without restarting containers

Potential Solutions:

  1. Symlink Swapping:
    • Make the main repo directory a symlink to current active worktree
    • Script to swap symlink target: ./bin/git-tree-swap.sh feature-branch
    • LocalWP/Docker follows the symlink to serve correct branch

Considerations:

  • How to handle shared dependencies (vendor/node_modules remain symlinked)
  • Database state (shared across all worktrees)
  • Asset building (each worktree needs compiled assets)
  • Container restart time vs. symlink switching speed

Troubleshooting

Dependencies Not Found

If symlinks aren't working:

ls -la vendor node_modules .env  # Check symlinks exist

If needed, recreate manually:

ln -s ../lindris-frontend/vendor vendor
ln -s ../lindris-frontend/node_modules node_modules
ln -s ../lindris-frontend/.env .env

Different Dependencies Needed

For branches with different dependencies:

# Remove symlinks and install separately
rm vendor node_modules
composer install
npm install

Docker Issues

Ensure main repo's containers are running:

cd local-frontend/lindris-frontend
./vendor/bin/sail up -d
description argument-hint allowed-tools
Checkout an existing branch to a new git worktree with symlinks to dependencies
<branch-name> [--no-cd]
Bash(./bin/git-tree.sh:*)

Create a git worktree in a parallel directory with symlinks to vendor, node_modules, and .env.

Step 1: Determine arguments

If first argument is provided ($1):

  • Use $1 as the branch name

If first argument is NOT provided:

  • Ask the user which branch they want to checkout as a worktree

Check if --no-cd flag is present in arguments:

  • If present, pass it to the script to prevent auto-cd
  • If not present, the script will automatically cd to the new worktree

Step 2: Execute the script

./scripts/git-tree.sh <branch-name> [--no-cd]

The script will:

  • Create a worktree in a parallel directory with gittree- prefix (e.g., ../gittree-feature-branch/)
  • Create symlinks to vendor/, node_modules/, and .env from the original repo
  • Prompt to create the branch if it doesn't exist
  • Automatically cd to the worktree directory (unless --no-cd flag is provided)

Step 3: Confirm

After execution, show the user the worktree location and confirm symlinks were created.

#!/bin/bash
#
# Create a git worktree in a parallel directory with symlinks to dependencies
#
# Usage: git-tree.sh <branch-name> [--repo <path>] [--no-cd]
#
# Arguments:
# branch-name Required. The branch to checkout in the new worktree
# --repo <path> Optional. Path to repository (defaults to current directory)
# --no-cd Optional. Don't change to the worktree directory after creation
#
# Examples:
# ./git-tree.sh feature-branch
# ./git-tree.sh bugfix-123 --repo /path/to/repo
# ./git-tree.sh feature-branch --no-cd
#
set -e
set -o pipefail
# Parse command line arguments
BRANCH=""
REPO_PATH=""
AUTO_CD=true
while [[ $# -gt 0 ]]; do
case $1 in
--repo)
REPO_PATH="$2"
shift 2
;;
--no-cd)
AUTO_CD=false
shift
;;
*)
if [[ -z "$BRANCH" ]]; then
BRANCH="$1"
else
echo "Error: Unknown argument $1"
exit 1
fi
shift
;;
esac
done
# Validate required arguments
if [[ -z "$BRANCH" ]]; then
echo "Error: Branch name is required"
echo "Usage: $0 <branch-name> [--repo <path>] [--no-cd]"
exit 1
fi
# Default to current directory if not specified
if [[ -z "$REPO_PATH" ]]; then
REPO_PATH=$(pwd)
fi
# Get absolute path and ensure we're in a git repo
REPO_PATH=$(cd "$REPO_PATH" && pwd)
if ! git -C "$REPO_PATH" rev-parse --git-dir > /dev/null 2>&1; then
echo "Error: $REPO_PATH is not a git repository"
exit 1
fi
# Get the repository name
REPO_NAME=$(basename "$REPO_PATH")
# Get the parent directory
PARENT_DIR=$(dirname "$REPO_PATH")
# Create worktree name with gittree- prefix
WORKTREE_NAME="gittree-${BRANCH}"
WORKTREE_PATH="${PARENT_DIR}/${WORKTREE_NAME}"
# Check if worktree already exists
if [[ -d "$WORKTREE_PATH" ]]; then
echo "Error: Worktree directory already exists: $WORKTREE_PATH"
exit 1
fi
# Check if branch exists
if git -C "$REPO_PATH" rev-parse --verify "$BRANCH" > /dev/null 2>&1; then
echo "Branch '$BRANCH' exists"
else
echo "Branch '$BRANCH' does not exist"
read -p "Create new branch '$BRANCH'? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted"
exit 1
fi
fi
# Create the worktree
echo "Creating worktree at: $WORKTREE_PATH"
git -C "$REPO_PATH" worktree add "$WORKTREE_PATH" "$BRANCH"
if [ $? -ne 0 ]; then
echo "Error: Failed to create worktree"
exit 1
fi
echo ""
echo "✓ Worktree created successfully"
echo ""
# Create symlinks for dependencies
SYMLINKS_CREATED=0
# Symlink vendor directory
if [[ -d "$REPO_PATH/vendor" ]]; then
echo "Creating symlink: vendor → ../${REPO_NAME}/vendor"
ln -s "../${REPO_NAME}/vendor" "$WORKTREE_PATH/vendor"
SYMLINKS_CREATED=$((SYMLINKS_CREATED + 1))
fi
# Symlink node_modules directory
if [[ -d "$REPO_PATH/node_modules" ]]; then
echo "Creating symlink: node_modules → ../${REPO_NAME}/node_modules"
ln -s "../${REPO_NAME}/node_modules" "$WORKTREE_PATH/node_modules"
SYMLINKS_CREATED=$((SYMLINKS_CREATED + 1))
fi
# Symlink .env file
if [[ -f "$REPO_PATH/.env" ]]; then
echo "Creating symlink: .env → ../${REPO_NAME}/.env"
ln -s "../${REPO_NAME}/.env" "$WORKTREE_PATH/.env"
SYMLINKS_CREATED=$((SYMLINKS_CREATED + 1))
fi
echo ""
if [[ $SYMLINKS_CREATED -eq 0 ]]; then
echo "⚠ No symlinks created (no vendor, node_modules, or .env found)"
else
echo "✓ Created $SYMLINKS_CREATED symlink(s)"
fi
echo ""
echo "Worktree location: $WORKTREE_PATH"
echo ""
# Change to worktree directory unless --no-cd flag was set
if [[ "$AUTO_CD" == true ]]; then
cd "$WORKTREE_PATH"
echo "Changed to worktree directory"
else
echo "To switch to the new worktree:"
echo " cd $WORKTREE_PATH"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment