> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diversion.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Obliterate

> Permanently delete file blobs from a repository to reclaim storage and remove sensitive content.

`obliterate` permanently deletes the underlying blob storage for every version of files that match a path pattern, across all branches and commits. Unlike `dv rm`, which records a deletion in history but keeps the old versions reachable, obliterate removes the bytes themselves and rewrites references in history so the blobs cannot be retrieved.

Use it when:

* A very large file (game build, dataset, mistakenly committed binary) is consuming storage and is no longer needed in history.
* A secret, credential, or other sensitive file was committed and must be expunged.

<Warning>
  Obliterate is **destructive and irreversible**. There is no undo. Always run a preview first and read the plan carefully before executing.
</Warning>

## Requirements

* You must be a [**repository admin**](/basic/access-levels#organization-level-access).
* Obliterate is not supported on git-synced repositories.

## How It Works

1. You supply one or more path globs (e.g. `'/assets/*.psd'`).
2. The server resolves every (path, version) match across the repo's history.
3. Each match is classified as either **deletable** (the underlying blob is only reachable through paths covered by your globs) or **not deleted** (the same blob content is reachable through some other path that you did not include).
4. On execute, the deletion runs asynchronously. You get back a `job_id` you can poll for progress.

The "not deleted" partition matters because Diversion stores file contents by hash -- the same bytes shared across paths are stored once. If the same content also lives at another path you did not include, the storage stays. The preview tells you exactly which other paths are blocking deletion -- you can widen your glob set and re-run.

## Glob Syntax

Patterns are **anchored to the repo root**. Always quote them so your shell does not expand the wildcards.

| Pattern             | Matches                                               |
| ------------------- | ----------------------------------------------------- |
| `/assets/*.psd`     | `.psd` files directly under `/assets/`, root-anchored |
| `/assets/.../*.psd` | `.psd` files anywhere under `/assets/` (recursive)    |
| `/.../secrets.env`  | Any file named `secrets.env` anywhere in the repo     |
| `/build/large.bin`  | One specific file                                     |

The preview returns a warning if a pattern is broadly unanchored (e.g. matches across the entire repo, or only at the root when you probably meant recursive). Redundant patterns -- ones already covered by another pattern in the same call -- are also flagged.

## Preview

Always start with a preview. Nothing changes on the server.

```bash theme={null}
dv obliterate preview '/assets/old_textures/*.psd'
```

The preview returns:

* **Headline counts**: deletable versions / files, not-deleted versions / files, and any matches in shared (grouped) storage that obliterate cannot reach.
* **Reclaim estimate**: total bytes that will be freed, deduplicated by content hash (the same blob at N paths counts once).
* **Deletable paths**: per-path summary -- versions and total bytes -- sorted by largest reclaim first.
* **Not deleted paths**: per-path summary plus `unblock_paths`: the other paths in the repo that still hold the same content. Add them to your glob set if you want the blob removed.
* **Per-glob summary**: for each input glob, the files / versions / total bytes it matched on its own (before cross-glob deduplication). Only surfaced in JSON output, and in the human CLI's per-glob block when you pass more than one glob.
* **Warnings**: surfaced when a glob is broadly unanchored or redundant.

Pass multiple globs in one call to plan their combined obliteration:

```bash theme={null}
dv obliterate preview '/assets/*.psd' '/builds/.../*.pak'
```

### Human vs JSON output

The human CLI shows the **top 20** deletable / not-deleted paths sorted by bytes desc, with a trailing line like `... and 980 more (use --json for the full list)` when the result was capped. The server returns the full byte-desc-sorted lists; only the display is capped.

For the full machine-readable plan, pass `--json`:

```bash theme={null}
dv obliterate preview --json '/assets/old_textures/*.psd' | jq '.deletable_paths | length'
```

`--json` emits a single JSON document on stdout with banners and warnings suppressed (warnings move into a `warning` string field on the document).

## Execute

Once the preview matches what you intend to delete:

```bash theme={null}
dv obliterate execute '/assets/old_textures/*.psd'
```

You will be prompted to type the repository name or ID to confirm. Pass `--yes` to skip the prompt (for scripting):

```bash theme={null}
dv obliterate execute --yes '/assets/old_textures/*.psd'
```

Execute returns the same plan as preview plus a `job_id` and starts polling the job until it reaches a terminal state. The polling cap is **18 seconds** (status endpoint every 3 seconds); for shorter jobs you will see the run complete inline, and for longer jobs the CLI prints the `job_id` and exits, leaving you to follow up with `dv obliterate status`.

Flags:

* `--nowait` -- enqueue and exit immediately without polling. The CLI prints the `job_id` and you check progress yourself.
* `--json` -- emit a single JSON document instead of the human banner. **Requires `--yes`** -- an interactive confirmation prompt would corrupt the JSON stream.

Exit codes:

| Code  | Meaning                                                                                                                                                                     |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`   | Polling reached a terminal state and the job completed without failures.                                                                                                    |
| `2`   | Polling reached a terminal state and the job had one or more failed deletions.                                                                                              |
| `101` | The 18s polling cap elapsed with the job still in progress. Distinguishes "gave up waiting" from "done" so scripts can branch on it; follow up with `dv obliterate status`. |

<Note>
  `execute` returns as soon as the job is enqueued -- the actual deletion runs in the background, which is why you poll the status endpoint for progress.
</Note>

<Note>
  Concurrent commits to the same files during an obliterate run are an edge case. Avoid running obliterate while other users are actively committing to the targeted paths.
</Note>

## Tracking a Job

Each execute returns a `job_id` (e.g. `dv.obliterate.593d2b8a8dbf47559bcc73c8bcf54f41`). Check progress with:

```bash theme={null}
dv obliterate status <job_id>
```

The response carries an overall `status` plus per-row counts:

```
Obliterate job dv.obliterate.593d2b8a8dbf47559bcc73c8bcf54f41
  Status:  in_progress
  Counts:  pending=18200 done=11800 failed=0
  Started: 2026-06-18T08:39:01.147736+00:00
```

### Status values

| Status        | Meaning                                                                                                                                          |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `queued`      | The worker has not started yet.                                                                                                                  |
| `in_progress` | Worker is running. `pending` is dropping as batches complete; `done` and `failed` are growing.                                                   |
| `succeeded`   | Worker finished and all rows are `done`.                                                                                                         |
| `failed`      | Worker finished but one or more rows are `failed`. The response includes the first 20 errors with the failing SHA and a truncated error message. |
| `archived`    | The job has aged out of the queue but its counts are still inspectable.                                                                          |

A 404 from the status endpoint means there is no obliterate job with that id in this repo.

## What Gets Touched

* **History**: the obliterated file versions are removed from the repo's change log.
* **Open merges**: any open merge whose conflict referenced an obliterated file is closed, since finalizing it would otherwise produce a commit with a dead reference.

## Limits

* The preview/execute resolution call has a server-side time budget (60 seconds). If your glob matches too many files, the call is rejected with an error asking you to narrow the pattern. Split the work into more specific glob sets and run them sequentially.
* Files stored in **shared (grouped) storage** -- small files packed together for efficiency -- cannot be individually obliterated. The preview reports them as a separate skipped count so you know how many were excluded.

## Related

* [`dv obliterate`](/cmd-ref/obliterate) -- CLI reference
* [Storage](/concepts/storage) -- how blobs are stored and shared
* [Going back in time](/basic/going-back-in-time) -- non-destructive history navigation
