Guide: Git Worktrees
dde supports Git worktrees natively. When you run a project from a worktree checkout, dde automatically detects this and assigns a unique hostname so you can run multiple branches of the same project simultaneously.
How Detection Works
Section titled “How Detection Works”When dde project:up runs, ConfigManager::detectWorktree() executes git worktree list --porcelain in the project directory. A worktree is detected when:
- The
git worktree listcommand succeeds and returns at least two entries. - The current project directory matches a non-main worktree entry (the first entry is always the main worktree).
If the project directory is the main worktree or the repository has no worktrees, detection returns null and standard hostname resolution applies.
Hostname Generation
Section titled “Hostname Generation”The hostname for a worktree follows the pattern:
<project-name>-<suffix>.testWhere <suffix> is derived from the worktree directory name through the sanitizeWorktreeSuffix() method:
- Strip project name prefix — if the directory starts with the project name followed by a hyphen, that prefix is removed. For example,
my-app-feature-xbecomesfeature-x. - Transliterate unicode — non-ASCII characters are converted to ASCII equivalents using Symfony’s
AsciiSlugger(e.g.ueforü). - Replace invalid characters — anything that is not
a-z,0-9, or-is replaced with a hyphen. - Collapse hyphens — consecutive hyphens are merged into one.
- Trim hyphens — leading and trailing hyphens are removed.
- Fallback — if the suffix is empty after processing, it defaults to
worktree. - Truncate — the result is capped at 63 characters (DNS label limit).
Examples
Section titled “Examples”Assuming project name my-app:
| Worktree directory | Hostname |
|---|---|
~/projects/my-app (main) | my-app.test |
~/projects/my-app-feature-x | my-app-feature-x.test |
~/projects/my-app-PROJ-123 | my-app-proj-123.test |
~/projects/my-app-hotfix | my-app-hotfix.test |
If the directory name does not start with the project name:
| Worktree directory | Hostname |
|---|---|
~/worktrees/bugfix-login | my-app-bugfix-login.test |
1. Create worktrees
Section titled “1. Create worktrees”Use standard Git commands to create worktrees:
cd ~/projects/my-appgit worktree add ~/projects/my-app-feature-x feature/feature-xgit worktree add ~/projects/my-app-PROJ-123 feature/PROJ-1232. Start each worktree
Section titled “2. Start each worktree”Each worktree needs its own dde project:up:
# Main worktreecd ~/projects/my-appdde project:up
# Feature worktreecd ~/projects/my-app-feature-xdde project:up3. Access in browser
Section titled “3. Access in browser”- Main:
https://my-app.test - Feature:
https://my-app-feature-x.test - Ticket:
https://my-app-proj-123.test
Each hostname gets its own trusted TLS certificate.
TLS Certificates
Section titled “TLS Certificates”When a worktree is detected, CertificateManager generates a certificate that includes the worktree hostname. The wildcard DNS resolution via dnsmasq (.test domain) ensures all worktree hostnames resolve correctly.
Traefik Labels
Section titled “Traefik Labels”The docker-compose override generated by DockerComposeManager::generateOverride() includes Traefik labels for the worktree hostname. This means each worktree gets its own Traefik routing rule, allowing simultaneous access to multiple branches.
WorktreeInfo Model
Section titled “WorktreeInfo Model”The detection result is stored in a WorktreeInfo DTO:
final readonly class WorktreeInfo{ public function __construct( public string $mainDirectory, // Path to the main worktree public string $worktreeDirectory, // Path to the current worktree public string $branch, // Branch name (e.g. "feature/feature-x") public string $suffix, // Directory basename (e.g. "my-app-feature-x") ) {}}Viewing Worktree Information
Section titled “Viewing Worktree Information”Use dde project:describe to see worktree details for the current project, including the detected hostname and worktree path.
Use dde project:open to open the project URL in your browser — it automatically resolves the correct worktree hostname.
Limitations
Section titled “Limitations”- Each worktree runs as an independent set of containers. They share system services (Traefik, dnsmasq, MariaDB, etc.) but have separate application containers.
- Database names are derived from the project name, not the worktree. If worktrees need isolated databases, configure
DATABASE_URLmanually per worktree. - The
.dde/directory is shared across worktrees (it lives in the repository). Worktree-specific hooks or plugins are not supported.