<!-- 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 log

List history along first parents, newest first.

```yaml
title: "swarmops log"
description: "List history along first parents, newest first."
group: "read"
since: "0.1.0"
mutates: false
identity_required: false
mcp_tool: "log"
envelope_op: "log"
exit_codes: [0, 2, 6]
errors: [E_NOT_A_REPO, E_INVALID_ARG]
syntax: "swarmops log [OPTIONS] [REV]"
```

## Syntax

```text
swarmops log [OPTIONS] [REV]
```

## Required arguments

None.

## Optional arguments

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

### `REV`

Syntax: `<REV>`.

Where to start (default: main).

### `--limit`

Syntax: `-n, --limit <N>`.
Default: `50`.

The most changesets to list.

### `--cursor`

Syntax: `--cursor <CURSOR>`.

Resume a log: the previous page's cursor, exactly as it was returned.

### `--path`

Syntax: `--path <PATH>...`.

Only the changesets that change PATH, or anything below it; give it again for more.

## Examples

### CLI

```sh
swarmops --repo [tmp]/store log
```

```json
{
  "ok": true,
  "op": "log",
  "api": 1,
  "result": {
    "entries": [
      {
        "id": "[c2]",
        "parents": [
          "[c1]"
        ],
        "author_agent": "agent/worker-17",
        "created_at": "[created_at]",
        "title": "second"
      },
      {
        "id": "[c1]",
        "parents": [],
        "author_agent": "agent/worker-17",
        "created_at": "[created_at]",
        "title": "first commit"
      }
    ],
    "truncated": false
  },
  "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

`log` walks history from a revision back through first parents, newest first, one page at a
time, and gives each changeset's id, parents, agent, time and title. With `--path` it keeps only
the changesets that changed the paths you name.

### Where it walks

From REV, `main` when it is omitted, back through each changeset's first parent to the first
changeset of the history. REV can be any revision, so `log <changeset>` walks an agent's commit
that has not landed, back into the history it was made on. A merge's other parents are never
walked. Each entry's `title` is the first line of the message, and `created_at` the committer's
time in Unix milliseconds.

Without `--path`, every changeset on the walk is listed, an empty one (`commit --allow-empty`)
among them.

### Paths

`--path P` keeps the changesets whose own changes, against their first parent, touch P: a file
at P (its content, its kind, or whether it is there) or anything below a directory P. Paths match
whole components, so `--path src/lib` matches neither `src/lib.rs` nor `src/library/`. One
trailing `/` is taken. Give `--path` again for more paths: a changeset that touches any of them
is listed once. A path that no changeset touched gives an empty page, not an error; an empty path
or one with a leading `/` is `E_INVALID_ARG`.

With `--path`, a changeset with no changes is never listed.

### Pages

`-n`/`--limit` (50 by default, at least 1) cuts the page. A page with more history after it says
`truncated: true` and gives `cursor`, the full id of its last entry. `--cursor` with that id
continues from that changeset's first parent:

- REV is not read when a cursor is given: the cursor alone says where the page starts.
- The paths are not in the cursor. Give the same `--path` options on every page of a filtered
  log; a page without them lists every changeset from there on.
- A cursor is a full changeset id the store holds; a prefix or anything else is `E_INVALID_ARG`.
- A cursor at the first changeset of a history gives an empty page.

A cursor is a changeset id, so the pages of one log stay on the history the first page started,
whatever lands meanwhile.

### Patterns

- **What landed since I last looked.** Keep the newest id you have seen; read `log` pages from
  `main` until that id appears.
- **Who changed this area.** `log --path src/api --path docs/api` lists the changesets that touched
  either directory, with their agents; `show` of one gives its files.
- **See an agent's chain.** `log <changeset>` of an agent's commit lists its first-parent chain
  and the history it was made on below it; the entries above the first one `main`'s history also
  holds are the commits a `land` of it would squash.
- **Through MCP**, the tool is `log`, with `rev`, `limit`, `cursor` and `paths` (an array). Ask
  for 100 entries or fewer a call: see
  [Result size limits](../../develop/mcp.md#result-size-limits).

### Caveats

- History is first-parent only: what a merge brought in from its other parents shows as the
  merge's own change, and the changesets on those parents are never listed. Native histories have
  no merges (`main` is linear, and lands squash); histories brought in by `git import` can.
- No renames are detected: a file moved from A to B touches both A and B.
- `--path` reads a derived value, the newest changeset on the chain that touched the path. The
  first reader that asks for it derives it: it walks back over the changesets that do not touch
  the path and stores the result, with a memo every 64 changesets of a longer walk, so that a
  later walk from anywhere on that stretch takes at most 64 steps. So the first filtered log of a
  path on a long history pays that walk; a later one reads the stored values and walks only what
  they do not cover. `swarmops derive` does not derive these values ahead, since they are per
  path. A store that cannot be written answers all the same, without keeping them.
- A derived value that fails its checks is `E_STORE_CORRUPT`, with the hint
  `swarmops verify --deep`.

### Performance

A plain log reads each changeset's index record and object: in the v0.1.0 measurement 100
first-parent steps took 419 µs on a chain of 200,000 changesets and 479 µs on 1,000,000, and
`log -n 5` gave its 5 entries in 0 ms on a store of 32,101 changesets. See
[the performance ledger](../../perf.md#v010--just-perf-in-a-quiet-window-2026-10-21).

### Guides

- [Reading and repairing history: Walk the history](../../guides/history-and-undo.md#walk-the-history)
- [The daily loop: Look at what landed](../../operate/daily-loop.md#3-look-at-what-landed)
- [Concepts: Derived value](../../concepts.md#derived-value)

## Return information

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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string or null` | no | When `truncated`: where the next page continues. Opaque: pass it back as given, as the next request's `cursor`. |
| `entries` | `array of ChangesetSummary` | yes | The changesets. |
| `truncated` | `boolean` | yes | Whether older history follows this page. |

Envelope `op`: `log`.

Exit codes:

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

## Errors

- [`E_NOT_A_REPO`](../errors.md#e_not_a_repo) (exit 6): No store at the given path, or no store was named.
- [`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: Look at what landed](../../operations.md#3-look-at-what-landed)
- [`swarmops resolve`](resolve.md)
- [`swarmops cat`](cat.md)
- [`swarmops ls`](ls.md)
- [`swarmops show`](show.md)
- [`swarmops history`](history.md)
- [`swarmops diff`](diff.md)
- [CLI reference](../cli.md)
- [Error codes](../errors.md)
