# Lazy mounts

`oak mount` puts a working tree on top of a remote repository **without cloning it**. Files are fetched the first time something reads them, so you can be editing a multi-gigabyte monorepo within seconds, and running ten tasks in parallel costs ten mounts, not ten clones.

```bash
oak mount acme/monorepo                 # mounts at ./monorepo
oak mount acme/monorepo ./fix-auth      # …or wherever you say
cd fix-auth
$EDITOR src/auth/callback.rs            # only the files you touch are downloaded
oak status && oak commit && oak push
```

Each mount runs as a background daemon; the command returns as soon as the mount is live. Before your first mount, do the [one-time setup](#setting-up-mounts) for your OS.

## How a mount works

- **A mount is one branch.** By default `oak mount` starts a fresh *virtual branch* off the latest `main`, named after the mount directory plus a short id — mount at `./fix-auth` and you're on `fix-auth--a1b2c3d4`.
- **Edits stay local until you push.** Changes go into an overlay on your machine — the **active commit**, the one you're amending as you edit. `oak commit` checkpoints it onto the virtual branch and starts a new one; `oak push` publishes the branch like any other.
- **The normal commands just work.** Inside a mount, `oak status`, `diff`, `commit`, `desc`, `log`, `hash`, `info`, `push`, `pull`, `finish`, `conflict`, and `agent state` all operate on the mount's virtual branch, so a mount feels like a clone.

A few things are different inside a mount:

| Command | Inside a mount |
|---|---|
| `oak switch` | Refused — a mount is one branch. Mount another branch elsewhere. |
| `oak merge` | Doesn't run. Merge from a clone or the web. |
| `oak reset`, `oak restore` | Don't run. Use `oak diff` / `oak status` to see the overlay, or end the mount with `--force` to discard it. |
| Hooks | Don't run. |
| `oak commit --json`, `--push` | Not supported; run `oak commit` then `oak push`. |

## Continue an existing branch

```bash
oak mount acme/monorepo ./fix-auth --branch fix-auth--a1b2c3d4
```

`--branch` mounts an existing remote branch instead of starting a fresh one: its history and files become the mount's. Use it to pick up a branch an agent already pushed, give it a follow-up, or [resolve its conflicts](/docs/merging#when-a-pushed-branch-conflicts) without a full clone.

## Finish, end, and list mounts

```bash
oak mount list                     # every mount, labelled live, stale, or orphaned
oak mount list --json              # with dirty and unpushed counts — good for watching a fleet

oak finish --desc-file desc.md --json          # inside a mount: describe, commit, push, then unmount
oak mount finish ./fix-auth --desc-file desc.md --json   # same thing, from outside

oak mount end ./fix-auth           # unmount and delete the directory; refuses if there's unpushed work
oak mount end ./fix-auth --force   # …discarding uncommitted changes
oak mount end                      # end every mount under ~/oaktree
```

`oak finish` / `oak mount finish` check everything they can **before** changing anything, and only unmount once the push has succeeded. If a step fails the mount stays intact and the output names the command to run next — so finishing never silently loses work.

## Setting up mounts

Mounts use your operating system's own virtual-filesystem layer. Each needs a one-time setup per machine; no third-party kernel extension or driver is involved. Everything other than mounting — clone, commit, push, pull — works without it.

### macOS: the Oak Mount app (macOS 26+)

On macOS the filesystem runs inside a signed FSKit extension, **OakFS**, shipped in the **Oak Mount** app. The app only carries the extension: it must be installed and enabled once, but it doesn't need to be running, and one install serves every mount.

1. Run your first `oak mount`. If the installer didn't already, the CLI downloads Oak Mount, verifies its checksum, installs it in `/Applications` (or `~/Applications`), and opens it once so macOS registers the extension.
2. Enable it: **System Settings → General → Login Items & Extensions → File System Extensions**, then turn on **OakFS**. The Oak Mount window has a button that takes you there. No restart needed.
3. Run `oak mount` again. From now on it attaches immediately.

If the extension is ever switched off, `oak mount` says so rather than failing mysteriously. Keep the app in `/Applications`.

**Pick a destination outside `~/Documents`, `~/Desktop`, `~/Downloads`, and iCloud Drive.** macOS requires Full Disk Access for your terminal to mount inside those; somewhere like `~/oaktree` avoids it.

### Linux: FUSE

1. Install FUSE 3: `sudo apt install fuse3` on Debian and Ubuntu, or your distro's equivalent. Oak mounts through the `fusermount3` helper it provides.
2. If your agent or editor runs as a different user, or in a sandbox, and needs to see into the mount, uncomment `user_allow_other` in `/etc/fuse.conf`.
3. On distros that gate FUSE behind a group (often `fuse`), add yourself to it and log in again.

### Windows: Projected File System

Windows mounts use ProjFS — the same primitive GVFS and Scalar use for huge Git monorepos. Enable it once, from an elevated PowerShell:

```powershell
Enable-WindowsOptionalFeature -Online -FeatureName Client-ProjFS -NoRestart
```

Or **Settings → Apps → Optional features → Windows Projected File System**.

### Check it works

```bash
oak mount acme/web /tmp/web-test && oak mount list
oak mount end /tmp/web-test
```

If the mount doesn't attach, the error almost always points back at one of the steps above.

## Keep build output off the mount

A mount is ideal for reading and editing source and poor for build output and tool caches. Keep those on your real disk:

- **Redirect build directories.** `CARGO_TARGET_DIR=/tmp/fix-auth-target cargo test`; likewise for `dist/`, `build/`, and other generated directories. A build cache on a mount is slow and can produce baffling, wrong errors.
- **Don't install `node_modules` on a mount.** On Linux, FUSE refuses to create symlinks and hard links (`EPERM`), which package managers rely on. Install outside the mount and point the tool at it.
- **Don't rewrite the whole tree at once.** A repo-wide formatter run, codemod, or huge `rm -rf` can wedge a mount. Scope formatters to what you changed (`cargo fmt -p <crate>`).

## Troubleshooting

| Symptom | Fix |
|---|---|
| Commands fail with `os error 22` | The mount is wedged. Commit and push what you can, then `oak mount end --force <dir>` and mount again. **`--force` discards uncommitted changes.** |
| `oak mount list` shows a mount as `stale` after a crash or reboot | Running `oak mount` again for it recovers it from its saved state. |
| A mount shows as `orphaned` (its directory is gone) | `oak mount forget --orphaned` drops those registrations; their saved state is kept and reported. |
| A registration won't go away | `oak mount forget <dir>` removes it without touching on-disk state (`--force` if it looks live). |

Mount state lives under `~/.oak/mounts/` (override with `OAK_MOUNTS_ROOT`).

## Mounts, clones, or sparse clones?

| | Mount | Clone | Sparse clone |
|---|---|---|---|
| Time to first edit on a huge repo | Seconds | Full download | Download of the subtree |
| Files on disk | Only what's been read | Everything | Only the cone |
| Needs OS setup | Yes | No | No |
| Good for builds | Keep output off the mount | Yes | Yes |
| Can merge from it | No | Yes | Yes |

For a plain on-disk checkout of part of a monorepo, see [Sparse clones](/docs/sparse-clones). To run many agents across an org's repos, see [Agent spaces](/docs/spaces).
