Worktrees
Git worktrees of an application, each with its own address, dev processes and database.
Anvil serves each Git worktree of an application next to the application, with
its own address, dev processes and database, so main and a feature branch can
run at the same time.
Only an application's own folder (parked or linked) can have worktrees, not an alias (a linked name pointing at another application's folder).
What a worktree gets
For an application shop and the branch feature/login:
| Folder | shop--feature-login, next to the application's folder |
| Address | shop--feature-login.test. The branch is lowercased; anything other than letters, digits, . and - becomes - |
| Dev processes | The application's process settings, run separately in the worktree's folder with their own port. They don't start when Anvil starts |
| PHP, language version, HTTPS | The application's, unless set for the worktree |
| Database | Its own: shop_feature_login, empty or a copy of the application's. A name that could also belong to another application's worktree ends in a short hash |
| Redis and cache keys | Their own prefix, shop_feature_login_ (REDIS_PREFIX and CACHE_PREFIX for Laravel and Statamic) |
Worktrees are listed under their application in the sidebar, including ones
made with git worktree add.
Create a worktree
In the app: the application's Git tab › New Worktree…, or right-click the application › New Worktree…:
- Branch, e.g.
feature/login, with the resulting address below. - New branch, from the app's current one: on creates the branch; off needs an existing one.
- Copy the database too: on copies the application's database; off starts empty for your migrations. Shown only when the database can be copied: a SQLite file, or MySQL or PostgreSQL in an Anvil service. On a server Anvil doesn't manage, the worktree points at a database named after it there, which you create yourself.
Create runs git worktree add, copies the files the
hooks list and writes the worktree's values into its env
files. A second sheet lists the hooks' commands, such as composer install,
and runs them only on Run.
In the terminal, inside the application's folder (or with --app):
anvil worktree add feature/login --new| Option | Meaning |
|---|---|
--app <app> | The application, when you aren't in its folder |
--new | Create the branch from the application's current one |
--copy-db | Start the worktree's database as a copy of the application's |
--copy-db=false | Start it empty |
| neither | Follow the hooks' Copy the database too (off unless you turned it on) |
It then asks Run composer install; … in shop--feature-login? [y/N]; y
creates the database, then runs the commands. Without a TTY it prints the
commands and runs nothing. After N, the database is created the first time you
run a command in the worktree through Anvil's terminal commands (or start it in
the desktop app).
Its database
The database is created the first time you start the worktree, run something in it through Anvil's terminal commands, run its after-create commands, or click Prepare database on its Overview. Until then, for a database in an Anvil service, its address says the database hasn't been created and serves nothing. The Overview's Database line shows the name and whether it is a copy or empty.
If a database with the worktree's name exists and Anvil didn't create it, Anvil
uses it only after Use existing on the Overview or
anvil worktree use-db shop--feature-login. The worktree's migrations and tests
then change that database.
A worktree never uses the application's database, unless you take the database keys out of the application's own hooks (see Where hooks come from). When something would point it there, the worktree is stopped and its Overview says why:
- A database key the hooks set (
DB_DATABASE,DB_URL,DATABASE_URL) is empty, or its placeholder has no value. - An env key named like a database (
POSTGRES_DB,DB_NAME,DB_LEGACY_DATABASE) still has the application's value and the hooks don't set it. Give it a value in the hooks' keys. composer.jsonsetsextra.runtime.dotenv_overload(the env files would win over the worktree's settings) orextra.runtime.dotenv_path(Anvil can't read the env files it uses).
Tests
Anvil reads the first existing of phpunit.xml, phpunit.dist.xml and
phpunit.xml.dist (ignoring commented-out lines), and .env.testing:
| Test config names | Tests run against |
|---|---|
Another database (:memory: included) | That one |
| The application's database | The worktree's own |
The application's database, in a setting that wins over Anvil's (a <server> entry or <env force="true">, or a key Anvil doesn't set) | Nothing: the worktree is stopped until you name a separate test database there |
The message reads like:
Anvil can't point shop--feature-login at its own database (phpunit.dist.xml's
DB_DATABASE names shop's database and wins over Anvil's: name a test database
of its own there), so nothing of it runsWorktree hooks
The hooks decide what a new worktree gets. Each application type has a preset; every field can be changed:
| Field | What it does | Laravel preset |
|---|---|---|
copy | Files or folders copied from the application (ignored files aren't in a checkout). A * in the last path segment matches any characters | .env |
copy_database | Copy the database too: on, a copy of the application's; off, an empty one | unset (Settings › Worktrees decides) |
keys | Env keys set for the worktree. Placeholders: {db} its database, {url} its address, {prefix} its key prefix, {port} its port, {name} its name | DB_DATABASE, DB_URL, DATABASE_URL = {db}; APP_URL = {url}; REDIS_PREFIX, CACHE_PREFIX = {prefix} |
check | Keys that must not keep the application's value in the worktree's env files | DB_DATABASE, DB_URL, DATABASE_URL, REDIS_PREFIX, CACHE_PREFIX |
after_create | Commands offered once the worktree is made, run only on Run. {install} is the install command for the lock file's package manager, such as npm ci | composer install, {install}, php artisan migrate |
For a key set to {db}, a URL value gets only its database name replaced, and a
SQLite path maps to the file inside the worktree.
The keys go into everything Anvil starts in the worktree and into the env files
Anvil copied: .env, .env.local, .env.development and
.env.development.local; for Symfony, .env, .env.local, .env.<APP_ENV>
and .env.<APP_ENV>.local. Anvil never writes .env.example, .env.testing or
.env.test. If a checked key still has the application's value in an env file,
the Overview names it and offers Rewrite with the hooks, which changes only
those lines.
Where hooks come from
Each field comes from the first of these that sets it:
- The application's own hooks: its Settings tab, Worktrees section
(
[sites."<name>".worktree]inconfig.toml). - The project's
.anvil.toml,[worktree], shared with everyone who clones it. .worktreeincludein the project: the files to copy.- The preset for the application's type, edited in Settings › Worktrees (Reset to default restores Anvil's).
- Settings › Worktrees › Copy the database too.
Only the application's own hooks can take the database keys out (the worktree
then shares the application's database). A .anvil.toml or preset that leaves
them out still gets them from the next source.
anvil worktree hooks [app] prints the effective hooks, each field's source,
and any mistakes.
# .anvil.toml
[worktree]
copy = [".env", "storage/app/public"]
after_create = ["composer install", "{install}", "php artisan migrate --seed"]Step by step: a Laravel application
shop uses MySQL in an Anvil service, with DB_DATABASE=shop in its .env.
- In
shop's Git tab, New Worktree…:feature/login, New branch on, Copy the database too on. Create makes../shop--feature-login, copies.envand setsDB_DATABASE=shop_feature_login,APP_URL=http://shop--feature-login.testandREDIS_PREFIX/CACHE_PREFIXtoshop_feature_login_. - Run on the next sheet creates the database, then runs
composer install,npm ci(or your lock file's install) andphp artisan migrate. - If your PHPUnit config forces
shop(<server name="DB_DATABASE" value="shop"/>), the worktree is stopped until you name a test database there. Laravel's defaultphpunit.xml(SQLite:memory:) is fine. - Start it from its Dev tab; it answers at
http://shop--feature-login.test. Run tests with Run ▾ (thetestscript fromcomposer.jsonorpackage.json) orphp artisan testin its folder.
From the terminal, inside shop:
anvil worktree add feature/login --new --copy-db
# Run composer install; npm ci; php artisan migrate in shop--feature-login? [y/N] yThen restart anvil start: it reads the application list at startup, so the
worktree answers only after the restart. Its dev processes (Vite, the queue)
don't run with the CLI alone: worktrees never start with Anvil, and
anvil app start works through the desktop app. Run them yourself in the
worktree's folder, e.g. npm run dev.
Remove a worktree
In the app: right-click the worktree › Remove Worktree…. Anvil stops its processes and removes its folder; the branch stays. Also delete … drops its databases too: the ones Anvil created, and one taken with Use existing. With uncommitted changes or unpushed commits the button becomes Remove anyway.
Before you confirm, Anvil lists ignored or untracked files that go with the
folder: "These will be deleted with the folder: …". That covers the worktree's
SQLite database, any *.sqlite, *.sqlite3 or *.db under database/, and
.env files the application's folder has no copy of or whose values differ
from its copy (apart from the keys the hooks write). vendor and
node_modules aren't listed.
In the terminal:
anvil worktree remove shop--feature-login [--drop-db] [--force] [--yes]| Running processes | Refuses while the worktree's dev server or any dev process runs. Stop them in the app, or with anvil app stop <worktree> while the app is open. With the CLI alone, stop what you started in the folder yourself; Anvil doesn't track it |
--drop-db | Drops the databases recorded for it (created by Anvil, or taken with Use existing); without it they stay. A SQLite database in the folder is removed with it, as are ignored files such as .env, vendor and node_modules |
--force | Removes it even with uncommitted changes or unpushed commits |
--yes | When the folder holds files like those above, the command names them and asks first. Without a TTY (a script, an agent) it refuses until you add --yes |
A database left behind by a removed worktree shows on the application's Overview with Delete and Keep.
Worktrees made outside Anvil
A worktree made with git worktree add is served with the same rules. Its
Overview offers to copy the files the hooks list (such as .env) and install
its packages. Whether its database starts as a copy follows Settings ›
Worktrees › Copy the database too, unless the application, its
.anvil.toml or a preset sets it.
Commands
| Command | What it does |
|---|---|
anvil worktree list [app] | Worktrees of one application, or of all: name, branch, folder |
anvil worktree add <branch> [--app <app>] [--new] [--copy-db[=false]] | Add a worktree, see Create a worktree |
anvil worktree hooks [app] | The hooks as they apply and where each field comes from |
anvil worktree use-db <name> [--yes] | Use a database with the worktree's name that Anvil didn't create; --yes doesn't ask |
anvil worktree remove <name> [--drop-db] [--force] [--yes] | Remove a worktree, see Remove a worktree |
In config.toml
| Field | Meaning |
|---|---|
worktree_database | "copy": Settings › Worktrees › Copy the database too is on. Left out when off |
[worktree_presets.<preset>] | Your changes to a preset (laravel, statamic, symfony, next, nuxt, node, python, other), with the hooks fields above |
[worktrees."<worktree>"] | Per worktree, written by Anvil: db ("copy" or "empty"), copies (the databases it uses, as service and name: made by Anvil or taken with Use existing) and adopted (set by Use existing) |
[sites."<app>".worktree] | The application's own hooks, with the hooks fields above |
See Configuration.