<!-- Generated by crates/swarmops-cli/src/reference.rs from the code: do not edit. Change the code, then run SWARMOPS_REGEN_REFERENCE=1 with the regen_reference test. -->

# swarmops resolve

Name the changeset REV names, and whether REV is a ref's name.

```yaml
title: "swarmops resolve"
description: "Name the changeset REV names, and whether REV is a ref's name."
group: "read"
since: "0.1.0"
mutates: false
identity_required: false
mcp_tool: "resolve_rev"
envelope_op: "resolve_rev"
exit_codes: [0, 2, 6]
errors: [E_REV_NOT_FOUND, E_INVALID_ARG]
syntax: "swarmops resolve [OPTIONS] <REV>"
```

## Syntax

```text
swarmops resolve [OPTIONS] <REV>
```

## Required arguments

### `REV`

Syntax: `<REV>`.

The revision: a ref name, a full changeset id or a unique prefix of 12 or more of its hex digits, then any number of \~N suffixes.

## Optional arguments

The command also takes the [global options](../cli.md#global-options).

## Examples

### CLI

```sh
swarmops --repo [tmp]/store resolve main
```

```json
{
  "ok": true,
  "op": "resolve_rev",
  "api": 1,
  "result": {
    "id": "[c2]",
    "kind": "ref"
  },
  "meta": {
    "repo": "[tmp]/store",
    "elapsed_ms": "[elapsed_ms]",
    "resolved": {
      "main": "[c2]"
    },
    "auth": "trust"
  }
}
```

Exit code 0. Recorded by the CLI tests. A name in square brackets, such as `[tmp]` or `[c1]`, stands for what differs from run to run: the test's temporary directory, an id, a time.

### MCP

No recorded example yet.

## Details

`resolve` answers one question: which changeset does this revision name right now? Every other
read resolves its revisions the same way, in the same snapshot it then reads, so `resolve` is how
a script or an agent fixes a revision to a full id before it does anything else with it.

### How a revision resolves

A revision is a base, then any number of `~N` suffixes:

| Base | Example | Resolves to |
| --- | --- | --- |
| A ref's name | `main`, `release/1.0` | The changeset the ref names in this snapshot. |
| A full changeset id | 64 hex digits | That changeset, if the store holds it. |
| An id prefix | 12 to 63 hex digits, either case | The one changeset whose id starts with it. |

Each `~N` walks `N` first parents back, and the suffixes add up: `main~1~1` is `main~2`, and
`main~0` is `main`'s own changeset. A merge's other parents are never walked.

`kind` is `ref` only when REV is exactly a ref's name. Anything else is `changeset`, including a
ref's name with a suffix such as `main~0`, and a full id that a ref happens to name. A `ref`
result names what the ref holds now, and moves when the ref does; the `id` you got does not.

The envelope's `meta.resolved` maps REV to the same id, as it does for every revision any
command resolves.

### Patterns

- **Pin once, read many times.** Resolve `main` once and pass the full `id` to every later `cat`,
  `ls`, `diff` or `log` of the same task. Each command takes its own snapshot, so two commands
  that both say `main` can read two different changesets if another agent lands between them.
- **Read what a compare-and-swap needs.** `ref set --expect`, `ref delete --expect` and
  `commit --parent ... --advance` protect only when they get the full id you read. Take it from
  `result.id` here, not from the ref's name.
- **Check a revision an agent was handed.** A prefix copied from a log, or a `main~N` a person
  typed, resolves here without reading any file, and the error says what is wrong with it.
- **Tell a ref from an id.** `kind` says whether the caller passed a moving name or a fixed
  changeset, for a tool that treats the two differently.

### Errors and their causes

- `E_INVALID_ARG` (exit 2): REV is not in the grammar. `HEAD`, `main^`, `main~` without a number,
  `main~-1`, an empty string, a ref name outside `main` and `release/<name>` (such as `Main` or
  `foo`), a prefix shorter than 12 hex digits, and `~N` suffixes whose sum overflows a 64-bit
  count are all refused here. The hint gives the grammar.
- `E_REV_NOT_FOUND` (exit 6): REV is well formed but names nothing: a ref that does not exist
  (the hint is `swarmops ref list`), an id or prefix of no changeset (an id of a blob or a tree
  among them; the hint is `swarmops log`), or a `~N` past the first changeset of the history (the
  hint is `swarmops log <base>`). A new store has no `main` until something creates it, such as
  a first `commit --advance main`, so `resolve main` on it is `E_REV_NOT_FOUND`.
- `E_AMBIGUOUS_ID` (exit 1): more than one changeset starts with the prefix. Give more digits;
  `swarmops log` prints whole ids.
- A ref that names a changeset the store does not hold is `E_REV_NOT_FOUND` as well, with that
  changeset's id in the message: damage that [`swarmops verify`](../commands/verify.md)
  reports.

### Caveats

- A prefix that is unique today can stop being unique when another changeset with the same
  leading digits is committed. Store full ids, never prefixes.
- A local reader sees a commit before its sync completes, so a power loss can take back a
  changeset that `resolve` already returned; see
  [known limitations](../../known-limitations.md#concurrency-and-the-lock).
- Like every read, `resolve` takes no lock, so a writer never blocks it. If commits land during
  every one of its attempts to take a snapshot, it fails with the retryable `E_BUSY` (exit 4).
- A `~N` of more than 1,024 steps lifts along the first-parent skip list, a derived value.
  `swarmops derive` derives it ahead of any reader; where it is missing, the first such walk
  derives and stores it, and a store that cannot be written resolves all the same, without
  storing it.

### Performance

`resolve` takes one snapshot of the store's metadata. In the v0.1.0 measurement a snapshot with a
changeset lookup took 50.9 µs p50 and 101.5 µs p99 on a store of 10,000 changesets, and 55.2 µs
and 107.6 µs on 100,000; the cost grows with the number of refs (1.55 ms p50 with 256 refs). See
[the performance ledger](../../perf.md#v010--just-perf-in-a-quiet-window-2026-10-21).

### Guides

- [Reading and repairing history: Name a revision](../../guides/history-and-undo.md#name-a-revision)
- [Concepts: Revision](../../concepts.md#revision)
- [Scripting the CLI](../../develop/cli-scripting.md)

## Return information

`result` is a `ResolvedRev` (`swarmops schema ResolvedRev` prints its JSON Schema).

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | yes | The full id of the changeset the revision names. |
| `kind` | `RevKind` | yes | What the revision is: `ref` when it is exactly a ref's name, `changeset` otherwise. |

Envelope `op`: `resolve_rev`.

Exit codes:

- `0`: Success.
- `2`: The request or the command line is malformed.
- `6`: Something the request names does not exist.

## Errors

- [`E_REV_NOT_FOUND`](../errors.md#e_rev_not_found) (exit 6): A revision names no changeset.
- [`E_INVALID_ARG`](../errors.md#e_invalid_arg) (exit 2): A malformed argument or request, including a command-line usage error.

Any command that opens a store may also fail with a store error, such as [`E_NOT_A_REPO`](../errors.md#e_not_a_repo), [`E_IO`](../errors.md#e_io) or [`E_STORE_CORRUPT`](../errors.md#e_store_corrupt); a command line the grammar refuses is [`E_INVALID_ARG`](../errors.md#e_invalid_arg) (exit 2).

## See also

- [Operating SwarmOps: One writer per trunk](../../operations.md#one-writer-per-trunk)
- [`swarmops cat`](cat.md)
- [`swarmops ls`](ls.md)
- [`swarmops show`](show.md)
- [`swarmops log`](log.md)
- [`swarmops history`](history.md)
- [`swarmops diff`](diff.md)
- [CLI reference](../cli.md)
- [Error codes](../errors.md)
