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.
Obliterate is destructive and irreversible. There is no undo. Always run a preview first and read the plan carefully before executing.
Requirements
- You must be a repository admin.
- Obliterate is not supported on git-synced repositories.
How It Works
- You supply one or more path globs (e.g.
'/assets/*.psd').
- The server resolves every (path, version) match across the repo’s history.
- 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).
- 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.
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:
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:
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:
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):
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. |
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.
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.
Tracking a Job
Each execute returns a job_id (e.g. dv.obliterate.593d2b8a8dbf47559bcc73c8bcf54f41). Check progress with:
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.