Getting Started

Workspaces & Worktrees

What a uxd workspace is, how ports keep them isolated, and the git worktree sharp edges worth knowing before you hit them.

A workspace is one materialized ref: a git worktree, a port allocation, a private data directory, and the state entry that ties them together.

uxd owns one bare, partial clone per project and creates one git worktree per ref. That is the whole model, and most of its behaviour — good and awkward — follows from it.

Why worktrees

A worktree gives you a real, separate directory that shares object storage with one clone. So you can:

  • Hold five branches open at once without five full clones on disk.
  • Leave your main checkout alone — no stashing to review someone else's branch.
  • Run several dev servers at the same time, each on its own port.

Port isolation

Each workspace gets a contiguous block of ports, sized by the project's ports key (1–10, default 1). The first is exported as $UXD_PORT and interpolated as {port}; the rest are $UXD_PORT_1, $UXD_PORT_2, … and {port+1}, {port+2}, ….

~/.uxd/my-project.toml
ports = 2

[env]
PORT = "{port}"
API_PORT = "{port+1}"

Allocation is deterministic first — the slug is hashed into a window above base_port, and uxd only scans upward if that block is taken or occupied. In practice a given branch keeps the same port across sessions.

See Run dev servers in parallel for the working setup.

The data directory

Every workspace gets $UXD_DATA_DIR — a private directory outside the worktree at {worktrees_path}/.data/<slug>. Use it for things that must not pollute the git tree: SQLite files, upload directories, caches.

~/.uxd/my-project.toml
[env]
DATABASE_URL = "file:{data_dir}/dev.db"

Known caveats

These are consequences of the worktree model, not bugs uxd can paper over. Knowing them saves an afternoon.

Inside a worktree, .git is a file pointing back at the primary repo. Tooling that assumes a .git directory — some Docker volume setups, older shell scripts — can trip on this.
Repositories that configure relative hook paths (husky does) may resolve them incorrectly from a worktree, and pnpm install can try to install hooks into the bare repo. uxd doctor warns when core.hooksPath is set. It does not modify your repository.
The first checkout of a worktree, and a diff across not-yet-fetched trees, need network access. Offline use is degraded by design — the trade for not downloading history you will never read.
Git refuses to check out the same branch in two worktrees. uxd detects this and points you at the existing workspace instead of failing obscurely.
rm -rf-ing a workspace directory leaves git's worktree metadata and uxd's state entry orphaned. list then reports the workspace as missing:
SLUG        KIND    BRANCH      STATUS   PORT  AGE  LAST USED
feat-login  branch  feat/login  missing  3053  2m   1m
Reconcile with:
uxd my-project clean --prune-state --yes

Inspecting workspaces

uxd my-project list                 # all workspaces for a project
uxd my-project list --du            # add a disk-usage column
uxd my-project feat/login info      # one workspace in detail
uxd my-project feat/login info --du
slug: feat-login
kind: branch
ref: feat/login
branch: feat/login
path: ~/dev/uxd/my-project/trees/feat-login
ports: 3053
status: clean
created: 2026-07-25T05:51:38.363Z
last used: 2026-07-25T05:51:38.827Z

Both accept --json for scripting. See Script with uxd.