Skip to content

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.

When dde project:up runs, ConfigManager::detectWorktree() executes git worktree list --porcelain in the project directory. A worktree is detected when:

  1. The git worktree list command succeeds and returns at least two entries.
  2. 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.

The hostname for a worktree follows the pattern:

<project-name>-<suffix>.test

Where <suffix> is derived from the worktree directory name through the sanitizeWorktreeSuffix() method:

  1. 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-x becomes feature-x.
  2. Transliterate unicode — non-ASCII characters are converted to ASCII equivalents using Symfony’s AsciiSlugger (e.g. ue for ü).
  3. Replace invalid characters — anything that is not a-z, 0-9, or - is replaced with a hyphen.
  4. Collapse hyphens — consecutive hyphens are merged into one.
  5. Trim hyphens — leading and trailing hyphens are removed.
  6. Fallback — if the suffix is empty after processing, it defaults to worktree.
  7. Truncate — the result is capped at 63 characters (DNS label limit).

Assuming project name my-app:

Worktree directoryHostname
~/projects/my-app (main)my-app.test
~/projects/my-app-feature-xmy-app-feature-x.test
~/projects/my-app-PROJ-123my-app-proj-123.test
~/projects/my-app-hotfixmy-app-hotfix.test

If the directory name does not start with the project name:

Worktree directoryHostname
~/worktrees/bugfix-loginmy-app-bugfix-login.test

Use standard Git commands to create worktrees:

Terminal window
cd ~/projects/my-app
git worktree add ~/projects/my-app-feature-x feature/feature-x
git worktree add ~/projects/my-app-PROJ-123 feature/PROJ-123

Each worktree needs its own dde project:up:

Terminal window
# Main worktree
cd ~/projects/my-app
dde project:up
# Feature worktree
cd ~/projects/my-app-feature-x
dde project:up
  • 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.

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.

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.

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")
) {}
}

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.

  • 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_URL manually per worktree.
  • The .dde/ directory is shared across worktrees (it lives in the repository). Worktree-specific hooks or plugins are not supported.