Workspaces & Worktrees
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}, ….
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.
[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.
.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.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.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.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
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.