# Quickstart

> Initialize a source workspace, create a filtered copy-on-write child, list it, and remove it with expected stdout signals.

- Repository: anomalyco/rift
- GitHub: https://github.com/anomalyco/rift
- Human docs: https://www.grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662
- Complete Markdown: https://www.grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662/llms-full.txt

## Source Files

- `README.md`
- `specs.md`
- `crates/cli/src/main.rs`
- `crates/core/src/lib.rs`
- `crates/core/src/filter.rs`

---

---
title: "Quickstart"
description: "Initialize a source workspace, create a filtered copy-on-write child, list it, and remove it with expected stdout signals."
---

The `rift` CLI registers a source directory, creates a filtered copy-on-write child under adjacent `.rifts` storage, lists direct children on stdout, and moves removed children into `.trash` without printing removal paths in the default invocation.

## Prerequisites

| Requirement | Detail |
| --- | --- |
| Platform | macOS (APFS) or Linux (btrfs subvolumes or native reflinks). Workspace creation is not implemented on Windows. |
| Filesystem | Copy-on-write must succeed for the selected backend; btrfs directories require `rift init` before `rift create`. |
| Install | Global npm/bun package `rift-snapshot`, or a local Cargo build via `scripts/install.sh`. |

<Tabs>
<Tab title="npm">

```bash
npm install -g rift-snapshot
```

</Tab>
<Tab title="bun">

```bash
bun add -g rift-snapshot
```

</Tab>
<Tab title="cargo">

```bash
./scripts/install.sh
```

Installs an optimized binary to `${CARGO_HOME:-$HOME/.cargo}/bin/rift`.

</Tab>
</Tabs>

<Warning>
This repository is marked experimental. CLI behavior and interfaces may change without notice.
</Warning>

## Resulting layout

After the workflow below, a source workspace `app` and one named child `parser-fix` produce this layout:

```text
~/code/
  app/                              source workspace (.rift marker)
  .rifts/
    app/
      parser-fix/                   created child workspace
      .trash/                       removed workspaces (after `rift remove`)
```

The central SQLite registry lives in the platform user data directory (`rift/rift.sqlite` under `dirs::data_local_dir()`). Paths in stdout are absolute canonical paths.

## Walkthrough

<Steps>
<Step title="Initialize the source workspace">

From your project directory:

```bash
cd ~/code/app
rift init
```

`rift init` resolves the target path before calling core `init`:

- If a managed ancestor exists, it initializes that root.
- Otherwise it selects the nearest Git root (directory containing `.git`).
- Pass `--here` to initialize exactly the current directory instead.

On first registration, Rift writes a `.rift` marker (ULID) and inserts a root record (`parent_id = NULL`) into the registry. On macOS and non-btrfs Linux, registration is in place. On btrfs, first init of an ordinary directory reflink-imports into a new subvolume and swaps it into the same path.

<RequestExample>

```bash
cd ~/code/app
rift init
```

</RequestExample>

<ResponseExample>

macOS or already-registered workspace (stderr):

```text
Ready  /Users/you/code/app
```

First-time btrfs conversion (stderr, progress then ready):

```text
Initializing  /Users/you/code/app

First-time setup can take a moment.
New rifts will be instant.

Creating BTRFS subvolume...
Importing workspace...

Ready  /Users/you/code/app
```

When run from inside the initialized tree (stderr):

```text
run `cd /Users/you/code/app` to enter the initialized workspace
```

Already initialized (stderr):

```text
Already initialized  /Users/you/code/app
```

Restored missing marker (stderr):

```text
Restored marker  /Users/you/code/app
```

</ResponseExample>

Exit code: `0` on success; `1` on failure with a message on stderr.

</Step>

<Step title="Create a filtered child workspace">

Create a child from the source. Filtered copy is the default: heavyweight regenerable artifacts are omitted; manifests and lockfiles are preserved.

```bash
rift create --name parser-fix --no-hooks
```

| Flag | Default | Quickstart use |
| --- | --- | --- |
| `--name` | Random `adjective-noun` (e.g. `amber-badger`) | `--name parser-fix` for predictable paths |
| `--copy-all` | off (filtered) | Omit; filtered is default |
| `--no-hooks` | hooks run when `.rift.toml` exists | Skip hooks when no config is present |
| `--into` | `<parent>/.rifts/<workspace-name>/` | Omit for default storage |

`rift create` searches upward for `.rift`, copies the resolved managed workspace, records the immediate parent in the registry, and prints the new workspace path to **stdout** (one line, absolute).

<RequestExample>

```bash
cd ~/code/app
rift create --name parser-fix --no-hooks
```

</RequestExample>

<ResponseExample>

stdout:

```text
/Users/you/code/.rifts/app/parser-fix
```

</ResponseExample>

Filtered copies exclude paths whose components match built-in artifact names at any depth, including `node_modules`, `target`, `.venv`, `dist`, `build`, `coverage`, and Yarn cache paths under `.yarn/`. Lockfiles such as `package-lock.json` and `Cargo.lock` are kept.

<Note>
If the source is a Git repository, the child receives detached `HEAD` at the same commit with index and working-tree state preserved. See [Git integration](/git-integration).
</Note>

</Step>

<Step title="List direct children">

From the source workspace (or any path inside it):

```bash
rift list
```

`rift list` resolves `of` upward to the nearest `.rift` marker (default: current working directory) and prints **one child path per line** on stdout, in registry order. No header or labels.

<RequestExample>

```bash
cd ~/code/app
rift list
```

</RequestExample>

<ResponseExample>

stdout:

```text
/Users/you/code/.rifts/app/parser-fix
```

When no children exist, stdout is empty and exit code is `0`.

</ResponseExample>

</Step>

<Step title="Remove the child workspace">

From inside the child, or with an explicit path:

```bash
cd /Users/you/code/.rifts/app/parser-fix
rift remove
```

For a **created** rift (not the source root), `rift remove` moves the full descendant subtree into adjacent `.trash` storage as `<storage-parent>/.trash/<id>-<name>`, updates the registry, and produces **no stdout** in the default CLI mode.

<RequestExample>

```bash
cd /Users/you/code/.rifts/app/parser-fix
rift remove
```

</RequestExample>

<ResponseExample>

stdout: *(empty)*

stderr: *(empty in default mode)*

Exit code: `0`

</ResponseExample>

After removal, `rift list` from the source prints nothing. The trashed directory remains on disk under `.trash` until garbage collection.

<Warning>
Removing the **source root** requires `rift remove -f` and prints `Unregistered  <path>` on stderr. That flow unregisters the source, removes its `.rift` marker, and trashes all descendants. It is not part of this quickstart.
</Warning>

</Step>

<Step title="Garbage-collect trashed storage (optional)">

Physically delete trashed directories:

```bash
rift gc
```

Each deleted trash path is printed on stdout, one per line.

<RequestExample>

```bash
rift gc
```

</RequestExample>

<ResponseExample>

stdout:

```text
/Users/you/code/.rifts/app/.trash/01HXXXXXXXXXXXXXX-parser-fix
```

</ResponseExample>

</Step>
</Steps>

## Stdout and stderr reference

| Command | Stream | Signal | When |
| --- | --- | --- | --- |
| `rift init` | stderr | `Ready  <path>` | Successful registration or conversion |
| `rift init` | stderr | `Already initialized  <path>` | Root already registered, marker intact |
| `rift init` | stderr | `Restored marker  <path>` | Registry entry exists but marker was missing |
| `rift init` | stderr | `run \`cd <path>\` to enter...` | Init from inside tree; default CLI (no shell wrapper) |
| `rift init` | stderr | BTRFS progress lines | First-time btrfs subvolume conversion |
| `rift create` | **stdout** | `<absolute-child-path>` | Always on success |
| `rift list` | **stdout** | `<child-path>` per line | One line per direct active child |
| `rift remove` | stdout | *(none)* | Default mode, created rift |
| `rift remove -f` | stderr | `Unregistered  <path>` | Source root unregistration only |
| `rift gc` | **stdout** | `<trash-path>` per line | Each physically deleted trash entry |

All failures print a message to **stderr** and exit with code **1**.

## Verification checklist

| Step | Filesystem check | Registry-backed check |
| --- | --- | --- |
| After `init` | `app/.rift` exists | `rift list` returns empty stdout |
| After `create` | `.rifts/app/parser-fix/` exists with its own `.rift` | `rift list` prints the child path |
| After `remove` | Child path gone; `.rifts/app/.trash/<id>-parser-fix` exists | `rift list` returns empty stdout |
| After `gc` | Trash directory deleted | `rift gc` printed the trash path |

Child `.rift` files contain a new ULID distinct from the source marker.

## Common failures

| Symptom (stderr) | Cause | Fix |
| --- | --- | --- |
| `no initialized workspace found; run \`rift init\` from the root folder` | No `.rift` marker or registry entry above CWD | Run `rift init` from the project root |
| `this workspace must be initialized first; run \`rift init\` from its root folder` | btrfs source not yet converted to subvolume | Run `rift init` on the btrfs directory first |
| `this workspace is missing its \`.rift\` marker; run \`rift init\` to restore it` | Registry entry exists, marker deleted | Run `rift init` to restore |
| `copy-on-write cloning unavailable: ...` | Unsupported platform or filesystem | Use a supported OS/backend; see [Copy strategies](/copy-strategies) |
| `postcreate hook failed at ...` | Hook command in `.rift.toml` failed | Fix hook or pass `--no-hooks`; workspace remains registered |

## End-to-end script

```bash
cd ~/code/app
rift init
CHILD=$(rift create --name parser-fix --no-hooks)
rift list
cd "$CHILD"
rift remove
rift gc
```

Capture `CHILD` from stdout for scripting. After `rift remove`, expect silent success; run `rift list` from the source to confirm the child is gone.

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Install `rift-snapshot` globally or build the CLI from source.
</Card>
<Card title="Initialize a workspace" href="/initialize-workspace">
Git-root vs `--here` selection, btrfs conversion progress, and marker restoration.
</Card>
<Card title="Create workspaces" href="/create-workspaces">
`--name`, `--into`, `--copy-all`, hooks, and random name generation.
</Card>
<Card title="Manage and remove workspaces" href="/manage-workspaces">
List, ancestors, trash semantics, `-f`, `--children`, and `rift gc`.
</Card>
<Card title="Storage layout" href="/storage-layout">
Default `.rifts` sibling storage, trash naming, and database location.
</Card>
</CardGroup>
