anthonyiles / worktree-isolation
Per-worktree test database isolation and bootstrap automation for PHP projects.
Package info
github.com/anthonyiles/worktree-isolation
Language:Shell
pkg:composer/anthonyiles/worktree-isolation
Requires
- php: ^8.2
Requires (Dev)
- illuminate/support: ^11.0|^12.0
- orchestra/testbench: ^9.0|^10.0
Suggests
- illuminate/support: Required for artisan commands (worktree:install, worktree:clean). Not needed if using vendor/bin/worktree directly.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-01 14:48:47 UTC
README
Make every git worktree of a PHP project ready to use the moment it's created, with its own dependencies, env files and databases.
After a one-off install, git worktree add automatically:
- copies
.envand.env.testingfrom the main checkout - gives the worktree its own MySQL/MariaDB test and development databases, so parallel worktrees (and AI agents running migrations in them) never touch each other's data or the main checkout's
- runs
composer install, and your JavaScript package manager's install when there's apackage.json
It works with native PHP (Herd, Valet), Docker Compose, Laravel Sail or any Docker image. Laravel isn't required.
Requirements
- PHP 8.2+ where your app runs (in the container for Docker setups)
- Bash and Git 2.54+ on the host
- MySQL or MariaDB, for per-worktree databases (SQLite needs nothing extra)
Install
composer require anthonyiles/worktree-isolation --dev vendor/bin/worktree install
Run install on the host, not inside Sail. It writes .worktree-isolation.env (commit it) and registers a git hook for this clone. Each teammate runs vendor/bin/worktree install once per clone; it keeps the committed config and just registers the hook, which then works for worktrees of any branch.
Pick the runtime that matches how you run PHP:
| Setup | Install command |
|---|---|
| Herd, Valet, local PHP | vendor/bin/worktree install |
| Laravel Sail | vendor/bin/worktree install (Sail's image and network are detected) |
| Docker Compose | vendor/bin/worktree install --runtime=docker-compose --compose-service=app |
| Any other Docker image | vendor/bin/worktree install --runtime=docker-image --docker-image=myapp --docker-network=myapp_default |
Not using Laravel? Add --test-command="php vendor/bin/phpunit" (or your runner). Laravel projects can use php artisan worktree:install with the same options.
Usage
Create worktrees as usual. Setup runs automatically:
git worktree add ../worktrees/feature-auth -b feature-auth
cd ../worktrees/feature-auth
Then run everything through vendor/bin/worktree:
vendor/bin/worktree test # run tests (args go to the test runner) vendor/bin/worktree test --filter=UserTest vendor/bin/worktree php artisan migrate # anything else runs in this worktree's runtime vendor/bin/worktree composer require foo/bar vendor/bin/worktree npm run dev vendor/bin/worktree setup # re-run setup, e.g. after fixing DB settings vendor/bin/worktree clean --stale # drop databases of removed worktrees
Tip: alias worktree='vendor/bin/worktree'.
With Sail or Docker Compose, don't call
sailordocker composedirectly inside a worktree. They exec into whichever container is already running, which is usually mounted on the main checkout, so commands run against the wrong code and database.vendor/bin/worktreealways targets the current worktree.
What setup does
- Copies
.envand.env.testing(or.env.testing.example) from the main checkout, if the worktree doesn't have them. - Points both at the worktree's own databases (see below), before anything else runs.
- Docker Compose only: starts the worktree's own stack with
docker compose -p <project>-<worktree> up -d. - Runs
composer install. - Creates the test and development databases. A new development database is migrated and seeded; an existing one is only migrated.
- If there's a
package.json, runsnpm install(orpnpm,yarnorbun, by lockfile). A failure here only warns.
Databases
Each worktree's databases are named after its directory:
| Main checkout | Worktree feature-auth |
|
|---|---|---|
Test (.env.testing) |
testing |
testing_wt_feature-auth |
Development (.env) |
myapp |
myapp_wt_feature-auth |
- Only MySQL/MariaDB connections are changed. SQLite and others are left alone.
- The main checkout's test database name must contain
test, and a worktree is never pointed at one of the main checkout's databases. - Long names are shortened with a hash (test names to 40 characters, development names to 64).
- The names are written into the worktree's env files, so
php artisan test,phpunitand the like use them too.vendor/bin/worktree testalso creates the test database if it's missing. vendor/bin/worktree cleandrops all per-worktree databases (after asking;--forceskips the prompt).--staleonly drops those whose worktree has been removed. The main checkout's databases are never dropped.
Configuration
.worktree-isolation.env, written by install. Only the runtime line is required; everything else is optional:
# native | docker-compose | docker-image WORKTREE_RUNTIME=native # WORKTREE_TEST_COMMAND=php artisan test # docker-compose # WORKTREE_COMPOSE_SERVICE=app # WORKTREE_COMPOSE_FILE=docker-compose.yml # WORKTREE_COMPOSE_PROJECT_BASE=my-project # stack name prefix; defaults to the directory name # WORKTREE_DB_WAIT_SECONDS=30 # how long setup waits for a new stack's database # docker-image # WORKTREE_DOCKER_IMAGE=sail-8.4/app # WORKTREE_DOCKER_NETWORK=myproject_sail # WORKTREE_DOCKER_WORKDIR=/var/www/html # Per-worktree development databases (false or unset: worktrees share the main checkout's) WORKTREE_DEV_DB_PER_WORKTREE=true # WORKTREE_DEV_DB_MIGRATE_COMMAND=php artisan migrate --no-interaction # empty to skip # WORKTREE_DEV_DB_SEED_COMMAND=php artisan db:seed --no-interaction # empty to skip # WORKTREE_TESTING_ENV_FILE=.env.testing # WORKTREE_TESTING_ENV_EXAMPLE=.env.testing.example
Commands are run as you'd type them in a shell, so quoting works.
Docker notes. Containers run as your host user, so files they create in the worktree stay yours. With Docker Compose, each worktree runs its own stack, so fixed host ports (e.g. 8080:80) clash between worktrees: make them configurable (e.g. ${APP_PORT:-8080}:80) if you run several at once. With the docker-image runtime, build the image first (sail build or docker compose build).
AI agents
Agents need to know to use vendor/bin/worktree in worktrees. Add the instructions to your agent files with:
vendor/bin/worktree install --agents # AGENTS.md / CLAUDE.md, whichever exist (else AGENTS.md) vendor/bin/worktree install --agents=CLAUDE.md,AGENTS.md # or name the files
This adds a short block between <!-- worktree-isolation:start --> and <!-- worktree-isolation:end --> markers, and updates it in place when re-run. A CLAUDE.md that imports @AGENTS.md is left alone. The full agent guide is docs/ai-agents.md.
License
MIT